From 3ca96c6ee4d116d9b213aa1a1b85b4c09ce98d1d Mon Sep 17 00:00:00 2001
From: Jihee Kim <jihee.kim@anl.gov>
Date: Tue, 2 Mar 2021 16:30:44 +0000
Subject: [PATCH] EM Barrel and Endcap

---
 benchmarks/ecal/config.yml                    |  12 +++
 benchmarks/ecal/full_emcal_electrons.sh       |  80 ++++++++++++++
 .../ecal/options/full_em_calorimeter_reco.py  | 101 ++++++++++++++++++
 .../ecal/scripts/full_emcal_electrons.cxx     |  89 +++++++++++++++
 .../scripts/full_emcal_electrons_analysis.cxx |  70 ++++++++++++
 5 files changed, 352 insertions(+)
 create mode 100644 benchmarks/ecal/full_emcal_electrons.sh
 create mode 100644 benchmarks/ecal/options/full_em_calorimeter_reco.py
 create mode 100644 benchmarks/ecal/scripts/full_emcal_electrons.cxx
 create mode 100644 benchmarks/ecal/scripts/full_emcal_electrons_analysis.cxx

diff --git a/benchmarks/ecal/config.yml b/benchmarks/ecal/config.yml
index a1902af7..ffdfdd73 100644
--- a/benchmarks/ecal/config.yml
+++ b/benchmarks/ecal/config.yml
@@ -21,3 +21,15 @@ ecal_1_emcal_pi0s:
   stage: run
   script:
     - bash benchmarks/ecal/emcal_pi0s.sh
+
+emcal_barrel_electrons:
+  image: eicweb.phy.anl.gov:4567/eic/juggler/juggler:$JUGGLER_TAG
+  needs: ["detector"]
+  timeout: 48 hours
+  artifacts:
+    expire_in: 20 weeks
+    paths:
+      - results/
+  stage: run
+  script:
+    - bash benchmarks/ecal/full_emcal_electrons.sh
diff --git a/benchmarks/ecal/full_emcal_electrons.sh b/benchmarks/ecal/full_emcal_electrons.sh
new file mode 100644
index 00000000..68ad8688
--- /dev/null
+++ b/benchmarks/ecal/full_emcal_electrons.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+
+./util/print_env.sh
+
+## To run the reconstruction, we need the following global variables:
+## - JUGGLER_INSTALL_PREFIX:   Install prefix for Juggler (simu/recon)
+## - JUGGLER_DETECTOR:         the detector package we want to use for this benchmark
+## - JUGGLER_DETECTOR_VERSION: the detector package we want to use for this benchmark
+## - DETECTOR_PATH:            full path to the detector definitions
+##
+## You can ready options/env.sh for more in-depth explanations of the variables
+## and how they can be controlled.
+source options/env.sh
+export JUGGLER_DETECTOR_PATH=${DETECTOR_PATH}
+
+if [[ ! -n  "${JUGGLER_N_EVENTS}" ]] ; then 
+  export JUGGLER_N_EVENTS=100
+fi
+
+if [[ ! -n  "${E_start}" ]] ; then
+  export E_start=0.5
+fi
+
+if [[ ! -n  "${E_end}" ]] ; then
+  export E_end=5.0
+fi
+
+export JUGGLER_FILE_NAME_TAG="emcal_uniform_electrons"
+export JUGGLER_GEN_FILE="${JUGGLER_FILE_NAME_TAG}.hepmc"
+
+export JUGGLER_SIM_FILE="sim_${JUGGLER_FILE_NAME_TAG}.root"
+export JUGGLER_REC_FILE="rec_${JUGGLER_FILE_NAME_TAG}.root"
+
+echo "JUGGLER_N_EVENTS = ${JUGGLER_N_EVENTS}"
+echo "JUGGLER_DETECTOR = ${JUGGLER_DETECTOR}"
+
+# generate the input events
+# note datasets is now only used to develop datasets.
+#git clone https://eicweb.phy.anl.gov/EIC/datasets.git datasets
+root -b -q "benchmarks/ecal/scripts/full_emcal_electrons.cxx(${JUGGLER_N_EVENTS}, ${E_start}, ${E_end}, \"${JUGGLER_FILE_NAME_TAG}.hepmc\")"
+if [[ "$?" -ne "0" ]] ; then
+  echo "ERROR running script"
+  exit 1
+fi
+
+# run geant4 simulations
+npsim --runType batch \
+      -v WARNING \
+      --part.minimalKineticEnergy 1*GeV  \
+      --numberOfEvents ${JUGGLER_N_EVENTS} \
+      --compactFile ${DETECTOR_PATH}/${JUGGLER_DETECTOR}.xml \
+      --inputFiles ${JUGGLER_FILE_NAME_TAG}.hepmc \
+      --outputFile  ${JUGGLER_SIM_FILE}
+# Need to figure out how to pass file name to juggler from the commandline
+if [[ "$?" -ne "0" ]] ; then
+  echo "ERROR running npdet"
+  exit 1
+fi
+
+xenv -x ${JUGGLER_INSTALL_PREFIX}/Juggler.xenv \
+  gaudirun.py benchmarks/ecal/options/full_em_calorimeter_reco.py
+if [[ "$?" -ne "0" ]] ; then
+  echo "ERROR running juggler"
+  exit 1
+fi
+
+root -b -q "benchmarks/ecal/scripts/full_emcal_electrons_analysis.cxx(\"${JUGGLER_REC_FILE}\")"
+if [[ "$?" -ne "0" ]] ; then
+  echo "ERROR running analysis script"
+  exit 1
+fi
+
+root_filesize=$(stat --format=%s "${JUGGLER_REC_FILE}")
+if [[ "${JUGGLER_N_EVENTS}" -lt "500" ]] ; then 
+  # file must be less than 10 MB to upload
+  if [[ "${root_filesize}" -lt "10000000" ]] ; then 
+    cp ${JUGGLER_REC_FILE} results/.
+  fi
+fi
+
diff --git a/benchmarks/ecal/options/full_em_calorimeter_reco.py b/benchmarks/ecal/options/full_em_calorimeter_reco.py
new file mode 100644
index 00000000..a61364b0
--- /dev/null
+++ b/benchmarks/ecal/options/full_em_calorimeter_reco.py
@@ -0,0 +1,101 @@
+from Gaudi.Configuration import *
+import os
+  
+from GaudiKernel.DataObjectHandleBase import DataObjectHandleBase
+from Configurables import ApplicationMgr, EICDataSvc, PodioOutput, GeoSvc
+from GaudiKernel import SystemOfUnits as units
+
+detector_name = "topside"
+if "JUGGLER_DETECTOR" in os.environ :
+  detector_name = str(os.environ["JUGGLER_DETECTOR"])
+
+if "JUGGLER_DETECTOR_PATH" in os.environ :
+      detector_name = str(os.environ["JUGGLER_DETECTOR_PATH"])+"/"+detector_name 
+
+input_sim_file = "jug_input.root"
+if "JUGGLER_SIM_FILE" in os.environ :
+  input_sim_file  = str(os.environ["JUGGLER_SIM_FILE"])
+else :
+    print(" ERROR : JUGGLER_SIM_FILE not set" )
+
+output_rec_file = "jug_rec.root"
+if "JUGGLER_REC_FILE" in os.environ :
+  output_rec_file = str(os.environ["JUGGLER_REC_FILE"])
+else :
+    print(" ERROR : JUGGLER_REC_FILE not set" )
+
+n_events = 100
+if "JUGGLER_N_EVENTS" in os.environ :
+  n_events = str(os.environ["JUGGLER_N_EVENTS"])
+
+geo_service  = GeoSvc("GeoSvc", detectors=["{}.xml".format(detector_name)])
+podioevent   = EICDataSvc("EventDataSvc", inputs=[input_sim_file], OutputLevel=DEBUG)
+
+from Configurables import PodioInput
+from Configurables import Jug__Base__InputCopier_dd4pod__Geant4ParticleCollection_dd4pod__Geant4ParticleCollection_ as MCCopier
+from Configurables import Jug__Base__InputCopier_dd4pod__CalorimeterHitCollection_dd4pod__CalorimeterHitCollection_ as CalCopier
+
+from Configurables import Jug__Digi__CrystalEndcapsDigi as CrystalEndcapsDigi
+from Configurables import Jug__Digi__EcalTungstenSamplingDigi as EcalTungstenSamplingDigi
+
+from Configurables import Jug__Reco__CrystalEndcapsReco as CrystalEndcapsReco
+from Configurables import Jug__Reco__CalorimeterIslandCluster as IslandCluster
+from Configurables import Jug__Reco__ClusterRecoCoG as RecoCoG
+
+podioinput = PodioInput("PodioReader", collections=["mcparticles","CrystalEcalHits","EcalBarrelHits","EcalEndcapHits"], OutputLevel=DEBUG)
+
+## copiers to get around input --> output copy bug. Note the "2" appended to the output collection.
+copier = MCCopier("MCCopier", 
+        inputCollection="mcparticles", 
+        outputCollection="mcparticles2",
+        OutputLevel=DEBUG) 
+
+calcopier = CalCopier("CalCopier", 
+        inputCollection="CrystalEcalHits", 
+        outputCollection="CrystalEcalHits2",
+        OutputLevel=DEBUG)
+
+embarreldigi = EcalTungstenSamplingDigi("ec_barrel_digi", 
+        inputHitCollection="EcalBarrelHits", 
+        outputHitCollection="RawEcalBarrelHits",
+        energyResolution=0.11,
+        OutputLevel=DEBUG)
+
+emendcapdigi = EcalTungstenSamplingDigi("ec_endcap_digi", 
+        inputHitCollection="EcalEndcapHits", 
+        outputHitCollection="RawEcalEndcapHits",
+        energyResolution=0.07,
+        OutputLevel=DEBUG)
+
+emcaldigi = CrystalEndcapsDigi("ecal_digi", 
+        inputHitCollection="CrystalEcalHits", 
+        outputHitCollection="RawDigiEcalHits")
+
+emcalreco = CrystalEndcapsReco("ecal_reco", 
+        inputHitCollection="RawDigiEcalHits", 
+        outputHitCollection="RecoEcalHits",
+        minModuleEdep=0.00001*units.MeV)
+
+emcalcluster = IslandCluster("emcal_cluster", 
+        inputHitCollection="RecoEcalHits", 
+        outputClusterCollection="EcalClusters",
+        minClusterCenterEdep=50.0*units.MeV, 
+        groupRange=2.0)
+
+clusterreco = RecoCoG("cluster_reco", 
+        clusterCollection="EcalClusters", 
+        logWeightBase=4.2, 
+        moduleDimZName="CrystalBox_z_length")
+
+out = PodioOutput("out", filename=output_rec_file)
+
+out.outputCommands = ["keep *"]
+
+ApplicationMgr(
+    TopAlg = [podioinput, copier, calcopier,
+              embarreldigi, emendcapdigi, emcaldigi, emcalreco, emcalcluster, clusterreco, out],
+    EvtSel = 'NONE',
+    EvtMax   = n_events,
+    ExtSvc = [podioevent],
+    OutputLevel=DEBUG
+ )
diff --git a/benchmarks/ecal/scripts/full_emcal_electrons.cxx b/benchmarks/ecal/scripts/full_emcal_electrons.cxx
new file mode 100644
index 00000000..f849c076
--- /dev/null
+++ b/benchmarks/ecal/scripts/full_emcal_electrons.cxx
@@ -0,0 +1,89 @@
+//////////////////////////////////////////////////////////////
+// Tungsten Sampling EMCAL detector
+// Barrel and Endcap 
+// Single Electron dataset
+// J.KIM 02/12/2021
+//
+//////////////////////////////////////////////////////////////
+#include "HepMC3/GenEvent.h"
+#include "HepMC3/ReaderAscii.h"
+#include "HepMC3/WriterAscii.h"
+#include "HepMC3/Print.h"
+
+#include <iostream>
+#include<random>
+#include<cmath>
+#include <math.h>
+#include <TMath.h>
+
+using namespace HepMC3;
+
+void full_emcal_electrons(int n_events = 1e6, double e_start = 0.0, double e_end = 30.0, const char* out_fname = "./data/emcal_electron_0GeVto30GeV_1000kEvt.hepmc")
+{
+  WriterAscii hepmc_output(out_fname);
+  int events_parsed = 0;
+  GenEvent evt(Units::GEV, Units::MM);
+
+  // Random number generator
+  TRandom *r1 = new TRandom();
+
+  // Constraining the solid angle, but larger than that subtended by the detector
+  // https://indico.bnl.gov/event/7449/contributions/35966/attachments/27177/41430/EIC-DWG-Calo-03192020.pdf
+  // See a figure on slide 26
+  double cos_theta_min = std::cos(M_PI * (45.0 / 180.0));
+  double cos_theta_max = std::cos(M_PI * (135.0 / 180.0));
+
+  for (events_parsed = 0; events_parsed < n_events; events_parsed++) {
+    // FourVector(px,py,pz,e,pdgid,status)
+    // type 4 is beam
+    // pdgid 11 - electron
+    // pdgid 111 - pi0
+    // pdgid 2212 - proton
+    GenParticlePtr p1 =
+        std::make_shared<GenParticle>(FourVector(0.0, 0.0, 10.0, 10.0), 11, 4);
+    GenParticlePtr p2 = std::make_shared<GenParticle>(
+        FourVector(0.0, 0.0, 0.0, 0.938), 2212, 4);
+
+    // Define momentum
+    // Momentum starting at 0 GeV/c to 30 GeV/c
+    Double_t p        = r1->Uniform(e_start, e_end);
+    Double_t phi      = r1->Uniform(0.0, 2.0 * M_PI);
+    Double_t costheta = r1->Uniform(cos_theta_min, cos_theta_max);
+    Double_t theta    = std::acos(costheta);
+    Double_t px       = p * std::cos(phi) * std::sin(theta);
+    Double_t py       = p * std::sin(phi) * std::sin(theta);
+    Double_t pz       = p * std::cos(theta);
+
+    // Generates random vectors, uniformly distributed over the surface of a
+    // sphere of given radius, in this case momentum.
+    //r1->Sphere(px, py, pz, p);
+
+    // type 1 is final state
+    // pdgid 11 - electron 0.510 MeV/c^2
+    GenParticlePtr p3 = std::make_shared<GenParticle>(
+        FourVector(
+            px, py, pz,
+            sqrt(p*p + (0.000511 * 0.000511))),
+        11, 1);
+
+    GenVertexPtr v1 = std::make_shared<GenVertex>();
+    v1->add_particle_in(p1);
+    v1->add_particle_in(p2);
+
+    v1->add_particle_out(p3);
+    evt.add_vertex(v1);
+
+    if (events_parsed == 0) {
+      std::cout << "First event: " << std::endl;
+      Print::listing(evt);
+    }
+
+    hepmc_output.write_event(evt);
+    if (events_parsed % 10000 == 0) {
+      std::cout << "Event: " << events_parsed << std::endl;
+    }
+    evt.clear();
+  }
+  hepmc_output.close();
+  std::cout << "Events parsed and written: " << events_parsed << std::endl;
+}
diff --git a/benchmarks/ecal/scripts/full_emcal_electrons_analysis.cxx b/benchmarks/ecal/scripts/full_emcal_electrons_analysis.cxx
new file mode 100644
index 00000000..25fbecb2
--- /dev/null
+++ b/benchmarks/ecal/scripts/full_emcal_electrons_analysis.cxx
@@ -0,0 +1,70 @@
+////////////////////////////////////////
+// Read reconstruction ROOT output file
+// Plot variables
+////////////////////////////////////////
+
+#include "ROOT/RDataFrame.hxx"
+#include <iostream>
+
+#include "dd4pod/Geant4ParticleCollection.h"
+#include "eicd/ClusterCollection.h"
+#include "eicd/ClusterData.h"
+
+#include "TCanvas.h"
+#include "TStyle.h"
+#include "TMath.h"
+#include "TH1.h"
+#include "TH1D.h"
+
+// If root cannot find a dd4pod header make sure to add the include dir to ROOT_INCLUDE_PATH:
+//  export ROOT_INCLUDE_PATH=$HOME/include:$ROOT_INCLUDE_PATH
+//  export ROOT_INCLUDE_PATH=/home/jihee/stow/development/include:$ROOT_INCLUDE_PATH
+
+using ROOT::RDataFrame;
+using namespace ROOT::VecOps;
+
+void full_emcal_electrons_analysis(const char* input_fname = "rec_emcal_uniform_electrons.root")
+{
+  // Setting for graphs
+  gROOT->SetStyle("Plain");
+  gStyle->SetOptFit(1);
+  gStyle->SetLineWidth(2);
+  gStyle->SetPadTickX(1);
+  gStyle->SetPadTickY(1);
+  gStyle->SetPadGridX(1);
+  gStyle->SetPadGridY(1);
+  gStyle->SetPadLeftMargin(0.14);
+  gStyle->SetPadRightMargin(0.14);
+
+  ROOT::EnableImplicitMT();
+  ROOT::RDataFrame d0("events", input_fname);
+
+  // Thrown Energy [GeV]
+  auto E_thr = [](std::vector<dd4pod::Geant4ParticleData> const& input) {
+  	std::vector<float> result;
+    	result.push_back(TMath::Sqrt(input[2].psx*input[2].psx + input[2].psy*input[2].psy + input[2].psz*input[2].psz + input[2].mass*input[2].mass));
+  return result;
+  };
+
+  // Define variables
+  auto d1 = d0.Define("E_thr",    E_thr,    {"mcparticles2"})
+	      ;
+
+  // Define Histograms
+  auto hEthr     = d1.Histo1D({"hEthr",     "Thrown Energy; E_{thr}[GeV]; Events",          100, -0.5, 30.5},   "E_thr");
+
+  // Event Counts
+  auto nevents_thrown    = d1.Count();
+
+  // Print out number of events
+  std::cout << "Number of Events: " << (*nevents_thrown) << " = all thrown events \n";
+
+  // Draw Histogram
+  TCanvas *c1 = new TCanvas("c1", "c1", 500, 500);
+  hEthr->GetYaxis()->SetTitleOffset(1.4);
+  hEthr->SetLineWidth(2);
+  hEthr->SetLineColor(kBlue);
+  hEthr->DrawClone();
+  c1->SaveAs("results/emcal_electrons_Ethr.png");
+  c1->SaveAs("results/emcal_electrons_Ethr.pdf");
+}
-- 
GitLab