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