diff --git a/Framework/Geometry/CoordinateSystem.h b/Framework/Geometry/CoordinateSystem.h
index 26da1a701139a5ed1b887d516582888807ae9d21..614c76da9d2f32925fa9d8c18828196fbbe8f19f 100644
--- a/Framework/Geometry/CoordinateSystem.h
+++ b/Framework/Geometry/CoordinateSystem.h
@@ -72,19 +72,19 @@ namespace corsika::geometry {
       Eigen::Matrix3d A, B;
 
       if (s > 0) {
-        A << 1, 0, -a1,                     // comment to prevent clang-format
-            0, 1, -a2,                      // .
-            a1, a2, 1;                      // .
+        A << 1, 0, a1,                      // comment to prevent clang-format
+            0, 1, a2,                       // .
+            -a1, -a2, 1;                    // .
         B << -a1 * a1 * c, -a1 * a2 * c, 0, // .
             -a1 * a2 * c, -a2 * a2 * c, 0,  // .
             0, 0, -(a1 * a1 + a2 * a2) * c; // .
 
       } else {
         A << 1, 0, a1,                      // .
-            0, -1, -a2,                     // .
-            a1, a2, -1;                     // .
-        B << -a1 * a1 * c, -a1 * a2 * c, 0, // .
-            +a1 * a2 * c, +a2 * a2 * c, 0,  // .
+            0, -1, a2,                      // .
+            a1, -a2, -1;                    // .
+        B << -a1 * a1 * c, +a1 * a2 * c, 0, // .
+            -a1 * a2 * c, +a2 * a2 * c, 0,  // .
             0, 0, (a1 * a1 + a2 * a2) * c;  // .
       }
 
diff --git a/Framework/Geometry/testGeometry.cc b/Framework/Geometry/testGeometry.cc
index 440e771e73446de8729d6c57a9b8aee69ca80414..6e054d336b1a2a66e6e615b506079eae4f4aebbf 100644
--- a/Framework/Geometry/testGeometry.cc
+++ b/Framework/Geometry/testGeometry.cc
@@ -123,6 +123,61 @@ TEST_CASE("transformations between CoordinateSystems") {
     auto comp3 = v1.GetComponents(combined);
     REQUIRE((comp1 - comp3).norm().magnitude() == Approx(0).margin(absMargin));
   }
+
+  SECTION("RotateToZ positive") {
+    Vector const v{rootCS, 0_m, 1_m, 1_m};
+    auto const csPrime = rootCS.RotateToZ(v);
+    Vector const zPrime{csPrime, 0_m, 0_m, 5_m};
+    Vector const xPrime{csPrime, 5_m, 0_m, 0_m};
+    Vector const yPrime{csPrime, 0_m, 5_m, 0_m};
+
+    CHECK(zPrime.dot(v).magnitude() > 0);
+    CHECK(zPrime.GetComponents(rootCS)[1].magnitude() ==
+          Approx(zPrime.GetComponents(rootCS)[2].magnitude()));
+    CHECK(zPrime.GetComponents(rootCS)[0].magnitude() == Approx(0));
+
+    CHECK(xPrime.GetComponents(rootCS).eVector.dot(
+              yPrime.GetComponents(rootCS).eVector) == Approx(0));
+    CHECK(zPrime.GetComponents(rootCS).eVector.dot(
+              xPrime.GetComponents(rootCS).eVector) == Approx(0));
+    CHECK(yPrime.GetComponents(rootCS).eVector.dot(
+              zPrime.GetComponents(rootCS).eVector) == Approx(0));
+
+    CHECK(yPrime.GetComponents(rootCS).eVector.dot(
+              yPrime.GetComponents(rootCS).eVector) == Approx((5_m * 5_m).magnitude()));
+    CHECK(xPrime.GetComponents(rootCS).eVector.dot(
+              xPrime.GetComponents(rootCS).eVector) == Approx((5_m * 5_m).magnitude()));
+    CHECK(zPrime.GetComponents(rootCS).eVector.dot(
+              zPrime.GetComponents(rootCS).eVector) == Approx((5_m * 5_m).magnitude()));
+  }
+
+  SECTION("RotateToZ negative") {
+    Vector const v{rootCS, 0_m, 0_m, -1_m};
+    auto const csPrime = rootCS.RotateToZ(v);
+    Vector const zPrime{csPrime, 0_m, 0_m, 5_m};
+    Vector const xPrime{csPrime, 5_m, 0_m, 0_m};
+    Vector const yPrime{csPrime, 0_m, 5_m, 0_m};
+
+    CHECK(zPrime.dot(v).magnitude() > 0);
+    CHECK(xPrime.GetComponents(rootCS).eVector.dot(v.GetComponents().eVector) ==
+          Approx(0));
+    CHECK(yPrime.GetComponents(rootCS).eVector.dot(v.GetComponents().eVector) ==
+          Approx(0));
+
+    CHECK(xPrime.GetComponents(rootCS).eVector.dot(
+              yPrime.GetComponents(rootCS).eVector) == Approx(0));
+    CHECK(zPrime.GetComponents(rootCS).eVector.dot(
+              xPrime.GetComponents(rootCS).eVector) == Approx(0));
+    CHECK(yPrime.GetComponents(rootCS).eVector.dot(
+              zPrime.GetComponents(rootCS).eVector) == Approx(0));
+
+    CHECK(yPrime.GetComponents(rootCS).eVector.dot(
+              yPrime.GetComponents(rootCS).eVector) == Approx((5_m * 5_m).magnitude()));
+    CHECK(xPrime.GetComponents(rootCS).eVector.dot(
+              xPrime.GetComponents(rootCS).eVector) == Approx((5_m * 5_m).magnitude()));
+    CHECK(zPrime.GetComponents(rootCS).eVector.dot(
+              zPrime.GetComponents(rootCS).eVector) == Approx((5_m * 5_m).magnitude()));
+  }
 }
 
 TEST_CASE("Sphere") {
diff --git a/Framework/Utilities/COMBoost.cc b/Framework/Utilities/COMBoost.cc
index 1d65d6439a2731a70988d440b76ed4e68a77262f..abdb7a48b88f733aba4c03dc4d8521a741aa27b5 100644
--- a/Framework/Utilities/COMBoost.cc
+++ b/Framework/Utilities/COMBoost.cc
@@ -15,58 +15,50 @@
 #include <corsika/utl/COMBoost.h>
 #include <corsika/utl/sgn.h>
 
+#include <cmath>
+
 using namespace corsika::utl;
 using namespace corsika::units::si;
 using namespace corsika::geometry;
 
 COMBoost::COMBoost(FourVector<HEPEnergyType, Vector<hepmomentum_d>> const& Pprojectile,
                    const HEPMassType massTarget)
-    : fCS(Pprojectile.GetSpaceLikeComponents().GetCoordinateSystem()) {
+    : originalCS_{Pprojectile.GetSpaceLikeComponents().GetCoordinateSystem()}
+    , rotatedCS_{originalCS_.RotateToZ(Pprojectile.GetSpaceLikeComponents())} {
   auto const pProjectile = Pprojectile.GetSpaceLikeComponents();
-  auto const pProjNorm = pProjectile.norm();
-  auto const a = (pProjectile / pProjNorm).GetComponents().eVector;
-  auto const a1 = a(0), a2 = a(1);
-
-  auto const s = sgn(a(2));
-  auto const c = 1 / (1 + s * a(2));
-
-  Eigen::Matrix3d A, B;
-
-  if (s > 0) {
-    A << 1, 0, -a1,                     // comment to prevent clang-format
-        0, 1, -a2,                      // .
-        a1, a2, 1;                      // .
-    B << -a1 * a1 * c, -a1 * a2 * c, 0, // .
-        -a1 * a2 * c, -a2 * a2 * c, 0,  // .
-        0, 0, -(a1 * a1 + a2 * a2) * c; // .
-
-  } else {
-    A << 1, 0, a1,                      // comment to prevent clang-format
-        0, -1, -a2,                     // .
-        a1, a2, -1;                     // .
-    B << -a1 * a1 * c, -a1 * a2 * c, 0, // .
-        +a1 * a2 * c, +a2 * a2 * c, 0,  // .
-        0, 0, (a1 * a1 + a2 * a2) * c;  // .
-  }
+  auto const pProjNormSquared = pProjectile.squaredNorm();
+  auto const pProjNorm = sqrt(pProjNormSquared);
 
-  fRotation = A + B;
+  auto const eProjectile = Pprojectile.GetTimeLikeComponent();
+  auto const massProjectileSquared = eProjectile * eProjectile - pProjNormSquared;
+  auto const s =
+      massTarget * massTarget + massProjectileSquared + 2 * eProjectile * massTarget;
 
-  // calculate boost
-  double const beta = pProjNorm / (Pprojectile.GetTimeLikeComponent() + massTarget);
+  auto const sqrtS = sqrt(s);
+  auto const sinhEta = -pProjNorm / sqrtS;
+  auto const coshEta = sqrt(1 + pProjNormSquared / s);
 
-  /* Accurracy matters here, beta = 1 - epsilon for ultra-relativistic boosts */
-  double const coshEta = 1 / std::sqrt((1 + beta) * (1 - beta));
-  //~ double const coshEta = 1 / std::sqrt((1-beta*beta));
-  double const sinhEta = -beta * coshEta;
+  setBoost(coshEta, sinhEta);
 
-  std::cout << "COMBoost (1-beta)=" << 1 - beta << " gamma=" << coshEta << std::endl;
-  std::cout << "  det = " << fRotation.determinant() - 1 << std::endl;
+  std::cout << "COMBoost (1-beta)=" << 1 - sinhEta / coshEta << " gamma=" << coshEta
+            << std::endl;
+  std::cout << "  det = " << boost_.determinant() - 1 << std::endl;
+}
 
-  fBoost << coshEta, sinhEta, sinhEta, coshEta;
+COMBoost::COMBoost(geometry::Vector<units::si::hepmomentum_d> const& momentum,
+                   units::si::HEPEnergyType mass)
+    : originalCS_{momentum.GetCoordinateSystem()}
+    , rotatedCS_{originalCS_.RotateToZ(momentum)} {
+  auto const squaredNorm = momentum.squaredNorm();
+  auto const norm = sqrt(squaredNorm);
+  auto const sinhEta = -norm / mass;
+  auto const coshEta = sqrt(1 + squaredNorm / (mass * mass));
+  setBoost(coshEta, sinhEta);
+}
 
-  fInverseBoost << coshEta, -sinhEta, -sinhEta, coshEta;
+void COMBoost::setBoost(double coshEta, double sinhEta) {
+  boost_ << coshEta, sinhEta, sinhEta, coshEta;
+  inverseBoost_ << coshEta, -sinhEta, -sinhEta, coshEta;
 }
 
-/*
-  Here we instantiate all physically meaningful versions of COMBoost
- */
+CoordinateSystem const& COMBoost::GetRotatedCS() const { return rotatedCS_; }
diff --git a/Framework/Utilities/COMBoost.h b/Framework/Utilities/COMBoost.h
index 48532531e5caa981f1d01daf258af5d737fa1216..e25d74e828c9140815dd98ec426871d8055a6415 100644
--- a/Framework/Utilities/COMBoost.h
+++ b/Framework/Utilities/COMBoost.h
@@ -25,72 +25,77 @@ namespace corsika::utl {
    */
 
   class COMBoost {
-    Eigen::Matrix3d fRotation;
-    Eigen::Matrix2d fBoost, fInverseBoost;
-    corsika::geometry::CoordinateSystem const& fCS;
+    Eigen::Matrix2d boost_, inverseBoost_;
+    corsika::geometry::CoordinateSystem const &originalCS_, rotatedCS_;
+
+    void setBoost(double coshEta, double sinhEta);
 
   public:
-    //! construct a COMBoost given four-vector of prjectile and mass of target
+    //! construct a COMBoost given four-vector of projectile and mass of target
     COMBoost(
         const corsika::geometry::FourVector<
             corsika::units::si::HEPEnergyType,
             corsika::geometry::Vector<corsika::units::si::hepmomentum_d>>& Pprojectile,
         const corsika::units::si::HEPEnergyType massTarget);
 
-    auto const& GetRotationMatrix() const { return fRotation; }
+    //! construct a COMBoost to boost into the rest frame given a 3-momentum and mass
+    COMBoost(geometry::Vector<units::si::hepmomentum_d> const& momentum,
+             units::si::HEPEnergyType mass);
 
     //! transforms a 4-momentum from lab frame to the center-of-mass frame
     template <typename FourVector>
     FourVector toCoM(const FourVector& p) const {
       using namespace corsika::units::si;
-      auto pComponents = p.GetSpaceLikeComponents().GetComponents(fCS);
-      Eigen::Vector3d eVecRotated = fRotation * pComponents.eVector;
+      auto pComponents = p.GetSpaceLikeComponents().GetComponents(rotatedCS_);
+      Eigen::Vector3d eVecRotated = pComponents.eVector;
       Eigen::Vector2d lab;
 
       lab << (p.GetTimeLikeComponent() * (1 / 1_GeV)),
           (eVecRotated(2) * (1 / 1_GeV).magnitude());
 
-      auto const boostedZ = fBoost * lab;
+      auto const boostedZ = boost_ * lab;
       auto const E_CoM = boostedZ(0) * 1_GeV;
 
       eVecRotated(2) = boostedZ(1) * (1_GeV).magnitude();
 
-      return FourVector(E_CoM,
-                        corsika::geometry::Vector<hepmomentum_d>(fCS, eVecRotated));
+      return FourVector(
+          E_CoM, corsika::geometry::Vector<hepmomentum_d>(rotatedCS_, eVecRotated));
     }
 
     //! transforms a 4-momentum from the center-of-mass frame back to lab frame
     template <typename FourVector>
     FourVector fromCoM(const FourVector& p) const {
       using namespace corsika::units::si;
+      auto pCM = p.GetSpaceLikeComponents().GetComponents(rotatedCS_);
+      auto const Ecm = p.GetTimeLikeComponent();
+
       Eigen::Vector2d com;
-      com << (p.GetTimeLikeComponent() * (1 / 1_GeV)),
-          (p.GetSpaceLikeComponents().GetComponents().eVector(2) *
-           (1 / 1_GeV).magnitude());
-
-      auto const plab = p.GetSpaceLikeComponents().GetComponents();
-      std::cout << "COMBoost::fromCoM Ecm=" << p.GetTimeLikeComponent() / 1_GeV
-                << " GeV, "
-                << " pcm = " << plab / 1_GeV << " (norm = " << plab.norm() / 1_GeV
+      com << (Ecm * (1 / 1_GeV)), (pCM.eVector(2) * (1 / 1_GeV).magnitude());
+
+      std::cout << "COMBoost::fromCoM Ecm=" << Ecm / 1_GeV << " GeV, "
+                << " pcm = " << pCM / 1_GeV << " (norm = " << pCM.norm() / 1_GeV
                 << " GeV), invariant mass = " << p.GetNorm() / 1_GeV << " GeV"
                 << std::endl;
 
-      auto const boostedZ = fInverseBoost * com;
+      auto const boostedZ = inverseBoost_ * com;
       auto const E_lab = boostedZ(0) * 1_GeV;
 
-      auto pLab = p.GetSpaceLikeComponents().GetComponents();
-      pLab.eVector(2) = boostedZ(1) * (1_GeV).magnitude();
-      pLab.eVector = fRotation.transpose() * pLab.eVector;
+      pCM.eVector(2) = boostedZ(1) * (1_GeV).magnitude();
 
-      FourVector f(E_lab, corsika::geometry::Vector(fCS, pLab));
+      geometry::Vector<typename decltype(pCM)::dimension> pLab{rotatedCS_, pCM};
+      pLab.rebase(originalCS_);
+
+      FourVector f(E_lab, pLab);
 
       std::cout << "COMBoost::fromCoM --> Elab=" << E_lab / 1_GeV << "GeV, "
-                << " pcm = " << pLab / 1_GeV << " (norm =" << pLab.norm() / 1_GeV
+                << " plab = " << pLab.GetComponents() << " (norm =" << pLab.norm() / 1_GeV
                 << " GeV), invariant mass = " << f.GetNorm() / 1_GeV << " GeV"
                 << std::endl;
 
       return f;
     }
+
+    geometry::CoordinateSystem const& GetRotatedCS() const;
   };
 } // namespace corsika::utl
 
diff --git a/Framework/Utilities/testCOMBoost.cc b/Framework/Utilities/testCOMBoost.cc
index 84226cf86dbd4cda644168f21224784407646183..b665c5fc004e938a370d2befd3f050a76a57ca4d 100644
--- a/Framework/Utilities/testCOMBoost.cc
+++ b/Framework/Utilities/testCOMBoost.cc
@@ -42,87 +42,6 @@ auto const s = [](HEPEnergyType E, QuantityVector<hepmomentum_d> const& p) {
   return E * E - p.squaredNorm();
 };
 
-TEST_CASE("rotation") {
-  // define projectile kinematics in lab frame
-  HEPMassType const projectileMass = 1_GeV;
-  HEPMassType const targetMass = 1.0e300_eV;
-  Vector<hepmomentum_d> pProjectileLab{rootCS, {0_GeV, 0_PeV, 1_GeV}};
-  HEPEnergyType const eProjectileLab = energy(projectileMass, pProjectileLab);
-  const FourVector PprojLab(eProjectileLab, pProjectileLab);
-
-  Eigen::Vector3d e1, e2, e3;
-  e1 << 1, 0, 0;
-  e2 << 0, 1, 0;
-  e3 << 0, 0, 1;
-
-  // define boost to com frame
-  SECTION("pos. z-axis") {
-    COMBoost boost({eProjectileLab, {rootCS, {0_GeV, 0_GeV, 1_GeV}}}, targetMass);
-    auto const& rot = boost.GetRotationMatrix();
-
-    CHECK((rot * e3 - e3).norm() == Approx(0).margin(absMargin));
-    CHECK((rot * e1).norm() == Approx(1));
-    CHECK((rot * e2).norm() == Approx(1));
-    CHECK((rot * e3).norm() == Approx(1));
-    CHECK(rot.determinant() == Approx(1));
-  }
-
-  SECTION("y-axis in upper half") {
-    COMBoost boost({eProjectileLab, {rootCS, {0_GeV, 1_GeV, 1_meV}}}, targetMass);
-    auto const& rot = boost.GetRotationMatrix();
-
-    CHECK((rot * e2 - e3).norm() == Approx(0).margin(absMargin));
-    CHECK((rot * e1).norm() == Approx(1));
-    CHECK((rot * e2).norm() == Approx(1));
-    CHECK((rot * e3).norm() == Approx(1));
-    CHECK(rot.determinant() == Approx(1));
-  }
-
-  SECTION("x-axis in upper half") {
-    COMBoost boost({eProjectileLab, {rootCS, {1_GeV, 0_GeV, 1_meV}}}, targetMass);
-    auto const& rot = boost.GetRotationMatrix();
-
-    CHECK((rot * e1 - e3).norm() == Approx(0).margin(absMargin));
-    CHECK((rot * e1).norm() == Approx(1));
-    CHECK((rot * e2).norm() == Approx(1));
-    CHECK((rot * e3).norm() == Approx(1));
-    CHECK(rot.determinant() == Approx(1));
-  }
-
-  SECTION("neg. z-axis") {
-    COMBoost boost({eProjectileLab, {rootCS, {0_GeV, 0_GeV, -1_GeV}}}, targetMass);
-    auto const& rot = boost.GetRotationMatrix();
-
-    CHECK((rot * (-e3) - e3).norm() == Approx(0).margin(absMargin));
-    CHECK((rot * e1).norm() == Approx(1));
-    CHECK((rot * e2).norm() == Approx(1));
-    CHECK((rot * e3).norm() == Approx(1));
-    CHECK(rot.determinant() == Approx(1));
-  }
-
-  SECTION("x-axis lower half") {
-    COMBoost boost({eProjectileLab, {rootCS, {1_GeV, 0_GeV, -1_meV}}}, targetMass);
-    auto const& rot = boost.GetRotationMatrix();
-
-    CHECK((rot * e1 - e3).norm() == Approx(0).margin(absMargin));
-    CHECK((rot * e1).norm() == Approx(1));
-    CHECK((rot * e2).norm() == Approx(1));
-    CHECK((rot * e3).norm() == Approx(1));
-    CHECK(rot.determinant() == Approx(1));
-  }
-
-  SECTION("y-axis lower half") {
-    COMBoost boost({eProjectileLab, {rootCS, {0_GeV, 1_GeV, -1_meV}}}, targetMass);
-    auto const& rot = boost.GetRotationMatrix();
-
-    CHECK((rot * e2 - e3).norm() == Approx(0).margin(absMargin));
-    CHECK((rot * e1).norm() == Approx(1));
-    CHECK((rot * e2).norm() == Approx(1));
-    CHECK((rot * e3).norm() == Approx(1));
-    CHECK(rot.determinant() == Approx(1));
-  }
-}
-
 TEST_CASE("boosts") {
   // define target kinematics in lab frame
   HEPMassType const targetMass = 1_GeV;
@@ -266,4 +185,31 @@ TEST_CASE("boosts") {
         PprojCoM.GetSpaceLikeComponents() + PtargCoM.GetSpaceLikeComponents();
     CHECK(sumPCoM.norm() / P0 == Approx(0).margin(absMargin)); // MAKE RELATIVE CHECK
   }
+
+  SECTION("rest frame") {
+    HEPMassType const projectileMass = 1_GeV;
+    HEPMomentumType const P0 = 1_TeV;
+    Vector<hepmomentum_d> pProjectileLab{rootCS, {0_GeV, P0, 0_GeV}};
+    HEPEnergyType const eProjectileLab = energy(projectileMass, pProjectileLab);
+    const FourVector PprojLab(eProjectileLab, pProjectileLab);
+
+    COMBoost boostRest(pProjectileLab, projectileMass);
+    auto const& csPrime = boostRest.GetRotatedCS();
+    FourVector const rest4Mom = boostRest.toCoM(PprojLab);
+
+    CHECK(rest4Mom.GetTimeLikeComponent() / 1_GeV == Approx(projectileMass / 1_GeV));
+    CHECK(rest4Mom.GetSpaceLikeComponents().norm() / 1_GeV ==
+          Approx(0).margin(absMargin));
+
+    FourVector const a{0_eV, Vector{csPrime, 0_eV, 5_GeV, 0_eV}};
+    FourVector const b{0_eV, Vector{rootCS, 3_GeV, 0_eV, 0_eV}};
+    auto const aLab = boostRest.fromCoM(a);
+    auto const bLab = boostRest.fromCoM(b);
+
+    CHECK(aLab.GetNorm() / a.GetNorm() == Approx(1));
+    CHECK(aLab.GetSpaceLikeComponents().GetComponents(csPrime)[1].magnitude() ==
+          Approx((5_GeV).magnitude()));
+    CHECK(bLab.GetSpaceLikeComponents().GetComponents(rootCS)[0].magnitude() ==
+          Approx((3_GeV).magnitude()));
+  }
 }
diff --git a/Processes/Pythia/Decay.cc b/Processes/Pythia/Decay.cc
index 7586f0b42c63e88f532ba6bba17f27348a09ae9c..27890d28bfe1ce42576fc81d08550ea80daf70de 100644
--- a/Processes/Pythia/Decay.cc
+++ b/Processes/Pythia/Decay.cc
@@ -12,8 +12,10 @@
 #include <corsika/process/pythia/Decay.h>
 #include <corsika/process/pythia/Random.h>
 
+#include <corsika/geometry/FourVector.h>
 #include <corsika/setup/SetupStack.h>
 #include <corsika/setup/SetupTrajectory.h>
+#include <corsika/utl/COMBoost.h>
 
 using std::cout;
 using std::endl;
@@ -103,16 +105,14 @@ namespace corsika::process::pythia {
 
   void Decay::SetHandleDecay(const vector<particles::Code> vParticleList) {
     handleAllDecays_ = false;
-    for (auto p : vParticleList) Decay::SetHandleDecay(p);
+    for (auto p : vParticleList) SetHandleDecay(p);
   }
 
   bool Decay::IsDecayHandled(const corsika::particles::Code vParticleCode) {
-    if (handleAllDecays_ && Decay::CanHandleDecay(vParticleCode))
+    if (handleAllDecays_ && CanHandleDecay(vParticleCode))
       return true;
     else
-      return Decay::handledDecays_.find(vParticleCode) != Decay::handledDecays_.end()
-                 ? true
-                 : false;
+      return handledDecays_.find(vParticleCode) != Decay::handledDecays_.end();
   }
 
   bool Decay::IsStable(const particles::Code vCode) {
@@ -192,12 +192,17 @@ namespace corsika::process::pythia {
     using namespace units;
     using namespace units::si;
 
-    auto const decayPoint = vP.GetPosition();
+    auto const& decayPoint = vP.GetPosition();
     auto const t0 = vP.GetTime();
 
-    // coordinate system, get global frame of reference
-    geometry::CoordinateSystem& rootCS =
-        geometry::RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
+    auto const& labMomentum = vP.GetMomentum();
+    geometry::CoordinateSystem const& labCS = labMomentum.GetCoordinateSystem();
+
+    // define target kinematics in lab frame
+    // define boost to and from CoM frame
+    // CoM frame definition in Pythia projectile: +z
+    utl::COMBoost const boost(labMomentum, vP.GetMass());
+    auto const& rotatedCS = boost.GetRotatedCS();
 
     fCount++;
 
@@ -213,12 +218,11 @@ namespace corsika::process::pythia {
     // input particle PDG
     auto const pdgCode = static_cast<int>(particles::GetPDG(particleId));
 
-    auto const pcomp = vP.GetMomentum().GetComponents();
-    double px = pcomp[0] / 1_GeV;
-    double py = pcomp[1] / 1_GeV;
-    double pz = pcomp[2] / 1_GeV;
-    double en = vP.GetEnergy() / 1_GeV;
-    double m = particles::GetMass(particleId) / 1_GeV;
+    double constexpr px = 0;
+    double constexpr py = 0;
+    double constexpr pz = 0;
+    double const en = vP.GetMass() / 1_GeV;
+    double const m = en;
 
     // add particle to pythia stack
     event.append(pdgCode, 1, 0, 0, px, py, pz, en, m);
@@ -236,17 +240,22 @@ namespace corsika::process::pythia {
       if (event[i].isFinal()) {
         auto const pyId =
             particles::ConvertFromPDG(static_cast<particles::PDGCode>(event[i].id()));
-        HEPEnergyType pyEn = event[i].e() * 1_GeV;
-        MomentumVector pyP(rootCS, {event[i].px() * 1_GeV, event[i].py() * 1_GeV,
-                                    event[i].pz() * 1_GeV});
+        HEPEnergyType const Erest = event[i].e() * 1_GeV;
+        MomentumVector const pRest(
+            rotatedCS,
+            {event[i].px() * 1_GeV, event[i].py() * 1_GeV, event[i].pz() * 1_GeV});
+        geometry::FourVector const fourMomRest{Erest, pRest};
+        auto const fourMomLab = boost.fromCoM(fourMomRest);
 
-        cout << "particle: id=" << pyId << " momentum=" << pyP.GetComponents() / 1_GeV
-             << " energy=" << pyEn << endl;
+        cout << "particle: id=" << pyId << " momentum="
+             << fourMomLab.GetSpaceLikeComponents().GetComponents(labCS) / 1_GeV
+             << " energy=" << fourMomLab.GetTimeLikeComponent() << endl;
 
         vP.AddSecondary(
             tuple<particles::Code, units::si::HEPEnergyType,
                   corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{
-                pyId, pyEn, pyP, decayPoint, t0});
+                pyId, fourMomLab.GetTimeLikeComponent(),
+                fourMomLab.GetSpaceLikeComponents(), decayPoint, t0});
       }
 
     // set particle stable
diff --git a/Processes/Pythia/testPythia.cc b/Processes/Pythia/testPythia.cc
index 5dd0cc8bc6c60e23697d722c8d983ce0f44efc89..daef1f9ef1648a65a184a0b075296bac3b7e16df 100644
--- a/Processes/Pythia/testPythia.cc
+++ b/Processes/Pythia/testPythia.cc
@@ -19,6 +19,7 @@
 #include <corsika/geometry/Point.h>
 #include <corsika/units/PhysicalUnits.h>
 
+#include <corsika/utl/CorsikaFenv.h>
 #include <catch2/catch.hpp>
 
 TEST_CASE("Pythia", "[processes]") {
@@ -88,6 +89,15 @@ TEST_CASE("Pythia", "[processes]") {
 using namespace corsika;
 using namespace corsika::units::si;
 
+template <typename TStackView>
+auto sumMomentum(TStackView const& view, geometry::CoordinateSystem const& vCS) {
+  geometry::Vector<hepenergy_d> sum{vCS, 0_eV, 0_eV, 0_eV};
+
+  for (auto const& p : view) { sum += p.GetMomentum(); }
+
+  return sum;
+}
+
 TEST_CASE("pythia process") {
 
   // setup environment, geometry
@@ -110,12 +120,12 @@ TEST_CASE("pythia process") {
   auto const* nodePtr = theMedium.get(); // save the medium for later use before moving it
 
   SECTION("pythia decay") {
-
+    feenableexcept(FE_INVALID);
     setup::Stack stack;
     const HEPEnergyType E0 = 10_GeV;
     HEPMomentumType P0 =
         sqrt(E0 * E0 - particles::PiPlus::GetMass() * particles::PiPlus::GetMass());
-    auto plab = corsika::stack::MomentumVector(cs, {0_GeV, 0_GeV, -P0});
+    auto plab = corsika::stack::MomentumVector(cs, {0_GeV, 0_GeV, P0});
     geometry::Point pos(cs, 0_m, 0_m, 0_m);
     auto particle = stack.AddParticle(
         std::tuple<particles::Code, units::si::HEPEnergyType,
@@ -131,7 +141,10 @@ TEST_CASE("pythia process") {
     model.Init();
     [[maybe_unused]] const TimeType time = model.GetLifetime(particle);
     model.DoDecay(projectile);
-    REQUIRE(stack.GetSize() == 3);
+    CHECK(stack.GetSize() == 3);
+    auto const pSum = sumMomentum(view, cs);
+    CHECK((pSum - plab).norm() / 1_GeV == Approx(0).margin(1e-4));
+    CHECK((pSum.norm() - plab.norm()) / 1_GeV == Approx(0).margin(1e-4));
   }
 
   SECTION("pythia decay config") {