1+ import logging
2+ import numpy as np
3+ from scipy .stats import norm
4+ from os import environ
5+ from IPython .display import display
6+ import pandas as pd
7+ import astropy .units as u
8+ from astropy .coordinates import Angle , SkyCoord
9+ from regions import CircleSkyRegion
10+ import matplotlib .pyplot as plt
11+ from gammapy .analysis import Analysis , AnalysisConfig
12+ from gammapy .datasets import MapDatasetOnOff , MapDataset ,FluxPointsDataset
13+ from gammapy .estimators import ExcessMapEstimator ,FluxPointsEstimator , FluxPoints
14+ from gammapy .makers import RingBackgroundMaker , SafeMaskMaker , MapDatasetMaker ,SpectrumDatasetMaker
15+ from gammapy .data import DataStore
16+ from gammapy .maps import MapAxis , WcsGeom
17+ from tqdm .auto import tqdm
18+ from gammapy .modeling .models import PowerLawSpectralModel , SkyModel
19+ from astroquery .simbad import Simbad
20+ from gammapy .maps import MapCoord
21+ from gammapy .modeling import Fit
22+ from astropy .table import Table
23+ import sys
24+ import seaborn as sns
25+
26+ sns .set_theme (font = "Serif" ,font_scale = 2 ,style = 'ticks' ,context = 'paper' ,palette = 'pastel' )
27+
28+ datastore_dir = sys .argv [1 ]
29+ source_pos = sys .argv [2 ] # [ra,dec] in deg
30+ plaw = sys .argv [3 ] # power law parameters as [index, amp] in TeV-1 s-1 cm-2
31+ source = sys .argv [4 ]
32+
33+ logfile = f'{ source } .log'
34+
35+ datastore = DataStore .from_dir (datastore_dir )
36+ observations = datastore .get_observations ()
37+
38+ source_pos = SkyCoord (source_pos [0 ],source_pos [1 ],unit = 'deg' )
39+
40+ energy_axis = MapAxis .from_energy_edges ([0.2 ,0.316 ,0.501 ,0.794 ,1.259 ,1.995 ,3.162 ,5.012 ,7.943 ,12.589 ,19.953 ,31.623 ]* u .TeV , unit = "TeV" )
41+ energy_axis_true = MapAxis .from_energy_bounds (
42+ 0.05 , 100 , 100 , unit = "TeV" , name = "energy_true"
43+ )
44+ geom = WcsGeom .create (
45+ skydir = (source_pos .ra .value , source_pos .dec .value ),
46+ binsz = 0.02 ,
47+ width = (4 , 4 ),
48+ frame = "icrs" ,
49+ proj = "CAR" ,
50+ axes = [energy_axis ],
51+ )
52+ simbad = Simbad ()
53+ simbad .reset_votable_fields ()
54+ simbad .add_votable_fields ('ra' , 'dec' , "flux(B)" , "flux(V)" , "jp11" )
55+ simbad .remove_votable_fields ('coordinates' )
56+
57+ srcs_tab = simbad .query_region (source_pos , radius = 1.5 * u .deg )
58+ srcs_tab = srcs_tab [srcs_tab ["FLUX_B" ]< 6 ]
59+ srcs_tab = srcs_tab [srcs_tab ["FLUX_V" ]!= np .ma .masked ]
60+
61+ geom_image = geom .to_image ().to_cube ([energy_axis .squash ()])
62+
63+ regions = CircleSkyRegion (center = source_pos , radius = 0.5 * u .deg )
64+ all_ex = [regions ]
65+ stars = []
66+ for star in srcs_tab :
67+ pos = SkyCoord (star ["RA" ], star ["DEC" ], frame = "fk5" , unit = (u .hourangle , u .deg ))
68+ star = CircleSkyRegion (center = pos , radius = 0.3 * u .deg )
69+ stars .append (star )
70+ all_ex .append (star )
71+
72+ exclusion_mask = ~ geom_image .region_mask (all_ex )
73+ stacked = MapDataset .create (
74+ geom = geom , energy_axis_true = energy_axis_true , name = "stacked"
75+ )
76+ offset_max = 1.75 * u .deg
77+ maker = MapDatasetMaker ()# add make exposure map?
78+ maker .available_selection = []
79+ maker_safe_mask = SafeMaskMaker (
80+ methods = ["aeff-max" ,"offset-max" ], aeff_percent = 10 , offset_max = 1.75 * u .deg
81+ )
82+
83+ for obs in observations :
84+ dataset = maker .run (stacked ,obs )
85+ dataset = maker_safe_mask .run (dataset , obs )
86+ stacked .stack (dataset )
87+
88+ ring_maker = RingBackgroundMaker (
89+ r_in = "0.63 deg" , width = "0.14 deg" , exclusion_mask = exclusion_mask
90+ )
91+ dataset_on_off = ring_maker .run (stacked )
92+
93+ pl = PowerLawSpectralModel (index = plaw [0 ],amplitude = plaw [1 ]* u .Unit ("TeV-1 s-1 cm-2" ))
94+ sky_model = SkyModel (spectral_model = pl ,name = 'Crab' )
95+
96+ on_region = CircleSkyRegion (center = source_pos , radius = np .sqrt (0.008 ) * u .deg )
97+ spectral_dataset = dataset_on_off .to_spectrum_dataset (on_region ,containment_correction = True )
98+ spectral_dataset .models = [sky_model ]
99+ fit = Fit ()
100+ result = fit .run (datasets = spectral_dataset )
101+
102+ index = result .models ['Crab' ].index
103+ index_err = result .models ['Crab' ].index .error
104+ amplitude = result .models ['Crab' ].amplitude
105+ amplitude_error = result .models ['Crab' ].amplitude
106+
107+ with open (logfile ) as log :
108+ log .write (f'index: { index } +/- { index_err } ' )
109+ log .write (f'amplitude: { amplitude } +/- { amplitude_error } ' )
110+
111+ fpe = FluxPointsEstimator (
112+ energy_edges = [0.2 ,0.316 ,0.501 ,0.794 ,1.259 ,1.995 ,3.162 ,5.012 ,7.943 ,12.589 ]* u .TeV , source = "Crab" , selection_optional = "all" ,reoptimize = False
113+ )
114+ flux_points = fpe .run (datasets = spectral_dataset )
115+
116+ flux_points .plot (sed_type = 'dnde' ,energy_power = 2 ,color = 'plum' )
117+ result .models [0 ].spectral_model .plot ([0.1 ,10 ]* u .TeV ,sed_type = 'dnde' ,color = 'b' ,energy_power = 2 )
118+ result .models [0 ].spectral_model .plot_error ([0.1 ,10 ]* u .TeV ,sed_type = 'dnde' ,alpha = 0.1 ,energy_power = 2 )
119+ plt .savefig (f'{ source } _sed.pdf' ,format = 'pdf' )
120+
121+ flux_points .write (f'{ source } _fluxpoints.ecsv' )
122+
123+ # make new dataset for significance calculations
124+ stacked = MapDataset .create (
125+ geom = geom , energy_axis_true = energy_axis_true , name = "stacked"
126+ )
127+
128+ maker_safe_mask = SafeMaskMaker (
129+ methods = ["offset-max" ], offset_max = 1.75 * u .deg
130+ )
131+
132+ for obs in observations :
133+ dataset = maker .run (stacked ,obs )
134+ dataset = maker_safe_mask .run (dataset , obs )
135+ stacked .stack (dataset )
136+
137+ ring_maker = RingBackgroundMaker (
138+ r_in = "0.63 deg" , width = "0.14 deg" , exclusion_mask = exclusion_mask
139+ )
140+ dataset_on_off = ring_maker .run (stacked )
141+
142+ estimator = ExcessMapEstimator (np .sqrt (0.008 ) * u .deg , selection_optional = ["alpha" ],correlate_off = False ,
143+ #gamma_min_sensitivity=10,bkg_syst_fraction_sensitivity=0.05,
144+ #apply_threshold_sensitivity=False,
145+ #spectral_model = pl,
146+ #sum_over_energy_groups=True,
147+ )
148+ lima_maps = estimator .run (dataset_on_off )
149+
150+ significance_map = lima_maps ["sqrt_ts" ]
151+ excess_map = lima_maps ["npred_excess" ]
152+
153+ my_cmap = sns .color_palette ("magma" , as_cmap = True )
154+
155+ # We can plot the excess and significance maps
156+ fig , (ax1 , ax2 ) = plt .subplots (
157+ figsize = (13 , 5 ), subplot_kw = {"projection" : lima_maps .geom .wcs }, ncols = 2
158+ )
159+ ax1 .set_title ("Significance map" )
160+ significance_map .plot (ax = ax1 , add_cbar = True ,cmap = my_cmap ,vmin = - 5 ,vmax = 5 )
161+ ax2 .set_title ("Excess map" )
162+ excess_map .plot (ax = ax2 , add_cbar = True ,cmap = my_cmap )
163+ plt .savefig (f'{ source } _sigmap.pdf' , format = 'pdf' )
164+
165+ significance_map_off = significance_map * exclusion_mask
166+
167+ # significance distribution
168+ significance_all = significance_map .data [np .isfinite (significance_map .data )].flatten ()
169+ significance_off = significance_map_off .data [exclusion_mask & np .isfinite (significance_map_off .data )].flatten ()
170+
171+ fig , ax = plt .subplots ()
172+ ax .hist (
173+ significance_all ,
174+ density = True ,
175+ alpha = 0.5 ,
176+ color = "red" ,
177+ label = "all bins" ,
178+ bins = np .linspace (- 5 ,10 ,50 ),
179+ )
180+
181+ ax .hist (
182+ significance_off ,
183+ density = True ,
184+ alpha = 0.5 ,
185+ color = "blue" ,
186+ label = "off bins" ,
187+ bins = np .linspace (- 5 ,10 ,50 ),
188+ )
189+
190+
191+ mu , std = norm .fit (significance_off )
192+ x = np .linspace (- 5 , 10 , 100 )
193+ p = norm .pdf (x , mu , std )
194+ ax .plot (x , p , lw = 2 , color = "black" )
195+ ax .plot (x ,norm .pdf (x ,0 ,1 ),color = 'k' ,ls = '--' )
196+
197+ ax .legend ()
198+ ax .set_xlabel ("Significance" )
199+ ax .set_yscale ("log" )
200+ ax .set_ylim (1e-5 , 1 )
201+ ax .set_xlim (- 5 , 10 )
202+
203+ print (f"Fit results: mu = { mu :.2f} , std = { std :.2f} " )
204+ ax .text (- 4.5 , 0.5 , f"Fit results: mu = { mu :.2f} , std = { std :.2f} " )
205+ plt .savefig (f'{ source } _sigdist.pdf' ,format = 'pdf' )
206+
207+ counts = lima_maps ['counts' ].get_by_coord ((source_pos ,1 * u .TeV ))[0 ]
208+ sig = lima_maps ['sqrt_ts' ].get_by_coord ((source_pos ,1 * u .TeV ))[0 ]
209+ bkg = lima_maps ['npred_background' ].get_by_coord ((source_pos ,1 * u .TeV ))[0 ]
210+ alpha = lima_maps ['alpha' ].get_by_coord ((source_pos ,1 * u .TeV ))[0 ]
211+
212+ with open (logfile ) as log :
213+ log .write (f'ON: { counts } ' )
214+ log .write (f'OFF: { bkg } ' )
215+ log .write (f'Significance: { sig } ' )
216+ log .write (f'Alpha: { alpha } ' )
0 commit comments