DUNE-DAQ
DUNE Trigger and Data Acquisition software
Loading...
Searching...
No Matches
ta_dump Namespace Reference

Functions

str find_save_name (int run_id, int file_index, bool overwrite)
 
None plot_all_event_displays (list[np.ndarray] tp_data, int run_id, int file_index, bool seconds=False)
 
None plot_pdf_time_delta_histograms (np.ndarray ta_data, list[np.ndarray] tp_data, PdfPages pdf, str time_label, bool logarithm)
 
None write_summary_stats (np.ndarray data, str filename, str title)
 
 parse ()
 
 main ()
 

Variables

 ALGORITHM_LABELS = list(trgdataformats.TriggerActivityData.Algorithm.__members__.keys())
 
list ALGORITHM_TICKS = [ta_alg.value for ta_alg in trgdataformats.TriggerActivityData.Algorithm.__members__.values()]
 
 TYPE_LABELS = list(trgdataformats.TriggerActivityData.Type.__members__.keys())
 
list TYPE_TICKS = [ta_type.value for ta_type in trgdataformats.TriggerActivityData.Type.__members__.values()]
 
int TICK_TO_SEC_SCALE = 16e-9
 

Detailed Description

Display diagnostic information for TAs for a given
tpstream file.

Function Documentation

◆ find_save_name()

str ta_dump.find_save_name ( int run_id,
int file_index,
bool overwrite )
Find a new save name or overwrite an existing one.

Parameters:
    run_id (int): The run number for the read file.
    file_index (int): The file index for the run number of the read file.
    overwrite (bool): Overwrite the 0th plot directory of the same naming.

Returns:
    (str): Save name to write as.

This is missing the file extension. It's the job of the save/write command
to append the extension.

Definition at line 28 of file ta_dump.py.

28def find_save_name(run_id: int, file_index: int, overwrite: bool) -> str:
29 """
30 Find a new save name or overwrite an existing one.
31
32 Parameters:
33 run_id (int): The run number for the read file.
34 file_index (int): The file index for the run number of the read file.
35 overwrite (bool): Overwrite the 0th plot directory of the same naming.
36
37 Returns:
38 (str): Save name to write as.
39
40 This is missing the file extension. It's the job of the save/write command
41 to append the extension.
42 """
43 # Try to find a new name.
44 name_iter = 0
45 save_name = f"ta_{run_id}-{file_index:04}_figures_{name_iter:04}"
46
47 # Outputs will always create a PDF, so use that as the comparison.
48 while not overwrite and os.path.exists(save_name + ".pdf"):
49 name_iter += 1
50 save_name = f"ta_{run_id}-{file_index:04}_figures_{name_iter:04}"
51 print(f"Saving outputs to ./{save_name}.*")
52
53 return save_name
54
55

◆ main()

ta_dump.main ( )
Drives the processing and plotting.

Definition at line 278 of file ta_dump.py.

278def main():
279 """
280 Drives the processing and plotting.
281 """
282 # Process Arguments & Data
283 args = parse()
284 filename = args.filename
285 verbosity = args.verbose
286 no_displays = args.no_displays
287 start_frag = args.start_frag
288 end_frag = args.end_frag
289 no_anomaly = args.no_anomaly
290 seconds = args.seconds
291 overwrite = args.overwrite
292 batch_mode = args.batch_mode
293
294 linear = args.linear
295 log = args.log
296
297 # User didn't pass either flag, so default to both being true.
298 if (not linear) and (not log):
299 linear = True
300 log = True
301
302 data = trgtools.TAReader(filename, verbosity, batch_mode)
303
304 # Check that there are TA fragments.
305 if len(data.get_fragment_paths()) == 0:
306 print("File doesn't contain any TriggerActivity fragments.")
307 return 1
308
309 # Load all case.
310 if start_frag == 0 and end_frag == -1:
311 data.read_all_fragments() # Has extra debug/warning info
312 else: # Only load some.
313 if end_frag != 0: # Python doesn't like [n:0]
314 frag_paths = data.get_fragment_paths()[start_frag:end_frag]
315 elif end_frag == 0:
316 frag_paths = data.get_fragment_paths()[start_frag:]
317
318 for path in frag_paths:
319 data.read_fragment(path)
320
321 # Find a new save name or overwrite an old one.
322 save_name = find_save_name(data.run_id, data.file_index, overwrite)
323
324 print(f"Number of TAs: {data.ta_data.shape[0]}") # Enforcing output for useful metric
325
326 # Plotting
327
328 if not no_anomaly:
329 anomaly_filename = f"{save_name}.txt"
330 print(f"Writing descriptive statistics to {anomaly_filename}.")
331 if os.path.isfile(anomaly_filename):
332 # Prepare a new ta_anomaly_summary.txt
333 os.remove(anomaly_filename)
334
335 time_label = "Time (s)" if seconds else "Time (Ticks)"
336
337 # Dictionary containing unique title, xlabel, and xticks (only some)
338 plot_hist_dict = {
339 'adc_integral': {
340 'title': "ADC Integral Histogram",
341 'xlabel': "ADC Integral",
342 'ylabel': "Count",
343 'linear': linear,
344 'linear_style': dict(color='#63ACBE', alpha=0.6, label='Linear'),
345 'log': log,
346 'log_style': dict(color='#EE442F', alpha=0.6, label='Log')
347 },
348 'adc_peak': {
349 'title': "ADC Peak Histogram",
350 'xlabel': "ADC Count",
351 'ylabel': "Count",
352 'linear': linear,
353 'linear_style': dict(color='#63ACBE', alpha=0.6, label='Linear'),
354 'log': log,
355 'log_style': dict(color='#EE442F', alpha=0.6, label='Log')
356 },
357 'algorithm': {
358 'bins': np.sort(np.array([(tick-0.45, tick+0.45) for tick in ALGORITHM_TICKS]).flatten()),
359 'title': "Algorithm Histogram",
360 'xlabel': 'Algorithm Type',
361 'ylabel': "Count",
362 'linear': True, # TODO: Hard set for now.
363 'linear_style': dict(color='k'),
364 'log': False,
365 'xticks': {
366 'labels': ALGORITHM_LABELS,
367 'ticks': ALGORITHM_TICKS,
368 'fontsize': 6,
369 'rotation': 60,
370 'ha': 'right' # Horizontal alignment
371 }
372 },
373 # TODO: Channel data members should bin on
374 # the available channels; however, this is
375 # inconsistent between detectors (APA/CRP).
376 # Requires loading channel maps.
377 'channel_end': {
378 'title': "Channel End Histogram",
379 'xlabel': "Channel Number",
380 'ylabel': "Count",
381 'linear': linear,
382 'linear_style': dict(color='#63ACBE', alpha=0.6, label='Linear'),
383 'log': log,
384 'log_style': dict(color='#EE442F', alpha=0.6, label='Log')
385 },
386 'channel_peak': {
387 'title': "Channel Peak Histogram",
388 'xlabel': "Channel Number",
389 'ylabel': "Count",
390 'linear': linear,
391 'linear_style': dict(color='#63ACBE', alpha=0.6, label='Linear'),
392 'log': log,
393 'log_style': dict(color='#EE442F', alpha=0.6, label='Log')
394 },
395 'channel_start': {
396 'title': "Channel Start Histogram",
397 'xlabel': "Channel Number",
398 'ylabel': "Count",
399 'linear': linear,
400 'linear_style': dict(color='#63ACBE', alpha=0.6, label='Linear'),
401 'log': log,
402 'log_style': dict(color='#EE442F', alpha=0.6, label='Log')
403 },
404 'detid': {
405 'title': "Detector ID Histogram",
406 'xlabel': "Detector IDs",
407 'ylabel': "Count",
408 'linear': linear,
409 'linear_style': dict(color='#63ACBE', alpha=0.6, label='Linear'),
410 'log': log,
411 'log_style': dict(color='#EE442F', alpha=0.6, label='Log'),
412 'use_integer_xticks': True
413 },
414 'num_tps': {
415 'title': "Number of TPs per TA Histogram",
416 'xlabel': "Number of TPs",
417 'ylabel': "Count",
418 'linear': linear,
419 'linear_style': dict(color='#63ACBE', alpha=0.6, label='Linear'),
420 'log': log,
421 'log_style': dict(color='#EE442F', alpha=0.6, label='Log')
422 },
423 'time_activity': {
424 'title': "Relative Time Activity Histogram",
425 'xlabel': time_label,
426 'ylabel': "Count",
427 'linear': linear,
428 'linear_style': dict(color='#63ACBE', alpha=0.6, label='Linear'),
429 'log': log,
430 'log_style': dict(color='#EE442F', alpha=0.6, label='Log')
431 },
432 'time_end': {
433 'title': "Relative Time End Histogram",
434 'xlabel': time_label,
435 'ylabel': "Count",
436 'linear': linear,
437 'linear_style': dict(color='#63ACBE', alpha=0.6, label='Linear'),
438 'log': log,
439 'log_style': dict(color='#EE442F', alpha=0.6, label='Log')
440 },
441 'time_peak': {
442 'title': "Relative Time Peak Histogram",
443 'xlabel': time_label,
444 'ylabel': "Count",
445 'linear': linear,
446 'linear_style': dict(color='#63ACBE', alpha=0.6, label='Linear'),
447 'log': log,
448 'log_style': dict(color='#EE442F', alpha=0.6, label='Log')
449 },
450 'time_start': {
451 'title': "Relative Time Start Histogram",
452 'xlabel': time_label,
453 'ylabel': "Count",
454 'linear': linear,
455 'linear_style': dict(color='#63ACBE', alpha=0.6, label='Linear'),
456 'log': log,
457 'log_style': dict(color='#EE442F', alpha=0.6, label='Log')
458 },
459 'type': {
460 'bins': np.sort(np.array([(tick-0.45, tick+0.45) for tick in TYPE_TICKS]).flatten()),
461 'title': "Type Histogram",
462 'xlabel': "Type",
463 'ylabel': "Count",
464 'linear': True, # TODO: Hard set for now.
465 'linear_style': dict(color='k'),
466 'log': False,
467 'xticks': {
468 'labels': TYPE_LABELS,
469 'ticks': TYPE_TICKS,
470 'fontsize': 6,
471 'rotation': 60,
472 'ha': 'right' # Horizontal alignment
473 }
474 },
475 'version': {
476 'title': "Version Histogram",
477 'xlabel': "Versions",
478 'ylabel': "Count",
479 'linear': linear,
480 'linear_style': dict(color='#63ACBE', alpha=0.6, label='Linear'),
481 'log': log,
482 'log_style': dict(color='#EE442F', alpha=0.6, label='Log'),
483 'use_integer_xticks': True
484 }
485 }
486
487 pdf_plotter = PDFPlotter(f"{save_name}.pdf")
488 # Generic Plots
489 for ta_key in data.ta_data.dtype.names:
490 if 'time' in ta_key: # Special case.
491 time = data.ta_data[ta_key]
492 if seconds:
493 time = time * TICK_TO_SEC_SCALE
494 min_time = np.min(time) # Prefer making the relative time change.
495 pdf_plotter.plot_histogram(time - min_time, plot_hist_dict[ta_key])
496 if not no_anomaly:
497 write_summary_stats(time - min_time, anomaly_filename, ta_key)
498 continue
499
500 if ta_key == 'algorithm' or ta_key == 'type': # Special case.
501 plot_data = np.array([datum.value for datum in data.ta_data[ta_key]], dtype=int)
502 pdf_plotter.plot_histogram(plot_data, plot_hist_dict[ta_key])
503 if not no_anomaly:
504 write_summary_stats(plot_data, anomaly_filename, ta_key)
505 del plot_data
506 continue
507
508 pdf_plotter.plot_histogram(data.ta_data[ta_key], plot_hist_dict[ta_key])
509 if not no_anomaly:
510 write_summary_stats(data.ta_data[ta_key], anomaly_filename, ta_key)
511
512 # Analysis Plots
513 pdf = pdf_plotter.get_pdf() # Needed for extra plots that are not general.
514 # ==== Time Delta Comparisons =====
515 if linear:
516 plot_pdf_time_delta_histograms(data.ta_data, data.tp_data, pdf, time_label, False)
517 if log:
518 plot_pdf_time_delta_histograms(data.ta_data, data.tp_data, pdf, time_label, True)
519 # =================================
520
521 # ==== Time Spans Per TA ====
522 time_peak = data.ta_data['time_peak']
523 time_end = data.ta_data['time_end']
524 time_start = data.ta_data['time_start']
525 ta_min_time = np.min((time_peak, time_end, time_start))
526
527 time_peak -= ta_min_time
528 time_end -= ta_min_time
529 time_start -= ta_min_time
530
531 if seconds:
532 ta_min_time = ta_min_time * TICK_TO_SEC_SCALE
533 time_peak = time_peak * TICK_TO_SEC_SCALE
534 time_end = time_end * TICK_TO_SEC_SCALE
535 time_start = time_start * TICK_TO_SEC_SCALE
536
537 yerr = np.array([time_peak - time_start, time_end - time_peak]).astype(np.int64)
538 time_unit = "Seconds" if seconds else "Ticks"
539 time_spans_dict = {
540 'title': "TA Relative Time Spans",
541 'xlabel': "TA",
542 'ylabel': time_label,
543 'errorbar_style': {
544 'yerr': yerr,
545 'capsize': 4,
546 'color': 'k',
547 'ecolor': "#EE442F",
548 'label': f"Avg {time_unit} / TA: {(time_peak[-1] - time_peak[0]) / len(time_peak):.2f}",
549 'mec': "#EE442F",
550 'mfc': "#EE442F",
551 'marker': 'h',
552 'markersize': 4.00
553 }
554 }
555 ta_count = np.arange(len(time_peak))
556 pdf_plotter.plot_errorbar(ta_count, time_peak, time_spans_dict)
557 # ===========================
558 pdf_plotter.close()
559
560 if not no_displays:
561 plot_all_event_displays(data.tp_data, data.run_id, data.file_index, seconds)
562
563 return None
564
565
int main(int argc, char **argv)

◆ parse()

ta_dump.parse ( )
Parses CLI input arguments.

Definition at line 209 of file ta_dump.py.

209def parse():
210 """
211 Parses CLI input arguments.
212 """
213 parser = argparse.ArgumentParser(
214 description="Display diagnostic information for TAs for a given HDF5 file."
215 )
216 parser.add_argument(
217 "filename",
218 help="Absolute path to tpstream file to display."
219 )
220 parser.add_argument(
221 "--verbose", '-v',
222 action="count",
223 help="Increment the verbose level (errors, warnings, all)."
224 "Save names and skipped writes are always printed. Default: 0.",
225 default=0
226 )
227 parser.add_argument(
228 "--no-displays",
229 action="store_true",
230 help="Stops the processing of event displays."
231 )
232 parser.add_argument(
233 "--start-frag",
234 type=int,
235 help="Starting fragment index to process from. Takes negative indexing. Default: -10.",
236 default=-10
237 )
238 parser.add_argument(
239 "--end-frag",
240 type=int,
241 help="Fragment index to stop processing (i.e. not inclusive). Takes negative indexing. Default: N.",
242 default=0
243 )
244 parser.add_argument(
245 "--no-anomaly",
246 action="store_true",
247 help="Pass to not write 'ta_anomaly_summary.txt'. Default: False."
248 )
249 parser.add_argument(
250 "--seconds",
251 action="store_true",
252 help="Pass to use seconds instead of time ticks. Default: False."
253 )
254 parser.add_argument(
255 "--linear",
256 action="store_true",
257 help="Pass to use linear histogram scaling. Default: plots both linear and log."
258 )
259 parser.add_argument(
260 "--log",
261 action="store_true",
262 help="Pass to use logarithmic histogram scaling. Default: plots both linear and log."
263 )
264 parser.add_argument(
265 "--overwrite",
266 action="store_true",
267 help="Overwrite old outputs. Default: False."
268 )
269 parser.add_argument(
270 "--batch-mode", "-b",
271 action="store_true",
272 help="Do you want to run in the batch mode (without loading bars/tqdm)?"
273 )
274
275 return parser.parse_args()
276
277

◆ plot_all_event_displays()

None ta_dump.plot_all_event_displays ( list[np.ndarray] tp_data,
int run_id,
int file_index,
bool seconds = False )
Plot all event displays.

Parameters:
    tp_data (list[np.ndarray]): List of TPs for each TA.
    run_id (int): Run number.
    file_index (int): File index of this run.
    seconds (bool): If True, plot using seconds as units.

Saves event displays to a single PDF.

Definition at line 56 of file ta_dump.py.

56def plot_all_event_displays(tp_data: list[np.ndarray], run_id: int, file_index: int, seconds: bool = False) -> None:
57 """
58 Plot all event displays.
59
60 Parameters:
61 tp_data (list[np.ndarray]): List of TPs for each TA.
62 run_id (int): Run number.
63 file_index (int): File index of this run.
64 seconds (bool): If True, plot using seconds as units.
65
66 Saves event displays to a single PDF.
67 """
68 time_unit = 's' if seconds else 'Ticks'
69
70 with PdfPages(f"event_displays_{run_id}.{file_index:04}.pdf") as pdf:
71 for tadx, ta in enumerate(tp_data):
72 if seconds:
73 ta['time_start'] = ta['time_start'] * TICK_TO_SEC_SCALE
74 plt.figure(figsize=(6, 4))
75
76 times = ta['time_start'] - np.min(ta['time_start'])
77 plt.scatter(times, ta['channel'], c='k', s=2)
78
79 # Auto limits were too wide; this narrows it.
80 max_time = np.max(times)
81 min_time = np.min(times)
82 time_diff = max_time - min_time
83
84 # Only change the xlim if there is more than one TP.
85 if time_diff != 0:
86 plt.xlim((min_time - 0.1*time_diff, max_time + 0.1*time_diff))
87
88 plt.title(f'Run {run_id}.{file_index:04} Event Display: {tadx:03}')
89 plt.xlabel(f"Relative Start Time ({time_unit})")
90 plt.ylabel("Channel")
91
92 plt.tight_layout()
93 pdf.savefig()
94 plt.close()
95
96 return None
97
98

◆ plot_pdf_time_delta_histograms()

None ta_dump.plot_pdf_time_delta_histograms ( np.ndarray ta_data,
list[np.ndarray] tp_data,
PdfPages pdf,
str time_label,
bool logarithm )
Plot the different time delta histograms to a PdfPages.

Parameters:
    ta_data (np.ndarray): Array of TA data members.
    tp_data (list[np.ndarray]): List of TPs per TA. tp_data[i] holds TP data for the i-th TA.
    pdf (PdfPages): PdfPages object to append plot to.
    time_label (str): Time label to plot with (ticks vs seconds).
    logarithm (bool): Use logarithmic scaling if true.

Returns:
    Nothing. Mutates :pdf: with the new plot.

Definition at line 99 of file ta_dump.py.

104 logarithm: bool) -> None:
105 """
106 Plot the different time delta histograms to a PdfPages.
107
108 Parameters:
109 ta_data (np.ndarray): Array of TA data members.
110 tp_data (list[np.ndarray]): List of TPs per TA. tp_data[i] holds TP data for the i-th TA.
111 pdf (PdfPages): PdfPages object to append plot to.
112 time_label (str): Time label to plot with (ticks vs seconds).
113 logarithm (bool): Use logarithmic scaling if true.
114
115 Returns:
116 Nothing. Mutates :pdf: with the new plot.
117 """
118 direct_diff = ta_data['time_end'] - ta_data['time_start']
119 last_tp_start_diff = []
120 last_tp_peak_diff = []
121 for idx, tp in enumerate(tp_data):
122 last_tp_start_diff.append(np.max(tp['time_start']) - ta_data[idx]['time_start'])
123 max_peak_arg = np.argmax(tp['samples_to_peak'])
124
125 # FIXME: Replace the hard-coded SOT to TOT scaling.
126 max_peak_time = tp[max_peak_arg]['samples_to_peak'].astype(np.uint64) * 32 + tp[max_peak_arg]['time_start']
127
128 last_tp_peak_diff.append(max_peak_time + - ta_data[idx]['time_start'])
129
130 last_tp_start_diff = np.array(last_tp_start_diff)
131 last_tp_peak_diff = np.array(last_tp_peak_diff)
132
133 # Seconds case.
134 if "Ticks" not in time_label:
135 direct_diff = direct_diff * TICK_TO_SEC_SCALE
136 last_tp_start_diff = last_tp_start_diff * TICK_TO_SEC_SCALE
137 last_tp_peak_diff = last_tp_peak_diff * TICK_TO_SEC_SCALE
138
139 bins = 40
140
141 plt.figure(figsize=(6, 4))
142
143 plt.hist(
144 (direct_diff, last_tp_start_diff, last_tp_peak_diff),
145 bins=bins,
146 label=(
147 "TA(End) - TA(Start)",
148 "Last TP(Start) - TA(Start)",
149 "Last TP(Peak) - TA(Start)"
150 ),
151 color=(
152 "#B2182B",
153 "#3BB27A",
154 "#2166AC"
155 ),
156 alpha=0.6
157 )
158
159 if logarithm:
160 plt.yscale('log')
161
162 plt.title("Time Difference Histograms")
163 plt.xlabel(time_label)
164 plt.legend(framealpha=0.4)
165
166 plt.tight_layout()
167 pdf.savefig()
168 plt.close()
169 return None
170
171

◆ write_summary_stats()

None ta_dump.write_summary_stats ( np.ndarray data,
str filename,
str title )
Writes the given summary statistics to :filename:.

Parameters:
    data (np.ndarray): Array of a TA data member.
    filename (str): File to append outputs to.
    title (str): Title of the TA data member.

Appends statistics to the given file.

Definition at line 172 of file ta_dump.py.

172def write_summary_stats(data: np.ndarray, filename: str, title: str) -> None:
173 """
174 Writes the given summary statistics to :filename:.
175
176 Parameters:
177 data (np.ndarray): Array of a TA data member.
178 filename (str): File to append outputs to.
179 title (str): Title of the TA data member.
180
181 Appends statistics to the given file.
182 """
183 # Algorithm, Det ID, etc. are not expected to vary.
184 # Check first that they don't vary, and move on if so.
185 if np.all(data == data[0]):
186 print(f"{title} data member is the same for all TAs. Skipping summary statistics.")
187 return None
188
189 summary = stats.describe(data)
190 std = np.sqrt(summary.variance)
191 with open(filename, 'a') as out:
192 out.write(f"{title}\n")
193 out.write(f"Reference Statistics:\n"
194 f"\tTotal # TAs = {summary.nobs},\n"
195 f"\tMean = {summary.mean:.2f},\n"
196 f"\tStd = {std:.2f},\n"
197 f"\tMin = {summary.minmax[0]},\n"
198 f"\tMax = {summary.minmax[1]}.\n")
199 std3_count = np.sum(data > summary.mean + 3*std) + np.sum(data < summary.mean - 3*std)
200 std2_count = np.sum(data > summary.mean + 2*std) + np.sum(data < summary.mean - 2*std)
201 out.write(f"Anomalies:\n"
202 f"\t# of >3 Sigma TAs = {std3_count},\n"
203 f"\t# of >2 Sigma TAs = {std2_count}.\n")
204 out.write("\n\n")
205
206 return None
207
208

Variable Documentation

◆ ALGORITHM_LABELS

ta_dump.ALGORITHM_LABELS = list(trgdataformats.TriggerActivityData.Algorithm.__members__.keys())

Definition at line 20 of file ta_dump.py.

◆ ALGORITHM_TICKS

list ta_dump.ALGORITHM_TICKS = [ta_alg.value for ta_alg in trgdataformats.TriggerActivityData.Algorithm.__members__.values()]

Definition at line 21 of file ta_dump.py.

◆ TICK_TO_SEC_SCALE

int ta_dump.TICK_TO_SEC_SCALE = 16e-9

Definition at line 25 of file ta_dump.py.

◆ TYPE_LABELS

ta_dump.TYPE_LABELS = list(trgdataformats.TriggerActivityData.Type.__members__.keys())

Definition at line 22 of file ta_dump.py.

◆ TYPE_TICKS

list ta_dump.TYPE_TICKS = [ta_type.value for ta_type in trgdataformats.TriggerActivityData.Type.__members__.values()]

Definition at line 23 of file ta_dump.py.