Skip to content
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

WIP: Add gain selection to r1 calibration #683

Closed
Closed
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
2fde044
fix bug when selecting a 2 gain 1 sample waveform
kosack Mar 16, 2018
1109b6c
start to clean up r1.py
kosack Mar 16, 2018
e91e722
add gain_channel_mask and waveform_full to R1CameraContainer
kosack Mar 16, 2018
fe82bae
perform gain selection at the R1 level
kosack Mar 16, 2018
a1002ed
add __init__ to GainSelector so NullGainSelector works
kosack Mar 16, 2018
5419edc
start to refactor ChargeExtractors to expect a single channel
kosack Mar 16, 2018
21a3e46
assume no channel in waveforms
kosack Mar 16, 2018
0a3b9e9
fix __init__ signature for GainSelector
kosack Mar 16, 2018
ea0b864
gain_channel_mask -> gain_channel
kosack Mar 16, 2018
a6c6292
start to refactor to expect gain-selected waveform
kosack Mar 16, 2018
d0484a1
gain_channel_mask -> gain_channel
kosack Mar 16, 2018
3d55e22
fix call to get_sum_array
kosack Mar 16, 2018
291c50e
ensure gain selection on a 1 sample telescope returns the correct shape
kosack Mar 16, 2018
fcd162f
change dl1.tel[x].cleaned -> waveform
kosack Mar 16, 2018
882ef6b
remove comment
kosack Mar 16, 2018
8668774
fixed tests for charge extractors
kosack Mar 17, 2018
70f154c
fix tests for dl0 and r1 to assume gain-selection
kosack Mar 17, 2018
0a9cad8
added a max_events option to simple_pipeline to help with testing
kosack Mar 17, 2018
5d13f15
fix bug where image came out with channel dimension
kosack Mar 17, 2018
aaadce0
update some examples
kosack Mar 17, 2018
fd45c4c
fix bug handling astri-like (1 sample) images
kosack Mar 17, 2018
e0a1eed
update more examples
kosack Mar 17, 2018
ccfe1f0
update theata_square example
kosack Mar 17, 2018
8323987
make sure gain_channel is propegated to dl1
kosack Mar 17, 2018
1ab783c
rename data -> event
kosack Mar 17, 2018
f454361
update some tests
kosack Mar 17, 2018
9c5ac5a
reformatting
kosack Mar 17, 2018
b3896fa
reformatting
kosack Mar 17, 2018
7e6c0c5
reformatting
kosack Mar 17, 2018
de85327
reformatting
kosack Mar 17, 2018
e7f5b64
make NullR1Calibrator use a SimpleGainSelector by default
kosack Mar 18, 2018
a026e91
small refactoring:
kosack Mar 18, 2018
44c7cab
switch to using CAPS for Enum values (following convention)
kosack Mar 18, 2018
27a9cd3
use IntEnum instead of Enum
kosack Mar 18, 2018
438c30e
Changes for Target classes
watsonjj Mar 18, 2018
62e18fb
Merge remote-tracking branch 'remotes/kosack/add_gain_selection_to_r1…
watsonjj Mar 18, 2018
e9c6815
Moved default gain_selector to CameraR1Calibrator
watsonjj Mar 18, 2018
06cac30
Merge pull request #2 from watsonjj/add_gain_selection_to_r1cal
kosack Mar 22, 2018
116890a
Revert "Add gain selection to r1cal"
kosack Mar 22, 2018
ff14efa
Merge pull request #3 from kosack/revert-2-add_gain_selection_to_r1cal
kosack Mar 22, 2018
83f1004
Recreate changes for add_gain_selection_to_r1cal
watsonjj Mar 22, 2018
99f2250
Merge branch 'master' of https://github.com/cta-observatory/ctapipe i…
kosack Mar 24, 2018
06f6fe7
Merge branch 'master' of https://github.com/cta-observatory/ctapipe i…
kosack Mar 26, 2018
a9063e2
Merge branch 'add_gain_selection_to_r1cal' into add_gain_selection_to…
kosack Mar 28, 2018
1d0b906
made calib_scale a config attribute and added more docs
kosack Apr 9, 2018
8befe1f
Merge branch 'master' of https://github.com/cta-observatory/ctapipe i…
kosack Apr 10, 2018
c45add2
Merge branch 'master' of https://github.com/cta-observatory/ctapipe i…
kosack Apr 11, 2018
66ab4d5
start to change to using un-calibrated gain switch threshold
kosack Apr 11, 2018
02986f2
Merge branch 'master' of https://github.com/cta-observatory/ctapipe i…
kosack Apr 12, 2018
5c99a58
Merge branch 'master' of https://github.com/cta-observatory/ctapipe i…
kosack Apr 13, 2018
893715d
for now, check for several column names in the threshold file (until …
kosack Apr 18, 2018
a61ade8
Merge branch 'master' of https://github.com/cta-observatory/ctapipe i…
kosack Apr 18, 2018
358c9bd
Merge branch 'master' of https://github.com/cta-observatory/ctapipe i…
kosack Apr 19, 2018
599520f
Merge branch 'add_gain_selection_to_r1cal' into add_gain_selection_to…
kosack Apr 19, 2018
0ae2e9b
abstractclassmethod -> abstractmethod
kosack Apr 19, 2018
6cca2de
Merge branch 'watsonjj-add_gain_selection_to_r1cal' into add_gain_sel…
kosack Apr 19, 2018
5544a16
switched order of constructor args to match Component
kosack Apr 19, 2018
8cdf822
Merge branch 'add_gain_selection_to_r1cal' into add_gain_selection_to…
kosack Apr 19, 2018
59a91f9
Merge pull request #4 from watsonjj/add_gain_selection_to_r1cal
kosack Apr 19, 2018
7e7e98d
Merge branch 'master' of https://github.com/cta-observatory/ctapipe i…
kosack May 3, 2018
facc9ac
Merge branch 'master' of https://github.com/cta-observatory/ctapipe i…
kosack May 3, 2018
8b16321
Merge branch 'add_gain_selection_to_r1cal' of https://github.com/kosa…
kosack May 3, 2018
a0f7582
Merge branch 'master' of https://github.com/cta-observatory/ctapipe i…
kosack May 4, 2018
00ff43f
Merge branch 'master' of https://github.com/cta-observatory/ctapipe i…
kosack Jun 26, 2018
d166f95
updated example
kosack Jun 26, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions ctapipe/calib/camera/calibrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ class CameraCalibrator(Component):
extractor_t0='ChargeExtractorFactory.t0',
window_width='ChargeExtractorFactory.window_width',
window_shift='ChargeExtractorFactory.window_shift',
sig_amp_cut_HG='ChargeExtractorFactory.sig_amp_cut_HG',
sig_amp_cut_LG='ChargeExtractorFactory.sig_amp_cut_LG',
peak_detection_threshold='ChargeExtractorFactory.peak_detection_threshold',
lwt='ChargeExtractorFactory.lwt',
clip_amplitude='CameraDL1Calibrator.clip_amplitude',
radius='CameraDL1Calibrator.radius',
Expand Down
15 changes: 11 additions & 4 deletions ctapipe/calib/camera/dl0.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class CameraDL0Reducer(Component):
will equal the r1 samples.
kwargs
"""

def __init__(self, config=None, tool=None, reductor=None, **kwargs):
super().__init__(config=config, parent=tool, **kwargs)
if reductor is None:
Expand Down Expand Up @@ -82,10 +83,16 @@ def reduce(self, event):
"""
tels = event.r1.tels_with_data
for telid in tels:
r1 = event.r1.tel[telid].waveform
if self.check_r1_exists(event, telid):
dl0_tel = event.dl0.tel[telid]
r1_tel = event.r1.tel[telid]
r1_waveform = r1_tel.waveform
if self._reductor is None:
event.dl0.tel[telid].waveform = r1
dl0_tel.waveform = r1_waveform
else:
reduction = self._reductor.reduce_waveforms(r1)
event.dl0.tel[telid].waveform = reduction
reduction = self._reductor.reduce_waveforms(r1_waveform)
dl0_tel.waveform = reduction

dl0_tel.gain_channel = r1_tel.gain_channel
dl0_tel.trigger_time = r1_tel.trigger_time
dl0_tel.trigger_type = r1_tel.trigger_type
12 changes: 7 additions & 5 deletions ctapipe/calib/camera/dl1.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,13 +194,14 @@ def calibrate(self, event):
for telid in event.dl0.tels_with_data:

if self.check_dl0_exists(event, telid):
dl0_tel = event.dl0.tel[telid]
waveforms = event.dl0.tel[telid].waveform
n_samples = waveforms.shape[2]
n_samples = waveforms.shape[1]
if n_samples == 1:
# To handle ASTRI and dst
corrected = waveforms[..., 0]
corrected = waveforms[:, 0]
window = np.ones(waveforms.shape)
peakpos = np.zeros(waveforms.shape[0:2])
peakpos = np.zeros(waveforms.shape[0:1])
cleaned = waveforms
else:
# Clean waveforms
Expand All @@ -215,7 +216,7 @@ def calibrate(self, event):
charge, peakpos, window = extract(cleaned)

# Apply integration correction
correction = self.get_correction(event, telid)[:, None]
correction = self.get_correction(event, telid)[0]
corrected = charge * correction

# Clip amplitude
Expand All @@ -227,4 +228,5 @@ def calibrate(self, event):
event.dl1.tel[telid].image = corrected
event.dl1.tel[telid].extracted_samples = window
event.dl1.tel[telid].peakpos = peakpos
event.dl1.tel[telid].cleaned = cleaned
event.dl1.tel[telid].waveform = cleaned
event.dl1.tel[telid].gain_channel = dl0_tel.gain_channel
10 changes: 6 additions & 4 deletions ctapipe/calib/camera/gainselection.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ def pick_gain_channel(waveforms, threshold, select_by_sample=False):

# if we have 2 channels:
if waveforms.shape[0] == 2:
waveforms = np.squeeze(waveforms)
new_waveforms = waveforms[0].copy()

if select_by_sample:
Expand All @@ -51,12 +50,12 @@ def pick_gain_channel(waveforms, threshold, select_by_sample=False):
new_waveforms[gain_mask] = waveforms[1][gain_mask]

elif waveforms.shape[0] == 1:
new_waveforms = np.squeeze(waveforms)
new_waveforms = np.squeeze(waveforms, axis=0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use np.squeeze instead of waveforms[0]?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure - that was just copied from Tino's code.. I will change it to waveforms[0] since it makes more sense

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just out of curiosity, I see multiple mentions of something called 'Tinos Code'. Is this something one should know about?...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just out of curiosity, I see multiple mentions of something called 'Tinos Code'. Is this something one should know about?...

Sorry that may be confusing: Tino (@tino-michael) is the one who created the full pipeline based on ctapipe that has been used so far to make sensitivity curves. To get it working, he had to create a few extra classes and scripts that are in his github repo, and they are not yet merged into ctapipe (this PR is the first step to doing that, but there will be several others once this is working). It turned out to be not as simple as just copying his code in, since we wanted to move things around a bit, like having the gain selection in R1, rather than in Dl1 as it is in his code, hence some refactoring.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I used squeeze and not just the index because at some point there was an inconsistency somewhere with the time dimension. sometimes the whole dimension was gone, sometimes it was there but with length 1...
as in the different behaviour of a = [1, 2, 3] and b = [[1, 2, 3]]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that problem with missing time dimensions, etc, has been fixed now in the new EventSource classes and the calibration code... at least I haven't seen it anymore with any of the test data I used.

gain_mask = np.zeros_like(new_waveforms).astype(bool)

else:
raise ValueError("input waveforms has shape %s. not sure what to do "
"with that.", waveforms.shape)
raise ValueError("input waveforms has shape {}. not sure what to do "
"with that.".format(waveforms.shape))

return new_waveforms, gain_mask

Expand All @@ -67,6 +66,9 @@ class GainSelector(Component, metaclass=ABCMeta):
single waveform.
"""

def __init__(self, config=None, parent=None, **kwargs):
super().__init__(config=config, parent=parent, **kwargs)

@abstractclassmethod
def select_gains(self, cam_id, multi_gain_waveform):
"""
Expand Down
40 changes: 27 additions & 13 deletions ctapipe/calib/camera/r1.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""
from abc import abstractmethod

from .gainselection import ThresholdGainSelector
from ...core import Component, Factory
from ...core.traits import Unicode
from ...io import EventSource
Expand Down Expand Up @@ -158,23 +159,31 @@ class HESSIOR1Calibrator(CameraR1Calibrator):
Tool executable that is calling this component.
Passes the correct logger to the component.
Set to None if no Tool to pass.

kwargs
"""

# CALIB_SCALE is only relevant for MC calibration.
#
# CALIB_SCALE is the factor needed to transform from mean p.e. units to
# units of the single-p.e. peak: Depends on the collection efficiency,
# the asymmetry of the single p.e. amplitude distribution and the
# electronic noise added to the signals. Default value is for GCT.
#
# To correctly calibrate to number of photoelectron, a fresh SPE calibration
# should be applied using a SPE sim_telarray run with an
# artificial light source.
#
# TODO: Handle calib_scale differently per simlated telescope

calib_scale = 1.05
"""
CALIB_SCALE is only relevant for MC calibration.

CALIB_SCALE is the factor needed to transform from mean p.e. units to
units of the single-p.e. peak: Depends on the collection efficiency,
the asymmetry of the single p.e. amplitude distribution and the
electronic noise added to the signals. Default value is for GCT.
def __init__(self, config=None, tool=None, gain_selector=None, **kwargs):
super().__init__(config=config, tool=tool, **kwargs)

To correctly calibrate to number of photoelectron, a fresh SPE calibration
should be applied using a SPE sim_telarray run with an
artificial light source.
"""
# TODO: Handle calib_scale differently per simlated telescope
self.gain_selector = gain_selector
if self.gain_selector is None:
self.gain_selector = ThresholdGainSelector(config, tool)

def calibrate(self, event):
if event.meta['origin'] != 'hessio':
Expand All @@ -188,11 +197,16 @@ def calibrate(self, event):
ped = event.mc.tel[telid].pedestal / n_samples
gain = event.mc.tel[telid].dc_to_pe * self.calib_scale
calibrated = (samples - ped[..., None]) * gain[..., None]
event.r1.tel[telid].waveform = calibrated

cam_id = event.inst.subarray.tel[telid].camera.cam_id
waveform, mask = self.gain_selector.select_gains(cam_id,
calibrated)
event.r1.tel[telid].waveform_full = calibrated
event.r1.tel[telid].waveform = waveform
event.r1.tel[telid].gain_channel = mask

class TargetIOR1Calibrator(CameraR1Calibrator):

class TargetIOR1Calibrator(CameraR1Calibrator):
pedestal_path = Unicode(
'',
allow_none=True,
Expand Down
6 changes: 4 additions & 2 deletions ctapipe/calib/camera/tests/test_calibrator.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from copy import deepcopy

from numpy.testing import assert_allclose

from ctapipe.calib.camera import (
CameraCalibrator,
HESSIOR1Calibrator,
Expand All @@ -11,14 +13,14 @@


def test_camera_calibrator(test_event):
event = deepcopy(test_event) # so we don't modify the test event
event = deepcopy(test_event) # so we don't modify the test event
telid = 11

calibrator = CameraCalibrator(r1_product="HESSIOR1Calibrator")

calibrator.calibrate(event)
image = event.dl1.tel[telid].image
assert_allclose(image[0, 0], -2.216, 1e-3)
assert_allclose(image[0], -2.216, 1e-3)


def test_manual_r1():
Expand Down
2 changes: 1 addition & 1 deletion ctapipe/calib/camera/tests/test_dl0.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def test_camera_dl0_reducer(test_event):
reducer = CameraDL0Reducer()
reducer.reduce(event)
waveforms = event.dl0.tel[telid].waveform
assert_almost_equal(waveforms[0, 0, 0], -0.091, 3)
assert_almost_equal(waveforms[0, 0], -0.091, 3)


def test_check_r1_exists(test_event):
Expand Down
12 changes: 9 additions & 3 deletions ctapipe/calib/camera/tests/test_dl1.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,20 @@ def test_camera_dl1_calibrator(test_event):

calibrator.calibrate(event)
image = event.dl1.tel[telid].image
assert_allclose(image[0, 0], -2.216, 1e-3)
waveform = event.dl1.tel[telid].waveform
assert_allclose(image[0], -2.216, 1e-3)
assert image.ndim == 1
assert len(image) > 1 # make sure image has more than one pixel
assert waveform.ndim == 2
assert event.dl1.tel[telid].gain_channel.shape == waveform.shape
assert event.dl1.tel[telid]


def test_check_dl0_exists(test_event):
event = deepcopy(test_event)
telid = 11
previous_calibration(event)
calibrator = CameraDL1Calibrator()
assert(calibrator.check_dl0_exists(event, telid) is True)
assert calibrator.check_dl0_exists(event, telid) is True
event.dl0.tel[telid].waveform = None
assert(calibrator.check_dl0_exists(event, telid) is False)
assert calibrator.check_dl0_exists(event, telid) is False
22 changes: 17 additions & 5 deletions ctapipe/calib/camera/tests/test_gainselection.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import numpy as np
import pytest

from ctapipe.calib.camera.gainselection import ThresholdGainSelector
from ctapipe.calib.camera.gainselection import SimpleGainSelector
from ctapipe.calib.camera.gainselection import ThresholdGainSelector
from ctapipe.calib.camera.gainselection import pick_gain_channel


Expand Down Expand Up @@ -97,11 +97,23 @@ def test_threshold_gain_selector():
with pytest.raises(ValueError):
selector.select_gains("NectarCam", np.ones((3, 1000, 30)))

# 1-gain channel input:
wf0 = np.ones((1, 1000, 1))
# 1-gain channel input with many samples:
wf0 = np.ones((1, 1000, 30))
wf1, gm = selector.select_gains("ASTRICam", wf0)
assert wf1.shape == (1000, 30)
assert gm.shape == (1000, 30)

# 2-gain channel input with no samples:
wf0 = np.random.uniform(10, size=(2, 2368, 1))
wf1, gm = selector.select_gains("ASTRICam", wf0)
assert wf1.shape == (2368, 1)
assert gm.shape == (2368, 1)

# 1-gain channel input with no samples:
wf0 = np.random.uniform(10, size=(1, 2368, 1))
wf1, gm = selector.select_gains("ASTRICam", wf0)
assert wf1.shape == (1000,)
assert gm.shape == (1000,)
assert wf1.shape == (2368, 1)
assert gm.shape == (2368, 1)


def test_simple_gain_selector():
Expand Down
2 changes: 1 addition & 1 deletion ctapipe/calib/camera/tests/test_r1.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def test_hessio_r1_calibrator(test_event):
calibrator = HESSIOR1Calibrator()
calibrator.calibrate(event)
r1 = event.r1.tel[telid].waveform
assert_almost_equal(r1[0, 0, 0], -0.091, 3)
assert_almost_equal(r1[0, 0], -0.091, 3)


def test_null_r1_calibrator(test_event):
Expand Down
Loading