From b1acfc01d9b242cb6a6306b5fce094aba39f5502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henning=20Schulze=20Ei=C3=9Fing?= Date: Tue, 23 Jan 2024 04:14:51 -0600 Subject: [PATCH 01/29] Add Option to read and ouput NV informations --- fuse/plugins/micro_physics/input.py | 364 +++++++++++++++++++++++----- 1 file changed, 310 insertions(+), 54 deletions(-) diff --git a/fuse/plugins/micro_physics/input.py b/fuse/plugins/micro_physics/input.py index 7b76854d..69e77cdb 100644 --- a/fuse/plugins/micro_physics/input.py +++ b/fuse/plugins/micro_physics/input.py @@ -19,10 +19,10 @@ @export class ChunkInput(strax.Plugin): - __version__ = "0.1.2" + __version__ = "0.2.0" depends_on = tuple() - provides = "geant4_interactions" + provides = ("geant4_interactions", "nv_pmthits") #Forbid rechunking rechunk_on_save = False @@ -31,7 +31,7 @@ class ChunkInput(strax.Plugin): input_timeout = FUSE_PLUGIN_TIMEOUT - dtype = [('x', np.float64), + dtype_tpc = [('x', np.float64), ('y', np.float64), ('z', np.float64), ('t', np.float64), @@ -48,7 +48,24 @@ class ChunkInput(strax.Plugin): ('z_pri', np.float32), ] - dtype = dtype + strax.time_fields + dtype_tpc = dtype_tpc + strax.time_fields + + + dtype_nv = [('pmthitEnergy', np.float32), + ('pmthitTime', np.float32), + ('pmthitID', np.int16), + ('evtid', np.int32), + ] + dtype_nv = dtype_nv + strax.time_fields + + dtype = dict() + dtype["geant4_interactions"] = dtype_tpc + dtype["nv_pmthits"] = dtype_nv + + data_kind = {"geant4_interactions": "geant4_interactions", + "nv_pmthits" : "nv_pmthits" + } + save_when = strax.SaveWhen.TARGET @@ -87,9 +104,9 @@ class ChunkInput(strax.Plugin): help='All interactions happening after this time (including the event time) will be cut.', ) - n_interactions_per_chunk = straxen.URLConfig( - default=1e5, type=(int, float), - help='Minimum number of interaction per chunk', + file_size_limit = straxen.URLConfig( + default=500, type=(int, float), + help='Target file size limit in MB', ) entry_start = straxen.URLConfig( @@ -112,6 +129,11 @@ class ChunkInput(strax.Plugin): help='Filter only nuclear recoil events (maximum ER energy deposit 10 keV)', ) + nv_output = straxen.URLConfig( + default=True, type=bool, + help='Decide if you want to have NV output or not', + ) + deterministic_seed = straxen.URLConfig( default=True, type=bool, help='Set the random seed from lineage and run_id, or pull the seed from the OS.', @@ -140,29 +162,41 @@ def setup(self): separation_scale = self.separation_scale, event_rate = self.source_rate, cut_delayed = self.cut_delayed, - n_interactions_per_chunk = self.n_interactions_per_chunk, + file_size_limit = self.file_size_limit, arg_debug = self.debug, outer_cylinder=None, #This is not running kwargs={'entry_start': self.entry_start, 'entry_stop': self.entry_stop}, cut_by_eventid=self.cut_by_eventid, cut_nr_only=self.nr_only, + neutron_veto_output=self.nv_output, ) self.file_reader_iterator = self.file_reader.output_chunk() def compute(self): try: - chunk_data, chunk_left, chunk_right, source_done = next(self.file_reader_iterator) - chunk_data["endtime"] = chunk_data["time"] + + if self.nv_output: + + chunk_data_tpc, chunk_left, chunk_right, source_done, chunk_data_nv = next(self.file_reader_iterator) + chunk_data_tpc["endtime"] = chunk_data_tpc["time"] + chunk_data_nv["endtime"] = chunk_data_nv["time"] + + else: + chunk_data_tpc, chunk_left, chunk_right, source_done = next(self.file_reader_iterator) + chunk_data_tpc["endtime"] = chunk_data_tpc["time"] + + chunk_data_nv = np.zeros(0, dtype=self.dtype["nv_pmthits"]) self.source_done = source_done - return self.chunk(start=chunk_left, - end=chunk_right, - data=chunk_data, - data_type='geant4_interactions' - ) + result = {"geant4_interactions" : self.chunk(start=chunk_left, end=chunk_right, data=chunk_data_tpc, data_type = "geant4_interactions"), + "nv_pmthits" : self.chunk(start=chunk_left, end=chunk_right, data=chunk_data_nv, data_type = "nv_pmthits") + } + + + return result except StopIteration: raise RuntimeError("Bug in chunk building!") @@ -193,7 +227,7 @@ def __init__(self, random_number_generator, separation_scale = 1e8, event_rate = 1, - n_interactions_per_chunk = 500, + file_size_limit = 500, cut_delayed = 4e12, last_chunk_length = 1e8, first_chunk_left = 1e6, @@ -203,6 +237,7 @@ def __init__(self, kwargs={}, cut_by_eventid=False, cut_nr_only=False, + neutron_veto_output=False, ): self.directory = directory @@ -210,7 +245,7 @@ def __init__(self, self.rng = random_number_generator self.separation_scale = separation_scale self.event_rate = event_rate / 1e9 #Conversion to ns - self.n_interactions_per_chunk = n_interactions_per_chunk + self.file_size_limit = file_size_limit self.cut_delayed = cut_delayed self.last_chunk_length = np.int64(last_chunk_length) self.first_chunk_left = np.int64(first_chunk_left) @@ -220,6 +255,7 @@ def __init__(self, self.kwargs = kwargs self.cut_by_eventid = cut_by_eventid self.cut_nr_only = cut_nr_only + self.neutron_veto_output = neutron_veto_output self.file = os.path.join(self.directory, self.file_name) @@ -237,7 +273,6 @@ def __init__(self, self.cut_string = None - self.dtype = [('x', np.float64), ('y', np.float64), ('z', np.float64), @@ -256,6 +291,16 @@ def __init__(self, ] self.dtype = self.dtype + strax.time_fields + + if self.neutron_veto_output: + self.nv_dtype = [('pmthitEnergy', np.float32), + ('pmthitTime', np.float32), + ('pmthitID', np.int16), + ('evtid', np.int32), + ] + self.nv_dtype = self.nv_dtype + strax.time_fields + + self.neutron_veto_column_names = ['pmthitEnergy','pmthitTime','pmthitID'] def output_chunk(self): @@ -264,38 +309,62 @@ def output_chunk(self): """ if self.file.endswith(".root"): - interactions, n_simulated_events, start, stop = self._load_root_file() + if self.neutron_veto_output: + interactions, n_simulated_events, start, stop, nv_interactions = self._load_root_file() + else: + interactions, n_simulated_events, start, stop = self._load_root_file() elif self.file.endswith(".csv"): interactions, n_simulated_events, start, stop = self._load_csv_file() else: raise ValueError(f'Cannot load events from file "{self.file}": .root or .cvs file needed.') # Removing all events with zero energy deposit - m = interactions['ed'] > 0 - if self.cut_by_eventid: + #m = interactions['ed'] > 0 + #if self.cut_by_eventid: # ufunc does not work here... - m2 = (interactions['evtid'] >= start) & (interactions['evtid'] < stop) - m = m & m2 - interactions = interactions[m] + # m2 = (interactions['evtid'] >= start) & (interactions['evtid'] < stop) + # m = m & m2 + + #if self.neutron_veto_output: + # m_nv = nv_interactions['pmthitEnergy'] > 0 + #if self.cut_by_eventid: + # m_nv = m_nv & m2 + # nv_interactions = nv_interactions[m_nv] + + #interactions = interactions[m] - if self.cut_nr_only: - log.info("'nr_only' set to True, keeping only the NR events") - m = ((interactions['type'] == "neutron")&(interactions['edproc'] == "hadElastic")) | (interactions['edproc'] == "ionIoni") - e_dep_er = ak.sum(interactions[~m]['ed'], axis=1) - e_dep_nr = ak.sum(interactions[m]['ed'], axis=1) - interactions = interactions[(e_dep_er<10) & (e_dep_nr>0)] + #if self.cut_nr_only: + # log.info("'nr_only' set to True, keeping only the NR events") + # m = ((interactions['type'] == "neutron")&(interactions['edproc'] == "hadElastic")) | (interactions['edproc'] == "ionIoni") + # e_dep_er = ak.sum(interactions[~m]['ed'], axis=1) + # e_dep_nr = ak.sum(interactions[m]['ed'], axis=1) + # interactions = interactions[(e_dep_er<10) & (e_dep_nr>0)] # Removing all events with no interactions: - m = ak.num(interactions['ed']) > 0 - interactions = interactions[m] + #m = ak.num(interactions['ed']) > 0 + #interactions = interactions[m] #Sort interactions in events by time and subtract time of the first interaction + + min_time_TPC = ak.min(interactions['t'], axis=1) + if self.neutron_veto_output: + min_time_NV = ak.min(nv_interactions['pmthitTime'], axis=1) + global_min_time = ak.min([min_time_TPC, min_time_NV], axis=0) + else: + global_min_time = min_time_TPC + interactions = interactions[ak.argsort(interactions['t'])] - interactions['t'] = interactions['t'] - interactions['t'][:, 0] + interactions['t'] = interactions['t'] - global_min_time inter_reshaped = full_array_to_numpy(interactions, self.dtype) - - #Need to check start and stop again.... + + if self.neutron_veto_output: + #m = ak.num(nv_interactions['pmthitEnergy']) > 0 + #nv_interactions = nv_interactions[m] + nv_interactions = nv_interactions[ak.argsort(nv_interactions['pmthitTime'])] + nv_interactions['pmthitTime'] = nv_interactions['pmthitTime'] - global_min_time + nv_inter_reshaped = full_array_to_numpy(nv_interactions, self.nv_dtype) + if self.event_rate > 0: event_times = self.rng.uniform(low = start/self.event_rate, high = stop/self.event_rate, @@ -303,16 +372,24 @@ def output_chunk(self): ).astype(np.int64) event_times = np.sort(event_times) - structure = np.unique(inter_reshaped["evtid"], return_counts = True)[1] + structure, counts = np.unique(inter_reshaped["evtid"], return_counts = True) - #Check again why [:len(structure)] is needed - interaction_time = np.repeat(event_times[:len(structure)], structure) + interaction_time = np.repeat(event_times[structure], counts) inter_reshaped["time"] = interaction_time + inter_reshaped["t"] + + if self.neutron_veto_output: + structure_nv, counts_nv = np.unique(nv_inter_reshaped["evtid"], return_counts = True) + interaction_time_nv = np.repeat(event_times[structure_nv], counts_nv) + #Apply the same event times to the neutron veto output + nv_inter_reshaped["time"] = interaction_time_nv + nv_inter_reshaped["pmthitTime"] + elif self.event_rate == 0: log.info("Using event times from provided input file.") if self.file.endswith(".root"): log.warning("Using event times from root file is not recommended! Use a source_rate > 0 instead.") inter_reshaped["time"] = inter_reshaped["t"] + if self.neutron_veto_output: + nv_inter_reshaped["time"] = nv_inter_reshaped["pmthitTime"] else: raise ValueError("Source rate cannot be negative!") @@ -320,17 +397,92 @@ def output_chunk(self): delay_cut = inter_reshaped["t"] <= self.cut_delayed log.info(f"Removing {np.sum(~delay_cut)} ( {np.sum(~delay_cut)/len(delay_cut) *100:.4} %) interactions later than {self.cut_delayed:.2e} ns.") inter_reshaped = inter_reshaped[delay_cut] + if self.neutron_veto_output: + nv_delay_cut = nv_inter_reshaped["pmthitTime"] <= self.cut_delayed + log.info(f"Removing {np.sum(~nv_delay_cut)} ( {np.sum(~nv_delay_cut)/len(nv_delay_cut) *100:.4} %) NV interactions later than {self.cut_delayed:.2e} ns.") + nv_inter_reshaped = nv_inter_reshaped[nv_delay_cut] sort_idx = np.argsort(inter_reshaped["time"]) inter_reshaped = inter_reshaped[sort_idx] + if self.neutron_veto_output: + nv_sort_idx = np.argsort(nv_inter_reshaped["time"]) + nv_inter_reshaped = nv_inter_reshaped[nv_sort_idx] #Group into chunks - chunk_idx = dynamic_chunking(inter_reshaped["time"], scale = self.separation_scale, n_min = self.n_interactions_per_chunk) - - #Calculate chunk start and end times - chunk_start = np.array([inter_reshaped[chunk_idx == i][0]["time"] for i in np.unique(chunk_idx)]) - chunk_end = np.array([inter_reshaped[chunk_idx == i][-1]["time"] for i in np.unique(chunk_idx)]) - + + #Move this somewhere else? + n_bytes_per_interaction_TPC = 6*8 + 5*4 + 2*2 + 4*40 + n_bytes_per_interaction_NV = 2*8 + 3*4 + 1*2 + + if self.neutron_veto_output: + #Assume that both sets are already sorted! + combined_times = np.append(inter_reshaped["time"], nv_inter_reshaped["time"]) + combined_types = np.append(np.zeros(len(inter_reshaped)), np.ones(len(nv_inter_reshaped))) + + sort_idx = np.argsort(combined_times) + combined_times = combined_times[sort_idx] + combined_types = combined_types[sort_idx] + combined_time_gaps = combined_times[1:] - combined_times[:-1] + combined_time_gaps = np.append(combined_time_gaps, 0) #Add last gap + + tpc_split_index, nv_split_index = dynamic_chunking_two_outputs(combined_time_gaps, + combined_types, + self.file_size_limit, + self.separation_scale, + n_bytes_per_interaction_TPC, + n_bytes_per_interaction_NV, + ) + inter_reshaped_chunks = np.array_split(inter_reshaped, tpc_split_index) + nv_inter_reshaped_chunks = np.array_split(nv_inter_reshaped, nv_split_index) + + #Calculate chunk start and end times + chunk_start_tpc = [] + chunk_end_tpc = [] + chunk_start_nv = [] + chunk_end_nv = [] + for i in range(len(inter_reshaped_chunks)): + + if len(inter_reshaped_chunks[i]) == 0: + chunk_start_tpc.append(np.inf) + chunk_end_tpc.append(-np.inf) + else: + chunk_start_tpc.append(inter_reshaped_chunks[i][0]["time"]) + chunk_end_tpc.append(inter_reshaped_chunks[i][-1]["time"]) + + if len(nv_inter_reshaped_chunks[i]) == 0: + chunk_start_nv.append(np.inf) + chunk_end_nv.append(-np.inf) + else: + chunk_start_nv.append(nv_inter_reshaped_chunks[i][0]["time"]) + chunk_end_nv.append(nv_inter_reshaped_chunks[i][-1]["time"]) + chunk_start_tpc = np.array(chunk_start_tpc) + chunk_end_tpc = np.array(chunk_end_tpc) + chunk_start_nv = np.array(chunk_start_nv) + chunk_end_nv = np.array(chunk_end_nv) + + chunk_start, chunk_end = combine_chunk_times(chunk_start_tpc, chunk_end_tpc, chunk_start_nv, chunk_end_nv) + + else: + #The only TPC case: + + time_gaps = inter_reshaped["time"][1:] - inter_reshaped["time"][:-1] + time_gaps = np.append(time_gaps, 0) #Add last gap + + split_index = dynamic_chunking(time_gaps, + self.file_size_limit, + self.separation_scale, + n_bytes_per_interaction_TPC, + ) + inter_reshaped_chunks = np.array_split(inter_reshaped, split_index) + + chunk_start_tpc = [] + chunk_end_tpc = [] + for i in range(len(inter_reshaped_chunks)): + chunk_start_tpc.append(inter_reshaped_chunks[i][0]["time"]) + chunk_end_tpc.append(inter_reshaped_chunks[i][-1]["time"]) + chunk_start = np.array(chunk_start_tpc) + chunk_end = np.array(chunk_end_tpc) + if (len(chunk_start) > 1) & (len(chunk_end) > 1): gap_length = chunk_start[1:] - chunk_end[:-1] @@ -342,17 +494,19 @@ def output_chunk(self): log.warning("Only one Chunk created! Only a few events simulated? If no, your chunking parameters might not be optimal.") log.warning("Try to decrease the source_rate or decrease the n_interactions_per_chunk") self.chunk_bounds = [chunk_start[0] - self.first_chunk_left, chunk_end[0]+self.last_chunk_length] - + source_done = False - unique_chunk_index_values = np.unique(chunk_idx) - log.info(f"Simulating data in {len(unique_chunk_index_values)} chunks.") - for c_ix, chunk_left, chunk_right in zip(unique_chunk_index_values, self.chunk_bounds[:-1], self.chunk_bounds[1:]): - - if c_ix == unique_chunk_index_values[-1]: + number_of_chunks = len(self.chunk_bounds)-1 + log.info(f"Simulating data in {number_of_chunks} chunks.") + for c_ix, (chunk_left, chunk_right) in enumerate(zip(self.chunk_bounds[:-1], self.chunk_bounds[1:])): + + if c_ix == number_of_chunks-1: source_done = True log.debug("Last chunk created!") - - yield inter_reshaped[chunk_idx == c_ix], chunk_left, chunk_right, source_done + if self.neutron_veto_output: + yield inter_reshaped_chunks[c_ix], chunk_left, chunk_right, source_done, nv_inter_reshaped_chunks[c_ix] + else: + yield inter_reshaped_chunks[c_ix], chunk_left, chunk_right, source_done def last_chunk_bounds(self): return self.chunk_bounds[-1] @@ -427,7 +581,25 @@ def _load_root_file(self): interactions['y_pri'] = ak.broadcast_arrays(xyz_pri['y_pri'], interactions['x'])[0] interactions['z_pri'] = ak.broadcast_arrays(xyz_pri['z_pri'], interactions['x'])[0] - return interactions, n_simulated_events, start, stop + #If we want to have NV output: + if self.neutron_veto_output: + + #Do we need this? + #nv_alias = {'pmthitTime': 'pmthitTime*10**9'} + + nv_interactions = ttree.arrays(self.neutron_veto_column_names, + #aliases=nv_alias, + **self.kwargs) + nv_interactions['pmthitTime'] = nv_interactions['pmthitTime']*10**9 + nv_eventids = ttree.arrays('eventid', **self.kwargs) + nv_eventids = ak.broadcast_arrays(nv_eventids['eventid'], nv_interactions['pmthitTime'])[0] + nv_interactions['evtid'] = nv_eventids + + return interactions, n_simulated_events, start, stop, nv_interactions + + else: + + return interactions, n_simulated_events, start, stop def _get_ttree(self): @@ -533,4 +705,88 @@ def _awkwardify_df(df): "evtid": reshape_awkward(df["eventid"].values , evt_offsets), } - return ak.Array(dictionary) \ No newline at end of file + return ak.Array(dictionary) + + +@njit(cache=True) +def dynamic_chunking(time_gaps, + file_size_limit, + min_gap_length, + n_bytes_per_interaction, + ): + + data_size_mb = 0 + split_index = [] + + running_index = 0 + + for g in time_gaps: + + data_size_mb += n_bytes_per_interaction / 1e6 + running_index += 1 + + if data_size_mb < file_size_limit: + continue + + if g >= min_gap_length: + data_size_mb = 0 + split_index.append(running_index) + + return np.array(split_index) + + + +@njit(cache=True) +def dynamic_chunking_two_outputs(combined_time_gaps, + combined_types, + file_size_limit, + min_gap_length, + n_bytes_per_interaction_TPC, + n_bytes_per_interaction_NV, + ): + """Function to split the TPC and NV data into chunks based on the time gaps between the interactions""" + + data_size_mb_tpc = 0 + data_size_mb_nv = 0 + split_index_tpc = [] + split_index_nv = [] + + running_index_tpc = 0 + running_index_nv = 0 + + for i, (t, g) in enumerate(zip(combined_types, combined_time_gaps)): + + if t == 0: + data_size_mb_tpc += n_bytes_per_interaction_TPC / 1e6 + running_index_tpc += 1 + elif t == 1: + data_size_mb_nv += n_bytes_per_interaction_NV / 1e6 + running_index_nv += 1 + + if (data_size_mb_tpc < file_size_limit) & (data_size_mb_nv < file_size_limit): + continue + + if g >= min_gap_length: + data_size_mb_tpc = data_size_mb_nv = 0 + split_index_tpc.append(running_index_tpc) + split_index_nv.append(running_index_nv) + + return np.array(split_index_tpc), np.array(split_index_nv) + +def combine_chunk_times(cs_tpc, ce_tpc, cs_nv, ce_nv): + """Function to combine TPC and NV chunk times""" + combined_cs = [] + + number_of_chunks = len(cs_tpc) + + for i in range(number_of_chunks): + if cs_tpc[i] < cs_nv[i]: + combined_cs.append(cs_tpc[i]) + else: + combined_cs.append(cs_nv[i]) + + combined_chunk_start = np.array(combined_cs) + combined_chunk_end = np.append(combined_chunk_start[1:], np.max([ce_tpc[-1], ce_nv[-1]])) + + + return combined_chunk_start.astype(np.int64), combined_chunk_end.astype(np.int64) \ No newline at end of file From 0f41f65012c4bca7cf2dc4139e73d906745d01d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henning=20Schulze=20Ei=C3=9Fing?= Date: Tue, 23 Jan 2024 05:30:49 -0600 Subject: [PATCH 02/29] Bugfixing --- fuse/common.py | 2 +- fuse/plugins/micro_physics/input.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/fuse/common.py b/fuse/common.py index 9a2ff81f..db9cf3b6 100644 --- a/fuse/common.py +++ b/fuse/common.py @@ -42,7 +42,7 @@ def dynamic_chunking(data, scale, n_min): def full_array_to_numpy(array, dtype): - len_output = len(awkward_to_flat_numpy(array["x"])) + len_output = len(awkward_to_flat_numpy(array[array.fields[0]])) numpy_data = np.zeros(len_output, dtype=dtype) diff --git a/fuse/plugins/micro_physics/input.py b/fuse/plugins/micro_physics/input.py index 69e77cdb..9d0b85b7 100644 --- a/fuse/plugins/micro_physics/input.py +++ b/fuse/plugins/micro_physics/input.py @@ -7,6 +7,7 @@ import pandas as pd import numpy as np import awkward as ak +from numba import njit export, __all__ = strax.exporter() From ece6952a7f45dbd4ee3043a78bb6c561cc924c81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henning=20Schulze=20Ei=C3=9Fing?= Date: Tue, 23 Jan 2024 05:49:33 -0600 Subject: [PATCH 03/29] Add empty NeutronVetoHitlets Plugin --- fuse/plugins/neutron_veto/nvhitlets.py | 131 +++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 fuse/plugins/neutron_veto/nvhitlets.py diff --git a/fuse/plugins/neutron_veto/nvhitlets.py b/fuse/plugins/neutron_veto/nvhitlets.py new file mode 100644 index 00000000..7bd532bd --- /dev/null +++ b/fuse/plugins/neutron_veto/nvhitlets.py @@ -0,0 +1,131 @@ +import strax +import straxen +import numpy as np +import logging + +logging.basicConfig(handlers=[logging.StreamHandler()]) +log = logging.getLogger('fuse.neutron_veto.nvhitlets') + +from ...common import FUSE_PLUGIN_TIMEOUT + +#A fuse plugin is a python class that inherits from strax.Plugin +#As naming convention we use CamelCase for the class name +class NeutronVetoHitlets(strax.Plugin): + + #Each plugin has a version number + #If the version number changes, fuse will know that it need to re-simulate the data + __version__ = "0.0.1" + + #You need to tell fuse and strax what the plugin needs as input + #In this case we need nv_pmthits + depends_on = ("nv_pmthits") + + #You need to tell fuse and strax what the plugin provides as output + #In this case we provide nv_hitlets + #You can later use st.make(run_number, "nv_hitlets") to run the simulation + provides = "nv_hitlets" + + #You need to tell fuse and strax what the data looks like + #Data of the same data_kind can be combined via "horizontal" concatenation and need + #to have the same output length. + data_kind = 'nv_hitlets' + + #You also need to tell strax what columns the data has + #A column needs a name and a numpy data type. I set everything to float64 here, we can reduce it later + #I used the columns described here: + # https://github.com/XENONnT/fuse/blob/f777d9c281d9e046c2f322a30b462a9b7dd8ee00/test_nv/Hitlet_nv_fuse.py#L114 + dtype = [('area', np.float64), + ('amplitude', np.float64), + ('time_amplitude', np.float64), + ('entropy', np.float64), + ('range_50p_area', np.float64), + ('range_80p_area', np.float64), + ('left_area', np.float64), + ('low_left_area', np.float64), + ('range_hdr_50p_area', np.float64), + ('range_hdr_80p_area', np.float64), + ('left_hdr', np.float64), + ('low_left_hdr', np.float64), + ('fwhm', np.float64), + ('left', np.float64), + ('fwtm', np.float64), + ('low_left', np.float64), + ] + dtype = dtype + strax.interval_dtype #-> Time, length, dt, channel + + #We need to disable automatic rechunking for fuse plugins + #As fuse is going from "leightweigt" data to "heavy" data, + #automatic rechunking can lead to problems in later plugins + rechunk_on_save = False + + #We need to specify when we want to save the data + save_when = strax.SaveWhen.TARGET + + #strax uses a rather short timeout, lets increase it as + #some of the fuse simulation steps can take a while + input_timeout = FUSE_PLUGIN_TIMEOUT + + #We need to tell strax what config options the plugin needs + #We will use the great URLConfigs that are a part of straxen + debug = straxen.URLConfig( + default=False, type=bool,track=False, + help='Show debug informations', + ) + + deterministic_seed = straxen.URLConfig( + default=True, type=bool, + help='Set the random seed from lineage and run_id, or pull the seed from the OS.', + ) + + #If you want to prepare something before we start to run the compute method + #you can put it into the setup method. The setup method is called once while the + #compute method is called independently for each chunk + def setup(self): + + #All plugins can report problmes or debug information via the logging feature + #You can set the log level via the debug config option. + #WARNING messages are always shown whild DEBUG messages are only shown if debug is True + if self.debug: + log.setLevel('DEBUG') + log.debug(f"Running NeutronVetoHitlets version {self.__version__} in debug mode") + else: + log.setLevel('WARNING') + + #Many plugins need to generate random numbers for simulation the corresponding physics process + #In fuse we want to make sure that the simulation is reproducible. + #Therefore we have the default setting of deterministic_seed = True + #In this case the random seed is generated from the run_id and the lineage + #The lineage includes all plugins and their verions that are connected to the input of the + #current plugin as well as all tracked config options and the strax version. + #The run_id is a user input. More on the deterministic seed can be found in + #a dedicated notebook. + #Please make sure that you use the random number generator self.rng when you need random numbers + #later in the plugin. + if self.deterministic_seed: + hash_string = strax.deterministic_hash((self.run_id, self.lineage)) + seed = int(hash_string.encode().hex(), 16) + self.rng = np.random.default_rng(seed = seed) + log.debug(f"Generating random numbers from seed {seed}") + else: + self.rng = np.random.default_rng() + log.debug(f"Generating random numbers with seed pulled from OS") + + + #The compute method is the heart of the plugin. It is executed for each chunk of input data and + #must produce data in the format specified in the self.dtype variable. + def compute(self, nv_pmthits): + + #Make sure your plugin can handle empty inputs + if len(nv_pmthits) == 0: + return np.zeros(0, self.dtype) + + #All your NV goes here + + + + #Build the output array with the correct length and dtype + # I just return some dummy data here + result = np.zeros(10, dtype = self.dtype) + result["time"] = nv_pmthits["time"][0:10] + + return result From f3fca20ad9f99eeb4f399fbb6f13603383d7ca73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henning=20Schulze=20Ei=C3=9Fing?= Date: Tue, 23 Jan 2024 05:51:08 -0600 Subject: [PATCH 04/29] Add nveto plugin to init files --- fuse/plugins/__init__.py | 5 ++++- fuse/plugins/neutron_veto/__init__.py | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 fuse/plugins/neutron_veto/__init__.py diff --git a/fuse/plugins/__init__.py b/fuse/plugins/__init__.py index 871f127e..c9151e0b 100644 --- a/fuse/plugins/__init__.py +++ b/fuse/plugins/__init__.py @@ -5,4 +5,7 @@ from .detector_physics import * from . import pmt_and_daq -from .pmt_and_daq import * \ No newline at end of file +from .pmt_and_daq import * + +from . import neutron_veto +from .neutron_veto import * \ No newline at end of file diff --git a/fuse/plugins/neutron_veto/__init__.py b/fuse/plugins/neutron_veto/__init__.py new file mode 100644 index 00000000..98e151ee --- /dev/null +++ b/fuse/plugins/neutron_veto/__init__.py @@ -0,0 +1,2 @@ +from . import nvhitlets +from .nvhitlets import * \ No newline at end of file From 7122eeb6cb840f28e2375f674a192249263b8b0f Mon Sep 17 00:00:00 2001 From: Layos Carlos Daniel Garcia <92477779+LayosDaniel@users.noreply.github.com> Date: Wed, 3 Apr 2024 14:51:59 +0200 Subject: [PATCH 05/29] Update nvhitlets.py For the last update on February 2024(Layos) --- fuse/plugins/neutron_veto/nvhitlets.py | 290 ++++++++++++++++++++++--- 1 file changed, 260 insertions(+), 30 deletions(-) diff --git a/fuse/plugins/neutron_veto/nvhitlets.py b/fuse/plugins/neutron_veto/nvhitlets.py index 7bd532bd..4e514cea 100644 --- a/fuse/plugins/neutron_veto/nvhitlets.py +++ b/fuse/plugins/neutron_veto/nvhitlets.py @@ -8,10 +8,149 @@ from ...common import FUSE_PLUGIN_TIMEOUT +import uproot as rt +import json +import scipy as scp +from scipy import interpolate +import scipy.constants as const +import awkward as ak +import pandas as pd +from sklearn.cluster import DBSCAN +import random as rd +from tqdm import tqdm +import tqdm.notebook as tq +import time +import cutax + +##--------------------------------------------COMMENTS------------------------------------------------------------## + +#This hitlet simulator is an extension of the work of Diego Ramirez, Daniel Wenz, Andrea Mancuso and Pavel Kavrigin. +#Functions of SPE charge sampling are in the PDF function based in the calibrations of nVeto done by Andrea Mancuso. Daniel Wenz provides a code that takes into account this functions to sample the charge, where this code is based. This leads to SPE a PDF function to sample charge in the hitlet simulator. +#We use the Quantum efficiencies QE for each PMT as a wavelength function for nVeto provided by Andrea Mancuso. + +#WIKI NOTES: +#1.The details of this hitlet simulation leading to the main functions 'G4_nveto_hitlets' and 'G4_mveto_hitlets' for muon and neutron Vetos: +#https://xe1t-wiki.lngs.infn.it/doku.php?id=xenon:xenonnt:layos:snhitlet_from_geant4_output +#In the note mentioned above we test several approaches of charge sampling, for that we can use the function 'G4_to_Veto_hitlets_comparison'. + +#2.THE LAST UPDATE, leading to the actual structure of the code is here :https://xe1t-wiki.lngs.infn.it/doku.php?id=xenon:xenonnt:layos:vetohitlets_v2 , this one is ongoing and we expect to test with SR1 AmBe runs. +#In the note mentioned above we test several approaches of charge sampling, for that we can use the function 'G4_to_Veto_hitlets_comparison'. + + +#--------------------------------------------------------HITLETS AUXILIAR FUNCTIONS--------------------------------------------------------------------# +def flat_list(l): + return np.array([item for sublist in l for item in sublist ]) + +def energytowavelenght(E): + Joules_to_eV=1.602*1e-19 + return 1e9*const.h*const.c/(E*Joules_to_eV) + +#SPE parameters: ID, pe, SPE, acceptance, threshold_pe(based on acceptance) +def SPE_parameters(json_file_spe_model,file_spe_acceptance): + with open(json_file_spe_model,'r') as f: + data_spe = json.loads(f.read()) + with open(file_spe_acceptance) as f: + acceptance = json.load(f) + SPE_ch= pd.DataFrame(columns=['pmtID','pe','SPE','acceptance','threshold_pe']) + SPE_ch['pmtID'],SPE_ch['pe'], SPE_ch['SPE'],SPE_ch['acceptance']=data_spe['pmtID'],data_spe['charge'],data_spe['SPE_values'],acceptance['acceptance'] + acceptance_ch= [threshold_acc(SPE_ch,i) for i in np.arange(2000,2120)] + SPE_ch['threshold_pe']=acceptance_ch + return SPE_ch + +def threshold_acc(SPE_df, ID): + SPE_ID=pd.DataFrame() + SPE_ID['cumulative']=np.cumsum(SPE_df[SPE_df.pmtID==ID].SPE.values[0]) + SPE_ID['charges']= SPE_df[SPE_df.pmtID==ID].pe.values[0] + accep= SPE_df[SPE_df.pmtID==ID].acceptance.values[0] + threshold= min(SPE_ID[SPE_ID.cumulative>=(1-accep)].charges.values) + return threshold + +#To get nVeto plugin, it should be a best way to do that... +st = cutax.contexts.xenonnt_online() +strax_nv = st.get_single_plugin('0', 'events_nv') + + + +#Quantum efficiency +def QE_nVeto(Q_E_nveto_file): + with open(Q_E_nveto_file,'r') as f: + data = json.loads(f.read()) + QE_array_n=[] + #nVeto + for i in np.arange(2000,2120): + QE_array_n.append(interpolate.interp1d(data['nv_pmt_qe_wavelength'],data['nv_pmt_qe'][str(i)], bounds_error=False,fill_value=0)) + #Watertank_QE + pmt_id= list(np.arange(2000,2120)) + QE_array=QE_array_n + pd_dict= {"pmt_id":pmt_id,"QE":QE_array} + return pd_dict + +#Cluster for stacket hitlets +def channel_cluster_nv(t): + db_cluster = DBSCAN(eps=8, min_samples=1)#As a preliminar value we fix distance between two photons arriving in the same pmt 8ns + t_val=np.array(t) + clusters=np.array(db_cluster.fit_predict(t_val.reshape(-1, 1))) + return clusters + +#Function to use in pivot_table module (clearly something we can optimize) +def recover_value(x): + m_size= np.array(x).size + if m_size==1: + ret=x + elif m_size>1: + ret=list(x) + return ret + + +def type_pri_evt(x): + if (type(x)==np.float32) or (type(x)==np.float64) or (type(x)==float): + ret=x + elif type(x)==list: + ret=x[0] + return ret +#For building independent time hitlets to compute into events or hitlets time from a source (ex: AmBe) +def time_hitlets_nv(time,ids,freq): + if type(time)==np.float64: + ret=time + elif type(time)==list: + ret=min(time) + return ret + freq*ids + +#Fonction to transform a hitlet dataframe output into 'hitlets_nv' ndarray +#Fonction to transform a hitlet dataframe output into 'hitlets_nv' ndarray +dtype = [('area', np.float64), + ('amplitude', np.float64), + ('time_amplitude', np.int16), + ('entropy', np.float64), + ('range_50p_area', np.float64), + ('range_80p_area', np.float64), + ('left_area', np.float64), + ('low_left_area', np.float64), + ('range_hdr_50p_area', np.float64), + ('range_hdr_80p_area', np.float64), + ('left_hdr', np.float64), + ('low_left_hdr', np.float64), + ('fwhm', np.float64), + ('left', np.float64), + ('fwtm', np.float64), + ('low_left', np.float64), + ] +dtype = dtype + strax.interval_dtype #-> Time, length, dt, channel #A fuse plugin is a python class that inherits from strax.Plugin #As naming convention we use CamelCase for the class name -class NeutronVetoHitlets(strax.Plugin): +def df_to_hit_array(data): + result = np.zeros(len(data), dtype = dtype) + result['time'] = data.time.values + result['length']= np.array([1.]*len(data)) + result['dt'] =np.array([10.]*len(data)) + result['channel']=data.pmthitID.values + result['area']= data.pe_area.values + result=strax.sort_by_time(result) + return result +#A fuse plugin is a python class that inherits from strax.Plugin +#As naming convention we use CamelCase for the class name +class NeutronVetoHitlets(strax.Plugin): #Each plugin has a version number #If the version number changes, fuse will know that it need to re-simulate the data __version__ = "0.0.1" @@ -34,24 +173,6 @@ class NeutronVetoHitlets(strax.Plugin): #A column needs a name and a numpy data type. I set everything to float64 here, we can reduce it later #I used the columns described here: # https://github.com/XENONnT/fuse/blob/f777d9c281d9e046c2f322a30b462a9b7dd8ee00/test_nv/Hitlet_nv_fuse.py#L114 - dtype = [('area', np.float64), - ('amplitude', np.float64), - ('time_amplitude', np.float64), - ('entropy', np.float64), - ('range_50p_area', np.float64), - ('range_80p_area', np.float64), - ('left_area', np.float64), - ('low_left_area', np.float64), - ('range_hdr_50p_area', np.float64), - ('range_hdr_80p_area', np.float64), - ('left_hdr', np.float64), - ('low_left_hdr', np.float64), - ('fwhm', np.float64), - ('left', np.float64), - ('fwtm', np.float64), - ('low_left', np.float64), - ] - dtype = dtype + strax.interval_dtype #-> Time, length, dt, channel #We need to disable automatic rechunking for fuse plugins #As fuse is going from "leightweigt" data to "heavy" data, @@ -76,10 +197,122 @@ class NeutronVetoHitlets(strax.Plugin): default=True, type=bool, help='Set the random seed from lineage and run_id, or pull the seed from the OS.', ) + def __init__(self): + self.path = '/home/layos/env_starter/fuse/private_nt_aux_files/sim_files/'#Have to put here the correct paths.... + self.QE_value = QE_nVeto(self.path+'nveto_pmt_qe.json') + self.SPE_nVeto = SPE_parameters(self.path+'SPE_'+'SR1'+'_test_fuse.json',self.path+'acceptance_SR0_test_fuse.json') + self.dtype=dtype + #Get Quantum efficiency + def QE_E(self,E,ID): + WL= energytowavelenght(E) + ind=ID-2000 + qe=self.QE_value['QE'][ind](WL) + return qe + + def get_acceptance(self,ID): + ind=ID-2000 + acc= self.SPE_nVeto.acceptance.values[ind] + return acc + #Get acceptance threshold + def get_threshold_acc(self,ID): + ind=ID-2000 + threshold= self.SPE_nVeto.threshold_pe.values[ind] + return threshold + + #Sampling charge from SPE + def pe_charge_N(self,pmt_id): + SPE_channel= self.SPE_nVeto[self.SPE_nVeto.pmtID==pmt_id] + charge=rd.choices(SPE_channel.pe.values[0],SPE_channel.SPE.values[0],k=1)[0] + return charge + + #--------------------------- Hitlet function ------------------------------------------------------------# + + def _nv_hitlets(self,pmthits, CE_Scaling=0.75): + + #-------------------------------------------------Arguments---------------------------------------------------# - #If you want to prepare something before we start to run the compute method - #you can put it into the setup method. The setup method is called once while the - #compute method is called independently for each chunk + #e_1,e_2= range of entries from root file + #root_keys= for primaries or flags that you to keep if you don't wont any just root_keys=[] + #QE_Scaling corrrespond to collection efficiency, no study has been done on the CE of muon Veto we use a default value close to the nVeto see https://xe1t-wiki.lngs.infn.it/doku.php?id=xenon:mancuso:hitletsimulator:collection_efficiency + #period : this could be related to the rate of a source, or the rate for a time bin if we reconstruct an spectrum. If no source 1e9 is the default value (see Comments) + #csv : if you want to save the hitlet when you run several chuncks + #Name : to name this csv file + #Isotopes : if we want to keep information about some activated isotopes (True) if False it cut these events.(See comments) + + #----------------------------------------------Commments-----------------------------------------------------------------#: + #1.There is no application of a threshold per channel based on the acceptation by default, but we keep the value in the data frame for each pmt, and one can do manually. This is in order to not condition the sampling, and compare it with the data with different cuts. + #.2. The period is set by default at 1s to care about no pyle up or merge of hitlets if one want to do an analysis for rare events (independent non sourced ones). If we simulate a calibration or a constant flux this value has to be changed to real rate one. + #.3. The way the code works, if information of Isotopes is keeped we cannot recover G4 primary parameters after building 'event_nv'. We have to think in this case a different way to do it. + #4.Stacked hitlets: using DBSCAN is maybe not required (as it makes the hitlet slower) and an easier approach can be used (some ideas???) + + + #0.---------------Load GEANT output-------------------# + + + + #1. First step PHOTON to first dinode + pmthits=ak.Array(pmthits) + mask=pmthits['pmthitID']>=2000 + pmthits=pmthits[mask] + print("Applying QE and CE") + qe = 1e-2*np.vectorize(self.QE_E)(pmthits['pmthitEnergy'],pmthits['pmthitID'])#Applying Quantum efficiency for each pmt + qe *= CE_Scaling #Applying collection efficiency + qe = qe*np.vectorize(self.get_acceptance)(pmthits['pmthitID'])#Applying acceptance: for the approach in which SPE PDF has already applied a threshold for low charges + pe = np.array([np.random.binomial(1, j, 1)[0] for j in qe]) + print("Loading hit survive") + maks_qe = pe>0 + pmthits=pmthits[maks_qe] + + #2. Stacked hitlets: this correspond to hitlets in the same pmt with a time difference below some estimated time response of the Channel (8 ns, i.e. 4 samples). + print('Looking for stacket hitlets') + #Here we set times related to the first hit, we only use that for stacket hitlets + times=[] + for i in (np.unique(pmthits.evtid)): + mask= pmthits.evtid==i + pmthits_evt=pmthits[mask] + cluster_times_ns = pmthits_evt.pmthitTime - min(pmthits_evt.pmthitTime) + times.append(cluster_times_ns) + pmthits['cluster_times_ns'] = flat_list(times) + pmthits['pe_area'] = np.vectorize(self.pe_charge_N)(pmthits['pmthitID']) + column = ['evtid','pmthitID'] + df=ak.to_dataframe(pmthits) + #return pmthits + df_c = pd.pivot_table(df, index = column, aggfunc={'cluster_times_ns': lambda x : list(x)}) + clusters = [channel_cluster_nv(i) for i in df_c.cluster_times_ns.values] + df_sorted = df.sort_values(['evtid','pmthitID'], ascending = [True,True]) + df_sorted.insert(len(df.columns), 'labels',flat_list(clusters)) + #We track the stacket hitlets using the labels and save the min time and max endtime + + #3.Creating hitlet dataframe + #We track the stacket hitlets using the labels and save the min time and max endtime + #***(Dataframe format is because group by is not already implemented into a ankward array) + col_index = ['evtid','pmthitID','labels'] + mask = np.isin(df_sorted.columns.values, col_index, invert=True) + col_hitlet=df_sorted.columns.values[mask] + arg_dicio = dict.fromkeys(col_hitlet) + for i in col_hitlet: + if i == 'pe_area': + arg_dicio[i]= np.sum + elif i == 'time': + arg_dicio[i]= np.min + elif i == 'endtime': + arg_dicio[i]= np.max + else: + arg_dicio[i]= lambda x: recover_value(x) + table_stack = pd.pivot_table(df_sorted, index = col_index, aggfunc=arg_dicio) + print('Creating hitlet dataframe') + hitlet_df = pd.DataFrame(columns=col_index ) + for j in tq.tqdm(range(len(col_index))): + hitlet_df[col_index[j]] = [table_stack.index[i][j] for i in range(len(table_stack))] + for i in tq.tqdm(col_hitlet): + hitlet_df.insert(len(hitlet_df.columns), i , table_stack[i].values) + print('getting time of hitlets_nv') + + #4.Transforming to hitlets_nv format + print("Creating hitlets_nv") + hitlets_nv = df_to_hit_array(hitlet_df) + return hitlet_df + def setup(self): #All plugins can report problmes or debug information via the logging feature @@ -114,18 +347,15 @@ def setup(self): #The compute method is the heart of the plugin. It is executed for each chunk of input data and #must produce data in the format specified in the self.dtype variable. def compute(self, nv_pmthits): - #Make sure your plugin can handle empty inputs if len(nv_pmthits) == 0: return np.zeros(0, self.dtype) #All your NV goes here + result = df_to_hit_array(self._nv_hitlets(nv_pmthits)) + return result + + + - - - #Build the output array with the correct length and dtype - # I just return some dummy data here - result = np.zeros(10, dtype = self.dtype) - result["time"] = nv_pmthits["time"][0:10] - return result From 3fde86f42003d5e43e7b4c20216c359f6a3a4b2e Mon Sep 17 00:00:00 2001 From: Layos Carlos Daniel Garcia <92477779+LayosDaniel@users.noreply.github.com> Date: Wed, 3 Apr 2024 15:00:13 +0200 Subject: [PATCH 06/29] Add files via upload --- .../neutron_veto/Testing_nv_hitlet.ipynb | 272 ++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb diff --git a/fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb b/fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb new file mode 100644 index 00000000..96482f90 --- /dev/null +++ b/fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb @@ -0,0 +1,272 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Getting Started\n", + "In this notebook you will learn how to run your first simulation with fuse.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Imports" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "You specified _auto_append_rucio_local=True and you are not on dali compute nodes, so we will add the following rucio local path: /project/lgrandi/rucio/\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/straxen/url_config.py:743: UserWarning: From straxen version 2.1.0 onward, URLConfig parameterswill be sorted alphabetically before being passed to the plugins, this will change the lineage hash for non-sorted URLs. To load data processed with non-sorted URLs, you will need to use an older version.\n", + " warnings.warn(\n" + ] + } + ], + "source": [ + "import fuse\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import awkward as ak\n", + "from tqdm import tqdm\n", + "import tqdm.notebook as tq\n", + "import pandas as pd\n", + "from sklearn.cluster import DBSCAN\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Warning! elife not in context config, skipping...\n", + "Warning! electron_drift_velocity not in context config, skipping...\n", + "Warning! electron_drift_time_gate not in context config, skipping...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + " warnings.warn(\n", + "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + " warnings.warn(\n", + "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + " warnings.warn(\n", + "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + " warnings.warn(\n", + "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + " warnings.warn(\n", + "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + " warnings.warn(\n" + ] + } + ], + "source": [ + "st = fuse.context.full_chain_context(output_folder = \"./fuse_data\")\n", + "st.set_config({\"path\": \"/project2/lgrandi/layos/\",\n", + " \"file_name\": \"output_n_Veto_neutron_AmBe_1.root\",\n", + " \"entry_stop\": 2000,\n", + " \"nv_output\": True,#On inclus les pmts du Neutron Veto (par defaut False)\n", + " \"debug\": True,\n", + " \"file_size_limit\": 500/1e4,#Poser question a Hening sur cela....\n", + " })\n", + "\n", + "run_number = \"00003\"#Attention à ce parametre, aujourd'hui triviale, mais qui tiens en compte du mapping pour la TPC en fonction du run." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:strax:Option gain_model_nv not taken by any registered plugin\n", + "WARNING:strax:Option gain_model_mv not taken by any registered plugin\n", + "WARNING:strax:Option gain_model_nv not taken by any registered plugin\n", + "WARNING:strax:Option gain_model_mv not taken by any registered plugin\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "4c1dc3bbff9c403ab2e12c8ca5a40169", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Loading nv_pmthits: | | 0.00 % [00:00)" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.hist(events_nv['area'], range=[0,50],bins=20)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.19" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From f7f1de030131f9d4fcc9d19ffdf8ec7db808d608 Mon Sep 17 00:00:00 2001 From: Layos Carlos Daniel Garcia <92477779+LayosDaniel@users.noreply.github.com> Date: Mon, 8 Apr 2024 12:38:32 +0200 Subject: [PATCH 07/29] Update nvhitlets.py --- fuse/plugins/neutron_veto/nvhitlets.py | 94 +++++++++++++------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/fuse/plugins/neutron_veto/nvhitlets.py b/fuse/plugins/neutron_veto/nvhitlets.py index 4e514cea..6d342d93 100644 --- a/fuse/plugins/neutron_veto/nvhitlets.py +++ b/fuse/plugins/neutron_veto/nvhitlets.py @@ -91,7 +91,19 @@ def channel_cluster_nv(t): t_val=np.array(t) clusters=np.array(db_cluster.fit_predict(t_val.reshape(-1, 1))) return clusters - +def get_clusters_arrays(arr,typ): + arr_nv_c=np.zeros(1, dtype=typ) + arr_nv_c['n_clusters_hits']= len(arr) + for i in arr.fields: + if (i=='time') or (i=='pmthitTime') or (i=='cluster_times_ns'): + arr_nv_c[i] = np.min(arr[i]) + elif i== 'endtime': + arr_nv_c[i]= np.max(arr[i]) + elif (i == 'pe_area') or (i=='pmthitEnergy'): + arr_nv_c[i]= np.sum(arr[i]) + elif (i=='evtid') or (i=='pmthitID') or (i=='labels') : + arr_nv_c[i]= np.unique(arr[i]) + return arr_nv_c #Function to use in pivot_table module (clearly something we can optimize) def recover_value(x): m_size= np.array(x).size @@ -147,6 +159,15 @@ def df_to_hit_array(data): result['area']= data.pe_area.values result=strax.sort_by_time(result) return result +def hit_array_to_nvhitlet(data): + result = np.zeros(len(data), dtype = dtype) + result['time'] = data['time'] + result['length']= np.array([1.]*len(data)) + result['dt'] =np.array([10.]*len(data)) + result['channel']=data['pmthitID'] + result['area']= data['pe_area'] + result=strax.sort_by_time(result) + return result #A fuse plugin is a python class that inherits from strax.Plugin #As naming convention we use CamelCase for the class name @@ -263,7 +284,17 @@ def _nv_hitlets(self,pmthits, CE_Scaling=0.75): maks_qe = pe>0 pmthits=pmthits[maks_qe] - #2. Stacked hitlets: this correspond to hitlets in the same pmt with a time difference below some estimated time response of the Channel (8 ns, i.e. 4 samples). + #2. Sampling charge from SPE + print("Sampling hitlets charge pe") + pmthits['pe_area'] = np.vectorize(self.pe_charge_N)(pmthits['pmthitID']) + dtypes=[] + for i in test_array.fields + ['labels','n_clusters_hits']: + if (i=='evtid') or (i=='time') or (i=='pmthitID') or (i=='endtime'): + dtypes.append((i,np.int64)) + else: + dtypes.append((i,np.float64)) + + #3. Stacked hitlets: this correspond to hitlets in the same pmt with a time difference below some estimated time response of the Channel (8 ns, i.e. 4 samples). print('Looking for stacket hitlets') #Here we set times related to the first hit, we only use that for stacket hitlets times=[] @@ -273,45 +304,19 @@ def _nv_hitlets(self,pmthits, CE_Scaling=0.75): cluster_times_ns = pmthits_evt.pmthitTime - min(pmthits_evt.pmthitTime) times.append(cluster_times_ns) pmthits['cluster_times_ns'] = flat_list(times) - pmthits['pe_area'] = np.vectorize(self.pe_charge_N)(pmthits['pmthitID']) - column = ['evtid','pmthitID'] - df=ak.to_dataframe(pmthits) - #return pmthits - df_c = pd.pivot_table(df, index = column, aggfunc={'cluster_times_ns': lambda x : list(x)}) - clusters = [channel_cluster_nv(i) for i in df_c.cluster_times_ns.values] - df_sorted = df.sort_values(['evtid','pmthitID'], ascending = [True,True]) - df_sorted.insert(len(df.columns), 'labels',flat_list(clusters)) - #We track the stacket hitlets using the labels and save the min time and max endtime - - #3.Creating hitlet dataframe - #We track the stacket hitlets using the labels and save the min time and max endtime - #***(Dataframe format is because group by is not already implemented into a ankward array) - col_index = ['evtid','pmthitID','labels'] - mask = np.isin(df_sorted.columns.values, col_index, invert=True) - col_hitlet=df_sorted.columns.values[mask] - arg_dicio = dict.fromkeys(col_hitlet) - for i in col_hitlet: - if i == 'pe_area': - arg_dicio[i]= np.sum - elif i == 'time': - arg_dicio[i]= np.min - elif i == 'endtime': - arg_dicio[i]= np.max - else: - arg_dicio[i]= lambda x: recover_value(x) - table_stack = pd.pivot_table(df_sorted, index = col_index, aggfunc=arg_dicio) - print('Creating hitlet dataframe') - hitlet_df = pd.DataFrame(columns=col_index ) - for j in tq.tqdm(range(len(col_index))): - hitlet_df[col_index[j]] = [table_stack.index[i][j] for i in range(len(table_stack))] - for i in tq.tqdm(col_hitlet): - hitlet_df.insert(len(hitlet_df.columns), i , table_stack[i].values) - print('getting time of hitlets_nv') - - #4.Transforming to hitlets_nv format - print("Creating hitlets_nv") - hitlets_nv = df_to_hit_array(hitlet_df) - return hitlet_df + arr_c_evt=[] + for i in tq.tqdm(np.unique(pmthits['evtid'])): + arr_evt = pmthits[pmthits['evtid']==i] + arr_c_pmt=[] + for j in np.unique(arr_evt['pmthitID']): + arr_pmt = arr_evt[arr_evt['pmthitID']==j] + labels = channel_cluster_nv(arr_pmt['cluster_times_ns']) + arr_pmt['labels'] = labels + arr_c =np.concatenate([get_clusters_arrays(arr_pmt[arr_pmt['labels']==l],dtypes) for l in np.unique(labels)]) + arr_c_pmt.append(arr_c) + arr_c_evt.append(np.concatenate(arr_c_pmt)) + nv_arrays = np.concatenate(arr_c_evt) + return nv_arrays def setup(self): @@ -352,10 +357,5 @@ def compute(self, nv_pmthits): return np.zeros(0, self.dtype) #All your NV goes here - result = df_to_hit_array(self._nv_hitlets(nv_pmthits)) + result = hit_array_to_nvhitlet(nv_pmthits) return result - - - - - From 55665865da2d212e3d801c9bdd92279190599a09 Mon Sep 17 00:00:00 2001 From: Layos Carlos Daniel Garcia <92477779+LayosDaniel@users.noreply.github.com> Date: Sat, 13 Apr 2024 11:31:54 +0200 Subject: [PATCH 08/29] Update nvhitlets.py --- fuse/plugins/neutron_veto/nvhitlets.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fuse/plugins/neutron_veto/nvhitlets.py b/fuse/plugins/neutron_veto/nvhitlets.py index 6d342d93..f6b3b4b2 100644 --- a/fuse/plugins/neutron_veto/nvhitlets.py +++ b/fuse/plugins/neutron_veto/nvhitlets.py @@ -248,7 +248,7 @@ def pe_charge_N(self,pmt_id): #--------------------------- Hitlet function ------------------------------------------------------------# - def _nv_hitlets(self,pmthits, CE_Scaling=0.75): + def _nv_hitlets(self,pmthits, CE_Scaling=0.75, Stacked=None): #-------------------------------------------------Arguments---------------------------------------------------# @@ -288,7 +288,7 @@ def _nv_hitlets(self,pmthits, CE_Scaling=0.75): print("Sampling hitlets charge pe") pmthits['pe_area'] = np.vectorize(self.pe_charge_N)(pmthits['pmthitID']) dtypes=[] - for i in test_array.fields + ['labels','n_clusters_hits']: + for i in pmthits.fields + ['labels','n_clusters_hits']: if (i=='evtid') or (i=='time') or (i=='pmthitID') or (i=='endtime'): dtypes.append((i,np.int64)) else: @@ -304,6 +304,7 @@ def _nv_hitlets(self,pmthits, CE_Scaling=0.75): cluster_times_ns = pmthits_evt.pmthitTime - min(pmthits_evt.pmthitTime) times.append(cluster_times_ns) pmthits['cluster_times_ns'] = flat_list(times) + dtypes=dtypes + [('cluster_times_ns', np.float64)] arr_c_evt=[] for i in tq.tqdm(np.unique(pmthits['evtid'])): arr_evt = pmthits[pmthits['evtid']==i] @@ -355,7 +356,7 @@ def compute(self, nv_pmthits): #Make sure your plugin can handle empty inputs if len(nv_pmthits) == 0: return np.zeros(0, self.dtype) - + hitlets= self._nv_hitlets(nv_pmthits) #All your NV goes here - result = hit_array_to_nvhitlet(nv_pmthits) + result = hit_array_to_nvhitlet(hitlets) return result From 8e62509fad78d88dc56dede947a63772ab7340bc Mon Sep 17 00:00:00 2001 From: Layos Carlos Daniel Garcia <92477779+LayosDaniel@users.noreply.github.com> Date: Sun, 14 Apr 2024 21:20:53 +0200 Subject: [PATCH 09/29] Update nvhitlets.py --- fuse/plugins/neutron_veto/nvhitlets.py | 73 ++++++++++++-------------- 1 file changed, 34 insertions(+), 39 deletions(-) diff --git a/fuse/plugins/neutron_veto/nvhitlets.py b/fuse/plugins/neutron_veto/nvhitlets.py index f6b3b4b2..611f8cd5 100644 --- a/fuse/plugins/neutron_veto/nvhitlets.py +++ b/fuse/plugins/neutron_veto/nvhitlets.py @@ -29,12 +29,7 @@ #We use the Quantum efficiencies QE for each PMT as a wavelength function for nVeto provided by Andrea Mancuso. #WIKI NOTES: -#1.The details of this hitlet simulation leading to the main functions 'G4_nveto_hitlets' and 'G4_mveto_hitlets' for muon and neutron Vetos: -#https://xe1t-wiki.lngs.infn.it/doku.php?id=xenon:xenonnt:layos:snhitlet_from_geant4_output -#In the note mentioned above we test several approaches of charge sampling, for that we can use the function 'G4_to_Veto_hitlets_comparison'. - -#2.THE LAST UPDATE, leading to the actual structure of the code is here :https://xe1t-wiki.lngs.infn.it/doku.php?id=xenon:xenonnt:layos:vetohitlets_v2 , this one is ongoing and we expect to test with SR1 AmBe runs. -#In the note mentioned above we test several approaches of charge sampling, for that we can use the function 'G4_to_Veto_hitlets_comparison'. +#https://xe1t-wiki.lngs.infn.it/doku.php?id=xenon:xenonnt:layos:nveto_hitlet_into_fuse #--------------------------------------------------------HITLETS AUXILIAR FUNCTIONS--------------------------------------------------------------------# @@ -122,11 +117,12 @@ def type_pri_evt(x): return ret #For building independent time hitlets to compute into events or hitlets time from a source (ex: AmBe) def time_hitlets_nv(time,ids,freq): - if type(time)==np.float64: + #freq: corresponds to the rate in [1/s] of the calibration source + if type(time)==list: + ret=min(time) + else: ret=time - elif type(time)==list: - ret=min(time) - return ret + freq*ids + return ret + freq*ids*1e9 #Fonction to transform a hitlet dataframe output into 'hitlets_nv' ndarray #Fonction to transform a hitlet dataframe output into 'hitlets_nv' ndarray @@ -248,23 +244,17 @@ def pe_charge_N(self,pmt_id): #--------------------------- Hitlet function ------------------------------------------------------------# - def _nv_hitlets(self,pmthits, CE_Scaling=0.75, Stacked=None): + def _nv_hitlets(self,pmthits, CE_Scaling=0.75, Stacked='No', period = 1.): #-------------------------------------------------Arguments---------------------------------------------------# - - #e_1,e_2= range of entries from root file - #root_keys= for primaries or flags that you to keep if you don't wont any just root_keys=[] #QE_Scaling corrrespond to collection efficiency, no study has been done on the CE of muon Veto we use a default value close to the nVeto see https://xe1t-wiki.lngs.infn.it/doku.php?id=xenon:mancuso:hitletsimulator:collection_efficiency - #period : this could be related to the rate of a source, or the rate for a time bin if we reconstruct an spectrum. If no source 1e9 is the default value (see Comments) - #csv : if you want to save the hitlet when you run several chuncks - #Name : to name this csv file - #Isotopes : if we want to keep information about some activated isotopes (True) if False it cut these events.(See comments) + #period : this could be related to the rate of a source, or the rate for a time bin if we reconstruct an spectrum. If no source 1 second is the default value (see Comments) + #----------------------------------------------Commments-----------------------------------------------------------------#: #1.There is no application of a threshold per channel based on the acceptation by default, but we keep the value in the data frame for each pmt, and one can do manually. This is in order to not condition the sampling, and compare it with the data with different cuts. #.2. The period is set by default at 1s to care about no pyle up or merge of hitlets if one want to do an analysis for rare events (independent non sourced ones). If we simulate a calibration or a constant flux this value has to be changed to real rate one. - #.3. The way the code works, if information of Isotopes is keeped we cannot recover G4 primary parameters after building 'event_nv'. We have to think in this case a different way to do it. - #4.Stacked hitlets: using DBSCAN is maybe not required (as it makes the hitlet slower) and an easier approach can be used (some ideas???) + #0.---------------Load GEANT output-------------------# @@ -293,30 +283,35 @@ def _nv_hitlets(self,pmthits, CE_Scaling=0.75, Stacked=None): dtypes.append((i,np.int64)) else: dtypes.append((i,np.float64)) - - #3. Stacked hitlets: this correspond to hitlets in the same pmt with a time difference below some estimated time response of the Channel (8 ns, i.e. 4 samples). - print('Looking for stacket hitlets') - #Here we set times related to the first hit, we only use that for stacket hitlets + + #3. Creating hitlet times + print('Getting time hitlets') times=[] for i in (np.unique(pmthits.evtid)): mask= pmthits.evtid==i pmthits_evt=pmthits[mask] cluster_times_ns = pmthits_evt.pmthitTime - min(pmthits_evt.pmthitTime) times.append(cluster_times_ns) - pmthits['cluster_times_ns'] = flat_list(times) + pmthits['cluster_times_ns'] = np.vectorize(time_hitlets_nv)(flat_list(times),pmthits['evtid'],period) dtypes=dtypes + [('cluster_times_ns', np.float64)] - arr_c_evt=[] - for i in tq.tqdm(np.unique(pmthits['evtid'])): - arr_evt = pmthits[pmthits['evtid']==i] - arr_c_pmt=[] - for j in np.unique(arr_evt['pmthitID']): - arr_pmt = arr_evt[arr_evt['pmthitID']==j] - labels = channel_cluster_nv(arr_pmt['cluster_times_ns']) - arr_pmt['labels'] = labels - arr_c =np.concatenate([get_clusters_arrays(arr_pmt[arr_pmt['labels']==l],dtypes) for l in np.unique(labels)]) - arr_c_pmt.append(arr_c) - arr_c_evt.append(np.concatenate(arr_c_pmt)) - nv_arrays = np.concatenate(arr_c_evt) + if Stacked=='No': + nv_arrays= pmthits + elif Stacked =='yes': + #3.1 Stacked hitlets: this correspond to hitlets in the same pmt with a time difference below some estimated time response of the Channel (8 ns, i.e. 4 samples). + print('Looking for stacket hitlets') + #Here we set times related to the first hit, we only use that for stacket hitlets + arr_c_evt=[] + for i in tq.tqdm(np.unique(pmthits['evtid'])): + arr_evt = pmthits[pmthits['evtid']==i] + arr_c_pmt=[] + for j in np.unique(arr_evt['pmthitID']): + arr_pmt = arr_evt[arr_evt['pmthitID']==j] + labels = channel_cluster_nv(arr_pmt['cluster_times_ns']) + arr_pmt['labels'] = labels + arr_c =np.concatenate([get_clusters_arrays(arr_pmt[arr_pmt['labels']==l],dtypes) for l in np.unique(labels)]) + arr_c_pmt.append(arr_c) + arr_c_evt.append(np.concatenate(arr_c_pmt)) + nv_arrays = np.concatenate(arr_c_evt) return nv_arrays def setup(self): @@ -352,11 +347,11 @@ def setup(self): #The compute method is the heart of the plugin. It is executed for each chunk of input data and #must produce data in the format specified in the self.dtype variable. - def compute(self, nv_pmthits): + def compute(self, nv_pmthits, eCE=0.75, Stacked_opt='No', rate = 1.): #Make sure your plugin can handle empty inputs if len(nv_pmthits) == 0: return np.zeros(0, self.dtype) - hitlets= self._nv_hitlets(nv_pmthits) + hitlets= self._nv_hitlets(nv_pmthits,CE_Scaling=eCE, Stacked=Stacked_opt, period = rate) #All your NV goes here result = hit_array_to_nvhitlet(hitlets) return result From 85ed17240470328e0b1cf1d57507d4fa0079f27e Mon Sep 17 00:00:00 2001 From: Layos Carlos Daniel Garcia <92477779+LayosDaniel@users.noreply.github.com> Date: Sun, 14 Apr 2024 21:27:21 +0200 Subject: [PATCH 10/29] Delete fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb --- .../neutron_veto/Testing_nv_hitlet.ipynb | 272 ------------------ 1 file changed, 272 deletions(-) delete mode 100644 fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb diff --git a/fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb b/fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb deleted file mode 100644 index 96482f90..00000000 --- a/fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb +++ /dev/null @@ -1,272 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Getting Started\n", - "In this notebook you will learn how to run your first simulation with fuse.\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Imports" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "You specified _auto_append_rucio_local=True and you are not on dali compute nodes, so we will add the following rucio local path: /project/lgrandi/rucio/\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/straxen/url_config.py:743: UserWarning: From straxen version 2.1.0 onward, URLConfig parameterswill be sorted alphabetically before being passed to the plugins, this will change the lineage hash for non-sorted URLs. To load data processed with non-sorted URLs, you will need to use an older version.\n", - " warnings.warn(\n" - ] - } - ], - "source": [ - "import fuse\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import awkward as ak\n", - "from tqdm import tqdm\n", - "import tqdm.notebook as tq\n", - "import pandas as pd\n", - "from sklearn.cluster import DBSCAN\n" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Warning! elife not in context config, skipping...\n", - "Warning! electron_drift_velocity not in context config, skipping...\n", - "Warning! electron_drift_time_gate not in context config, skipping...\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", - " warnings.warn(\n", - "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", - " warnings.warn(\n", - "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", - " warnings.warn(\n", - "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", - " warnings.warn(\n", - "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", - " warnings.warn(\n", - "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", - " warnings.warn(\n" - ] - } - ], - "source": [ - "st = fuse.context.full_chain_context(output_folder = \"./fuse_data\")\n", - "st.set_config({\"path\": \"/project2/lgrandi/layos/\",\n", - " \"file_name\": \"output_n_Veto_neutron_AmBe_1.root\",\n", - " \"entry_stop\": 2000,\n", - " \"nv_output\": True,#On inclus les pmts du Neutron Veto (par defaut False)\n", - " \"debug\": True,\n", - " \"file_size_limit\": 500/1e4,#Poser question a Hening sur cela....\n", - " })\n", - "\n", - "run_number = \"00003\"#Attention à ce parametre, aujourd'hui triviale, mais qui tiens en compte du mapping pour la TPC en fonction du run." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "WARNING:strax:Option gain_model_nv not taken by any registered plugin\n", - "WARNING:strax:Option gain_model_mv not taken by any registered plugin\n", - "WARNING:strax:Option gain_model_nv not taken by any registered plugin\n", - "WARNING:strax:Option gain_model_mv not taken by any registered plugin\n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "4c1dc3bbff9c403ab2e12c8ca5a40169", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Loading nv_pmthits: | | 0.00 % [00:00)" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.hist(events_nv['area'], range=[0,50],bins=20)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.19" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} From c490b83cf2c4fdf041f3fa7ffc5d6064b8bf9d74 Mon Sep 17 00:00:00 2001 From: Layos Carlos Daniel Garcia <92477779+LayosDaniel@users.noreply.github.com> Date: Sun, 14 Apr 2024 21:27:39 +0200 Subject: [PATCH 11/29] Add files via upload --- .../neutron_veto/Testing_nv_hitlet.ipynb | 280 ++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb diff --git a/fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb b/fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb new file mode 100644 index 00000000..909dfa79 --- /dev/null +++ b/fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb @@ -0,0 +1,280 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Getting Started\n", + "In this notebook you will learn how to run your first simulation with fuse.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Imports" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "You specified _auto_append_rucio_local=True and you are not on dali compute nodes, so we will add the following rucio local path: /project/lgrandi/rucio/\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/straxen/url_config.py:743: UserWarning: From straxen version 2.1.0 onward, URLConfig parameterswill be sorted alphabetically before being passed to the plugins, this will change the lineage hash for non-sorted URLs. To load data processed with non-sorted URLs, you will need to use an older version.\n", + " warnings.warn(\n" + ] + } + ], + "source": [ + "import fuse\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import awkward as ak\n", + "from tqdm import tqdm\n", + "import tqdm.notebook as tq\n", + "import pandas as pd\n", + "from sklearn.cluster import DBSCAN\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Warning! elife not in context config, skipping...\n", + "Warning! electron_drift_velocity not in context config, skipping...\n", + "Warning! electron_drift_time_gate not in context config, skipping...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + " warnings.warn(\n", + "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + " warnings.warn(\n", + "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + " warnings.warn(\n", + "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + " warnings.warn(\n", + "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + " warnings.warn(\n", + "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + " warnings.warn(\n" + ] + } + ], + "source": [ + "st = fuse.context.full_chain_context(output_folder = \"./fuse_data\")\n", + "st.set_config({\"path\": \"/project2/lgrandi/layos/\",\n", + " \"file_name\": \"output_n_Veto_neutron_AmBe_1.root\",\n", + " \"entry_stop\": 2000,\n", + " \"nv_output\": True,#On inclus les pmts du Neutron Veto (par defaut False)\n", + " \"debug\": True,\n", + " \"file_size_limit\": 500/1e4,#Poser question a Hening sur cela....\n", + " })\n", + "\n", + "run_number = \"00003\"#Attention à ce parametre, aujourd'hui triviale, mais qui tiens en compte du mapping pour la TPC en fonction du run." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:strax:Option gain_model_nv not taken by any registered plugin\n", + "WARNING:strax:Option gain_model_mv not taken by any registered plugin\n", + "WARNING:strax:Option gain_model_nv not taken by any registered plugin\n", + "WARNING:strax:Option gain_model_mv not taken by any registered plugin\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "5412e58e12c7457faaecdd7e46cf7ffe", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Loading nv_pmthits: | | 0.00 % [00:00)" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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0549e5jnCOjq6lIgEGCeI2TChAl6//33VVtbG9wuueQSTZ8+Pfhv5tke7e3t2r17twYNGmTG3/MpORU3BqxcudJyOp3W8uXLrY8++si66667rKSkJKulpSXapcW0/fv3W++995713nvvWZKsJ554wnrvvfeszz77zLIsy1q8eLGVlJRkvf7669bOnTutG264wcrMzLS+/vrrKFceO+bMmWO53W5r06ZNVnNzc3A7cOBAsM8999xjZWRkWJWVldb27dut3NxcKzc3N4pVx6YHH3zQqqqqshoaGqydO3daDz74oOVwOKx//etflmUxz3b59lU8lsU8R8ovfvELa9OmTVZDQ4P173//28rLy7NSUlKsvXv3WpYV/XkmoHzLM888Y2VkZFgJCQnWZZddZm3ZsiXaJcW8N99805LUbZsxY4ZlWd9cavzwww9baWlpltPptCZMmGDV1dVFt+gY09P8SrKWLVsW7PP1119bP//5z62zzz7b6t+/v/XjH//Yam5ujl7RMWrmzJnWkCFDrISEBGvgwIHWhAkTguHEsphnu/xvQGGeI+Pmm2+2Bg0aZCUkJFg/+MEPrJtvvtmqr68Ptkd7nh2WZVmnZq0GAADg++EcFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACM8/8BbptSjozjcEMAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.hist(events_nv['area'], range=[0,50],bins=20,alpha=0.3)\n", + "plt.hist(events_nv_stacket['area'], range=[0,50],bins=20,alpha=0.3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.19" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 5c08fbd9fdd4808aa946dbc4cf45025b1c89575a Mon Sep 17 00:00:00 2001 From: Layos Carlos Daniel Garcia <92477779+LayosDaniel@users.noreply.github.com> Date: Mon, 15 Apr 2024 16:58:05 +0200 Subject: [PATCH 12/29] Update nvhitlets.py --- fuse/plugins/neutron_veto/nvhitlets.py | 39 +++++++++++++++----------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/fuse/plugins/neutron_veto/nvhitlets.py b/fuse/plugins/neutron_veto/nvhitlets.py index 611f8cd5..bd58e2b4 100644 --- a/fuse/plugins/neutron_veto/nvhitlets.py +++ b/fuse/plugins/neutron_veto/nvhitlets.py @@ -40,17 +40,26 @@ def energytowavelenght(E): Joules_to_eV=1.602*1e-19 return 1e9*const.h*const.c/(E*Joules_to_eV) -#SPE parameters: ID, pe, SPE, acceptance, threshold_pe(based on acceptance) -def SPE_parameters(json_file_spe_model,file_spe_acceptance): - with open(json_file_spe_model,'r') as f: - data_spe = json.loads(f.read()) - with open(file_spe_acceptance) as f: - acceptance = json.load(f) - SPE_ch= pd.DataFrame(columns=['pmtID','pe','SPE','acceptance','threshold_pe']) - SPE_ch['pmtID'],SPE_ch['pe'], SPE_ch['SPE'],SPE_ch['acceptance']=data_spe['pmtID'],data_spe['charge'],data_spe['SPE_values'],acceptance['acceptance'] - acceptance_ch= [threshold_acc(SPE_ch,i) for i in np.arange(2000,2120)] - SPE_ch['threshold_pe']=acceptance_ch - return SPE_ch +def create_SPE_file(path,sr='0'): + #path to the aux_files + #SR : to change configuration for each SR + spe_df=pd.DataFrame(columns=['pmtID', 'pe', 'SPE_values','acceptance']) + array_x = np.load(path + 'x_data_sr'+sr+'.npy') + array_y = np.load(path+'sr'+sr+'_pdfs.npy') + spe_df['pmtID']= np.arange(2000,2120) + spe_df['pe']=array_x.tolist() + spe_df['SPE_values']=array_y.tolist() + spe_df['acceptance']= np.load(path+'spe_acc_sr'+sr+'.npy',allow_pickle=True) + return np.save(path+'SPE_SR'+sr+'.npy',spe_df.to_records()) + +#SPE parameters: ID, pe, SPE, acceptance +def SPE_parameters(file_spe_model): + data_spe=np.load(file_spe_model,allow_pickle=True) + #SPE_ch= pd.DataFrame(columns=['pmtID','pe','SPE','acceptance']) + #SPE_ch['pmtID'],SPE_ch['pe'], SPE_ch['SPE'],SPE_ch['acceptance']=data_spe['pmtID'],data_spe['charge'],data_spe['SPE_values'],data_spe['acceptance'] + #acceptance_ch= [threshold_acc(SPE_ch,i) for i in np.arange(2000,2120)] + #SPE_ch['threshold_pe']=acceptance_ch + return data_spe def threshold_acc(SPE_df, ID): SPE_ID=pd.DataFrame() @@ -217,7 +226,7 @@ class NeutronVetoHitlets(strax.Plugin): def __init__(self): self.path = '/home/layos/env_starter/fuse/private_nt_aux_files/sim_files/'#Have to put here the correct paths.... self.QE_value = QE_nVeto(self.path+'nveto_pmt_qe.json') - self.SPE_nVeto = SPE_parameters(self.path+'SPE_'+'SR1'+'_test_fuse.json',self.path+'acceptance_SR0_test_fuse.json') + self.SPE_nVeto = SPE_parameters(self.path+'SPE_SR0.npy') self.dtype=dtype #Get Quantum efficiency def QE_E(self,E,ID): @@ -227,8 +236,7 @@ def QE_E(self,E,ID): return qe def get_acceptance(self,ID): - ind=ID-2000 - acc= self.SPE_nVeto.acceptance.values[ind] + acc=self.SPE_nVeto[self.SPE_nVeto['pmtID']==ID]['acceptance'] return acc #Get acceptance threshold def get_threshold_acc(self,ID): @@ -239,7 +247,7 @@ def get_threshold_acc(self,ID): #Sampling charge from SPE def pe_charge_N(self,pmt_id): SPE_channel= self.SPE_nVeto[self.SPE_nVeto.pmtID==pmt_id] - charge=rd.choices(SPE_channel.pe.values[0],SPE_channel.SPE.values[0],k=1)[0] + charge=rd.choices(SPE_channel['pe'][0],SPE_channel['SPE_values'][0],k=1)[0] return charge #--------------------------- Hitlet function ------------------------------------------------------------# @@ -258,7 +266,6 @@ def _nv_hitlets(self,pmthits, CE_Scaling=0.75, Stacked='No', period = 1.): #0.---------------Load GEANT output-------------------# - #1. First step PHOTON to first dinode From 3bdfcdd8f9fb18b2fc72e36b2e15a1bbef3790ca Mon Sep 17 00:00:00 2001 From: Pietro Di Gangi Date: Tue, 28 Jan 2025 05:39:29 -0600 Subject: [PATCH 13/29] Added option to select different sciencerun configs (currently sr0, sr1) --- fuse/plugins/neutron_veto/nvhitlets.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/fuse/plugins/neutron_veto/nvhitlets.py b/fuse/plugins/neutron_veto/nvhitlets.py index bd58e2b4..cec13ce9 100644 --- a/fuse/plugins/neutron_veto/nvhitlets.py +++ b/fuse/plugins/neutron_veto/nvhitlets.py @@ -223,11 +223,12 @@ class NeutronVetoHitlets(strax.Plugin): default=True, type=bool, help='Set the random seed from lineage and run_id, or pull the seed from the OS.', ) - def __init__(self): - self.path = '/home/layos/env_starter/fuse/private_nt_aux_files/sim_files/'#Have to put here the correct paths.... + def __init__(self, sr=0): + self.path = '/home/digangi/private_nt_aux_files/sim_files/' #pietro #Have to put here the correct paths.... self.QE_value = QE_nVeto(self.path+'nveto_pmt_qe.json') - self.SPE_nVeto = SPE_parameters(self.path+'SPE_SR0.npy') + self.SPE_nVeto = SPE_parameters(self.path+'SPE_SR'+str(sr)+'.npy') #pietro self.dtype=dtype + #Get Quantum efficiency def QE_E(self,E,ID): WL= energytowavelenght(E) @@ -238,6 +239,7 @@ def QE_E(self,E,ID): def get_acceptance(self,ID): acc=self.SPE_nVeto[self.SPE_nVeto['pmtID']==ID]['acceptance'] return acc + #Get acceptance threshold def get_threshold_acc(self,ID): ind=ID-2000 @@ -269,19 +271,27 @@ def _nv_hitlets(self,pmthits, CE_Scaling=0.75, Stacked='No', period = 1.): #1. First step PHOTON to first dinode + # awkward array with 'evtid', 'pmthitTime', 'pmthitEnergy', 'pmthitID' pmthits=ak.Array(pmthits) + # select NV PMTs (need to exclude MV PMTs?) mask=pmthits['pmthitID']>=2000 pmthits=pmthits[mask] + print("Applying QE and CE") - qe = 1e-2*np.vectorize(self.QE_E)(pmthits['pmthitEnergy'],pmthits['pmthitID'])#Applying Quantum efficiency for each pmt - qe *= CE_Scaling #Applying collection efficiency - qe = qe*np.vectorize(self.get_acceptance)(pmthits['pmthitID'])#Applying acceptance: for the approach in which SPE PDF has already applied a threshold for low charges + # Applying Quantum efficiency for each pmt + qe = 1e-2*np.vectorize(self.QE_E)(pmthits['pmthitEnergy'],pmthits['pmthitID']) + # Applying effective collection efficiency + qe *= CE_Scaling + # Applying acceptance per pmt: for the approach in which SPE PDF has already applied a threshold for low charges + qe = qe*np.vectorize(self.get_acceptance)(pmthits['pmthitID']) + # Generate a photoelectron based on (binomial) conversion probability qe*eCE*spe_acc pe = np.array([np.random.binomial(1, j, 1)[0] for j in qe]) + # Discard pmthits which do not generate a pe print("Loading hit survive") maks_qe = pe>0 pmthits=pmthits[maks_qe] - #2. Sampling charge from SPE + #2. Sampling charge from SPE for each pmthit with a generated pe print("Sampling hitlets charge pe") pmthits['pe_area'] = np.vectorize(self.pe_charge_N)(pmthits['pmthitID']) dtypes=[] From 751c3768774a9610f62e90fda197c335c83a5041 Mon Sep 17 00:00:00 2001 From: Pietro Di Gangi Date: Mon, 10 Feb 2025 08:37:52 -0600 Subject: [PATCH 14/29] getting rid of awkward arrays and using only numpy arrays --- .../neutron_veto/Testing_nv_hitlet.ipynb | 588 +++++++++++++++++- fuse/plugins/neutron_veto/nvhitlets.py | 78 ++- 2 files changed, 602 insertions(+), 64 deletions(-) diff --git a/fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb b/fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb index 909dfa79..ae9c5466 100644 --- a/fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb +++ b/fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb @@ -31,12 +31,17 @@ "name": "stderr", "output_type": "stream", "text": [ - "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/straxen/url_config.py:743: UserWarning: From straxen version 2.1.0 onward, URLConfig parameterswill be sorted alphabetically before being passed to the plugins, this will change the lineage hash for non-sorted URLs. To load data processed with non-sorted URLs, you will need to use an older version.\n", + "/home/digangi/cutax/cutax/cut_lists/science.py:28: UserWarning: Removing cut \n", + " warnings.warn(f\"Removing cut {cut}\", UserWarning)\n", + "/home/digangi/.local/lib/python3.9/site-packages/straxen/config/preprocessors.py:16: UserWarning: From straxen version 2.1.0 onward, URLConfig parameterswill be sorted alphabetically before being passed to the plugins, this will change the lineage hash for non-sorted URLs. To load data processed with non-sorted URLs, you will need to use an older version.\n", " warnings.warn(\n" ] } ], "source": [ + "import sys\n", + "sys.path.insert(0, '/home/digangi/cutax/') #on midway3\n", + "\n", "import fuse\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", @@ -65,17 +70,17 @@ "name": "stderr", "output_type": "stream", "text": [ - "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + "/home/digangi/.local/lib/python3.9/site-packages/strax/context.py:380: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", " warnings.warn(\n", - "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + "/home/digangi/.local/lib/python3.9/site-packages/strax/context.py:380: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", " warnings.warn(\n", - "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + "/home/digangi/.local/lib/python3.9/site-packages/strax/context.py:380: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", " warnings.warn(\n", - "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + "/home/digangi/.local/lib/python3.9/site-packages/strax/context.py:380: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", " warnings.warn(\n", - "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + "/home/digangi/.local/lib/python3.9/site-packages/strax/context.py:380: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", " warnings.warn(\n", - "/opt/XENONnT/anaconda/envs/XENONnT_development/lib/python3.9/site-packages/strax/context.py:349: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + "/home/digangi/.local/lib/python3.9/site-packages/strax/context.py:380: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", " warnings.warn(\n" ] } @@ -85,9 +90,58 @@ "st.set_config({\"path\": \"/project2/lgrandi/layos/\",\n", " \"file_name\": \"output_n_Veto_neutron_AmBe_1.root\",\n", " \"entry_stop\": 2000,\n", - " \"nv_output\": True,#On inclus les pmts du Neutron Veto (par defaut False)\n", + " \"nv_output\": True, #On inclus les pmts du Neutron Veto (par defaut False)\n", " \"debug\": True,\n", - " \"file_size_limit\": 500/1e4,#Poser question a Hening sur cela....\n", + " \"file_size_limit\": 500/1e4, #Poser question a Hening sur cela....\n", + " })\n", + "\n", + "run_number = \"00003\"#Attention à ce parametre, aujourd'hui triviale, mais qui tiens en compte du mapping pour la TPC en fonction du run." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Warning! elife not in context config, skipping...\n", + "Warning! electron_drift_velocity not in context config, skipping...\n", + "Warning! electron_drift_time_gate not in context config, skipping...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/digangi/.local/lib/python3.9/site-packages/strax/context.py:380: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + " warnings.warn(\n", + "/home/digangi/.local/lib/python3.9/site-packages/strax/context.py:380: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + " warnings.warn(\n", + "/home/digangi/.local/lib/python3.9/site-packages/strax/context.py:380: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + " warnings.warn(\n", + "/home/digangi/.local/lib/python3.9/site-packages/strax/context.py:380: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + " warnings.warn(\n", + "/home/digangi/.local/lib/python3.9/site-packages/strax/context.py:380: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + " warnings.warn(\n", + "/home/digangi/.local/lib/python3.9/site-packages/strax/context.py:380: UserWarning: Provides of multi-output plugins overlap, deregister old plugins .\n", + " warnings.warn(\n" + ] + } + ], + "source": [ + "g4dir = \"/project/lgrandi/xenonnt/simulations/nveto/ambe/geant4/ambe_nveto_gd0p02_2024-01-23/gd_ambe_sr1_top_cw7d8m_with_gamma/results/\"\n", + "filename = \"nT_mc_gdt7p8m_wc_00000.root\"\n", + "\n", + "st = fuse.context.full_chain_context(output_folder = \"./fuse_data\")\n", + "st.set_config({\"path\": g4dir,\n", + " \"file_name\": filename,\n", + " \"entry_stop\": 2000,\n", + " \"nv_output\": True, #On inclus les pmts du Neutron Veto (par defaut False)\n", + " \"debug\": True,\n", + " \"file_size_limit\": 500/1e4, #Poser question a Hening sur cela....\n", " })\n", "\n", "run_number = \"00003\"#Attention à ce parametre, aujourd'hui triviale, mais qui tiens en compte du mapping pour la TPC en fonction du run." @@ -97,6 +151,20 @@ "cell_type": "code", "execution_count": 3, "metadata": {}, + "outputs": [], + "source": [ + "st.set_config(\n", + " {\n", + " \"deteministic_seed\": False,\n", + " \"user_defined_random_seed\": 42\n", + " }\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, "outputs": [ { "name": "stderr", @@ -104,14 +172,18 @@ "text": [ "WARNING:strax:Option gain_model_nv not taken by any registered plugin\n", "WARNING:strax:Option gain_model_mv not taken by any registered plugin\n", + "WARNING:strax:Option deteministic_seed not taken by any registered plugin\n", + "WARNING:strax:Option user_defined_random_seed not taken by any registered plugin\n", "WARNING:strax:Option gain_model_nv not taken by any registered plugin\n", - "WARNING:strax:Option gain_model_mv not taken by any registered plugin\n" + "WARNING:strax:Option gain_model_mv not taken by any registered plugin\n", + "WARNING:strax:Option deteministic_seed not taken by any registered plugin\n", + "WARNING:strax:Option user_defined_random_seed not taken by any registered plugin\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "5412e58e12c7457faaecdd7e46cf7ffe", + "model_id": "216ccda11e494bbaa5c22626763cd7ac", "version_major": 2, "version_minor": 0 }, @@ -129,16 +201,47 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "391" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "nv_hitlet = fuse.neutron_veto.NeutronVetoHitlets()" + "len(np.unique(nv_pmthits['evtid']))" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "179349" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(nv_pmthits)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -153,12 +256,20 @@ } ], "source": [ - "hitlets_nv=nv_hitlet.compute(nv_pmthits)" + "#sr=1\n", + "#eCE=0.87\n", + "\n", + "sr=0\n", + "eCE=0.75\n", + "\n", + "nv_hitlet = fuse.neutron_veto.NeutronVetoHitlets(sr=sr)\n", + "\n", + "hitlets_nv=nv_hitlet.compute(nv_pmthits, eCE=eCE)" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -169,18 +280,18 @@ "Loading hit survive\n", "Sampling hitlets charge pe\n", "Getting time hitlets\n", - "Looking for stacket hitlets\n" + "Looking for stacked hitlets\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "3cefca70540547c59fc4454c87355865", + "model_id": "ede11f11374542d194be21852952061d", "version_major": 2, "version_minor": 0 }, "text/plain": [ - " 0%| | 0/1824 [00:00" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.hist(events_nv['area'], range=[0,200],bins=20,alpha=0.3, label ='events_nv')\n", + "plt.hist(events_nv_stacket['area'], range=[0,200],bins=20,alpha=0.3, label = 'events_nv_stacked')\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Output saved to file: /scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr0config_seed42_nparraynT_mc_gdt7p8m_wc_00000.root\n" + ] + } + ], + "source": [ + "output_file = '/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/' + 'fuseHitSim_sr' + str(sr) + 'config_seed42_nparray' + filename\n", + "np.save(output_file, events_nv)\n", + "print('Output saved to file: ', output_file)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "code", "execution_count": 12, @@ -221,22 +435,134 @@ { "data": { "text/plain": [ - "(array([ 4., 61., 90., 127., 164., 201., 200., 179., 173., 148., 126.,\n", - " 94., 61., 54., 41., 9., 10., 6., 4., 3.]),\n", - " array([ 0. , 2.5, 5. , 7.5, 10. , 12.5, 15. , 17.5, 20. , 22.5, 25. ,\n", - " 27.5, 30. , 32.5, 35. , 37.5, 40. , 42.5, 45. , 47.5, 50. ]),\n", - " )" + "array([(4.1504655, 3.7096519e+01, 2006, 0, 776003102, 776003102),\n", + " (7.0233135, 1.2525828e+05, 123, 0, 776128323, 776128323),\n", + " (7.020823 , 1.2526002e+05, 300, 0, 776128325, 776128325),\n", + " ...,\n", + " (4.4297585, 1.4902604e+13, 2111, 370, 15282534046465, 15282534046465),\n", + " (2.2376018, 1.4902604e+13, 2035, 370, 15282534046465, 15282534046465),\n", + " (3.5634096, 1.4902604e+13, 2103, 370, 15282534046465, 15282534046465)],\n", + " dtype=[('pmthitEnergy', '[{pmthitEnergy: 4.15, pmthitTime: 37.1, pmthitID: 2006, evtid: 0, ...},\n", + " {pmthitEnergy: 7.02, pmthitTime: 1.25e+05, pmthitID: 123, evtid: 0, ...},\n", + " {pmthitEnergy: 7.02, pmthitTime: 1.25e+05, pmthitID: 300, evtid: 0, ...},\n", + " {pmthitEnergy: 6.95, pmthitTime: 1.25e+05, pmthitID: 465, evtid: 0, ...},\n", + " {pmthitEnergy: 6.95, pmthitTime: 1.25e+05, pmthitID: 87, evtid: 0, ...},\n", + " {pmthitEnergy: 6.96, pmthitTime: 1.25e+05, pmthitID: 392, evtid: 0, ...},\n", + " {pmthitEnergy: 6.98, pmthitTime: 1.25e+05, pmthitID: 54, evtid: 0, ...},\n", + " {pmthitEnergy: 6.99, pmthitTime: 1.25e+05, pmthitID: 465, evtid: 0, ...},\n", + " {pmthitEnergy: 6.93, pmthitTime: 1.25e+05, pmthitID: 365, evtid: 0, ...},\n", + " {pmthitEnergy: 7.02, pmthitTime: 1.25e+05, pmthitID: 54, evtid: 0, ...},\n", + " ...,\n", + " {pmthitEnergy: 3.22, pmthitTime: 1.49e+13, pmthitID: 2113, evtid: 370, ...},\n", + " {pmthitEnergy: 2.13, pmthitTime: 1.49e+13, pmthitID: 2005, evtid: 370, ...},\n", + " {pmthitEnergy: 4.97, pmthitTime: 1.49e+13, pmthitID: 2112, evtid: 370, ...},\n", + " {pmthitEnergy: 2.24, pmthitTime: 1.49e+13, pmthitID: 2113, evtid: 370, ...},\n", + " {pmthitEnergy: 3.32, pmthitTime: 1.49e+13, pmthitID: 2035, evtid: 370, ...},\n", + " {pmthitEnergy: 4.66, pmthitTime: 1.49e+13, pmthitID: 2005, evtid: 370, ...},\n", + " {pmthitEnergy: 4.43, pmthitTime: 1.49e+13, pmthitID: 2111, evtid: 370, ...},\n", + " {pmthitEnergy: 2.24, pmthitTime: 1.49e+13, pmthitID: 2035, evtid: 370, ...},\n", + " {pmthitEnergy: 3.56, pmthitTime: 1.49e+13, pmthitID: 2103, evtid: 370, ...}]\n", + "-----------------------------------------------------------------------------\n", + "type: 179349 * {\n", + " pmthitEnergy: float32,\n", + " pmthitTime: float32,\n", + " pmthitID: int16,\n", + " evtid: int32,\n", + " time: int64,\n", + " endtime: int64\n", + "}" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ak_pmthits=ak.Array(nv_pmthits)\n", + "ak_pmthits" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "179349\n", + "177403\n" + ] + } + ], + "source": [ + "# awkward array with 'evtid', 'pmthitTime', 'pmthitEnergy', 'pmthitID'\n", + "#pmthits=ak.Array(nv_pmthits)\n", + "pmthits=nv_pmthits\n", + "print(len(pmthits))\n", + "# select NV PMTs (need to exclude MV PMTs?)\n", + "mask=pmthits['pmthitID']>=2000\n", + "pmthits=pmthits[mask]\n", + "print(len(pmthits))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Applying QE and CE\n", + "Loading hit survive\n", + "Sampling hitlets charge pe\n", + "Getting time hitlets\n", + "Looking for stacket hitlets\n" + ] }, { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAGdCAYAAAA44ojeAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/H5lhTAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAkwklEQVR4nO3de3DU1f3/8deGmAU0uzGEZNmSQLyBiAkXNWa8gaSEYFFLbBXRovBDpUElaSumo0BsZ8KoVatVaKcKdiqidAQrHWgDkaA1IAQzeM0QJkqcXFBpdkmQJZDP7w+/bN0moAv7Yc+G52PmM8Pnc86efe+Z6L7m7OfisCzLEgAAgEHiol0AAADA/yKgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMEx/tAk5EV1eXmpqalJiYKIfDEe1yAADA92BZlvbv3y+v16u4uOOvkcRkQGlqalJ6enq0ywAAACegsbFRgwcPPm6fmAwoiYmJkr75gC6XK8rVAACA78Pv9ys9PT34PX48MRlQjv6s43K5CCgAAMSY73N6BifJAgAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABgnPtoFALFkw0ettoybNyLNlnEBIFaxggIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGCSuglJeX69JLL1ViYqJSU1N14403qq6uLqTPwYMHVVRUpAEDBuiss85SYWGhWltbQ/rs2bNH1113nfr376/U1FT96le/0uHDh0/+0wAAgF4hrIBSVVWloqIibdmyRRUVFers7NTEiRPV0dER7FNcXKw33nhDq1atUlVVlZqamjR16tRg+5EjR3Tdddfp0KFDeuedd/Tiiy9q+fLlWrBgQeQ+FQAAiGkOy7KsE33xF198odTUVFVVVenqq6+Wz+fTwIEDtWLFCt10002SpE8++UQXXnihqqurdfnll2vdunX60Y9+pKamJqWlpUmSli5dqvnz5+uLL75QQkLCd76v3++X2+2Wz+eTy+U60fKBsG34qPW7O52AvBFptowLACYJ5/s7/mTeyOfzSZKSk5MlSTU1Ners7FReXl6wz/Dhw5WRkREMKNXV1br44ouD4USS8vPzNWfOHH344YcaPXp0t/cJBAIKBAIhHxA4prp1Ng4+xsaxAQBHnXBA6erq0rx583TFFVdo5MiRkqSWlhYlJCQoKSkppG9aWppaWlqCfb4dTo62H23rSXl5ucrKyk60VCBiUpoq7Rl4xDR7xgWAGHXCV/EUFRXpgw8+0MqVKyNZT49KS0vl8/mCW2Njo+3vCQAAoueEVlDmzp2rtWvXavPmzRo8eHDwuMfj0aFDh9TW1hayitLa2iqPxxPs8+6774aMd/Qqn6N9/pfT6ZTT6TyRUgEAQAwKawXFsizNnTtXq1evVmVlpTIzM0Pax44dqzPOOEMbN24MHqurq9OePXuUm5srScrNzdX777+vvXv3BvtUVFTI5XJpxIgRJ/NZAABALxHWCkpRUZFWrFih119/XYmJicFzRtxut/r16ye3261Zs2appKREycnJcrlcuvfee5Wbm6vLL79ckjRx4kSNGDFCt99+ux599FG1tLTooYceUlFREaskAABAUpgBZcmSJZKkcePGhRxftmyZ7rjjDknSk08+qbi4OBUWFioQCCg/P1/PPfdcsG+fPn20du1azZkzR7m5uTrzzDM1Y8YMPfLIIyf3SQAAQK9xUvdBiRbug4LjsvEy49rGNlvGHZXHVTwAer9wvr95Fg8AADAOAQUAABjnpO4kCyAy7LqFvsRt9AHEJlZQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjcKM2oJfjJnAAYhErKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxuEyY/Q6tY1t0S4BAHCSWEEBAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMbhTrKAAVKaKm0b+0vvtbaNDQB2YQUFAAAYJ+yAsnnzZk2ZMkVer1cOh0Nr1qwJaXc4HD1ujz32WLDP0KFDu7UvXrz4pD8MAADoHcIOKB0dHcrOztazzz7bY3tzc3PI9sILL8jhcKiwsDCk3yOPPBLS79577z2xTwAAAHqdsM9BKSgoUEFBwTHbPR5PyP7rr7+u8ePH65xzzgk5npiY2K0vAACAZPM5KK2trfrHP/6hWbNmdWtbvHixBgwYoNGjR+uxxx7T4cOHjzlOIBCQ3+8P2QAAQO9l61U8L774ohITEzV16tSQ4/fdd5/GjBmj5ORkvfPOOyotLVVzc7OeeOKJHscpLy9XWVmZnaUCAACD2BpQXnjhBU2fPl19+/YNOV5SUhL8d1ZWlhISEnT33XervLxcTqez2zilpaUhr/H7/UpPT7evcAAAEFW2BZS33npLdXV1euWVV76zb05Ojg4fPqxPP/1Uw4YN69budDp7DC4AAKB3su0clOeff15jx45Vdnb2d/atra1VXFycUlNT7SoHAADEkLBXUNrb21VfXx/cb2hoUG1trZKTk5WRkSHpm59gVq1apd/97nfdXl9dXa2tW7dq/PjxSkxMVHV1tYqLi3Xbbbfp7LPPPomPAqAndt6lViOm2Tc2gNNa2AFl+/btGj9+fHD/6LkhM2bM0PLlyyVJK1eulGVZmjat+/+8nE6nVq5cqUWLFikQCCgzM1PFxcUh55gAAIDTm8OyLCvaRYTL7/fL7XbL5/PJ5XJFuxwYpnbDy9Eu4bQxKo8VFADfXzjf3zyLBwAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjhP00YyBi6tZFuwIAgKFYQQEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGCTugbN68WVOmTJHX65XD4dCaNWtC2u+44w45HI6QbdKkSSF99u3bp+nTp8vlcikpKUmzZs1Se3v7SX0QAADQe8SH+4KOjg5lZ2dr5syZmjp1ao99Jk2apGXLlgX3nU5nSPv06dPV3NysiooKdXZ26s4779Rdd92lFStWhFsOYlhtY1u0SwAAGCrsgFJQUKCCgoLj9nE6nfJ4PD22ffzxx1q/fr22bdumSy65RJL0zDPPaPLkyXr88cfl9XrDLQkAAPQytpyDsmnTJqWmpmrYsGGaM2eOvvrqq2BbdXW1kpKSguFEkvLy8hQXF6etW7faUQ4AAIgxYa+gfJdJkyZp6tSpyszM1O7du/XrX/9aBQUFqq6uVp8+fdTS0qLU1NTQIuLjlZycrJaWlh7HDAQCCgQCwX2/3x/psgEAgEEiHlBuueWW4L8vvvhiZWVl6dxzz9WmTZs0YcKEExqzvLxcZWVlkSoRAAAYzvbLjM855xylpKSovr5ekuTxeLR3796QPocPH9a+ffuOed5KaWmpfD5fcGtsbLS7bAAAEEW2B5TPP/9cX331lQYNGiRJys3NVVtbm2pqaoJ9Kisr1dXVpZycnB7HcDqdcrlcIRsAAOi9wv6Jp729PbgaIkkNDQ2qra1VcnKykpOTVVZWpsLCQnk8Hu3evVsPPPCAzjvvPOXn50uSLrzwQk2aNEmzZ8/W0qVL1dnZqblz5+qWW27hCh4g1tSts2fcYce/UhBA7xf2Csr27ds1evRojR49WpJUUlKi0aNHa8GCBerTp4927typ66+/XhdccIFmzZqlsWPH6q233gq5F8pLL72k4cOHa8KECZo8ebKuvPJK/elPf4rcpwIAADEt7BWUcePGybKsY7b/85///M4xkpOTuSkbAAA4Jp7FAwAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGCftW9wBwVG1jmy3jjhpmy7AAYggrKAAAwDgEFAAAYBx+4gFgnA0ftdo2dt6INNvGBhA5rKAAAADjEFAAAIBxCCgAAMA4nIOC47LzXIAU20YGAMQ6VlAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAME7YAWXz5s2aMmWKvF6vHA6H1qxZE2zr7OzU/PnzdfHFF+vMM8+U1+vVz372MzU1NYWMMXToUDkcjpBt8eLFJ/1hAABA7xD204w7OjqUnZ2tmTNnaurUqSFtBw4c0I4dO/Twww8rOztb//nPf3T//ffr+uuv1/bt20P6PvLII5o9e3ZwPzEx8QQ/AuyU0lQZ7RIAAKehsANKQUGBCgoKemxzu92qqKgIOfaHP/xBl112mfbs2aOMjIzg8cTERHk8nnDfHgAAnAbCDijh8vl8cjgcSkpKCjm+ePFi/eY3v1FGRoZuvfVWFRcXKz7e9nIAxABbV+5GTLNvbAARY2siOHjwoObPn69p06bJ5XIFj993330aM2aMkpOT9c4776i0tFTNzc164oknehwnEAgoEAgE9/1+v51lAwCAKLMtoHR2duqnP/2pLMvSkiVLQtpKSkqC/87KylJCQoLuvvtulZeXy+l0dhurvLxcZWVldpUKAAAMY8tlxkfDyWeffaaKioqQ1ZOe5OTk6PDhw/r00097bC8tLZXP5wtujY2NNlQNAABMEfEVlKPhZNeuXXrzzTc1YMCA73xNbW2t4uLilJqa2mO70+nscWUFAAD0TmEHlPb2dtXX1wf3GxoaVFtbq+TkZA0aNEg33XSTduzYobVr1+rIkSNqaWmRJCUnJyshIUHV1dXaunWrxo8fr8TERFVXV6u4uFi33Xabzj777Mh9MgAAELPCDijbt2/X+PHjg/tHzyeZMWOGFi1apL///e+SpFGjRoW87s0339S4cePkdDq1cuVKLVq0SIFAQJmZmSouLg45LwUAAJzewg4o48aNk2VZx2w/XpskjRkzRlu2bAn3bQEAwGmEZ/EAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIwTdkDZvHmzpkyZIq/XK4fDoTVr1oS0W5alBQsWaNCgQerXr5/y8vK0a9eukD779u3T9OnT5XK5lJSUpFmzZqm9vf2kPggAAOg94sN9QUdHh7KzszVz5kxNnTq1W/ujjz6qp59+Wi+++KIyMzP18MMPKz8/Xx999JH69u0rSZo+fbqam5tVUVGhzs5O3Xnnnbrrrru0YsWKk/9Ep6kNH7XaMm6KLaMCAHB8YQeUgoICFRQU9NhmWZaeeuopPfTQQ7rhhhskSX/5y1+UlpamNWvW6JZbbtHHH3+s9evXa9u2bbrkkkskSc8884wmT56sxx9/XF6v9yQ+DgAA6A0ieg5KQ0ODWlpalJeXFzzmdruVk5Oj6upqSVJ1dbWSkpKC4USS8vLyFBcXp61bt/Y4biAQkN/vD9kAAEDvFdGA0tLSIklKS0sLOZ6WlhZsa2lpUWpqakh7fHy8kpOTg33+V3l5udxud3BLT0+PZNkAAMAwMXEVT2lpqXw+X3BrbGyMdkkAAMBGEQ0oHo9HktTaGnrCZmtra7DN4/Fo7969Ie2HDx/Wvn37gn3+l9PplMvlCtkAAEDvFdGAkpmZKY/Ho40bNwaP+f1+bd26Vbm5uZKk3NxctbW1qaamJtinsrJSXV1dysnJiWQ5AAAgRoV9FU97e7vq6+uD+w0NDaqtrVVycrIyMjI0b948/fa3v9X5558fvMzY6/XqxhtvlCRdeOGFmjRpkmbPnq2lS5eqs7NTc+fO1S233MIVPAAAQNIJBJTt27dr/Pjxwf2SkhJJ0owZM7R8+XI98MAD6ujo0F133aW2tjZdeeWVWr9+ffAeKJL00ksvae7cuZowYYLi4uJUWFiop59+OgIfBwAA9AYOy7KsaBcRLr/fL7fbLZ/Px/ko/8e2G7U1VdoyLhAto/KmRbsE4LQVzvd3TFzFAwAATi8EFAAAYBwCCgAAME7YJ8kCQEyrW2fPuMN6fkYZgBPDCgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAONwFU8vwR1fAQC9CSsoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADG4TJjAKeV2sY2W8YdNcyWYYHTFisoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIwT8YAydOhQORyObltRUZEkady4cd3a7rnnnkiXAQAAYlh8pAfctm2bjhw5Etz/4IMP9MMf/lA/+clPgsdmz56tRx55JLjfv3//SJcBAABiWMQDysCBA0P2Fy9erHPPPVfXXHNN8Fj//v3l8Xgi/dYAAKCXsPUclEOHDumvf/2rZs6cKYfDETz+0ksvKSUlRSNHjlRpaakOHDhw3HECgYD8fn/IBgAAeq+Ir6B825o1a9TW1qY77rgjeOzWW2/VkCFD5PV6tXPnTs2fP191dXV67bXXjjlOeXm5ysrK7CwVAAAYxGFZlmXX4Pn5+UpISNAbb7xxzD6VlZWaMGGC6uvrde655/bYJxAIKBAIBPf9fr/S09Pl8/nkcrkiXncsqt3wcrRLAE5ro/KmRbsEwHh+v19ut/t7fX/btoLy2WefacOGDcddGZGknJwcSTpuQHE6nXI6nRGvEQAAmMm2c1CWLVum1NRUXXfddcftV1tbK0kaNGiQXaUAAIAYY8sKSldXl5YtW6YZM2YoPv6/b7F7926tWLFCkydP1oABA7Rz504VFxfr6quvVlZWlh2lAACAGGRLQNmwYYP27NmjmTNnhhxPSEjQhg0b9NRTT6mjo0Pp6ekqLCzUQw89ZEcZAAAgRtkSUCZOnKiezr1NT09XVVWVHW8JAAB6EZ7FAwAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMY9vTjAHgdLLho1bbxs4bkWbb2ICpWEEBAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOlxmfSnXrol0BAJukNFXaN/iIafaNDRiKFRQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA63ugcA09n1mIxhBfaMC0QAKygAAMA4BBQAAGCciAeURYsWyeFwhGzDhw8Pth88eFBFRUUaMGCAzjrrLBUWFqq1tTXSZQAAgBhmywrKRRddpObm5uD29ttvB9uKi4v1xhtvaNWqVaqqqlJTU5OmTp1qRxkAACBG2XKSbHx8vDweT7fjPp9Pzz//vFasWKFrr71WkrRs2TJdeOGF2rJliy6//HI7ygEAADHGlhWUXbt2yev16pxzztH06dO1Z88eSVJNTY06OzuVl5cX7Dt8+HBlZGSourr6mOMFAgH5/f6QDQAA9F4RDyg5OTlavny51q9fryVLlqihoUFXXXWV9u/fr5aWFiUkJCgpKSnkNWlpaWppaTnmmOXl5XK73cEtPT090mUDAACDRPwnnoKC/15Xn5WVpZycHA0ZMkSvvvqq+vXrd0JjlpaWqqSkJLjv9/sJKQAA9GK2X2aclJSkCy64QPX19fJ4PDp06JDa2tpC+rS2tvZ4zspRTqdTLpcrZAMAAL2X7XeSbW9v1+7du3X77bdr7NixOuOMM7Rx40YVFhZKkurq6rRnzx7l5ubaXUrU1Ta2RbsEAABiQsQDyi9/+UtNmTJFQ4YMUVNTkxYuXKg+ffpo2rRpcrvdmjVrlkpKSpScnCyXy6V7771Xubm5XMEDAACCIh5QPv/8c02bNk1fffWVBg4cqCuvvFJbtmzRwIEDJUlPPvmk4uLiVFhYqEAgoPz8fD333HORLgMAAMQwh2VZVrSLCJff75fb7ZbP54up81FqN7wc7RIAxKBR6Un2DMzDAnGKhfP9zbN4AACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADj2P4sHgCAoerW2Tc2N4HDSWIFBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYhzvJAoDhahvbbBl3VHqSLeMCkcAKCgAAMA4BBQAAGIeAAgAAjENAAQAAxuEkWQA4Tdl18q0kjRpm29A4TbCCAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgnIgHlPLycl166aVKTExUamqqbrzxRtXV1YX0GTdunBwOR8h2zz33RLoUAAAQoyIeUKqqqlRUVKQtW7aooqJCnZ2dmjhxojo6OkL6zZ49W83NzcHt0UcfjXQpAAAgRkX8Rm3r168P2V++fLlSU1NVU1Ojq6++Oni8f//+8ng8kX57AADQC9h+DorP55MkJScnhxx/6aWXlJKSopEjR6q0tFQHDhw45hiBQEB+vz9kAwAAvZett7rv6urSvHnzdMUVV2jkyJHB47feequGDBkir9ernTt3av78+aqrq9Nrr73W4zjl5eUqKyuzs1QAAGAQh2VZll2Dz5kzR+vWrdPbb7+twYMHH7NfZWWlJkyYoPr6ep177rnd2gOBgAKBQHDf7/crPT1dPp9PLpfLltrtULvh5WiXAACnxKi8adEuAQby+/1yu93f6/vbthWUuXPnau3atdq8efNxw4kk5eTkSNIxA4rT6ZTT6bSlTgAAYJ6IBxTLsnTvvfdq9erV2rRpkzIzM7/zNbW1tZKkQYMGRbocAAAQgyIeUIqKirRixQq9/vrrSkxMVEtLiyTJ7XarX79+2r17t1asWKHJkydrwIAB2rlzp4qLi3X11VcrKysr0uUAAIAYFPGAsmTJEknf3Izt25YtW6Y77rhDCQkJ2rBhg5566il1dHQoPT1dhYWFeuihhyJdCgAAiFG2/MRzPOnp6aqqqor02wIAgF6EZ/EAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAME7En2YMAIBdNnzUatvYeSPSbBsb4WMFBQAAGIcVFABAxNm50oHTAysoAADAOAQUAABgHH7iAQDEjJSmSvsGHzHNvrERNgIKACDibA0SOC3wEw8AADAOAQUAABiHgAIAAIxDQAEAAMbhJFkAACSpbp0tw244MsaWcaXefXt+VlAAAIBxWEEBAEBSbWObPQN77Rm2t2MFBQAAGIcVlJ7Y9DskAOD0w91vTwwBpQe2LfMBABBBtRtetm3sUXnRDT9R/Ynn2Wef1dChQ9W3b1/l5OTo3XffjWY5AADAEFELKK+88opKSkq0cOFC7dixQ9nZ2crPz9fevXujVRIAADBE1ALKE088odmzZ+vOO+/UiBEjtHTpUvXv318vvPBCtEoCAACGiMo5KIcOHVJNTY1KS0uDx+Li4pSXl6fq6upu/QOBgAKBQHDf5/NJkvx+vy31tXccsGVcAABihR3fsUfHtCzrO/tGJaB8+eWXOnLkiNLSQu+Al5aWpk8++aRb//LycpWVlXU7np6ebluNAACc3v6fbSPv379fbrf7uH1i4iqe0tJSlZSUBPe7urq0b98+DRgwQA6HI6Lv5ff7lZ6ersbGRrlcroiOjf9ink8N5vnUYJ5PDeb51LFrri3L0v79++X1fvfd66ISUFJSUtSnTx+1traGHG9tbZXH4+nW3+l0yul0hhxLSkqys0S5XC7+AzgFmOdTg3k+NZjnU4N5PnXsmOvvWjk5KionySYkJGjs2LHauHFj8FhXV5c2btyo3NzcaJQEAAAMErWfeEpKSjRjxgxdcskluuyyy/TUU0+po6NDd955Z7RKAgAAhohaQLn55pv1xRdfaMGCBWppadGoUaO0fv36bifOnmpOp1MLFy7s9pMSIot5PjWY51ODeT41mOdTx4S5dljf51ofAACAU4inGQMAAOMQUAAAgHEIKAAAwDgEFAAAYBwCyrc8++yzGjp0qPr27aucnBy9++670S4p5m3evFlTpkyR1+uVw+HQmjVrQtoty9KCBQs0aNAg9evXT3l5edq1a1d0io1R5eXluvTSS5WYmKjU1FTdeOONqqurC+lz8OBBFRUVacCAATrrrLNUWFjY7UaJ+G5LlixRVlZW8OZVubm5WrduXbCdeY68xYsXy+FwaN68ecFjzHNkLFq0SA6HI2QbPnx4sD3a80xA+T+vvPKKSkpKtHDhQu3YsUPZ2dnKz8/X3r17o11aTOvo6FB2draeffbZHtsfffRRPf3001q6dKm2bt2qM888U/n5+Tp48OAprjR2VVVVqaioSFu2bFFFRYU6Ozs1ceJEdXR0BPsUFxfrjTfe0KpVq1RVVaWmpiZNnTo1ilXHpsGDB2vx4sWqqanR9u3bde211+qGG27Qhx9+KIl5jrRt27bpj3/8o7KyskKOM8+Rc9FFF6m5uTm4vf3228G2qM+zBcuyLOuyyy6zioqKgvtHjhyxvF6vVV5eHsWqehdJ1urVq4P7XV1dlsfjsR577LHgsba2NsvpdFovv/xyFCrsHfbu3WtJsqqqqizL+mZOzzjjDGvVqlXBPh9//LElyaquro5Wmb3G2Wefbf35z39mniNs//791vnnn29VVFRY11xzjXX//fdblsXfcyQtXLjQys7O7rHNhHlmBUXSoUOHVFNTo7y8vOCxuLg45eXlqbq6OoqV9W4NDQ1qaWkJmXe3262cnBzm/ST4fD5JUnJysiSppqZGnZ2dIfM8fPhwZWRkMM8n4ciRI1q5cqU6OjqUm5vLPEdYUVGRrrvuupD5lPh7jrRdu3bJ6/XqnHPO0fTp07Vnzx5JZsxzTDzN2G5ffvmljhw50u0utmlpafrkk0+iVFXv19LSIkk9zvvRNoSnq6tL8+bN0xVXXKGRI0dK+maeExISuj1gk3k+Me+//75yc3N18OBBnXXWWVq9erVGjBih2tpa5jlCVq5cqR07dmjbtm3d2vh7jpycnBwtX75cw4YNU3Nzs8rKynTVVVfpgw8+MGKeCShAL1JUVKQPPvgg5HdkRNawYcNUW1srn8+nv/3tb5oxY4aqqqqiXVav0djYqPvvv18VFRXq27dvtMvp1QoKCoL/zsrKUk5OjoYMGaJXX31V/fr1i2Jl3+AnHkkpKSnq06dPt7OTW1tb5fF4olRV73d0bpn3yJg7d67Wrl2rN998U4MHDw4e93g8OnTokNra2kL6M88nJiEhQeedd57Gjh2r8vJyZWdn6/e//z3zHCE1NTXau3evxowZo/j4eMXHx6uqqkpPP/204uPjlZaWxjzbJCkpSRdccIHq6+uN+HsmoOib/+GMHTtWGzduDB7r6urSxo0blZubG8XKerfMzEx5PJ6Qeff7/dq6dSvzHgbLsjR37lytXr1alZWVyszMDGkfO3aszjjjjJB5rqur0549e5jnCOjq6lIgEGCeI2TChAl6//33VVtbG9wuueQSTZ8+Pfhv5tke7e3t2r17twYNGmTG3/MpORU3BqxcudJyOp3W8uXLrY8++si66667rKSkJKulpSXapcW0/fv3W++995713nvvWZKsJ554wnrvvfeszz77zLIsy1q8eLGVlJRkvf7669bOnTutG264wcrMzLS+/vrrKFceO+bMmWO53W5r06ZNVnNzc3A7cOBAsM8999xjZWRkWJWVldb27dut3NxcKzc3N4pVx6YHH3zQqqqqshoaGqydO3daDz74oOVwOKx//etflmUxz3b59lU8lsU8R8ovfvELa9OmTVZDQ4P173//28rLy7NSUlKsvXv3WpYV/XkmoHzLM888Y2VkZFgJCQnWZZddZm3ZsiXaJcW8N99805LUbZsxY4ZlWd9cavzwww9baWlpltPptCZMmGDV1dVFt+gY09P8SrKWLVsW7PP1119bP//5z62zzz7b6t+/v/XjH//Yam5ujl7RMWrmzJnWkCFDrISEBGvgwIHWhAkTguHEsphnu/xvQGGeI+Pmm2+2Bg0aZCUkJFg/+MEPrJtvvtmqr68Ptkd7nh2WZVmnZq0GAADg++EcFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACM8/8BbptSjozjcEMAAAAASUVORK5CYII=", + "application/vnd.jupyter.widget-view+json": { + "model_id": "3cefca70540547c59fc4454c87355865", + "version_major": 2, + "version_minor": 0 + }, "text/plain": [ - "
" + " 0%| | 0/1824 [00:00" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Stampa del numero di eventi\n", + "print('[AM HitSim] NV events generated:', len(events_AM)) \n", + "print('[fuse HitSim] NV events generated:', len(events_NV)) \n", + "print('[fuse HitSim] NV events generated:', len(events_NV_nparray))\n", + "\n", + "print('fuse - AM:', (len(events_NV)-len(events_AM))/len(events_NV)*100, '%')\n", + "\n", + "plt.figure(figsize=(12,8))\n", + "\n", + "# Parametri comuni per l'istogramma\n", + "bin_range = [0, 200]\n", + "bins = 20\n", + "\n", + "# Istogramma per AM HitSim\n", + "#counts_AM, edges_AM, _ = plt.hist(events_AM['area'], histtype='step', range=bin_range, bins=bins, label='AM HitSim: ' + str(len(events_AM)) + ' events', color='b')\n", + "#bin_centers_AM = (edges_AM[:-1] + edges_AM[1:]) / 2\n", + "#errors_AM = np.sqrt(counts_AM)\n", + "\n", + "# Istogramma per fuse HitSim\n", + "counts_nv, edges_nv, _ = plt.hist(events_NV['area'], histtype='step', range=bin_range, bins=bins, label='fuse HitSim ' + str(len(events_NV)) + ' events', color='orange')\n", + "bin_centers_nv = (edges_nv[:-1] + edges_nv[1:]) / 2\n", + "errors_nv = np.sqrt(counts_nv)\n", + "\n", + "# Istogramma per fuse HitSim\n", + "counts_nv_, edges_nv_, _ = plt.hist(events_NV_nparray['area'], histtype='step', range=bin_range, bins=bins, label='fuse HitSim (np.array) ' + str(len(events_NV_nparray)) + ' events', color='r')\n", + "bin_centers_nv_ = (edges_nv_[:-1] + edges_nv_[1:]) / 2\n", + "errors_nv_ = np.sqrt(counts_nv_)\n", + "\n", + "\n", + "# Aggiunta delle barre d'errore con tacchette orizzontali\n", + "#plt.errorbar(bin_centers_AM, counts_AM, yerr=errors_AM, fmt='none', color='b', \n", + "# label='AM HitSim Error', capsize=4, capthick=1, alpha=0.5)\n", + "plt.errorbar(bin_centers_nv, counts_nv, yerr=errors_nv, fmt='none', color='orange', \n", + " label='fuse HitSim Error', capsize=4, capthick=1, alpha=0.5)\n", + "plt.errorbar(bin_centers_nv_, counts_nv_, yerr=errors_nv_, fmt='none', color='r', \n", + " label='fuse HitSim (np.array) Error', capsize=4, capthick=1, alpha=0.5)\n", + "\n", + "\n", + "# Personalizzazione del grafico\n", + "plt.xlabel('Area')\n", + "plt.ylabel('Counts')\n", + "plt.title('gd_ambe_top_cw7d8m_with_gamma - Config: SR1, eCE=0.87')\n", + "plt.legend()\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "code", "execution_count": null, @@ -272,7 +796,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.19" + "version": "3.9.20" } }, "nbformat": 4, diff --git a/fuse/plugins/neutron_veto/nvhitlets.py b/fuse/plugins/neutron_veto/nvhitlets.py index cec13ce9..654ca0e4 100644 --- a/fuse/plugins/neutron_veto/nvhitlets.py +++ b/fuse/plugins/neutron_veto/nvhitlets.py @@ -95,19 +95,23 @@ def channel_cluster_nv(t): t_val=np.array(t) clusters=np.array(db_cluster.fit_predict(t_val.reshape(-1, 1))) return clusters + def get_clusters_arrays(arr,typ): arr_nv_c=np.zeros(1, dtype=typ) arr_nv_c['n_clusters_hits']= len(arr) - for i in arr.fields: - if (i=='time') or (i=='pmthitTime') or (i=='cluster_times_ns'): + + for i in arr.dtype.names: # <-- CORRETTO: usare dtype.names invece di fields + if i in ['time', 'pmthitTime', 'cluster_times_ns']: arr_nv_c[i] = np.min(arr[i]) - elif i== 'endtime': - arr_nv_c[i]= np.max(arr[i]) - elif (i == 'pe_area') or (i=='pmthitEnergy'): - arr_nv_c[i]= np.sum(arr[i]) - elif (i=='evtid') or (i=='pmthitID') or (i=='labels') : - arr_nv_c[i]= np.unique(arr[i]) + elif i == 'endtime': + arr_nv_c[i] = np.max(arr[i]) + elif i in ['pe_area', 'pmthitEnergy']: + arr_nv_c[i] = np.sum(arr[i]) + elif i in ['evtid', 'pmthitID', 'labels']: + arr_nv_c[i] = np.unique(arr[i]) + return arr_nv_c + #Function to use in pivot_table module (clearly something we can optimize) def recover_value(x): m_size= np.array(x).size @@ -133,7 +137,6 @@ def time_hitlets_nv(time,ids,freq): ret=time return ret + freq*ids*1e9 -#Fonction to transform a hitlet dataframe output into 'hitlets_nv' ndarray #Fonction to transform a hitlet dataframe output into 'hitlets_nv' ndarray dtype = [('area', np.float64), ('amplitude', np.float64), @@ -254,7 +257,7 @@ def pe_charge_N(self,pmt_id): #--------------------------- Hitlet function ------------------------------------------------------------# - def _nv_hitlets(self,pmthits, CE_Scaling=0.75, Stacked='No', period = 1.): + def _nv_hitlets(self, pmthits, CE_Scaling=0.75, Stacked='No', period = 1.): #-------------------------------------------------Arguments---------------------------------------------------# #QE_Scaling corrrespond to collection efficiency, no study has been done on the CE of muon Veto we use a default value close to the nVeto see https://xe1t-wiki.lngs.infn.it/doku.php?id=xenon:mancuso:hitletsimulator:collection_efficiency @@ -270,13 +273,27 @@ def _nv_hitlets(self,pmthits, CE_Scaling=0.75, Stacked='No', period = 1.): #0.---------------Load GEANT output-------------------# - #1. First step PHOTON to first dinode - # awkward array with 'evtid', 'pmthitTime', 'pmthitEnergy', 'pmthitID' - pmthits=ak.Array(pmthits) + + # Adding extra fields to pmthits np.array + extra_fields = [ + ('pe_area', '=2000 pmthits=pmthits[mask] - + + #1. First step PHOTON to first dinode print("Applying QE and CE") # Applying Quantum efficiency for each pmt qe = 1e-2*np.vectorize(self.QE_E)(pmthits['pmthitEnergy'],pmthits['pmthitID']) @@ -294,28 +311,24 @@ def _nv_hitlets(self,pmthits, CE_Scaling=0.75, Stacked='No', period = 1.): #2. Sampling charge from SPE for each pmthit with a generated pe print("Sampling hitlets charge pe") pmthits['pe_area'] = np.vectorize(self.pe_charge_N)(pmthits['pmthitID']) - dtypes=[] - for i in pmthits.fields + ['labels','n_clusters_hits']: - if (i=='evtid') or (i=='time') or (i=='pmthitID') or (i=='endtime'): - dtypes.append((i,np.int64)) - else: - dtypes.append((i,np.float64)) #3. Creating hitlet times print('Getting time hitlets') times=[] - for i in (np.unique(pmthits.evtid)): - mask= pmthits.evtid==i - pmthits_evt=pmthits[mask] - cluster_times_ns = pmthits_evt.pmthitTime - min(pmthits_evt.pmthitTime) + for i in np.unique(pmthits['evtid']): + mask = pmthits['evtid'] == i + pmthits_evt = pmthits[mask] + cluster_times_ns = pmthits_evt['pmthitTime'] - min(pmthits_evt['pmthitTime']) times.append(cluster_times_ns) - pmthits['cluster_times_ns'] = np.vectorize(time_hitlets_nv)(flat_list(times),pmthits['evtid'],period) - dtypes=dtypes + [('cluster_times_ns', np.float64)] + pmthits['cluster_times_ns'] = np.concatenate(times) + if Stacked=='No': - nv_arrays= pmthits + return pmthits + + #3.1 Stacked hitlets: this correspond to hitlets in the same pmt with a time difference below some estimated time response of the Channel (8 ns, i.e. 4 samples). elif Stacked =='yes': - #3.1 Stacked hitlets: this correspond to hitlets in the same pmt with a time difference below some estimated time response of the Channel (8 ns, i.e. 4 samples). - print('Looking for stacket hitlets') + + print('Looking for stacked hitlets') #Here we set times related to the first hit, we only use that for stacket hitlets arr_c_evt=[] for i in tq.tqdm(np.unique(pmthits['evtid'])): @@ -325,11 +338,12 @@ def _nv_hitlets(self,pmthits, CE_Scaling=0.75, Stacked='No', period = 1.): arr_pmt = arr_evt[arr_evt['pmthitID']==j] labels = channel_cluster_nv(arr_pmt['cluster_times_ns']) arr_pmt['labels'] = labels - arr_c =np.concatenate([get_clusters_arrays(arr_pmt[arr_pmt['labels']==l],dtypes) for l in np.unique(labels)]) + arr_c = np.concatenate([get_clusters_arrays(arr_pmt[arr_pmt['labels'] == l], new_dtype) for l in np.unique(labels)]) + #arr_c =np.concatenate([get_clusters_arrays(arr_pmt[arr_pmt['labels']==l],dtypes) for l in np.unique(labels)]) arr_c_pmt.append(arr_c) arr_c_evt.append(np.concatenate(arr_c_pmt)) - nv_arrays = np.concatenate(arr_c_evt) - return nv_arrays + + return np.concatenate(arr_c_evt) def setup(self): From f10a94b32c49115b2480e9e343ed934290f7eafa Mon Sep 17 00:00:00 2001 From: Pietro Di Gangi Date: Mon, 10 Feb 2025 08:48:44 -0600 Subject: [PATCH 15/29] selecting only NV PMTs --- fuse/plugins/neutron_veto/nvhitlets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuse/plugins/neutron_veto/nvhitlets.py b/fuse/plugins/neutron_veto/nvhitlets.py index 654ca0e4..eacf97b2 100644 --- a/fuse/plugins/neutron_veto/nvhitlets.py +++ b/fuse/plugins/neutron_veto/nvhitlets.py @@ -290,7 +290,7 @@ def _nv_hitlets(self, pmthits, CE_Scaling=0.75, Stacked='No', period = 1.): pmthits = pmthits_extended # select NV PMTs (need to exclude MV PMTs?) - mask=pmthits['pmthitID']>=2000 + mask=(pmthits['pmthitID']>=2000)&(pmthits['pmthitID']<2121) pmthits=pmthits[mask] #1. First step PHOTON to first dinode From 3d2bcb7302e09c7f4189257719626dba897fbc37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henning=20Schulze=20Ei=C3=9Fing?= Date: Thu, 13 Feb 2025 16:05:56 +0100 Subject: [PATCH 16/29] Move code --- fuse/common.py | 94 ++++++++++++++++++++--------- fuse/plugins/micro_physics/input.py | 68 +-------------------- 2 files changed, 67 insertions(+), 95 deletions(-) diff --git a/fuse/common.py b/fuse/common.py index fb54da1f..6db06fa0 100644 --- a/fuse/common.py +++ b/fuse/common.py @@ -25,34 +25,72 @@ ) -@numba.njit() -def dynamic_chunking(data, scale, n_min): - idx_sort = np.argsort(data) - idx_undo_sort = np.argsort(idx_sort) - - data_sorted = data[idx_sort] - - diff = data_sorted[1:] - data_sorted[:-1] - - clusters = [0] - c = 0 - n_cluster = 0 - for value in diff: - if value <= scale: - clusters.append(c) - n_cluster += 1 - elif n_cluster + 1 < n_min: - clusters.append(c) - n_cluster += 1 - elif value > scale: - c = c + 1 - clusters.append(c) - n_cluster = 0 - - clusters = np.array(clusters) - clusters_undo_sort = clusters[idx_undo_sort] - - return clusters_undo_sort +@numba.njit(cache=True) +def dynamic_chunking(time_gaps, + file_size_limit, + min_gap_length, + n_bytes_per_interaction, + ): + + data_size_mb = 0 + clusters_index = [] + + running_index = 0 + + for g in time_gaps: + + data_size_mb += n_bytes_per_interaction / 1e6 + + if data_size_mb < file_size_limit: + clusters_index.append(running_index) + continue + + if g >= min_gap_length: + running_index += 1 + data_size_mb = 0 + clusters_index.append(running_index) + else: + clusters_index.append(running_index) + + return np.array(clusters_index) + + +@numba.njit(cache=True) +def dynamic_chunking_two_outputs(combined_time_gaps, + combined_types, + file_size_limit, + min_gap_length, + n_bytes_per_interaction_TPC, + n_bytes_per_interaction_NV, + ): + """Function to split the TPC and NV data into chunks based on the time gaps between the interactions""" + + data_size_mb_tpc = 0 + data_size_mb_nv = 0 + + combined_cluster_index = [] + running_index = 0 + + for i, (interaction_type, delta_t) in enumerate(zip(combined_types, combined_time_gaps)): + + if interaction_type == 0: + # TPC interaction + data_size_mb_tpc += n_bytes_per_interaction_TPC / 1e6 + elif interaction_type == 1: + # NV interaction + data_size_mb_nv += n_bytes_per_interaction_NV / 1e6 + + if (data_size_mb_tpc < file_size_limit) & (data_size_mb_nv < file_size_limit): + combined_cluster_index.append(running_index) + continue + + if delta_t >= min_gap_length: + running_index += 1 + data_size_mb_tpc = data_size_mb_nv = 0 + combined_cluster_index.append(running_index) + else: + combined_cluster_index.append(running_index) + return np.array(combined_cluster_index) def full_array_to_numpy(array, dtype): diff --git a/fuse/plugins/micro_physics/input.py b/fuse/plugins/micro_physics/input.py index f6cfda7a..4bf14a7c 100644 --- a/fuse/plugins/micro_physics/input.py +++ b/fuse/plugins/micro_physics/input.py @@ -11,7 +11,7 @@ from numba import njit from ...dtypes import g4_fields, primary_positions_fields, deposit_positions_fields -from ...common import full_array_to_numpy, reshape_awkward, dynamic_chunking, awkward_to_flat_numpy +from ...common import full_array_to_numpy, reshape_awkward, dynamic_chunking, awkward_to_flat_numpy, dynamic_chunking_two_outputs from ...plugin import FuseBasePlugin export, __all__ = strax.exporter() @@ -836,69 +836,3 @@ def _awkwardify_df(df): return ak.Array(dictionary) -@njit(cache=True) -def dynamic_chunking(time_gaps, - file_size_limit, - min_gap_length, - n_bytes_per_interaction, - ): - - data_size_mb = 0 - clusters_index = [] - - running_index = 0 - - for g in time_gaps: - - data_size_mb += n_bytes_per_interaction / 1e6 - - if data_size_mb < file_size_limit: - clusters_index.append(running_index) - continue - - if g >= min_gap_length: - running_index += 1 - data_size_mb = 0 - clusters_index.append(running_index) - else: - clusters_index.append(running_index) - - return np.array(clusters_index) - - -@njit(cache=True) -def dynamic_chunking_two_outputs(combined_time_gaps, - combined_types, - file_size_limit, - min_gap_length, - n_bytes_per_interaction_TPC, - n_bytes_per_interaction_NV, - ): - """Function to split the TPC and NV data into chunks based on the time gaps between the interactions""" - - data_size_mb_tpc = 0 - data_size_mb_nv = 0 - - combined_cluster_index = [] - running_index = 0 - - for i, (interaction_type, delta_t) in enumerate(zip(combined_types, combined_time_gaps)): - - if interaction_type == 0: - # TPC interaction - data_size_mb_tpc += n_bytes_per_interaction_TPC / 1e6 - elif interaction_type == 1: - # NV interaction - data_size_mb_nv += n_bytes_per_interaction_NV / 1e6 - - if (data_size_mb_tpc < file_size_limit) & (data_size_mb_nv < file_size_limit): - combined_cluster_index.append(running_index) - continue - - if delta_t >= min_gap_length: - running_index += 1 - data_size_mb_tpc = data_size_mb_nv = 0 - combined_cluster_index.append(running_index) - else: - combined_cluster_index.append(running_index) - return np.array(combined_cluster_index) \ No newline at end of file From 6a069d3791263bcd662ce42786b188b9f8e5051d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 13 Feb 2025 15:06:44 +0000 Subject: [PATCH 17/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- fuse/common.py | 35 +- fuse/plugins/micro_physics/input.py | 151 ++--- .../neutron_veto/Testing_nv_hitlet.ipynb | 278 ++++++--- fuse/plugins/neutron_veto/__init__.py | 2 +- fuse/plugins/neutron_veto/nvhitlets.py | 542 +++++++++--------- 5 files changed, 576 insertions(+), 432 deletions(-) diff --git a/fuse/common.py b/fuse/common.py index 6db06fa0..dffe9db9 100644 --- a/fuse/common.py +++ b/fuse/common.py @@ -26,11 +26,12 @@ @numba.njit(cache=True) -def dynamic_chunking(time_gaps, - file_size_limit, - min_gap_length, - n_bytes_per_interaction, - ): +def dynamic_chunking( + time_gaps, + file_size_limit, + min_gap_length, + n_bytes_per_interaction, +): data_size_mb = 0 clusters_index = [] @@ -38,7 +39,7 @@ def dynamic_chunking(time_gaps, running_index = 0 for g in time_gaps: - + data_size_mb += n_bytes_per_interaction / 1e6 if data_size_mb < file_size_limit: @@ -51,19 +52,21 @@ def dynamic_chunking(time_gaps, clusters_index.append(running_index) else: clusters_index.append(running_index) - + return np.array(clusters_index) @numba.njit(cache=True) -def dynamic_chunking_two_outputs(combined_time_gaps, - combined_types, - file_size_limit, - min_gap_length, - n_bytes_per_interaction_TPC, - n_bytes_per_interaction_NV, - ): - """Function to split the TPC and NV data into chunks based on the time gaps between the interactions""" +def dynamic_chunking_two_outputs( + combined_time_gaps, + combined_types, + file_size_limit, + min_gap_length, + n_bytes_per_interaction_TPC, + n_bytes_per_interaction_NV, +): + """Function to split the TPC and NV data into chunks based on the time gaps + between the interactions.""" data_size_mb_tpc = 0 data_size_mb_nv = 0 @@ -72,7 +75,7 @@ def dynamic_chunking_two_outputs(combined_time_gaps, running_index = 0 for i, (interaction_type, delta_t) in enumerate(zip(combined_types, combined_time_gaps)): - + if interaction_type == 0: # TPC interaction data_size_mb_tpc += n_bytes_per_interaction_TPC / 1e6 diff --git a/fuse/plugins/micro_physics/input.py b/fuse/plugins/micro_physics/input.py index 4bf14a7c..48227b28 100644 --- a/fuse/plugins/micro_physics/input.py +++ b/fuse/plugins/micro_physics/input.py @@ -11,7 +11,13 @@ from numba import njit from ...dtypes import g4_fields, primary_positions_fields, deposit_positions_fields -from ...common import full_array_to_numpy, reshape_awkward, dynamic_chunking, awkward_to_flat_numpy, dynamic_chunking_two_outputs +from ...common import ( + full_array_to_numpy, + reshape_awkward, + dynamic_chunking, + awkward_to_flat_numpy, + dynamic_chunking_two_outputs, +) from ...plugin import FuseBasePlugin export, __all__ = strax.exporter() @@ -20,11 +26,11 @@ # Remove the path and file name option from the config and do this with the run_number?? @export class ChunkInput(FuseBasePlugin): - """Plugin to read XENONnT Geant4 root or csv files for the TPC and NV detector. + """Plugin to read XENONnT Geant4 root or csv files for the TPC and NV + detector. The plugin can distribute the events in time based on a source rate and will create multiple chunks of data if needed. - """ __version__ = "0.4.0" @@ -34,20 +40,19 @@ class ChunkInput(FuseBasePlugin): dtype_tpc = deposit_positions_fields + g4_fields + primary_positions_fields + strax.time_fields - dtype_nv = [('pmthitEnergy', np.float32), - ('pmthitTime', np.float32), - ('pmthitID', np.int16), - ('evtid', np.int32), - ] + dtype_nv = [ + ("pmthitEnergy", np.float32), + ("pmthitTime", np.float32), + ("pmthitID", np.int16), + ("evtid", np.int32), + ] dtype_nv = dtype_nv + strax.time_fields dtype = dict() dtype["geant4_interactions"] = dtype_tpc dtype["nv_pmthits"] = dtype_nv - data_kind = {"geant4_interactions": "geant4_interactions", - "nv_pmthits" : "nv_pmthits" - } + data_kind = {"geant4_interactions": "geant4_interactions", "nv_pmthits": "nv_pmthits"} save_when = strax.SaveWhen.TARGET @@ -91,8 +96,9 @@ class ChunkInput(FuseBasePlugin): ) file_size_limit = straxen.URLConfig( - default=500, type=(int, float), - help='Target file size limit in MB', + default=500, + type=(int, float), + help="Target file size limit in MB", ) entry_start = straxen.URLConfig( @@ -132,8 +138,9 @@ class ChunkInput(FuseBasePlugin): ) nv_output = straxen.URLConfig( - default=True, type=bool, - help='Decide if you want to have NV output or not', + default=True, + type=bool, + help="Decide if you want to have NV output or not", ) def setup(self): @@ -157,7 +164,7 @@ def setup(self): cut_nr_only=self.nr_only, fixed_event_spacing=self.fixed_event_spacing, log=self.log, - neutron_veto_output = self.nv_output + neutron_veto_output=self.nv_output, ) self.file_reader_iterator = self.file_reader.output_chunk() @@ -165,11 +172,12 @@ def compute(self): try: if self.nv_output: - chunk_data, chunk_left, chunk_right, source_done, nv_chunk_data = next(self.file_reader_iterator) + chunk_data, chunk_left, chunk_right, source_done, nv_chunk_data = next( + self.file_reader_iterator + ) chunk_data["endtime"] = chunk_data["time"] nv_chunk_data["endtime"] = nv_chunk_data["time"] - else: chunk_data, chunk_left, chunk_right, source_done = next(self.file_reader_iterator) chunk_data["endtime"] = chunk_data["time"] @@ -178,18 +186,15 @@ def compute(self): self.source_done = source_done result = { - "geant4_interactions" : self.chunk( + "geant4_interactions": self.chunk( start=chunk_left, end=chunk_right, data=chunk_data, - data_type = "geant4_interactions" - ), - "nv_pmthits" : self.chunk( - start=chunk_left, - end=chunk_right, - data=nv_chunk_data, - data_type = "nv_pmthits" - ) + data_type="geant4_interactions", + ), + "nv_pmthits": self.chunk( + start=chunk_left, end=chunk_right, data=nv_chunk_data, data_type="nv_pmthits" + ), } return result @@ -209,6 +214,8 @@ def is_ready(self, chunk_i): self.ready = False self.ready ^= True # Flip return self.ready + + class file_loader: """Load the complete root file and return interactions in chunks.""" @@ -265,14 +272,15 @@ def __init__( # Set the nveto dtype -> move to dtype file later!!!! if self.neutron_veto_output: - self.nv_dtype = [('pmthitEnergy', np.float32), - ('pmthitTime', np.float32), - ('pmthitID', np.int16), - ('evtid', np.int32), - ] + self.nv_dtype = [ + ("pmthitEnergy", np.float32), + ("pmthitTime", np.float32), + ("pmthitID", np.int16), + ("evtid", np.int32), + ] self.nv_dtype = self.nv_dtype + strax.time_fields - self.neutron_veto_column_names = ['pmthitEnergy','pmthitTime','pmthitID'] + self.neutron_veto_column_names = ["pmthitEnergy", "pmthitTime", "pmthitID"] # Prepare cut for root and csv case if self.outer_cylinder: @@ -289,7 +297,9 @@ def output_chunk(self): if self.file_type == "root": if self.neutron_veto_output: - interactions, n_simulated_events, start, stop, nv_interactions = self._load_root_file() + interactions, n_simulated_events, start, stop, nv_interactions = ( + self._load_root_file() + ) else: interactions, n_simulated_events, start, stop = self._load_root_file() elif self.file_type == "csv": @@ -300,28 +310,27 @@ def output_chunk(self): ) # Find the global start time of the interactions to keep TPC and NV in sync - min_time_TPC = ak.min(interactions['t'], axis=1) + min_time_TPC = ak.min(interactions["t"], axis=1) if self.neutron_veto_output: - min_time_NV = ak.min(nv_interactions['pmthitTime'], axis=1) + min_time_NV = ak.min(nv_interactions["pmthitTime"], axis=1) global_min_time = ak.min([min_time_TPC, min_time_NV], axis=0) else: global_min_time = min_time_TPC - # Sort interactions in events by time and subtract time of the first interaction + # Sort interactions in events by time and subtract time of the first interaction interactions = interactions[ak.argsort(interactions["t"])] # Remove the global min time from the interactions if self.event_rate > 0: - interactions['t'] = interactions['t'] - global_min_time - + interactions["t"] = interactions["t"] - global_min_time + if self.neutron_veto_output: - nv_interactions = nv_interactions[ak.argsort(nv_interactions['pmthitTime'])] - nv_interactions['pmthitTime'] = nv_interactions['pmthitTime'] - global_min_time + nv_interactions = nv_interactions[ak.argsort(nv_interactions["pmthitTime"])] + nv_interactions["pmthitTime"] = nv_interactions["pmthitTime"] - global_min_time - # Now lets distribute the interactions in time making sure that the + # Now lets distribute the interactions in time making sure that the # detectors stay in sync with each other - # Right now this only works if there are TPC interactions. For NV only we have to update this part... num_interactions = len(interactions["t"]) @@ -363,7 +372,6 @@ def output_chunk(self): else: raise ValueError("Source rate cannot be negative!") - # Now that all events are distributed in time and the two detectors are still in sync # we can apply some cuts to the data @@ -390,7 +398,6 @@ def output_chunk(self): # Add all NV specific cuts here, I will just start with the delay cut for now nv_interactions = nv_interactions[nv_interactions["pmthitTime"] < self.cut_delayed] - # Make into a flat numpy array interaction_time = awkward_to_flat_numpy(interactions["time"]) # First caclulate sort index for the interaction times @@ -407,24 +414,24 @@ def output_chunk(self): nv_interaction_time = nv_interaction_time.astype(np.int64) nv_interaction_time = nv_interaction_time[nv_sort_idx] - # Chunking the data! - #Move this somewhere else? Is this correct? - n_bytes_per_interaction_TPC = 6*8 + 5*4 + 2*2 + 4*40 - n_bytes_per_interaction_NV = 2*8 + 3*4 + 1*2 + # Move this somewhere else? Is this correct? + n_bytes_per_interaction_TPC = 6 * 8 + 5 * 4 + 2 * 2 + 4 * 40 + n_bytes_per_interaction_NV = 2 * 8 + 3 * 4 + 1 * 2 if not self.neutron_veto_output: - #Start with the TPC only case + # Start with the TPC only case time_gaps = interaction_time[1:] - interaction_time[:-1] - time_gaps = np.append(time_gaps, 0) #Add last gap + time_gaps = np.append(time_gaps, 0) # Add last gap - chunk_idx = dynamic_chunking(time_gaps, - self.file_size_limit, - self.separation_scale, - n_bytes_per_interaction_TPC, - ) + chunk_idx = dynamic_chunking( + time_gaps, + self.file_size_limit, + self.separation_scale, + n_bytes_per_interaction_TPC, + ) unique_chunk_index_values = np.unique(chunk_idx) chunk_start = np.array( @@ -495,13 +502,15 @@ def output_chunk(self): # We need to combine the times of TPC and NV interactions so they always end up in the same chunk combined_times = np.append(interaction_time, nv_interaction_time) - combined_types = np.append(np.zeros(len(interaction_time)), np.ones(len(nv_interaction_time))) + combined_types = np.append( + np.zeros(len(interaction_time)), np.ones(len(nv_interaction_time)) + ) sort_idx = np.argsort(combined_times) combined_times = combined_times[sort_idx] combined_types = combined_types[sort_idx] combined_time_gaps = combined_times[1:] - combined_times[:-1] - combined_time_gaps = np.append(combined_time_gaps, 0) #Add last gap + combined_time_gaps = np.append(combined_time_gaps, 0) # Add last gap combined_chunk_index = dynamic_chunking_two_outputs( combined_time_gaps, @@ -515,10 +524,16 @@ def output_chunk(self): unique_combined_chunk_index_values = np.unique(combined_chunk_index) chunk_start = np.array( - [combined_times[combined_chunk_index == i][0] for i in unique_combined_chunk_index_values] + [ + combined_times[combined_chunk_index == i][0] + for i in unique_combined_chunk_index_values + ] ) chunk_end = np.array( - [combined_times[combined_chunk_index == i][-1] for i in unique_combined_chunk_index_values] + [ + combined_times[combined_chunk_index == i][-1] + for i in unique_combined_chunk_index_values + ] ) if (len(chunk_start) > 1) & (len(chunk_end) > 1): @@ -701,25 +716,26 @@ def _load_root_file(self): interactions["y_pri"] = ak.broadcast_arrays(xyz_pri["y_pri"], interactions["x"])[0] interactions["z_pri"] = ak.broadcast_arrays(xyz_pri["z_pri"], interactions["x"])[0] - if self.neutron_veto_output: nv_interactions = ttree.arrays( self.neutron_veto_column_names, entry_start=start_index, entry_stop=stop_index, ) - + # Time conversion to ns - nv_interactions['pmthitTime'] = nv_interactions['pmthitTime']*10**9 + nv_interactions["pmthitTime"] = nv_interactions["pmthitTime"] * 10**9 - # Add event numbers + # Add event numbers nv_eventids = ttree.arrays( - 'eventid', + "eventid", entry_start=start_index, entry_stop=stop_index, ) - nv_eventids = ak.broadcast_arrays(nv_eventids['eventid'], nv_interactions['pmthitTime'])[0] - nv_interactions['evtid'] = nv_eventids + nv_eventids = ak.broadcast_arrays( + nv_eventids["eventid"], nv_interactions["pmthitTime"] + )[0] + nv_interactions["evtid"] = nv_eventids return interactions, n_simulated_events, start_index, stop_index, nv_interactions @@ -835,4 +851,3 @@ def _awkwardify_df(df): } return ak.Array(dictionary) - diff --git a/fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb b/fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb index ae9c5466..365d47a7 100644 --- a/fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb +++ b/fuse/plugins/neutron_veto/Testing_nv_hitlet.ipynb @@ -40,7 +40,8 @@ ], "source": [ "import sys\n", - "sys.path.insert(0, '/home/digangi/cutax/') #on midway3\n", + "\n", + "sys.path.insert(0, \"/home/digangi/cutax/\") # on midway3\n", "\n", "import fuse\n", "import matplotlib.pyplot as plt\n", @@ -49,7 +50,7 @@ "from tqdm import tqdm\n", "import tqdm.notebook as tq\n", "import pandas as pd\n", - "from sklearn.cluster import DBSCAN\n" + "from sklearn.cluster import DBSCAN" ] }, { @@ -86,16 +87,19 @@ } ], "source": [ - "st = fuse.context.full_chain_context(output_folder = \"./fuse_data\")\n", - "st.set_config({\"path\": \"/project2/lgrandi/layos/\",\n", - " \"file_name\": \"output_n_Veto_neutron_AmBe_1.root\",\n", - " \"entry_stop\": 2000,\n", - " \"nv_output\": True, #On inclus les pmts du Neutron Veto (par defaut False)\n", - " \"debug\": True,\n", - " \"file_size_limit\": 500/1e4, #Poser question a Hening sur cela....\n", - " })\n", + "st = fuse.context.full_chain_context(output_folder=\"./fuse_data\")\n", + "st.set_config(\n", + " {\n", + " \"path\": \"/project2/lgrandi/layos/\",\n", + " \"file_name\": \"output_n_Veto_neutron_AmBe_1.root\",\n", + " \"entry_stop\": 2000,\n", + " \"nv_output\": True, # On inclus les pmts du Neutron Veto (par defaut False)\n", + " \"debug\": True,\n", + " \"file_size_limit\": 500 / 1e4, # Poser question a Hening sur cela....\n", + " }\n", + ")\n", "\n", - "run_number = \"00003\"#Attention à ce parametre, aujourd'hui triviale, mais qui tiens en compte du mapping pour la TPC en fonction du run." + "run_number = \"00003\" # Attention à ce parametre, aujourd'hui triviale, mais qui tiens en compte du mapping pour la TPC en fonction du run." ] }, { @@ -135,16 +139,19 @@ "g4dir = \"/project/lgrandi/xenonnt/simulations/nveto/ambe/geant4/ambe_nveto_gd0p02_2024-01-23/gd_ambe_sr1_top_cw7d8m_with_gamma/results/\"\n", "filename = \"nT_mc_gdt7p8m_wc_00000.root\"\n", "\n", - "st = fuse.context.full_chain_context(output_folder = \"./fuse_data\")\n", - "st.set_config({\"path\": g4dir,\n", - " \"file_name\": filename,\n", - " \"entry_stop\": 2000,\n", - " \"nv_output\": True, #On inclus les pmts du Neutron Veto (par defaut False)\n", - " \"debug\": True,\n", - " \"file_size_limit\": 500/1e4, #Poser question a Hening sur cela....\n", - " })\n", + "st = fuse.context.full_chain_context(output_folder=\"./fuse_data\")\n", + "st.set_config(\n", + " {\n", + " \"path\": g4dir,\n", + " \"file_name\": filename,\n", + " \"entry_stop\": 2000,\n", + " \"nv_output\": True, # On inclus les pmts du Neutron Veto (par defaut False)\n", + " \"debug\": True,\n", + " \"file_size_limit\": 500 / 1e4, # Poser question a Hening sur cela....\n", + " }\n", + ")\n", "\n", - "run_number = \"00003\"#Attention à ce parametre, aujourd'hui triviale, mais qui tiens en compte du mapping pour la TPC en fonction du run." + "run_number = \"00003\" # Attention à ce parametre, aujourd'hui triviale, mais qui tiens en compte du mapping pour la TPC en fonction du run." ] }, { @@ -153,12 +160,7 @@ "metadata": {}, "outputs": [], "source": [ - "st.set_config(\n", - " {\n", - " \"deteministic_seed\": False,\n", - " \"user_defined_random_seed\": 42\n", - " }\n", - ")" + "st.set_config({\"deteministic_seed\": False, \"user_defined_random_seed\": 42})" ] }, { @@ -216,7 +218,7 @@ } ], "source": [ - "len(np.unique(nv_pmthits['evtid']))" + "len(np.unique(nv_pmthits[\"evtid\"]))" ] }, { @@ -256,15 +258,15 @@ } ], "source": [ - "#sr=1\n", - "#eCE=0.87\n", + "# sr=1\n", + "# eCE=0.87\n", "\n", - "sr=0\n", - "eCE=0.75\n", + "sr = 0\n", + "eCE = 0.75\n", "\n", "nv_hitlet = fuse.neutron_veto.NeutronVetoHitlets(sr=sr)\n", "\n", - "hitlets_nv=nv_hitlet.compute(nv_pmthits, eCE=eCE)" + "hitlets_nv = nv_hitlet.compute(nv_pmthits, eCE=eCE)" ] }, { @@ -299,7 +301,7 @@ } ], "source": [ - "hitlets_nv_stacked=nv_hitlet.compute(nv_pmthits,eCE=eCE, Stacked_opt='yes')" + "hitlets_nv_stacked = nv_hitlet.compute(nv_pmthits, eCE=eCE, Stacked_opt=\"yes\")" ] }, { @@ -336,12 +338,15 @@ } ], "source": [ - "#Hitlets_nv is an array to be computed by strax plugin, exemple\n", + "# Hitlets_nv is an array to be computed by strax plugin, exemple\n", "import cutax\n", + "\n", "st = cutax.contexts.xenonnt_online()\n", - "strax_nv = st.get_single_plugin('0', 'events_nv')\n", - "events_nv = strax_nv.compute(hitlets_nv,min(hitlets_nv['time']),max(hitlets_nv['time']))\n", - "events_nv_stacket = strax_nv.compute(hitlets_nv_stacked,min(hitlets_nv_stacked['time']),max(hitlets_nv_stacked['time']))" + "strax_nv = st.get_single_plugin(\"0\", \"events_nv\")\n", + "events_nv = strax_nv.compute(hitlets_nv, min(hitlets_nv[\"time\"]), max(hitlets_nv[\"time\"]))\n", + "events_nv_stacket = strax_nv.compute(\n", + " hitlets_nv_stacked, min(hitlets_nv_stacked[\"time\"]), max(hitlets_nv_stacked[\"time\"])\n", + ")" ] }, { @@ -381,8 +386,8 @@ } ], "source": [ - "plt.hist(events_nv['area'], range=[0,200],bins=20,alpha=0.3, label ='events_nv')\n", - "plt.hist(events_nv_stacket['area'], range=[0,200],bins=20,alpha=0.3, label = 'events_nv_stacked')\n", + "plt.hist(events_nv[\"area\"], range=[0, 200], bins=20, alpha=0.3, label=\"events_nv\")\n", + "plt.hist(events_nv_stacket[\"area\"], range=[0, 200], bins=20, alpha=0.3, label=\"events_nv_stacked\")\n", "plt.legend()\n", "plt.show()" ] @@ -401,9 +406,15 @@ } ], "source": [ - "output_file = '/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/' + 'fuseHitSim_sr' + str(sr) + 'config_seed42_nparray' + filename\n", + "output_file = (\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/\"\n", + " + \"fuseHitSim_sr\"\n", + " + str(sr)\n", + " + \"config_seed42_nparray\"\n", + " + filename\n", + ")\n", "np.save(output_file, events_nv)\n", - "print('Output saved to file: ', output_file)" + "print(\"Output saved to file: \", output_file)" ] }, { @@ -502,7 +513,7 @@ } ], "source": [ - "ak_pmthits=ak.Array(nv_pmthits)\n", + "ak_pmthits = ak.Array(nv_pmthits)\n", "ak_pmthits" ] }, @@ -529,12 +540,12 @@ ], "source": [ "# awkward array with 'evtid', 'pmthitTime', 'pmthitEnergy', 'pmthitID'\n", - "#pmthits=ak.Array(nv_pmthits)\n", - "pmthits=nv_pmthits\n", + "# pmthits=ak.Array(nv_pmthits)\n", + "pmthits = nv_pmthits\n", "print(len(pmthits))\n", "# select NV PMTs (need to exclude MV PMTs?)\n", - "mask=pmthits['pmthitID']>=2000\n", - "pmthits=pmthits[mask]\n", + "mask = pmthits[\"pmthitID\"] >= 2000\n", + "pmthits = pmthits[mask]\n", "print(len(pmthits))" ] }, @@ -570,7 +581,7 @@ } ], "source": [ - "hitlets_nv_stacked=nv_hitlet.compute(nv_pmthits,Stacked_opt='yes')" + "hitlets_nv_stacked = nv_hitlet.compute(nv_pmthits, Stacked_opt=\"yes\")" ] }, { @@ -600,17 +611,37 @@ "outputs": [], "source": [ "### SR0 config\n", - "events_AM = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/AMhitsim_nVETOEvents_gd_ambe_sr1_top_cw7d8m_with_gamma_ce0.75.npy')\n", - "events_NV_0 = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_nT_mc_gdt7p8m_wc_00000.root.npy')\n", - "events_NV_1 = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_nT_mc_gdt7p8m_wc_00001.root.npy')\n", - "events_NV_2 = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_nT_mc_gdt7p8m_wc_00002.root.npy')\n", - "events_NV_3 = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_nT_mc_gdt7p8m_wc_00003.root.npy')\n", - "events_NV_4 = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_nT_mc_gdt7p8m_wc_00004.root.npy')\n", - "events_NV_5 = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_nT_mc_gdt7p8m_wc_00005.root.npy')\n", - "events_NV_6 = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_nT_mc_gdt7p8m_wc_00006.root.npy')\n", - "events_NV_7 = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_nT_mc_gdt7p8m_wc_00007.root.npy')\n", + "events_AM = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/AMhitsim_nVETOEvents_gd_ambe_sr1_top_cw7d8m_with_gamma_ce0.75.npy\"\n", + ")\n", + "events_NV_0 = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_nT_mc_gdt7p8m_wc_00000.root.npy\"\n", + ")\n", + "events_NV_1 = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_nT_mc_gdt7p8m_wc_00001.root.npy\"\n", + ")\n", + "events_NV_2 = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_nT_mc_gdt7p8m_wc_00002.root.npy\"\n", + ")\n", + "events_NV_3 = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_nT_mc_gdt7p8m_wc_00003.root.npy\"\n", + ")\n", + "events_NV_4 = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_nT_mc_gdt7p8m_wc_00004.root.npy\"\n", + ")\n", + "events_NV_5 = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_nT_mc_gdt7p8m_wc_00005.root.npy\"\n", + ")\n", + "events_NV_6 = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_nT_mc_gdt7p8m_wc_00006.root.npy\"\n", + ")\n", + "events_NV_7 = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_nT_mc_gdt7p8m_wc_00007.root.npy\"\n", + ")\n", "\n", - "events_NV_0_nparray = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_nT_mc_gdt7p8m_wc_00007.root.npy')" + "events_NV_0_nparray = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_nT_mc_gdt7p8m_wc_00007.root.npy\"\n", + ")" ] }, { @@ -620,15 +651,33 @@ "outputs": [], "source": [ "### SR1 config\n", - "events_AM = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/AMhitsim_nVETOEvents_gd_ambe_sr1_top_cw7d8m_with_gamma_ce0.87.npy')\n", - "events_NV_0 = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr1config_nT_mc_gdt7p8m_wc_00000.root.npy')\n", - "events_NV_1 = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr1config_nT_mc_gdt7p8m_wc_00001.root.npy')\n", - "events_NV_2 = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr1config_nT_mc_gdt7p8m_wc_00002.root.npy')\n", - "events_NV_3 = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr1config_nT_mc_gdt7p8m_wc_00003.root.npy')\n", - "events_NV_4 = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr1config_nT_mc_gdt7p8m_wc_00004.root.npy')\n", - "events_NV_5 = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr1config_nT_mc_gdt7p8m_wc_00005.root.npy')\n", - "events_NV_6 = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr1config_nT_mc_gdt7p8m_wc_00006.root.npy')\n", - "events_NV_7 = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr1config_nT_mc_gdt7p8m_wc_00007.root.npy')" + "events_AM = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/AMhitsim_nVETOEvents_gd_ambe_sr1_top_cw7d8m_with_gamma_ce0.87.npy\"\n", + ")\n", + "events_NV_0 = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr1config_nT_mc_gdt7p8m_wc_00000.root.npy\"\n", + ")\n", + "events_NV_1 = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr1config_nT_mc_gdt7p8m_wc_00001.root.npy\"\n", + ")\n", + "events_NV_2 = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr1config_nT_mc_gdt7p8m_wc_00002.root.npy\"\n", + ")\n", + "events_NV_3 = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr1config_nT_mc_gdt7p8m_wc_00003.root.npy\"\n", + ")\n", + "events_NV_4 = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr1config_nT_mc_gdt7p8m_wc_00004.root.npy\"\n", + ")\n", + "events_NV_5 = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr1config_nT_mc_gdt7p8m_wc_00005.root.npy\"\n", + ")\n", + "events_NV_6 = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr1config_nT_mc_gdt7p8m_wc_00006.root.npy\"\n", + ")\n", + "events_NV_7 = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr1config_nT_mc_gdt7p8m_wc_00007.root.npy\"\n", + ")" ] }, { @@ -648,8 +697,19 @@ } ], "source": [ - "events_NV = np.concatenate([events_NV_0, events_NV_1, events_NV_2, events_NV_3, events_NV_4, events_NV_5, events_NV_6, events_NV_7])\n", - "#events_NV = np.concatenate([events_NV_7])\n", + "events_NV = np.concatenate(\n", + " [\n", + " events_NV_0,\n", + " events_NV_1,\n", + " events_NV_2,\n", + " events_NV_3,\n", + " events_NV_4,\n", + " events_NV_5,\n", + " events_NV_6,\n", + " events_NV_7,\n", + " ]\n", + ")\n", + "# events_NV = np.concatenate([events_NV_7])\n", "\n", "len(events_NV)" ] @@ -680,9 +740,15 @@ "metadata": {}, "outputs": [], "source": [ - "events_AM = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/AMhitsim_nVETOEvents_gd_ambe_sr1_top_cw7d8m_with_gamma_ce0.75_.npy')\n", - "events_NV = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr0config_seed42_nT_mc_gdt7p8m_wc_00000.root.npy')\n", - "events_NV_nparray = np.load('/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr0config_seed42_nparraynT_mc_gdt7p8m_wc_00000.root.npy')" + "events_AM = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/AMhitsim_nVETOEvents_gd_ambe_sr1_top_cw7d8m_with_gamma_ce0.75_.npy\"\n", + ")\n", + "events_NV = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr0config_seed42_nT_mc_gdt7p8m_wc_00000.root.npy\"\n", + ")\n", + "events_NV_nparray = np.load(\n", + " \"/scratch/midway3/digangi/simulations/NeutronVeto/hitlet_sim/output_data/fuseHitSim_sr0config_seed42_nparraynT_mc_gdt7p8m_wc_00000.root.npy\"\n", + ")" ] }, { @@ -713,49 +779,81 @@ ], "source": [ "# Stampa del numero di eventi\n", - "print('[AM HitSim] NV events generated:', len(events_AM)) \n", - "print('[fuse HitSim] NV events generated:', len(events_NV)) \n", - "print('[fuse HitSim] NV events generated:', len(events_NV_nparray))\n", + "print(\"[AM HitSim] NV events generated:\", len(events_AM))\n", + "print(\"[fuse HitSim] NV events generated:\", len(events_NV))\n", + "print(\"[fuse HitSim] NV events generated:\", len(events_NV_nparray))\n", "\n", - "print('fuse - AM:', (len(events_NV)-len(events_AM))/len(events_NV)*100, '%')\n", + "print(\"fuse - AM:\", (len(events_NV) - len(events_AM)) / len(events_NV) * 100, \"%\")\n", "\n", - "plt.figure(figsize=(12,8))\n", + "plt.figure(figsize=(12, 8))\n", "\n", "# Parametri comuni per l'istogramma\n", "bin_range = [0, 200]\n", "bins = 20\n", "\n", "# Istogramma per AM HitSim\n", - "#counts_AM, edges_AM, _ = plt.hist(events_AM['area'], histtype='step', range=bin_range, bins=bins, label='AM HitSim: ' + str(len(events_AM)) + ' events', color='b')\n", - "#bin_centers_AM = (edges_AM[:-1] + edges_AM[1:]) / 2\n", - "#errors_AM = np.sqrt(counts_AM)\n", + "# counts_AM, edges_AM, _ = plt.hist(events_AM['area'], histtype='step', range=bin_range, bins=bins, label='AM HitSim: ' + str(len(events_AM)) + ' events', color='b')\n", + "# bin_centers_AM = (edges_AM[:-1] + edges_AM[1:]) / 2\n", + "# errors_AM = np.sqrt(counts_AM)\n", "\n", "# Istogramma per fuse HitSim\n", - "counts_nv, edges_nv, _ = plt.hist(events_NV['area'], histtype='step', range=bin_range, bins=bins, label='fuse HitSim ' + str(len(events_NV)) + ' events', color='orange')\n", + "counts_nv, edges_nv, _ = plt.hist(\n", + " events_NV[\"area\"],\n", + " histtype=\"step\",\n", + " range=bin_range,\n", + " bins=bins,\n", + " label=\"fuse HitSim \" + str(len(events_NV)) + \" events\",\n", + " color=\"orange\",\n", + ")\n", "bin_centers_nv = (edges_nv[:-1] + edges_nv[1:]) / 2\n", "errors_nv = np.sqrt(counts_nv)\n", "\n", "# Istogramma per fuse HitSim\n", - "counts_nv_, edges_nv_, _ = plt.hist(events_NV_nparray['area'], histtype='step', range=bin_range, bins=bins, label='fuse HitSim (np.array) ' + str(len(events_NV_nparray)) + ' events', color='r')\n", + "counts_nv_, edges_nv_, _ = plt.hist(\n", + " events_NV_nparray[\"area\"],\n", + " histtype=\"step\",\n", + " range=bin_range,\n", + " bins=bins,\n", + " label=\"fuse HitSim (np.array) \" + str(len(events_NV_nparray)) + \" events\",\n", + " color=\"r\",\n", + ")\n", "bin_centers_nv_ = (edges_nv_[:-1] + edges_nv_[1:]) / 2\n", "errors_nv_ = np.sqrt(counts_nv_)\n", "\n", "\n", "# Aggiunta delle barre d'errore con tacchette orizzontali\n", - "#plt.errorbar(bin_centers_AM, counts_AM, yerr=errors_AM, fmt='none', color='b', \n", + "# plt.errorbar(bin_centers_AM, counts_AM, yerr=errors_AM, fmt='none', color='b',\n", "# label='AM HitSim Error', capsize=4, capthick=1, alpha=0.5)\n", - "plt.errorbar(bin_centers_nv, counts_nv, yerr=errors_nv, fmt='none', color='orange', \n", - " label='fuse HitSim Error', capsize=4, capthick=1, alpha=0.5)\n", - "plt.errorbar(bin_centers_nv_, counts_nv_, yerr=errors_nv_, fmt='none', color='r', \n", - " label='fuse HitSim (np.array) Error', capsize=4, capthick=1, alpha=0.5)\n", + "plt.errorbar(\n", + " bin_centers_nv,\n", + " counts_nv,\n", + " yerr=errors_nv,\n", + " fmt=\"none\",\n", + " color=\"orange\",\n", + " label=\"fuse HitSim Error\",\n", + " capsize=4,\n", + " capthick=1,\n", + " alpha=0.5,\n", + ")\n", + "plt.errorbar(\n", + " bin_centers_nv_,\n", + " counts_nv_,\n", + " yerr=errors_nv_,\n", + " fmt=\"none\",\n", + " color=\"r\",\n", + " label=\"fuse HitSim (np.array) Error\",\n", + " capsize=4,\n", + " capthick=1,\n", + " alpha=0.5,\n", + ")\n", "\n", "\n", "# Personalizzazione del grafico\n", - "plt.xlabel('Area')\n", - "plt.ylabel('Counts')\n", - "plt.title('gd_ambe_top_cw7d8m_with_gamma - Config: SR1, eCE=0.87')\n", + "plt.xlabel(\"Area\")\n", + "plt.ylabel(\"Counts\")\n", + "plt.title(\"gd_ambe_top_cw7d8m_with_gamma - Config: SR1, eCE=0.87\")\n", "plt.legend()\n", - "plt.show()\n" + "plt.show()" ] }, { diff --git a/fuse/plugins/neutron_veto/__init__.py b/fuse/plugins/neutron_veto/__init__.py index 98e151ee..4d62ff79 100644 --- a/fuse/plugins/neutron_veto/__init__.py +++ b/fuse/plugins/neutron_veto/__init__.py @@ -1,2 +1,2 @@ from . import nvhitlets -from .nvhitlets import * \ No newline at end of file +from .nvhitlets import * diff --git a/fuse/plugins/neutron_veto/nvhitlets.py b/fuse/plugins/neutron_veto/nvhitlets.py index eacf97b2..01a336bf 100644 --- a/fuse/plugins/neutron_veto/nvhitlets.py +++ b/fuse/plugins/neutron_veto/nvhitlets.py @@ -4,7 +4,7 @@ import logging logging.basicConfig(handlers=[logging.StreamHandler()]) -log = logging.getLogger('fuse.neutron_veto.nvhitlets') +log = logging.getLogger("fuse.neutron_veto.nvhitlets") from ...common import FUSE_PLUGIN_TIMEOUT @@ -24,262 +24,286 @@ ##--------------------------------------------COMMENTS------------------------------------------------------------## -#This hitlet simulator is an extension of the work of Diego Ramirez, Daniel Wenz, Andrea Mancuso and Pavel Kavrigin. -#Functions of SPE charge sampling are in the PDF function based in the calibrations of nVeto done by Andrea Mancuso. Daniel Wenz provides a code that takes into account this functions to sample the charge, where this code is based. This leads to SPE a PDF function to sample charge in the hitlet simulator. -#We use the Quantum efficiencies QE for each PMT as a wavelength function for nVeto provided by Andrea Mancuso. +# This hitlet simulator is an extension of the work of Diego Ramirez, Daniel Wenz, Andrea Mancuso and Pavel Kavrigin. +# Functions of SPE charge sampling are in the PDF function based in the calibrations of nVeto done by Andrea Mancuso. Daniel Wenz provides a code that takes into account this functions to sample the charge, where this code is based. This leads to SPE a PDF function to sample charge in the hitlet simulator. +# We use the Quantum efficiencies QE for each PMT as a wavelength function for nVeto provided by Andrea Mancuso. -#WIKI NOTES: -#https://xe1t-wiki.lngs.infn.it/doku.php?id=xenon:xenonnt:layos:nveto_hitlet_into_fuse +# WIKI NOTES: +# https://xe1t-wiki.lngs.infn.it/doku.php?id=xenon:xenonnt:layos:nveto_hitlet_into_fuse - -#--------------------------------------------------------HITLETS AUXILIAR FUNCTIONS--------------------------------------------------------------------# + +# --------------------------------------------------------HITLETS AUXILIAR FUNCTIONS--------------------------------------------------------------------# def flat_list(l): - return np.array([item for sublist in l for item in sublist ]) + return np.array([item for sublist in l for item in sublist]) + def energytowavelenght(E): - Joules_to_eV=1.602*1e-19 - return 1e9*const.h*const.c/(E*Joules_to_eV) - -def create_SPE_file(path,sr='0'): - #path to the aux_files - #SR : to change configuration for each SR - spe_df=pd.DataFrame(columns=['pmtID', 'pe', 'SPE_values','acceptance']) - array_x = np.load(path + 'x_data_sr'+sr+'.npy') - array_y = np.load(path+'sr'+sr+'_pdfs.npy') - spe_df['pmtID']= np.arange(2000,2120) - spe_df['pe']=array_x.tolist() - spe_df['SPE_values']=array_y.tolist() - spe_df['acceptance']= np.load(path+'spe_acc_sr'+sr+'.npy',allow_pickle=True) - return np.save(path+'SPE_SR'+sr+'.npy',spe_df.to_records()) - -#SPE parameters: ID, pe, SPE, acceptance + Joules_to_eV = 1.602 * 1e-19 + return 1e9 * const.h * const.c / (E * Joules_to_eV) + + +def create_SPE_file(path, sr="0"): + # path to the aux_files + # SR : to change configuration for each SR + spe_df = pd.DataFrame(columns=["pmtID", "pe", "SPE_values", "acceptance"]) + array_x = np.load(path + "x_data_sr" + sr + ".npy") + array_y = np.load(path + "sr" + sr + "_pdfs.npy") + spe_df["pmtID"] = np.arange(2000, 2120) + spe_df["pe"] = array_x.tolist() + spe_df["SPE_values"] = array_y.tolist() + spe_df["acceptance"] = np.load(path + "spe_acc_sr" + sr + ".npy", allow_pickle=True) + return np.save(path + "SPE_SR" + sr + ".npy", spe_df.to_records()) + + +# SPE parameters: ID, pe, SPE, acceptance def SPE_parameters(file_spe_model): - data_spe=np.load(file_spe_model,allow_pickle=True) - #SPE_ch= pd.DataFrame(columns=['pmtID','pe','SPE','acceptance']) - #SPE_ch['pmtID'],SPE_ch['pe'], SPE_ch['SPE'],SPE_ch['acceptance']=data_spe['pmtID'],data_spe['charge'],data_spe['SPE_values'],data_spe['acceptance'] - #acceptance_ch= [threshold_acc(SPE_ch,i) for i in np.arange(2000,2120)] - #SPE_ch['threshold_pe']=acceptance_ch + data_spe = np.load(file_spe_model, allow_pickle=True) + # SPE_ch= pd.DataFrame(columns=['pmtID','pe','SPE','acceptance']) + # SPE_ch['pmtID'],SPE_ch['pe'], SPE_ch['SPE'],SPE_ch['acceptance']=data_spe['pmtID'],data_spe['charge'],data_spe['SPE_values'],data_spe['acceptance'] + # acceptance_ch= [threshold_acc(SPE_ch,i) for i in np.arange(2000,2120)] + # SPE_ch['threshold_pe']=acceptance_ch return data_spe - + + def threshold_acc(SPE_df, ID): - SPE_ID=pd.DataFrame() - SPE_ID['cumulative']=np.cumsum(SPE_df[SPE_df.pmtID==ID].SPE.values[0]) - SPE_ID['charges']= SPE_df[SPE_df.pmtID==ID].pe.values[0] - accep= SPE_df[SPE_df.pmtID==ID].acceptance.values[0] - threshold= min(SPE_ID[SPE_ID.cumulative>=(1-accep)].charges.values) + SPE_ID = pd.DataFrame() + SPE_ID["cumulative"] = np.cumsum(SPE_df[SPE_df.pmtID == ID].SPE.values[0]) + SPE_ID["charges"] = SPE_df[SPE_df.pmtID == ID].pe.values[0] + accep = SPE_df[SPE_df.pmtID == ID].acceptance.values[0] + threshold = min(SPE_ID[SPE_ID.cumulative >= (1 - accep)].charges.values) return threshold - -#To get nVeto plugin, it should be a best way to do that... -st = cutax.contexts.xenonnt_online() -strax_nv = st.get_single_plugin('0', 'events_nv') +# To get nVeto plugin, it should be a best way to do that... +st = cutax.contexts.xenonnt_online() +strax_nv = st.get_single_plugin("0", "events_nv") -#Quantum efficiency + +# Quantum efficiency def QE_nVeto(Q_E_nveto_file): - with open(Q_E_nveto_file,'r') as f: + with open(Q_E_nveto_file, "r") as f: data = json.loads(f.read()) - QE_array_n=[] - #nVeto - for i in np.arange(2000,2120): - QE_array_n.append(interpolate.interp1d(data['nv_pmt_qe_wavelength'],data['nv_pmt_qe'][str(i)], bounds_error=False,fill_value=0)) - #Watertank_QE - pmt_id= list(np.arange(2000,2120)) - QE_array=QE_array_n - pd_dict= {"pmt_id":pmt_id,"QE":QE_array} + QE_array_n = [] + # nVeto + for i in np.arange(2000, 2120): + QE_array_n.append( + interpolate.interp1d( + data["nv_pmt_qe_wavelength"], + data["nv_pmt_qe"][str(i)], + bounds_error=False, + fill_value=0, + ) + ) + # Watertank_QE + pmt_id = list(np.arange(2000, 2120)) + QE_array = QE_array_n + pd_dict = {"pmt_id": pmt_id, "QE": QE_array} return pd_dict -#Cluster for stacket hitlets + +# Cluster for stacket hitlets def channel_cluster_nv(t): - db_cluster = DBSCAN(eps=8, min_samples=1)#As a preliminar value we fix distance between two photons arriving in the same pmt 8ns - t_val=np.array(t) - clusters=np.array(db_cluster.fit_predict(t_val.reshape(-1, 1))) + db_cluster = DBSCAN( + eps=8, min_samples=1 + ) # As a preliminar value we fix distance between two photons arriving in the same pmt 8ns + t_val = np.array(t) + clusters = np.array(db_cluster.fit_predict(t_val.reshape(-1, 1))) return clusters - -def get_clusters_arrays(arr,typ): - arr_nv_c=np.zeros(1, dtype=typ) - arr_nv_c['n_clusters_hits']= len(arr) + + +def get_clusters_arrays(arr, typ): + arr_nv_c = np.zeros(1, dtype=typ) + arr_nv_c["n_clusters_hits"] = len(arr) for i in arr.dtype.names: # <-- CORRETTO: usare dtype.names invece di fields - if i in ['time', 'pmthitTime', 'cluster_times_ns']: + if i in ["time", "pmthitTime", "cluster_times_ns"]: arr_nv_c[i] = np.min(arr[i]) - elif i == 'endtime': + elif i == "endtime": arr_nv_c[i] = np.max(arr[i]) - elif i in ['pe_area', 'pmthitEnergy']: + elif i in ["pe_area", "pmthitEnergy"]: arr_nv_c[i] = np.sum(arr[i]) - elif i in ['evtid', 'pmthitID', 'labels']: + elif i in ["evtid", "pmthitID", "labels"]: arr_nv_c[i] = np.unique(arr[i]) - return arr_nv_c + return arr_nv_c + -#Function to use in pivot_table module (clearly something we can optimize) +# Function to use in pivot_table module (clearly something we can optimize) def recover_value(x): - m_size= np.array(x).size - if m_size==1: - ret=x - elif m_size>1: - ret=list(x) + m_size = np.array(x).size + if m_size == 1: + ret = x + elif m_size > 1: + ret = list(x) return ret - - + + def type_pri_evt(x): - if (type(x)==np.float32) or (type(x)==np.float64) or (type(x)==float): - ret=x - elif type(x)==list: - ret=x[0] + if (type(x) == np.float32) or (type(x) == np.float64) or (type(x) == float): + ret = x + elif type(x) == list: + ret = x[0] return ret -#For building independent time hitlets to compute into events or hitlets time from a source (ex: AmBe) -def time_hitlets_nv(time,ids,freq): - #freq: corresponds to the rate in [1/s] of the calibration source - if type(time)==list: - ret=min(time) + + +# For building independent time hitlets to compute into events or hitlets time from a source (ex: AmBe) +def time_hitlets_nv(time, ids, freq): + # freq: corresponds to the rate in [1/s] of the calibration source + if type(time) == list: + ret = min(time) else: - ret=time - return ret + freq*ids*1e9 - -#Fonction to transform a hitlet dataframe output into 'hitlets_nv' ndarray -dtype = [('area', np.float64), - ('amplitude', np.float64), - ('time_amplitude', np.int16), - ('entropy', np.float64), - ('range_50p_area', np.float64), - ('range_80p_area', np.float64), - ('left_area', np.float64), - ('low_left_area', np.float64), - ('range_hdr_50p_area', np.float64), - ('range_hdr_80p_area', np.float64), - ('left_hdr', np.float64), - ('low_left_hdr', np.float64), - ('fwhm', np.float64), - ('left', np.float64), - ('fwtm', np.float64), - ('low_left', np.float64), - ] -dtype = dtype + strax.interval_dtype #-> Time, length, dt, channel -#A fuse plugin is a python class that inherits from strax.Plugin -#As naming convention we use CamelCase for the class name + ret = time + return ret + freq * ids * 1e9 + + +# Fonction to transform a hitlet dataframe output into 'hitlets_nv' ndarray +dtype = [ + ("area", np.float64), + ("amplitude", np.float64), + ("time_amplitude", np.int16), + ("entropy", np.float64), + ("range_50p_area", np.float64), + ("range_80p_area", np.float64), + ("left_area", np.float64), + ("low_left_area", np.float64), + ("range_hdr_50p_area", np.float64), + ("range_hdr_80p_area", np.float64), + ("left_hdr", np.float64), + ("low_left_hdr", np.float64), + ("fwhm", np.float64), + ("left", np.float64), + ("fwtm", np.float64), + ("low_left", np.float64), +] +dtype = dtype + strax.interval_dtype # -> Time, length, dt, channel + + +# A fuse plugin is a python class that inherits from strax.Plugin +# As naming convention we use CamelCase for the class name def df_to_hit_array(data): - result = np.zeros(len(data), dtype = dtype) - result['time'] = data.time.values - result['length']= np.array([1.]*len(data)) - result['dt'] =np.array([10.]*len(data)) - result['channel']=data.pmthitID.values - result['area']= data.pe_area.values - result=strax.sort_by_time(result) + result = np.zeros(len(data), dtype=dtype) + result["time"] = data.time.values + result["length"] = np.array([1.0] * len(data)) + result["dt"] = np.array([10.0] * len(data)) + result["channel"] = data.pmthitID.values + result["area"] = data.pe_area.values + result = strax.sort_by_time(result) return result + + def hit_array_to_nvhitlet(data): - result = np.zeros(len(data), dtype = dtype) - result['time'] = data['time'] - result['length']= np.array([1.]*len(data)) - result['dt'] =np.array([10.]*len(data)) - result['channel']=data['pmthitID'] - result['area']= data['pe_area'] - result=strax.sort_by_time(result) + result = np.zeros(len(data), dtype=dtype) + result["time"] = data["time"] + result["length"] = np.array([1.0] * len(data)) + result["dt"] = np.array([10.0] * len(data)) + result["channel"] = data["pmthitID"] + result["area"] = data["pe_area"] + result = strax.sort_by_time(result) return result - -#A fuse plugin is a python class that inherits from strax.Plugin -#As naming convention we use CamelCase for the class name + + +# A fuse plugin is a python class that inherits from strax.Plugin +# As naming convention we use CamelCase for the class name class NeutronVetoHitlets(strax.Plugin): - #Each plugin has a version number - #If the version number changes, fuse will know that it need to re-simulate the data + # Each plugin has a version number + # If the version number changes, fuse will know that it need to re-simulate the data __version__ = "0.0.1" - - #You need to tell fuse and strax what the plugin needs as input - #In this case we need nv_pmthits - depends_on = ("nv_pmthits") - - #You need to tell fuse and strax what the plugin provides as output - #In this case we provide nv_hitlets - #You can later use st.make(run_number, "nv_hitlets") to run the simulation + + # You need to tell fuse and strax what the plugin needs as input + # In this case we need nv_pmthits + depends_on = "nv_pmthits" + + # You need to tell fuse and strax what the plugin provides as output + # In this case we provide nv_hitlets + # You can later use st.make(run_number, "nv_hitlets") to run the simulation provides = "nv_hitlets" - - #You need to tell fuse and strax what the data looks like - #Data of the same data_kind can be combined via "horizontal" concatenation and need - #to have the same output length. - data_kind = 'nv_hitlets' - - #You also need to tell strax what columns the data has - #A column needs a name and a numpy data type. I set everything to float64 here, we can reduce it later - #I used the columns described here: + + # You need to tell fuse and strax what the data looks like + # Data of the same data_kind can be combined via "horizontal" concatenation and need + # to have the same output length. + data_kind = "nv_hitlets" + + # You also need to tell strax what columns the data has + # A column needs a name and a numpy data type. I set everything to float64 here, we can reduce it later + # I used the columns described here: # https://github.com/XENONnT/fuse/blob/f777d9c281d9e046c2f322a30b462a9b7dd8ee00/test_nv/Hitlet_nv_fuse.py#L114 - - #We need to disable automatic rechunking for fuse plugins - #As fuse is going from "leightweigt" data to "heavy" data, - #automatic rechunking can lead to problems in later plugins + + # We need to disable automatic rechunking for fuse plugins + # As fuse is going from "leightweigt" data to "heavy" data, + # automatic rechunking can lead to problems in later plugins rechunk_on_save = False - #We need to specify when we want to save the data + # We need to specify when we want to save the data save_when = strax.SaveWhen.TARGET - #strax uses a rather short timeout, lets increase it as - #some of the fuse simulation steps can take a while + # strax uses a rather short timeout, lets increase it as + # some of the fuse simulation steps can take a while input_timeout = FUSE_PLUGIN_TIMEOUT - - #We need to tell strax what config options the plugin needs - #We will use the great URLConfigs that are a part of straxen + + # We need to tell strax what config options the plugin needs + # We will use the great URLConfigs that are a part of straxen debug = straxen.URLConfig( - default=False, type=bool,track=False, - help='Show debug informations', + default=False, + type=bool, + track=False, + help="Show debug informations", ) deterministic_seed = straxen.URLConfig( - default=True, type=bool, - help='Set the random seed from lineage and run_id, or pull the seed from the OS.', + default=True, + type=bool, + help="Set the random seed from lineage and run_id, or pull the seed from the OS.", ) + def __init__(self, sr=0): - self.path = '/home/digangi/private_nt_aux_files/sim_files/' #pietro #Have to put here the correct paths.... - self.QE_value = QE_nVeto(self.path+'nveto_pmt_qe.json') - self.SPE_nVeto = SPE_parameters(self.path+'SPE_SR'+str(sr)+'.npy') #pietro - self.dtype=dtype - - #Get Quantum efficiency - def QE_E(self,E,ID): - WL= energytowavelenght(E) - ind=ID-2000 - qe=self.QE_value['QE'][ind](WL) + self.path = "/home/digangi/private_nt_aux_files/sim_files/" # pietro #Have to put here the correct paths.... + self.QE_value = QE_nVeto(self.path + "nveto_pmt_qe.json") + self.SPE_nVeto = SPE_parameters(self.path + "SPE_SR" + str(sr) + ".npy") # pietro + self.dtype = dtype + + # Get Quantum efficiency + def QE_E(self, E, ID): + WL = energytowavelenght(E) + ind = ID - 2000 + qe = self.QE_value["QE"][ind](WL) return qe - - def get_acceptance(self,ID): - acc=self.SPE_nVeto[self.SPE_nVeto['pmtID']==ID]['acceptance'] + + def get_acceptance(self, ID): + acc = self.SPE_nVeto[self.SPE_nVeto["pmtID"] == ID]["acceptance"] return acc - #Get acceptance threshold - def get_threshold_acc(self,ID): - ind=ID-2000 - threshold= self.SPE_nVeto.threshold_pe.values[ind] + # Get acceptance threshold + def get_threshold_acc(self, ID): + ind = ID - 2000 + threshold = self.SPE_nVeto.threshold_pe.values[ind] return threshold - - #Sampling charge from SPE - def pe_charge_N(self,pmt_id): - SPE_channel= self.SPE_nVeto[self.SPE_nVeto.pmtID==pmt_id] - charge=rd.choices(SPE_channel['pe'][0],SPE_channel['SPE_values'][0],k=1)[0] + + # Sampling charge from SPE + def pe_charge_N(self, pmt_id): + SPE_channel = self.SPE_nVeto[self.SPE_nVeto.pmtID == pmt_id] + charge = rd.choices(SPE_channel["pe"][0], SPE_channel["SPE_values"][0], k=1)[0] return charge - #--------------------------- Hitlet function ------------------------------------------------------------# + # --------------------------- Hitlet function ------------------------------------------------------------# - def _nv_hitlets(self, pmthits, CE_Scaling=0.75, Stacked='No', period = 1.): - - #-------------------------------------------------Arguments---------------------------------------------------# - #QE_Scaling corrrespond to collection efficiency, no study has been done on the CE of muon Veto we use a default value close to the nVeto see https://xe1t-wiki.lngs.infn.it/doku.php?id=xenon:mancuso:hitletsimulator:collection_efficiency - #period : this could be related to the rate of a source, or the rate for a time bin if we reconstruct an spectrum. If no source 1 second is the default value (see Comments) + def _nv_hitlets(self, pmthits, CE_Scaling=0.75, Stacked="No", period=1.0): - - #----------------------------------------------Commments-----------------------------------------------------------------#: - #1.There is no application of a threshold per channel based on the acceptation by default, but we keep the value in the data frame for each pmt, and one can do manually. This is in order to not condition the sampling, and compare it with the data with different cuts. - #.2. The period is set by default at 1s to care about no pyle up or merge of hitlets if one want to do an analysis for rare events (independent non sourced ones). If we simulate a calibration or a constant flux this value has to be changed to real rate one. + # -------------------------------------------------Arguments---------------------------------------------------# + # QE_Scaling corrrespond to collection efficiency, no study has been done on the CE of muon Veto we use a default value close to the nVeto see https://xe1t-wiki.lngs.infn.it/doku.php?id=xenon:mancuso:hitletsimulator:collection_efficiency + # period : this could be related to the rate of a source, or the rate for a time bin if we reconstruct an spectrum. If no source 1 second is the default value (see Comments) - - - #0.---------------Load GEANT output-------------------# + # ----------------------------------------------Commments-----------------------------------------------------------------#: + # 1.There is no application of a threshold per channel based on the acceptation by default, but we keep the value in the data frame for each pmt, and one can do manually. This is in order to not condition the sampling, and compare it with the data with different cuts. + # .2. The period is set by default at 1s to care about no pyle up or merge of hitlets if one want to do an analysis for rare events (independent non sourced ones). If we simulate a calibration or a constant flux this value has to be changed to real rate one. + # 0.---------------Load GEANT output-------------------# - # Adding extra fields to pmthits np.array extra_fields = [ - ('pe_area', '=2000)&(pmthits['pmthitID']<2121) - pmthits=pmthits[mask] + mask = (pmthits["pmthitID"] >= 2000) & (pmthits["pmthitID"] < 2121) + pmthits = pmthits[mask] - #1. First step PHOTON to first dinode + # 1. First step PHOTON to first dinode print("Applying QE and CE") # Applying Quantum efficiency for each pmt - qe = 1e-2*np.vectorize(self.QE_E)(pmthits['pmthitEnergy'],pmthits['pmthitID']) + qe = 1e-2 * np.vectorize(self.QE_E)(pmthits["pmthitEnergy"], pmthits["pmthitID"]) # Applying effective collection efficiency - qe *= CE_Scaling + qe *= CE_Scaling # Applying acceptance per pmt: for the approach in which SPE PDF has already applied a threshold for low charges - qe = qe*np.vectorize(self.get_acceptance)(pmthits['pmthitID']) + qe = qe * np.vectorize(self.get_acceptance)(pmthits["pmthitID"]) # Generate a photoelectron based on (binomial) conversion probability qe*eCE*spe_acc pe = np.array([np.random.binomial(1, j, 1)[0] for j in qe]) # Discard pmthits which do not generate a pe print("Loading hit survive") - maks_qe = pe>0 - pmthits=pmthits[maks_qe] - - #2. Sampling charge from SPE for each pmthit with a generated pe + maks_qe = pe > 0 + pmthits = pmthits[maks_qe] + + # 2. Sampling charge from SPE for each pmthit with a generated pe print("Sampling hitlets charge pe") - pmthits['pe_area'] = np.vectorize(self.pe_charge_N)(pmthits['pmthitID']) - - #3. Creating hitlet times - print('Getting time hitlets') - times=[] - for i in np.unique(pmthits['evtid']): - mask = pmthits['evtid'] == i + pmthits["pe_area"] = np.vectorize(self.pe_charge_N)(pmthits["pmthitID"]) + + # 3. Creating hitlet times + print("Getting time hitlets") + times = [] + for i in np.unique(pmthits["evtid"]): + mask = pmthits["evtid"] == i pmthits_evt = pmthits[mask] - cluster_times_ns = pmthits_evt['pmthitTime'] - min(pmthits_evt['pmthitTime']) + cluster_times_ns = pmthits_evt["pmthitTime"] - min(pmthits_evt["pmthitTime"]) times.append(cluster_times_ns) - pmthits['cluster_times_ns'] = np.concatenate(times) - - if Stacked=='No': + pmthits["cluster_times_ns"] = np.concatenate(times) + + if Stacked == "No": return pmthits - #3.1 Stacked hitlets: this correspond to hitlets in the same pmt with a time difference below some estimated time response of the Channel (8 ns, i.e. 4 samples). - elif Stacked =='yes': - - print('Looking for stacked hitlets') - #Here we set times related to the first hit, we only use that for stacket hitlets - arr_c_evt=[] - for i in tq.tqdm(np.unique(pmthits['evtid'])): - arr_evt = pmthits[pmthits['evtid']==i] - arr_c_pmt=[] - for j in np.unique(arr_evt['pmthitID']): - arr_pmt = arr_evt[arr_evt['pmthitID']==j] - labels = channel_cluster_nv(arr_pmt['cluster_times_ns']) - arr_pmt['labels'] = labels - arr_c = np.concatenate([get_clusters_arrays(arr_pmt[arr_pmt['labels'] == l], new_dtype) for l in np.unique(labels)]) - #arr_c =np.concatenate([get_clusters_arrays(arr_pmt[arr_pmt['labels']==l],dtypes) for l in np.unique(labels)]) + # 3.1 Stacked hitlets: this correspond to hitlets in the same pmt with a time difference below some estimated time response of the Channel (8 ns, i.e. 4 samples). + elif Stacked == "yes": + + print("Looking for stacked hitlets") + # Here we set times related to the first hit, we only use that for stacket hitlets + arr_c_evt = [] + for i in tq.tqdm(np.unique(pmthits["evtid"])): + arr_evt = pmthits[pmthits["evtid"] == i] + arr_c_pmt = [] + for j in np.unique(arr_evt["pmthitID"]): + arr_pmt = arr_evt[arr_evt["pmthitID"] == j] + labels = channel_cluster_nv(arr_pmt["cluster_times_ns"]) + arr_pmt["labels"] = labels + arr_c = np.concatenate( + [ + get_clusters_arrays(arr_pmt[arr_pmt["labels"] == l], new_dtype) + for l in np.unique(labels) + ] + ) + # arr_c =np.concatenate([get_clusters_arrays(arr_pmt[arr_pmt['labels']==l],dtypes) for l in np.unique(labels)]) arr_c_pmt.append(arr_c) arr_c_evt.append(np.concatenate(arr_c_pmt)) return np.concatenate(arr_c_evt) - + def setup(self): - #All plugins can report problmes or debug information via the logging feature - #You can set the log level via the debug config option. - #WARNING messages are always shown whild DEBUG messages are only shown if debug is True + # All plugins can report problmes or debug information via the logging feature + # You can set the log level via the debug config option. + # WARNING messages are always shown whild DEBUG messages are only shown if debug is True if self.debug: - log.setLevel('DEBUG') + log.setLevel("DEBUG") log.debug(f"Running NeutronVetoHitlets version {self.__version__} in debug mode") - else: - log.setLevel('WARNING') - - #Many plugins need to generate random numbers for simulation the corresponding physics process - #In fuse we want to make sure that the simulation is reproducible. - #Therefore we have the default setting of deterministic_seed = True - #In this case the random seed is generated from the run_id and the lineage - #The lineage includes all plugins and their verions that are connected to the input of the - #current plugin as well as all tracked config options and the strax version. - #The run_id is a user input. More on the deterministic seed can be found in - #a dedicated notebook. - #Please make sure that you use the random number generator self.rng when you need random numbers - #later in the plugin. + else: + log.setLevel("WARNING") + + # Many plugins need to generate random numbers for simulation the corresponding physics process + # In fuse we want to make sure that the simulation is reproducible. + # Therefore we have the default setting of deterministic_seed = True + # In this case the random seed is generated from the run_id and the lineage + # The lineage includes all plugins and their verions that are connected to the input of the + # current plugin as well as all tracked config options and the strax version. + # The run_id is a user input. More on the deterministic seed can be found in + # a dedicated notebook. + # Please make sure that you use the random number generator self.rng when you need random numbers + # later in the plugin. if self.deterministic_seed: hash_string = strax.deterministic_hash((self.run_id, self.lineage)) seed = int(hash_string.encode().hex(), 16) - self.rng = np.random.default_rng(seed = seed) + self.rng = np.random.default_rng(seed=seed) log.debug(f"Generating random numbers from seed {seed}") - else: + else: self.rng = np.random.default_rng() log.debug(f"Generating random numbers with seed pulled from OS") - - #The compute method is the heart of the plugin. It is executed for each chunk of input data and - #must produce data in the format specified in the self.dtype variable. - def compute(self, nv_pmthits, eCE=0.75, Stacked_opt='No', rate = 1.): - #Make sure your plugin can handle empty inputs + # The compute method is the heart of the plugin. It is executed for each chunk of input data and + # must produce data in the format specified in the self.dtype variable. + def compute(self, nv_pmthits, eCE=0.75, Stacked_opt="No", rate=1.0): + # Make sure your plugin can handle empty inputs if len(nv_pmthits) == 0: return np.zeros(0, self.dtype) - hitlets= self._nv_hitlets(nv_pmthits,CE_Scaling=eCE, Stacked=Stacked_opt, period = rate) - #All your NV goes here + hitlets = self._nv_hitlets(nv_pmthits, CE_Scaling=eCE, Stacked=Stacked_opt, period=rate) + # All your NV goes here result = hit_array_to_nvhitlet(hitlets) return result From c65634769ff2bd29c63fb573722e507efa985967 Mon Sep 17 00:00:00 2001 From: Pietro Di Gangi Date: Fri, 14 Feb 2025 02:19:01 -0600 Subject: [PATCH 18/29] code (commented) for json input spe files --- fuse/plugins/neutron_veto/nvhitlets.py | 27 +++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/fuse/plugins/neutron_veto/nvhitlets.py b/fuse/plugins/neutron_veto/nvhitlets.py index 01a336bf..71f8eb21 100644 --- a/fuse/plugins/neutron_veto/nvhitlets.py +++ b/fuse/plugins/neutron_veto/nvhitlets.py @@ -57,13 +57,20 @@ def create_SPE_file(path, sr="0"): # SPE parameters: ID, pe, SPE, acceptance def SPE_parameters(file_spe_model): - data_spe = np.load(file_spe_model, allow_pickle=True) + data_spe = np.load(file_spe_model, allow_pickle=True) #pietro del. + ''' Pietro add. json spe input + with open(file_spe_model, 'r') as f: + data_spe = json.load(f) + data_dict = {entry['pmtID']: entry for entry in data_spe} + ''' + # SPE_ch= pd.DataFrame(columns=['pmtID','pe','SPE','acceptance']) # SPE_ch['pmtID'],SPE_ch['pe'], SPE_ch['SPE'],SPE_ch['acceptance']=data_spe['pmtID'],data_spe['charge'],data_spe['SPE_values'],data_spe['acceptance'] # acceptance_ch= [threshold_acc(SPE_ch,i) for i in np.arange(2000,2120)] # SPE_ch['threshold_pe']=acceptance_ch - return data_spe - + + return data_spe # pietro del + #return data_dict # pietro add def threshold_acc(SPE_df, ID): SPE_ID = pd.DataFrame() @@ -258,7 +265,8 @@ class NeutronVetoHitlets(strax.Plugin): def __init__(self, sr=0): self.path = "/home/digangi/private_nt_aux_files/sim_files/" # pietro #Have to put here the correct paths.... self.QE_value = QE_nVeto(self.path + "nveto_pmt_qe.json") - self.SPE_nVeto = SPE_parameters(self.path + "SPE_SR" + str(sr) + ".npy") # pietro + self.SPE_nVeto = SPE_parameters(self.path + "SPE_SR" + str(sr) + ".npy") # pietro del. + #self.SPE_nVeto = SPE_parameters(self.path+'nveto_spe_sr'+str(sr)+'.json') # pietro add. json spe input self.dtype = dtype # Get Quantum efficiency @@ -269,7 +277,8 @@ def QE_E(self, E, ID): return qe def get_acceptance(self, ID): - acc = self.SPE_nVeto[self.SPE_nVeto["pmtID"] == ID]["acceptance"] + acc = self.SPE_nVeto[self.SPE_nVeto["pmtID"] == ID]["acceptance"] # pietro del. + #acc = self.SPE_nVeto.get(ID)['acceptance'] # pietro add. json spe input return acc # Get acceptance threshold @@ -280,8 +289,12 @@ def get_threshold_acc(self, ID): # Sampling charge from SPE def pe_charge_N(self, pmt_id): - SPE_channel = self.SPE_nVeto[self.SPE_nVeto.pmtID == pmt_id] - charge = rd.choices(SPE_channel["pe"][0], SPE_channel["SPE_values"][0], k=1)[0] + SPE_channel = self.SPE_nVeto[self.SPE_nVeto.pmtID == pmt_id] # pietro del. + charge = rd.choices(SPE_channel["pe"][0], SPE_channel["SPE_values"][0], k=1)[0] # pietro del. + + #SPE_channel = self.SPE_nVeto.get(pmt_id) # pietro add. json spe input + #charge=rd.choices(SPE_channel['pe'],SPE_channel['SPE_values'],k=1)[0] #pietro add. json spe input + return charge # --------------------------- Hitlet function ------------------------------------------------------------# From af6b2cee27ed30f5092c5669420a0fdb37918079 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 14 Feb 2025 08:20:01 +0000 Subject: [PATCH 19/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- fuse/plugins/neutron_veto/nvhitlets.py | 40 ++++++++++++++------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/fuse/plugins/neutron_veto/nvhitlets.py b/fuse/plugins/neutron_veto/nvhitlets.py index 71f8eb21..5d92e16f 100644 --- a/fuse/plugins/neutron_veto/nvhitlets.py +++ b/fuse/plugins/neutron_veto/nvhitlets.py @@ -57,20 +57,22 @@ def create_SPE_file(path, sr="0"): # SPE parameters: ID, pe, SPE, acceptance def SPE_parameters(file_spe_model): - data_spe = np.load(file_spe_model, allow_pickle=True) #pietro del. - ''' Pietro add. json spe input - with open(file_spe_model, 'r') as f: - data_spe = json.load(f) - data_dict = {entry['pmtID']: entry for entry in data_spe} - ''' - + data_spe = np.load(file_spe_model, allow_pickle=True) # pietro del. + """Pietro add. + + json spe input with open(file_spe_model, 'r') as f: data_spe = + json.load(f) data_dict = {entry['pmtID']: entry for entry in + data_spe} + """ + # SPE_ch= pd.DataFrame(columns=['pmtID','pe','SPE','acceptance']) # SPE_ch['pmtID'],SPE_ch['pe'], SPE_ch['SPE'],SPE_ch['acceptance']=data_spe['pmtID'],data_spe['charge'],data_spe['SPE_values'],data_spe['acceptance'] # acceptance_ch= [threshold_acc(SPE_ch,i) for i in np.arange(2000,2120)] # SPE_ch['threshold_pe']=acceptance_ch - - return data_spe # pietro del - #return data_dict # pietro add + + return data_spe # pietro del + # return data_dict # pietro add + def threshold_acc(SPE_df, ID): SPE_ID = pd.DataFrame() @@ -266,7 +268,7 @@ def __init__(self, sr=0): self.path = "/home/digangi/private_nt_aux_files/sim_files/" # pietro #Have to put here the correct paths.... self.QE_value = QE_nVeto(self.path + "nveto_pmt_qe.json") self.SPE_nVeto = SPE_parameters(self.path + "SPE_SR" + str(sr) + ".npy") # pietro del. - #self.SPE_nVeto = SPE_parameters(self.path+'nveto_spe_sr'+str(sr)+'.json') # pietro add. json spe input + # self.SPE_nVeto = SPE_parameters(self.path+'nveto_spe_sr'+str(sr)+'.json') # pietro add. json spe input self.dtype = dtype # Get Quantum efficiency @@ -277,8 +279,8 @@ def QE_E(self, E, ID): return qe def get_acceptance(self, ID): - acc = self.SPE_nVeto[self.SPE_nVeto["pmtID"] == ID]["acceptance"] # pietro del. - #acc = self.SPE_nVeto.get(ID)['acceptance'] # pietro add. json spe input + acc = self.SPE_nVeto[self.SPE_nVeto["pmtID"] == ID]["acceptance"] # pietro del. + # acc = self.SPE_nVeto.get(ID)['acceptance'] # pietro add. json spe input return acc # Get acceptance threshold @@ -289,12 +291,14 @@ def get_threshold_acc(self, ID): # Sampling charge from SPE def pe_charge_N(self, pmt_id): - SPE_channel = self.SPE_nVeto[self.SPE_nVeto.pmtID == pmt_id] # pietro del. - charge = rd.choices(SPE_channel["pe"][0], SPE_channel["SPE_values"][0], k=1)[0] # pietro del. + SPE_channel = self.SPE_nVeto[self.SPE_nVeto.pmtID == pmt_id] # pietro del. + charge = rd.choices(SPE_channel["pe"][0], SPE_channel["SPE_values"][0], k=1)[ + 0 + ] # pietro del. + + # SPE_channel = self.SPE_nVeto.get(pmt_id) # pietro add. json spe input + # charge=rd.choices(SPE_channel['pe'],SPE_channel['SPE_values'],k=1)[0] #pietro add. json spe input - #SPE_channel = self.SPE_nVeto.get(pmt_id) # pietro add. json spe input - #charge=rd.choices(SPE_channel['pe'],SPE_channel['SPE_values'],k=1)[0] #pietro add. json spe input - return charge # --------------------------- Hitlet function ------------------------------------------------------------# From c83bb4b5cc6e70c662fafeea709a02663dc9959f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henning=20Schulze=20Ei=C3=9Fing?= Date: Fri, 14 Feb 2025 10:57:56 +0100 Subject: [PATCH 20/29] fix some awkward code --- fuse/common.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fuse/common.py b/fuse/common.py index dffe9db9..696000a7 100644 --- a/fuse/common.py +++ b/fuse/common.py @@ -97,13 +97,12 @@ def dynamic_chunking_two_outputs( def full_array_to_numpy(array, dtype): - len_output = len(awkward_to_flat_numpy(array["x"])) + len_output = len(awkward_to_flat_numpy(array[array.fields[0]])) numpy_data = np.zeros(len_output, dtype=dtype) for field in array.fields: numpy_data[field] = awkward_to_flat_numpy(array[field]) - return numpy_data From 1e376dfaecf4f274602c1665c06438238826a1d8 Mon Sep 17 00:00:00 2001 From: Pietro Di Gangi Date: Fri, 14 Feb 2025 06:09:14 -0600 Subject: [PATCH 21/29] handling input nv spe files in json format --- fuse/plugins/neutron_veto/nvhitlets.py | 29 +++++++------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/fuse/plugins/neutron_veto/nvhitlets.py b/fuse/plugins/neutron_veto/nvhitlets.py index 5d92e16f..333706db 100644 --- a/fuse/plugins/neutron_veto/nvhitlets.py +++ b/fuse/plugins/neutron_veto/nvhitlets.py @@ -57,21 +57,15 @@ def create_SPE_file(path, sr="0"): # SPE parameters: ID, pe, SPE, acceptance def SPE_parameters(file_spe_model): - data_spe = np.load(file_spe_model, allow_pickle=True) # pietro del. - """Pietro add. - - json spe input with open(file_spe_model, 'r') as f: data_spe = - json.load(f) data_dict = {entry['pmtID']: entry for entry in - data_spe} - """ - + with open(file_spe_model, 'r') as f: + data_spe = json.load(f) + data_dict = {entry['pmtID']: entry for entry in data_spe} # SPE_ch= pd.DataFrame(columns=['pmtID','pe','SPE','acceptance']) # SPE_ch['pmtID'],SPE_ch['pe'], SPE_ch['SPE'],SPE_ch['acceptance']=data_spe['pmtID'],data_spe['charge'],data_spe['SPE_values'],data_spe['acceptance'] # acceptance_ch= [threshold_acc(SPE_ch,i) for i in np.arange(2000,2120)] # SPE_ch['threshold_pe']=acceptance_ch - return data_spe # pietro del - # return data_dict # pietro add + return data_dict def threshold_acc(SPE_df, ID): @@ -267,8 +261,7 @@ class NeutronVetoHitlets(strax.Plugin): def __init__(self, sr=0): self.path = "/home/digangi/private_nt_aux_files/sim_files/" # pietro #Have to put here the correct paths.... self.QE_value = QE_nVeto(self.path + "nveto_pmt_qe.json") - self.SPE_nVeto = SPE_parameters(self.path + "SPE_SR" + str(sr) + ".npy") # pietro del. - # self.SPE_nVeto = SPE_parameters(self.path+'nveto_spe_sr'+str(sr)+'.json') # pietro add. json spe input + self.SPE_nVeto = SPE_parameters(self.path+'nveto_spe_sr'+str(sr)+'.json') self.dtype = dtype # Get Quantum efficiency @@ -279,8 +272,7 @@ def QE_E(self, E, ID): return qe def get_acceptance(self, ID): - acc = self.SPE_nVeto[self.SPE_nVeto["pmtID"] == ID]["acceptance"] # pietro del. - # acc = self.SPE_nVeto.get(ID)['acceptance'] # pietro add. json spe input + acc = self.SPE_nVeto.get(ID)['acceptance'] return acc # Get acceptance threshold @@ -291,13 +283,8 @@ def get_threshold_acc(self, ID): # Sampling charge from SPE def pe_charge_N(self, pmt_id): - SPE_channel = self.SPE_nVeto[self.SPE_nVeto.pmtID == pmt_id] # pietro del. - charge = rd.choices(SPE_channel["pe"][0], SPE_channel["SPE_values"][0], k=1)[ - 0 - ] # pietro del. - - # SPE_channel = self.SPE_nVeto.get(pmt_id) # pietro add. json spe input - # charge=rd.choices(SPE_channel['pe'],SPE_channel['SPE_values'],k=1)[0] #pietro add. json spe input + SPE_channel = self.SPE_nVeto.get(pmt_id) + charge=rd.choices(SPE_channel['pe'],SPE_channel['SPE_values'],k=1)[0] return charge From 7500038379289bab8438002b50419fb234ec2892 Mon Sep 17 00:00:00 2001 From: Pietro Di Gangi Date: Fri, 14 Feb 2025 08:57:15 -0600 Subject: [PATCH 22/29] tentative urlconfig setup for nv qe and spe inputs --- fuse/context.py | 25 +++++++++++++++++++++++++ fuse/plugins/neutron_veto/nvhitlets.py | 12 +++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/fuse/context.py b/fuse/context.py index 9ab82af6..1725ed2d 100644 --- a/fuse/context.py +++ b/fuse/context.py @@ -294,6 +294,31 @@ def modify_s2_pattern_map( return s2_pattern_map +@URLConfig.register("nveto_pmt_qe") +def nveto_pmt_qe_dict(data): + """Get NV PMT quantum efficiecny values and interpolate.""" + #with open(Q_E_nveto_file,'r') as f: + # data = json.loads(f.read()) + QE_array_n=[] + #nVeto + for i in np.arange(2000,2120): + QE_array_n.append(interpolate.interp1d(data['nv_pmt_qe_wavelength'],data['nv_pmt_qe'][str(i)], bounds_error=False,fill_value=0)) + #Watertank_QE + pmt_id= list(np.arange(2000,2120)) + QE_array=QE_array_n + pd_dict= {"pmt_id":pmt_id,"QE":QE_array} + return pd_dict + + +@URLConfig.register("nveto_spe_sr1") +def nveto_pmt_qe_dict(data_spe): + """Get dictionary with NV SPE parameters.""" + #with open(file_spe_model, 'r') as f: + # data_spe = json.load(f) + data_dict = {entry['pmtID']: entry for entry in data_spe} + return data_dict + + # Probably not needed! @URLConfig.register("simple_load") def load(data): diff --git a/fuse/plugins/neutron_veto/nvhitlets.py b/fuse/plugins/neutron_veto/nvhitlets.py index 333706db..ad27f0d1 100644 --- a/fuse/plugins/neutron_veto/nvhitlets.py +++ b/fuse/plugins/neutron_veto/nvhitlets.py @@ -258,8 +258,18 @@ class NeutronVetoHitlets(strax.Plugin): help="Set the random seed from lineage and run_id, or pull the seed from the OS.", ) + nveto_pmt_qe = straxen.URLConfig( + default="nveto_pmt_qe://resource://simulation_config://" + "SIMULATION_CONFIG_FILE.json?&key=nveto_pmt_qe", + help="Quantum efficiency of NV PMTs" + + nveto_spe_sr1 = straxen.URLConfig( + default="nveto_spe_sr1://resource://simulation_config://" + "SIMULATION_CONFIG_FILE.json?&key=nveto_spe_sr1", + help="SR1 SPE model of NV PMTs" + def __init__(self, sr=0): - self.path = "/home/digangi/private_nt_aux_files/sim_files/" # pietro #Have to put here the correct paths.... + self.path = "/home/digangi/private_nt_aux_files/sim_files/" # pietro - need to modify this to work with urlconfig self.QE_value = QE_nVeto(self.path + "nveto_pmt_qe.json") self.SPE_nVeto = SPE_parameters(self.path+'nveto_spe_sr'+str(sr)+'.json') self.dtype = dtype From 1da4ed3812ad7132aa36117d4c711f6f5570617c Mon Sep 17 00:00:00 2001 From: Pietro Di Gangi Date: Fri, 14 Feb 2025 08:59:26 -0600 Subject: [PATCH 23/29] fix typo --- fuse/context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuse/context.py b/fuse/context.py index 1725ed2d..0180d6c4 100644 --- a/fuse/context.py +++ b/fuse/context.py @@ -311,7 +311,7 @@ def nveto_pmt_qe_dict(data): @URLConfig.register("nveto_spe_sr1") -def nveto_pmt_qe_dict(data_spe): +def nveto_spe_sr1_dict(data_spe): """Get dictionary with NV SPE parameters.""" #with open(file_spe_model, 'r') as f: # data_spe = json.load(f) From 0d4d075f956b64d8860f8c3dc3f194d5e8b97e8c Mon Sep 17 00:00:00 2001 From: Alexander Deisting <42942024+deisting@users.noreply.github.com> Date: Tue, 25 Feb 2025 12:14:17 +0100 Subject: [PATCH 24/29] Fixing missing brackets in nvhitlets.py --- fuse/plugins/neutron_veto/nvhitlets.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fuse/plugins/neutron_veto/nvhitlets.py b/fuse/plugins/neutron_veto/nvhitlets.py index ad27f0d1..095e9bdf 100644 --- a/fuse/plugins/neutron_veto/nvhitlets.py +++ b/fuse/plugins/neutron_veto/nvhitlets.py @@ -261,12 +261,14 @@ class NeutronVetoHitlets(strax.Plugin): nveto_pmt_qe = straxen.URLConfig( default="nveto_pmt_qe://resource://simulation_config://" "SIMULATION_CONFIG_FILE.json?&key=nveto_pmt_qe", - help="Quantum efficiency of NV PMTs" + help="Quantum efficiency of NV PMTs", + ) nveto_spe_sr1 = straxen.URLConfig( default="nveto_spe_sr1://resource://simulation_config://" "SIMULATION_CONFIG_FILE.json?&key=nveto_spe_sr1", - help="SR1 SPE model of NV PMTs" + help="SR1 SPE model of NV PMTs", + ) def __init__(self, sr=0): self.path = "/home/digangi/private_nt_aux_files/sim_files/" # pietro - need to modify this to work with urlconfig From bd92920468cdf40d9c7db3d6551c275bb08f8c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henning=20Schulze=20Ei=C3=9Fing?= Date: Fri, 23 May 2025 10:30:02 +0200 Subject: [PATCH 25/29] Resolving merge problems --- fuse/context_utils.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/fuse/context_utils.py b/fuse/context_utils.py index 228d0dd1..e9506013 100644 --- a/fuse/context_utils.py +++ b/fuse/context_utils.py @@ -3,6 +3,7 @@ import straxen from straxen import URLConfig from copy import deepcopy +from scipy import interpolate def write_sr_information_to_config(context, corrections_run_id): @@ -166,3 +167,27 @@ def lce_from_pattern_map(map, pmt_mask): lcemap.data["map"] = np.sum(lcemap.data["map"][:][:][:], axis=3, keepdims=True, where=pmt_mask) lcemap.__init__(lcemap.data) return lcemap + +@URLConfig.register("nveto_pmt_qe") +def nveto_pmt_qe_dict(data): + """Get NV PMT quantum efficiecny values and interpolate.""" + #with open(Q_E_nveto_file,'r') as f: + # data = json.loads(f.read()) + QE_array_n=[] + #nVeto + for i in np.arange(2000,2120): + QE_array_n.append(interpolate.interp1d(data['nv_pmt_qe_wavelength'],data['nv_pmt_qe'][str(i)], bounds_error=False,fill_value=0)) + #Watertank_QE + pmt_id= list(np.arange(2000,2120)) + QE_array=QE_array_n + pd_dict= {"pmt_id":pmt_id,"QE":QE_array} + return pd_dict + + +@URLConfig.register("nveto_spe_sr1") +def nveto_spe_sr1_dict(data_spe): + """Get dictionary with NV SPE parameters.""" + #with open(file_spe_model, 'r') as f: + # data_spe = json.load(f) + data_dict = {entry['pmtID']: entry for entry in data_spe} + return data_dict \ No newline at end of file From ee2822bad08448a27c69a4c8299fb03b22e222b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henning=20Schulze=20Ei=C3=9Fing?= Date: Fri, 23 May 2025 13:39:17 +0200 Subject: [PATCH 26/29] Clean up NeutronVetoHitlets code --- fuse/dtypes.py | 23 ++ fuse/plugins/neutron_veto/nvhitlets.py | 501 +++++++++---------------- 2 files changed, 203 insertions(+), 321 deletions(-) diff --git a/fuse/dtypes.py b/fuse/dtypes.py index 2d9122dc..917b183c 100644 --- a/fuse/dtypes.py +++ b/fuse/dtypes.py @@ -75,3 +75,26 @@ (("ID of the cluster creating the photon", "cluster_id"), np.int32), (("Type of the photon. S1 (1), S2 (2) or PMT AP (0)", "photon_type"), np.int8), ] + + +# @ Experts: Please add a description of the fields in the dtype +# See fields above for inspiration. +# Is float64 necessary? Switch to float32 if possible to save space. +neutron_veto_hitlet_dtype = [ + ("area", np.float64), + ("amplitude", np.float64), + ("time_amplitude", np.int16), + ("entropy", np.float64), + ("range_50p_area", np.float64), + ("range_80p_area", np.float64), + ("left_area", np.float64), + ("low_left_area", np.float64), + ("range_hdr_50p_area", np.float64), + ("range_hdr_80p_area", np.float64), + ("left_hdr", np.float64), + ("low_left_hdr", np.float64), + ("fwhm", np.float64), + ("left", np.float64), + ("fwtm", np.float64), + ("low_left", np.float64), +] \ No newline at end of file diff --git a/fuse/plugins/neutron_veto/nvhitlets.py b/fuse/plugins/neutron_veto/nvhitlets.py index 095e9bdf..c518d789 100644 --- a/fuse/plugins/neutron_veto/nvhitlets.py +++ b/fuse/plugins/neutron_veto/nvhitlets.py @@ -1,308 +1,86 @@ import strax import straxen import numpy as np -import logging - -logging.basicConfig(handlers=[logging.StreamHandler()]) -log = logging.getLogger("fuse.neutron_veto.nvhitlets") - -from ...common import FUSE_PLUGIN_TIMEOUT - -import uproot as rt -import json -import scipy as scp -from scipy import interpolate import scipy.constants as const -import awkward as ak -import pandas as pd from sklearn.cluster import DBSCAN -import random as rd -from tqdm import tqdm -import tqdm.notebook as tq -import time -import cutax - -##--------------------------------------------COMMENTS------------------------------------------------------------## - -# This hitlet simulator is an extension of the work of Diego Ramirez, Daniel Wenz, Andrea Mancuso and Pavel Kavrigin. -# Functions of SPE charge sampling are in the PDF function based in the calibrations of nVeto done by Andrea Mancuso. Daniel Wenz provides a code that takes into account this functions to sample the charge, where this code is based. This leads to SPE a PDF function to sample charge in the hitlet simulator. -# We use the Quantum efficiencies QE for each PMT as a wavelength function for nVeto provided by Andrea Mancuso. - -# WIKI NOTES: -# https://xe1t-wiki.lngs.infn.it/doku.php?id=xenon:xenonnt:layos:nveto_hitlet_into_fuse - - -# --------------------------------------------------------HITLETS AUXILIAR FUNCTIONS--------------------------------------------------------------------# -def flat_list(l): - return np.array([item for sublist in l for item in sublist]) - - -def energytowavelenght(E): - Joules_to_eV = 1.602 * 1e-19 - return 1e9 * const.h * const.c / (E * Joules_to_eV) - - -def create_SPE_file(path, sr="0"): - # path to the aux_files - # SR : to change configuration for each SR - spe_df = pd.DataFrame(columns=["pmtID", "pe", "SPE_values", "acceptance"]) - array_x = np.load(path + "x_data_sr" + sr + ".npy") - array_y = np.load(path + "sr" + sr + "_pdfs.npy") - spe_df["pmtID"] = np.arange(2000, 2120) - spe_df["pe"] = array_x.tolist() - spe_df["SPE_values"] = array_y.tolist() - spe_df["acceptance"] = np.load(path + "spe_acc_sr" + sr + ".npy", allow_pickle=True) - return np.save(path + "SPE_SR" + sr + ".npy", spe_df.to_records()) - - -# SPE parameters: ID, pe, SPE, acceptance -def SPE_parameters(file_spe_model): - with open(file_spe_model, 'r') as f: - data_spe = json.load(f) - data_dict = {entry['pmtID']: entry for entry in data_spe} - # SPE_ch= pd.DataFrame(columns=['pmtID','pe','SPE','acceptance']) - # SPE_ch['pmtID'],SPE_ch['pe'], SPE_ch['SPE'],SPE_ch['acceptance']=data_spe['pmtID'],data_spe['charge'],data_spe['SPE_values'],data_spe['acceptance'] - # acceptance_ch= [threshold_acc(SPE_ch,i) for i in np.arange(2000,2120)] - # SPE_ch['threshold_pe']=acceptance_ch - - return data_dict - - -def threshold_acc(SPE_df, ID): - SPE_ID = pd.DataFrame() - SPE_ID["cumulative"] = np.cumsum(SPE_df[SPE_df.pmtID == ID].SPE.values[0]) - SPE_ID["charges"] = SPE_df[SPE_df.pmtID == ID].pe.values[0] - accep = SPE_df[SPE_df.pmtID == ID].acceptance.values[0] - threshold = min(SPE_ID[SPE_ID.cumulative >= (1 - accep)].charges.values) - return threshold - - -# To get nVeto plugin, it should be a best way to do that... -st = cutax.contexts.xenonnt_online() -strax_nv = st.get_single_plugin("0", "events_nv") - - -# Quantum efficiency -def QE_nVeto(Q_E_nveto_file): - with open(Q_E_nveto_file, "r") as f: - data = json.loads(f.read()) - QE_array_n = [] - # nVeto - for i in np.arange(2000, 2120): - QE_array_n.append( - interpolate.interp1d( - data["nv_pmt_qe_wavelength"], - data["nv_pmt_qe"][str(i)], - bounds_error=False, - fill_value=0, - ) - ) - # Watertank_QE - pmt_id = list(np.arange(2000, 2120)) - QE_array = QE_array_n - pd_dict = {"pmt_id": pmt_id, "QE": QE_array} - return pd_dict - - -# Cluster for stacket hitlets -def channel_cluster_nv(t): - db_cluster = DBSCAN( - eps=8, min_samples=1 - ) # As a preliminar value we fix distance between two photons arriving in the same pmt 8ns - t_val = np.array(t) - clusters = np.array(db_cluster.fit_predict(t_val.reshape(-1, 1))) - return clusters -def get_clusters_arrays(arr, typ): - arr_nv_c = np.zeros(1, dtype=typ) - arr_nv_c["n_clusters_hits"] = len(arr) - - for i in arr.dtype.names: # <-- CORRETTO: usare dtype.names invece di fields - if i in ["time", "pmthitTime", "cluster_times_ns"]: - arr_nv_c[i] = np.min(arr[i]) - elif i == "endtime": - arr_nv_c[i] = np.max(arr[i]) - elif i in ["pe_area", "pmthitEnergy"]: - arr_nv_c[i] = np.sum(arr[i]) - elif i in ["evtid", "pmthitID", "labels"]: - arr_nv_c[i] = np.unique(arr[i]) +from ...plugin import FuseBasePlugin - return arr_nv_c +from ...dtypes import ( + neutron_veto_hitlet_dtype +) +class NeutronVetoHitlets(FuseBasePlugin): + """ + Plugin to simulate the Neutron Veto to hitlets. + + @Experts: Please add a better description of the plugin here. + """ -# Function to use in pivot_table module (clearly something we can optimize) -def recover_value(x): - m_size = np.array(x).size - if m_size == 1: - ret = x - elif m_size > 1: - ret = list(x) - return ret - - -def type_pri_evt(x): - if (type(x) == np.float32) or (type(x) == np.float64) or (type(x) == float): - ret = x - elif type(x) == list: - ret = x[0] - return ret - - -# For building independent time hitlets to compute into events or hitlets time from a source (ex: AmBe) -def time_hitlets_nv(time, ids, freq): - # freq: corresponds to the rate in [1/s] of the calibration source - if type(time) == list: - ret = min(time) - else: - ret = time - return ret + freq * ids * 1e9 - - -# Fonction to transform a hitlet dataframe output into 'hitlets_nv' ndarray -dtype = [ - ("area", np.float64), - ("amplitude", np.float64), - ("time_amplitude", np.int16), - ("entropy", np.float64), - ("range_50p_area", np.float64), - ("range_80p_area", np.float64), - ("left_area", np.float64), - ("low_left_area", np.float64), - ("range_hdr_50p_area", np.float64), - ("range_hdr_80p_area", np.float64), - ("left_hdr", np.float64), - ("low_left_hdr", np.float64), - ("fwhm", np.float64), - ("left", np.float64), - ("fwtm", np.float64), - ("low_left", np.float64), -] -dtype = dtype + strax.interval_dtype # -> Time, length, dt, channel - - -# A fuse plugin is a python class that inherits from strax.Plugin -# As naming convention we use CamelCase for the class name -def df_to_hit_array(data): - result = np.zeros(len(data), dtype=dtype) - result["time"] = data.time.values - result["length"] = np.array([1.0] * len(data)) - result["dt"] = np.array([10.0] * len(data)) - result["channel"] = data.pmthitID.values - result["area"] = data.pe_area.values - result = strax.sort_by_time(result) - return result - - -def hit_array_to_nvhitlet(data): - result = np.zeros(len(data), dtype=dtype) - result["time"] = data["time"] - result["length"] = np.array([1.0] * len(data)) - result["dt"] = np.array([10.0] * len(data)) - result["channel"] = data["pmthitID"] - result["area"] = data["pe_area"] - result = strax.sort_by_time(result) - return result - - -# A fuse plugin is a python class that inherits from strax.Plugin -# As naming convention we use CamelCase for the class name -class NeutronVetoHitlets(strax.Plugin): - # Each plugin has a version number - # If the version number changes, fuse will know that it need to re-simulate the data __version__ = "0.0.1" - # You need to tell fuse and strax what the plugin needs as input - # In this case we need nv_pmthits depends_on = "nv_pmthits" - - # You need to tell fuse and strax what the plugin provides as output - # In this case we provide nv_hitlets - # You can later use st.make(run_number, "nv_hitlets") to run the simulation provides = "nv_hitlets" - - # You need to tell fuse and strax what the data looks like - # Data of the same data_kind can be combined via "horizontal" concatenation and need - # to have the same output length. data_kind = "nv_hitlets" - # You also need to tell strax what columns the data has - # A column needs a name and a numpy data type. I set everything to float64 here, we can reduce it later - # I used the columns described here: - # https://github.com/XENONnT/fuse/blob/f777d9c281d9e046c2f322a30b462a9b7dd8ee00/test_nv/Hitlet_nv_fuse.py#L114 - - # We need to disable automatic rechunking for fuse plugins - # As fuse is going from "leightweigt" data to "heavy" data, - # automatic rechunking can lead to problems in later plugins - rechunk_on_save = False - - # We need to specify when we want to save the data - save_when = strax.SaveWhen.TARGET - - # strax uses a rather short timeout, lets increase it as - # some of the fuse simulation steps can take a while - input_timeout = FUSE_PLUGIN_TIMEOUT - - # We need to tell strax what config options the plugin needs - # We will use the great URLConfigs that are a part of straxen - debug = straxen.URLConfig( - default=False, - type=bool, - track=False, - help="Show debug informations", - ) - - deterministic_seed = straxen.URLConfig( - default=True, - type=bool, - help="Set the random seed from lineage and run_id, or pull the seed from the OS.", - ) + dtype = neutron_veto_hitlet_dtype + strax.interval_dtype + # Fix these URL configs! nveto_pmt_qe = straxen.URLConfig( default="nveto_pmt_qe://resource://simulation_config://" "SIMULATION_CONFIG_FILE.json?&key=nveto_pmt_qe", help="Quantum efficiency of NV PMTs", ) - nveto_spe_sr1 = straxen.URLConfig( + # Rename this to be not SR dependent + nveto_spe_parameters = straxen.URLConfig( default="nveto_spe_sr1://resource://simulation_config://" "SIMULATION_CONFIG_FILE.json?&key=nveto_spe_sr1", help="SR1 SPE model of NV PMTs", ) - def __init__(self, sr=0): - self.path = "/home/digangi/private_nt_aux_files/sim_files/" # pietro - need to modify this to work with urlconfig - self.QE_value = QE_nVeto(self.path + "nveto_pmt_qe.json") - self.SPE_nVeto = SPE_parameters(self.path+'nveto_spe_sr'+str(sr)+'.json') - self.dtype = dtype + # Add a few extra configs to remove them elsewhere - # Get Quantum efficiency - def QE_E(self, E, ID): - WL = energytowavelenght(E) - ind = ID - 2000 - qe = self.QE_value["QE"][ind](WL) - return qe + # @experts: would the stacking change the output dtype of the plugin? If yes, it will not work. + stack_hitlets = straxen.URLConfig( + default=False, + help="Option to enable or disable hitlet stacking", + ) - def get_acceptance(self, ID): - acc = self.SPE_nVeto.get(ID)['acceptance'] - return acc + # Is this value something you would like to track in the config files or should it just be set here? + ce_scaling = straxen.URLConfig( + type=(int, float), + default=0.75, + help="Add good description here", + ) - # Get acceptance threshold - def get_threshold_acc(self, ID): - ind = ID - 2000 - threshold = self.SPE_nVeto.threshold_pe.values[ind] - return threshold + # Next steps: remove this part. + # def __init__(self, sr=0): + # self.path = "/home/digangi/private_nt_aux_files/sim_files/" # pietro - need to modify this to work with urlconfig + # self.QE_value = QE_nVeto(self.path + "nveto_pmt_qe.json") + # self.SPE_nVeto = SPE_parameters(self.path+'nveto_spe_sr'+str(sr)+'.json') - # Sampling charge from SPE - def pe_charge_N(self, pmt_id): - SPE_channel = self.SPE_nVeto.get(pmt_id) - charge=rd.choices(SPE_channel['pe'],SPE_channel['SPE_values'],k=1)[0] + def compute(self, nv_pmthits): + + if len(nv_pmthits) == 0: + return np.zeros(0, self.dtype) + + hitlets = self._nv_hitlets(nv_pmthits) + + result = np.zeros(len(hitlets), dtype=self.dtype) + result["time"] = hitlets["time"] + result["length"] = 1 + result["dt"] = 10 + result["channel"] = hitlets["pmthitID"] + result["area"] = hitlets["pe_area"] + + return strax.sort_by_time(result) - return charge - # --------------------------- Hitlet function ------------------------------------------------------------# + def _nv_hitlets(self, pmthits): - def _nv_hitlets(self, pmthits, CE_Scaling=0.75, Stacked="No", period=1.0): + # Henning comment: I would remove this long comment block below. If it is strictly needed, maybe it can got into the docstring of the class? # -------------------------------------------------Arguments---------------------------------------------------# # QE_Scaling corrrespond to collection efficiency, no study has been done on the CE of muon Veto we use a default value close to the nVeto see https://xe1t-wiki.lngs.infn.it/doku.php?id=xenon:mancuso:hitletsimulator:collection_efficiency @@ -334,26 +112,26 @@ def _nv_hitlets(self, pmthits, CE_Scaling=0.75, Stacked="No", period=1.0): pmthits = pmthits[mask] # 1. First step PHOTON to first dinode - print("Applying QE and CE") + self.log.debug("Applying QE and CE") # Applying Quantum efficiency for each pmt qe = 1e-2 * np.vectorize(self.QE_E)(pmthits["pmthitEnergy"], pmthits["pmthitID"]) # Applying effective collection efficiency - qe *= CE_Scaling + qe *= self.ce_scaling # Applying acceptance per pmt: for the approach in which SPE PDF has already applied a threshold for low charges qe = qe * np.vectorize(self.get_acceptance)(pmthits["pmthitID"]) # Generate a photoelectron based on (binomial) conversion probability qe*eCE*spe_acc pe = np.array([np.random.binomial(1, j, 1)[0] for j in qe]) # Discard pmthits which do not generate a pe - print("Loading hit survive") + self.log.debug("Loading hit survive") maks_qe = pe > 0 pmthits = pmthits[maks_qe] # 2. Sampling charge from SPE for each pmthit with a generated pe - print("Sampling hitlets charge pe") + self.log.debug("Sampling hitlets charge pe") pmthits["pe_area"] = np.vectorize(self.pe_charge_N)(pmthits["pmthitID"]) # 3. Creating hitlet times - print("Getting time hitlets") + self.log.debug("Getting time hitlets") times = [] for i in np.unique(pmthits["evtid"]): mask = pmthits["evtid"] == i @@ -362,16 +140,18 @@ def _nv_hitlets(self, pmthits, CE_Scaling=0.75, Stacked="No", period=1.0): times.append(cluster_times_ns) pmthits["cluster_times_ns"] = np.concatenate(times) - if Stacked == "No": - return pmthits - # 3.1 Stacked hitlets: this correspond to hitlets in the same pmt with a time difference below some estimated time response of the Channel (8 ns, i.e. 4 samples). - elif Stacked == "yes": + # Same comment as above: If this option produces different output dtypes it will not work. One could add a second output to the plugin if needed or add a new plugin that takes pmthits as input and produces the stacked hitlets as output. - print("Looking for stacked hitlets") + if not self.stack_hitlets: + return pmthits + + # 3.1 Stacked hitlets: this correspond to hitlets in the same pmt with a time difference below some estimated time response of the Channel (8 ns, i.e. 4 samples). + elif self.stack_hitlets: + self.log.debug("Looking for stacked hitlets") # Here we set times related to the first hit, we only use that for stacket hitlets arr_c_evt = [] - for i in tq.tqdm(np.unique(pmthits["evtid"])): + for i in np.unique(pmthits["evtid"]): arr_evt = pmthits[pmthits["evtid"] == i] arr_c_pmt = [] for j in np.unique(arr_evt["pmthitID"]): @@ -390,43 +170,122 @@ def _nv_hitlets(self, pmthits, CE_Scaling=0.75, Stacked="No", period=1.0): return np.concatenate(arr_c_evt) - def setup(self): - - # All plugins can report problmes or debug information via the logging feature - # You can set the log level via the debug config option. - # WARNING messages are always shown whild DEBUG messages are only shown if debug is True - if self.debug: - log.setLevel("DEBUG") - log.debug(f"Running NeutronVetoHitlets version {self.__version__} in debug mode") - else: - log.setLevel("WARNING") - - # Many plugins need to generate random numbers for simulation the corresponding physics process - # In fuse we want to make sure that the simulation is reproducible. - # Therefore we have the default setting of deterministic_seed = True - # In this case the random seed is generated from the run_id and the lineage - # The lineage includes all plugins and their verions that are connected to the input of the - # current plugin as well as all tracked config options and the strax version. - # The run_id is a user input. More on the deterministic seed can be found in - # a dedicated notebook. - # Please make sure that you use the random number generator self.rng when you need random numbers - # later in the plugin. - if self.deterministic_seed: - hash_string = strax.deterministic_hash((self.run_id, self.lineage)) - seed = int(hash_string.encode().hex(), 16) - self.rng = np.random.default_rng(seed=seed) - log.debug(f"Generating random numbers from seed {seed}") - else: - self.rng = np.random.default_rng() - log.debug(f"Generating random numbers with seed pulled from OS") - - # The compute method is the heart of the plugin. It is executed for each chunk of input data and - # must produce data in the format specified in the self.dtype variable. - def compute(self, nv_pmthits, eCE=0.75, Stacked_opt="No", rate=1.0): - # Make sure your plugin can handle empty inputs - if len(nv_pmthits) == 0: - return np.zeros(0, self.dtype) - hitlets = self._nv_hitlets(nv_pmthits, CE_Scaling=eCE, Stacked=Stacked_opt, period=rate) - # All your NV goes here - result = hit_array_to_nvhitlet(hitlets) - return result + # Get Quantum efficiency + def QE_E(self, E, ID): + WL = energy_to_wavelenght(E) + ind = ID - 2000 + qe = self.nveto_pmt_qe["QE"][ind](WL) + return qe + + def get_acceptance(self, ID): + acc = self.nveto_spe_parameters.get(ID)['acceptance'] + return acc + + # Get acceptance threshold + def get_threshold_acc(self, ID): + ind = ID - 2000 + threshold = self.nveto_spe_parameters.threshold_pe.values[ind] + return threshold + + # Sampling charge from SPE + def pe_charge_N(self, pmt_id): + SPE_channel = self.nveto_spe_parameters.get(pmt_id) + + + # We can not use the line below as we have to make sure fuse is producing reproducible results. For this reason we have to stick to the random generator of the plugin. + # charge=rd.choices(SPE_channel['pe'],SPE_channel['SPE_values'],k=1)[0] + + #I'm not sure if numpy choice is exactly the same as random.choices. Please check this. + charge=self.rng.choice(SPE_channel['pe'],SPE_channel['SPE_values'],k=1)[0] + + return charge + + +def energy_to_wavelenght(E): + Joules_to_eV = 1.602 * 1e-19 + return 1e9 * const.h * const.c / (E * Joules_to_eV) + +# Cluster for stacket hitlets +def channel_cluster_nv(t): + db_cluster = DBSCAN( + eps=8, min_samples=1 + ) # As a preliminar value we fix distance between two photons arriving in the same pmt 8ns + t_val = np.array(t) + clusters = np.array(db_cluster.fit_predict(t_val.reshape(-1, 1))) + return clusters + +def get_clusters_arrays(arr, typ): + arr_nv_c = np.zeros(1, dtype=typ) + arr_nv_c["n_clusters_hits"] = len(arr) + + for i in arr.dtype.names: # <-- CORRETTO: usare dtype.names invece di fields + if i in ["time", "pmthitTime", "cluster_times_ns"]: + arr_nv_c[i] = np.min(arr[i]) + elif i == "endtime": + arr_nv_c[i] = np.max(arr[i]) + elif i in ["pe_area", "pmthitEnergy"]: + arr_nv_c[i] = np.sum(arr[i]) + elif i in ["evtid", "pmthitID", "labels"]: + arr_nv_c[i] = np.unique(arr[i]) + + return arr_nv_c + + +# I do not see this used anywhere: -> Delete? +# def create_SPE_file(path, sr="0"): +# # path to the aux_files +# # SR : to change configuration for each SR +# spe_df = pd.DataFrame(columns=["pmtID", "pe", "SPE_values", "acceptance"]) +# array_x = np.load(path + "x_data_sr" + sr + ".npy") +# array_y = np.load(path + "sr" + sr + "_pdfs.npy") +# spe_df["pmtID"] = np.arange(2000, 2120) +# spe_df["pe"] = array_x.tolist() +# spe_df["SPE_values"] = array_y.tolist() +# spe_df["acceptance"] = np.load(path + "spe_acc_sr" + sr + ".npy", allow_pickle=True) +# return np.save(path + "SPE_SR" + sr + ".npy", spe_df.to_records()) + + +# This one is the same as the nveto_spe_sr1_dict function right? +# SPE parameters: ID, pe, SPE, acceptance +# def SPE_parameters(file_spe_model): +# with open(file_spe_model, 'r') as f: +# data_spe = json.load(f) +# data_dict = {entry['pmtID']: entry for entry in data_spe} +# # SPE_ch= pd.DataFrame(columns=['pmtID','pe','SPE','acceptance']) +# # SPE_ch['pmtID'],SPE_ch['pe'], SPE_ch['SPE'],SPE_ch['acceptance']=data_spe['pmtID'],data_spe['charge'],data_spe['SPE_values'],data_spe['acceptance'] +# # acceptance_ch= [threshold_acc(SPE_ch,i) for i in np.arange(2000,2120)] +# # SPE_ch['threshold_pe']=acceptance_ch + +# return data_dict + +# I do not see this used anywhere: -> Delete? +# def threshold_acc(SPE_df, ID): +# SPE_ID = pd.DataFrame() +# SPE_ID["cumulative"] = np.cumsum(SPE_df[SPE_df.pmtID == ID].SPE.values[0]) +# SPE_ID["charges"] = SPE_df[SPE_df.pmtID == ID].pe.values[0] +# accep = SPE_df[SPE_df.pmtID == ID].acceptance.values[0] +# threshold = min(SPE_ID[SPE_ID.cumulative >= (1 - accep)].charges.values) +# return threshold + + +# This one is the one in the URLConfig nveto_pmt_qe right? +# Quantum efficiency +# def QE_nVeto(Q_E_nveto_file): +# with open(Q_E_nveto_file, "r") as f: +# data = json.loads(f.read()) +# QE_array_n = [] +# # nVeto +# for i in np.arange(2000, 2120): +# QE_array_n.append( +# interpolate.interp1d( +# data["nv_pmt_qe_wavelength"], +# data["nv_pmt_qe"][str(i)], +# bounds_error=False, +# fill_value=0, +# ) +# ) +# # Watertank_QE +# pmt_id = list(np.arange(2000, 2120)) +# QE_array = QE_array_n +# pd_dict = {"pmt_id": pmt_id, "QE": QE_array} +# return pd_dict \ No newline at end of file From d332b178e25529a0d1517c84806c07a58257a987 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 29 May 2025 12:46:24 +0000 Subject: [PATCH 27/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- fuse/context_utils.py | 35 ++++++++++++++--------- fuse/dtypes.py | 2 +- fuse/plugins/neutron_veto/nvhitlets.py | 39 ++++++++++++-------------- 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/fuse/context_utils.py b/fuse/context_utils.py index 67396a7a..b2ace6ca 100644 --- a/fuse/context_utils.py +++ b/fuse/context_utils.py @@ -172,6 +172,7 @@ def lce_from_pattern_map(map, pmt_mask): lcemap.__init__(lcemap.data) return lcemap + def apply_mc_overrides(context, config_file): """Apply config overrides from 'mc_overrides' using from_config.""" try: @@ -188,26 +189,34 @@ def apply_mc_overrides(context, config_file): except Exception as e: raise ValueError(f"[mc_overrides] Failed to apply overrides from {config_file}: {e}") from e + @URLConfig.register("nveto_pmt_qe") def nveto_pmt_qe_dict(data): """Get NV PMT quantum efficiecny values and interpolate.""" - #with open(Q_E_nveto_file,'r') as f: + # with open(Q_E_nveto_file,'r') as f: # data = json.loads(f.read()) - QE_array_n=[] - #nVeto - for i in np.arange(2000,2120): - QE_array_n.append(interpolate.interp1d(data['nv_pmt_qe_wavelength'],data['nv_pmt_qe'][str(i)], bounds_error=False,fill_value=0)) - #Watertank_QE - pmt_id= list(np.arange(2000,2120)) - QE_array=QE_array_n - pd_dict= {"pmt_id":pmt_id,"QE":QE_array} + QE_array_n = [] + # nVeto + for i in np.arange(2000, 2120): + QE_array_n.append( + interpolate.interp1d( + data["nv_pmt_qe_wavelength"], + data["nv_pmt_qe"][str(i)], + bounds_error=False, + fill_value=0, + ) + ) + # Watertank_QE + pmt_id = list(np.arange(2000, 2120)) + QE_array = QE_array_n + pd_dict = {"pmt_id": pmt_id, "QE": QE_array} return pd_dict @URLConfig.register("nveto_spe_sr1") def nveto_spe_sr1_dict(data_spe): """Get dictionary with NV SPE parameters.""" - #with open(file_spe_model, 'r') as f: - # data_spe = json.load(f) - data_dict = {entry['pmtID']: entry for entry in data_spe} - return data_dict \ No newline at end of file + # with open(file_spe_model, 'r') as f: + # data_spe = json.load(f) + data_dict = {entry["pmtID"]: entry for entry in data_spe} + return data_dict diff --git a/fuse/dtypes.py b/fuse/dtypes.py index 917b183c..228aa72a 100644 --- a/fuse/dtypes.py +++ b/fuse/dtypes.py @@ -97,4 +97,4 @@ ("left", np.float64), ("fwtm", np.float64), ("low_left", np.float64), -] \ No newline at end of file +] diff --git a/fuse/plugins/neutron_veto/nvhitlets.py b/fuse/plugins/neutron_veto/nvhitlets.py index c518d789..ed744abb 100644 --- a/fuse/plugins/neutron_veto/nvhitlets.py +++ b/fuse/plugins/neutron_veto/nvhitlets.py @@ -7,14 +7,12 @@ from ...plugin import FuseBasePlugin -from ...dtypes import ( - neutron_veto_hitlet_dtype -) +from ...dtypes import neutron_veto_hitlet_dtype + class NeutronVetoHitlets(FuseBasePlugin): - """ - Plugin to simulate the Neutron Veto to hitlets. - + """Plugin to simulate the Neutron Veto to hitlets. + @Experts: Please add a better description of the plugin here. """ @@ -26,7 +24,7 @@ class NeutronVetoHitlets(FuseBasePlugin): dtype = neutron_veto_hitlet_dtype + strax.interval_dtype - # Fix these URL configs! + # Fix these URL configs! nveto_pmt_qe = straxen.URLConfig( default="nveto_pmt_qe://resource://simulation_config://" "SIMULATION_CONFIG_FILE.json?&key=nveto_pmt_qe", @@ -42,7 +40,7 @@ class NeutronVetoHitlets(FuseBasePlugin): # Add a few extra configs to remove them elsewhere - # @experts: would the stacking change the output dtype of the plugin? If yes, it will not work. + # @experts: would the stacking change the output dtype of the plugin? If yes, it will not work. stack_hitlets = straxen.URLConfig( default=False, help="Option to enable or disable hitlet stacking", @@ -55,7 +53,7 @@ class NeutronVetoHitlets(FuseBasePlugin): help="Add good description here", ) - # Next steps: remove this part. + # Next steps: remove this part. # def __init__(self, sr=0): # self.path = "/home/digangi/private_nt_aux_files/sim_files/" # pietro - need to modify this to work with urlconfig # self.QE_value = QE_nVeto(self.path + "nveto_pmt_qe.json") @@ -65,9 +63,9 @@ def compute(self, nv_pmthits): if len(nv_pmthits) == 0: return np.zeros(0, self.dtype) - + hitlets = self._nv_hitlets(nv_pmthits) - + result = np.zeros(len(hitlets), dtype=self.dtype) result["time"] = hitlets["time"] result["length"] = 1 @@ -77,7 +75,6 @@ def compute(self, nv_pmthits): return strax.sort_by_time(result) - def _nv_hitlets(self, pmthits): # Henning comment: I would remove this long comment block below. If it is strictly needed, maybe it can got into the docstring of the class? @@ -140,12 +137,11 @@ def _nv_hitlets(self, pmthits): times.append(cluster_times_ns) pmthits["cluster_times_ns"] = np.concatenate(times) - # Same comment as above: If this option produces different output dtypes it will not work. One could add a second output to the plugin if needed or add a new plugin that takes pmthits as input and produces the stacked hitlets as output. if not self.stack_hitlets: return pmthits - + # 3.1 Stacked hitlets: this correspond to hitlets in the same pmt with a time difference below some estimated time response of the Channel (8 ns, i.e. 4 samples). elif self.stack_hitlets: self.log.debug("Looking for stacked hitlets") @@ -178,7 +174,7 @@ def QE_E(self, E, ID): return qe def get_acceptance(self, ID): - acc = self.nveto_spe_parameters.get(ID)['acceptance'] + acc = self.nveto_spe_parameters.get(ID)["acceptance"] return acc # Get acceptance threshold @@ -191,12 +187,11 @@ def get_threshold_acc(self, ID): def pe_charge_N(self, pmt_id): SPE_channel = self.nveto_spe_parameters.get(pmt_id) - # We can not use the line below as we have to make sure fuse is producing reproducible results. For this reason we have to stick to the random generator of the plugin. # charge=rd.choices(SPE_channel['pe'],SPE_channel['SPE_values'],k=1)[0] - #I'm not sure if numpy choice is exactly the same as random.choices. Please check this. - charge=self.rng.choice(SPE_channel['pe'],SPE_channel['SPE_values'],k=1)[0] + # I'm not sure if numpy choice is exactly the same as random.choices. Please check this. + charge = self.rng.choice(SPE_channel["pe"], SPE_channel["SPE_values"], k=1)[0] return charge @@ -205,6 +200,7 @@ def energy_to_wavelenght(E): Joules_to_eV = 1.602 * 1e-19 return 1e9 * const.h * const.c / (E * Joules_to_eV) + # Cluster for stacket hitlets def channel_cluster_nv(t): db_cluster = DBSCAN( @@ -214,6 +210,7 @@ def channel_cluster_nv(t): clusters = np.array(db_cluster.fit_predict(t_val.reshape(-1, 1))) return clusters + def get_clusters_arrays(arr, typ): arr_nv_c = np.zeros(1, dtype=typ) arr_nv_c["n_clusters_hits"] = len(arr) @@ -248,8 +245,8 @@ def get_clusters_arrays(arr, typ): # This one is the same as the nveto_spe_sr1_dict function right? # SPE parameters: ID, pe, SPE, acceptance # def SPE_parameters(file_spe_model): -# with open(file_spe_model, 'r') as f: -# data_spe = json.load(f) +# with open(file_spe_model, 'r') as f: +# data_spe = json.load(f) # data_dict = {entry['pmtID']: entry for entry in data_spe} # # SPE_ch= pd.DataFrame(columns=['pmtID','pe','SPE','acceptance']) # # SPE_ch['pmtID'],SPE_ch['pe'], SPE_ch['SPE'],SPE_ch['acceptance']=data_spe['pmtID'],data_spe['charge'],data_spe['SPE_values'],data_spe['acceptance'] @@ -288,4 +285,4 @@ def get_clusters_arrays(arr, typ): # pmt_id = list(np.arange(2000, 2120)) # QE_array = QE_array_n # pd_dict = {"pmt_id": pmt_id, "QE": QE_array} -# return pd_dict \ No newline at end of file +# return pd_dict From 7290e78423ff6cf93c88295aa0c1a7429099a202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henning=20Schulze=20Ei=C3=9Fing?= Date: Mon, 22 Sep 2025 15:20:56 +0200 Subject: [PATCH 28/29] Speed up things and make URLConfigs work. --- fuse/context_utils.py | 19 +-- fuse/plugins/neutron_veto/nvhitlets.py | 192 ++++++++++--------------- 2 files changed, 83 insertions(+), 128 deletions(-) diff --git a/fuse/context_utils.py b/fuse/context_utils.py index fba8550a..0bcd8564 100644 --- a/fuse/context_utils.py +++ b/fuse/context_utils.py @@ -211,8 +211,7 @@ def apply_mc_overrides(context, config_file): @URLConfig.register("nveto_pmt_qe") def nveto_pmt_qe_dict(data): """Get NV PMT quantum efficiecny values and interpolate.""" - # with open(Q_E_nveto_file,'r') as f: - # data = json.loads(f.read()) + QE_array_n = [] # nVeto for i in np.arange(2000, 2120): @@ -224,17 +223,19 @@ def nveto_pmt_qe_dict(data): fill_value=0, ) ) - # Watertank_QE + pmt_id = list(np.arange(2000, 2120)) QE_array = QE_array_n - pd_dict = {"pmt_id": pmt_id, "QE": QE_array} - return pd_dict + return QE_array + # pd_dict = {"pmt_id": pmt_id, "QE": QE_array} + + # return pd_dict -@URLConfig.register("nveto_spe_sr1") -def nveto_spe_sr1_dict(data_spe): + +@URLConfig.register("nveto_spe") +def nveto_spe_dict(data_spe): """Get dictionary with NV SPE parameters.""" - # with open(file_spe_model, 'r') as f: - # data_spe = json.load(f) + data_dict = {entry["pmtID"]: entry for entry in data_spe} return data_dict diff --git a/fuse/plugins/neutron_veto/nvhitlets.py b/fuse/plugins/neutron_veto/nvhitlets.py index ed744abb..1c89db7f 100644 --- a/fuse/plugins/neutron_veto/nvhitlets.py +++ b/fuse/plugins/neutron_veto/nvhitlets.py @@ -9,7 +9,9 @@ from ...dtypes import neutron_veto_hitlet_dtype +export, __all__ = strax.exporter() +@export class NeutronVetoHitlets(FuseBasePlugin): """Plugin to simulate the Neutron Veto to hitlets. @@ -27,38 +29,30 @@ class NeutronVetoHitlets(FuseBasePlugin): # Fix these URL configs! nveto_pmt_qe = straxen.URLConfig( default="nveto_pmt_qe://resource://simulation_config://" - "SIMULATION_CONFIG_FILE.json?&key=nveto_pmt_qe", + "SIMULATION_CONFIG_FILE.json?" + "&key=nveto_pmt_qe" + "&fmt=json", help="Quantum efficiency of NV PMTs", ) - # Rename this to be not SR dependent nveto_spe_parameters = straxen.URLConfig( - default="nveto_spe_sr1://resource://simulation_config://" - "SIMULATION_CONFIG_FILE.json?&key=nveto_spe_sr1", - help="SR1 SPE model of NV PMTs", + default="nveto_spe://resource://simulation_config://" + "SIMULATION_CONFIG_FILE.json?&key=nveto_pmt_spe&fmt=json", + help="SPE model of NV PMTs", ) - # Add a few extra configs to remove them elsewhere - - # @experts: would the stacking change the output dtype of the plugin? If yes, it will not work. stack_hitlets = straxen.URLConfig( default=False, help="Option to enable or disable hitlet stacking", ) - # Is this value something you would like to track in the config files or should it just be set here? ce_scaling = straxen.URLConfig( type=(int, float), - default=0.75, + default="take://resource://SIMULATION_CONFIG_FILE.json?fmt=json&take=nveto_eCE", + cache=True, help="Add good description here", ) - # Next steps: remove this part. - # def __init__(self, sr=0): - # self.path = "/home/digangi/private_nt_aux_files/sim_files/" # pietro - need to modify this to work with urlconfig - # self.QE_value = QE_nVeto(self.path + "nveto_pmt_qe.json") - # self.SPE_nVeto = SPE_parameters(self.path+'nveto_spe_sr'+str(sr)+'.json') - def compute(self, nv_pmthits): if len(nv_pmthits) == 0: @@ -77,19 +71,6 @@ def compute(self, nv_pmthits): def _nv_hitlets(self, pmthits): - # Henning comment: I would remove this long comment block below. If it is strictly needed, maybe it can got into the docstring of the class? - - # -------------------------------------------------Arguments---------------------------------------------------# - # QE_Scaling corrrespond to collection efficiency, no study has been done on the CE of muon Veto we use a default value close to the nVeto see https://xe1t-wiki.lngs.infn.it/doku.php?id=xenon:mancuso:hitletsimulator:collection_efficiency - # period : this could be related to the rate of a source, or the rate for a time bin if we reconstruct an spectrum. If no source 1 second is the default value (see Comments) - - # ----------------------------------------------Commments-----------------------------------------------------------------#: - # 1.There is no application of a threshold per channel based on the acceptation by default, but we keep the value in the data frame for each pmt, and one can do manually. This is in order to not condition the sampling, and compare it with the data with different cuts. - # .2. The period is set by default at 1s to care about no pyle up or merge of hitlets if one want to do an analysis for rare events (independent non sourced ones). If we simulate a calibration or a constant flux this value has to be changed to real rate one. - - # 0.---------------Load GEANT output-------------------# - - # Adding extra fields to pmthits np.array extra_fields = [ ("pe_area", "= 2000) & (pmthits["pmthitID"] < 2121) pmthits = pmthits[mask] - # 1. First step PHOTON to first dinode - self.log.debug("Applying QE and CE") - # Applying Quantum efficiency for each pmt - qe = 1e-2 * np.vectorize(self.QE_E)(pmthits["pmthitEnergy"], pmthits["pmthitID"]) + self.log.debug("Applying QE") + # Applying Quantum efficiency for each pmt + # --- super super slow..... + # qe = 1e-2 * np.vectorize(self.QE_E)(pmthits["pmthitEnergy"], pmthits["pmthitID"]) + + # A faster approach + qe = np.zeros(len(pmthits), dtype=np.float32) + unique_ids = np.unique(pmthits["pmthitID"]) + NVeto_PMT_QE = self.nveto_pmt_qe + for uid in unique_ids: + mask_id = pmthits["pmthitID"] == uid + qe[mask_id] = 1e-2 * QE_E(pmthits["pmthitEnergy"][mask_id], uid, NVeto_PMT_QE) + + + self.log.debug("Applying CE") # Applying effective collection efficiency qe *= self.ce_scaling + # Applying acceptance per pmt: for the approach in which SPE PDF has already applied a threshold for low charges - qe = qe * np.vectorize(self.get_acceptance)(pmthits["pmthitID"]) + self.log.debug("Applying per pmt acceptance") + # also very slow, think this is a bottleneck of the URLConfigs, it is not very efficient to call them in a loop many times. + # qe = qe * np.vectorize(self.get_acceptance)(pmthits["pmthitID"]) + + NV_SPE = self.nveto_spe_parameters + acceptance_dict = {k: v["acceptance"] for k, v in NV_SPE.items()} + qe = qe * np.vectorize(acceptance_dict.get)(pmthits["pmthitID"]) + + + self.log.debug("Binomial sampling") # Generate a photoelectron based on (binomial) conversion probability qe*eCE*spe_acc pe = np.array([np.random.binomial(1, j, 1)[0] for j in qe]) - # Discard pmthits which do not generate a pe - self.log.debug("Loading hit survive") + maks_qe = pe > 0 pmthits = pmthits[maks_qe] # 2. Sampling charge from SPE for each pmthit with a generated pe self.log.debug("Sampling hitlets charge pe") - pmthits["pe_area"] = np.vectorize(self.pe_charge_N)(pmthits["pmthitID"]) + # Same performance problems as above. Lets try something faster + # pmthits["pe_area"] = np.vectorize(self.pe_charge_N)(pmthits["pmthitID"]) + spe_charge = np.zeros(len(pmthits), dtype=np.float32) + unique_ids = np.unique(pmthits["pmthitID"]) + for uid in unique_ids: + mask_id = pmthits["pmthitID"] == uid + + # Just call the random choice once per PMT ID + SPE_channel = NV_SPE.get(uid) + spe_charge[mask_id] = self.rng.choice(SPE_channel["pe"], p = SPE_channel["SPE_values"], size=np.sum(mask_id)) + pmthits["pe_area"] = spe_charge # 3. Creating hitlet times self.log.debug("Getting time hitlets") @@ -137,15 +148,12 @@ def _nv_hitlets(self, pmthits): times.append(cluster_times_ns) pmthits["cluster_times_ns"] = np.concatenate(times) - # Same comment as above: If this option produces different output dtypes it will not work. One could add a second output to the plugin if needed or add a new plugin that takes pmthits as input and produces the stacked hitlets as output. - if not self.stack_hitlets: return pmthits - # 3.1 Stacked hitlets: this correspond to hitlets in the same pmt with a time difference below some estimated time response of the Channel (8 ns, i.e. 4 samples). elif self.stack_hitlets: self.log.debug("Looking for stacked hitlets") - # Here we set times related to the first hit, we only use that for stacket hitlets + arr_c_evt = [] for i in np.unique(pmthits["evtid"]): arr_evt = pmthits[pmthits["evtid"] == i] @@ -160,22 +168,25 @@ def _nv_hitlets(self, pmthits): for l in np.unique(labels) ] ) - # arr_c =np.concatenate([get_clusters_arrays(arr_pmt[arr_pmt['labels']==l],dtypes) for l in np.unique(labels)]) + arr_c_pmt.append(arr_c) arr_c_evt.append(np.concatenate(arr_c_pmt)) return np.concatenate(arr_c_evt) # Get Quantum efficiency - def QE_E(self, E, ID): - WL = energy_to_wavelenght(E) - ind = ID - 2000 - qe = self.nveto_pmt_qe["QE"][ind](WL) - return qe - def get_acceptance(self, ID): - acc = self.nveto_spe_parameters.get(ID)["acceptance"] - return acc + # def QE_E(self, E, ID): + # WL = energy_to_wavelenght(E) + # ind = ID - 2000 + # qe = self.nveto_pmt_qe[ind](WL) + # # qe = self.nveto_pmt_qe["QE"][ind](WL) + + # return qe + + # def get_acceptance(self, ID): + # acc = self.nveto_spe_parameters.get(ID)["acceptance"] + # return acc # Get acceptance threshold def get_threshold_acc(self, ID): @@ -184,22 +195,25 @@ def get_threshold_acc(self, ID): return threshold # Sampling charge from SPE - def pe_charge_N(self, pmt_id): - SPE_channel = self.nveto_spe_parameters.get(pmt_id) - - # We can not use the line below as we have to make sure fuse is producing reproducible results. For this reason we have to stick to the random generator of the plugin. - # charge=rd.choices(SPE_channel['pe'],SPE_channel['SPE_values'],k=1)[0] + # def pe_charge_N(self, pmt_id): + # SPE_channel = self.nveto_spe_parameters.get(pmt_id) - # I'm not sure if numpy choice is exactly the same as random.choices. Please check this. - charge = self.rng.choice(SPE_channel["pe"], SPE_channel["SPE_values"], k=1)[0] + # charge = self.rng.choice(SPE_channel["pe"], SPE_channel["SPE_values"], k=1)[0] - return charge + # return charge def energy_to_wavelenght(E): Joules_to_eV = 1.602 * 1e-19 return 1e9 * const.h * const.c / (E * Joules_to_eV) +def QE_E(E, ID, nveto_pmt_qe): + WL = energy_to_wavelenght(E) + ind = ID - 2000 + qe = nveto_pmt_qe[ind](WL) + # qe = self.nveto_pmt_qe["QE"][ind](WL) + return qe + # Cluster for stacket hitlets def channel_cluster_nv(t): @@ -225,64 +239,4 @@ def get_clusters_arrays(arr, typ): elif i in ["evtid", "pmthitID", "labels"]: arr_nv_c[i] = np.unique(arr[i]) - return arr_nv_c - - -# I do not see this used anywhere: -> Delete? -# def create_SPE_file(path, sr="0"): -# # path to the aux_files -# # SR : to change configuration for each SR -# spe_df = pd.DataFrame(columns=["pmtID", "pe", "SPE_values", "acceptance"]) -# array_x = np.load(path + "x_data_sr" + sr + ".npy") -# array_y = np.load(path + "sr" + sr + "_pdfs.npy") -# spe_df["pmtID"] = np.arange(2000, 2120) -# spe_df["pe"] = array_x.tolist() -# spe_df["SPE_values"] = array_y.tolist() -# spe_df["acceptance"] = np.load(path + "spe_acc_sr" + sr + ".npy", allow_pickle=True) -# return np.save(path + "SPE_SR" + sr + ".npy", spe_df.to_records()) - - -# This one is the same as the nveto_spe_sr1_dict function right? -# SPE parameters: ID, pe, SPE, acceptance -# def SPE_parameters(file_spe_model): -# with open(file_spe_model, 'r') as f: -# data_spe = json.load(f) -# data_dict = {entry['pmtID']: entry for entry in data_spe} -# # SPE_ch= pd.DataFrame(columns=['pmtID','pe','SPE','acceptance']) -# # SPE_ch['pmtID'],SPE_ch['pe'], SPE_ch['SPE'],SPE_ch['acceptance']=data_spe['pmtID'],data_spe['charge'],data_spe['SPE_values'],data_spe['acceptance'] -# # acceptance_ch= [threshold_acc(SPE_ch,i) for i in np.arange(2000,2120)] -# # SPE_ch['threshold_pe']=acceptance_ch - -# return data_dict - -# I do not see this used anywhere: -> Delete? -# def threshold_acc(SPE_df, ID): -# SPE_ID = pd.DataFrame() -# SPE_ID["cumulative"] = np.cumsum(SPE_df[SPE_df.pmtID == ID].SPE.values[0]) -# SPE_ID["charges"] = SPE_df[SPE_df.pmtID == ID].pe.values[0] -# accep = SPE_df[SPE_df.pmtID == ID].acceptance.values[0] -# threshold = min(SPE_ID[SPE_ID.cumulative >= (1 - accep)].charges.values) -# return threshold - - -# This one is the one in the URLConfig nveto_pmt_qe right? -# Quantum efficiency -# def QE_nVeto(Q_E_nveto_file): -# with open(Q_E_nveto_file, "r") as f: -# data = json.loads(f.read()) -# QE_array_n = [] -# # nVeto -# for i in np.arange(2000, 2120): -# QE_array_n.append( -# interpolate.interp1d( -# data["nv_pmt_qe_wavelength"], -# data["nv_pmt_qe"][str(i)], -# bounds_error=False, -# fill_value=0, -# ) -# ) -# # Watertank_QE -# pmt_id = list(np.arange(2000, 2120)) -# QE_array = QE_array_n -# pd_dict = {"pmt_id": pmt_id, "QE": QE_array} -# return pd_dict + return arr_nv_c \ No newline at end of file From cd5150352719bc7b683b675c049a3068ac5225f8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 23 Sep 2025 08:16:51 +0000 Subject: [PATCH 29/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- fuse/context_utils.py | 2 +- fuse/plugins/neutron_veto/nvhitlets.py | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/fuse/context_utils.py b/fuse/context_utils.py index 2e632735..fd779094 100644 --- a/fuse/context_utils.py +++ b/fuse/context_utils.py @@ -224,7 +224,7 @@ def nveto_pmt_qe_dict(data): return QE_array # pd_dict = {"pmt_id": pmt_id, "QE": QE_array} - + # return pd_dict diff --git a/fuse/plugins/neutron_veto/nvhitlets.py b/fuse/plugins/neutron_veto/nvhitlets.py index 1c89db7f..18cb9bdf 100644 --- a/fuse/plugins/neutron_veto/nvhitlets.py +++ b/fuse/plugins/neutron_veto/nvhitlets.py @@ -11,6 +11,7 @@ export, __all__ = strax.exporter() + @export class NeutronVetoHitlets(FuseBasePlugin): """Plugin to simulate the Neutron Veto to hitlets. @@ -90,7 +91,7 @@ def _nv_hitlets(self, pmthits): pmthits = pmthits[mask] self.log.debug("Applying QE") - # Applying Quantum efficiency for each pmt + # Applying Quantum efficiency for each pmt # --- super super slow..... # qe = 1e-2 * np.vectorize(self.QE_E)(pmthits["pmthitEnergy"], pmthits["pmthitID"]) @@ -102,21 +103,19 @@ def _nv_hitlets(self, pmthits): mask_id = pmthits["pmthitID"] == uid qe[mask_id] = 1e-2 * QE_E(pmthits["pmthitEnergy"][mask_id], uid, NVeto_PMT_QE) - self.log.debug("Applying CE") # Applying effective collection efficiency qe *= self.ce_scaling - + # Applying acceptance per pmt: for the approach in which SPE PDF has already applied a threshold for low charges self.log.debug("Applying per pmt acceptance") - # also very slow, think this is a bottleneck of the URLConfigs, it is not very efficient to call them in a loop many times. + # also very slow, think this is a bottleneck of the URLConfigs, it is not very efficient to call them in a loop many times. # qe = qe * np.vectorize(self.get_acceptance)(pmthits["pmthitID"]) NV_SPE = self.nveto_spe_parameters acceptance_dict = {k: v["acceptance"] for k, v in NV_SPE.items()} qe = qe * np.vectorize(acceptance_dict.get)(pmthits["pmthitID"]) - self.log.debug("Binomial sampling") # Generate a photoelectron based on (binomial) conversion probability qe*eCE*spe_acc pe = np.array([np.random.binomial(1, j, 1)[0] for j in qe]) @@ -132,10 +131,12 @@ def _nv_hitlets(self, pmthits): unique_ids = np.unique(pmthits["pmthitID"]) for uid in unique_ids: mask_id = pmthits["pmthitID"] == uid - + # Just call the random choice once per PMT ID SPE_channel = NV_SPE.get(uid) - spe_charge[mask_id] = self.rng.choice(SPE_channel["pe"], p = SPE_channel["SPE_values"], size=np.sum(mask_id)) + spe_charge[mask_id] = self.rng.choice( + SPE_channel["pe"], p=SPE_channel["SPE_values"], size=np.sum(mask_id) + ) pmthits["pe_area"] = spe_charge # 3. Creating hitlet times @@ -153,7 +154,7 @@ def _nv_hitlets(self, pmthits): elif self.stack_hitlets: self.log.debug("Looking for stacked hitlets") - + arr_c_evt = [] for i in np.unique(pmthits["evtid"]): arr_evt = pmthits[pmthits["evtid"] == i] @@ -181,7 +182,7 @@ def _nv_hitlets(self, pmthits): # ind = ID - 2000 # qe = self.nveto_pmt_qe[ind](WL) # # qe = self.nveto_pmt_qe["QE"][ind](WL) - + # return qe # def get_acceptance(self, ID): @@ -207,6 +208,7 @@ def energy_to_wavelenght(E): Joules_to_eV = 1.602 * 1e-19 return 1e9 * const.h * const.c / (E * Joules_to_eV) + def QE_E(E, ID, nveto_pmt_qe): WL = energy_to_wavelenght(E) ind = ID - 2000 @@ -239,4 +241,4 @@ def get_clusters_arrays(arr, typ): elif i in ["evtid", "pmthitID", "labels"]: arr_nv_c[i] = np.unique(arr[i]) - return arr_nv_c \ No newline at end of file + return arr_nv_c