diff --git a/Documentation/Examples/CMakeLists.txt b/Documentation/Examples/CMakeLists.txt
index a3978ae9e04c23e61e02adc7ed25dd681c1bc796..c677f3ab9a3dccd82235fee8d4690be4de697d17 100644
--- a/Documentation/Examples/CMakeLists.txt
+++ b/Documentation/Examples/CMakeLists.txt
@@ -31,6 +31,7 @@ target_link_libraries (cascade_example SuperStupidStack CORSIKAunits
   CORSIKAcascade
   ProcessEnergyLoss
   ProcessStackInspector
+  ProcessParticleCut
   ProcessTrackWriter
   ProcessTrackingLine
   CORSIKAprocesses
@@ -50,6 +51,7 @@ target_link_libraries (boundary_example SuperStupidStack CORSIKAunits CORSIKAlog
   ProcessSibyll
   CORSIKAcascade
   ProcessTrackWriter
+  ProcessParticleCut
   ProcessTrackingLine
   CORSIKAprocesses
   CORSIKAparticles
@@ -72,6 +74,7 @@ if (Pythia8_FOUND)
     ProcessEnergyLoss
     ProcessTrackWriter
     ProcessTrackingLine
+    ProcessParticleCut
     ProcessHadronicElasticModel
     ProcessStackInspector
     CORSIKAprocesses
@@ -97,6 +100,7 @@ target_link_libraries (vertical_EAS SuperStupidStack CORSIKAunits
   ProcessTrackWriter
   ProcessTrackingLine
   ProcessHadronicElasticModel
+  ProcessParticleCut
   ProcessStackInspector
   CORSIKAprocesses
   CORSIKAcascade
diff --git a/Documentation/Examples/boundary_example.cc b/Documentation/Examples/boundary_example.cc
index 037c8b352d2431f83133e85f909d737a2e653d8e..169cb1c6a30a46a16494ac7e37b5bae2e875c256 100644
--- a/Documentation/Examples/boundary_example.cc
+++ b/Documentation/Examples/boundary_example.cc
@@ -29,6 +29,8 @@
 
 #include <corsika/process/track_writer/TrackWriter.h>
 
+#include <corsika/process/particle_cut/ParticleCut.h>
+
 #include <corsika/units/PhysicalUnits.h>
 
 #include <corsika/random/RNGManager.h>
@@ -51,158 +53,6 @@ using namespace corsika::environment;
 using namespace std;
 using namespace corsika::units::si;
 
-class ProcessCut : public process::SecondariesProcess<ProcessCut> {
-
-  HEPEnergyType fECut;
-
-  HEPEnergyType fEnergy = 0_GeV;
-  HEPEnergyType fEmEnergy = 0_GeV;
-  int fEmCount = 0;
-  HEPEnergyType fInvEnergy = 0_GeV;
-  int fInvCount = 0;
-
-public:
-  ProcessCut(const HEPEnergyType cut)
-      : fECut(cut) {}
-
-  template <typename Particle>
-  bool isBelowEnergyCut(Particle& p) const {
-    // nuclei
-    if (p.GetPID() == particles::Code::Nucleus) {
-      auto const ElabNuc = p.GetEnergy() / p.GetNuclearA();
-      auto const EcmNN = sqrt(2. * ElabNuc * 0.93827_GeV);
-      if (ElabNuc < fECut || EcmNN < 10_GeV)
-        return true;
-      else
-        return false;
-    } else {
-      // TODO: center-of-mass energy hard coded
-      const HEPEnergyType Ecm = sqrt(2. * p.GetEnergy() * 0.93827_GeV);
-      if (p.GetEnergy() < fECut || Ecm < 10_GeV)
-        return true;
-      else
-        return false;
-    }
-  }
-
-  bool isEmParticle(Code pCode) const {
-    bool is_em = false;
-    // FOR NOW: switch
-    switch (pCode) {
-      case Code::Electron:
-        is_em = true;
-        break;
-      case Code::Positron:
-        is_em = true;
-        break;
-      case Code::Gamma:
-        is_em = true;
-        break;
-      default:
-        break;
-    }
-    return is_em;
-  }
-
-  void defineEmParticles() const {
-    // create bool array identifying em particles
-  }
-
-  bool isInvisible(Code pCode) const {
-    bool is_inv = false;
-    // FOR NOW: switch
-    switch (pCode) {
-      case Code::NuE:
-        is_inv = true;
-        break;
-      case Code::NuEBar:
-        is_inv = true;
-        break;
-      case Code::NuMu:
-        is_inv = true;
-        break;
-      case Code::NuMuBar:
-        is_inv = true;
-        break;
-      case Code::MuPlus:
-        is_inv = true;
-        break;
-      case Code::MuMinus:
-        is_inv = true;
-        break;
-
-      case Code::Neutron:
-        is_inv = true;
-        break;
-
-      case Code::AntiNeutron:
-        is_inv = true;
-        break;
-
-      default:
-        break;
-    }
-    return is_inv;
-  }
-
-  template <typename TSecondaries>
-  EProcessReturn DoSecondaries(TSecondaries& vS) {
-    auto p = vS.begin();
-    while (p != vS.end()) {
-      const Code pid = p.GetPID();
-      HEPEnergyType energy = p.GetEnergy();
-      cout << "ProcessCut: DoSecondaries: " << pid << " E= " << energy
-           << ", EcutTot=" << (fEmEnergy + fInvEnergy + fEnergy) / 1_GeV << " GeV"
-           << endl;
-      if (isEmParticle(pid)) {
-        cout << "removing em. particle..." << endl;
-        fEmEnergy += energy;
-        fEmCount += 1;
-        p.Delete();
-      } else if (isInvisible(pid)) {
-        cout << "removing inv. particle..." << endl;
-        fInvEnergy += energy;
-        fInvCount += 1;
-        p.Delete();
-      } else if (isBelowEnergyCut(p)) {
-        cout << "removing low en. particle..." << endl;
-        fEnergy += energy;
-        p.Delete();
-      } else if (p.GetTime() > 10_ms) {
-        cout << "removing OLD particle..." << endl;
-        fEnergy += energy;
-        p.Delete();
-      } else {
-        ++p; // next entry in SecondaryView
-      }
-    }
-    return EProcessReturn::eOk;
-  }
-
-  void Init() {
-    fEmEnergy = 0. * 1_GeV;
-    fEmCount = 0;
-    fInvEnergy = 0. * 1_GeV;
-    fInvCount = 0;
-    fEnergy = 0. * 1_GeV;
-    // defineEmParticles();
-  }
-
-  void ShowResults() {
-    cout << " ******************************" << endl
-         << " ParticleCut: " << endl
-         << " energy in em.  component (GeV):  " << fEmEnergy / 1_GeV << endl
-         << " no. of em.  particles injected:  " << fEmCount << endl
-         << " energy in inv. component (GeV):  " << fInvEnergy / 1_GeV << endl
-         << " no. of inv. particles injected:  " << fInvCount << endl
-         << " energy below particle cut (GeV): " << fEnergy / 1_GeV << endl
-         << " ******************************" << endl;
-  }
-
-  HEPEnergyType GetInvEnergy() const { return fInvEnergy; }
-  HEPEnergyType GetCutEnergy() const { return fEnergy; }
-  HEPEnergyType GetEmEnergy() const { return fEmEnergy; }
-};
 
 template <bool deleteParticle>
 struct MyBoundaryCrossingProcess
@@ -276,7 +126,8 @@ int main() {
   process::sibyll::Decay decay{{particles::Code::PiPlus, particles::Code::PiMinus,
                                 particles::Code::KPlus, particles::Code::KMinus,
                                 particles::Code::K0Long, particles::Code::K0Short}};
-  ProcessCut cut(20_GeV);
+
+  process::particle_cut::ParticleCut cut(20_GeV);
 
   process::track_writer::TrackWriter trackWriter("tracks.dat");
   MyBoundaryCrossingProcess<true> boundaryCrossing("crossings.dat");
diff --git a/Documentation/Examples/cascade_example.cc b/Documentation/Examples/cascade_example.cc
index a26f1af688fefea6d381be70c4f957b4d75215d8..7690d664811f4192d224044ab39ebea7b92d6136 100644
--- a/Documentation/Examples/cascade_example.cc
+++ b/Documentation/Examples/cascade_example.cc
@@ -31,6 +31,8 @@
 
 #include <corsika/process/track_writer/TrackWriter.h>
 
+#include <corsika/process/particle_cut/ParticleCut.h>
+
 #include <corsika/units/PhysicalUnits.h>
 
 #include <corsika/random/RNGManager.h>
@@ -52,159 +54,6 @@ using namespace corsika::environment;
 using namespace std;
 using namespace corsika::units::si;
 
-class ProcessCut : public process::SecondariesProcess<ProcessCut> {
-
-  HEPEnergyType fECut;
-
-  HEPEnergyType fEnergy = 0_GeV;
-  HEPEnergyType fEmEnergy = 0_GeV;
-  int fEmCount = 0;
-  HEPEnergyType fInvEnergy = 0_GeV;
-  int fInvCount = 0;
-
-public:
-  ProcessCut(const HEPEnergyType cut)
-      : fECut(cut) {}
-
-  template <typename Particle>
-  bool isBelowEnergyCut(Particle& p) const {
-    // nuclei
-    if (p.GetPID() == particles::Code::Nucleus) {
-      auto const ElabNuc = p.GetEnergy() / p.GetNuclearA();
-      auto const EcmNN = sqrt(2. * ElabNuc * 0.93827_GeV);
-      if (ElabNuc < fECut || EcmNN < 10_GeV)
-        return true;
-      else
-        return false;
-    } else {
-      // TODO: center-of-mass energy hard coded
-      const HEPEnergyType Ecm = sqrt(2. * p.GetEnergy() * 0.93827_GeV);
-      if (p.GetEnergy() < fECut || Ecm < 10_GeV)
-        return true;
-      else
-        return false;
-    }
-  }
-
-  bool isEmParticle(Code pCode) const {
-    bool is_em = false;
-    // FOR NOW: switch
-    switch (pCode) {
-      case Code::Electron:
-        is_em = true;
-        break;
-      case Code::Positron:
-        is_em = true;
-        break;
-      case Code::Gamma:
-        is_em = true;
-        break;
-      default:
-        break;
-    }
-    return is_em;
-  }
-
-  void defineEmParticles() const {
-    // create bool array identifying em particles
-  }
-
-  bool isInvisible(Code pCode) const {
-    bool is_inv = false;
-    // FOR NOW: switch
-    switch (pCode) {
-      case Code::NuE:
-        is_inv = true;
-        break;
-      case Code::NuEBar:
-        is_inv = true;
-        break;
-      case Code::NuMu:
-        is_inv = true;
-        break;
-      case Code::NuMuBar:
-        is_inv = true;
-        break;
-      case Code::MuPlus:
-        is_inv = true;
-        break;
-      case Code::MuMinus:
-        is_inv = true;
-        break;
-
-      case Code::Neutron:
-        is_inv = true;
-        break;
-
-      case Code::AntiNeutron:
-        is_inv = true;
-        break;
-
-      default:
-        break;
-    }
-    return is_inv;
-  }
-
-  template <typename TSecondaries>
-  EProcessReturn DoSecondaries(TSecondaries& vS) {
-    auto p = vS.begin();
-    while (p != vS.end()) {
-      const Code pid = p.GetPID();
-      HEPEnergyType energy = p.GetEnergy();
-      cout << "ProcessCut: DoSecondaries: " << pid << " E= " << energy
-           << ", EcutTot=" << (fEmEnergy + fInvEnergy + fEnergy) / 1_GeV << " GeV"
-           << endl;
-      if (isEmParticle(pid)) {
-        cout << "removing em. particle..." << endl;
-        fEmEnergy += energy;
-        fEmCount += 1;
-        p.Delete();
-      } else if (isInvisible(pid)) {
-        cout << "removing inv. particle..." << endl;
-        fInvEnergy += energy;
-        fInvCount += 1;
-        p.Delete();
-      } else if (isBelowEnergyCut(p)) {
-        cout << "removing low en. particle..." << endl;
-        fEnergy += energy;
-        p.Delete();
-      } else if (p.GetTime() > 10_ms) {
-        cout << "removing OLD particle..." << endl;
-        fEnergy += energy;
-        p.Delete();
-      } else {
-        ++p; // next entry in SecondaryView
-      }
-    }
-    return EProcessReturn::eOk;
-  }
-
-  void Init() {
-    fEmEnergy = 0. * 1_GeV;
-    fEmCount = 0;
-    fInvEnergy = 0. * 1_GeV;
-    fInvCount = 0;
-    fEnergy = 0. * 1_GeV;
-    // defineEmParticles();
-  }
-
-  void ShowResults() {
-    cout << " ******************************" << endl
-         << " ParticleCut: " << endl
-         << " energy in em.  component (GeV):  " << fEmEnergy / 1_GeV << endl
-         << " no. of em.  particles injected:  " << fEmCount << endl
-         << " energy in inv. component (GeV):  " << fInvEnergy / 1_GeV << endl
-         << " no. of inv. particles injected:  " << fInvCount << endl
-         << " energy below particle cut (GeV): " << fEnergy / 1_GeV << endl
-         << " ******************************" << endl;
-  }
-
-  HEPEnergyType GetInvEnergy() const { return fInvEnergy; }
-  HEPEnergyType GetCutEnergy() const { return fEnergy; }
-  HEPEnergyType GetEmEnergy() const { return fEmEnergy; }
-};
-
 //
 // The example main program for a particle cascade
 //
@@ -257,7 +106,7 @@ int main() {
   process::sibyll::Interaction sibyll;
   process::sibyll::NuclearInteraction sibyllNuc(sibyll, env);
   process::sibyll::Decay decay(trackedHadrons);
-  ProcessCut cut(20_GeV);
+  process::particle_cut::ParticleCut cut(20_GeV);
 
   process::track_writer::TrackWriter trackWriter("tracks.dat");
   process::energy_loss::EnergyLoss eLoss;
diff --git a/Documentation/Examples/cascade_proton_example.cc b/Documentation/Examples/cascade_proton_example.cc
index e5b4b3456c42c8e03f28847e41d0babdee9ab464..4e47bf5cecc4c12e04d052655cda951086dea8c3 100644
--- a/Documentation/Examples/cascade_proton_example.cc
+++ b/Documentation/Examples/cascade_proton_example.cc
@@ -32,6 +32,8 @@
 
 #include <corsika/process/track_writer/TrackWriter.h>
 
+#include <corsika/process/particle_cut/ParticleCut.h>
+
 #include <corsika/units/PhysicalUnits.h>
 
 #include <corsika/random/RNGManager.h>
@@ -57,158 +59,6 @@ using namespace corsika::environment;
 using namespace std;
 using namespace corsika::units::si;
 
-class ProcessCut : public process::SecondariesProcess<ProcessCut> {
-
-  HEPEnergyType fECut;
-
-  HEPEnergyType fEnergy = 0_GeV;
-  HEPEnergyType fEmEnergy = 0_GeV;
-  int fEmCount = 0;
-  HEPEnergyType fInvEnergy = 0_GeV;
-  int fInvCount = 0;
-
-public:
-  ProcessCut(const HEPEnergyType cut)
-      : fECut(cut) {}
-
-  template <typename Particle>
-  bool isBelowEnergyCut(Particle& p) const {
-    // nuclei
-    if (p.GetPID() == particles::Code::Nucleus) {
-      auto const ElabNuc = p.GetEnergy() / p.GetNuclearA();
-      auto const EcmNN = sqrt(2. * ElabNuc * 0.93827_GeV);
-      if (ElabNuc < fECut || EcmNN < 10_GeV)
-        return true;
-      else
-        return false;
-    } else {
-      // TODO: center-of-mass energy hard coded
-      const HEPEnergyType Ecm = sqrt(2. * p.GetEnergy() * 0.93827_GeV);
-      if (p.GetEnergy() < fECut || Ecm < 10_GeV)
-        return true;
-      else
-        return false;
-    }
-  }
-
-  bool isEmParticle(Code pCode) const {
-    bool is_em = false;
-    // FOR NOW: switch
-    switch (pCode) {
-      case Code::Electron:
-        is_em = true;
-        break;
-      case Code::Positron:
-        is_em = true;
-        break;
-      case Code::Gamma:
-        is_em = true;
-        break;
-      default:
-        break;
-    }
-    return is_em;
-  }
-
-  void defineEmParticles() const {
-    // create bool array identifying em particles
-  }
-
-  bool isInvisible(Code pCode) const {
-    bool is_inv = false;
-    // FOR NOW: switch
-    switch (pCode) {
-      case Code::NuE:
-        is_inv = true;
-        break;
-      case Code::NuEBar:
-        is_inv = true;
-        break;
-      case Code::NuMu:
-        is_inv = true;
-        break;
-      case Code::NuMuBar:
-        is_inv = true;
-        break;
-      case Code::MuPlus:
-        is_inv = true;
-        break;
-      case Code::MuMinus:
-        is_inv = true;
-        break;
-
-      case Code::Neutron:
-        is_inv = true;
-        break;
-
-      case Code::AntiNeutron:
-        is_inv = true;
-        break;
-
-      default:
-        break;
-    }
-    return is_inv;
-  }
-
-  template <typename TSecondaries>
-  EProcessReturn DoSecondaries(TSecondaries& vS) {
-    auto p = vS.begin();
-    while (p != vS.end()) {
-      const Code pid = p.GetPID();
-      HEPEnergyType energy = p.GetEnergy();
-      cout << "ProcessCut: DoSecondaries: " << pid << " E= " << energy
-           << ", EcutTot=" << (fEmEnergy + fInvEnergy + fEnergy) / 1_GeV << " GeV"
-           << endl;
-      if (isEmParticle(pid)) {
-        cout << "removing em. particle..." << endl;
-        fEmEnergy += energy;
-        fEmCount += 1;
-        p.Delete();
-      } else if (isInvisible(pid)) {
-        cout << "removing inv. particle..." << endl;
-        fInvEnergy += energy;
-        fInvCount += 1;
-        p.Delete();
-      } else if (isBelowEnergyCut(p)) {
-        cout << "removing low en. particle..." << endl;
-        fEnergy += energy;
-        p.Delete();
-      } else if (p.GetTime() > 10_ms) {
-        cout << "removing OLD particle..." << endl;
-        fEnergy += energy;
-        p.Delete();
-      } else {
-        ++p; // next entry in SecondaryView
-      }
-    }
-    return EProcessReturn::eOk;
-  }
-
-  void Init() {
-    fEmEnergy = 0. * 1_GeV;
-    fEmCount = 0;
-    fInvEnergy = 0. * 1_GeV;
-    fInvCount = 0;
-    fEnergy = 0. * 1_GeV;
-    // defineEmParticles();
-  }
-
-  void ShowResults() {
-    cout << " ******************************" << endl
-         << " ParticleCut: " << endl
-         << " energy in em.  component (GeV):  " << fEmEnergy / 1_GeV << endl
-         << " no. of em.  particles injected:  " << fEmCount << endl
-         << " energy in inv. component (GeV):  " << fInvEnergy / 1_GeV << endl
-         << " no. of inv. particles injected:  " << fInvCount << endl
-         << " energy below particle cut (GeV): " << fEnergy / 1_GeV << endl
-         << " ******************************" << endl;
-  }
-
-  HEPEnergyType GetInvEnergy() const { return fInvEnergy; }
-  HEPEnergyType GetCutEnergy() const { return fEnergy; }
-  HEPEnergyType GetEmEnergy() const { return fEmEnergy; }
-};
 
 //
 // The example main program for a particle cascade
@@ -251,7 +101,7 @@ int main() {
   //  process::sibyll::NuclearInteraction sibyllNuc(env, sibyll);
   //  process::sibyll::Decay decay(trackedHadrons);
   process::pythia::Decay decay(trackedHadrons);
-  ProcessCut cut(20_GeV);
+  process::particle_cut::ParticleCut cut(20_GeV);
 
   // random::RNGManager::GetInstance().RegisterRandomStream("HadronicElasticModel");
   // process::HadronicElasticModel::HadronicElasticInteraction
diff --git a/Documentation/Examples/vertical_EAS.cc b/Documentation/Examples/vertical_EAS.cc
index 00f38d8b56f4082669b6ba6b995d14d2f5e135cb..872ea2d943bb808edeb295b767cc931dc3e49032 100644
--- a/Documentation/Examples/vertical_EAS.cc
+++ b/Documentation/Examples/vertical_EAS.cc
@@ -28,6 +28,7 @@
 #include <corsika/process/sibyll/Interaction.h>
 #include <corsika/process/sibyll/NuclearInteraction.h>
 
+#include <corsika/process/particle_cut/ParticleCut.h>
 #include <corsika/process/track_writer/TrackWriter.h>
 
 #include <corsika/units/PhysicalUnits.h>
@@ -55,159 +56,6 @@ using namespace corsika::environment;
 using namespace std;
 using namespace corsika::units::si;
 
-class ProcessCut : public process::SecondariesProcess<ProcessCut> {
-
-  HEPEnergyType fECut;
-
-  HEPEnergyType fEnergy = 0_GeV;
-  HEPEnergyType fEmEnergy = 0_GeV;
-  int fEmCount = 0;
-  HEPEnergyType fInvEnergy = 0_GeV;
-  int fInvCount = 0;
-
-public:
-  ProcessCut(const HEPEnergyType cut)
-      : fECut(cut) {}
-
-  template <typename Particle>
-  bool isBelowEnergyCut(Particle& p) const {
-    // nuclei
-    if (p.GetPID() == particles::Code::Nucleus) {
-      auto const ElabNuc = p.GetEnergy() / p.GetNuclearA();
-      auto const EcmNN = sqrt(2. * ElabNuc * 0.93827_GeV);
-      if (ElabNuc < fECut || EcmNN < 10_GeV)
-        return true;
-      else
-        return false;
-    } else {
-      // TODO: center-of-mass energy hard coded
-      const HEPEnergyType Ecm = sqrt(2. * p.GetEnergy() * 0.93827_GeV);
-      if (p.GetEnergy() < fECut || Ecm < 10_GeV)
-        return true;
-      else
-        return false;
-    }
-  }
-
-  bool isEmParticle(Code pCode) const {
-    bool is_em = false;
-    // FOR NOW: switch
-    switch (pCode) {
-      case Code::Electron:
-        is_em = true;
-        break;
-      case Code::Positron:
-        is_em = true;
-        break;
-      case Code::Gamma:
-        is_em = true;
-        break;
-      default:
-        break;
-    }
-    return is_em;
-  }
-
-  void defineEmParticles() const {
-    // create bool array identifying em particles
-  }
-
-  bool isInvisible(Code pCode) const {
-    bool is_inv = false;
-    // FOR NOW: switch
-    switch (pCode) {
-      case Code::NuE:
-        is_inv = true;
-        break;
-      case Code::NuEBar:
-        is_inv = true;
-        break;
-      case Code::NuMu:
-        is_inv = true;
-        break;
-      case Code::NuMuBar:
-        is_inv = true;
-        break;
-      case Code::MuPlus:
-        is_inv = true;
-        break;
-      case Code::MuMinus:
-        is_inv = true;
-        break;
-
-      case Code::Neutron:
-        is_inv = true;
-        break;
-
-      case Code::AntiNeutron:
-        is_inv = true;
-        break;
-
-      default:
-        break;
-    }
-    return is_inv;
-  }
-
-  template <typename TSecondaries>
-  EProcessReturn DoSecondaries(TSecondaries& vS) {
-    auto p = vS.begin();
-    while (p != vS.end()) {
-      const Code pid = p.GetPID();
-      HEPEnergyType energy = p.GetEnergy();
-      cout << "ProcessCut: DoSecondaries: " << pid << " E= " << energy
-           << ", EcutTot=" << (fEmEnergy + fInvEnergy + fEnergy) / 1_GeV << " GeV"
-           << endl;
-      if (isEmParticle(pid)) {
-        cout << "removing em. particle..." << endl;
-        fEmEnergy += energy;
-        fEmCount += 1;
-        p.Delete();
-      } else if (isInvisible(pid)) {
-        cout << "removing inv. particle..." << endl;
-        fInvEnergy += energy;
-        fInvCount += 1;
-        p.Delete();
-      } else if (isBelowEnergyCut(p)) {
-        cout << "removing low en. particle..." << endl;
-        fEnergy += energy;
-        p.Delete();
-      } else if (p.GetTime() > 10_ms) {
-        cout << "removing OLD particle..." << endl;
-        fEnergy += energy;
-        p.Delete();
-      } else {
-        ++p; // next entry in SecondaryView
-      }
-    }
-    return EProcessReturn::eOk;
-  }
-
-  void Init() {
-    fEmEnergy = 0. * 1_GeV;
-    fEmCount = 0;
-    fInvEnergy = 0. * 1_GeV;
-    fInvCount = 0;
-    fEnergy = 0. * 1_GeV;
-    // defineEmParticles();
-  }
-
-  void ShowResults() {
-    cout << " ******************************" << endl
-         << " ParticleCut: " << endl
-         << " energy in em.  component (GeV):  " << fEmEnergy / 1_GeV << endl
-         << " no. of em.  particles injected:  " << fEmCount << endl
-         << " energy in inv. component (GeV):  " << fInvEnergy / 1_GeV << endl
-         << " no. of inv. particles injected:  " << fInvCount << endl
-         << " energy below particle cut (GeV): " << fEnergy / 1_GeV << endl
-         << " ******************************" << endl;
-  }
-
-  HEPEnergyType GetInvEnergy() const { return fInvEnergy; }
-  HEPEnergyType GetCutEnergy() const { return fEnergy; }
-  HEPEnergyType GetEmEnergy() const { return fEmEnergy; }
-};
-
 //
 // The example main program for a particle cascade
 //
@@ -250,8 +98,7 @@ int main() {
   process::sibyll::Interaction sibyll;
   process::sibyll::NuclearInteraction sibyllNuc(sibyll, env);
   process::sibyll::Decay decay(trackedHadrons);
-  ProcessCut cut(20_GeV);
-
+  process::particle_cut::ParticleCut cut(20_GeV);
   // random::RNGManager::GetInstance().RegisterRandomStream("HadronicElasticModel");
   // process::HadronicElasticModel::HadronicElasticInteraction
   // hadronicElastic(env);
diff --git a/Processes/CMakeLists.txt b/Processes/CMakeLists.txt
index 6afe1cc51acdc689b97a90dec334d727b61c7f60..b54f76d8fd3d16d37de019ffb202015ab15bd663 100644
--- a/Processes/CMakeLists.txt
+++ b/Processes/CMakeLists.txt
@@ -11,6 +11,7 @@ add_subdirectory (HadronicElasticModel)
 add_subdirectory (EnergyLoss)
 add_subdirectory (UrQMD)
 add_subdirectory (SwitchProcess)
+add_subdirectory (ParticleCut)
 
 #add_custom_target(CORSIKAprocesses)
 add_library (CORSIKAprocesses INTERFACE)
@@ -23,3 +24,4 @@ add_dependencies(CORSIKAprocesses ProcessStackInspector)
 add_dependencies(CORSIKAprocesses ProcessTrackingLine)
 add_dependencies(CORSIKAprocesses ProcessEnergyLoss)
 add_dependencies(CORSIKAprocesses ProcessUrQMD)
+add_dependencies(CORSIKAprocesses ProcessParticleCut)
diff --git a/Processes/ParticleCut/CMakeLists.txt b/Processes/ParticleCut/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..48b539b47d273f0c1e359cb3f2d4f68cc07f1ea9
--- /dev/null
+++ b/Processes/ParticleCut/CMakeLists.txt
@@ -0,0 +1,67 @@
+set (
+  MODEL_SOURCES
+  ParticleCut.cc
+)
+
+set (
+  MODEL_HEADERS
+  ParticleCut.h
+  )
+
+set (
+  MODEL_NAMESPACE
+  corsika/process/particle_cut
+  )
+
+add_library (ProcessParticleCut STATIC ${MODEL_SOURCES})
+CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessParticleCut ${MODEL_NAMESPACE} ${MODEL_HEADERS})
+
+set_target_properties (
+  ProcessParticleCut
+  PROPERTIES
+  VERSION ${PROJECT_VERSION}
+  SOVERSION 1
+#  PUBLIC_HEADER "${MODEL_HEADERS}"
+  )
+
+# target dependencies on other libraries (also the header onlys)
+target_link_libraries (
+  ProcessParticleCut
+  CORSIKAunits
+  CORSIKAparticles
+  CORSIKAsetup
+  )
+
+target_include_directories (
+  ProcessParticleCut 
+  INTERFACE 
+  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
+  $<INSTALL_INTERFACE:include/include>
+  )
+
+install (
+  TARGETS ProcessParticleCut
+  LIBRARY DESTINATION lib
+  ARCHIVE DESTINATION lib
+#  PUBLIC_HEADER DESTINATION include/${MODEL_NAMESPACE}
+  )
+
+# --------------------
+# code unit testing
+add_executable (testParticleCut
+  testParticleCut.cc
+  ${MODEL_HEADERS}
+  )
+
+target_link_libraries (
+  testParticleCut
+  ProcessParticleCut
+  CORSIKAunits
+  CORSIKAstackinterface
+  CORSIKAprocesssequence
+  CORSIKAsetup
+  CORSIKAgeometry
+  CORSIKAenvironment
+  CORSIKAthirdparty # for catch2
+  )
+CORSIKA_ADD_TEST(testParticleCut)
diff --git a/Processes/ParticleCut/ParticleCut.cc b/Processes/ParticleCut/ParticleCut.cc
new file mode 100644
index 0000000000000000000000000000000000000000..28e7013d08d864471de08c742250b3ecb4357760
--- /dev/null
+++ b/Processes/ParticleCut/ParticleCut.cc
@@ -0,0 +1,140 @@
+
+/*
+ * (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/particle_cut/ParticleCut.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 particle_cut {
+
+    template <typename TParticle>
+    bool ParticleCut::ParticleIsBelowEnergyCut(TParticle const& vP) const {
+      auto const energyLab = vP.GetEnergy();
+      // nuclei
+      if (vP.GetPID() == particles::Code::Nucleus) {
+	// calculate energy per nucleon
+        auto const ElabNuc = energyLab / vP.GetNuclearA();
+        return (ElabNuc < fECut);
+      } else {
+        return (energyLab < fECut);
+      }
+    }
+
+    bool ParticleCut::ParticleIsEmParticle(Code vCode) const {
+      // FOR NOW: switch
+      switch (vCode) {
+        case Code::Gamma:
+        case Code::Electron:
+        case Code::Positron:
+          return true;
+        default:
+          return false;
+      }
+    }
+
+    bool ParticleCut::ParticleIsInvisible(Code vCode) const {
+      bool is_inv = false;
+      // FOR NOW: switch
+      switch (vCode) {
+        case Code::NuE:
+          is_inv = true;
+          break;
+        case Code::NuEBar:
+          is_inv = true;
+          break;
+        case Code::NuMu:
+          is_inv = true;
+          break;
+        case Code::NuMuBar:
+          is_inv = true;
+          break;
+        case Code::MuPlus:
+          is_inv = true;
+          break;
+        case Code::MuMinus:
+          is_inv = true;
+          break;
+
+        case Code::Neutron:
+          is_inv = true;
+          break;
+
+        case Code::AntiNeutron:
+          is_inv = true;
+          break;
+
+        default:
+          break;
+      }
+      return is_inv;
+    }
+
+    EProcessReturn ParticleCut::DoSecondaries(corsika::setup::StackView& vS) {
+      auto p = vS.begin();
+      while (p != vS.end()) {
+        const Code pid = p.GetPID();
+        HEPEnergyType energy = p.GetEnergy();
+        cout << "ProcessCut: DoSecondaries: " << pid << " E= " << energy
+             << ", EcutTot=" << (fEmEnergy + fInvEnergy + fEnergy) / 1_GeV << " GeV"
+             << endl;
+        if (ParticleIsEmParticle(pid)) {
+          cout << "removing em. particle..." << endl;
+          fEmEnergy += energy;
+          fEmCount += 1;
+          p.Delete();
+        } else if (ParticleIsInvisible(pid)) {
+          cout << "removing inv. particle..." << endl;
+          fInvEnergy += energy;
+          fInvCount += 1;
+          p.Delete();
+        } else if (ParticleIsBelowEnergyCut(p)) {
+          cout << "removing low en. particle..." << endl;
+          fEnergy += energy;
+          p.Delete();
+        } else if (p.GetTime() > 10_ms) {
+          cout << "removing OLD particle..." << endl;
+          fEnergy += energy;
+          p.Delete();
+        } else {
+          ++p; // next entry in SecondaryView
+        }
+      }
+      return EProcessReturn::eOk;
+    }
+
+    void ParticleCut::Init() {
+      fEmEnergy = 0._GeV;
+      fEmCount = 0;
+      fInvEnergy = 0._GeV;
+      fInvCount = 0;
+      fEnergy = 0._GeV;
+      // defineEmParticles();
+    }
+
+    void ParticleCut::ShowResults() {
+      cout << " ******************************" << endl
+           << " ParticleCut: " << endl
+           << " energy in em.  component (GeV):  " << fEmEnergy / 1_GeV << endl
+           << " no. of em.  particles injected:  " << fEmCount << endl
+           << " energy in inv. component (GeV):  " << fInvEnergy / 1_GeV << endl
+           << " no. of inv. particles injected:  " << fInvCount << endl
+           << " energy below particle cut (GeV): " << fEnergy / 1_GeV << endl
+           << " ******************************" << endl;
+    }
+  } // namespace particle_cut
+} // namespace corsika::process
diff --git a/Processes/ParticleCut/ParticleCut.h b/Processes/ParticleCut/ParticleCut.h
new file mode 100644
index 0000000000000000000000000000000000000000..6d5f7a685cccbce7f2e079a3c291f90dbf20039b
--- /dev/null
+++ b/Processes/ParticleCut/ParticleCut.h
@@ -0,0 +1,53 @@
+/*
+ * (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;
+      int fEmCount = 0;
+      units::si::HEPEnergyType fInvEnergy = 0 * units::si::electronvolt;
+      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; }
+    };
+  } // namespace particle_cut
+} // namespace corsika::process
+
+#endif
diff --git a/Processes/ParticleCut/testParticleCut.cc b/Processes/ParticleCut/testParticleCut.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c1a69696f06a331289ce20ae901fc3329d46fb34
--- /dev/null
+++ b/Processes/ParticleCut/testParticleCut.cc
@@ -0,0 +1,109 @@
+
+/*
+ * (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/particle_cut/ParticleCut.h>
+
+#include <corsika/environment/Environment.h>
+#include <corsika/geometry/Point.h>
+#include <corsika/geometry/RootCoordinateSystem.h>
+#include <corsika/geometry/Vector.h>
+#include <corsika/units/PhysicalUnits.h>
+#include <corsika/utl/CorsikaFenv.h>
+
+#include <corsika/setup/SetupStack.h>
+
+#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one
+                          // cpp file
+#include <catch2/catch.hpp>
+
+using namespace corsika;
+using namespace corsika::process::particle_cut;
+using namespace corsika::units;
+using namespace corsika::units::si;
+
+TEST_CASE("ParticleCut", "[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 Eabove = 1_TeV;
+  const HEPEnergyType Ebelow = 10_GeV;
+  // list of arbitrary particles
+  std::vector<particles::Code> particleList = {
+      particles::Code::PiPlus,   particles::Code::PiMinus, particles::Code::KPlus,
+      particles::Code::KMinus,   particles::Code::K0Long,  particles::Code::K0Short,
+      particles::Code::Electron, particles::Code::MuPlus,  particles::Code::NuE,
+      particles::Code::Neutron};
+
+  SECTION("cut invisible") {
+
+    ParticleCut cut(20_GeV);
+
+    // 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, Eabove,
+            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
+    for (auto proType : particleList)
+      projectile.AddSecondary(std::tuple<particles::Code, units::si::HEPEnergyType,
+                                         corsika::stack::MomentumVector, geometry::Point,
+                                         units::si::TimeType>{
+          proType, Eabove, corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}),
+          geometry::Point(rootCS, 0_m, 0_m, 0_m), 0_ns});
+
+    cut.DoSecondaries(view);
+
+    REQUIRE(view.GetSize() == 6);
+  }
+
+  SECTION("cut low energy") {
+    ParticleCut cut(20_GeV);
+
+    // 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, Eabove,
+            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 below the threshold
+    // only cut is by species
+    for (auto proType : particleList)
+      projectile.AddSecondary(std::tuple<particles::Code, units::si::HEPEnergyType,
+                                         corsika::stack::MomentumVector, geometry::Point,
+                                         units::si::TimeType>{
+          proType, Ebelow, corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}),
+          geometry::Point(rootCS, 0_m, 0_m, 0_m), 0_ns});
+
+    cut.DoSecondaries(view);
+
+    REQUIRE(view.GetSize() == 0);
+  }
+}