예제 #1
0
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)
예제 #2
0
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
예제 #3
0
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])
예제 #4
0
    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")
예제 #5
0
 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)
예제 #6
0
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())
예제 #7
0
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
예제 #8
0
def test_n_events():
    path = get_data("testing/simtel_test.simtel.gz")
    reader = SimtelReader(path, n_events=3)
    assert (len(list(reader)) == 3)
예제 #9
0
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):
예제 #10
0
 def lvr3_6mm_75um_uncoated(cls):
     path = get_data("datasheet/ov_lvr3_6mm_75um_uncoated.txt")
     return SiPMOvervoltage.from_csv(path)
예제 #11
0
 def lvr3_6mm_75um_silicon(cls):
     path = get_data("datasheet/ov_lvr3_6mm_75um_silicon.txt")
     return SiPMOvervoltage.from_csv(path)
예제 #12
0
 def lct5_6mm_75um_epoxy(cls):
     path = get_data("datasheet/ov_lct5_6mm_75um_epoxy.txt")
     return SiPMOvervoltage.from_csv(path)