From 824e9fc37b47a986ec283c1f9ec4ffe6e9c9e9a6 Mon Sep 17 00:00:00 2001
From: Maximilian Sackel <maximilian.sackel@gmx.de>
Date: Mon, 2 Dec 2019 15:41:33 +0100
Subject: [PATCH] First very limited experiment of the electro magnetic process
 with proposal.

The proposal library has to be installed manually and corresponding cmake variables have to be exported.
---
 Documentation/Examples/CMakeLists.txt      | 157 ++++++++++--------
 Documentation/Examples/cascade_example.cc  |  15 +-
 Documentation/Examples/proposal_example.cc | 179 +++++++++++++++++++++
 Processes/CMakeLists.txt                   |   5 +-
 Processes/ParticleCut/ParticleCut.cc       |  13 +-
 Processes/ParticleCut/ParticleCut.h        |   4 +-
 Processes/Proposal/CMakeLists.txt          |  46 ++++++
 Processes/Proposal/ContinuousProcess.cc    | 106 ++++++++++++
 Processes/Proposal/ContinuousProcess.h     | 128 +++++++++++++++
 Processes/Proposal/Interaction.cc          | 113 +++++++++++++
 Processes/Proposal/Interaction.h           | 107 ++++++++++++
 11 files changed, 796 insertions(+), 77 deletions(-)
 create mode 100644 Documentation/Examples/proposal_example.cc
 create mode 100644 Processes/Proposal/CMakeLists.txt
 create mode 100644 Processes/Proposal/ContinuousProcess.cc
 create mode 100644 Processes/Proposal/ContinuousProcess.h
 create mode 100644 Processes/Proposal/Interaction.cc
 create mode 100644 Processes/Proposal/Interaction.h

diff --git a/Documentation/Examples/CMakeLists.txt b/Documentation/Examples/CMakeLists.txt
index 1592372c2..1c4a2e76c 100644
--- a/Documentation/Examples/CMakeLists.txt
+++ b/Documentation/Examples/CMakeLists.txt
@@ -14,72 +14,26 @@ target_link_libraries (stack_example SuperStupidStack CORSIKAunits)
 # address sanitizer is making this example too slow, so we only do "undefined"
 CORSIKA_ADD_EXAMPLE (boundary_example)
 target_link_libraries (boundary_example
-  SuperStupidStack
-  CORSIKAunits
-  CORSIKAlogging
-  CORSIKArandom
-  ProcessSibyll
-  CORSIKAcascade
-  ProcessTrackWriter
-  ProcessParticleCut
-  ProcessTrackingLine
-  CORSIKAprocesses
-  CORSIKAparticles
-  CORSIKAgeometry
-  CORSIKAenvironment
-  CORSIKAprocesssequence
-  )
-
-CORSIKA_ADD_EXAMPLE (cascade_example)
-target_link_libraries (cascade_example
-  SuperStupidStack
-  CORSIKAunits
-  CORSIKAlogging
-  CORSIKArandom
-  ProcessSibyll
-  CORSIKAcascade
-  ProcessEnergyLoss
-  ProcessTrackWriter
-  ProcessStackInspector
-  ProcessTrackingLine
-  ProcessParticleCut
-  ProcessHadronicElasticModel
-  ProcessStackInspector
-  CORSIKAprocesses
-  CORSIKAcascade
-  CORSIKAparticles
-  CORSIKAgeometry
-  CORSIKAenvironment
-  CORSIKAprocesssequence
-  )
-
-if (Pythia8_FOUND)
-  CORSIKA_ADD_EXAMPLE (cascade_proton_example)
-  target_link_libraries (cascade_proton_example
     SuperStupidStack
     CORSIKAunits
     CORSIKAlogging
     CORSIKArandom
     ProcessSibyll
     ProcessPythia8
+    ProcessProposal
     CORSIKAcascade
-    ProcessEnergyLoss
     ProcessTrackWriter
-    ProcessStackInspector
-    ProcessTrackingLine
     ProcessParticleCut
-    ProcessHadronicElasticModel
-    ProcessStackInspector
+    ProcessTrackingLine
     CORSIKAprocesses
-    CORSIKAcascade
     CORSIKAparticles
     CORSIKAgeometry
     CORSIKAenvironment
     CORSIKAprocesssequence
     )
 
-  CORSIKA_ADD_EXAMPLE (vertical_EAS RUN_OPTIONS 4 2 10000.)
-  target_link_libraries (vertical_EAS
+CORSIKA_ADD_EXAMPLE (cascade_example)
+target_link_libraries (cascade_example
     SuperStupidStack
     CORSIKAunits
     CORSIKAlogging
@@ -91,14 +45,14 @@ if (Pythia8_FOUND)
     CORSIKAcascade
     ProcessCONEXSourceCut
     ProcessEnergyLoss
-    ProcessObservationPlane
-    ProcessInteractionCounter
     ProcessTrackWriter
+    ProcessStackInspector
     ProcessTrackingLine
+    ProcessProposal
     ProcessParticleCut
     ProcessOnShellCheck
+    ProcessHadronicElasticModel
     ProcessStackInspector
-    ProcessLongitudinalProfile
     CORSIKAprocesses
     CORSIKAcascade
     CORSIKAparticles
@@ -106,21 +60,96 @@ if (Pythia8_FOUND)
     CORSIKAenvironment
     CORSIKAprocesssequence
     )
+
+if (Pythia8_FOUND)
+    CORSIKA_ADD_EXAMPLE (cascade_proton_example)
+    target_link_libraries (cascade_proton_example
+        SuperStupidStack
+        CORSIKAunits
+        CORSIKAlogging
+        CORSIKArandom
+        ProcessSibyll
+        ProcessPythia
+        CORSIKAcascade
+        ProcessEnergyLoss
+        ProcessTrackWriter
+        ProcessStackInspector
+        ProcessTrackingLine
+        ProcessParticleCut
+        ProcessHadronicElasticModel
+        ProcessStackInspector
+        CORSIKAprocesses
+        CORSIKAcascade
+        CORSIKAparticles
+        CORSIKAgeometry
+        CORSIKAenvironment
+        CORSIKAprocesssequence
+        )
+
+    CORSIKA_ADD_EXAMPLE (vertical_EAS RUN_OPTIONS 4 2 10000.)
+    target_link_libraries (vertical_EAS
+        SuperStupidStack
+        CORSIKAunits
+        CORSIKAlogging
+        CORSIKArandom
+        ProcessSibyll
+        ProcessPythia
+        ProcessUrQMD
+        ProcessSwitch
+        CORSIKAcascade
+        ProcessEnergyLoss
+        ProcessObservationPlane
+        ProcessInteractionCounter
+        ProcessTrackWriter
+        ProcessTrackingLine
+        ProcessParticleCut
+        ProcessStackInspector
+        ProcessLongitudinalProfile
+        CORSIKAprocesses
+        CORSIKAcascade
+        CORSIKAparticles
+        CORSIKAgeometry
+        CORSIKAenvironment
+        CORSIKAprocesssequence
+        )
 endif()
 
 CORSIKA_ADD_EXAMPLE (stopping_power stopping_power)
 target_link_libraries (stopping_power
-  SuperStupidStack
-  CORSIKAunits
-  ProcessEnergyLoss
-  CORSIKAparticles
-  CORSIKAgeometry
-  CORSIKAenvironment
-  )
+    SuperStupidStack
+    CORSIKAunits
+    ProcessEnergyLoss
+    CORSIKAparticles
+    CORSIKAgeometry
+    CORSIKAenvironment
+    )
 
 CORSIKA_ADD_EXAMPLE (staticsequence_example)
 target_link_libraries (staticsequence_example
-  CORSIKAprocesssequence
-  CORSIKAunits
-  CORSIKAgeometry
-  CORSIKAlogging)
+    CORSIKAprocesssequence
+    CORSIKAunits
+    CORSIKAgeometry
+    CORSIKAlogging)
+
+CORSIKA_ADD_EXAMPLE (proposal_example RUN_OPTIONS 100.)
+target_link_libraries (proposal_example
+    SuperStupidStack
+    CORSIKAunits
+    CORSIKAlogging
+    CORSIKArandom
+    CORSIKAcascade
+    ProcessObservationPlane
+    ProcessInteractionCounter
+    ProcessTrackWriter
+    ProcessProposal
+    ProcessTrackingLine
+    ProcessParticleCut
+    ProcessStackInspector
+    ProcessLongitudinalProfile
+    CORSIKAprocesses
+    CORSIKAcascade
+    CORSIKAparticles
+    CORSIKAgeometry
+    CORSIKAenvironment
+    CORSIKAprocesssequence
+    )
diff --git a/Documentation/Examples/cascade_example.cc b/Documentation/Examples/cascade_example.cc
index 93902e146..7aa0561c0 100644
--- a/Documentation/Examples/cascade_example.cc
+++ b/Documentation/Examples/cascade_example.cc
@@ -23,6 +23,8 @@
 
 #include <corsika/geometry/Sphere.h>
 
+#include <corsika/process/proposal/Interaction.h>
+
 #include <corsika/process/sibyll/Decay.h>
 #include <corsika/process/sibyll/Interaction.h>
 #include <corsika/process/sibyll/NuclearInteraction.h>
@@ -135,31 +137,34 @@ int main() {
 
   random::RNGManager::GetInstance().RegisterRandomStream("sibyll");
   random::RNGManager::GetInstance().RegisterRandomStream("pythia");
+  random::RNGManager::GetInstance().RegisterRandomStream("proposal");
   process::sibyll::Interaction sibyll;
   process::sibyll::NuclearInteraction sibyllNuc(sibyll, env);
   process::sibyll::Decay decay;
   // cascade with only HE model ==> HE cut
   process::particle_cut::ParticleCut cut(80_GeV);
+  process::proposal::Interaction proposal(env, cut);
 
   process::track_writer::TrackWriter trackWriter("tracks.dat");
   process::energy_loss::EnergyLoss eLoss{showerAxis};
 
   // assemble all processes into an ordered process list
-  auto sequence = stackInspect << sibyll << sibyllNuc << decay << eLoss << cut
-                               << trackWriter;
+  auto sequence = stackInspect << sibyll << sibyllNuc << proposal << decay
+      /* << eLoss */
+      << cut << trackWriter;
 
   // define air shower object, run simulation
   cascade::Cascade EAS(env, tracking, sequence, stack);
 
   EAS.Run();
 
-  eLoss.PrintProfile(); // print longitudinal profile
+  /* eLoss.PrintProfile(); // print longitudinal profile */
 
   cut.ShowResults();
   const HEPEnergyType Efinal =
       cut.GetCutEnergy() + cut.GetInvEnergy() + cut.GetEmEnergy();
   cout << "total cut energy (GeV): " << Efinal / 1_GeV << endl
        << "relative difference (%): " << (Efinal / E0 - 1) * 100 << endl;
-  cout << "total dEdX energy (GeV): " << eLoss.GetTotal() / 1_GeV << endl
-       << "relative difference (%): " << eLoss.GetTotal() / E0 * 100 << endl;
+  /* cout << "total dEdX energy (GeV): " << eLoss.GetTotal() / 1_GeV << endl */
+  /*      << "relative difference (%): " << eLoss.GetTotal() / E0 * 100 << endl; */
 }
diff --git a/Documentation/Examples/proposal_example.cc b/Documentation/Examples/proposal_example.cc
new file mode 100644
index 000000000..3fb41d169
--- /dev/null
+++ b/Documentation/Examples/proposal_example.cc
@@ -0,0 +1,179 @@
+/*
+ * (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/cascade/Cascade.h>
+#include <corsika/environment/Environment.h>
+#include <corsika/environment/LayeredSphericalAtmosphereBuilder.h>
+#include <corsika/environment/NuclearComposition.h>
+#include <corsika/environment/ShowerAxis.h>
+#include <corsika/geometry/Plane.h>
+#include <corsika/geometry/Sphere.h>
+#include <corsika/process/ProcessSequence.h>
+#include <corsika/process/StackProcess.h>
+#include <corsika/process/interaction_counter/InteractionCounter.h>
+#include <corsika/process/longitudinal_profile/LongitudinalProfile.h>
+#include <corsika/process/observation_plane/ObservationPlane.h>
+#include <corsika/process/particle_cut/ParticleCut.h>
+#include <corsika/process/tracking_line/TrackingLine.h>
+#include <corsika/random/RNGManager.h>
+#include <corsika/setup/SetupStack.h>
+#include <corsika/setup/SetupTrajectory.h>
+#include <corsika/units/PhysicalUnits.h>
+#include <corsika/utl/CorsikaFenv.h>
+#include <corsika/process/track_writer/TrackWriter.h>
+#include <corsika/process/proposal/Interaction.h>
+
+#include <iomanip>
+#include <iostream>
+#include <limits>
+#include <string>
+#include <typeinfo>
+
+using namespace corsika;
+using namespace corsika::process;
+using namespace corsika::units;
+using namespace corsika::particles;
+using namespace corsika::random;
+using namespace corsika::setup;
+using namespace corsika::geometry;
+using namespace corsika::environment;
+
+using namespace std;
+using namespace corsika::units::si;
+
+void registerRandomStreams() {
+  random::RNGManager::GetInstance().RegisterRandomStream("cascade");
+  random::RNGManager::GetInstance().RegisterRandomStream("proposal");
+  // add PROPOSAL here (?)
+  random::RNGManager::GetInstance().SeedAll();
+}
+
+int main(int argc, char** argv) {
+  if (argc != 2) {
+    std::cerr << "usage: proposal_example <energy/GeV>" << std::endl;
+    return 1;
+  }
+  feenableexcept(FE_INVALID);
+  // initialize random number sequence(s)
+  registerRandomStreams();
+
+  // setup environment, geometry
+  using EnvType = Environment<setup::IEnvironmentModel>;
+  EnvType env;
+  const CoordinateSystem& rootCS = env.GetCoordinateSystem();
+  Point const center{rootCS, 0_m, 0_m, 0_m};
+  environment::LayeredSphericalAtmosphereBuilder builder{center};
+  builder.setNuclearComposition(
+      {{particles::Code::Nitrogen, particles::Code::Oxygen},
+       {0.7847f, 1.f - 0.7847f}}); // values taken from AIRES manual, Ar removed for now
+
+  builder.addExponentialLayer(1222.6562_g / (1_cm * 1_cm), 994186.38_cm, 4_km);
+  builder.addExponentialLayer(1144.9069_g / (1_cm * 1_cm), 878153.55_cm, 10_km);
+  builder.addExponentialLayer(1305.5948_g / (1_cm * 1_cm), 636143.04_cm, 40_km);
+  builder.addExponentialLayer(540.1778_g / (1_cm * 1_cm), 772170.16_cm, 100_km);
+  builder.addLinearLayer(1e9_cm, 112.8_km);
+
+  builder.assemble(env);
+
+  // setup particle stack, and add primary particle
+  setup::Stack stack;
+  stack.Clear();
+  const Code beamCode = Code::Electron;
+  auto const mass = particles::GetMass(beamCode);
+  const HEPEnergyType E0 = 1_GeV * std::stof(std::string(argv[1]));
+  double theta = 0.;
+  auto const thetaRad = theta / 180. * M_PI;
+
+  auto elab2plab = [](HEPEnergyType Elab, HEPMassType m) {
+    return sqrt((Elab - m) * (Elab + m));
+  };
+  HEPMomentumType P0 = elab2plab(E0, mass);
+  auto momentumComponents = [](double thetaRad, HEPMomentumType ptot) {
+    return std::make_tuple(ptot * sin(thetaRad), 0_eV, -ptot * cos(thetaRad));
+  };
+
+  auto const [px, py, pz] = momentumComponents(thetaRad, P0);
+  auto plab = corsika::stack::MomentumVector(rootCS, {px, py, pz});
+  cout << "input particle: " << beamCode << endl;
+  cout << "input angles: theta=" << theta << endl;
+  cout << "input momentum: " << plab.GetComponents() / 1_GeV << ", norm = " << plab.norm()
+       << endl;
+
+  auto const observationHeight = 1.4_km + builder.earthRadius;
+  auto const injectionHeight = 112.75_km + builder.earthRadius;
+  auto const t = -observationHeight * cos(thetaRad) +
+                 sqrt(-si::detail::static_pow<2>(sin(thetaRad) * observationHeight) +
+                      si::detail::static_pow<2>(injectionHeight));
+  Point const showerCore{rootCS, 0_m, 0_m, observationHeight};
+  Point const injectionPos =
+      showerCore +
+      Vector<dimensionless_d>{rootCS, {-sin(thetaRad), 0, cos(thetaRad)}} * t;
+
+  std::cout << "point of injection: " << injectionPos.GetCoordinates() << std::endl;
+
+  stack.AddParticle(
+      std::tuple<particles::Code, units::si::HEPEnergyType,
+                 corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{
+          beamCode, E0, plab, injectionPos, 0_ns});
+
+  std::cout << "shower axis length: " << (showerCore - injectionPos).norm() * 1.02
+            << std::endl;
+
+  environment::ShowerAxis const showerAxis{injectionPos,
+                                           (showerCore - injectionPos) * 1.02, env};
+
+  // setup processes, decays and interactions
+
+  // PROPOSAL processs proposal{...};
+  PROPOSAL::InterpolationDef::path_to_tables = "~/.local/share/PROPOSAL/tables/";
+  PROPOSAL::InterpolationDef::path_to_tables_readonly = "~/.local/share/PROPOSAL/tables/";
+
+  process::particle_cut::ParticleCut cut(10_GeV);
+  process::proposal::Interaction proposal(env, cut);
+  process::interaction_counter::InteractionCounter proposalCounted(proposal);
+  process::track_writer::TrackWriter trackWriter("tracks.dat");
+
+  // energy cut; n.b. ParticleCut needs to be modified not to discard EM particles!
+  /* process::particle_cut::ParticleCut cut{60_GeV}; */
+
+  // long. profile; columns for gamma, e+, e- still need to be added
+  process::longitudinal_profile::LongitudinalProfile longprof{showerAxis};
+
+  Plane const obsPlane(showerCore, Vector<dimensionless_d>(rootCS, {0., 0., 1.}));
+  process::observation_plane::ObservationPlane observationLevel(obsPlane,
+                                                                "particles.dat");
+
+  auto sequence = proposalCounted << longprof << proposal << cut << observationLevel << trackWriter;
+  // define air shower object, run simulation
+  tracking_line::TrackingLine tracking;
+  cascade::Cascade EAS(env, tracking, sequence, stack);
+  EAS.Init();
+
+  // to fix the point of first interaction, uncomment the following two lines:
+  //  EAS.SetNodes();
+  //  EAS.forceInteraction();
+
+  EAS.Run();
+
+  cut.ShowResults();
+  const HEPEnergyType Efinal =
+      cut.GetCutEnergy() + cut.GetInvEnergy() + cut.GetEmEnergy();
+  cout << "total cut energy (GeV): " << Efinal / 1_GeV << endl
+       << "relative difference (%): " << (Efinal / E0 - 1) * 100 << endl;
+
+  auto const hists = proposalCounted.GetHistogram();
+  hists.saveLab("inthist_lab.txt");
+  hists.saveCMS("inthist_cms.txt");
+
+  longprof.save("longprof.txt");
+
+  std::ofstream finish("finished");
+  finish << "run completed without error" << std::endl;
+}
diff --git a/Processes/CMakeLists.txt b/Processes/CMakeLists.txt
index 08c5dca48..c405641bb 100644
--- a/Processes/CMakeLists.txt
+++ b/Processes/CMakeLists.txt
@@ -1,7 +1,7 @@
 # general
 add_subdirectory (NullModel)
 # tracking
-add_subdirectory (TrackingLine) 
+add_subdirectory (TrackingLine)
 # hadron interaction models
 add_subdirectory (Sibyll)
 add_subdirectory (QGSJetII)
@@ -13,6 +13,8 @@ if (CONEX_FOUND)
 endif (CONEX_FOUND)
 add_subdirectory (HadronicElasticModel)
 add_subdirectory (UrQMD)
+add_subdirectory (SwitchProcess)
+add_subdirectory (Proposal)
 
 # continuous physics
 add_subdirectory (EnergyLoss)
@@ -34,6 +36,7 @@ add_subdirectory (SwitchProcess)
 add_library (CORSIKAprocesses INTERFACE)
 add_dependencies(CORSIKAprocesses ProcessNullModel)
 add_dependencies(CORSIKAprocesses ProcessSibyll)
+add_dependencies(CORSIKAprocesses ProcessProposal)
 if (Pythia8_FOUND)
   add_dependencies(CORSIKAprocesses ProcessPythia)
 endif (Pythia8_FOUND)
diff --git a/Processes/ParticleCut/ParticleCut.cc b/Processes/ParticleCut/ParticleCut.cc
index bd5a5fb61..1a154b282 100644
--- a/Processes/ParticleCut/ParticleCut.cc
+++ b/Processes/ParticleCut/ParticleCut.cc
@@ -66,12 +66,13 @@ namespace corsika::process {
       C8LOG_DEBUG(fmt::format("ParticleCut: checking {}, E= {} GeV, EcutTot={} GeV", pid,
                               energy / 1_GeV,
                               (fEmEnergy + fInvEnergy + fEnergy) / 1_GeV));
-      if (ParticleIsEmParticle(pid)) {
-        C8LOG_DEBUG("removing em. particle...");
-        fEmEnergy += energy;
-        fEmCount += 1;
-        return true;
-      } else if (ParticleIsInvisible(pid)) {
+      /* if (ParticleIsEmParticle(pid)) { */
+      /*   C8LOG_DEBUG("removing em. particle..."); */
+      /*   fEmEnergy += energy; */
+      /*   fEmCount += 1; */
+      /*   return true; */
+      /* } else */
+      if (ParticleIsInvisible(pid)) {
         C8LOG_DEBUG("removing inv. particle...");
         fInvEnergy += energy;
         fInvCount += 1;
diff --git a/Processes/ParticleCut/ParticleCut.h b/Processes/ParticleCut/ParticleCut.h
index 4c0f6371a..af73dbb47 100644
--- a/Processes/ParticleCut/ParticleCut.h
+++ b/Processes/ParticleCut/ParticleCut.h
@@ -31,7 +31,8 @@ namespace corsika::process {
       unsigned int fInvCount = 0;
 
     public:
-      ParticleCut(const units::si::HEPEnergyType vCut);
+      ParticleCut(const units::si::HEPEnergyType eCut)
+          : fECut(eCut) {}
 
       EProcessReturn DoSecondaries(corsika::setup::StackView&);
 
@@ -44,6 +45,7 @@ namespace corsika::process {
 
       void ShowResults();
 
+      units::si::HEPEnergyType GetECut() const { return fECut; }
       units::si::HEPEnergyType GetInvEnergy() const { return fInvEnergy; }
       units::si::HEPEnergyType GetCutEnergy() const { return fEnergy; }
       units::si::HEPEnergyType GetEmEnergy() const { return fEmEnergy; }
diff --git a/Processes/Proposal/CMakeLists.txt b/Processes/Proposal/CMakeLists.txt
new file mode 100644
index 000000000..8d2a19788
--- /dev/null
+++ b/Processes/Proposal/CMakeLists.txt
@@ -0,0 +1,46 @@
+# find_package(PROPOSAL REQUIRED ${LIB_INCLUDE})
+
+
+FILE (GLOB MODEL_SOURCES *.cc)
+FILE (GLOB MODEL_HEADERS *.h)
+SET (MODEL_NAMESPACE corsika/process/proposal)
+
+ADD_LIBRARY (ProcessProposal STATIC ${MODEL_SOURCES})
+CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessProposal ${MODEL_NAMESPACE} ${MODEL_HEADERS})
+
+SET_TARGET_PROPERTIES ( ProcessProposal PROPERTIES VERSION ${PROJECT_VERSION}
+    SOVERSION 1
+    #  PUBLIC_HEADER "${MODEL_HEADERS}"
+  )
+
+TARGET_LINK_LIBRARIES (
+  ProcessProposal
+  CORSIKAparticles
+  CORSIKAutilities
+  CORSIKAunits
+  CORSIKAthirdparty
+  CORSIKAgeometry
+  CORSIKAenvironment
+  ${PROPOSAL_LIBRARY}
+  )
+
+TARGET_INCLUDE_DIRECTORIES (
+  ProcessProposal
+  INTERFACE
+  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
+  $<INSTALL_INTERFACE:include/include>
+  )
+
+TARGET_INCLUDE_DIRECTORIES (
+  ProcessProposal
+  SYSTEM
+  PUBLIC ${PROPOSAL_INCLUDE_DIR}
+  INTERFACE ${PROPOSAL_INCLUDE_DIR}
+  )
+
+INSTALL (
+  TARGETS ProcessProposal
+  LIBRARY DESTINATION lib
+  ARCHIVE DESTINATION lib
+#  PUBLIC_HEADER DESTINATION include/${MODEL_NAMESPACE}
+  )
diff --git a/Processes/Proposal/ContinuousProcess.cc b/Processes/Proposal/ContinuousProcess.cc
new file mode 100644
index 000000000..5914d5296
--- /dev/null
+++ b/Processes/Proposal/ContinuousProcess.cc
@@ -0,0 +1,106 @@
+#include <PROPOSAL/PROPOSAL.h>
+#include <corsika/environment/IMediumModel.h>
+#include <corsika/environment/NuclearComposition.h>
+#include <corsika/process/proposal/ContinuousProcess.h>
+#include <corsika/process/proposal/Interaction.h>
+#include <corsika/setup/SetupEnvironment.h>
+#include <corsika/setup/SetupStack.h>
+#include <corsika/setup/SetupTrajectory.h>
+#include <corsika/units/PhysicalUnits.h>
+#include <corsika/utl/COMBoost.h>
+
+using SetupParticle = corsika::setup::Stack::ParticleType;
+using SetupTrack = corsika::setup::Trajectory;
+
+namespace corsika::process::proposal {
+  using namespace corsika::setup;
+  using namespace corsika::environment;
+  using namespace corsika::units::si;
+
+  unordered_map<particles::Code, PROPOSAL::ParticleDef> ContinuousProcess::particles{
+      {particles::Code::Gamma, PROPOSAL::GammaDef()},
+      {particles::Code::Electron, PROPOSAL::EMinusDef()},
+      {particles::Code::Positron, PROPOSAL::EPlusDef()},
+      {particles::Code::MuMinus, PROPOSAL::MuMinusDef()},
+      {particles::Code::MuPlus, PROPOSAL::MuPlusDef()},
+      {particles::Code::TauPlus, PROPOSAL::TauPlusDef()},
+      {particles::Code::TauMinus, PROPOSAL::TauMinusDef()},
+  };
+
+  bool ContinuousProcess::CanInteract(particles::Code pcode) const noexcept {
+    auto search = particles.find(pcode);
+    if (search != particles.end()) return true;
+    return false;
+  }
+
+  template <>
+  ContinuousProcess::ContinuousProcess(SetupEnvironment const& _env,
+                                       CORSIKA_ParticleCut const& _cut)
+      : cut(make_shared<const PROPOSAL::EnergyCutSettings>(_cut.GetECut() / 1_MeV, 1,
+                                                           false)) {
+    auto all_compositions = std::vector<const NuclearComposition*>();
+    _env.GetUniverse()->walk([&](auto& vtn) {
+      if (vtn.HasModelProperties())
+        all_compositions.push_back(&vtn.GetModelProperties().GetNuclearComposition());
+    });
+    for (auto& ncarg : all_compositions) {
+      auto comp_vec = std::vector<PROPOSAL::Components::Component>();
+      auto frac_iter = ncarg->GetFractions().cbegin();
+      for (auto& pcode : ncarg->GetComponents()) {
+        comp_vec.emplace_back(GetName(pcode), GetNucleusZ(pcode), GetNucleusA(pcode),
+                              *frac_iter);
+        ++frac_iter;
+      }
+      media[ncarg] = PROPOSAL::Medium(
+          "Modified Air", 1., PROPOSAL::Air().GetI(), PROPOSAL::Air().GetC(),
+          PROPOSAL::Air().GetA(), PROPOSAL::Air().GetM(), PROPOSAL::Air().GetX0(),
+          PROPOSAL::Air().GetX1(), PROPOSAL::Air().GetD0(), 1.0, comp_vec);
+    }
+  }
+
+  HEPEnergyType ContinuousProcess::TotalEnergyLoss(SetupParticle const& vP,
+                                                   GrammageType const vDX) {
+    auto calc_ptr = GetCalculator(vP);
+    auto upper_energy = calc_ptr->second->UpperLimitTrackIntegral(
+        vP.GetEnergy() / 1_MeV, vDX / 1_g * 1_cm * 1_cm);
+    std::cout << "upper_energy: " << upper_energy << "MeV" << std::endl;
+    return upper_energy * 1_MeV;
+  }
+
+   void ContinuousProcess::Init() {}
+  template <>
+  EProcessReturn ContinuousProcess::DoContinuous(SetupParticle& vP, SetupTrack const& vT) {
+    if (vP.GetChargeNumber() == 0) return process::EProcessReturn::eOk;
+    std::cout << "DoContinuous..." << std::endl;
+    GrammageType const dX =
+        vP.GetNode()->GetModelProperties().IntegratedGrammage(vT, vT.GetLength());
+    HEPEnergyType dE = TotalEnergyLoss(vP, dX);
+    auto E = vP.GetEnergy();
+    const auto Ekin = E - vP.GetMass();
+    auto Enew = E + dE;
+    auto status = process::EProcessReturn::eOk;
+    if (-dE > Ekin) {
+      dE = -Ekin;
+      Enew = vP.GetMass();
+      status = process::EProcessReturn::eParticleAbsorbed;
+    }
+    vP.SetEnergy(Enew);
+    auto pnew = vP.GetMomentum();
+    vP.SetMomentum(pnew * Enew / pnew.GetNorm());
+    return status;
+  }
+
+  template <>
+  units::si::LengthType ContinuousProcess::MaxStepLength(SetupParticle const& vP,
+                                                         SetupTrack const& vT) {
+    auto constexpr dX = 1_g / square(1_cm);
+    auto const dE = -TotalEnergyLoss(vP, dX); // dE > 0
+    auto const maxLoss = 0.01 * vP.GetEnergy();
+    auto const maxGrammage = maxLoss / dE * dX;
+
+    return vP.GetNode()->GetModelProperties().ArclengthFromGrammage(vT,
+                                                                    maxGrammage) *
+           1.0001; // to make sure particle gets absorbed when DoContinuous() is called
+  }
+
+} // namespace corsika::process::proposal
diff --git a/Processes/Proposal/ContinuousProcess.h b/Processes/Proposal/ContinuousProcess.h
new file mode 100644
index 000000000..58b19ec29
--- /dev/null
+++ b/Processes/Proposal/ContinuousProcess.h
@@ -0,0 +1,128 @@
+/*
+ * (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_proposal_interaction_h_
+#define _corsika_process_proposal_interaction_h_
+
+#include <PROPOSAL/PROPOSAL.h>
+#include <corsika/environment/Environment.h>
+#include <corsika/particles/ParticleProperties.h>
+#include <corsika/process/ContinuousProcess.h>
+#include <corsika/process/particle_cut/ParticleCut.h>
+#include <corsika/random/RNGManager.h>
+#include <corsika/random/UniformRealDistribution.h>
+#include <unordered_map>
+#include "PROPOSAL/PROPOSAL.h"
+
+using std::unordered_map;
+
+using namespace corsika::environment;
+
+using CORSIKA_ParticleCut = corsika::process::particle_cut::ParticleCut;
+
+namespace corsika::process::proposal {
+
+  class ContinuousProcess
+      : public corsika::process::ContinuousProcess<ContinuousProcess> {
+  private:
+    shared_ptr<const PROPOSAL::EnergyCutSettings> cut;
+
+    static unordered_map<particles::Code, PROPOSAL::ParticleDef> particles;
+    unordered_map<const NuclearComposition*, PROPOSAL::Medium> media;
+
+    corsika::random::RNG& fRNG =
+        corsika::random::RNGManager::GetInstance().GetRandomStream("proposal");
+
+    bool CanInteract(particles::Code pcode) const noexcept;
+
+    struct interaction_hash {
+        size_t operator()(const std::pair<const NuclearComposition*, particles::Code>& p) const
+        {
+            auto hash1 = std::hash<const NuclearComposition*>{}(p.first);
+            auto hash2 = std::hash<particles::Code>{}(p.second);
+            return hash1 ^ hash2;
+        }
+    };
+
+    unordered_map<std::pair<const NuclearComposition*, particles::Code>, unique_ptr<PROPOSAL::Displacement>, interaction_hash> calc;
+
+    auto BuildCalculator(particles::Code corsika_code, NuclearComposition const& comp) {
+        auto medium = media.at(&comp);
+        if (corsika_code == particles::Code::Gamma) {
+            auto cross = GetStdCrossSections(PROPOSAL::GammaDef(), media.at(&comp), cut, true);
+            auto [insert_it, success] =
+                    calc.insert({std::make_pair(&comp, corsika_code), PROPOSAL::make_displacement(cross, true)});
+            return insert_it;
+        }
+        if (corsika_code == particles::Code::Electron) {
+            auto cross = GetStdCrossSections(PROPOSAL::EMinusDef(), media.at(&comp), cut, true);
+            auto [insert_it, success] =
+            calc.insert({std::make_pair(&comp, corsika_code), PROPOSAL::make_displacement(cross, true)});
+            return insert_it;
+        }
+        if (corsika_code == particles::Code::Positron) {
+            auto cross = GetStdCrossSections(PROPOSAL::EPlusDef(), media.at(&comp), cut, true);
+            auto [insert_it, success] =
+            calc.insert({std::make_pair(&comp, corsika_code), PROPOSAL::make_displacement(cross, true)});
+            return insert_it;
+        }
+        if (corsika_code == particles::Code::MuMinus) {
+            auto cross = GetStdCrossSections(PROPOSAL::MuMinusDef(), media.at(&comp), cut, true);
+            auto [insert_it, success] =
+            calc.insert({std::make_pair(&comp, corsika_code), PROPOSAL::make_displacement(cross, true)});
+            return insert_it;
+        }
+        if (corsika_code == particles::Code::MuPlus) {
+            auto cross = GetStdCrossSections(PROPOSAL::MuPlusDef(), media.at(&comp), cut, true);
+            auto [insert_it, success] =
+            calc.insert({std::make_pair(&comp, corsika_code), PROPOSAL::make_displacement(cross, true)});
+            return insert_it;
+        }
+        if (corsika_code == particles::Code::TauMinus) {
+            auto cross = GetStdCrossSections(PROPOSAL::TauMinusDef(), media.at(&comp), cut, true);
+            auto [insert_it, success] =
+            calc.insert({std::make_pair(&comp, corsika_code), PROPOSAL::make_displacement(cross, true)});
+            return insert_it;
+        }
+        if (corsika_code == particles::Code::TauPlus) {
+            auto cross = GetStdCrossSections(PROPOSAL::TauPlusDef(), media.at(&comp), cut, true);
+            auto [insert_it, success] =
+            calc.insert({std::make_pair(&comp, corsika_code), PROPOSAL::make_displacement(cross, true)});
+            return insert_it;
+        }
+        throw std::runtime_error("PROPOSAL could not find corresponding builder");
+    }
+    
+    template <typename Particle>
+    auto GetCalculator(Particle& vP) {
+      auto& comp = vP.GetNode()->GetModelProperties().GetNuclearComposition();
+      auto calc_it = calc.find(std::make_pair(&comp, vP.GetPID()));
+      if (calc_it != calc.end()) return calc_it;
+      return BuildCalculator(vP.GetPID(), comp);
+    }
+
+    units::si::HEPEnergyType TotalEnergyLoss(setup::Stack::ParticleType const&,
+                                             const units::si::GrammageType);
+
+  public:
+    template <typename TEnvironment>
+    ContinuousProcess(TEnvironment const& env, CORSIKA_ParticleCut const& cut);
+
+    void Init();
+
+    template <typename Particle, typename Track>
+    EProcessReturn DoContinuous(Particle&, Track const&) ;
+
+    template <typename Particle, typename Track>
+    units::si::LengthType MaxStepLength(Particle const& p, Track const& track) ;
+  };
+} // namespace corsika::process::proposal
+
+#endif
diff --git a/Processes/Proposal/Interaction.cc b/Processes/Proposal/Interaction.cc
new file mode 100644
index 000000000..4394ae868
--- /dev/null
+++ b/Processes/Proposal/Interaction.cc
@@ -0,0 +1,113 @@
+
+#include <corsika/environment/IMediumModel.h>
+#include <corsika/environment/NuclearComposition.h>
+#include <corsika/process/proposal/Interaction.h>
+#include <corsika/setup/SetupEnvironment.h>
+#include <corsika/setup/SetupStack.h>
+#include <corsika/setup/SetupTrajectory.h>
+#include <corsika/units/PhysicalUnits.h>
+#include <corsika/utl/COMBoost.h>
+#include <limits>
+#include <memory>
+#include <random>
+#include <tuple>
+
+using Component_PROPOSAL = PROPOSAL::Components::Component;
+
+namespace corsika::process::proposal {
+  using namespace corsika::setup;
+  using namespace corsika::environment;
+  using namespace corsika::units::si;
+
+  std::unordered_map<particles::Code, PROPOSAL::ParticleDef> Interaction::particles{
+      {particles::Code::Gamma, PROPOSAL::GammaDef()},
+      {particles::Code::Electron, PROPOSAL::EMinusDef()},
+      {particles::Code::Positron, PROPOSAL::EPlusDef()},
+      {particles::Code::MuMinus, PROPOSAL::MuMinusDef()},
+      {particles::Code::MuPlus, PROPOSAL::MuPlusDef()},
+      {particles::Code::TauPlus, PROPOSAL::TauPlusDef()},
+      {particles::Code::TauMinus, PROPOSAL::TauMinusDef()},
+  };
+
+  bool Interaction::CanInteract(particles::Code pcode) const noexcept {
+    if (particles.find(pcode) != particles.end()) return true;
+    return false;
+  }
+
+  template <>
+  Interaction::Interaction(SetupEnvironment const& _env, CORSIKA_ParticleCut const& _cut)
+      : cut(make_shared<const PROPOSAL::EnergyCutSettings>(_cut.GetECut() / 1_MeV, 1,
+                                                           false))
+      , fRNG(corsika::random::RNGManager::GetInstance().GetRandomStream("proposal")) {
+    auto all_compositions = std::vector<const NuclearComposition*>();
+    _env.GetUniverse()->walk([&](auto& vtn) {
+      if (vtn.HasModelProperties()) {
+        all_compositions.push_back(&vtn.GetModelProperties().GetNuclearComposition());
+      }
+    });
+    for (auto& ncarg : all_compositions) {
+      auto comp_vec = std::vector<Component_PROPOSAL>();
+      auto frac_iter = ncarg->GetFractions().cbegin();
+      for (auto& pcode : ncarg->GetComponents()) {
+        comp_vec.emplace_back(GetName(pcode), GetNucleusZ(pcode), GetNucleusA(pcode),
+                              *frac_iter);
+        ++frac_iter;
+      }
+      media[ncarg] = PROPOSAL::Medium(
+          "Modified Air", 1., PROPOSAL::Air().GetI(), PROPOSAL::Air().GetC(),
+          PROPOSAL::Air().GetA(), PROPOSAL::Air().GetM(), PROPOSAL::Air().GetX0(),
+          PROPOSAL::Air().GetX1(), PROPOSAL::Air().GetD0(), 1.0, comp_vec);
+    }
+  }
+
+  template <>
+  corsika::process::EProcessReturn Interaction::DoInteraction(
+      setup::StackView::StackIterator& vP) {
+    if (CanInteract(vP.GetPID())) {
+      auto calc = GetCalculator(vP); // [CrossSections]
+      std::uniform_real_distribution<double> distr(0., 1.);
+      auto [type, comp_ptr, v] =
+          get<INTERACTION>(calc->second)
+              ->TypeInteraction(vP.GetEnergy() / 1_MeV, distr(fRNG));
+      auto rnd =
+          vector<double>(get<SECONDARIES>(calc->second)->RequiredRandomNumbers(type));
+      for (auto& it : rnd) it = distr(fRNG);
+      auto point = PROPOSAL::Vector3D(vP.GetPosition().GetX() / 1_cm,
+                                      vP.GetPosition().GetY() / 1_cm,
+                                      vP.GetPosition().GetZ() / 1_cm);
+      auto d = vP.GetDirection().GetComponents();
+      auto direction = PROPOSAL::Vector3D(d.GetX().magnitude(), d.GetY().magnitude(),
+                                          d.GetZ().magnitude());
+      auto loss = make_tuple(static_cast<int>(type), point, direction,
+                             v * vP.GetEnergy() / 1_MeV, 0.);
+      auto sec = get<SECONDARIES>(calc->second)
+                     ->CalculateSecondaries(vP.GetEnergy() / 1_MeV, loss, *comp_ptr, rnd);
+      for (auto& s : sec) {
+        auto E = get<PROPOSAL::Loss::ENERGY>(s) * 1_MeV;
+        auto vec = corsika::geometry::QuantityVector(
+            get<PROPOSAL::Loss::DIRECTION>(s).GetX() * E,
+            get<PROPOSAL::Loss::DIRECTION>(s).GetY() * E,
+            get<PROPOSAL::Loss::DIRECTION>(s).GetZ() * E);
+        auto p = corsika::stack::MomentumVector(
+            corsika::geometry::RootCoordinateSystem::GetInstance()
+                .GetRootCoordinateSystem(),
+            vec);
+        auto sec_code = corsika::particles::ConvertFromPDG(
+            static_cast<particles::PDGCode>(get<PROPOSAL::Loss::TYPE>(s)));
+        vP.AddSecondary(make_tuple(sec_code, E, p, vP.GetPosition(), vP.GetTime()));
+      }
+    }
+    return process::EProcessReturn::eOk;
+  }
+
+  template <>
+  corsika::units::si::GrammageType Interaction::GetInteractionLength(
+      setup::Stack::StackIterator const& vP) {
+    if (CanInteract(vP.GetPID())) {
+      auto calc = GetCalculator(vP);
+      return get<INTERACTION>(calc->second)->MeanFreePath(vP.GetEnergy() / 1_MeV) * 1_g /
+             (1_cm * 1_cm);
+    }
+    return std::numeric_limits<double>::infinity() * 1_g / (1_cm * 1_cm);
+  }
+} // namespace corsika::process::proposal
diff --git a/Processes/Proposal/Interaction.h b/Processes/Proposal/Interaction.h
new file mode 100644
index 000000000..54597a656
--- /dev/null
+++ b/Processes/Proposal/Interaction.h
@@ -0,0 +1,107 @@
+/*
+ * (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_proposal_interaction_h_
+#define _corsika_process_proposalythia_interaction_h_
+
+#include <corsika/environment/Environment.h>
+#include <corsika/particles/ParticleProperties.h>
+#include <corsika/process/InteractionProcess.h>
+#include <corsika/process/particle_cut/ParticleCut.h>
+#include <corsika/random/RNGManager.h>
+#include <corsika/random/UniformRealDistribution.h>
+#include <unordered_map>
+#include "PROPOSAL/PROPOSAL.h"
+
+using namespace corsika::environment;
+
+using CORSIKA_ParticleCut = corsika::process::particle_cut::ParticleCut;
+using std::make_pair;
+using std::make_tuple;
+
+namespace corsika::process::proposal {
+
+  class Interaction : public corsika::process::InteractionProcess<Interaction> {
+
+    shared_ptr<const PROPOSAL::EnergyCutSettings> cut;
+
+    static std::unordered_map<particles::Code, PROPOSAL::ParticleDef> particles;
+    std::unordered_map<const NuclearComposition*, PROPOSAL::Medium> media;
+
+    corsika::random::RNG& fRNG;
+
+    bool CanInteract(particles::Code pcode) const noexcept;
+
+    using calculator_t = tuple<unique_ptr<PROPOSAL::SecondariesCalculator>,
+                               unique_ptr<PROPOSAL::Interaction>>;
+    using calc_key_t = std::pair<const NuclearComposition*, particles::Code>;
+
+    struct interaction_hash {
+      size_t operator()(const calc_key_t& p) const {
+        return std::hash<const NuclearComposition*>{}(p.first) ^
+               std::hash<particles::Code>{}(p.second);
+      }
+    };
+
+    std::unordered_map<calc_key_t, calculator_t, interaction_hash> calculators;
+
+    template <typename Particle>
+    auto BuildCalculator(particles::Code corsika_code, Particle p_def,
+                         NuclearComposition const& comp) {
+      auto cross = GetStdCrossSections(p_def, media.at(&comp), cut, true);
+      auto inter_types = PROPOSAL::CrossSectionVector::GetInteractionTypes(cross);
+      auto [insert_it, success] = calculators.insert(
+          {make_pair(&comp, corsika_code),
+           make_tuple(PROPOSAL::make_secondaries(inter_types, p_def, media.at(&comp)),
+                      PROPOSAL::make_interaction(cross, true))});
+      return insert_it;
+    }
+
+    auto BuildCalculator(particles::Code corsika_code, NuclearComposition const& comp) {
+      if (corsika_code == particles::Code::Gamma)
+        return BuildCalculator(particles::Code::Gamma, PROPOSAL::GammaDef(), comp);
+      if (corsika_code == particles::Code::Electron)
+        return BuildCalculator(particles::Code::Electron, PROPOSAL::EMinusDef(), comp);
+      if (corsika_code == particles::Code::Positron)
+        return BuildCalculator(particles::Code::Positron, PROPOSAL::EPlusDef(), comp);
+      if (corsika_code == particles::Code::MuMinus)
+        return BuildCalculator(particles::Code::MuMinus, PROPOSAL::MuMinusDef(), comp);
+      if (corsika_code == particles::Code::MuPlus)
+        return BuildCalculator(particles::Code::MuPlus, PROPOSAL::MuPlusDef(), comp);
+      if (corsika_code == particles::Code::TauMinus)
+        return BuildCalculator(particles::Code::TauMinus, PROPOSAL::TauMinusDef(), comp);
+      if (corsika_code == particles::Code::TauPlus)
+        return BuildCalculator(particles::Code::TauPlus, PROPOSAL::TauPlusDef(), comp);
+      throw std::runtime_error("PROPOSAL could not find corresponding builder");
+    }
+
+    enum { SECONDARIES, INTERACTION };
+    template <typename Particle>
+    auto GetCalculator(Particle& vP) {
+      auto& comp = vP.GetNode()->GetModelProperties().GetNuclearComposition();
+      auto calc_it = calculators.find(make_pair(&comp, vP.GetPID()));
+      if (calc_it != calculators.end()) return calc_it;
+      return BuildCalculator(vP.GetPID(), comp);
+    }
+
+  public:
+    template <typename TEnvironment>
+    Interaction(TEnvironment const& env, CORSIKA_ParticleCut const& cut);
+
+    void Init() {};
+
+    template <typename Particle>
+    corsika::process::EProcessReturn DoInteraction(Particle&);
+
+    template <typename TParticle>
+    corsika::units::si::GrammageType GetInteractionLength(TParticle const& p);
+  }; // namespace corsika::process::proposal
+} // namespace corsika::process::proposal
+#endif
-- 
GitLab