File indexing completed on 2025-07-01 07:56:15
0001
0002
0003
0004
0005 require 'optparse'
0006 require 'ostruct'
0007 require 'fileutils'
0008
0009
0010
0011 EtaTestValues = {
0012 :ideal => 2.0,
0013 :min => 1.6,
0014 :max => 3.5,
0015 }
0016 IdealEnergy = 12.0
0017 IdealParticle = 'pi+'
0018
0019
0020
0021
0022
0023
0024 opt = OpenStruct.new
0025 opt.sim_mode = ''
0026 opt.sim_file = 'out/sim.edm4hep.root'
0027 opt.rec_file = 'out/rec.edm4hep.root'
0028 opt.ana_file = 'out/ana.edm4hep.root'
0029 opt.run_sim = true
0030 opt.run_rec = true
0031 opt.run_ana = true
0032 opt.run_rec_down = false
0033 opt.run_ana_down = false
0034 opt.num_events = 10
0035 opt.benchmark_exe = 'benchmark_rich_reconstruction'
0036 opt.algos = Array.new
0037 opt.plots = Array.new
0038 opt.verbosity = 0
0039 opt.dry_run = false
0040 opt.using_ci = false
0041
0042
0043 avail_sim_modes = [
0044 'fixedEtaIdeal',
0045 'fixedEtaMin',
0046 'fixedEtaMax',
0047 ]
0048
0049
0050 required_set = false
0051 OptionParser.new do |o|
0052 o.banner = "USAGE: #{$0} [OPTIONS]..."
0053 o.separator ''
0054 o.separator 'required options, one of either:'.upcase
0055 o.separator ''
0056 o.on("-r", "--rec-only", "Run only the reconstruction, then the analysis benchmark") do |a|
0057 opt.run_rec_down = true
0058 required_set = true
0059 end
0060 o.separator ''
0061 o.on("-b", "--ana-only", "Run only the analysis benchmark") do |a|
0062 opt.run_ana_down = true
0063 required_set = true
0064 end
0065 o.separator ''
0066 o.on("-s", "--sim-mode [SIMULATION_MODE]", "Run the simulation, reconstruction, and analysis",
0067 "[SIMULATION_MODE] must be one of:") do |a|
0068 unless avail_sim_modes.include? a
0069 $stderr.puts "ERROR: unknown simulation mode '#{a}'"
0070 exit 1
0071 end
0072 opt.sim_mode = a
0073 required_set = true
0074 end
0075 avail_sim_modes.each{ |it| o.separator ' '*40+it }
0076 o.separator ''
0077 o.separator 'optional options:'.upcase
0078 o.separator ''
0079 o.on("--sim-file [FILE]", "simulation file name", "default = #{opt.sim_file}") { |a| opt.sim_file=a }
0080 o.on("--rec-file [FILE]", "reconstruction file name", "default = #{opt.rec_file}") { |a| opt.rec_file=a }
0081 o.on("--ana-file [FILE]", "analysis file name", "default = #{opt.ana_file}") { |a| opt.ana_file=a }
0082 o.separator ''
0083 o.on("--[no-]run-sim", "simulation on/off", "default = #{opt.run_sim ? 'on' : 'off'}") { |a| opt.run_sim=a }
0084 o.on("--[no-]run-rec", "reconstruction on/off", "default = #{opt.run_rec ? 'on' : 'off'}") { |a| opt.run_rec=a }
0085 o.on("--[no-]run-ana", "analysis benchmark on/off", "default = #{opt.run_ana ? 'on' : 'off'}") { |a| opt.run_ana=a }
0086 o.separator ''
0087 o.on("-n", "--num-events [NUM_EVENTS]", Integer, "Number of events", "default = #{opt.num_events}") { |a| opt.num_events=a }
0088 o.on("-a", "--algos [ALGORITHMS]...", Array, "List of analysis algorithms to run; default = all",
0089 "delimit by commas, no spaces",
0090 "for more info, run: #{opt.benchmark_exe}") { |a| opt.algos=a }
0091 o.on("-p", "--plots [PLOTS]...", Array, "List of plots to draw",
0092 "delimit by commas, no spaces",
0093 "default = all") { |a| opt.plots=a }
0094 o.on("-x", "--benchmark-exe [EXECUTABLE]", "benchmark executable",
0095 "default = #{opt.benchmark_exe} (from $PATH)") { |a| opt.benchmark_exe=a}
0096 o.separator ''
0097 o.on("-v", "--verbose", "Increase verbosity (-vv for more verbose)") { |a| opt.verbosity+=1 }
0098 o.on("-d", "--dry-run", "Dry run (just print commands)") { |a| opt.dry_run=true }
0099 o.on("--ci", "output plots to ./results, for CI artifact collection") { |a| opt.using_ci=true }
0100 o.separator ''
0101 o.on_tail("-h", "--help", "Show this message") do
0102 puts o
0103 exit 2
0104 end
0105 end.parse!(ARGV.length>0 ? ARGV : ['--help'])
0106
0107
0108 puts 'settings: {'.upcase
0109 opt.each_pair { |k,v| puts "#{k.to_s.rjust(20)} => #{v}" }
0110 puts '}'
0111
0112
0113 unless required_set
0114 $stderr.puts "ERROR: required options have not been set"
0115 $stderr.puts "run '#{$0} --help' for guidance"
0116 exit 1
0117 end
0118
0119
0120 run_step = { :sim=>false, :rec=>false, :ana=>false, }
0121 if opt.run_ana_down
0122 run_step[:ana] = true
0123 elsif opt.run_rec_down
0124 run_step[:rec] = true
0125 run_step[:ana] = true
0126 else
0127 run_step[:sim] = opt.run_sim
0128 run_step[:rec] = opt.run_rec
0129 run_step[:ana] = opt.run_ana
0130 end
0131 puts "steps to run: #{run_step}"
0132
0133
0134 if ENV['DETECTOR_PATH'].nil? or ENV['DETECTOR_CONFIG'].nil?
0135 $stderr.puts "ERROR: unknown DETECTOR_PATH or DETECTOR_CONFIG"
0136 exit 1
0137 end
0138 compact_file = "#{ENV['DETECTOR_PATH']}/#{ENV['DETECTOR_CONFIG']}.xml"
0139
0140
0141
0142
0143 def theta2xyz(theta)
0144 [ Math.sin(theta), 0.0, Math.cos(theta) ]
0145 end
0146
0147 def eta2theta(eta)
0148 2 * Math.atan(Math.exp(-eta))
0149 end
0150
0151
0152
0153 simulate_fixed_angle = Proc.new do |theta, energy, particle|
0154 [
0155 "npsim",
0156 "--runType batch",
0157 "--compactFile #{compact_file}",
0158 "--outputFile #{opt.sim_file}",
0159 "--part.userParticleHandler=''",
0160 "--enableGun",
0161 "--numberOfEvents #{opt.num_events}",
0162 "--gun.particle #{particle}",
0163 "--gun.energy #{energy}*GeV",
0164 "--gun.direction '(#{theta2xyz(theta).join ", "})'",
0165 ]
0166 end
0167
0168
0169
0170
0171 sim_cmd = Array.new
0172 case opt.sim_mode
0173 when /^fixedEta/
0174 key = opt.sim_mode.sub('fixedEta','').downcase.to_sym
0175 fixed_eta = EtaTestValues[key]
0176 if fixed_eta.nil?
0177 $stderr.puts "ERROR: EtaTestValues[#{key}] is not defined"
0178 exit 1
0179 end
0180 fixed_theta = eta2theta fixed_eta
0181 puts """Simulating fixed-eta events:
0182 - eta = #{fixed_eta}
0183 - theta = #{180.0 * fixed_theta / Math::PI} degrees
0184 - energy = #{IdealEnergy} GeV
0185 - particle = #{IdealParticle}
0186 """
0187 sim_cmd = simulate_fixed_angle.call fixed_theta, IdealEnergy, IdealParticle
0188 else
0189 exit 1 if run_step[:sim]
0190 end
0191
0192
0193
0194
0195 output_collections = [
0196 "DRICHHits",
0197 "MCParticles",
0198 "DRICHRawHits",
0199 "DRICHRawHitsAssociations",
0200 "DRICHAerogelTracks",
0201 "DRICHGasTracks",
0202 "DRICHAerogelIrtCherenkovParticleID",
0203 "DRICHGasIrtCherenkovParticleID",
0204 "DRICHMergedIrtCherenkovParticleID",
0205 "ReconstructedChargedParticleAssociationsWithDRICHPID",
0206 ]
0207 recon_cmd = [
0208 'eicrecon',
0209 "-Ppodio:output_collections=\"#{output_collections.join ','}\"",
0210 '-Pjana:nevents="0"',
0211 '-Pjana:debug_plugin_loading="1"',
0212 '-Pacts:MaterialMap="calibrations/materials-map.cbor"',
0213 "-Ppodio:output_file=\"#{opt.rec_file}\"",
0214 '-PDRICH:DRICHIrtCherenkovParticleID:cheatPhotonVertex=true',
0215 opt.sim_file,
0216 ]
0217
0218
0219
0220
0221 analysis_cmd = [
0222 opt.benchmark_exe,
0223 "-i #{opt.rec_file}",
0224 "-o #{opt.ana_file}",
0225 ]
0226 analysis_cmd.append "-a #{opt.algos.join ' '}" if opt.algos.size > 0
0227 analysis_cmd.append '-' + 'v'*opt.verbosity if opt.verbosity > 0
0228
0229
0230
0231 draw_cmd = [
0232 "#{__dir__}/draw_benchmark.py",
0233 "-i #{opt.ana_file}",
0234 "-o #{opt.using_ci ? "results/
0235 ]
0236
0237
0238
0239
0240
0241 exe = Proc.new do |cmd_args, name, step|
0242 if run_step[step]
0243 case step
0244 when :sim
0245 FileUtils.mkdir_p File.dirname(opt.sim_file)
0246 when :rec
0247 FileUtils.mkdir_p File.dirname(opt.rec_file)
0248 when :ana
0249 FileUtils.mkdir_p File.dirname(opt.ana_file)
0250 end
0251 cmd = cmd_args.join ' '
0252 puts "benchmark #{name} command:".upcase
0253 cmd_args.each_with_index do |arg,i|
0254 line = i==0 ? '' : ' '
0255 line += arg
0256 line += ' \\' unless i+1==cmd_args.size
0257 puts line
0258 end
0259 unless opt.dry_run
0260 puts '-'*50
0261 puts "#{name} execution:".upcase
0262 system cmd or raise "benchmark #{name} failed!".upcase
0263 end
0264 puts '-'*50
0265 end
0266 end
0267 puts '-'*50
0268
0269
0270 exe.call sim_cmd, 'simulation', :sim
0271 exe.call recon_cmd, 'reconstruction', :rec
0272 exe.call analysis_cmd, 'analysis', :ana
0273 exe.call draw_cmd, 'draw', :ana