diff --git a/corsika/detail/setup/SetupStack.hpp b/corsika/detail/setup/SetupStack.hpp
index 2b40ccbdb0eafcc2c48ead0dc437c1314d6e2286..bec6c0aa1595a59d20f0ef044827e39608e23440 100644
--- a/corsika/detail/setup/SetupStack.hpp
+++ b/corsika/detail/setup/SetupStack.hpp
@@ -20,61 +20,76 @@
 namespace corsika {
 
   namespace setup::detail {
-
-    // ------------------------------------------
-    // add geometry node data to stack. This is fundamentally needed
-    // for robust tracking through multiple volumes.
-
-    // the GeometryNode stack needs to know the type of geometry-nodes from the
-    // environment:
-    template <typename TStackIter>
-    using SetupGeometryDataInterface =
-        typename node::MakeGeometryDataInterface<TStackIter, setup::Environment>::type;
-
-    // combine particle data stack with geometry information for tracking
-    template <typename TStackIter>
-    using StackWithGeometryInterface =
-        CombinedParticleInterface<VectorStack::pi_type, SetupGeometryDataInterface,
-                                  TStackIter>;
-
-    using StackWithGeometry =
-        CombinedStack<typename VectorStack::stack_data_type,
-                      node::GeometryData<setup::Environment>, StackWithGeometryInterface,
-                      DefaultSecondaryProducer>;
-
-    // ------------------------------------------
-    // add weight data to stack. This is fundamentally needed
-    // for thinning.
-
-    // the "pure" weight stack (interface)
-    template <typename TStackIter>
-    using SetupWeightDataInterface =
-        typename weights::MakeWeightDataInterface<TStackIter>::type;
-
-    // combine geometry-node-vector data stack with weight information for tracking
-    template <typename TStackIter>
-    using StackWithWeightInterface =
-        CombinedParticleInterface<StackWithGeometry::pi_type, SetupWeightDataInterface,
-                                  TStackIter>;
-
-    // the combined stack data: particle + geometry + weight
-    using StackWithWeight =
-        CombinedStack<typename StackWithGeometry::stack_data_type, weights::WeightData,
-                      StackWithWeightInterface, DefaultSecondaryProducer>;
-
-    // ------------------------------------------
-    // Add [OPTIONAL] history data to stack, too.
-    // This keeps the entire lineage of particles in memory.
-
-    template <typename TStackIter>
-    using StackWithHistoryInterface =
-        CombinedParticleInterface<StackWithWeight::pi_type,
-                                  history::HistoryEventDataInterface, TStackIter>;
-
-    using StackWithHistory =
-        CombinedStack<typename StackWithWeight::stack_data_type,
-                      history::HistoryEventData, StackWithHistoryInterface,
-                      history::HistorySecondaryProducer>;
+    template <typename TEnvironment = setup::Environment>
+    class StackGenerator {
+    private:
+      using env_type = TEnvironment;
+
+      // ------------------------------------------
+      // add geometry node data to stack. This is fundamentally needed
+      // for robust tracking through multiple volumes.
+
+      // the GeometryNode stack needs to know the type of geometry-nodes from the
+      // environment:
+
+      template <typename TStackIter>
+      using SetupGeometryDataInterface =
+          typename node::MakeGeometryDataInterface<TStackIter, env_type>::type;
+
+      // combine particle data stack with geometry information for tracking
+      template <typename TStackIter>
+      using StackWithGeometryInterface =
+          CombinedParticleInterface<VectorStack::pi_type, SetupGeometryDataInterface,
+                                    TStackIter>;
+
+      using StackWithGeometry =
+          CombinedStack<typename VectorStack::stack_data_type,
+                        node::GeometryData<env_type>, StackWithGeometryInterface,
+                        DefaultSecondaryProducer>;
+
+      template <class T>
+      using StackWithGeometry_PI_type = typename StackWithGeometry::template pi_type<T>;
+
+      // ------------------------------------------
+      // add weight data to stack. This is fundamentally needed
+      // for thinning.
+
+      // the "pure" weight stack (interface)
+      template <typename TStackIter>
+      using SetupWeightDataInterface =
+          typename weights::MakeWeightDataInterface<TStackIter>::type;
+
+      // combine geometry-node-vector data stack with weight information for tracking
+      template <typename TStackIter>
+      using StackWithWeightInterface =
+          CombinedParticleInterface<StackWithGeometry_PI_type, SetupWeightDataInterface,
+                                    TStackIter>;
+
+    public:
+      // the combined stack data: particle + geometry + weight
+      using StackWithWeight =
+          CombinedStack<typename StackWithGeometry::stack_data_type, weights::WeightData,
+                        StackWithWeightInterface, DefaultSecondaryProducer>;
+
+    private:
+      template <typename T>
+      using StackWithWeight_PI_type = typename StackWithWeight::template pi_type<T>;
+
+      // ------------------------------------------
+      // Add [OPTIONAL] history data to stack, too.
+      // This keeps the entire lineage of particles in memory.
+
+      template <typename TStackIter>
+      using StackWithHistoryInterface =
+          CombinedParticleInterface<StackWithWeight_PI_type,
+                                    history::HistoryEventDataInterface, TStackIter>;
+
+    public:
+      using StackWithHistory =
+          CombinedStack<typename StackWithWeight::stack_data_type,
+                        history::HistoryEventData, StackWithHistoryInterface,
+                        history::HistorySecondaryProducer>;
+    };
 
   } // namespace setup::detail
 
diff --git a/corsika/setup/SetupStack.hpp b/corsika/setup/SetupStack.hpp
index 9028c2ab175127d07f3c430bbbba5edb5c82722d..7d1894e4cd8c00fa88ec290e4032a11cc5550d1b 100644
--- a/corsika/setup/SetupStack.hpp
+++ b/corsika/setup/SetupStack.hpp
@@ -24,18 +24,21 @@ namespace corsika::setup {
   /*
    * the version with history
    */
-  using Stack = detail::StackWithHistory;
+  template <typename TEnvironment>
+  using Stack = typename detail::StackGenerator<TEnvironment>::StackWithWeight;
 
 #else // WITH_HISTORY
 
   /*
    * the version without history (and geometry data and weights)
    */
-  using Stack = detail::StackWithWeight;
+  template <typename TEnvironment>
+  using Stack = typename detail::StackGenerator<TEnvironment>::StackWithWeight;
 
 #endif
 
   // the correct secondary stack view
-  using StackView = typename Stack::stack_view_type;
+  template <typename TEnvironment>
+  using StackView = typename Stack<TEnvironment>::stack_view_type;
 
 } // namespace corsika::setup
diff --git a/examples/em_shower.cpp b/examples/em_shower.cpp
index 615b840c18d53d1d9d6bfc4ec92c0203e9c3619b..fe67c6477184f4f7d17e66b94c6ba52d8d12695a 100644
--- a/examples/em_shower.cpp
+++ b/examples/em_shower.cpp
@@ -110,7 +110,7 @@ int main(int argc, char** argv) {
     set_energy_production_threshold(pcode, energy);
 
   // setup particle stack, and add primary particle
-  setup::Stack stack;
+  setup::Stack<EnvType> stack;
   stack.clear();
   const Code beamCode = Code::Electron;
   auto const mass = get_mass(beamCode);