From 7de91386ceb1669a4420d4d3652ae24cf55c2849 Mon Sep 17 00:00:00 2001
From: ralfulrich <ralf.ulrich@kit.edu>
Date: Thu, 22 Oct 2020 08:21:27 +0200
Subject: [PATCH] changed for SwitchProcessSequence to functor, added
 process::sequence factory function

---
 Documentation/Examples/boundary_example.cc    |  2 +-
 Documentation/Examples/cascade_example.cc     |  3 +-
 .../Examples/cascade_proton_example.cc        |  2 +-
 Documentation/Examples/em_shower.cc           |  4 +-
 .../Examples/staticsequence_example.cc        |  2 +-
 Documentation/Examples/vertical_EAS.cc        | 14 +++--
 Framework/Cascade/CMakeLists.txt              |  1 -
 Framework/Cascade/testCascade.cc              |  6 +-
 Framework/ProcessSequence/CMakeLists.txt      | 12 ++++
 Framework/ProcessSequence/NullModel.h         | 22 ++++++++
 Framework/ProcessSequence/ProcessSequence.h   | 55 +++++++++++++++----
 .../ProcessSequence/SwitchProcessSequence.h   | 18 +++---
 Framework/ProcessSequence/testNullModel.cc    | 29 ++++++++++
 .../ProcessSequence/testProcessSequence.cc    | 38 ++++++++-----
 Processes/CMakeLists.txt                      |  2 -
 Processes/NullModel/NullModel.cc              | 31 -----------
 Processes/NullModel/NullModel.h               | 30 ----------
 Processes/NullModel/testNullModel.cc          | 53 ------------------
 18 files changed, 158 insertions(+), 166 deletions(-)
 create mode 100644 Framework/ProcessSequence/NullModel.h
 create mode 100644 Framework/ProcessSequence/testNullModel.cc
 delete mode 100644 Processes/NullModel/NullModel.cc
 delete mode 100644 Processes/NullModel/NullModel.h
 delete mode 100644 Processes/NullModel/testNullModel.cc

diff --git a/Documentation/Examples/boundary_example.cc b/Documentation/Examples/boundary_example.cc
index 84efed04a..9bf4e188a 100644
--- a/Documentation/Examples/boundary_example.cc
+++ b/Documentation/Examples/boundary_example.cc
@@ -132,7 +132,7 @@ int main() {
   MyBoundaryCrossingProcess<true> boundaryCrossing("crossings.dat");
 
   // assemble all processes into an ordered process list
-  auto sequence = sibyll % decay % cut % boundaryCrossing % trackWriter;
+  auto sequence = process::sequence(sibyll, decay, cut, boundaryCrossing, trackWriter);
 
   // setup particle stack, and add primary particles
   setup::Stack stack;
diff --git a/Documentation/Examples/cascade_example.cc b/Documentation/Examples/cascade_example.cc
index 45e26dc85..1e240145b 100644
--- a/Documentation/Examples/cascade_example.cc
+++ b/Documentation/Examples/cascade_example.cc
@@ -150,7 +150,8 @@ int main() {
   process::energy_loss::EnergyLoss eLoss{showerAxis, cut.GetECut()};
 
   // assemble all processes into an ordered process list
-  auto sequence = stackInspect % sibyll % sibyllNuc % decay % eLoss % cut % trackWriter;
+  auto sequence =
+      process::sequence(stackInspect, sibyll, sibyllNuc, decay, eLoss, cut, trackWriter);
 
   // define air shower object, run simulation
   cascade::Cascade EAS(env, tracking, sequence, stack);
diff --git a/Documentation/Examples/cascade_proton_example.cc b/Documentation/Examples/cascade_proton_example.cc
index 235ee2d9e..4ca053f37 100644
--- a/Documentation/Examples/cascade_proton_example.cc
+++ b/Documentation/Examples/cascade_proton_example.cc
@@ -140,7 +140,7 @@ int main() {
 
   // assemble all processes into an ordered process list
   // auto sequence = sibyll << decay << hadronicElastic << cut << trackWriter;
-  auto sequence = pythia % decay % cut % trackWriter % stackInspect;
+  auto sequence = process::sequence(pythia, decay, cut, trackWriter, stackInspect);
 
   // cout << "decltype(sequence)=" << type_id_with_cvr<decltype(sequence)>().pretty_name()
   // << "\n";
diff --git a/Documentation/Examples/em_shower.cc b/Documentation/Examples/em_shower.cc
index 95dfedfc4..7a5d15dbb 100644
--- a/Documentation/Examples/em_shower.cc
+++ b/Documentation/Examples/em_shower.cc
@@ -149,8 +149,8 @@ int main(int argc, char** argv) {
   process::observation_plane::ObservationPlane observationLevel(obsPlane,
                                                                 "particles.dat");
 
-  auto sequence =
-      proposalCounted % em_continuous % longprof % cut % observationLevel % trackWriter;
+  auto sequence = process::sequence(proposalCounted, em_continuous, longprof, cut,
+                                    observationLevel, trackWriter);
   // define air shower object, run simulation
   tracking_line::TrackingLine tracking;
   cascade::Cascade EAS(env, tracking, sequence, stack);
diff --git a/Documentation/Examples/staticsequence_example.cc b/Documentation/Examples/staticsequence_example.cc
index 64fbf6bc0..659a3d6c2 100644
--- a/Documentation/Examples/staticsequence_example.cc
+++ b/Documentation/Examples/staticsequence_example.cc
@@ -84,7 +84,7 @@ void modular() {
   Process3 m3;      // * 1.0
   Process4 m4(1.5); // * 1.5
 
-  auto sequence = m1 % m2 % m3 % m4;
+  auto sequence = process::sequence(m1, m2, m3, m4);
 
   DummyData particle;
   DummyTrajectory track;
diff --git a/Documentation/Examples/vertical_EAS.cc b/Documentation/Examples/vertical_EAS.cc
index 9b0cf6182..7258f99d5 100644
--- a/Documentation/Examples/vertical_EAS.cc
+++ b/Documentation/Examples/vertical_EAS.cc
@@ -220,18 +220,20 @@ int main(int argc, char** argv) {
     HEPEnergyType cutE_;
     EnergySwitch(HEPEnergyType cutE)
         : cutE_(cutE) {}
-    process::SwitchResult select(const Particle& p) {
+    process::SwitchResult operator()(const Particle& p) {
       if (p.GetEnergy() < cutE_)
         return process::SwitchResult::First;
       else
         return process::SwitchResult::Second;
     }
   };
-  auto hadronSequence = process::select(urqmdCounted, sibyllNucCounted % sibyllCounted,
-                                        EnergySwitch(55_GeV));
-  auto decaySequence = decayPythia % decaySibyll;
-  auto sequence = hadronSequence % reset_particle_mass % decaySequence % proposalCounted %
-                  em_continuous % cut % observationLevel % longprof;
+  auto hadronSequence =
+      process::select(urqmdCounted, process::sequence(sibyllNucCounted, sibyllCounted),
+                      EnergySwitch(55_GeV));
+  auto decaySequence = process::sequence(decayPythia, decaySibyll);
+  auto sequence =
+      process::sequence(hadronSequence, reset_particle_mass, decaySequence,
+                        proposalCounted, em_continuous, cut, observationLevel, longprof);
 
   // define air shower object, run simulation
   tracking_line::TrackingLine tracking;
diff --git a/Framework/Cascade/CMakeLists.txt b/Framework/Cascade/CMakeLists.txt
index 9bb601934..2a1209973 100644
--- a/Framework/Cascade/CMakeLists.txt
+++ b/Framework/Cascade/CMakeLists.txt
@@ -51,6 +51,5 @@ target_link_libraries (
   CORSIKAcascade
   ProcessStackInspector
   ProcessTrackingLine
-  ProcessNullModel
   CORSIKAtesting
   )
diff --git a/Framework/Cascade/testCascade.cc b/Framework/Cascade/testCascade.cc
index 6dad05ac5..7cf7b9224 100644
--- a/Framework/Cascade/testCascade.cc
+++ b/Framework/Cascade/testCascade.cc
@@ -11,7 +11,7 @@
 #include <corsika/cascade/Cascade.h>
 
 #include <corsika/process/ProcessSequence.h>
-#include <corsika/process/null_model/NullModel.h>
+#include <corsika/process/NullModel.h>
 #include <corsika/process/stack_inspector/StackInspector.h>
 #include <corsika/process/tracking_line/TrackingLine.h>
 
@@ -132,13 +132,13 @@ TEST_CASE("Cascade", "[Cascade]") {
   tracking_line::TrackingLine tracking;
 
   stack_inspector::StackInspector<TestCascadeStack> stackInspect(1, true, E0);
-  null_model::NullModel nullModel;
+  process::NullModel nullModel;
 
   const GrammageType X0 = 20_g / square(1_cm);
   const HEPEnergyType Ecrit = 85_MeV;
   ProcessSplit split(X0);
   ProcessCut cut(Ecrit);
-  auto sequence = nullModel % stackInspect % split % cut;
+  auto sequence = process::sequence(nullModel, stackInspect, split, cut);
   TestCascadeStack stack;
   stack.Clear();
   stack.AddParticle(
diff --git a/Framework/ProcessSequence/CMakeLists.txt b/Framework/ProcessSequence/CMakeLists.txt
index 2c4a0fe5d..7a7554893 100644
--- a/Framework/ProcessSequence/CMakeLists.txt
+++ b/Framework/ProcessSequence/CMakeLists.txt
@@ -20,6 +20,7 @@ set (
   SwitchProcessSequence.h
   ProcessReturn.h
   ProcessTraits.h
+  NullModel.h
   )
 
 CORSIKA_COPY_HEADERS_TO_NAMESPACE (CORSIKAprocesssequence ${CORSIKAprocesssequence_NAMESPACE} ${CORSIKAprocesssequence_HEADERS})
@@ -61,6 +62,17 @@ target_link_libraries (
   CORSIKAtesting
   )
 
+# --------------------
+# code unit testing
+CORSIKA_ADD_TEST (testNullModel)
+target_link_libraries (
+  testNullModel
+  CORSIKAsetup
+  CORSIKAgeometry
+  CORSIKAunits
+  CORSIKAtesting
+  )
+
 # # CORSIKA_ADD_TEST (testSwitchProcessSequence)
 # # target_link_libraries (
 # #   testSwitchProcessSequence
diff --git a/Framework/ProcessSequence/NullModel.h b/Framework/ProcessSequence/NullModel.h
new file mode 100644
index 000000000..feb0debd9
--- /dev/null
+++ b/Framework/ProcessSequence/NullModel.h
@@ -0,0 +1,22 @@
+/*
+ * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+ * the license.
+ */
+
+#pragma once
+
+#include <corsika/process/BaseProcess.h>
+
+namespace corsika::process {
+
+  class NullModel : public corsika::process::BaseProcess<NullModel> {
+
+  public:
+    NullModel() = default;
+    ~NullModel() = default;
+  };
+
+} // namespace corsika::process
diff --git a/Framework/ProcessSequence/ProcessSequence.h b/Framework/ProcessSequence/ProcessSequence.h
index c0b7c0d13..22d9ed1f2 100644
--- a/Framework/ProcessSequence/ProcessSequence.h
+++ b/Framework/ProcessSequence/ProcessSequence.h
@@ -17,6 +17,7 @@
 #include <corsika/process/ProcessReturn.h>
 #include <corsika/process/SecondariesProcess.h>
 #include <corsika/process/StackProcess.h>
+#include <corsika/process/NullModel.h>
 #include <corsika/units/PhysicalUnits.h>
 #include <corsika/logging/Logging.h>
 
@@ -40,7 +41,7 @@ namespace corsika::process {
      \comment Using CRTP pattern,
      https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
    */
-  template <typename TProcess1, typename TProcess2>
+  template <typename TProcess1, typename TProcess2 = NullModel>
   class ProcessSequence : public BaseProcess<ProcessSequence<TProcess1, TProcess2>> {
 
     using TProcess1type = typename std::decay<TProcess1>::type;
@@ -294,14 +295,35 @@ namespace corsika::process {
     }
   };
 
-  // the % operator assembles many BaseProcess, ContinuousProcess, and
-  // Interaction/DecayProcess objects into a ProcessSequence, all combinatorics
-  // must be allowed, this is why we define a macro to define all
-  // combinations here:
-
-  // enable the % operator to construct ProcessSequence from two
-  // Processes, only if both, Processes1 and Processes2, derive from
-  // BaseProcesses
+  /**
+   * \function sequence
+   *
+   * to construct ProcessSequences in a flexible and dynamic way the
+   * `sequence` factory functions are provided
+   *
+   * Any objects of type
+   *  - BaseProcess,
+   *  - ContinuousProcess, and
+   *  - Interaction/DecayProcess,
+   *  - StackProcess,
+   *  - SecondariesProcess
+   * can be assembled into a ProcessSequence, all
+   * combinatorics are allowed.
+
+   * The sequence function checks that all its arguments are all of
+   * types derived from BaseProcess. Also the ProcessSequence itself
+   * is derived from type BaseProcess
+   **/
+
+  template <typename... TProcesses, typename TProcess1>
+  inline typename std::enable_if<
+      std::is_base_of<BaseProcess<typename std::decay<TProcess1>::type>,
+                      typename std::decay<TProcess1>::type>::value,
+      ProcessSequence<TProcess1, decltype(sequence(std::declval<TProcesses>()...))>>::type
+  sequence(TProcess1&& vA, TProcesses&&... vBs) {
+    return ProcessSequence<TProcess1, decltype(sequence(std::declval<TProcesses>()...))>(
+        vA, sequence(std::forward<TProcesses>(vBs)...));
+  }
 
   template <typename TProcess1, typename TProcess2>
   inline typename std::enable_if<
@@ -310,10 +332,23 @@ namespace corsika::process {
           std::is_base_of<BaseProcess<typename std::decay<TProcess2>::type>,
                           typename std::decay<TProcess2>::type>::value,
       ProcessSequence<TProcess1, TProcess2>>::type
-  operator%(TProcess1&& vA, TProcess2&& vB) {
+  sequence(TProcess1&& vA, TProcess2&& vB) {
     return ProcessSequence<TProcess1, TProcess2>(vA, vB);
   }
 
+  /**
+   * also allow a single Process in ProcessSequence, accompany by
+   * `NullModel`
+   **/
+  template <typename TProcess>
+  inline typename std::enable_if<
+      std::is_base_of<BaseProcess<typename std::decay<TProcess>::type>,
+                      typename std::decay<TProcess>::type>::value,
+      ProcessSequence<TProcess, NullModel>>::type
+  sequence(TProcess&& vA) {
+    return ProcessSequence<TProcess, NullModel>(vA, NullModel());
+  }
+
   /// traits marker to identify objectas ProcessSequence
   template <typename TProcess1, typename TProcess2>
   struct is_process_sequence<corsika::process::ProcessSequence<TProcess1, TProcess2>>
diff --git a/Framework/ProcessSequence/SwitchProcessSequence.h b/Framework/ProcessSequence/SwitchProcessSequence.h
index 49e73a262..b7e49a891 100644
--- a/Framework/ProcessSequence/SwitchProcessSequence.h
+++ b/Framework/ProcessSequence/SwitchProcessSequence.h
@@ -40,7 +40,7 @@ namespace corsika::process {
      just classes. This allows us to handle both, rvalue as well as
      lvalue Processes in the SwitchProcessSequence.
 
-     TSelect has to implement a `select(const Particle&)` and has to
+     TSelect has to implement a `operator()(const Particle&)` and has to
      return either SwitchResult::First or SwitchResult::Second. Note:
      TSelect may absolutely also use random numbers to sample between
      its results. This can be used to achieve arbitrarily smooth
@@ -78,7 +78,7 @@ namespace corsika::process {
     EProcessReturn DoBoundaryCrossing(Particle& particle, VTNType const& from,
                                       VTNType const& to) {
 
-      switch (select_.select(particle)) {
+      switch (select_(particle)) {
         case SwitchResult::First: {
           if constexpr (std::is_base_of_v<BoundaryCrossingProcess<TProcess1type>,
                                           TProcess1type> ||
@@ -101,7 +101,7 @@ namespace corsika::process {
 
     template <typename TParticle, typename TTrack>
     inline EProcessReturn DoContinuous(TParticle& particle, TTrack& vT) {
-      switch (select_.select(particle)) {
+      switch (select_(particle)) {
         case SwitchResult::First: {
           if constexpr (std::is_base_of_v<ContinuousProcess<TProcess1type>,
                                           TProcess1type> ||
@@ -125,7 +125,7 @@ namespace corsika::process {
     template <typename TSecondaries>
     inline void DoSecondaries(TSecondaries& vS) {
       const auto& particle = vS.parent();
-      switch (select_.select(particle)) {
+      switch (select_(particle)) {
         case SwitchResult::First: {
           if constexpr (std::is_base_of_v<SecondariesProcess<TProcess1type>,
                                           TProcess1type> ||
@@ -149,7 +149,7 @@ namespace corsika::process {
     inline corsika::units::si::LengthType MaxStepLength(TParticle& particle,
                                                         TTrack& vTrack) {
 
-      switch (select_.select(particle)) {
+      switch (select_(particle)) {
         case SwitchResult::First: {
           if constexpr (std::is_base_of_v<ContinuousProcess<TProcess1type>,
                                           TProcess1type> ||
@@ -182,7 +182,7 @@ namespace corsika::process {
         TParticle&& particle) {
       using namespace corsika::units::si;
 
-      switch (select_.select(particle)) {
+      switch (select_(particle)) {
         case SwitchResult::First: {
           if constexpr (std::is_base_of_v<InteractionProcess<TProcess1type>,
                                           TProcess1type> ||
@@ -210,7 +210,7 @@ namespace corsika::process {
         [[maybe_unused]] corsika::units::si::InverseGrammageType lambda_inv_sum =
             corsika::units::si::InverseGrammageType::zero()) {
 
-      switch (select_.select(view.parent())) {
+      switch (select_(view.parent())) {
         case SwitchResult::First: {
           if constexpr (t1ProcSeq) {
             // if A_ is a process sequence --> check inside
@@ -261,7 +261,7 @@ namespace corsika::process {
     inline corsika::units::si::InverseTimeType GetInverseLifetime(TParticle&& particle) {
       using namespace corsika::units::si;
 
-      switch (select_.select(particle)) {
+      switch (select_(particle)) {
         case SwitchResult::First: {
           if constexpr (std::is_base_of_v<DecayProcess<TProcess1type>, TProcess1type> ||
                         t1ProcSeq) {
@@ -289,7 +289,7 @@ namespace corsika::process {
         [[maybe_unused]] corsika::units::si::InverseTimeType decay_inv_sum =
             corsika::units::si::InverseTimeType::zero()) {
 
-      switch (select_.select(view.parent())) {
+      switch (select_(view.parent())) {
         case SwitchResult::First: {
           if constexpr (t1ProcSeq) {
             // if A_ is a process sequence --> check inside
diff --git a/Framework/ProcessSequence/testNullModel.cc b/Framework/ProcessSequence/testNullModel.cc
new file mode 100644
index 000000000..33bfda3d0
--- /dev/null
+++ b/Framework/ProcessSequence/testNullModel.cc
@@ -0,0 +1,29 @@
+/*
+ * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+ * the license.
+ */
+
+#include <catch2/catch.hpp>
+
+#include <corsika/process/NullModel.h>
+
+#include <corsika/geometry/Point.h>
+#include <corsika/geometry/RootCoordinateSystem.h>
+#include <corsika/geometry/Vector.h>
+
+#include <corsika/units/PhysicalUnits.h>
+
+#include <corsika/setup/SetupStack.h>
+#include <corsika/setup/SetupTrajectory.h>
+
+using namespace corsika::units::si;
+using namespace corsika::process;
+using namespace corsika;
+
+TEST_CASE("NullModel", "[processes]") {
+
+  SECTION("interface") { [[maybe_unused]] NullModel model; }
+}
diff --git a/Framework/ProcessSequence/testProcessSequence.cc b/Framework/ProcessSequence/testProcessSequence.cc
index 2e529efb4..f303d0e80 100644
--- a/Framework/ProcessSequence/testProcessSequence.cc
+++ b/Framework/ProcessSequence/testProcessSequence.cc
@@ -261,9 +261,15 @@ TEST_CASE("Process Sequence", "[Process Sequence]") {
     Process4 m4(3);
     CHECK(globalCount == 4);
 
-    auto sequence = m1 % m2 % m3 % m4;
-    CHECK(is_process_sequence_v<decltype(sequence)> == true);
+    auto sequence1 = process::sequence(m1, m2, m3, m4);
+    CHECK(is_process_sequence_v<decltype(sequence1)> == true);
     CHECK(is_process_sequence_v<decltype(m2)> == false);
+
+    auto sequence2 = process::sequence(m1, m2, m3);
+    CHECK(is_process_sequence_v<decltype(sequence2)> == true);
+
+    auto sequence3 = process::sequence(m4);
+    CHECK(is_process_sequence_v<decltype(sequence3)> == true);
   }
 
   SECTION("interaction length") {
@@ -274,7 +280,7 @@ TEST_CASE("Process Sequence", "[Process Sequence]") {
 
     DummyData particle;
 
-    auto sequence2 = cp1 % m2 % m3;
+    auto sequence2 = sequence(cp1, m2, m3);
     GrammageType const tot = sequence2.GetInteractionLength(particle);
     InverseGrammageType const tot_inv = sequence2.GetInverseInteractionLength(particle);
     cout << "lambda_tot=" << tot << "; lambda_tot_inv=" << tot_inv << endl;
@@ -290,7 +296,7 @@ TEST_CASE("Process Sequence", "[Process Sequence]") {
 
     DummyData particle;
 
-    auto sequence2 = cp1 % m2 % m3 % d3;
+    auto sequence2 = sequence(cp1, m2, m3, d3);
     TimeType const tot = sequence2.GetLifetime(particle);
     InverseTimeType const tot_inv = sequence2.GetInverseLifetime(particle);
     cout << "lambda_tot=" << tot << "; lambda_tot_inv=" << tot_inv << endl;
@@ -305,7 +311,7 @@ TEST_CASE("Process Sequence", "[Process Sequence]") {
     Process2 m2(2);
     Process3 m3(3);
 
-    auto sequence2 = cp1 % m2 % m3 % cp2;
+    auto sequence2 = sequence(cp1, m2, m3, cp2);
 
     DummyData particle;
     DummyTrajectory track;
@@ -333,7 +339,7 @@ TEST_CASE("Process Sequence", "[Process Sequence]") {
     Stack1 s1(1);
     Stack1 s2(2);
 
-    auto sequence = s1 % s2;
+    auto sequence = process::sequence(s1, s2);
 
     DummyStack stack;
 
@@ -350,7 +356,7 @@ TEST_CASE("Switch Process Sequence", "[Process Sequence]") {
   SECTION("Check construction") {
 
     struct TestSelect {
-      corsika::process::SwitchResult select(const DummyData& p) const {
+      corsika::process::SwitchResult operator()(const DummyData& p) const {
         std::cout << "TestSelect data=" << p.data_[0] << std::endl;
         if (p.data_[0] > 0) return corsika::process::SwitchResult::First;
         return corsika::process::SwitchResult::Second;
@@ -358,16 +364,18 @@ TEST_CASE("Switch Process Sequence", "[Process Sequence]") {
     };
     TestSelect select;
 
-    auto sequence1 = Process1(0) % ContinuousProcess2(0) % Decay1(0);
-    auto sequence2 = ContinuousProcess3(0) % Process2(0) % Decay2(0);
+    auto sequence1 = process::sequence(Process1(0), ContinuousProcess2(0), Decay1(0));
+    auto sequence2 = process::sequence(ContinuousProcess3(0), Process2(0), Decay2(0));
 
-    auto sequence = ContinuousProcess1(0) % Process3(0) %
-                    SwitchProcessSequence(sequence1, sequence2, select);
+    auto sequence =
+        process::sequence(ContinuousProcess1(0), Process3(0),
+                          SwitchProcessSequence(sequence1, sequence2, select));
 
-    auto sequence_alt =
-        (ContinuousProcess1(0) % Process3(0)) %
-        process::select(Process1(0) % ContinuousProcess2(0) % Decay1(0),
-                        ContinuousProcess3(0) % Process2(0) % Decay2(0), select);
+    auto sequence_alt = process::sequence(
+        ContinuousProcess1(0), Process3(0),
+        process::select(process::sequence(Process1(0), ContinuousProcess2(0), Decay1(0)),
+                        process::sequence(ContinuousProcess3(0), Process2(0), Decay2(0)),
+                        select));
 
     // check that same process sequence can be build in different ways
     CHECK(typeid(sequence) == typeid(sequence_alt));
diff --git a/Processes/CMakeLists.txt b/Processes/CMakeLists.txt
index 53a01ca25..4e9f59df3 100644
--- a/Processes/CMakeLists.txt
+++ b/Processes/CMakeLists.txt
@@ -2,7 +2,6 @@
 add_subdirectory (AnalyticProcessors)
 add_subdirectory (ExampleProcessors)
 
-add_subdirectory (NullModel)
 # tracking
 add_subdirectory (TrackingLine)
 # hadron interaction models
@@ -36,7 +35,6 @@ add_subdirectory (InteractionCounter)
 ##########################################
 # add_custom_target(CORSIKAprocesses)
 add_library (CORSIKAprocesses INTERFACE)
-add_dependencies (CORSIKAprocesses ProcessNullModel)
 add_dependencies (CORSIKAprocesses ProcessSibyll)
 add_dependencies (CORSIKAprocesses ProcessProposal)
 if (Pythia8_FOUND)
diff --git a/Processes/NullModel/NullModel.cc b/Processes/NullModel/NullModel.cc
deleted file mode 100644
index 1a3afc793..000000000
--- a/Processes/NullModel/NullModel.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
- *
- * This software is distributed under the terms of the GNU General Public
- * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
- * the license.
- */
-
-#include <corsika/process/null_model/NullModel.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-
-using namespace corsika;
-namespace corsika::process::null_model {
-
-  NullModel::NullModel(units::si::LengthType maxStepLength)
-      : fMaxStepLength(maxStepLength) {}
-
-  template <>
-  process::EProcessReturn NullModel::DoContinuous(setup::Stack::ParticleType&,
-                                                  setup::Trajectory&) const {
-    return process::EProcessReturn::eOk;
-  }
-
-  template <>
-  units::si::LengthType NullModel::MaxStepLength(setup::Stack::ParticleType&,
-                                                 setup::Trajectory&) const {
-    return fMaxStepLength;
-  }
-
-} // namespace corsika::process::null_model
diff --git a/Processes/NullModel/NullModel.h b/Processes/NullModel/NullModel.h
deleted file mode 100644
index ba80599f3..000000000
--- a/Processes/NullModel/NullModel.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
- *
- * This software is distributed under the terms of the GNU General Public
- * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
- * the license.
- */
-
-#pragma once
-
-#include <corsika/process/BaseProcess.h>
-#include <corsika/units/PhysicalUnits.h>
-
-namespace corsika::process::null_model {
-
-  class NullModel : public corsika::process::BaseProcess<NullModel> {
-    corsika::units::si::LengthType const fMaxStepLength;
-
-  public:
-    NullModel(corsika::units::si::LengthType maxStepLength =
-                  corsika::units::si::meter * std::numeric_limits<double>::infinity());
-
-    template <typename Particle, typename Track>
-    process::EProcessReturn DoContinuous(Particle&, Track&) const;
-
-    template <typename Particle, typename Track>
-    corsika::units::si::LengthType MaxStepLength(Particle&, Track&) const;
-  };
-
-} // namespace corsika::process::null_model
diff --git a/Processes/NullModel/testNullModel.cc b/Processes/NullModel/testNullModel.cc
deleted file mode 100644
index 653393e11..000000000
--- a/Processes/NullModel/testNullModel.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
- *
- * This software is distributed under the terms of the GNU General Public
- * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
- * the license.
- */
-
-#include <catch2/catch.hpp>
-
-#include <corsika/process/null_model/NullModel.h>
-
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-
-using namespace corsika::units::si;
-using namespace corsika::process::null_model;
-using namespace corsika;
-
-TEST_CASE("NullModel", "[processes]") {
-
-  auto const& dummyCS =
-      geometry::RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-  geometry::Point const origin(dummyCS, {0_m, 0_m, 0_m});
-  geometry::Vector<units::si::SpeedType::dimension_type> v(dummyCS, 0_m / second,
-                                                           0_m / second, 1_m / second);
-  geometry::Line line(origin, v);
-  geometry::Trajectory<geometry::Line> track(line, 10_s);
-
-  setup::Stack stack;
-  setup::Stack::ParticleType particle = stack.AddParticle(
-      std::tuple<particles::Code, units::si::HEPEnergyType,
-                 corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{
-          particles::Code::Electron, 100_GeV,
-          corsika::stack::MomentumVector(dummyCS, {0_GeV, 0_GeV, -1_GeV}),
-          geometry::Point(dummyCS, {0_m, 0_m, 10_km}), 0_ns});
-  SECTION("interface") {
-
-    NullModel model(10_m);
-
-    [[maybe_unused]] const process::EProcessReturn ret =
-        model.DoContinuous(particle, track);
-    LengthType const length = model.MaxStepLength(particle, track);
-
-    CHECK((length / 10_m) == Approx(1));
-  }
-}
-- 
GitLab