From 2af74c694d1ea2603a13bed7cfe96615593a12eb Mon Sep 17 00:00:00 2001
From: ralfulrich <ralf.ulrich@kit.edu>
Date: Tue, 19 Oct 2021 00:01:33 +0200
Subject: [PATCH] sibyll works...

---
 corsika/detail/framework/core/Cascade.inl     | 39 ++++----
 .../detail/framework/geometry/FourVector.inl  | 22 ++---
 .../framework/process/ProcessSequence.inl     | 77 +++++++++------
 .../process/SwitchProcessSequence.inl         | 98 ++++++++++---------
 corsika/detail/framework/utility/COMBoost.inl | 52 +++++++---
 .../modules/sibyll/InteractionModel.inl       | 31 +++---
 .../sibyll/NuclearInteractionModel.inl        | 80 ++++++++-------
 corsika/framework/core/Cascade.hpp            | 10 +-
 corsika/framework/geometry/FourVector.hpp     | 83 +++++++++-------
 corsika/framework/process/ProcessSequence.hpp | 33 ++++++-
 .../process/SwitchProcessSequence.hpp         |  4 +-
 corsika/framework/utility/COMBoost.hpp        | 56 ++++++-----
 corsika/modules/sibyll/InteractionModel.hpp   | 13 ++-
 .../sibyll/NuclearInteractionModel.hpp        |  8 +-
 tests/framework/testCascade.cpp               | 10 +-
 tests/framework/testProcessSequence.cpp       | 44 +++++----
 tests/modules/testSibyll.cpp                  | 37 ++++---
 17 files changed, 404 insertions(+), 293 deletions(-)

diff --git a/corsika/detail/framework/core/Cascade.inl b/corsika/detail/framework/core/Cascade.inl
index ae11cf09a..0e00520bb 100644
--- a/corsika/detail/framework/core/Cascade.inl
+++ b/corsika/detail/framework/core/Cascade.inl
@@ -80,18 +80,18 @@ namespace corsika {
 
     // determine sqrtS per nucleon pair, sqrtS_NN
     Code const projectileId = particle.getPID();
-    unsigned int const projectileA =
-        (is_nucleus(projectileId) ? get_nucleus_A(projectileId) : 1);
-    HEPEnergyType const ElabNN = particle.getEnergy() / projectileA;
-    HEPEnergyType const sqrtSnn = sqrt(2 * ElabNN * constants::nucleonMass);
-
-    COMBoost const boost{{ElabNN, particle.getMomentum() / projectileA},
-                         constants::nucleonMass};
+    HEPEnergyType const Elab = particle.getEnergy();
+    FourMomentum const projectileP4{Elab, particle.getMomentum()};
     CrossSectionType const sigma =
         composition.getWeightedSum([=](Code const targetId) -> CrossSectionType {
-          return sequence_.getCrossSection(projectileId, targetId, sqrtSnn);
+          FourMomentum const targetP4(
+              get_mass(targetId),
+              MomentumVector(particle.getMomentum().getCoordinateSystem(),
+                             {0_GeV, 0_GeV, 0_GeV}));
+          return sequence_.getCrossSection(projectileId, targetId, projectileP4,
+                                           targetP4);
         });
-    interaction(secondaries, boost, sqrtSnn, composition, sigma);
+    interaction(secondaries, projectileP4, composition, sigma);
     sequence_.doSecondaries(secondaries);
     particle.erase(); // primary particle is done
   }
@@ -114,16 +114,19 @@ namespace corsika {
 
     // determine sqrtS per nucleon pair, sqrtS_NN
     Code const projectileId = particle.getPID();
-    unsigned int const projectileA =
-        (is_nucleus(projectileId) ? get_nucleus_A(projectileId) : 1);
-    HEPEnergyType const ElabNN = particle.getEnergy() / projectileA;
-    HEPEnergyType const sqrtSnn = sqrt(2 * ElabNN * constants::nucleonMass);
+    HEPEnergyType const Elab = particle.getEnergy();
+    FourMomentum const projectileP4{Elab, particle.getMomentum()};
 
     // determine combined full inelastic cross section of the particles in the material
 
     CrossSectionType const total_cx =
         composition.getWeightedSum([=](Code const targetId) -> CrossSectionType {
-          return sequence_.getCrossSection(projectileId, targetId, sqrtSnn);
+          FourMomentum const targetP4(
+              get_mass(targetId),
+              MomentumVector(particle.getMomentum().getCoordinateSystem(),
+                             {0_GeV, 0_GeV, 0_GeV}));
+          return sequence_.getCrossSection(projectileId, targetId, projectileP4,
+                                           targetP4);
         });
 
     // calculate interaction length in medium
@@ -293,9 +296,7 @@ namespace corsika {
     */
     if (distance_interact < distance_decay) {
       // define boost of NUCLEON-NUCLEON frame
-      COMBoost const boost({ElabNN, particle.getMomentum() / projectileA},
-                           constants::nucleonMass);
-      interaction(secondaries, boost, sqrtSnn, composition, total_cx);
+      interaction(secondaries, projectileP4, composition, total_cx);
     } else {
       [[maybe_unused]] auto projectile = secondaries.getProjectile();
 
@@ -334,7 +335,7 @@ namespace corsika {
 
   template <typename TTracking, typename TProcessList, typename TOutput, typename TStack>
   inline ProcessReturn Cascade<TTracking, TProcessList, TOutput, TStack>::interaction(
-      stack_view_type& view, COMBoost const& boost, HEPEnergyType const sqrtSnn,
+      stack_view_type& view, FourMomentum const& projectileP4,
       NuclearComposition const& composition,
       CrossSectionType const initial_cross_section) {
 
@@ -346,7 +347,7 @@ namespace corsika {
 
     UniformRealDistribution<CrossSectionType> uniDist(initial_cross_section);
     CrossSectionType const sample_process_by_cx = uniDist(rng_);
-    auto const returnCode = sequence_.selectInteraction(view, boost, sqrtSnn, composition,
+    auto const returnCode = sequence_.selectInteraction(view, projectileP4, composition,
                                                         rng_, sample_process_by_cx);
     if (returnCode != ProcessReturn::Interacted) {
       CORSIKA_LOG_DEBUG("Particle did not interact!");
diff --git a/corsika/detail/framework/geometry/FourVector.inl b/corsika/detail/framework/geometry/FourVector.inl
index 5d871430d..62542aae5 100644
--- a/corsika/detail/framework/geometry/FourVector.inl
+++ b/corsika/detail/framework/geometry/FourVector.inl
@@ -54,8 +54,8 @@ namespace corsika {
   }
 
   template <typename TTimeType, typename TSpaceVecType>
-  inline FourVector<TTimeType, TSpaceVecType>& FourVector<TTimeType, TSpaceVecType>::
-  operator+=(FourVector const& b) {
+  inline FourVector<TTimeType, TSpaceVecType>&
+  FourVector<TTimeType, TSpaceVecType>::operator+=(FourVector const& b) {
     timeLike_ += b.timeLike_;
     spaceLike_ += b.spaceLike_;
 
@@ -63,39 +63,39 @@ namespace corsika {
   }
 
   template <typename TTimeType, typename TSpaceVecType>
-  inline FourVector<TTimeType, TSpaceVecType>& FourVector<TTimeType, TSpaceVecType>::
-  operator-=(FourVector const& b) {
+  inline FourVector<TTimeType, TSpaceVecType>&
+  FourVector<TTimeType, TSpaceVecType>::operator-=(FourVector const& b) {
     timeLike_ -= b.timeLike_;
     spaceLike_ -= b.spaceLike_;
     return *this;
   }
 
   template <typename TTimeType, typename TSpaceVecType>
-  inline FourVector<TTimeType, TSpaceVecType>& FourVector<TTimeType, TSpaceVecType>::
-  operator*=(double const b) {
+  inline FourVector<TTimeType, TSpaceVecType>&
+  FourVector<TTimeType, TSpaceVecType>::operator*=(double const b) {
     timeLike_ *= b;
     spaceLike_ *= b;
     return *this;
   }
 
   template <typename TTimeType, typename TSpaceVecType>
-  inline FourVector<TTimeType, TSpaceVecType>& FourVector<TTimeType, TSpaceVecType>::
-  operator/=(double const b) {
+  inline FourVector<TTimeType, TSpaceVecType>&
+  FourVector<TTimeType, TSpaceVecType>::operator/=(double const b) {
     timeLike_ /= b;
     spaceLike_.getComponents() /= b;
     return *this;
   }
 
   template <typename TTimeType, typename TSpaceVecType>
-  inline FourVector<TTimeType, TSpaceVecType>& FourVector<TTimeType, TSpaceVecType>::
-  operator/(double const b) {
+  inline FourVector<TTimeType, TSpaceVecType>&
+  FourVector<TTimeType, TSpaceVecType>::operator/(double const b) {
     *this /= b;
     return *this;
   }
 
   template <typename TTimeType, typename TSpaceVecType>
   inline typename FourVector<TTimeType, TSpaceVecType>::norm_type
-      FourVector<TTimeType, TSpaceVecType>::operator*(FourVector const& b) {
+  FourVector<TTimeType, TSpaceVecType>::operator*(FourVector const& b) {
     if constexpr (std::is_same<time_type, decltype(std::declval<space_type>() / meter *
                                                    second)>::value)
       return timeLike_ * b.timeLike_ * constants::cSquared - spaceLike_.norm();
diff --git a/corsika/detail/framework/process/ProcessSequence.inl b/corsika/detail/framework/process/ProcessSequence.inl
index e7718f885..fb977f252 100644
--- a/corsika/detail/framework/process/ProcessSequence.inl
+++ b/corsika/detail/framework/process/ProcessSequence.inl
@@ -314,7 +314,8 @@ namespace corsika {
   ProcessSequence<TProcess1, TProcess2, IndexStart, IndexProcess1,
                   IndexProcess2>::getCrossSection(Code const projectileId,
                                                   Code const targetId,
-                                                  HEPEnergyType const sqrtSnn) const {
+                                                  FourMomentum const& projectileP4,
+                                                  FourMomentum const& targetP4) const {
 
     CrossSectionType tot = CrossSectionType::zero();
 
@@ -322,14 +323,14 @@ namespace corsika {
                                                  // errors if process1_type is invalid
       if constexpr (is_interaction_process_v<process1_type> ||
                     process1_type::is_process_sequence) {
-        tot += A_.getCrossSection(projectileId, targetId, sqrtSnn);
+        tot += A_.getCrossSection(projectileId, targetId, projectileP4, targetP4);
       }
     }
     if constexpr (is_process_v<process2_type>) { // to protect from further compiler
                                                  // errors if process2_type is invalid
       if constexpr (is_interaction_process_v<process2_type> ||
                     process2_type::is_process_sequence) {
-        tot += B_.getCrossSection(projectileId, targetId, sqrtSnn);
+        tot += B_.getCrossSection(projectileId, targetId, projectileP4, targetP4);
       }
     }
     return tot;
@@ -413,8 +414,7 @@ namespace corsika {
   template <typename TSecondaryView>
   inline ProcessReturn
   ProcessSequence<TProcess1, TProcess2, IndexStart, IndexProcess1, IndexProcess2>::
-      selectInteraction(TSecondaryView&& view, COMBoost const& boost,
-                        [[maybe_unused]] HEPEnergyType const sqrtSnn,
+      selectInteraction(TSecondaryView&& view, FourMomentum const& projectileP4,
                         [[maybe_unused]] NuclearComposition const& composition,
                         [[maybe_unused]] TRNG&& rng,
                         [[maybe_unused]] CrossSectionType const cx_select,
@@ -426,8 +426,8 @@ namespace corsika {
                                                  // errors if process1_type is invalid
       if constexpr (process1_type::is_process_sequence) {
         // if A is a process sequence --> check inside
-        ProcessReturn const ret = A_.selectInteraction(view, boost, sqrtSnn, composition,
-                                                       rng, cx_select, cx_sum);
+        ProcessReturn const ret =
+            A_.selectInteraction(view, projectileP4, composition, rng, cx_select, cx_sum);
         // if A_ did succeed, stop routine. Not checking other static branch B_.
         if (ret != ProcessReturn::Ok) { return ret; }
       } else if constexpr (is_interaction_process_v<process1_type>) {
@@ -436,17 +436,23 @@ namespace corsika {
         Code const projectileId = projectile.getPID();
 
         // get cross section vector for all material components
-        static_assert(has_method_getCrossSection_v<TProcess1,        // process object
-                                                   CrossSectionType, // return type
-                                                   Code,             // parameters
-                                                   Code, HEPEnergyType>,
-                      "TProcess1 has no method with correct signature \"CrossSectionType "
-                      "getCrossSection(Code, Code, HEPEnergyType)\" required by "
-                      "InteractionProcess<TProcess1>. ");
+        static_assert(
+            has_method_getCrossSection_v<TProcess1,        // process object
+                                         CrossSectionType, // return type
+                                         Code, Code,       // parameters
+                                         FourMomentum const&, FourMomentum const&>,
+            "TProcess1 has no method with correct signature \"CrossSectionType "
+            "getCrossSection(Code, Code, FourMomentum const&, FourMomentum "
+            "const&)\" required by "
+            "InteractionProcess<TProcess1>. ");
 
         std::vector<CrossSectionType> const weightedCrossSections =
             composition.getWeighted([=](Code const targetId) -> CrossSectionType {
-              return A_.getCrossSection(projectileId, targetId, sqrtSnn);
+              FourMomentum const targetP4(
+                  get_mass(targetId),
+                  MomentumVector(projectile.getMomentum().getCoordinateSystem(),
+                                 {0_GeV, 0_GeV, 0_GeV}));
+              return A_.getCrossSection(projectileId, targetId, projectileP4, targetP4);
             });
 
         cx_sum += std::accumulate(weightedCrossSections.cbegin(),
@@ -457,6 +463,10 @@ namespace corsika {
 
           // now also sample targetId from weighted cross sections
           Code const targetId = composition.sampleTarget(weightedCrossSections, rng);
+          FourMomentum const targetP4(
+              get_mass(targetId),
+              MomentumVector(projectile.getMomentum().getCoordinateSystem(),
+                             {0_GeV, 0_GeV, 0_GeV}));
 
           // interface checking on TProcess1
           static_assert(
@@ -464,13 +474,14 @@ namespace corsika {
                                       void,            // return type
                                       TSecondaryView,  // template argument
                                       TSecondaryView&, // method parameters
-                                      COMBoost const&, Code, Code, HEPEnergyType>,
+                                      Code, Code, FourMomentum const&,
+                                      FourMomentum const&>,
               "TProcess1 has no method with correct signature \"void "
-              "doInteraction<TSecondaryView>(TSecondaryView&, COMBoost&, Code, "
-              "Code, HEPEnergyType)\" required for "
+              "doInteraction<TSecondaryView>(TSecondaryView&, "
+              "Code, Code, FourMomentum const&, FourMomentum const&)\" required for "
               "InteractionProcess<TProcess1>. ");
 
-          A_.template doInteraction(view, boost, projectileId, targetId, sqrtSnn);
+          A_.template doInteraction(view, projectileId, targetId, projectileP4, targetP4);
 
           return ProcessReturn::Interacted;
         }
@@ -482,7 +493,7 @@ namespace corsika {
 
       if constexpr (process2_type::is_process_sequence) {
         // if B_ is a process sequence --> check inside
-        return B_.selectInteraction(view, boost, sqrtSnn, composition, rng, cx_select,
+        return B_.selectInteraction(view, projectileP4, composition, rng, cx_select,
                                     cx_sum);
       } else if constexpr (is_interaction_process_v<process2_type>) {
 
@@ -492,15 +503,20 @@ namespace corsika {
         // get cross section vector for all material components
         static_assert(has_method_getCrossSection_v<TProcess2,        // process object
                                                    CrossSectionType, // return type
-                                                   Code,             // parameters
-                                                   Code, HEPEnergyType>,
+                                                   Code, Code, FourMomentum const&,
+                                                   FourMomentum const&>, // parameters
                       "TProcess2 has no method with correct signature \"CrossSectionType "
-                      "getCrossSection(Code, Code, HEPEnergyType)\" required by "
+                      "getCrossSection(Code, Code, FourMomentum const&, FourMomentum "
+                      "const&)\" required by "
                       "InteractionProcess<TProcess1>. ");
 
         std::vector<CrossSectionType> const weightedCrossSections =
             composition.getWeighted([=](Code const targetId) -> CrossSectionType {
-              return B_.getCrossSection(projectileId, targetId, sqrtSnn);
+              FourMomentum const targetP4(
+                  get_mass(targetId),
+                  MomentumVector(projectile.getMomentum().getCoordinateSystem(),
+                                 {0_GeV, 0_GeV, 0_GeV}));
+              return B_.getCrossSection(projectileId, targetId, projectileP4, targetP4);
             });
 
         cx_sum += std::accumulate(weightedCrossSections.begin(),
@@ -511,6 +527,10 @@ namespace corsika {
 
           // now also sample targetId from weighted cross sections
           Code const targetId = composition.sampleTarget(weightedCrossSections, rng);
+          FourMomentum const targetP4(
+              get_mass(targetId),
+              MomentumVector(projectile.getMomentum().getCoordinateSystem(),
+                             {0_GeV, 0_GeV, 0_GeV}));
 
           // interface checking on TProcess2
           static_assert(
@@ -518,13 +538,14 @@ namespace corsika {
                                       void,            // return type
                                       TSecondaryView,  // template argument
                                       TSecondaryView&, // method parameters
-                                      COMBoost const&, Code, Code, HEPEnergyType>,
+                                      Code, Code, FourMomentum const&,
+                                      FourMomentum const&>,
               "TProcess1 has no method with correct signature \"void "
-              "doInteraction<TSecondaryView>(TSecondaryView&, COMBoost&, Code, "
-              "Code, HEPEnergyType)\" required for "
+              "doInteraction<TSecondaryView>(TSecondaryView&, "
+              "Code, Code, FourMomentum const&, FourMomentum const&)\" required for "
               "InteractionProcess<TProcess2>. ");
 
-          B_.doInteraction(view, boost, projectileId, targetId, sqrtSnn);
+          B_.doInteraction(view, projectileId, targetId, projectileP4, targetP4);
 
           return ProcessReturn::Interacted;
         }
diff --git a/corsika/detail/framework/process/SwitchProcessSequence.inl b/corsika/detail/framework/process/SwitchProcessSequence.inl
index 46e9bfbd2..b7cf27db0 100644
--- a/corsika/detail/framework/process/SwitchProcessSequence.inl
+++ b/corsika/detail/framework/process/SwitchProcessSequence.inl
@@ -243,8 +243,8 @@ namespace corsika {
   template <typename TSecondaryView, typename TRNG>
   inline ProcessReturn SwitchProcessSequence<
       TCondition, TSequence, USequence, IndexStart, IndexProcess1,
-      IndexProcess2>::selectInteraction(TSecondaryView& view, COMBoost const& boost,
-                                        HEPEnergyType const sqrtSnn,
+      IndexProcess2>::selectInteraction(TSecondaryView& view,
+                                        FourMomentum const& projectileP4,
                                         NuclearComposition const& composition, TRNG& rng,
                                         [[maybe_unused]] CrossSectionType const cx_select,
                                         [[maybe_unused]] CrossSectionType cx_sum) {
@@ -252,7 +252,7 @@ namespace corsika {
     if (select_(view.parent())) {
       if constexpr (process1_type::is_process_sequence) {
         // if A_ is a process sequence --> check inside
-        return A_.selectInteraction(view, boost, sqrtSnn, composition, rng, cx_select,
+        return A_.selectInteraction(view, projectileP4, composition, rng, cx_select,
                                     cx_sum);
       } else if constexpr (is_interaction_process_v<process1_type>) {
 
@@ -265,16 +265,18 @@ namespace corsika {
             has_method_getCrossSection_v<TSequence,        // process object
                                          CrossSectionType, // return type
                                          Code,             // parameters
-                                         Code, HEPEnergyType, unsigned int, unsigned int>,
+                                         Code, FourMomentum const&, FourMomentum const&>,
             "TSequence has no method with correct signature \"CrossSectionType "
-            "getCrossSection(Code, Code, HEPEnergyType, unsigned int, unsigned int"
-            ")\" required by InteractionProcess<TSequence>. ");
+            "getCrossSection(Code, Code, FourMomentum const&, FourMomntum const&)\" "
+            "required by InteractionProcess<TSequence>. ");
 
         std::vector<CrossSectionType> const weightedCrossSections =
             composition.getWeighted([=](Code const targetId) -> CrossSectionType {
-              return A_.getCrossSection(
-                  projectileId, targetId, sqrtSnn, projectileA,
-                  is_nucleus(targetId) ? get_nucleus_A(targetId) : 0);
+              FourMomentum const targetP4(
+                  get_mass(targetId),
+                  MomentumVector(projectile.getMomentum().getCoordinateSystem(),
+                                 {0_GeV, 0_GeV, 0_GeV}));
+              return A_.getCrossSection(projectileId, targetId, projectileP4, targetP4);
             });
 
         cx_sum += std::accumulate(weightedCrossSections.cbegin(),
@@ -283,23 +285,24 @@ namespace corsika {
 
           // now also sample targetId from weighted cross sections
           Code const targetId = composition.sampleTarget(weightedCrossSections, rng);
+          FourMomentum const targetP4(
+              get_mass(targetId),
+              MomentumVector(projectile.getMomentum().getCoordinateSystem(),
+                             {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
-                                      COMBoost const&, Code, Code, HEPEnergyType,
-                                      unsigned int, unsigned int>,
-              "USequence has no method with correct signature \"void "
-              "doInteraction<TSecondaryView>(TSecondaryView&, COMBoost&, Code, "
-              "Code, HEPEnergyType, unsigned int, unsigned int)\" required for "
-              "InteractionProcess<USequence>. ");
-
-          A_.template doInteraction(view, boost, projectileId, targetId, sqrtSnn,
-                                    projectileA,
-                                    is_nucleus(targetId) ? get_nucleus_A(targetId) : 0);
+          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>. ");
+
+          A_.template doInteraction(view, projectileId, targetId, projectileP4, targetP4);
 
           return ProcessReturn::Interacted;
 
@@ -310,7 +313,7 @@ namespace corsika {
 
       if constexpr (process2_type::is_process_sequence) {
         // if B_ is a process sequence --> check inside
-        return B_.selectInteraction(view, boost, sqrtSnn, composition, rng, cx_select,
+        return B_.selectInteraction(view, projectileP4, composition, rng, cx_select,
                                     cx_sum);
       } else if constexpr (is_interaction_process_v<process2_type>) {
 
@@ -323,16 +326,18 @@ namespace corsika {
             has_method_getCrossSection_v<USequence,        // process object
                                          CrossSectionType, // return type
                                          Code,             // parameters
-                                         Code, HEPEnergyType, unsigned int, unsigned int>,
+                                         Code, FourMomentum const&, FourMomentum const&>,
             "USequence has no method with correct signature \"CrossSectionType "
-            "getCrossSection(Code, Code, HEPEnergyType, unsigned int, unsigned int"
-            ")\" required by InteractionProcess<USequence>. ");
+            "getCrossSection(Code, Code, FourMomentum const&, FourMomentum const&)\" "
+            "required by InteractionProcess<USequence>. ");
 
         std::vector<CrossSectionType> const weightedCrossSections =
             composition.getWeighted([=](Code const targetId) -> CrossSectionType {
-              return B_.getCrossSection(
-                  projectileId, targetId, sqrtSnn, projectileA,
-                  is_nucleus(targetId) ? get_nucleus_A(targetId) : 0);
+              FourMomentum const targetP4(
+                  get_mass(targetId),
+                  MomentumVector(projectile.getMomentum().getCoordinateSystem(),
+                                 {0_GeV, 0_GeV, 0_GeV}));
+              return B_.getCrossSection(projectileId, targetId, projectileP4, targetP4);
             });
 
         cx_sum += std::accumulate(weightedCrossSections.cbegin(),
@@ -342,23 +347,24 @@ namespace corsika {
 
           // now also sample targetId from weighted cross sections
           Code const targetId = composition.sampleTarget(weightedCrossSections, rng);
+          FourMomentum const targetP4(
+              get_mass(targetId),
+              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
-                                      COMBoost const&, Code, Code, HEPEnergyType,
-                                      unsigned int, unsigned int>,
-              "USequence has no method with correct signature \"void "
-              "doInteraction<TSecondaryView>(TSecondaryView&, COMBoost&, Code, "
-              "Code, HEPEnergyType, unsigned int, unsigned int)\" required for "
-              "InteractionProcess<USequence>. ");
-
-          B_.template doInteraction(view, boost, projectileId, targetId, sqrtSnn,
-                                    projectileA,
-                                    is_nucleus(targetId) ? get_nucleus_A(targetId) : 0);
+          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);
 
           return ProcessReturn::Interacted;
         } // end collision in branch B
diff --git a/corsika/detail/framework/utility/COMBoost.inl b/corsika/detail/framework/utility/COMBoost.inl
index 842a6f99e..7551b6e0a 100644
--- a/corsika/detail/framework/utility/COMBoost.inl
+++ b/corsika/detail/framework/utility/COMBoost.inl
@@ -19,17 +19,17 @@
 
 namespace corsika {
 
-  inline COMBoost::COMBoost(FourVector<HEPEnergyType, MomentumVector> const& Pprojectile,
+  inline COMBoost::COMBoost(FourMomentum const& P4projectile,
                             HEPMassType const massTarget)
-      : originalCS_{Pprojectile.getSpaceLikeComponents().getCoordinateSystem()}
+      : originalCS_{P4projectile.getSpaceLikeComponents().getCoordinateSystem()}
       , rotatedCS_{
-            make_rotationToZ(Pprojectile.getSpaceLikeComponents().getCoordinateSystem(),
-                             Pprojectile.getSpaceLikeComponents())} {
-    auto const pProjectile = Pprojectile.getSpaceLikeComponents();
+            make_rotationToZ(P4projectile.getSpaceLikeComponents().getCoordinateSystem(),
+                             P4projectile.getSpaceLikeComponents())} {
+    auto const pProjectile = P4projectile.getSpaceLikeComponents();
     auto const pProjNormSquared = pProjectile.getSquaredNorm();
     auto const pProjNorm = sqrt(pProjNormSquared);
 
-    auto const eProjectile = Pprojectile.getTimeLikeComponent();
+    auto const eProjectile = P4projectile.getTimeLikeComponent();
     auto const massProjectileSquared = eProjectile * eProjectile - pProjNormSquared;
     auto const s =
         massTarget * massTarget + massProjectileSquared + 2 * eProjectile * massTarget;
@@ -43,7 +43,29 @@ namespace corsika {
                       coshEta, boost_.determinant() - 1);
   }
 
-  inline COMBoost::COMBoost(MomentumVector const& momentum, HEPEnergyType mass)
+  inline COMBoost::COMBoost(FourMomentum const& P4projectile,
+                            FourMomentum const& P4target)
+      : originalCS_{P4projectile.getSpaceLikeComponents().getCoordinateSystem()}
+      , rotatedCS_{make_rotationToZ(
+            P4projectile.getSpaceLikeComponents().getCoordinateSystem(),
+            P4projectile.getSpaceLikeComponents() + P4target.getSpaceLikeComponents())} {
+    // this is the center-of-momentum CM frame
+    auto const pCM =
+        P4projectile.getSpaceLikeComponents() + P4target.getSpaceLikeComponents();
+    auto const pCM2 = pCM.getSquaredNorm();
+    auto const pCMnorm = sqrt(pCM2);
+
+    auto const s = (P4projectile + P4target).getNormSqr();
+    auto const sqrtS = sqrt(s);
+    auto const sinhEta = -pCMnorm / sqrtS;
+    auto const coshEta = sqrt(1 + pCM2 / s);
+
+    setBoost(coshEta, sinhEta);
+    CORSIKA_LOG_TRACE("COMBoost (1-beta)={}, gamma={}, det={}", 1 - sinhEta / coshEta,
+                      coshEta, boost_.determinant() - 1);
+  }
+
+  inline COMBoost::COMBoost(MomentumVector const& momentum, HEPEnergyType const mass)
       : originalCS_{momentum.getCoordinateSystem()}
       , rotatedCS_{make_rotationToZ(momentum.getCoordinateSystem(), momentum)} {
     auto const squaredNorm = momentum.getSquaredNorm();
@@ -56,12 +78,12 @@ namespace corsika {
   }
 
   template <typename FourVector>
-  inline FourVector COMBoost::toCoM(FourVector const& p) const {
-    auto pComponents = p.getSpaceLikeComponents().getComponents(rotatedCS_);
+  inline FourVector COMBoost::toCoM(FourVector const& p4) const {
+    auto pComponents = p4.getSpaceLikeComponents().getComponents(rotatedCS_);
     Eigen::Vector3d eVecRotated = pComponents.getEigenVector();
     Eigen::Vector2d lab;
 
-    lab << (p.getTimeLikeComponent() * (1 / 1_GeV)),
+    lab << (p4.getTimeLikeComponent() * (1 / 1_GeV)),
         (eVecRotated(2) * (1 / 1_GeV).magnitude());
 
     auto const boostedZ = boost_ * lab;
@@ -73,9 +95,9 @@ namespace corsika {
   }
 
   template <typename FourVector>
-  inline FourVector COMBoost::fromCoM(FourVector const& p) const {
-    auto pCM = p.getSpaceLikeComponents().getComponents(rotatedCS_);
-    auto const Ecm = p.getTimeLikeComponent();
+  inline FourVector COMBoost::fromCoM(FourVector const& p4) const {
+    auto pCM = p4.getSpaceLikeComponents().getComponents(rotatedCS_);
+    auto const Ecm = p4.getTimeLikeComponent();
 
     Eigen::Vector2d com;
     com << (Ecm * (1 / 1_GeV)), (pCM.getEigenVector()(2) * (1 / 1_GeV).magnitude());
@@ -83,7 +105,7 @@ namespace corsika {
     CORSIKA_LOG_TRACE(
         "COMBoost::fromCoM Ecm={} GeV"
         " pcm={} GeV (norm = {} GeV), invariant mass={} GeV",
-        Ecm / 1_GeV, pCM / 1_GeV, pCM.getNorm() / 1_GeV, p.getNorm() / 1_GeV);
+        Ecm / 1_GeV, pCM / 1_GeV, pCM.getNorm() / 1_GeV, p4.getNorm() / 1_GeV);
 
     auto const boostedZ = inverseBoost_ * com;
     auto const E_lab = boostedZ(0) * 1_GeV;
@@ -102,7 +124,7 @@ namespace corsika {
     return FourVector{E_lab, pLab};
   }
 
-  inline void COMBoost::setBoost(double coshEta, double sinhEta) {
+  inline void COMBoost::setBoost(double const coshEta, double const sinhEta) {
     boost_ << coshEta, sinhEta, sinhEta, coshEta;
     inverseBoost_ << coshEta, -sinhEta, -sinhEta, coshEta;
   }
diff --git a/corsika/detail/modules/sibyll/InteractionModel.inl b/corsika/detail/modules/sibyll/InteractionModel.inl
index 604ea7b9e..c0716be3a 100644
--- a/corsika/detail/modules/sibyll/InteractionModel.inl
+++ b/corsika/detail/modules/sibyll/InteractionModel.inl
@@ -60,9 +60,13 @@ namespace corsika::sibyll {
 
   inline std::tuple<CrossSectionType, CrossSectionType>
   InteractionModel::getCrossSectionInelEla(Code const projectileId, Code const targetId,
-                                           HEPEnergyType const sqrtSnn) const {
+                                           FourMomentum const& projectileP4,
+                                           FourMomentum const& targetP4) const {
 
-    isValid(projectileId, targetId, sqrtSnn); // throws
+    int targetSibCode = 1; // nucleon or particle count
+    if (is_nucleus(targetId)) { targetSibCode = get_nucleus_A(targetId); }
+    // sqrtS per target nucleon
+    HEPEnergyType const sqrtSnn = (projectileP4 + targetP4 / targetSibCode).getNorm();
 
     double dummy, dum1, dum3, dum4, dumdif[3]; // dummies needed for fortran call
     int const iBeam = corsika::sibyll::getSibyllXSCode(
@@ -92,24 +96,23 @@ namespace corsika::sibyll {
 
   template <typename TSecondaryView>
   inline void InteractionModel::doInteraction(TSecondaryView& secondaries,
-                                              COMBoost const& boost,
                                               Code const projectileId,
                                               Code const targetId,
-                                              HEPEnergyType const sqrtSnn) {
+                                              FourMomentum const& projectileP4,
+                                              FourMomentum const& targetP4) {
+
+    int targetSibCode = 1; // nucleon or particle count
+    if (is_nucleus(targetId)) { targetSibCode = get_nucleus_A(targetId); }
+    CORSIKA_LOG_DEBUG("sibyll code: {} (nucleon/particle count)", targetSibCode);
+
+    // sqrtS per target nucleon
+    HEPEnergyType const sqrtSnn = (projectileP4 + targetP4 / targetSibCode).getNorm();
+    COMBoost const boost(projectileP4, targetP4 / targetSibCode);
 
     isValid(projectileId, targetId, sqrtSnn); // throws
 
     CORSIKA_LOG_DEBUG("pId={} tId={} sqrtSnn={}GeV", projectileId, targetId, sqrtSnn);
 
-    int targetSibCode = -1;
-    if (is_nucleus(targetId)) {
-      targetSibCode = get_nucleus_A(targetId);
-    } else {
-      // nucleon target: p or n
-      targetSibCode = 1;
-    }
-    CORSIKA_LOG_DEBUG("sibyll code: {}", targetSibCode);
-
     // beam id for sibyll
     int const projectileSibyllCode = corsika::sibyll::convertToSibyllRaw(projectileId);
 
@@ -177,6 +180,6 @@ namespace corsika::sibyll {
         Elab_final / 1_GeV, (Elab_final - Elab_initial) / Elab_initial * 100,
         constants::nucleonMass * get_nwounded() / 1_GeV,
         (Plab_final / 1_GeV).getComponents());
-  }
+  } // namespace corsika::sibyll
 
 } // namespace corsika::sibyll
diff --git a/corsika/detail/modules/sibyll/NuclearInteractionModel.inl b/corsika/detail/modules/sibyll/NuclearInteractionModel.inl
index 7c84c79e5..906cfa8b6 100644
--- a/corsika/detail/modules/sibyll/NuclearInteractionModel.inl
+++ b/corsika/detail/modules/sibyll/NuclearInteractionModel.inl
@@ -116,8 +116,7 @@ namespace corsika::sibyll {
       if (ptarg == Code::Argon) continue; // NEED TO IGNORE Argon ....
       ++k;
       CORSIKA_LOG_DEBUG("init target component: {}", ptarg);
-      int const ib =
-          get_nucleus_A(ptarg); // this assumes the universe is only made out of "Nuclei"
+      int const ib = get_nucleus_A(ptarg);
       hadronicInteraction_.isValid(Code::Proton, ptarg, 100_GeV); // throws
       targetComponentsIndex_.insert(std::pair<Code, int>(ptarg, k));
       // loop over energies, fNEnBins log. energy bins
@@ -125,10 +124,15 @@ namespace corsika::sibyll {
         // hard coded energy grid, has to be aligned to definition in signuc2!!, no
         // comment..
         HEPEnergyType const Ecm = pow(10., 1. + 1. * i) * 1_GeV;
+        CoordinateSystemPtr cs = get_root_CoordinateSystem();
+        HEPMomentumType const pcm = sqrt(Ecm * Ecm - Proton::mass * Proton::mass);
+        FourMomentum projectileP4(Ecm,
+                                  {cs, pcm, 0_eV, 0_eV}); // this is ONLY needd for sqrtS
+        FourMomentum targetP4(0_eV, {cs, 0_eV, 0_eV, 0_eV});
         // get p-p cross sections
         auto const protonId = Code::Proton;
-        auto const [siginel, sigela] =
-            hadronicInteraction_.getCrossSectionInelEla(protonId, protonId, Ecm);
+        auto const [siginel, sigela] = hadronicInteraction_.getCrossSectionInelEla(
+            protonId, protonId, projectileP4, targetP4);
         const double dsig = siginel / 1_mb;
         const double dsigela = sigela / 1_mb;
         // loop over projectiles, mass numbers from 2 to fMaxNucleusAProjectile
@@ -170,8 +174,10 @@ namespace corsika::sibyll {
   CrossSectionType inline NuclearInteractionModel<
       TEnvironment, TNucleonModel>::getCrossSection(Code const projectileId,
                                                     Code const targetId,
-                                                    HEPEnergyType const sqrtSnn) const {
+                                                    FourMomentum const& projectileP4,
+                                                    FourMomentum const& targetP4) const {
 
+    HEPEnergyType const sqrtSnn = (projectileP4 + targetP4).getNorm();
     isValid(projectileId, targetId, sqrtSnn); // throws
     HEPEnergyType const LabEnergyPerNuc =
         static_pow<2>(sqrtSnn) / (2 * constants::nucleonMass);
@@ -184,26 +190,29 @@ namespace corsika::sibyll {
   template <typename TEnvironment, typename TNucleonModel>
   template <typename TSecondaryView>
   inline void NuclearInteractionModel<TEnvironment, TNucleonModel>::doInteraction(
-      TSecondaryView& view, COMBoost const& boost, Code const projectileId,
-      Code const targetId, HEPEnergyType const sqrtSnn) {
+      TSecondaryView& view, Code const projectileId, Code const targetId,
+      FourMomentum const& projectileP4, FourMomentum const& targetP4) {
 
-    isValid(projectileId, targetId, sqrtSnn); // throws
+    // model is only designed for projectile nuclei. Collisions are broken down into
+    // "nucleon-target" collisions.
+    size_t const projectileA = get_nucleus_A(projectileId);
+
+    // this is center-of-mass for projectile_nucleon - target
+    FourMomentum const nucleonP4 = projectileP4 / projectileA;
+    HEPEnergyType const sqrtSnucleon = (nucleonP4 + targetP4).getNorm();
+    isValid(projectileId, targetId, sqrtSnucleon); // throws
     // projectile is always nucleus!
-    unsigned int const projectileA = get_nucleus_A(projectileId);
+    // Elab corresponding to sqrtSnucleon -> fixed target projectile
+    COMBoost const boost(nucleonP4, targetP4);
 
-    CORSIKA_LOG_DEBUG("pId={} tId={} sqrtSnn={}GeV Aproj={}", projectileId, targetId,
-                      sqrtSnn / 1_GeV, projectileA);
+    CORSIKA_LOG_DEBUG("pId={} tId={} sqrtSnucleon={}GeV Aproj={}", projectileId, targetId,
+                      sqrtSnucleon / 1_GeV, projectileA);
     count_++;
 
-    HEPEnergyType const ProjMass = get_mass(projectileId);
-
-    // lab. Energy per projectile nucleon
-    HEPEnergyType const eProjectileLab =
-        static_pow<2>(sqrtSnn) / (2 * constants::nucleonMass);
-    HEPMomentumType const pProjectileLab =
-        sqrt(static_pow<2>(eProjectileLab) - static_pow<2>(constants::nucleonMass));
-    MomentumVector const p3ProjectileLab(boost.getRotatedCS(),
-                                         {0_GeV, 0_GeV, pProjectileLab});
+    // lab. momentum per projectile nucleon
+    HEPMomentumType const pNucleonLab = nucleonP4.getSpaceLikeComponents().getNorm();
+    // nucleon momentum in direction of CM motion (lab system)
+    MomentumVector const p3NucleonLab(boost.getRotatedCS(), {0_GeV, 0_GeV, pNucleonLab});
 
     /*
       FOR NOW: allow nuclei with A<18 or protons/nucleon only.
@@ -211,8 +220,10 @@ namespace corsika::sibyll {
       allowed air in atmosphere also contains some Argon.
     */
     int kATarget = -1;
+    size_t targetA = 1;
     if (is_nucleus(targetId)) {
       kATarget = get_nucleus_A(targetId);
+      targetA = kATarget;
     } else if (targetId == Code::Proton || targetId == Code::Neutron ||
                targetId == Code::Hydrogen) {
       kATarget = 1;
@@ -227,7 +238,9 @@ namespace corsika::sibyll {
     // (needed to determine number of nucleon-nucleon scatterings)
     auto const protonId = Code::Proton;
     auto const [prodCrossSection, elaCrossSection] =
-        hadronicInteraction_.getCrossSectionInelEla(protonId, protonId, sqrtSnn);
+        hadronicInteraction_.getCrossSectionInelEla(
+            protonId, protonId, nucleonP4,
+            targetP4 / targetA); // todo check, wrong RU
     double const sigProd = prodCrossSection / 1_mb;
     double const sigEla = elaCrossSection / 1_mb;
     // sample number of interactions (only input variables, output in common cnucms)
@@ -270,8 +283,10 @@ namespace corsika::sibyll {
     // (LCOV_EXCL_STOP)
 
     // position and time of interaction, not used in NUCLIB
-    Point pOrig{boost.getOriginalCS(), {0_m, 0_m, 0_m}};
-    TimeType delay = 0_s; // there is no time in sibyll
+    auto const& projectile = view.parent();
+    // position and time of interaction, not used in NUCLI
+    Point const& pOrig = projectile.getPosition();
+    TimeType const delay = projectile.getTime();
 
     CORSIKA_LOG_DEBUG("Interaction: position of interaction: {} {}",
                       pOrig.getCoordinates(), delay / 1_s);
@@ -298,12 +313,8 @@ namespace corsika::sibyll {
 
       // CORSIKA 7 way
       // spectators inherit momentum from original projectile
-      double const mass_ratio = mass / ProjMass;
-      auto const p3lab = p3ProjectileLab * mass_ratio;
-
-      CORSIKA_LOG_DEBUG("mass ratio {}, fragment momentum {}", mass_ratio,
-                        p3lab.getComponents() / 1_GeV);
-
+      auto const p3lab = p3NucleonLab * nuclA;
+      CORSIKA_LOG_DEBUG("fragment momentum {}", p3lab.getComponents() / 1_GeV);
       view.addSecondary(std::make_tuple(specCode, p3lab, pOrig, delay));
     }
 
@@ -318,8 +329,7 @@ namespace corsika::sibyll {
       // CORSIKA 7 way
       // elastic nucleons inherit momentum from original projectile
       // neglecting momentum transfer in interaction
-      double const mass_ratio = get_mass(elaNucCode) / ProjMass;
-      auto const p3lab = p3ProjectileLab * mass_ratio;
+      auto const p3lab = p3NucleonLab;
       view.addSecondary(std::make_tuple(elaNucCode, p3lab, pOrig, delay));
     }
 
@@ -327,20 +337,20 @@ namespace corsika::sibyll {
     CORSIKA_LOG_DEBUG("calculate inelastic nucleon-nucleon interactions..");
     for (int j = 0; j < nInelNucleons; ++j) {
       // TODO: sample neutron or proton
-      auto pCode = Code::Proton;
+      auto const pCode = Code::Proton;
       // temporarily add to stack, will be removed after interaction in DoInteraction
       CORSIKA_LOG_DEBUG("inelastic interaction no. {}", j);
       typename TSecondaryView::inner_stack_value_type nucleonStack;
       auto inelasticNucleon =
-          nucleonStack.addParticle(std::make_tuple(pCode, p3ProjectileLab, pOrig, delay));
+          nucleonStack.addParticle(std::make_tuple(pCode, p3NucleonLab, pOrig, delay));
       inelasticNucleon.setNode(view.getProjectile().getNode());
       // create inelastic interaction for each nucleon
       CORSIKA_LOG_TRACE("calling HadronicInteraction...");
       // create new StackView for each of the nucleons
       TSecondaryView nucleon_secondaries(inelasticNucleon);
       // all inner hadronic event generator
-      hadronicInteraction_.doInteraction(nucleon_secondaries, boost, pCode, targetId,
-                                         sqrtSnn);
+      hadronicInteraction_.doInteraction(nucleon_secondaries, pCode, targetId, nucleonP4,
+                                         targetP4);
       for (const auto& pSec : nucleon_secondaries) {
         view.addSecondary(std::make_tuple(pSec.getPID(), pSec.getMomentum(),
                                           pSec.getPosition(), pSec.getTime()));
diff --git a/corsika/framework/core/Cascade.hpp b/corsika/framework/core/Cascade.hpp
index 796655a03..ac7a899eb 100644
--- a/corsika/framework/core/Cascade.hpp
+++ b/corsika/framework/core/Cascade.hpp
@@ -12,11 +12,12 @@
 
 #include <corsika/framework/process/ProcessReturn.hpp>
 #include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/core/Logging.hpp>
 #include <corsika/framework/random/ExponentialDistribution.hpp>
 #include <corsika/framework/random/RNGManager.hpp>
 #include <corsika/framework/random/UniformRealDistribution.hpp>
 #include <corsika/framework/stack/SecondaryView.hpp>
-#include <corsika/framework/core/Logging.hpp>
+#include <corsika/framework/geometry/FourVector.hpp>
 
 #include <corsika/media/Environment.hpp>
 
@@ -33,8 +34,6 @@
 
 namespace corsika {
 
-  class COMBoost; // fwd-decl
-
   /**
    *
    * The Cascade class is constructed from template arguments making
@@ -98,7 +97,7 @@ namespace corsika {
 
     /**
      * set the nodes for all particles on the stack according to their numerical
-     * position
+     * position.
      */
     void setNodes();
 
@@ -129,8 +128,7 @@ namespace corsika {
     void step(particle_type& vParticle);
 
     ProcessReturn decay(stack_view_type& view, InverseTimeType initial_inv_decay_time);
-    ProcessReturn interaction(stack_view_type& view, COMBoost const& boost,
-                              HEPEnergyType const sqrtSnn,
+    ProcessReturn interaction(stack_view_type& view, FourMomentum const& projectileP4,
                               NuclearComposition const& composition,
                               CrossSectionType const initial_cross_section);
     void setEventType(stack_view_type& view, history::EventType);
diff --git a/corsika/framework/geometry/FourVector.hpp b/corsika/framework/geometry/FourVector.hpp
index 0b87accec..0a23f4e94 100644
--- a/corsika/framework/geometry/FourVector.hpp
+++ b/corsika/framework/geometry/FourVector.hpp
@@ -9,36 +9,42 @@
 #pragma once
 
 #include <corsika/framework/core/PhysicalUnits.hpp>
-#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/core/PhysicalGeometry.hpp>
 #include <type_traits>
 
+/**
+ * @file FourVector.hpp
+ * @author Ralf Ulrich
+ * @brief General FourVector object.
+ * @date 2021-10-16
+ */
+
 namespace corsika {
 
   /**
-     Description of physical four-vectors
-
-     FourVector fully supports units, e.g. E in [GeV/c] and p in [GeV],
-     or also t in [s] and r in [m], etc.
-
-     However, for HEP applications it is also possible to use E and p
-     both in [GeV].
-
-     Thus, the input units of time-like and space-like coordinates
-     must either be idential (e.g. GeV) or scaled by "c" as in
-     [E/c]=[p].
-
-
-     The FourVector can return its squared-norm \ref getNormSqr and its
-     norm \ref getNorm, whereas norm is sqrt(abs(norm-squared)). The
-     physical units are always calculated and returned properly.
-
-     FourVector can also return if it is TimeLike, SpaceLike or PhotonLike.
-
-     When a FourVector is initialized with a lvalue references,
-     e.g. as `FourVector<TimeType&, Vector<length_d>&>`, references
-     are also used as internal data types, which should lead to
-     complete disappearance of the FourVector class during
-     optimization.
+   * Description of physical four-vectors
+   *
+   * FourVector fully supports units, e.g. E in [GeV/c] and p in [GeV],
+   * or also t in [s] and r in [m], etc.
+   *
+   * However, for HEP applications it is also possible to use E and p
+   * both in [GeV].
+   *
+   * Thus, the input units of time-like and space-like coordinates
+   * must either be idential (e.g. GeV) or scaled by "c" as in
+   * [E/c]=[p].
+   *
+   * The FourVector can return its squared-norm \ref getNormSqr and its
+   * norm \ref getNorm, whereas norm is sqrt(abs(norm-squared)). The
+   * physical units are always calculated and returned properly.
+   *
+   * FourVector can also return if it is TimeLike, SpaceLike or PhotonLike.
+   *
+   * When a FourVector is initialized with a lvalue references,
+   * e.g. as `FourVector<TimeType&, Vector<length_d>&>`, references
+   * are also used as internal data types, which should lead to
+   * complete disappearance of the FourVector class during
+   * optimization.
    */
 
   template <typename TTimeType, typename TSpaceVecType>
@@ -73,13 +79,11 @@ namespace corsika {
         , spaceLike_(eS) {}
 
     /**
-     *
      * @return timeLike_
      */
     TTimeType getTimeLikeComponent() const;
 
     /**
-     *
      * @return spaceLike_
      */
     TSpaceVecType& getSpaceLikeComponents();
@@ -124,12 +128,12 @@ namespace corsika {
     FourVector& operator/(double const);
 
     /**
-       Scalar product of two FourVectors
-
-       Note that the product between two 4-vectors assumes that you use
-       the same "c" convention for both. Only the LHS vector is checked
-       for this. You cannot mix different conventions due to
-       unit-checking.
+     * Scalar product of two FourVectors.
+     *
+     *  Note that the product between two 4-vectors assumes that you use
+     *  the same "c" convention for both. Only the LHS vector is checked
+     *  for this. You cannot mix different conventions due to
+     *  unit-checking.
      */
     norm_type operator*(FourVector const& b);
 
@@ -152,7 +156,7 @@ namespace corsika {
      *  value-copies.
      * @{
      *
-     **/
+     */
     friend FourVector<time_type, space_vec_type> operator+(FourVector const& a,
                                                            FourVector const& b) {
       return FourVector<time_type, space_vec_type>(a.timeLike_ + b.timeLike_,
@@ -179,20 +183,25 @@ namespace corsika {
 
   private:
     /**
-       This function is there to automatically remove the eventual
-       extra factor of "c" for the time-like quantity.
+     * This function is there to automatically remove the eventual
+     * extra factor of "c" for the time-like quantity.
      */
     norm_square_type getTimeSquared() const;
   };
 
   /**
    * streaming operator
-   **/
+   */
 
   template <typename TTimeType, typename TSpaceVecType>
   std::ostream& operator<<(std::ostream& os,
                            corsika::FourVector<TTimeType, TSpaceVecType> const& qv);
 
+  /**
+   * @typedef FourMomentum A FourVector with HEPEnergyType and MomentumVector.
+   */
+  typedef FourVector<HEPEnergyType, MomentumVector> FourMomentum;
+
 } // namespace corsika
 
 #include <corsika/detail/framework/geometry/FourVector.inl>
diff --git a/corsika/framework/process/ProcessSequence.hpp b/corsika/framework/process/ProcessSequence.hpp
index b14c9d8d3..4ecf3d0cd 100644
--- a/corsika/framework/process/ProcessSequence.hpp
+++ b/corsika/framework/process/ProcessSequence.hpp
@@ -29,6 +29,8 @@
 #include <corsika/framework/core/PhysicalUnits.hpp>
 #include <corsika/framework/core/ParticleProperties.hpp>
 
+#include <corsika/framework/geometry/FourVector.hpp>
+
 namespace corsika {
 
   class COMBoost;           // fwd-decl
@@ -242,16 +244,43 @@ namespace corsika {
     ContinuousProcessStepLength getMaxStepLength(TParticle&& particle, TTrack&& vTrack);
 
     CrossSectionType getCrossSection(Code const projectileId, Code const targetId,
-                                     HEPEnergyType const sqrtSnn) const;
+                                     FourMomentum const& projectilP4,
+                                     FourMomentum const& targetP4) const;
 
     template <typename TParticle>
     TimeType getLifetime(TParticle&& particle) {
       return 1. / getInverseLifetime(particle);
     }
 
+    /**
+     * @brief Selects one concrete InteractionProcess and samples a target nucleus from
+     * the material.
+     *
+     * The selectInteraction method statically loops over all active InteractionProcess
+     * and calculates the material-weighted cross section for all of them. In an iterative
+     * way those cross sections are summed up. The random number cx_select, uniformely
+     * drawn from the cross section before energy losses, is used to discriminate the
+     * selected sub-process here. If the cross section after the step smaller than it was
+     * before, there is a non-zero probability that the particle survives and no
+     * interaction takes place. This method becomes imprecise when cross section rise with
+     * falling energies.
+     *
+     * If a sub-process was selected, the target nucleus is selected from the material
+     * (weighted with cross section). The interaction is then executed.
+     *
+     * @tparam TSecondaryView Object type as storage for new secondary particles.
+     * @tparam TRNG Object type to produce random numbers.
+     * @param view Object to store new secondary particles.
+     * @param projectileP4 The four momentum of the projectile.
+     * @param composition The environment/material composition.
+     * @param rng Random number object.
+     * @param cx_select Drawn random numer, uniform between [0, cx_initial]
+     * @param cx_sum For interal use, to sum up cross section contributions.
+     * @return ProcessReturn
+     */
     template <typename TSecondaryView, typename TRNG>
     inline ProcessReturn selectInteraction(
-        TSecondaryView&& view, COMBoost const& boost, HEPEnergyType const sqrtSnn,
+        TSecondaryView&& view, FourMomentum const& projectileP4,
         NuclearComposition const& composition, TRNG&& rng,
         CrossSectionType const cx_select,
         CrossSectionType cx_sum = CrossSectionType::zero());
diff --git a/corsika/framework/process/SwitchProcessSequence.hpp b/corsika/framework/process/SwitchProcessSequence.hpp
index d3f01120c..74f07fff1 100644
--- a/corsika/framework/process/SwitchProcessSequence.hpp
+++ b/corsika/framework/process/SwitchProcessSequence.hpp
@@ -150,8 +150,8 @@ namespace corsika {
                                      HEPEnergyType const sqrtSnn) const;
 
     template <typename TSecondaryView, typename TRNG>
-    ProcessReturn selectInteraction(TSecondaryView& view, COMBoost const& boost,
-                                    HEPEnergyType const sqrtSnn,
+    ProcessReturn selectInteraction(TSecondaryView& view,
+                                    FourMomentum const& projectileP4,
                                     NuclearComposition const& composition, TRNG& rng,
                                     CrossSectionType const cx_select,
                                     CrossSectionType cx_sum = CrossSectionType::zero());
diff --git a/corsika/framework/utility/COMBoost.hpp b/corsika/framework/utility/COMBoost.hpp
index d1f9ee785..8bba6b43d 100644
--- a/corsika/framework/utility/COMBoost.hpp
+++ b/corsika/framework/utility/COMBoost.hpp
@@ -19,29 +19,29 @@
 namespace corsika {
 
   /**
-     @defgroup Utilities
-
-     Collection of classes and methods to perform recurring tasks.
-   **/
+   * @defgroup Utilities
+   *
+   * Collection of classes and methods to perform recurring tasks.
+   */
 
   /**
-     @class COMBoost
-     @ingroup Utilities
-
-     This utility class handles Lorentz boost between different
-     referenence frames, using FourVector.
-
-     The class is initialized with projectile and optionally target
-     energy/momentum data. During initialization, a rotation matrix is
-     calculated to represent the projectile movement (and thus the
-     boost) along the z-axis. Also the inverse of this rotation is
-     calculated. The Lorentz boost matrix and its inverse are
-     determined as 2x2 matrices considering the energy and
-     pz-momentum.
-
-     Different constructors are offered with different specialization
-     for the cases of collisions (projectile-target) or just decays
-     (projectile only).
+   *  @class COMBoost
+   * @ingroup Utilities
+   *
+   * This utility class handles Lorentz boost between different
+   * referenence frames, using FourVector.
+   *
+   * The class is initialized with projectile and optionally target
+   * energy/momentum data. During initialization, a rotation matrix is
+   * calculated to represent the projectile movement (and thus the
+   * boost) along the z-axis. Also the inverse of this rotation is
+   * calculated. The Lorentz boost matrix and its inverse are
+   * determined as 2x2 matrices considering the energy and
+   * pz-momentum.
+   *
+   * Different constructors are offered with different specialization
+   * for the cases of collisions (projectile-target) or just decays
+   * (projectile only).
    */
 
   class COMBoost {
@@ -49,19 +49,21 @@ namespace corsika {
   public:
     //! construct a COMBoost given four-vector of projectile and mass of target (target at
     //! rest)
-    COMBoost(FourVector<HEPEnergyType, MomentumVector> const& Pprojectile,
-             HEPEnergyType const massTarget);
+    COMBoost(FourMomentum const& P4projectile, HEPEnergyType const massTarget);
+
+    //! construct a COMBoost given two four-vectors of projectile target
+    COMBoost(FourMomentum const& P4projectile, FourMomentum const& P4target);
 
     //! construct a COMBoost to boost into the rest frame given a 3-momentum and mass
-    COMBoost(MomentumVector const& momentum, HEPEnergyType mass);
+    COMBoost(MomentumVector const& momentum, HEPEnergyType const mass);
 
     //! transforms a 4-momentum from lab frame to the center-of-mass frame
     template <typename FourVector>
-    FourVector toCoM(FourVector const& p) const;
+    FourVector toCoM(FourVector const& p4) const;
 
     //! transforms a 4-momentum from the center-of-mass frame back to lab frame
     template <typename FourVector>
-    FourVector fromCoM(FourVector const& p) const;
+    FourVector fromCoM(FourVector const& p4) const;
 
     //! returns the rotated coordinate system
     CoordinateSystemPtr getRotatedCS() const;
@@ -71,7 +73,7 @@ namespace corsika {
 
   protected:
     //! internal method
-    void setBoost(double coshEta, double sinhEta);
+    void setBoost(double const coshEta, double const sinhEta);
 
   private:
     Eigen::Matrix2d boost_;
diff --git a/corsika/modules/sibyll/InteractionModel.hpp b/corsika/modules/sibyll/InteractionModel.hpp
index bf4b13081..2b4191368 100644
--- a/corsika/modules/sibyll/InteractionModel.hpp
+++ b/corsika/modules/sibyll/InteractionModel.hpp
@@ -64,7 +64,8 @@ namespace corsika::sibyll {
      * @return a tuple of: inelastic cross section, elastic cross section
      */
     std::tuple<CrossSectionType, CrossSectionType> getCrossSectionInelEla(
-        Code const projectile, Code const target, HEPEnergyType const sqrtSnn) const;
+        Code const projectile, Code const target, FourMomentum const& projectileP4,
+        FourMomentum const& targetP4) const;
 
     /**
      * @brief returns inelastic (production) cross section.
@@ -82,8 +83,10 @@ namespace corsika::sibyll {
      * elastic cross section
      */
     CrossSectionType getCrossSection(Code const projectile, Code const target,
-                                     HEPEnergyType const sqrtSnn) const {
-      return std::get<0>(getCrossSectionInelEla(projectile, target, sqrtSnn));
+                                     FourMomentum const& projectileP4,
+                                     FourMomentum const& targetP4) const {
+      return std::get<0>(
+          getCrossSectionInelEla(projectile, target, projectileP4, targetP4));
     }
 
     /**
@@ -92,8 +95,8 @@ namespace corsika::sibyll {
      */
 
     template <typename TSecondaries>
-    void doInteraction(TSecondaries&, COMBoost const& boost, Code const projectile,
-                       Code const target, HEPEnergyType const sqrtSnn);
+    void doInteraction(TSecondaries&, Code const projectile, Code const target,
+                       FourMomentum const& projectileP4, FourMomentum const& targetP4);
 
   private:
     HEPEnergyType constexpr getMinEnergyCoM() const { return minEnergyCoM_; }
diff --git a/corsika/modules/sibyll/NuclearInteractionModel.hpp b/corsika/modules/sibyll/NuclearInteractionModel.hpp
index 4606f80e2..807214574 100644
--- a/corsika/modules/sibyll/NuclearInteractionModel.hpp
+++ b/corsika/modules/sibyll/NuclearInteractionModel.hpp
@@ -44,11 +44,13 @@ namespace corsika::sibyll {
     unsigned int constexpr getMaxNFragments() const { return gMaxNFragments_; }
     unsigned int constexpr getNEnergyBins() const { return gNEnBins_; }
 
-    CrossSectionType getCrossSection(Code const, Code const, HEPEnergyType const) const;
+    CrossSectionType getCrossSection(Code const, Code const,
+                                     FourMomentum const& projectileP4,
+                                     FourMomentum const& targetP4) const;
 
     template <typename TSecondaryView>
-    void doInteraction(TSecondaryView&, COMBoost const& boost, Code const, Code const,
-                       HEPEnergyType const);
+    void doInteraction(TSecondaryView&, Code const, Code const,
+                       FourMomentum const& projectileP4, FourMomentum const& targetP4);
 
   private:
     int count_ = 0;
diff --git a/tests/framework/testCascade.cpp b/tests/framework/testCascade.cpp
index d2d9ef068..f2ac504e3 100644
--- a/tests/framework/testCascade.cpp
+++ b/tests/framework/testCascade.cpp
@@ -17,11 +17,10 @@
 #include <corsika/framework/core/ParticleProperties.hpp>
 #include <corsika/framework/core/Logging.hpp>
 
-#include <corsika/framework/utility/COMBoost.hpp>
-
 #include <corsika/framework/geometry/Point.hpp>
-#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
 #include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/geometry/FourVector.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
 
 #include <corsika/media/HomogeneousMedium.hpp>
 #include <corsika/media/NuclearComposition.hpp>
@@ -89,12 +88,13 @@ public:
 class ProcessSplit : public InteractionProcess<ProcessSplit> {
 
 public:
-  CrossSectionType getCrossSection(Code const, Code const, HEPEnergyType const) const {
+  CrossSectionType getCrossSection(Code const, Code const, FourMomentum const&,
+                                   FourMomentum const&) const {
     return 1_mb;
   }
 
   template <typename TView>
-  void doInteraction(TView& view, COMBoost const&, Code, Code, HEPEnergyType) {
+  void doInteraction(TView& view, Code, Code, FourMomentum const&, FourMomentum const&) {
     ++calls_;
     auto vP = view.getProjectile();
     const HEPEnergyType Ekin = vP.getKineticEnergy();
diff --git a/tests/framework/testProcessSequence.cpp b/tests/framework/testProcessSequence.cpp
index 312e8495a..ac4c6392d 100644
--- a/tests/framework/testProcessSequence.cpp
+++ b/tests/framework/testProcessSequence.cpp
@@ -58,7 +58,10 @@ struct DummyData {
   double data_[nData] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   typedef DummyNode node_type; // for BoundaryCrossingProcess
   Code getPID() const { return Code::Proton; }
-  // MomentumVector getMomentum() const {}
+  MomentumVector getMomentum() const {
+    // only need the coordinate system
+    return MomentumVector{get_root_CoordinateSystem(), 0_eV, 0_eV, 0_eV};
+  }
   HEPEnergyType getEnergy() const { return 10_GeV; }
   unsigned int getNuclearA() const { return 1; }
 };
@@ -208,13 +211,14 @@ public:
   }
 
   template <typename TView>
-  void doInteraction(TView& v, COMBoost const&, Code const, Code const,
-                     HEPEnergyType const) const {
+  void doInteraction(TView& v, Code const, Code const, FourMomentum const&,
+                     FourMomentum const&) const {
     checkInteract |= 1;
     for (int i = 0; i < nData; ++i) v.parent().data_[i] += 1 + i;
   }
 
-  CrossSectionType getCrossSection(Code const, Code const, HEPEnergyType const) const {
+  CrossSectionType getCrossSection(Code const, Code const, FourMomentum const&,
+                                   FourMomentum const&) const {
     return 10_mb;
   }
 
@@ -234,14 +238,15 @@ public:
   }
 
   template <typename TView>
-  void doInteraction(TView& v, COMBoost const&, Code const, Code const,
-                     HEPEnergyType const) const {
+  void doInteraction(TView& v, Code const, Code const, FourMomentum const&,
+                     FourMomentum const&) const {
     checkInteract |= 2;
     for (int i = 0; i < nData; ++i) v.parent().data_[i] /= 1.1;
     CORSIKA_LOG_DEBUG("Process2::doInteraction");
   }
 
-  CrossSectionType getCrossSection(Code const, Code const, HEPEnergyType const) const {
+  CrossSectionType getCrossSection(Code const, Code const, FourMomentum const&,
+                                   FourMomentum const&) const {
     CORSIKA_LOG_DEBUG("Process2::getCrossSection");
     return 20_mb;
   }
@@ -262,14 +267,15 @@ public:
   }
 
   template <typename TView>
-  void doInteraction(TView& v, COMBoost const&, Code const, Code const,
-                     HEPEnergyType const) const {
+  void doInteraction(TView& v, Code const, Code const, FourMomentum const&,
+                     FourMomentum const&) const {
     checkInteract |= 4;
     for (int i = 0; i < nData; ++i) v.parent().data_[i] *= 1.01;
     CORSIKA_LOG_DEBUG("Process3::doInteraction");
   }
 
-  CrossSectionType getCrossSection(Code const, Code const, HEPEnergyType const) const {
+  CrossSectionType getCrossSection(Code const, Code const, FourMomentum const&,
+                                   FourMomentum const&) const {
     CORSIKA_LOG_DEBUG("Process3::getCrossSection");
     return 30_mb;
   }
@@ -297,8 +303,8 @@ public:
     return ProcessReturn::Ok;
   }
   template <typename TView>
-  void doInteraction(TView&, COMBoost const&, Code const, Code const,
-                     HEPEnergyType const) const {
+  void doInteraction(TView&, Code const, Code const, FourMomentum const&,
+                     FourMomentum const&) const {
     checkInteract |= 8;
   }
 
@@ -613,7 +619,7 @@ TEST_CASE("ProcessSequence General", "ProcessSequence") {
 
 TEST_CASE("SwitchProcessSequence", "ProcessSequence") {
 
-  logging::set_level(logging::level::trace);
+  logging::set_level(logging::level::info);
 
   CoordinateSystemPtr rootCS = get_root_CoordinateSystem();
 
@@ -717,9 +723,9 @@ TEST_CASE("SwitchProcessSequence", "ProcessSequence") {
     particle.data_[0] = 100; // data positive   --> sequence1
 
     DummyRNG rng;
-    COMBoost const noBoost({10_GeV, {rootCS, {0_eV, 0_eV, 0_eV}}}, 0_GeV);
+    FourMomentum const projectileP4{10_GeV, {rootCS, {0_eV, 0_eV, 0_eV}}};
     NuclearComposition const noComposition({Code::Nitrogen}, {1});
-    sequence3.selectInteraction(view, noBoost, 10_GeV, noComposition, rng, cx_select);
+    sequence3.selectInteraction(view, projectileP4, noComposition, rng, cx_select);
     sequence3.selectDecay(view, time_select);
     CHECK(checkInteract == 0b100); // this is Process3
     CHECK(checkDecay == 0b001);    // this is Decay1
@@ -727,7 +733,7 @@ TEST_CASE("SwitchProcessSequence", "ProcessSequence") {
     CHECK(checkSec == 0);
     cx_select = 1.01 * 30_mb;
     checkInteract = 0;
-    sequence3.selectInteraction(view, noBoost, 10_GeV, noComposition, rng, cx_select);
+    sequence3.selectInteraction(view, projectileP4, noComposition, rng, cx_select);
     CHECK(checkInteract == 0b001); // this is Process1
 
     checkDecay = 0;
@@ -735,7 +741,7 @@ TEST_CASE("SwitchProcessSequence", "ProcessSequence") {
     checkSec = 0;
     checkCont = 0;
     particle.data_[0] = -100; // data negative   --> sequence2
-    sequence3.selectInteraction(view, noBoost, 10_GeV, noComposition, rng, cx_select);
+    sequence3.selectInteraction(view, projectileP4, noComposition, rng, cx_select);
     sequence3.selectDecay(view, time_select);
     CHECK(checkInteract == 0b010); // this is Process2
     CHECK(checkDecay == 0b010);    // this is Decay2
@@ -763,7 +769,7 @@ TEST_CASE("SwitchProcessSequence", "ProcessSequence") {
     checkSec = 0;
     checkCont = 0;
     particle.data_[0] = -100; // data negative --> sequence1
-    sequence4.selectInteraction(view, noBoost, 10_GeV, noComposition, rng, cx_select);
+    sequence4.selectInteraction(view, projectileP4, noComposition, rng, cx_select);
     sequence4.doSecondaries(view);
     sequence4.selectDecay(view, time_select);
     sequence4.doSecondaries(view);
@@ -777,7 +783,7 @@ TEST_CASE("SwitchProcessSequence", "ProcessSequence") {
     time_select = 1e5 / second;
     checkDecay = 0;
     checkInteract = 0;
-    sequence3.selectInteraction(view, noBoost, 10_GeV, noComposition, rng, cx_select);
+    sequence3.selectInteraction(view, projectileP4, noComposition, rng, cx_select);
     sequence3.selectDecay(view, time_select);
     CHECK(checkInteract == 0);
     CHECK(checkDecay == 0);
diff --git a/tests/modules/testSibyll.cpp b/tests/modules/testSibyll.cpp
index 73e812b5c..a08e33d8c 100644
--- a/tests/modules/testSibyll.cpp
+++ b/tests/modules/testSibyll.cpp
@@ -161,12 +161,14 @@ TEST_CASE("SibyllInterface", "modules") {
     CHECK_NOTHROW(model.isValid(Code::Proton, Code::Proton, 999999_GeV));
 
     //  hydrogen target == proton target == neutron target
+    FourMomentum const aP4(100_GeV, {cs, 99_GeV, 0_GeV, 0_GeV});
+    FourMomentum const bP4(1_GeV, {cs, 0_GeV, 0_GeV, 0_GeV});
     auto const [xs_prod_pp, xs_ela_pp] =
-        model.getCrossSectionInelEla(Code::Proton, Code::Proton, 100_GeV);
+        model.getCrossSectionInelEla(Code::Proton, Code::Proton, aP4, bP4);
     auto const [xs_prod_pn, xs_ela_pn] =
-        model.getCrossSectionInelEla(Code::Proton, Code::Neutron, 100_GeV);
+        model.getCrossSectionInelEla(Code::Proton, Code::Neutron, aP4, bP4);
     auto const [xs_prod_pHydrogen, xs_ela_pHydrogen] =
-        model.getCrossSectionInelEla(Code::Proton, Code::Hydrogen, 100_GeV);
+        model.getCrossSectionInelEla(Code::Proton, Code::Hydrogen, aP4, bP4);
     CHECK(xs_prod_pp == xs_prod_pHydrogen);
     CHECK(xs_prod_pp == xs_prod_pn);
     CHECK(xs_ela_pp == xs_ela_pHydrogen);
@@ -178,16 +180,15 @@ TEST_CASE("SibyllInterface", "modules") {
   SECTION("InteractionInterface - low energy") {
 
     const HEPEnergyType P0 = 60_GeV;
-    MomentumVector plab =
-        MomentumVector(cs, {P0, 0_eV, 0_eV}); // this is secret knowledge about setupStack
+    MomentumVector const plab = MomentumVector(cs, {P0, 0_eV, 0_eV});
     // also print particles after sibyll was called
     corsika::sibyll::InteractionModel model;
     model.setVerbose(true);
     HEPEnergyType const Elab = sqrt(static_pow<2>(P0) + static_pow<2>(Proton::mass));
-    HEPEnergyType const sqrtSnn = sqrt(2 * Elab * constants::nucleonMass);
+    FourMomentum const projectileP4(Elab, plab);
+    FourMomentum const nucleonP4(Oxygen::mass, MomentumVector(cs, {0_eV, 0_eV, 0_eV}));
     view.clear();
-    COMBoost boost = getCOMboost(Elab, plab, cs);
-    model.doInteraction(view, boost, Code::Proton, Code::Oxygen, sqrtSnn);
+    model.doInteraction(view, Code::Proton, Code::Oxygen, projectileP4, nucleonP4);
     auto const pSum = sumMomentum(view, cs);
 
     /*
@@ -254,7 +255,7 @@ TEST_CASE("SibyllInterface", "modules") {
           Approx(0).margin(plab.getNorm() * 0.05 / 1_GeV));
     CHECK(pSum.getNorm() / P0 == Approx(1).margin(0.05));
     [[maybe_unused]] CrossSectionType const cx =
-        model.getCrossSection(Code::Proton, Code::Oxygen, sqrtSnn);
+        model.getCrossSection(Code::Proton, Code::Oxygen, projectileP4, nucleonP4);
     CHECK(cx / 1_mb == Approx(300).margin(1));
     // CHECK(view.getEntries() == 9); //! \todo: this was 20 before refactory-2020: check
     //                                           "also sibyll not stable wrt. to compiler
@@ -264,24 +265,22 @@ TEST_CASE("SibyllInterface", "modules") {
   SECTION("NuclearInteractionInterface") {
 
     HEPMomentumType const P0 = 50_TeV;
-    MomentumVector plab = MomentumVector(cs, {P0, 0_eV, 0_eV});
+    MomentumVector const plab = MomentumVector(cs, {P0, 0_eV, 0_eV});
     corsika::sibyll::InteractionModel hmodel;
     NuclearInteractionModel model(hmodel, *env);
     Code const pid = Code::Oxygen;
-    size_t const A = get_nucleus_A(pid);
     HEPEnergyType const Elab = sqrt(static_pow<2>(P0) + static_pow<2>(get_mass(pid)));
-    HEPEnergyType const sqrtSnn = sqrt((Elab / A + constants::nucleonMass + P0 / A) *
-                                       (Elab / A + constants::nucleonMass - P0 / A));
-    model.doInteraction(view, getCOMboost(Elab, plab, cs), Code::Oxygen, Code::Oxygen,
-                        sqrtSnn);
-    CrossSectionType const cx =
-        model.getCrossSection(Code::Oxygen, Code::Oxygen, sqrtSnn);
+    FourMomentum const P4(Elab, plab);
+    FourMomentum const targetP4(get_mass(Code::Oxygen),
+                                MomentumVector(cs, {0_eV, 0_eV, 0_eV}));
+    model.doInteraction(view, pid, Code::Oxygen, P4, targetP4);
+    CrossSectionType const cx = model.getCrossSection(pid, Code::Oxygen, P4, targetP4);
     // Felix, are those changes OK? Below are the checks before refactory-2020
     // CHECK(length / 1_g * 1_cm * 1_cm == Approx(44.2).margin(.1));
     // CHECK(view.getSize() == 11);
-    CHECK(cx / 1_mb == Approx(1300).margin(300)); // this is not physics validation
+    CHECK(cx / 1_mb == Approx(1100).margin(100)); // this is not physics validation
     // CHECK(view.getSize() == 20); // also sibyll not stable wrt. to compiler changes
-    CHECK(view.getSize() == Approx(100).margin(90)); // this is not physics validation
+    CHECK(view.getSize() == Approx(200).margin(90)); // this is not physics validation
   }
 }
 
-- 
GitLab