diff --git a/.gitignore b/.gitignore index ea6d19ba7c20560a5fe7ccbb7c3aec2143ff4b7b..d8d55fee0fc12219f75f341b70fd505ea6544ec3 100644 --- a/.gitignore +++ b/.gitignore @@ -44,4 +44,8 @@ calorimeters/test/ # output files results/* -*.sif +# ROOT files +*.root + +# local runtime files +.local diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e1a1d43619e8ec95475cbfd332fc3e42cc3cba12..58e58a0c83c56477665fd3f1ebe15c66c04d0799 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,7 +9,6 @@ default: stages: - generate - process - - analyze - collect - finish diff --git a/.rootlogon.C b/.rootlogon.C new file mode 100644 index 0000000000000000000000000000000000000000..0be78dd3599f186dcabb06c3c8d0c20177a8f509 --- /dev/null +++ b/.rootlogon.C @@ -0,0 +1,81 @@ +{ + // Ensure fmt is loaded + R__LOAD_LIBRARY(libfmt); + + // setup a local build directory so we don't polute our source code with + // ROOT dictionaries etc. + gSystem->SetBuildDir("/tmp/root_build"); + + // style definition based off the ATLAS style + TStyle* s = gStyle; + + // use plain black on white colors + Int_t icol = 0; // WHITE + s->SetFrameBorderMode(icol); + s->SetFrameFillColor(icol); + s->SetCanvasBorderMode(icol); + s->SetCanvasColor(icol); + s->SetPadBorderMode(icol); + s->SetPadColor(icol); + s->SetStatColor(icol); + // s->SetFillColor(icol); // don't use: white fill color flor *all* + // objects + + // set the paper & margin sizes + s->SetPaperSize(TStyle::kUSLetter); + s->SetPaperSize(20, 26); + + // set margin sizes + s->SetPadTopMargin(0.05); + s->SetPadRightMargin(0.05); + s->SetPadBottomMargin(0.15); + s->SetPadLeftMargin(0.12); + + // set title offsets (for axis label) + s->SetTitleXOffset(1.3); + s->SetTitleYOffset(1.1); + + // use large fonts + // Int_t font=72; // Helvetica italics + Int_t font = 43; // Helvetica + Double_t tsize = 26; + s->SetTextFont(font); + + s->SetTextSize(tsize); + s->SetLabelFont(font, "x"); + s->SetTitleFont(font, "x"); + s->SetLabelFont(font, "y"); + s->SetTitleFont(font, "y"); + s->SetLabelFont(font, "z"); + s->SetTitleFont(font, "z"); + + s->SetLabelSize(tsize, "x"); + s->SetTitleSize(tsize, "x"); + s->SetLabelSize(tsize, "y"); + s->SetTitleSize(tsize, "y"); + s->SetLabelSize(tsize, "z"); + s->SetTitleSize(tsize, "z"); + + // use bold lines and markers + s->SetMarkerStyle(20); + s->SetMarkerSize(1.2); + s->SetHistLineWidth(2.); + s->SetLineStyleString(2, "[12 12]"); // postscript dashes + + // get rid of X error bars and y error bar caps + // s->SetErrorX(0.001); + + // do not display any of the standard histogram decorations + s->SetOptTitle(0); + // s->SetOptStat(1111); + s->SetOptStat(0); + // s->SetOptFit(1111); + s->SetOptFit(0); + + // put tick marks on top and RHS of plots + s->SetPadTickX(1); + s->SetPadTickY(1); + + // lower amount of y-ticks + s->SetNdivisions(505, "Y"); +} diff --git a/config/env.sh b/config/env.sh index 0177dabc68f8e36e44746f8445adee9991735296..73aaf5981f656dfcd19cbf5fead712e88a35b27d 100755 --- a/config/env.sh +++ b/config/env.sh @@ -1,29 +1,80 @@ #!/bin/bash -if [[ ! -n "${JUGGLER_DETECTOR}" ]] ; then +## ============================================================================= +## Global configuration variables for the benchmark scripts +## The script defines the following environment variables that are meant to +## be overriden by the Gitlab continuous integration (CI) +## +## - JUGGLER_DETECTOR: detector package to be used for the benchmark +## - JUGGLER_N_EVENTS: #events processed by simulation/reconstruction +## - JUGGLER_INSTALL_PREFIX: location where Juggler (digi/recon) is installed +## +## It also defines the following additional variables for internal usage +## - LOCAL_PREFIX: prefix for packages installed during the benchmark +## - DETECTOR_PREFIX: prefix for the detector definitions +## - DETECTOR_PATH: actual path with the detector definitions +## +## Finally, it makes sure LOCAL_PREFIX and JUGGLER_PREFIX are added to PATH +## and LD_LIBRARY_PATH +## ============================================================================= + +echo "Setting up the Physics Benchmarks environment" + +## ============================================================================= +## Default variable definitions, normally these should be set +## by the CI. In case of local development you may want to change these +## in case you would like to modify the detector package or +## number of events to be analyzed during the benchmark + +## Detector package to be used during the benchmark process +if [ ! -n "${JUGGLER_DETECTOR}" ] ; then export JUGGLER_DETECTOR="topside" fi +echo "JUGGLER_DETECTOR: ${JUGGLER_DETECTOR}" -if [[ ! -n "${JUGGLER_N_EVENTS}" ]] ; then +## Number of events that will be processed by the reconstruction +if [ ! -n "${JUGGLER_N_EVENTS}" ] ; then export JUGGLER_N_EVENTS=100 fi +echo "JUGGLER_N_EVENTS: ${JUGGLER_N_EVENTS}" -if [[ ! -n "${JUGGLER_INSTALL_PREFIX}" ]] ; then +## Install prefix for juggler, needed to locate the Juggler xenv files. +## Also used by the CI as install prefix for other packages where needed. +## You should not have to touch this. Note that for local usage a different +## prefix structure is automatically used. +if [ ! -n "${JUGGLER_INSTALL_PREFIX}" ] ; then export JUGGLER_INSTALL_PREFIX="/usr/local" fi +## Ensure the juggler prefix is an absolute path +export JUGGLER_INSTALL_PREFIX=`realpath ${JUGGLER_INSTALL_PREFIX}` +echo "JUGGLER_INSTALL_PREFIX: ${JUGGLER_INSTALL_PREFIX}" -# not sure this is needed -if [[ ! -n "${DETECTOR_PREFIX}" ]]; then - # reuse the custom juggler install prefix for detector - export DETECTOR_INSTALL_PREFIX=${JUGGLER_INSTALL_PREFIX} -fi +## ============================================================================= +## Other utility variables that govern how some of the dependent packages +## are built and installed. You should not have to change these. -## ensure absolute paths -# not sure this is needed either -export JUGGLER_INSTALL_PREFIX=`realpath ${JUGGLER_INSTALL_PREFIX}` -export DETECTOR_INSTALL_PREFIX=`realpath ${DETECTOR_INSTALL_PREFIX}` +## local prefix to be used for local storage of packages +## downloaded/installed during the benchmark process +LOCAL_PREFIX=".local" +mkdir -p ${LOCAL_PREFIX} +export LOCAL_PREFIX=`realpath ${LOCAL_PREFIX}` +echo "LOCAL_PREFIX: ${LOCAL_PREFIX}" + +## detector prefix: prefix for the detector definitions +export DETECTOR_PREFIX="${LOCAL_PREFIX}/detector" +mkdir -p ${DETECTOR_PREFIX} +echo "DETECTOR_PREFIX: ${DETECTOR_PREFIX}" + +## detector path: actual detector definition path +export DETECTOR_PATH="${DETECTOR_PREFIX}/${JUGGLER_DETECTOR}" +echo "DETECTOR_PATH: ${DETECTOR_PATH}" + +## ============================================================================= +## Setup PATH and LD_LIBRARY_PATH to include our prefixes +echo "Adding JUGGLER_INSTALL_PREFIX and LOCAL_PREFIX to PATH and LD_LIBRARY_PATH" +export PATH=${JUGGLER_INSTALL_PREFIX}/bin:${LOCAL_PREFIX}/bin:${PATH} +export LD_LIBRARY_PATH=${JUGGLER_INSTALL_PREFIX}/lib:${LOCAL_PREFIX}/lib:${LD_LIBRARY_PATH} -## setup root results artifact path -# this should be in the CI File instead -# https://docs.gitlab.com/ee/ci/yaml/README.html#variables -# export RESULTS_PATH=`realpath results` +## ============================================================================= +## That's all! +echo "Environment setup complete." diff --git a/dis/config.yml b/dis/config.yml index a300e5f034632f6d966711e166770c19696d9f07..c0dda0dc156b2ce3861891eb91dd3f9f694c6d22 100644 --- a/dis/config.yml +++ b/dis/config.yml @@ -1,5 +1,5 @@ dis:run_test: - stage: analyze + stage: process timeout: 1 hours script: - bash dis/dis.sh diff --git a/dvcs/config.yml b/dvcs/config.yml index 9582ea62208316fd874bec5864122a4c4f6cc337..73cfc8e89e443ef17df3d92122c0859791726b33 100644 --- a/dvcs/config.yml +++ b/dvcs/config.yml @@ -7,15 +7,8 @@ dvcs:process: paths: - results -dvcs:analysis: - stage: analyze - needs: ["dvcs:process"] - script: - - echo "THIS IS A PLACE HOLDER" - - dvcs:results: stage: collect - needs: ["dvcs:analysis"] + needs: ["dvcs:process"] script: - echo "All DVCS benchmarks successful" diff --git a/dvmp/analysis/mt.h b/dvmp/analysis/mt.h new file mode 100644 index 0000000000000000000000000000000000000000..198050c6ddc37e68518761b8ccc410e0d71ea123 --- /dev/null +++ b/dvmp/analysis/mt.h @@ -0,0 +1,11 @@ +#ifndef MT_H +#define MT_H + +// Defines the number of threads to run within the ROOT analysis scripts. +// TODO: make this a file configured by the CI scripts so we can specify +// the number of threads (and the number of processes) at a global +// level + +constexpr const int kNumThreads = 8; + +#endif diff --git a/dvmp/analysis/plot.h b/dvmp/analysis/plot.h new file mode 100644 index 0000000000000000000000000000000000000000..69ebba341af239541496a410c66f78eb33e8adac --- /dev/null +++ b/dvmp/analysis/plot.h @@ -0,0 +1,40 @@ +#ifndef PLOT_H +#define PLOT_H + +#include <TColor.h> +#include <fmt/core.h> +#include <vector> + +namespace plot { + +const int kMpBlue = TColor::GetColor(0x1f, 0x77, 0xb4); +const int kMpOrange = TColor::GetColor(0xff, 0x7f, 0x0e); +const int kMpGreen = TColor::GetColor(0x2c, 0xa0, 0x2c); +const int kMpRed = TColor::GetColor(0xd6, 0x27, 0x28); +const int kMpPurple = TColor::GetColor(0x94, 0x67, 0xbd); +const int kMpBrown = TColor::GetColor(0x8c, 0x56, 0x4b); +const int kMpPink = TColor::GetColor(0xe3, 0x77, 0xc2); +const int kMpGrey = TColor::GetColor(0x7f, 0x7f, 0x7f); +const int kMpMoss = TColor::GetColor(0xbc, 0xbd, 0x22); +const int kMpCyan = TColor::GetColor(0x17, 0xbe, 0xcf); + +const std::vector<int> kPalette = {kMpBlue, kMpOrange, kMpGreen, kMpRed, + kMpPurple, kMpBrown, kMpPink, kMpGrey, + kMpMoss, kMpCyan}; + +void draw_label(int ebeam, int pbeam, const std::string_view detector, + std::string_view vm, std::string_view what) { + auto t = new TPaveText(.15, 0.800, .7, .925, "NB NDC"); + t->SetFillColorAlpha(kWhite, 0.4); + t->SetTextFont(43); + t->SetTextSize(25); + t->AddText(fmt::format("#bf{{{} }}SIMULATION", detector).c_str()); + t->AddText(fmt::format("{} for {} DVMP.", what, vm).c_str()); + t->AddText(fmt::format("{} GeV on {} GeV", ebeam, pbeam, what).c_str()); + t->SetTextAlign(12); + t->Draw(); +} + +} // namespace plot + +#endif diff --git a/dvmp/analysis/util.h b/dvmp/analysis/util.h new file mode 100644 index 0000000000000000000000000000000000000000..874b70240c41323df0419a3b5276faae2e292054 --- /dev/null +++ b/dvmp/analysis/util.h @@ -0,0 +1,127 @@ +#ifndef UTIL_H +#define UTIL_H + +#include <algorithm> +#include <cmath> +#include <exception> +#include <fmt/core.h> +#include <limits> +#include <string> +#include <vector> + +#include <Math/Vector4D.h> + +#include "dd4pod/Geant4ParticleCollection.h" +#include "eicd/TrackParametersCollection.h" + +namespace util { + +// Exception definition for unknown particle errors +// FIXME: A utility exception base class should be included in the analysis +// utility library, so we can skip most of this boilerplate +class unknown_particle_error : public std::exception { +public: + unknown_particle_error(std::string_view particle) : m_particle{particle} {} + virtual const char* what() const throw() { + return fmt::format("Unknown particle type: {}", m_particle).c_str(); + } + virtual const char* type() const throw() { return "unknown_particle_error"; } + +private: + const std::string m_particle; +}; + +// Simple function to return the appropriate PDG mass for the particles +// we care about for this process. +// FIXME: consider something more robust (maybe based on hepPDT) to the +// analysis utility library +inline double get_pdg_mass(std::string_view part) { + if (part == "electron") { + return 0.0005109989461; + } else if (part == "muon") { + return .1056583745; + } else if (part == "jpsi") { + return 3.0969; + } else if (part == "upsilon") { + return 9.49630; + } else { + throw unknown_particle_error{part}; + } +} + +// Get a vector of 4-momenta from raw tracking info, using an externally +// provided particle mass assumption. +// FIXME: should be part of utility library +inline auto +momenta_from_tracking(const std::vector<eic::TrackParametersData>& tracks, + const double mass) { + std::vector<ROOT::Math::PxPyPzMVector> momenta{tracks.size()}; + // transform our raw tracker info into proper 4-momenta + std::transform(tracks.begin(), tracks.end(), momenta.begin(), + [mass](const auto& track) { + // make sure we don't divide by zero + if (fabs(track.qOverP) < 1e-9) { + return ROOT::Math::PtEtaPhiMVector{}; + } + const double pt = 1. / track.qOverP * sin(track.theta); + const double eta = -log(tan(track.theta / 2)); + const double phi = track.phi; + return ROOT::Math::PtEtaPhiMVector{pt, eta, phi, mass}; + }); + return momenta; +} + +// Get a vector of 4-momenta from the simulation data. +// FIXME: should be part of utility library +// TODO: Add PID selector (maybe using ranges?) +inline auto +momenta_from_simulation(const std::vector<dd4pod::Geant4ParticleData>& parts) { + std::vector<ROOT::Math::PxPyPzMVector> momenta{parts.size()}; + // transform our simulation particle data into 4-momenta + std::transform(parts.begin(), parts.end(), momenta.begin(), + [](const auto& part) { + return ROOT::Math::PxPyPzMVector{part.psx, part.psy, + part.psz, part.mass}; + }); + return momenta; +} + +// Find the decay pair candidates from a vector of particles (parts), +// with invariant mass closest to a desired value (pdg_mass) +// FIXME: not sure if this belongs here, or in the utility library. Probably the +// utility library +inline std::pair<ROOT::Math::PxPyPzMVector, ROOT::Math::PxPyPzMVector> +find_decay_pair(const std::vector<ROOT::Math::PxPyPzMVector>& parts, + const double pdg_mass) { + int first = -1; + int second = -1; + double best_mass = -1; + + // go through all particle combinatorics, calculate the invariant mass + // for each combination, and remember which combination is the closest + // to the desired pdg_mass + for (int i = 0; i < parts.size(); ++i) { + for (int j = i + 1; j < parts.size(); ++j) { + const double new_mass{(parts[i] + parts[j]).mass()}; + if (fabs(new_mass - pdg_mass) < fabs(best_mass - pdg_mass)) { + first = i; + second = j; + best_mass = new_mass; + } + } + } + if (first < 0) { + return {{}, {}}; + } + return {parts[first], parts[second]}; +} +// Calculate the invariant mass of a given pair of particles +inline double +get_im(const std::pair<ROOT::Math::PxPyPzMVector, ROOT::Math::PxPyPzMVector>& + particle_pair) { + return (particle_pair.first + particle_pair.second).mass(); +} + +} // namespace util + +#endif diff --git a/dvmp/analysis/vm_mass.cxx b/dvmp/analysis/vm_mass.cxx new file mode 100644 index 0000000000000000000000000000000000000000..19c56db8957e780333f690e4033aee988a3a5a99 --- /dev/null +++ b/dvmp/analysis/vm_mass.cxx @@ -0,0 +1,108 @@ +#include "mt.h" +#include "plot.h" +#include "util.h" + +#include <ROOT/RDataFrame.hxx> +#include <cmath> +#include <fmt/color.h> +#include <fmt/core.h> +#include <iostream> +#include <string> +#include <vector> + +// Run VM invariant-mass-based benchmarks on an input reconstruction file for +// a desired vector meson (e.g. jpsi) and a desired decay particle (e.g. muon) +// Output figures are written to our output prefix (which includes the output +// file prefix), and labeled with our detector name. +// TODO: I think it would be better to pass small json configuration file to +// the test, instead of this ever-expanding list of function arguments. +int vm_mass(std::string_view rec_file, std::string_view vm_name, + std::string_view decay_name, std::string_view detector, + std::string output_prefix) { + fmt::print(fmt::emphasis::bold | fg(fmt::color::forest_green), + "Running VM invariant mass analysis...\n"); + fmt::print(" - Vector meson: {}\n", vm_name); + fmt::print(" - Decay particle: {}\n", decay_name); + fmt::print(" - Detector package: {}\n", detector); + fmt::print(" - output prefix: {}\n", output_prefix); + + // Run this in multi-threaded mode if desired + ROOT::EnableImplicitMT(kNumThreads); + + // The particles we are looking for. E.g. J/psi decaying into e+e- + const double vm_mass = util::get_pdg_mass(vm_name); + const double decay_mass = util::get_pdg_mass(decay_name); + + // Ensure our output prefix always ends on a dot, a slash or a dash + if (output_prefix.back() != '.' && output_prefix.back() != '/' && + output_prefix.back() != '-') { + output_prefix += "-"; + } + + // Open our input file file as a dataframe + ROOT::RDataFrame d{"events", rec_file}; + + // utility lambda functions to bind the vector meson and decay particle types + auto momenta_from_tracking = + [decay_mass](const std::vector<eic::TrackParametersData>& tracks) { + return util::momenta_from_tracking(tracks, decay_mass); + }; + auto find_decay_pair = + [vm_mass](const std::vector<ROOT::Math::PxPyPzMVector>& parts) { + return util::find_decay_pair(parts, vm_mass); + }; + + // Define analysis flow + auto d_im = + d.Define("p_rec", momenta_from_tracking, {"outputTrackParameters"}) + .Define("N", "p_rec.size()") + .Define("p_sim", util::momenta_from_simulation, {"mcparticles2"}) + .Define("decay_pair_rec", find_decay_pair, {"p_rec"}) + .Define("decay_pair_sim", find_decay_pair, {"p_sim"}) + .Define("mass_rec", util::get_im, {"decay_pair_rec"}) + .Define("mass_sim", util::get_im, {"decay_pair_sim"}); + + // Define output histograms + auto h_im_rec = d_im.Histo1D( + {"h_im_rec", ";m_{ll'} (GeV);#", 100, -1.1, vm_mass + 5}, "mass_rec"); + auto h_im_sim = d_im.Histo1D( + {"h_im_sim", ";m_{ll'} (GeV);#", 100, -1.1, vm_mass + 5}, "mass_sim"); + + // Plot our histograms. + // TODO: to start I'm explicitly plotting the histograms, but want to + // factorize out the plotting code moving forward. + { + TCanvas c{"canvas", "canvas", 800, 800}; + gPad->SetLogx(false); + gPad->SetLogy(false); + auto& h0 = *h_im_sim; + auto& h1 = *h_im_rec; + // histogram style + h0.SetLineColor(plot::kMpBlue); + h0.SetLineWidth(2); + h1.SetLineColor(plot::kMpOrange); + h1.SetLineWidth(2); + // axes + h0.GetXaxis()->CenterTitle(); + h0.GetYaxis()->CenterTitle(); + // draw everything + h0.DrawClone("hist"); + h1.DrawClone("hist same"); + // FIXME hardcoded beam configuration + plot::draw_label(10, 100, detector, vm_name, "Invariant mass"); + TText* tptr; + auto t = new TPaveText(.6, .8417, .9, .925, "NB NDC"); + t->SetFillColorAlpha(kWhite, 0); + t->SetTextFont(43); + t->SetTextSize(25); + tptr = t->AddText("simulated"); + tptr->SetTextColor(plot::kMpBlue); + tptr = t->AddText("reconstructed"); + tptr->SetTextColor(plot::kMpOrange); + t->Draw(); + // Print canvas to output file + c.Print(fmt::format("{}vm_mass.png", output_prefix).c_str()); + } + // That's all! + return 0; +} diff --git a/dvmp/config.yml b/dvmp/config.yml index 65eb4ab823c1ddc1788b9749be4a366a1f244fd9..4a56b84ea109e93b058e9c5e2934e5e3f4f7c6bb 100644 --- a/dvmp/config.yml +++ b/dvmp/config.yml @@ -17,7 +17,7 @@ dvmp:generate: script: - ./dvmp/scripts/generate.sh --ebeam 10 --pbeam 100 --config jpsi_central --decay muon --decay electron -dvmp:jpsi_central:process: +dvmp:process: stage: process needs: ["dvmp:generate"] timeout: 1 hour @@ -27,15 +27,8 @@ dvmp:jpsi_central:process: paths: - results -dvmp:jpsi_central:test_analysis: - stage: analyze - needs: ["dvmp:jpsi_central:process"] - script: - - echo "THIS IS A PLACE HOLDER" - - dvmp:results: stage: collect - needs: ["dvmp:jpsi_central:test_analysis"] + needs: ["dvmp:process"] script: - echo "All DVMP benchmarks successful" diff --git a/dvmp/dvmp.sh b/dvmp/dvmp.sh index 7b7b2d9ae76f7952c1708c838f3e819e8b98b320..908f94ef814d2461a7dde86bdca4cffb1d0a4ab1 100644 --- a/dvmp/dvmp.sh +++ b/dvmp/dvmp.sh @@ -1,78 +1,104 @@ #!/bin/bash -if [[ ! -n "${JUGGLER_DETECTOR}" ]] ; then - export JUGGLER_DETECTOR="topside" -fi +## ============================================================================= +## Run the DVMP benchmarks in 5 steps: +## 1. Build/install detector package +## 2. Detector simulation through npsim +## 3. Digitization and reconstruction through Juggler +## 4. Root-based Physics analyses +## 5. Finalize +## ============================================================================= -if [[ ! -n "${JUGGLER_N_EVENTS}" ]] ; then - export JUGGLER_N_EVENTS=100 -fi +echo "Running the DVMP benchmarks" -# only used when running locally (not in CI) -if [[ ! -n "${JUGGLER_INSTALL_PREFIX}" ]] ; then - export JUGGLER_INSTALL_PREFIX="/usr/local" -fi +## make sure we launch this script from the project root directory +PROJECT_ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"/.. +pushd ${PROJECT_ROOT} -# these variables might not need exported. -export JUGGLER_FILE_NAME_TAG="dvmp" -# Generator file -export JUGGLER_GEN_FILE="results/dvmp/jpsi_central_electron-10on100-gen.hepmc" -#export JUGGLER_GEN_FILE="${JUGGLER_FILE_NAME_TAG}.hepmc" +## ============================================================================= +## Load the environment variables. To build the detector we need the following +## variables: +## +## - JUGGLER_INSTALL_PREFIX: Install prefix for Juggler (simu/recon) +## - JUGGLER_DETECTOR: the detector package we want to use for this benchmark +## - DETECTOR_PATH: full path to the detector definitions +## +## You can ready config/env.sh for more in-depth explanations of the variables +## and how they can be controlled. +source config/env.sh +## Extra environment variables for DVMP: +## file tag for these tests +JUGGLER_FILE_NAME_TAG="dvmp" +# Generator file, hardcoded for now FIXME +JUGGLER_GEN_FILE="results/dvmp/jpsi_central_electron-10on100-gen.hepmc" +# FIXME use the input file name, as we will be generating a lot of these +# in the future... +## note: these variables need to be exported to be accessible from +## the juggler options.py. We should really work on a dedicated +## juggler launcher to get rid of these "magic" variables. FIXME 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}" -echo "JUGGLER_FILE_NAME_TAG = ${JUGGLER_FILE_NAME_TAG}" - -### Build the detector constructors. -git clone https://eicweb.phy.anl.gov/EIC/detectors/${JUGGLER_DETECTOR}.git -git clone https://eicweb.phy.anl.gov/EIC/detectors/accelerator.git -pushd ${JUGGLER_DETECTOR} -ln -s ../accelerator/eic -popd -mkdir ${JUGGLER_DETECTOR}/build -pushd ${JUGGLER_DETECTOR}/build -cmake ../. -DCMAKE_INSTALL_PREFIX=/usr/local && make -j30 install -popd -pushd ${JUGGLER_DETECTOR} +## ============================================================================= +## Step 1: Build/install the desired detector package +bash util/build_detector.sh -## run geant4 simulations +## ============================================================================= +## Step 2: Run the simulation +echo "Running Geant4 simulation" npsim --runType batch \ --part.minimalKineticEnergy 1000*GeV \ -v WARNING \ --numberOfEvents ${JUGGLER_N_EVENTS} \ - --compactFile ${JUGGLER_DETECTOR}.xml \ - --inputFiles ../${JUGGLER_GEN_FILE} \ - --outputFile ${JUGGLER_SIM_FILE} -if [[ "$?" -ne "0" ]] ; then - echo "ERROR running script" + --compactFile ${DETECTOR_PATH}/${JUGGLER_DETECTOR}.xml \ + --inputFiles ${JUGGLER_GEN_FILE} \ + --outputFile ${JUGGLER_SIM_FILE} +if [ "$?" -ne "0" ] ; then + echo "ERROR running npsim" exit 1 fi -# Need to figure out how to pass file name to juggler from the commandline +## ============================================================================= +## Step 3: Run digitization & reconstruction +echo "Running the digitization and reconstruction" +# FIXME Need to figure out how to pass file name to juggler from the commandline xenv -x ${JUGGLER_INSTALL_PREFIX}/Juggler.xenv \ - gaudirun.py ../options/tracker_reconstruction.py -if [[ "$?" -ne "0" ]] ; then + gaudirun.py options/tracker_reconstruction.py +if [ "$?" -ne "0" ] ; then echo "ERROR running juggler" exit 1 fi ls -l -popd -pwd -mkdir -p results/dis +## ============================================================================= +## Step 4: Analysis +root -b -q "dvmp/analysis/vm_mass.cxx(\ + \"${JUGGLER_REC_FILE}\", \ + \"jpsi\", \ + \"electron\", \ + \"${JUGGLER_DETECTOR}\", \ + \"results/dvmp/plot\")" -echo "STAND-IN FOR ANALYSIS SCRIPT" -#root -b -q "dis/scripts/rec_dis_electrons.cxx(\"${JUGGLER_DETECTOR}/${JUGGLER_REC_FILE}\")" -#if [[ "$?" -ne "0" ]] ; then -# echo "ERROR running root script" -# exit 1 -#fi +if [ "$?" -ne "0" ] ; then + echo "ERROR running root script" + exit 1 +fi -if [[ "${JUGGLER_N_EVENTS}" -lt "500" ]] ; then -cp ${JUGGLER_DETECTOR}/${JUGGLER_REC_FILE} results/dvmp/. +## ============================================================================= +## Step 5: finalize +echo "Finalizing ${JUGGLER_FILE_NAME_TAG} benchmark" + +## Copy over reconsturction artifacts as long as we don't have +## too many events +if [ "${JUGGLER_N_EVENTS}" -lt "500" ] ; then + cp ${JUGGLER_REC_FILE} results/dvmp/. fi +## cleanup output files +rm ${JUGGLER_REC_FILE} ${JUGGLER_SIM_FILE} + +## ============================================================================= +## All done! +echo "${JUGGLER_FILE_NAME_TAG} benchmarks complete" diff --git a/options/tracker_reconstruction.py b/options/tracker_reconstruction.py index 75f104aee62a105069b4f2114548a48893761f6d..a9181a0a6beeeb464a341165bce4d1a8b16d8909 100644 --- a/options/tracker_reconstruction.py +++ b/options/tracker_reconstruction.py @@ -12,8 +12,10 @@ if "JUGGLER_DETECTOR" in os.environ : input_sim_file = str(os.environ["JUGGLER_SIM_FILE"]) output_rec_file = str(os.environ["JUGGLER_REC_FILE"]) n_events = str(os.environ["JUGGLER_N_EVENTS"]) +detector_path = str(os.environ["DETECTOR_PATH"]) -geo_service = GeoSvc("GeoSvc", detectors=["{}.xml".format(detector_name)]) +geo_service = GeoSvc("GeoSvc", + detectors=["{}/{}.xml".format(detector_path, detector_name)]) podioevent = EICDataSvc("EventDataSvc", inputs=[input_sim_file], OutputLevel=DEBUG) from Configurables import PodioInput diff --git a/util/build_detector.sh b/util/build_detector.sh index 3d08a73c2dac69e04f9eddc2c82db258cf7abf55..066b11c2f1257286c0c282b59fb272866fc79d5d 100755 --- a/util/build_detector.sh +++ b/util/build_detector.sh @@ -1,28 +1,64 @@ #!/bin/bash -## Init the environment +## ============================================================================= +## Build and install the JUGGLER_DETECTOR detector package into our local prefix +## ============================================================================= + +## make sure we launch this script from the project root directory +PROJECT_ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"/.. +pushd ${PROJECT_ROOT} + +## ============================================================================= +## Load the environment variables. To build the detector we need the following +## variables: +## +## - JUGGLER_DETECTOR: the detector package we want to use for this benchmark +## - LOCAL_PREFIX: location where local packages should be installed +## - DETECTOR_PREFIX: prefix for the detector definitions +## - DETECTOR_PATH: full path for the detector definitions +## this is the same as ${DETECTOR_PREFIX}/${JUGGLER_DETECTOR} +## +## You can ready config/env.sh for more in-depth explanations of the variables +## and how they can be controlled. source config/env.sh -## Build and install the detector plugins. -if [[ ! -d ${JUGGLER_DETECTOR} ]]; then +## ============================================================================= +## Step 1: download/update the detector definitions (if needed) +pushd ${DETECTOR_PREFIX} + +## We need an up-to-date copy of the detector +if [ ! -d ${JUGGLER_DETECTOR} ]; then + echo "Fetching ${JUGGLER_DETECTOR}" git clone https://eicweb.phy.anl.gov/EIC/detectors/${JUGGLER_DETECTOR}.git - # this might be temporary. There are multiple solutions here but this is the simple pattern for now - # I do not want to use git submodules here -whit - git clone https://eicweb.phy.anl.gov/EIC/detectors/accelerator.git - pushd ${JUGGLER_DETECTOR} - ln -s ../accelerator/eic - popd else + echo "Updating ${JUGGLER_DETECTOR}" pushd ${JUGGLER_DETECTOR} git pull --ff-only popd +fi +## We also need an up-to-date copy of the accelerator. For now this is done +## manually. Down the road we could maybe automize this with cmake +if [ ! -d accelerator ]; then + echo "Fetching accelerator" + git clone https://eicweb.phy.anl.gov/EIC/detectors/accelerator.git +else + echo "Updating accelerator" pushd accelerator git pull --ff-only popd fi -mkdir -p detector-build -pushd detector-build -# Always keep the detector directory at the top level. -echo cmake ../${JUGGLER_DETECTOR} -DCMAKE_INSTALL_PREFIX=${DETECTOR_INSTALL_PREFIX} && make -j30 install -cmake ../${JUGGLER_DETECTOR} -DCMAKE_INSTALL_PREFIX=${DETECTOR_INSTALL_PREFIX} && make -j30 install -popd +## Now symlink the accelerator definition into the detector definition +echo "Linking accelerator definition into detector definition" +ln -s -f ${DETECTOR_PREFIX}/accelerator/eic ${DETECTOR_PATH}/eic + +## ============================================================================= +## Step 2: Compile and install the detector definition +echo "Building and installing the ${JUGGLER_DETECTOR} package" + +mkdir -p ${DETECTOR_PREFIX}/build +pushd ${DETECTOR_PREFIX}/build +cmake ${DETECTOR_PATH} -DCMAKE_INSTALL_PREFIX=${LOCAL_PREFIX} && make -j30 install + +## ============================================================================= +## Step 3: That's all! +echo "Detector build/install complete!" diff --git a/util/download_events.sh b/util/download.sh similarity index 53% rename from util/download_events.sh rename to util/download.sh index 3cf10fe626f43f31b1fde444c5f4153bb7be793c..47be238b461e3797bf124c46d21aaf33a348f8a5 100755 --- a/util/download_events.sh +++ b/util/download.sh @@ -1,18 +1,19 @@ #!/bin/bash -## Init the environment -source config/env.sh +## ============================================================================= +## Download generator & reconstruction artifacts for one or more physics +## processes. +## ============================================================================= -## Generates different configurations from the master configuration -## for both electron and muon decay channels - -echo "Download generator artifacts for one or more of the physics processes" +## make sure we launch this script from the project root directory +PROJECT_ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"/.. +pushd ${PROJECT_ROOT} PROCS=() -BRANCH="dvmp" +BRANCH="master" function print_the_help { - echo "USAGE: $0 [-c config [[-c config ...]] script1 [script2...]" + echo "USAGE: -p process [-p process2] [-b git_branch]" echo "OPTIONS:" echo " -p,--process Physics process name (can be defined multiple times)." @@ -25,7 +26,6 @@ function print_the_help { exit } - while [ $# -gt 0 ] do key="$1" @@ -35,7 +35,7 @@ do shift # past argument shift # past value ;; - --branch) + -b|--branch) BRANCH="$2" shift # past argument shift # past value @@ -45,12 +45,14 @@ do shift ;; *) # unknown option - echo "unknown option" + echo "unknown option: $1" exit 1 ;; esac done +echo "Downloading generator & reconstruction artifacts for one or more physics processes" + if [ ${#PROCS[@]} -eq 0 ]; then echo "ERROR: need one or more processes: -p <process name> " exit 1 @@ -58,10 +60,14 @@ fi for proc in ${PROCS[@]}; do echo "Dowloading artifacts for $proc (branch: $BRANCH)" - wget https://eicweb.phy.anl.gov/EIC/benchmarks/physics_benchmarks/-/jobs/artifacts/$BRANCH/download?job=${proc}:jpsi_central:generate -O results.zip + wget https://eicweb.phy.anl.gov/EIC/benchmarks/physics_benchmarks/-/jobs/artifacts/$BRANCH/download?job=${proc}:generate -O results_gen.zip + ## FIXME this needs to be smarter, probably through more flags... + wget https://eicweb.phy.anl.gov/EIC/benchmarks/physics_benchmarks/-/jobs/artifacts/$BRANCH/download?job=${proc}:process -O results_rec.zip echo "Unpacking artifacts..." - unzip -u results.zip + unzip -u -o results_gen.zip + unzip -u -o results_rec.zip echo "Cleaning up..." - rm results.zip + rm results_???.zip done +popd echo "All done" diff --git a/util/start_dev_shell.sh b/util/start_dev_shell.sh index 9741c2a924fe5e4562c909d21696bd6f27da25f4..d452525c4fce25ea3bb207a38cdc3b5e22ae9b14 100755 --- a/util/start_dev_shell.sh +++ b/util/start_dev_shell.sh @@ -1,21 +1,85 @@ #!/bin/bash +## ============================================================================= +## Setup (if needed) and start a development shell environment on Linux or MacOS +## ============================================================================= + +## make sure we launch this script from the project root directory +PROJECT_ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"/.. +pushd ${PROJECT_ROOT} + +## We do not load the global development environment here, as this script is +## to be executed on a "naked" system outside of any container + +## ============================================================================= +## Step 1: Parse command line options + +## do we want to force-update the container (only affects Linux) +## default: we do not want to do this. +FORCE_UPDATE= + +function print_the_help { + echo "USAGE: ./util/start_dev_shell [-f]" + echo "OPTIONS:" + echo " -f,--force Force-update container (Only affects Linux)" + echo " -h,--help Print this message" + echo "" + echo " This script will setup and launch a containerized development + environment" + exit +} +while [ $# -gt 0 ] +do + key="$1" + case $key in + -f|--force) + FORCE_UPDATE="true" + shift # past value + ;; + -h|--help) + print_the_help + shift + ;; + *) # unknown option + echo "unknown option $1" + exit 1 + ;; + esac +done + +## get OS type OS=`uname -s` -if [ "${OS}" = "Linux" ]; then - echo "Detected OS: Linux" - if [ ! -f juggler_latest.sif ]; then - echo "Need to fetch singularity image" - wget https://eicweb.phy.anl.gov/eic/juggler/-/jobs/artifacts/master/raw/build/juggler.sif?job=docker:singularity -O juggler_latest.sif - fi - echo "Launching dev shell (through singularity)..." - singularity exec juggler_latest.sif eic-shell -elif [ "${OS}" = "Darwin" ]; then - echo "Detector OS: MacOS" - echo "Syncing docker container" - docker pull sly2j/juggler:latest - echo "Launching dev shell (through docker)..." - docker run -v /Users:/Users -w=$PWD -i -t --rm sly2j/juggler:latest eic-shell -else - echo "ERROR: dev shell not available for this OS (${OS})" -fi +## ============================================================================= +## Step 2: Update container and launch shell +echo "Launching a containerized development shell" + +case ${OS} in + Linux) + echo " - Detected OS: Linux" + ## Use the same prefix as we use for other local packages + export PREFIX=.local/lib + if [ ! -f $PREFIX/juggler_latest.sif ] || [ ! -z ${FORCE_UPDATE} ]; then + echo " - Fetching singularity image" + mkdir -p $PREFIX + wget https://eicweb.phy.anl.gov/eic/juggler/-/jobs/artifacts/master/raw/build/juggler.sif?job=docker:singularity + -O $PREFIX/juggler_latest.sif + fi + echo " - Using singularity to launch shell..." + singularity exec $PREFIX/juggler_latest.sif eic-shell + ;; + Darwin) + echo " - Detector OS: MacOS" + echo " - Syncing docker container" + docker pull sly2j/juggler:latest + echo " - Using docker to launch shell..." + docker run -v /Users:/Users -w=$PWD -i -t --rm sly2j/juggler:latest eic-shell + ;; + *) + echo "ERROR: dev shell not available for this OS (${OS})" + exit 1 +esac + +## ============================================================================= +## Step 3: All done +echo "Exiting development environment..."