def test_readme_example(self): # This is same as C++ NEST with naming nc = nestpy.NESTcalc() A = 131.293 Z = 54. density = 2.9 # g/cm^3 interaction = nestpy.INTERACTION_TYPE(0) # NR E = 10 # keV print('For an %s keV %s' % (E, interaction)) # Get particle yields y = nc.GetYields( interaction, E, density, 124, # Drift field, V/cm A, Z, (1, 1)) print('The photon yield is:', y.PhotonYield) print('With statistical fluctuations', nc.GetQuanta(y, density).photons)
def setUpClass(cls): cls.detector = nestpy.VDetector() cls.detector.Initialization() cls.it = nestpy.INTERACTION_TYPE(0) cls.nestcalc = nestpy.NESTcalc(cls.detector) cls.nuisance = cls.nestcalc.default_NuisParam cls.free = cls.nestcalc.default_FreeParam
def setUpClass(cls): cls.detector = nestpy.VDetector() cls.detector.Initialization() cls.it = nestpy.INTERACTION_TYPE(0) cls.nestcalc = nestpy.NESTcalc(cls.detector) cls.result = cls.nestcalc.FullCalculation( cls.it, 10., 3., 100., 131, 56, [11., 1.1, 0.0480, -0.0533, 12.6, 0.3, 2., 0.3, 2., 0.5, 1.], [1., 1., 0.1, 0.5, 0.07], True)
def kr83m_instructions(c): """ Instruction that is meant to simulate Kr83m events in the TPC. """ import nestpy half_life = 156.94e-9 # Kr intermediate state half-life in ns decay_energies = [32.2, 9.4] # Decay energies in KeV n = c['nevents'] = c['event_rate'] * c['chunk_size'] * c['nchunk'] c['total_time'] = c['chunk_size'] * c['nchunk'] # Uses 4*n to get the two energies for S1 and S2 instructions = np.zeros(4 * n, dtype=wfsim.instruction_dtype) instructions['event_number'] = np.digitize( instructions['time'], 1e9 * np.arange(c['nchunk']) * c['chunk_size']) - 1 instructions['type'] = np.tile([1, 2], 2 * n) instructions['recoil'] = ['er' for i in range(4 * n)] # Random positioning r = np.sqrt(np.random.uniform(0, c['tpc_radius']**2, n)) t = np.random.uniform(-np.pi, np.pi, n) instructions['x'] = np.repeat(r * np.cos(t), 4) instructions['y'] = np.repeat(r * np.sin(t), 4) instructions['z'] = -10 * np.ones(4 * n) # Choosing shallow z positioning # For correct times need to include the 156.94 ns half life of the intermediate state uniform_times = c['total_time'] * (np.arange(n) + 0.5) / n delayed_times = uniform_times + np.random.exponential( half_life / np.log(2), len(uniform_times)) instructions['time'] = np.repeat(list(zip(uniform_times, delayed_times)), 2) * 1e9 # Defining XENON-like detector nc = nestpy.NESTcalc(nestpy.VDetector()) A = 131.293 Z = 54 density = 2.862 # g/cm^3 # SR1 Value drift_field = 82 # V/cm # SR1 Value interaction = nestpy.INTERACTION_TYPE(7) # gamma energy = np.tile(decay_energies, n) quanta = [] for en in energy: y = nc.GetYields(interaction, en, density, drift_field, A, Z, (1, 1)) quanta.append(nc.GetQuanta(y, density).photons) quanta.append(nc.GetQuanta(y, density).electrons) instructions['amp'] = quanta return instructions
def setUpClass(cls): cls.detector = nestpy.VDetector() cls.detector.Initialization() cls.it = nestpy.INTERACTION_TYPE(0) cls.nestcalc = nestpy.NESTcalc(cls.detector) cls.nuisance = cls.nestcalc.default_NuisParam cls.free = cls.nestcalc.default_FreeParam cls.result = cls.nestcalc.FullCalculation(cls.it, 10., 3., 100., 131, 56, cls.nuisance, cls.free, True) cls.position = [2, 3, 4]
def test_readme_example(self): import nestpy # This is same as C++ NEST with naming nc = nestpy.NESTcalc(nestpy.VDetector()) interaction = nestpy.INTERACTION_TYPE(0) # NR E = 10 # keV print('For an %s keV %s' % (E, interaction)) # Get particle yields y = nc.GetYields(interaction, E) print('The photon yield is:', y.PhotonYield) print('With statistical fluctuations', nc.GetQuanta(y).photons)
def GetInteractionObject(name): name = name.lower() if name == 'er': raise ValueError("For 'er', specify either 'gammaray' or 'beta'") nest_interaction_number = dict( nr=0, wimp=1, b8=2, dd=3, ambe=4, cf=5, ion=6, gammaray=7, beta=8, ch3t=9, c14=10, kr83m=11, nonetype=12, ) interaction_object = nestpy.INTERACTION_TYPE(nest_interaction_number[name]) return interaction_object
def read_g4(file): nc = nestpy.NESTcalc(nestpy.VDetector()) A = 131.293 Z = 54. density = 2.9 # g/cm^3 drift_field = 124 # V/cm interaction = nestpy.INTERACTION_TYPE(7) data = uproot.open(file) all_ttrees = dict( data.allitems( filterclass=lambda cls: issubclass(cls, uproot.tree.TTreeMethods))) e = all_ttrees[b'events/events;1'] ins = np.zeros(2 * len(e), dtype=instruction_dtype) sort_key = np.argsort(e.array('time')[:, 0]) e_dep, ins['x'], ins['y'], ins['z'], ins['t'] = e.array('etot')[sort_key], \ np.repeat(e.array('xp_pri')[sort_key], 2) / 10, \ np.repeat(e.array('yp_pri')[sort_key], 2) / 10, \ np.repeat(e.array('zp_pri')[sort_key], 2) / 10, \ 1e9*np.repeat(e.array('time')[:, 0][sort_key], 2) ins['event_number'] = 0 ins['type'] = np.tile(('s1', 's2'), len(e)) ins['recoil'] = np.repeat('er', 2 * len(e)) quanta = [] for en in e_dep: y = nc.GetYields(interaction, en, density, drift_field, A, Z, (1, 1)) quanta.append(nc.GetQuanta(y, density).photons) quanta.append(nc.GetQuanta(y, density).electrons) ins['amp'] = quanta return ins
def test_nestcalc_get_yields_defaults(self): yields = self.nestcalc.GetYields(nestpy.INTERACTION_TYPE(0), 10)
def photon_timings(t, n_photon_hits, recoil_type, config, phase, channels=None, positions=None, e_dep=None, n_photons_emitted=None, n_excitons=None, local_field=None, resource=None, nestpy_calc=None): """Calculate distribution of photon arrival timnigs :param t: 1d array of ints :param n_photon_hits: number of photon hits, 1d array of ints :param recoil_type: 1d array of ints :param config: dict wfsim config :param phase: str "liquid" :param channels: list of photon hit channels :param positions: nx3 array of true XYZ positions from instruction :param e_dep: energy of the deposit, 1d float array :param n_photons_emitted: number of orignally emitted photons/quanta, 1d int array :param n_excitons: number of exctions in deposit, 1d int array :param local_field: local field in the point of the deposit, 1d array of floats :param resource: pointer to resources class of wfsim that contains s1 timing splines returns photon timing array""" _photon_timings = np.repeat(t, n_photon_hits) _n_hits_total = len(_photon_timings) if len(_photon_timings) == 0: return _photon_timings.astype(np.int64) if 'optical_propagation' in config['s1_model_type']: z_positions = np.repeat(positions[:, 2], n_photon_hits) _photon_timings += S1.optical_propagation( channels, z_positions, config, spline=resource.s1_optical_propagation_spline).astype(np.int64) if 'simple' in config['s1_model_type']: # Simple S1 model enabled: use it for ER and NR. _photon_timings += np.random.exponential( config['s1_decay_time'], _n_hits_total).astype(np.int64) _photon_timings += np.random.normal(0, config['s1_decay_spread'], _n_hits_total).astype(np.int64) if 'nest' in config['s1_model_type'] or 'custom' in config[ 's1_model_type']: # Pulse model depends on recoil type counts_start = 0 for i, counts in enumerate(n_photon_hits): if 'custom' in config['s1_model_type']: for k in vars(NestId): if k.startswith('_'): continue if recoil_type[i] in getattr(NestId, k): str_recoil_type = k try: _photon_timings[counts_start: counts_start + counts] += \ getattr(S1, str_recoil_type.lower())( size=counts, config=config, phase=phase).astype(np.int64) except AttributeError: raise AttributeError( f"Recoil type must be ER, NR, alpha or LED, " f"not {recoil_type}. Check nest ids") if 'nest' in config['s1_model_type']: # Allow overwriting with "override_s1_photon_time_field" # xenon:j_angevaare:wfsim_photon_timing_bug _local_field = config.get('override_s1_photon_time_field', local_field[i]) _local_field = (_local_field if _local_field > 0 else local_field[i]) scint_time = nestpy_calc.GetPhotonTimes( nestpy.INTERACTION_TYPE(recoil_type[i]), n_photons_emitted[i], n_excitons[i], _local_field, e_dep[i]) scint_time = np.clip( scint_time, 0, config.get('maximum_recombination_time', 10000)) # The first part of the scint_time is from exciton only, see # https://github.com/NESTCollaboration/nestpy/blob/fe3d5d7da5d9b33ac56fbea519e02ef55152bc1d/src/nestpy/NEST.cpp#L164-L179 _photon_timings[counts_start: counts_start + counts] += \ np.random.choice(scint_time, counts, replace=False).astype(np.int64) counts_start += counts return _photon_timings
def test_intteraction_type_constructor(self): it = nestpy.INTERACTION_TYPE(0) assert it is not None assert str(it) != "" assert isinstance(it, nestpy.INTERACTION_TYPE)
def GetFlowImage(pid, eDep, field, savefig=True, output_dir='./', output_filename='test.png'): if not os.path.exists(output_dir): os.makedirs(output_dir) fig, ax = plt.subplots(1, 1, figsize=(XC.fig_width, XC.fig_height)) ax.set_xlim([0, XC.axes_xmax]) ax.set_ylim([XC.axes_ymin, 0]) SetColors(pid) if pid == 'NR': interaction = nestpy.INTERACTION_TYPE(0) elif pid == 'gamma': interaction = nestpy.INTERACTION_TYPE(7) elif pid == 'beta' or pid == 'ER': interaction = nestpy.INTERACTION_TYPE(8) yields = nestpy.NESTcalc(nestpy.VDetector()).GetYields(interaction, energy=eDep, density=DC.Density, drift_field=field) Ni = (yields.PhotonYield + yields.ElectronYield) / yields.ExcitonRatio / ( 1. + 1. / yields.ExcitonRatio) Nex = yields.PhotonYield + yields.ElectronYield - Ni Nph = yields.PhotonYield Ne = yields.ElectronYield L = yields.Lindhard if (pid == 'ER' or pid == 'beta' or pid == 'gamma'): # An estimate of how much energy goes into heat for ERs L = 1. - XC.ER_heat_fraction SingTripRatio = NESThelper.GetSingTripRatio(pid, eDep, field) SetText(pid, eDep, field, Ni, Nex, Nph, Ne, SingTripRatio) SetArrowWidths(pid, Ni, Nex, Nph, Ne, L, SingTripRatio) for a in range(len(XC.arrow_properties['name'])): DrawArrow(fig, ax, a) for t in range(len(XC.text_properties['name'])): DrawText(fig, ax, t) DrawAtom(fig, ax, pid) ax.set_xlim([0, XC.axes_xmax]) ax.set_ylim([XC.axes_ymin, 0]) ax.axis('off') fig.tight_layout() if savefig: fig.savefig(output_dir + output_filename, transparent=False) im_out = Image.open(output_dir + output_filename) else: buf = io.BytesIO() fig.savefig(buf, format='png') buf.seek(0) im_out = Image.open(buf) plt.close('all') return im_out
def test_nestcalc_get_yields_named(self): yields = self.nestcalc.GetYields(nestpy.INTERACTION_TYPE(0), energy=10)
def setUpClass(cls): cls.detector = nestpy.VDetector() cls.detector.Initialization() cls.it = nestpy.INTERACTION_TYPE(0) cls.nestcalc = nestpy.NESTcalc(cls.detector)
def quanta_from_NEST(en, model, e_field, A, Z, create_s2, **kwargs): """ Function which uses NEST to yield photons and electrons for a given set of parameters. Note: In case the energy deposit is outside of the range of NEST a -1 is returned. Args: en (numpy.array): Energy deposit of the interaction [keV] model (numpy.array): Nest Id for qunata generation (integers) e_field (numpy.array): Field value in the interaction site [V/cm] A (numpy.array): Atomic mass number Z (numpy.array): Atomic number create_s2 (bool): Specifies if S2 can be produced by interaction, in this case electrons are generated. kwargs: Additional keyword arguments which can be taken by GetYields e.g. density. Returns: photons (numpy.array): Number of generated photons electrons (numpy.array): Number of generated electrons excitons (numpy.array): Number of generated excitons """ nc = nestpy.NESTcalc(nestpy.VDetector()) density = 2.862 # g/cm^3 # Fix for Kr83m events. # Energies have to be very close to 32.1 keV or 9.4 keV # See: https://github.com/NESTCollaboration/nest/blob/master/src/NEST.cpp#L567 # and: https://github.com/NESTCollaboration/nest/blob/master/src/NEST.cpp#L585 max_allowed_energy_difference = 1 # keV if model == 11: if abs(en - 32.1) < max_allowed_energy_difference: en = 32.1 if abs(en - 9.4) < max_allowed_energy_difference: en = 9.4 # Some addition taken from # https://github.com/NESTCollaboration/nestpy/blob/e82c71f864d7362fee87989ed642cd875845ae3e/src/nestpy/helpers.py#L94-L100 if model == 0 and en > 2e2: warnings.warn( f"Energy deposition of {en} keV beyond NEST validity for NR model of 200 keV - Remove Interaction" ) return -1, -1, -1 if model == 7 and en > 3e3: warnings.warn( f"Energy deposition of {en} keV beyond NEST validity for gamma model of 3 MeV - Remove Interaction" ) return -1, -1, -1 if model == 8 and en > 3e3: warnings.warn( f"Energy deposition of {en} keV beyond NEST validity for beta model of 3 MeV - Remove Interaction" ) return -1, -1, -1 y = nc.GetYields(interaction=nestpy.INTERACTION_TYPE(model), energy=en, drift_field=e_field, A=A, Z=Z, **kwargs) event_quanta = nc.GetQuanta( y) # Density argument is not use in function... photons = event_quanta.photons excitons = event_quanta.excitons electrons = 0 if create_s2: electrons = event_quanta.electrons return photons, electrons, excitons
def _rand_instructions( event_rate: int, chunk_size: int, n_chunk: int, drift_field: float, energy_range: ty.Union[tuple, list, np.ndarray], tpc_length: float = straxen.tpc_z, tpc_radius: float = straxen.tpc_r, nest_inst_types: ty.Union[ty.List[int], ty.Tuple[ty.List], np.ndarray, None] = None, ) -> np.ndarray: import nestpy if nest_inst_types is None: nest_inst_types = [7] n_events = event_rate * chunk_size * n_chunk total_time = chunk_size * n_chunk inst = np.zeros(2 * n_events, dtype=instruction_dtype) inst[:] = -1 uniform_times = total_time * (np.arange(n_events) + 0.5) / n_events inst['time'] = np.repeat(uniform_times, 2) * int(1e9) inst['event_number'] = np.digitize( inst['time'], 1e9 * np.arange(n_chunk) * chunk_size) - 1 inst['type'] = np.tile([1, 2], n_events) r = np.sqrt(np.random.uniform(0, tpc_radius**2, n_events)) t = np.random.uniform(-np.pi, np.pi, n_events) inst['x'] = np.repeat(r * np.cos(t), 2) inst['y'] = np.repeat(r * np.sin(t), 2) inst['z'] = np.repeat(np.random.uniform(-tpc_length, 0, n_events), 2) # Here we'll define our XENON-like detector nest_calc = nestpy.NESTcalc(nestpy.VDetector()) nucleus_A = 131.293 nucleus_Z = 54. lxe_density = 2.862 # g/cm^3 #SR1 Value energy = np.random.uniform(*energy_range, n_events) quanta = [] exciton = [] recoil = [] e_dep = [] for energy_deposit in tqdm(energy, desc='generating instructions from nest'): interaction_type = np.random.choice(nest_inst_types) interaction = nestpy.INTERACTION_TYPE(interaction_type) y = nest_calc.GetYields( interaction, energy_deposit, lxe_density, drift_field, nucleus_A, nucleus_Z, ) q = nest_calc.GetQuanta(y, lxe_density) quanta.append(q.photons) quanta.append(q.electrons) exciton.append(q.excitons) exciton.append(0) # both S1 and S2 recoil += [interaction_type, interaction_type] e_dep += [energy_deposit, energy_deposit] inst['amp'] = quanta inst['local_field'] = drift_field inst['n_excitons'] = exciton inst['recoil'] = recoil inst['e_dep'] = e_dep for field in inst.dtype.names: if np.any(inst[field] == -1): log.warn(f'{field} is not (fully) filled') return inst
import nestpy import numpy as np import scipy as sp with open('mock.txt') as f: events = f.read().splitlines() events = np.asarray([float(num) for num in events]) print(events.shape) detector = nestpy.DetectorExample_XENON10() detector.Initialization() # nc = nestpy.testNEST(detector, 10, 'NR', 100., 120., 10., "0., 0., 0.", "120.", -1., 1, True, 1.0) # print(nc[0]) nc = nestpy.NESTcalc(detector) interaction = nestpy.INTERACTION_TYPE(0) yields = np.empty((events.shape[0], 2)) g1 = detector.get_g1() g2 = nc.CalculateG2(False) electron = np.random.choice(events, events.shape, replace=True) for i in range(events.shape[0]): y = nc.GetYields(interaction, events[i]) x = nc.GetQuanta(y) s1 = x.photons * g1 s2 = x.electrons * g2[3] yields[i, 0] = s1 yields[i, 1] = s2 np.save('s1_s2_data', yields)