diff --git a/Processes/CMakeLists.txt b/Processes/CMakeLists.txt
index 2835a7043cc80101ef22d1301af981b5735de6bb..6afe1cc51acdc689b97a90dec334d727b61c7f60 100644
--- a/Processes/CMakeLists.txt
+++ b/Processes/CMakeLists.txt
@@ -10,6 +10,7 @@ add_subdirectory (TrackingLine)
 add_subdirectory (HadronicElasticModel)
 add_subdirectory (EnergyLoss)
 add_subdirectory (UrQMD)
+add_subdirectory (SwitchProcess)
 
 #add_custom_target(CORSIKAprocesses)
 add_library (CORSIKAprocesses INTERFACE)
diff --git a/Processes/SwitchProcess/CMakeLists.txt b/Processes/SwitchProcess/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0eb1418498c2c8a9771364c30592ec9303bcc6ef
--- /dev/null
+++ b/Processes/SwitchProcess/CMakeLists.txt
@@ -0,0 +1,48 @@
+set (
+  MODEL_HEADERS
+  SwitchProcess.h
+  )
+
+set (
+  MODEL_NAMESPACE
+  corsika/process/switch_process
+  )
+
+add_library (ProcessSwitch INTERFACE)
+CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessSwitch ${MODEL_NAMESPACE} ${MODEL_HEADERS})
+
+set_target_properties (
+  ProcessStackInspector
+  PROPERTIES
+  VERSION ${PROJECT_VERSION}
+  SOVERSION 1
+#  PUBLIC_HEADER "${MODEL_HEADERS}"
+  )
+
+# target dependencies on other libraries (also the header onlys)
+target_link_libraries (
+  ProcessSwitch
+  INTERFACE
+  CORSIKAunits
+  CORSIKAprocesssequence
+  )
+
+target_include_directories (
+  ProcessSwitch 
+  INTERFACE 
+  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
+  $<INSTALL_INTERFACE:include/include>
+  )
+
+install (FILES ${MODEL_HEADERS} DESTINATION include/${MODEL_NAMESPACE})
+
+# --------------------
+# code unit testing
+#add_executable (testStackInspector testSwitchProcess.cc)
+
+#target_link_libraries (
+#  testSwitchProcess
+#  ProcessSwitch
+#  CORSIKAthirdparty # for catch2
+#  )
+#CORSIKA_ADD_TEST(testStackInspector)
diff --git a/Processes/SwitchProcess/SwitchProcess.h b/Processes/SwitchProcess/SwitchProcess.h
new file mode 100644
index 0000000000000000000000000000000000000000..85d614109ff3f83bd7d3151b71f0a604f4a6bcfc
--- /dev/null
+++ b/Processes/SwitchProcess/SwitchProcess.h
@@ -0,0 +1,85 @@
+#ifndef _corsika_SwitchProcess_h
+#define _corsika_SwitchProcess_h
+
+#include <corsika/process/InteractionProcess.h>
+#include <corsika/process/ProcessSequence.h>
+#include <corsika/setup/SetupStack.h>
+#include <corsika/units/PhysicalUnits.h>
+
+namespace corsika::process::switch_process {
+
+  /**
+   * This process provides an energy-based switch between two interaction processes P1 and
+   * P1. For energies below the threshold, P1 is invoked, otherwise P2. Both can be either
+   * single interaction processes or multiple ones combined in a ProcessSequence. A
+   * SwitchProcess itself will always be regarded as a ProcessSequence rather than an
+   * InteractionProcess when assembled into a greater ProcessSequence.
+   */
+
+  template <class TLowEProcess, class THighEProcess>
+  class SwitchProcess
+      : public InteractionProcess<SwitchProcess<TLowEProcess, THighEProcess>> {
+    TLowEProcess& fLowEProcess;
+    THighEProcess& fHighEProcess;
+    units::si::HEPEnergyType const fThresholdEnergy;
+
+  public:
+    SwitchProcess(TLowEProcess& vLowEProcess, THighEProcess& vHighEProcess,
+                  units::si::HEPEnergyType vThresholdEnergy)
+        : fLowEProcess(vLowEProcess)
+        , fHighEProcess(vHighEProcess)
+        , fThresholdEnergy(vThresholdEnergy) {}
+
+    void Init() {
+      fLowEProcess.Init();
+      fHighEProcess.Init();
+    }
+
+    template <typename TParticle>
+    units::si::GrammageType GetInteractionLength(TParticle& vParticle) {
+      if (vParticle.GetEnergy() < fThresholdEnergy) {
+        if constexpr (is_process_sequence_v<TLowEProcess>) {
+          return fLowEProcess.GetTotalInteractionLength(vParticle);
+        } else {
+          return fLowEProcess.GetInteractionLength(vParticle);
+        }
+      } else {
+        if constexpr (is_process_sequence_v<THighEProcess>) {
+          return fHighEProcess.GetTotalInteractionLength(vParticle);
+        } else {
+          return fHighEProcess.GetInteractionLength(vParticle);
+        }
+      }
+    }
+
+    // required to conform to ProcessSequence interface. We cannot just
+    // implement DoInteraction() because we want to call SelectInteraction
+    // in case a member process is a ProcessSequence.
+    template <typename TParticle, typename TSecondaries>
+    EProcessReturn SelectInteraction(
+        TParticle& vP, TSecondaries& vS,
+        [[maybe_unused]] corsika::units::si::InverseGrammageType lambda_select,
+        corsika::units::si::InverseGrammageType& lambda_inv_count) {
+
+      if (vP.GetEnergy() < fThresholdEnergy) {
+        if constexpr (is_process_sequence_v<TLowEProcess>) {
+          return fLowEProcess.SelectInteraction(vP, vS, lambda_select, lambda_inv_count);
+        } else {
+          return fLowEProcess.DoInteraction(vS);
+        }
+      } else {
+        if constexpr (is_process_sequence_v<THighEProcess>) {
+          return fHighEProcess.SelectInteraction(vP, vS, lambda_select, lambda_inv_count);
+        } else {
+          return fHighEProcess.DoInteraction(vS);
+        }
+      }
+    }
+  };
+} // namespace corsika::process::switch_process
+
+template <typename A, typename B>
+struct corsika::process::is_process_sequence<
+    corsika::process::switch_process::SwitchProcess<A, B>> : std::true_type {};
+
+#endif