New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Stack EDISP for a set of observations #772
Changes from 5 commits
62bcba1
265cb99
0b731a2
de5634e
3da0fa0
c060892
191385d
b500281
037a452
661e8dd
8f5ff46
803a32b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,10 +12,11 @@ | |
from astropy.time import Time | ||
from astropy.coordinates import SkyCoord | ||
from ..utils.scripts import make_path | ||
from ..utils.energy import Energy | ||
from .obs_table import ObservationTable | ||
from .hdu_index_table import HDUIndexTable | ||
from .utils import _earth_location_from_dict | ||
from ..irf import EnergyDependentTablePSF | ||
from ..irf import EnergyDependentTablePSF, IRFStacker | ||
|
||
__all__ = [ | ||
'DataStore', | ||
|
@@ -668,6 +669,7 @@ class ObservationList(UserList): | |
|
||
Could be extended to hold a more generic class of observations | ||
""" | ||
|
||
def __str__(self): | ||
s = self.__class__.__name__ + '\n' | ||
s += 'Number of observations: {}\n'.format(len(self)) | ||
|
@@ -711,3 +713,59 @@ def make_psf(self, position, energy=None, theta=None): | |
psf_tot = EnergyDependentTablePSF(energy=energy, offset=theta, exposure=exposure, | ||
psf_value=psf_value.T) | ||
return psf_tot | ||
|
||
def make_mean_edisp(self, position, e_true, e_reco, low_reco_threshold=None, high_reco_threshold=None): | ||
r"""Make mean rmf for a given position and a set of observations. | ||
|
||
Compute the mean rmf of a set of observations j at a given position | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of repeating the definiton here I would just add a link to the |
||
|
||
The stacking of :math:`j` observations is implemented as follows. :math:`k` | ||
and :math:`l` denote a bin in reconstructed and true energy, respectively. | ||
|
||
.. math:: | ||
|
||
\epsilon_{jk} =\left\{\begin{array}{cl} 1, & \mbox{if | ||
bin k is inside the energy thresholds}\\ 0, & \mbox{otherwise} \end{array}\right. | ||
|
||
\overline{\mathrm{edisp}}_{kl} = \frac{\sum_{j} \mathrm{edisp}_{jkl} | ||
\cdot \mathrm{aeff}_{jl} \cdot t_j \cdot \epsilon_{jk}}{\sum_{j} \mathrm{aeff}_{jl} | ||
\cdot t_j} | ||
|
||
Parameters | ||
---------- | ||
position : `~astropy.coordinates.SkyCoord` | ||
Position at which to compute the mean EDISP | ||
e_true : `~gammapy.utils.energy.EnergyBounds` | ||
True energy axis | ||
e_reco : `~gammapy.utils.energy.EnergyBounds` | ||
Reconstructed energy axis | ||
low_reco_threshold : `~gammapy.utils.energy.Energy` | ||
low energy threshold in reco energy, default 0.002 TeV | ||
high_reco_threshold : `~gammapy.utils.energy.Energy` | ||
high energy threshold in reco energy , default 150 TeV | ||
Returns | ||
------- | ||
stacked_rmf: `~gammapy.irf.EnergyDispersion` | ||
Stacked RMF for a set of observation | ||
""" | ||
|
||
list_arf = list() | ||
list_edisp = list() | ||
list_livetime = list() | ||
if not low_reco_threshold: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We looked at this in detail and decided that it's OK / best to use quantities as default keyword arguments. |
||
low_reco_threshold = Energy(0.002, "TeV") | ||
if not high_reco_threshold: | ||
high_reco_threshold = Energy(150, "TeV") | ||
list_low_threshold = [low_reco_threshold] * len(self) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you hard code the same energy threshold for all observations? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @joleroi There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I don't know enough about image analysis, it seemed weird to have the same threshold everywhere. Thanks for clarifying! |
||
list_high_threshold = [high_reco_threshold] * len(self) | ||
for obs in self: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the following paragraph there are many lines that are longer than the recommended length of 79 characters. This makes the code really hard to read. Of course its no problem if a line is sometimes a little bit too long, but if you can avoid it, please do so. Actually, since you are using pycharm the editor should show you line that are too long and sometimes even give you the option to wrap them automatically (but I don't really know pycharm). In this case you can simply split one long line into several statements, e.g. instead of
For a general style guide for python see https://www.python.org/dev/peps/pep-0008/#introduction, or check out the pep8 command line tool |
||
offset = position.separation(obs.pointing_radec) | ||
list_arf.append(obs.aeff.to_effective_area_table(offset, energy=e_true)) | ||
list_edisp.append(obs.edisp.to_energy_dispersion(offset, e_reco=e_reco, e_true=e_true)) | ||
list_livetime.append(obs.observation_live_time_duration) | ||
|
||
irf_stack = IRFStacker(list_arf=list_arf, list_edisp=list_edisp, list_livetime=list_livetime, | ||
list_low_threshold=list_low_threshold, list_high_threshold=list_high_threshold) | ||
irf_stack.mean_edisp() | ||
|
||
return irf_stack.stacked_edisp |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,14 @@ | ||
# Licensed under a 3-clause BSD style license - see LICENSE.rst | ||
from __future__ import absolute_import, division, print_function, unicode_literals | ||
import numpy as np | ||
from numpy.testing import assert_allclose | ||
from numpy.testing import assert_allclose, assert_equal | ||
from astropy.tests.helper import pytest, assert_quantity_allclose | ||
from astropy.coordinates import Angle, SkyCoord | ||
from astropy.units import Quantity | ||
import astropy.units as u | ||
from ...data import DataStore, DataManager | ||
from ...data import DataStore, DataManager, ObservationList | ||
from ...utils.testing import data_manager, requires_data, requires_dependency | ||
from ...utils.energy import Energy | ||
from ...datasets import gammapy_extra | ||
from ...utils.energy import EnergyBounds | ||
|
||
|
@@ -150,3 +151,38 @@ def test_make_psf(pars, result): | |
assert_quantity_allclose(psf.energy[10], result["psf_energy"]) | ||
assert_quantity_allclose(psf.exposure[10], result["psf_exposure"]) | ||
assert_quantity_allclose(psf.psf_value[10, 50], result["psf_value"]) | ||
|
||
|
||
def test_make_meanedisp(tmpdir): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You have to mark this test
https://travis-ci.org/gammapy/gammapy/jobs/175732342#L1722 And if it uses scipy or some other optional dependency, also
Also, please rename: |
||
position = SkyCoord(83.63, 22.01, unit='deg') | ||
store = gammapy_extra.filename("datasets/hess-crab4-hd-hap-prod2") | ||
data_store = DataStore.from_dir(store) | ||
|
||
obs1 = data_store.obs(23523) | ||
obs2 = data_store.obs(23592) | ||
obslist = ObservationList([obs1, obs2]) | ||
|
||
e_true = EnergyBounds.equal_log_spacing(0.01, 150, 80, "TeV") | ||
e_reco = EnergyBounds.equal_log_spacing(0.5, 100, 15, "TeV") | ||
rmf = obslist.make_mean_edisp(position=position, e_true=e_true, e_reco=e_reco) | ||
|
||
assert len(rmf.e_true.nodes) == 80 | ||
assert len(rmf.e_reco.nodes) == 15 | ||
assert_quantity_allclose(rmf.data[53, 8], 0.0559785805550798) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Assert on thresholds and / or behaviour near threshold for the stacked EDISP? |
||
|
||
rmf2 = obslist.make_mean_edisp(position=position, e_true=e_true, e_reco=e_reco, | ||
low_reco_threshold=Energy(1, "TeV"), high_reco_threshold=Energy(60, "TeV")) | ||
# Test that the energy dispersion is equal to zero for the reco energy bin inferior to low_reco_threshold and | ||
# superior to high_reco_threshold | ||
i2 = np.where(rmf2.data[:, 13] != 0)[0] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I find this hard to read. I don't know what index There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, there is. It's inherited from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does show up here: This can be configured globally or per-class in Sphinx, whether inherited attributes are shown for sub-classes. At the moment the Gammapy (and I think Astropy) docs don't show inherited methods, you have to click on the link to the base class at the top. I'm not sure if changing the default to always show inherited attributes / methods, or to do it on a case-by-case basis to get "optimal" API docs is an improvement / maintainable. @joleroi or @JouvinLea - If you think this could / should be improved, please open a separate issue stating your preference. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I agree. @JouvinLea if you think we should show the inherited methods for the IRF classes please open an issue. |
||
assert len(i2) == 0 | ||
i2 = np.where(rmf2.data[:, 1] != 0)[0] | ||
assert len(i2) == 0 | ||
# Test that for the reco energy bin inside the thresholds, the edisp defined without threshold in the same than | ||
# the edisp defined with the low and high reco energy thresholds | ||
i = np.where(rmf.data[:, 12] != 0)[0] | ||
i2 = np.where(rmf2.data[:, 12] != 0)[0] | ||
assert_equal(i, i2) | ||
i = np.where(rmf.data[:, 2] != 0)[0] | ||
i2 = np.where(rmf2.data[:, 2] != 0)[0] | ||
assert_equal(i, i2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docstring still says "rmf" three times where IMO it would be better to say "edisp".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the same argument actually hold for
arf
vs.aeff
.arf
stands for auxilliary response file and is not really a synonym for effective area.