3 Command-line interface to larnd-sim module. 9 import numpy.lib.recfunctions
as rfn
12 from cupy.cuda.nvtx
import RangePush, RangePop
17 from numba.cuda
import device_array
18 from numba.cuda.random
import create_xoroshiro128p_states
22 from larndsim
import consts
23 from larndsim.cuda_dict
import CudaDict
30 | | __ _ _ __ _ __ __| |______ ___ _ _ __ ___ 31 | |/ _` | '__| '_ \ / _` |______/ __| | '_ ` _ \\ 32 | | (_| | | | | | | (_| | \__ \ | | | | | | 33 |_|\__,_|_| |_| |_|\__,_| |___/_|_| |_| |_| 39 Swap x and z coordinates in tracks. 40 This is because the convention in larnd-sim is different 41 from the convention in edep-sim. FIXME. 44 tracks (:obj:`numpy.ndarray`): tracks array. 47 :obj:`numpy.ndarray`: tracks with swapped axes. 49 x_start = np.copy(tracks[
'x_start'] )
50 x_end = np.copy(tracks[
'x_end'])
51 x = np.copy(tracks[
'x'])
53 tracks[
'x_start'] = np.copy(tracks[
'z_start'])
54 tracks[
'x_end'] = np.copy(tracks[
'z_end'])
55 tracks[
'x'] = np.copy(tracks[
'z'])
57 tracks[
'z_start'] = x_start
58 tracks[
'z_end'] = x_end
64 if rng_states
is None:
65 return create_xoroshiro128p_states(n, seed=seed)
66 elif n > len(rng_states):
67 new_states = device_array(n, dtype=rng_states.dtype)
68 new_states[:len(rng_states)] = rng_states
69 new_states[len(rng_states):] = create_xoroshiro128p_states(n - len(rng_states), seed=seed)
77 response_file=
'../larndsim/bin/response_44.npy',
78 light_lut_filename=
'../larndsim/bin/lightLUT.npy',
81 pixel_thresholds_file=
None):
83 Command-line interface to run the simulation of a pixelated LArTPC 86 input_filename (str): path of the edep-sim input file 87 pixel_layout (str): path of the YAML file containing the pixel 88 layout and connection details. 89 detector_properties (str): path of the YAML file containing 90 the detector properties 91 output_filename (str): path of the HDF5 output file. If not specified 92 the output is added to the input file. 93 response_file (str, optional): path of the Numpy array containing the pre-calculated 94 field responses. Defaults to ../larndsim/bin/response_44.npy. 95 light_lut_file (str, optional): path of the Numpy array containing the light 96 look-up table. Defaults to ../larndsim/bin/lightLUT.npy. 97 bad_channels (str, optional): path of the YAML file containing the channels to be 98 disabled. Defaults to None 99 n_tracks (int, optional): number of tracks to be simulated. Defaults to None 101 pixel_thresholds_file (str): path to npz file containing pixel thresholds. Defaults 104 start_simulation =
time()
106 RangePush(
"run_simulation")
109 print(
"**************************\nLOADING SETTINGS AND INPUT\n**************************")
110 print(
"Pixel layout file:", pixel_layout)
111 print(
"Detector propeties file:", detector_properties)
112 print(
"edep-sim input file:", input_filename)
113 print(
"Response file:", response_file)
115 print(
"Disabled channel list: ", bad_channels)
116 RangePush(
"load_detector_properties")
117 consts.load_properties(detector_properties, pixel_layout)
118 from larndsim.consts
import light, detector, physics
121 RangePush(
"load_larndsim_modules")
124 from larndsim
import quenching, drifting, detsim, pixels_from_track, fee, lightLUT
127 RangePush(
"load_pixel_thresholds")
128 if pixel_thresholds_file
is not None:
129 print(
"Pixel thresholds file:", pixel_thresholds_file)
130 pixel_thresholds_lut = CudaDict.load(pixel_thresholds_file, 256)
132 pixel_thresholds_lut = CudaDict(cp.array([fee.DISCRIMINATION_THRESHOLD]), 1, 1)
135 RangePush(
"load_hd5_file")
137 with h5py.File(input_filename,
'r') as f: 138 tracks = np.array(f['segments'])
140 trajectories = np.array(f[
'trajectories'])
141 input_has_trajectories =
True 143 input_has_trajectories =
False 146 vertices = np.array(f[
'eventTruth'])
147 input_has_vertices =
True 149 print(
"Input file does not have true vertices info")
150 input_has_vertices =
False 155 if light.LIGHT_SIMULATED:
156 light_sim_dat = np.zeros([len(tracks), light.N_OP_CHANNEL*2], dtype=[(
'n_photons_det',
'f4'),(
't0_det',
'f4')])
159 print(
"Empty input dataset, exiting")
163 tracks = tracks[:n_tracks]
164 if light.LIGHT_SIMULATED:
165 light_sim_dat = light_sim_dat[:n_tracks]
167 if 'n_photons' not in tracks.dtype.names:
168 n_photons = np.zeros(tracks.shape[0], dtype=[(
'n_photons',
'f4')])
169 tracks = rfn.merge_arrays((tracks, n_photons), flatten=
True)
175 response = cp.load(response_file)
178 BPG = ceil(tracks.shape[0] / TPB)
180 print(
"******************\nRUNNING SIMULATION\n******************")
183 print(
"Quenching electrons...",end=
'')
184 start_quenching =
time()
185 quenching.quench[BPG,TPB](tracks, physics.BIRKS)
186 end_quenching =
time()
187 print(f
" {end_quenching-start_quenching:.2f} s")
189 print(
"Drifting electrons...",end=
'')
190 start_drifting =
time()
191 drifting.drift[BPG,TPB](tracks)
192 end_drifting =
time()
193 print(f
" {end_drifting-start_drifting:.2f} s")
195 if light.LIGHT_SIMULATED:
196 print(
"Calculating optical responses...",end=
'')
197 start_lightLUT =
time()
198 lut = cp.load(light_lut_filename)
200 BPG = ceil(tracks.shape[0] / TPB)
201 lightLUT.calculate_light_incidence[BPG,TPB](tracks, lut, light_sim_dat)
202 end_lightLUT =
time()
203 print(f
" {end_lightLUT-start_lightLUT:.2f} s")
208 adc_tot_ticks_list = []
209 track_pixel_map_tot = []
211 current_fractions_tot = []
214 tot_evids = np.unique(tracks[
'eventID'])
215 _, _, start_idx = np.intersect1d(tot_evids, tracks[
'eventID'], return_indices=
True)
216 _, _, rev_idx = np.intersect1d(tot_evids, tracks[
'eventID'][::-1], return_indices=
True)
217 end_idx = len(tracks[
'eventID']) - 1 - rev_idx
220 tracks_batch_runtimes = []
226 for ievd
in tqdm(range(0, tot_evids.shape[0], step), desc=
'Simulating events...', ncols=80, smoothing=0):
227 start_tracks_batch =
time()
228 first_event = tot_evids[ievd]
229 last_event = tot_evids[
min(ievd+step, tot_evids.shape[0]-1)]
231 if first_event == last_event:
235 track_subset = tracks[
min(start_idx[ievd:ievd + step]):
max(end_idx[ievd:ievd + step])+1]
236 evt_tracks = track_subset[(track_subset[
'eventID'] >= first_event) & (track_subset[
'eventID'] < last_event)]
237 first_trk_id = np.where(track_subset[
'eventID'] == evt_tracks[
'eventID'][0])[0][0] +
min(start_idx[ievd:ievd + step])
239 for itrk
in tqdm(range(0, evt_tracks.shape[0], BATCH_SIZE), desc=
' Simulating event %i batches...' % ievd, leave=
False, ncols=80):
240 selected_tracks = evt_tracks[itrk:itrk+BATCH_SIZE]
241 RangePush(
"event_id_map")
242 event_ids = selected_tracks[
'eventID']
243 unique_eventIDs = np.unique(event_ids)
249 RangePush(
"pixels_from_track")
250 max_radius = ceil(
max(selected_tracks[
"tran_diff"])*5/detector.PIXEL_PITCH)
253 BPG = ceil(selected_tracks.shape[0] / TPB)
254 max_pixels = np.array([0])
255 pixels_from_track.max_pixels[BPG,TPB](selected_tracks, max_pixels)
259 max_neighboring_pixels = (2*max_radius+1)*max_pixels[0]+(1+2*max_radius)*max_radius*2
261 active_pixels = cp.full((selected_tracks.shape[0], max_pixels[0]), -1, dtype=np.int32)
262 neighboring_pixels = cp.full((selected_tracks.shape[0], max_neighboring_pixels), -1, dtype=np.int32)
263 n_pixels_list = cp.zeros(shape=(selected_tracks.shape[0]))
265 if not active_pixels.shape[1]
or not neighboring_pixels.shape[1]:
268 pixels_from_track.get_pixels[BPG,TPB](selected_tracks,
275 RangePush(
"unique_pix")
276 shapes = neighboring_pixels.shape
277 joined = neighboring_pixels.reshape(shapes[0] * shapes[1])
278 unique_pix = cp.unique(joined)
279 unique_pix = unique_pix[(unique_pix != -1)]
282 if not unique_pix.shape[0]:
285 RangePush(
"time_intervals")
287 max_length = cp.array([0])
288 track_starts = cp.empty(selected_tracks.shape[0])
289 detsim.time_intervals[BPG,TPB](track_starts, max_length, selected_tracks)
292 RangePush(
"tracks_current")
294 signals = cp.zeros((selected_tracks.shape[0],
295 neighboring_pixels.shape[1],
296 cp.asnumpy(max_length)[0]), dtype=np.float32)
298 BPG_X = ceil(signals.shape[0] / TPB[0])
299 BPG_Y = ceil(signals.shape[1] / TPB[1])
300 BPG_Z = ceil(signals.shape[2] / TPB[2])
301 BPG = (BPG_X, BPG_Y, BPG_Z)
303 detsim.tracks_current_mc[BPG,TPB](signals, neighboring_pixels, selected_tracks, response, rng_states)
306 RangePush(
"pixel_index_map")
308 pixel_index_map = cp.full((selected_tracks.shape[0], neighboring_pixels.shape[1]), -1)
309 for i_
in range(selected_tracks.shape[0]):
310 compare = neighboring_pixels[i_, ..., cp.newaxis] == unique_pix
311 indices = cp.where(compare)
312 pixel_index_map[i_, indices[0]] = indices[1]
315 RangePush(
"track_pixel_map")
317 track_pixel_map = cp.full((unique_pix.shape[0], detsim.MAX_TRACKS_PER_PIXEL), -1)
319 BPG = ceil(unique_pix.shape[0] / TPB)
320 detsim.get_track_pixel_map[BPG, TPB](track_pixel_map, unique_pix, neighboring_pixels)
323 RangePush(
"sum_pixels_signals")
326 BPG_X = ceil(signals.shape[0] / TPB[0])
327 BPG_Y = ceil(signals.shape[1] / TPB[1])
328 BPG_Z = ceil(signals.shape[2] / TPB[2])
329 BPG = (BPG_X, BPG_Y, BPG_Z)
330 pixels_signals = cp.zeros((len(unique_pix), len(detector.TIME_TICKS)))
331 pixels_tracks_signals = cp.zeros((len(unique_pix),len(detector.TIME_TICKS),track_pixel_map.shape[1]))
332 detsim.sum_pixel_signals[BPG,TPB](pixels_signals,
337 pixels_tracks_signals)
340 RangePush(
"get_adc_values")
342 time_ticks = cp.linspace(0, len(unique_eventIDs) * detector.TIME_INTERVAL[1], pixels_signals.shape[1]+1)
343 integral_list = cp.zeros((pixels_signals.shape[0], fee.MAX_ADC_VALUES))
344 adc_ticks_list = cp.zeros((pixels_signals.shape[0], fee.MAX_ADC_VALUES))
345 current_fractions = cp.zeros((pixels_signals.shape[0], fee.MAX_ADC_VALUES, track_pixel_map.shape[1]))
348 BPG = ceil(pixels_signals.shape[0] / TPB)
350 pixel_thresholds_lut.tpb = TPB
351 pixel_thresholds_lut.bpg = BPG
352 pixel_thresholds = pixel_thresholds_lut[unique_pix.ravel()].reshape(unique_pix.shape)
354 fee.get_adc_values[BPG, TPB](pixels_signals,
355 pixels_tracks_signals,
364 adc_list = fee.digitize(integral_list)
365 adc_event_ids = np.full(adc_list.shape, unique_eventIDs[0])
368 event_id_list.append(adc_event_ids)
369 adc_tot_list.append(cp.asnumpy(adc_list))
370 adc_tot_ticks_list.append(cp.asnumpy(adc_ticks_list))
371 unique_pix_tot.append(cp.asnumpy(unique_pix))
372 current_fractions_tot.append(cp.asnumpy(current_fractions))
373 track_pixel_map[track_pixel_map != -1] += first_trk_id + itrk
374 track_pixel_map_tot.append(cp.asnumpy(track_pixel_map))
378 end_tracks_batch =
time()
379 tracks_batch_runtimes.append(end_tracks_batch - start_tracks_batch)
381 print(
"*************\nSAVING RESULT\n*************")
382 RangePush(
"Exporting to HDF5")
384 event_id_list = np.concatenate(event_id_list, axis=0)
385 adc_tot_list = np.concatenate(adc_tot_list, axis=0)
386 adc_tot_ticks_list = np.concatenate(adc_tot_ticks_list, axis=0)
387 unique_pix_tot = np.concatenate(unique_pix_tot, axis=0)
388 current_fractions_tot = np.concatenate(current_fractions_tot, axis=0)
389 track_pixel_map_tot = np.concatenate(track_pixel_map_tot, axis=0)
390 fee.export_to_hdf5(event_id_list,
394 current_fractions_tot,
397 bad_channels=bad_channels)
400 with h5py.File(output_filename,
'a')
as output_file:
401 output_file.create_dataset(
"tracks", data=tracks)
402 if light.LIGHT_SIMULATED:
403 output_file.create_dataset(
'light_dat', data=light_sim_dat)
404 if input_has_trajectories:
405 output_file.create_dataset(
"trajectories", data=trajectories)
406 if input_has_vertices:
407 output_file.create_dataset(
"eventTruth", data=vertices)
408 output_file[
'configs'].attrs[
'pixel_layout'] = pixel_layout
410 print(
"Output saved in:", output_filename)
413 end_simulation =
time()
414 print(f
"Elapsed time: {end_simulation-start_simulation:.2f} s")
416 if __name__ ==
"__main__":
417 fire.Fire(run_simulation)
def run_simulation(input_filename, pixel_layout, detector_properties, output_filename, response_file='../larndsim/bin/response_44.npy', light_lut_filename='../larndsim/bin/lightLUT.npy', bad_channels=None, n_tracks=None, pixel_thresholds_file=None)
def maybe_create_rng_states(n, seed=0, rng_states=None)
static int max(int a, int b)
T min(sqlite3 *const db, std::string const &table_name, std::string const &column_name)
def swap_coordinates(tracks)