diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 4f8190234dfb414ebcf22cf9b68be15bcad2345c..d522499b09cd15557bea954c55c5d1c56848c87d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -33,9 +33,9 @@ detector:
     - ./util/build_detector.sh
 
 include:
-  - local: 'dis/config.yml'
-  - local: 'dvmp/config.yml'
-  - local: 'dvcs/config.yml'
+  - local: 'benchmarks/dis/config.yml'
+  - local: 'benchmarks/dvmp/config.yml'
+  - local: 'benchmarks/dvcs/config.yml'
 
 summary:
   stage: finish
diff --git a/.rootlogon.C b/.rootlogon.C
index 0be78dd3599f186dcabb06c3c8d0c20177a8f509..808058da0a709843cf4aa848d950cd2bbb64e6f1 100644
--- a/.rootlogon.C
+++ b/.rootlogon.C
@@ -1,6 +1,9 @@
 {
   // Ensure fmt is loaded
   R__LOAD_LIBRARY(libfmt);
+  //
+  // top-level include-dir
+  gROOT->ProcessLine(".include include");
 
   // setup a local build directory so we don't polute our source code with
   // ROOT dictionaries etc.
@@ -37,7 +40,7 @@
 
   // use large fonts
   // Int_t font=72; // Helvetica italics
-  Int_t font = 43; // Helvetica
+  Int_t    font  = 43; // Helvetica
   Double_t tsize = 26;
   s->SetTextFont(font);
 
diff --git a/benchmarks.json b/benchmarks/benchmarks.json
similarity index 100%
rename from benchmarks.json
rename to benchmarks/benchmarks.json
diff --git a/dis/.gitignore b/benchmarks/dis/.gitignore
similarity index 100%
rename from dis/.gitignore
rename to benchmarks/dis/.gitignore
diff --git a/dis/README.md b/benchmarks/dis/README.md
similarity index 100%
rename from dis/README.md
rename to benchmarks/dis/README.md
diff --git a/dis/analysis/rec_dis_electrons.cxx b/benchmarks/dis/analysis/rec_dis_electrons.cxx
similarity index 100%
rename from dis/analysis/rec_dis_electrons.cxx
rename to benchmarks/dis/analysis/rec_dis_electrons.cxx
diff --git a/dis/benchmark.json b/benchmarks/dis/benchmark.json
similarity index 100%
rename from dis/benchmark.json
rename to benchmarks/dis/benchmark.json
diff --git a/dis/config.yml b/benchmarks/dis/config.yml
similarity index 100%
rename from dis/config.yml
rename to benchmarks/dis/config.yml
diff --git a/dis/dis.sh b/benchmarks/dis/dis.sh
similarity index 95%
rename from dis/dis.sh
rename to benchmarks/dis/dis.sh
index 7c68e130ef94d42141529c1140bd0006f2eecd21..a863c7c5c66bcbccc6c97c4c99fd615e2bb95e1a 100644
--- a/dis/dis.sh
+++ b/benchmarks/dis/dis.sh
@@ -67,7 +67,7 @@ fi
 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
+  gaudirun.py config/tracker_reconstruction.py
 if [ "$?" -ne "0" ] ; then
   echo "ERROR running juggler"
   exit 1
@@ -76,7 +76,7 @@ ls -l
 
 ## =============================================================================
 ## Step 4: Analysis
-root -b -q "dis/analysis/rec_dis_electrons.cxx(\"${JUGGLER_DETECTOR}/${JUGGLER_REC_FILE}\")"
+root -b -q "benchmarks/dis/analysis/rec_dis_electrons.cxx(\"${JUGGLER_DETECTOR}/${JUGGLER_REC_FILE}\")"
 if [[ "$?" -ne "0" ]] ; then
   echo "ERROR running root script"
   exit 1
diff --git a/dis/gen.sh b/benchmarks/dis/gen.sh
similarity index 94%
rename from dis/gen.sh
rename to benchmarks/dis/gen.sh
index e56e4edd49380efd16dc897b1589c6a56bac9909..d34947aa81117e225ef894eaa768552ea2767a31 100644
--- a/dis/gen.sh
+++ b/benchmarks/dis/gen.sh
@@ -35,8 +35,8 @@ JUGGLER_FILE_NAME_TAG="dis"
 ## =============================================================================
 ## Step 1: Dummy event generator
 ## TODO better file name that encodes the actual configuration we're running
-echo "Compiling   dis/src/pythia_dis.cc ..."
-g++ dis/src/pythia_dis.cc -o pythia_dis  \
+echo "Compiling   benchmarks/dis/generator/pythia_dis.cc ..."
+g++ benchmarks/dis/generator/pythia_dis.cc -o pythia_dis  \
    -I/usr/local/include  -Iinclude \
    -O2 -std=c++11 -pedantic -W -Wall -Wshadow -fPIC  \
    -L/usr/local/lib -Wl,-rpath,/usr/local/lib -lpythia8 -ldl \
diff --git a/dis/generator/gen_central_electrons.cxx b/benchmarks/dis/generator/gen_central_electrons.cxx
similarity index 100%
rename from dis/generator/gen_central_electrons.cxx
rename to benchmarks/dis/generator/gen_central_electrons.cxx
diff --git a/dis/src/pythia_dis.cc b/benchmarks/dis/generator/pythia_dis.cc
similarity index 100%
rename from dis/src/pythia_dis.cc
rename to benchmarks/dis/generator/pythia_dis.cc
diff --git a/dvcs/config.yml b/benchmarks/dvcs/config.yml
similarity index 86%
rename from dvcs/config.yml
rename to benchmarks/dvcs/config.yml
index 0f63429f92eaf871c30b8bb9189e9a058cef87ee..043724aa3d9650eda4a5e7c4ba91b985a3fc5bdc 100644
--- a/dvcs/config.yml
+++ b/benchmarks/dvcs/config.yml
@@ -18,5 +18,3 @@ dvcs:results:
     paths:
       - results
         #  reports:
-        #    junit: ["results/dvcs/dvcs_report.xml"]
-
diff --git a/dvcs/dvcs.sh b/benchmarks/dvcs/dvcs.sh
similarity index 92%
rename from dvcs/dvcs.sh
rename to benchmarks/dvcs/dvcs.sh
index 436406f26997f0f265f342f5222b00c1486493f1..8d14dd0bf7ee15219a92abff66da17c7542fa8e9 100644
--- a/dvcs/dvcs.sh
+++ b/benchmarks/dvcs/dvcs.sh
@@ -51,7 +51,7 @@ fi
 
 mkdir -p results/dvcs
 
-root -b -q "dvcs/scripts/dvcs_tests.cxx(\"${JUGGLER_REC_FILE}\")"
+root -b -q "benchmarks/dvcs/scripts/dvcs_tests.cxx(\"${JUGGLER_REC_FILE}\")"
 if [[ "$?" -ne "0" ]] ; then
   echo "ERROR running root script"
   exit 1
@@ -62,10 +62,6 @@ if [[ "${JUGGLER_N_EVENTS}" -lt "500" ]] ; then
 cp ${JUGGLER_REC_FILE} results/dvcs/.
 fi
 
-# Collect the results
-#cp dvcs/report.xml results/dvcs/.
-#cp dvcs/report2.xml results/dvcs/.
-
 
 
 
diff --git a/dvcs/scripts/dvcs_tests.cxx b/benchmarks/dvcs/scripts/dvcs_tests.cxx
similarity index 100%
rename from dvcs/scripts/dvcs_tests.cxx
rename to benchmarks/dvcs/scripts/dvcs_tests.cxx
diff --git a/dvcs/scripts/merge_results.py b/benchmarks/dvcs/scripts/merge_results.py
similarity index 100%
rename from dvcs/scripts/merge_results.py
rename to benchmarks/dvcs/scripts/merge_results.py
diff --git a/dvmp/analysis/plot.h b/benchmarks/dvmp/analysis/plot.h
similarity index 100%
rename from dvmp/analysis/plot.h
rename to benchmarks/dvmp/analysis/plot.h
diff --git a/dvmp/analysis/vm_invar.cxx b/benchmarks/dvmp/analysis/vm_invar.cxx
similarity index 60%
rename from dvmp/analysis/vm_invar.cxx
rename to benchmarks/dvmp/analysis/vm_invar.cxx
index f8723733fc38bc811fc14cdae22498d3debf68b2..d6d0804f73b0e18d2a90c2ec478d26168d214f21 100644
--- a/dvmp/analysis/vm_invar.cxx
+++ b/benchmarks/dvmp/analysis/vm_invar.cxx
@@ -1,7 +1,8 @@
-#include "benchmark.hh"
-#include "mt.h"
 #include "plot.h"
-#include "util.h"
+
+#include <benchmark.h>
+#include <mt.h>
+#include <util.h>
 
 #include <ROOT/RDataFrame.hxx>
 #include <cmath>
@@ -20,21 +21,21 @@
 // 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) {
+int vm_invar(const std::string& config_name)
+{
   // read our configuration
-  std::ifstream config_file{config_name};
+  std::ifstream  config_file{config_name};
   nlohmann::json config;
   config_file >> config;
 
-  const std::string rec_file = config["rec_file"];
-  const std::string vm_name = config["vm_name"];
-  const std::string decay_name = config["decay"];
-  const std::string detector = config["detector"];
-  std::string output_prefix = config["output_prefix"];
-  const std::string test_tag = config["test_tag"];
+  const std::string rec_file      = config["rec_file"];
+  const std::string vm_name       = config["vm_name"];
+  const std::string decay_name    = config["decay"];
+  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 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);
@@ -43,10 +44,8 @@ int vm_invar(const std::string& config_name) {
   // 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)},
+      {{"name", fmt::format("{}_{}_{}_mass_resolution", test_tag, vm_name, decay_name)},
+       {"title", fmt::format("{} -> {} Invariant Mass Resolution", vm_name, decay_name)},
        {"description", "Invariant Mass Resolution calculated from raw "
                        "tracking data using a Gaussian fit."},
        {"quantity", "resolution"},
@@ -56,12 +55,11 @@ int vm_invar(const std::string& config_name) {
   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 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() != '-') {
+  if (output_prefix.back() != '.' && output_prefix.back() != '/' && output_prefix.back() != '-') {
     output_prefix += "-";
   }
 
@@ -70,74 +68,68 @@ int vm_invar(const std::string& config_name) {
 
   // 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 momenta_from_tracking = [decay_mass](const std::vector<eic::TrackParametersData>& tracks) {
+    return util::momenta_from_tracking(tracks, decay_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("invariant_quantities", util::calc_inv_quant_simu, {"p_sim"})
-          .Define("nu_sim" , util::get_nu_simu, {"invariant_quantities"})
-          .Define("Q2_sim" , util::get_Q2_simu, {"invariant_quantities"})
-          .Define("x_sim" ,  util::get_x_simu, {"invariant_quantities"});
-          //================================================================
+  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("invariant_quantities", util::calc_inv_quant_simu, {"p_sim"})
+                  .Define("nu_sim", util::get_nu_simu, {"invariant_quantities"})
+                  .Define("Q2_sim", util::get_Q2_simu, {"invariant_quantities"})
+                  .Define("x_sim", util::get_x_simu, {"invariant_quantities"});
+  //================================================================
 
   // Define output histograms
-  
-  auto h_nu_sim = d_im.Histo1D(
-      {"h_nu_sim", ";#nu/1000;#", 100, 0., 2.}, "nu_sim");
-  auto h_Q2_sim = d_im.Histo1D(
-      {"h_Q2_sim", ";Q^{2};#", 100, 0., 15.}, "Q2_sim");
-  auto h_x_sim = d_im.Histo1D(
-      {"h_x_sim", ";x;#", 100, 0., 0.1}, "x_sim");
 
+  auto h_nu_sim = d_im.Histo1D({"h_nu_sim", ";#nu/1000;#", 100, 0., 2.}, "nu_sim");
+  auto h_Q2_sim = d_im.Histo1D({"h_Q2_sim", ";Q^{2};#", 100, 0., 15.}, "Q2_sim");
+  auto h_x_sim  = d_im.Histo1D({"h_x_sim", ";x;#", 100, 0., 0.1}, "x_sim");
 
   // Plot our histograms.
   // TODO: to start I'm explicitly plotting the histograms, but want to
   // factorize out the plotting code moving forward.
   {
-    
+
     // Print canvas to output file
-    
+
     TCanvas c{"canvas2", "canvas2", 1800, 600};
     c.Divide(3, 1, 0.0001, 0.0001);
-    //pad 1 nu
+    // pad 1 nu
     c.cd(1);
-    //gPad->SetLogx(false);
-    //gPad->SetLogy(false);
+    // gPad->SetLogx(false);
+    // gPad->SetLogy(false);
     auto& hnu = *h_nu_sim;
     // histogram style
     hnu.SetLineColor(plot::kMpBlue);
     hnu.SetLineWidth(2);
     // axes
     hnu.GetXaxis()->CenterTitle();
-    //hnu.GetXaxis()->SetTitle("#times1000");
+    // hnu.GetXaxis()->SetTitle("#times1000");
     // draw everything
     hnu.DrawClone("hist");
     // FIXME hardcoded beam configuration
     plot::draw_label(10, 100, detector, vm_name, "#nu");
     TText* tptr21;
-    auto t21 = new TPaveText(.6, .8417, .9, .925, "NB NDC");
+    auto   t21 = new TPaveText(.6, .8417, .9, .925, "NB NDC");
     t21->SetFillColorAlpha(kWhite, 0);
     t21->SetTextFont(43);
     t21->SetTextSize(25);
     tptr21 = t21->AddText("simulated");
     tptr21->SetTextColor(plot::kMpBlue);
-    //tptr1 = t1->AddText("reconstructed");
-    //tptr1->SetTextColor(plot::kMpOrange);
+    // tptr1 = t1->AddText("reconstructed");
+    // tptr1->SetTextColor(plot::kMpOrange);
     t21->Draw();
-    
-    //pad 2 Q2
+
+    // pad 2 Q2
     c.cd(2);
-    //gPad->SetLogx(false);
-    //gPad->SetLogy(false);
+    // gPad->SetLogx(false);
+    // gPad->SetLogy(false);
     auto& hQ2 = *h_Q2_sim;
     // histogram style
     hQ2.SetLineColor(plot::kMpBlue);
@@ -149,20 +141,20 @@ int vm_invar(const std::string& config_name) {
     // FIXME hardcoded beam configuration
     plot::draw_label(10, 100, detector, vm_name, "Q^{2}");
     TText* tptr22;
-    auto t22 = new TPaveText(.6, .8417, .9, .925, "NB NDC");
+    auto   t22 = new TPaveText(.6, .8417, .9, .925, "NB NDC");
     t22->SetFillColorAlpha(kWhite, 0);
     t22->SetTextFont(43);
     t22->SetTextSize(25);
     tptr22 = t22->AddText("simulated");
     tptr22->SetTextColor(plot::kMpBlue);
-    //tptr1 = t1->AddText("reconstructed");
-    //tptr1->SetTextColor(plot::kMpOrange);
+    // tptr1 = t1->AddText("reconstructed");
+    // tptr1->SetTextColor(plot::kMpOrange);
     t22->Draw();
-    
-    //pad 1 nu
+
+    // pad 1 nu
     c.cd(3);
-    //gPad->SetLogx(false);
-    //gPad->SetLogy(false);
+    // gPad->SetLogx(false);
+    // gPad->SetLogy(false);
     auto& hx = *h_x_sim;
     // histogram style
     hx.SetLineColor(plot::kMpBlue);
@@ -174,16 +166,16 @@ int vm_invar(const std::string& config_name) {
     // FIXME hardcoded beam configuration
     plot::draw_label(10, 100, detector, vm_name, "x");
     TText* tptr23;
-    auto t23 = new TPaveText(.6, .8417, .9, .925, "NB NDC");
+    auto   t23 = new TPaveText(.6, .8417, .9, .925, "NB NDC");
     t23->SetFillColorAlpha(kWhite, 0);
     t23->SetTextFont(43);
     t23->SetTextSize(25);
     tptr23 = t23->AddText("simulated");
     tptr23->SetTextColor(plot::kMpBlue);
-    //tptr1 = t1->AddText("reconstructed");
-    //tptr1->SetTextColor(plot::kMpOrange);
+    // tptr1 = t1->AddText("reconstructed");
+    // tptr1->SetTextColor(plot::kMpOrange);
     t23->Draw();
-    
+
     c.Print(fmt::format("{}InvariantQuantities.png", output_prefix).c_str());
   }
 
@@ -192,8 +184,7 @@ int vm_invar(const std::string& config_name) {
   vm_mass_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(vm_mass_resolution_test, fmt::format("{}vm_invar.json", output_prefix));
 
   // That's all!
   return 0;
diff --git a/dvmp/analysis/vm_mass.cxx b/benchmarks/dvmp/analysis/vm_mass.cxx
similarity index 61%
rename from dvmp/analysis/vm_mass.cxx
rename to benchmarks/dvmp/analysis/vm_mass.cxx
index fb736909c0e957805f8a3bec3ef91358d4f54b76..6420e1fe1b55d2304d0817dfe6b4b51a7623c1c2 100644
--- a/dvmp/analysis/vm_mass.cxx
+++ b/benchmarks/dvmp/analysis/vm_mass.cxx
@@ -1,7 +1,8 @@
-#include "benchmark.hh"
-#include "mt.h"
 #include "plot.h"
-#include "util.h"
+
+#include <benchmark.h>
+#include <mt.h>
+#include <util.h>
 
 #include <ROOT/RDataFrame.hxx>
 #include <cmath>
@@ -20,21 +21,21 @@
 // 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_mass(const std::string& config_name) {
+int vm_mass(const std::string& config_name)
+{
   // read our configuration
-  std::ifstream config_file{config_name};
+  std::ifstream  config_file{config_name};
   nlohmann::json config;
   config_file >> config;
 
-  const std::string rec_file = config["rec_file"];
-  const std::string vm_name = config["vm_name"];
-  const std::string decay_name = config["decay"];
-  const std::string detector = config["detector"];
-  std::string output_prefix = config["output_prefix"];
-  const std::string test_tag = config["test_tag"];
+  const std::string rec_file      = config["rec_file"];
+  const std::string vm_name       = config["vm_name"];
+  const std::string decay_name    = config["decay"];
+  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 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);
@@ -43,10 +44,8 @@ int vm_mass(const std::string& config_name) {
   // 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)},
+      {{"name", fmt::format("{}_{}_{}_mass_resolution", test_tag, vm_name, decay_name)},
+       {"title", fmt::format("{} -> {} Invariant Mass Resolution", vm_name, decay_name)},
        {"description", "Invariant Mass Resolution calculated from raw "
                        "tracking data using a Gaussian fit."},
        {"quantity", "resolution"},
@@ -56,12 +55,11 @@ int vm_mass(const std::string& config_name) {
   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 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() != '-') {
+  if (output_prefix.back() != '.' && output_prefix.back() != '/' && output_prefix.back() != '-') {
     output_prefix += "-";
   }
 
@@ -70,56 +68,41 @@ int vm_mass(const std::string& config_name) {
 
   // 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);
-      };
-  
-      
-    //util::PrintGeant4(mcparticles2);
+  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);
+  };
+
+  // util::PrintGeant4(mcparticles2);
   // 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("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"});
-          
+  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("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 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_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"); 
-      
-  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_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");
 
+  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");
 
   // Plot our histograms.
   // TODO: to start I'm explicitly plotting the histograms, but want to
@@ -127,10 +110,10 @@ int vm_mass(const std::string& config_name) {
   {
     TCanvas c{"canvas", "canvas", 1200, 1200};
     c.Divide(2, 2, 0.0001, 0.0001);
-    //pad 1 mass
+    // pad 1 mass
     c.cd(1);
-    //gPad->SetLogx(false);
-    //gPad->SetLogy(false);
+    // gPad->SetLogx(false);
+    // gPad->SetLogy(false);
     auto& h11 = *h_im_sim;
     auto& h12 = *h_im_rec;
     // histogram style
@@ -147,7 +130,7 @@ int vm_mass(const std::string& config_name) {
     // FIXME hardcoded beam configuration
     plot::draw_label(10, 100, detector, vm_name, "Invariant mass");
     TText* tptr1;
-    auto t1 = new TPaveText(.6, .8417, .9, .925, "NB NDC");
+    auto   t1 = new TPaveText(.6, .8417, .9, .925, "NB NDC");
     t1->SetFillColorAlpha(kWhite, 0);
     t1->SetTextFont(43);
     t1->SetTextSize(25);
@@ -156,11 +139,11 @@ int vm_mass(const std::string& config_name) {
     tptr1 = t1->AddText("reconstructed");
     tptr1->SetTextColor(plot::kMpOrange);
     t1->Draw();
-    
-    //pad 2 pt
+
+    // pad 2 pt
     c.cd(2);
-    //gPad->SetLogx(false);
-    //gPad->SetLogy(false);
+    // gPad->SetLogx(false);
+    // gPad->SetLogy(false);
     auto& h21 = *h_pt_sim;
     auto& h22 = *h_pt_rec;
     // histogram style
@@ -177,7 +160,7 @@ int vm_mass(const std::string& config_name) {
     // FIXME hardcoded beam configuration
     plot::draw_label(10, 100, detector, vm_name, "Transverse Momentum");
     TText* tptr2;
-    auto t2 = new TPaveText(.6, .8417, .9, .925, "NB NDC");
+    auto   t2 = new TPaveText(.6, .8417, .9, .925, "NB NDC");
     t2->SetFillColorAlpha(kWhite, 0);
     t2->SetTextFont(43);
     t2->SetTextSize(25);
@@ -186,11 +169,11 @@ int vm_mass(const std::string& config_name) {
     tptr2 = t2->AddText("reconstructed");
     tptr2->SetTextColor(plot::kMpOrange);
     t2->Draw();
-    
-    //pad 3 phi
+
+    // pad 3 phi
     c.cd(3);
-    //gPad->SetLogx(false);
-    //gPad->SetLogy(false);
+    // gPad->SetLogx(false);
+    // gPad->SetLogy(false);
     auto& h31 = *h_phi_sim;
     auto& h32 = *h_phi_rec;
     // histogram style
@@ -207,7 +190,7 @@ int vm_mass(const std::string& config_name) {
     // FIXME hardcoded beam configuration
     plot::draw_label(10, 100, detector, vm_name, "#phi");
     TText* tptr3;
-    auto t3 = new TPaveText(.6, .8417, .9, .925, "NB NDC");
+    auto   t3 = new TPaveText(.6, .8417, .9, .925, "NB NDC");
     t3->SetFillColorAlpha(kWhite, 0);
     t3->SetTextFont(43);
     t3->SetTextSize(25);
@@ -216,11 +199,11 @@ int vm_mass(const std::string& config_name) {
     tptr3 = t3->AddText("reconstructed");
     tptr3->SetTextColor(plot::kMpOrange);
     t3->Draw();
-    
-    //pad 4 rapidity
+
+    // pad 4 rapidity
     c.cd(4);
-    //gPad->SetLogx(false);
-    //gPad->SetLogy(false);
+    // gPad->SetLogx(false);
+    // gPad->SetLogy(false);
     auto& h41 = *h_y_sim;
     auto& h42 = *h_y_rec;
     // histogram style
@@ -237,7 +220,7 @@ int vm_mass(const std::string& config_name) {
     // FIXME hardcoded beam configuration
     plot::draw_label(10, 100, detector, vm_name, "Rapidity");
     TText* tptr4;
-    auto t4 = new TPaveText(.6, .8417, .9, .925, "NB NDC");
+    auto   t4 = new TPaveText(.6, .8417, .9, .925, "NB NDC");
     t4->SetFillColorAlpha(kWhite, 0);
     t4->SetTextFont(43);
     t4->SetTextSize(25);
@@ -248,7 +231,6 @@ int vm_mass(const std::string& config_name) {
     t4->Draw();
 
     c.Print(fmt::format("{}vm_mass_pt_phi_rapidity.png", output_prefix).c_str());
-
   }
 
   // TODO we're not actually doing an IM fit yet, so for now just return an
@@ -256,8 +238,7 @@ int vm_mass(const std::string& config_name) {
   vm_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(vm_mass_resolution_test, fmt::format("{}vm_mass.json", output_prefix));
 
   // That's all!
   return 0;
diff --git a/dvmp/benchmark.json b/benchmarks/dvmp/benchmark.json
similarity index 100%
rename from dvmp/benchmark.json
rename to benchmarks/dvmp/benchmark.json
diff --git a/dvmp/config.yml b/benchmarks/dvmp/config.yml
similarity index 79%
rename from dvmp/config.yml
rename to benchmarks/dvmp/config.yml
index 5a17fddd9ab77ae7225b22f4299ab49cdf404ab5..b9e831fbe6a05ca40b88151ee9f8bb85e4384b11 100644
--- a/dvmp/config.yml
+++ b/benchmarks/dvmp/config.yml
@@ -6,8 +6,8 @@ dvmp:generate:
   cache:
     key:
       files:
-        - dvmp/generator/jpsi_central.json
-        - dvmp/scripts/jpsi_central-generate.sh
+        - benchmarks/dvmp/generator/jpsi_central.json
+        - benchmarks/dvmp/scripts/jpsi_central-generate.sh
       prefix: "$CI_COMMIT_REF_SLUG"
     paths:
       - input/dvmp
@@ -15,7 +15,7 @@ dvmp:generate:
     paths:
       - input
   script:
-    - ./util/run_many.py ./dvmp/gen.sh 
+    - ./util/run_many.py ./benchmarks/dvmp/gen.sh 
           -c jpsi_barrel 
           -e 10x100 
           --decay muon --decay electron
@@ -26,7 +26,7 @@ dvmp:process:
   needs: ["detector", "dvmp:generate"]
   timeout: 1 hour
   script:
-    - ./util/run_many.py ./dvmp/dvmp.sh 
+    - ./util/run_many.py ./benchmarks/dvmp/dvmp.sh 
           -c jpsi_barrel 
           -e 10x100 
           --decay muon --decay electron
diff --git a/dvmp/dvmp.sh b/benchmarks/dvmp/dvmp.sh
similarity index 95%
rename from dvmp/dvmp.sh
rename to benchmarks/dvmp/dvmp.sh
index 0d263a96ef1e2c61d5fe8506bedc44f22de13f4f..f1f0c554bdd585960c9e861237e9622340cceb06 100755
--- a/dvmp/dvmp.sh
+++ b/benchmarks/dvmp/dvmp.sh
@@ -93,14 +93,14 @@ 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 options/tracker_reconstruction.py \
+  gaudirun.py config/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 options/tracker_reconstruction.py \
+    gaudirun.py config/tracker_reconstruction.py \
     2>&1 > ${REC_LOG}
   if [ "$?" -ne "0" ] ; then
     echo "ERROR running juggler, both attempts failed"
@@ -127,8 +127,8 @@ EOF
 #cat ${CONFIG}
 
 ## run the analysis script with this configuration
-root -b -q "dvmp/analysis/vm_mass.cxx(\"${CONFIG}\")"
-root -b -q "dvmp/analysis/vm_invar.cxx(\"${CONFIG}\")"
+root -b -q "benchmarks/dvmp/analysis/vm_mass.cxx(\"${CONFIG}\")"
+root -b -q "benchmarks/dvmp/analysis/vm_invar.cxx(\"${CONFIG}\")"
 if [ "$?" -ne "0" ] ; then
   echo "ERROR running root script"
   exit 1
diff --git a/dvmp/env.sh b/benchmarks/dvmp/env.sh
similarity index 100%
rename from dvmp/env.sh
rename to benchmarks/dvmp/env.sh
diff --git a/dvmp/gen.sh b/benchmarks/dvmp/gen.sh
similarity index 100%
rename from dvmp/gen.sh
rename to benchmarks/dvmp/gen.sh
diff --git a/dvmp/generator/jpsi_barrel.json.in b/benchmarks/dvmp/generator/jpsi_barrel.json.in
similarity index 100%
rename from dvmp/generator/jpsi_barrel.json.in
rename to benchmarks/dvmp/generator/jpsi_barrel.json.in
diff --git a/dvmp/generator/jpsi_central.json.in b/benchmarks/dvmp/generator/jpsi_central.json.in
similarity index 100%
rename from dvmp/generator/jpsi_central.json.in
rename to benchmarks/dvmp/generator/jpsi_central.json.in
diff --git a/options/tracker_reconstruction.py b/config/tracker_reconstruction.py
similarity index 100%
rename from options/tracker_reconstruction.py
rename to config/tracker_reconstruction.py
diff --git a/dis/generator/placeholder b/dis/generator/placeholder
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/dis/util/placeholder b/dis/util/placeholder
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/dvcs/tests/report.xml b/dvcs/tests/report.xml
deleted file mode 100644
index 2ba9c6e484d8b0ce7c0c7164759f22f0d7d82d60..0000000000000000000000000000000000000000
--- a/dvcs/tests/report.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<testsuites>
-  <testsuite name="DVCS" tests="3" failures="1">
-    <testcase classname="foo1" name="ASuccessfulTest" time="10.0"/>
-    <testcase classname="foo2" name="AnotherSuccessfulTest"  time="20.0"/>
-    <testcase classname="foo3" name="AFailingTest"  time="30.0">
-      <failure type="NotEnoughFoo"> details about failure </failure>
-    </testcase>
-  </testsuite>
-</testsuites>
-
diff --git a/dvcs/tests/report2.xml b/dvcs/tests/report2.xml
deleted file mode 100644
index f437ef44d7b01cb33b69259fead289a20fc82072..0000000000000000000000000000000000000000
--- a/dvcs/tests/report2.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<testsuite tests="3">
-    <testcase time="1.00" classname="bar1" name="ASuccessfulTest2"/>
-    <testcase time="2.00" classname="bar2" name="AnotherSuccessfulTest2">
-      <!--
-      <system-out>[[ATTACHMENT|/absolute/path/to/some/file]]</system-out>
-      -->
-    </testcase>
-    <testcase time="3.00" classname="bar3" name="AFailingTest2">
-        <failure type="NotEnoughFoo"> MORE details about failure </failure>
-    </testcase>
-</testsuite>
-
diff --git a/dvmp/.gitignore b/dvmp/.gitignore
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/dvmp/analysis/benchmark.hh b/dvmp/analysis/benchmark.hh
deleted file mode 120000
index a96772424fe616aa97770a31a305b3ae74f9911b..0000000000000000000000000000000000000000
--- a/dvmp/analysis/benchmark.hh
+++ /dev/null
@@ -1 +0,0 @@
-../../util/benchmark.hh
\ No newline at end of file
diff --git a/dvmp/analysis/exception.hh b/dvmp/analysis/exception.hh
deleted file mode 120000
index e50e23b6af1bfcf85181374407bd3e80d9d65696..0000000000000000000000000000000000000000
--- a/dvmp/analysis/exception.hh
+++ /dev/null
@@ -1 +0,0 @@
-../../util/exception.hh
\ No newline at end of file
diff --git a/include/benchmark.h b/include/benchmark.h
new file mode 100644
index 0000000000000000000000000000000000000000..48c9694dc5491bc32fa6b89bba8f672ec4cc3eae
--- /dev/null
+++ b/include/benchmark.h
@@ -0,0 +1,118 @@
+#ifndef BENCHMARK_H
+#define BENCHMARK_H
+
+#include "exception.h"
+#include <fmt/core.h>
+#include <fstream>
+#include <iostream>
+#include <nlohmann/json.hpp>
+#include <string>
+
+// Bookkeeping of test data to store data of one or more tests in a json file to
+// facilitate future accounting.
+//
+// Usage Example 1 (single test):
+// ==============================
+// 1. define our test
+//      eic::util::Test test1{
+//        {{"name", "example_test"},
+//        {"title", "Example Test"},
+//        {"description", "This is an example of a test definition"},
+//        {"quantity", "efficiency"},
+//        {"target", "1"}}};
+// 2. set pass/fail/error status and return value (in this case .99)
+//      test1.pass(0.99)
+// 3. write our test data to a json file
+//      eic::util::write_test(test1, "test1.json");
+//
+// Usage Example 2 (multiple tests):
+// =================================
+// 1. define our tests
+//      eic::util::Test test1{
+//        {{"name", "example_test"},
+//        {"title", "Example Test"},
+//        {"description", "This is an example of a test definition"},
+//        {"quantity", "efficiency"},
+//        {"target", "1"}}};
+//      eic::util::Test test2{
+//        {{"name", "another_test"},
+//        {"title", "Another example Test"},
+//        {"description", "This is a second example of a test definition"},
+//        {"quantity", "resolution"},
+//        {"target", "3."}}};
+// 2. set pass/fail/error status and return value (in this case .99)
+//      test1.fail(10)
+// 3. write our test data to a json file
+//      eic::util::write_test({test1, test2}, "test.json");
+
+// Namespace for utility scripts, FIXME this should be part of an independent
+// library
+namespace eic::util {
+
+  struct TestDefinitionError : Exception {
+    TestDefinitionError(std::string_view msg) : Exception(msg, "test_definition_error") {}
+  };
+
+  // Wrapper for our test data json, with three methods to set the status
+  // after test completion (to pass, fail or error). The default value
+  // is error.
+  // The following fields should be defined in the definitions json
+  // for the test to make sense:
+  //  - name: unique identifier for this test
+  //  - title: Slightly more verbose identifier for this test
+  //  - description: Concise description of what is tested
+  //  - quantity: What quantity is tested? Unites of value/target
+  //  - target: Target value of <quantity> that we want to reach
+  //  - value: Actual value of <quantity>
+  //  - 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)}
+    {
+      // 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"}) {
+        if (json.find(field) == json.end()) {
+          throw TestDefinitionError{fmt::format("Error in test definition: field '{}' missing", field)};
+        }
+      }
+      // Default "weight" to 1 if not set
+      if (json.find("weight") == json.end()) {
+        json["weight"] = 1.0;
+      }
+    }
+    // Set this test to pass/fail/error
+    void pass(double value) { update_result("pass", value); }
+    void fail(double value) { update_result("fail", value); }
+    void error(double value = 0) { update_result("error", value); }
+
+    nlohmann::json json;
+
+  private:
+    void update_result(std::string_view status, double value)
+    {
+      json["result"] = status;
+      json["value"]  = value;
+    }
+  };
+
+  void write_test(const std::vector<Test>& data, const std::string& fname)
+  {
+    nlohmann::json test;
+    for (auto& entry : data) {
+      test["tests"].push_back(entry.json);
+    }
+    std::cout << fmt::format("Writing test data to {}\n", fname);
+    std::ofstream output_file(fname);
+    output_file << std::setw(4) << test << "\n";
+  }
+  void write_test(const Test& data, const std::string& fname)
+  {
+    std::vector<Test> vtd{data};
+    write_test(vtd, fname);
+  }
+
+} // namespace eic::util
+
+#endif
diff --git a/include/exception.h b/include/exception.h
new file mode 100644
index 0000000000000000000000000000000000000000..b630bd008a0d8855211eca5ee0af60650aec1314
--- /dev/null
+++ b/include/exception.h
@@ -0,0 +1,22 @@
+#ifndef UTIL_EXCEPTION_H
+#define UTIL_EXCEPTION_H
+
+#include <exception>
+#include <string>
+
+namespace eic::util {
+  class Exception : public std::exception {
+  public:
+    Exception(std::string_view msg, std::string_view type = "exception") : msg_{msg}, type_{type} {}
+
+    virtual const char* what() const throw() { return msg_.c_str(); }
+    virtual const char* type() const throw() { return type_.c_str(); }
+    virtual ~Exception() throw() {}
+
+  private:
+    std::string msg_;
+    std::string type_;
+  };
+} // namespace eic::util
+
+#endif
diff --git a/dvmp/analysis/mt.h b/include/mt.h
similarity index 100%
rename from dvmp/analysis/mt.h
rename to include/mt.h
diff --git a/dvmp/analysis/util.h b/include/util.h
similarity index 100%
rename from dvmp/analysis/util.h
rename to include/util.h
diff --git a/util/benchmark.hh b/util/benchmark.hh
deleted file mode 100644
index f8abec063205d43beba254d1af3a919c387d2acd..0000000000000000000000000000000000000000
--- a/util/benchmark.hh
+++ /dev/null
@@ -1,117 +0,0 @@
-#ifndef BENCHMARK_LOADED
-#define BENCHMARK_LOADED
-
-#include "exception.hh"
-#include <fmt/core.h>
-#include <fstream>
-#include <iostream>
-#include <nlohmann/json.hpp>
-#include <string>
-
-// Bookkeeping of test data to store data of one or more tests in a json file to
-// facilitate future accounting.
-//
-// Usage Example 1 (single test):
-// ==============================
-// 1. define our test
-//      eic::util::Test test1{
-//        {{"name", "example_test"},
-//        {"title", "Example Test"},
-//        {"description", "This is an example of a test definition"},
-//        {"quantity", "efficiency"},
-//        {"target", "1"}}};
-// 2. set pass/fail/error status and return value (in this case .99)
-//      test1.pass(0.99)
-// 3. write our test data to a json file
-//      eic::util::write_test(test1, "test1.json");
-//
-// Usage Example 2 (multiple tests):
-// =================================
-// 1. define our tests
-//      eic::util::Test test1{
-//        {{"name", "example_test"},
-//        {"title", "Example Test"},
-//        {"description", "This is an example of a test definition"},
-//        {"quantity", "efficiency"},
-//        {"target", "1"}}};
-//      eic::util::Test test2{
-//        {{"name", "another_test"},
-//        {"title", "Another example Test"},
-//        {"description", "This is a second example of a test definition"},
-//        {"quantity", "resolution"},
-//        {"target", "3."}}};
-// 2. set pass/fail/error status and return value (in this case .99)
-//      test1.fail(10)
-// 3. write our test data to a json file
-//      eic::util::write_test({test1, test2}, "test.json");
-
-// Namespace for utility scripts, FIXME this should be part of an independent
-// library
-namespace eic::util {
-
-struct TestDefinitionError : Exception {
-  TestDefinitionError(std::string_view msg)
-      : Exception(msg, "test_definition_error") {}
-};
-
-// Wrapper for our test data json, with three methods to set the status
-// after test completion (to pass, fail or error). The default value
-// is error.
-// The following fields should be defined in the definitions json
-// for the test to make sense:
-//  - name: unique identifier for this test
-//  - title: Slightly more verbose identifier for this test
-//  - description: Concise description of what is tested
-//  - quantity: What quantity is tested? Unites of value/target
-//  - target: Target value of <quantity> that we want to reach
-//  - value: Actual value of <quantity>
-//  - 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)} {
-    // 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"}) {
-      if (json.find(field) == json.end()) {
-        throw TestDefinitionError{
-            fmt::format("Error in test definition: field '{}' missing", field)};
-      }
-    }
-    // Default "weight" to 1 if not set
-    if (json.find("weight") == json.end()) {
-      json["weight"] = 1.0;
-    }
-  }
-  // Set this test to pass/fail/error
-  void pass(double value) { update_result("pass", value); }
-  void fail(double value) { update_result("fail", value); }
-  void error(double value = 0) { update_result("error", value); }
-
-  nlohmann::json json;
-
-private:
-  void update_result(std::string_view status, double value) {
-    json["result"] = status;
-    json["value"] = value;
-  }
-};
-
-void write_test(const std::vector<Test>& data, const std::string& fname) {
-  nlohmann::json test;
-  for (auto& entry : data) {
-    test["tests"].push_back(entry.json);
-  }
-  std::cout << fmt::format("Writing test data to {}\n", fname);
-  std::ofstream output_file(fname);
-  output_file << std::setw(4) << test << "\n";
-}
-void write_test(const Test& data, const std::string& fname) {
-  std::vector<Test> vtd{data};
-  write_test(vtd, fname);
-}
-
-} // namespace eic::util
-
-#endif
diff --git a/util/collect_benchmarks.py b/util/collect_benchmarks.py
index 8f9fd675e5d824580788a13690596515d333b988..0af7e9a12b37eb7616be3400d4046f0088bcf223 100755
--- a/util/collect_benchmarks.py
+++ b/util/collect_benchmarks.py
@@ -9,7 +9,7 @@ directory.
 """
 
 ## Our master definition file, the benchmark project directory
-MASTER_FILE=r'benchmarks.json'
+MASTER_FILE=r'benchmarks/benchmarks.json'
 
 ## Our results directory
 RESULTS_PATH=r'results'
diff --git a/util/collect_tests.py b/util/collect_tests.py
index c56d8c8b2ffd6a1f7ef525c88f66ed6614bb8857..4d860ca79d9f996204a5ca9dc447fa10ed8ec4f4 100755
--- a/util/collect_tests.py
+++ b/util/collect_tests.py
@@ -15,7 +15,7 @@ files to identify them as benchmark components.
 """
 
 ## Our benchmark definition file, stored in the benchmark root directory
-BENCHMARK_FILE=r'{}/benchmark.json'
+BENCHMARK_FILE=r'benchmarks/{}/benchmark.json'
 
 ## Our benchmark results directory
 RESULTS_PATH=r'results/{}'
diff --git a/util/exception.hh b/util/exception.hh
deleted file mode 100644
index d97ef27cd2f1e3ed4237d489af4cf0b742d88143..0000000000000000000000000000000000000000
--- a/util/exception.hh
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef UTIL_EXCEPTION
-#define UTIL_EXCEPTION
-
-#include <exception>
-#include <string>
-
-namespace eic::util {
-class Exception : public std::exception {
-public:
-  Exception(std::string_view msg, std::string_view type = "exception")
-      : msg_{msg}, type_{type} {}
-
-  virtual const char* what() const throw() { return msg_.c_str(); }
-  virtual const char* type() const throw() { return type_.c_str(); }
-  virtual ~Exception() throw() {}
-
-private:
-  std::string msg_;
-  std::string type_;
-};
-} // namespace eic::util
-
-#endif