From a5f76dd8cc34bfd803cd4f782fef251fff8dc794 Mon Sep 17 00:00:00 2001
From: Christopher Dilks <dilks@jlab.org>
Date: Mon, 25 Sep 2023 13:01:54 +0000
Subject: [PATCH] ci: enable dRICH benchmarks and run 3 fixed-eta tests

---
 .gitlab-ci.yml                   |  1 +
 benchmarks/rich/config.yml       | 26 +++++++--
 benchmarks/rich/run_benchmark.rb | 93 +++++++++++++++++++++-----------
 3 files changed, 86 insertions(+), 34 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e9acf2ab..db247109 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -112,6 +112,7 @@ include:
   - local: 'benchmarks/imaging_ecal/config.yml'
   - local: 'benchmarks/imaging_shower_ML/config.yml'
   - local: 'benchmarks/far_forward/config.yml'
+  - local: 'benchmarks/rich/config.yml'
 
 final_report:
   stage: finish
diff --git a/benchmarks/rich/config.yml b/benchmarks/rich/config.yml
index 3cc8b89f..fc6fc2a9 100644
--- a/benchmarks/rich/config.yml
+++ b/benchmarks/rich/config.yml
@@ -1,8 +1,26 @@
-rich_job_x:
+rich_compile:
+  stage: compile
+  extends: .compile_benchmark
+  script:
+    - cmake -S . -B build -DCMAKE_INSTALL_PREFIX=.local
+    - cmake --build build -j2
+    - cmake --install build
+
+drich_fixed_eta:
   extends: .rec_benchmark
-  timeout: 24 hours
+  timeout: 2 hours
   stage: run
+  needs: 
+    - rich_compile
   script:
-    - bash benchmarks/rich/forward_hadrons.sh
-
+    - |
+      for mode in fixedEtaIdeal fixedEtaMin fixedEtaMax ; do
+        ruby benchmarks/rich/run_benchmark.rb --ci -s $mode --num-events 50
+      done
 
+rich_collect:
+  stage: collect
+  needs:
+    - drich_fixed_eta
+  script:
+    - echo "Done collecting artifacts."
diff --git a/benchmarks/rich/run_benchmark.rb b/benchmarks/rich/run_benchmark.rb
index 0a1a748e..1ab97270 100755
--- a/benchmarks/rich/run_benchmark.rb
+++ b/benchmarks/rich/run_benchmark.rb
@@ -4,11 +4,16 @@
 
 require 'optparse'
 require 'ostruct'
+require 'fileutils'
 
 ###################################
 # constants:
-IdealTheta    = 23.5 # [degrees]
-IdealEnergy   = 20.0 # [GeV]
+EtaTestValues = {
+  :ideal => 2.0,
+  :min   => 1.6,
+  :max   => 3.5,
+}
+IdealEnergy   = 12.0 # [GeV]
 IdealParticle = 'pi+'
 ###################################
 
@@ -17,7 +22,7 @@ IdealParticle = 'pi+'
 
 # default opt
 opt = OpenStruct.new
-opt.evgen         = ''
+opt.sim_mode      = ''
 opt.sim_file      = 'out/sim.edm4hep.root'
 opt.rec_file      = 'out/rec.edm4hep.root'
 opt.ana_file      = 'out/ana.edm4hep.root'
@@ -32,12 +37,13 @@ opt.algos         = Array.new
 opt.plots         = Array.new
 opt.verbosity     = 0
 opt.dry_run       = false
+opt.using_ci      = false
 
-# available event generation modes
-avail_evgens = [
-  'idealAngle',
-  'minTheta',
-  'maxTheta',
+# available simulation modes
+avail_sim_modes = [
+  'fixedEtaIdeal',
+  'fixedEtaMin',
+  'fixedEtaMax',
 ]
 
 # parse options
@@ -57,16 +63,16 @@ OptionParser.new do |o|
     required_set = true
   end
   o.separator ''
-  o.on("-e", "--evgen [EVGEN_MODE]", "Run the event generation, reconstruction, and analysis",
-       "[EVGEN_MODE] must be one of:") do |a|
-    unless avail_evgens.include? a
-      $stderr.puts "ERROR: unknown event generation mode '#{a}'"
+  o.on("-s", "--sim-mode [SIMULATION_MODE]", "Run the simulation, reconstruction, and analysis",
+       "[SIMULATION_MODE] must be one of:") do |a|
+    unless avail_sim_modes.include? a
+      $stderr.puts "ERROR: unknown simulation mode '#{a}'"
       exit 1
     end
-    opt.evgen = a
+    opt.sim_mode = a
     required_set = true
   end
-  avail_evgens.each{ |it| o.separator ' '*40+it }
+  avail_sim_modes.each{ |it| o.separator ' '*40+it }
   o.separator ''
   o.separator 'optional options:'.upcase
   o.separator ''
@@ -90,6 +96,7 @@ OptionParser.new do |o|
   o.separator ''
   o.on("-v", "--verbose", "Increase verbosity (-vv for more verbose)") { |a| opt.verbosity+=1 }
   o.on("-d", "--dry-run", "Dry run (just print commands)") { |a| opt.dry_run=true }
+  o.on("--ci", "output plots to ./results, for CI artifact collection") { |a| opt.using_ci=true }
   o.separator ''
   o.on_tail("-h", "--help", "Show this message") do
     puts o
@@ -131,36 +138,53 @@ end
 compact_file = "#{ENV['DETECTOR_PATH']}/#{ENV['DETECTOR_CONFIG']}.xml"
 
 
-# event generator command generators
+# simulation command generators
 # ---------------------------------------------------
 def theta2xyz(theta)
-  rad = theta * Math::PI / 180
-  [ Math.sin(rad), 0.0, Math.cos(rad) ]
+  [ Math.sin(theta), 0.0, Math.cos(theta) ]
 end
 
+def eta2theta(eta)
+  2 * Math.atan(Math.exp(-eta))
+end
+
+
 # fixed angle particle gun
-evgen_fixed_angle = Proc.new do |theta, energy, particle|
+simulate_fixed_angle = Proc.new do |theta, energy, particle|
   [
     "npsim",
     "--runType batch",
     "--compactFile #{compact_file}",
     "--outputFile #{opt.sim_file}",
-    "--part.userParticleHandler=''",
+    "--part.userParticleHandler=''",  # allow opticalphotons in MC particles
     "--enableGun",
     "--numberOfEvents #{opt.num_events}",
     "--gun.particle #{particle}",
     "--gun.energy #{energy}*GeV",
-    "--gun.direction (#{theta2xyz(theta).join ", "})",
+    "--gun.direction '(#{theta2xyz(theta).join ", "})'",
   ]
 end
 
 
-# define event generator command
+# define simulation command
 # ---------------------------------------------------
-evgen_cmd = Array.new
-case opt.evgen
-when 'idealAngle'
-  evgen_cmd = evgen_fixed_angle.call IdealTheta, IdealEnergy, IdealParticle
+sim_cmd = Array.new
+case opt.sim_mode
+when /^fixedEta/
+  key       = opt.sim_mode.sub('fixedEta','').downcase.to_sym
+  fixed_eta = EtaTestValues[key]
+  if fixed_eta.nil?
+    $stderr.puts "ERROR: EtaTestValues[#{key}] is not defined"
+    exit 1
+  end
+  fixed_theta = eta2theta fixed_eta
+  puts """Simulating fixed-eta events:
+  - eta      = #{fixed_eta}
+  - theta    = #{180.0 * fixed_theta / Math::PI} degrees
+  - energy   = #{IdealEnergy} GeV
+  - particle = #{IdealParticle}
+  """
+  sim_cmd = simulate_fixed_angle.call fixed_theta, IdealEnergy, IdealParticle
 else
   exit 1 if run_step[:sim]
 end
@@ -187,6 +211,7 @@ recon_cmd = [
   '-Pjana:debug_plugin_loading="1"',
   '-Pacts:MaterialMap="calibrations/materials-map.cbor"',
   "-Ppodio:output_file=\"#{opt.rec_file}\"",
+  '-PDRICH:DRICHIrtCherenkovParticleID:cheatPhotonVertex=true',  # allow knowledge of true photons, for accurate residual determination
   opt.sim_file,
 ]
 
@@ -206,7 +231,7 @@ analysis_cmd.append '-' + 'v'*opt.verbosity if opt.verbosity > 0
 draw_cmd = [
   "#{__dir__}/draw_benchmark.py",
   "-i #{opt.ana_file}",
-  "-o #{opt.ana_file.gsub(/edm4hep.root$/,"plots")}"
+  "-o #{opt.using_ci ? "results/#{opt.sim_mode}" : opt.ana_file.gsub(/edm4hep.root$/,"plots")}"
 ]
 
 # execute commands
@@ -215,6 +240,14 @@ draw_cmd = [
 # proc: execute a command; raise runtime exception if failure (exit nonzero)
 exe = Proc.new do |cmd_args, name, step|
   if run_step[step]
+    case step
+    when :sim
+      FileUtils.mkdir_p File.dirname(opt.sim_file)
+    when :rec
+      FileUtils.mkdir_p File.dirname(opt.rec_file)
+    when :ana
+      FileUtils.mkdir_p File.dirname(opt.ana_file)
+    end
     cmd = cmd_args.join ' '
     puts "benchmark #{name} command:".upcase
     cmd_args.each_with_index do |arg,i|
@@ -234,7 +267,7 @@ end
 puts '-'*50
 
 # execute the commands
-exe.call evgen_cmd,    'event generation', :sim
-exe.call recon_cmd,    'reconstruction',   :rec
-exe.call analysis_cmd, 'analysis',         :ana
-exe.call draw_cmd,     'draw',             :ana
+exe.call sim_cmd,      'simulation',     :sim
+exe.call recon_cmd,    'reconstruction', :rec
+exe.call analysis_cmd, 'analysis',       :ana
+exe.call draw_cmd,     'draw',           :ana
-- 
GitLab