Beispiel #1
0
 def lineage_hash(self):
     """Deterministic hash of the lineage"""
     # We cache the hash computation to benefit tight loops calling
     # this property
     if self._lineage_hash == '':
         self._lineage_hash = strax.deterministic_hash(self.lineage)
     return strax.deterministic_hash(self.lineage)
Beispiel #2
0
    def init_spe_scaling_factor_distributions(self):
        h = deterministic_hash(self.config)
        if h in _cached_uniform_to_pe_arr:
            self.__uniform_to_pe_arr = _cached_uniform_to_pe_arr[h]
            return

        # Extract the spe pdf from a csv file into a pandas dataframe
        spe_shapes = self.resource.photon_area_distribution

        # Create a converter array from uniform random numbers to SPE gains (one interpolator per channel)
        # Scale the distributions so that they have an SPE mean of 1 and then calculate the cdf
        uniform_to_pe_arr = []
        for ch in spe_shapes.columns[1:]:  # skip the first element which is the 'charge' header
            if spe_shapes[ch].sum() > 0:
                # mean_spe = (spe_shapes['charge'].values * spe_shapes[ch]).sum() / spe_shapes[ch].sum()
                scaled_bins = spe_shapes['charge'].values  # / mean_spe
                cdf = np.cumsum(spe_shapes[ch]) / np.sum(spe_shapes[ch])
            else:
                # if sum is 0, just make some dummy axes to pass to interpolator
                cdf = np.linspace(0, 1, 10)
                scaled_bins = np.zeros_like(cdf)

            grid_cdf = np.linspace(0, 1, 2001)
            grid_scale = interp1d(cdf, scaled_bins,
                                  kind='next',
                                  bounds_error=False,
                                  fill_value=(scaled_bins[0], scaled_bins[-1]))(grid_cdf)

            uniform_to_pe_arr.append(grid_scale)

        if len(uniform_to_pe_arr):
            self.__uniform_to_pe_arr = np.stack(uniform_to_pe_arr)
            _cached_uniform_to_pe_arr[h] = self.__uniform_to_pe_arr

        log.debug('Spe scaling factors created, cached with key %s' % h)
Beispiel #3
0
def _read_optical_nveto(config, events, mask):
    """Helper function for nveto to read photon channels and timings from G4 and apply QE's
    :params config: dict, wfsim configuration
    :params events: g4 root file
    :params mask: 1d bool array to select events
    
    returns two flatterned nested arrays of channels and timings, 
    """
    channels = np.hstack(events["pmthitID"].array(library="np")[mask])
    timings = np.hstack(events["pmthitTime"].array(library="np")[mask] *
                        1e9).astype(np.int64)

    constant_hc = 1239.841984  # (eV*nm) to calculate (wavelength lambda) = h * c / energy
    wavelengths = np.hstack(constant_hc /
                            events["pmthitEnergy"].array(library="np")[mask])

    # Caching a 2d array of interpolated value of (channel, wavelength [every nm])
    h = strax.deterministic_hash(config)
    nveto_channels = np.arange(config['channel_map']['nveto'][0],
                               config['channel_map']['nveto'][1] + 1)
    if h not in _cached_wavelength_to_qe_arr:
        resource = wfsim.load_config(config)
        if getattr(resource, 'nv_pmt_qe', None) is None:
            log.warning('nv pmt qe data not specified all qe default to 100 %')
            _cached_wavelength_to_qe_arr[h] = np.ones(
                [len(nveto_channels), 1000]) * 100
        else:
            qe_data = resource.nv_pmt_qe
            wavelength_to_qe_arr = np.zeros([len(nveto_channels), 1000])
            for ich, channel in enumerate(nveto_channels):
                wavelength_to_qe_arr[ich] = interp1d(
                    qe_data['nv_pmt_qe_wavelength'],
                    qe_data['nv_pmt_qe'][str(channel)],
                    bounds_error=False,
                    kind='linear',
                    fill_value=(0, 0))(np.arange(1000))
            _cached_wavelength_to_qe_arr[h] = wavelength_to_qe_arr

    # retrieving qe by index
    hit_mask = (channels >= nveto_channels[0]) & (channels <=
                                                  nveto_channels[-1])
    channels[~hit_mask] = nveto_channels[0]

    wavelengths[(wavelengths < 0) | (wavelengths >= 999)] = 0
    qes = _cached_wavelength_to_qe_arr[h][
        channels - nveto_channels[0],
        np.around(wavelengths).astype(np.int64)]
    hit_mask &= np.random.rand(
        len(qes)) <= qes * config.get('nv_pmt_ce_factor', 1.0) / 100

    amplitudes, og_offset = [], 0
    for tmp in events["pmthitID"].array(library="np")[mask]:
        og_length = len(tmp)
        amplitudes.append(hit_mask[og_offset:og_offset + og_length].sum())
        og_offset += og_length

    return channels[hit_mask], timings[hit_mask], np.array(amplitudes, int)
Beispiel #4
0
def load_config(config):
    """Create a Resource instance from the configuration

    Uses a cache to avoid re-creating instances from the same config
    """
    h = strax.deterministic_hash(config)
    if h in _cached_configs:
        return _cached_configs[h]
    result = Resource(config)
    _cached_configs[h] = result
    return result
Beispiel #5
0
def load_config(config):
    """Create a Resource instance from the configuration

    Uses a cache to avoid re-creating instances from the same config
    """
    h = strax.deterministic_hash(Resource.config_to_file(config))
    if h in _cached_configs:
        return _cached_configs[h]
    result = Resource(config)
    _cached_configs[h] = result
    log.debug(f'Caching config file set {h}')
    return result
Beispiel #6
0
    def get_default(self, run_id, run_defaults: dict = None):
        """Return default value for the option"""
        if run_defaults is not None and self.name in run_defaults:
            return run_defaults[self.name]
        if self.default is not OMITTED:
            return self.default
        if self.default_factory is not OMITTED:
            return self.default_factory()

        if self.default_by_run is not OMITTED:
            # TODO: This legacy code for handling default_per_run will soon
            # be removed!
            if run_id is None:
                run_id = 0  # TODO: think if this makes sense

            if isinstance(run_id, str):
                is_superrun = run_id.startswith('_')
                if not is_superrun:
                    run_id = int(run_id.replace('_', ''))
            else:
                is_superrun = False

            if callable(self.default_by_run):
                raise RuntimeError(
                    "Using functions to specify per-run defaults is no longer"
                    "supported: specify a (first_run, option) list, or "
                    "a URL of a file to process in the plugin")

            if is_superrun:
                return '<SUBRUN-DEPENDENT:%s>' % strax.deterministic_hash(
                    self.default_by_run)

            use_value = OMITTED
            for i, (start_run, value) in enumerate(self.default_by_run):
                if start_run > run_id:
                    break
                use_value = value
            if use_value is OMITTED:
                raise ValueError(
                    f"Run id {run_id} is smaller than the "
                    "lowest run id {start_run} for which the default "
                    "of the option {self.name} is known.")
            return use_value

        raise InvalidConfiguration(f"Missing option {self.name} "
                                   f"required by {self.taken_by}")
Beispiel #7
0
    def init_pmt_current_templates(self):
        """
        Create spe templates, for 10ns sample duration and 1ns rounding we have:
        _pmt_current_templates[i] : photon timing fall between [10*m+i, 10*m+i+1)
        (i, m are integers)
        """
        h = deterministic_hash(self.config)
        if h in _cached_pmt_current_templates:
            self._pmt_current_templates = _cached_pmt_current_templates[h]
            return

        # Interpolate on cdf ensures that each spe pulse would sum up to 1 pe*sample duration^-1
        pe_pulse_function = interp1d(
            self.config.get('pe_pulse_ts'),
            np.cumsum(self.config.get('pe_pulse_ys')),
            bounds_error=False, fill_value=(0, 1))

        # Samples are always multiples of sample_duration
        sample_duration = self.config.get('sample_duration', 10)
        samples_before = self.config.get('samples_before_pulse_center', 2)
        samples_after = self.config.get('samples_after_pulse_center', 20)
        pmt_pulse_time_rounding = self.config.get('pmt_pulse_time_rounding', 1.0)

        # Let's fix this, so everything can be turned into int
        assert pmt_pulse_time_rounding == 1

        samples = np.linspace(-samples_before * sample_duration,
                              + samples_after * sample_duration,
                              1 + samples_before + samples_after)
        self._template_length = np.int64(len(samples) - 1)

        templates = []
        for r in np.arange(0, sample_duration, pmt_pulse_time_rounding):
            pmt_current = np.diff(pe_pulse_function(samples - r)) / sample_duration  # pe / 10 ns
            # Normalize here to counter tiny rounding error from interpolation
            pmt_current *= (1 / sample_duration) / np.sum(pmt_current)  # pe / 10 ns
            templates.append(pmt_current)
        self._pmt_current_templates = np.array(templates)
        _cached_pmt_current_templates[h] = self._pmt_current_templates

        log.debug('Spe waveform templates created with %s ns resolution, cached with key %s'
                  % (pmt_pulse_time_rounding, h))
Beispiel #8
0
    def fetch(self, plugin):
        """override the Config.fetch method
        this is called when the attribute is accessed
        """
        # first fetch the user-set value
        # from the config dictionary
        url = super().fetch(plugin)

        if not isinstance(url, str):
            # if the value is not a string it is evaluated
            # as a literal config and returned as is.
            return url

        if self.SCHEME_SEP not in url:
            # no protocol in the url so its evaluated
            # as string-literal config and returned as is
            return url

        # separate out the query part of the URL which
        # will become the method kwargs
        url, url_kwargs = self.split_url_kwargs(url)

        kwargs = {
            k: self.fetch_attribute(plugin, v)
            for k, v in url_kwargs.items()
        }

        # construct a deterministic hash key
        key = strax.deterministic_hash((url, kwargs))

        # fetch from cache if exists
        value = self.cache.get(key, None)

        # not in cache, lets fetch it
        if value is None:
            value = self.dispatch(url, **kwargs)
            self.cache[key] = value

        return value
Beispiel #9
0
 def _get_fn(key):
     return key.data_type + '_' + strax.deterministic_hash(key.lineage)
Beispiel #10
0
 def __repr__(self):
     return '_'.join([
         self.run_id, self.data_type,
         strax.deterministic_hash(self.lineage)
     ])
Beispiel #11
0
 def _key_dirname(key):
     return '_'.join(
         [key.run_id, key.data_type,
          strax.deterministic_hash(key.lineage)])