def grd_to_tilt(): grd_coord = GroundFrame(x=1 * u.m, y=2 * u.m, z=0 * u.m) tilt_coord = grd_coord.transform_to( TiltedGroundFrame( pointing_direction=AltAz(alt=90 * u.deg, az=180 * u.deg))) print(project_to_ground(tilt_coord)) print("Tilted Coordinate", tilt_coord)
def grd_to_tilt(): grd_coord = GroundFrame(x=1 * u.m, y=2 * u.m, z=0 * u.m) tilt_coord = grd_coord.transform_to( TiltedGroundFrame( pointing_direction=HorizonFrame(alt=90 * u.deg, az=180 * u.deg) ) ) print(project_to_ground(tilt_coord)) print("Tilted Coordinate", tilt_coord)
def plot_hillas_lines(self, hillas_dict, length, frame="ground"): # xx, yy, zz = np.array(spherical_to_cartesian( # 1, # self.array_pointing.alt, # 0 * u.deg) # ) # -self.array_pointing.az)) # # plane = Plane(Point3D(0, 0, 0), normal_vector=(xx, yy, zz)) tilted_system = self.tilted_frame tel_coords_gnd = self.subarray.tel_coords tilt_tel_pos = tel_coords_gnd.transform_to(tilted_system) for tel_id in self.tel_ids: moments = hillas_dict[tel_id] if frame == "ground": color = [255, 0, 0] gnd_pos = project_to_ground( tilt_tel_pos[self.subarray.tel_indices[tel_id]]) tel_x_pos = gnd_pos.x.to_value(u.m) tel_y_pos = gnd_pos.y.to_value(u.m) tel_z_pos = gnd_pos.z.to_value(u.m) elif frame == "tilted": color = [0, 255, 0] # i need those coordinates in GroundFrame to transform to TiltedGroundFrame # The selection is done afterwards tel_x_pos = tilt_tel_pos[ self.subarray.tel_indices[tel_id]].x.to_value(u.m) tel_y_pos = tilt_tel_pos[ self.subarray.tel_indices[tel_id]].y.to_value(u.m) tel_z_pos = 0 # tilted_tel_pos = tilt_tel_pos[self.subarray.tel_indices[tel_id]] hillas_line_actor = hillas_lines( moments=moments, length=length, tel_coords=[tel_x_pos, tel_y_pos, tel_z_pos], frame=frame, array_pointing=self.array_pointing, tilted_frame=tilted_system, # plane=plane, ) # if frame == "tilted": # hillas_line_actor.RotateZ(0)#self.array_pointing.az.value) # hillas_line_actor.RotateY(0)#90 - self.array_pointing.alt.value) # self.tel_id[tel_id].append(hillas_line_actor) hillas_line_actor.GetProperty().SetColor(color) self.ren.AddActor(hillas_line_actor)
def estimate_core_position(self, hillas_dict, array_pointing): """ Estimate the core position by intersection the major ellipse lines of each telescope. Parameters ----------- hillas_dict: dict[HillasContainer] dictionary of hillas moments array_pointing: SkyCoord[HorizonFrame] Pointing direction of the array Returns ----------- core_x: u.Quantity estimated x position of impact core_y: u.Quantity estimated y position of impact """ if self.divergent_mode: psi = u.Quantity(list(self.corrected_angle_dict.values())) else: psi = u.Quantity([h.psi for h in hillas_dict.values()]) z = np.zeros(len(psi)) uvw_vectors = np.column_stack([np.cos(psi).value, np.sin(psi).value, z]) tilted_frame = TiltedGroundFrame(pointing_direction=array_pointing) ground_frame = GroundFrame() positions = [ ( SkyCoord(*plane.pos, frame=ground_frame) .transform_to(tilted_frame) .cartesian.xyz ) for plane in self.hillas_planes.values() ] core_position = line_line_intersection_3d(uvw_vectors, positions) core_pos_tilted = SkyCoord( x=core_position[0] * u.m, y=core_position[1] * u.m, frame=tilted_frame ) core_pos = project_to_ground(core_pos_tilted) return core_pos.x, core_pos.y
def estimate_core_position(self, hillas_dict, telescope_pointing): ''' Estimate the core position by intersection the major ellipse lines of each telescope. Parameters ----------- hillas_dict: dict[HillasContainer] dictionary of hillas moments telescope_pointing: SkyCoord[HorizonFrame] Pointing direction of the array Returns ----------- core_x: u.Quantity estimated x position of impact core_y: u.Quantity estimated y position of impact ''' psi = u.Quantity([h.psi for h in hillas_dict.values()]) z = np.zeros(len(psi)) uvw_vectors = np.column_stack([np.cos(psi).value, np.sin(psi).value, z]) tilted_frame = TiltedGroundFrame(pointing_direction=telescope_pointing) ground_frame = GroundFrame() positions = [ ( SkyCoord(*plane.pos, frame=ground_frame) .transform_to(tilted_frame) .cartesian.xyz ) for plane in self.hillas_planes.values() ] core_position = line_line_intersection_3d(uvw_vectors, positions) core_pos_tilted = SkyCoord( x=core_position[0] * u.m, y=core_position[1] * u.m, frame=tilted_frame ) core_pos = project_to_ground(core_pos_tilted) return core_pos.x, core_pos.y
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 predict(self, shower_seed, energy_seed): """ Parameters ---------- shower_seed: ReconstructedShowerContainer Seed shower geometry to be used in the fit energy_seed: ReconstructedEnergyContainer Seed energy to be used in fit Returns ------- ReconstructedShowerContainer, ReconstructedEnergyContainer: Reconstructed ImPACT shower geometry and energy """ horizon_seed = HorizonFrame(az=shower_seed.az, alt=shower_seed.alt) nominal_seed = horizon_seed.transform_to(NominalFrame( array_direction=self.array_direction)) source_x = nominal_seed.x.to(u.rad).value source_y = nominal_seed.y.to(u.rad).value ground = GroundFrame(x=shower_seed.core_x, y=shower_seed.core_y, z=0 * u.m) tilted = ground.transform_to( TiltedGroundFrame(pointing_direction=self.array_direction) ) tilt_x = tilted.x.to(u.m).value tilt_y = tilted.y.to(u.m).value zenith = 90 * u.deg - self.array_direction.alt if len(self.hillas_parameters) > 3: shift = [1] else: shift = [1.5, 1, 0.5, 0, -0.5, -1, -1.5] seed_list = spread_line_seed(self.hillas_parameters, self.tel_pos_x, self.tel_pos_y, source_x[0], source_y[0], tilt_x, tilt_y, energy_seed.energy.value, shift_frac = shift) chosen_seed = self.choose_seed(seed_list) # Perform maximum likelihood fit fit_params, errors, like = self.minimise(params=chosen_seed[0], step=chosen_seed[1], limits=chosen_seed[2], minimiser_name=self.minimiser_name) # Create a container class for reconstructed shower shower_result = ReconstructedShowerContainer() # Convert the best fits direction and core to Horizon and ground systems and # copy to the shower container nominal = NominalFrame(x=fit_params[0] * u.rad, y=fit_params[1] * u.rad, array_direction=self.array_direction) horizon = nominal.transform_to(HorizonFrame()) shower_result.alt, shower_result.az = horizon.alt, horizon.az tilted = TiltedGroundFrame(x=fit_params[2] * u.m, y=fit_params[3] * u.m, pointing_direction=self.array_direction) ground = project_to_ground(tilted) shower_result.core_x = ground.x shower_result.core_y = ground.y shower_result.is_valid = True # Currently no errors not availible to copy NaN shower_result.alt_uncert = np.nan shower_result.az_uncert = np.nan shower_result.core_uncert = np.nan # Copy reconstructed Xmax shower_result.h_max = fit_params[5] * self.get_shower_max(fit_params[0], fit_params[1], fit_params[2], fit_params[3], zenith.to(u.rad).value) shower_result.h_max *= np.cos(zenith) shower_result.h_max_uncert = errors[5] * shower_result.h_max shower_result.goodness_of_fit = like # Create a container class for reconstructed energy energy_result = ReconstructedEnergyContainer() # Fill with results energy_result.energy = fit_params[4] * u.TeV energy_result.energy_uncert = errors[4] * u.TeV energy_result.is_valid = True return shower_result, energy_result
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
def predict(self, shower_seed, energy_seed): """ Parameters ---------- shower_seed: ReconstructedShowerContainer Seed shower geometry to be used in the fit energy_seed: ReconstructedEnergyContainer Seed energy to be used in fit Returns ------- ReconstructedShowerContainer, ReconstructedEnergyContainer: Reconstructed ImPACT shower geometry and energy """ horizon_seed = SkyCoord( az=shower_seed.az, alt=shower_seed.alt, frame=HorizonFrame() ) nominal_seed = horizon_seed.transform_to( NominalFrame(origin=self.array_direction) ) source_x = nominal_seed.delta_az.to_value(u.rad) source_y = nominal_seed.delta_alt.to_value(u.rad) ground = GroundFrame(x=shower_seed.core_x, y=shower_seed.core_y, z=0 * u.m) tilted = ground.transform_to( TiltedGroundFrame(pointing_direction=self.array_direction) ) tilt_x = tilted.x.to(u.m).value tilt_y = tilted.y.to(u.m).value zenith = 90 * u.deg - self.array_direction.alt if len(self.hillas_parameters) > 3: shift = [1] else: shift = [1.5, 1, 0.5, 0, -0.5, -1, -1.5] seed_list = spread_line_seed(self.hillas_parameters, self.tel_pos_x, self.tel_pos_y, source_x[0], source_y[0], tilt_x, tilt_y, energy_seed.energy.value, shift_frac = shift) chosen_seed = self.choose_seed(seed_list) # Perform maximum likelihood fit fit_params, errors, like = self.minimise(params=chosen_seed[0], step=chosen_seed[1], limits=chosen_seed[2], minimiser_name=self.minimiser_name) # Create a container class for reconstructed shower shower_result = ReconstructedShowerContainer() # Convert the best fits direction and core to Horizon and ground systems and # copy to the shower container nominal = SkyCoord( x=fit_params[0] * u.rad, y=fit_params[1] * u.rad, frame=NominalFrame(origin=self.array_direction) ) horizon = nominal.transform_to(HorizonFrame()) shower_result.alt, shower_result.az = horizon.alt, horizon.az tilted = TiltedGroundFrame( x=fit_params[2] * u.m, y=fit_params[3] * u.m, pointing_direction=self.array_direction ) ground = project_to_ground(tilted) shower_result.core_x = ground.x shower_result.core_y = ground.y shower_result.is_valid = True # Currently no errors not availible to copy NaN shower_result.alt_uncert = np.nan shower_result.az_uncert = np.nan shower_result.core_uncert = np.nan # Copy reconstructed Xmax shower_result.h_max = fit_params[5] * self.get_shower_max(fit_params[0], fit_params[1], fit_params[2], fit_params[3], zenith.to(u.rad).value) shower_result.h_max *= np.cos(zenith) shower_result.h_max_uncert = errors[5] * shower_result.h_max shower_result.goodness_of_fit = like # Create a container class for reconstructed energy energy_result = ReconstructedEnergyContainer() # Fill with results energy_result.energy = fit_params[4] * u.TeV energy_result.energy_uncert = errors[4] * u.TeV energy_result.is_valid = True return shower_result, energy_result
def hillas_lines(moments, length, tel_coords, frame, array_pointing, tilted_frame, plane=None): angle_in = moments.psi.to_value(u.rad) # a = Point3D(1, 0, 0) # b = Point3D(np.cos(angle), np.sin(angle), 0) if frame == "tilted": angle = angle_in + np.pi / 2. # origin = Point3D(0, 0, 0) # line_a = Ray3D(origin, a) # line_b = Ray3D(origin, b) # angle_tilt = line_b.angle_between(line_a) # angle_tilted = (float(angle_tilt.evalf()) * u.rad).to_value(u.deg) a = np.array([1, 0, 0]) b = np.array([np.cos(angle), np.sin(angle), 0]) angle_tilt_np = u.Quantity(angle_util(a, b), u.rad).to_value(u.deg) psi = angle_tilt_np elif frame == "ground": # b_in_plane = plane.projection(b) # a_in_plane = plane.projection(a) # line_a_plane = Ray3D(origin, a_in_plane) # line_b_plane = Ray3D(origin, b_in_plane) # angle_gnd = line_b_plane.angle_between(line_a_plane) # angle_ground = (float(angle_gnd.evalf()) * u.rad).to_value(u.deg) # psi = angle_ground - array_pointing.az.to_value(u.deg) if angle_in < 0: angle_in = angle_in + np.pi angle = angle_in fix_a_in_plane = SkyCoord(x=1 * u.m, y=0 * u.m, frame=tilted_frame) fix_b_in_plane = SkyCoord(x=np.cos(angle) * u.m, y=np.sin(angle) * u.m, frame=tilted_frame) angle_fix_np = u.Quantity( angle_util( project_to_ground(fix_a_in_plane).cartesian.xyz.to_value(u.m), project_to_ground(fix_b_in_plane).cartesian.xyz.to_value(u.m)), u.rad).to_value(u.deg) psi = angle_fix_np - array_pointing.az.to_value(u.deg) + 90 cylinder = vtk.vtkCylinderSource() cylinder.SetResolution(4) cylinder.SetRadius(1) cylinder.SetHeight(length) cylinder.Update() rotate_cylinder = rotate_object(cylinder, 'z', psi) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(rotate_cylinder.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) transform = vtk.vtkTransform() transform.PostMultiply() transform.Translate(*tel_coords) if frame == "tilted": transform.RotateY(90 - array_pointing.alt.value) transform.RotateZ(-array_pointing.az.value) actor.SetUserTransform(transform) return actor
def predict(self, hillas_dict, subarray, 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) grd_coord = subarray.tel_coords tilt_coord = grd_coord.transform_to(tilted_frame) tel_ids = list(hillas_dict.keys()) tel_indices = subarray.tel_ids_to_indices(tel_ids) tel_x = { tel_id: tilt_coord.x[tel_index] for tel_id, tel_index in zip(tel_ids, tel_indices) } tel_y = { tel_id: tilt_coord.y[tel_index] for tel_id, tel_index in zip(tel_ids, tel_indices) } nom_frame = NominalFrame(origin=array_pointing) hillas_dict_mod = {} for tel_id, hillas in hillas_dict.items(): if isinstance(hillas, CameraHillasParametersContainer): focal_length = 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) else: telescope_frame = TelescopeFrame( telescope_pointing=telescopes_pointings[tel_id] ) cog_coords = SkyCoord( fov_lon=hillas.fov_lon, fov_lat=hillas.fov_lat, frame=telescope_frame, ) cog_coords_nom = cog_coords.transform_to(nom_frame) hillas_dict_mod[tel_id] = HillasParametersContainer( fov_lon=cog_coords_nom.fov_lon, fov_lat=cog_coords_nom.fov_lat, psi=hillas.psi, width=hillas.width, length=hillas.length, intensity=hillas.intensity, ) src_fov_lon, src_fov_lat, err_fov_lon, err_fov_lat = 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_fov_lon *= u.rad err_fov_lat *= u.rad nom = SkyCoord( fov_lon=src_fov_lon * u.rad, fov_lat=src_fov_lat * u.rad, frame=nom_frame ) sky_pos = nom.transform_to(array_pointing.frame) tilt = SkyCoord(x=core_x * u.m, y=core_y * u.m, frame=tilted_frame) grd = project_to_ground(tilt) x_max = self.reconstruct_xmax( nom.fov_lon, nom.fov_lat, tilt.x, tilt.y, hillas_dict_mod, tel_x, tel_y, 90 * u.deg - array_pointing.alt, ) src_error = np.sqrt(err_fov_lon ** 2 + err_fov_lat ** 2) result = ReconstructedGeometryContainer( alt=sky_pos.altaz.alt.to(u.rad), az=sky_pos.altaz.az.to(u.rad), core_x=grd.x, core_y=grd.y, core_uncert=u.Quantity(np.sqrt(core_err_x ** 2 + core_err_y ** 2), u.m), tel_ids=[h for h in hillas_dict_mod.keys()], average_intensity=np.mean([h.intensity for h in hillas_dict_mod.values()]), is_valid=True, alt_uncert=src_error.to(u.rad), az_uncert=src_error.to(u.rad), h_max=x_max, h_max_uncert=u.Quantity(np.nan * x_max.unit), goodness_of_fit=np.nan, ) return result
def predict(self, shower_seed, energy_seed): """Predict method for the ImPACT reconstructor. Used to calculate the reconstructed ImPACT shower geometry and energy. Parameters ---------- shower_seed: ReconstructedShowerContainer Seed shower geometry to be used in the fit energy_seed: ReconstructedEnergyContainer Seed energy to be used in fit Returns ------- ReconstructedShowerContainer, ReconstructedEnergyContainer: """ self.reset_interpolator() horizon_seed = SkyCoord(az=shower_seed.az, alt=shower_seed.alt, frame=AltAz()) nominal_seed = horizon_seed.transform_to(self.nominal_frame) source_x = nominal_seed.fov_lon.to_value(u.rad) source_y = nominal_seed.fov_lat.to_value(u.rad) ground = GroundFrame(x=shower_seed.core_x, y=shower_seed.core_y, z=0 * u.m) tilted = ground.transform_to( TiltedGroundFrame(pointing_direction=self.array_direction)) tilt_x = tilted.x.to(u.m).value tilt_y = tilted.y.to(u.m).value zenith = 90 * u.deg - self.array_direction.alt seeds = spread_line_seed( self.hillas_parameters, self.tel_pos_x, self.tel_pos_y, source_x, source_y, tilt_x, tilt_y, energy_seed.energy.value, shift_frac=[1], )[0] # Perform maximum likelihood fit fit_params, errors, like = self.minimise( params=seeds[0], step=seeds[1], limits=seeds[2], minimiser_name=self.minimiser_name, ) # Create a container class for reconstructed shower shower_result = ReconstructedGeometryContainer() # Convert the best fits direction and core to Horizon and ground systems and # copy to the shower container nominal = SkyCoord( fov_lon=fit_params[0] * u.rad, fov_lat=fit_params[1] * u.rad, frame=self.nominal_frame, ) horizon = nominal.transform_to(AltAz()) shower_result.alt, shower_result.az = horizon.alt, horizon.az tilted = TiltedGroundFrame( x=fit_params[2] * u.m, y=fit_params[3] * u.m, pointing_direction=self.array_direction, ) ground = project_to_ground(tilted) shower_result.core_x = ground.x shower_result.core_y = ground.y shower_result.is_valid = True # Currently no errors not available to copy NaN shower_result.alt_uncert = np.nan shower_result.az_uncert = np.nan shower_result.core_uncert = np.nan # Copy reconstructed Xmax shower_result.h_max = fit_params[5] * self.get_shower_max( fit_params[0], fit_params[1], fit_params[2], fit_params[3], zenith.to(u.rad).value, ) shower_result.h_max *= np.cos(zenith) shower_result.h_max_uncert = errors[5] * shower_result.h_max shower_result.goodness_of_fit = like # Create a container class for reconstructed energy energy_result = ReconstructedEnergyContainer() # Fill with results energy_result.energy = fit_params[4] * u.TeV energy_result.energy_uncert = errors[4] * u.TeV energy_result.is_valid = True return shower_result, energy_result
def predict(self, shower_seed, energy_seed): """ Parameters ---------- shower_seed: ReconstructedShowerContainer Seed shower geometry to be used in the fit energy_seed: ReconstructedEnergyContainer Seed energy to be used in fit Returns ------- ReconstructedShowerContainer, ReconstructedEnergyContainer: Reconstructed ImPACT shower geometry and energy """ horizon_seed = HorizonFrame(az=shower_seed.az, alt=shower_seed.alt) nominal_seed = horizon_seed.transform_to( NominalFrame(array_direction=self.array_direction)) print(nominal_seed) print(horizon_seed) print(self.array_direction) source_x = nominal_seed.x[0].to(u.rad).value source_y = nominal_seed.y[0].to(u.rad).value ground = GroundFrame(x=shower_seed.core_x, y=shower_seed.core_y, z=0 * u.m) tilted = ground.transform_to( TiltedGroundFrame(pointing_direction=self.array_direction)) tilt_x = tilted.x.to(u.m).value tilt_y = tilted.y.to(u.m).value lower_en_limit = energy_seed.energy * 0.5 en_seed = energy_seed.energy if lower_en_limit < 0.04 * u.TeV: lower_en_limit = 0.04 * u.TeV en_seed = 0.041 * u.TeV seed = (source_x, source_y, tilt_x, tilt_y, en_seed.value, 0.8) step = (0.001, 0.001, 10, 10, en_seed.value * 0.1, 0.1) limits = ((source_x - 0.01, source_x + 0.01), (source_y - 0.01, source_y + 0.01), (tilt_x - 100, tilt_x + 100), (tilt_y - 100, tilt_y + 100), (lower_en_limit.value, en_seed.value * 2), (0.5, 2)) fit_params, errors = self.minimise(params=seed, step=step, limits=limits, minimiser_name=self.minimiser_name) # container class for reconstructed showers ''' shower_result = ReconstructedShowerContainer() nominal = NominalFrame(x=fit_params[0] * u.rad, y=fit_params[1] * u.rad, array_direction=self.array_direction) horizon = nominal.transform_to(HorizonFrame()) shower_result.alt, shower_result.az = horizon.alt, horizon.az tilted = TiltedGroundFrame(x=fit_params[2] * u.m, y=fit_params[3] * u.m, pointing_direction=self.array_direction) ground = project_to_ground(tilted) shower_result.core_x = ground.x shower_result.core_y = ground.y shower_result.is_valid = True shower_result.alt_uncert = np.nan shower_result.az_uncert = np.nan shower_result.core_uncert = np.nan zenith = 90 * u.deg - self.array_direction.alt shower_result.h_max = fit_params[5] * \ self.get_shower_max(fit_params[0], fit_params[1], fit_params[2], fit_params[3], zenith.to(u.rad).value) shower_result.h_max_uncert = errors[5] * shower_result.h_max shower_result.goodness_of_fit = np.nan shower_result.tel_ids = list(self.image.keys()) energy_result = ReconstructedEnergyContainer() energy_result.energy = fit_params[4] * u.TeV energy_result.energy_uncert = errors[4] * u.TeV energy_result.is_valid = True energy_result.tel_ids = list(self.image.keys()) # Return interesting stuff return shower_result, energy_result
def predict(self, shower_seed, energy_seed): """ Parameters ---------- source_x: float Initial guess of source position in the nominal frame source_y: float Initial guess of source position in the nominal frame core_x: float Initial guess of the core position in the tilted system core_y: float Initial guess of the core position in the tilted system energy: float Initial guess of energy Returns ------- Shower object with fit results """ horizon_seed = HorizonFrame(az=shower_seed.az, alt=shower_seed.alt) nominal_seed = horizon_seed.transform_to( NominalFrame(array_direction=self.array_direction) ) source_x = nominal_seed.x.to(u.rad).value source_y = nominal_seed.y.to(u.rad).value ground = GroundFrame(x=shower_seed.core_x, y=shower_seed.core_y, z=0 * u.m) tilted = ground.transform_to( TiltedGroundFrame(pointing_direction=self.array_direction) ) tilt_x = tilted.x.to(u.m).value tilt_y = tilted.y.to(u.m).value lower_en_limit = energy_seed.energy * 0.1 if lower_en_limit < 0.04 * u.TeV: lower_en_limit = 0.04 * u.TeV # Create Minuit object with first guesses at parameters, strip away the # units as Minuit doesnt like them min = Minuit(self.get_likelihood, print_level=1, source_x=source_x, error_source_x=0.01 / 57.3, fix_source_x=False, limit_source_x=(source_x - 0.5 / 57.3, source_x + 0.5 / 57.3), source_y=source_y, error_source_y=0.01 / 57.3, fix_source_y=False, limit_source_y=(source_y - 0.5 / 57.3, source_y + 0.5 / 57.3), core_x=tilt_x, error_core_x=10, limit_core_x=(tilt_x - 200, tilt_x + 200), core_y=tilt_y, error_core_y=10, limit_core_y=(tilt_y - 200, tilt_y + 200), energy=energy_seed.energy.value, error_energy=energy_seed.energy.value * 0.05, limit_energy=(lower_en_limit.value, energy_seed.energy.value * 10.), x_max_scale=1, error_x_max_scale=0.1, limit_x_max_scale=(0.5, 2), fix_x_max_scale=False, errordef=1) min.tol *= 1000 min.strategy = 0 # Perform minimisation migrad = min.migrad() fit_params = min.values errors = min.errors # print(migrad) # print(min.minos()) # container class for reconstructed showers ''' shower_result = ReconstructedShowerContainer() nominal = NominalFrame(x=fit_params["source_x"] * u.rad, y=fit_params["source_y"] * u.rad, array_direction=self.array_direction) horizon = nominal.transform_to(HorizonFrame()) shower_result.alt, shower_result.az = horizon.alt, horizon.az tilted = TiltedGroundFrame(x=fit_params["core_x"] * u.m, y=fit_params["core_y"] * u.m, pointing_direction=self.array_direction) ground = project_to_ground(tilted) shower_result.core_x = ground.x shower_result.core_y = ground.y shower_result.is_valid = True shower_result.alt_uncert = np.nan shower_result.az_uncert = np.nan shower_result.core_uncert = np.nan zenith = 90 * u.deg - self.array_direction[0] shower_result.h_max = fit_params["x_max_scale"] * \ self.get_shower_max(fit_params["source_x"], fit_params["source_y"], fit_params["core_x"], fit_params["core_y"], zenith.to(u.rad).value) shower_result.h_max_uncert = errors["x_max_scale"] * shower_result.h_max shower_result.goodness_of_fit = np.nan shower_result.tel_ids = list(self.image.keys()) energy_result = ReconstructedEnergyContainer() energy_result.energy = fit_params["energy"] * u.TeV energy_result.energy_uncert = errors["energy"] * u.TeV energy_result.is_valid = True energy_result.tel_ids = list(self.image.keys()) # Return interesting stuff return shower_result, energy_result
def predict(self, shower_seed, energy_seed): """ Parameters ---------- source_x: float Initial guess of source position in the nominal frame source_y: float Initial guess of source position in the nominal frame core_x: float Initial guess of the core position in the tilted system core_y: float Initial guess of the core position in the tilted system energy: float Initial guess of energy Returns ------- Shower object with fit results """ horizon_seed = HorizonFrame(az=shower_seed.az, alt=shower_seed.alt) nominal_seed = horizon_seed.transform_to( NominalFrame(array_direction=self.array_direction)) source_x = nominal_seed.x.to(u.rad).value source_y = nominal_seed.y.to(u.rad).value ground = GroundFrame(x=shower_seed.core_x, y=shower_seed.core_y, z=0 * u.m) tilted = ground.transform_to( TiltedGroundFrame(pointing_direction=self.array_direction)) tilt_x = tilted.x.to(u.m).value tilt_y = tilted.y.to(u.m).value lower_en_limit = energy_seed.energy * 0.1 if lower_en_limit < 0.04 * u.TeV: lower_en_limit = 0.04 * u.TeV # Create Minuit object with first guesses at parameters, strip away the # units as Minuit doesnt like them min = Minuit(self.get_likelihood, print_level=1, source_x=source_x, error_source_x=0.01 / 57.3, fix_source_x=False, limit_source_x=(source_x - 0.5 / 57.3, source_x + 0.5 / 57.3), source_y=source_y, error_source_y=0.01 / 57.3, fix_source_y=False, limit_source_y=(source_y - 0.5 / 57.3, source_y + 0.5 / 57.3), core_x=tilt_x, error_core_x=10, limit_core_x=(tilt_x - 200, tilt_x + 200), core_y=tilt_y, error_core_y=10, limit_core_y=(tilt_y - 200, tilt_y + 200), energy=energy_seed.energy.value, error_energy=energy_seed.energy.value * 0.05, limit_energy=(lower_en_limit.value, energy_seed.energy.value * 10.), x_max_scale=1, error_x_max_scale=0.1, limit_x_max_scale=(0.5, 2), fix_x_max_scale=False, errordef=1) min.tol *= 1000 min.strategy = 0 # Perform minimisation migrad = min.migrad() fit_params = min.values errors = min.errors # print(migrad) # print(min.minos()) # container class for reconstructed showers ''' shower_result = ReconstructedShowerContainer() nominal = NominalFrame(x=fit_params["source_x"] * u.rad, y=fit_params["source_y"] * u.rad, array_direction=self.array_direction) horizon = nominal.transform_to(HorizonFrame()) shower_result.alt, shower_result.az = horizon.alt, horizon.az tilted = TiltedGroundFrame(x=fit_params["core_x"] * u.m, y=fit_params["core_y"] * u.m, pointing_direction=self.array_direction) ground = project_to_ground(tilted) shower_result.core_x = ground.x shower_result.core_y = ground.y shower_result.is_valid = True shower_result.alt_uncert = np.nan shower_result.az_uncert = np.nan shower_result.core_uncert = np.nan zenith = 90 * u.deg - self.array_direction[0] shower_result.h_max = fit_params["x_max_scale"] * \ self.get_shower_max(fit_params["source_x"], fit_params["source_y"], fit_params["core_x"], fit_params["core_y"], zenith.to(u.rad).value) shower_result.h_max_uncert = errors["x_max_scale"] * shower_result.h_max shower_result.goodness_of_fit = np.nan shower_result.tel_ids = list(self.image.keys()) energy_result = ReconstructedEnergyContainer() energy_result.energy = fit_params["energy"] * u.TeV energy_result.energy_uncert = errors["energy"] * u.TeV energy_result.is_valid = True energy_result.tel_ids = list(self.image.keys()) # Return interesting stuff return shower_result, energy_result
def predict(self, shower_seed, energy_seed): """ Parameters ---------- shower_seed: ReconstructedShowerContainer Seed shower geometry to be used in the fit energy_seed: ReconstructedEnergyContainer Seed energy to be used in fit Returns ------- ReconstructedShowerContainer, ReconstructedEnergyContainer: Reconstructed ImPACT shower geometry and energy """ horizon_seed = HorizonFrame(az=shower_seed.az, alt=shower_seed.alt) nominal_seed = horizon_seed.transform_to(NominalFrame(array_direction=self.array_direction)) print(nominal_seed) print(horizon_seed) print(self.array_direction) source_x = nominal_seed.x[0].to(u.rad).value source_y = nominal_seed.y[0].to(u.rad).value ground = GroundFrame(x=shower_seed.core_x, y=shower_seed.core_y, z=0 * u.m) tilted = ground.transform_to( TiltedGroundFrame(pointing_direction=self.array_direction) ) tilt_x = tilted.x.to(u.m).value tilt_y = tilted.y.to(u.m).value lower_en_limit = energy_seed.energy * 0.5 en_seed = energy_seed.energy if lower_en_limit < 0.04 * u.TeV: lower_en_limit = 0.04 * u.TeV en_seed = 0.041 * u.TeV seed = (source_x, source_y, tilt_x, tilt_y, en_seed.value, 0.8) step = (0.001, 0.001, 10, 10, en_seed.value*0.1, 0.1) limits = ((source_x-0.01, source_x+0.01), (source_y-0.01, source_y+0.01), (tilt_x-100, tilt_x+100), (tilt_y-100, tilt_y+100), (lower_en_limit.value, en_seed.value*2), (0.5,2)) fit_params, errors = self.minimise(params=seed, step=step, limits=limits, minimiser_name=self.minimiser_name) # container class for reconstructed showers ''' shower_result = ReconstructedShowerContainer() nominal = NominalFrame(x=fit_params[0] * u.rad, y=fit_params[1] * u.rad, array_direction=self.array_direction) horizon = nominal.transform_to(HorizonFrame()) shower_result.alt, shower_result.az = horizon.alt, horizon.az tilted = TiltedGroundFrame(x=fit_params[2] * u.m, y=fit_params[3] * u.m, pointing_direction=self.array_direction) ground = project_to_ground(tilted) shower_result.core_x = ground.x shower_result.core_y = ground.y shower_result.is_valid = True shower_result.alt_uncert = np.nan shower_result.az_uncert = np.nan shower_result.core_uncert = np.nan zenith = 90*u.deg - self.array_direction.alt shower_result.h_max = fit_params[5] * \ self.get_shower_max(fit_params[0], fit_params[1], fit_params[2], fit_params[3], zenith.to(u.rad).value) shower_result.h_max_uncert = errors[5] * shower_result.h_max shower_result.goodness_of_fit = np.nan shower_result.tel_ids = list(self.image.keys()) energy_result = ReconstructedEnergyContainer() energy_result.energy = fit_params[4] * u.TeV energy_result.energy_uncert = errors[4] * u.TeV energy_result.is_valid = True energy_result.tel_ids = list(self.image.keys()) # Return interesting stuff return shower_result, energy_result
def estimate_core_position(self, event, hillas_dict, array_pointing, corrected_angle_dict, hillas_planes): """ Estimate the core position by intersection the major ellipse lines of each telescope. Parameters ----------- hillas_dict: dict[HillasContainer] dictionary of hillas moments array_pointing: SkyCoord[HorizonFrame] Pointing direction of the array Returns ----------- core_x: u.Quantity estimated x position of impact core_y: u.Quantity estimated y position of impact Notes ----- The part of the algorithm taking into account divergent pointing mode and the usage of a corrected psi angle is explained in [gasparetto]_ section 7.1.4. """ # Since psi has been recalculated in the fake CameraFrame # it doesn't need any further corrections because it is now independent # of both pointing and cleaning/parametrization frame. # This angle will be used to visualize the telescope-wise directions of # the shower core the ground. psi_core = corrected_angle_dict # Record these values for tel_id in hillas_dict.keys(): event.dl1.tel[tel_id].parameters.core.psi = psi_core[tel_id] # Transform them for numpy psi = u.Quantity(list(psi_core.values())) # Estimate the position of the shower's core # from the TiltedFram to the GroundFrame z = np.zeros(len(psi)) uvw_vectors = np.column_stack( [np.cos(psi).value, np.sin(psi).value, z]) tilted_frame = TiltedGroundFrame(pointing_direction=array_pointing) ground_frame = GroundFrame() positions = [(SkyCoord( *plane.pos, frame=ground_frame).transform_to(tilted_frame).cartesian.xyz) for plane in hillas_planes.values()] core_position = line_line_intersection_3d(uvw_vectors, positions) core_pos_tilted = SkyCoord(x=core_position[0] * u.m, y=core_position[1] * u.m, frame=tilted_frame) core_pos = project_to_ground(core_pos_tilted) return core_pos.x, core_pos.y
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