HPS-MC
 
Loading...
Searching...
No Matches
tools.py
Go to the documentation of this file.
1"""! Tools that can be used in HPSMC jobs."""
2
3import json
4import os
5import gzip
6import shutil
7import subprocess
8import tarfile
9
10from subprocess import PIPE
11
12from hpsmc.component import Component
13import hpsmc.func as func
14
15
17 """!
18 Run the SLIC Geant4 simulation.
19
20 Optional parameters are: **nevents**, **macros**, **run_number**, **disable_particle_table** \n
21 Required parameters are: **detector** \n
22 Required configurations are: **slic_dir**, **detector_dir**
23 """
24
25 def __init__(self, **kwargs):
26
27 self.macros = []
28
29 self.run_number = None
30
31 self.detector_dir = None
32
36
37 Component.__init__(
38 self, name="slic", command="slic", output_ext=".slcio", **kwargs
39 )
40
41 def cmd_args(self):
42 """!
43 Setup command arguments.
44 @return list of arguments
45 """
46 if not len(self.input_files()):
47 raise Exception("No inputs given for SLIC.")
48
49 args = [
50 "-g",
51 self.__detector_file(),
52 # "-i", self.input_files()[0],
53 "-o",
54 self.output_files()[0],
55 "-d%s" % str(self.seedseed),
56 ]
57
58 if self.neventsnevents is not None:
59 args.extend(["-r", str(self.neventsnevents)])
60
61 if self.run_number is not None:
62 args.extend(["-m", "run_number.mac"])
63
64 if not self.disable_particle_table:
65 tbl = self.__particle_tbl()
66 if os.path.exists(tbl):
67 args.extend(["-P", tbl])
68 else:
69 raise Exception("SLIC particle.tbl does not exist: %s" % tbl)
70
71 if len(self.macros):
72 # args = []
73 for macro in self.macros:
74 if macro == "run_number.mac":
75 raise Exception("Macro name '%s' is not allowed." % macro)
76 if not os.path.isabs(macro):
77 raise Exception("Macro '%s' is not an absolute path." % macro)
78 args.extend(["-m", macro])
79 else:
80 args.extend(["-i", self.input_files()[0]])
81
82 return args
83
84 def __detector_file(self):
85 """! Return path to detector file."""
86 return os.path.join(self.detector_dir, self.detector, self.detector + ".lcdd")
87
88 def __particle_tbl(self):
89 """! Return path to particle table."""
90 return os.path.join(self.slic_dir, "share", "particle.tbl")
91
92 def config(self, parser):
93 """! Configure SLIC component."""
94 super().config(parser)
95
96 if self.detector_dir is None:
97 self.detector_dir = "{}/share/detectors".format(self.hpsmc_dir)
98 if not os.path.isdir(self.detector_dir):
99 raise Exception("Failed to find valid detector_dir")
100 self.logger.debug(
101 "Using detector_dir from install: {}".format(self.detector_dir)
102 )
103
104 def setup(self):
105 """! Setup SLIC component."""
106 if not os.path.exists(self.slic_dir):
107 raise Exception("slic_dir does not exist: %s" % self.slic_dir)
108
109 self.env_script = self.slic_dir + os.sep + "bin" + os.sep + "slic-env.sh"
110 if not os.path.exists(self.env_script):
111 raise Exception("SLIC setup script does not exist: %s" % self.namename)
112
113 if self.run_number is not None:
114 run_number_cmd = "/lcio/runNumber %d" % self.run_number
115 run_number_mac = open("run_number.mac", "w")
116 run_number_mac.write(run_number_cmd)
117 run_number_mac.close()
118
120 """!
121 Return list of optional parameters.
122
123 Optional parameters are: **nevents**, **macros**, **run_number**
124 @return list of optional parameters
125 """
126 return ["nevents", "macros", "run_number", "disable_particle_table"]
127
129 """!
130 Return list of required parameters.
131
132 Required parameters are: **detector**
133 @return list of required parameters
134 """
135 return ["detector"]
136
138 """!
139 Return list of required configurations.
140
141 Required configurations are: **slic_dir**, **detector_dir**
142 @return list of required configurations
143 """
144 return ["slic_dir", "detector_dir"]
145
146 def execute(self, log_out, log_err):
147 """!
148 Execute SLIC component.
149
150 Component is executed by creating command line input
151 from command and command arguments.
152 @return return code of process
153 """
154 # SLIC needs to be run inside bash as the Geant4 setup script is a piece of #@$@#$.
155 cl = 'bash -c ". %s && %s %s"' % (
156 self.env_script,
158 " ".join(self.cmd_argscmd_args()),
159 )
160
161 # self.logger.info("Executing '%s' with command: %s" % (self.name, cl))
162 proc = subprocess.Popen(cl, shell=True, stdout=log_out, stderr=log_err)
163 proc.communicate()
164 proc.wait()
165
166 return proc.returncode
167
168
170 """!
171 Copy the SQLite database file to the desired location.
172 """
173
174 def __init__(self, **kwargs):
175 """!
176 Initialize SQLiteProc to copy the SQLite file.
177
178 This component simply copies source_file to destination_file (see execute); it does not run a command,
179 so no command arguments are assembled here. Logging is deferred until after Component.__init__ has run,
180 as required by the Component base class.
181 """
182 self.source_file = kwargs.get("source_file")
183 self.destination_file = kwargs.get("destination_file")
184
185 # Ensure to call the parent constructor properly
186 Component.__init__(self, name="sqlite_file_copy", **kwargs)
187
188 def cmd_args(self):
189 """!
190 Return dummy command arguments to satisfy the parent class.
191 """
192 cmd_args = ["(no-command-needed)"]
193
194 if not all(isinstance(arg, str) for arg in cmd_args):
195 raise ValueError("All arguments must be strings.")
196 # return ["(no-command-needed)"]
197 return ["--source", self.source_file, "--destination", self.destination_file]
198
199 def execute(self, log_out, log_err):
200 """!
201 Execute the file copy operation.
202 """
203
204 try:
205 # Copy the file
206
207 self.logger.info(
208 f"Copying file from {self.source_file} to {self.destination_file}"
209 )
210 shutil.copy(self.source_file, self.destination_file)
211
212 # Provide a job-local tmp dir (used e.g. as java.io.tmpdir by downstream Java tools).
213 os.makedirs("tmp", exist_ok=True)
214
215 # Log success
216 self.logger.info(f"Successfully copied file to {self.destination_file}")
217
218 return 0 # Success code
219
220 except Exception as e:
221 self.logger.error(f"Error during file copy: {e}")
222 return 1 # Error code
223
224
226 """!
227 Run the hps-java JobManager class.
228
229 Input files have slcio format.
230
231 Required parameters are: **steering_files** \n
232 Optional parameters are: **detector**, **run_number**, **defs**
233 """
234
235 def __init__(self, steering=None, **kwargs):
236
238 self.run_number = None
239
240 self.neventsnevents = None
241
242 self.detector = None
243
245
246 self.defs = None
247
248 self.java_args = None
249
251
252 self.lcsim_cache_dir = None
253
254 self.conditions_user = None
255
257
258 self.conditions_url = None
259
260 self.steering = steering
261
263
264 if "overlay_file" in kwargs:
265 self.overlay_file = kwargs["overlay_file"]
266 else:
267 self.overlay_file = None
268
269 Component.__init__(
270 self,
271 name="job_manager",
272 command="java",
273 description="HPS Java Job Manager",
274 output_ext=".slcio",
275 **kwargs,
276 )
277
278 # Automatically append steering file key to output file name
279 if self.append_tokappend_tok is None:
281 self.logger.debug(
282 "Append token for '%s' automatically set to '%s' from steering key."
284 )
285
286 def config(self, parser):
287 """! Configure JobManager component."""
288 super().config(parser)
289 # if installed these are set in the environment script...
290 if self.hps_java_bin_jar is None:
291 if os.getenv("HPS_JAVA_BIN_JAR", None) is not None:
292 self.hps_java_bin_jar = os.getenv("HPS_JAVA_BIN_JAR", None)
293 self.logger.debug(
294 "Set HPS_JAVA_BIN_JAR from environment: {}".format(
296 )
297 )
298 else:
299 raise Exception(
300 "hps_java_bin_jar not set in environment or config file!"
301 )
302 if self.conditions_url is None:
303 if os.getenv("CONDITIONS_URL", None) is not None:
304 self.conditions_url = os.getenv("CONDITIONS_URL", None)
305 self.logger.debug(
306 "Set CONDITIONS_URL from environment: {}".format(
308 )
309 )
310
312 """!
313 Return list of required configurations.
314
315 Required configurations are: **hps_java_bin_jar**
316 @retun list of required configurations.
317 """
318 return ["hps_java_bin_jar"]
319
320 def setup(self):
321 """! Setup JobManager component."""
322 if not len(self.input_files()):
323 raise Exception("No inputs provided to hps-java.")
324
325 if self.steering not in self.steering_files:
326 raise Exception(
327 "Steering '%s' not found in: %s" % (self.steering, self.steering_files)
328 )
330
331 def cmd_args(self):
332 """!
333 Setup command arguments.
334 @return list of arguments
335 """
336 args = []
337
338 if self.java_args is not None:
339 self.logger.debug("Setting java_args from config: %s" % self.java_args)
340 args.append(self.java_args)
341
342 if self.logging_config_file is not None:
343 self.logger.debug(
344 "Setting logging_config_file from config: %s" % self.logging_config_file
345 )
346 args.append("-Djava.util.logging.config.file=%s" % self.logging_config_file)
347
348 if self.lcsim_cache_dir is not None:
349 self.logger.debug(
350 "Setting lcsim_cache_dir from config: %s" % self.lcsim_cache_dir
351 )
352 args.append("-Dorg.lcsim.cacheDir=%s" % self.lcsim_cache_dir)
353
354 if self.conditions_user is not None:
355 self.logger.debug(
356 "Setting conditions_user from config: %s" % self.conditions_user
357 )
358 args.append("-Dorg.hps.conditions.user=%s" % self.conditions_user)
359 if self.conditions_password is not None:
360 self.logger.debug("Setting conditions_password from config (not shown)")
361 args.append("-Dorg.hps.conditions.password=%s" % self.conditions_password)
362 if self.conditions_url is not None:
363 self.logger.debug(
364 "Setting conditions_url from config: %s" % self.conditions_url
365 )
366 args.append("-Dorg.hps.conditions.url=%s" % self.conditions_url)
367
368 args.append("-jar")
369 args.append(self.hps_java_bin_jar)
370
371
372 if self.event_print_interval is not None:
373 args.append("-e")
374 args.append(str(self.event_print_interval))
375
376 if self.run_number is not None:
377 args.append("-R")
378 args.append(str(self.run_number))
379
380 if self.detector is not None:
381 args.append("-d")
382 args.append(self.detector)
383
384 if len(self.output_files()):
385 args.append("-D")
386 args.append("outputFile=" + os.path.splitext(self.output_files()[0])[0])
387
388 if self.defs:
389 for k, v in self.defs.items():
390 args.append("-D")
391 args.append(k + "=" + str(v))
392
393 if not os.path.isfile(self.steering_file):
394 args.append("-r")
395 self.logger.debug(
396 "Steering does not exist at '%s' so assuming it is a resource."
397 % self.steering_file
398 )
399 else:
400 if not os.path.isabs(self.steering_file):
401 raise Exception(
402 "Steering looks like a file but is not an abs path: %s"
403 % self.steering_file
404 )
405 args.append(self.steering_file)
406
407 if self.neventsnevents is not None:
408 args.append("-n")
409 args.append(str(self.neventsnevents))
410
411 for input_file in self.input_files():
412 args.append("-i")
413 args.append(input_file)
414
415 if self.overlay_file is not None:
416 args.append("-D")
417 args.append("overlayFile=" + os.path.splitext(self.overlay_file)[0])
418
419 return args
420
422 """!
423 Return list of required parameters.
424
425 Required parameters are: **steering_files**
426 @return list of required parameters
427 """
428 return ["steering_files"]
429
431 """!
432 Return list of optional parameters.
433
434 Optional parameters are: **detector**, **run_number**, **defs**
435 @return list of optional parameters
436 """
437 return ["detector", "run_number", "defs", "nevents"]
438
439
441 """!
442 Run the make_mini_dst command on the input file.
443
444 Required parameters are: **input_file**
445 Required configs are: **minidst_install_dir**
446 """
447
448 def __init__(self, **kwargs):
449 """!
450 Initialize ProcessMiniDst with default input file and the command to run.
451 """
452 self.input_file = None
453 self.minidst_args = None
454 # Ensure to call the parent constructor properly
455 Component.__init__(self, name='make_mini_dst',
456 command='make_mini_dst',
457 description='Create the MiniDST ROOT file',
458 output_ext='.root',
459 **kwargs)
460
461 def setup(self):
462 """! Setup the MiniDST component."""
463 # Check if input files exist
464 if not len(self.input_files()):
465 raise Exception("No input files provided to make_mini_dst.")
466
467 if not os.path.exists(self.minidst_install_dir):
468 raise Exception("minidst_install_dir does not exist: %s" % self.minidst_install_dir)
469
471 """!
472 Return list of required parameters.
473
474 Required parameters are only the standard "input_files".
475 @return list of required parameters
476 """
477 return []
478
480 """!
481 Return list of optional parameters.
482
483 There are currently no optional parameters.
484 @return list of optional parameters
485 """
486 return []
487
489 """!
490 Return list of required configs.
491
492 Required configs are: **minidst_install_dir**
493 @return list of required configs
494 """
495 return ["minidst_install_dir"]
496
497 def output_files(self):
498 """! Adjust names of output files."""
499 if self.outputsoutputs is None:
500 f, ext = os.path.splitext(self.input_files()[0])
501 self.outputsoutputs = f"{f}_minidst.root"
502 print(f"Set outputs to: {self.outputs}")
503
504 return self.outputsoutputs
505
506 def cmd_args(self):
507 """!
508 Setup command arguments for make_mini_dst.
509 @return list of arguments
510 """
511 args = []
512
513 print("===== Make MiniDST with input files: ", end=" ")
514 for i in range(len(self.input_files())):
515 print(f"{self.input_files()[i]}", end=", ")
516 print(f" ==> {self.output_files()}")
517
518 if self.minidst_args is not None:
519 args.extend(self.minidst_args)
520
521 args.extend(['-o', self.output_filesoutput_files()])
522 args.extend(self.input_files())
523 return args
524
525
527 """!
528 Run the hpstr analysis tool.
529
530 Required parameters are: **config_files** \n
531 Optional parameters are: **year**, **is_data**, **nevents** \n
532 Required configs are: **hpstr_install_dir**, **hpstr_base**
533 """
534
535 def __init__(self, cfg=None, is_data=0, year=None, tracking=None, **kwargs):
536
537 self.cfg = cfg
538
539 self.is_data = is_data
540
541 self.year = year
542
543 self.tracking = tracking
544
546 self.hpstr_base = None
547
548 Component.__init__(self, name="hpstr", command="hpstr", **kwargs)
549
550 def setup(self):
551 """! Setup HPSTR component."""
552 if not os.path.exists(self.hpstr_install_dir):
553 raise Exception(
554 "hpstr_install_dir does not exist: %s" % self.hpstr_install_dir
555 )
556 self.env_script = (
557 self.hpstr_install_dir + os.sep + "bin" + os.sep + "hpstr-env.sh"
558 )
559
560 # The config file to use is read from a dict in the JSON parameters.
561 if self.cfg not in self.config_files:
562 raise Exception(
563 "Config '%s' was not found in: %s" % (self.cfg, self.config_files)
564 )
565 config_file = self.config_files[self.cfg]
566 if len(os.path.dirname(config_file)):
567 # If there is a directory name then we expect an absolute path not in the hpstr dir.
568 if os.path.isabs(config_file):
569 self.cfg_path = config_file
570 else:
571 # The config must be an abs path.
572 raise Exception(
573 "The config has a directory but is not an abs path: %s" % self.cfg
574 )
575 else:
576 # Assume the cfg file is within the hpstr base dir.
577 self.cfg_path = os.path.join(
578 self.hpstr_base, "processors", "config", config_file
579 )
580 self.logger.debug("Set config path: %s" % self.cfg_path)
581
582 # For ROOT output, automatically append the cfg key from the job params.
583 if os.path.splitext(self.input_files()[0])[1] == ".root":
585 self.logger.debug(
586 "Automatically appending token to output file: %s" % self.append_tokappend_tok
587 )
588
590 """!
591 Return list of required parameters.
592
593 Required parameters are: **config_files**
594 @return list of required parameters
595 """
596 return ["config_files"]
597
599 """!
600 Return list of optional parameters.
601
602 Optional parameters are: **year**, **is_data**, **nevents**
603 @return list of optional parameters
604 """
605 return ["year", "is_data", "nevents", "tracking"]
606
608 """!
609 Return list of required configs.
610
611 Required configs are: **hpstr_install_dir**, **hpstr_base**
612 @return list of required configs
613 """
614 return ["hpstr_install_dir", "hpstr_base"]
615
616 def cmd_args(self):
617 """!
618 Setup command arguments.
619 @return list of arguments
620 """
621 args = [
622 self.cfg_path,
623 "-t",
624 str(self.is_data),
625 "-i",
626 self.input_files()[0],
627 "-o",
628 self.output_filesoutput_files()[0],
629 ]
630 if self.neventsnevents is not None:
631 args.extend(["-n", str(self.neventsnevents)])
632 if self.year is not None:
633 args.extend(["-y", str(self.year)])
634 if self.tracking is not None:
635 args.extend(["-w", str(self.tracking)])
636 return args
637
638 def output_files(self):
639 """! Adjust names of output files."""
640 f, ext = os.path.splitext(self.input_files()[0])
641 if ".slcio" in ext:
642 return ["%s.root" % f]
643 else:
644 if not self.append_tokappend_tok:
645 self.append_tokappend_tok = self.cfg
646 return ["%s_%s.root" % (f, self.append_tokappend_tok)]
647
648 def execute(self, log_out, log_err):
649 """! Execute HPSTR component."""
650 args = self.cmd_argscmd_args()
651 cl = 'bash -c ". %s && %s %s"' % (
652 self.env_script,
654 " ".join(self.cmd_argscmd_args()),
655 )
656
657 self.logger.debug("Executing '%s' with command: %s" % (self.namename, cl))
658 proc = subprocess.Popen(cl, shell=True, stdout=log_out, stderr=log_err)
659 proc.communicate()
660 proc.wait()
661
662 return proc.returncode
663
664
665
666
667
669 """!
670 Generic class for StdHep tools.
671 """
672
673
674 seed_names = [
675 "beam_coords",
676 "beam_coords_old",
677 "lhe_tridents",
678 "lhe_tridents_displacetime",
679 "lhe_tridents_displaceuni",
680 "merge_poisson",
681 "mix_signal",
682 "random_sample",
683 ]
684
685 def __init__(self, name=None, **kwargs):
686
687 Component.__init__(self, name=name, command="stdhep_" + name, **kwargs)
688
689 def cmd_args(self):
690 """!
691 Setup command arguments.
692 @return list of arguments
693 """
694 args = []
695
696 if self.name in StdHepTool.seed_names:
697 args.extend(["-s", str(self.seedseed)])
698
699 if len(self.output_files()) == 1:
700 args.insert(0, self.output_files()[0])
701 elif len(self.output_files()) > 1:
702 raise Exception("Too many outputs specified for StdHepTool.")
703 else:
704 raise Exception("No outputs specified for StdHepTool.")
705
706 if len(self.input_files()):
707 for i in self.inputs[::-1]:
708 args.insert(0, i)
709 else:
710 raise Exception("No inputs specified for StdHepTool.")
711
712 return args
713
714
716 """!
717 Transform StdHep events into beam coordinates.
718
719 Optional parameters are: **beam_sigma_x**, **beam_sigma_y**, **beam_rot_x**,
720 **beam_rot_y**, **beam_rot_z**, **target_x**, **target_y**, **target_z**
721 """
722
723 def __init__(self, **kwargs):
724
726 self.beam_sigma_x = None
727
728 self.beam_sigma_y = None
729
730 self.target_x = None
731
732 self.target_y = None
733
734 self.target_z = None
735
736 self.beam_rot_x = None
737
738 self.beam_rot_y = None
739
740 self.beam_rot_z = None
741
742 StdHepTool.__init__(self, name="beam_coords", append_tok="rot", **kwargs)
743
744 def cmd_args(self):
745 """!
746 Setup command arguments.
747 @return list of arguments
748 """
749 args = StdHepTool.cmd_args(self)
750
751 if self.beam_sigma_x is not None:
752 args.extend(["-x", str(self.beam_sigma_x)])
753 if self.beam_sigma_y is not None:
754 args.extend(["-y", str(self.beam_sigma_y)])
755
756 if self.beam_rot_x is not None:
757 args.extend(["-u", str(self.beam_rot_x)])
758 if self.beam_rot_y is not None:
759 args.extend(["-v", str(self.beam_rot_y)])
760 if self.beam_rot_z is not None:
761 args.extend(["-w", str(self.beam_rot_z)])
762
763 if self.target_x is not None:
764 args.extend(["-X", str(self.target_x)])
765 if self.target_y is not None:
766 args.extend(["-Y", str(self.target_y)])
767 if self.target_z is not None:
768 args.extend(["-Z", str(self.target_z)])
769
770 return args
771
773 """!
774 Return list of optional parameters.
775
776 Optional parameters are: **beam_sigma_x**, **beam_sigma_y**, **beam_rot_x**,
777 **beam_rot_y**, **beam_rot_z**, **target_x**, **target_y**, **target_z**
778 @return list of optional parameters
779 """
780 return [
781 "beam_sigma_x",
782 "beam_sigma_y",
783 "beam_rot_x",
784 "beam_rot_y",
785 "beam_rot_z",
786 "target_x",
787 "target_y",
788 "target_z",
789 ]
790
791
793 """!
794 Randomly sample StdHep events into a new file.
795
796 Optional parameters are: **nevents**, **mu**
797 """
798
799 def __init__(self, **kwargs):
800 StdHepTool.__init__(self, name="random_sample", append_tok="sampled", **kwargs)
801
802 self.mu = None
803
804 def cmd_args(self):
805 """!
806 Setup command arguments.
807 @return list of arguments
808 """
809 args = []
810
811 if self.name in StdHepTool.seed_names:
812 args.extend(["-s", str(self.seedseedseed)])
813
814 args.extend(["-N", str(1)])
815
816 if self.neventsnevents is not None:
817 args.extend(["-n", str(self.neventsnevents)])
818
819 if self.mu is not None:
820 args.extend(["-m", str(self.mu)])
821
822 if len(self.output_files()) == 1:
823 # only use file name, not extension because extension is added by tool
824 args.insert(0, os.path.splitext(self.output_files()[0])[0])
825 elif len(self.output_files()) > 1:
826 raise Exception("Too many outputs specified for RandomSample.")
827 else:
828 raise Exception("No outputs specified for RandomSample.")
829
830 if len(self.input_files()):
831 for i in self.inputs[::-1]:
832 args.insert(0, i)
833 else:
834 raise Exception("No inputs were provided.")
835
836 return args
837
839 """!
840 Return list of optional parameters.
841
842 Optional parameters are: **nevents**, **mu**
843 @return list of optional parameters
844 """
845 return ["nevents", "mu"]
846
847 def execute(self, log_out, log_err):
848 """! Execute RandomSample component"""
849 returncode = Component.execute(self, log_out, log_err)
850
851 # Move file to proper output file location.
852 src = "%s_1.stdhep" % os.path.splitext(self.output_files()[0])[0]
853 dest = "%s.stdhep" % os.path.splitext(self.output_files()[0])[0]
854 self.logger.debug("Moving '%s' to '%s'" % (src, dest))
855 shutil.move(src, dest)
856
857 return returncode
858
859
861 """!
862 Convert LHE files to StdHep.
863 """
864
865 def __init__(self, **kwargs):
866
867 self.ctau = None
868 StdHepTool.__init__(self,
869 name='lhe_phi',
870 output_ext='.stdhep',
871 **kwargs)
872
873 def cmd_args(self):
874 """!
875 Setup command arguments.
876 @return list of arguments
877 """
878 args = StdHepTool.cmd_args(self)
879 return args
880
881
883 """!
884 Convert LHE files to StdHep, displacing the time by given ctau.
885
886 Optional parameters are: **ctau**
887 """
888
889 def __init__(self, **kwargs):
890
891 self.ctau = None
892 StdHepTool.__init__(
893 self, name="lhe_tridents_displacetime", output_ext=".stdhep", **kwargs
894 )
895
896 def cmd_args(self):
897 """!
898 Setup command arguments.
899 @return list of arguments
900 """
901 args = StdHepTool.cmd_args(self)
902 if self.ctau is not None:
903 args.extend(["-l", str(self.ctau)])
904 return args
905
907 """!
908 Return list of optional parameters.
909
910 Optional parameters are: **ctau**
911 @return list of optional parameters
912 """
913 return ["ctau"]
914
915
917 """!
918 Convert LHE files to StdHep, displacing the time by given ctau.
919
920 Optional parameters are: **ctau**
921 """
922
923 def __init__(self, **kwargs):
924
925 self.ctau = None
926 StdHepTool.__init__(
927 self, name="lhe_tridents_displaceuni", output_ext=".stdhep", **kwargs
928 )
929
930 def cmd_args(self):
931 """!
932 Setup command arguments.
933 @return list of arguments
934 """
935 args = StdHepTool.cmd_args(self)
936 if self.ctau is not None:
937 args.extend(["-l", str(self.ctau)])
938 return args
939
941 """!
942 Return list of optional parameters.
943
944 Optional parameters are: **ctau**
945 @return list of optional parameters
946 """
947 return ["ctau"]
948
949
951 """!
952 Add mother particles for physics samples.
953 """
954
955 def __init__(self, **kwargs):
956 StdHepTool.__init__(self, name="add_mother", append_tok="mom", **kwargs)
957
958
960 """! Add full truth mother particles for physics samples"""
961
962 def __init__(self, **kwargs):
963 StdHepTool.__init__(
964 self, "add_mother_full_truth", append_tok="mom_full_truth", **kwargs
965 )
966 if len(self.inputsinputs) != 2:
967 raise Exception(
968 "Must have 2 input files: a stdhep file and a lhe file in order"
969 )
971 base, ext = os.path.splitext(self.input_file_1)
972 if ext != ".stdhep":
973 raise Exception("The first input file must be a stdhep file")
975 base, ext = os.path.splitext(self.input_file_2)
976 if ext != ".lhe":
977 raise Exception("The second input file must be a lhe file")
978
979 def cmd_args(self):
980 """!
981 Setup command arguments.
982 @return list of arguments
983 """
984 return super().cmd_args()
985
986
988 """!
989 Merge StdHep files, applying poisson sampling.
990
991 Required parameters are: **target_thickness**, **num_electrons**
992 """
993
994 def __init__(self, xsec=0, **kwargs):
995
996 self.xsec = xsec
997
999
1000 self.num_electrons = None
1001
1002 StdHepTool.__init__(self, name="merge_poisson", append_tok="sampled", **kwargs)
1003
1004 def setup(self):
1005 """! Setup MergePoisson component."""
1006 if self.xsec > 0:
1007 self.mu = func.lint(self.target_thickness, self.num_electrons) * self.xsec
1008 else:
1009 raise Exception("Cross section is missing.")
1010 self.logger.info("mu is %f", self.mu)
1011
1013 """!
1014 Return list of required parameters.
1015
1016 Required parameters are: **target_thickness**, **num_electrons**
1017 @return list of required parameters
1018 """
1019 return ["target_thickness", "num_electrons"]
1020
1021 def cmd_args(self):
1022 """!
1023 Setup command arguments.
1024 @return list of arguments
1025 """
1026 args = []
1027 if self.name in StdHepTool.seed_names:
1028 args.extend(["-s", str(self.seedseedseed)])
1029
1030 args.extend(["-m", str(self.mu), "-N", str(1), "-n", str(self.neventsnevents)])
1031
1032 if len(self.output_files()) == 1:
1033 # only use file name, not extension because extension is added by tool
1034 args.insert(0, os.path.splitext(self.output_files()[0])[0])
1035 elif len(self.output_files()) > 1:
1036 raise Exception("Too many outputs specified for MergePoisson.")
1037 else:
1038 raise Exception("No outputs specified for MergePoisson.")
1039
1040 if len(self.input_files()):
1041 for i in self.inputs[::-1]:
1042 args.insert(0, i)
1043 else:
1044 raise Exception("No inputs were provided.")
1045
1046 return args
1047
1048 def execute(self, log_out, log_err):
1049 """! Execute MergePoisson component."""
1050 returncode = Component.execute(self, log_out, log_err)
1051
1052 # Move file from tool to proper output file location.
1053 src = "%s_1.stdhep" % os.path.splitext(self.output_files()[0])[0]
1054 dest = "%s.stdhep" % os.path.splitext(self.output_files()[0])[0]
1055 self.logger.debug("Moving '%s' to '%s'" % (src, dest))
1056 shutil.move(src, dest)
1057
1058 return returncode
1059
1060
1062 """!
1063 Merge StdHep files.
1064
1065 Optional parameters are: none \n
1066 Required parameters are: none
1067 """
1068
1069 def __init__(self, **kwargs):
1070 StdHepTool.__init__(self, name="merge_files", **kwargs)
1071
1073 """!
1074 Return list of optional parameters.
1075
1076 Optional parameters are: none
1077 @return list of optional parameters
1078 """
1079 return []
1080
1082 """!
1083 Return list of required parameters.
1084
1085 Required parameters are: none
1086 @return list of required parameters
1087 """
1088 return []
1089
1090
1092 """!
1093 Count number of events in a StdHep file.
1094 """
1095
1096 def __init__(self, **kwargs):
1097 Component.__init__(
1098 self, name="stdhep_count", command="stdhep_count.sh", **kwargs
1099 )
1100
1101 def cmd_args(self):
1102 """!
1103 Setup command arguments.
1104 @return list of arguments
1105 """
1106
1107 return [self.input_files()[0]]
1108
1109 def execute(self, log_out, log_err):
1110 """! Execute StdHepCount component."""
1111 cl = [self.command]
1112 cl.extend(self.cmd_argscmd_args())
1113 proc = subprocess.Popen(cl, stdout=PIPE)
1114 (output, err) = proc.communicate()
1115
1116 nevents = int(output.split()[1])
1117 print("StdHep file '%s' has %d events." % (self.input_files()[0], nevents))
1118
1119 return proc.returncode
1120
1121
1123 """!
1124 Generic base class for Java based tools.
1125 """
1126
1127 def __init__(self, name, java_class, **kwargs):
1128
1129 self.java_class = java_class
1130
1131 self.java_args = None
1132
1133 self.conditions_url = None
1134 Component.__init__(self, name, "java", **kwargs)
1135
1137 """!
1138 Return list of required config.
1139
1140 Required config are: **hps_java_bin_jar**
1141 @return list of required config
1142 """
1143 return ["hps_java_bin_jar"]
1144
1145 def cmd_args(self):
1146 """!
1147 Setup command arguments.
1148 @return list of arguments
1149 """
1150 args = []
1151 if self.java_args is not None:
1152 self.logger.debug("Setting java_args from config: %s" + self.java_args)
1153 args.append(self.java_args)
1154 if self.conditions_url is not None:
1155 self.logger.debug(
1156 "Setting conditions_url from config: %s" % self.conditions_url
1157 )
1158 args.append("-Dorg.hps.conditions.url=%s" % self.conditions_url)
1159 args.append("-cp")
1160 args.append(self.hps_java_bin_jar)
1161 args.append(self.java_class)
1162 return args
1163
1164 def config(self, parser):
1165 super().config(parser)
1166
1167
1169 """!
1170 Convert EVIO events to LCIO using the hps-java EvioToLcio command line tool.
1171
1172 Input files have evio format (format used by DAQ system).
1173
1174 Required parameters are: **detector**, **steering_files** \n
1175 Optional parameters are: **run_number**, **skip_events**, **nevents**, **event_print_interval**
1176 """
1177
1178 def __init__(self, steering=None, **kwargs):
1179
1180 self.detector = None
1181
1182 self.run_number = None
1183
1184 self.skip_events = None
1185
1187
1188 self.steering = steering
1189
1190 JavaTool.__init__(
1191 self,
1192 name="evio_to_lcio",
1193 java_class="org.hps.evio.EvioToLcio",
1194 output_ext=".slcio",
1195 **kwargs,
1196 )
1197
1199 """!
1200 Return list of required parameters.
1201
1202 Required parameters are: **detector**, **steering_files**
1203 @return list of required parameters
1204 """
1205 return ["detector", "steering_files"]
1206
1208 """!
1209 Return list of optional parameters.
1210
1211 Optional parameters are: **run_number**, **skip_events**, **nevents**, **event_print_interval**
1212 @return list of optional parameters
1213 """
1214 return ["run_number", "skip_events", "nevents", "event_print_interval"]
1215
1216 def setup(self):
1217 """! Setup EvioToLcio component."""
1218 super().setup()
1219 if self.steering not in self.steering_files:
1220 raise Exception(
1221 "Steering '%s' not found in: %s" % (self.steering, self.steering_files)
1222 )
1224
1225 def cmd_args(self):
1226 """!
1227 Setup command arguments.
1228 @return list of arguments
1229 """
1230 args = JavaTool.cmd_args(self)
1231 if not len(self.output_files()):
1232 raise Exception("No output files were provided.")
1233 output_file = self.output_files()[0]
1234 # Keep Java's scratch under the job dir (created by SQLiteProc) rather than the shared system /tmp.
1235 args.append("-Djava.io.tmpdir=./tmp")
1236 args.append("-DoutputFile=%s" % os.path.splitext(output_file)[0])
1237 # Fall back to a job-local SQLite conditions snapshot when no conditions URL was configured, so
1238 # offline/el9 running does not require the central conditions database. A configured conditions_url
1239 # (handled by JavaTool.cmd_args above) still takes precedence.
1240 if self.conditions_url is None:
1241 args.append("-Dorg.hps.conditions.url=jdbc:sqlite:./hps_local_conditions.db")
1242 args.extend(["-d", self.detector])
1243 if self.run_number is not None:
1244 args.extend(["-R", str(self.run_number)])
1245 if self.skip_events is not None:
1246 args.extend(["-s", str(self.skip_events)])
1247
1248 if not os.path.isfile(self.steering_file):
1249 args.append("-r")
1250 self.logger.debug(
1251 "Steering does not exist at '%s' so assuming it is a resource."
1252 % self.steering_file
1253 )
1254 else:
1255 if not os.path.isabs(self.steering_file):
1256 raise Exception(
1257 "Steering looks like a file but is not an abs path: %s"
1258 % self.steering_file
1259 )
1260 args.extend(["-x", self.steering_file])
1261
1262 if self.neventsnevents is not None:
1263 args.extend(["-n", str(self.neventsnevents)])
1264
1265 args.append("-b")
1266
1267 for inputfile in self.input_files():
1268 args.append(inputfile)
1269
1270 if self.event_print_interval is not None:
1271 args.extend(["-e", str(self.event_print_interval)])
1272
1273 return args
1274
1275
1277 """!
1278 Space MC events and apply energy filters to process before readout.
1279
1280 Optional parameters are: **filter_ecal_hit_ecut**, **filter_event_interval**,
1281 **filter_nevents_read**, **filter_nevents_write**, **filter_no_cuts** \n
1282 Required config are: **hps_java_bin_jar**
1283 """
1284
1285 def __init__(self, **kwargs):
1286 if "filter_no_cuts" in kwargs:
1287 self.filter_no_cuts = kwargs["filter_no_cuts"]
1288 else:
1289
1290 self.filter_no_cuts = False
1291
1292 if "filter_ecal_pairs" in kwargs:
1293 self.filter_ecal_pairs = kwargs["filter_ecal_pairs"]
1294 else:
1295 self.filter_ecal_pairs = False
1296
1297 if "filter_ecal_hit_ecut" in kwargs:
1298 self.filter_ecal_hit_ecut = kwargs["filter_ecal_hit_ecut"]
1299 else:
1300
1301 self.filter_ecal_hit_ecut = -1.0
1302 # self.filter_ecal_hit_ecut = 0.05
1303
1304 if "filter_event_interval" in kwargs:
1305 self.filter_event_interval = kwargs["filter_event_interval"]
1306 else:
1307
1308 self.filter_event_interval = 250
1309
1310 if "filter_nevents_read" in kwargs:
1311 self.filter_nevents_read = kwargs["filter_nevents_read"]
1312 else:
1313
1314 self.filter_nevents_read = -1
1315
1316 if "filter_nevents_write" in kwargs:
1317 self.filter_nevents_write = kwargs["filter_nevents_write"]
1318 else:
1319
1320 self.filter_nevents_write = -1
1321
1323
1324 JavaTool.__init__(
1325 self,
1326 name="filter_bunches",
1327 java_class="org.hps.util.FilterMCBunches",
1328 append_tok="filt",
1329 **kwargs,
1330 )
1331
1332 def config(self, parser):
1333 """! Configure FilterBunches component."""
1334 super().config(parser)
1335 if self.hps_java_bin_jarhps_java_bin_jar is None:
1336 if os.getenv("HPS_JAVA_BIN_JAR", None) is not None:
1337 self.hps_java_bin_jarhps_java_bin_jar = os.getenv("HPS_JAVA_BIN_JAR", None)
1338 self.logger.debug(
1339 "Set HPS_JAVA_BIN_JAR from environment: {}".format(
1341 )
1342 )
1343
1344 def cmd_args(self):
1345 """!
1346 Setup command arguments.
1347 @return list of arguments
1348 """
1349 args = JavaTool.cmd_args(self)
1350 args.append("-e")
1351 args.append(str(self.filter_event_interval))
1352 for i in self.input_files():
1353 args.append(i)
1354 args.append(self.output_files()[0])
1355 if self.filter_ecal_pairs:
1356 args.append("-d")
1357 if self.filter_ecal_hit_ecut > 0:
1358 args.append("-E")
1359 args.append(str(self.filter_ecal_hit_ecut))
1360 if self.filter_nevents_read > 0:
1361 args.append("-n")
1362 args.append(str(self.filter_nevents_read))
1363 if self.filter_nevents_write > 0:
1364 args.append("-w")
1365 args.append(str(self.filter_nevents_write))
1366 if self.filter_no_cuts:
1367 args.append("-a")
1368 return args
1369
1371 """!
1372 Return list of optional parameters.
1373
1374 Optional parameters are: **filter_ecal_hit_ecut**, **filter_event_interval**,
1375 **filter_nevents_read**, **filter_nevents_write**, **filter_no_cuts** \n
1376 @return list of optional parameters
1377 """
1378 return [
1379 "filter_ecal_hit_ecut",
1380 "filter_event_interval",
1381 "filter_nevents_read",
1382 "filter_nevents_write",
1383 "filter_no_cuts",
1384 ]
1385
1387 """!
1388 Return list of required config.
1389
1390 Required config are: **hps_java_bin_jar**
1391 @return list of required config
1392 """
1393 return ["hps_java_bin_jar"]
1394
1395
1397 """!
1398 Apply hodo-hit filter and space MC events to process before readout.
1399
1400 The nevents parameter is not settable from JSON in this class. It should
1401 be supplied as an init argument in the job script if it needs to be
1402 customized (the default nevents and event_interval used to apply spacing
1403 should usually not need to be changed by the user). \n
1404
1405 Optional parameters are: **num_hodo_hits**, **event_interval**
1406 """
1407
1408 def __init__(self, **kwargs):
1409 if "num_hodo_hits" in kwargs:
1410 self.num_hodo_hits = kwargs["num_hodo_hits"]
1411 else:
1412 self.num_hodo_hits = 0
1413
1414 if "event_interval" in kwargs:
1415 self.event_interval = kwargs["event_interval"]
1416 else:
1417 self.event_interval = 250
1418
1419 JavaTool.__init__(
1420 self,
1421 name="filter_events",
1422 java_class="org.hps.util.ExtractEventsWithHitAtHodoEcal",
1423 append_tok="filt",
1424 **kwargs,
1425 )
1426
1427 def cmd_args(self):
1428 """!
1429 Setup command arguments.
1430 @return list of arguments
1431 """
1432 args = JavaTool.cmd_args(self)
1433 args.append("-e")
1434 args.append(str(self.event_interval))
1435 for i in self.input_files():
1436 args.append(i)
1437 args.append(self.output_files()[0])
1438 if self.num_hodo_hits > 0:
1439 args.append("-M")
1440 args.append(str(self.num_hodo_hits))
1441 if self.neventsnevents:
1442 args.append("-w")
1443 args.append(str(self.neventsnevents))
1444 return args
1445
1447 """!
1448 Return list of optional parameters.
1449
1450 Optional parameters are: **num_hodo_hits**, **event_interval**
1451 @return list of optional parameters
1452 """
1453 return ["num_hodo_hits", "event_interval"]
1454
1455
1457 """!
1458 Unzip the input files to outputs.
1459 """
1460
1461 def __init__(self, **kwargs):
1462 Component.__init__(self, name="unzip", command="gunzip", **kwargs)
1463
1464 def output_files(self):
1465 """! Return list of output files."""
1466 if self.outputs:
1467 return self.outputs
1468 return [os.path.splitext(i)[0] for i in self.input_files()]
1469
1470 def execute(self, log_out, log_err):
1471 """! Execute Unzip component."""
1472 for i in range(0, len(self.input_files())):
1473 inputfile = self.input_files()[i]
1474 outputfile = self.output_filesoutput_files()[i]
1475 with gzip.open(inputfile, "rb") as in_file, open(
1476 outputfile, "wb"
1477 ) as out_file:
1478 shutil.copyfileobj(in_file, out_file)
1479 self.logger.debug("Unzipped '%s' to '%s'" % (inputfile, outputfile))
1480 return 0
1481
1482
1484 """!
1485 Dump LCIO event information.
1486
1487 Required parameters are: none \n
1488 Required config are: **lcio_dir**
1489 """
1490
1491 def __init__(self, **kwargs):
1492
1493 self.lcio_dir = None
1494 Component.__init__(self, name="lcio_dump_event", command="dumpevent", **kwargs)
1495
1496 if "event_num" in kwargs:
1497 self.event_num = kwargs["event_num"]
1498 else:
1499 self.event_num = 1
1500
1501 def config(self, parser):
1502 """! Configure LCIODumpEvent component."""
1503 super().config(parser)
1504 if self.lcio_dir is None:
1505 self.lcio_dir = self.hpsmc_dir
1506
1507 def setup(self):
1508 """! Setup LCIODumpEvent component."""
1509 self.commandcommand = self.lcio_dir + "/bin/dumpevent"
1510
1511 def cmd_args(self):
1512 """!
1513 Setup command arguments.
1514 @return list of arguments
1515 """
1516 if not len(self.input_files()):
1517 raise Exception("Missing required inputs for LCIODumpEvent.")
1518 args = []
1519 args.append(self.input_files()[0])
1520 args.append(str(self.event_num))
1521 return args
1522
1524 """!
1525 Return list of required config.
1526
1527 Required config are: **lcio_dir**
1528 @return list of required config
1529 """
1530 return ["lcio_dir"]
1531
1533 """!
1534 Return list of required parameters.
1535
1536 Required parameters are: none
1537 @return list of required parameters
1538 """
1539 return []
1540
1541
1543 """!
1544 Count events in an LHE file.
1545 """
1546
1547 def __init__(self, minevents=0, fail_on_underflow=False, **kwargs):
1548 self.minevents = minevents
1549 Component.__init__(self, name="lhe_count", **kwargs)
1550
1551 def setup(self):
1552 """! Setup LHECount component."""
1553 if not len(self.input_files()):
1554 raise Exception("Missing at least one input file.")
1555
1556 def cmd_exists(self):
1557 """!
1558 Check if command exists.
1559 @return True if command exists
1560 """
1561 return True
1562
1563 def execute(self, log_out, log_err):
1564 """! Execute LHECount component."""
1565 for i in self.inputs:
1566 with gzip.open(i, "rb") as in_file:
1567 lines = in_file.readlines()
1568
1569 nevents = 0
1570 for line in lines:
1571 if "<event>" in line:
1572 nevents += 1
1573
1574 print("LHE file '%s' has %d events." % (i, nevents))
1575
1576 if nevents < self.minevents:
1577 msg = "LHE file '%s' does not contain the minimum %d events." % (
1578 i,
1579 nevents,
1580 )
1581 if self.fail_on_underflow:
1582 raise Exception(msg)
1583 else:
1584 self.logger.warning(msg)
1585 return 0
1586
1587
1589 """!
1590 Tar files into an archive.
1591 """
1592
1593 def __init__(self, **kwargs):
1594 Component.__init__(self, name="tar_files", **kwargs)
1595
1596 def cmd_exists(self):
1597 """!
1598 Check if command exists.
1599 @return True if command exists
1600 """
1601 return True
1602
1603 def execute(self, log_out, log_err):
1604 """! Execute TarFiles component."""
1605 self.logger.debug("Opening '%s' for writing ..." % self.outputs[0])
1606 tar = tarfile.open(self.outputs[0], "w")
1607 for i in self.inputs:
1608 self.logger.debug("Adding '%s' to archive" % i)
1609 tar.add(i)
1610 tar.close()
1611 self.logger.info("Wrote archive '%s'" % self.outputs[0])
1612 return 0
1613
1614
1616 """!
1617 Move input files to new locations.
1618 """
1619
1620 def __init__(self, **kwargs):
1621 Component.__init__(self, name="move_files", **kwargs)
1622
1623 def cmd_exists(self):
1624 """!
1625 Check if command exists.
1626 @return True if command exists
1627 """
1628 return True
1629
1630 def execute(self, log_out, log_err):
1631 """! Execute TarFiles component."""
1632 if len(self.inputsinputs) != len(self.outputsoutputs):
1633 raise Exception("Input and output lists are not the same length!")
1634 for io in zip(self.inputsinputs, self.outputsoutputs):
1635 src = io[0]
1636 dest = io[1]
1637 self.logger.info("Moving %s -> %s" % (src, dest))
1638 shutil.move(src, dest)
1639 return 0
1640
1641
1643 """!
1644 Generic component for LCIO tools.
1645
1646 Required parameters are: none \n
1647 Required config are: **lcio_bin_jar**
1648 """
1649
1650 def __init__(self, name=None, **kwargs):
1651
1652 self.lcio_bin_jar = None
1653 Component.__init__(self, name, command="java", **kwargs)
1654
1655 def config(self, parser):
1656 """! Configure LCIOTool component."""
1657 super().config(parser)
1658 if self.lcio_bin_jar is None:
1659 self.config_from_environ()
1660
1661 def cmd_args(self):
1662 """!
1663 Setup command arguments.
1664 @return list of arguments
1665 """
1666 if not self.name:
1667 raise Exception("Name required to write cmd args for LCIOTool.")
1668 return ["-jar", self.lcio_bin_jar, self.name]
1669
1671 """!
1672 Return list of required config.
1673
1674 Required config are: **lcio_bin_jar**
1675 @return list of required config
1676 """
1677 return ["lcio_bin_jar"]
1678
1680 """!
1681 Return list of required parameters.
1682
1683 Required parameters are: none
1684 @return list of required parameters
1685 """
1686 return []
1687
1688
1690 """!
1691 Concatenate LCIO files together.
1692 """
1693
1694 def __init__(self, **kwargs):
1695 LCIOTool.__init__(self, name="concat", **kwargs)
1696
1697 def cmd_args(self):
1698 """!
1699 Setup command arguments.
1700 @return list of arguments
1701 """
1702 args = LCIOTool.cmd_args(self)
1703 if not len(self.input_files()):
1704 raise Exception("Missing at least one input file.")
1705 if not len(self.output_files()):
1706 raise Exception("Missing an output file.")
1707 for i in self.input_files():
1708 args.extend(["-f", i])
1709 args.extend(["-o", self.outputs[0]])
1710 return args
1711
1712
1714 """!
1715 Count events in LCIO files.
1716
1717 Required parameters are: none \n
1718 Optional parameters are: none
1719 """
1720
1721 def __init__(self, **kwargs):
1722 LCIOTool.__init__(self, name="count", **kwargs)
1723
1724 def cmd_args(self):
1725 """!
1726 Setup command arguments.
1727 @return list of arguments
1728 """
1729 args = LCIOTool.cmd_args(self)
1730 if not len(self.inputsinputs):
1731 raise Exception("Missing an input file.")
1732 args.extend(["-f", self.inputsinputs[0]])
1733 return args
1734
1736 """!
1737 Return list of required parameters.
1738
1739 Required parameters are: none
1740 @return list of required parameters
1741 """
1742 return []
1743
1745 """!
1746 Return list of optional parameters.
1747
1748 Optional parameters are: none
1749 @return list of optional parameters
1750 """
1751 return []
1752
1753
1755 """!
1756 Merge LCIO files.
1757 """
1758
1759 def __init__(self, **kwargs):
1760 LCIOTool.__init__(self, name="merge", **kwargs)
1761
1762 def cmd_args(self):
1763 """!
1764 Setup command arguments.
1765 @return list of arguments
1766 """
1767 args = LCIOTool.cmd_args(self)
1768 if not len(self.input_files()):
1769 raise Exception("Missing at least one input file.")
1770 if not len(self.output_files()):
1771 raise Exception("Missing an output file.")
1772 for i in self.input_files():
1773 args.extend(["-f", i])
1774 args.extend(["-o", self.outputs[0]])
1775 if self.neventsnevents is not None:
1776 args.extend(["-n", str(self.neventsnevents)])
1777 return args
1778
1779
1780"""
1781MergeROOT tool for hps-mc
1782Merges ROOT files using hadd with validation
1783"""
1784
1785
1787 """
1788 Merge ROOT files using hadd with event count validation.
1789
1790 This component uses ROOT's hadd utility to merge multiple ROOT files
1791 into a single output file, and validates that all events are preserved.
1792 """
1793
1794 def __init__(self, **kwargs):
1795 """
1796 Initialize MergeROOT component.
1797
1798 Parameters
1799 ----------
1800 inputs : list
1801 List of input ROOT files to merge
1802 outputs : list
1803 List containing the output merged ROOT file name
1804 force : bool, optional
1805 Force overwrite of output file (default: True)
1806 compression : int, optional
1807 Compression level for output file (0-9, default: None uses hadd default)
1808 validate : bool, optional
1809 Validate event counts after merge (default: True)
1810 write_stats : bool, optional
1811 Write JSON stats file after merge (default: True when validate=True)
1812 job_id : int, optional
1813 Job ID to include in stats output
1814 """
1815 Component.__init__(self, **kwargs)
1816
1817 # Set default command
1818 if not hasattr(self, "command") or self.commandcommand is None:
1819 self.commandcommand = "hadd"
1820
1821 # Set force overwrite by default
1822 if not hasattr(self, "force"):
1823 self.force = True
1824
1825 # Optional compression level
1826 if not hasattr(self, "compression"):
1827 self.compression = None
1828
1829 # Enable validation by default
1830 if not hasattr(self, "validate"):
1831 self.validate = True
1832
1833 # Write stats JSON (default: True when validate=True)
1834 if not hasattr(self, "write_stats"):
1836
1837 # Optional job ID for stats output
1838 if not hasattr(self, "job_id"):
1839 self.job_id = None
1840
1841 # Store event counts
1844
1845 # Track validation result
1847
1848 def cmd_args(self):
1849 """
1850 Build command line arguments for hadd.
1851
1852 Returns
1853 -------
1854 list
1855 List of command arguments
1856 """
1857 import sys
1858 sys.stderr.write("MergeROOT DEBUG: cmd_args() called\n")
1859 sys.stderr.write(" self.force=%s, self.compression=%s\n" % (self.force, self.compression))
1860 sys.stderr.write(" self.inputs=%s\n" % self.inputsinputs)
1861 sys.stderr.write(" self.outputs=%s\n" % self.outputsoutputs)
1862 sys.stderr.flush()
1863
1864 args = []
1865
1866 # Add force flag if enabled
1867 if self.force:
1868 args.append("-f")
1869
1870 # Add compression level if specified
1871 if self.compression is not None:
1872 args.extend(["-fk", "-f%d" % self.compression])
1873
1874 # Add output file
1875 if self.outputsoutputs and len(self.outputsoutputs) > 0:
1876 args.append(self.outputsoutputs[0])
1877 else:
1878 sys.stderr.write("MergeROOT DEBUG: ERROR - No output file specified!\n")
1879 sys.stderr.flush()
1880 raise RuntimeError("MergeROOT: No output file specified")
1881
1882 # Add input files
1883 if self.inputsinputs and len(self.inputsinputs) > 0:
1884 args.extend(self.inputsinputs)
1885 else:
1886 sys.stderr.write("MergeROOT DEBUG: ERROR - No input files specified!\n")
1887 sys.stderr.flush()
1888 raise RuntimeError("MergeROOT: No input files specified")
1889
1890 sys.stderr.write("MergeROOT DEBUG: cmd_args() returning: %s\n" % args)
1891 sys.stderr.flush()
1892 return args
1893
1894 def scan_root_file(self, filename, log_out=None):
1895 """
1896 Scan a ROOT file and extract TTree event counts.
1897
1898 Parameters
1899 ----------
1900 filename : str
1901 Path to ROOT file
1902 log_out : file, optional
1903 Log file for output (used to report multiple key cycles)
1904
1905 Returns
1906 -------
1907 dict
1908 Dictionary mapping tree names to entry counts
1909 """
1910 try:
1911 import ROOT
1912 except ImportError:
1913 raise RuntimeError(
1914 "MergeROOT: PyROOT is required for validation but not available"
1915 )
1916
1917 tree_counts = {}
1918 tree_cycles = {} # Track cycle numbers: {tree_name: [(cycle, entries), ...]}
1919
1920 # Open ROOT file
1921 root_file = ROOT.TFile.Open(filename, "READ")
1922 if not root_file or root_file.IsZombie():
1923 raise RuntimeError("MergeROOT: Cannot open ROOT file: %s" % filename)
1924
1925 # Iterate through all keys in the file
1926 for key in root_file.GetListOfKeys():
1927 obj = key.ReadObj()
1928
1929 # Check if it's a TTree
1930 if obj.InheritsFrom("TTree"):
1931 tree_name = obj.GetName()
1932 cycle = key.GetCycle()
1933 num_entries = obj.GetEntries()
1934
1935 if tree_name not in tree_cycles:
1936 tree_cycles[tree_name] = []
1937 tree_cycles[tree_name].append((cycle, num_entries))
1938
1939 root_file.Close()
1940
1941 # Process collected cycles - use highest cycle number for each tree
1942 for tree_name, cycles in tree_cycles.items():
1943 if len(cycles) > 1:
1944 # Sort by cycle number (highest first)
1945 cycles.sort(key=lambda x: x[0], reverse=True)
1946 highest_cycle, highest_entries = cycles[0]
1947 if log_out:
1948 log_out.write(" WARNING: Multiple key cycles found for tree '%s':\n" % tree_name)
1949 for cyc, ent in cycles:
1950 marker = " <-- using" if cyc == highest_cycle else ""
1951 log_out.write(" Cycle %d: %d entries%s\n" % (cyc, ent, marker))
1952 tree_counts[tree_name] = highest_entries
1953 else:
1954 tree_counts[tree_name] = cycles[0][1]
1955
1956 return tree_counts
1957
1958 def scan_input_files(self, log_out):
1959 """
1960 Scan all input files and store tree event counts.
1961
1962 Parameters
1963 ----------
1964 log_out : file
1965 Log file for output
1966 """
1967 log_out.write("\n" + "=" * 70 + "\n")
1968 log_out.write("MergeROOT: Scanning input files for TTrees\n")
1969 log_out.write("=" * 70 + "\n")
1970
1971 for input_file in self.inputsinputs:
1972 if not os.path.exists(input_file):
1973 raise RuntimeError("MergeROOT: Input file not found: %s" % input_file)
1974
1975 log_out.write("\nScanning: %s\n" % input_file)
1976 tree_counts = self.scan_root_file(input_file, log_out)
1977
1978 if not tree_counts:
1979 log_out.write(" WARNING: No TTrees found in this file\n")
1980 else:
1981 for tree_name, count in tree_counts.items():
1982 log_out.write(" Tree '%s': %d events\n" % (tree_name, count))
1983
1984 self.input_tree_counts[input_file] = tree_counts
1985
1986 log_out.write("\n" + "=" * 70 + "\n")
1987 log_out.flush()
1988
1989 def scan_output_file(self, log_out):
1990 """
1991 Scan output file and store tree event counts.
1992
1993 Parameters
1994 ----------
1995 log_out : file
1996 Log file for output
1997 """
1998 output_file = self.outputsoutputs[0]
1999
2000 log_out.write("\n" + "=" * 70 + "\n")
2001 log_out.write("MergeROOT: Scanning output file for TTrees\n")
2002 log_out.write("=" * 70 + "\n")
2003 log_out.write("\nScanning: %s\n" % output_file)
2004
2005 self.output_tree_counts = self.scan_root_file(output_file, log_out)
2006
2007 if not self.output_tree_counts:
2008 log_out.write(" WARNING: No TTrees found in output file\n")
2009 else:
2010 for tree_name, count in self.output_tree_counts.items():
2011 log_out.write(" Tree '%s': %d events\n" % (tree_name, count))
2012
2013 log_out.write("\n" + "=" * 70 + "\n")
2014 log_out.flush()
2015
2016 def validate_merge(self, log_out):
2017 """
2018 Validate that event counts match between input and output files.
2019
2020 Parameters
2021 ----------
2022 log_out : file
2023 Log file for output
2024
2025 Returns
2026 -------
2027 bool
2028 True if validation passes, False otherwise
2029 """
2030 log_out.write("\n" + "=" * 70 + "\n")
2031 log_out.write("MergeROOT: Validating merge results\n")
2032 log_out.write("=" * 70 + "\n\n")
2033
2034 # Calculate sum of events per tree across all input files
2035 total_input_counts = {}
2036
2037 for input_file, tree_counts in self.input_tree_counts.items():
2038 for tree_name, count in tree_counts.items():
2039 if tree_name not in total_input_counts:
2040 total_input_counts[tree_name] = 0
2041 total_input_counts[tree_name] += count
2042
2043 # Check that all input trees are in output
2044 all_valid = True
2045
2046 if not total_input_counts:
2047 log_out.write("WARNING: No TTrees found in input files\n")
2048 return True
2049
2050 log_out.write("Event count validation:\n")
2051 log_out.write("-" * 70 + "\n")
2052 log_out.write(
2053 "%-30s %15s %15s %10s\n"
2054 % ("Tree Name", "Input Events", "Output Events", "Status")
2055 )
2056 log_out.write("-" * 70 + "\n")
2057
2058 for tree_name, input_count in sorted(total_input_counts.items()):
2059 output_count = self.output_tree_counts.get(tree_name, 0)
2060
2061 if output_count == input_count:
2062 status = "✓ PASS"
2063 else:
2064 status = "✗ FAIL"
2065 all_valid = False
2066
2067 log_out.write(
2068 "%-30s %15d %15d %10s\n"
2069 % (tree_name, input_count, output_count, status)
2070 )
2071
2072 # Check for trees in output that weren't in input
2073 extra_trees = set(self.output_tree_counts.keys()) - set(
2074 total_input_counts.keys()
2075 )
2076 if extra_trees:
2077 log_out.write("\nWARNING: Output contains trees not found in inputs:\n")
2078 for tree_name in extra_trees:
2079 log_out.write(
2080 " - %s: %d events\n"
2081 % (tree_name, self.output_tree_counts[tree_name])
2082 )
2083
2084 log_out.write("-" * 70 + "\n")
2085
2086 if all_valid:
2087 log_out.write("\n✓ VALIDATION PASSED: All event counts match!\n")
2088 else:
2089 log_out.write("\n✗ VALIDATION FAILED: Event count mismatch detected!\n")
2090
2091 log_out.write("=" * 70 + "\n\n")
2092 log_out.flush()
2093
2094 return all_valid
2095
2096 def print_summary(self, log_out):
2097 """
2098 Print a summary of the merge operation.
2099
2100 Parameters
2101 ----------
2102 log_out : file
2103 Log file for output
2104 """
2105 log_out.write("\n" + "=" * 70 + "\n")
2106 log_out.write("MergeROOT: Summary\n")
2107 log_out.write("=" * 70 + "\n")
2108 log_out.write("Input files: %d\n" % len(self.inputsinputs))
2109
2110 for i, input_file in enumerate(self.inputsinputs, 1):
2111 log_out.write(" %d. %s\n" % (i, input_file))
2112
2113 log_out.write("\nOutput file: %s\n" % self.outputsoutputs[0])
2114 log_out.write(
2115 "Compression level: %s\n"
2116 % (self.compression if self.compression else "default")
2117 )
2118
2119 # Print total events per tree
2120 if self.output_tree_counts:
2121 log_out.write("\nTotal events in merged file:\n")
2122 for tree_name, count in sorted(self.output_tree_counts.items()):
2123 log_out.write(" %-30s: %d events\n" % (tree_name, count))
2124
2125 log_out.write("=" * 70 + "\n")
2126 log_out.flush()
2127
2129 """
2130 Get the stats JSON filename based on the output ROOT filename.
2131
2132 Returns
2133 -------
2134 str
2135 Path to stats JSON file (e.g., 'merged_X_job1.root' -> 'merged_X_job1_stats.json')
2136 """
2137 if not self.outputsoutputs or len(self.outputsoutputs) == 0:
2138 return None
2139 output_file = self.outputsoutputs[0]
2140 base, _ = os.path.splitext(output_file)
2141 return base + "_stats.json"
2142
2143 def write_stats_json(self, log_out, validation_passed):
2144 """
2145 Write merge statistics to a JSON file.
2146
2147 Parameters
2148 ----------
2149 log_out : file
2150 Log file for output
2151 validation_passed : bool
2152 Whether the validation passed
2153 """
2154 stats_file = self.get_stats_filename()
2155 if stats_file is None:
2156 log_out.write("WARNING: Cannot determine stats filename, skipping stats output\n")
2157 return
2158
2159 log_out.write("\n" + "=" * 70 + "\n")
2160 log_out.write("MergeROOT: Writing stats to %s\n" % stats_file)
2161 log_out.write("=" * 70 + "\n")
2162
2163 # Calculate total input events per tree
2164 total_input_events = {}
2165 for input_file, tree_counts in self.input_tree_counts.items():
2166 for tree_name, count in tree_counts.items():
2167 if tree_name not in total_input_events:
2168 total_input_events[tree_name] = 0
2169 total_input_events[tree_name] += count
2170
2171 # Build input files list with event counts
2172 input_files_list = []
2173 for input_file in self.inputsinputs:
2174 tree_counts = self.input_tree_counts.get(input_file, {})
2175 input_files_list.append({
2176 "path": input_file,
2177 "events": tree_counts
2178 })
2179
2180 # Build stats dictionary
2181 stats = {
2182 "job_id": self.job_id,
2183 "output_file": self.outputsoutputs[0] if self.outputsoutputs else None,
2184 "output_events": self.output_tree_counts,
2185 "input_files": input_files_list,
2186 "total_input_events": total_input_events,
2187 "validation_passed": validation_passed,
2188 "num_input_files": len(self.inputsinputs)
2189 }
2190
2191 # Write JSON file
2192 with open(stats_file, 'w') as f:
2193 json.dump(stats, f, indent=2)
2194
2195 log_out.write("Stats written successfully\n")
2196 log_out.write("=" * 70 + "\n")
2197 log_out.flush()
2198
2199 def execute(self, log_out, log_err):
2200 """
2201 Execute MergeROOT component using hadd.
2202
2203 Parameters
2204 ----------
2205 log_out : file
2206 Log file for stdout
2207 log_err : file
2208 Log file for stderr
2209
2210 Returns
2211 -------
2212 int
2213 Return code from hadd command
2214 """
2215 # Debug: Entry point
2216 log_out.write("\n" + "=" * 70 + "\n")
2217 log_out.write("MergeROOT: DEBUG - Entering execute()\n")
2218 log_out.write("=" * 70 + "\n")
2219 log_out.write("DEBUG: self.command = %s\n" % self.commandcommand)
2220 log_out.write("DEBUG: self.inputs = %s\n" % self.inputsinputs)
2221 log_out.write("DEBUG: self.outputs = %s\n" % self.outputsoutputs)
2222 log_out.write("DEBUG: self.force = %s\n" % self.force)
2223 log_out.write("DEBUG: self.compression = %s\n" % self.compression)
2224 log_out.write("DEBUG: self.validate = %s\n" % self.validate)
2225 log_out.flush()
2226
2227 # Check that hadd command exists
2228 log_out.write("\nDEBUG: Checking if hadd command exists...\n")
2229 log_out.flush()
2230 if not self.cmd_exists():
2231 raise RuntimeError("MergeROOT: hadd command not found in PATH")
2232 log_out.write("DEBUG: hadd command found\n")
2233 log_out.flush()
2234
2235 # Check that input files exist
2236 log_out.write("\nDEBUG: Checking input files exist...\n")
2237 log_out.flush()
2238 for input_file in self.inputsinputs:
2239 log_out.write("DEBUG: Checking: %s\n" % input_file)
2240 log_out.flush()
2241 if not os.path.exists(input_file):
2242 raise RuntimeError("MergeROOT: Input file not found: %s" % input_file)
2243 log_out.write("DEBUG: -> exists (size: %d bytes)\n" % os.path.getsize(input_file))
2244 log_out.flush()
2245
2246 # Scan input files before merge if validation is enabled
2247 log_out.write("\nDEBUG: Validation enabled = %s\n" % self.validate)
2248 log_out.flush()
2249 if self.validate:
2250 try:
2251 log_out.write("DEBUG: Starting input file scan...\n")
2252 log_out.flush()
2253 self.scan_input_files(log_out)
2254 log_out.write("DEBUG: Input file scan complete\n")
2255 log_out.flush()
2256 except Exception as e:
2257 log_out.write("\nWARNING: Could not scan input files: %s\n" % str(e))
2258 log_out.write("Proceeding with merge without validation.\n")
2259 self.validate = False
2260
2261 # Build full command
2262 log_out.write("\nDEBUG: Building command arguments...\n")
2263 log_out.flush()
2264 cmd = [self.commandcommand] + self.cmd_argscmd_args()
2265 log_out.write("DEBUG: cmd_args() returned: %s\n" % self.cmd_argscmd_args())
2266 log_out.flush()
2267
2268 # Log the command
2269 log_out.write("\n" + "=" * 70 + "\n")
2270 log_out.write("MergeROOT: Executing hadd\n")
2271 log_out.write("=" * 70 + "\n")
2272 log_out.write("Command: %s\n" % " ".join(cmd))
2273 log_out.write("=" * 70 + "\n\n")
2274 log_out.flush()
2275
2276 # Execute hadd
2277 log_out.write("DEBUG: About to call subprocess.Popen...\n")
2278 log_out.flush()
2279 proc = subprocess.Popen(cmd, stdout=log_out, stderr=log_err)
2280 log_out.write("DEBUG: Popen returned, PID = %s\n" % proc.pid)
2281 log_out.flush()
2282 log_out.write("DEBUG: Waiting for process to complete...\n")
2283 log_out.flush()
2284 proc.wait()
2285 log_out.write("DEBUG: Process completed, returncode = %d\n" % proc.returncode)
2286 log_out.flush()
2287
2288 # Check return code
2289 if proc.returncode != 0:
2290 log_out.write("DEBUG: hadd FAILED with return code %d\n" % proc.returncode)
2291 log_out.flush()
2292 raise RuntimeError(
2293 "MergeROOT: hadd failed with return code %d" % proc.returncode
2294 )
2295
2296 # Verify output file was created
2297 log_out.write("DEBUG: Checking if output file exists: %s\n" % self.outputsoutputs[0])
2298 log_out.flush()
2299 if not os.path.exists(self.outputsoutputs[0]):
2300 raise RuntimeError(
2301 "MergeROOT: Output file was not created: %s" % self.outputsoutputs[0]
2302 )
2303 log_out.write("DEBUG: Output file exists, size = %d bytes\n" % os.path.getsize(self.outputsoutputs[0]))
2304 log_out.flush()
2305
2306 log_out.write("\n✓ hadd completed successfully\n")
2307 log_out.flush()
2308
2309 # Scan output file and validate if enabled
2310 log_out.write("\nDEBUG: Post-merge validation check, self.validate = %s\n" % self.validate)
2311 log_out.flush()
2312 validation_passed = True
2313 if self.validate:
2314 try:
2315 log_out.write("DEBUG: Starting output file scan...\n")
2316 log_out.flush()
2317 self.scan_output_file(log_out)
2318 log_out.write("DEBUG: Output file scan complete\n")
2319 log_out.flush()
2320 log_out.write("DEBUG: Starting merge validation...\n")
2321 log_out.flush()
2322 validation_passed = self.validate_merge(log_out)
2323 self._validation_passed = validation_passed
2324 log_out.write("DEBUG: Merge validation complete, passed = %s\n" % validation_passed)
2325 log_out.flush()
2326
2327 if not validation_passed:
2328 raise RuntimeError("MergeROOT: Event count validation failed!")
2329
2330 except Exception as e:
2331 log_out.write("\nERROR during validation: %s\n" % str(e))
2332 log_out.flush()
2333 raise
2334
2335 # Write stats JSON if enabled
2336 log_out.write("\nDEBUG: write_stats = %s\n" % self.write_stats)
2337 log_out.flush()
2338 if self.write_stats:
2339 try:
2340 self.write_stats_json(log_out, validation_passed)
2341 except Exception as e:
2342 log_out.write("\nWARNING: Could not write stats JSON: %s\n" % str(e))
2343 log_out.flush()
2344
2345 # Print summary
2346 log_out.write("\nDEBUG: Printing summary...\n")
2347 log_out.flush()
2348 self.print_summary(log_out)
2349
2350 log_out.write("\nDEBUG: MergeROOT.execute() returning %d\n" % proc.returncode)
2351 log_out.flush()
2352 return proc.returncode
2353
2354 def output_files(self):
2355 """
2356 Return list of output files.
2357
2358 Returns
2359 -------
2360 list
2361 List containing the merged output ROOT file and optionally the stats JSON
2362 """
2363 files = list(self.outputsoutputs) if self.outputsoutputs else []
2364 if self.write_stats:
2365 stats_file = self.get_stats_filename()
2366 if stats_file and stats_file not in files:
2367 files.append(stats_file)
2368 return files
2369
2371 """
2372 Return list of required configuration parameters.
2373
2374 Returns
2375 -------
2376 list
2377 List of required config parameters (empty for MergeROOT)
2378 """
2379 return []
Base class for components in a job.
Definition component.py:15
output_files(self)
Return a list of output files created by this component.
Definition component.py:233
config_from_environ(self)
Configure component from environment variables which are just upper case versions of the required con...
Definition component.py:258
cmd_exists(self)
Check if the component's assigned command exists.
Definition component.py:96
cmd_args(self)
Return the command arguments of this component.
Definition component.py:108
input_files(self)
Get a list of input files for this component.
Definition component.py:229
Add full truth mother particles for physics samples.
Definition tools.py:959
__init__(self, **kwargs)
Definition tools.py:962
cmd_args(self)
Setup command arguments.
Definition tools.py:979
Add mother particles for physics samples.
Definition tools.py:950
__init__(self, **kwargs)
Definition tools.py:955
Transform StdHep events into beam coordinates.
Definition tools.py:715
beam_rot_x
beam rotation in x?
Definition tools.py:736
__init__(self, **kwargs)
Definition tools.py:723
optional_parameters(self)
Return list of optional parameters.
Definition tools.py:772
beam_sigma_y
beam sigma in y
Definition tools.py:728
target_x
target x position
Definition tools.py:730
target_y
target y position
Definition tools.py:732
beam_rot_z
beam rotation in z?
Definition tools.py:740
beam_rot_y
beam rotation in y?
Definition tools.py:738
cmd_args(self)
Setup command arguments.
Definition tools.py:744
target_z
target z position
Definition tools.py:734
Convert LHE files to StdHep, displacing the time by given ctau.
Definition tools.py:882
__init__(self, **kwargs)
Definition tools.py:889
optional_parameters(self)
Return list of optional parameters.
Definition tools.py:906
cmd_args(self)
Setup command arguments.
Definition tools.py:896
Convert LHE files to StdHep, displacing the time by given ctau.
Definition tools.py:916
__init__(self, **kwargs)
Definition tools.py:923
optional_parameters(self)
Return list of optional parameters.
Definition tools.py:940
cmd_args(self)
Setup command arguments.
Definition tools.py:930
Convert EVIO events to LCIO using the hps-java EvioToLcio command line tool.
Definition tools.py:1168
run_number
run number
Definition tools.py:1182
optional_parameters(self)
Return list of optional parameters.
Definition tools.py:1207
detector
detector name
Definition tools.py:1180
required_parameters(self)
Return list of required parameters.
Definition tools.py:1198
setup(self)
Setup EvioToLcio component.
Definition tools.py:1216
__init__(self, steering=None, **kwargs)
Definition tools.py:1178
steering
steering file
Definition tools.py:1188
skip_events
number of events that are skipped
Definition tools.py:1184
event_print_interval
event print interval
Definition tools.py:1186
cmd_args(self)
Setup command arguments.
Definition tools.py:1225
Apply hodo-hit filter and space MC events to process before readout.
Definition tools.py:1396
optional_parameters(self)
Return list of optional parameters.
Definition tools.py:1446
cmd_args(self)
Setup command arguments.
Definition tools.py:1427
Space MC events and apply energy filters to process before readout.
Definition tools.py:1276
filter_event_interval
Default event filtering interval.
Definition tools.py:1305
__init__(self, **kwargs)
Definition tools.py:1285
filter_ecal_hit_ecut
No default ecal hit cut energy (negative val to be ignored)
Definition tools.py:1298
optional_parameters(self)
Return list of optional parameters.
Definition tools.py:1370
filter_nevents_read
Default is no maximum nevents to read.
Definition tools.py:1311
filter_no_cuts
By default cuts are on.
Definition tools.py:1287
config(self, parser)
Configure FilterBunches component.
Definition tools.py:1332
required_config(self)
Return list of required config.
Definition tools.py:1386
filter_nevents_write
Default is no maximum nevents to write.
Definition tools.py:1317
cmd_args(self)
Setup command arguments.
Definition tools.py:1344
Run the hpstr analysis tool.
Definition tools.py:526
execute(self, log_out, log_err)
Execute HPSTR component.
Definition tools.py:648
output_files(self)
Adjust names of output files.
Definition tools.py:638
optional_parameters(self)
Return list of optional parameters.
Definition tools.py:598
required_parameters(self)
Return list of required parameters.
Definition tools.py:589
setup(self)
Setup HPSTR component.
Definition tools.py:550
__init__(self, cfg=None, is_data=0, year=None, tracking=None, **kwargs)
Definition tools.py:535
cfg
configuration
Definition tools.py:537
required_config(self)
Return list of required configs.
Definition tools.py:607
tracking
tracking option (KF, GBL, BOTH)
Definition tools.py:543
is_data
run mode
Definition tools.py:539
cmd_args(self)
Setup command arguments.
Definition tools.py:616
Generic base class for Java based tools.
Definition tools.py:1122
java_class
java class
Definition tools.py:1129
config(self, parser)
Automatic configuration.
Definition tools.py:1164
required_config(self)
Return list of required config.
Definition tools.py:1136
cmd_args(self)
Setup command arguments.
Definition tools.py:1145
java_args
java arguments
Definition tools.py:1131
__init__(self, name, java_class, **kwargs)
Definition tools.py:1127
Run the hps-java JobManager class.
Definition tools.py:225
optional_parameters(self)
Return list of optional parameters.
Definition tools.py:430
detector
detector name
Definition tools.py:242
required_parameters(self)
Return list of required parameters.
Definition tools.py:421
setup(self)
Setup JobManager component.
Definition tools.py:320
__init__(self, steering=None, **kwargs)
Definition tools.py:235
lcsim_cache_dir
lcsim cache directory
Definition tools.py:252
steering
steering file
Definition tools.py:260
config(self, parser)
Configure JobManager component.
Definition tools.py:286
hps_java_bin_jar
location of hps-java installation?
Definition tools.py:262
logging_config_file
file for config logging
Definition tools.py:250
required_config(self)
Return list of required configurations.
Definition tools.py:311
event_print_interval
event print interval
Definition tools.py:244
cmd_args(self)
Setup command arguments.
Definition tools.py:331
java_args
java arguments
Definition tools.py:248
conditions_password
no idea
Definition tools.py:256
Concatenate LCIO files together.
Definition tools.py:1689
__init__(self, **kwargs)
Definition tools.py:1694
cmd_args(self)
Setup command arguments.
Definition tools.py:1697
Count events in LCIO files.
Definition tools.py:1713
__init__(self, **kwargs)
Definition tools.py:1721
optional_parameters(self)
Return list of optional parameters.
Definition tools.py:1744
required_parameters(self)
Return list of required parameters.
Definition tools.py:1735
cmd_args(self)
Setup command arguments.
Definition tools.py:1724
Dump LCIO event information.
Definition tools.py:1483
__init__(self, **kwargs)
Definition tools.py:1491
lcio_dir
lcio directory
Definition tools.py:1493
required_parameters(self)
Return list of required parameters.
Definition tools.py:1532
setup(self)
Setup LCIODumpEvent component.
Definition tools.py:1507
config(self, parser)
Configure LCIODumpEvent component.
Definition tools.py:1501
required_config(self)
Return list of required config.
Definition tools.py:1523
cmd_args(self)
Setup command arguments.
Definition tools.py:1511
Merge LCIO files.
Definition tools.py:1754
__init__(self, **kwargs)
Definition tools.py:1759
cmd_args(self)
Setup command arguments.
Definition tools.py:1762
Generic component for LCIO tools.
Definition tools.py:1642
lcio_bin_jar
lcio bin jar (whatever this is)
Definition tools.py:1652
required_parameters(self)
Return list of required parameters.
Definition tools.py:1679
config(self, parser)
Configure LCIOTool component.
Definition tools.py:1655
required_config(self)
Return list of required config.
Definition tools.py:1670
cmd_args(self)
Setup command arguments.
Definition tools.py:1661
__init__(self, name=None, **kwargs)
Definition tools.py:1650
Count events in an LHE file.
Definition tools.py:1542
execute(self, log_out, log_err)
Execute LHECount component.
Definition tools.py:1563
__init__(self, minevents=0, fail_on_underflow=False, **kwargs)
Definition tools.py:1547
setup(self)
Setup LHECount component.
Definition tools.py:1551
cmd_exists(self)
Check if command exists.
Definition tools.py:1556
Merge StdHep files.
Definition tools.py:1061
__init__(self, **kwargs)
Definition tools.py:1069
optional_parameters(self)
Return list of optional parameters.
Definition tools.py:1072
required_parameters(self)
Return list of required parameters.
Definition tools.py:1081
Merge StdHep files, applying poisson sampling.
Definition tools.py:987
execute(self, log_out, log_err)
Execute MergePoisson component.
Definition tools.py:1048
target_thickness
target thickness in cm
Definition tools.py:998
__init__(self, xsec=0, **kwargs)
Definition tools.py:994
required_parameters(self)
Return list of required parameters.
Definition tools.py:1012
setup(self)
Setup MergePoisson component.
Definition tools.py:1004
xsec
cross section in pb
Definition tools.py:996
num_electrons
number of electrons per bunch
Definition tools.py:1000
cmd_args(self)
Setup command arguments.
Definition tools.py:1021
execute(self, log_out, log_err)
Definition tools.py:2199
__init__(self, **kwargs)
Definition tools.py:1794
scan_output_file(self, log_out)
Definition tools.py:1989
scan_root_file(self, filename, log_out=None)
Definition tools.py:1894
scan_input_files(self, log_out)
Definition tools.py:1958
print_summary(self, log_out)
Definition tools.py:2096
validate_merge(self, log_out)
Definition tools.py:2016
write_stats_json(self, log_out, validation_passed)
Definition tools.py:2143
Move input files to new locations.
Definition tools.py:1615
execute(self, log_out, log_err)
Execute TarFiles component.
Definition tools.py:1630
__init__(self, **kwargs)
Definition tools.py:1620
cmd_exists(self)
Check if command exists.
Definition tools.py:1623
Convert LHE files to StdHep.
Definition tools.py:860
__init__(self, **kwargs)
Definition tools.py:865
cmd_args(self)
Setup command arguments.
Definition tools.py:873
Run the make_mini_dst command on the input file.
Definition tools.py:440
output_files(self)
Adjust names of output files.
Definition tools.py:497
__init__(self, **kwargs)
Initialize ProcessMiniDst with default input file and the command to run.
Definition tools.py:448
optional_parameters(self)
Return list of optional parameters.
Definition tools.py:479
required_parameters(self)
Return list of required parameters.
Definition tools.py:470
setup(self)
Setup the MiniDST component.
Definition tools.py:461
required_config(self)
Return list of required configs.
Definition tools.py:488
cmd_args(self)
Setup command arguments for make_mini_dst.
Definition tools.py:506
Randomly sample StdHep events into a new file.
Definition tools.py:792
execute(self, log_out, log_err)
Execute RandomSample component.
Definition tools.py:847
__init__(self, **kwargs)
Definition tools.py:799
mu
median of distribution?
Definition tools.py:802
optional_parameters(self)
Return list of optional parameters.
Definition tools.py:838
cmd_args(self)
Setup command arguments.
Definition tools.py:804
Run the SLIC Geant4 simulation.
Definition tools.py:16
execute(self, log_out, log_err)
Execute SLIC component.
Definition tools.py:146
__init__(self, **kwargs)
Definition tools.py:25
run_number
Run number to set on output file (optional)
Definition tools.py:29
optional_parameters(self)
Return list of optional parameters.
Definition tools.py:119
required_parameters(self)
Return list of required parameters.
Definition tools.py:128
__particle_tbl(self)
Return path to particle table.
Definition tools.py:88
setup(self)
Setup SLIC component.
Definition tools.py:104
disable_particle_table
Optionally disable loading of the particle table shipped with slic Note: This should not be used with...
Definition tools.py:35
__detector_file(self)
Return path to detector file.
Definition tools.py:84
macros
List of macros to run (optional)
Definition tools.py:27
config(self, parser)
Configure SLIC component.
Definition tools.py:92
required_config(self)
Return list of required configurations.
Definition tools.py:137
detector_dir
To be set from config or install dir.
Definition tools.py:31
cmd_args(self)
Setup command arguments.
Definition tools.py:41
Copy the SQLite database file to the desired location.
Definition tools.py:169
execute(self, log_out, log_err)
Execute the file copy operation.
Definition tools.py:199
__init__(self, **kwargs)
Initialize SQLiteProc to copy the SQLite file.
Definition tools.py:174
cmd_args(self)
Return dummy command arguments to satisfy the parent class.
Definition tools.py:188
Count number of events in a StdHep file.
Definition tools.py:1091
execute(self, log_out, log_err)
Execute StdHepCount component.
Definition tools.py:1109
__init__(self, **kwargs)
Definition tools.py:1096
cmd_args(self)
Setup command arguments.
Definition tools.py:1101
Generic class for StdHep tools.
Definition tools.py:668
cmd_args(self)
Setup command arguments.
Definition tools.py:689
__init__(self, name=None, **kwargs)
Definition tools.py:685
Tar files into an archive.
Definition tools.py:1588
execute(self, log_out, log_err)
Execute TarFiles component.
Definition tools.py:1603
__init__(self, **kwargs)
Definition tools.py:1593
cmd_exists(self)
Check if command exists.
Definition tools.py:1596
Unzip the input files to outputs.
Definition tools.py:1456
execute(self, log_out, log_err)
Execute Unzip component.
Definition tools.py:1470
output_files(self)
Return list of output files.
Definition tools.py:1464
__init__(self, **kwargs)
Definition tools.py:1461
Miscellaneous math functions.
Definition func.py:1