Skip to content

Commit 2f897f9

Browse files
committed
The _get_spec_prod internal method of BaseSource will now check for radius matches within a precision specified in the XGA utils file, rather than requiring an exact match. For issue #1372
1 parent 3af9bd2 commit 2f897f9

File tree

2 files changed

+46
-47
lines changed

2 files changed

+46
-47
lines changed

xga/__init__.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
# This code is a part of X-ray: Generate and Analyse (XGA), a module designed for the XMM Cluster Survey (XCS).
2-
# Last modified by David J Turner (turne540@msu.edu) 04/11/2024, 10:36. Copyright (c) The Contributors
2+
# Last modified by David J Turner (turne540@msu.edu) 16/07/2025, 12:25. Copyright (c) The Contributors
33
from . import _version
44
__version__ = _version.get_versions()['version']
55

6-
from .utils import xga_conf, CENSUS, OUTPUT, NUM_CORES, XGA_EXTRACT, BASE_XSPEC_SCRIPT, MODEL_PARS, \
7-
MODEL_UNITS, ABUND_TABLES, XSPEC_FIT_METHOD, COUNTRATE_CONV_SCRIPT, NHC, BLACKLIST, HY_MASS, MEAN_MOL_WEIGHT, \
8-
SAS_VERSION, XSPEC_VERSION, SAS_AVAIL, DEFAULT_COSMO, TELESCOPES, USABLE, DEFAULT_TELE_SEARCH_DIST, COMBINED_INSTS, \
9-
eSASS_AVAIL, SRC_REGION_COLOURS, check_telescope_choices, PRETTY_TELESCOPE_NAMES, ESASS_VERSION
6+
from .utils import (xga_conf, CENSUS, OUTPUT, NUM_CORES, XGA_EXTRACT, BASE_XSPEC_SCRIPT, MODEL_PARS, MODEL_UNITS,
7+
ABUND_TABLES, XSPEC_FIT_METHOD, COUNTRATE_CONV_SCRIPT, NHC, BLACKLIST, HY_MASS, MEAN_MOL_WEIGHT,
8+
SAS_VERSION, XSPEC_VERSION, SAS_AVAIL, DEFAULT_COSMO, TELESCOPES, USABLE, DEFAULT_TELE_SEARCH_DIST,
9+
COMBINED_INSTS, eSASS_AVAIL, SRC_REGION_COLOURS, check_telescope_choices, PRETTY_TELESCOPE_NAMES,
10+
ESASS_VERSION, RAD_MATCH_PRECISION)

xga/sources/base.py

Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# This code is a part of X-ray: Generate and Analyse (XGA), a module designed for the XMM Cluster Survey (XCS).
2-
# Last modified by David J Turner (turne540@msu.edu) 16/07/2025, 10:48. Copyright (c) The Contributors
2+
# Last modified by David J Turner (turne540@msu.edu) 16/07/2025, 12:25. Copyright (c) The Contributors
33

44
import gc
55
import os
@@ -35,7 +35,7 @@
3535
from ..sourcetools.misc import coord_to_name
3636
from ..utils import ALLOWED_PRODUCTS, dict_search, xmm_det, xmm_sky, OUTPUT, SRC_REGION_COLOURS, \
3737
DEFAULT_COSMO, ALLOWED_INST, COMBINED_INSTS, obs_id_test, PRETTY_TELESCOPE_NAMES, OBS_ID_REGEX, \
38-
check_telescope_choices
38+
check_telescope_choices, RAD_MATCH_PRECISION
3939

4040
# This disables an annoying astropy warning that pops up all the time with XMM images
4141
# Don't know if I should do this really
@@ -3293,52 +3293,50 @@ def _get_spec_prod(self, outer_radius: Union[str, Quantity], obs_id: str = None,
32933293
else:
32943294
raise TypeError("You may only pass a quantity or a string as outer_radius")
32953295

3296-
if over_sample is not None:
3297-
over_sample = int(over_sample)
3298-
if min_counts is not None:
3299-
min_counts = int(min_counts)
3300-
if min_sn is not None:
3301-
min_sn = float(min_sn)
3302-
3303-
# Sets up the extra part of the storage key name depending on if grouping is enabled
3304-
if group_spec and min_counts is not None:
3305-
extra_name = "_mincnt{}".format(min_counts)
3306-
elif group_spec and min_sn is not None:
3307-
extra_name = "_minsn{}".format(min_sn)
3308-
else:
3309-
extra_name = ''
3310-
3311-
# And if it was oversampled during generation then we need to include that as well
3312-
if over_sample is not None:
3313-
extra_name += "_ovsamp{ov}".format(ov=over_sample)
3314-
3315-
if outer_radius != 'region':
3316-
# The key under which these spectra will be stored
3317-
spec_storage_name = "ra{ra}_dec{dec}_ri{ri}_ro{ro}_grp{gr}"
3318-
spec_storage_name = spec_storage_name.format(ra=self.default_coord[0].value,
3319-
dec=self.default_coord[1].value,
3320-
ri=inn_rad_num.value, ro=out_rad_num.value,
3321-
gr=group_spec)
3322-
else:
3323-
spec_storage_name = "region_grp{gr}".format(gr=group_spec)
3324-
3325-
# Adds on the extra information about grouping to the storage key
3326-
spec_storage_name += extra_name
3296+
# Checking spectrum generation inputs, and making sure they are in the right types
3297+
over_sample = int(over_sample) if over_sample is not None else None
3298+
min_counts = int(min_counts) if min_counts is not None else None
3299+
min_sn = float(min_sn) if min_sn is not None else None
33273300

33283301
if obs_id == 'combined':
3329-
matched_prods = self.get_products('combined_spectrum', obs_id=obs_id, inst=inst,
3330-
extra_key=spec_storage_name, telescope=telescope)
3302+
matched_prods = self.get_products('combined_spectrum', obs_id=obs_id, inst=inst, telescope=telescope)
33313303
else:
3332-
matched_prods = self.get_products('spectrum', obs_id=obs_id, inst=inst, extra_key=spec_storage_name,
3333-
telescope=telescope)
3304+
matched_prods = self.get_products('spectrum', obs_id=obs_id, inst=inst, telescope=telescope)
3305+
3306+
matched_prods : List[Spectrum]
3307+
3308+
# Checking for matching radii first - this will likely whittle down the spectra best of all. We have had
3309+
# matching problems sometimes because of float precision (the last digit flips and is no longer an exact
3310+
# match to the other radius)
3311+
matched_prods = [m_prod for m_prod in matched_prods
3312+
if np.isclose(inn_rad_num, m_prod.inner_rad, rtol=0, atol=RAD_MATCH_PRECISION)
3313+
and np.isclose(out_rad_num, m_prod.outer_rad, rtol=0, atol=RAD_MATCH_PRECISION)]
3314+
3315+
# Separating the matching steps can also give us the opportunity to say exactly where matching failed
3316+
# in the future. Now we check for matches to the spectrum generation settings - in a for loop this time
3317+
# because we have to distinguish between searching for grouped and ungrouped spectra
3318+
final_matched_prods = []
3319+
for m_prod in matched_prods:
3320+
# If the current spectrum doesn't match user specified grouping (or not) boolean, we move on
3321+
if not group_spec == m_prod.grouped:
3322+
continue
3323+
# Getting here means that the grouped status of the current spectrum matches the user
3324+
# specification, and then if they aren't grouped we don't need to do the other comparisons
3325+
elif not group_spec:
3326+
final_matched_prods.append(m_prod)
3327+
continue
33343328

3335-
if len(matched_prods) == 1:
3336-
matched_prods = matched_prods[0]
3337-
elif len(matched_prods) == 0:
3338-
raise NoProductAvailableError("Cannot find any spectra matching your input.")
3329+
# If we're here then we have to compare this spectrum's grouping settings to those passed by
3330+
# the user
3331+
if min_counts == m_prod.min_counts and min_sn == m_prod.min_sn and over_sample == m_prod.over_sample:
3332+
final_matched_prods.append(m_prod)
33393333

3340-
return matched_prods
3334+
if len(final_matched_prods) == 1:
3335+
final_matched_prods = final_matched_prods[0]
3336+
elif len(final_matched_prods) == 0:
3337+
raise NoProductAvailableError("Cannot find any spectra matching your input.")
33413338

3339+
return final_matched_prods
33423340

33433341
def get_spectra(self, outer_radius: Union[str, Quantity], obs_id: str = None, inst: str = None,
33443342
inner_radius: Union[str, Quantity] = Quantity(0, 'arcsec'), group_spec: bool = True,

0 commit comments

Comments
 (0)