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
Esempio n. 2
0
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
Esempio n. 3
0
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
Esempio n. 4
0
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
Esempio n. 5
0
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