def test_FitGammaHillas(): ''' a test of the complete fit procedure on one event including: • tailcut cleaning • hillas parametrisation • GreatCircle creation • direction fit • position fit in the end, proper units in the output are asserted ''' filename = get_dataset("gamma_test.simtel.gz") fit = HillasReconstructor() cam_geom = {} tel_phi = {} tel_theta = {} source = hessio_event_source(filename) for event in source: hillas_dict = {} for tel_id in event.dl0.tels_with_data: if tel_id not in cam_geom: cam_geom[tel_id] = CameraGeometry.guess( event.inst.pixel_pos[tel_id][0], event.inst.pixel_pos[tel_id][1], event.inst.optical_foclen[tel_id]) tel_phi[tel_id] = event.mc.tel[tel_id].azimuth_raw * u.rad tel_theta[tel_id] = (np.pi/2-event.mc.tel[tel_id].altitude_raw)*u.rad pmt_signal = event.r0.tel[tel_id].adc_sums[0] mask = tailcuts_clean(cam_geom[tel_id], pmt_signal, picture_thresh=10., boundary_thresh=5.) pmt_signal[mask == 0] = 0 try: moments = hillas_parameters(event.inst.pixel_pos[tel_id][0], event.inst.pixel_pos[tel_id][1], pmt_signal) hillas_dict[tel_id] = moments except HillasParameterizationError as e: print(e) continue if len(hillas_dict) < 2: continue fit_result = fit.predict(hillas_dict, event.inst, tel_phi, tel_theta) print(fit_result) fit_result.alt.to(u.deg) fit_result.az.to(u.deg) fit_result.core_x.to(u.m) assert fit_result.is_valid return
def test_tailcuts_clean_min_neighbors_1(): """ requiring that picture pixels have at least one neighbor above picture_thres: """ # start with simple 3-pixel camera geom = CameraGeometry.make_rectangular(3, 1, (-1, 1)) p = 15 # picture value b = 7 # boundary value testcases = {(p, p, 0): [True, True, False], (p, 0, p): [False, False, False], (p, b, p): [False, False, False], (p, b, 0): [False, False, False], (b, b, 0): [False, False, False], (0, p, 0): [False, False, False], (p, p, p): [True, True, True]} for image, mask in testcases.items(): result = cleaning.tailcuts_clean(geom, np.array(image), picture_thresh=15, boundary_thresh=5, min_number_picture_neighbors=1, keep_isolated_pixels=False) assert (result == mask).all()
def update(frame): centroid = np.random.uniform(-0.5, 0.5, size=2) width = np.random.uniform(0, 0.01) length = np.random.uniform(0, 0.03) + width angle = np.random.uniform(0, 360) intens = np.random.exponential(2) * 50 model = toymodel.generate_2d_shower_model(centroid=centroid, width=width, length=length, psi=angle * u.deg) image, sig, bg = toymodel.make_toymodel_shower_image(geom, model.pdf, intensity=intens, nsb_level_pe=5000) # alternate between cleaned and raw images if self._counter > 20: plt.suptitle("Image Cleaning ON") cleanmask = cleaning.tailcuts_clean(geom, image, pedvars=80) for ii in range(3): cleaning.dilate(geom, cleanmask) image[cleanmask == 0] = 0 # zero noise pixels if self._counter >= 40: plt.suptitle("Image Cleaning OFF") self._counter = 0 disp.image = image disp.set_limits_percent(100) self._counter += 1
def test_array_draw(): filename = get_dataset("gamma_test.simtel.gz") cam_geom = {} source = hessio_event_source(filename, max_events=2) r1 = HessioR1Calibrator(None, None) dl0 = CameraDL0Reducer(None, None) calibrator = CameraDL1Calibrator(None, None) 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 = event.inst.subarray.tel[tel_id].camera fl = event.inst.subarray.tel[tel_id].optics.effective_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)) mask = tailcuts_clean(geom, pmt_signal, picture_thresh=10., boundary_thresh=5.) try: moments = hillas_parameters(nom_coord.x, nom_coord.y, 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 run(self, list_of_file, max_events): signal_place_after_clean = np.zeros(1855) sum_ped_ev = 0 alive_ped_ev = 0 for input_file in list_of_file: print(input_file) r0_r1_calibrator = LSTR0Corrections(pedestal_path=None, r1_sample_start=3, r1_sample_end=39) reader = LSTEventSource(input_url=input_file, max_events=max_events) for i, ev in enumerate(reader): r0_r1_calibrator.calibrate(ev) if i % 10000 == 0: print(ev.r0.event_id) if ev.lst.tel[1].evt.tib_masked_trigger == 32: sum_ped_ev += 1 self.r1_dl1_calibrator(ev) img = ev.dl1.tel[1].image geom = ev.inst.subarray.tel[1].camera clean = tailcuts_clean(geom, img, **self.cleaning_parameters) cleaned = img.copy() cleaned[~clean] = 0.0 signal_place_after_clean[np.where(clean == True)] += 1 if np.sum(cleaned > 0) > 0: alive_ped_ev += 1 fig, ax = plt.subplots(figsize=(10, 8)) geom = ev.inst.subarray.tel[1].camera disp0 = CameraDisplay(geom, ax=ax) disp0.image = signal_place_after_clean / sum_ped_ev disp0.add_colorbar(ax=ax, label="N times signal remain after cleaning [%]") disp0.cmap = 'gnuplot2' ax.set_title("{} \n {}/{}".format( input_file.split("/")[-1][8:21], alive_ped_ev, sum_ped_ev), fontsize=25) print("{}/{}".format(alive_ped_ev, sum_ped_ev)) ax.set_xlabel(" ") ax.set_ylabel(" ") plt.tight_layout() plt.show()
def test_reconstruction(): """ a test of the complete fit procedure on one event including: • tailcut cleaning • hillas parametrisation • HillasPlane creation • direction fit • position fit in the end, proper units in the output are asserted """ filename = get_dataset_path("gamma_test.simtel.gz") fit = HillasReconstructor() tel_azimuth = {} tel_altitude = {} source = event_source(filename) for event in source: hillas_dict = {} for tel_id in event.dl0.tels_with_data: geom = event.inst.subarray.tel[tel_id].camera tel_azimuth[tel_id] = event.mc.tel[tel_id].azimuth_raw * u.rad tel_altitude[tel_id] = event.mc.tel[tel_id].altitude_raw * u.rad pmt_signal = event.r0.tel[tel_id].image[0] mask = tailcuts_clean(geom, pmt_signal, picture_thresh=10., boundary_thresh=5.) pmt_signal[mask == 0] = 0 try: moments = hillas_parameters(geom, pmt_signal) hillas_dict[tel_id] = moments except HillasParameterizationError as e: print(e) continue if len(hillas_dict) < 2: continue fit_result = fit.predict(hillas_dict, event.inst, tel_azimuth, tel_altitude) print(fit_result) fit_result.alt.to(u.deg) fit_result.az.to(u.deg) fit_result.core_x.to(u.m) assert fit_result.is_valid
def test_tailcuts_clean_threshold_array(): """Tests that tailcuts can also work with individual thresholds per pixel""" rng = np.random.default_rng(1337) geom = CameraGeometry.from_name("LSTCam") # artifical event having a "shower" and a "star" at these locations star_x = 0.5 * u.m star_y = 0.5 * u.m shower_x = -0.5 * u.m shower_y = -0.5 * u.m star_pixels = ( np.sqrt((geom.pix_x - star_x) ** 2 + (geom.pix_y - star_y) ** 2) < 0.1 * u.m ) shower = ( np.sqrt((geom.pix_x - shower_x) ** 2 + (geom.pix_y - shower_y) ** 2) < 0.2 * u.m ) # noise level at the star cluster is much higher than normal camera noise = rng.normal(3, 0.2, len(geom)) noise[star_pixels] = rng.normal(10, 1, np.count_nonzero(star_pixels)) # large signal at the signal location image = rng.poisson(noise).astype(float) signal = rng.normal(20, 2, np.count_nonzero(shower)) image[shower] += signal picture_threshold = 3 * noise boundary_threshold = 1.5 * noise # test that normal cleaning also contains star cluster # and that cleaning with pixel wise values removes star cluster normal_cleaning = cleaning.tailcuts_clean( geom, image, picture_threshold.mean(), boundary_threshold.mean() ) pixel_cleaning = cleaning.tailcuts_clean( geom, image, picture_threshold, boundary_threshold ) assert np.count_nonzero(normal_cleaning & star_pixels) > 0 assert np.count_nonzero(pixel_cleaning & star_pixels) == 0
def fit_muon(x, y, image, geom, tailcuts): """ Fit the muon ring Parameters ---------- x, y : `floats` Coordinates in the TelescopeFrame image : `np.ndarray` Number of photoelectrons in each pixel geom : CameraGeometry tailcuts :`list` Tail cuts for image cleaning Returns ------- muonringparam clean_mask: `np.ndarray` Mask after cleaning dist: `np.ndarray` Distance of every pixel to the center of the muon ring image_clean: `np.ndarray` Image after cleaning """ fitter = MuonRingFitter(fit_method='kundu_chaudhuri') clean_mask = tailcuts_clean( geom, image, picture_thresh=tailcuts[0], boundary_thresh=tailcuts[1], ) ring = fitter(x, y, image, clean_mask) max_allowed_outliers_distance = 0.4 # Do an iterative fit removing pixels which are beyond # max_allowed_outliers_distance * radius of the ring # (along the radial direction) # The goal is to improve fit for good rings # with very few additional non-ring bright pixels. for _ in (0, 0): # just to iterate the fit twice more dist = np.sqrt((x - ring.center_x)**2 + (y - ring.center_y)**2) ring_dist = np.abs(dist - ring.radius) clean_mask *= (ring_dist < ring.radius * max_allowed_outliers_distance) ring = fitter(x, y, image, clean_mask) image_clean = image * clean_mask return ring, clean_mask, dist, image_clean
def test_reconstruction(): """ a test of the complete fit procedure on one event including: • tailcut cleaning • hillas parametrisation • HillasPlane creation • direction fit • position fit in the end, proper units in the output are asserted """ filename = get_dataset_path("gamma_test.simtel.gz") fit = HillasReconstructor() tel_azimuth = {} tel_altitude = {} source = EventSourceFactory.produce(input_url=filename) for event in source: hillas_dict = {} for tel_id in event.dl0.tels_with_data: geom = event.inst.subarray.tel[tel_id].camera tel_azimuth[tel_id] = event.mc.tel[tel_id].azimuth_raw * u.rad tel_altitude[tel_id] = event.mc.tel[tel_id].altitude_raw * u.rad pmt_signal = event.r0.tel[tel_id].image[0] mask = tailcuts_clean(geom, pmt_signal, picture_thresh=10., boundary_thresh=5.) pmt_signal[mask == 0] = 0 try: moments = hillas_parameters(geom, pmt_signal) hillas_dict[tel_id] = moments except HillasParameterizationError as e: print(e) continue if len(hillas_dict) < 2: continue fit_result = fit.predict(hillas_dict, event.inst, tel_azimuth, tel_altitude) print(fit_result) fit_result.alt.to(u.deg) fit_result.az.to(u.deg) fit_result.core_x.to(u.m) assert fit_result.is_valid
def zero_suppression_tailcut_dilation(geom, image, number_of_dilation=3, **kwargs): """ Zero suppression and tailcut cleaning with dilation. Parameters ---------- geom: `ctapipe.instrument.CameraGeometry` Camera geometry information image: object - ndarray Pixel values. A `event.inst.subarray.tel[telescope_id].camera` object. kwargs: dict A dictionary containing the parameters of the selected volume reducer algorithm. number_of_dilation: int The number of times dilation will be applied to `pixel_mask`. Set by default to 3. Returns ------- mask_0_suppression: object - ndarray A boolean mask (array) that contains the zero suppressed pixels. Notes ----- `tailcut_clean` + 3 * `dilate` volume reducer method. Default parameters for the tailcut_clean: >>> 'picture_thresh': 8 >>> 'boundary_thresh': 4 >>> 'keep_isolated_pixels': True >>> 'min_number_picture_neighbors': 0 """ kwargs.setdefault('picture_thresh', 8) kwargs.setdefault('boundary_thresh', 4) kwargs.setdefault('keep_isolated_pixels', True) kwargs.setdefault('min_number_picture_neighbors', 0) mask_0_suppression = tailcuts_clean( geom, image, picture_thresh=kwargs['picture_thresh'], boundary_thresh=kwargs['boundary_thresh'], keep_isolated_pixels=kwargs['keep_isolated_pixels'], min_number_picture_neighbors=kwargs['min_number_picture_neighbors']) # Dilate pixel mask. 3 times by default for i in range(number_of_dilation): mask_0_suppression = dilate(geom, mask_0_suppression) return mask_0_suppression
def test_FitGammaHillas(): filename = get_path("gamma_test.simtel.gz") fit = FitGammaHillas() fit.setup_geometry(*load_hessio(filename), phi=180 * u.deg, theta=20 * u.deg) tel_geom = {} source = hessio_event_source(filename) for event in source: hillas_dict = {} for tel_id in set(event.trig.tels_with_trigger) & set( event.dl0.tels_with_data): if tel_id not in tel_geom: tel_geom[tel_id] = CameraGeometry.guess( fit.cameras(tel_id)['PixX'].to(u.m), fit.cameras(tel_id)['PixY'].to(u.m), fit.telescopes['FL'][tel_id - 1] * u.m) pmt_signal = event.dl0.tel[tel_id].adc_sums[0] mask = tailcuts_clean(tel_geom[tel_id], pmt_signal, 1, picture_thresh=10., boundary_thresh=5.) pmt_signal[mask is False] = 0 try: moments, moms2 = hillas_parameters( fit.cameras(tel_id)['PixX'], fit.cameras(tel_id)['PixY'], pmt_signal) hillas_dict[tel_id] = moments except HillasParameterizationError as e: print(e) continue if len(hillas_dict) < 2: continue fit_result = fit.predict(hillas_dict) print(fit_result) assert fit_result return
def clean_image(self, input_img, high_threshold=10., low_threshold=8., kill_isolated_pixels=False, verbose=False, cam_id=None, output_data_dict=None, **kwargs): """Apply ctapipe's tail-cut image cleaning on ``. """ if cam_id is None: raise Exception("cam_id have to be defined") # TODO # 2D ARRAY (FITS IMAGE) TO CTAPIPE IMAGE ############### geom_1d = geometry_converter.get_geom1d(cam_id) img_1d = geometry_converter.image_2d_to_1d(input_img, cam_id) # APPLY TAILCUT CLEANING ############################## mask = tailcuts_clean(geom_1d, img_1d, picture_thresh=high_threshold, boundary_thresh=low_threshold) img_1d[mask == False] = 0 # CTAPIPE IMAGE TO 2D ARRAY (FITS IMAGE) ############### cleaned_img_2d = geometry_converter.image_1d_to_2d(img_1d, cam_id) # KILL ISOLATED PIXELS ################################# img_cleaned_islands_delta_pe, img_cleaned_islands_delta_abs_pe, img_cleaned_islands_delta_num_pixels = kill_isolated_pixels_stats(cleaned_img_2d) img_cleaned_num_islands = number_of_islands(cleaned_img_2d) if output_data_dict is not None: output_data_dict["img_cleaned_islands_delta_pe"] = img_cleaned_islands_delta_pe output_data_dict["img_cleaned_islands_delta_abs_pe"] = img_cleaned_islands_delta_abs_pe output_data_dict["img_cleaned_islands_delta_num_pixels"] = img_cleaned_islands_delta_num_pixels output_data_dict["img_cleaned_num_islands"] = img_cleaned_num_islands if kill_isolated_pixels: if verbose: print("Kill isolated pixels") cleaned_img_2d = scipy_kill_isolated_pixels(cleaned_img_2d) return cleaned_img_2d
def leak(img): cleanmask = cleaning.tailcuts_clean(geom, img, **opts) mask = False if any(cleanmask): leakage_values = leakage(geom, img, cleanmask) if hasattr(leakage_values, 'leakage{}_intensity'.format(leakage_number)): mask = leakage_values['leakage{}_intensity'.format( leakage_number)] <= leakage_value elif hasattr(leakage_values, 'intensity_width_{}'.format(leakage_number)): mask = leakage_values['intensity_width_{}'.format( leakage_number)] <= leakage_value return mask
def test_tailcuts_clean(): # start with simple 3-pixel camera geom = CameraGeometry.make_rectangular(3, 1, (-1, 1)) p = 15 #picture value b = 7 # boundary value # for no isolated pixels: testcases = {(p, p, 0): [True, True, False], (p, 0, p): [False, False, False], (p, b, p): [True, True, True], (p, b, 0): [True, True, False], (b, b, 0): [False, False, False], (0, p ,0): [False, False, False]} for image, mask in testcases.items(): result = cleaning.tailcuts_clean(geom, np.array(image), picture_thresh=15, boundary_thresh=5, keep_isolated_pixels=False) assert (result == mask).all() # allowing isolated pixels testcases = {(p, p, 0): [True, True, False], (p, 0, p): [True, False, True], (p, b, p): [True, True, True], (p, b, 0): [True, True, False], (b, b, 0): [False, False, False], (0, p, 0): [False, True, False]} for image, mask in testcases.items(): result = cleaning.tailcuts_clean(geom, np.array(image), picture_thresh=15, boundary_thresh=5, keep_isolated_pixels=True) assert (result == mask).all()
def test_tailcuts_clean_simple(): geom = CameraGeometry.from_name("LSTCam") image = np.zeros_like(geom.pix_id, dtype=np.float64) num_pix = 40 some_neighs = geom.neighbors[num_pix][0:3] # pick 3 neighbors image[num_pix] = 5.0 # set a single image pixel image[some_neighs] = 3.0 # make some boundaries that are neighbors image[10] = 3.0 # a boundary that is not a neighbor mask = cleaning.tailcuts_clean(geom, image, picture_thresh=4.5, boundary_thresh=2.5) assert 10 not in geom.pix_id[mask] assert set(some_neighs).union({num_pix}) == set(geom.pix_id[mask]) assert np.count_nonzero(mask) == 4
def fit_muon(x, y, image, geom, tailcuts): """ Fit the muon ring Paramenters --------- x, y: `floats` coordinates in the NominalFrame image: `np.ndarray` number of photoelectrons in each pixel geom: CameraGeometry image: `list` tail cuts for image cleaning Returns --------- muonringparam: `` clean_mask: `np.ndarray` mask after cleaning dist: `np.ndarray` distance of every pixel to the center of the muon ring image_clean: `np.ndarray` image after cleaning """ muonring = ChaudhuriKunduRingFitter(None) clean_mask = tailcuts_clean(geom, image, picture_thresh=tailcuts[0], boundary_thresh=tailcuts[1]) image_clean = image * clean_mask muonringparam = muonring.fit(x, y, image_clean) 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, image_clean * (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, image_clean * (ring_dist < muonringparam.ring_radius * 0.4)) return muonringparam, clean_mask, dist, image_clean
def test_tailcuts_clean_simple(): geom = CameraGeometry.from_name("LSTCam") image = np.zeros_like(geom.pix_id, dtype=np.float) num_pix = 40 some_neighs = geom.neighbors[num_pix][0:3] # pick 4 neighbors image[num_pix] = 5.0 # set a single image pixel image[some_neighs] = 3.0 # make some boundaries that are neighbors image[10] = 3.0 # a boundary that is not a neighbor mask = cleaning.tailcuts_clean(geom, image, picture_thresh=4.5, boundary_thresh=2.5) print((mask > 0).sum(), "clean pixels") print(geom.pix_id[mask]) assert 10 not in geom.pix_id[mask] assert set(some_neighs).union({num_pix}) == set(geom.pix_id[mask]) assert (mask > 0).sum() == 4
def fit_muon(x, y, image, geom, tailcuts): """ Fit the muon ring Paramenters --------- x, y: `floats` coordinates in the NominalFrame image: `np.ndarray` number of photoelectrons in each pixel geom: CameraGeometry image: `list` tail cuts for image cleaning Returns --------- muonringparam: `` clean_mask: `np.ndarray` mask after cleaning dist: `np.ndarray` distance of every pixel to the center of the muon ring image_clean: `np.ndarray` image after cleaning """ muonring = ChaudhuriKunduRingFitter(None) clean_mask = tailcuts_clean(geom, image, picture_thresh=tailcuts[0], boundary_thresh=tailcuts[1]) image_clean = image * clean_mask muonringparam = muonring.fit(x, y, image_clean) max_allowed_outliers_distance = 0.4 # Do an iterative fit removing pixels which are beyond max_allowed_outliers_distance*ring_radius # of the ring (along the radial direction): # The goal is to improve fit for good rings with very few additional non-ring bright pixels. for _ in [0] * 2: # just to iterate the fit twice more dist = np.sqrt((x - muonringparam.ring_center_x)**2 + (y - muonringparam.ring_center_y)**2) ring_dist = np.abs(dist - muonringparam.ring_radius) muonringparam = muonring.fit( x, y, image_clean * (ring_dist < muonringparam.ring_radius * max_allowed_outliers_distance)) return muonringparam, clean_mask, dist, image_clean
def test_FitGammaHillas(): filename = get_path("gamma_test.simtel.gz") fit = FitGammaHillas() fit.setup_geometry(*load_hessio(filename), phi=180 * u.deg, theta=20 * u.deg) tel_geom = {} source = hessio_event_source(filename) for event in source: hillas_dict = {} for tel_id in set(event.trig.tels_with_trigger) & set(event.dl0.tels_with_data): if tel_id not in tel_geom: tel_geom[tel_id] = CameraGeometry.guess( fit.cameras(tel_id)["PixX"].to(u.m), fit.cameras(tel_id)["PixY"].to(u.m), fit.telescopes["FL"][tel_id - 1] * u.m, ) pmt_signal = event.dl0.tel[tel_id].adc_sums[0] mask = tailcuts_clean(tel_geom[tel_id], pmt_signal, 1, picture_thresh=10.0, boundary_thresh=5.0) pmt_signal[mask is False] = 0 try: moments, moms2 = hillas_parameters(fit.cameras(tel_id)["PixX"], fit.cameras(tel_id)["PixY"], pmt_signal) hillas_dict[tel_id] = moments except HillasParameterizationError as e: print(e) continue if len(hillas_dict) < 2: continue fit_result = fit.predict(hillas_dict) print(fit_result) assert fit_result return
def update(frame): centroid = np.random.uniform(-0.5, 0.5, size=2) width = np.random.uniform(0, 0.01) length = np.random.uniform(0, 0.03) + width angle = np.random.uniform(0, 360) intens = np.random.exponential(2) * 50 model = toymodel.generate_2d_shower_model(centroid=centroid, width=width, length=length, psi=angle * u.deg) image, sig, bg = toymodel.make_toymodel_shower_image(geom, model.pdf, intensity=intens, nsb_level_pe=5000) # alternate between cleaned and raw images if self._counter == self.cleanframes: plt.suptitle("Image Cleaning ON") self.imclean = True if self._counter == self.cleanframes*2: plt.suptitle("Image Cleaning OFF") self.imclean = False self._counter = 0 if self.imclean: cleanmask = cleaning.tailcuts_clean(geom, image, pedvars=80) for ii in range(3): cleaning.dilate(geom, cleanmask) image[cleanmask == 0] = 0 # zero noise pixels self.log.debug("count = {}, image sum={} max={}" .format(self._counter, image.sum(), image.max())) disp.image = image if self.autoscale: disp.set_limits_percent(95) else: disp.set_limits_minmax(-100, 4000) disp.axes.figure.canvas.draw() self._counter += 1 return [ax,]
def clean_tail(self, img, cam_geom): mask = tailcuts_clean( cam_geom, img, picture_thresh=self.tail_thresholds[cam_geom.cam_id][1], boundary_thresh=self.tail_thresholds[cam_geom.cam_id][0]) if self.dilate: dilate(cam_geom, mask) img[~mask] = 0 self.cutflow.count("tailcut cleaning") if "ASTRI" in cam_geom.cam_id: # turn into 2d to apply island cleaning img = astri_to_2d_array(img) # if set, remove all signal patches but the biggest one new_img = self.island_cleaning(img) # turn back into 1d array new_img = array_2d_to_astri(new_img) new_geom = cam_geom else: # turn into 2d to apply island cleaning rot_geom, rot_img = convert_geometry_hex1d_to_rect2d( cam_geom, img, cam_geom.cam_id) # if set, remove all signal patches but the biggest one cleaned_img = self.island_cleaning(rot_img, self.hex_neighbours_1ring) # turn back into 1d array unrot_geom, unrot_img = convert_geometry_rect2d_back_to_hexe1d( rot_geom, cleaned_img, cam_geom.cam_id) new_img = unrot_img new_geom = unrot_geom if self.cutflow.cut("edge event", img=new_img, geom=new_geom): raise EdgeEvent return new_img, new_geom
def clean_tail(self, img, cam_geom): mask = tailcuts_clean( cam_geom, img, picture_thresh=self.tail_thresholds[cam_geom.cam_id][1], boundary_thresh=self.tail_thresholds[cam_geom.cam_id][0]) if self.dilate: dilate(cam_geom, mask) img[~mask] = 0 self.cutflow.count("tailcut cleaning") if "ASTRI" in cam_geom.cam_id: # turn into 2d to apply island cleaning img = astri_to_2d_array(img) # if set, remove all signal patches but the biggest one new_img = self.island_cleaning(img) # turn back into 1d array new_img = array_2d_to_astri(new_img) new_geom = cam_geom else: # turn into 2d to apply island cleaning rot_geom, rot_img = convert_geometry_hex1d_to_rect2d( cam_geom, img, cam_geom.cam_id) # if set, remove all signal patches but the biggest one cleaned_img = self.island_cleaning(rot_img, self.hex_neighbours_1ring) # turn back into 1d array unrot_geom, unrot_img = convert_geometry_rect2d_back_to_hexe1d( rot_geom, cleaned_img, cam_geom.cam_id) new_img = unrot_img new_geom = unrot_geom if self.cutflow.cut("edge event", img=new_img, geom=new_geom, rows=self.edge_width): raise EdgeEvent return new_img, new_geom
def test_tailcuts_clean(): geom = io.CameraGeometry.from_name("HESS", 1) image = np.zeros_like(geom.pix_id, dtype=np.float) pedvar = np.ones_like(geom.pix_id, dtype=np.float) N = 40 some_neighs = geom.neighbors[N][0:3] # pick 4 neighbors image[N] = 5.0 # set a single image pixel image[some_neighs] = 3.0 # make some boundaries that are neighbors image[10] = 3.0 # a boundary that is not a neighbor mask = cleaning.tailcuts_clean(geom, image, pedvar, picture_thresh=4.5, boundary_thresh=2.5) print((mask > 0).sum(), "clean pixels") print(geom.pix_id[mask]) assert 10 not in geom.pix_id[mask] assert set(some_neighs).union({N}) == set(geom.pix_id[mask]) assert (mask > 0).sum() == 4
def test_tailcuts_clean_with_isolated_pixels(): # start with simple 3-pixel camera geom = CameraGeometry.make_rectangular(3, 1, (-1, 1)) p = 15 # picture value b = 7 # boundary value testcases = { (p, p, 0): [True, True, False], (p, 0, p): [True, False, True], (p, b, p): [True, True, True], (p, b, 0): [True, True, False], (0, p, 0): [False, True, False], (b, b, 0): [False, False, False] } for image, mask in testcases.items(): result = cleaning.tailcuts_clean(geom, np.array(image), picture_thresh=15, boundary_thresh=5, keep_isolated_pixels=True) assert (result == mask).all()
def test_convert_geometry(): filename = get_path("gamma_test.simtel.gz") cam_geom = {} source = hessio_event_source(filename) # testing a few images just for the sake of being thorough counter = 5 for event in source: for tel_id in event.dl0.tels_with_data: if tel_id not in cam_geom: cam_geom[tel_id] = CameraGeometry.guess( event.inst.pixel_pos[tel_id][0], event.inst.pixel_pos[tel_id][1], event.inst.optical_foclen[tel_id]) # we want to test conversion of hex to rectangular pixel grid if cam_geom[tel_id].pix_type is not "hexagonal": continue print(tel_id, cam_geom[tel_id].pix_type) pmt_signal = apply_mc_calibration( #event.dl0.tel[tel_id].adc_samples[0], event.dl0.tel[tel_id].adc_sums[0], event.mc.tel[tel_id].dc_to_pe[0], event.mc.tel[tel_id].pedestal[0]) new_geom, new_signal = convert_geometry_1d_to_2d( cam_geom[tel_id], pmt_signal, cam_geom[tel_id].cam_id, add_rot=-2) unrot_geom, unrot_signal = convert_geometry_back( new_geom, new_signal, cam_geom[tel_id].cam_id, event.inst.optical_foclen[tel_id], add_rot=4) # if run as main, do some plotting if __name__ == "__main__": fig = plt.figure() plt.style.use('seaborn-talk') ax1 = fig.add_subplot(131) disp1 = CameraDisplay( cam_geom[tel_id], image=np.sum(pmt_signal, axis=1) if pmt_signal.shape[-1] == 25 else pmt_signal, ax=ax1) disp1.cmap = plt.cm.hot disp1.add_colorbar() plt.title("original geometry") ax2 = fig.add_subplot(132) disp2 = CameraDisplay( new_geom, image=np.sum(new_signal, axis=2) if new_signal.shape[-1] == 25 else new_signal, ax=ax2) disp2.cmap = plt.cm.hot disp2.add_colorbar() plt.title("slanted geometry") ax3 = fig.add_subplot(133) disp3 = CameraDisplay( unrot_geom, image=np.sum(unrot_signal, axis=1) if unrot_signal.shape[-1] == 25 else unrot_signal, ax=ax3) disp3.cmap = plt.cm.hot disp3.add_colorbar() plt.title("geometry converted back to hex") plt.show() # do some tailcuts cleaning mask1 = tailcuts_clean(cam_geom[tel_id], pmt_signal, 1, picture_thresh=10., boundary_thresh=5.) mask2 = tailcuts_clean(unrot_geom, unrot_signal, 1, picture_thresh=10., boundary_thresh=5.) pmt_signal[mask1 == False] = 0 unrot_signal[mask2 == False] = 0 ''' testing back and forth conversion on hillas parameters... ''' try: moments1 = hillas_parameters(cam_geom[tel_id].pix_x, cam_geom[tel_id].pix_y, pmt_signal) moments2 = hillas_parameters(unrot_geom.pix_x, unrot_geom.pix_y, unrot_signal) except (HillasParameterizationError, AssertionError) as e: ''' we don't want this test to fail because the hillas code threw an error ''' print(e) counter -= 1 if counter < 0: return else: continue ''' test if the hillas parameters from the original geometry and the forth-and-back rotated geometry are close ''' assert np.allclose([ moments1.length.value, moments1.width.value, moments1.phi.value ], [ moments2.length.value, moments2.width.value, moments2.phi.value ], rtol=1e-2, atol=1e-2) counter -= 1 if counter < 0: return
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()
if __name__ == '__main__': # Prepare the camera geometry geom = io.CameraGeometry.from_name('hess', 1) disp = visualization.CameraDisplay(geom) disp.set_limits_minmax(0, 350) disp.add_colorbar() # make a mock shower model model = mock.generate_2d_shower_model(centroid=(0.2, 0.2), width=0.01, length=0.1, psi='45d') # generate mock image in camera for this shower model. image, signal, noise = mock.make_mock_shower_image(geom, model.pdf, intensity=50, nsb_level_pe=100) #Image cleaning clean_mask = tailcuts_clean(geom, image, 1, 10, 5) #pedvars = 1 and core and boundary threshold in pe image[~clean_mask] = 0 #Pixel values in the camera pix_x = geom.pix_x.value pix_y = geom.pix_y.value # Hillas parameters hillas1, hillas2 = hillas_parameters(pix_x, pix_y, image) print(hillas1, hillas2) #Overlay moments disp.image = image disp.overlay_moments(hillas1, color = 'seagreen', linewidth = 2) plt.show()
pointing_azimuth = {} pointing_altitude = {} time_gradients = {} for telescope_id, dl1 in event.dl1.tel.items(): camera = event.inst.subarray.tels[telescope_id].camera image = dl1.image[0] peakpos = dl1.peakpos[0] # cleaning boundary, picture, min_neighbors = cleaning_level[camera.cam_id] clean = tailcuts_clean( camera, image, boundary_thresh=boundary, picture_thresh=picture, min_number_picture_neighbors=min_neighbors ) # ignore images with less than 5 pixels after cleaning if clean.sum() < 5: continue # image parameters hillas_c = hillas_parameters(camera[clean], image[clean]) leakage_c = leakage(camera, image, clean) n_islands, island_ids = number_of_islands(camera, clean) timing_c = timing_parameters( camera[clean], image[clean], peakpos[clean], hillas_c
def iterate_param(possible_cleaning_levels, event, camera, image, telescope_id, quality_param_list, hillas_containers, index=0): """ iteration of the possible cleaning levels and research of the optimal through quality parameters Parameters ---------- possible_cleaning_levels: array with all the possible cleaning levels chosen event: calibrated event camera: Camera geometry information image: pixel array that has to be cleaned telescope_id: unique number for the identification of a telescope quality_param_list: list of all the quality parameters of a cleaned image for all the possible cleaning levels chosen hillas_containers: dictionary with telescope IDs as key and HillasParametersContainer instances as values index: index of the current cleaning level chosen for the comparision Returns ------- new_param: the better parameters for the given image """ while index < len(possible_cleaning_levels): picture, boundary, min_neighbors = possible_cleaning_levels[index] clean = tailcuts_clean(camera, image, boundary_thresh=boundary, picture_thresh=picture, min_number_picture_neighbors=min_neighbors) # if the size of the clean mask is too little, we can't have a good ellipse reconstruction flag = verify_size_clean_mask(clean, 6) if flag: break cleaned_image = event.dl1.tel[telescope_id].image.copy() cleaned_image[~clean] = 0.0 hillas_c = hillas_parameters(camera[clean], image[clean]) hillas_containers[telescope_id] = hillas_c closest_approach = closest_approach_distance( hillas_containers[telescope_id]) likelihood = likelihood_function(event, cleaned_image, telescope_id) num_diff_pixels = numbers_of_different_pixels(camera, event, cleaned_image, telescope_id, picture, boundary, min_neighbors) quality_param_list.append( [closest_approach, likelihood, num_diff_pixels]) index_best_parameters = optimize_param(quality_param_list, index) index = index + 1 new_param = possible_cleaning_levels[index_best_parameters] return new_param
def test_reconstructors(reconstructors): """ a test of the complete fit procedure on one event including: • tailcut cleaning • hillas parametrisation • HillasPlane creation • direction fit • position fit in the end, proper units in the output are asserted """ filename = get_dataset_path("gamma_test_large.simtel.gz") source = event_source(filename, max_events=10) horizon_frame = AltAz() # record how many events were reconstructed by each reconstructor reconstructed_events = np.zeros((len(reconstructors))) for event in source: array_pointing = SkyCoord(az=event.mc.az, alt=event.mc.alt, frame=horizon_frame) hillas_dict = {} telescope_pointings = {} for tel_id in event.dl0.tels_with_data: geom = source.subarray.tel[tel_id].camera.geometry telescope_pointings[tel_id] = SkyCoord( alt=event.pointing.tel[tel_id].altitude, az=event.pointing.tel[tel_id].azimuth, frame=horizon_frame) pmt_signal = event.r0.tel[tel_id].waveform[0].sum(axis=1) mask = tailcuts_clean(geom, pmt_signal, picture_thresh=10., boundary_thresh=5.) pmt_signal[mask == 0] = 0 try: moments = hillas_parameters(geom, pmt_signal) hillas_dict[tel_id] = moments except HillasParameterizationError as e: print(e) continue if len(hillas_dict) < 2: continue for count, reco_method in enumerate(reconstructors): reconstructed_events[count] += 1 reconstructor = reco_method() reconstructor_out = reconstructor.predict(hillas_dict, source.subarray, array_pointing, telescope_pointings) reconstructor_out.alt.to(u.deg) reconstructor_out.az.to(u.deg) reconstructor_out.core_x.to(u.m) assert reconstructor_out.is_valid np.testing.assert_array_less(np.zeros_like(reconstructed_events), reconstructed_events)
def main(): std_config = get_standard_config() if args.config_file is not None: config = replace_config(std_config, read_configuration_file(args.config_file)) else: config = std_config print(config['tailcut']) foclen = OpticsDescription.from_name('LST').equivalent_focal_length cam_table = Table.read(args.input_file, path="instrument/telescope/camera/LSTCam") camera_geom = CameraGeometry.from_table(cam_table) dl1_container = DL1ParametersContainer() parameters_to_update = list(HillasParametersContainer().keys()) parameters_to_update.extend([ 'concentration_cog', 'concentration_core', 'concentration_pixel', 'leakage_intensity_width_1', 'leakage_intensity_width_2', 'leakage_pixels_width_1', 'leakage_pixels_width_2', 'n_islands', 'intercept', 'time_gradient', 'n_pixels', 'wl', 'r', ]) nodes_keys = get_dataset_keys(args.input_file) if args.noimage: nodes_keys.remove(dl1_images_lstcam_key) auto_merge_h5files([args.input_file], args.output_file, nodes_keys=nodes_keys) with tables.open_file(args.input_file, mode='r') as input: image_table = input.root[dl1_images_lstcam_key] with tables.open_file(args.output_file, mode='a') as output: params = output.root[dl1_params_lstcam_key].read() for ii, row in enumerate(image_table): if ii % 10000 == 0: print(ii) image = row['image'] peak_time = row['peak_time'] signal_pixels = tailcuts_clean(camera_geom, image, **config['tailcut']) n_pixels = np.count_nonzero(signal_pixels) if n_pixels > 0: num_islands, island_labels = number_of_islands( camera_geom, signal_pixels) n_pixels_on_island = np.bincount( island_labels.astype(np.int)) n_pixels_on_island[ 0] = 0 # first island is no-island and should not be considered max_island_label = np.argmax(n_pixels_on_island) signal_pixels[island_labels != max_island_label] = False hillas = hillas_parameters(camera_geom[signal_pixels], image[signal_pixels]) dl1_container.fill_hillas(hillas) dl1_container.set_timing_features( camera_geom[signal_pixels], image[signal_pixels], peak_time[signal_pixels], hillas) dl1_container.set_leakage(camera_geom, image, signal_pixels) dl1_container.set_concentration(camera_geom, image, hillas) dl1_container.n_islands = num_islands dl1_container.wl = dl1_container.width / dl1_container.length dl1_container.n_pixels = n_pixels width = np.rad2deg(np.arctan2(dl1_container.width, foclen)) length = np.rad2deg( np.arctan2(dl1_container.length, foclen)) dl1_container.width = width dl1_container.length = length dl1_container.r = np.sqrt(dl1_container.x**2 + dl1_container.y**2) else: # for consistency with r0_to_dl1.py: for key in dl1_container.keys(): dl1_container[key] = \ u.Quantity(0, dl1_container.fields[key].unit) dl1_container.width = u.Quantity(np.nan, u.m) dl1_container.length = u.Quantity(np.nan, u.m) dl1_container.wl = u.Quantity(np.nan, u.m) for p in parameters_to_update: params[ii][p] = u.Quantity(dl1_container[p]).value output.root[dl1_params_lstcam_key][:] = params
def analyze_muon_event(event, params=None, geom_dict=None): """ Generic muon event analyzer. Parameters ---------- event : ctapipe dl1 event container Returns ------- muonringparam, muonintensityparam : MuonRingParameter and MuonIntensityParameter container event """ # Declare a dict to define the muon cuts (ASTRI and SCT missing) muon_cuts = {} names = ['LST:LSTCam','MST:NectarCam','MST:FlashCam','MST-SCT:SCTCam','SST-1M:DigiCam','SST-GCT:CHEC','SST-ASTRI:ASTRICam'] TailCuts = [(5,7),(5,7),(10,12),(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)] 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)] TotalPix = [1855.,1855.,1764.,11328.,1296.,2048.,2368.]#8% (or 6%) as limit MinPix = [148.,148.,141.,680.,104.,164.,142.] #Need to either convert from the pixel area in m^2 or check the camera specs AngPixelWidth = [0.1,0.2,0.18,0.067,0.24,0.2,0.17] #Found from TDRs (or the pixel area) hole_rad = []#Need to check and implement cam_rad = [2.26,3.96,3.87,4.,4.45,2.86,5.25]#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] sct = [False,False,False,True,False,True,True] muon_cuts = {'Name':names,'TailCuts':TailCuts,'Impact':impact,'RingWidth':ringwidth,'TotalPix':TotalPix,'MinPix':MinPix,'CamRad':cam_rad,'SecRad':sec_rad,'SCT':sct,'AngPixW':AngPixelWidth} #print(muon_cuts) muonringlist = []#[None] * len(event.dl0.tels_with_data) muonintensitylist = []#[None] * len(event.dl0.tels_with_data) tellist = [] #for tid in event.dl0.tels_with_data: # tellist.append(tid) muon_event_param = {'TelIds':tellist,'MuonRingParams':muonringlist,'MuonIntensityParams':muonintensitylist} #muonringparam = None #muonintensityparam = None for telid in event.dl0.tels_with_data: #print("Analysing muon event for tel",telid) muonringparam = None muonintensityparam = None #idx = muon_event_param['TelIds'].index(telid) x, y = event.inst.pixel_pos[telid] #image = event.dl1.tel[telid].calibrated_image image = event.dl1.tel[telid].image[0] # Get geometry geom = None if geom_dict is not None and telid in geom_dict: geom = geom_dict[telid] else: log.debug("[calib] Guessing camera geometry") geom = CameraGeometry.guess(*event.inst.pixel_pos[telid], event.inst.optical_foclen[telid]) log.debug("[calib] Camera geometry found") if geom_dict is not None: geom_dict[telid] = geom teldes = event.inst.subarray.tel[telid] dict_index = muon_cuts['Name'].index(str(teldes)) #print('found an index of',dict_index,'for camera',geom.cam_id) #tailcuts = (5.,7.) tailcuts = muon_cuts['TailCuts'][dict_index] #print("Tailcuts are",tailcuts[0],tailcuts[1]) #rot_angle = 0.*u.deg #if event.inst.optical_foclen[telid] > 10.*u.m and event.dl0.tel[telid].num_pixels != 1764: #rot_angle = -100.14*u.deg clean_mask = tailcuts_clean(geom,image,picture_thresh=tailcuts[0],boundary_thresh=tailcuts[1]) camera_coord = CameraFrame(x=x,y=y,z=np.zeros(x.shape)*u.m, focal_length = event.inst.optical_foclen[telid], rotation=geom.pix_rotation) #print("Camera",geom.cam_id,"focal length",event.inst.optical_foclen[telid],"rotation",geom.pix_rotation) #TODO: correct this hack for values over 90 altval = event.mcheader.run_array_direction[1] if (altval > np.pi/2.): altval = np.pi/2. altaz = HorizonFrame(alt=altval*u.rad,az=event.mcheader.run_array_direction[0]*u.rad) nom_coord = camera_coord.transform_to(NominalFrame(array_direction=altaz,pointing_direction=altaz)) x = nom_coord.x.to(u.deg) y = nom_coord.y.to(u.deg) img = image*clean_mask noise = 5. weight = img / (img+noise) muonring = ChaudhuriKunduRingFitter(None) #print("img:",np.sum(image),"mask:",np.sum(clean_mask), "x=",x,"y=",y) if not sum(img):#Nothing left after tail cuts continue muonringparam = muonring.fit(x,y,image*clean_mask) #muonringparam = muonring.fit(x,y,weight) 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) #print("1: x",muonringparam.ring_center_x,"y",muonringparam.ring_center_y,"radius",muonringparam.ring_radius) muonringparam = muonring.fit(x,y,img*(ring_dist<muonringparam.ring_radius*0.4)) #print("2: x",muonringparam.ring_center_x,"y",muonringparam.ring_center_y,"radius",muonringparam.ring_radius) muonringparam.tel_id = telid muonringparam.run_id = event.dl0.run_id muonringparam.event_id = event.dl0.event_id dist_mask = np.abs(dist-muonringparam.ring_radius)<muonringparam.ring_radius*0.4 rad = list() cx = list() cy = list() mc_x = event.mc.core_x mc_y = event.mc.core_y pix_im = image*dist_mask nom_dist = np.sqrt(np.power(muonringparam.ring_center_x,2)+np.power(muonringparam.ring_center_y,2)) #numpix = event.dl0.tel[telid].num_pixels minpix = muon_cuts['MinPix'][dict_index]#0.06*numpix #or 8% mir_rad = np.sqrt(event.inst.mirror_dish_area[telid]/(np.pi))#need to consider units? (what about hole? Area is then less...) #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(np.sum(pix_im>tailcuts[0])>0.1*minpix and np.sum(pix_im)>minpix and nom_dist < muon_cuts['CamRad'][dict_index]*u.deg and muonringparam.ring_radius<1.5*u.deg and muonringparam.ring_radius>1.*u.deg): #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) #embed() ctel = MuonLineIntegrate(mir_rad,0.2*u.m,pixel_width=muon_cuts['AngPixW'][dict_index]*u.deg,sct_flag=muon_cuts['SCT'][dict_index], secondary_radius=muon_cuts['SecRad'][dict_index]) if (image.shape[0] == muon_cuts['TotalPix'][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.run_id = event.dl0.run_id muonintensityoutput.event_id = event.dl0.event_id muonintensityoutput.mask = dist_mask print("Tel",telid,"Impact parameter = ",muonintensityoutput.impact_parameter,"mir_rad",mir_rad,"ring_width=",muonintensityoutput.ring_width) #if(muonintensityoutput.impact_parameter > muon_cuts['Impact'][dict_index][1]*mir_rad or muonintensityoutput.impact_parameter < muon_cuts['Impact'][dict_index][0]*mir_rad): # print("Failed on impact parameter low cut",muon_cuts['Impact'][dict_index][0]*mir_rad,"high cut",muon_cuts['Impact'][dict_index][1]*mir_rad,"for",geom.cam_id) #if(muonintensityoutput.ring_width > muon_cuts['RingWidth'][dict_index][1]*u.deg or muonintensityoutput.ring_width < muon_cuts['RingWidth'][dict_index][0]*u.deg): # print("Failed on ring width low cut",muon_cuts['RingWidth'][dict_index][0]*u.deg,"high cut",muon_cuts['RingWidth'][dict_index][1]*u.deg,"for ",geom.cam_id) #print("Cuts <",muon_cuts["Impact"][dict_index][1]*mir_rad,"cuts >",muon_cuts['Impact'][dict_index][0]*u.m,'ring_width < ',muon_cuts['RingWidth'][dict_index][1]*u.deg,'ring_width >',muon_cuts['RingWidth'][dict_index][0]*u.deg) if( muonintensityoutput.impact_parameter < muon_cuts['Impact'][dict_index][1]*mir_rad and muonintensityoutput.impact_parameter > muon_cuts['Impact'][dict_index][0]*u.m and muonintensityoutput.ring_width < muon_cuts['RingWidth'][dict_index][1]*u.deg and muonintensityoutput.ring_width > muon_cuts['RingWidth'][dict_index][0]*u.deg ): muonintensityparam = muonintensityoutput idx = tellist.index(telid) muonintensitylist[idx] = muonintensityparam print("Muon in tel",telid,"# tels in event=",len(event.dl0.tels_with_data)) else: continue #print("Fitted ring centre (2):",muonringparam.ring_center_x,muonringparam.ring_center_y) #return muonringparam, muonintensityparam return muon_event_param
def process_event(event, config): ''' Processes ''' reco_algorithm = config.reco_algorithm features = {} params = {} pointing_azimuth = {} pointing_altitude = {} tel_x = {} tel_y = {} tel_focal_lengths = {} cleaning_method = config.cleaning_method valid_cleaning_methods = ['tailcuts_clean', 'fact_image_cleaning'] if cleaning_method not in valid_cleaning_methods: print('Cleaning Method not implemented') print('Please use one of ', valid_cleaning_methods) return None for telescope_id, dl1 in event.dl1.tel.items(): camera = event.inst.subarray.tels[telescope_id].camera if camera.cam_id not in config.allowed_cameras: continue telescope_type_name = event.inst.subarray.tels[ telescope_id].optics.tel_type if cleaning_method == 'tailcuts_clean': boundary_thresh, picture_thresh, min_number_picture_neighbors = config.cleaning_level[ camera.cam_id] mask = tailcuts_clean(camera, dl1.image[0], *config.cleaning_level[camera.cam_id]) elif cleaning_method == 'fact_image_cleaning': mask = fact_image_cleaning( camera, dl1.image[0], *config.cleaning_level_fact[camera.cam_id]) try: cleaned = dl1.image[0].copy() cleaned[~mask] = 0 hillas_container = hillas_parameters( camera, cleaned, ) params[telescope_id] = hillas_container except HillasParameterizationError: continue # probably wise to add try...except blocks here as well # Add more Features here (look what ctapipe can do, timing?) num_islands, island_labels = number_of_islands(camera, mask) island_dict = { 'num_islands': num_islands, 'island_labels': island_labels } leakage_container = leakage(camera, dl1.image[0], mask) timing_container = timing_parameters(camera, dl1.image[0], dl1.peakpos[0], hillas_container) pointing_azimuth[ telescope_id] = event.mc.tel[telescope_id].azimuth_raw * u.rad pointing_altitude[ telescope_id] = event.mc.tel[telescope_id].altitude_raw * u.rad tel_x[telescope_id] = event.inst.subarray.positions[telescope_id][0] tel_y[telescope_id] = event.inst.subarray.positions[telescope_id][1] telescope_description = event.inst.subarray.tel[telescope_id] tel_focal_lengths[ telescope_id] = telescope_description.optics.equivalent_focal_length d = { 'array_event_id': event.dl0.event_id, 'telescope_id': int(telescope_id), 'camera_name': camera.cam_id, 'camera_id': config.names_to_id[camera.cam_id], 'run_id': event.r0.obs_id, 'telescope_type_name': telescope_type_name, 'telescope_type_id': config.types_to_id[telescope_type_name], 'pointing_azimuth': event.mc.tel[telescope_id].azimuth_raw, 'pointing_altitude': event.mc.tel[telescope_id].altitude_raw, 'mirror_area': telescope_description.optics.mirror_area, 'focal_length': telescope_description.optics.equivalent_focal_length, } d.update(hillas_container.as_dict()) d.update(leakage_container.as_dict()) d.update(island_dict) d.update(timing_container.as_dict()) features[telescope_id] = ({k: strip_unit(v) for k, v in d.items()}) if reco_algorithm == 'intersection': reco = HillasIntersection() array_direction = SkyCoord(alt=event.mcheader.run_array_direction[1], az=event.mcheader.run_array_direction[0], frame='altaz') reconstruction = reco.predict(params, tel_x, tel_y, tel_focal_lengths, array_direction) elif reco_algorithm == 'planes': reco = HillasReconstructor() reconstruction = reco.predict(params, event.inst, pointing_altitude, pointing_azimuth) for telescope_id in event.dl1.tel.keys(): if telescope_id not in params: continue camera = event.inst.subarray.tels[telescope_id].camera if camera.cam_id not in config.allowed_cameras: continue pos = event.inst.subarray.positions[telescope_id] x, y = pos[0], pos[1] core_x = reconstruction.core_x core_y = reconstruction.core_y d = np.sqrt((core_x - x)**2 + (core_y - y)**2) features[telescope_id]['distance_to_core'] = d.value return pd.DataFrame(list(features.values())), reconstruction, params
def test_reconstruction(): """ a test of the complete fit procedure on one event including: • tailcut cleaning • hillas parametrisation • HillasPlane creation • direction fit • position fit in the end, proper units in the output are asserted """ filename = get_dataset_path("gamma_test_large.simtel.gz") source = EventSource(filename, max_events=10) calib = CameraCalibrator(subarray=source.subarray) horizon_frame = AltAz() reconstructed_events = 0 for event in source: calib(event) mc = event.simulation.shower array_pointing = SkyCoord(az=mc.az, alt=mc.alt, frame=horizon_frame) hillas_dict = {} telescope_pointings = {} for tel_id, dl1 in event.dl1.tel.items(): geom = source.subarray.tel[tel_id].camera.geometry telescope_pointings[tel_id] = SkyCoord( alt=event.pointing.tel[tel_id].altitude, az=event.pointing.tel[tel_id].azimuth, frame=horizon_frame, ) mask = tailcuts_clean(geom, dl1.image, picture_thresh=10.0, boundary_thresh=5.0) try: moments = hillas_parameters(geom[mask], dl1.image[mask]) hillas_dict[tel_id] = moments except HillasParameterizationError as e: print(e) continue if len(hillas_dict) < 2: continue else: reconstructed_events += 1 # The three reconstructions below gives the same results fit = HillasReconstructor() fit_result_parall = fit.predict(hillas_dict, source.subarray, array_pointing) fit = HillasReconstructor() fit_result_tel_point = fit.predict(hillas_dict, source.subarray, array_pointing, telescope_pointings) for key in fit_result_parall.keys(): print(key, fit_result_parall[key], fit_result_tel_point[key]) fit_result_parall.alt.to(u.deg) fit_result_parall.az.to(u.deg) fit_result_parall.core_x.to(u.m) assert fit_result_parall.is_valid assert reconstructed_events > 0
def test_invalid_events(): """ The HillasReconstructor is supposed to fail in these cases: - less than two teleskopes - any width is NaN - any width is 0 This test uses the same sample simtel file as test_reconstruction(). As there are no invalid events in this file, multiple hillas_dicts are constructed to make sure Exceptions get thrown in the mentioned edge cases. Test will fail if no Exception or another Exception gets thrown.""" filename = get_dataset_path("gamma_test_large.simtel.gz") fit = HillasReconstructor() tel_azimuth = {} tel_altitude = {} source = EventSource(filename, max_events=10) subarray = source.subarray calib = CameraCalibrator(subarray) for event in source: calib(event) hillas_dict = {} for tel_id, dl1 in event.dl1.tel.items(): geom = source.subarray.tel[tel_id].camera.geometry tel_azimuth[tel_id] = event.pointing.tel[tel_id].azimuth tel_altitude[tel_id] = event.pointing.tel[tel_id].altitude mask = tailcuts_clean(geom, dl1.image, picture_thresh=10.0, boundary_thresh=5.0) try: moments = hillas_parameters(geom[mask], dl1.image[mask]) hillas_dict[tel_id] = moments except HillasParameterizationError as e: continue # construct a dict only containing the last telescope events # (#telescopes < 2) hillas_dict_only_one_tel = dict() hillas_dict_only_one_tel[tel_id] = hillas_dict[tel_id] with pytest.raises(TooFewTelescopesException): fit.predict(hillas_dict_only_one_tel, subarray, tel_azimuth, tel_altitude) # construct a hillas dict with the width of the last event set to 0 # (any width == 0) hillas_dict_zero_width = hillas_dict.copy() hillas_dict_zero_width[tel_id]["width"] = 0 * u.m with pytest.raises(InvalidWidthException): fit.predict(hillas_dict_zero_width, subarray, tel_azimuth, tel_altitude) # construct a hillas dict with the width of the last event set to np.nan # (any width == nan) hillas_dict_nan_width = hillas_dict.copy() hillas_dict_zero_width[tel_id]["width"] = np.nan * u.m with pytest.raises(InvalidWidthException): fit.predict(hillas_dict_nan_width, subarray, tel_azimuth, tel_altitude)
def test_reconstructors(reconstructors): """ a test of the complete fit procedure on one event including: • tailcut cleaning • hillas parametrisation • HillasPlane creation • direction fit • position fit in the end, proper units in the output are asserted""" filename = get_dataset_path( "gamma_LaPalma_baseline_20Zd_180Az_prod3b_test.simtel.gz") source = EventSource(filename, max_events=10) subarray = source.subarray calib = CameraCalibrator(source.subarray) horizon_frame = AltAz() for event in source: calib(event) sim_shower = event.simulation.shower array_pointing = SkyCoord(az=sim_shower.az, alt=sim_shower.alt, frame=horizon_frame) telescope_pointings = {} for tel_id, dl1 in event.dl1.tel.items(): geom = source.subarray.tel[tel_id].camera.geometry telescope_pointings[tel_id] = SkyCoord( alt=event.pointing.tel[tel_id].altitude, az=event.pointing.tel[tel_id].azimuth, frame=horizon_frame, ) mask = tailcuts_clean(geom, dl1.image, picture_thresh=10.0, boundary_thresh=5.0) dl1.parameters = ImageParametersContainer() try: moments = hillas_parameters(geom[mask], dl1.image[mask]) except HillasParameterizationError: dl1.parameters.hillas = HillasParametersContainer() continue # Make sure we provide only good images for the test if np.isnan(moments.width.value) or (moments.width.value == 0): dl1.parameters.hillas = HillasParametersContainer() else: dl1.parameters.hillas = moments hillas_dict = { tel_id: dl1.parameters.hillas for tel_id, dl1 in event.dl1.tel.items() if np.isfinite(dl1.parameters.hillas.intensity) } if len(hillas_dict) < 2: continue for count, reco_method in enumerate(reconstructors): if reco_method is HillasReconstructor: reconstructor = HillasReconstructor(subarray) reconstructor(event) event.dl2.stereo.geometry["HillasReconstructor"].alt.to(u.deg) event.dl2.stereo.geometry["HillasReconstructor"].az.to(u.deg) event.dl2.stereo.geometry["HillasReconstructor"].core_x.to(u.m) assert event.dl2.stereo.geometry[ "HillasReconstructor"].is_valid else: reconstructor = reco_method() try: reconstructor_out = reconstructor.predict( hillas_dict, source.subarray, array_pointing, telescope_pointings, ) except InvalidWidthException: continue reconstructor_out.alt.to(u.deg) reconstructor_out.az.to(u.deg) reconstructor_out.core_x.to(u.m) assert reconstructor_out.is_valid
def display_event(event, calibrate = 0, max_tel = 4, cleaning=None): """an extremely inefficient display. It creates new instances of CameraDisplay for every event and every camera, and also new axes for each event. It's hacked, but it works """ print("Displaying... please wait (this is an inefficient implementation)") global fig ntels = min(max_tel, len(event.dl0.tels_with_data)) fig.clear() plt.suptitle("EVENT {}".format(event.dl0.event_id)) disps = [] mc_sum = None all_moments = [] for ii, tel_id in enumerate(event.dl0.tels_with_data): if ii >= max_tel: break print("\t draw cam {}...".format(tel_id)) nn = int(ceil((ntels)**.5)) ax = plt.subplot(nn, 2*nn, 2*(ii+1)-1) x, y = event.inst.pixel_pos[tel_id] geom = io.CameraGeometry.guess(x, y, event.inst.optical_foclen[tel_id]) disp = visualization.CameraDisplay(geom, ax=ax, title="CT{0} DetectorResponse".format(tel_id)) disp.pixels.set_antialiaseds(False) disp.autoupdate = False disp.cmap = plt.cm.hot chan = 0 signals = event.dl0.tel[tel_id].adc_sums[chan].astype(float)[:] if calibrate: signals = apply_mc_calibration_ASTRI(event.dl0.tel[tel_id].adc_sums, tel_id) if cleaning == 'tailcut': mask = tailcuts_clean(geom, signals, 1,picture_thresh=10.,boundary_thresh=8.) dilate(geom, mask) signals[mask==False] = 0 moments = hillas_parameters_2(geom.pix_x, geom.pix_y, signals) disp.image = signals disp.overlay_moments(moments, color='seagreen', linewidth=3) disp.set_limits_percent(95) disp.add_colorbar() disps.append(disp) ax = plt.subplot(nn, 2*nn, 2*(ii+1)) geom = io.CameraGeometry.guess(x, y, event.inst.optical_foclen[tel_id]) disp = visualization.CameraDisplay(geom, ax=ax, title="CT{0} PhotoElectrons".format(tel_id)) disp.pixels.set_antialiaseds(False) disp.autoupdate = False disp.cmap = plt.cm.hot chan = 0 #print (event.mc.tel[tel_id].photo_electrons) for jj in range(len(event.mc.tel[tel_id].photo_electron_image)): event.dl0.tel[tel_id].adc_sums[chan][jj] = event.mc.tel[tel_id].photo_electron_image[jj] signals2 = event.dl0.tel[tel_id].adc_sums[chan].astype(float) moments2 = hillas_parameters_2(geom.pix_x, geom.pix_y, signals2) all_moments.append(moments2) if mc_sum is None: mc_sum = signals2 else: scale = 3 if tel_id == 37 else 1 for i, val in enumerate(signals2): mc_sum[i] += val/scale disp.image = mc_sum for moments2 in all_moments: disp.overlay_moments(moments2, color='seagreen', linewidth=2) disp.set_limits_percent(95) disp.add_colorbar() disps.append(disp) return disps
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:LSTCam', 'MST:NectarCam', '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[0] # 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=HorizonFrame() ) 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 func(paths, outpath, ro, rc, rn): # iterate on each proton file & concatenate charge arrays for n, f in enumerate(paths): # get the data from the file try: print("Opening file #{}...".format(n)) data_p = tables.open_file(f) _, LST_event_index, LST_image_charge, LST_image_peak_times = get_LST_data(data_p) _, ei_alt, ei_az, ei_mc_energy = get_event_data(data_p) # Excluding the 0th element - it's the empty one!! LST_event_index = LST_event_index[1:] LST_image_charge = LST_image_charge[1:] LST_image_peak_times = LST_image_peak_times[1:] # get camera geometry & camera pixels coordinates camera = CameraGeometry.from_name("LSTCam") points = np.array([np.array(camera.pix_x / u.m), np.array(camera.pix_y / u.m)]).T # original choice by Nicola: 100x100 points in 2.5m x 2.5m #grid_x, grid_y = np.mgrid[-1.25:1.25:100j, -1.25:1.25:100j] # I choose instead 96x88 px in 2.40m x 2.20m: same spatial separation, less points grid_x, grid_y = np.mgrid[-1.20:1.20:96j, -1.10:1.10:88j] ''' was probably useless and wrong even with the old simulations if alt_array > 90: alt_array = 90 ''' # LST coordinates (pointing position) #point = AltAz(alt=alt_array * u.deg, az=az_array * u.deg) lst_image_charge_interp = [] lst_image_peak_times_interp = [] # alt az of the array [deg] #az_array = 180. # before was ai_run_array_direction[0][0], now it is hardcoded as it's not present in new files #alt_array = 70. # ai_run_array_direction[0][1] #delta_az = [] #delta_alt = [] intensities = [] intensities_width_2 = [] acc_idxs = [] # accepted indexes # in principle it can be removed when cuts (line AAA) are not used here cleaning_level = {'LSTCam': (3.5, 7.5, 2)} count = 0 #rejected = open(f[:-3] + "_rejected.txt", "w") for i in trange(0, len(LST_image_charge), desc="Images interpolation"): image = LST_image_charge[i] time = LST_image_peak_times[i] boundary, picture, min_neighbors = cleaning_level['LSTCam'] clean = tailcuts_clean( camera, image, boundary_thresh=boundary, picture_thresh=picture, min_number_picture_neighbors=min_neighbors ) if len(np.where(clean > 0)[0]) != 0: hillas = hillas_parameters(camera[clean], image[clean]) intensity = hillas['intensity'] l = leakage(camera, image, clean) # print(l) leakage2_intensity = l['intensity_width_2'] # if intensity > 50 and leakage2_intensity < 0.2: # ------>AAA --- CUT DIRECTLY DURING INTERP # cubic interpolation interp_img = griddata(points, image, (grid_x, grid_y), fill_value=0, method='cubic') interp_time = griddata(points, time, (grid_x, grid_y), fill_value=0, method='cubic') # delta az, delta alt computation #az = ei_az[LST_event_index[i]] #alt = ei_alt[LST_event_index[i]] #src = AltAz(alt=alt * u.rad, az=az * u.rad) #source_direction = src.transform_to(NominalFrame(origin=point)) # appending to arrays lst_image_charge_interp.append(interp_img) lst_image_peak_times_interp.append(interp_time) #delta_az.append(source_direction.delta_az.deg) #delta_alt.append(source_direction.delta_alt.deg) intensities.append(intensity) intensities_width_2.append(leakage2_intensity) acc_idxs += [i] # also this one can be removed when no cuts here else: count += 1 # #rejected.write("{}.\tImage #{} rejected (no islands)!\n".format(count, i)) # print("No islands: image #{} rejected! (cumulative: {})\n".format(i, count), end='\r') # lst_image_charge_interp = np.array(lst_image_charge_interp) print("Number of rejected images: {} ({:.1f}%)".format(count, count / len(intensities) *100)) data_p.close() #rejected.close() fpath = Path(f) newname = fpath.name[:-3] + '_interp.h5' filename = str(PurePath(outpath, newname)) print("Writing file: " + filename + "\n") data_file = h5py.File(filename, 'w') data_file.create_dataset('Event_Info/ei_alt', data=np.array(ei_alt)) data_file.create_dataset('Event_Info/ei_az', data=np.array(ei_az)) data_file.create_dataset('Event_Info/ei_mc_energy', data=np.array(ei_mc_energy)) data_file.create_dataset('LST/LST_event_index', data=np.array(LST_event_index)[acc_idxs]) data_file.create_dataset('LST/LST_image_charge', data=np.array(LST_image_charge)[acc_idxs]) data_file.create_dataset('LST/LST_image_peak_times', data=np.array(LST_image_peak_times)[acc_idxs]) # data_file.create_dataset('LST/LST_event_index', data=np.array(LST_event_index)) # data_file.create_dataset('LST/LST_image_charge', data=np.array(LST_image_charge)) # data_file.create_dataset('LST/LST_image_peak_times', data=np.array(LST_image_peak_times)) data_file.create_dataset('LST/LST_image_charge_interp', data=np.array(lst_image_charge_interp)) data_file.create_dataset('LST/LST_image_peak_times_interp', data=np.array(lst_image_peak_times_interp)) #data_file.create_dataset('LST/delta_alt', data=np.array(delta_alt)) #data_file.create_dataset('LST/delta_az', data=np.array(delta_az)) data_file.create_dataset('LST/intensities', data=np.array(intensities)) data_file.create_dataset('LST/intensities_width_2', data=np.array(intensities_width_2)) data_file.close() # in the interpolated files there will be all the original events # but for the LST only the ones actually see at least from one LST (as in the original files) # and that are above thresholds cuts if ro == '1': remove(f) print('Removing original file') except HDF5ExtError: print('\nUnable to open file' + f) if rc == '1': print('Removing it...') remove(f) except NoSuchNodeError: print('This file has a problem with the data structure: ' + f) if rn == '1': print('Removing it...') remove(f)
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 main(): args = parser.parse_args() event_generator = fact_event_generator( args.inputfile, args.drsfile, allowed_triggers={4}, ) fig = plt.figure(figsize=(12, 6)) ax1 = fig.add_axes([0, 0, 0.4, 1]) ax1.set_axis_off() divider = make_axes_locatable(ax1) cax1 = divider.append_axes('right', size="5%", pad=0.05) ax2 = fig.add_axes([0.5, 0.0, 0.4, 1]) ax2.set_axis_off() divider = make_axes_locatable(ax2) cax2 = divider.append_axes('right', size="5%", pad=0.05) geom = CameraGeometry.from_name('FACT') disp1 = CameraDisplay(geom, ax=ax1) disp1.add_colorbar(cax=cax1, label='Photons') disp2 = CameraDisplay(geom, ax=ax2) disp2.add_colorbar(cax=cax2, label='ArrivalTime') ax1.set_title('Photons') ax2.set_title('Peak Position') for e in event_generator: dl1_calibrator.calibrate(e) image = e.dl1.tel[0].image[0] cleaning_mask = tailcuts_clean(geom, image, 5, 3.5) if sum(cleaning_mask) < 15: continue hillas_container = hillas_parameters( geom.pix_x[cleaning_mask], geom.pix_y[cleaning_mask], image[cleaning_mask], ) disp1.overlay_moments(hillas_container, linewidth=1.5, color='c', with_label=False) disp1.highlight_pixels(cleaning_mask) disp1.image = e.dl1.tel[0].image[0] disp2.image = e.dl1.tel[0].peakpos[0] for disp in (disp1, disp2): disp.highlight_pixels(cleaning_mask, color='r', linewidth=1.5) fig.suptitle('FACT Event {}'.format(e.trig.gps_time.iso)) plt.pause(0.01) input('Press enter for next event')
def main(): std_config = get_standard_config() log.setLevel(logging.INFO) handler = logging.StreamHandler() logging.getLogger().addHandler(handler) if args.config_file is not None: config = replace_config(std_config, read_configuration_file(args.config_file)) else: config = std_config log.info(f"Tailcut config used: {config['tailcut']}") foclen = OpticsDescription.from_name('LST').equivalent_focal_length cam_table = Table.read(args.input_file, path="instrument/telescope/camera/LSTCam") camera_geom = CameraGeometry.from_table(cam_table) dl1_container = DL1ParametersContainer() parameters_to_update = list(HillasParametersContainer().keys()) parameters_to_update.extend([ 'concentration_cog', 'concentration_core', 'concentration_pixel', 'leakage_intensity_width_1', 'leakage_intensity_width_2', 'leakage_pixels_width_1', 'leakage_pixels_width_2', 'n_islands', 'intercept', 'time_gradient', 'n_pixels', 'wl', 'log_intensity' ]) nodes_keys = get_dataset_keys(args.input_file) if args.noimage: nodes_keys.remove(dl1_images_lstcam_key) auto_merge_h5files([args.input_file], args.output_file, nodes_keys=nodes_keys) with tables.open_file(args.input_file, mode='r') as input: image_table = input.root[dl1_images_lstcam_key] dl1_params_input = input.root[dl1_params_lstcam_key].colnames disp_params = {'disp_dx', 'disp_dy', 'disp_norm', 'disp_angle', 'disp_sign'} if set(dl1_params_input).intersection(disp_params): parameters_to_update.extend(disp_params) with tables.open_file(args.output_file, mode='a') as output: params = output.root[dl1_params_lstcam_key].read() for ii, row in enumerate(image_table): dl1_container.reset() image = row['image'] peak_time = row['peak_time'] signal_pixels = tailcuts_clean(camera_geom, image, **config['tailcut']) n_pixels = np.count_nonzero(signal_pixels) if n_pixels > 0: num_islands, island_labels = number_of_islands(camera_geom, signal_pixels) n_pixels_on_island = np.bincount(island_labels.astype(np.int)) n_pixels_on_island[0] = 0 # first island is no-island and should not be considered max_island_label = np.argmax(n_pixels_on_island) signal_pixels[island_labels != max_island_label] = False hillas = hillas_parameters(camera_geom[signal_pixels], image[signal_pixels]) dl1_container.fill_hillas(hillas) dl1_container.set_timing_features(camera_geom[signal_pixels], image[signal_pixels], peak_time[signal_pixels], hillas) dl1_container.set_leakage(camera_geom, image, signal_pixels) dl1_container.set_concentration(camera_geom, image, hillas) dl1_container.n_islands = num_islands dl1_container.wl = dl1_container.width / dl1_container.length dl1_container.n_pixels = n_pixels width = np.rad2deg(np.arctan2(dl1_container.width, foclen)) length = np.rad2deg(np.arctan2(dl1_container.length, foclen)) dl1_container.width = width dl1_container.length = length dl1_container.log_intensity = np.log10(dl1_container.intensity) if set(dl1_params_input).intersection(disp_params): disp_dx, disp_dy, disp_norm, disp_angle, disp_sign = disp( dl1_container['x'].to_value(u.m), dl1_container['y'].to_value(u.m), params['src_x'][ii], params['src_y'][ii] ) dl1_container['disp_dx'] = disp_dx dl1_container['disp_dy'] = disp_dy dl1_container['disp_norm'] = disp_norm dl1_container['disp_angle'] = disp_angle dl1_container['disp_sign'] = disp_sign for p in parameters_to_update: params[ii][p] = u.Quantity(dl1_container[p]).value output.root[dl1_params_lstcam_key][:] = params
def test_FitGammaHillas(): ''' a test of the complete fit procedure on one event including: • tailcut cleaning • hillas parametrisation • GreatCircle creation • direction fit • position fit in the end, proper units in the output are asserted ''' filename = get_dataset("gamma_test.simtel.gz") fit = HillasReconstructor() cam_geom = {} tel_phi = {} tel_theta = {} source = hessio_event_source(filename) for event in source: hillas_dict = {} for tel_id in event.dl0.tels_with_data: if tel_id not in cam_geom: cam_geom[tel_id] = CameraGeometry.guess( event.inst.pixel_pos[tel_id][0], event.inst.pixel_pos[tel_id][1], event.inst.optical_foclen[tel_id]) tel_phi[tel_id] = event.mc.tel[tel_id].azimuth_raw * u.rad tel_theta[tel_id] = (np.pi / 2 - event.mc.tel[tel_id].altitude_raw) * u.rad pmt_signal = event.r0.tel[tel_id].adc_sums[0] mask = tailcuts_clean(cam_geom[tel_id], pmt_signal, picture_thresh=10., boundary_thresh=5.) pmt_signal[mask == 0] = 0 try: moments = hillas_parameters(event.inst.pixel_pos[tel_id][0], event.inst.pixel_pos[tel_id][1], pmt_signal) hillas_dict[tel_id] = moments except HillasParameterizationError as e: print(e) continue if len(hillas_dict) < 2: continue fit_result = fit.predict(hillas_dict, event.inst, tel_phi, tel_theta) print(fit_result) fit_result.alt.to(u.deg) fit_result.az.to(u.deg) fit_result.core_x.to(u.m) assert fit_result.is_valid return
def check_interleave_pedestal_cleaning(path_list, calib_time_file, calib_file, max_events=10000): signal_place_after_clean = np.zeros(1855) sum_ped_ev = 0 alive_ped_ev = 0 for path in path_list: print(path) r0_r1_calibrator = LSTR0Corrections(pedestal_path=None, r1_sample_start=3, r1_sample_end=39) r1_dl1_calibrator = LSTCameraCalibrator(calibration_path=calib_file, time_calibration_path=calib_time_file, extractor_product="LocalPeakWindowSum", config=charge_config, allowed_tels=[1]) reader = LSTEventSource(input_url=path, max_events=max_events) for i, ev in enumerate(reader): r0_r1_calibrator.calibrate(ev) if i%10000 == 0: print(ev.r0.event_id) if ev.lst.tel[1].evt.tib_masked_trigger == 32: sum_ped_ev += 1 r1_dl1_calibrator(ev) img = ev.dl1.tel[1].image geom = ev.inst.subarray.tel[1].camera clean = tailcuts_clean( geom, img, picture_thresh=6, boundary_thresh=3, min_number_picture_neighbors=1, keep_isolated_pixels=False ) cleaned = img.copy() cleaned[~clean] = 0.0 signal_place_after_clean[np.where(clean == True)] += 1 if np.sum(cleaned>0) > 0: alive_ped_ev += 1 fig, ax = plt.subplots(figsize=(8, 8)) geom = ev.inst.subarray.tel[1].camera disp0 = CameraDisplay(geom, ax=ax) disp0.image = signal_place_after_clean/sum_ped_ev disp0.add_colorbar(ax=ax, label="N times signal remain after cleaning") disp0.cmap = 'gnuplot2' ax.set_title("{} \n {}/{}".format(path.split("/")[-1][8:21], alive_ped_ev, sum_ped_ev)) print(path.split("/")[-1][8:21]) print("{}/{}".format(alive_ped_ev, sum_ped_ev)) ax.set_xlabel(" ") ax.set_ylabel(" ") plt.tight_layout() plt.show() return signal_place_after_clean, sum_ped_ev
def test_convert_geometry(): filename = get_path("gamma_test.simtel.gz") cam_geom = {} source = hessio_event_source(filename) # testing a few images just for the sake of being thorough counter = 5 for event in source: for tel_id in event.dl0.tels_with_data: if tel_id not in cam_geom: cam_geom[tel_id] = CameraGeometry.guess( event.inst.pixel_pos[tel_id][0], event.inst.pixel_pos[tel_id][1], event.inst.optical_foclen[tel_id]) # we want to test conversion of hex to rectangular pixel grid if cam_geom[tel_id].pix_type is not "hexagonal": continue print(tel_id, cam_geom[tel_id].pix_type) pmt_signal = apply_mc_calibration( #event.dl0.tel[tel_id].adc_samples[0], event.dl0.tel[tel_id].adc_sums[0], event.mc.tel[tel_id].dc_to_pe[0], event.mc.tel[tel_id].pedestal[0]) new_geom, new_signal = convert_geometry_1d_to_2d( cam_geom[tel_id], pmt_signal, cam_geom[tel_id].cam_id, add_rot=-2) unrot_geom, unrot_signal = convert_geometry_back( new_geom, new_signal, cam_geom[tel_id].cam_id, event.inst.optical_foclen[tel_id], add_rot=4) # if run as main, do some plotting if __name__ == "__main__": fig = plt.figure() plt.style.use('seaborn-talk') ax1 = fig.add_subplot(131) disp1 = CameraDisplay(cam_geom[tel_id], image=np.sum(pmt_signal, axis=1) if pmt_signal.shape[-1] == 25 else pmt_signal, ax=ax1) disp1.cmap = plt.cm.hot disp1.add_colorbar() plt.title("original geometry") ax2 = fig.add_subplot(132) disp2 = CameraDisplay(new_geom, image=np.sum(new_signal, axis=2) if new_signal.shape[-1] == 25 else new_signal, ax=ax2) disp2.cmap = plt.cm.hot disp2.add_colorbar() plt.title("slanted geometry") ax3 = fig.add_subplot(133) disp3 = CameraDisplay(unrot_geom, image=np.sum(unrot_signal, axis=1) if unrot_signal.shape[-1] == 25 else unrot_signal, ax=ax3) disp3.cmap = plt.cm.hot disp3.add_colorbar() plt.title("geometry converted back to hex") plt.show() # do some tailcuts cleaning mask1 = tailcuts_clean(cam_geom[tel_id], pmt_signal, 1, picture_thresh=10., boundary_thresh=5.) mask2 = tailcuts_clean(unrot_geom, unrot_signal, 1, picture_thresh=10., boundary_thresh=5.) pmt_signal[mask1==False] = 0 unrot_signal[mask2==False] = 0 ''' testing back and forth conversion on hillas parameters... ''' try: moments1 = hillas_parameters(cam_geom[tel_id].pix_x, cam_geom[tel_id].pix_y, pmt_signal) moments2 = hillas_parameters(unrot_geom.pix_x, unrot_geom.pix_y, unrot_signal) except (HillasParameterizationError, AssertionError) as e: ''' we don't want this test to fail because the hillas code threw an error ''' print(e) counter -= 1 if counter < 0: return else: continue ''' test if the hillas parameters from the original geometry and the forth-and-back rotated geometry are close ''' assert np.allclose( [moments1.length.value, moments1.width.value, moments1.phi.value], [moments2.length.value, moments2.width.value, moments2.phi.value], rtol=1e-2, atol=1e-2) counter -= 1 if counter < 0: return
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
""" fig, axs = plt.subplots(1, 1, figsize=(6, 3)) d1 = CameraDisplay(camera, ax=axs) axs.set_title('Cleaned_Image(fact_cleaning) ' + camera.cam_id) d1.image = cleaned1 plt.show() plt.close(fig) """ print("\n time spent in cycle(fact_cleaning)->", finish - start) start = datetime.datetime.now() clean2 = tailcuts_clean(camera, image, boundary_thresh=boundary, picture_thresh=picture, min_number_picture_neighbors=min_neighbors) finish = datetime.datetime.now() # display dl1 cleaned images cleaned2 = dl1.image.copy() cleaned2[~clean2] = 0.0 """ fig, axs = plt.subplots(1, 1, figsize=(6, 3)) d1 = CameraDisplay(camera, ax=axs) axs.set_title('Cleaned_Image(tailcuts_cleaning) ' + camera.cam_id) d1.image = cleaned2 plt.show()
def plot_muon_event(event, muonparams): if muonparams['MuonRingParams'] is not None: # Plot the muon event and overlay muon parameters fig = plt.figure(figsize=(16, 7)) colorbar = None colorbar2 = None subarray = event.inst.subarray # 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 rotr_angle = geom.pix_rotation # The following two lines have been commented out to avoid a rotation error. # if geom.cam_id == 'LSTCam' or geom.cam_id == 'NectarCam': # rotr_angle = 0. * u.deg # Convert to camera frame (centre & radius) altaz = HorizonFrame(alt=event.mc.alt, az=event.mc.az) ring_nominal = SkyCoord( delta_az=muonparams['MuonRingParams'][idx].ring_center_x, delta_alt=muonparams['MuonRingParams'][idx].ring_center_y, frame=NominalFrame(origin=altaz) ) flen = subarray.tel[tel_id].optics.equivalent_focal_length ring_camcoord = ring_nominal.transform_to(CameraFrame( pointing_direction=altaz, focal_length=flen, rotation=rotr_angle)) centroid = (ring_camcoord.x.value, ring_camcoord.y.value) radius = muonparams['MuonRingParams'][idx].ring_radius ringrad_camcoord = 2 * radius.to(u.rad) * flen # But not FC? px = subarray.tel[tel_id].camera.pix_x py = subarray.tel[tel_id].camera.pix_y camera_coord = SkyCoord( x=px, y=py, frame=CameraFrame( focal_length=flen, rotation=geom.pix_rotation, ) ) nom_coord = camera_coord.transform_to( NominalFrame(origin=altaz) ) px = nom_coord.delta_az.to(u.deg) py = nom_coord.delta_alt.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) pix_rmask = ring_dist < muonparams['MuonRingParams'][idx].ring_radius * 0.4 if muonparams['MuonIntensityParams'][idx] is not None: signals *= muonparams['MuonIntensityParams'][idx].mask elif muonparams['MuonIntensityParams'][idx] is None: signals *= pix_rmask 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: 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): logger.warning("Lengths do not match...len(pred)=%s len(" "mask)=", len(pred), np.sum(muonparams['MuonIntensityParams'][idx].mask)) # Numpy broadcasting - fill in the shape plotpred = np.zeros(image.shape) truelocs = np.where(muonparams['MuonIntensityParams'][idx].mask == True) plotpred[truelocs] = pred camera2 = plotter.draw_camera(tel_id, plotpred, ax2) if np.isnan(max(plotpred)) or np.isnan(min(plotpred)): logger.debug("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()