Dark Matter Analysis of the Draco dSph Galaxy
This tutorial demonstrates how to perform an analysis of the Draco dSph galaxy. This tutorial assumes that you have first gone through the PG 1553 analysis tutorial. In this example we will use the following data selection which is chosen to match the selection used in the 6-year LAT Dwarf Analysis.
10x10 degree ROI
Start Time (MET) = 239557417 seconds
Stop Time (MET) = 428903014 seconds
Minimum Energy = 500 MeV
Maximum Energy = 500000 MeV
zmax = 100 deg
P8R2_SOURCE_V6 (evclass=128)
The P8 dSph paper used a multi-component analysis that used the four PSF event types in a joint likelihood. In this example we will perform a single component analysis using all SOURCE-class events (evtype=3).
Get the Data and Setup the Analysis
[1]:
import os
import numpy as np
from fermipy.gtanalysis import GTAnalysis
from fermipy.plotting import ROIPlotter, SEDPlotter
import matplotlib.pyplot as plt
import matplotlib
In this thread we will use a pregenerated data set which is contained in a tar archive in the data directory of the fermipy-extra repository.
[2]:
if os.path.isfile('../data/draco.tar.gz'):
!tar xzf ../data/draco.tar.gz
else:
!curl -OL --output-dir ./../data/ https://raw.githubusercontent.com/fermiPy/fermipy-extras/master/data/draco.tar.gz
!tar xzf ./../data/draco.tar.gz
We will begin by looking at the contents of the configuration file. The configuration is similar to our PG1553 example except for the addition of a ‘draco’ component in the ROI model. We also set the ROI coordinates explicitly since the ROI center isn’t at the position of a 3FGL source.
[3]:
!cat draco/config.yaml
logging :
verbosity : 3
data:
evfile: draco_ft1.fits
scfile: null
ltcube : ltcube_239557414_428903014_z100_r180_gti.fits
binning:
# Binning
roiwidth : 10.0
npix : null
binsz : 0.1 # spatial bin size in deg
binsperdec : 8 # nb energy bins per decade
coordsys : 'GAL'
selection:
# Data selections
emin : 500
emax : 500000
zmax : 100
evclass : 128
evtype : 3
tmin : 239557414
tmax : 428903014 # 6 years
filter : null
# Set the ROI center to these coordinates
ra: 260.05167
dec: 57.91528
gtlike:
# IRFs
edisp : True
irfs : 'P8R2_SOURCE_V6'
edisp_disable : ['isodiff','galdiff']
# Settings for ROI model
model:
# Include catalog sources within this distance from the ROI center
src_radius : null
# Include catalog sources within a box of width roisrc.
src_roiwidth : 15.0
galdiff : 'gll_iem_v07.fits'
isodiff : 'iso_P8R3_SOURCE_V3_v1.txt'
# List of catalogs to be used in the model.
catalogs :
- '3FGL'
sources :
- { name : 'draco', ra : 260.05167, dec : 57.91528,
SpectrumType : 'PowerLaw', Index : 2.0, Prefactor : { 'value' : 0.0, 'scale' : 1e-13 } }
components: null
fileio:
outdir : draco
Note that the setup for a joint analysis is identical to the above except for the modification to the components section. The following example shows the components configuration one would use to define a joint analysis with the four PSF event types:
components:
- { selection : { evtype : 4 } }
- { selection : { evtype : 8 } }
- { selection : { evtype : 16 } }
- { selection : { evtype : 32 } }
To get started we will first instantiate a GTAnalysis instance using the config file in the draco directory and the run the setup() method. This will prepare all the ancillary files and create the pylikelihood instance for binned analysis. Note that in this example these files have already been generated so the routines that would normally be executed to create these files will be skipped.
[4]:
gta = GTAnalysis('draco/config.yaml')
gta.setup()
gta.write_roi('fit0')
2024-12-19 20:27:39 INFO GTAnalysis.__init__():
--------------------------------------------------------------------------------
fermipy version 1.3.1+6.g74aa
ScienceTools version 2.2.0
2024-12-19 20:27:39 INFO GTAnalysis.setup(): Running setup.
2024-12-19 20:27:39 INFO GTBinnedAnalysis.setup(): Running setup for component 00
2024-12-19 20:27:39 INFO GTBinnedAnalysis._select_data(): Skipping data selection.
2024-12-19 20:27:39 INFO GTBinnedAnalysis.setup(): Using external LT cube.
{'Prefactor': 0, 'Index1': 1, 'Scale': 2, 'Cutoff': 3, 'Index2': 4}
2024-12-19 20:27:40 INFO GTBinnedAnalysis._create_expcube(): Skipping gtexpcube.
WARNING: FITSFixedWarning: 'datfix' made the change 'Set DATEREF to '2001-01-01T00:01:04.184' from MJDREF.
Set MJD-OBS to 54682.655283 from DATE-OBS.
Set MJD-END to 56874.155007 from DATE-END'. [astropy.wcs.wcs]
2024-12-19 20:27:40 INFO GTBinnedAnalysis._create_srcmaps(): Skipping gtsrcmaps.
2024-12-19 20:27:40 INFO GTBinnedAnalysis.setup(): Finished setup for component 00
2024-12-19 20:27:40 INFO GTBinnedAnalysis._create_binned_analysis(): Creating BinnedAnalysis for component 00.
2024-12-19 20:27:45 INFO GTAnalysis.setup(): Initializing source properties
2024-12-19 20:27:48 INFO GTAnalysis.setup(): Finished setup.
2024-12-19 20:27:48 INFO GTBinnedAnalysis.write_xml(): Writing /home/runner/work/fermipy/fermipy/fermipy-extra/notebooks/draco/fit0_00.xml...
2024-12-19 20:27:48 INFO GTAnalysis.write_fits(): Writing /home/runner/work/fermipy/fermipy/fermipy-extra/notebooks/draco/fit0.fits...
WARNING: Format %s cannot be mapped to the accepted TDISPn keyword values. Format will not be moved into TDISPn keyword. [astropy.io.fits.column]
WARNING: Format %f cannot be mapped to the accepted TDISPn keyword values. Format will not be moved into TDISPn keyword. [astropy.io.fits.column]
WARNING: Format %s cannot be mapped to the accepted TDISPn keyword values. Format will not be moved into TDISPn keyword. [astropy.io.fits.column]
2024-12-19 20:27:56 INFO GTAnalysis.write_roi(): Writing /home/runner/work/fermipy/fermipy/fermipy-extra/notebooks/draco/fit0.npy...
Print the ROI model
We can print the ROI object to see a list of sources in the model along with their distance from the ROI center (offset), TS, and number of predicted counts (Npred). Since we haven’t yet fit any sources, the ts of all sources will initially be assigned as nan.
[5]:
gta.print_roi()
2024-12-19 20:27:56 INFO GTAnalysis.print_roi():
name SpatialModel SpectrumType offset ts npred
--------------------------------------------------------------------------------
draco PointSource PowerLaw 0.000 nan 0.0
3FGL J1725.3+5853 PointSource PowerLaw 1.181 nan 278.2
3FGL J1732.7+5914 PointSource PowerLaw 2.107 nan 123.1
3FGL J1707.8+5626 PointSource PowerLaw 2.231 nan 154.4
3FGL J1729.0+6049 PointSource PowerLaw 3.121 nan 187.7
3FGL J1722.7+6104 PointSource PowerLaw 3.184 nan 203.8
3FGL J1742.2+5947 PointSource PowerLaw 3.416 nan 296.9
3FGL J1656.9+6008 PointSource PowerLaw 3.725 nan 280.4
3FGL J1731.9+5428 PointSource PowerLaw 3.801 nan 206.3
3FGL J1658.3+6149 PointSource PowerLaw 4.772 nan 141.3
3FGL J1740.4+5347 PointSource PowerLaw 5.008 nan 152.2
3FGL J1756.3+5523 PointSource PowerLaw 5.562 nan 133.9
3FGL J1637.9+5719 PointSource PowerLaw 5.679 nan 33.9
3FGL J1740.3+5211 PointSource PowerLaw 6.405 nan 110.1
3FGL J1649.4+5238 PointSource PowerLaw 6.851 nan 184.6
3FGL J1645.9+6336 PointSource PowerLaw 7.051 nan 16.3
3FGL J1815.1+5919 PointSource PowerLaw 7.280 nan 5.3
3FGL J1625.0+5651 PointSource PowerLaw 7.497 nan 2.2
3FGL J1806.8+5346 PointSource PowerLaw 7.733 nan 5.8
3FGL J1810.7+5335 PointSource PowerLaw 8.303 nan 1.1
3FGL J1807.8+6427 PointSource PowerLaw 8.671 nan 1.6
3FGL J1647.4+4950 PointSource PowerLaw 9.393 nan 5.9
isodiff ConstantValue FileFunction ----- nan 11027.5
galdiff MapCubeFunctio PowerLaw ----- nan 19753.2
We can assess the quality of our pre-fit model by running the residmap method. This will generate four maps
[6]:
resid = gta.residmap('draco_prefit',model={'SpatialModel' : 'PointSource', 'Index' : 2.0})
2024-12-19 20:27:56 INFO GTAnalysis.residmap(): Generating residual maps
2024-12-19 20:27:56 INFO GTAnalysis.add_source(): Adding source residmap_testsource
2024-12-19 20:27:56 INFO GTAnalysis.delete_source(): Deleting source residmap_testsource
2024-12-19 20:27:57 INFO GTAnalysis.residmap(): Finished residual maps
2024-12-19 20:28:01 WARNING GTAnalysis.residmap(): Saving maps in .npy files is disabled b/c of incompatibilities in python3, remove the maps from the /home/runner/work/fermipy/fermipy/fermipy-extra/notebooks/draco/draco_prefit_pointsource_powerlaw_2.00_residmap.npy
2024-12-19 20:28:01 INFO GTAnalysis.residmap(): Execution time: 5.55 s
[7]:
fig = plt.figure(figsize=(14,6))
ROIPlotter(resid['data'],roi=gta.roi).plot(vmin=400,vmax=1000,subplot=121,cmap='magma')
plt.gca().set_title('Data')
ROIPlotter(resid['model'],roi=gta.roi).plot(vmin=400,vmax=1000,subplot=122,cmap='magma')
plt.gca().set_title('Model')
plt.show()
[8]:
plt.clf()
fig = plt.figure(figsize=(14,6))
ROIPlotter(resid['sigma'],roi=gta.roi).plot(vmin=-5,vmax=5,levels=[-5,-3,3,5],subplot=121,cmap='RdBu_r')
plt.gca().set_title('Significance')
ROIPlotter(resid['excess'],roi=gta.roi).plot(vmin=-100,vmax=100,subplot=122,cmap='RdBu_r')
plt.gca().set_title('Excess')
plt.show()
<Figure size 640x480 with 0 Axes>
Now we will run the optimize method. This method will iteratively optimize the parameters of all components in the ROI in several stages: * Simultaneously fitting the normalization of the brightest model components containing at least some fraction of the total model counts (default 95%). * Individually fitting the normalization of all remaining sources if they have Npred above some threshold (default 1). * Individually fitting the normalization and shape of any component with TS larger than some threshold (default 25).
Running optimize gives us a baseline model that we can use as a starting point for subsequent stages of the analysis. We will also save the results of the analysis with write_roi. By saving the analysis state we can restore the analysis to this point at any time with the load_roi method. (NOTE: This step is computationally intensive and can take up to 5-10 minutes)
[9]:
gta.optimize()
gta.write_roi('fit1')
2024-12-19 20:28:03 INFO GTAnalysis.optimize(): Starting
Joint fit ['galdiff', 'isodiff', '3FGL J1742.2+5947', '3FGL J1656.9+6008', '3FGL J1725.3+5853']
/home/runner/miniconda3/envs/fermipy/lib/python3.9/site-packages/scipy/interpolate/_fitpack2.py:313: UserWarning:
The maximal number of iterations maxit (set to 20 by the program)
allowed for finding a smoothing spline with fp=s has been reached: s
too small.
There is an approximation returned but the corresponding weighted sum
of squared residuals does not satisfy the condition abs(fp-s)/s < tol.
warnings.warn(message)
Fitting shape isodiff TS: 681.461
Fitting shape 3FGL J1649.4+5238 TS: 430.540
Fitting shape galdiff TS: 375.586
Fitting shape 3FGL J1725.3+5853 TS: 275.194
Fitting shape 3FGL J1742.2+5947 TS: 183.520
Fitting shape 3FGL J1756.3+5523 TS: 111.435
Fitting shape 3FGL J1740.4+5347 TS: 83.850
Fitting shape 3FGL J1656.9+6008 TS: 58.697
Fitting shape 3FGL J1658.3+6149 TS: 46.272
2024-12-19 20:28:06 INFO GTAnalysis.optimize(): Finished
2024-12-19 20:28:06 INFO GTAnalysis.optimize(): LogLike: -60852.744380 Delta-LogLike: 93.667374
2024-12-19 20:28:06 INFO GTAnalysis.optimize(): Execution time: 3.12 s
2024-12-19 20:28:06 INFO GTBinnedAnalysis.write_xml(): Writing /home/runner/work/fermipy/fermipy/fermipy-extra/notebooks/draco/fit1_00.xml...
2024-12-19 20:28:06 INFO GTAnalysis.write_fits(): Writing /home/runner/work/fermipy/fermipy/fermipy-extra/notebooks/draco/fit1.fits...
Fitting shape 3FGL J1731.9+5428 TS: 30.116
WARNING: Format %s cannot be mapped to the accepted TDISPn keyword values. Format will not be moved into TDISPn keyword. [astropy.io.fits.column]
WARNING: Format %f cannot be mapped to the accepted TDISPn keyword values. Format will not be moved into TDISPn keyword. [astropy.io.fits.column]
WARNING: Format %s cannot be mapped to the accepted TDISPn keyword values. Format will not be moved into TDISPn keyword. [astropy.io.fits.column]
2024-12-19 20:28:13 INFO GTAnalysis.write_roi(): Writing /home/runner/work/fermipy/fermipy/fermipy-extra/notebooks/draco/fit1.npy...
After running optimize we can rerun print_roi to see a summary of the updated model. All sources that were fit in this step now have ts values and an Npred value the reflects the optimized normalization of that source. Note that model components that were not fit during the optimize step still have ts=nan.
[10]:
gta.print_roi()
2024-12-19 20:28:13 INFO GTAnalysis.print_roi():
name SpatialModel SpectrumType offset ts npred
--------------------------------------------------------------------------------
draco PointSource PowerLaw 0.000 nan 0.0
3FGL J1725.3+5853 PointSource PowerLaw 1.181 285.61 370.4
3FGL J1732.7+5914 PointSource PowerLaw 2.107 9.57 52.4
3FGL J1707.8+5626 PointSource PowerLaw 2.231 -0.00 0.0
3FGL J1729.0+6049 PointSource PowerLaw 3.121 8.13 74.0
3FGL J1722.7+6104 PointSource PowerLaw 3.184 22.56 128.4
3FGL J1742.2+5947 PointSource PowerLaw 3.416 186.37 293.2
3FGL J1656.9+6008 PointSource PowerLaw 3.725 59.91 181.1
3FGL J1731.9+5428 PointSource PowerLaw 3.801 32.67 178.1
3FGL J1658.3+6149 PointSource PowerLaw 4.772 44.91 127.0
3FGL J1740.4+5347 PointSource PowerLaw 5.008 76.63 148.3
3FGL J1756.3+5523 PointSource PowerLaw 5.562 102.94 118.1
3FGL J1637.9+5719 PointSource PowerLaw 5.679 -0.00 0.0
3FGL J1740.3+5211 PointSource PowerLaw 6.405 14.87 151.3
3FGL J1649.4+5238 PointSource PowerLaw 6.851 430.23 283.9
3FGL J1645.9+6336 PointSource PowerLaw 7.051 1.92 54.2
3FGL J1815.1+5919 PointSource PowerLaw 7.280 3.42 101.8
3FGL J1625.0+5651 PointSource PowerLaw 7.497 -0.00 0.0
3FGL J1806.8+5346 PointSource PowerLaw 7.733 2.02 60.7
3FGL J1810.7+5335 PointSource PowerLaw 8.303 -0.00 0.0
3FGL J1807.8+6427 PointSource PowerLaw 8.671 0.02 5.9
3FGL J1647.4+4950 PointSource PowerLaw 9.393 -0.00 0.0
isodiff ConstantValue FileFunction ----- 14276.56 16996.0
galdiff MapCubeFunctio PowerLaw ----- 9227.47 14402.7
To evaluate the quality of the optimized model we can rerun the residmap method. In the updated residual map that we see that there is no longer a negative residual in the vicinity of J1707.
[11]:
resid_postfit = gta.residmap('draco_postfit',model={'SpatialModel' : 'PointSource', 'Index' : 2.0})
2024-12-19 20:28:13 INFO GTAnalysis.residmap(): Generating residual maps
2024-12-19 20:28:13 INFO GTAnalysis.add_source(): Adding source residmap_testsource
2024-12-19 20:28:14 INFO GTAnalysis.delete_source(): Deleting source residmap_testsource
2024-12-19 20:28:14 INFO GTAnalysis.residmap(): Finished residual maps
2024-12-19 20:28:19 WARNING GTAnalysis.residmap(): Saving maps in .npy files is disabled b/c of incompatibilities in python3, remove the maps from the /home/runner/work/fermipy/fermipy/fermipy-extra/notebooks/draco/draco_postfit_pointsource_powerlaw_2.00_residmap.npy
2024-12-19 20:28:19 INFO GTAnalysis.residmap(): Execution time: 5.56 s
[12]:
plt.clf()
fig = plt.figure(figsize=(14,6))
ROIPlotter(resid_postfit['sigma'],roi=gta.roi).plot(vmin=-5,vmax=5,levels=[-5,-3,3,5],subplot=121,cmap='RdBu_r')
plt.gca().set_title('Significance')
ROIPlotter(resid_postfit['excess'],roi=gta.roi).plot(vmin=-100,vmax=100,subplot=122,cmap='RdBu_r')
plt.gca().set_title('Excess')
plt.show()
<Figure size 640x480 with 0 Axes>
Another diagnostic for the quality of the ROI model is the TS map. The tsmap method can be used to generate a TS map of the ROI with a given test source model. Here we use the same source model we did for the residual map – a point source with a power-law index of 2.
[13]:
tsmap_postfit = gta.tsmap('draco_postfit',model={'SpatialModel' : 'PointSource', 'Index' : 2.0})
2024-12-19 20:28:19 INFO GTAnalysis.tsmap(): Generating TS map
2024-12-19 20:28:20 INFO GTAnalysis._make_tsmap_fast(): Fitting test source.
2024-12-19 20:28:26 INFO GTAnalysis.tsmap(): Finished TS map
2024-12-19 20:28:32 WARNING GTAnalysis.tsmap(): Saving TS maps in .npy files is disabled b/c of incompatibilities in python3, remove the maps from the /home/runner/work/fermipy/fermipy/fermipy-extra/notebooks/draco/draco_postfit_pointsource_powerlaw_2.00_tsmap.npy
2024-12-19 20:28:32 INFO GTAnalysis.tsmap(): Execution time: 12.91 s
Here we see that the excess in the northeast part of the ROI appears more prominent than in the residual map. This excess is detected as a new point source with TS > 25.
[14]:
plt.clf()
fig = plt.figure(figsize=(14,6))
ROIPlotter(tsmap_postfit['sqrt_ts'],roi=gta.roi).plot(levels=[0,3,5,7],vmin=0,vmax=5,subplot=121,cmap='magma')
plt.gca().set_title('Sqrt(TS)')
ROIPlotter(tsmap_postfit['npred'],roi=gta.roi).plot(vmin=0,vmax=100,subplot=122,cmap='magma')
plt.gca().set_title('NPred')
plt.show()
<Figure size 640x480 with 0 Axes>
We can add this source into the model by running the find_sources method. This method will generate a TS map of the region and add a point-source at the location of each peak with TS > 25.
[15]:
src = gta.find_sources()
2024-12-19 20:28:33 INFO GTAnalysis.find_sources(): Starting.
2024-12-19 20:28:33 INFO GTAnalysis.tsmap(): Generating TS map
2024-12-19 20:28:33 INFO GTAnalysis._make_tsmap_fast(): Fitting test source.
2024-12-19 20:28:40 INFO GTAnalysis.tsmap(): Finished TS map
2024-12-19 20:28:46 WARNING GTAnalysis.tsmap(): Saving TS maps in .npy files is disabled b/c of incompatibilities in python3, remove the maps from the /home/runner/work/fermipy/fermipy/fermipy-extra/notebooks/draco/sourcefind_00_pointsource_powerlaw_2.00_tsmap.npy
2024-12-19 20:28:46 INFO GTAnalysis.tsmap(): Execution time: 12.82 s
2024-12-19 20:28:46 INFO GTAnalysis._build_src_dicts_from_peaks(): Found source
name: PS J1705.4+5435
ts: 46.116322
2024-12-19 20:28:46 INFO GTAnalysis.add_source(): Adding source PS J1705.4+5435
2024-12-19 20:28:47 INFO GTAnalysis.free_source(): Fixing parameters for PS J1705.4+5435 : ['Prefactor']
2024-12-19 20:28:47 INFO GTAnalysis._find_sources_iterate(): Performing spectral fit for PS J1705.4+5435.
2024-12-19 20:28:47 INFO GTAnalysis.free_source(): Freeing parameters for PS J1705.4+5435 : ['Prefactor', 'Index']
2024-12-19 20:28:47 INFO GTAnalysis.fit(): Starting fit.
2024-12-19 20:28:47 INFO GTAnalysis.fit(): Fit returned successfully. Quality: 3 Status: 0
2024-12-19 20:28:47 INFO GTAnalysis.fit(): LogLike: -60829.036 DeltaLogLike: 0.375
2024-12-19 20:28:47 INFO GTAnalysis._find_sources_iterate(): {'Index': {'error': 0.1633566874821993, 'value': -1.8535601832037727},
'Prefactor': {'error': 3.922743698097836e-14, 'value': 1.1565639986364977e-13},
'Scale': {'error': nan, 'value': 1000.0}}
2024-12-19 20:28:47 INFO GTAnalysis.free_source(): Fixing parameters for PS J1705.4+5435 : ['Prefactor', 'Index']
2024-12-19 20:28:47 INFO GTAnalysis.find_sources(): Found 1 sources in iteration 0.
2024-12-19 20:28:47 INFO GTAnalysis.tsmap(): Generating TS map
2024-12-19 20:28:47 INFO GTAnalysis._make_tsmap_fast(): Fitting test source.
2024-12-19 20:28:54 INFO GTAnalysis.tsmap(): Finished TS map
2024-12-19 20:29:00 WARNING GTAnalysis.tsmap(): Saving TS maps in .npy files is disabled b/c of incompatibilities in python3, remove the maps from the /home/runner/work/fermipy/fermipy/fermipy-extra/notebooks/draco/sourcefind_01_pointsource_powerlaw_2.00_tsmap.npy
2024-12-19 20:29:00 INFO GTAnalysis.tsmap(): Execution time: 12.77 s
2024-12-19 20:29:00 INFO GTAnalysis.find_sources(): Found 0 sources in iteration 1.
2024-12-19 20:29:00 INFO GTAnalysis.find_sources(): Done.
2024-12-19 20:29:00 INFO GTAnalysis.find_sources(): Execution time: 26.60 s
From the log we can see that a new source PS J1705.4+5434 was found with TS~50. Rerunning the tsmap method we can see that this source is located at the peak of the TS Map that was previously present in the northeast corner of the ROI.
[16]:
print(gta.roi['PS J1705.4+5435'])
tsmap_newsrcs = gta.tsmap('draco_newsrcs',model={'SpatialModel' : 'PointSource', 'Index' : 2.0})
2024-12-19 20:29:00 INFO GTAnalysis.tsmap(): Generating TS map
Name : PS J1705.4+5435
Associations : ['PS J1705.4+5435']
RA/DEC : 256.359/ 54.584
GLON/GLAT : 82.440/ 36.995
TS : 47.42
Npred : 68.28
Flux : 2.442e-10 +/- 7.07e-11
EnergyFlux : 1.249e-06 +/- 4.75e-07
SpatialModel : PointSource
SpectrumType : PowerLaw
Spectral Parameters
b'Prefactor' : 1.157e-13 +/- 3.923e-14
b'Index' : -1.854 +/- 0.1634
b'Scale' : 1000 +/- nan
2024-12-19 20:29:00 INFO GTAnalysis._make_tsmap_fast(): Fitting test source.
2024-12-19 20:29:07 INFO GTAnalysis.tsmap(): Finished TS map
2024-12-19 20:29:12 WARNING GTAnalysis.tsmap(): Saving TS maps in .npy files is disabled b/c of incompatibilities in python3, remove the maps from the /home/runner/work/fermipy/fermipy/fermipy-extra/notebooks/draco/draco_newsrcs_pointsource_powerlaw_2.00_tsmap.npy
2024-12-19 20:29:12 INFO GTAnalysis.tsmap(): Execution time: 12.73 s
[17]:
plt.clf()
fig = plt.figure(figsize=(14,6))
ROIPlotter(tsmap_newsrcs['sqrt_ts'],roi=gta.roi).plot(levels=[0,3,5,7],vmin=0,vmax=5,subplot=121,cmap='magma')
plt.gca().set_title('Sqrt(TS)')
ROIPlotter(tsmap_newsrcs['npred'],roi=gta.roi).plot(vmin=0,vmax=100,subplot=122,cmap='magma')
plt.gca().set_title('NPred')
plt.show()
<Figure size 640x480 with 0 Axes>
Spectral Analysis
After optimizing the ROI model we are ready to perform our analysis of the source of interest (draco). We will begin by freeing draco along with all other point sources within 3 deg of the ROI center and refitting their normalizations.
[18]:
gta.free_sources(distance=3.0,pars='norm')
gta.free_sources(distance=3.0,pars='shape',minmax_ts=[100.,None])
fit_results = gta.fit()
2024-12-19 20:29:13 INFO GTAnalysis.free_source(): Freeing parameters for 3FGL J1725.3+5853 : ['Prefactor']
2024-12-19 20:29:13 INFO GTAnalysis.free_source(): Freeing parameters for 3FGL J1732.7+5914 : ['Prefactor']
2024-12-19 20:29:13 INFO GTAnalysis.free_source(): Freeing parameters for 3FGL J1707.8+5626 : ['Prefactor']
2024-12-19 20:29:13 INFO GTAnalysis.free_source(): Freeing parameters for isodiff : ['Normalization']
2024-12-19 20:29:13 INFO GTAnalysis.free_source(): Freeing parameters for galdiff : ['Prefactor']
2024-12-19 20:29:13 INFO GTAnalysis.free_source(): Freeing parameters for 3FGL J1725.3+5853 : ['Index']
2024-12-19 20:29:13 INFO GTAnalysis.free_source(): Freeing parameters for galdiff : ['Index']
2024-12-19 20:29:13 INFO GTAnalysis.fit(): Starting fit.
2024-12-19 20:29:14 INFO GTAnalysis.fit(): Fit returned successfully. Quality: 3 Status: 0
2024-12-19 20:29:14 INFO GTAnalysis.fit(): LogLike: -60828.889 DeltaLogLike: 0.147
After running the fit completes we can execute the spectral analysis of draco with the sed method. For comparison we will also perform the spectral analysis of a nearby source (3FGL J1725.3+5853).
[19]:
sed_draco = gta.sed('draco')
sed_j1725 = gta.sed('3FGL J1725.3+5853')
gta.write_roi('fit_sed')
2024-12-19 20:29:14 INFO GTAnalysis.sed(): Computing SED for draco
2024-12-19 20:29:14 INFO GTAnalysis._make_sed(): Fitting SED
2024-12-19 20:29:14 INFO GTAnalysis.free_source(): Fixing parameters for 3FGL J1725.3+5853 : ['Index']
2024-12-19 20:29:14 INFO GTAnalysis.free_source(): Fixing parameters for galdiff : ['Index']
2024-12-19 20:29:16 INFO GTAnalysis.sed(): Finished SED
2024-12-19 20:29:21 INFO GTAnalysis.sed(): Execution time: 6.58 s
2024-12-19 20:29:21 INFO GTAnalysis.sed(): Computing SED for 3FGL J1725.3+5853
2024-12-19 20:29:21 INFO GTAnalysis._make_sed(): Fitting SED
2024-12-19 20:29:21 INFO GTAnalysis.free_source(): Fixing parameters for 3FGL J1725.3+5853 : ['Index']
2024-12-19 20:29:21 INFO GTAnalysis.free_source(): Fixing parameters for galdiff : ['Index']
2024-12-19 20:29:23 INFO GTAnalysis.sed(): Finished SED
2024-12-19 20:29:27 INFO GTAnalysis.sed(): Execution time: 6.60 s
2024-12-19 20:29:27 INFO GTBinnedAnalysis.write_xml(): Writing /home/runner/work/fermipy/fermipy/fermipy-extra/notebooks/draco/fit_sed_00.xml...
2024-12-19 20:29:27 INFO GTAnalysis.write_fits(): Writing /home/runner/work/fermipy/fermipy/fermipy-extra/notebooks/draco/fit_sed.fits...
WARNING: Format %s cannot be mapped to the accepted TDISPn keyword values. Format will not be moved into TDISPn keyword. [astropy.io.fits.column]
WARNING: Format %f cannot be mapped to the accepted TDISPn keyword values. Format will not be moved into TDISPn keyword. [astropy.io.fits.column]
WARNING: Format %s cannot be mapped to the accepted TDISPn keyword values. Format will not be moved into TDISPn keyword. [astropy.io.fits.column]
2024-12-19 20:29:35 INFO GTAnalysis.write_roi(): Writing /home/runner/work/fermipy/fermipy/fermipy-extra/notebooks/draco/fit_sed.npy...
We can now inspect the fit results by looking at the elements of the output dictionary. By default the sed method will perform a likelihood scan in each energy bin which is saved in the dloglike_scan array. In the following example we plot the likelihood profile in the first energy bin and overplot the flux upper limit in that bin (vertical black line). fermiPy uses the delta-log-likelihood method to evaluate ULs and we can see that the 95% CL flux upper limit intersects with the point at which the log-likelihood has decreased by 2.71/2 from its maximum value (horizontal red line).
[20]:
# E^2 x Differential flux ULs in each bin in units of MeV cm^{-2} s^{-1}
print(sed_draco['e2dnde_ul95'])
e2dnde_scan = sed_draco['norm_scan']*sed_draco['ref_e2dnde'][:,None]
plt.clf()
plt.figure()
plt.plot(e2dnde_scan[0],
sed_draco['dloglike_scan'][0]-np.max(sed_draco['dloglike_scan'][0]))
plt.gca().set_ylim(-5,1)
plt.gca().axvline(sed_draco['e2dnde_ul95'][0],color='k')
plt.gca().axhline(-2.71/2.,color='r')
plt.show()
[5.89887449e-07 7.40784333e-08 3.50826586e-07 1.65682293e-07
2.69980569e-07 6.93004525e-08 8.74054631e-08 1.41262715e-07
1.10740078e-07 1.44185870e-07 1.66109671e-07 5.64218009e-07
2.78529951e-07 4.42748824e-07 5.03010639e-07 9.68111468e-07
8.22075165e-07 1.09978045e-06 1.47308769e-06 1.97487217e-06
2.64097776e-06 3.54582850e-06 4.74794210e-06 6.50502049e-06]
<Figure size 640x480 with 0 Axes>
We can also visualize the results of the scan with the SEDPlotter class. This class accepts a source object as its argument and creates a visualization of the SED as a sequence of points with errors. Setting showlnl=True overplots the likelihood function in each bin as a color gradient (the so-called castro plot).
[21]:
plt.clf()
fig = plt.figure(figsize=(14,4))
ylim=[1E-8,1E-5]
fig.add_subplot(121)
SEDPlotter(sed_draco).plot()
plt.gca().set_ylim(ylim)
fig.add_subplot(122)
SEDPlotter(sed_j1725).plot()
plt.gca().set_ylim(ylim)
plt.show()
<Figure size 640x480 with 0 Axes>
[22]:
plt.clf()
fig = plt.figure(figsize=(14,4))
fig.add_subplot(121)
SEDPlotter(sed_draco).plot(showlnl=True,ylim=ylim)
plt.gca().set_ylim(ylim)
fig.add_subplot(122)
SEDPlotter(sed_j1725).plot(showlnl=True,ylim=ylim)
plt.gca().set_ylim(ylim)
plt.show()
<Figure size 640x480 with 0 Axes>
Setting DM Upper Limits
Now that we have run a spectral analysis we can use the bin-by-bin likelihoods to gamma-ray flux from DM annihilations in Draco. In the following sample code we demonstrate how to calculate the UL on the DM cross section for a given DM spectral model.
[23]:
import pyLikelihood
# Load the sed data structure
data = sed_draco
# Instantiate a DM Fit Function for a DM particle spectrum given the following parameters
# Mass = 100 GeV
# Cross-Section: 3 x 10^{-26} cm^{3} s^{-1}
# J-Factor: 10^19 GeV^2 cm^{-5}
# Channel: b-bbar
dmf = pyLikelihood.DMFitFunction()
dmf.readFunction(os.path.expandvars('$FERMIPY_ROOT/data/gammamc_dif.dat'))
dmf.setParam('norm',1E19)
dmf.setParam('sigmav',3E-26)
dmf.setParam('mass',100.0)
dmf.setParam('bratio',1.0)
dmf.setParam('channel0',4)
def integrate_eflux(fn,ebins,nstep=10):
"""Compute energy flux within a sequence of energy bins."""
loge = np.linspace(ebins[0],ebins[-1],100)
dfde = [fn(pyLikelihood.dArg(10**x)) for x in loge]
dfde = np.array(dfde)
x = ebins
dx = (x[1:] - x[:-1])
yedge = x[1:,np.newaxis] + np.linspace(0,1,nstep)[np.newaxis,:]*dx[:,np.newaxis]
dy = 10**yedge[:,1:]-10**yedge[:,:-1]
y = 0.5*(yedge[:,1:]+yedge[:,:-1])
eflux = np.interp(np.ravel(y),loge,dfde)
eflux = np.sum(eflux.reshape(y.shape)*10**y*dy,axis=1)
return eflux
class SEDLike(object):
def __init__(self,sed):
self._sed = sed
self._eflux_scan = sed['norm_scan']*sed['ref_eflux'][:,None]
def __call__(self,eflux):
lnl = np.zeros(eflux.shape)
for i, ectr in enumerate(self._sed['e_ctr']):
v = np.interp(eflux[i],
self._eflux_scan[i],
self._sed['dloglike_scan'][i])
lnl[i] += v
return np.sum(lnl,axis=0)
ebins = np.log10(np.array(list(data['e_min']) + list([data['e_max'][-1]])))
eflux = integrate_eflux(dmf,ebins)
sigmav = 3.E-26*np.logspace(-3.,1.,101)
eflux = eflux[:,np.newaxis]*np.logspace(-3,1,101)[np.newaxis,:]
slike = SEDLike(data)
lnl = slike(eflux)
lnl -= np.max(lnl)
# Plot log-likelihood profile
plt.clf()
plt.figure()
plt.plot(sigmav,lnl)
plt.gca().set_xscale('log')
plt.gca().axhline(-2.71/2.,color='k')
plt.gca().set_ylim(-4,1)
plt.gca().set_xlabel('Cross Section [cm$^{3}$ s$^{-1}$]')
plt.show()
sigmav_ul = float(np.interp(2.71/2.,-lnl,sigmav))
print ('Sigma-V Upper Limit: %.2e cm^3/s' % sigmav_ul)
<Figure size 640x480 with 0 Axes>
Sigma-V Upper Limit: 3.09e-26 cm^3/s
[ ]: