File indexing completed on 2025-05-13 08:03:04
0001
0002 """
0003 run_all_energies.py - EIC Simulation Runner
0004
0005 This script runs particle physics simulations for the Electron-Ion Collider (EIC)
0006 using npsim and eicrecon across multiple beam energy configurations and
0007 Q² values, generating simulation data files.
0008
0009 Features:
0010 - Sets up the environment with appropriate library paths
0011 - Runs npsim simulations with specified parameters
0012 - Processes output files with eicrecon for reconstruction
0013 - Supports customization of simulation parameters via command-line arguments
0014 - Runs with sensible defaults when no arguments are provided
0015
0016 Usage:
0017 ./run_all_energies.py # Run with default settings
0018 ./run_all_energies.py [options] # Run with custom options
0019
0020 Default behavior:
0021 Without any arguments, the script will:
0022 - Use /mnt/dd4hep-plugin/firebird_steering.py as the steering file
0023 - Run simulations for beam energies: 5x41, 10x100, 18x275
0024 - Use minimum Q² values: 1, 100, 1000
0025 - Process 5 events per configuration
0026 - Use the detector path from DETECTOR_PATH environment variable
0027 or /opt/detector/epic-main/share/epic/epic_full.xml as fallback
0028 - Use /var/project/prefix/lib for plugin libraries
0029 - Save output files to the current directory
0030
0031 Examples:
0032 ./run_all_energies.py # Run with defaults
0033 ./run_all_energies.py --events 10 # Run with 10 events per configuration
0034 ./run_all_energies.py -o /path/to/outputs # Save outputs to specific directory
0035 ./run_all_energies.py --plugin-path /custom/lib/path # Use custom plugin path
0036 ./run_all_energies.py --steering /path/to/steering.py --beams 5x41 10x100
0037 ./run_all_energies.py --minq2s 1 100 # Run with only Q² values 1 and 100
0038 """
0039
0040 import subprocess
0041 import os
0042 import argparse
0043
0044
0045 def setup_environment(plugin_path='prefix/lib'):
0046 """
0047 Updates the LD_LIBRARY_PATH environment variable to include custom library paths.
0048
0049 Parameters:
0050 plugin_path (str): The library path to prepend to LD_LIBRARY_PATH
0051 """
0052
0053 current_ld_library_path = os.environ.get('LD_LIBRARY_PATH', '')
0054
0055
0056 new_ld_library_path = (plugin_path + ':' + current_ld_library_path) if current_ld_library_path else plugin_path
0057
0058
0059 os.environ['LD_LIBRARY_PATH'] = new_ld_library_path
0060 print("Updated LD_LIBRARY_PATH:", os.environ['LD_LIBRARY_PATH'])
0061
0062
0063 def run_command(command):
0064 """
0065 Executes a given command in the shell and prints the output as it appears.
0066
0067 Parameters:
0068 command (list): A list containing the command and its arguments.
0069
0070 Returns:
0071 int: The return code of the executed command.
0072 """
0073 print("Executing:", " ".join(command))
0074 process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
0075
0076
0077 while True:
0078 output = process.stdout.readline()
0079 if output == '' and process.poll() is not None:
0080 break
0081 if output:
0082 print(output.strip())
0083
0084
0085 err = process.stderr.read()
0086 if err:
0087 print("Error:", err)
0088
0089
0090 return_code = process.wait()
0091 print("Command completed with return code:", return_code)
0092 print("\n" + "-"*50 + "\n")
0093 return return_code
0094
0095
0096 def get_hepmc_path(beam, minq2):
0097 """
0098 Generates the path to the HepMC input file based on beam and minQ2 parameters.
0099
0100 Parameters:
0101 beam (str): The energy configuration for the beam.
0102 minq2 (int): The minimum Q2 value.
0103
0104 Returns:
0105 str: The path to the HepMC file.
0106 """
0107 return f"root://dtn-eic.jlab.org//volatile/eic/EPIC/EVGEN/DIS/NC/{beam}/minQ2={minq2}/pythia8NCDIS_{beam}_minQ2={minq2}_beamEffects_xAngle=-0.025_hiDiv_1.hepmc3.tree.root"
0108
0109
0110 def get_base_name(beam, minq2, event_num, output_dir=None):
0111 """
0112 Generates the base filename for output files.
0113
0114 Parameters:
0115 beam (str): The energy configuration for the beam.
0116 minq2 (int): The minimum Q2 value.
0117 event_num (int): The number of events to simulate.
0118 output_dir (str, optional): Directory for output files. If provided,
0119 the path will be prepended to the filename.
0120
0121 Returns:
0122 str: The base filename (with path) for output files.
0123 """
0124 filename = f"py8_dis-cc_{beam}_minq2-{minq2}_minp-150mev_vtxcut-5m_nevt-{event_num}"
0125
0126 if output_dir:
0127
0128 os.makedirs(output_dir, exist_ok=True)
0129 return os.path.join(output_dir, filename)
0130
0131 return filename
0132
0133
0134 def run_simulation(beam, minq2, event_num, detector_path, steering_file, output_dir=None):
0135 """
0136 Runs the simulation for a given beam, Q2 value, and event number, then converts the output file.
0137
0138 Parameters:
0139 beam (str): The energy configuration for the beam.
0140 minq2 (int): The minimum Q2 value.
0141 event_num (int): The number of events to simulate.
0142 detector_path (str): Path to the detector configuration XML file.
0143 steering_file (str): Path to the steering file for npsim.
0144 output_dir (str, optional): Directory to store output files.
0145
0146 Returns:
0147 bool: True if the simulation completed successfully, False otherwise.
0148 """
0149
0150 url = get_hepmc_path(beam, minq2)
0151
0152
0153 output_base = get_base_name(beam, minq2, event_num, output_dir)
0154 output_edm4hep = output_base + ".edm4hep.root"
0155 output_evttxt = output_base + ".evt.txt"
0156 event_prefix = f"CC_{beam}_minq2_{minq2}"
0157
0158
0159 npsim_command = [
0160 "npsim",
0161 "--compactFile", detector_path,
0162 "-N", str(event_num),
0163 "--inputFiles", url,
0164 "--random.seed", "1",
0165 "--outputFile", output_edm4hep,
0166 "--steeringFile", steering_file
0167 ]
0168
0169
0170 if run_command(npsim_command) != 0:
0171 print(f"Error running npsim for beam={beam}, minq2={minq2}")
0172 return False
0173
0174
0175 reconstruction_command = [
0176 "eicrecon",
0177 f"-Pjana:debug_plugin_loading=1",
0178 f"-Pjana:nevents={event_num}",
0179 f"-Pjana:timeout=0",
0180 f"-Ppodio:output_file={output_base}.edm4eic.root",
0181 f"-Pdd4hep:xml_files={detector_path}",
0182 f"{output_base}.edm4hep.root"
0183 ]
0184
0185
0186 if run_command(reconstruction_command) != 0:
0187 print(f"Error running eicrecon for beam={beam}, minq2={minq2}")
0188 return False
0189
0190 return True
0191
0192
0193 def main():
0194 """
0195 Main function that parses command-line arguments and runs the simulation.
0196 """
0197
0198 default_detector_base = os.getenv('DETECTOR_PATH', '/opt/detector/epic-main/share/epic/')
0199 default_detector_path = os.path.join(default_detector_base, "epic_full.xml")
0200 default_steering_path = "firebird_steering.py"
0201 default_plugin_path = "prefix/lib"
0202 default_output_path = "tmp"
0203
0204 parser = argparse.ArgumentParser(
0205 description="EIC Simulation Runner",
0206 formatter_class=argparse.ArgumentDefaultsHelpFormatter
0207 )
0208
0209
0210 parser.add_argument(
0211 "--steering",
0212 default=default_steering_path,
0213 help="Path to the steering file for npsim"
0214 )
0215 parser.add_argument(
0216 "--detector",
0217 default=default_detector_path,
0218 help="Path to the detector configuration XML file"
0219 )
0220 parser.add_argument(
0221 "--events",
0222 type=int,
0223 default=5,
0224 help="Number of events to simulate per configuration"
0225 )
0226 parser.add_argument(
0227 "--beams",
0228 nargs="+",
0229 default=['5x41', '10x100', '18x275'],
0230 help="Beam energy configurations to simulate"
0231 )
0232 parser.add_argument(
0233 "--minq2s",
0234 nargs="+",
0235 type=int,
0236 default=[1, 100, 1000],
0237 help="Minimum Q2 values to simulate"
0238 )
0239 parser.add_argument(
0240 "--skip-setup",
0241 action="store_true",
0242 help="Skip environment setup step"
0243 )
0244 parser.add_argument(
0245 "--plugin-path",
0246 default=default_plugin_path,
0247 help="Path to plugin libraries to be added to LD_LIBRARY_PATH"
0248 )
0249 parser.add_argument(
0250 "-o", "--output",
0251 dest="output_dir",
0252 default=default_output_path,
0253 help="Directory for output files"
0254 )
0255
0256 args = parser.parse_args()
0257
0258
0259 if not args.skip_setup:
0260 setup_environment(args.plugin_path)
0261
0262
0263 detector_path = args.detector
0264 if not os.path.exists(detector_path):
0265 print(f"Error: Detector file not found: {detector_path}")
0266 return 1
0267
0268
0269 steering_file = args.steering
0270 if not os.path.exists(steering_file):
0271 print(f"Error: Steering file not found: {steering_file}")
0272 return 1
0273
0274
0275 if args.output_dir:
0276 os.makedirs(args.output_dir, exist_ok=True)
0277 print(f"Output files will be saved to: {args.output_dir}")
0278
0279
0280 for beam in args.beams:
0281 for minq2 in args.minq2s:
0282 print("\n" + "-" * 100)
0283 print(f"Running simulation for beam={beam}, minq2={minq2}, events={args.events}")
0284 success = run_simulation(
0285 beam=beam,
0286 minq2=minq2,
0287 event_num=args.events,
0288 detector_path=detector_path,
0289 steering_file=steering_file,
0290 output_dir=args.output_dir
0291 )
0292 print("\n" + "/" * 100)
0293 if not success:
0294 print(f"Simulation failed for beam={beam}, minq2={minq2}")
0295
0296 return 0
0297
0298
0299 if __name__ == "__main__":
0300 exit(main())