1
|
|
|
# Licensed under a 3-clause BSD style license - see LICENSE.rst |
2
|
|
|
import pytest |
3
|
|
|
import numpy as np |
4
|
|
|
from numpy.testing import assert_allclose |
5
|
|
|
from astropy import units as u |
6
|
|
|
from astropy.coordinates import SkyCoord, EarthLocation |
7
|
|
|
from astropy.time import Time |
8
|
|
|
from ...utils.testing import requires_data |
9
|
|
|
from ...maps import WcsGeom, HpxGeom, MapAxis |
10
|
|
|
from ...irf import Background3D |
11
|
|
|
from ..background import make_map_background_irf |
12
|
|
|
from ...data.pointing import FixedPointingInfo |
13
|
|
|
|
14
|
|
|
pytest.importorskip("healpy") |
15
|
|
|
|
16
|
|
|
|
17
|
|
|
@pytest.fixture(scope="session") |
18
|
|
|
def fixed_pointing_info(): |
19
|
|
|
filename = ( |
20
|
|
|
"$GAMMAPY_DATA/cta-1dc/data/baseline/gps/gps_baseline_110380.fits" |
21
|
|
|
) |
22
|
|
|
return FixedPointingInfo.read(filename) |
23
|
|
|
|
24
|
|
|
|
25
|
|
|
@pytest.fixture(scope="session") |
26
|
|
|
def fixed_pointing_info_aligned(fixed_pointing_info): |
27
|
|
|
# Create Fixed Pointing Info aligined between sky and horizon coordinates |
28
|
|
|
# (removes rotation in FoV and results in predictable solid angles) |
29
|
|
|
origin = SkyCoord( |
30
|
|
|
0, 0, |
31
|
|
|
unit='deg', frame='icrs', |
32
|
|
|
location=EarthLocation(lat=90*u.deg, lon=0*u.deg), |
33
|
|
|
obstime=Time('2000-9-21 12:00:00') |
34
|
|
|
) |
35
|
|
|
fpi = fixed_pointing_info |
36
|
|
|
meta = fpi.meta.copy() |
37
|
|
|
meta['RA_PNT'] = origin.icrs.ra |
38
|
|
|
meta['DEC_PNT'] = origin.icrs.dec |
39
|
|
|
meta['GEOLON'] = origin.location.lon |
40
|
|
|
meta['GEOLAT'] = origin.location.lat |
41
|
|
|
meta['ALTITUDE'] = origin.location.height |
42
|
|
|
time_start = origin.obstime.datetime - fpi.time_ref.datetime |
43
|
|
|
meta['TSTART'] = time_start.total_seconds() |
44
|
|
|
meta['TSTOP'] = meta['TSTART'] + 60 |
45
|
|
|
return FixedPointingInfo(meta) |
46
|
|
|
|
47
|
|
|
|
48
|
|
|
@pytest.fixture(scope="session") |
49
|
|
|
def bkg_3d(): |
50
|
|
|
filename = ( |
51
|
|
|
"$GAMMAPY_DATA/cta-1dc/caldb/data/cta/1dc/bcf/South_z20_50h/irf_file.fits" |
52
|
|
|
) |
53
|
|
|
return Background3D.read(filename, hdu="BACKGROUND") |
54
|
|
|
|
55
|
|
|
|
56
|
|
|
def bkg_3d_custom(symmetry='constant'): |
57
|
|
|
if symmetry == 'constant': |
58
|
|
|
data = np.ones((2, 3, 3)) * u.Unit("s-1 MeV-1 sr-1") |
59
|
|
|
elif symmetry == 'symmetric': |
60
|
|
|
data = np.ones((2, 3, 3)) * u.Unit("s-1 MeV-1 sr-1") |
61
|
|
|
data[:, 1, 1] *= 2 |
62
|
|
|
elif symmetry == 'asymmetric': |
63
|
|
|
data = np.indices((3, 3))[1] + 1 |
64
|
|
|
data = np.stack(2 * [data]) * u.Unit("s-1 MeV-1 sr-1") |
65
|
|
|
else: |
66
|
|
|
raise ValueError("Unkown value for symmetry: {}".format(symmetry)) |
67
|
|
|
|
68
|
|
|
energy = [0.1, 10, 1000] * u.TeV |
69
|
|
|
fov_lon = [-3, -1, 1, 3] * u.deg |
70
|
|
|
fov_lat = [-3, -1, 1, 3] * u.deg |
71
|
|
|
return Background3D( |
72
|
|
|
energy_lo=energy[:-1], |
73
|
|
|
energy_hi=energy[1:], |
74
|
|
|
fov_lon_lo=fov_lon[:-1], |
75
|
|
|
fov_lon_hi=fov_lon[1:], |
76
|
|
|
fov_lat_lo=fov_lat[:-1], |
77
|
|
|
fov_lat_hi=fov_lat[1:], |
78
|
|
|
data=data, |
79
|
|
|
) |
80
|
|
|
|
81
|
|
|
|
82
|
|
|
def make_map_background_irf_with_symmetry(fpi, symmetry='constant'): |
83
|
|
|
axis = MapAxis.from_edges( |
84
|
|
|
[0.1, 1, 10], name="energy", unit="TeV", interp="log" |
85
|
|
|
) |
86
|
|
|
return make_map_background_irf( |
87
|
|
|
pointing=fpi, |
88
|
|
|
ontime="42 s", |
89
|
|
|
bkg=bkg_3d_custom(symmetry), |
90
|
|
|
geom=WcsGeom.create( |
91
|
|
|
npix=(3, 3), |
92
|
|
|
binsz=4, |
93
|
|
|
axes=[axis], |
94
|
|
|
skydir=fpi.radec |
95
|
|
|
), |
96
|
|
|
) |
97
|
|
|
|
98
|
|
|
|
99
|
|
|
def geom(map_type, ebounds, skydir): |
100
|
|
|
axis = MapAxis.from_edges(ebounds, name="energy", unit="TeV", interp="log") |
101
|
|
|
if map_type == "wcs": |
102
|
|
|
return WcsGeom.create(npix=(4, 3), binsz=2, axes=[axis], skydir=skydir) |
103
|
|
|
elif map_type == "hpx": |
104
|
|
|
return HpxGeom(256, axes=[axis]) |
105
|
|
|
else: |
106
|
|
|
raise ValueError() |
107
|
|
|
|
108
|
|
|
|
109
|
|
|
@requires_data("gammapy-data") |
110
|
|
|
@pytest.mark.parametrize( |
111
|
|
|
"pars", |
112
|
|
|
[ |
113
|
|
|
{ |
114
|
|
|
"map_type": "wcs", |
115
|
|
|
"ebounds": [0.1, 1, 10], |
116
|
|
|
"shape": (2, 3, 4), |
117
|
|
|
"sum": 940.167672, |
118
|
|
|
}, |
119
|
|
|
{ |
120
|
|
|
"map_type": "wcs", |
121
|
|
|
"ebounds": [0.1, 10], |
122
|
|
|
"shape": (1, 3, 4), |
123
|
|
|
"sum": 1019.495516, |
124
|
|
|
}, |
125
|
|
|
# TODO: make this work for HPX |
126
|
|
|
# 'HpxGeom' object has no attribute 'separation' |
127
|
|
|
# { |
128
|
|
|
# 'geom': geom(map_type='hpx', ebounds=[0.1, 1, 10]), |
129
|
|
|
# 'shape': '???', |
130
|
|
|
# 'sum': '???', |
131
|
|
|
# }, |
132
|
|
|
], |
133
|
|
|
) |
134
|
|
|
|
135
|
|
|
def test_make_map_background_irf(bkg_3d, pars, fixed_pointing_info): |
136
|
|
|
m = make_map_background_irf( |
137
|
|
|
pointing=fixed_pointing_info, |
138
|
|
|
ontime="42 s", |
139
|
|
|
bkg=bkg_3d, |
140
|
|
|
geom=geom( |
141
|
|
|
map_type=pars['map_type'], |
142
|
|
|
ebounds=pars['ebounds'], |
143
|
|
|
skydir=fixed_pointing_info.radec |
144
|
|
|
), |
145
|
|
|
) |
146
|
|
|
|
147
|
|
|
assert m.data.shape == pars["shape"] |
148
|
|
|
assert m.unit == "" |
149
|
|
|
assert_allclose(m.data.sum(), pars["sum"], rtol=1e-5) |
150
|
|
|
|
151
|
|
|
|
152
|
|
|
def test_make_map_background_irf_constant(fixed_pointing_info_aligned): |
153
|
|
|
m = make_map_background_irf_with_symmetry( |
154
|
|
|
fpi=fixed_pointing_info_aligned, symmetry='constant' |
155
|
|
|
) |
156
|
|
|
for d in m.data: |
157
|
|
|
assert_allclose(d[1, :], d[1, 0]) # Constant along lon |
158
|
|
|
assert_allclose(d[0, 1], d[2, 1]) # Symmetric along lat |
159
|
|
|
with pytest.raises(AssertionError): |
160
|
|
|
# Not constant along lat due to changes in |
161
|
|
|
# solid angle (great circle) |
162
|
|
|
assert_allclose(d[:, 1], d[0, 1]) |
163
|
|
|
|
164
|
|
|
|
165
|
|
|
def test_make_map_background_irf_sym(fixed_pointing_info_aligned): |
166
|
|
|
m = make_map_background_irf_with_symmetry( |
167
|
|
|
fpi=fixed_pointing_info_aligned, symmetry='symmetric' |
168
|
|
|
) |
169
|
|
|
for d in m.data: |
170
|
|
|
assert_allclose(d[1, 0], d[1, 2], rtol=1e-4) # Symmetric along lon |
171
|
|
|
assert_allclose(d[0, 1], d[2, 1], rtol=1e-4) # Symmetric along lat |
172
|
|
|
|
173
|
|
|
|
174
|
|
|
def test_make_map_background_irf_asym(fixed_pointing_info_aligned): |
175
|
|
|
m = make_map_background_irf_with_symmetry( |
176
|
|
|
fpi=fixed_pointing_info_aligned, symmetry='asymmetric' |
177
|
|
|
) |
178
|
|
|
for d in m.data: |
179
|
|
|
# TODO: |
180
|
|
|
# Dimensions of skymap data are [energy, lat, lon] (and is |
181
|
|
|
# representated as [lon, lat, energy] in the api, but the bkg irf |
182
|
|
|
# dimensions are currently [energy, lon, lat] - Will be changed in |
183
|
|
|
# the future (perhaps when IRFs use the skymaps class) |
184
|
|
|
assert_allclose(d[1, 0], d[1, 2], rtol=1e-4) # Symmetric along lon |
185
|
|
|
with pytest.raises(AssertionError): |
186
|
|
|
assert_allclose(d[0, 1], d[2, 1], rtol=1e-4) # Symmetric along lat |
187
|
|
|
assert_allclose(d[0, 1]*9, d[2, 1], rtol=1e-4) # Asymmetric along lat |
188
|
|
|
|