10
10
11
11
import numpy as np
12
12
from astropy .units import Quantity
13
+ from regions import CircleSkyRegion , EllispeSkyRegion
13
14
14
15
from .misc import evtool_combine_evts
15
16
from .phot import evtool_image
22
23
from ...samples .base import BaseSample
23
24
from ...sources import BaseSource , ExtendedSource , GalaxyCluster
24
25
26
+ def parse_custom_bkg_esass (region : Union [CircleSkyRegion , EllispeSkyRegion ], within_radii : bool = False ):
27
+ """
28
+ Parse a region object from the `regions` module and return the arguments needed
29
+ to call `get_annular_sas_region`.
30
+
31
+ :param region: A region from the `regions` module
32
+ :param str func: Which XGA function will this region be parsed to
33
+ :return: Tuple containing (inner_radius, outer_radius, central_coord, rot_angle)
34
+ """
35
+ if isinstance (region , CircleSkyRegion ):
36
+ outer_radius = region .radius if not within_radii else region .radius * 1.5
37
+ central_coord = Quantity ([region .center .ra .deg , region .center .dec .deg ], 'deg' )
38
+ rot_angle = Quantity (0 , 'deg' ) # No rotation for circular regions
39
+ inner_radius = Quantity (0 , 'deg' )
40
+
41
+ elif isinstance (region , EllipseSkyRegion ):
42
+ outer_radius = Quantity ([region .height / 2 , region .width / 2 ]) if not within_radii \
43
+ else Quantity ((region .height / 2 )* 1.5 )
44
+ central_coord = Quantity ([region .center .ra .deg , region .center .dec .deg ], 'deg' )
45
+ rot_angle = Quantity (region .angle .to ('deg' ).value , 'deg' )
46
+ inner_radius = Quantity ([0 , 0 ], 'deg' )
47
+
48
+ else :
49
+ raise TypeError (f"Unsupported region type: { type (region )} " )
50
+
51
+ return inner_radius , outer_radius , central_coord , rot_angle
52
+
25
53
26
54
def _spec_cmds (sources : Union [BaseSource , BaseSample ], outer_radius : Union [str , Quantity ],
27
55
inner_radius : Union [str , Quantity ] = Quantity (0 , 'arcsec' ), group_spec : bool = True , min_counts : int = 5 ,
28
56
min_sn : float = None , num_cores : int = NUM_CORES , disable_progress : bool = False ,
29
- combine_tm : bool = True , combine_obs : bool = True , force_gen : bool = False ):
57
+ combine_tm : bool = True , combine_obs : bool = True , force_gen : bool = False ,
58
+ custom_bkg : Union [str , List [str ]] = None ):
30
59
"""
31
60
An internal function to generate all the commands necessary to produce a srctool spectrum, but is not
32
61
decorated by the esass_call function, so the commands aren't immediately run. This means it can be used for
@@ -63,6 +92,7 @@ def _spec_cmds(sources: Union[BaseSource, BaseSample], outer_radius: Union[str,
63
92
:param bool combine_obs: Setting this to False will generate an image for each associated observation,
64
93
instead of for one combined observation.
65
94
:param bool force_gen: This boolean flag will force the regeneration of spectra, even if they already exist.
95
+ :param str custom_bkg: A string to be input into the backreg argument of srctool.
66
96
"""
67
97
def _append_spec_info (evt_list ):
68
98
"""
@@ -164,11 +194,40 @@ def _append_spec_info(evt_list):
164
194
reg = get_annular_esass_region (source , inner_radii [s_ind ], outer_radii [s_ind ], obs_id ,
165
195
interloper_regions = interloper_regions ,
166
196
central_coord = source .default_coord , rand_ident = rand_ident )
167
- b_reg = get_annular_esass_region (source , outer_radii [s_ind ] * source .background_radius_factors [0 ],
168
- outer_radii [s_ind ] * source .background_radius_factors [1 ], obs_id ,
169
- interloper_regions = back_inter_reg ,
170
- central_coord = source .default_coord , bkg_reg = True ,
171
- rand_ident = rand_ident )
197
+
198
+ # This finds any regions which
199
+ if custom_bkg is None :
200
+ back_inter_reg = source .regions_within_radii (outer_radii [s_ind ] * source .background_radius_factors [0 ],
201
+ outer_radii [s_ind ] * source .background_radius_factors [1 ],
202
+ "erosita" , source .default_coord )
203
+ b_reg = get_annular_esass_region (source , outer_radii [s_ind ] * source .background_radius_factors [0 ],
204
+ outer_radii [s_ind ] * source .background_radius_factors [1 ], obs_id ,
205
+ interloper_regions = back_inter_reg ,
206
+ central_coord = source .default_coord , bkg_reg = True ,
207
+ rand_ident = rand_ident )
208
+ else :
209
+ if isinstance (custom_bkg [s_ind ], dict ):
210
+ try :
211
+ use_custom_bkg = custom_bkg [s_ind ][obs_id ]
212
+ except KeyError :
213
+ raise KeyError ("If using a dictionary for the custom_bkg argument, there"
214
+ " must be an entry for every obs_id assigned to the source,"
215
+ " or the obs_id must be 'combined' if setting the combine_obs"
216
+ " argument to True." )
217
+ else :
218
+ use_custom_bkg = custom_bkg [s_ind ]
219
+
220
+ _ , bkg_outr , bkg_coord , _ = parse_custom_bkg_esass (use_custom_bkg , True )
221
+ back_inter_reg = source .regions_within_radii (Quantity (0 , 'deg' ), bkg_outr ,
222
+ 'erosita' , bkg_coord )
223
+ bkg_innr , bkg_outr , bkg_coord , bkg_rot_angle = parse_custom_bkg_esass (use_custom_bkg )
224
+ breg = get_annular_esass_region (source , bkg_innr , bkg_outr , obs_id ,
225
+ interloper_regions = back_inter_reg ,
226
+ central_coord = bkg_coord , bkg_reg = True ,
227
+ rand_ident = rand_ident ,
228
+ rot_angle = bkg_rot_angle )
229
+
230
+
172
231
inn_rad_degrees = inner_radii [s_ind ]
173
232
out_rad_degrees = outer_radii [s_ind ]
174
233
@@ -444,6 +503,13 @@ def _append_spec_info(evt_list):
444
503
ex_src = False
445
504
et = 'POINT'
446
505
506
+ if isinstance (custom_bkg , dict ):
507
+ custom_bkg = [custom_bkg ]
508
+
509
+ if isinstance (custom_bkg , list ):
510
+ if len (custom_bkg ) != len (sources ):
511
+ raise ValueError ("If inputting a list of dictionaries into the 'custom_bkg' argument, the length "
512
+ "of the list must be the same as the length of the sample." )
447
513
# TODO implement the det map EXTTPYE, at the moment this spectrum will treat the target as a point source
448
514
# Defining the various eSASS commands that need to be populated
449
515
# There will be a different command for extended and point sources
@@ -528,14 +594,9 @@ def _append_spec_info(evt_list):
528
594
if outer_radius != 'region' :
529
595
# Finding interloper regions within the radii we have specified has been put here because it all works in
530
596
# degrees and as such only needs to be run once for all the different observations.
531
- # TODO ASSUMPTION8 telescope agnostic version of the regions_within_radii will have telescope argument
532
597
interloper_regions = source .regions_within_radii (inner_radii [s_ind ], outer_radii [s_ind ], "erosita" ,
533
598
source .default_coord )
534
- # This finds any regions which
535
- # TODO ASSUMPTION8 telescope agnostic version of the regions_within_radii will have telescope argument
536
- back_inter_reg = source .regions_within_radii (outer_radii [s_ind ] * source .background_radius_factors [0 ],
537
- outer_radii [s_ind ] * source .background_radius_factors [1 ],
538
- "erosita" , source .default_coord )
599
+
539
600
src_inn_rad_str = inner_radii [s_ind ].value
540
601
src_out_rad_str = outer_radii [s_ind ].value
541
602
# The key under which these spectra will be stored
@@ -555,7 +616,7 @@ def _append_spec_info(evt_list):
555
616
# This function then uses the evtlist to generate spec commands, final paths,
556
617
# and extra info, it will then append them to the cmds, final_paths, and extrainfo lists
557
618
# that are defined above
558
- _append_spec_info (evt_list )
619
+ _append_spec_info (evt_list , s_ind )
559
620
560
621
561
622
else :
@@ -565,7 +626,7 @@ def _append_spec_info(evt_list):
565
626
# This function then uses the evtlist to generate spec commands, final paths,
566
627
# and extra info, it will then append them to the cmds, final_paths, and extrainfo lists
567
628
# that are defined above
568
- _append_spec_info (evt_list )
629
+ _append_spec_info (evt_list , s_ind )
569
630
570
631
571
632
sources_cmds .append (np .array (cmds ))
0 commit comments