diff --git a/corsika/detail/stack/NuclearStackExtension.inl b/corsika/detail/stack/NuclearStackExtension.inl
new file mode 100644
index 0000000000000000000000000000000000000000..3cba30ed7af0e482929523e980d8a552c161f900
--- /dev/null
+++ b/corsika/detail/stack/NuclearStackExtension.inl
@@ -0,0 +1,227 @@
+/*
+ * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/stack/Stack.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/stack/SuperStupidStack.hpp>
+
+#include <algorithm>
+#include <tuple>
+#include <vector>
+
+namespace corsika::nuclear_stack {
+
+template <template <typename> class InnerParticleInterface,
+          typename StackIteratorInterface>
+void NuclearParticleInterface< InnerParticleInterface,StackIteratorInterface>::setParticleData(particle_data_type const& v)
+{
+
+      if (std::get<0>(v) == Code::Nucleus) {
+        std::ostringstream err;
+        err << "NuclearStackExtension: no A and Z specified for new Nucleus!";
+        throw std::runtime_error(err.str());
+      }
+
+      super_type::setParticleData(v);
+      setNucleusRef(-1); // this is not a nucleus
+    }
+
+template <template <typename> class InnerParticleInterface,
+          typename StackIteratorInterface>
+void NuclearParticleInterface< InnerParticleInterface,StackIteratorInterface>::setParticleData(altenative_particle_data_type const& v)
+{
+      const unsigned short A = std::get<5>(v);
+      const unsigned short Z = std::get<6>(v);
+      if (std::get<0>(v) != Code::Nucleus || A == 0 || Z == 0) {
+        std::ostringstream err;
+        err << "NuclearStackExtension: no A and Z specified for new Nucleus!";
+        throw std::runtime_error(err.str());
+      }
+      setNucleusRef(
+          super_type::getStackData().getNucleusNextRef()); // store this nucleus data ref
+      setNuclearA(A);
+      setNuclearZ(Z);
+      super_type::setParticleData(particle_data_type{std::get<0>(v), std::get<1>(v),
+                                                     std::get<2>(v), std::get<3>(v),
+                                                     std::get<4>(v)});
+    }
+
+template <template <typename> class InnerParticleInterface,
+          typename StackIteratorInterface>
+void NuclearParticleInterface< InnerParticleInterface,StackIteratorInterface>::setParticleData(super_type& p, particle_data_type const& v) {
+    if (std::get<0>(v) == Code::Nucleus) {
+      std::ostringstream err;
+      err << "NuclearStackExtension: no A and Z specified for new Nucleus!";
+      throw std::runtime_error(err.str());
+    }
+
+    super_type::setParticleData(
+        p, particle_data_type{std::get<0>(v), std::get<1>(v), std::get<2>(v),
+                              std::get<3>(v), std::get<4>(v)});
+
+    setNucleusRef(-1); // this is not a nucleus
+  }
+
+template <template <typename> class InnerParticleInterface,
+          typename StackIteratorInterface>
+void NuclearParticleInterface< InnerParticleInterface,StackIteratorInterface>::setParticleData(super_type& p, altenative_particle_data_type const& v) {
+
+    const unsigned short A = std::get<5>(v);
+    const unsigned short Z = std::get<6>(v);
+
+    if (std::get<0>(v) != Code::Nucleus || A == 0 || Z == 0) {
+      std::ostringstream err;
+      err << "NuclearStackExtension: no A and Z specified for new Nucleus!";
+      throw std::runtime_error(err.str());
+    }
+
+    setNucleusRef(
+        super_type::getStackData().getNucleusNextRef()); // store this nucleus data ref
+    setNuclearA(A);
+    setNuclearZ(Z);
+    super_type::setParticleData(
+        p, particle_data_type{std::get<0>(v), std::get<1>(v), std::get<2>(v),
+                              std::get<3>(v), std::get<4>(v)});
+  }
+
+template <template <typename> class InnerParticleInterface,
+          typename StackIteratorInterface>
+std::string NuclearParticleInterface< InnerParticleInterface,StackIteratorInterface>::as_string() const {
+    return fmt::format(
+        "{}, nuc({})", super_type::as_string(),
+        (isNucleus() ? fmt::format("A={}, Z={}", getNuclearA(), getNuclearZ())
+                     : "n/a"));
+  }
+
+
+template <template <typename> class InnerParticleInterface,
+          typename StackIteratorInterface>
+HEPMassType NuclearParticleInterface< InnerParticleInterface,StackIteratorInterface>::getMass() const {
+    if (super_type::getPID() == Code::Nucleus)
+      return getNucleusMass(getNuclearA(), getNuclearZ());
+    return super_type::getMass();
+  }
+
+template <template <typename> class InnerParticleInterface,
+          typename StackIteratorInterface>
+int16_t NuclearParticleInterface< InnerParticleInterface,StackIteratorInterface>::getChargeNumber() const {
+    if (super_type::getPID() == Code::Nucleus) return getNuclearZ();
+    return super_type::getChargeNumber();
+  }
+
+template <typename InnerStackImpl>
+int NuclearStackExtensionImpl<InnerStackImpl>::getNucleusNextRef(){
+    nuclearA_.push_back(0);
+    nuclearZ_.push_back(0);
+    return nuclearA_.size() - 1;
+  }
+
+template <typename InnerStackImpl>
+int NuclearStackExtensionImpl<InnerStackImpl>::getNucleusRef(const unsigned int i) const {
+    if (nucleusRef_[i] >= 0) return nucleusRef_[i];
+    std::ostringstream err;
+    err << "NuclearStackExtension: no nucleus at ref=" << i;
+    throw std::runtime_error(err.str());
+  }
+
+template <typename InnerStackImpl>
+void NuclearStackExtensionImpl<InnerStackImpl>::copy(const unsigned int i1, const unsigned int i2) {
+    // index range check
+    if (i1 >= getSize() || i2 >= getSize()) {
+      std::ostringstream err;
+      err << "NuclearStackExtension: trying to access data beyond size of stack!";
+      throw std::runtime_error(err.str());
+    }
+    // copy internal particle data p[i2] = p[i1]
+    super_type::copy(i1, i2);
+    // check if any of p[i1] or p[i2] was a Code::Nucleus
+    const int ref1 = nucleusRef_[i1];
+    const int ref2 = nucleusRef_[i2];
+    if (ref2 < 0) {
+      if (ref1 >= 0) {
+        // i1 is nucleus, i2 is not
+        nucleusRef_[i2] = getNucleusNextRef();
+        nuclearA_[nucleusRef_[i2]] = nuclearA_[ref1];
+        nuclearZ_[nucleusRef_[i2]] = nuclearZ_[ref1];
+      } else {
+        // neither i1 nor i2 are nuclei
+      }
+    } else {
+      if (ref1 >= 0) {
+        // both are nuclei, i2 is overwritten with nucleus i1
+        // fNucleusRef stays the same, but A and Z data is overwritten
+        nuclearA_[ref2] = nuclearA_[ref1];
+        nuclearZ_[ref2] = nuclearZ_[ref1];
+      } else {
+        // i2 is overwritten with non-nucleus i1
+        nucleusRef_[i2] = -1;                       // flag as non-nucleus
+        nuclearA_.erase(nuclearA_.cbegin() + ref2); // remove data for i2
+        nuclearZ_.erase(nuclearZ_.cbegin() + ref2); // remove data for i2
+        const int n = nucleusRef_.size(); // update fNucleusRef: indices above ref2
+        // must be decremented by 1
+        for (int i = 0; i < n; ++i) {
+          if (nucleusRef_[i] > ref2) { nucleusRef_[i] -= 1; }
+        }
+      }
+    }
+  }
+
+template <typename InnerStackImpl>
+void NuclearStackExtensionImpl<InnerStackImpl>::clear() {
+    super_type::clear();
+    nucleusRef_.clear();
+    nuclearA_.clear();
+    nuclearZ_.clear();
+  }
+
+template <typename InnerStackImpl>
+void NuclearStackExtensionImpl<InnerStackImpl>::swap(const unsigned int i1, const unsigned int i2) {
+    // index range check
+    if (i1 >= getSize() || i2 >= getSize()) {
+      std::ostringstream err;
+      err << "NuclearStackExtension: trying to access data beyond size of stack!";
+      throw std::runtime_error(err.str());
+    }
+    // swap original particle data
+    super_type::swap(i1, i2);
+    // swap corresponding nuclear reference data
+    std::swap(nucleusRef_[i2], nucleusRef_[i1]);
+  }
+
+
+template <typename InnerStackImpl>
+void NuclearStackExtensionImpl<InnerStackImpl>::incrementSize() {
+    super_type::incrementSize();
+    nucleusRef_.push_back(-1);
+  }
+
+template <typename InnerStackImpl>
+void NuclearStackExtensionImpl<InnerStackImpl>::decrementSize() {
+    super_type::decrementSize();
+    if (nucleusRef_.size() > 0) {
+      const int ref = nucleusRef_.back();
+      nucleusRef_.pop_back();
+      if (ref >= 0) {
+        nuclearA_.erase(nuclearA_.begin() + ref);
+        nuclearZ_.erase(nuclearZ_.begin() + ref);
+        const int n = nucleusRef_.size();
+        for (int i = 0; i < n; ++i) {
+          if (nucleusRef_[i] >= ref) { nucleusRef_[i] -= 1; }
+        }
+      }
+    }
+  }
+
+
+
+} // namespace corsika::nuclear_stack
diff --git a/corsika/stack/NuclearStackExtension.hpp b/corsika/stack/NuclearStackExtension.hpp
index 520accc5b70f6500a9b308c576cff2d7a8eba309..c120963993886acf3dbb0604b9efa1c6552d96db 100644
--- a/corsika/stack/NuclearStackExtension.hpp
+++ b/corsika/stack/NuclearStackExtension.hpp
@@ -53,75 +53,15 @@ namespace corsika::nuclear_stack {
                        unsigned short, unsigned short>
         altenative_particle_data_type;
 
-    void setParticleData(particle_data_type const& v) {
+    void setParticleData(particle_data_type const& v) ;
 
-      if (std::get<0>(v) == Code::Nucleus) {
-        std::ostringstream err;
-        err << "NuclearStackExtension: no A and Z specified for new Nucleus!";
-        throw std::runtime_error(err.str());
-      }
+    void setParticleData(altenative_particle_data_type const& v);
 
-      super_type::setParticleData(v);
-      setNucleusRef(-1); // this is not a nucleus
-    }
-
-    void setParticleData(altenative_particle_data_type const& v) {
-      const unsigned short A = std::get<5>(v);
-      const unsigned short Z = std::get<6>(v);
-      if (std::get<0>(v) != Code::Nucleus || A == 0 || Z == 0) {
-        std::ostringstream err;
-        err << "NuclearStackExtension: no A and Z specified for new Nucleus!";
-        throw std::runtime_error(err.str());
-      }
-      setNucleusRef(
-          super_type::getStackData().getNucleusNextRef()); // store this nucleus data ref
-      setNuclearA(A);
-      setNuclearZ(Z);
-      super_type::setParticleData(particle_data_type{std::get<0>(v), std::get<1>(v),
-                                                     std::get<2>(v), std::get<3>(v),
-                                                     std::get<4>(v)});
-    }
-
-    void setParticleData(super_type& p, particle_data_type const& v) {
-      if (std::get<0>(v) == Code::Nucleus) {
-        std::ostringstream err;
-        err << "NuclearStackExtension: no A and Z specified for new Nucleus!";
-        throw std::runtime_error(err.str());
-      }
-
-      super_type::setParticleData(
-          p, particle_data_type{std::get<0>(v), std::get<1>(v), std::get<2>(v),
-                                std::get<3>(v), std::get<4>(v)});
-
-      setNucleusRef(-1); // this is not a nucleus
-    }
-
-    void setParticleData(super_type& p, altenative_particle_data_type const& v) {
-
-      const unsigned short A = std::get<5>(v);
-      const unsigned short Z = std::get<6>(v);
+    void setParticleData(super_type& p, particle_data_type const& v);
 
-      if (std::get<0>(v) != Code::Nucleus || A == 0 || Z == 0) {
-        std::ostringstream err;
-        err << "NuclearStackExtension: no A and Z specified for new Nucleus!";
-        throw std::runtime_error(err.str());
-      }
+    void setParticleData(super_type& p, altenative_particle_data_type const& v);
 
-      setNucleusRef(
-          super_type::getStackData().getNucleusNextRef()); // store this nucleus data ref
-      setNuclearA(A);
-      setNuclearZ(Z);
-      super_type::setParticleData(
-          p, particle_data_type{std::get<0>(v), std::get<1>(v), std::get<2>(v),
-                                std::get<3>(v), std::get<4>(v)});
-    }
-
-    std::string as_string() const {
-      return fmt::format(
-          "{}, nuc({})", super_type::as_string(),
-          (isNucleus() ? fmt::format("A={}, Z={}", getNuclearA(), getNuclearZ())
-                       : "n/a"));
-    }
+    std::string as_string() const;
 
     /**
      * @name individual setters
@@ -150,18 +90,11 @@ namespace corsika::nuclear_stack {
     /**
      * Overwrite normal getParticleMass function with nuclear version
      */
-    HEPMassType getMass() const {
-      if (super_type::getPID() == Code::Nucleus)
-        return getNucleusMass(getNuclearA(), getNuclearZ());
-      return super_type::getMass();
-    }
+    HEPMassType getMass() const;
     /**
      * Overwirte normal getChargeNumber function with nuclear version
      **/
-    int16_t getChargeNumber() const {
-      if (super_type::getPID() == Code::Nucleus) return getNuclearZ();
-      return super_type::getChargeNumber();
-    }
+    int16_t getChargeNumber() const;
 
     int getNucleusRef() const {
       return super_type::getStackData().getNucleusRef(super_type::getIndex());
@@ -215,12 +148,7 @@ namespace corsika::nuclear_stack {
 
     void dump() { super_type::dump(); }
 
-    void clear() {
-      super_type::clear();
-      nucleusRef_.clear();
-      nuclearA_.clear();
-      nuclearZ_.clear();
-    }
+    void clear();
 
     unsigned int getSize() const { return nucleusRef_.size(); }
 
@@ -241,101 +169,24 @@ namespace corsika::nuclear_stack {
     int getNuclearZ(const unsigned int i) const { return nuclearZ_[getNucleusRef(i)]; }
     // this function will create new storage for Nuclear Properties, and return the
     // reference to it
-    int getNucleusNextRef() {
-      nuclearA_.push_back(0);
-      nuclearZ_.push_back(0);
-      return nuclearA_.size() - 1;
-    }
+    int getNucleusNextRef() ;
 
-    int getNucleusRef(const unsigned int i) const {
-      if (nucleusRef_[i] >= 0) return nucleusRef_[i];
-      std::ostringstream err;
-      err << "NuclearStackExtension: no nucleus at ref=" << i;
-      throw std::runtime_error(err.str());
-    }
+    int getNucleusRef(const unsigned int i) const;
 
     bool isNucleus(const unsigned int i) const { return nucleusRef_[i] >= 0; }
 
     /**
      *   Function to copy particle at location i1 in stack to i2
      */
-    void copy(const unsigned int i1, const unsigned int i2) {
-      // index range check
-      if (i1 >= getSize() || i2 >= getSize()) {
-        std::ostringstream err;
-        err << "NuclearStackExtension: trying to access data beyond size of stack!";
-        throw std::runtime_error(err.str());
-      }
-      // copy internal particle data p[i2] = p[i1]
-      super_type::copy(i1, i2);
-      // check if any of p[i1] or p[i2] was a Code::Nucleus
-      const int ref1 = nucleusRef_[i1];
-      const int ref2 = nucleusRef_[i2];
-      if (ref2 < 0) {
-        if (ref1 >= 0) {
-          // i1 is nucleus, i2 is not
-          nucleusRef_[i2] = getNucleusNextRef();
-          nuclearA_[nucleusRef_[i2]] = nuclearA_[ref1];
-          nuclearZ_[nucleusRef_[i2]] = nuclearZ_[ref1];
-        } else {
-          // neither i1 nor i2 are nuclei
-        }
-      } else {
-        if (ref1 >= 0) {
-          // both are nuclei, i2 is overwritten with nucleus i1
-          // fNucleusRef stays the same, but A and Z data is overwritten
-          nuclearA_[ref2] = nuclearA_[ref1];
-          nuclearZ_[ref2] = nuclearZ_[ref1];
-        } else {
-          // i2 is overwritten with non-nucleus i1
-          nucleusRef_[i2] = -1;                       // flag as non-nucleus
-          nuclearA_.erase(nuclearA_.cbegin() + ref2); // remove data for i2
-          nuclearZ_.erase(nuclearZ_.cbegin() + ref2); // remove data for i2
-          const int n = nucleusRef_.size(); // update fNucleusRef: indices above ref2
-          // must be decremented by 1
-          for (int i = 0; i < n; ++i) {
-            if (nucleusRef_[i] > ref2) { nucleusRef_[i] -= 1; }
-          }
-        }
-      }
-    }
-
+    void copy(const unsigned int i1, const unsigned int i2) ;
     /**
      *   Function to copy particle at location i2 in stack to i1
      */
-    void swap(const unsigned int i1, const unsigned int i2) {
-      // index range check
-      if (i1 >= getSize() || i2 >= getSize()) {
-        std::ostringstream err;
-        err << "NuclearStackExtension: trying to access data beyond size of stack!";
-        throw std::runtime_error(err.str());
-      }
-      // swap original particle data
-      super_type::swap(i1, i2);
-      // swap corresponding nuclear reference data
-      std::swap(nucleusRef_[i2], nucleusRef_[i1]);
-    }
+    void swap(const unsigned int i1, const unsigned int i2) ;
 
-    void incrementSize() {
-      super_type::incrementSize();
-      nucleusRef_.push_back(-1);
-    }
+    void incrementSize() ;
 
-    void decrementSize() {
-      super_type::decrementSize();
-      if (nucleusRef_.size() > 0) {
-        const int ref = nucleusRef_.back();
-        nucleusRef_.pop_back();
-        if (ref >= 0) {
-          nuclearA_.erase(nuclearA_.begin() + ref);
-          nuclearZ_.erase(nuclearZ_.begin() + ref);
-          const int n = nucleusRef_.size();
-          for (int i = 0; i < n; ++i) {
-            if (nucleusRef_[i] >= ref) { nucleusRef_[i] -= 1; }
-          }
-        }
-      }
-    }
+    void decrementSize() ;
 
   private:
     /// the actual memory to store particle data
@@ -361,3 +212,5 @@ namespace corsika::nuclear_stack {
                                                   ExtendedParticleInterfaceType>;
 
 } // namespace corsika::nuclear_stack
+
+#include <corsika/detail/stack/NuclearStackExtension.inl>