def __init__(self, config, tool, extractor=None, **kwargs): """ The calibrator for DL1 charge extraction. Fills the dl1 container. It handles the integration correction and, if required, the list of neighbours. 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. extractor : ctapipe.calib.camera.charge_extractors.ChargeExtractor The extractor to use to extract the charge from the waveforms. By default the NeighbourPeakIntegrator with default configuration is used. kwargs """ super().__init__(config=config, parent=tool, **kwargs) self._extractor = extractor if self._extractor is None: self._extractor = NeighbourPeakIntegrator(config, tool) self._current_url = None self._dl0_empty_warn = False self.neighbour_dict = {} self.correction_dict = {}
def __init__(self, config, tool, extractor=None, **kwargs): """ The calibrator for DL1 charge extraction. Fills the dl1 container. It handles the integration correction and, if required, the list of neighbours. 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. extractor : ctapipe.calib.camera.charge_extractors.ChargeExtractor The extractor to use to extract the charge from the waveforms. By default the NeighbourPeakIntegrator with default configuration is used. kwargs """ super().__init__(config=config, parent=tool, **kwargs) self._extractor = extractor if self._extractor is None: self._extractor = NeighbourPeakIntegrator(config, tool) self._current_url = None self.neighbour_dict = {} self.correction_dict = {}
def test_nb_peak_integration(): telid = 11 event = get_test_event() data = event.r0.tel[telid].adc_samples nsamples = data.shape[2] ped = 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 = CameraGeometry.guess(*event.inst.pixel_pos[telid], event.inst.optical_foclen[telid]) nei = geom.neighbors integrator = NeighbourPeakIntegrator(None, None) integrator.neighbours = nei integration, peakpos, window = integrator.extract_charge(data_ped) assert_almost_equal(integration[0][0], -64, 0) assert_almost_equal(integration[1][0], -64, 0) assert peakpos[0][0] == 20 assert peakpos[1][0] == 20
def test_nb_peak_integration(): telid = 11 event = get_test_event() data = event.dl0.tel[telid].adc_samples nsamples = data.shape[2] ped = 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 = CameraGeometry.guess(*event.inst.pixel_pos[telid], event.inst.optical_foclen[telid]) nei = geom.neighbors integrator = NeighbourPeakIntegrator(None, None) integrator.neighbours = nei integration = integrator.extract_charge(data_ped) peakpos = integrator.peakpos print(integration[0][0]) assert_almost_equal(integration[0][0], -64, 0) assert_almost_equal(integration[1][0], -64, 0) assert peakpos[0][0] == 20 assert peakpos[1][0] == 20
class CameraDL1Calibrator(Component): name = 'CameraCalibrator' radius = Float(None, allow_none=True, help='Pixels within radius from a pixel are considered ' 'neighbours to the pixel. Set to None for the default ' '(1.4 * min_pixel_seperation).').tag(config=True) correction = Bool(True, help='Apply an integration correction to the charge to ' 'account for the full cherenkov signal that your ' 'smaller integration window may be ' 'missing.').tag(config=True) clip_amplitude = Float(None, allow_none=True, help='Amplitude in p.e. above which the signal is ' 'clipped. Set to None for no ' 'clipping.').tag(config=True) def __init__(self, config, tool, extractor=None, **kwargs): """ The calibrator for DL1 charge extraction. Fills the dl1 container. It handles the integration correction and, if required, the list of neighbours. 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. extractor : ctapipe.calib.camera.charge_extractors.ChargeExtractor The extractor to use to extract the charge from the waveforms. By default the NeighbourPeakIntegrator with default configuration is used. kwargs """ super().__init__(config=config, parent=tool, **kwargs) self._extractor = extractor if self._extractor is None: self._extractor = NeighbourPeakIntegrator(config, tool) self._current_url = None self._dl0_empty_warn = False self.neighbour_dict = {} self.correction_dict = {} def _check_url_change(self, event): """ Check if the event comes from a different file to the previous events. If it has, then the neighbour and correction dicts need to be reset as telescope ids might not indicate the same telescope type as before. Parameters ---------- event : container A `ctapipe` event container """ if 'input' in event.meta: url = event.meta['input'] if not self._current_url: self._current_url = url if url != self._current_url: self.log.warning("A new CameraDL1Calibrator should be created" "for each individual file so stored " "neighbours and integration_correction " "match the correct telid") self.neighbour_dict = {} self.correction_dict = {} def check_dl0_exists(self, event, telid): """ Check that dl0 data exists. If it does not, then do not change dl1. This ensures that if the containers were filled from a file containing dl1 data, it is not overwritten by non-existant data. Parameters ---------- event : container A `ctapipe` event container telid : int The telescope id. Returns ------- bool True if dl0.tel[telid].pe_samples is not None, else false. """ dl0 = event.dl0.tel[telid].pe_samples if dl0 is not None: return True else: if not self._dl0_empty_warn: self.log.warning("Encountered an event with no DL0 data. " "DL1 is unchanged in this circumstance.") self._dl0_empty_warn = True return False def get_neighbours(self, event, telid): """ Obtain the neighbouring pixels for this telescope. Parameters ---------- event : container A `ctapipe` event container telid : int The telescope id. The neighbours are calculated once per telescope. """ if telid in self.neighbour_dict: return self.neighbour_dict[telid] else: pixel_pos = event.inst.pixel_pos[telid] if not self.radius: pixsep = get_min_pixel_seperation(*pixel_pos) self.radius = 1.4 * pixsep.value self.neighbour_dict[telid] = \ find_neighbor_pixels(*pixel_pos, self.radius) return self.neighbour_dict[telid] def get_correction(self, event, telid): """ Obtain the integration correction for this telescope. Parameters ---------- event : container A `ctapipe` event container telid : int The telescope id. The integration correction is calculated once per telescope. """ if telid in self.correction_dict: return self.correction_dict[telid] else: try: shift = self._extractor.input_shift width = self._extractor.input_width self.correction_dict[telid] = \ integration_correction(event, telid, width, shift) return self.correction_dict[telid] except AttributeError: return 1 def calibrate(self, event): """ Fill the dl1 container with the calibration data that results from the configuration of this calibrator. Parameters ---------- event : container A `ctapipe` event container """ self._check_url_change(event) for telid in event.dl0.tels_with_data: if self.check_dl0_exists(event, telid): waveforms = event.dl0.tel[telid].pe_samples if self._extractor.requires_neighbours(): self._extractor.neighbours = self.get_neighbours( event, telid) charge = self._extractor.extract_charge(waveforms) extracted_samples = self._extractor.extracted_samples peakpos = self._extractor.peakpos if self.correction: corrected = charge * self.get_correction(event, telid) else: corrected = charge if self.clip_amplitude: corrected[corrected > self.clip_amplitude] = \ self.clip_amplitude event.dl1.tel[telid].image = corrected event.dl1.tel[telid].extracted_samples = extracted_samples event.dl1.tel[telid].peakpos = peakpos
class CameraDL1Calibrator(Component): name = 'CameraCalibrator' radius = Float(None, allow_none=True, help='Pixels within radius from a pixel are considered ' 'neighbours to the pixel. Set to None for the default ' '(1.4 * min_pixel_seperation).').tag(config=True) correction = Bool(True, help='Apply an integration correction to the charge to ' 'account for the full cherenkov signal that your ' 'smaller integration window may be ' 'missing.').tag(config=True) clip_amplitude = Float(None, allow_none=True, help='Amplitude in p.e. above which the signal is ' 'clipped. Set to None for no ' 'clipping.').tag(config=True) def __init__(self, config, tool, extractor=None, **kwargs): """ The calibrator for DL1 charge extraction. Fills the dl1 container. It handles the integration correction and, if required, the list of neighbours. 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. extractor : ctapipe.calib.camera.charge_extractors.ChargeExtractor The extractor to use to extract the charge from the waveforms. By default the NeighbourPeakIntegrator with default configuration is used. kwargs """ super().__init__(config=config, parent=tool, **kwargs) self._extractor = extractor if self._extractor is None: self._extractor = NeighbourPeakIntegrator(config, tool) self._current_url = None self.neighbour_dict = {} self.correction_dict = {} def _check_url_change(self, event): """ Check if the event comes from a different file to the previous events. If it has, then the neighbour and correction dicts need to be reset as telescope ids might not indicate the same telescope type as before. Parameters ---------- event : container A `ctapipe` event container """ if 'input' in event.meta: url = event.meta['input'] if not self._current_url: self._current_url = url if url != self._current_url: self.log.warning("A new CameraDL1Calibrator should be created" "for each individual file so stored " "neighbours and integration_correction " "match the correct telid") self.neighbour_dict = {} self.correction_dict = {} def get_neighbours(self, event, telid): """ Obtain the neighbouring pixels for this telescope. Parameters ---------- event : container A `ctapipe` event container telid : int The telescope id. The neighbours are calculated once per telescope. """ if telid in self.neighbour_dict: return self.neighbour_dict[telid] else: pixel_pos = event.inst.pixel_pos[telid] if not self.radius: pixsep = get_min_pixel_seperation(*pixel_pos) self.radius = 1.4 * pixsep.value self.neighbour_dict[telid] = \ find_neighbor_pixels(*pixel_pos, self.radius) return self.neighbour_dict[telid] def get_correction(self, event, telid): """ Obtain the integration correction for this telescope. Parameters ---------- event : container A `ctapipe` event container telid : int The telescope id. The integration correction is calculated once per telescope. """ if telid in self.correction_dict: return self.correction_dict[telid] else: try: shift = self._extractor.input_shift width = self._extractor.input_width self.correction_dict[telid] = \ integration_correction(event, telid, width, shift) return self.correction_dict[telid] except AttributeError: return 1 def obtain_dl0(self, event, telid): """ Obtain the dl0 adc_samples. For hessio files, this means to calibrate from r0 to dl0. As what is currently stored as dl0 in hessio.py is actually r0. Parameters ---------- event : container A `ctapipe` event container telid : int The telescope id. Returns ------- waveforms : ndarray The dl0 PE samples inside a numpy array of shape (n_samples) """ # TODO: dl0 should be correctly filled with pe_samples in IO if event.meta['origin'] == 'hessio': return mc_r0_to_dl0_calibration(event, telid) else: self.log.exception("no calibration created for data origin: " "{}".format(event.meta['origin'])) def calibrate(self, event): """ Fill the dl1 container with the calibration data that results from the configuration of this calibrator. Parameters ---------- event : container A `ctapipe` event container """ self._check_url_change(event) for telid in event.dl0.tels_with_data: waveforms = self.obtain_dl0(event, telid) if self._extractor.requires_neighbours(): self._extractor.neighbours = self.get_neighbours(event, telid) charge = self._extractor.extract_charge(waveforms) extracted_samples = self._extractor.extracted_samples peakpos = self._extractor.peakpos if self.correction: corrected = charge * self.get_correction(event, telid) else: corrected = charge if self.clip_amplitude: corrected[corrected > self.clip_amplitude] = \ self.clip_amplitude event.dl1.tel[telid].image = corrected event.dl1.tel[telid].extracted_samples = extracted_samples event.dl1.tel[telid].peakpos = peakpos def calibrate_source(self, source): """ Generator for calibrating all events in a file. Parameters ---------- source : generator A `ctapipe` event generator such as `ctapipe.io.hessio.hessio_event_source` Returns ------- generator A new generator that also contains the dl1 calibration. """ self.log.info("Calibration generator appended to source") for event in source: self.calibrate(event) yield event