def test_simtel_reader_pixel_remap(): path = get_data("testing/simtel_test.simtel.gz") reader = SimtelReader(path, disable_remapping=True, only_triggered_events=True) events_original = list(reader) pixel_x_original = reader.camera_settings[1]['pixel_x'] pixel_y_original = reader.camera_settings[1]['pixel_y'] reader = SimtelReader(path, disable_remapping=False, only_triggered_events=True) events_remapped = list(reader) mapping = SSTCameraMapping() pixel_x_remapped = mapping.pixel.x pixel_y_remapped = mapping.pixel.y for pe_original, pe_remapped in zip(events_original, events_remapped): image_original = pe_original.get_photoelectrons_per_pixel( reader.n_pixels) cog_x_original = np.average(pixel_x_original, weights=image_original) cog_y_original = np.average(pixel_y_original, weights=image_original) image_remapped = pe_remapped.get_photoelectrons_per_pixel( reader.n_pixels) cog_x_remapped = np.average(pixel_x_remapped, weights=image_remapped) cog_y_remapped = np.average(pixel_y_remapped, weights=image_remapped) np.testing.assert_allclose(cog_x_original, cog_x_remapped, rtol=1e-4) np.testing.assert_allclose(cog_y_original, cog_y_remapped, rtol=1e-4)
def test_simtelreader(): path = get_data("testing/simtel_test.simtel.gz") reader = SimtelReader(path, disable_remapping=False, only_triggered_events=False) photoelectrons = [] for pe in reader: photoelectrons.append(pe) assert len(photoelectrons) == 144 for pe in photoelectrons: assert len(pe.pixel) == len(pe.time) == len(pe.charge) assert len(photoelectrons[0].pixel) == 24 assert photoelectrons[0].time.min() == 30 assert (photoelectrons[0].charge == 1).all() reader = SimtelReader(path, disable_remapping=False, only_triggered_events=True) photoelectrons = [] for pe in reader: photoelectrons.append(pe) assert len(photoelectrons) == 15 assert len(photoelectrons[0].pixel) == 86
def test_comparison_to_ctapipe_true_image(): path = get_data("testing/simtel_test.simtel.gz") source = SimTelEventSource(input_url=path) ctapipe_images = {} for event in source: for telid, tel in event.mc.tel.items(): key = (event.index['event_id'], telid) ctapipe_images[key] = tel.true_image.astype(int) reader = SimtelReader(path, disable_remapping=True, only_triggered_events=True) photoelectron_images = {} for pe in reader: key = (pe.metadata['event_id'], pe.metadata['telescope_id']) photoelectron_images[key] = pe.get_photoelectrons_per_pixel( reader.n_pixels) assert ctapipe_images.keys() == photoelectron_images.keys() for key in ctapipe_images.keys(): assert np.array_equal(ctapipe_images[key], photoelectron_images[key]) # Check the triggered-events are still correct when reading the non-triggered reader = SimtelReader(path, disable_remapping=True, only_triggered_events=False) photoelectron_images = {} for pe in reader: key = (pe.metadata['event_id'], pe.metadata['telescope_id']) photoelectron_images[key] = pe.get_photoelectrons_per_pixel( reader.n_pixels) for key in ctapipe_images.keys(): assert np.array_equal(ctapipe_images[key], photoelectron_images[key])
def __init__( self, path=get_data("datasheet/efficiency/window_durham_needle.csv")): self.df = pd.read_csv(path) self.df = self.df.set_index("wavelength") / 100 # embed() self._df_no_interp = self.df.copy() self.df = self.df.interpolate(method='cubic', limit_area="inside") # self.df.iloc[:20, :] = self.df.iloc[:20, :].fillna(0) # self.df.iloc[-100:, :] = self.df.iloc[-100:, :].fillna(0.35) # self.df = self.df.interpolate(method="cubic") self.df = self.df.interpolate(limit_area="outside", limit_direction="both")
def LVR3_75um_6mm(cls): path = get_data("datasheet/efficiency/pde_LVR3_75um_6mm.csv") df = pd.read_csv(path, index_col="Wavelength") return cls(df=df)
def main(): # noinspection PyTypeChecker parser = argparse.ArgumentParser( description="Run a camera definition file through the sstcam-simulation " "chain to obtain events with a uniform lab illumination", formatter_class=argparse.ArgumentDefaultsHelpFormatter ) parser.add_argument( '-i', dest='input_path', help='Path to the camera definition file' ) # parser.add_argument( # '-n', dest='n_events', type=int, # help='Number of events to simulate per illumination' # ) parser.add_argument( '--nsb', default=40, type=float, dest='nsb_rate', help='NSB Rate to simulate (MHz)' ) parser.add_argument( '--trigger', default=600, type=float, dest='trigger_rate', help='Desired camera trigger rate' ) parser.add_argument( '--50peref', default=False, action='store_true', dest='use_50pe_ref', help='Use a 50pe reference pulse for the charge extraction' ) args = parser.parse_args() camera_definition_path = args.input_path # n_events = args.n_events nsb_rate = args.nsb_rate trigger_rate = args.trigger_rate use_50pe_ref = args.use_50pe_ref proton_path = get_data("cherenkov/proton.h5") if not exists(proton_path): msg = ''' Cherenkov files have not been downloaded to sstcam_simulation/data/cherenkov. The files can be downloaded from Nextcloud https://pcloud.mpi-hd.mpg.de/index.php/f/142621 ''' raise ValueError(msg) spe_path = camera_definition_path.replace(".pkl", "_spe.pdf") bias_scan_path = camera_definition_path.replace(".pkl", "_biasscan.pdf") events_path = camera_definition_path.replace(".pkl", "_events.h5") if use_50pe_ref: events_path = camera_definition_path.replace(".pkl", "_events_50peref.h5") print(f"Loading camera: {camera_definition_path}") camera = Camera.load(camera_definition_path) # Simulate just 1 superpixel camera.mapping.reinitialise(4) if use_50pe_ref: ref_x, ref_y = measure_50pe_pulse(camera) extractor = ChargeExtractor(ref_x, ref_y, camera.mapping) else: extractor = ChargeExtractor.from_camera(camera) # Determine calibration measured_opct, measured_opct_err = measure_opct(camera, spe_path) pedestal = obtain_pedestal(camera, extractor, nsb_rate) threshold = obtain_trigger_threshold(camera, nsb_rate, trigger_rate, bias_scan_path) camera.update_trigger_threshold(threshold) camera.coupling.update_nsb_rate(nsb_rate) generator = LabIlluminationGenerator(camera, extractor, pedestal, nsb_rate) with EventsWriter(events_path, generator.event_table_layout) as writer: writer.add_metadata( camera_definition_path=camera_definition_path, input_opct=camera.photoelectron_spectrum.opct, time_constant=camera.photoelectron_spectrum.time_constant, nsb_rate=nsb_rate, trigger_rate=trigger_rate, trigger_threshold=threshold, pedestal=pedestal, measured_opct=measured_opct, measured_opct_err=measured_opct_err, ) illuminations = np.geomspace(0.1, 1000, 100) for illumination in tqdm(illuminations): generator.set_illumination(illumination) n_events = 2000 if illumination <= 30 else 100 for _ in range(n_events): writer.append(generator.generate_event())
class TemplateNoise(ElectronicNoise): default_path = get_data("datasheet/noise_LMH6722_opamp.txt") def __init__(self, n_samples, sample_width, filepath=default_path, stddev=1, seed=None): """ Noise defined by a template such as that from a datasheet Parameters ---------- n_samples : int Number of samples in the readout/waveform sample_width : float Width of samples in the readout/waveform (ns) stddev : float Standard deviation of the noise Units: photoelectrons / ns seed : int or tuple Seed for the numpy random number generator. Ensures the reproducibility of an event if you know its seed """ self._n_samples = n_samples self._sample_width = sample_width self._filepath = filepath self._stddev = stddev self._seed = seed self._frequency, self._v_root = np.loadtxt(filepath, delimiter=',', unpack=True) # Find scaling for requested stddev n_samples_long = int(1e7) voltage = self.get_interpolated_voltage(n_samples_long, sample_width) frequency_spectrum = self.get_frequency_spectrum(voltage) noise = self.get_noise(frequency_spectrum, n_samples_long) self.scale = stddev / np.std(noise) def get_interpolated_voltage(self, n_samples, sample_width): df = np.fft.fftfreq(n_samples) / sample_width df_positive = df[:len(df) // 2] delta_df_positive = df_positive[1] - df_positive[0] f = interp1d(self._frequency, self._v_root) frequency_min = np.min(self._frequency) frequency_max = np.max(self._frequency) frequency_range = frequency_max - frequency_min frequency_interp = np.arange(frequency_min, frequency_max, frequency_range / n_samples) v_root_interp = f(frequency_interp) return v_root_interp * np.sqrt(delta_df_positive) def get_frequency_spectrum(self, voltage): rng = np.random.default_rng(seed=self._seed) phi = rng.uniform(0, 2 * np.pi, size=voltage.size) # Randomising phi from 0 to 2pi cplx = np.zeros(voltage.size, dtype=complex) i = np.arange(1, voltage.size // 2) cplx.real[i] = voltage[i] * np.cos(phi[i]) cplx.imag[i] = -voltage[i] * np.sin(phi[i]) cplx.real[-i] = voltage[i] * np.cos(phi[i]) cplx.imag[-i] = voltage[i] * np.sin(phi[i]) return cplx @staticmethod def get_noise(frequency_spectrum, n_samples): return np.fft.ifft( frequency_spectrum) * n_samples * 1e-9 # Convert to Volts @staticmethod def get_noise_envelope(noise, sample_len): """ Return back to the noise envelope from the simulated noise Parameters ---------- noise : ndarray Noise component of the waveform sample_len : int Number of samples in the readout Returns ------- ndarray """ spectrum = np.fft.fft(noise * 1e9 / sample_len) # Convert to nV and rescale for FFT return np.abs(spectrum) def add_to_readout(self, readout): voltage = self.get_interpolated_voltage(self._n_samples, self._sample_width) frequency_spectrum = self.get_frequency_spectrum(voltage) noise = self.get_noise(frequency_spectrum, self._n_samples) return readout + noise * self.scale
def test_n_events(): path = get_data("testing/simtel_test.simtel.gz") reader = SimtelReader(path, n_events=3) assert (len(list(reader)) == 3)
from sstcam_simulation.data import get_data from sstcam_simulation.utils.window_durham_needle import WindowDurhamNeedle from sstcam_simulation.utils.sipm.pde import PDEvsWavelength, read_lct5_resin_coated, read_prototype import numpy as np import pandas as pd from numba import njit from astropy import units as u import yaml NSB_FLUX_UNIT = 1 / (u.cm**2 * u.ns * u.sr) NSB_DIFF_FLUX_UNIT = NSB_FLUX_UNIT / u.nm PATH_ENV = get_data("datasheet/efficiency/environment.csv") PROD4_PATH_WINDOW = get_data("datasheet/efficiency/window_prod4.csv") PROD4_PATH_PDE = get_data("datasheet/efficiency/pde_prod4.csv") PROD4_PATH_TEL = get_data("datasheet/efficiency/telescope_prod4_astri.csv") PROD4_PATH_QUAN = get_data("datasheet/efficiency/quantities_prod4.yml") SSTCAM_PATH_QUAN = get_data("datasheet/efficiency/quantities_sstcam.yml") @njit(fastmath=True) def _pixel_active_solid_angle_nb(pixel_diameter, focal_length): pixel_area = pixel_diameter**2 equivalent_circular_area = pixel_diameter**2 / 4 * np.pi angle_pixel = pixel_diameter / focal_length area_ratio = pixel_area / equivalent_circular_area return 2 * np.pi * (1.0 - np.cos(0.5 * angle_pixel)) * area_ratio @njit(fastmath=True) def _integrate(x, y, x_min, x_max):
def lvr3_6mm_75um_uncoated(cls): path = get_data("datasheet/ov_lvr3_6mm_75um_uncoated.txt") return SiPMOvervoltage.from_csv(path)
def lvr3_6mm_75um_silicon(cls): path = get_data("datasheet/ov_lvr3_6mm_75um_silicon.txt") return SiPMOvervoltage.from_csv(path)
def lct5_6mm_75um_epoxy(cls): path = get_data("datasheet/ov_lct5_6mm_75um_epoxy.txt") return SiPMOvervoltage.from_csv(path)