From 61df23dc8f4c7db48ee3f8c0fbeeefc478100d31 Mon Sep 17 00:00:00 2001
From: Felix Riehn <felix@matilda>
Date: Wed, 3 Jun 2020 23:18:40 +0100
Subject: [PATCH] added process to check particle masses and shift on-shell if
 necessary

---
 Documentation/Examples/CMakeLists.txt      |  1 +
 Documentation/Examples/vertical_EAS.cc     |  6 +-
 Processes/CMakeLists.txt                   |  3 +-
 Processes/OnShellCheck/CMakeLists.txt      | 67 ++++++++++++++++
 Processes/OnShellCheck/OnShellCheck.cc     | 71 +++++++++++++++++
 Processes/OnShellCheck/OnShellCheck.h      | 39 ++++++++++
 Processes/OnShellCheck/ParticleCut.h       | 55 +++++++++++++
 Processes/OnShellCheck/testOnShellCheck.cc | 91 ++++++++++++++++++++++
 8 files changed, 331 insertions(+), 2 deletions(-)
 create mode 100644 Processes/OnShellCheck/CMakeLists.txt
 create mode 100644 Processes/OnShellCheck/OnShellCheck.cc
 create mode 100644 Processes/OnShellCheck/OnShellCheck.h
 create mode 100644 Processes/OnShellCheck/ParticleCut.h
 create mode 100644 Processes/OnShellCheck/testOnShellCheck.cc

diff --git a/Documentation/Examples/CMakeLists.txt b/Documentation/Examples/CMakeLists.txt
index fc7c01522..0118d7c87 100644
--- a/Documentation/Examples/CMakeLists.txt
+++ b/Documentation/Examples/CMakeLists.txt
@@ -99,6 +99,7 @@ if (Pythia8_FOUND)
     ProcessTrackWriter
     ProcessTrackingLine
     ProcessParticleCut
+    ProcessOnShellCheck
     ProcessStackInspector
     ProcessLongitudinalProfile
     CORSIKAprocesses
diff --git a/Documentation/Examples/vertical_EAS.cc b/Documentation/Examples/vertical_EAS.cc
index a81414dc1..974398dfe 100644
--- a/Documentation/Examples/vertical_EAS.cc
+++ b/Documentation/Examples/vertical_EAS.cc
@@ -23,6 +23,7 @@
 #include <corsika/process/longitudinal_profile/LongitudinalProfile.h>
 #include <corsika/process/observation_plane/ObservationPlane.h>
 #include <corsika/process/particle_cut/ParticleCut.h>
+#include <corsika/process/on_shell_check/OnShellCheck.h>
 #include <corsika/process/pythia/Decay.h>
 #include <corsika/process/sibyll/Decay.h>
 #include <corsika/process/sibyll/Interaction.h>
@@ -183,6 +184,8 @@ int main(int argc, char** argv) {
 
   process::particle_cut::ParticleCut cut{60_GeV};
 
+  process::on_shell_check::OnShellCheck reset_particle_mass(1.e-2,1.e-2);
+  
   process::energy_loss::EnergyLoss eLoss(showerAxis);
   process::longitudinal_profile::LongitudinalProfile longprof{showerAxis};
 
@@ -199,7 +202,8 @@ int main(int argc, char** argv) {
   process::switch_process::SwitchProcess switchProcess(urqmdCounted, sibyllSequence,
                                                        55_GeV);
   auto decaySequence = decayPythia << decaySibyll;
-  auto sequence = switchProcess << decaySequence << longprof << eLoss << cut
+
+  auto sequence = switchProcess << reset_particle_mass << decaySequence << longprof << eLoss << cut
                                 << observationLevel;
 
   // define air shower object, run simulation
diff --git a/Processes/CMakeLists.txt b/Processes/CMakeLists.txt
index 536077c5b..f27d027a1 100644
--- a/Processes/CMakeLists.txt
+++ b/Processes/CMakeLists.txt
@@ -24,7 +24,7 @@ add_subdirectory (StackInspector)
 # secondaries process
 # cuts, thinning, etc.
 add_subdirectory (ParticleCut)
-
+add_subdirectory (OnShellCheck)
 # meta-processes
 add_subdirectory (InteractionCounter)
 add_subdirectory (SwitchProcess)
@@ -42,3 +42,4 @@ add_dependencies(CORSIKAprocesses ProcessTrackingLine)
 add_dependencies(CORSIKAprocesses ProcessEnergyLoss)
 add_dependencies(CORSIKAprocesses ProcessUrQMD)
 add_dependencies(CORSIKAprocesses ProcessParticleCut)
+add_dependencies(CORSIKAprocesses ProcessOnShellCheck)
diff --git a/Processes/OnShellCheck/CMakeLists.txt b/Processes/OnShellCheck/CMakeLists.txt
new file mode 100644
index 000000000..0d2191ad3
--- /dev/null
+++ b/Processes/OnShellCheck/CMakeLists.txt
@@ -0,0 +1,67 @@
+set (
+  MODEL_SOURCES
+  OnShellCheck.cc
+)
+
+set (
+  MODEL_HEADERS
+  OnShellCheck.h
+  )
+
+set (
+  MODEL_NAMESPACE
+  corsika/process/on_shell_check
+  )
+
+add_library (ProcessOnShellCheck STATIC ${MODEL_SOURCES})
+CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessOnShellCheck ${MODEL_NAMESPACE} ${MODEL_HEADERS})
+
+set_target_properties (
+  ProcessOnShellCheck
+  PROPERTIES
+  VERSION ${PROJECT_VERSION}
+  SOVERSION 1
+#  PUBLIC_HEADER "${MODEL_HEADERS}"
+  )
+
+# target dependencies on other libraries (also the header onlys)
+target_link_libraries (
+  ProcessOnShellCheck
+  CORSIKAunits
+  CORSIKAparticles
+  CORSIKAprocesssequence
+  CORSIKAsetup
+  )
+
+target_include_directories (
+  ProcessOnShellCheck 
+  INTERFACE 
+  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
+  $<INSTALL_INTERFACE:include/include>
+  )
+
+install (
+  TARGETS ProcessOnShellCheck
+  LIBRARY DESTINATION lib
+  ARCHIVE DESTINATION lib
+#  PUBLIC_HEADER DESTINATION include/${MODEL_NAMESPACE}
+  )
+
+# --------------------
+# code unit testing
+CORSIKA_ADD_TEST(testOnShellCheck SOURCES
+  testOnShellCheck.cc
+  ${MODEL_HEADERS}
+)
+
+target_link_libraries (
+  testOnShellCheck
+  ProcessOnShellCheck
+  CORSIKAunits
+  CORSIKAstackinterface
+  CORSIKAprocesssequence
+  CORSIKAsetup
+  CORSIKAgeometry
+  CORSIKAenvironment
+  CORSIKAtesting
+  )
diff --git a/Processes/OnShellCheck/OnShellCheck.cc b/Processes/OnShellCheck/OnShellCheck.cc
new file mode 100644
index 000000000..8abfc2011
--- /dev/null
+++ b/Processes/OnShellCheck/OnShellCheck.cc
@@ -0,0 +1,71 @@
+
+/*
+ * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * See file AUTHORS for a list of contributors.
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+ * the license.
+ */
+
+#include <corsika/process/on_shell_check/OnShellCheck.h>
+#include <corsika/geometry/FourVector.h>
+
+using namespace std;
+
+using namespace corsika;
+using namespace corsika::process;
+using namespace corsika::units::si;
+using namespace corsika::particles;
+using namespace corsika::setup;
+
+namespace corsika::process {
+  namespace on_shell_check {
+
+    void OnShellCheck::Init(){
+      std::cout << "OnShellCheck: mass tolerance is set to " << mass_tolerance_ * 100
+                << "%" << endl
+                << "              energy tolerance is set to " << energy_tolerance_ * 100
+                << "%" << std::endl;
+    }
+
+    EProcessReturn OnShellCheck::DoSecondaries(corsika::setup::StackView& vS) {
+      for (auto& p : vS) {
+        auto const pid = p.GetPID();
+	//if(pid==particles::Code::Gamma || particles::IsNeutrino(pid) || particles::IsNucleus(pid)) continue;
+	if(!particles::IsHadron(pid)) continue;
+        auto const e_original = p.GetEnergy();
+        auto const p_original = p.GetMomentum();
+        auto const Plab = corsika::geometry::FourVector(e_original, p_original);
+        auto const m_kinetic = Plab.GetNorm();
+        auto const m_corsika = particles::GetMass(pid);
+        auto const m_err = abs(m_kinetic - m_corsika) / m_corsika;
+        if (m_err > mass_tolerance_) {
+          const HEPEnergyType e_shifted =
+              sqrt(p_original.GetSquaredNorm() + m_corsika * m_corsika);
+          auto const e_shift_relative = (e_shifted / e_original - 1);
+	  /* 
+	     For now we warn if the necessary shift is larger than 1%.
+	     we could promote this to an error.
+	   */
+	  if (abs(e_shift_relative) > energy_tolerance_) {
+	    std::cout << "OnShellCheck::DoSecondaries: warning! shifted particle energy by "
+		      << e_shift_relative*100 << " %" << std::endl;
+	  }
+          std::cout << "OnShellCheck::DoSecondaries: shift particle mass for " << pid
+                    << std::endl
+                    << std::setw(20) << std::setfill(' ')
+                    << "corsika mass (GeV): " << m_corsika / 1_GeV << std::endl
+                    << "kinetic mass (GeV): " << m_kinetic / 1_GeV << std::endl
+                    << "(m_kin-m_cor)/en: " << m_err << std::endl
+                    << "mass tolerance: " << mass_tolerance_ << std::endl;
+          // reset energy
+          p.SetEnergy(e_shifted);
+        } else
+	  std::cout << "OnShellCheck::DoSecondaries: particle mass for " << pid << " OK" << std::endl;
+      }
+      return EProcessReturn::eOk;
+    }
+  }
+} // namespace on_shell_check
diff --git a/Processes/OnShellCheck/OnShellCheck.h b/Processes/OnShellCheck/OnShellCheck.h
new file mode 100644
index 000000000..f9a047324
--- /dev/null
+++ b/Processes/OnShellCheck/OnShellCheck.h
@@ -0,0 +1,39 @@
+/*
+ * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * See file AUTHORS for a list of contributors.
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+ * the license.
+ */
+
+#ifndef _corsika_process_on_shell_check_OnShellCheck_h_
+#define _corsika_process_on_shell_check_OnShellCheck_h_
+
+#include <corsika/particles/ParticleProperties.h>
+#include <corsika/process/SecondariesProcess.h>
+#include <corsika/setup/SetupStack.h>
+#include <corsika/units/PhysicalUnits.h>
+
+namespace corsika::process {
+  namespace on_shell_check {
+    class OnShellCheck : public process::SecondariesProcess<OnShellCheck> {
+
+    public:
+      OnShellCheck(const double vMassTolerance, const double vEnergyTolerance)
+          : mass_tolerance_(vMassTolerance)
+          , energy_tolerance_(vEnergyTolerance) {}
+
+      EProcessReturn DoSecondaries(corsika::setup::StackView&);
+
+      void Init();
+
+    private:
+      double mass_tolerance_;
+      double energy_tolerance_;
+    };
+  } // namespace on_shell_check
+} // namespace corsika::process
+
+#endif
diff --git a/Processes/OnShellCheck/ParticleCut.h b/Processes/OnShellCheck/ParticleCut.h
new file mode 100644
index 000000000..739a1919e
--- /dev/null
+++ b/Processes/OnShellCheck/ParticleCut.h
@@ -0,0 +1,55 @@
+/*
+ * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * See file AUTHORS for a list of contributors.
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+ * the license.
+ */
+
+#ifndef _corsika_process_particle_cut_ParticleCut_h_
+#define _corsika_process_particle_cut_ParticleCut_h_
+
+#include <corsika/particles/ParticleProperties.h>
+#include <corsika/process/SecondariesProcess.h>
+#include <corsika/setup/SetupStack.h>
+#include <corsika/units/PhysicalUnits.h>
+
+namespace corsika::process {
+  namespace particle_cut {
+    class ParticleCut : public process::SecondariesProcess<ParticleCut> {
+
+      units::si::HEPEnergyType const fECut;
+
+      units::si::HEPEnergyType fEnergy = 0 * units::si::electronvolt;
+      units::si::HEPEnergyType fEmEnergy = 0 * units::si::electronvolt;
+      unsigned int fEmCount = 0;
+      units::si::HEPEnergyType fInvEnergy = 0 * units::si::electronvolt;
+      unsigned int fInvCount = 0;
+
+    public:
+      ParticleCut(const units::si::HEPEnergyType vCut)
+          : fECut(vCut) {}
+
+      bool ParticleIsInvisible(particles::Code) const;
+      EProcessReturn DoSecondaries(corsika::setup::StackView&);
+
+      template <typename TParticle>
+      bool ParticleIsBelowEnergyCut(TParticle const&) const;
+
+      bool ParticleIsEmParticle(particles::Code) const;
+
+      void Init();
+      void ShowResults();
+
+      units::si::HEPEnergyType GetInvEnergy() const { return fInvEnergy; }
+      units::si::HEPEnergyType GetCutEnergy() const { return fEnergy; }
+      units::si::HEPEnergyType GetEmEnergy() const { return fEmEnergy; }
+      unsigned int GetNumberEmParticles() const { return fEmCount; }
+      unsigned int GetNumberInvParticles() const { return fInvCount; }
+    };
+  } // namespace particle_cut
+} // namespace corsika::process
+
+#endif
diff --git a/Processes/OnShellCheck/testOnShellCheck.cc b/Processes/OnShellCheck/testOnShellCheck.cc
new file mode 100644
index 000000000..4337446a9
--- /dev/null
+++ b/Processes/OnShellCheck/testOnShellCheck.cc
@@ -0,0 +1,91 @@
+/*
+ * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * See file AUTHORS for a list of contributors.
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+ * the license.
+ */
+
+#include <corsika/process/on_shell_check/OnShellCheck.h>
+
+#include <corsika/environment/Environment.h>
+#include <corsika/geometry/Point.h>
+#include <corsika/geometry/RootCoordinateSystem.h>
+#include <corsika/geometry/Vector.h>
+#include <corsika/geometry/FourVector.h>
+#include <corsika/units/PhysicalUnits.h>
+#include <corsika/utl/CorsikaFenv.h>
+
+#include <corsika/setup/SetupStack.h>
+
+#include <catch2/catch.hpp>
+
+using namespace corsika;
+using namespace corsika::process::on_shell_check;
+using namespace corsika::units;
+using namespace corsika::units::si;
+
+TEST_CASE("OnShellCheck", "[processes]") {
+  feenableexcept(FE_INVALID);
+  using EnvType = environment::Environment<setup::IEnvironmentModel>;
+  EnvType env;
+  const geometry::CoordinateSystem& rootCS = env.GetCoordinateSystem();
+
+  // setup empty particle stack
+  setup::Stack stack;
+  stack.Clear();
+  // two energies
+  const HEPEnergyType E = 10_GeV;
+  // list of arbitrary particles
+  std::array<particles::Code, 2> particleList = {
+      particles::Code::PiPlus,   particles::Code::PiMinus};
+
+  std::array<double, 2> mass_shifts = {1.1, 1.001};
+
+  SECTION("check particle masses") {
+
+    OnShellCheck check(1.e-2, 0.01);
+
+    check.Init();
+    
+    // add primary particle to stack
+    auto particle = stack.AddParticle(
+        std::tuple<particles::Code, units::si::HEPEnergyType,
+                   corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{
+            particles::Code::Proton, E,
+            corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}),
+            geometry::Point(rootCS, 0_m, 0_m, 0_m), 0_ns});
+    // view on secondary particles
+    corsika::stack::SecondaryView view(particle);
+    // ref. to primary particle through the secondary view.
+    // only this way the secondary view is populated
+    auto projectile = view.GetProjectile();
+    // add secondaries, all with energies above the threshold
+    // only cut is by species
+    int count = -1;
+    for (auto proType : particleList) {
+      count++;
+      const auto pz = sqrt((E - particles::GetMass(proType)*mass_shifts[count]) *
+                           (E + particles::GetMass(proType)*mass_shifts[count]));
+      auto const momentum = corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, pz});
+      projectile.AddSecondary(std::tuple<particles::Code, units::si::HEPEnergyType,
+                                         corsika::stack::MomentumVector, geometry::Point,
+                                         units::si::TimeType>{
+          proType, E, momentum, geometry::Point(rootCS, 0_m, 0_m, 0_m), 0_ns});
+
+      check.DoSecondaries(view);
+    }
+    int i = -1;
+    for ( auto& p : view) {
+      i++;
+      auto const Plab = corsika::geometry::FourVector(p.GetEnergy(), p.GetMomentum());
+      auto const m_kinetic = Plab.GetNorm();
+      if(i==0)
+	REQUIRE(m_kinetic / particles::PiPlus::GetMass() == Approx(1));
+      else
+	REQUIRE_FALSE(m_kinetic / particles::PiMinus::GetMass() == Approx(1));
+    }
+  }
+}
-- 
GitLab