Exemplo n.º 1
0
    def predict(self, hillas_parameters, tel_x, tel_y, array_direction):
        """

        Parameters
        ----------
        hillas_parameters: dict
            Dictionary containing Hillas parameters for all telescopes
            in reconstruction
        tel_x: dict
            Dictionary containing telescope position on ground for all
            telescopes in reconstruction
        tel_y: dict
            Dictionary containing telescope position on ground for all
            telescopes in reconstruction
        array_direction: AltAz
            Pointing direction of the array

        Returns
        -------
        ReconstructedShowerContainer:

        """
        src_x, src_y, err_x, err_y = self.reconstruct_nominal(
            hillas_parameters)
        core_x, core_y, core_err_x, core_err_y = self.reconstruct_tilted(
            hillas_parameters, tel_x, tel_y)
        err_x *= u.rad
        err_y *= u.rad

        nom = SkyCoord(x=src_x * u.rad,
                       y=src_y * u.rad,
                       frame=NominalFrame(array_direction=array_direction))
        horiz = nom.transform_to(AltAz())

        result = ReconstructedShowerContainer()
        result.alt, result.az = horiz.alt, horiz.az

        tilt = SkyCoord(
            x=core_x * u.m,
            y=core_y * u.m,
            frame=TiltedGroundFrame(pointing_direction=array_direction),
        )
        grd = project_to_ground(tilt)
        result.core_x = grd.x
        result.core_y = grd.y

        x_max = self.reconstruct_xmax(
            nom.x,
            nom.y,
            tilt.x,
            tilt.y,
            hillas_parameters,
            tel_x,
            tel_y,
            90 * u.deg - array_direction.alt,
        )

        result.core_uncert = np.sqrt(core_err_x**2 + core_err_y**2) * u.m

        result.tel_ids = [h for h in hillas_parameters.keys()]
        result.average_intensity = np.mean(
            [h.intensity for h in hillas_parameters.values()])
        result.is_valid = True

        src_error = np.sqrt(err_x**2 + err_y**2)
        result.alt_uncert = src_error.to(u.deg)
        result.az_uncert = src_error.to(u.deg)
        result.h_max = x_max
        result.h_max_uncert = np.nan
        result.goodness_of_fit = np.nan

        return result
Exemplo n.º 2
0
    def predict(self,
                hillas_dict,
                inst,
                array_pointing,
                telescopes_pointings=None):
        """

        Parameters
        ----------
        hillas_dict: dict
            Dictionary containing Hillas parameters for all telescopes
            in reconstruction
        inst : ctapipe.io.InstrumentContainer
            instrumental description
        array_pointing: SkyCoord[AltAz]
            pointing direction of the array
        telescopes_pointings: dict[SkyCoord[AltAz]]
            dictionary of pointing direction per each telescope

        Returns
        -------
        ReconstructedShowerContainer:

        """

        # filter warnings for missing obs time. this is needed because MC data has no obs time
        warnings.filterwarnings(action='ignore',
                                category=MissingFrameAttributeWarning)

        # stereoscopy needs at least two telescopes
        if len(hillas_dict) < 2:
            raise TooFewTelescopesException(
                "need at least two telescopes, have {}".format(
                    len(hillas_dict)))

        # check for np.nan or 0 width's as these screw up weights
        if any(
            [np.isnan(hillas_dict[tel]['width'].value)
             for tel in hillas_dict]):
            raise InvalidWidthException(
                "A HillasContainer contains an ellipse of width==np.nan")

        if any([hillas_dict[tel]['width'].value == 0 for tel in hillas_dict]):
            raise InvalidWidthException(
                "A HillasContainer contains an ellipse of width==0")

        if telescopes_pointings is None:
            telescopes_pointings = {
                tel_id: array_pointing
                for tel_id in hillas_dict.keys()
            }

        tilted_frame = TiltedGroundFrame(pointing_direction=array_pointing)

        ground_positions = inst.subarray.tel_coords
        grd_coord = GroundFrame(x=ground_positions.x,
                                y=ground_positions.y,
                                z=ground_positions.z)

        tilt_coord = grd_coord.transform_to(tilted_frame)

        tel_x = {
            tel_id: tilt_coord.x[tel_id - 1]
            for tel_id in list(hillas_dict.keys())
        }
        tel_y = {
            tel_id: tilt_coord.y[tel_id - 1]
            for tel_id in list(hillas_dict.keys())
        }

        nom_frame = NominalFrame(origin=array_pointing)

        hillas_dict_mod = copy.deepcopy(hillas_dict)

        for tel_id, hillas in hillas_dict_mod.items():
            # prevent from using rads instead of meters as inputs
            assert hillas.x.to(u.m).unit == u.Unit('m')

            focal_length = inst.subarray.tel[
                tel_id].optics.equivalent_focal_length

            camera_frame = CameraFrame(
                telescope_pointing=telescopes_pointings[tel_id],
                focal_length=focal_length,
            )
            cog_coords = SkyCoord(x=hillas.x, y=hillas.y, frame=camera_frame)
            cog_coords_nom = cog_coords.transform_to(nom_frame)
            hillas.x = cog_coords_nom.delta_alt
            hillas.y = cog_coords_nom.delta_az

        src_x, src_y, err_x, err_y = self.reconstruct_nominal(hillas_dict_mod)
        core_x, core_y, core_err_x, core_err_y = self.reconstruct_tilted(
            hillas_dict_mod, tel_x, tel_y)

        err_x *= u.rad
        err_y *= u.rad

        nom = SkyCoord(delta_az=src_x * u.rad,
                       delta_alt=src_y * u.rad,
                       frame=nom_frame)
        # nom = sky_pos.transform_to(nom_frame)
        sky_pos = nom.transform_to(array_pointing.frame)

        result = ReconstructedShowerContainer()
        result.alt = sky_pos.altaz.alt.to(u.rad)
        result.az = sky_pos.altaz.az.to(u.rad)

        tilt = SkyCoord(
            x=core_x * u.m,
            y=core_y * u.m,
            frame=tilted_frame,
        )
        grd = project_to_ground(tilt)
        result.core_x = grd.x
        result.core_y = grd.y

        x_max = self.reconstruct_xmax(
            nom.delta_az,
            nom.delta_alt,
            tilt.x,
            tilt.y,
            hillas_dict_mod,
            tel_x,
            tel_y,
            90 * u.deg - array_pointing.alt,
        )

        result.core_uncert = np.sqrt(core_err_x**2 + core_err_y**2) * u.m

        result.tel_ids = [h for h in hillas_dict_mod.keys()]
        result.average_intensity = np.mean(
            [h.intensity for h in hillas_dict_mod.values()])
        result.is_valid = True

        src_error = np.sqrt(err_x**2 + err_y**2)
        result.alt_uncert = src_error.to(u.rad)
        result.az_uncert = src_error.to(u.rad)
        result.h_max = x_max
        result.h_max_uncert = np.nan
        result.goodness_of_fit = np.nan

        return result
def process_event(event, calibrator, config):
    '''Processes one event.
    Calls calculate_image_features and performs a stereo hillas reconstruction.
    ReconstructedShowerContainer will be filled with nans (+units) if hillas failed.

    Returns:
    --------
    ArrayEventContainer
    list(telescope_event_containers.values())
    '''

    logging.info(f'processing event {event.dl0.event_id}')
    calibrator(event)

    telescope_types = []
    hillas_reconstructor = HillasReconstructor()
    telescope_event_containers = {}
    horizon_frame = AltAz()
    telescope_pointings = {}
    array_pointing = SkyCoord(
        az=event.mcheader.run_array_direction[0],
        alt=event.mcheader.run_array_direction[1],
        frame=horizon_frame,
    )

    for telescope_id, dl1 in event.dl1.tel.items():
        cam_type = event.inst.subarray.tels[telescope_id].camera.cam_id
        if cam_type not in config.cleaning_level.keys():
            logging.info(f"No cleaning levels for camera {cam_type}. Skipping event")
            continue
        telescope_types.append(str(event.inst.subarray.tels[telescope_id].optics))
        try:
            telescope_event_containers[telescope_id] = calculate_image_features(
                telescope_id, event, dl1, config
            )
        except HillasParameterizationError:
            logging.info(
                f'Error calculating hillas features for event {event.dl0.event_id, telescope_id}',
                exc_info=True,
            )
            continue
        except Exception:
            logging.info(
                f'Error calculating image features for event {event.dl0.event_id, telescope_id}',
                exc_info=True,
            )
            continue
        telescope_pointings[telescope_id] = SkyCoord(
            alt=telescope_event_containers[telescope_id].pointing.altitude,
            az=telescope_event_containers[telescope_id].pointing.azimuth,
            frame=horizon_frame,
        )

    if len(telescope_event_containers) < 1:
        raise Exception('None of the allowed telescopes triggered for event %s', event.dl0.event_id)

    parameters = {tel_id: telescope_event_containers[tel_id].hillas for tel_id in telescope_event_containers}

    try:
        reconstruction_container = hillas_reconstructor.predict(
            parameters, event.inst, array_pointing, telescopes_pointings=telescope_pointings
        )
        reconstruction_container.prefix = ''
    except Exception:
        logging.info(
            'Not enough telescopes for which Hillas parameters could be reconstructed', exc_info=True
        )
        reconstruction_container = ReconstructedShowerContainer()
        reconstruction_container.alt = u.Quantity(np.nan, u.rad)
        reconstruction_container.alt_uncert = u.Quantity(np.nan, u.rad)
        reconstruction_container.az = u.Quantity(np.nan, u.rad)
        reconstruction_container.az_uncert = np.nan
        reconstruction_container.core_x = u.Quantity(np.nan, u.m)
        reconstruction_container.core_y = u.Quantity(np.nan, u.m)
        reconstruction_container.core_uncert = np.nan
        reconstruction_container.h_max = u.Quantity(np.nan, u.m)
        reconstruction_container.h_max_uncert = np.nan
        reconstruction_container.is_valid = False
        reconstruction_container.tel_ids = []
        reconstruction_container.average_intensity = np.nan
        reconstruction_container.goodness_of_fit = np.nan
        reconstruction_container.prefix = ''

    calculate_distance_to_core(telescope_event_containers, event, reconstruction_container)

    mc_container = copy.deepcopy(event.mc)
    mc_container.tel = None
    mc_container.prefix = 'mc'

    counter = Counter(telescope_types)
    array_event = ArrayEventContainer(
        array_event_id=event.dl0.event_id,
        run_id=event.r0.obs_id,
        reco=reconstruction_container,
        total_intensity=sum([t.hillas.intensity for t in telescope_event_containers.values()]),
        num_triggered_lst=counter['LST'],
        num_triggered_mst=counter['MST'],
        num_triggered_sst=counter['SST'],
        num_triggered_telescopes=len(telescope_types),
        mc=mc_container,
    )

    return array_event, list(telescope_event_containers.values())
Exemplo n.º 4
0
    def predict(self, hillas_dict, inst, pointing_alt, pointing_az):
        '''
        The function you want to call for the reconstruction of the
        event. It takes care of setting up the event and consecutively
        calls the functions for the direction and core position
        reconstruction.  Shower parameters not reconstructed by this
        class are set to np.nan

        Parameters
        -----------
        hillas_dict: dict
            dictionary with telescope IDs as key and
            HillasParametersContainer instances as values
        inst : ctapipe.io.InstrumentContainer
            instrumental description
        pointing_alt: dict[astropy.coordinates.Angle]
            dict mapping telescope ids to pointing altitude
        pointing_az: dict[astropy.coordinates.Angle]
            dict mapping telescope ids to pointing azimuth

        Raises
        ------
        TooFewTelescopesException
            if len(hillas_dict) < 2
        '''

        # filter warnings for missing obs time. this is needed because MC data has no obs time
        warnings.filterwarnings(action='ignore',
                                category=MissingFrameAttributeWarning)

        # stereoscopy needs at least two telescopes
        if len(hillas_dict) < 2:
            raise TooFewTelescopesException(
                "need at least two telescopes, have {}".format(
                    len(hillas_dict)))

        self.initialize_hillas_planes(hillas_dict, inst.subarray, pointing_alt,
                                      pointing_az)

        # algebraic direction estimate
        direction, err_est_dir = self.estimate_direction()

        alt = u.Quantity(list(pointing_alt.values()))
        az = u.Quantity(list(pointing_az.values()))
        if np.any(alt != alt[0]) or np.any(az != az[0]):
            warnings.warn('Divergent pointing not supported')

        telescope_pointing = SkyCoord(alt=alt[0], az=az[0], frame=AltAz())
        # core position estimate using a geometric approach
        core_pos = self.estimate_core_position(hillas_dict, telescope_pointing)

        # container class for reconstructed showers
        result = ReconstructedShowerContainer()
        _, lat, lon = cartesian_to_spherical(*direction)

        # estimate max height of shower
        h_max = self.estimate_h_max()

        # astropy's coordinates system rotates counter-clockwise.
        # Apparently we assume it to be clockwise.
        result.alt, result.az = lat, -lon
        result.core_x = core_pos[0]
        result.core_y = core_pos[1]
        result.core_uncert = np.nan

        result.tel_ids = [h for h in hillas_dict.keys()]
        result.average_intensity = np.mean(
            [h.intensity for h in hillas_dict.values()])
        result.is_valid = True

        result.alt_uncert = err_est_dir
        result.az_uncert = np.nan

        result.h_max = h_max
        result.h_max_uncert = np.nan

        result.goodness_of_fit = np.nan

        return result
Exemplo n.º 5
0
    def predict(self, hillas_parameters, tel_x, tel_y, array_direction):
        """

        Parameters
        ----------
        hillas_parameters: dict
            Dictionary containing Hillas parameters for all telescopes
            in reconstruction
        tel_x: dict
            Dictionary containing telescope position on ground for all
            telescopes in reconstruction
        tel_y: dict
            Dictionary containing telescope position on ground for all
            telescopes in reconstruction
        array_direction: HorizonFrame
            Pointing direction of the array

        Returns
        -------
        ReconstructedShowerContainer:

        """
        src_x, src_y, err_x, err_y = self.reconstruct_nominal(hillas_parameters)
        core_x, core_y, core_err_x, core_err_y = self.reconstruct_tilted(
            hillas_parameters, tel_x, tel_y)
        err_x *= u.rad
        err_y *= u.rad

        nom = SkyCoord(
            x=src_x * u.rad,
            y=src_y * u.rad,
            frame=NominalFrame(array_direction=array_direction)
        )
        horiz = nom.transform_to(HorizonFrame())

        result = ReconstructedShowerContainer()
        result.alt, result.az = horiz.alt, horiz.az

        tilt = SkyCoord(
            x=core_x * u.m,
            y=core_y * u.m,
            frame=TiltedGroundFrame(pointing_direction=array_direction),
        )
        grd = project_to_ground(tilt)
        result.core_x = grd.x
        result.core_y = grd.y

        x_max = self.reconstruct_xmax(
            nom.x, nom.y,
            tilt.x, tilt.y,
            hillas_parameters,
            tel_x, tel_y,
            90 * u.deg - array_direction.alt,
        )

        result.core_uncert = np.sqrt(core_err_x**2 + core_err_y**2) * u.m

        result.tel_ids = [h for h in hillas_parameters.keys()]
        result.average_intensity = np.mean([h.intensity for h in hillas_parameters.values()])
        result.is_valid = True

        src_error = np.sqrt(err_x**2 + err_y**2)
        result.alt_uncert = src_error.to(u.deg)
        result.az_uncert = src_error.to(u.deg)
        result.h_max = x_max
        result.h_max_uncert = np.nan
        result.goodness_of_fit = np.nan

        return result
Exemplo n.º 6
0
    def predict(self, hillas_dict, inst, pointing_alt, pointing_az):
        '''
        The function you want to call for the reconstruction of the
        event. It takes care of setting up the event and consecutively
        calls the functions for the direction and core position
        reconstruction.  Shower parameters not reconstructed by this
        class are set to np.nan

        Parameters
        -----------
        hillas_dict: dict
            dictionary with telescope IDs as key and
            HillasParametersContainer instances as values
        inst : ctapipe.io.InstrumentContainer
            instrumental description
        pointing_alt: dict[astropy.coordinates.Angle]
            dict mapping telescope ids to pointing altitude
        pointing_az: dict[astropy.coordinates.Angle]
            dict mapping telescope ids to pointing azimuth

        Raises
        ------
        TooFewTelescopesException
            if len(hillas_dict) < 2
        '''

        # filter warnings for missing obs time. this is needed because MC data has no obs time
        warnings.filterwarnings(action='ignore', category=MissingFrameAttributeWarning)
        
        # stereoscopy needs at least two telescopes
        if len(hillas_dict) < 2:
            raise TooFewTelescopesException(
                "need at least two telescopes, have {}"
                .format(len(hillas_dict)))

        self.initialize_hillas_planes(
            hillas_dict,
            inst.subarray,
            pointing_alt,
            pointing_az
        )

        # algebraic direction estimate
        direction, err_est_dir = self.estimate_direction()

        alt = u.Quantity(list(pointing_alt.values()))
        az = u.Quantity(list(pointing_az.values()))
        if np.any(alt != alt[0]) or np.any(az != az[0]):
            warnings.warn('Divergent pointing not supported')

        telescope_pointing = SkyCoord(alt=alt[0], az=az[0], frame=HorizonFrame())
        # core position estimate using a geometric approach
        core_pos = self.estimate_core_position(hillas_dict, telescope_pointing)

        # container class for reconstructed showers
        result = ReconstructedShowerContainer()
        _, lat, lon = cartesian_to_spherical(*direction)

        # estimate max height of shower
        h_max = self.estimate_h_max()

        # astropy's coordinates system rotates counter-clockwise.
        # Apparently we assume it to be clockwise.
        result.alt, result.az = lat, -lon
        result.core_x = core_pos[0]
        result.core_y = core_pos[1]
        result.core_uncert = np.nan

        result.tel_ids = [h for h in hillas_dict.keys()]
        result.average_intensity = np.mean([h.intensity for h in hillas_dict.values()])
        result.is_valid = True

        result.alt_uncert = err_est_dir
        result.az_uncert = np.nan

        result.h_max = h_max
        result.h_max_uncert = np.nan

        result.goodness_of_fit = np.nan

        return result
Exemplo n.º 7
0
    def predict(self,
                hillas_dict,
                inst,
                array_pointing,
                telescopes_pointings=None):
        """
        The function you want to call for the reconstruction of the
        event. It takes care of setting up the event and consecutively
        calls the functions for the direction and core position
        reconstruction.  Shower parameters not reconstructed by this
        class are set to np.nan

        Parameters
        -----------
        hillas_dict: dict
            dictionary with telescope IDs as key and
            HillasParametersContainer instances as values
        inst : ctapipe.io.InstrumentContainer
            instrumental description
        array_pointing: SkyCoord[AltAz]
            pointing direction of the array
        telescopes_pointings: dict[SkyCoord[AltAz]]
            dictionary of pointing direction per each telescope

        Raises
        ------
        TooFewTelescopesException
            if len(hillas_dict) < 2
        InvalidWidthException
            if any width is np.nan or 0
        """

        # filter warnings for missing obs time. this is needed because MC data has no obs time
        warnings.filterwarnings(action='ignore',
                                category=MissingFrameAttributeWarning)

        # stereoscopy needs at least two telescopes
        if len(hillas_dict) < 2:
            raise TooFewTelescopesException(
                "need at least two telescopes, have {}".format(
                    len(hillas_dict)))

        # check for np.nan or 0 width's as these screw up weights
        if any(
            [np.isnan(hillas_dict[tel]['width'].value)
             for tel in hillas_dict]):
            raise InvalidWidthException(
                "A HillasContainer contains an ellipse of width==np.nan")

        if any([hillas_dict[tel]['width'].value == 0 for tel in hillas_dict]):
            raise InvalidWidthException(
                "A HillasContainer contains an ellipse of width==0")

        # use the single telescope pointing also for parallel pointing: code is more general
        if telescopes_pointings is None:
            telescopes_pointings = {
                tel_id: array_pointing
                for tel_id in hillas_dict.keys()
            }
        else:
            self.divergent_mode = True
            self.corrected_angle_dict = {}

        self.initialize_hillas_planes(hillas_dict, inst.subarray,
                                      telescopes_pointings, array_pointing)

        # algebraic direction estimate
        direction, err_est_dir = self.estimate_direction()

        # array pointing is needed to define the tilted frame
        core_pos = self.estimate_core_position(hillas_dict, array_pointing)

        # container class for reconstructed showers
        result = ReconstructedShowerContainer()
        _, lat, lon = cartesian_to_spherical(*direction)

        # estimate max height of shower
        h_max = self.estimate_h_max()

        # astropy's coordinates system rotates counter-clockwise.
        # Apparently we assume it to be clockwise.
        result.alt, result.az = lat, -lon
        result.core_x = core_pos[0]
        result.core_y = core_pos[1]
        result.core_uncert = np.nan

        result.tel_ids = [h for h in hillas_dict.keys()]
        result.average_intensity = np.mean(
            [h.intensity for h in hillas_dict.values()])
        result.is_valid = True

        result.alt_uncert = err_est_dir
        result.az_uncert = np.nan

        result.h_max = h_max
        result.h_max_uncert = np.nan

        result.goodness_of_fit = np.nan

        return result