def sky_to_camera(alt, az, focal, pointing_alt, pointing_az): """ Coordinate transform from aky position (alt, az) (in angles) to camera coordinates (x, y) in distance Parameters ---------- alt: astropy Quantity az: astropy Quantity focal: astropy Quantity pointing_alt: pointing altitude in angle unit pointing_az: pointing altitude in angle unit Returns ------- """ pointing_direction = HorizonFrame(alt=pointing_alt, az=pointing_az) event_direction = HorizonFrame(alt=alt, az=az) nom_frame = NominalFrame(array_direction=pointing_direction, pointing_direction=pointing_direction) event_dir_nom = event_direction.transform_to(nom_frame) camera_pos = NominalFrame( pointing_direction=pointing_direction, x=event_dir_nom.x.to(u.rad).value * focal, y=event_dir_nom.y.to(u.rad).value * focal, ) # return focal * (event_dir_nom.x.to(u.rad).value, event_dir_nom.y.to(u.rad).value) return camera_pos
def camera_to_sky(pos_x, pos_y, focal, pointing_alt, pointing_az): """ Parameters ---------- pos_x: X coordinate in camera (distance) pos_y: Y coordinate in camera (distance) focal: telescope focal (distance) pointing_alt: pointing altitude in angle unit pointing_az: pointing altitude in angle unit Returns ------- (alt, az) Example: -------- import astropy.units as u import numpy as np x = np.array([1,0]) * u.m y = np.array([1,1]) * u.m """ pointing_direction = HorizonFrame(alt=pointing_alt, az=pointing_az) source_pos_in_camera = NominalFrame( array_direction=pointing_direction, pointing_direction=pointing_direction, x=pos_x / focal * u.rad, y=pos_y / focal * u.rad, ) return source_pos_in_camera.transform_to(pointing_direction)
def nominal_to_altaz(): t = np.zeros(10) t[5] = 1 nom = NominalFrame(x=t * u.deg, y=t * u.deg, array_direction=[75 * u.deg, 180 * u.deg]) alt_az = nom.transform_to(HorizonFrame) print("AltAz Coordinate", alt_az)
def get_event_pos_in_sky(hillas, disp, tel, pointing_direction): side = 1 # TODO: method to guess side focal = tel.optics.equivalent_focal_length source_pos_in_camera = NominalFrame(array_direction=pointing_direction, pointing_direction=pointing_direction, x=(hillas.x + side * disp * np.cos(hillas.phi)) / focal * u.rad, y=(hillas.y + side * disp * np.sin(hillas.phi)) / focal * u.rad ) horizon_frame = HorizonFrame(alt=pointing_direction.alt, az=pointing_direction.az) return source_pos_in_camera.transform_to(horizon_frame)
def test_intersection_nominal_reconstruction(): """ Testing the reconstruction of the position in the nominal frame with a three-telescopes system. This is done using a squared configuration, of which the impact point occupies a vertex, ad the three telescopes the other three vertices. """ hill_inter = HillasIntersection() delta = 1.0 * u.m horizon_frame = AltAz() altitude = 70 * u.deg azimuth = 10 * u.deg array_direction = SkyCoord(alt=altitude, az=azimuth, frame=horizon_frame) nominal_frame = NominalFrame(origin=array_direction) focal_length = 28 * u.m camera_frame = CameraFrame(focal_length=focal_length, telescope_pointing=array_direction) cog_coords_camera_1 = SkyCoord(x=delta, y=0 * u.m, frame=camera_frame) cog_coords_camera_2 = SkyCoord(x=delta / 0.7, y=delta / 0.7, frame=camera_frame) cog_coords_camera_3 = SkyCoord(x=0 * u.m, y=delta, frame=camera_frame) cog_coords_nom_1 = cog_coords_camera_1.transform_to(nominal_frame) cog_coords_nom_2 = cog_coords_camera_2.transform_to(nominal_frame) cog_coords_nom_3 = cog_coords_camera_3.transform_to(nominal_frame) # x-axis is along the altitude and y-axis is along the azimuth hillas_1 = HillasParametersContainer(x=cog_coords_nom_1.delta_alt, y=cog_coords_nom_1.delta_az, intensity=100, psi=0 * u.deg) hillas_2 = HillasParametersContainer(x=cog_coords_nom_2.delta_alt, y=cog_coords_nom_2.delta_az, intensity=100, psi=45 * u.deg) hillas_3 = HillasParametersContainer(x=cog_coords_nom_3.delta_alt, y=cog_coords_nom_3.delta_az, intensity=100, psi=90 * u.deg) hillas_dict = {1: hillas_1, 2: hillas_2, 3: hillas_3} reco_nominal = hill_inter.reconstruct_nominal(hillas_parameters=hillas_dict) nominal_pos = SkyCoord( delta_az=u.Quantity(reco_nominal[0], u.rad), delta_alt=u.Quantity(reco_nominal[1], u.rad), frame=nominal_frame ) np.testing.assert_allclose(nominal_pos.altaz.az.to_value(u.deg), azimuth.to_value(u.deg), atol=1e-8) np.testing.assert_allclose(nominal_pos.altaz.alt.to_value(u.deg), altitude.to_value(u.deg), atol=1e-8)
def sky_to_camera(alt, az, focal, pointing_alt, pointing_az): """ Coordinate transform from aky position (alt, az) (in angles) to camera coordinates (x, y) in distance Parameters ---------- alt: astropy Quantity az: astropy Quantity focal: astropy Quantity pointing_alt: pointing altitude in angle unit pointing_az: pointing altitude in angle unit Returns ------- """ pointing_direction = SkyCoord(alt=pointing_alt, az=pointing_az, frame=horizon_frame) camera_frame = CameraFrame(focal_length=focal, telescope_pointing=pointing_direction) event_direction = SkyCoord(alt=alt, az=az, frame=horizon_frame) nom_frame = NominalFrame(origin=pointing_direction, ) camera_pos = event_direction.transform_to(camera_frame) return camera_pos
def get_muon_center(geom, equivalent_focal_length): """ Get the x,y coordinates of the center of the muon ring in the NominalFrame Paramenters --------- geom: CameraGeometry equivalent_focal_length: Focal length of the telescope Returns --------- x, y: `floats` coordinates in the NominalFrame """ x, y = geom.pix_x, geom.pix_y telescope_pointing = SkyCoord(alt=70 * u.deg, az=0 * u.deg, frame=AltAz()) camera_coord = SkyCoord(x=x, y=y, frame=CameraFrame( focal_length=equivalent_focal_length, rotation=geom.pix_rotation, telescope_pointing=telescope_pointing)) nom_coord = camera_coord.transform_to( NominalFrame(origin=telescope_pointing)) x = nom_coord.delta_az.to(u.deg) y = nom_coord.delta_alt.to(u.deg) return x, y
def get_prediction(self, tel_id, shower_reco, energy_reco): horizon_seed = HorizonFrame(az=shower_reco.az, alt=shower_reco.alt) nominal_seed = horizon_seed.transform_to( NominalFrame(array_direction=horizon_seed)) source_x = nominal_seed.x.to(u.rad).value source_y = nominal_seed.y.to(u.rad).value ground = GroundFrame(x=shower_reco.core_x, y=shower_reco.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 x_max = shower_reco.h_max / np.cos(zenith) # Calculate expected Xmax given this energy x_max_exp = guess_shower_depth(energy_reco.energy) # Convert to binning of Xmax, addition of 100 can probably be removed x_max_bin = x_max - x_max_exp # Check for range if x_max_bin > 250 * (u.g * u.cm**-2): x_max_bin = 250 * (u.g * u.cm**-2) if x_max_bin < -250 * (u.g * u.cm**-2): x_max_bin = -250 * (u.g * u.cm**-2) x_max_bin = x_max_bin.value impact = np.sqrt( pow(self.tel_pos_x[tel_id] - tilt_x, 2) + pow(self.tel_pos_y[tel_id] - tilt_y, 2)) phi = np.arctan2((self.tel_pos_y[tel_id] - tilt_y), (self.tel_pos_x[tel_id] - tilt_x)) pix_x_rot, pix_y_rot = self.rotate_translate(self.pixel_x[tel_id] * -1, self.pixel_y[tel_id], source_x, source_y, phi) prediction = self.image_prediction(self.type[tel_id], (90 * u.deg) - shower_reco.alt, shower_reco.az, energy_reco.energy.value, impact, x_max_bin, pix_x_rot * (180 / math.pi), pix_y_rot * (180 / math.pi)) prediction *= self.scale[self.type[tel_id]] # prediction *= self.pixel_area[tel_id] prediction[prediction < 0] = 0 prediction[np.isnan(prediction)] = 0 return prediction
def test_array_draw(): filename = get_dataset("gamma_test.simtel.gz") cam_geom = {} source = hessio_event_source(filename, max_events=2) r1 = HESSIOR1Calibrator() dl0 = CameraDL0Reducer() calibrator = CameraDL1Calibrator() for event in source: array_pointing = SkyCoord( event.mcheader.run_array_direction[1] * u.rad, event.mcheader.run_array_direction[0] * u.rad, frame=AltAz) # array_view = ArrayPlotter(instrument=event.inst, # system=TiltedGroundFrame( # pointing_direction=array_pointing)) hillas_dict = {} r1.calibrate(event) dl0.reduce(event) calibrator.calibrate(event) # calibrate the events # store MC pointing direction for the array for tel_id in event.dl0.tels_with_data: pmt_signal = event.dl1.tel[tel_id].image[0] geom = deepcopy(event.inst.subarray.tel[tel_id].camera) fl = event.inst.subarray.tel[tel_id].optics.equivalent_focal_length # Transform the pixels positions into nominal coordinates camera_coord = CameraFrame(x=geom.pix_x, y=geom.pix_y, z=np.zeros(geom.pix_x.shape) * u.m, focal_length=fl, rotation=90 * u.deg - geom.cam_rotation) nom_coord = camera_coord.transform_to( NominalFrame(array_direction=array_pointing, pointing_direction=array_pointing)) geom.pix_x = nom_coord.x geom.pix_y = nom_coord.y mask = tailcuts_clean(geom, pmt_signal, picture_thresh=10., boundary_thresh=5.) try: moments = hillas_parameters(geom, pmt_signal * mask) hillas_dict[tel_id] = moments nom_coord = NominalPlotter(hillas_parameters=hillas_dict, draw_axes=True) nom_coord.draw_array() except HillasParameterizationError as e: print(e) continue
def draw_tilted_surface(self, shower_seed, energy_seed, bins=50, core_range=100 * u.m): """ Simple reconstruction for evaluating the likelihood in a grid across the nominal system, fixing all values but the core position of the gamma rays. Useful for checking the reconstruction performance of the algorithm Parameters ---------- shower_seed: ReconstructedShowerContainer Best fit ImPACT shower geometry energy_seed: ReconstructedEnergyContainer Best fit ImPACT energy bins: int Number of bins in surface evaluation nominal_range: Quantity Range over which to create likelihood surface Returns ------- ndarray, ndarray, ndarray: Bin centres in X and Y coordinates and the values of the likelihood at each position """ 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[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) tilt_y = tilted.y.to(u.m) x_ground_list = np.linspace(tilt_x - core_range, tilt_x + core_range, num=bins) y_ground_list = np.linspace(tilt_y - core_range, tilt_y + core_range, num=bins) w = np.zeros([bins, bins]) zenith = 90*u.deg - self.array_direction.alt for xb in range(bins): for yb in range(bins): x_max_scale = shower_seed.h_max / \ self.get_shower_max(source_x, source_y, x_ground_list[xb].value, y_ground_list[yb].value, zenith.to(u.rad).value) w[xb][yb] = self.get_likelihood(source_x, source_y, x_ground_list[xb].value, y_ground_list[yb].value, energy_seed.energy.value, x_max_scale) return x_ground_list, y_ground_list, w
def nominal_to_altaz(): nom = SkyCoord( x=0 * u.deg, y=0 * u.deg, frame=NominalFrame(origin=AltAz(alt=75 * u.deg, az=180 * u.deg))) alt_az = nom.transform_to(AltAz()) print("HorizonCoordinate", alt_az)
def test_intersection_xmax_reco(): """ Test the reconstruction of xmax with two LSTs that are pointing at zenith = 0. The telescopes are places along the x and y axis at the same distance from the center. The impact point is hard-coded to be happening in the center of this cartesian system. """ hill_inter = HillasIntersection() horizon_frame = AltAz() zen_pointing = 10 * u.deg array_direction = SkyCoord(alt=90 * u.deg - zen_pointing, az=0 * u.deg, frame=horizon_frame) nom_frame = NominalFrame(origin=array_direction) source_sky_pos_reco = SkyCoord(alt=90 * u.deg - zen_pointing, az=0 * u.deg, frame=horizon_frame) nom_pos_reco = source_sky_pos_reco.transform_to(nom_frame) delta = 1.0 * u.m # LST focal length focal_length = 28 * u.m hillas_dict = { 1: HillasParametersContainer( x=-(delta / focal_length) * u.rad, y=((0 * u.m) / focal_length) * u.rad, intensity=1, ), 2: HillasParametersContainer( x=((0 * u.m) / focal_length) * u.rad, y=-(delta / focal_length) * u.rad, intensity=1, ), } x_max = hill_inter.reconstruct_xmax( source_x=nom_pos_reco.fov_lon, source_y=nom_pos_reco.fov_lat, core_x=0 * u.m, core_y=0 * u.m, hillas_parameters=hillas_dict, tel_x={ 1: (150 * u.m), 2: (0 * u.m) }, tel_y={ 1: (0 * u.m), 2: (150 * u.m) }, zen=zen_pointing, ) print(x_max)
def cam_to_nom(): pix = [np.ones(2048), np.ones(2048), np.zeros(2048)] * u.m camera_coord = CameraFrame(pix) # In this case we bypass the telescope system nom_coord = camera_coord.transform_to( NominalFrame(array_direction=[75 * u.deg, 180 * u.deg], pointing_direction=[70 * u.deg, 180 * u.deg], focal_length=15 * u.m)) print("Nominal Coordinate", nom_coord)
def __init__(self, config, tool, event, **kwargs): super().__init__(config=config, tool=tool, **kwargs) self.camera_geom_dict = {} self.nominal_geom_dict = {} self.inst = event.inst array_pointing = HorizonFrame( alt=event.mcheader.run_array_direction[1] * u.rad, az=event.mcheader.run_array_direction[0] * u.rad) self.nom_system = NominalFrame(array_direction=array_pointing, pointing_direction=array_pointing)
def get_prediction(self, tel_id, shower_reco, energy_reco): horizon_seed = HorizonFrame(az=shower_reco.az, alt=shower_reco.alt) nominal_seed = horizon_seed.transform_to( NominalFrame(array_direction=horizon_seed)) source_x = nominal_seed.x.to(u.rad).value source_y = nominal_seed.y.to(u.rad).value print(self.array_direction[0]) ground = GroundFrame(x=shower_reco.core_x, y=shower_reco.core_y, z=0 * u.m) tilted = ground.transform_to( TiltedGroundFrame(pointing_direction=HorizonFrame( alt=self.array_direction[0], az=self.array_direction[1]))) tilt_x = tilted.x.to(u.m).value tilt_y = tilted.y.to(u.m).value zenith = 90 * u.deg - self.array_direction[0] azimuth = self.array_direction[1] x_max_exp = 300 + 93 * np.log10(energy_reco.energy.value) x_max = shower_reco.h_max / np.cos(zenith) # Convert to binning of Xmax, addition of 100 can probably be removed x_max_bin = x_max.value - x_max_exp if x_max_bin > 100: x_max_bin = 100 if x_max_bin < -100: x_max_bin = -100 impact = np.sqrt( pow(self.tel_pos_x[tel_id] - tilt_x, 2) + pow(self.tel_pos_y[tel_id] - tilt_y, 2)) phi = np.arctan2((self.tel_pos_y[tel_id] - tilt_y), (self.tel_pos_x[tel_id] - tilt_x)) pix_x_rot, pix_y_rot = self.rotate_translate(self.pixel_x[tel_id] * -1, self.pixel_y[tel_id], source_x, source_y, phi) prediction = self.image_prediction(self.type[tel_id], 20 * u.deg, 0 * u.deg, energy_reco.energy.value, impact, x_max_bin, pix_x_rot * (180 / math.pi), pix_y_rot * (180 / math.pi)) prediction *= self.scale[self.type[tel_id]] prediction[prediction < 0] = 0 prediction[np.isnan(prediction)] = 0 return prediction
def test_cam_to_nominal(): from ctapipe.coordinates import CameraFrame, NominalFrame telescope_pointing = SkyCoord(alt=70 * u.deg, az=0 * u.deg, frame=AltAz()) array_pointing = SkyCoord(alt=72 * u.deg, az=0 * u.deg, frame=AltAz()) cam_frame = CameraFrame(focal_length=28 * u.m, telescope_pointing=telescope_pointing) cam = SkyCoord(x=0.5 * u.m, y=0.1 * u.m, frame=cam_frame) nom_frame = NominalFrame(origin=array_pointing) cam.transform_to(nom_frame)
def to_nominal_frame(self, alt, az): alt_LST = 70 # deg if self.plike: alt_LST = 69.6 # deg az_LST = 180 # deg point = AltAz(alt=alt_LST * u.deg, az=az_LST * u.deg) # alt = np.array(indexes['alt']) # altitude # az = np.array(indexes['az']) # azimuth # print("\nalt shape: {}, az shape: {}".format(indexes['alt'].shape, indexes['az'].shape)) src = AltAz(alt=alt * u.deg, az=az * u.deg) source_direction = src.transform_to(NominalFrame(origin=point)) delta_alt = source_direction.delta_alt.deg delta_az = source_direction.delta_az.deg return delta_alt, delta_az
def cam_to_nom(): pix = [np.ones(2048), np.ones(2048), np.zeros(2048)] * u.m camera_coord = CameraFrame(pix, focal_length=15 * u.m) # In this case we bypass the telescope system nom_coord = camera_coord.transform_to( NominalFrame( pointing_direction=HorizonFrame(alt=70 * u.deg, az=180 * u.deg), array_direction=HorizonFrame(alt=75 * u.deg, az=180 * u.deg) ) ) alt_az = camera_coord.transform_to( HorizonFrame( pointing_direction=HorizonFrame(alt=70 * u.deg, az=180 * u.deg), array_direction=HorizonFrame(alt=75 * u.deg, az=180 * u.deg) ) ) print("Nominal Coordinate", nom_coord) print("AltAz coordinate", alt_az)
def cam_to_nom(): pix_x = np.ones(2048) * u.m pix_y = np.ones(2048) * u.m pointing_direction = SkyCoord(alt=70 * u.deg, az=180 * u.deg, frame=AltAz()) camera_frame = CameraFrame(focal_length=15 * u.m, telescope_pointing=pointing_direction) camera_coord = SkyCoord(pix_x, pix_y, frame=camera_frame) # In this case we bypass the telescope system nominal_frame = NominalFrame(origin=AltAz(alt=75 * u.deg, az=180 * u.deg)) nom_coord = camera_coord.transform_to(nominal_frame) horizon = camera_coord.transform_to(AltAz()) print("Nominal Coordinate", nom_coord) print("Horizon coordinate", horizon)
def get_event_pos_in_camera(event, tel): """ Return the position of the source in the camera frame Parameters ---------- event: `ctapipe.io.containers.DataContainer` tel: `ctapipe.instruement.telescope.TelescopeDescription` Returns ------- (x, y) (float, float): position in the camera """ array_pointing = HorizonFrame(alt=event.mcheader.run_array_direction[1], az=event.mcheader.run_array_direction[0]) event_direction = HorizonFrame(alt=event.mc.alt.to(u.rad), az=event.mc.az.to(u.rad)) nom_frame = NominalFrame(array_direction=array_pointing, pointing_direction=array_pointing) event_dir_nom = event_direction.transform_to(nom_frame) focal = tel.optics.equivalent_focal_length return focal * (event_dir_nom.x.to(u.rad).value, event_dir_nom.y.to(u.rad).value)
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_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 plot_muon_event(event, muonparams, args=None): if muonparams['MuonRingParams'] is not None: # Plot the muon event and overlay muon parameters fig = plt.figure(figsize=(16, 7)) colorbar = None colorbar2 = None #for tel_id in event.dl0.tels_with_data: for tel_id in muonparams['TelIds']: idx = muonparams['TelIds'].index(tel_id) if not muonparams['MuonRingParams'][idx]: continue #otherwise... npads = 2 # Only create two pads if there is timing information extracted # from the calibration ax1 = fig.add_subplot(1, npads, 1) plotter = CameraPlotter(event) image = event.dl1.tel[tel_id].image[0] geom = event.inst.subarray.tel[tel_id].camera tailcuts = (5., 7.) # Try a higher threshold for if geom.cam_id == 'FlashCam': tailcuts = (10., 12.) clean_mask = tailcuts_clean(geom, image, picture_thresh=tailcuts[0], boundary_thresh=tailcuts[1]) signals = image * clean_mask #print("Ring Centre in Nominal Coords:",muonparams[0].ring_center_x,muonparams[0].ring_center_y) muon_incl = np.sqrt( muonparams['MuonRingParams'][idx].ring_center_x**2. + muonparams['MuonRingParams'][idx].ring_center_y**2.) muon_phi = np.arctan( muonparams['MuonRingParams'][idx].ring_center_y / muonparams['MuonRingParams'][idx].ring_center_x) rotr_angle = geom.pix_rotation # if event.inst.optical_foclen[tel_id] > 10.*u.m and # event.dl0.tel[tel_id].num_pixels != 1764: if geom.cam_id == 'LSTCam' or geom.cam_id == 'NectarCam': #print("Resetting the rotation angle") rotr_angle = 0. * u.deg # Convert to camera frame (centre & radius) altaz = HorizonFrame(alt=event.mc.alt, az=event.mc.az) ring_nominal = NominalFrame( x=muonparams['MuonRingParams'][idx].ring_center_x, y=muonparams['MuonRingParams'][idx].ring_center_y, array_direction=altaz, pointing_direction=altaz) # embed() ring_camcoord = ring_nominal.transform_to( CameraFrame(pointing_direction=altaz, focal_length=event.inst.optical_foclen[tel_id], rotation=rotr_angle)) centroid_rad = np.sqrt(ring_camcoord.y**2 + ring_camcoord.x**2) centroid = (ring_camcoord.x.value, ring_camcoord.y.value) ringrad_camcoord = muonparams['MuonRingParams'][idx].ring_radius.to(u.rad) \ * event.inst.optical_foclen[tel_id] * 2. # But not FC? px, py = event.inst.pixel_pos[tel_id] flen = event.inst.optical_foclen[tel_id] camera_coord = CameraFrame(x=px, y=py, focal_length=flen, rotation=geom.pix_rotation) nom_coord = camera_coord.transform_to( NominalFrame(array_direction=altaz, pointing_direction=altaz)) px = nom_coord.x.to(u.deg) py = nom_coord.y.to(u.deg) dist = np.sqrt( np.power(px - muonparams['MuonRingParams'][idx].ring_center_x, 2) + np.power(py - muonparams['MuonRingParams'][idx].ring_center_y, 2)) ring_dist = np.abs(dist - muonparams['MuonRingParams'][idx].ring_radius) pixRmask = ring_dist < muonparams['MuonRingParams'][ idx].ring_radius * 0.4 #if muonparams[1] is not None: if muonparams['MuonIntensityParams'][idx] is not None: signals *= muonparams['MuonIntensityParams'][idx].mask camera1 = plotter.draw_camera(tel_id, signals, ax1) cmaxmin = (max(signals) - min(signals)) cmin = min(signals) if not cmin: cmin = 1. if not cmaxmin: cmaxmin = 1. cmap_charge = colors.LinearSegmentedColormap.from_list( 'cmap_c', [(0 / cmaxmin, 'darkblue'), (np.abs(cmin) / cmaxmin, 'black'), (2.0 * np.abs(cmin) / cmaxmin, 'blue'), (2.5 * np.abs(cmin) / cmaxmin, 'green'), (1, 'yellow')]) camera1.pixels.set_cmap(cmap_charge) if not colorbar: camera1.add_colorbar(ax=ax1, label=" [photo-electrons]") colorbar = camera1.colorbar else: camera1.colorbar = colorbar camera1.update(True) camera1.add_ellipse(centroid, ringrad_camcoord.value, ringrad_camcoord.value, 0., 0., color="red") if muonparams['MuonIntensityParams'][idx] is not None: # continue #Comment this...(should ringwidthfrac also be *0.5?) ringwidthfrac = muonparams['MuonIntensityParams'][ idx].ring_width / muonparams['MuonRingParams'][ idx].ring_radius ringrad_inner = ringrad_camcoord * (1. - ringwidthfrac) ringrad_outer = ringrad_camcoord * (1. + ringwidthfrac) camera1.add_ellipse(centroid, ringrad_inner.value, ringrad_inner.value, 0., 0., color="magenta") camera1.add_ellipse(centroid, ringrad_outer.value, ringrad_outer.value, 0., 0., color="magenta") npads = 2 ax2 = fig.add_subplot(1, npads, npads) pred = muonparams['MuonIntensityParams'][idx].prediction if len(pred) != np.sum( muonparams['MuonIntensityParams'][idx].mask): print("Warning! Lengths do not match...len(pred)=", len(pred), "len(mask)=", np.sum(muonparams['MuonIntensityParams'][idx].mask)) # Numpy broadcasting - fill in the shape plotpred = np.zeros(image.shape) plotpred[muonparams['MuonIntensityParams'][idx].mask == True] = pred camera2 = plotter.draw_camera(tel_id, plotpred, ax2) if np.isnan(max(plotpred)) or np.isnan(min(plotpred)): print("nan prediction, skipping...") continue c2maxmin = (max(plotpred) - min(plotpred)) if not c2maxmin: c2maxmin = 1. c2map_charge = colors.LinearSegmentedColormap.from_list( 'c2map_c', [(0 / c2maxmin, 'darkblue'), (np.abs(min(plotpred)) / c2maxmin, 'black'), (2.0 * np.abs(min(plotpred)) / c2maxmin, 'blue'), (2.5 * np.abs(min(plotpred)) / c2maxmin, 'green'), (1, 'yellow')]) camera2.pixels.set_cmap(c2map_charge) if not colorbar2: camera2.add_colorbar(ax=ax2, label=" [photo-electrons]") colorbar2 = camera2.colorbar else: camera2.colorbar = colorbar2 camera2.update(True) plt.pause(1.) # make shorter # plt.pause(0.1) # if pp is not None: # pp.savefig(fig) #fig.savefig(str(args.output_path) + "_" + # str(event.dl0.event_id) + '.png') plt.close()
def analyze_muon_event(event): """ Generic muon event analyzer. Parameters ---------- event : ctapipe dl1 event container Returns ------- muonringparam, muonintensityparam : MuonRingParameter and MuonIntensityParameter container event """ names = [ 'LST_LST_LSTCam', 'MST_MST_NectarCam', 'MST_MST_FlashCam', 'MST_SCT_SCTCam', 'SST_1M_DigiCam', 'SST_GCT_CHEC', 'SST_ASTRI_ASTRICam', 'SST_ASTRI_CHEC' ] tail_cuts = [(5, 7), (5, 7), (10, 12), (5, 7), (5, 7), (5, 7), (5, 7), (5, 7)] # 10, 12? impact = [(0.2, 0.9), (0.1, 0.95), (0.2, 0.9), (0.2, 0.9), (0.1, 0.95), (0.1, 0.95), (0.1, 0.95), (0.1, 0.95)] * u.m ringwidth = [(0.04, 0.08), (0.02, 0.1), (0.01, 0.1), (0.02, 0.1), (0.01, 0.5), (0.02, 0.2), (0.02, 0.2), (0.02, 0.2)] * u.deg total_pix = [1855., 1855., 1764., 11328., 1296., 2048., 2368., 2048] # 8% (or 6%) as limit min_pix = [148., 148., 141., 680., 104., 164., 142., 164] # Need to either convert from the pixel area in m^2 or check the camera specs ang_pixel_width = [0.1, 0.2, 0.18, 0.067, 0.24, 0.2, 0.17, 0.2, 0.163 ] * u.deg # Found from TDRs (or the pixel area) hole_rad = [ 0.308 * u.m, 0.244 * u.m, 0.244 * u.m, 4.3866 * u.m, 0.160 * u.m, 0.130 * u.m, 0.171 * u.m, 0.171 * u.m ] # Assuming approximately spherical hole cam_rad = [2.26, 3.96, 3.87, 4., 4.45, 2.86, 5.25, 2.86] * u.deg # Above found from the field of view calculation sec_rad = [ 0. * u.m, 0. * u.m, 0. * u.m, 2.7 * u.m, 0. * u.m, 1. * u.m, 1.8 * u.m, 1.8 * u.m ] sct = [False, False, False, True, False, True, True, True] # Added cleaning here. All these options should go to an input card cleaning = True muon_cuts = { 'Name': names, 'tail_cuts': tail_cuts, 'Impact': impact, 'RingWidth': ringwidth, 'total_pix': total_pix, 'min_pix': min_pix, 'CamRad': cam_rad, 'SecRad': sec_rad, 'SCT': sct, 'AngPixW': ang_pixel_width, 'HoleRad': hole_rad } logger.debug(muon_cuts) muonringlist = [] # [None] * len(event.dl0.tels_with_data) muonintensitylist = [] # [None] * len(event.dl0.tels_with_data) tellist = [] muon_event_param = { 'TelIds': tellist, 'MuonRingParams': muonringlist, 'MuonIntensityParams': muonintensitylist } for telid in event.dl0.tels_with_data: logger.debug("Analysing muon event for tel %d", telid) image = event.dl1.tel[telid].image # Get geometry teldes = event.inst.subarray.tel[telid] geom = teldes.camera x, y = geom.pix_x, geom.pix_y dict_index = muon_cuts['Name'].index(str(teldes)) logger.debug('found an index of %d for camera %d', dict_index, geom.cam_id) tailcuts = muon_cuts['tail_cuts'][dict_index] logger.debug("Tailcuts are %s", tailcuts) clean_mask = tailcuts_clean(geom, image, picture_thresh=tailcuts[0], boundary_thresh=tailcuts[1]) # TODO: correct this hack for values over 90 altval = event.mcheader.run_array_direction[1] if altval > Angle(90, unit=u.deg): warnings.warn('Altitude over 90 degrees') altval = Angle(90, unit=u.deg) telescope_pointing = SkyCoord(alt=altval, az=event.mcheader.run_array_direction[0], frame=AltAz()) camera_coord = SkyCoord( x=x, y=y, frame=CameraFrame( focal_length=teldes.optics.equivalent_focal_length, rotation=geom.pix_rotation, telescope_pointing=telescope_pointing, )) nom_coord = camera_coord.transform_to( NominalFrame(origin=telescope_pointing)) x = nom_coord.delta_az.to(u.deg) y = nom_coord.delta_alt.to(u.deg) if (cleaning): img = image * clean_mask else: img = image muonring = ChaudhuriKunduRingFitter(None) logger.debug("img: %s mask: %s, x=%s y= %s", np.sum(image), np.sum(clean_mask), x, y) if not sum(img): # Nothing left after tail cuts continue muonringparam = muonring.fit(x, y, image * clean_mask) dist = np.sqrt( np.power(x - muonringparam.ring_center_x, 2) + np.power(y - muonringparam.ring_center_y, 2)) ring_dist = np.abs(dist - muonringparam.ring_radius) muonringparam = muonring.fit( x, y, img * (ring_dist < muonringparam.ring_radius * 0.4)) dist = np.sqrt( np.power(x - muonringparam.ring_center_x, 2) + np.power(y - muonringparam.ring_center_y, 2)) ring_dist = np.abs(dist - muonringparam.ring_radius) muonringparam = muonring.fit( x, y, img * (ring_dist < muonringparam.ring_radius * 0.4)) muonringparam.tel_id = telid muonringparam.obs_id = event.dl0.obs_id muonringparam.event_id = event.dl0.event_id dist_mask = np.abs( dist - muonringparam.ring_radius) < muonringparam.ring_radius * 0.4 pix_im = image * dist_mask nom_dist = np.sqrt( np.power(muonringparam.ring_center_x, 2) + np.power(muonringparam.ring_center_y, 2)) minpix = muon_cuts['min_pix'][dict_index] # 0.06*numpix #or 8% mir_rad = np.sqrt(teldes.optics.mirror_area.to("m2") / np.pi) # Camera containment radius - better than nothing - guess pixel # diameter of 0.11, all cameras are perfectly circular cam_rad = # np.sqrt(numpix*0.11/(2.*np.pi)) if (npix_above_threshold(pix_im, tailcuts[0]) > 0.1 * minpix and npix_composing_ring(pix_im) > minpix and nom_dist < muon_cuts['CamRad'][dict_index] and muonringparam.ring_radius < 1.5 * u.deg and muonringparam.ring_radius > 1. * u.deg): muonringparam.ring_containment = ring_containment( muonringparam.ring_radius, muon_cuts['CamRad'][dict_index], muonringparam.ring_center_x, muonringparam.ring_center_y) # Guess HESS is 0.16 # sec_rad = 0.*u.m # sct = False # if numpix == 2048 and mir_rad > 2.*u.m and mir_rad < 2.1*u.m: # sec_rad = 1.*u.m # sct = True # # Store muon ring parameters (passing cuts stage 1) # muonringlist[idx] = muonringparam tellist.append(telid) muonringlist.append(muonringparam) muonintensitylist.append(None) ctel = MuonLineIntegrate( mir_rad, hole_radius=muon_cuts['HoleRad'][dict_index], pixel_width=muon_cuts['AngPixW'][dict_index], sct_flag=muon_cuts['SCT'][dict_index], secondary_radius=muon_cuts['SecRad'][dict_index]) if image.shape[0] == muon_cuts['total_pix'][dict_index]: muonintensityoutput = ctel.fit_muon( muonringparam.ring_center_x, muonringparam.ring_center_y, muonringparam.ring_radius, x[dist_mask], y[dist_mask], image[dist_mask]) muonintensityoutput.tel_id = telid muonintensityoutput.obs_id = event.dl0.obs_id muonintensityoutput.event_id = event.dl0.event_id muonintensityoutput.mask = dist_mask idx_ring = np.nonzero(pix_im) muonintensityoutput.ring_completeness = ring_completeness( x[idx_ring], y[idx_ring], pix_im[idx_ring], muonringparam.ring_radius, muonringparam.ring_center_x, muonringparam.ring_center_y, threshold=30, bins=30) muonintensityoutput.ring_size = np.sum(pix_im) dist_ringwidth_mask = np.abs( dist - muonringparam.ring_radius) < ( muonintensityoutput.ring_width) pix_ringwidth_im = image * dist_ringwidth_mask idx_ringwidth = np.nonzero(pix_ringwidth_im) muonintensityoutput.ring_pix_completeness = npix_above_threshold( pix_ringwidth_im[idx_ringwidth], tailcuts[0]) / len( pix_im[idx_ringwidth]) logger.debug( "Tel %d Impact parameter = %s mir_rad=%s " "ring_width=%s", telid, muonintensityoutput.impact_parameter, mir_rad, muonintensityoutput.ring_width) conditions = [ muonintensityoutput.impact_parameter * u.m < muon_cuts['Impact'][dict_index][1] * mir_rad, muonintensityoutput.impact_parameter > muon_cuts['Impact'][dict_index][0], muonintensityoutput.ring_width < muon_cuts['RingWidth'][dict_index][1], muonintensityoutput.ring_width > muon_cuts['RingWidth'][dict_index][0] ] if all(conditions): muonintensityparam = muonintensityoutput idx = tellist.index(telid) muonintensitylist[idx] = muonintensityparam logger.debug("Muon found in tel %d, tels in event=%d", telid, len(event.dl0.tels_with_data)) else: continue return muon_event_param
def plot_muon_event(event, muonparams, geom_dict=None, args=None): if muonparams[0] is not None: # Plot the muon event and overlay muon parameters fig = plt.figure(figsize=(16, 7)) # if args.display: # plt.show(block=False) #pp = PdfPages(args.output_path) if args.output_path is not None else None # pp = None #For now, need to correct this colorbar = None colorbar2 = None for tel_id in event.dl0.tels_with_data: npads = 2 # Only create two pads if there is timing information extracted # from the calibration ax1 = fig.add_subplot(1, npads, 1) plotter = CameraPlotter(event, geom_dict) #image = event.dl1.tel[tel_id].calibrated_image image = event.dl1.tel[tel_id].image[0] # Get geometry geom = None if geom_dict is not None and tel_id in geom_dict: geom = geom_dict[tel_id] else: #log.debug("[calib] Guessing camera geometry") geom = CameraGeometry.guess(*event.inst.pixel_pos[tel_id], event.inst.optical_foclen[tel_id]) #log.debug("[calib] Camera geometry found") if geom_dict is not None: geom_dict[tel_id] = geom tailcuts = (5., 7.) # Try a higher threshold for if geom.cam_id == 'FlashCam': tailcuts = (10., 12.) #print("Using Tail Cuts:",tailcuts) clean_mask = tailcuts_clean(geom, image, picture_thresh=tailcuts[0], boundary_thresh=tailcuts[1]) signals = image * clean_mask #print("Ring Centre in Nominal Coords:",muonparams[0].ring_center_x,muonparams[0].ring_center_y) muon_incl = np.sqrt(muonparams[0].ring_center_x**2. + muonparams[0].ring_center_y**2.) muon_phi = np.arctan(muonparams[0].ring_center_y / muonparams[0].ring_center_x) rotr_angle = geom.pix_rotation # if event.inst.optical_foclen[tel_id] > 10.*u.m and # event.dl0.tel[tel_id].num_pixels != 1764: if geom.cam_id == 'LSTCam' or geom.cam_id == 'NectarCam': #print("Resetting the rotation angle") rotr_angle = 0. * u.deg # Convert to camera frame (centre & radius) altaz = HorizonFrame(alt=event.mc.alt, az=event.mc.az) ring_nominal = NominalFrame(x=muonparams[0].ring_center_x, y=muonparams[0].ring_center_y, array_direction=altaz, pointing_direction=altaz) # embed() ring_camcoord = ring_nominal.transform_to(CameraFrame( pointing_direction=altaz, focal_length=event.inst.optical_foclen[tel_id], rotation=rotr_angle)) centroid_rad = np.sqrt(ring_camcoord.y**2 + ring_camcoord.x**2) centroid = (ring_camcoord.x.value, ring_camcoord.y.value) ringrad_camcoord = muonparams[0].ring_radius.to(u.rad) \ * event.inst.optical_foclen[tel_id] * 2. # But not FC? #rot_angle = 0.*u.deg # if event.inst.optical_foclen[tel_id] > 10.*u.m and event.dl0.tel[tel_id].num_pixels != 1764: #rot_angle = -100.14*u.deg px, py = event.inst.pixel_pos[tel_id] flen = event.inst.optical_foclen[tel_id] camera_coord = CameraFrame(x=px, y=py, z=np.zeros(px.shape) * u.m, focal_length=flen, rotation=geom.pix_rotation) nom_coord = camera_coord.transform_to( NominalFrame(array_direction=altaz, pointing_direction=altaz) ) #,focal_length = event.inst.optical_foclen[tel_id])) # tel['TelescopeTable_VersionFeb2016'][tel['TelescopeTable_VersionFeb2016']['TelID']==telid]['FL'][0]*u.m)) px = nom_coord.x.to(u.deg) py = nom_coord.y.to(u.deg) dist = np.sqrt(np.power( px - muonparams[0].ring_center_x, 2) + np.power(py - muonparams[0].ring_center_y, 2)) ring_dist = np.abs(dist - muonparams[0].ring_radius) pixRmask = ring_dist < muonparams[0].ring_radius * 0.4 if muonparams[1] is not None: signals *= muonparams[1].mask camera1 = plotter.draw_camera(tel_id, signals, ax1) cmaxmin = (max(signals) - min(signals)) if not cmaxmin: cmaxmin = 1. cmap_charge = colors.LinearSegmentedColormap.from_list( 'cmap_c', [(0 / cmaxmin, 'darkblue'), (np.abs(min(signals)) / cmaxmin, 'black'), (2.0 * np.abs(min(signals)) / cmaxmin, 'blue'), (2.5 * np.abs(min(signals)) / cmaxmin, 'green'), (1, 'yellow')] ) camera1.pixels.set_cmap(cmap_charge) if not colorbar: camera1.add_colorbar(ax=ax1, label=" [photo-electrons]") colorbar = camera1.colorbar else: camera1.colorbar = colorbar camera1.update(True) camera1.add_ellipse(centroid, ringrad_camcoord.value, ringrad_camcoord.value, 0., 0., color="red") # ax1.set_title("CT {} ({}) - Mean pixel charge" # .format(tel_id, geom_dict[tel_id].cam_id)) if muonparams[1] is not None: # continue #Comment this...(should ringwidthfrac also be *0.5?) ringwidthfrac = muonparams[ 1].ring_width / muonparams[0].ring_radius ringrad_inner = ringrad_camcoord * (1. - ringwidthfrac) ringrad_outer = ringrad_camcoord * (1. + ringwidthfrac) camera1.add_ellipse(centroid, ringrad_inner.value, ringrad_inner.value, 0., 0., color="magenta") camera1.add_ellipse(centroid, ringrad_outer.value, ringrad_outer.value, 0., 0., color="magenta") npads = 2 ax2 = fig.add_subplot(1, npads, npads) pred = muonparams[1].prediction if len(pred) != np.sum(muonparams[1].mask): print("Warning! Lengths do not match...len(pred)=", len(pred), "len(mask)=", np.sum(muonparams[1].mask)) # Numpy broadcasting - fill in the shape plotpred = np.zeros(image.shape) plotpred[muonparams[1].mask == True] = pred camera2 = plotter.draw_camera(tel_id, plotpred, ax2) c2maxmin = (max(plotpred) - min(plotpred)) if not c2maxmin: c2maxmin = 1. c2map_charge = colors.LinearSegmentedColormap.from_list( 'c2map_c', [(0 / c2maxmin, 'darkblue'), (np.abs(min(plotpred)) / c2maxmin, 'black'), (2.0 * np.abs(min(plotpred)) / c2maxmin, 'blue'), (2.5 * np.abs(min(plotpred)) / c2maxmin, 'green'), (1, 'yellow')] ) camera2.pixels.set_cmap(c2map_charge) if not colorbar2: camera2.add_colorbar(ax=ax2, label=" [photo-electrons]") colorbar2 = camera2.colorbar else: camera2.colorbar = colorbar2 camera2.update(True) plt.pause(1.) # make shorter # plt.pause(0.1) # if pp is not None: # pp.savefig(fig) fig.savefig(str(args.output_path) + "_" + str(event.dl0.event_id) + '.png') plt.close()
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 nominal_to_altaz(): t = np.zeros(10) t[5] = 1 nom = NominalFrame(x=t*u.deg,y=t*u.deg,array_direction = [75*u.deg,180*u.deg]) alt_az = nom.transform_to(HorizonFrame) print("AltAz Coordinate",alt_az)
plt.pause(0.01) if args.write: plt.savefig('CT{:03d}_EV{:010d}_S{:02d}.png' .format(args.tel, event.dl0.event_id, ii)) else: # display integrated event: im = event.dl0.tel[args.tel].adc_sums[args.channel] im = apply_mc_calibration(im, args.tel) disp.image = im if args.hillas: clean_mask = reco.cleaning.tailcuts_clean(geom,im,1,picture_thresh=10,boundary_thresh=5) camera_coord = CameraFrame(x=x,y=y,z=np.zeros(x.shape)*u.m) nom_coord = camera_coord.transform_to(NominalFrame(array_direction=[70*u.deg,0*u.deg], pointing_direction=[70*u.deg,0*u.deg], focal_length=tel['TelescopeTable_VersionFeb2016'][tel['TelescopeTable_VersionFeb2016']['TelID']==args.tel]['FL'][0]*u.m)) image = np.asanyarray(im * clean_mask, dtype=np.float64) nom_x = nom_coord.x nom_y = nom_coord.y hillas = reco.hillas_parameters(x,y,im * clean_mask) hillas_nom = reco.hillas_parameters(nom_x,nom_y,im * clean_mask) print (hillas) print (hillas_nom) disp.image = im * clean_mask disp.overlay_moments(hillas, color='seagreen', linewidth=3)
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
cleaning_level = { 'LSTCam': (3.5, 7.5, 2), # ?? (3, 6) for Abelardo... 'FlashCam': (4, 8, 2), # there is some scaling missing? 'ASTRICam': (5, 7, 2), } input_url = get_dataset_path('gamma_test_large.simtel.gz') with event_source(input_url=input_url) as source: calibrator = CameraCalibrator(eventsource=source, ) for event in source: calibrator.calibrate(event) nominal_frame = NominalFrame( origin=SkyCoord(alt=70 * u.deg, az=0 * u.deg, frame=HorizonFrame)) nom_delta_az = [] nom_delta_alt = [] photons = [] for tel_id, dl1 in event.dl1.tel.items(): camera = event.inst.subarray.tels[tel_id].camera focal_length = event.inst.subarray.tels[ tel_id].optics.equivalent_focal_length image = dl1.image[0] # telescope mc info mc_tel = event.mc.tel[tel_id] telescope_pointing = SkyCoord(
def plot_muon_event(event, muonparams, args=None): if muonparams['MuonRingParams'] is not None: # Plot the muon event and overlay muon parameters fig = plt.figure(figsize=(16, 7)) colorbar = None colorbar2 = None #for tel_id in event.dl0.tels_with_data: for tel_id in muonparams['TelIds']: idx = muonparams['TelIds'].index(tel_id) if not muonparams['MuonRingParams'][idx]: continue #otherwise... npads = 2 # Only create two pads if there is timing information extracted # from the calibration ax1 = fig.add_subplot(1, npads, 1) plotter = CameraPlotter(event) image = event.dl1.tel[tel_id].image[0] geom = event.inst.subarray.tel[tel_id].camera tailcuts = (5., 7.) # Try a higher threshold for if geom.cam_id == 'FlashCam': tailcuts = (10., 12.) clean_mask = tailcuts_clean(geom, image, picture_thresh=tailcuts[0], boundary_thresh=tailcuts[1]) signals = image * clean_mask #print("Ring Centre in Nominal Coords:",muonparams[0].ring_center_x,muonparams[0].ring_center_y) muon_incl = np.sqrt(muonparams['MuonRingParams'][idx].ring_center_x**2. + muonparams['MuonRingParams'][idx].ring_center_y**2.) muon_phi = np.arctan(muonparams['MuonRingParams'][idx].ring_center_y / muonparams['MuonRingParams'][idx].ring_center_x) rotr_angle = geom.pix_rotation # if event.inst.optical_foclen[tel_id] > 10.*u.m and # event.dl0.tel[tel_id].num_pixels != 1764: if geom.cam_id == 'LSTCam' or geom.cam_id == 'NectarCam': #print("Resetting the rotation angle") rotr_angle = 0. * u.deg # Convert to camera frame (centre & radius) altaz = HorizonFrame(alt=event.mc.alt, az=event.mc.az) ring_nominal = NominalFrame(x=muonparams['MuonRingParams'][idx].ring_center_x, y=muonparams['MuonRingParams'][idx].ring_center_y, array_direction=altaz, pointing_direction=altaz) # embed() ring_camcoord = ring_nominal.transform_to(CameraFrame( pointing_direction=altaz, focal_length=event.inst.optical_foclen[tel_id], rotation=rotr_angle)) centroid_rad = np.sqrt(ring_camcoord.y**2 + ring_camcoord.x**2) centroid = (ring_camcoord.x.value, ring_camcoord.y.value) ringrad_camcoord = muonparams['MuonRingParams'][idx].ring_radius.to(u.rad) \ * event.inst.optical_foclen[tel_id] * 2. # But not FC? px, py = event.inst.pixel_pos[tel_id] flen = event.inst.optical_foclen[tel_id] camera_coord = CameraFrame(x=px, y=py, z=np.zeros(px.shape) * u.m, focal_length=flen, rotation=geom.pix_rotation) nom_coord = camera_coord.transform_to( NominalFrame(array_direction=altaz, pointing_direction=altaz) ) px = nom_coord.x.to(u.deg) py = nom_coord.y.to(u.deg) dist = np.sqrt(np.power( px - muonparams['MuonRingParams'][idx].ring_center_x, 2) + np.power(py - muonparams['MuonRingParams'][idx].ring_center_y, 2)) ring_dist = np.abs(dist - muonparams['MuonRingParams'][idx].ring_radius) pixRmask = ring_dist < muonparams['MuonRingParams'][idx].ring_radius * 0.4 #if muonparams[1] is not None: if muonparams['MuonIntensityParams'][idx] is not None: signals *= muonparams['MuonIntensityParams'][idx].mask camera1 = plotter.draw_camera(tel_id, signals, ax1) cmaxmin = (max(signals) - min(signals)) cmin = min(signals) if not cmin: cmin = 1. if not cmaxmin: cmaxmin = 1. cmap_charge = colors.LinearSegmentedColormap.from_list( 'cmap_c', [(0 / cmaxmin, 'darkblue'), (np.abs(cmin) / cmaxmin, 'black'), (2.0 * np.abs(cmin) / cmaxmin, 'blue'), (2.5 * np.abs(cmin) / cmaxmin, 'green'), (1, 'yellow')] ) camera1.pixels.set_cmap(cmap_charge) if not colorbar: camera1.add_colorbar(ax=ax1, label=" [photo-electrons]") colorbar = camera1.colorbar else: camera1.colorbar = colorbar camera1.update(True) camera1.add_ellipse(centroid, ringrad_camcoord.value, ringrad_camcoord.value, 0., 0., color="red") if muonparams['MuonIntensityParams'][idx] is not None: # continue #Comment this...(should ringwidthfrac also be *0.5?) ringwidthfrac = muonparams['MuonIntensityParams'][idx].ring_width / muonparams['MuonRingParams'][idx].ring_radius ringrad_inner = ringrad_camcoord * (1. - ringwidthfrac) ringrad_outer = ringrad_camcoord * (1. + ringwidthfrac) camera1.add_ellipse(centroid, ringrad_inner.value, ringrad_inner.value, 0., 0., color="magenta") camera1.add_ellipse(centroid, ringrad_outer.value, ringrad_outer.value, 0., 0., color="magenta") npads = 2 ax2 = fig.add_subplot(1, npads, npads) pred = muonparams['MuonIntensityParams'][idx].prediction if len(pred) != np.sum(muonparams['MuonIntensityParams'][idx].mask): print("Warning! Lengths do not match...len(pred)=", len(pred), "len(mask)=", np.sum(muonparams['MuonIntensityParams'][idx].mask)) # Numpy broadcasting - fill in the shape plotpred = np.zeros(image.shape) plotpred[muonparams['MuonIntensityParams'][idx].mask == True] = pred camera2 = plotter.draw_camera(tel_id, plotpred, ax2) if np.isnan(max(plotpred)) or np.isnan(min(plotpred)): print("nan prediction, skipping...") continue c2maxmin = (max(plotpred) - min(plotpred)) if not c2maxmin: c2maxmin = 1. c2map_charge = colors.LinearSegmentedColormap.from_list( 'c2map_c', [(0 / c2maxmin, 'darkblue'), (np.abs(min(plotpred)) / c2maxmin, 'black'), (2.0 * np.abs(min(plotpred)) / c2maxmin, 'blue'), (2.5 * np.abs(min(plotpred)) / c2maxmin, 'green'), (1, 'yellow')] ) camera2.pixels.set_cmap(c2map_charge) if not colorbar2: camera2.add_colorbar(ax=ax2, label=" [photo-electrons]") colorbar2 = camera2.colorbar else: camera2.colorbar = colorbar2 camera2.update(True) plt.pause(1.) # make shorter # plt.pause(0.1) # if pp is not None: # pp.savefig(fig) #fig.savefig(str(args.output_path) + "_" + # str(event.dl0.event_id) + '.png') plt.close()
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): """ 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
table = "CameraTable_VersionFeb2016_TelID" for tel_id in container.dl0.tels_with_data: x, y = event.meta.pixel_pos[tel_id] if geom == 0: geom = io.CameraGeometry.guess(x, y,event.meta.optical_foclen[tel_id]) image = apply_mc_calibration(event.dl0.tel[tel_id].adc_sums[0], tel_id) if image.shape[0] >1000: continue clean_mask = tailcuts_clean(geom,image,1,picture_thresh=5,boundary_thresh=7) camera_coord = CameraFrame(x=x,y=y,z=np.zeros(x.shape)*u.m) nom_coord = camera_coord.transform_to(NominalFrame(array_direction=[container.mc.alt,container.mc.az], pointing_direction=[container.mc.alt,container.mc.az], focal_length=tel['TelescopeTable_VersionFeb2016'][tel['TelescopeTable_VersionFeb2016']['TelID']==tel_id]['FL'][0]*u.m)) x = nom_coord.x.to(u.deg) y = nom_coord.y.to(u.deg) img = image*clean_mask noise = 5 weight = img / (img+noise) centre_x,centre_y,radius = chaudhuri_kundu_circle_fit(x,y,image*clean_mask) dist = np.sqrt(np.power(x-centre_x,2) + np.power(y-centre_y,2)) ring_dist = np.abs(dist-radius) centre_x,centre_y,radius = chaudhuri_kundu_circle_fit(x,y,image*(ring_dist<radius*0.3)) dist = np.sqrt(np.power(x-centre_x,2) + np.power(y-centre_y,2))
def set_event_properties( self, image, time, pixel_x, pixel_y, type_tel, tel_x, tel_y, array_direction, hillas, ): """The setter class is used to set the event properties within this class before minimisation can take place. This simply copies a bunch of useful properties to class members, so that we can use them later without passing all this information around. Parameters ---------- image: dict Amplitude of pixels in camera images time: dict Time information per each pixel in camera images pixel_x: dict X position of pixels in nominal system pixel_y: dict Y position of pixels in nominal system type_tel: dict Type of telescope tel_x: dict X position of telescope in TiltedGroundFrame tel_y: dict Y position of telescope in TiltedGroundFrame array_direction: SkyCoord[AltAz] Array pointing direction in the AltAz Frame hillas: dict dictionary with telescope IDs as key and HillasParametersContainer instances as values Returns ------- None """ # First store these parameters in the class so we can use them # in minimisation For most values this is simply copying self.image = image self.tel_pos_x = np.zeros(len(tel_x)) self.tel_pos_y = np.zeros(len(tel_x)) self.ped = np.zeros(len(tel_x)) self.tel_types, self.tel_id = list(), list() max_pix_x = 0 px, py, pa, pt = list(), list(), list(), list() self.hillas_parameters = list() # So here we must loop over the telescopes for x, i in zip(tel_x, range(len(tel_x))): px.append(pixel_x[x].to(u.rad).value) if len(px[i]) > max_pix_x: max_pix_x = len(px[i]) py.append(pixel_y[x].to(u.rad).value) pa.append(image[x]) pt.append(time[x]) self.tel_pos_x[i] = tel_x[x].to(u.m).value self.tel_pos_y[i] = tel_y[x].to(u.m).value self.ped[i] = self.ped_table[type_tel[x]] self.tel_types.append(type_tel[x]) self.tel_id.append(x) self.hillas_parameters.append(hillas[x]) # Most interesting stuff is now copied to the class, but to remove our requirement # for loops we must copy the pixel positions to an array with the length of the # largest image # First allocate everything shape = (len(tel_x), max_pix_x) self.pixel_x, self.pixel_y = ma.zeros(shape), ma.zeros(shape) self.image, self.time, self.ped = ( ma.zeros(shape), ma.zeros(shape), ma.zeros(shape), ) self.tel_types = np.array(self.tel_types) # Copy everything into our masked arrays for i in range(len(tel_x)): array_len = len(px[i]) self.pixel_x[i][:array_len] = px[i] self.pixel_y[i][:array_len] = py[i] self.image[i][:array_len] = pa[i] self.time[i][:array_len] = pt[i] self.ped[i][:array_len] = self.ped_table[self.tel_types[i]] # Set the image mask mask = self.image == 0.0 self.pixel_x[mask], self.pixel_y[mask] = ma.masked, ma.masked self.image[mask] = ma.masked self.time[mask] = ma.masked self.array_direction = array_direction self.nominal_frame = NominalFrame(origin=self.array_direction) # Finally run some functions to get ready for the event self.get_hillas_mean() self.initialise_templates(type_tel)