From 5403cbe9a7eae8b8eb253cf530de16c292763e2d Mon Sep 17 00:00:00 2001 From: watsonjj Date: Thu, 21 Mar 2019 20:28:45 +0100 Subject: [PATCH 01/13] Remove dependency on example_event (from ctapipe_extra) --- ctapipe/image/tests/test_charge_extraction.py | 112 +++++++----------- 1 file changed, 45 insertions(+), 67 deletions(-) diff --git a/ctapipe/image/tests/test_charge_extraction.py b/ctapipe/image/tests/test_charge_extraction.py index 10eaaad9a1f..6d02beaa9ee 100644 --- a/ctapipe/image/tests/test_charge_extraction.py +++ b/ctapipe/image/tests/test_charge_extraction.py @@ -1,6 +1,8 @@ +import pytest import numpy as np +from scipy.stats import norm from numpy.testing import assert_almost_equal - +from ctapipe.instrument import CameraGeometry from ctapipe.image.charge_extractors import ( ChargeExtractor, FullIntegrator, @@ -12,95 +14,71 @@ ) -def test_full_integration(example_event): - telid = list(example_event.r0.tel)[0] - data = example_event.r0.tel[telid].waveform - nsamples = data.shape[2] - ped = example_event.mc.tel[telid].pedestal - data_ped = data - np.atleast_3d(ped / nsamples) - data_ped = np.array([data_ped[0], data_ped[0]]) # Test LG functionality +@pytest.fixture(scope='module') +def camera_waveforms(): + camera = CameraGeometry.from_name("CHEC") - integrator = FullIntegrator() - integration, peakpos, window = integrator.extract_charge(data_ped) + n_pixels = camera.n_pixels + n_samples = 96 + pulse_sigma = 6 + r = np.random.RandomState(1) + r.uniform(0, 10, 5) + x = np.arange(n_samples) + y = norm.pdf(x, r.uniform(n_samples // 2 - 10, n_samples // 2 + 10, n_pixels)[:, None], pulse_sigma) + y *= r.uniform(100, 1000, n_pixels)[:, None] -def test_simple_integration(example_event): - telid = list(example_event.r0.tel)[0] + # 2 Channels + y = np.stack([y, y * r.uniform(0, 0.5, n_pixels)[:, None]]) - data = example_event.r0.tel[telid].waveform - nsamples = data.shape[2] - ped = example_event.mc.tel[telid].pedestal - data_ped = data - np.atleast_3d(ped / nsamples) - data_ped = np.array([data_ped[0], data_ped[0]]) # Test LG functionality + return y, camera - integrator = SimpleIntegrator() - integration, peakpos, window = integrator.extract_charge(data_ped) +def test_full_integration(camera_waveforms): + waveforms, camera = camera_waveforms + integrator = FullIntegrator() + integration, peakpos, window = integrator.extract_charge(waveforms) -def test_global_peak_integration(example_event): - telid = list(example_event.r0.tel)[0] - data = example_event.r0.tel[telid].waveform - nsamples = data.shape[2] - ped = example_event.mc.tel[telid].pedestal - data_ped = data - np.atleast_3d(ped / nsamples) - data_ped = np.array([data_ped[0], data_ped[0]]) # Test LG functionality - integrator = GlobalPeakIntegrator() - integration, peakpos, window = integrator.extract_charge(data_ped) +def test_simple_integration(camera_waveforms): + waveforms, camera = camera_waveforms + integrator = SimpleIntegrator() + integration, peakpos, window = integrator.extract_charge(waveforms) -def test_local_peak_integration(example_event): - telid = list(example_event.r0.tel)[0] - data = example_event.r0.tel[telid].waveform - nsamples = data.shape[2] - ped = example_event.mc.tel[telid].pedestal - data_ped = data - np.atleast_3d(ped / nsamples) - data_ped = np.array([data_ped[0], data_ped[0]]) # Test LG functionality +def test_global_peak_integration(camera_waveforms): + waveforms, camera = camera_waveforms + integrator = GlobalPeakIntegrator() + integration, peakpos, window = integrator.extract_charge(waveforms) - integrator = LocalPeakIntegrator() - integration, peakpos, window = integrator.extract_charge(data_ped) +def test_local_peak_integration(camera_waveforms): + waveforms, camera = camera_waveforms + integrator = LocalPeakIntegrator() + integration, peakpos, window = integrator.extract_charge(waveforms) -def test_nb_peak_integration(example_event): - telid = list(example_event.r0.tel)[0] - data = example_event.r0.tel[telid].waveform - nsamples = data.shape[2] - ped = example_event.mc.tel[telid].pedestal - data_ped = data - np.atleast_3d(ped / nsamples) - data_ped = np.array([data_ped[0], data_ped[0]]) # Test LG functionality - geom = example_event.inst.subarray.tel[telid].camera - nei = geom.neighbor_matrix_where +def test_nb_peak_integration(camera_waveforms): + waveforms, camera = camera_waveforms + nei = camera.neighbor_matrix_where integrator = NeighbourPeakIntegrator() integrator.neighbours = nei - integration, peakpos, window = integrator.extract_charge(data_ped) + integration, peakpos, window = integrator.extract_charge(waveforms) -def test_averagewf_peak_integration(example_event): - telid = list(example_event.r0.tel)[0] - data = example_event.r0.tel[telid].waveform - nsamples = data.shape[2] - ped = example_event.mc.tel[telid].pedestal - data_ped = data - np.atleast_3d(ped / nsamples) - data_ped = np.array([data_ped[0], data_ped[0]]) # Test LG functionality - +def test_averagewf_peak_integration(camera_waveforms): + waveforms, camera = camera_waveforms integrator = AverageWfPeakIntegrator() - integration, peakpos, window = integrator.extract_charge(data_ped) + integration, peakpos, window = integrator.extract_charge(waveforms) - assert_almost_equal(integration[0][0], 73, 0) - assert_almost_equal(integration[1][0], 73, 0) + assert_almost_equal(integration[0][0], 51.647, 3) + assert_almost_equal(integration[1][0], 1.194, 3) -def test_charge_extractor_factory(example_event): +def test_charge_extractor_factory(camera_waveforms): + waveforms, camera = camera_waveforms extractor = ChargeExtractor.from_name('LocalPeakIntegrator') - - telid = list(example_event.r0.tel)[0] - data = example_event.r0.tel[telid].waveform - nsamples = data.shape[2] - ped = example_event.mc.tel[telid].pedestal - data_ped = data - np.atleast_3d(ped / nsamples) - - extractor.extract_charge(data_ped) + extractor.extract_charge(waveforms) def test_charge_extractor_factory_args(): From 89bfbf47d017d3e3082540fa0cf18064b63ee529 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Thu, 21 Mar 2019 22:41:18 +0100 Subject: [PATCH 02/13] Add checks for values to ensure behaviour is kept for upcoming changes --- ctapipe/image/tests/test_charge_extraction.py | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/ctapipe/image/tests/test_charge_extraction.py b/ctapipe/image/tests/test_charge_extraction.py index 6d02beaa9ee..8ef90df475d 100644 --- a/ctapipe/image/tests/test_charge_extraction.py +++ b/ctapipe/image/tests/test_charge_extraction.py @@ -1,7 +1,7 @@ import pytest import numpy as np from scipy.stats import norm -from numpy.testing import assert_almost_equal +from numpy.testing import assert_allclose from ctapipe.instrument import CameraGeometry from ctapipe.image.charge_extractors import ( ChargeExtractor, @@ -39,24 +39,36 @@ def test_full_integration(camera_waveforms): integrator = FullIntegrator() integration, peakpos, window = integrator.extract_charge(waveforms) + assert_allclose(integration[0][0], 267.843, rtol=1e-3) + assert_allclose(integration[1][0], 6.194, rtol=1e-3) + def test_simple_integration(camera_waveforms): waveforms, camera = camera_waveforms integrator = SimpleIntegrator() integration, peakpos, window = integrator.extract_charge(waveforms) + assert_allclose(integration[0][0], 3.524e-06, rtol=1e-3) + assert_allclose(integration[1][0], 8.15e-08, rtol=1e-3) + def test_global_peak_integration(camera_waveforms): waveforms, camera = camera_waveforms integrator = GlobalPeakIntegrator() integration, peakpos, window = integrator.extract_charge(waveforms) + assert_allclose(integration[0][0], 51.647, rtol=1e-3) + assert_allclose(integration[1][0], 1.194, rtol=1e-3) + def test_local_peak_integration(camera_waveforms): waveforms, camera = camera_waveforms integrator = LocalPeakIntegrator() integration, peakpos, window = integrator.extract_charge(waveforms) + assert_allclose(integration[0][0], 118.027, rtol=1e-3) + assert_allclose(integration[1][0], 2.729, rtol=1e-3) + def test_nb_peak_integration(camera_waveforms): waveforms, camera = camera_waveforms @@ -65,14 +77,17 @@ def test_nb_peak_integration(camera_waveforms): integrator.neighbours = nei integration, peakpos, window = integrator.extract_charge(waveforms) + assert_allclose(integration[0][0], 95.3, rtol=1e-3) + assert_allclose(integration[1][0], 0.2237, rtol=1e-3) + def test_averagewf_peak_integration(camera_waveforms): waveforms, camera = camera_waveforms integrator = AverageWfPeakIntegrator() integration, peakpos, window = integrator.extract_charge(waveforms) - assert_almost_equal(integration[0][0], 51.647, 3) - assert_almost_equal(integration[1][0], 1.194, 3) + assert_allclose(integration[0][0], 51.647, rtol=1e-3) + assert_allclose(integration[1][0], 1.194, rtol=1e-3) def test_charge_extractor_factory(camera_waveforms): From 3834bc7ee0a646b35ed74be81ba0c9b02412f41c Mon Sep 17 00:00:00 2001 From: watsonjj Date: Fri, 22 Mar 2019 11:11:17 +0100 Subject: [PATCH 03/13] Make hi and lo gain channels contain truly different waveforms for the tests --- ctapipe/image/tests/test_charge_extraction.py | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/ctapipe/image/tests/test_charge_extraction.py b/ctapipe/image/tests/test_charge_extraction.py index 8ef90df475d..86c900380a9 100644 --- a/ctapipe/image/tests/test_charge_extraction.py +++ b/ctapipe/image/tests/test_charge_extraction.py @@ -20,16 +20,26 @@ def camera_waveforms(): n_pixels = camera.n_pixels n_samples = 96 + mid = n_samples // 2 pulse_sigma = 6 - r = np.random.RandomState(1) - r.uniform(0, 10, 5) + r_hi = np.random.RandomState(1) + r_lo = np.random.RandomState(2) x = np.arange(n_samples) - y = norm.pdf(x, r.uniform(n_samples // 2 - 10, n_samples // 2 + 10, n_pixels)[:, None], pulse_sigma) - y *= r.uniform(100, 1000, n_pixels)[:, None] - # 2 Channels - y = np.stack([y, y * r.uniform(0, 0.5, n_pixels)[:, None]]) + # Randomize times + t_pulse_hi = r_hi.uniform(mid - 10, mid + 10, n_pixels)[:, None] + t_pulse_lo = r_lo.uniform(mid - 10, mid + 10, n_pixels)[:, None] + + # Create pulses + y_hi = norm.pdf(x, t_pulse_hi, pulse_sigma) + y_lo = norm.pdf(x, t_pulse_lo, pulse_sigma) + + # Randomize amplitudes + y_hi *= r_hi.uniform(100, 1000, n_pixels)[:, None] + y_lo *= r_lo.uniform(100, 1000, n_pixels)[:, None] + + y = np.stack([y_hi, y_lo]) return y, camera @@ -39,8 +49,8 @@ def test_full_integration(camera_waveforms): integrator = FullIntegrator() integration, peakpos, window = integrator.extract_charge(waveforms) - assert_allclose(integration[0][0], 267.843, rtol=1e-3) - assert_allclose(integration[1][0], 6.194, rtol=1e-3) + assert_allclose(integration[0][0], 545.945, rtol=1e-3) + assert_allclose(integration[1][0], 970.025, rtol=1e-3) def test_simple_integration(camera_waveforms): @@ -48,8 +58,8 @@ def test_simple_integration(camera_waveforms): integrator = SimpleIntegrator() integration, peakpos, window = integrator.extract_charge(waveforms) - assert_allclose(integration[0][0], 3.524e-06, rtol=1e-3) - assert_allclose(integration[1][0], 8.15e-08, rtol=1e-3) + assert_allclose(integration[0][0], 8.125e-09, rtol=1e-3) + assert_allclose(integration[1][0], 9.372e-09, rtol=1e-3) def test_global_peak_integration(camera_waveforms): @@ -57,8 +67,8 @@ def test_global_peak_integration(camera_waveforms): integrator = GlobalPeakIntegrator() integration, peakpos, window = integrator.extract_charge(waveforms) - assert_allclose(integration[0][0], 51.647, rtol=1e-3) - assert_allclose(integration[1][0], 1.194, rtol=1e-3) + assert_allclose(integration[0][0], 232.559, rtol=1e-3) + assert_allclose(integration[1][0], 418.967, rtol=1e-3) def test_local_peak_integration(camera_waveforms): @@ -66,8 +76,8 @@ def test_local_peak_integration(camera_waveforms): integrator = LocalPeakIntegrator() integration, peakpos, window = integrator.extract_charge(waveforms) - assert_allclose(integration[0][0], 118.027, rtol=1e-3) - assert_allclose(integration[1][0], 2.729, rtol=1e-3) + assert_allclose(integration[0][0], 240.3, rtol=1e-3) + assert_allclose(integration[1][0], 427.158, rtol=1e-3) def test_nb_peak_integration(camera_waveforms): @@ -77,8 +87,8 @@ def test_nb_peak_integration(camera_waveforms): integrator.neighbours = nei integration, peakpos, window = integrator.extract_charge(waveforms) - assert_allclose(integration[0][0], 95.3, rtol=1e-3) - assert_allclose(integration[1][0], 0.2237, rtol=1e-3) + assert_allclose(integration[0][0], 94.671, rtol=1e-3) + assert_allclose(integration[1][0], 400.856, rtol=1e-3) def test_averagewf_peak_integration(camera_waveforms): @@ -86,8 +96,8 @@ def test_averagewf_peak_integration(camera_waveforms): integrator = AverageWfPeakIntegrator() integration, peakpos, window = integrator.extract_charge(waveforms) - assert_allclose(integration[0][0], 51.647, rtol=1e-3) - assert_allclose(integration[1][0], 1.194, rtol=1e-3) + assert_allclose(integration[0][0], 232.559, rtol=1e-3) + assert_allclose(integration[1][0], 427.158, rtol=1e-3) def test_charge_extractor_factory(camera_waveforms): From d5d023acec5d01ed734be242d5f7e8956d06c9f7 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Fri, 22 Mar 2019 11:39:06 +0100 Subject: [PATCH 04/13] Move lo gain waveforms to a different part of the wf to really distinguish tests --- ctapipe/image/tests/test_charge_extraction.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ctapipe/image/tests/test_charge_extraction.py b/ctapipe/image/tests/test_charge_extraction.py index 86c900380a9..19a21708ff4 100644 --- a/ctapipe/image/tests/test_charge_extraction.py +++ b/ctapipe/image/tests/test_charge_extraction.py @@ -29,7 +29,7 @@ def camera_waveforms(): # Randomize times t_pulse_hi = r_hi.uniform(mid - 10, mid + 10, n_pixels)[:, None] - t_pulse_lo = r_lo.uniform(mid - 10, mid + 10, n_pixels)[:, None] + t_pulse_lo = r_lo.uniform(mid + 10, mid + 20, n_pixels)[:, None] # Create pulses y_hi = norm.pdf(x, t_pulse_hi, pulse_sigma) @@ -55,11 +55,11 @@ def test_full_integration(camera_waveforms): def test_simple_integration(camera_waveforms): waveforms, camera = camera_waveforms - integrator = SimpleIntegrator() + integrator = SimpleIntegrator(t0=48) integration, peakpos, window = integrator.extract_charge(waveforms) - assert_allclose(integration[0][0], 8.125e-09, rtol=1e-3) - assert_allclose(integration[1][0], 9.372e-09, rtol=1e-3) + assert_allclose(integration[0][0], 232.559, rtol=1e-3) + assert_allclose(integration[1][0], 32.539, rtol=1e-3) def test_global_peak_integration(camera_waveforms): @@ -68,7 +68,7 @@ def test_global_peak_integration(camera_waveforms): integration, peakpos, window = integrator.extract_charge(waveforms) assert_allclose(integration[0][0], 232.559, rtol=1e-3) - assert_allclose(integration[1][0], 418.967, rtol=1e-3) + assert_allclose(integration[1][0], 425.406, rtol=1e-3) def test_local_peak_integration(camera_waveforms): @@ -88,7 +88,7 @@ def test_nb_peak_integration(camera_waveforms): integration, peakpos, window = integrator.extract_charge(waveforms) assert_allclose(integration[0][0], 94.671, rtol=1e-3) - assert_allclose(integration[1][0], 400.856, rtol=1e-3) + assert_allclose(integration[1][0], 426.887, rtol=1e-3) def test_averagewf_peak_integration(camera_waveforms): @@ -97,7 +97,7 @@ def test_averagewf_peak_integration(camera_waveforms): integration, peakpos, window = integrator.extract_charge(waveforms) assert_allclose(integration[0][0], 232.559, rtol=1e-3) - assert_allclose(integration[1][0], 427.158, rtol=1e-3) + assert_allclose(integration[1][0], 425.406, rtol=1e-3) def test_charge_extractor_factory(camera_waveforms): From f56dec1cc366f528a865676d2e8d4d749661679e Mon Sep 17 00:00:00 2001 From: watsonjj Date: Fri, 22 Mar 2019 13:02:30 +0100 Subject: [PATCH 05/13] Simplify structure of the charge_extractor module --- ctapipe/image/charge_extractors.py | 736 +++++++---------------------- 1 file changed, 166 insertions(+), 570 deletions(-) diff --git a/ctapipe/image/charge_extractors.py b/ctapipe/image/charge_extractors.py index a40d0a35997..b475c8cddd0 100644 --- a/ctapipe/image/charge_extractors.py +++ b/ctapipe/image/charge_extractors.py @@ -2,18 +2,67 @@ Charge extraction algorithms to reduce the image to one value per pixel """ -__all__ = ['ChargeExtractor', 'FullIntegrator', 'SimpleIntegrator', - 'GlobalPeakIntegrator', 'LocalPeakIntegrator', - 'NeighbourPeakIntegrator', 'AverageWfPeakIntegrator'] +__all__ = [ + 'ChargeExtractor', + 'FullIntegrator', + 'SimpleIntegrator', + 'GlobalPeakIntegrator', + 'LocalPeakIntegrator', + 'NeighbourPeakIntegrator', + 'AverageWfPeakIntegrator', +] from abc import abstractmethod import numpy as np -from traitlets import Int, Float +from traitlets import Int from ctapipe.core import Component from ctapipe.utils.neighbour_sum import get_sum_array +def extract_charge_from_peakpos_array(waveforms, peakpos, width, shift): + """ + Build the numpy array of bools defining the integration window. + + Parameters + ---------- + waveforms : ndarray + Waveforms stored in a numpy array. + Shape: (n_chan, n_pix, n_samples) + peakpos : ndarray + Numpy array of the peak position for each pixel. + Shape: (n_chan, n_pix) + width : ndarray or int + Window size of integration window. + Shape (if numpy array): (n_chan, n_pix) + shift : ndarray or int + Window size of integration window. + Shape (if numpy array): (n_chan, n_pix) + + Returns + ------- + charge : ndarray + Extracted charge. + Shape: (n_chan, n_pix) + + """ + # Ensure window is within waveform + # width[width > n_samples] = n_samples + # start[start < 0] = 0 + # sum_check = start + width > n_samples + # start[sum_check] = n_samples - width[sum_check] + + start = peakpos - shift + end = start + width + ind = np.indices(waveforms.shape)[2] + integration_window = (ind >= start[..., None]) & (ind < end[..., None]) + windowed = np.ma.array(waveforms, mask=~integration_window) + charge = windowed.sum(2).data + + # TODO: remove integration window return + return charge, integration_window + + class ChargeExtractor(Component): def __init__(self, config=None, parent=None, **kwargs): @@ -71,25 +120,6 @@ def check_neighbour_set(self): self.log.exception("neighbours attribute must be set") raise ValueError() - @abstractmethod - def get_peakpos(self, waveforms): - """ - Get the peak position from the waveforms - - Parameters - ---------- - waveforms : ndarray - Waveforms stored in a numpy array of shape - (n_chan, n_pix, n_samples). - - Returns - ------- - peakpos : ndarray - Numpy array of the peak position for each pixel. - Has shape of (n_chan, n_pix). - - """ - @abstractmethod def extract_charge(self, waveforms): """ @@ -105,589 +135,155 @@ def extract_charge(self, waveforms): Returns ------- charge : ndarray - Extracted charge stored in a numpy array of shape (n_chan, n_pix). + Extracted charge. + Shape: (n_chan, n_pix) + peakpos : ndarray + Position of the peak found in each pixel. + Shape: (n_chan, n_pix) window : ndarray Bool numpy array defining the samples included in the integration window. + Shape: (n_chan, n_pix, n_samples) """ -class Integrator(ChargeExtractor): - - def __init__(self, config=None, parent=None, **kwargs): - """ - Base component for charge extractors that perform integration. - - Attributes - ---------- - neighbours : list - List of neighbours for each pixel. Changes per telescope. - - Parameters - ---------- - config : traitlets.loader.Config - Configuration specified by config file or cmdline arguments. - Used to set traitlet values. - Set to None if no configuration to pass. - tool : ctapipe.core.Tool - Tool executable that is calling this component. - Passes the correct logger to the component. - Set to None if no Tool to pass. - kwargs - """ - super().__init__(config=config, parent=parent, **kwargs) - - @staticmethod - def check_window_width_and_start(n_samples, start, width): - """ - Check that the combination of window width and start positions fit - within the readout window. - - Parameters - ---------- - n_samples : int - Number of samples in the waveform - start : ndarray - Numpy array containing the window start for each pixel. Shape = - (n_chan, n_pix) - width : ndarray - Numpy array containing the window width for each pixel. Shape = - (n_chan, n_pix) - - """ - width[width > n_samples] = n_samples - start[start < 0] = 0 - sum_check = start + width > n_samples - start[sum_check] = n_samples - width[sum_check] - - @abstractmethod - def get_peakpos(self, waveforms): - """""" - - @abstractmethod - def _get_window_start(self, waveforms, peakpos): - """ - Get the starting point for the integration window - - Parameters - ---------- - waveforms : ndarray - Waveforms stored in a numpy array of shape - (n_chan, n_pix, n_samples). - peakpos : ndarray - Numpy array containing the peak position for each pixel. - Shape = (n_chan, n_pix) - - Returns - ------- - start : ndarray - Numpy array containing the window start for each pixel. - Shape = (n_chan, n_pix) - - """ - - @abstractmethod - def _get_window_width(self, waveforms): - """ - Get the width of the integration window - - Parameters - ---------- - waveforms : ndarray - Waveforms stored in a numpy array of shape - (n_chan, n_pix, n_samples). - - Returns - ------- - width : ndarray - Numpy array containing the window width for each pixel. - Shape = (n_chan, n_pix) - - """ - - def get_start_and_width(self, waveforms, peakpos): - """ - Obtain the numpy arrays containing the start and width for the - integration window for each pixel. - - Parameters - ---------- - waveforms : ndarray - Waveforms stored in a numpy array of shape - (n_chan, n_pix, n_samples). - peakpos : ndarray - Numpy array of the peak position for each pixel. - Has shape of (n_chan, n_pix). - - Returns - ------- - w_start : ndarray - Numpy array containing the Start sample of integration window. - Shape: (n_chan, n_pix). - w_width : ndarray - Numpy array containing the window size of integration window. - Shape (n_chan, n_pix). - """ - w_start = self._get_window_start(waveforms, peakpos) - w_width = self._get_window_width(waveforms) - n_samples = waveforms.shape[2] - self.check_window_width_and_start(n_samples, w_start, w_width) - return w_start, w_width - - @staticmethod - def get_window(waveforms, start, width): - """ - Build the a numpy array of bools defining the integration window. - - Parameters - ---------- - waveforms : ndarray - Waveforms stored in a numpy array of shape - (n_chan, n_pix, n_samples). - start : ndarray - Numpy array containing the Start sample of integration window. - Shape: (n_chan, n_pix). - width : ndarray - Numpy array containing the window size of integration window. - Shape (n_chan, n_pix). - - Returns - ------- - integration_window : ndarray - Numpy array containing True where the samples lay within the - integration window, and False where the samples lay outside. Has - shape of (n_chan, n_pix, n_samples). - - """ - end = start + width - - # Obtain integration window using the indices of the waveforms array - ind = np.indices(waveforms.shape)[2] - integration_window = (ind >= start[..., None]) & (ind < end[..., None]) - return integration_window - - @staticmethod - def extract_from_window(waveforms, window): - """ - Extract the charge but applying an intregration window to the - waveforms. - - Parameters - ---------- - waveforms : ndarray - Waveforms stored in a numpy array of shape - (n_chan, n_pix, n_samples). - window : ndarray - Numpy array containing True where the samples lay within the - integration window, and False where the samples lay outside. Has - shape of (n_chan, n_pix, n_samples). - - Returns - ------- - charge : ndarray - Extracted charge stored in a numpy array of shape (n_chan, n_pix). - """ - windowed = np.ma.array(waveforms, mask=~window) - charge = windowed.sum(2).data - return charge - - def get_window_from_waveforms(self, waveforms): - """ - Consolidating function to obtain the window and peakpos given - a waveform. - - Parameters - ---------- - waveforms : ndarray - Waveforms stored in a numpy array of shape - (n_chan, n_pix, n_samples). - - Returns - ------- - window : ndarray - Numpy array containing True where the samples lay within the - integration window, and False where the samples lay outside. Has - shape of (n_chan, n_pix, n_samples). - peakpos : ndarray - Numpy array of the peak position for each pixel. - Has shape of (n_chan, n_pix). - - """ - peakpos = self.get_peakpos(waveforms) - start, width = self.get_start_and_width(waveforms, peakpos) - window = self.get_window(waveforms, start, width) - return window, peakpos +class FullIntegrator(ChargeExtractor): + """ + Charge extractor that integrates the entire waveform. + """ def extract_charge(self, waveforms): - window, peakpos = self.get_window_from_waveforms(waveforms) - charge = self.extract_from_window(waveforms, window) - return charge, peakpos, window - + # TODO: remove integration window return + peakpos = np.zeros(waveforms.shape[:2], dtype=np.intp) + window = np.ones(waveforms.shape, dtype=np.bool) + return waveforms.sum(2), peakpos, window -class FullIntegrator(Integrator): - def __init__(self, config=None, parent=None, **kwargs): - """ - Charge extractor that integrates the entire waveform. +class SimpleIntegrator(ChargeExtractor): + window_start = Int( + 0, help='Define the start position for the integration window' + ).tag(config=True) + window_width = Int( + 7, help='Define the width of the integration window' + ).tag(config=True) - Attributes - ---------- - neighbours : list - List of neighbours for each pixel. Changes per telescope. - - Parameters - ---------- - config : traitlets.loader.Config - Configuration specified by config file or cmdline arguments. - Used to set traitlet values. - Set to None if no configuration to pass. - tool : ctapipe.core.Tool - Tool executable that is calling this component. - Passes the correct logger to the component. - Set to None if no Tool to pass. - kwargs - """ - super().__init__(config=config, parent=parent, **kwargs) - - def _get_window_start(self, waveforms, peakpos): - nchan, npix, nsamples = waveforms.shape - return np.zeros((nchan, npix), dtype=np.intp) - - def _get_window_width(self, waveforms): - nchan, npix, nsamples = waveforms.shape - return np.full((nchan, npix), nsamples, dtype=np.intp) - - def get_peakpos(self, waveforms): - nchan, npix, nsamples = waveforms.shape - return np.zeros((nchan, npix), dtype=np.intp) - - -class WindowIntegrator(Integrator): - window_shift = Int(3, help='Define the shift of the integration window ' - 'from the peakpos ' - '(peakpos - shift)').tag(config=True) - window_width = Int(7, help='Define the width of the integration ' - 'window').tag(config=True) - - def __init__(self, config=None, parent=None, **kwargs): - """ - Base component for charge extractors that perform integration within - a window. - - Attributes - ---------- - neighbours : list - List of neighbours for each pixel. Changes per telescope. - - Parameters - ---------- - config : traitlets.loader.Config - Configuration specified by config file or cmdline arguments. - Used to set traitlet values. - Set to None if no configuration to pass. - tool : ctapipe.core.Tool - Tool executable that is calling this component. - Passes the correct logger to the component. - Set to None if no Tool to pass. - kwargs - """ - super().__init__(config=config, parent=parent, **kwargs) - - def get_peakpos(self, waveforms): - return self._obtain_peak_position(waveforms) - - def _get_window_start(self, waveforms, peakpos): - return peakpos - self.window_shift - - def _get_window_width(self, waveforms): - nchan, npix, nsamples = waveforms.shape - return np.full((nchan, npix), self.window_width, dtype=np.intp) - - @abstractmethod - def _obtain_peak_position(self, waveforms): - """ - Find the peak to define integration window around. - - Parameters - ---------- - waveforms : ndarray - Waveforms stored in a numpy array of shape - (n_chan, n_pix, n_samples). - - Returns - ------- - peakpos : ndarray - Numpy array of the peak position for each pixel. Has shape of - (n_chan, n_pix). - - """ - - -class SimpleIntegrator(WindowIntegrator): - t0 = Int(0, help='Define the peak position for all ' - 'pixels').tag(config=True) - - def __init__(self, config=None, parent=None, **kwargs): - """ - Charge extractor that integrates within a window defined by the user. - - Attributes - ---------- - neighbours : list - List of neighbours for each pixel. Changes per telescope. - - Parameters - ---------- - config : traitlets.loader.Config - Configuration specified by config file or cmdline arguments. - Used to set traitlet values. - Set to None if no configuration to pass. - tool : ctapipe.core.Tool - Tool executable that is calling this component. - Passes the correct logger to the component. - Set to None if no Tool to pass. - kwargs - """ - super().__init__(config=config, parent=parent, **kwargs) - - def _obtain_peak_position(self, waveforms): - nchan, npix, nsamples = waveforms.shape - return np.full((nchan, npix), self.t0, dtype=np.intp) - - -class PeakFindingIntegrator(WindowIntegrator): - sig_amp_cut_HG = Float(None, allow_none=True, - help='Define the cut above which a sample is ' - 'considered as significant for PeakFinding ' - 'in the HG channel').tag(config=True) - sig_amp_cut_LG = Float(None, allow_none=True, - help='Define the cut above which a sample is ' - 'considered as significant for PeakFinding ' - 'in the LG channel').tag(config=True) - - def __init__(self, config=None, parent=None, **kwargs): - """ - Base component for charge extractors that perform integration within - a window defined around a peak position. - - Attributes - ---------- - neighbours : list - List of neighbours for each pixel. Changes per telescope. - - Parameters - ---------- - config : traitlets.loader.Config - Configuration specified by config file or cmdline arguments. - Used to set traitlet values. - Set to None if no configuration to pass. - tool : ctapipe.core.Tool - Tool executable that is calling this component. - Passes the correct logger to the component. - Set to None if no Tool to pass. - kwargs - """ - super().__init__(config=config, parent=parent, **kwargs) - self._sig_channel = None - self._sig_pixels = None - - # Extract significant entries - def _extract_significant_entries(self, waveforms): - """ - Obtain the samples that the user has specified as significant. - - Parameters - ---------- - waveforms : ndarray - Waveforms stored in a numpy array of shape - (n_chan, n_pix, n_samples). - - Returns - ------- - significant_samples : masked ndarray - Identical to waveforms, except the insignificant samples are - masked. - - """ - nchan, npix, nsamples = waveforms.shape - if self.sig_amp_cut_LG or self.sig_amp_cut_HG: - sig_entries = np.ones(waveforms.shape, dtype=bool) - if self.sig_amp_cut_HG: - sig_entries[0] = waveforms[0] > self.sig_amp_cut_HG - if nchan > 1 and self.sig_amp_cut_LG: - sig_entries[1] = waveforms[1] > self.sig_amp_cut_LG - self._sig_pixels = np.any(sig_entries, axis=2) - self._sig_channel = np.any(self._sig_pixels, axis=1) - if not self._sig_channel[0]: - self.log.error("sigamp excludes all values in HG channel") - return np.ma.array(waveforms, mask=~sig_entries) - else: - self._sig_channel = np.ones(nchan, dtype=bool) - self._sig_pixels = np.ones((nchan, npix), dtype=bool) - return waveforms - - @abstractmethod - def _obtain_peak_position(self, waveforms): - """""" + def extract_charge(self, waveforms): + start = self.window_start + end = self.window_start + self.window_width + # TODO: remove integration window return + peakpos = np.zeros(waveforms.shape[:2], dtype=np.intp) + window = np.ones(waveforms.shape, dtype=np.bool) + return waveforms[..., start:end].sum(2), peakpos, window -class GlobalPeakIntegrator(PeakFindingIntegrator): +class GlobalPeakIntegrator(ChargeExtractor): """ Charge extractor that defines an integration window about the global peak in the image. - - Attributes - ---------- - neighbours : list - List of neighbours for each pixel. Changes per telescope. - - Parameters - ---------- - config : traitlets.loader.Config - Configuration specified by config file or cmdline arguments. - Used to set traitlet values. - Set to None if no configuration to pass. - tool : ctapipe.core.Tool - Tool executable that is calling this component. - Passes the correct logger to the component. - Set to None if no Tool to pass. - kwargs """ + window_shift = Int( + 3, help='Define the shift of the integration window ' + 'from the peakpos (peakpos - shift)' + ).tag(config=True) + window_width = Int( + 7, help='Define the width of the integration window' + ).tag(config=True) - def __init__(self, config=None, parent=None, **kwargs): + def extract_charge(self, waveforms): + max_t = waveforms.argmax(2) + max_s = waveforms.max(2) + peakpos = np.round( + np.average(max_t, weights=max_s, axis=1) + ).astype(np.int) + start = peakpos - self.window_shift + end = start + self.window_width + charge = np.stack([ + waveforms[0, :, start[0]:end[0]].sum(1), # HI channel + waveforms[1, :, start[1]:end[1]].sum(1), # LO channel + ]) + + # TODO: remove integration window return + ind = np.indices(waveforms.shape)[2] + window = (ind >= start[..., None, None]) & (ind < end[..., None, None]) - super().__init__(config=config, parent=parent, **kwargs) + return charge, peakpos, window - def _obtain_peak_position(self, waveforms): - nchan, npix, nsamples = waveforms.shape - significant_samples = self._extract_significant_entries(waveforms) - max_t = significant_samples.argmax(2) - max_s = significant_samples.max(2) - - peakpos = np.zeros((nchan, npix), dtype=np.int) - peakpos[0, :] = np.round(np.average(max_t[0], weights=max_s[0])) - if nchan > 1: - if self._sig_channel[1]: - peakpos[1, :] = np.round( - np.average(max_t[1], weights=max_s[1])) - else: - self.log.info("LG not significant, using HG for peak finding " - "instead") - peakpos[1, :] = peakpos[0] - return peakpos - - -class LocalPeakIntegrator(PeakFindingIntegrator): - """ - Charge extractor that defines an integration window about the local - peak in each pixel. - - Attributes - ---------- - neighbours : list - List of neighbours for each pixel. Changes per telescope. - - Parameters - ---------- - config : traitlets.loader.Config - Configuration specified by config file or cmdline arguments. - Used to set traitlet values. - Set to None if no configuration to pass. - tool : ctapipe.core.Tool - Tool executable that is calling this component. - Passes the correct logger to the component. - Set to None if no Tool to pass. - kwargs - """ - def __init__(self, config=None, parent=None, **kwargs): - super().__init__(config=config, parent=parent, **kwargs) +class LocalPeakIntegrator(ChargeExtractor): + """ + Charge extractor that defines an integration window about the local + peak in each pixel. + """ + window_shift = Int( + 3, help='Define the shift of the integration window ' + 'from the peakpos (peakpos - shift)' + ).tag(config=True) + window_width = Int( + 7, help='Define the width of the integration window' + ).tag(config=True) - def _obtain_peak_position(self, waveforms): - nchan, npix, nsamples = waveforms.shape - significant_samples = self._extract_significant_entries(waveforms) - peakpos = np.full((nchan, npix), significant_samples.argmax(2), - dtype=np.int) - sig_pix = self._sig_pixels - if nchan > 1: # If the LG is not significant, use the HG peakpos - peakpos[1] = np.where(sig_pix[1] < sig_pix[0], - peakpos[0], peakpos[1]) - return peakpos + def extract_charge(self, waveforms): + peakpos = waveforms.argmax(2).astype(np.int) + charge, window = extract_charge_from_peakpos_array( + waveforms, peakpos, self.window_width, self.window_shift + ) + return charge, peakpos, window -class NeighbourPeakIntegrator(PeakFindingIntegrator): +class NeighbourPeakIntegrator(ChargeExtractor): """ Charge extractor that defines an integration window defined by the peaks in the neighbouring pixels. - - Attributes - ---------- - neighbours : list - List of neighbours for each pixel. Changes per telescope. - - Parameters - ---------- - config : traitlets.loader.Config - Configuration specified by config file or cmdline arguments. - Used to set traitlet values. - Set to None if no configuration to pass. - tool : ctapipe.core.Tool - Tool executable that is calling this component. - Passes the correct logger to the component. - Set to None if no Tool to pass. - kwargs """ - lwt = Int(0, help='Weight of the local pixel (0: peak from neighbours ' - 'only, 1: local pixel counts as much ' - 'as any neighbour').tag(config=True) - - def __init__(self, config=None, parent=None, **kwargs): - super().__init__(config=config, parent=parent, **kwargs) - - @staticmethod - def requires_neighbours(): - return True + window_shift = Int( + 3, help='Define the shift of the integration window ' + 'from the peakpos (peakpos - shift)' + ).tag(config=True) + window_width = Int( + 7, help='Define the width of the integration window' + ).tag(config=True) + lwt = Int( + 0, help='Weight of the local pixel (0: peak from neighbours only, ' + '1: local pixel counts as much as any neighbour)' + ).tag(config=True) - def _obtain_peak_position(self, waveforms): + def extract_charge(self, waveforms): shape = waveforms.shape - significant_samples = self._extract_significant_entries(waveforms) - sig_sam = significant_samples.astype(np.float32) - sum_data = np.zeros_like(sig_sam) + waveforms_32 = waveforms.astype(np.float32) + sum_data = np.zeros_like(waveforms_32) n = self.neighbours.astype(np.uint16) - get_sum_array(sig_sam, sum_data, *shape, n, n.shape[0], self.lwt) - return sum_data.argmax(2).astype(np.int) + get_sum_array(waveforms_32, sum_data, *shape, n, n.shape[0], self.lwt) + peakpos = sum_data.argmax(2).astype(np.int) + charge, window = extract_charge_from_peakpos_array( + waveforms, peakpos, self.window_width, self.window_shift + ) + return charge, peakpos, window -class AverageWfPeakIntegrator(PeakFindingIntegrator): +class AverageWfPeakIntegrator(ChargeExtractor): """ Charge extractor that defines an integration window defined by the average waveform across all pixels. - - Attributes - ---------- - neighbours : list - List of neighbours for each pixel. Changes per telescope. - - Parameters - ---------- - config : traitlets.loader.Config - Configuration specified by config file or cmdline arguments. - Used to set traitlet values. - Set to None if no configuration to pass. - tool : ctapipe.core.Tool - Tool executable that is calling this component. - Passes the correct logger to the component. - Set to None if no Tool to pass. - kwargs """ + window_shift = Int( + 3, help='Define the shift of the integration window ' + 'from the peakpos (peakpos - shift)' + ).tag(config=True) + window_width = Int( + 7, help='Define the width of the integration window' + ).tag(config=True) - def __init__(self, config=None, parent=None, **kwargs): - super().__init__(config=config, parent=parent, **kwargs) + def extract_charge(self, waveforms): + peakpos = waveforms.mean(1).argmax(1) + start = peakpos - self.window_shift + end = start + self.window_width + charge = np.stack([ + waveforms[0, :, start[0]:end[0]].sum(1), # HI channel + waveforms[1, :, start[1]:end[1]].sum(1), # LO channel + ]) + + # TODO: remove integration window return + ind = np.indices(waveforms.shape)[2] + window = (ind >= start[..., None, None]) & (ind < end[..., None, None]) - def _obtain_peak_position(self, waveforms): - nchan, npix, nsamples = waveforms.shape - significant_samples = self._extract_significant_entries(waveforms) - peakpos = np.zeros((nchan, npix), dtype=np.int) - avg_wf = np.mean(significant_samples, axis=1) - peakpos += np.argmax(avg_wf, axis=1)[:, None] - return peakpos + return charge, peakpos, window From 689cec955cd0612c958a3410bd94a09096d4bfd8 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Fri, 22 Mar 2019 14:30:22 +0100 Subject: [PATCH 06/13] Fixes for tests --- ctapipe/image/charge_extractors.py | 3 +++ ctapipe/image/tests/test_charge_extraction.py | 2 +- ctapipe/image/waveform_cleaning.py | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ctapipe/image/charge_extractors.py b/ctapipe/image/charge_extractors.py index b475c8cddd0..b123ebf3784 100644 --- a/ctapipe/image/charge_extractors.py +++ b/ctapipe/image/charge_extractors.py @@ -247,6 +247,9 @@ class NeighbourPeakIntegrator(ChargeExtractor): '1: local pixel counts as much as any neighbour)' ).tag(config=True) + def requires_neighbours(self): + return True + def extract_charge(self, waveforms): shape = waveforms.shape waveforms_32 = waveforms.astype(np.float32) diff --git a/ctapipe/image/tests/test_charge_extraction.py b/ctapipe/image/tests/test_charge_extraction.py index 19a21708ff4..7038d00f9f0 100644 --- a/ctapipe/image/tests/test_charge_extraction.py +++ b/ctapipe/image/tests/test_charge_extraction.py @@ -55,7 +55,7 @@ def test_full_integration(camera_waveforms): def test_simple_integration(camera_waveforms): waveforms, camera = camera_waveforms - integrator = SimpleIntegrator(t0=48) + integrator = SimpleIntegrator(window_start=45) integration, peakpos, window = integrator.extract_charge(waveforms) assert_allclose(integration[0][0], 232.559, rtol=1e-3) diff --git a/ctapipe/image/waveform_cleaning.py b/ctapipe/image/waveform_cleaning.py index 9da74adeec4..ea8b864d42e 100644 --- a/ctapipe/image/waveform_cleaning.py +++ b/ctapipe/image/waveform_cleaning.py @@ -146,7 +146,7 @@ def apply(self, waveforms): # Obtain waveform with pulse masked baseline_sub_b = baseline_sub[None, ...] - window, _ = self.extractor.get_window_from_waveforms(waveforms) + _, _, window = self.extractor.extract_charge(waveforms) windowed = np.ma.array(baseline_sub_b, mask=window[0]) no_pulse = np.ma.filled(windowed, 0)[0] From cbbf0bd8e397e00441af9fbd21af34f2759edffe Mon Sep 17 00:00:00 2001 From: watsonjj Date: Fri, 22 Mar 2019 14:57:11 +0100 Subject: [PATCH 07/13] Fix codacy complaints --- ctapipe/image/tests/test_charge_extraction.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/ctapipe/image/tests/test_charge_extraction.py b/ctapipe/image/tests/test_charge_extraction.py index 7038d00f9f0..a2aa8e28baa 100644 --- a/ctapipe/image/tests/test_charge_extraction.py +++ b/ctapipe/image/tests/test_charge_extraction.py @@ -45,36 +45,36 @@ def camera_waveforms(): def test_full_integration(camera_waveforms): - waveforms, camera = camera_waveforms + waveforms, _ = camera_waveforms integrator = FullIntegrator() - integration, peakpos, window = integrator.extract_charge(waveforms) + integration, _, _ = integrator.extract_charge(waveforms) assert_allclose(integration[0][0], 545.945, rtol=1e-3) assert_allclose(integration[1][0], 970.025, rtol=1e-3) def test_simple_integration(camera_waveforms): - waveforms, camera = camera_waveforms + waveforms, _ = camera_waveforms integrator = SimpleIntegrator(window_start=45) - integration, peakpos, window = integrator.extract_charge(waveforms) + integration, _, _ = integrator.extract_charge(waveforms) assert_allclose(integration[0][0], 232.559, rtol=1e-3) assert_allclose(integration[1][0], 32.539, rtol=1e-3) def test_global_peak_integration(camera_waveforms): - waveforms, camera = camera_waveforms + waveforms, _ = camera_waveforms integrator = GlobalPeakIntegrator() - integration, peakpos, window = integrator.extract_charge(waveforms) + integration, _, _ = integrator.extract_charge(waveforms) assert_allclose(integration[0][0], 232.559, rtol=1e-3) assert_allclose(integration[1][0], 425.406, rtol=1e-3) def test_local_peak_integration(camera_waveforms): - waveforms, camera = camera_waveforms + waveforms, _ = camera_waveforms integrator = LocalPeakIntegrator() - integration, peakpos, window = integrator.extract_charge(waveforms) + integration, _, _ = integrator.extract_charge(waveforms) assert_allclose(integration[0][0], 240.3, rtol=1e-3) assert_allclose(integration[1][0], 427.158, rtol=1e-3) @@ -85,23 +85,23 @@ def test_nb_peak_integration(camera_waveforms): nei = camera.neighbor_matrix_where integrator = NeighbourPeakIntegrator() integrator.neighbours = nei - integration, peakpos, window = integrator.extract_charge(waveforms) + integration, _, _ = integrator.extract_charge(waveforms) assert_allclose(integration[0][0], 94.671, rtol=1e-3) assert_allclose(integration[1][0], 426.887, rtol=1e-3) def test_averagewf_peak_integration(camera_waveforms): - waveforms, camera = camera_waveforms + waveforms, _ = camera_waveforms integrator = AverageWfPeakIntegrator() - integration, peakpos, window = integrator.extract_charge(waveforms) + integration, _, _ = integrator.extract_charge(waveforms) assert_allclose(integration[0][0], 232.559, rtol=1e-3) assert_allclose(integration[1][0], 425.406, rtol=1e-3) def test_charge_extractor_factory(camera_waveforms): - waveforms, camera = camera_waveforms + waveforms, _ = camera_waveforms extractor = ChargeExtractor.from_name('LocalPeakIntegrator') extractor.extract_charge(waveforms) From 75c46bc3acbb150fada97a57a97c7013adcac2f5 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Sat, 23 Mar 2019 20:17:55 +0100 Subject: [PATCH 08/13] Restore SimpleIntegrator docstring --- ctapipe/image/charge_extractors.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ctapipe/image/charge_extractors.py b/ctapipe/image/charge_extractors.py index b123ebf3784..56562781b3b 100644 --- a/ctapipe/image/charge_extractors.py +++ b/ctapipe/image/charge_extractors.py @@ -160,6 +160,9 @@ def extract_charge(self, waveforms): class SimpleIntegrator(ChargeExtractor): + """ + Charge extractor that integrates within a window defined by the user. + """ window_start = Int( 0, help='Define the start position for the integration window' ).tag(config=True) From 3db7781522bf0f460c90303fecc22d01818adb5d Mon Sep 17 00:00:00 2001 From: watsonjj Date: Mon, 25 Mar 2019 13:37:48 +0100 Subject: [PATCH 09/13] Replace masked array usage in extract_charge_from_peakpos_array --- ctapipe/image/charge_extractors.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/ctapipe/image/charge_extractors.py b/ctapipe/image/charge_extractors.py index 56562781b3b..ec060b2b464 100644 --- a/ctapipe/image/charge_extractors.py +++ b/ctapipe/image/charge_extractors.py @@ -46,18 +46,11 @@ def extract_charge_from_peakpos_array(waveforms, peakpos, width, shift): Shape: (n_chan, n_pix) """ - # Ensure window is within waveform - # width[width > n_samples] = n_samples - # start[start < 0] = 0 - # sum_check = start + width > n_samples - # start[sum_check] = n_samples - width[sum_check] - start = peakpos - shift end = start + width ind = np.indices(waveforms.shape)[2] integration_window = (ind >= start[..., None]) & (ind < end[..., None]) - windowed = np.ma.array(waveforms, mask=~integration_window) - charge = windowed.sum(2).data + charge = (waveforms * integration_window).sum(axis=2) # TODO: remove integration window return return charge, integration_window From 2a05cb0c30508e48df68875be633f1549b8f55fc Mon Sep 17 00:00:00 2001 From: watsonjj Date: Mon, 25 Mar 2019 16:51:02 +0100 Subject: [PATCH 10/13] Create abstract classes for easier traitlet configuration --- ctapipe/image/charge_extractors.py | 56 ++++++++----------- ctapipe/image/tests/test_charge_extraction.py | 9 +-- 2 files changed, 27 insertions(+), 38 deletions(-) diff --git a/ctapipe/image/charge_extractors.py b/ctapipe/image/charge_extractors.py index ec060b2b464..b74f7c4fae6 100644 --- a/ctapipe/image/charge_extractors.py +++ b/ctapipe/image/charge_extractors.py @@ -152,16 +152,22 @@ def extract_charge(self, waveforms): return waveforms.sum(2), peakpos, window -class SimpleIntegrator(ChargeExtractor): +class WindowIntegrator(ChargeExtractor): + """ + Abstract class for defining the integration window width traitlet + """ + window_width = Int( + 7, help='Define the width of the integration window' + ).tag(config=True) + + +class SimpleIntegrator(WindowIntegrator): """ Charge extractor that integrates within a window defined by the user. """ window_start = Int( 0, help='Define the start position for the integration window' ).tag(config=True) - window_width = Int( - 7, help='Define the width of the integration window' - ).tag(config=True) def extract_charge(self, waveforms): start = self.window_start @@ -172,18 +178,21 @@ def extract_charge(self, waveforms): return waveforms[..., start:end].sum(2), peakpos, window -class GlobalPeakIntegrator(ChargeExtractor): +class PeakFindingIntegrator(WindowIntegrator): """ - Charge extractor that defines an integration window about the global - peak in the image. + Abstract class for defining the integration window shift traitlet """ window_shift = Int( 3, help='Define the shift of the integration window ' 'from the peakpos (peakpos - shift)' ).tag(config=True) - window_width = Int( - 7, help='Define the width of the integration window' - ).tag(config=True) + + +class GlobalPeakIntegrator(PeakFindingIntegrator): + """ + Charge extractor that defines an integration window about the global + peak in the image. + """ def extract_charge(self, waveforms): max_t = waveforms.argmax(2) @@ -205,18 +214,11 @@ def extract_charge(self, waveforms): return charge, peakpos, window -class LocalPeakIntegrator(ChargeExtractor): +class LocalPeakIntegrator(PeakFindingIntegrator): """ Charge extractor that defines an integration window about the local peak in each pixel. """ - window_shift = Int( - 3, help='Define the shift of the integration window ' - 'from the peakpos (peakpos - shift)' - ).tag(config=True) - window_width = Int( - 7, help='Define the width of the integration window' - ).tag(config=True) def extract_charge(self, waveforms): peakpos = waveforms.argmax(2).astype(np.int) @@ -226,18 +228,11 @@ def extract_charge(self, waveforms): return charge, peakpos, window -class NeighbourPeakIntegrator(ChargeExtractor): +class NeighbourPeakIntegrator(PeakFindingIntegrator): """ Charge extractor that defines an integration window defined by the peaks in the neighbouring pixels. """ - window_shift = Int( - 3, help='Define the shift of the integration window ' - 'from the peakpos (peakpos - shift)' - ).tag(config=True) - window_width = Int( - 7, help='Define the width of the integration window' - ).tag(config=True) lwt = Int( 0, help='Weight of the local pixel (0: peak from neighbours only, ' '1: local pixel counts as much as any neighbour)' @@ -259,18 +254,11 @@ def extract_charge(self, waveforms): return charge, peakpos, window -class AverageWfPeakIntegrator(ChargeExtractor): +class AverageWfPeakIntegrator(PeakFindingIntegrator): """ Charge extractor that defines an integration window defined by the average waveform across all pixels. """ - window_shift = Int( - 3, help='Define the shift of the integration window ' - 'from the peakpos (peakpos - shift)' - ).tag(config=True) - window_width = Int( - 7, help='Define the width of the integration window' - ).tag(config=True) def extract_charge(self, waveforms): peakpos = waveforms.mean(1).argmax(1) diff --git a/ctapipe/image/tests/test_charge_extraction.py b/ctapipe/image/tests/test_charge_extraction.py index a2aa8e28baa..50902a743e8 100644 --- a/ctapipe/image/tests/test_charge_extraction.py +++ b/ctapipe/image/tests/test_charge_extraction.py @@ -126,7 +126,8 @@ def test_charge_extractor_factory_args(): assert local_peak_integrator.window_width == 20 assert local_peak_integrator.window_shift == 3 - ChargeExtractor.from_name( - 'FullIntegrator', - config=config, - ) + with pytest.warns(UserWarning): + ChargeExtractor.from_name( + 'FullIntegrator', + config=config, + ) From 3193e7ce1ff916b72d6794090c43247a75740854 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Mon, 25 Mar 2019 16:51:12 +0100 Subject: [PATCH 11/13] Fixes for tools --- ctapipe/tools/bokeh/file_viewer.py | 15 ++++----------- ctapipe/tools/display_dl1.py | 6 ++---- ctapipe/tools/display_integrator.py | 6 ++---- ctapipe/tools/extract_charge_resolution.py | 6 ++---- 4 files changed, 10 insertions(+), 23 deletions(-) diff --git a/ctapipe/tools/bokeh/file_viewer.py b/ctapipe/tools/bokeh/file_viewer.py index a7000fa6ee4..1542da61bd1 100644 --- a/ctapipe/tools/bokeh/file_viewer.py +++ b/ctapipe/tools/bokeh/file_viewer.py @@ -49,11 +49,9 @@ class BokehFileViewer(Tool): ped='TargetIOR1Calibrator.pedestal_path', tf='TargetIOR1Calibrator.tf_path', pe='TargetIOR1Calibrator.pe_path', - simpleintegrator_t0='SimpleIntegrator.t0', window_width='WindowIntegrator.window_width', - window_shift='WindowIntegrator.window_shift', - sig_amp_cut_HG='PeakFindingIntegrator.sig_amp_cut_HG', - sig_amp_cut_LG='PeakFindingIntegrator.sig_amp_cut_LG', + window_shift='PeakFindingIntegrator.window_shift', + window_start='SimpleIntegrator.window_start', lwt='NeighbourPeakIntegrator.lwt', )) @@ -370,10 +368,6 @@ def create_dl1_widgets(self): extractor_t0=TextInput(title="T0:", value=''), extractor_window_width=TextInput(title="Window Width:", value=''), extractor_window_shift=TextInput(title="Window Shift:", value=''), - extractor_sig_amp_cut_HG=TextInput(title="Significant Amplitude " - "Cut (HG):", value=''), - extractor_sig_amp_cut_LG=TextInput(title="Significant Amplitude " - "Cut (LG):", value=''), extractor_lwt=TextInput(title="Local Pixel Weight:", value='')) for val in self.w_dl1_dict.values(): @@ -386,8 +380,6 @@ def create_dl1_widgets(self): self.w_dl1_dict['extractor_t0'], self.w_dl1_dict['extractor_window_width'], self.w_dl1_dict['extractor_window_shift'], - self.w_dl1_dict['extractor_sig_amp_cut_HG'], - self.w_dl1_dict['extractor_sig_amp_cut_LG'], self.w_dl1_dict['extractor_lwt']) def update_dl1_widget_values(self): @@ -418,8 +410,9 @@ def on_dl1_widget_change(self, _, __, ___): self._updating_dl1 = True cmdline = [] for key, val in self.w_dl1_dict.items(): + k = key.replace("extractor_", "").replace("cleaner_", "") if val.value: - cmdline.append(f'--{key}') + cmdline.append(f'--{k}') cmdline.append(val.value) self.parse_command_line(cmdline) extractor = ChargeExtractor.from_name( diff --git a/ctapipe/tools/display_dl1.py b/ctapipe/tools/display_dl1.py index 6b3fd7ce3d9..0110810011b 100644 --- a/ctapipe/tools/display_dl1.py +++ b/ctapipe/tools/display_dl1.py @@ -150,11 +150,9 @@ class DisplayDL1Calib(Tool): dict( max_events='EventSource.max_events', extractor='DisplayDL1Calib.extractor_product', - t0='SimpleIntegrator.t0', window_width='WindowIntegrator.window_width', - window_shift='WindowIntegrator.window_shift', - sig_amp_cut_HG='PeakFindingIntegrator.sig_amp_cut_HG', - sig_amp_cut_LG='PeakFindingIntegrator.sig_amp_cut_LG', + window_shift='PeakFindingIntegrator.window_shift', + window_start='SimpleIntegrator.window_start', lwt='NeighbourPeakIntegrator.lwt', clip_amplitude='CameraDL1Calibrator.clip_amplitude', T='DisplayDL1Calib.telescope', diff --git a/ctapipe/tools/display_integrator.py b/ctapipe/tools/display_integrator.py index 6358d5ad205..168cb7e94a5 100644 --- a/ctapipe/tools/display_integrator.py +++ b/ctapipe/tools/display_integrator.py @@ -268,11 +268,9 @@ class DisplayIntegrator(Tool): f='EventSource.input_url', max_events='EventSource.max_events', extractor='DisplayIntegrator.extractor_product', - t0='SimpleIntegrator.t0', window_width='WindowIntegrator.window_width', - window_shift='WindowIntegrator.window_shift', - sig_amp_cut_HG='PeakFindingIntegrator.sig_amp_cut_HG', - sig_amp_cut_LG='PeakFindingIntegrator.sig_amp_cut_LG', + window_shift='PeakFindingIntegrator.window_shift', + window_start='SimpleIntegrator.window_start', lwt='NeighbourPeakIntegrator.lwt', clip_amplitude='CameraDL1Calibrator.clip_amplitude', radius='CameraDL1Calibrator.radius', diff --git a/ctapipe/tools/extract_charge_resolution.py b/ctapipe/tools/extract_charge_resolution.py index 8c8f619b0a3..77b916ffeed 100644 --- a/ctapipe/tools/extract_charge_resolution.py +++ b/ctapipe/tools/extract_charge_resolution.py @@ -46,10 +46,8 @@ class ChargeResolutionGenerator(Tool): T='SimTelEventSource.allowed_tels', extractor='ChargeResolutionGenerator.extractor_product', window_width='WindowIntegrator.window_width', - window_shift='WindowIntegrator.window_shift', - t0='SimpleIntegrator.t0', - sig_amp_cut_HG='PeakFindngIntegrator.sig_amp_cut_HG', - sig_amp_cut_LG='PeakFindngIntegrator.sig_amp_cut_LG', + window_shift='PeakFindingIntegrator.window_shift', + window_start='SimpleIntegrator.window_start', lwt='NeighbourPeakIntegrator.lwt', clip_amplitude='CameraDL1Calibrator.clip_amplitude', radius='CameraDL1Calibrator.radius', From fea6b350650659654e0487442c90ca1a8e334ce0 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Tue, 26 Mar 2019 11:05:48 +0100 Subject: [PATCH 12/13] Adopted suggestion for configuration discussed in #1029 - Removed abstract classes that defined common traitlet - Each ChargeExtractor once again has its own traitlets defined - Tools have been updated to not expose the ChargeExtractor traitlets as aliases - can still be configured explicitly (via cmdline or config file) --- ctapipe/image/charge_extractors.py | 56 +++++++++++++--------- ctapipe/tools/bokeh/file_viewer.py | 13 ++--- ctapipe/tools/display_dl1.py | 5 -- ctapipe/tools/display_integrator.py | 6 --- ctapipe/tools/extract_charge_resolution.py | 6 --- 5 files changed, 37 insertions(+), 49 deletions(-) diff --git a/ctapipe/image/charge_extractors.py b/ctapipe/image/charge_extractors.py index b74f7c4fae6..63d27c747af 100644 --- a/ctapipe/image/charge_extractors.py +++ b/ctapipe/image/charge_extractors.py @@ -152,22 +152,16 @@ def extract_charge(self, waveforms): return waveforms.sum(2), peakpos, window -class WindowIntegrator(ChargeExtractor): - """ - Abstract class for defining the integration window width traitlet - """ - window_width = Int( - 7, help='Define the width of the integration window' - ).tag(config=True) - - -class SimpleIntegrator(WindowIntegrator): +class SimpleIntegrator(ChargeExtractor): """ Charge extractor that integrates within a window defined by the user. """ window_start = Int( 0, help='Define the start position for the integration window' ).tag(config=True) + window_width = Int( + 7, help='Define the width of the integration window' + ).tag(config=True) def extract_charge(self, waveforms): start = self.window_start @@ -178,22 +172,19 @@ def extract_charge(self, waveforms): return waveforms[..., start:end].sum(2), peakpos, window -class PeakFindingIntegrator(WindowIntegrator): +class GlobalPeakIntegrator(ChargeExtractor): """ - Abstract class for defining the integration window shift traitlet + Charge extractor that defines an integration window about the global + peak in the image. """ + window_width = Int( + 7, help='Define the width of the integration window' + ).tag(config=True) window_shift = Int( 3, help='Define the shift of the integration window ' 'from the peakpos (peakpos - shift)' ).tag(config=True) - -class GlobalPeakIntegrator(PeakFindingIntegrator): - """ - Charge extractor that defines an integration window about the global - peak in the image. - """ - def extract_charge(self, waveforms): max_t = waveforms.argmax(2) max_s = waveforms.max(2) @@ -214,11 +205,18 @@ def extract_charge(self, waveforms): return charge, peakpos, window -class LocalPeakIntegrator(PeakFindingIntegrator): +class LocalPeakIntegrator(ChargeExtractor): """ Charge extractor that defines an integration window about the local peak in each pixel. """ + window_width = Int( + 7, help='Define the width of the integration window' + ).tag(config=True) + window_shift = Int( + 3, help='Define the shift of the integration window ' + 'from the peakpos (peakpos - shift)' + ).tag(config=True) def extract_charge(self, waveforms): peakpos = waveforms.argmax(2).astype(np.int) @@ -228,11 +226,18 @@ def extract_charge(self, waveforms): return charge, peakpos, window -class NeighbourPeakIntegrator(PeakFindingIntegrator): +class NeighbourPeakIntegrator(ChargeExtractor): """ Charge extractor that defines an integration window defined by the peaks in the neighbouring pixels. """ + window_width = Int( + 7, help='Define the width of the integration window' + ).tag(config=True) + window_shift = Int( + 3, help='Define the shift of the integration window ' + 'from the peakpos (peakpos - shift)' + ).tag(config=True) lwt = Int( 0, help='Weight of the local pixel (0: peak from neighbours only, ' '1: local pixel counts as much as any neighbour)' @@ -254,11 +259,18 @@ def extract_charge(self, waveforms): return charge, peakpos, window -class AverageWfPeakIntegrator(PeakFindingIntegrator): +class AverageWfPeakIntegrator(ChargeExtractor): """ Charge extractor that defines an integration window defined by the average waveform across all pixels. """ + window_width = Int( + 7, help='Define the width of the integration window' + ).tag(config=True) + window_shift = Int( + 3, help='Define the shift of the integration window ' + 'from the peakpos (peakpos - shift)' + ).tag(config=True) def extract_charge(self, waveforms): peakpos = waveforms.mean(1).argmax(1) diff --git a/ctapipe/tools/bokeh/file_viewer.py b/ctapipe/tools/bokeh/file_viewer.py index 1542da61bd1..acb5aa16791 100644 --- a/ctapipe/tools/bokeh/file_viewer.py +++ b/ctapipe/tools/bokeh/file_viewer.py @@ -46,13 +46,6 @@ class BokehFileViewer(Tool): max_events='EventSource.max_events', extractor='BokehFileViewer.extractor_product', cleaner='BokehFileViewer.cleaner_product', - ped='TargetIOR1Calibrator.pedestal_path', - tf='TargetIOR1Calibrator.tf_path', - pe='TargetIOR1Calibrator.pe_path', - window_width='WindowIntegrator.window_width', - window_shift='PeakFindingIntegrator.window_shift', - window_start='SimpleIntegrator.window_start', - lwt='NeighbourPeakIntegrator.lwt', )) classes = List( @@ -410,10 +403,10 @@ def on_dl1_widget_change(self, _, __, ___): self._updating_dl1 = True cmdline = [] for key, val in self.w_dl1_dict.items(): - k = key.replace("extractor_", "").replace("cleaner_", "") + k = key.replace("extractor_", "ChargeExtractor.") + k = k.replace("cleaner_", "WaveformCleaner.") if val.value: - cmdline.append(f'--{k}') - cmdline.append(val.value) + cmdline.append(f'--{k}={val.value}') self.parse_command_line(cmdline) extractor = ChargeExtractor.from_name( self.extractor_product, diff --git a/ctapipe/tools/display_dl1.py b/ctapipe/tools/display_dl1.py index 0110810011b..b0006f01c32 100644 --- a/ctapipe/tools/display_dl1.py +++ b/ctapipe/tools/display_dl1.py @@ -150,11 +150,6 @@ class DisplayDL1Calib(Tool): dict( max_events='EventSource.max_events', extractor='DisplayDL1Calib.extractor_product', - window_width='WindowIntegrator.window_width', - window_shift='PeakFindingIntegrator.window_shift', - window_start='SimpleIntegrator.window_start', - lwt='NeighbourPeakIntegrator.lwt', - clip_amplitude='CameraDL1Calibrator.clip_amplitude', T='DisplayDL1Calib.telescope', O='ImagePlotter.output_path' ) diff --git a/ctapipe/tools/display_integrator.py b/ctapipe/tools/display_integrator.py index 168cb7e94a5..acb5ec9ebef 100644 --- a/ctapipe/tools/display_integrator.py +++ b/ctapipe/tools/display_integrator.py @@ -268,12 +268,6 @@ class DisplayIntegrator(Tool): f='EventSource.input_url', max_events='EventSource.max_events', extractor='DisplayIntegrator.extractor_product', - window_width='WindowIntegrator.window_width', - window_shift='PeakFindingIntegrator.window_shift', - window_start='SimpleIntegrator.window_start', - lwt='NeighbourPeakIntegrator.lwt', - clip_amplitude='CameraDL1Calibrator.clip_amplitude', - radius='CameraDL1Calibrator.radius', E='DisplayIntegrator.event_index', T='DisplayIntegrator.telescope', C='DisplayIntegrator.channel', diff --git a/ctapipe/tools/extract_charge_resolution.py b/ctapipe/tools/extract_charge_resolution.py index 77b916ffeed..ac766bb6ab9 100644 --- a/ctapipe/tools/extract_charge_resolution.py +++ b/ctapipe/tools/extract_charge_resolution.py @@ -45,12 +45,6 @@ class ChargeResolutionGenerator(Tool): max_events='SimTelEventSource.max_events', T='SimTelEventSource.allowed_tels', extractor='ChargeResolutionGenerator.extractor_product', - window_width='WindowIntegrator.window_width', - window_shift='PeakFindingIntegrator.window_shift', - window_start='SimpleIntegrator.window_start', - lwt='NeighbourPeakIntegrator.lwt', - clip_amplitude='CameraDL1Calibrator.clip_amplitude', - radius='CameraDL1Calibrator.radius', max_pe='ChargeResolutionCalculator.max_pe', O='ChargeResolutionGenerator.output_path', )) From 15ad53bb844a0815f79e95d0e00ca877cf334c49 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Wed, 27 Mar 2019 14:10:20 +0100 Subject: [PATCH 13/13] Corrections to address comments - Replace None with np.newaxis where appropriate - Fix documentation --- ctapipe/image/charge_extractors.py | 16 +++++-- ctapipe/image/tests/test_charge_extraction.py | 44 +++++++++---------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/ctapipe/image/charge_extractors.py b/ctapipe/image/charge_extractors.py index 63d27c747af..d63a14ac723 100644 --- a/ctapipe/image/charge_extractors.py +++ b/ctapipe/image/charge_extractors.py @@ -22,7 +22,8 @@ def extract_charge_from_peakpos_array(waveforms, peakpos, width, shift): """ - Build the numpy array of bools defining the integration window. + Sum the samples from the waveform using the window defined by a + peak postion, window width, and window shift. Parameters ---------- @@ -44,12 +45,17 @@ def extract_charge_from_peakpos_array(waveforms, peakpos, width, shift): charge : ndarray Extracted charge. Shape: (n_chan, n_pix) + integration_window : ndarray + Boolean array indicating which samples were included in the + charge extraction + Shape: (n_chan, n_pix, n_samples) """ start = peakpos - shift end = start + width ind = np.indices(waveforms.shape)[2] - integration_window = (ind >= start[..., None]) & (ind < end[..., None]) + integration_window = ((ind >= start[..., np.newaxis]) & + (ind < end[..., np.newaxis])) charge = (waveforms * integration_window).sum(axis=2) # TODO: remove integration window return @@ -200,7 +206,8 @@ def extract_charge(self, waveforms): # TODO: remove integration window return ind = np.indices(waveforms.shape)[2] - window = (ind >= start[..., None, None]) & (ind < end[..., None, None]) + window = ((ind >= start[..., np.newaxis, np.newaxis]) & + (ind < end[..., np.newaxis, np.newaxis])) return charge, peakpos, window @@ -283,6 +290,7 @@ def extract_charge(self, waveforms): # TODO: remove integration window return ind = np.indices(waveforms.shape)[2] - window = (ind >= start[..., None, None]) & (ind < end[..., None, None]) + window = ((ind >= start[..., np.newaxis, np.newaxis]) & + (ind < end[..., np.newaxis, np.newaxis])) return charge, peakpos, window diff --git a/ctapipe/image/tests/test_charge_extraction.py b/ctapipe/image/tests/test_charge_extraction.py index 50902a743e8..6e476285a74 100644 --- a/ctapipe/image/tests/test_charge_extraction.py +++ b/ctapipe/image/tests/test_charge_extraction.py @@ -28,16 +28,16 @@ def camera_waveforms(): x = np.arange(n_samples) # Randomize times - t_pulse_hi = r_hi.uniform(mid - 10, mid + 10, n_pixels)[:, None] - t_pulse_lo = r_lo.uniform(mid + 10, mid + 20, n_pixels)[:, None] + t_pulse_hi = r_hi.uniform(mid - 10, mid + 10, n_pixels)[:, np.newaxis] + t_pulse_lo = r_lo.uniform(mid + 10, mid + 20, n_pixels)[:, np.newaxis] # Create pulses y_hi = norm.pdf(x, t_pulse_hi, pulse_sigma) y_lo = norm.pdf(x, t_pulse_lo, pulse_sigma) # Randomize amplitudes - y_hi *= r_hi.uniform(100, 1000, n_pixels)[:, None] - y_lo *= r_lo.uniform(100, 1000, n_pixels)[:, None] + y_hi *= r_hi.uniform(100, 1000, n_pixels)[:, np.newaxis] + y_lo *= r_lo.uniform(100, 1000, n_pixels)[:, np.newaxis] y = np.stack([y_hi, y_lo]) @@ -47,37 +47,37 @@ def camera_waveforms(): def test_full_integration(camera_waveforms): waveforms, _ = camera_waveforms integrator = FullIntegrator() - integration, _, _ = integrator.extract_charge(waveforms) + charge, _, _ = integrator.extract_charge(waveforms) - assert_allclose(integration[0][0], 545.945, rtol=1e-3) - assert_allclose(integration[1][0], 970.025, rtol=1e-3) + assert_allclose(charge[0][0], 545.945, rtol=1e-3) + assert_allclose(charge[1][0], 970.025, rtol=1e-3) def test_simple_integration(camera_waveforms): waveforms, _ = camera_waveforms integrator = SimpleIntegrator(window_start=45) - integration, _, _ = integrator.extract_charge(waveforms) + charge, _, _ = integrator.extract_charge(waveforms) - assert_allclose(integration[0][0], 232.559, rtol=1e-3) - assert_allclose(integration[1][0], 32.539, rtol=1e-3) + assert_allclose(charge[0][0], 232.559, rtol=1e-3) + assert_allclose(charge[1][0], 32.539, rtol=1e-3) def test_global_peak_integration(camera_waveforms): waveforms, _ = camera_waveforms integrator = GlobalPeakIntegrator() - integration, _, _ = integrator.extract_charge(waveforms) + charge, _, _ = integrator.extract_charge(waveforms) - assert_allclose(integration[0][0], 232.559, rtol=1e-3) - assert_allclose(integration[1][0], 425.406, rtol=1e-3) + assert_allclose(charge[0][0], 232.559, rtol=1e-3) + assert_allclose(charge[1][0], 425.406, rtol=1e-3) def test_local_peak_integration(camera_waveforms): waveforms, _ = camera_waveforms integrator = LocalPeakIntegrator() - integration, _, _ = integrator.extract_charge(waveforms) + charge, _, _ = integrator.extract_charge(waveforms) - assert_allclose(integration[0][0], 240.3, rtol=1e-3) - assert_allclose(integration[1][0], 427.158, rtol=1e-3) + assert_allclose(charge[0][0], 240.3, rtol=1e-3) + assert_allclose(charge[1][0], 427.158, rtol=1e-3) def test_nb_peak_integration(camera_waveforms): @@ -85,19 +85,19 @@ def test_nb_peak_integration(camera_waveforms): nei = camera.neighbor_matrix_where integrator = NeighbourPeakIntegrator() integrator.neighbours = nei - integration, _, _ = integrator.extract_charge(waveforms) + charge, _, _ = integrator.extract_charge(waveforms) - assert_allclose(integration[0][0], 94.671, rtol=1e-3) - assert_allclose(integration[1][0], 426.887, rtol=1e-3) + assert_allclose(charge[0][0], 94.671, rtol=1e-3) + assert_allclose(charge[1][0], 426.887, rtol=1e-3) def test_averagewf_peak_integration(camera_waveforms): waveforms, _ = camera_waveforms integrator = AverageWfPeakIntegrator() - integration, _, _ = integrator.extract_charge(waveforms) + charge, _, _ = integrator.extract_charge(waveforms) - assert_allclose(integration[0][0], 232.559, rtol=1e-3) - assert_allclose(integration[1][0], 425.406, rtol=1e-3) + assert_allclose(charge[0][0], 232.559, rtol=1e-3) + assert_allclose(charge[1][0], 425.406, rtol=1e-3) def test_charge_extractor_factory(camera_waveforms):