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)
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)
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)
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
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
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}")
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))
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
def _get_fn(key): return key.data_type + '_' + strax.deterministic_hash(key.lineage)
def __repr__(self): return '_'.join([ self.run_id, self.data_type, strax.deterministic_hash(self.lineage) ])
def _key_dirname(key): return '_'.join( [key.run_id, key.data_type, strax.deterministic_hash(key.lineage)])