This is a getting started tutorial for Gammapy.
In this tutorial we will use the Second Fermi-LAT Catalog of High-Energy Sources (3FHL) catalog, corresponding event list and images to learn how to work with some of the central Gammapy data structures.
We will cover the following topics:
Sky maps
Event lists
Source catalogs
Spectral models and flux points
If you're not yet familiar with the listed Astropy classes, maybe check out the Astropy introduction for Gammapy users first.
Important: to run this tutorial the environment variable GAMMAPY_DATA
must be defined and point to the directory on your machine where the datasets needed are placed. To check whether your setup is correct you can execute the following cell:
import os
path = os.path.expandvars("$GAMMAPY_DATA")
if not os.path.exists(path):
raise Exception("gammapy-data repository not found!")
else:
print("Great your setup is correct!")
In case you encounter an error, you can un-comment and execute the following cell to continue. But we recommend to set up your enviroment correctly as decribed here after you are done with this notebook.
# os.environ['GAMMAPY_DATA'] = os.path.join(os.getcwd(), '..')
Now we can continue with the usual IPython notebooks and Python imports:
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import astropy.units as u
from astropy.coordinates import SkyCoord
from astropy.visualization import simple_norm
The gammapy.maps package contains classes to work with sky images and cubes.
In this section, we will use a simple 2D sky image and will learn how to:
from gammapy.maps import Map
gc_3fhl = Map.read("$GAMMAPY_DATA/fermi-3fhl-gc/fermi-3fhl-gc-counts.fits.gz")
The image is a WCSNDMap
object:
gc_3fhl
The shape of the image is 400 x 200 pixel and it is defined using a cartesian projection in galactic coordinates.
The geom
attribute is a WcsGeom
object:
gc_3fhl.geom
Let's take a closer look a the .data
attribute:
gc_3fhl.data
That looks familiar! It just an ordinary 2 dimensional numpy array, which means you can apply any known numpy method to it:
print("Total number of counts in the image: {:.0f}".format(gc_3fhl.data.sum()))
To show the image on the screen we can use the plot
method. It basically calls plt.imshow, passing the gc_3fhl.data
attribute but in addition handles axis with world coordinates using wcsaxes and defines some defaults for nicer plots (e.g. the colormap 'afmhot'):
gc_3fhl.plot(stretch="sqrt");
To make the structures in the image more visible we will smooth the data using a Gausian kernel with a radius of 0.5 deg. Again smooth()
is a wrapper around existing functionality from the scientific Python libraries. In this case it is Scipy's gaussian_filter method. For convenience the kernel shape can be specified with as string and the smoothing radius with a quantity. It returns again a map object, that we can plot directly the same way we did above:
gc_3fhl_smoothed = gc_3fhl.smooth(kernel="gauss", width=0.2 * u.deg)
gc_3fhl_smoothed.plot(stretch="sqrt");
The smoothed plot already looks much nicer, but still the image is rather large. As we are mostly interested in the inner part of the image, we will cut out a quadratic region of the size 9 deg x 9 deg around Vela. Therefore we use Map.cutout
to make a cutout map:
# define center and size of the cutout region
center = SkyCoord(0, 0, unit="deg", frame="galactic")
gc_3fhl_cutout = gc_3fhl_smoothed.cutout(center, 9 * u.deg)
gc_3fhl_cutout.plot(stretch="sqrt");
For a more detailed introdcution to ganmmapy.maps
, take a look a the intro_maps.ipynb notebook.
Sag A*
(you can find examples in the WCSAxes documentation).
Almost any high-level gamma-ray data analysis starts with the raw measured counts data, which is stored in event lists. In Gammapy event lists are represented by the gammapy.data.EventList class.
In this section we will learn how to:
EventList
attributes such as .table
and .energy
Let's start with the import from the gammapy.data submodule:
from gammapy.data import EventList
Very similar to the sky map class an event list can be created, by passing a filename to the .read()
method:
events_3fhl = EventList.read(
"$GAMMAPY_DATA/fermi-3fhl-gc/fermi-3fhl-gc-events.fits.gz"
)
This time the actual data is stored as an astropy.table.Table object. It can be accessed with .table
attribute:
events_3fhl.table
You can do len over event_3fhl.table to find the total number of events.
print("Total number of events: {}".format(len(events_3fhl.table)))
And we can access any other attribute of the Table
object as well:
events_3fhl.table.colnames
For convenience we can access the most important event parameters as properties on the EventList
objects. The attributes will return corresponding Astropy objects to represent the data, such as astropy.units.Quantity, astropy.coordinates.SkyCoord or astropy.time.Time objects:
events_3fhl.energy.to("GeV")
events_3fhl.galactic
# events_3fhl.radec
events_3fhl.time
In addition EventList
provides convenience methods to filter the event lists. One possible use case is to find the highest energy event within a radius of 0.5 deg around the vela position:
# select all events within a radius of 0.5 deg around center
events_gc_3fhl = events_3fhl.select_sky_cone(center=center, radius=0.5 * u.deg)
# sort events by energy
events_gc_3fhl.table.sort("ENERGY")
# and show highest energy photon
events_gc_3fhl.energy[-1].to("GeV")
Gammapy provides a convenient interface to access and work with catalog based data.
In this section we will learn how to:
Let's start with importing the 3FHL catalog object from the gammapy.catalog submodule:
from gammapy.catalog import SourceCatalog3FHL
First we initialize the Fermi-LAT 3FHL catalog and directly take a look at the .table
attribute:
fermi_3fhl = SourceCatalog3FHL()
fermi_3fhl.table
This looks very familiar again. The data is just stored as an astropy.table.Table object. We have all the methods and attributes of the Table
object available. E.g. we can sort the underlying table by Signif_Avg
to find the top 5 most significant sources:
# sort table by significance
fermi_3fhl.table.sort("Signif_Avg")
# invert the order to find the highest values and take the top 5
top_five_TS_3fhl = fermi_3fhl.table[::-1][:5]
# print the top five significant sources with association and source class
top_five_TS_3fhl[["Source_Name", "ASSOC1", "ASSOC2", "CLASS", "Signif_Avg"]]
If you are interested in the data of an individual source you can access the information from catalog using the name of the source or any alias source name that is defined in the catalog:
mkn_421_3fhl = fermi_3fhl["3FHL J1104.4+3812"]
# or use any alias source name that is defined in the catalog
mkn_421_3fhl = fermi_3fhl["Mkn 421"]
print(mkn_421_3fhl.data["Signif_Avg"])
WcsGeom.contains()
and SourceCatalog.positions
might be helpful for this. Add markers for all these sources and try to add labels with the source names. The function ax.text() might be also helpful.
In the previous section we learned how access basic data from individual sources in the catalog. Now we will go one step further and explore the full spectral information of sources. We will learn how to:
As a first example we will start with the Crab Nebula:
crab_3fhl = fermi_3fhl["Crab Nebula"]
print(crab_3fhl.spectral_model)
The crab_3fhl.spectral_model
is an instance of the gammapy.spectrum.models.PowerLaw2 model, with the parameter values and errors taken from the 3FHL catalog.
Let's plot the spectral model in the energy range between 10 GeV and 2000 GeV:
ax_crab_3fhl = crab_3fhl.spectral_model.plot(
energy_range=[10, 2000] * u.GeV, energy_power=0
)
We assign the return axes object to variable called ax_crab_3fhl
, because we will re-use it later to plot the flux points on top.
To compute the differential flux at 100 GeV we can simply call the model like normal Python function and convert to the desired units:
crab_3fhl.spectral_model(100 * u.GeV).to("cm-2 s-1 GeV-1")
Next we can compute the integral flux of the Crab between 10 GeV and 2000 GeV:
crab_3fhl.spectral_model.integral(emin=10 * u.GeV, emax=2000 * u.GeV).to(
"cm-2 s-1"
)
We can easily convince ourself, that it corresponds to the value given in the Fermi-LAT 3FHL catalog:
crab_3fhl.data["Flux"]
In addition we can compute the energy flux between 10 GeV and 2000 GeV:
crab_3fhl.spectral_model.energy_flux(emin=10 * u.GeV, emax=2000 * u.GeV).to(
"erg cm-2 s-1"
)
Next we will access the flux points data of the Crab:
print(crab_3fhl.flux_points)
If you want to learn more about the different flux point formats you can read the specification here.
No we can check again the underlying astropy data structure by accessing the .table
attribute:
crab_3fhl.flux_points.table
Finally let's combine spectral model and flux points in a single plot and scale with energy_power=2
to obtain the spectral energy distribution:
ax = crab_3fhl.spectral_model.plot(
energy_range=[10, 2000] * u.GeV, energy_power=2
)
crab_3fhl.flux_points.to_sed_type("dnde").plot(ax=ax, energy_power=2);
This was a quick introduction to some of the high-level classes in Astropy and Gammapy.