Skip to content

Commit 7280b6e

Browse files
committed
add script and unit tests
1 parent 48c2607 commit 7280b6e

File tree

4 files changed

+178
-74
lines changed

4 files changed

+178
-74
lines changed

irf/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
from .collection_area import collection_area
2+
from .exposure_map import estimate_exposure_time, build_exposure_map
23

3-
4-
__all__ = ['collection_area']
4+
__all__ = ['collection_area', 'estimate_exposure_time', 'build_exposure_map']

irf/exposure_map.py

Lines changed: 27 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
import astropy.units as u
22
from astropy.coordinates import Angle, SkyCoord
3+
from astropy import wcs
34
from regions import CircleSkyRegion
4-
from astroquery.skyview import SkyView
5-
from astropy.wcs import WCS
6-
import matplotlib.pyplot as plt
7-
import fact.io as fio
85
import numpy as np
9-
import pandas as pd
106
from scipy.stats import expon
117

128

139
def estimate_exposure_time(timestamps):
1410
'''
15-
Takes numpy datetime64[ns] timestamps and returns an estimates of the exposure time in seconds
11+
Takes numpy datetime64[ns] timestamps and returns an estimates of the exposure time in seconds.
1612
'''
1713
delta_s = np.diff(timestamps).astype(int) * float(1e-9)
14+
1815
# take only events that came within 30 seconds or so
1916
delta_s = delta_s[delta_s < 30]
2017
scale = delta_s.mean()
@@ -43,71 +40,38 @@ def build_exposure_regions(pointing_coords, fov=4.5 * u.deg):
4340
return unique_pointing_positions, regions
4441

4542

43+
def _build_standard_wcs(image_center, shape, naxis=2, fov=9 * u.deg):
44+
width, height = shape
45+
46+
w = wcs.WCS(naxis=2)
47+
w.wcs.crpix = [width / 2 + 0.5, height / 2 + 0.5]
48+
w.wcs.cdelt = np.array([-fov.value / width, fov.value / height])
49+
w.wcs.crval = [image_center.ra.deg, image_center.dec.deg]
50+
w.wcs.ctype = ["RA---TAN", "DEC--TAN"]
51+
w.wcs.radesys = 'FK5'
52+
w.wcs.equinox = 2000.0
53+
w.wcs.cunit = ['deg', 'deg']
54+
w._naxis = [width, height]
55+
return w
56+
57+
4658
@u.quantity_input(event_ra=u.hourangle, event_dec=u.deg, fov=u.deg)
47-
def build_exposure_map(pointing_coords, event_time, fov=4.5 * u.deg):
48-
unique_pointing_positions, regions = build_exposure_regions(pointing_coords)
59+
def build_exposure_map(pointing_coords, event_time, fov=4.5 * u.deg, wcs=None, shape=(1000, 1000)):
4960

50-
img_center = SkyCoord(ra=pointing_coords.ra.mean(), dec=pointing_coords.dec.mean())
51-
print('getting image')
52-
hdu = SkyView.get_images(position=img_center, pixels=1000, survey=['DSS'], width=2 * fov, height=2 * fov)[0][0]
53-
img = hdu.data
54-
wcs = WCS(hdu.header)
61+
if not wcs:
62+
image_center = SkyCoord(ra=pointing_coords.ra.mean(), dec=pointing_coords.dec.mean())
63+
wcs = _build_standard_wcs(image_center, shape, fov=2 * fov)
64+
65+
unique_pointing_positions, regions = build_exposure_regions(pointing_coords)
5566

56-
print('starting loop')
5767
times = []
5868
for p in unique_pointing_positions:
5969
m = (pointing_coords.ra == p.ra) & (pointing_coords.dec == p.dec)
6070
exposure_time = estimate_exposure_time(event_time[m])
61-
print(exposure_time.to('h'))
6271
times.append(exposure_time)
6372

64-
print(sum(times).to('h'))
65-
66-
# import IPython; IPython.embed()
67-
masks = [r.to_pixel(wcs).to_mask().to_image(img.shape) for r in regions]
68-
#
73+
masks = [r.to_pixel(wcs).to_mask().to_image(shape) for r in regions]
6974
cutout = sum(masks).astype(bool)
70-
mask = sum([w * m for w, m in zip(times, masks)])
71-
72-
return np.ma.masked_array(mask.to('h'), mask=~cutout), wcs, img
73-
74-
75-
def plot_exposure(img, mask, wcs):
76-
ax = plt.subplot(projection=wcs)
77-
78-
ax.imshow(img, cmap='gray')
79-
d = ax.imshow(mask, alpha=0.7)
80-
cb = plt.colorbar(d)
81-
cb.set_label('Live Time / Hours')
82-
ax.set_xlabel('Galactic Longitude')
83-
ax.set_ylabel('Galactic Latitude')
84-
85-
crab = SkyCoord.from_name('Crab Nebula')
86-
ax.scatter(crab.ra.deg, crab.dec.deg, transform=ax.get_transform('icrs'), label='Crab Nebula')
87-
ax.legend()
88-
89-
90-
if __name__ == '__main__':
91-
runs = fio.read_data('crab_dl3.hdf5', key='runs')
92-
dl3 = fio.read_data('crab_dl3.hdf5', key='events')
93-
94-
data = pd.merge(runs, dl3, on=['run_id', 'night'] )
95-
96-
timestamps = pd.to_datetime(data.timestamp).values
97-
total_ontime = estimate_exposure_time(timestamps)
98-
print('total exposure time: {}'.format(total_ontime.to(u.h)))
99-
100-
ra = data.ra_prediction.values * u.hourangle
101-
dec = data.dec_prediction.values * u.deg
102-
103-
ra_pointing = data.right_ascension.values * u.hourangle
104-
dec_pointing = data.declination.values * u.deg
105-
106-
event_coords = SkyCoord(ra=ra, dec=dec)
107-
pointing_coords = SkyCoord(ra=ra_pointing, dec=dec_pointing)
108-
109-
mask, wcs, img = build_exposure_map(pointing_coords, timestamps)
75+
mask = sum([w.to('h').value * m for w, m in zip(times, masks)])
11076

111-
ax = plot_exposure(img, mask, wcs)
112-
plt.savefig('exposure.pdf')
113-
# plt.show()
77+
return np.ma.masked_array(mask, mask=~cutout), wcs
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
from irf import estimate_exposure_time, build_exposure_map
2+
import click
3+
import fact.io as fio
4+
import pandas as pd
5+
from astroquery.skyview import SkyView
6+
from astropy.wcs import WCS
7+
from astropy.visualization import ImageNormalize, ZScaleInterval, AsinhStretch
8+
import astropy.units as u
9+
from astropy.coordinates import SkyCoord
10+
11+
import matplotlib.pyplot as plt
12+
13+
14+
@u.quantity_input(fov=u.deg)
15+
def get_sdss_sky_image(img_center, fov=9 * u.deg, n_pix=1000):
16+
'''
17+
A small helper method which uses astroquery to get an image from the sdss.
18+
This requires internet access and fails pretty often due to http timeouts.
19+
'''
20+
hdu = SkyView.get_images(
21+
position=img_center,
22+
pixels=n_pix,
23+
survey=['DSS'],
24+
width=fov,
25+
height=fov)[0][0]
26+
27+
img = hdu.data
28+
wcs = WCS(hdu.header)
29+
return img, wcs
30+
31+
32+
@click.command()
33+
@click.argument(
34+
'dl3_path',
35+
type=click.Path(file_okay=True, dir_okay=False),
36+
)
37+
@click.argument(
38+
'output_path',
39+
type=click.Path(file_okay=True, dir_okay=False, exists=False),
40+
)
41+
@click.option(
42+
'-n',
43+
'--n_pix',
44+
default=1000,
45+
help='number of pixels along the edge of the produced image',
46+
)
47+
@click.option(
48+
'-s',
49+
'--source_name',
50+
default=None,
51+
help=
52+
'If supplied, e.g. "Crab Nebula" will draw the position of that source into the image',
53+
)
54+
@click.option(
55+
'--background/--no-background',
56+
default=True,
57+
help='If true, downloads SDSS image for backgrund image in the plot')
58+
def main(dl3_path, output_path, n_pix, source_name, background):
59+
'''
60+
Takes FACT dl3 output and plots a skymap which is being saved to the output_path.
61+
'''
62+
runs = fio.read_data(dl3_path, key='runs')
63+
dl3 = fio.read_data(dl3_path, key='events')
64+
65+
data = pd.merge(runs, dl3, on=['run_id', 'night'])
66+
67+
timestamps = pd.to_datetime(data.timestamp).values
68+
total_ontime = estimate_exposure_time(timestamps)
69+
print('Total estimated exposure time: {}'.format(total_ontime.to(u.h)))
70+
71+
ra_pointing = data.right_ascension.values * u.hourangle
72+
dec_pointing = data.declination.values * u.deg
73+
pointing = SkyCoord(ra=ra_pointing, dec=dec_pointing)
74+
75+
img = None
76+
wcs = None
77+
if background:
78+
img_center = SkyCoord(ra=pointing.ra.mean(), dec=pointing.dec.mean())
79+
img, wcs = get_sdss_sky_image(
80+
img_center=img_center, n_pix=n_pix, fov=9 * u.deg)
81+
82+
mask, wcs = build_exposure_map(pointing, timestamps, shape=(n_pix, n_pix))
83+
84+
ax = plot_exposure(mask, wcs, image=img)
85+
if source_name:
86+
source = SkyCoord.from_name('Crab Nebula')
87+
ax.scatter(
88+
source.ra.deg,
89+
source.dec.deg,
90+
transform=ax.get_transform('icrs'),
91+
label=source_name,
92+
s=10**2,
93+
facecolors='none',
94+
edgecolors='r',
95+
)
96+
ax.legend()
97+
98+
plt.savefig(output_path, dpi=200)
99+
100+
101+
def plot_exposure(mask, wcs, image=None):
102+
plt.figure(figsize=(10, 10))
103+
ax = plt.subplot(projection=wcs)
104+
105+
if image is not None:
106+
norm = ImageNormalize(
107+
image,
108+
interval=ZScaleInterval(contrast=0.05),
109+
stretch=AsinhStretch(a=0.2))
110+
ax.imshow(image, cmap='gray', norm=norm, interpolation='nearest')
111+
112+
d = ax.imshow(mask, alpha=0.7)
113+
cb = plt.colorbar(d)
114+
cb.set_label('Live Time / Hours')
115+
ax.set_xlabel('Galactic Longitude')
116+
ax.set_ylabel('Galactic Latitude')
117+
118+
return ax
119+
120+
121+
if __name__ == '__main__':
122+
main()

irf/tests/test_exposure_map.py

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
import pytest
33
import fact.io as fio
44
import pandas as pd
5-
from irf import estimate_exposure_time
5+
6+
import astropy.units as u
7+
from astropy.coordinates import SkyCoord
8+
9+
from irf import estimate_exposure_time, build_exposure_map
610

711

812
FIXTURE_DIR = os.path.join(
@@ -22,15 +26,29 @@ def runs():
2226

2327

2428

25-
def test_exposure_time(runs, events):
26-
n_crab_sample = 687226
27-
n_test_sample = len(events)
29+
def test_exposure_map(runs, events):
30+
data = pd.merge(runs, events, on=['run_id', 'night'])
31+
32+
timestamps = pd.to_datetime(data.timestamp).values
33+
34+
ra_pointing = data.right_ascension.values * u.hourangle
35+
dec_pointing = data.declination.values * u.deg
36+
pointing = SkyCoord(ra=ra_pointing, dec=dec_pointing)
2837

29-
live_time_crab = runs.ontime.sum() / 3600 # to hours
38+
mask, wcs = build_exposure_map(pointing, timestamps, fov=4.5 * u.deg)
39+
40+
assert mask.mask.any() # assert whether some region is masked
41+
42+
43+
44+
def test_exposure_time(runs, events):
45+
n_crab_sample = 687226
46+
n_test_sample = len(events)
3047

31-
expected_live_time_sample = n_test_sample / n_crab_sample * live_time_crab
48+
live_time_crab = runs.ontime.sum() / 3600 # to hours
3249

33-
timestamps = pd.to_datetime(events.timestamp).values
34-
exposure = estimate_exposure_time(timestamps).to('h').value
50+
expected_live_time_sample = n_test_sample / n_crab_sample * live_time_crab
3551

36-
assert expected_live_time_sample == pytest.approx(exposure, 0.05)
52+
timestamps = pd.to_datetime(events.timestamp).values
53+
exposure = estimate_exposure_time(timestamps).to('h').value
54+
assert expected_live_time_sample == pytest.approx(exposure, 0.05)

0 commit comments

Comments
 (0)