From 843c0309f3e349df2853c347837c7233cbb3441b Mon Sep 17 00:00:00 2001
From: ralfulrich <ralf.ulrich@kit.edu>
Date: Mon, 25 Oct 2021 22:20:48 +0200
Subject: [PATCH] more unit testing

---
 .../framework/process/ProcessSequence.inl     |  10 +-
 .../process/SwitchProcessSequence.inl         |  86 +++++------
 examples/mars.cpp                             |   2 +-
 tests/framework/testProcessSequence.cpp       | 135 +++++++++++++-----
 4 files changed, 151 insertions(+), 82 deletions(-)

diff --git a/corsika/detail/framework/process/ProcessSequence.inl b/corsika/detail/framework/process/ProcessSequence.inl
index 024c82809..5711ff249 100644
--- a/corsika/detail/framework/process/ProcessSequence.inl
+++ b/corsika/detail/framework/process/ProcessSequence.inl
@@ -312,10 +312,10 @@ namespace corsika {
             int IndexProcess2>
   template <typename TParticle>
   inline CrossSectionType
-  ProcessSequence<TProcess1, TProcess2, IndexStart, IndexProcess1,
-                  IndexProcess2>::getCrossSection(TParticle const& projectile,
-                                                  Code const targetId,
-                                                  FourMomentum const& targetP4) const {
+  ProcessSequence<TProcess1, TProcess2, IndexStart, IndexProcess1, IndexProcess2>::
+      getCrossSection([[maybe_unused]] TParticle const& projectile,
+                      [[maybe_unused]] Code const targetId,
+                      [[maybe_unused]] FourMomentum const& targetP4) const {
 
     CrossSectionType tot = CrossSectionType::zero();
 
@@ -338,8 +338,8 @@ namespace corsika {
       } else if constexpr (process2_type::is_process_sequence) {
         tot += B_.getCrossSection(projectile, targetId, targetP4);
       }
-      return tot;
     }
+    return tot;
   }
 
   template <typename TProcess1, typename TProcess2, int IndexStart, int IndexProcess1,
diff --git a/corsika/detail/framework/process/SwitchProcessSequence.inl b/corsika/detail/framework/process/SwitchProcessSequence.inl
index 02e197ec0..fdf1e685e 100644
--- a/corsika/detail/framework/process/SwitchProcessSequence.inl
+++ b/corsika/detail/framework/process/SwitchProcessSequence.inl
@@ -256,17 +256,18 @@ namespace corsika {
 
         auto const& projectile = view.parent();
         Code const projectileId = projectile.getPID();
-        unsigned int const projectileA = projectile.getNuclearA();
 
         // get cross section vector for all material components
+        // for selected process A
         static_assert(
             has_method_getCrossSection_v<TSequence,        // process object
                                          CrossSectionType, // return type
-                                         Code,             // parameters
-                                         Code, FourMomentum const&, FourMomentum const&>,
+                                         Code, Code,       // parameters
+                                         FourMomentum const&, FourMomentum const&>,
             "TSequence has no method with correct signature \"CrossSectionType "
-            "getCrossSection(Code, Code, FourMomentum const&, FourMomntum const&)\" "
-            "required by InteractionProcess<TSequence>. ");
+            "getCrossSection(Code, Code, FourMomentum const&, FourMomentum "
+            "const&)\" required by "
+            "InteractionProcess<TSequence>. ");
 
         std::vector<CrossSectionType> const weightedCrossSections =
             composition.getWeighted([=](Code const targetId) -> CrossSectionType {
@@ -289,21 +290,21 @@ namespace corsika {
                              {0_GeV, 0_GeV, 0_GeV}));
 
           // interface checking on TProcess1
-          static_assert(has_method_doInteract_v<TSequence,       // process object
-                                                void,            // return type
-                                                TSecondaryView,  // template argument
-                                                TSecondaryView&, // method parameters
-                                                Code, Code, FourMomentum const&,
-                                                FourMomentum const&>,
-                        "USequence has no method with correct signature \"void "
-                        "doInteraction<TSecondaryView>(TSecondaryView&, Code, "
-                        "Code, FourMomentum const&, FourMomntum const&)\" required for "
-                        "InteractionProcess<USequence>. ");
+          static_assert(
+              has_method_doInteract_v<TSequence,       // process object
+                                      void,            // return type
+                                      TSecondaryView,  // template argument
+                                      TSecondaryView&, // method parameters
+                                      Code, Code, FourMomentum const&,
+                                      FourMomentum const&>,
+              "TSequence has no method with correct signature \"void "
+              "doInteraction<TSecondaryView>(TSecondaryView&, "
+              "Code, Code, FourMomentum const&, FourMomentum const&)\" required for "
+              "InteractionProcess<TSequence>. ");
 
           A_.template doInteraction(view, projectileId, targetId, projectileP4, targetP4);
 
           return ProcessReturn::Interacted;
-
         } // end collision branch A
       }
 
@@ -317,17 +318,16 @@ namespace corsika {
 
         auto const& projectile = view.parent();
         Code const projectileId = projectile.getPID();
-        unsigned int const projectileA = projectile.getNuclearA();
 
-        // get cross section vector for all material components
-        static_assert(
-            has_method_getCrossSection_v<USequence,        // process object
-                                         CrossSectionType, // return type
-                                         Code,             // parameters
-                                         Code, FourMomentum const&, FourMomentum const&>,
-            "USequence has no method with correct signature \"CrossSectionType "
-            "getCrossSection(Code, Code, FourMomentum const&, FourMomentum const&)\" "
-            "required by InteractionProcess<USequence>. ");
+        // get cross section vector for all material components, for selected process B
+        static_assert(has_method_getCrossSection_v<USequence,        // process object
+                                                   CrossSectionType, // return type
+                                                   Code, Code, FourMomentum const&,
+                                                   FourMomentum const&>, // parameters
+                      "USequence has no method with correct signature \"CrossSectionType "
+                      "getCrossSection(Code, Code, FourMomentum const&, FourMomentum "
+                      "const&)\" required by "
+                      "InteractionProcess<USequence>. ");
 
         std::vector<CrossSectionType> const weightedCrossSections =
             composition.getWeighted([=](Code const targetId) -> CrossSectionType {
@@ -338,10 +338,11 @@ namespace corsika {
               return B_.getCrossSection(projectileId, targetId, projectileP4, targetP4);
             });
 
-        cx_sum += std::accumulate(weightedCrossSections.cbegin(),
-                                  weightedCrossSections.cend(), CrossSectionType::zero());
+        cx_sum += std::accumulate(weightedCrossSections.begin(),
+                                  weightedCrossSections.end(), CrossSectionType::zero());
 
-        if (cx_select < cx_sum) {
+        // check if we should execute THIS process and then EXIT
+        if (cx_select <= cx_sum) {
 
           // now also sample targetId from weighted cross sections
           Code const targetId = composition.sampleTarget(weightedCrossSections, rng);
@@ -350,19 +351,20 @@ namespace corsika {
               MomentumVector(projectile.getMomentum().getCoordinateSystem(),
                              {0_GeV, 0_GeV, 0_GeV}));
 
-          // interface checking on TProcess1
-          static_assert(has_method_doInteract_v<USequence,       // process object
-                                                void,            // return type
-                                                TSecondaryView,  // template argument
-                                                TSecondaryView&, // method parameters
-                                                Code, Code, FourMomentum const&,
-                                                FourMomentum const&>,
-                        "USequence has no method with correct signature \"void "
-                        "doInteraction<TSecondaryView>(TSecondaryView&, Code, "
-                        "Code, FourMomentum const&, FourMomentum const&)\" required for "
-                        "InteractionProcess<USequence>. ");
-
-          B_.template doInteraction(view, projectileId, targetId, projectileP4, targetP4);
+          // interface checking on TProcess2
+          static_assert(
+              has_method_doInteract_v<USequence,       // process object
+                                      void,            // return type
+                                      TSecondaryView,  // template argument
+                                      TSecondaryView&, // method parameters
+                                      Code, Code, FourMomentum const&,
+                                      FourMomentum const&>,
+              "USequence has no method with correct signature \"void "
+              "doInteraction<TSecondaryView>(TSecondaryView&, "
+              "Code, Code, FourMomentum const&, FourMomentum const&)\" required for "
+              "InteractionProcess<USequence>. ");
+
+          B_.doInteraction(view, projectileId, targetId, projectileP4, targetP4);
 
           return ProcessReturn::Interacted;
         } // end collision in branch B
diff --git a/examples/mars.cpp b/examples/mars.cpp
index e5deda391..60b4f87d4 100644
--- a/examples/mars.cpp
+++ b/examples/mars.cpp
@@ -360,7 +360,7 @@ int main(int argc, char** argv) {
     HEPEnergyType cutE_;
     EnergySwitch(HEPEnergyType cutE)
         : cutE_(cutE) {}
-    bool operator()(const Particle& p) { return (p.getKineticEnergy() < cutE_); }
+    bool operator()(Particle const& p) const { return (p.getKineticEnergy() < cutE_); }
   };
   auto hadronSequence = make_select(EnergySwitch(63.1_GeV), urqmdCounted, heModelCounted);
   auto decaySequence = make_sequence(decayPythia, decaySibyll);
diff --git a/tests/framework/testProcessSequence.cpp b/tests/framework/testProcessSequence.cpp
index ac4c6392d..7cf2c55c4 100644
--- a/tests/framework/testProcessSequence.cpp
+++ b/tests/framework/testProcessSequence.cpp
@@ -63,7 +63,6 @@ struct DummyData {
     return MomentumVector{get_root_CoordinateSystem(), 0_eV, 0_eV, 0_eV};
   }
   HEPEnergyType getEnergy() const { return 10_GeV; }
-  unsigned int getNuclearA() const { return 1; }
 };
 
 // there is no real trajectory/track
@@ -454,27 +453,6 @@ TEST_CASE("ProcessSequence General", "ProcessSequence") {
               sequence2_rv.getProcess2().getProcess2())>); // Process3
   }
 
-  SECTION("interaction length") {
-    globalCount = 0;
-    ContinuousProcess1 cp1(0, 1_m);
-    Process2 m2(1);
-    Process3 m3(2);
-
-    DummyData particle;
-
-    auto sequence2 = make_sequence(cp1, m2, m3);
-    /*GrammageType const tot = sequence2.getInteractionLength(particle);
-    InverseGrammageType const tot_inv = sequence2.getInverseInteractionLength(particle);
-    CORSIKA_LOG_DEBUG(
-        "lambda_tot={}"
-        "; lambda_tot_inv={}",
-        tot, tot_inv);
-
-    CHECK(tot / 1_g * square(1_cm) == 12);
-    CHECK(tot_inv * 1_g / square(1_cm) == 1. / 12);*/
-    globalCount = 0;
-  }
-
   SECTION("lifetime") {
     globalCount = 0;
     ContinuousProcess1 cp1(0, 1_m);
@@ -640,12 +618,20 @@ TEST_CASE("SwitchProcessSequence", "ProcessSequence") {
   auto sec1 = Secondaries1();
   auto sec2 = Secondaries2();
 
-  auto sequence1 = make_sequence(Process1(0), cp2, Decay1(0), sec1, Boundary1(1.0));
-  auto sequence2 = make_sequence(cp3, Process2(0), Boundary1(-1.0), Decay2(0), sec2);
+  auto sequence1 =
+      make_sequence(Process1(0), cp2, Decay1(0), sec1, Boundary1(1.0)); // 10 mb
+  auto sequence2 =
+      make_sequence(cp3, Process2(0), Boundary1(-1.0), Decay2(0), sec2); // 20 mb
 
-  auto sequence3 = make_sequence(cp1, Process3(0),
+  auto sequence3 = make_sequence(cp1, Process3(0), // 30 mb
                                  SwitchProcessSequence(select1, sequence1, sequence2));
 
+  // it is even more typical to have just one sub-process inside the branches of
+  // SwitchProcessSequence
+  auto sequence3_short =
+      make_sequence(cp1, Process3(0), // 30 mb
+                    SwitchProcessSequence(select1, Process1(0), Process2(0)));
+
   auto sequence4 =
       make_sequence(cp1, Boundary1(2.0), Process3(0),
                     SwitchProcessSequence(select1, sequence1, Boundary1(-1.0)));
@@ -778,15 +764,96 @@ TEST_CASE("SwitchProcessSequence", "ProcessSequence") {
     CHECK(checkCont == 0);
     CHECK(checkSec == 0);
 
-    // check that large "select" value will correctly ignore the call
-    cx_select = 1e5_mb;
-    time_select = 1e5 / second;
-    checkDecay = 0;
-    checkInteract = 0;
-    sequence3.selectInteraction(view, projectileP4, noComposition, rng, cx_select);
-    sequence3.selectDecay(view, time_select);
-    CHECK(checkInteract == 0);
-    CHECK(checkDecay == 0);
+    // now check sequence3, which contains a SwitchProcessSequence that contains two
+    // longer sequences in each branch.
+    {
+      // check that large "select" value will correctly ignore the call
+      cx_select = 1e5_mb;
+      time_select = 1e5 / second;
+      checkDecay = 0;
+      checkInteract = 0;
+      sequence3.selectInteraction(view, projectileP4, noComposition, rng, cx_select);
+      sequence3.selectDecay(view, time_select);
+      CHECK(checkInteract == 0);
+      CHECK(checkDecay == 0);
+
+      // for a small cx_select selection must be sucessful
+      cx_select = 28_mb; // -> Process3
+      checkInteract = 0;
+      particle.data_[0] = -100; // data negative --> sequence2
+      CHECK(sequence3.getCrossSection(particle, Code::Oxygen,
+                                      {Oxygen::mass, {rootCS, {0_eV, 0_eV, 0_eV}}}) /
+                1_mb ==
+            Approx(50.));
+      sequence3.selectInteraction(view, projectileP4, noComposition, rng, cx_select);
+      CHECK(checkInteract == 4); // 2^3
+
+      particle.data_[0] = 100; // data positive --> sequence1
+      checkInteract = 0;
+      CHECK(sequence3.getCrossSection(particle, Code::Oxygen,
+                                      {Oxygen::mass, {rootCS, {0_eV, 0_eV, 0_eV}}}) /
+                1_mb ==
+            Approx(40.));
+      sequence3.selectInteraction(view, projectileP4, noComposition, rng, cx_select);
+      CHECK(checkInteract == 4); // 2^3
+
+      cx_select = 32_mb; // -> Process2 or Process1
+      checkInteract = 0;
+      particle.data_[0] = -100; // data negative --> Process2
+      sequence3.selectInteraction(view, projectileP4, noComposition, rng, cx_select);
+      CHECK(checkInteract == 2); // 2^2
+
+      particle.data_[0] = 100; // data positive --> Process1
+      checkInteract = 0;
+      sequence3.selectInteraction(view, projectileP4, noComposition, rng, cx_select);
+      CHECK(checkInteract == 1); // 2^1
+    }
+
+    // now check sequence3, which contains a SwitchProcessSequence that contains just two
+    // bare InteractionProcess-es in each branch.
+    {
+      // check that large "select" value will correctly ignore the call
+      cx_select = 1e5_mb;
+      checkInteract = 0;
+      sequence3_short.selectInteraction(view, projectileP4, noComposition, rng,
+                                        cx_select);
+      CHECK(checkInteract == 0);
+
+      // for a small cx_select selection must be sucessful
+      cx_select = 28_mb; // -> Process3
+      checkInteract = 0;
+      particle.data_[0] = -100; // data negative --> sequence2
+      CHECK(sequence3_short.getCrossSection(
+                particle, Code::Oxygen, {Oxygen::mass, {rootCS, {0_eV, 0_eV, 0_eV}}}) /
+                1_mb ==
+            Approx(50.));
+      sequence3_short.selectInteraction(view, projectileP4, noComposition, rng,
+                                        cx_select);
+      CHECK(checkInteract == 4); // 2^3
+
+      particle.data_[0] = 100; // data positive --> sequence1
+      checkInteract = 0;
+      CHECK(sequence3_short.getCrossSection(
+                particle, Code::Oxygen, {Oxygen::mass, {rootCS, {0_eV, 0_eV, 0_eV}}}) /
+                1_mb ==
+            Approx(40.));
+      sequence3_short.selectInteraction(view, projectileP4, noComposition, rng,
+                                        cx_select);
+      CHECK(checkInteract == 4); // 2^3
+
+      cx_select = 32_mb; // -> Process2 or Process1
+      checkInteract = 0;
+      particle.data_[0] = -100; // data negative --> Process2
+      sequence3_short.selectInteraction(view, projectileP4, noComposition, rng,
+                                        cx_select);
+      CHECK(checkInteract == 2); // 2^2
+
+      particle.data_[0] = 100; // data positive --> Process1
+      checkInteract = 0;
+      sequence3_short.selectInteraction(view, projectileP4, noComposition, rng,
+                                        cx_select);
+      CHECK(checkInteract == 1); // 2^1
+    }
   }
 
   SECTION("Check SecondariesProcesses in SwitchProcessSequence") {
-- 
GitLab