diff --git a/.clang-format b/.clang-format index 05b10dc8ce7802fa782322aca13d6d67d14c6cf8..c43c6165c7c67310628bd74e3b38b2c0a5a072a7 100644 --- a/.clang-format +++ b/.clang-format @@ -44,7 +44,7 @@ BreakConstructorInitializersBeforeComma: false BreakConstructorInitializers: BeforeColon BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true -ColumnLimit: 120 +ColumnLimit: 100 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: false diff --git a/.rootlogon.C b/.rootlogon.C index 808058da0a709843cf4aa848d950cd2bbb64e6f1..7055e9129f770742f8288bd5196505582a2ec46c 100644 --- a/.rootlogon.C +++ b/.rootlogon.C @@ -7,6 +7,7 @@ // setup a local build directory so we don't polute our source code with // ROOT dictionaries etc. + gSystem->Exec("mkdir -p /tmp/root_build"); gSystem->SetBuildDir("/tmp/root_build"); // style definition based off the ATLAS style diff --git a/accelerator b/accelerator new file mode 160000 index 0000000000000000000000000000000000000000..f3ff428e3b926a41e95beaa984d8dc05cec37cc7 --- /dev/null +++ b/accelerator @@ -0,0 +1 @@ +Subproject commit f3ff428e3b926a41e95beaa984d8dc05cec37cc7 diff --git a/benchmarks/dis/analysis/dis.h b/benchmarks/dis/analysis/dis.h new file mode 100644 index 0000000000000000000000000000000000000000..0e88a01a008726cd6c6f2301fa07aef9d0da03ce --- /dev/null +++ b/benchmarks/dis/analysis/dis.h @@ -0,0 +1,26 @@ +#ifndef DVMP_H +#define DVMP_H + +#include <util.h> + +#include <algorithm> +#include <cmath> +#include <exception> +#include <fmt/core.h> +#include <limits> +#include <string> +#include <vector> + +#include <Math/Vector4D.h> + +// Additional utility functions for DVMP benchmarks. Where useful, these can be +// promoted to the top-level util library +namespace util { + + // ADD EXTRA DIS UTILTIY FUNCTIONS HERE + + //========================================================================================================= + +} // namespace util + +#endif diff --git a/benchmarks/dis/analysis/dis_electrons.cxx b/benchmarks/dis/analysis/dis_electrons.cxx new file mode 100644 index 0000000000000000000000000000000000000000..02dc7b0dac383580c671aa98591f0a64cca3671b --- /dev/null +++ b/benchmarks/dis/analysis/dis_electrons.cxx @@ -0,0 +1,116 @@ +#include "dis.h" +#include "plot.h" + +#include <benchmark.h> +#include <mt.h> +#include <util.h> + +#include "ROOT/RDataFrame.hxx" +#include <cmath> +#include <fmt/color.h> +#include <fmt/core.h> +#include <fstream> +#include <iostream> +#include <nlohmann/json.hpp> +#include <string> +#include <vector> + +int dis_electrons(const std::string& config_name) +{ + // read our configuration + std::ifstream config_file{config_name}; + nlohmann::json config; + config_file >> config; + + const std::string rec_file = config["rec_file"]; + const std::string detector = config["detector"]; + std::string output_prefix = config["output_prefix"]; + const std::string test_tag = config["test_tag"]; + + fmt::print(fmt::emphasis::bold | fg(fmt::color::forest_green), + "Running DIS electron analysis...\n"); + fmt::print(" - Detector package: {}\n", detector); + fmt::print(" - input file: {}\n", rec_file); + fmt::print(" - output prefix: {}\n", output_prefix); + fmt::print(" - test tag: {}\n", test_tag); + + // create our test definition + // test_tag + eic::util::Test dis_Q2_resolution{ + {{"name", fmt::format("{}_Q2_resolution", test_tag)}, + {"title", "DIS Q2 resolution"}, + {"description", + fmt::format("DIS Q2 resolution with {}, estimated using a Gaussian fit.", detector)}, + {"quantity", "resolution (in %)"}, + {"target", "0.1"}}}; + + // Run this in multi-threaded mode if desired + ROOT::EnableImplicitMT(kNumThreads); + + const double electron_mass = util::get_pdg_mass("electron"); + + // Ensure our output prefix always ends on a dot, a slash or a dash + // Necessary when generating output plots + if (output_prefix.back() != '.' && output_prefix.back() != '/' && output_prefix.back() != '-') { + output_prefix += "-"; + } + + ROOT::RDataFrame d("events", rec_file); + + // utility lambda functions to bind the reconstructed particle type + // (as we have no PID yet) + auto momenta_from_tracking = + [electron_mass](const std::vector<eic::TrackParametersData>& tracks) { + return util::momenta_from_tracking(tracks, electron_mass); + }; + + auto d0 = d.Define("p_rec", momenta_from_tracking, {"outputTrackParameters"}) + .Define("N", "p_rec.size()") + .Define("p_sim", util::momenta_from_simulation, {"mcparticles2"}) + .Define("mom_sim", util::mom, {"p_sim"}) + .Define("mom_rec", util::mom, {"p_rec"}); + + auto h_mom_sim = d0.Histo1D({"h_mom_sim", "; GeV; counts", 100, 0, 50}, "mom_sim"); + auto h_mom_rec = d0.Histo1D({"h_mom_rec", "; GeV; counts", 100, 0, 50}, "mom_rec"); + + auto c = new TCanvas(); + + // 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", 1200, 1200}; + c.cd(); + // gPad->SetLogx(false); + gPad->SetLogy(true); + auto& h1 = *h_mom_sim; + auto& h2 = *h_mom_rec; + // histogram style + h1.SetLineColor(plot::kMpBlue); + h1.SetLineWidth(2); + h2.SetLineColor(plot::kMpOrange); + h2.SetLineWidth(2); + // axes + h1.GetXaxis()->CenterTitle(); + h1.GetYaxis()->CenterTitle(); + // draw everything + h1.DrawClone("hist"); + h2.DrawClone("hist same"); + // FIXME hardcoded beam configuration + plot::draw_label(18, 275, detector); + TText* tptr1; + auto t1 = new TPaveText(.6, .8417, .9, .925, "NB NDC"); + t1->SetFillColorAlpha(kWhite, 0); + t1->SetTextFont(43); + t1->SetTextSize(25); + tptr1 = t1->AddText("simulated"); + tptr1->SetTextColor(plot::kMpBlue); + tptr1 = t1->AddText("reconstructed"); + tptr1->SetTextColor(plot::kMpOrange); + t1->Draw(); + + c.Print(fmt::format("{}momentum.png", output_prefix).c_str()); + } + + return 0; +} diff --git a/benchmarks/dis/analysis/rec_dis_electrons.cxx b/benchmarks/dis/analysis/rec_dis_electrons.cxx deleted file mode 100644 index b7227a1ddb33c2564fd160e2124da774a9711161..0000000000000000000000000000000000000000 --- a/benchmarks/dis/analysis/rec_dis_electrons.cxx +++ /dev/null @@ -1,113 +0,0 @@ -#include "ROOT/RDataFrame.hxx" -#include <iostream> - -#include "dd4pod/Geant4ParticleCollection.h" -#include "eicd/TrackParametersCollection.h" -#include "eicd/ClusterCollection.h" -#include "eicd/ClusterData.h" - -using ROOT::RDataFrame; -using namespace ROOT::VecOps; - -auto p_track = [](std::vector<eic::TrackParametersData> const& in) { - std::vector<double> result; - for (size_t i = 0; i < in.size(); ++i) { - result.push_back(std::abs(1.0/(in[i].qOverP))); - } - return result; -}; - -std::vector<float> pt (std::vector<dd4pod::Geant4ParticleData> const& in){ - std::vector<float> result; - for (size_t i = 0; i < in.size(); ++i) { - result.push_back(std::sqrt(in[i].psx * in[i].psx + in[i].psy * in[i].psy)); - } - return result; -} - -auto momentum = [](std::vector<ROOT::Math::PxPyPzMVector> const& in) { - std::vector<double> result; - for (size_t i = 0; i < in.size(); ++i) { - result.push_back(in[i].E()); - } - return result; -}; -auto theta = [](std::vector<ROOT::Math::PxPyPzMVector> const& in) { - std::vector<double> result; - for (size_t i = 0; i < in.size(); ++i) { - result.push_back(in[i].Theta()*180/M_PI); - } - return result; -}; -auto fourvec = [](ROOT::VecOps::RVec<dd4pod::Geant4ParticleData> const& in) { - std::vector<ROOT::Math::PxPyPzMVector> result; - ROOT::Math::PxPyPzMVector lv; - for (size_t i = 0; i < in.size(); ++i) { - lv.SetCoordinates(in[i].psx, in[i].psy, in[i].psz, in[i].mass); - result.push_back(lv); - } - return result; -}; - -auto delta_p = [](const std::vector<double>& tracks, const std::vector<double>& thrown) { - std::vector<double> res; - for (const auto& p1 : thrown) { - for (const auto& p2 : tracks) { - res.push_back(p1 - p2); - } - } - return res; -}; - -int rec_dis_electrons(const char* fname = "topside/rec_central_electrons.root") -{ - - ROOT::EnableImplicitMT(); - ROOT::RDataFrame df("events", fname); - - auto df0 = df.Define("isThrown", "mcparticles2.genStatus == 1") - .Define("thrownParticles", "mcparticles2[isThrown]") - .Define("thrownP", fourvec, {"thrownParticles"}) - .Define("p_thrown", momentum, {"thrownP"}) - .Define("nTracks", "outputTrackParameters.size()") - .Define("p_track", p_track, {"outputTrackParameters"}) - .Define("p_track1", p_track, {"outputTrackParameters1"}) - .Define("p_track2", p_track, {"outputTrackParameters2"}) - .Define("delta_p",delta_p, {"p_track", "p_thrown"}) - .Define("delta_p1",delta_p, {"p_track1", "p_thrown"}) - .Define("delta_p2",delta_p, {"p_track2", "p_thrown"}); - - auto h_nTracks = df0.Histo1D({"h_nTracks", "; N tracks ", 10, 0, 10}, "nTracks"); - auto h_pTracks = df0.Histo1D({"h_pTracks", "; GeV/c ", 100, 0, 10}, "p_track"); - - auto h_delta_p = df0.Histo1D({"h_delta_p", "; GeV/c ", 100, -10, 10}, "delta_p"); - auto h_delta_p1 = df0.Histo1D({"h_delta_p1", "; GeV/c ", 100, -10, 10}, "delta_p1"); - auto h_delta_p2 = df0.Histo1D({"h_delta_p2", "; GeV/c ", 100, -10, 10}, "delta_p2"); - - auto c = new TCanvas(); - - h_nTracks->DrawCopy(); - c->SaveAs("results/dis/rec_central_electrons_nTracks.png"); - c->SaveAs("results/dis/rec_central_electrons_nTracks.pdf"); - - h_pTracks->DrawCopy(); - c->SaveAs("results/dis/rec_central_electrons_pTracks.png"); - c->SaveAs("results/dis/rec_central_electrons_pTracks.pdf"); - - THStack * hs = new THStack("hs_delta_p","; GeV/c "); - TH1D* h1 = (TH1D*) h_delta_p->Clone(); - hs->Add(h1); - h1 = (TH1D*) h_delta_p1->Clone(); - h1->SetLineColor(2); - hs->Add(h1); - h1 = (TH1D*) h_delta_p2->Clone(); - h1->SetLineColor(4); - h1->SetFillStyle(3001); - h1->SetFillColor(4); - hs->Add(h1); - hs->Draw("nostack"); - c->SaveAs("results/dis/rec_central_electrons_delta_p.png"); - c->SaveAs("results/dis/rec_central_electrons_delta_p.pdf"); - - return 0; -} diff --git a/benchmarks/dis/dis.sh b/benchmarks/dis/dis.sh old mode 100644 new mode 100755 index a863c7c5c66bcbccc6c97c4c99fd615e2bb95e1a..77fb85a577d6726e413166a153f2893b81aab1ef --- a/benchmarks/dis/dis.sh +++ b/benchmarks/dis/dis.sh @@ -1,51 +1,61 @@ #!/bin/bash ## ============================================================================= -## Run the DVMP benchmarks in 5 steps: -## 1. Build/install detector package +## Run the DIS benchmarks in 5 steps: +## 1. Parse the command line and setup environment ## 2. Detector simulation through npsim ## 3. Digitization and reconstruction through Juggler ## 4. Root-based Physics analyses ## 5. Finalize ## ============================================================================= -echo "Running the DIS benchmarks" - ## make sure we launch this script from the project root directory -PROJECT_ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"/.. +PROJECT_ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"/../.. pushd ${PROJECT_ROOT} +echo "Running the DIS benchmarks" + ## ============================================================================= -## Load the environment variables. To build the detector we need the following -## variables: +## Step 1: Setup the environment variables ## +## First parse the command line flags. +## This sets the following environment variables: +## - CONFIG: The specific generator configuration +## - EBEAM: The electron beam energy +## - PBEAM: The ion beam energy +source util/parse_cmd.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 ## - DETECTOR_PATH: full path to the detector definitions ## -## You can ready config/env.sh for more in-depth explanations of the variables +## You can ready options/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="dis" -# TODO use the input file name, as we will be generating a lot of these -# in the future... -# FIXME Generator file hardcoded for now -## 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_GEN_FILE="results/dis/${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" +source options/env.sh + +## We also need the following benchmark-specific variables: +## +## - BENCHMARK_TAG: Unique identified for this benchmark process. +## - BEAM_TAG: Identifier for the chosen beam configuration +## - INPUT_PATH: Path for generator-level input to the benchmarks +## - TMP_PATH: Path for temporary data (not exported as artifacts) +## - RESULTS_PATH: Path for benchmark output figures and files +## +## You can read dvmp/env.sh for more in-depth explanations of the variables. +source benchmarks/dis/env.sh +## Get a unique file names based on the configuration options +GEN_FILE=${INPUT_PATH}/gen-${CONFIG}_${JUGGLER_N_EVENTS}.hepmc + +SIM_FILE=${TMP_PATH}/sim-${CONFIG}.root +SIM_LOG=${TMP_PATH}/sim-${CONFIG}.log -## ============================================================================= -## Step 1: Build/install the desired detector package -## TODO remove this -#bash util/build_detector.sh +REC_FILE=${TMP_PATH}/rec-${CONFIG}.root +REC_LOG=${TMP_PATH}/sim-${CONFIG}.log + +PLOT_TAG=${CONFIG} ## ============================================================================= ## Step 2: Run the simulation @@ -55,8 +65,8 @@ npsim --runType batch \ -v WARNING \ --numberOfEvents ${JUGGLER_N_EVENTS} \ --compactFile ${DETECTOR_PATH}/${JUGGLER_DETECTOR}.xml \ - --inputFiles ${JUGGLER_GEN_FILE} \ - --outputFile ${JUGGLER_SIM_FILE} + --inputFiles ${GEN_FILE} \ + --outputFile ${SIM_FILE} if [ "$?" -ne "0" ] ; then echo "ERROR running npsim" exit 1 @@ -65,36 +75,67 @@ fi ## ============================================================================= ## 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 +## FIXME Need to figure out how to pass file name to juggler from the commandline +## the tracker_reconstruction.py options file uses the following environment +## variables: +## - JUGGLER_SIM_FILE: input detector simulation +## - JUGGLER_REC_FILE: output reconstructed data +## - JUGGLER_DETECTOR_PATH: Location of the detector geometry +## - JUGGLER_N_EVENTS: number of events to process (part of global environment) +## - JUGGLER_DETECTOR: detector package (part of global environment) +export JUGGLER_SIM_FILE=${SIM_FILE} +export JUGGLER_REC_FILE=${REC_FILE} +export JUGGLER_DETECTOR_PATH=${DETECTOR_PATH} xenv -x ${JUGGLER_INSTALL_PREFIX}/Juggler.xenv \ - gaudirun.py config/tracker_reconstruction.py + gaudirun.py options/tracker_reconstruction.py \ + 2>&1 > ${REC_LOG} +## on-error, first retry running juggler again as there is still a random +## crash we need to address FIXME if [ "$?" -ne "0" ] ; then - echo "ERROR running juggler" - exit 1 + echo "Juggler crashed, retrying..." + xenv -x ${JUGGLER_INSTALL_PREFIX}/Juggler.xenv \ + gaudirun.py options/tracker_reconstruction.py \ + 2>&1 > ${REC_LOG} + if [ "$?" -ne "0" ] ; then + echo "ERROR running juggler, both attempts failed" + exit 1 + fi fi -ls -l ## ============================================================================= ## Step 4: Analysis -root -b -q "benchmarks/dis/analysis/rec_dis_electrons.cxx(\"${JUGGLER_DETECTOR}/${JUGGLER_REC_FILE}\")" +## write a temporary configuration file for the analysis script +echo "Running analysis" +CONFIG="${TMP_PATH}/${PLOT_TAG}.json" +cat << EOF > ${CONFIG} +{ + "rec_file": "${REC_FILE}", + "detector": "${JUGGLER_DETECTOR}", + "output_prefix": "${RESULTS_PATH}/${PLOT_TAG}", + "test_tag": "${BEAM_TAG}" +} +EOF +#cat ${CONFIG} +root -b -q "benchmarks/dis/analysis/dis_electrons.cxx+(\"${CONFIG}\")" +#root -b -q "benchmarks/dis/analysis/dis_electrons.cxx(\"${CONFIG}\")" if [[ "$?" -ne "0" ]] ; then - echo "ERROR running root script" + echo "ERROR running rec_dis_electron script" exit 1 fi ## ============================================================================= ## Step 5: finalize -echo "Finalizing ${JUGGLER_FILE_NAME_TAG} benchmark" +echo "Finalizing DIS benchmark" -## Copy over reconsturction artifacts as long as we don't have +## Move 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/dis/. + cp ${REC_FILE} ${RESULTS_PATH} fi -## cleanup output files -rm ${JUGGLER_REC_FILE} ${JUGGLER_SIM_FILE} +## Always move over log files to the results path +cp ${REC_LOG} ${RESULTS_PATH} ## ============================================================================= ## All done! -echo "${JUGGLER_FILE_NAME_TAG} benchmarks complete" +echo "DIS benchmarks complete" diff --git a/benchmarks/dis/env.sh b/benchmarks/dis/env.sh new file mode 100644 index 0000000000000000000000000000000000000000..1a5b153f74f55b5a764019826ad061c7a6d196ba --- /dev/null +++ b/benchmarks/dis/env.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +## ============================================================================= +## Local configuration variables for this particular benchmark +## It defines the following additional variables: +## +## - BENCHMARK_TAG: Tag to identify this particular benchmark +## - BEAM_TAG Tag to identify the beam configuration +## - INPUT_PATH: Path for generator-level input to the benchmarks +## - TMP_PATH: Path for temporary data (not exported as artifacts) +## - RESULTS_PATH: Path for benchmark output figures and files +## +## This script assumes that EBEAM and PBEAM are set as part of the +## calling script (usually as command line argument). +## +## ============================================================================= + +## Tag for the local benchmark. Should be the same as the directory name for +## this particular benchmark set (for clarity). +## This tag is used for the output artifacts directory (results/${JUGGLER_TAG}) +## and a tag in some of the output files. +export BENCHMARK_TAG="dis" +echo "Setting up the local environment for the ${BENCHMARK_TAG^^} benchmarks" + +## Extra beam tag to identify the desired beam configuration +export BEAM_TAG="${EBEAM}on${PBEAM}" + +## Data path for input data (generator-level hepmc file) +INPUT_PATH="input/${BENCHMARK_TAG}/${BEAM_TAG}" +mkdir -p ${INPUT_PATH} +export INPUT_PATH=`realpath ${INPUT_PATH}` +echo "INPUT_PATH: ${INPUT_PATH}" + + +## Data path for temporary data (not exported as artifacts) +TMP_PATH=${LOCAL_PREFIX}/tmp/${BEAM_TAG} +mkdir -p ${TMP_PATH} +export TMP_PATH=`realpath ${TMP_PATH}` +echo "TMP_PATH: ${TMP_PATH}" + +## Data path for benchmark output (plots and reconstructed files +## if not too big). +RESULTS_PATH="results/${BENCHMARK_TAG}/${BEAM_TAG}" +mkdir -p ${RESULTS_PATH} +export RESULTS_PATH=`realpath ${RESULTS_PATH}` +echo "RESULTS_PATH: ${RESULTS_PATH}" + +## ============================================================================= +## That's all! +echo "Local environment setup complete." diff --git a/benchmarks/dis/gen.sh b/benchmarks/dis/gen.sh old mode 100644 new mode 100755 index e09f2c4c7bf79f6e38e9ee09bdd0deacf3124177..4a42081ae6f437d542de677613eef22f4c6c9516 --- a/benchmarks/dis/gen.sh +++ b/benchmarks/dis/gen.sh @@ -3,38 +3,63 @@ ## ============================================================================= ## Standin for a proper pythia generation process, similar to how we ## generate events for DVMP +## Runs in 5 steps: +## 1. Parse the command line and setup the environment +## 2. Check if we can load the requested file from the cache +## 3. Build generator exe +## 4. Run the actual generator +## 5. Finalize +## ============================================================================= ## ============================================================================= - -## TODO: use JUGGLER_FILE_NAME_TAG instead of explicitly refering to dis - -echo "Running the DIS benchmarks" ## 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: +## Step 1: Setup the environment variables +## First parse the command line flags. +## This sets the following environment variables: +## - CONFIG: The specific generator configuration --> not currenlty used FIXME +## - EBEAM: The electron beam energy --> not currently used FIXME +## - PBEAM: The ion beam energy --> not currently used FIXME +source util/parse_cmd.sh $@ + +## To run the generator, 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 -## - DETECTOR_PATH: full path to the detector definitions +## - LOCAL_PREFIX: Place to cache local packages and data +## - JUGGLER_N_EVENTS: Number of events to process +## - JUGGLER_RNG_SEED: Random seed for event generation. ## -## You can ready config/env.sh for more in-depth explanations of the variables +## You can read options/env.sh for more in-depth explanations of the variables ## and how they can be controlled. -source config/env.sh +source options/env.sh + +## We also need the following benchmark-specific variables: +## +## - BENCHMARK_TAG: Unique identified for this benchmark process. +## - INPUT_PATH: Path for generator-level input to the benchmarks +## - TMP_PATH: Path for temporary data (not exported as artifacts) +## +## You can read dvmp/env.sh for more in-depth explanations of the variables. +source benchmarks/dis/env.sh -## Setup local environment -export DATA_PATH=results/dis +## Get a unique file name prefix based on the configuration options +GEN_TAG=gen-${CONFIG}_${JUGGLER_N_EVENTS} ## Generic file prefix -## Extra environment variables for DVMP: -## file tag for these tests -JUGGLER_FILE_NAME_TAG="dis" +## ============================================================================= +## Step 2: Check if we really need to run, or can use the cache. +if [ -f "${INPUT_PATH}/${GEN_TAG}.hepmc" ]; then + echo "Found cached generator output for $GEN_TAG, no need to rerun" + exit 0 +fi + +echo "Generator output for $GEN_TAG not found in cache, need to run generator" ## ============================================================================= -## Step 1: Dummy event generator -## TODO better file name that encodes the actual configuration we're running +## Step 3: Build generator exe +## TODO: need to configurability to the generator exe + echo "Compiling benchmarks/dis/generator/pythia_dis.cxx ..." g++ benchmarks/dis/generator/pythia_dis.cxx -o pythia_dis \ -I/usr/local/include -Iinclude \ @@ -46,20 +71,26 @@ if [[ "$?" -ne "0" ]] ; then exit 1 fi echo "done" -pwd -ls -lrth -./pythia_dis dis.hepmc +## ============================================================================= +## Step 4: Run the event generator +echo "Running the generator" +./pythia_dis ${TMP_PATH}/${GEN_TAG}.hepmc if [[ "$?" -ne "0" ]] ; then echo "ERROR running pythia" exit 1 fi + +## ============================================================================= +## Step 5: Finally, move relevant output into the artifacts directory and clean up ## ============================================================================= -## Step 2: finalize -echo "Moving event generator output into ${DATA_PATH}" -#mv .local/${JUGGLER_FILE_NAME_TAG}.hepmc ${DATA_PATH}/${JUGGLER_FILE_NAME_TAG}.hepmc +echo "Moving generator output into ${INPUT_PATH}" +mv ${TMP_PATH}/${GEN_TAG}.hepmc ${INPUT_PATH}/${GEN_TAG}.hepmc +## this step only matters for local execution +echo "Cleaning up" +## does nothing ## ============================================================================= ## All done! -echo "dis event generation complete" +echo "$BENCHMARK_TAG event generation complete" diff --git a/benchmarks/dis/generator/pythia_dis.cxx b/benchmarks/dis/generator/pythia_dis.cxx index ff819a7e3243373539aace192d0990003ca3368d..a48679683baf6ae692825275e713f79b8317f685 100644 --- a/benchmarks/dis/generator/pythia_dis.cxx +++ b/benchmarks/dis/generator/pythia_dis.cxx @@ -1,7 +1,7 @@ #include "Pythia8/Pythia.h" #include "Pythia8Plugins/HepMC3.h" -#include <unistd.h> #include <cassert> +#include <unistd.h> using namespace Pythia8; @@ -13,7 +13,7 @@ using std::string; enum class mode { none, help, list, part }; struct settings { - double E_electron = 10.0; // GeV + double E_electron = 18.0; // GeV double E_ion = 275.0; // GeV std::string outfile = "dis.hepmc"; int ion_PID = 2212; @@ -22,7 +22,7 @@ struct settings { bool success = false; double Q2_min = 4.0; int N_events = 1000; - mode selected = mode::none; + mode selected = mode::none; }; template <typename T> @@ -63,16 +63,15 @@ void print_man_page(T cli, const char* argv0) } //______________________________________________________________________________ -settings cmdline_settings(int argc, char* argv[]) { +settings cmdline_settings(int argc, char* argv[]) +{ settings s; - auto lastOpt = - " options:" % (option("-h", "--help").set(s.selected, mode::help) % "show help", - value("file", s.outfile).if_missing([] { - std::cout << "You need to provide an output filename argument!\n"; - }) % "output file"); + auto lastOpt = " options:" % (option("-h", "--help").set(s.selected, mode::help) % "show help", + value("file", s.outfile).if_missing([] { + std::cout << "You need to provide an output filename argument!\n"; + }) % "output file"); - auto cli = - (command("help").set(s.selected, mode::help) | lastOpt); + auto cli = (command("help").set(s.selected, mode::help) | lastOpt); assert(cli.flags_are_prefix_free()); @@ -95,7 +94,8 @@ settings cmdline_settings(int argc, char* argv[]) { } //______________________________________________________________________________ -int main(int argc, char* argv[]) { +int main(int argc, char* argv[]) +{ settings s = cmdline_settings(argc, argv); if (!s.success) { diff --git a/benchmarks/dvcs/dvcs.sh b/benchmarks/dvcs/dvcs.sh index 39143562a090fdde535a899ae5bf7e1420f7a0a5..35bbcafeddfe3e2e218ba286f209464cf8a297d7 100644 --- a/benchmarks/dvcs/dvcs.sh +++ b/benchmarks/dvcs/dvcs.sh @@ -15,9 +15,9 @@ echo "JUGGLER_FILE_NAME_TAG = ${JUGGLER_FILE_NAME_TAG}" ## - 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 +## You can ready options/env.sh for more in-depth explanations of the variables ## and how they can be controlled. -source config/env.sh +source options/env.sh curl -o test_proton_dvcs_eic.hepmc "https://eicweb.phy.anl.gov/api/v4/projects/345/jobs/artifacts/master/raw/data/test_proton_dvcs_eic.hepmc?job=compile" @@ -43,7 +43,7 @@ fi # Need to figure out how to pass file name to juggler from the commandline xenv -x ${JUGGLER_INSTALL_PREFIX}/Juggler.xenv \ - gaudirun.py config/tracker_reconstruction.py + gaudirun.py options/tracker_reconstruction.py if [[ "$?" -ne "0" ]] ; then echo "ERROR running juggler" exit 1 diff --git a/benchmarks/dvmp/analysis/dvmp.h b/benchmarks/dvmp/analysis/dvmp.h new file mode 100644 index 0000000000000000000000000000000000000000..371b454f8d92f4c0cd5b17edd4a8f5e1ecc77e77 --- /dev/null +++ b/benchmarks/dvmp/analysis/dvmp.h @@ -0,0 +1,56 @@ +#ifndef DVMP_H +#define DVMP_H + +#include <util.h> + +#include <algorithm> +#include <cmath> +#include <exception> +#include <fmt/core.h> +#include <limits> +#include <string> +#include <vector> + +#include <Math/Vector4D.h> + +// Additional utility functions for DVMP benchmarks. Where useful, these can be +// promoted to the top-level util library +namespace util { + + // Calculate the 4-vector sum of a given pair of particles + inline ROOT::Math::PxPyPzMVector + get_sum(const std::pair<ROOT::Math::PxPyPzMVector, ROOT::Math::PxPyPzMVector>& particle_pair) + { + return (particle_pair.first + particle_pair.second); + } + + //======================================================================================================== + // for structure functions + + struct inv_quant { // add more when needed + double nu, Q2, x; + }; + + // for simu + inline inv_quant calc_inv_quant_simu(const std::vector<ROOT::Math::PxPyPzMVector>& parts) + { + ROOT::Math::PxPyPzMVector q(parts[0] - parts[2]); + ROOT::Math::PxPyPzMVector P(parts[3]); + + double nu = q.Dot(P) / P.mass(); + double Q2 = -q.Dot(q); + inv_quant quantities = {nu, Q2, Q2 / 2. / P.mass() / nu}; + return quantities; + } + + inline double get_nu_simu(inv_quant quantities) { return quantities.nu / 1000.; } + inline double get_Q2_simu(inv_quant quantities) { return quantities.Q2; } + inline double get_x_simu(inv_quant quantities) { return quantities.x; } + + // for tracking, add later + + //========================================================================================================= + +} // namespace util + +#endif diff --git a/benchmarks/dvmp/analysis/plot.h b/benchmarks/dvmp/analysis/plot.h deleted file mode 100644 index 69ebba341af239541496a410c66f78eb33e8adac..0000000000000000000000000000000000000000 --- a/benchmarks/dvmp/analysis/plot.h +++ /dev/null @@ -1,40 +0,0 @@ -#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/benchmarks/dvmp/analysis/vm_invar.cxx b/benchmarks/dvmp/analysis/vm_invar.cxx index d6d0804f73b0e18d2a90c2ec478d26168d214f21..03349825902a7e1210251fab0b4d8b467ddfae9d 100644 --- a/benchmarks/dvmp/analysis/vm_invar.cxx +++ b/benchmarks/dvmp/analysis/vm_invar.cxx @@ -1,3 +1,4 @@ +#include "dvmp.h" #include "plot.h" #include <benchmark.h> @@ -18,8 +19,6 @@ // 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. // FIXME: MC does not trace back into particle history. Need to fix that int vm_invar(const std::string& config_name) { @@ -35,17 +34,20 @@ int vm_invar(const std::string& config_name) std::string output_prefix = config["output_prefix"]; const std::string test_tag = config["test_tag"]; - fmt::print(fmt::emphasis::bold | fg(fmt::color::forest_green), "Running VM invariant mass analysis...\n"); + 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(" - input file: {}\n", rec_file); fmt::print(" - output prefix: {}\n", output_prefix); // create our test definition // test_tag - eic::util::Test vm_mass_resolution_test{ - {{"name", fmt::format("{}_{}_{}_mass_resolution", test_tag, vm_name, decay_name)}, - {"title", fmt::format("{} -> {} Invariant Mass Resolution", vm_name, decay_name)}, + eic::util::Test Q2_resolution_test{ + {{"name", fmt::format("{}_{}_{}_Q2_resolution", test_tag, vm_name, decay_name)}, + {"title", + fmt::format("Q^2 Resolution for {} -> {} events with {}", vm_name, decay_name, detector)}, {"description", "Invariant Mass Resolution calculated from raw " "tracking data using a Gaussian fit."}, {"quantity", "resolution"}, @@ -114,7 +116,7 @@ int vm_invar(const std::string& config_name) // draw everything hnu.DrawClone("hist"); // FIXME hardcoded beam configuration - plot::draw_label(10, 100, detector, vm_name, "#nu"); + plot::draw_label(10, 100, detector); TText* tptr21; auto t21 = new TPaveText(.6, .8417, .9, .925, "NB NDC"); t21->SetFillColorAlpha(kWhite, 0); @@ -139,7 +141,7 @@ int vm_invar(const std::string& config_name) // draw everything hQ2.DrawClone("hist"); // FIXME hardcoded beam configuration - plot::draw_label(10, 100, detector, vm_name, "Q^{2}"); + plot::draw_label(10, 100, detector); TText* tptr22; auto t22 = new TPaveText(.6, .8417, .9, .925, "NB NDC"); t22->SetFillColorAlpha(kWhite, 0); @@ -164,7 +166,7 @@ int vm_invar(const std::string& config_name) // draw everything hx.DrawClone("hist"); // FIXME hardcoded beam configuration - plot::draw_label(10, 100, detector, vm_name, "x"); + plot::draw_label(10, 100, detector); TText* tptr23; auto t23 = new TPaveText(.6, .8417, .9, .925, "NB NDC"); t23->SetFillColorAlpha(kWhite, 0); @@ -179,12 +181,12 @@ int vm_invar(const std::string& config_name) c.Print(fmt::format("{}InvariantQuantities.png", output_prefix).c_str()); } - // TODO we're not actually doing an IM fit yet, so for now just return an + // TODO we're not actually getting the resolutions yet // error for the test result - vm_mass_resolution_test.error(-1); + Q2_resolution_test.error(-1); // write out our test data - eic::util::write_test(vm_mass_resolution_test, fmt::format("{}vm_invar.json", output_prefix)); + eic::util::write_test(Q2_resolution_test, fmt::format("{}vm_invar.json", output_prefix)); // That's all! return 0; diff --git a/benchmarks/dvmp/analysis/vm_mass.cxx b/benchmarks/dvmp/analysis/vm_mass.cxx index 6420e1fe1b55d2304d0817dfe6b4b51a7623c1c2..82abba6f77d8343df259e89d823e0b7d14419534 100644 --- a/benchmarks/dvmp/analysis/vm_mass.cxx +++ b/benchmarks/dvmp/analysis/vm_mass.cxx @@ -1,3 +1,4 @@ +#include "dvmp.h" #include "plot.h" #include <benchmark.h> @@ -35,17 +36,20 @@ int vm_mass(const std::string& config_name) std::string output_prefix = config["output_prefix"]; const std::string test_tag = config["test_tag"]; - fmt::print(fmt::emphasis::bold | fg(fmt::color::forest_green), "Running VM invariant mass analysis...\n"); + 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(" - input file: {}\n", rec_file); fmt::print(" - output prefix: {}\n", output_prefix); // create our test definition // test_tag - eic::util::Test vm_mass_resolution_test{ + eic::util::Test mass_resolution_test{ {{"name", fmt::format("{}_{}_{}_mass_resolution", test_tag, vm_name, decay_name)}, - {"title", fmt::format("{} -> {} Invariant Mass Resolution", vm_name, decay_name)}, + {"title", fmt::format("{} Invariant Mass Resolution for {} -> {} with {}", vm_name, vm_name, + decay_name, detector)}, {"description", "Invariant Mass Resolution calculated from raw " "tracking data using a Gaussian fit."}, {"quantity", "resolution"}, @@ -82,18 +86,23 @@ int vm_mass(const std::string& config_name) .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("pt_rec", util::get_pt, {"decay_pair_rec"}) - .Define("pt_sim", util::get_pt, {"decay_pair_sim"}) - .Define("phi_rec", util::get_phi, {"decay_pair_rec"}) - .Define("phi_sim", util::get_phi, {"decay_pair_sim"}) - .Define("rapidity_rec", util::get_y, {"decay_pair_rec"}) - .Define("rapidity_sim", util::get_y, {"decay_pair_sim"}); + .Define("p_vm_rec", "decay_pair_rec.first + decay_pair_rec.second") + .Define("p_vm_sim", "decay_pair_sim.first + decay_pair_sim.second") + //.Define("p_vm_sim", util::get_sum, {"decay_pair_sim"}) + .Define("mass_rec", "p_vm_rec.M()") + .Define("mass_sim", "p_vm_sim.M()") + .Define("pt_rec", "p_vm_rec.pt()") + .Define("pt_sim", "p_vm_sim.pt()") + .Define("phi_rec", "p_vm_rec.phi()") + .Define("phi_sim", "p_vm_sim.phi()") + .Define("eta_rec", "p_vm_rec.eta()") + .Define("eta_sim", "p_vm_sim.eta()"); // Define output histograms - auto h_im_rec = d_im.Histo1D({"h_im_rec", ";m_{ll'} (GeV/c^{2});#", 100, -1.1, vm_mass + 5}, "mass_rec"); - auto h_im_sim = d_im.Histo1D({"h_im_sim", ";m_{ll'} (GeV/c^{2});#", 100, -1.1, vm_mass + 5}, "mass_sim"); + auto h_im_rec = + d_im.Histo1D({"h_im_rec", ";m_{ll'} (GeV/c^{2});#", 100, -1.1, vm_mass + 5}, "mass_rec"); + auto h_im_sim = + d_im.Histo1D({"h_im_sim", ";m_{ll'} (GeV/c^{2});#", 100, -1.1, vm_mass + 5}, "mass_sim"); auto h_pt_rec = d_im.Histo1D({"h_pt_rec", ";p_{T} (GeV/c);#", 400, 0., 40.}, "pt_rec"); auto h_pt_sim = d_im.Histo1D({"h_pt_sim", ";p_{T} (GeV/c);#", 400, 0., 40.}, "pt_sim"); @@ -101,8 +110,8 @@ int vm_mass(const std::string& config_name) auto h_phi_rec = d_im.Histo1D({"h_phi_rec", ";#phi_{ll'};#", 90, -M_PI, M_PI}, "phi_rec"); auto h_phi_sim = d_im.Histo1D({"h_phi_sim", ";#phi_{ll'};#", 90, -M_PI, M_PI}, "phi_sim"); - auto h_y_rec = d_im.Histo1D({"h_y_rec", ";y_{ll'};#", 1000, -5., 5.}, "rapidity_rec"); - auto h_y_sim = d_im.Histo1D({"h_y_sim", ";y_{ll'};#", 1000, -5., 5.}, "rapidity_sim"); + auto h_eta_rec = d_im.Histo1D({"h_eta_rec", ";#eta_{ll'};#", 1000, -5., 5.}, "eta_rec"); + auto h_eta_sim = d_im.Histo1D({"h_eta_sim", ";#eta_{ll'};#", 1000, -5., 5.}, "eta_sim"); // Plot our histograms. // TODO: to start I'm explicitly plotting the histograms, but want to @@ -128,7 +137,7 @@ int vm_mass(const std::string& config_name) h11.DrawClone("hist"); h12.DrawClone("hist same"); // FIXME hardcoded beam configuration - plot::draw_label(10, 100, detector, vm_name, "Invariant mass"); + plot::draw_label(10, 100, detector); TText* tptr1; auto t1 = new TPaveText(.6, .8417, .9, .925, "NB NDC"); t1->SetFillColorAlpha(kWhite, 0); @@ -158,7 +167,7 @@ int vm_mass(const std::string& config_name) h21.DrawClone("hist"); h22.DrawClone("hist same"); // FIXME hardcoded beam configuration - plot::draw_label(10, 100, detector, vm_name, "Transverse Momentum"); + plot::draw_label(10, 100, detector); TText* tptr2; auto t2 = new TPaveText(.6, .8417, .9, .925, "NB NDC"); t2->SetFillColorAlpha(kWhite, 0); @@ -188,7 +197,7 @@ int vm_mass(const std::string& config_name) h31.DrawClone("hist"); h32.DrawClone("hist same"); // FIXME hardcoded beam configuration - plot::draw_label(10, 100, detector, vm_name, "#phi"); + plot::draw_label(10, 100, detector); TText* tptr3; auto t3 = new TPaveText(.6, .8417, .9, .925, "NB NDC"); t3->SetFillColorAlpha(kWhite, 0); @@ -204,8 +213,8 @@ int vm_mass(const std::string& config_name) c.cd(4); // gPad->SetLogx(false); // gPad->SetLogy(false); - auto& h41 = *h_y_sim; - auto& h42 = *h_y_rec; + auto& h41 = *h_eta_sim; + auto& h42 = *h_eta_rec; // histogram style h41.SetLineColor(plot::kMpBlue); h41.SetLineWidth(2); @@ -218,7 +227,7 @@ int vm_mass(const std::string& config_name) h41.DrawClone("hist"); h42.DrawClone("hist same"); // FIXME hardcoded beam configuration - plot::draw_label(10, 100, detector, vm_name, "Rapidity"); + plot::draw_label(10, 100, detector); TText* tptr4; auto t4 = new TPaveText(.6, .8417, .9, .925, "NB NDC"); t4->SetFillColorAlpha(kWhite, 0); @@ -235,10 +244,10 @@ int vm_mass(const std::string& config_name) // TODO we're not actually doing an IM fit yet, so for now just return an // error for the test result - vm_mass_resolution_test.error(-1); + mass_resolution_test.error(-1); // write out our test data - eic::util::write_test(vm_mass_resolution_test, fmt::format("{}vm_mass.json", output_prefix)); + eic::util::write_test(mass_resolution_test, fmt::format("{}_mass.json", output_prefix)); // That's all! return 0; diff --git a/benchmarks/dvmp/dvmp.sh b/benchmarks/dvmp/dvmp.sh index 87a0cef4e895a75fe73f111e2da92726fb12dfde..1746a8600c1a3e20a8968b43db51884d2311e2fc 100755 --- a/benchmarks/dvmp/dvmp.sh +++ b/benchmarks/dvmp/dvmp.sh @@ -35,9 +35,9 @@ source util/parse_cmd.sh $@ ## - 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 +## You can ready options/env.sh for more in-depth explanations of the variables ## and how they can be controlled. -source config/env.sh +source options/env.sh ## We also need the following benchmark-specific variables: ## @@ -93,21 +93,20 @@ export JUGGLER_SIM_FILE=${SIM_FILE} export JUGGLER_REC_FILE=${REC_FILE} export JUGGLER_DETECTOR_PATH=${DETECTOR_PATH} xenv -x ${JUGGLER_INSTALL_PREFIX}/Juggler.xenv \ - gaudirun.py config/tracker_reconstruction.py \ + gaudirun.py options/tracker_reconstruction.py \ 2>&1 > ${REC_LOG} ## on-error, first retry running juggler again as there is still a random ## crash we need to address FIXME if [ "$?" -ne "0" ] ; then echo "Juggler crashed, retrying..." xenv -x ${JUGGLER_INSTALL_PREFIX}/Juggler.xenv \ - gaudirun.py config/tracker_reconstruction.py \ + gaudirun.py options/tracker_reconstruction.py \ 2>&1 > ${REC_LOG} if [ "$?" -ne "0" ] ; then echo "ERROR running juggler, both attempts failed" exit 1 fi fi -#ls -l ## ============================================================================= ## Step 4: Analysis @@ -127,10 +126,14 @@ EOF #cat ${CONFIG} ## run the analysis script with this configuration -root -b -q "benchmarks/dvmp/analysis/vm_mass.cxx(\"${CONFIG}\")" -root -b -q "benchmarks/dvmp/analysis/vm_invar.cxx(\"${CONFIG}\")" +root -b -q "benchmarks/dvmp/analysis/vm_mass.cxx+(\"${CONFIG}\")" if [ "$?" -ne "0" ] ; then - echo "ERROR running root script" + echo "ERROR running vm_mass script" + exit 1 +fi +root -b -q "benchmarks/dvmp/analysis/vm_invar.cxx+(\"${CONFIG}\")" +if [ "$?" -ne "0" ] ; then + echo "ERROR running vm_invar script" exit 1 fi @@ -145,11 +148,11 @@ if [ "${JUGGLER_N_EVENTS}" -lt "500" ] ; then fi ## Always move over log files to the results path -mv ${SIM_LOG} ${REC_LOG} ${RESULTS_PATH} +mv ${REC_LOG} ${RESULTS_PATH} ## cleanup output files -rm -f ${REC_FILE} ${SIM_FILE} +#rm -f ${REC_FILE} ${SIM_FILE} ## --> not needed for CI ## ============================================================================= ## All done! diff --git a/benchmarks/dvmp/gen.sh b/benchmarks/dvmp/gen.sh index cfb64aa079b5a266b89b89a05cc082232e9a48f2..8bef0a116f14014869d134c38720e7717ad5e49d 100755 --- a/benchmarks/dvmp/gen.sh +++ b/benchmarks/dvmp/gen.sh @@ -33,9 +33,9 @@ source util/parse_cmd.sh $@ ## - JUGGLER_N_EVENTS: Number of events to process ## - JUGGLER_RNG_SEED: Random seed for event generation. ## -## You can read config/env.sh for more in-depth explanations of the variables +## You can read options/env.sh for more in-depth explanations of the variables ## and how they can be controlled. -source config/env.sh +source options/env.sh ## We also need the following benchmark-specific variables: ## @@ -107,4 +107,6 @@ done echo "Cleaning up" rm ${TMP_PATH}/${GEN_TAG}.json +## ============================================================================= ## All done! +echo "$BENCHMARK_TAG event generation complete" diff --git a/include/benchmark.h b/include/benchmark.h index 48c9694dc5491bc32fa6b89bba8f672ec4cc3eae..0634be0a1494739c4e7337f99e35ffbac0be7f7f 100644 --- a/include/benchmark.h +++ b/include/benchmark.h @@ -4,6 +4,7 @@ #include "exception.h" #include <fmt/core.h> #include <fstream> +#include <iomanip> #include <iostream> #include <nlohmann/json.hpp> #include <string> @@ -67,14 +68,19 @@ namespace eic::util { // - weight: Weight for this test (this is defaulted to 1.0 if not specified) // - result: pass/fail/error struct Test { - Test(nlohmann::json definition) : json{std::move(definition)} + // note: round braces for the json constructor, as else it will pick the wrong + // initializer-list constructur (it will put everything in an array) + Test(const std::map<std::string, std::string>& definition) : json(definition) { + // std::cout << json.dump() << std::endl; // initialize with error (as we don't have a value yet) error(); // Check that all required fields are present - for (const auto& field : {"name", "title", "description", "quantity", "target", "value", "result"}) { + for (const auto& field : + {"name", "title", "description", "quantity", "target", "value", "result"}) { if (json.find(field) == json.end()) { - throw TestDefinitionError{fmt::format("Error in test definition: field '{}' missing", field)}; + throw TestDefinitionError{ + fmt::format("Error in test definition: field '{}' missing", field)}; } } // Default "weight" to 1 if not set diff --git a/include/plot.h b/include/plot.h new file mode 100644 index 0000000000000000000000000000000000000000..c198616325d54d73ae5e4c08f5ba7113ae77f817 --- /dev/null +++ b/include/plot.h @@ -0,0 +1,42 @@ +#ifndef PLOT_H +#define PLOT_H + +#include <TCanvas.h> +#include <TColor.h> +#include <TPad.h> +#include <TPaveText.h> +#include <TStyle.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) + { + 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("{} GeV on {} GeV", ebeam, pbeam).c_str()); + t->SetTextAlign(12); + t->Draw(); + } + +} // namespace plot + +#endif diff --git a/include/util.h b/include/util.h index dad7048e016d7c5d03cca9eb37481472ea4e5a74..56fb12893787e1d1fff00bd01653ad23b149f48e 100644 --- a/include/util.h +++ b/include/util.h @@ -1,6 +1,8 @@ #ifndef UTIL_H #define UTIL_H +// TODO: should probably be moved to a global benchmark utility library + #include <algorithm> #include <cmath> #include <exception> @@ -16,182 +18,141 @@ 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::PxPyPzMVector{}; - } - const double p = fabs(1. / track.qOverP); - const double px = p * cos(track.phi) * sin(track.theta); - const double py = p * sin(track.phi) * sin(track.theta); - const double pz = p * cos(track.theta); - return ROOT::Math::PxPyPzMVector{px, py, pz, 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; - } + // 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}; } } - if (first < 0) { - return {{}, {}}; + + // Get a vector of 4-momenta from raw tracking info, using an externally + // provided particle mass assumption. + 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::PxPyPzMVector{}; + } + const double p = fabs(1. / track.qOverP); + const double px = p * cos(track.phi) * sin(track.theta); + const double py = p * sin(track.phi) * sin(track.theta); + const double pz = p * cos(track.theta); + return ROOT::Math::PxPyPzMVector{px, py, pz, mass}; + }); + return momenta; } - 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(); -} - -// Calculate the transverse momentum of a given pair of particles -inline double -get_pt(const std::pair<ROOT::Math::PxPyPzMVector, ROOT::Math::PxPyPzMVector>& - particle_pair) { - double px_pair = (particle_pair.first + particle_pair.second).px(); - double py_pair = (particle_pair.first + particle_pair.second).py(); - return sqrt(px_pair*px_pair + py_pair*py_pair); -} - -// Calculate the azimuthal angle of a given pair of particles -inline double -get_phi(const std::pair<ROOT::Math::PxPyPzMVector, ROOT::Math::PxPyPzMVector>& - particle_pair) { - double px_pair = (particle_pair.first + particle_pair.second).px(); - double py_pair = (particle_pair.first + particle_pair.second).py(); - double phi_pair = std::atan2(py_pair,px_pair); - //if(py_pair <= 0.) phi_pair = - phi_pair; - return phi_pair; -} - -// Calculate the rapidity of a given pair of particles -inline double -get_y(const std::pair<ROOT::Math::PxPyPzMVector, ROOT::Math::PxPyPzMVector>& - particle_pair) { - double px_pair = (particle_pair.first + particle_pair.second).px(); - double py_pair = (particle_pair.first + particle_pair.second).py(); - double pz_pair = (particle_pair.first + particle_pair.second).pz(); - double mass_pair = (particle_pair.first + particle_pair.second).mass(); - double energy_pair = sqrt(mass_pair*mass_pair + px_pair*px_pair + py_pair*py_pair + pz_pair*pz_pair); - return 0.5*log((energy_pair + pz_pair)/(energy_pair - pz_pair)); -} - -//======================================================================================================== -//for structure functions - -struct inv_quant{ //add more when needed - double nu, Q2, x; -}; - -//for simu -inline inv_quant calc_inv_quant_simu(const std::vector<ROOT::Math::PxPyPzMVector>& parts){ - ROOT::Math::PxPyPzMVector q(parts[0] - parts[2]); - ROOT::Math::PxPyPzMVector P(parts[3]); - - double nu = q.Dot(P) / P.mass(); - double Q2 = - q.Dot(q); - inv_quant quantities = {nu, Q2, Q2/2./P.mass()/nu}; - return quantities; -} - -inline double get_nu_simu(inv_quant quantities) { - return quantities.nu/1000.; -} -inline double get_Q2_simu(inv_quant quantities) { - return quantities.Q2; -} -inline double get_x_simu(inv_quant quantities) { - return quantities.x; -} - -//for tracking, add later - -//========================================================================================================= + // Get a vector of 4-momenta from the simulation data. + // 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) + 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 (size_t i = 0; i < parts.size(); ++i) { + for (size_t 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 magnitude of the momentum of a vector of 4-vectors + inline auto mom(const std::vector<ROOT::Math::PxPyPzMVector>& momenta) + { + std::vector<double> P(momenta.size()); + // transform our raw tracker info into proper 4-momenta + std::transform(momenta.begin(), momenta.end(), P.begin(), + [](const auto& mom) { return mom.P(); }); + return P; + } + // Calculate the transverse momentum of a vector of 4-vectors + inline auto pt(const std::vector<ROOT::Math::PxPyPzMVector>& momenta) + { + std::vector<double> pt(momenta.size()); + // transform our raw tracker info into proper 4-momenta + std::transform(momenta.begin(), momenta.end(), pt.begin(), + [](const auto& mom) { return mom.pt(); }); + return pt; + } + // Calculate the azimuthal angle phi of a vector of 4-vectors + inline auto phi(const std::vector<ROOT::Math::PxPyPzMVector>& momenta) + { + std::vector<double> phi(momenta.size()); + // transform our raw tracker info into proper 4-momenta + std::transform(momenta.begin(), momenta.end(), phi.begin(), + [](const auto& mom) { return mom.phi(); }); + return phi; + } + // Calculate the pseudo-rapidity of a vector of particles + inline auto eta(const std::vector<ROOT::Math::PxPyPzMVector>& momenta) + { + std::vector<double> eta(momenta.size()); + // transform our raw tracker info into proper 4-momenta + std::transform(momenta.begin(), momenta.end(), eta.begin(), + [](const auto& mom) { return mom.eta(); }); + return eta; + } + //========================================================================================================= } // namespace util diff --git a/config/env.sh b/options/env.sh similarity index 100% rename from config/env.sh rename to options/env.sh diff --git a/config/tracker_reconstruction.py b/options/tracker_reconstruction.py similarity index 100% rename from config/tracker_reconstruction.py rename to options/tracker_reconstruction.py diff --git a/pythia_dis b/pythia_dis new file mode 100755 index 0000000000000000000000000000000000000000..91a83f279189632193eaa87181405aaad17b8378 Binary files /dev/null and b/pythia_dis differ diff --git a/tools/start_dev_shell.sh b/tools/dev-shell similarity index 100% rename from tools/start_dev_shell.sh rename to tools/dev-shell diff --git a/util/build_detector.sh b/util/build_detector.sh index 03641f48151b95011700f80ffd1c0814417b47aa..02313585d56e0ca28af161bc19a7f7b3ae5f53c7 100755 --- a/util/build_detector.sh +++ b/util/build_detector.sh @@ -18,9 +18,9 @@ pushd ${PROJECT_ROOT} ## - DETECTOR_PATH: full path for the detector definitions ## this is the same as ${DETECTOR_PREFIX}/${JUGGLER_DETECTOR} ## -## You can read config/env.sh for more in-depth explanations of the variables +## You can read options/env.sh for more in-depth explanations of the variables ## and how they can be controlled. -source config/env.sh +source options/env.sh ## ============================================================================= ## Step 1: download/update the detector definitions (if needed)