diff --git a/corsika/stack/NuclearStackExtension.hpp b/corsika/stack/NuclearStackExtension.hpp
index c4f8274233ea273caf75e2410a74148534fea267..46809532afa29069d4c0d97f545ed59dddb7bbe1 100644
--- a/corsika/stack/NuclearStackExtension.hpp
+++ b/corsika/stack/NuclearStackExtension.hpp
@@ -15,350 +15,378 @@
 #include <corsika/framework/geometry/Vector.hpp>
 #include <corsika/stack/SuperStupidStack.hpp>
 
-#include <corsika/logging/Logging.h>
-
 #include <algorithm>
 #include <tuple>
 #include <vector>
 
 namespace corsika {
 
-  /**
-   * @namespace nuclear_extension
-   *
-   * Add A and Z data to existing stack of particle properties.
-   *
-   * Only for Code::Nucleus particles A and Z are stored, not for all
-   * normal elementary particles.
-   *
-   * Thus in your code, make sure to always check <code>
-   * particle.GetPID()==Code::Nucleus </code> before attempting to
-   * read any nuclear information.
-   *
-   *
-   */
-
-  typedef corsika::Vector<hepmomentum_d> MomentumVector;
-
-  namespace nuclear_extension {
-
-    /**
-     * @class NuclearParticleInterface
-     *
-     * Define ParticleInterface for NuclearStackExtension Stack derived from
-     * ParticleInterface of Inner stack class
-     */
-    template <template <typename> typename InnerParticleInterface,
-              typename StackIteratorInterface>
-    class NuclearParticleInterface
-        : public InnerParticleInterface<StackIteratorInterface> {
-
-    protected:
-      using InnerParticleInterface<StackIteratorInterface>::GetStackData;
-      using InnerParticleInterface<StackIteratorInterface>::GetIndex;
-
-    public:
-      void SetParticleData(
-          const std::tuple<corsika::Code, HEPEnergyType, corsika::MomentumVector,
-                           corsika::Point, TimeType>& v) {
-        if (std::get<0>(v) == corsika::Code::Nucleus) {
-          std::ostringstream err;
-          err << "NuclearStackExtension: no A and Z specified for new Nucleus!";
-          throw std::runtime_error(err.str());
-        }
-        InnerParticleInterface<StackIteratorInterface>::SetParticleData(v);
-        SetNucleusRef(-1); // this is not a nucleus
-      }
-
-      void SetParticleData(
-          const std::tuple<corsika::Code, HEPEnergyType, corsika::MomentumVector,
-                           corsika::Point, TimeType, unsigned short, unsigned short>& v) {
-        const unsigned short A = std::get<5>(v);
-        const unsigned short Z = std::get<6>(v);
-        if (std::get<0>(v) != corsika::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(GetStackData().GetNucleusNextRef()); // store this nucleus data ref
-        SetNuclearA(A);
-        SetNuclearZ(Z);
-        InnerParticleInterface<StackIteratorInterface>::SetParticleData(
-            std::tuple<corsika::Code, HEPEnergyType, corsika::MomentumVector,
-                       corsika::Point, TimeType>{std::get<0>(v), std::get<1>(v),
-                                                 std::get<2>(v), std::get<3>(v),
-                                                 std::get<4>(v)});
-      }
-
-      void SetParticleData(
-          InnerParticleInterface<StackIteratorInterface>& p,
-          const std::tuple<corsika::Code, HEPEnergyType, corsika::MomentumVector,
-                           corsika::Point, TimeType>& v) {
-        if (std::get<0>(v) == corsika::Code::Nucleus) {
-          std::ostringstream err;
-          err << "NuclearStackExtension: no A and Z specified for new Nucleus!";
-          throw std::runtime_error(err.str());
-        }
-        InnerParticleInterface<StackIteratorInterface>::SetParticleData(
-            p, std::tuple<corsika::Code, HEPEnergyType, corsika::MomentumVector,
-                          corsika::Point, TimeType>{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(
-          InnerParticleInterface<StackIteratorInterface>& p,
-          const std::tuple<corsika::Code, HEPEnergyType, corsika::MomentumVector,
-                           corsika::Point, TimeType, unsigned short, unsigned short>& v) {
-        const unsigned short A = std::get<5>(v);
-        const unsigned short Z = std::get<6>(v);
-        if (std::get<0>(v) != corsika::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(GetStackData().GetNucleusNextRef()); // store this nucleus data ref
-        SetNuclearA(A);
-        SetNuclearZ(Z);
-        InnerParticleInterface<StackIteratorInterface>::SetParticleData(
-            p, std::tuple<corsika::Code, HEPEnergyType, corsika::MomentumVector,
-                          corsika::Point, TimeType>{std::get<0>(v), std::get<1>(v),
-                                                    std::get<2>(v), std::get<3>(v),
-                                                    std::get<4>(v)});
-      }
-
-      /**
-       * @name individual setters
-       * @{
-       */
-      void SetNuclearA(const unsigned short vA) {
-        GetStackData().SetNuclearA(GetIndex(), vA);
-      }
-      void SetNuclearZ(const unsigned short vZ) {
-        GetStackData().SetNuclearZ(GetIndex(), vZ);
-      }
-      /// @}
-
-      /**
-       * @name individual getters
-       * @{
-       */
-      int GetNuclearA() const { return GetStackData().GetNuclearA(GetIndex()); }
-      int GetNuclearZ() const { return GetStackData().GetNuclearZ(GetIndex()); }
-      /// @}
-
-      /**
-       * Overwrite normal GetParticleMass function with nuclear version
-       */
-      HEPMassType GetMass() const {
-        if (InnerParticleInterface<StackIteratorInterface>::GetPID() ==
-            corsika::Code::Nucleus)
-          return corsika::nucleus_mass(GetNuclearA(), GetNuclearZ());
-        return InnerParticleInterface<StackIteratorInterface>::GetMass();
-      }
-      /**
-       * Overwirte normal GetChargeNumber function with nuclear version
-       **/
-      int16_t GetChargeNumber() const {
-        if (InnerParticleInterface<StackIteratorInterface>::GetPID() ==
-            corsika::Code::Nucleus)
-          return GetNuclearZ();
-        return InnerParticleInterface<StackIteratorInterface>::GetChargeNumber();
-      }
-
-      int GetNucleusRef() const { return GetStackData().GetNucleusRef(GetIndex()); }
-
-    protected:
-      void SetNucleusRef(const int vR) { GetStackData().SetNucleusRef(GetIndex(), vR); }
-    };
-
-    /**
-     * @class NuclearStackExtension
-     *
-     * Memory implementation of adding nuclear inforamtion to the
-     * existing particle stack defined in class InnerStackImpl.
-     *
-     * Inside the NuclearStackExtension class there is a dedicated
-     * fNucleusRef index, where fNucleusRef[i] is referring to the
-     * correct A and Z for a specific particle index i. fNucleusRef[i]
-     * == -1 means that this is not a nucleus, and a subsequent call to
-     * GetNucleusA would produce an exception.
-     */
-    template <typename InnerStackImpl>
-    class NuclearStackExtensionImpl : public InnerStackImpl {
-
-    public:
-      void Init() { InnerStackImpl::Init(); }
-      void Dump() { InnerStackImpl::Dump(); }
-
-      void Clear() {
-        InnerStackImpl::Clear();
-        fNucleusRef.clear();
-        fNuclearA.clear();
-        fNuclearZ.clear();
-      }
-
-      unsigned int GetSize() const { return fNucleusRef.size(); }
-      unsigned int GetCapacity() const { return fNucleusRef.capacity(); }
-
-      void SetNuclearA(const unsigned int i, const unsigned short vA) {
-        fNuclearA[GetNucleusRef(i)] = vA;
-      }
-      void SetNuclearZ(const unsigned int i, const unsigned short vZ) {
-        fNuclearZ[GetNucleusRef(i)] = vZ;
-      }
-      void SetNucleusRef(const unsigned int i, const int v) { fNucleusRef[i] = v; }
-
-      int GetNuclearA(const unsigned int i) const { return fNuclearA[GetNucleusRef(i)]; }
-      int GetNuclearZ(const unsigned int i) const { return fNuclearZ[GetNucleusRef(i)]; }
-      // this function will create new storage for Nuclear Properties, and return the
-      // reference to it
-      int GetNucleusNextRef() {
-        fNuclearA.push_back(0);
-        fNuclearZ.push_back(0);
-        return fNuclearA.size() - 1;
-      }
-
-      int GetNucleusRef(const unsigned int i) const {
-        if (fNucleusRef[i] >= 0) return fNucleusRef[i];
-        std::ostringstream err;
-        err << "NuclearStackExtension: no nucleus at ref=" << i;
-        throw std::runtime_error(err.str());
-      }
-
-      /**
-       *   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]
-        InnerStackImpl::Copy(i1, i2);
-        // check if any of p[i1] or p[i2] was a Code::Nucleus
-        const int ref1 = fNucleusRef[i1];
-        const int ref2 = fNucleusRef[i2];
-        if (ref2 < 0) {
-          if (ref1 >= 0) {
-            // i1 is nucleus, i2 is not
-            fNucleusRef[i2] = GetNucleusNextRef();
-            fNuclearA[fNucleusRef[i2]] = fNuclearA[ref1];
-            fNuclearZ[fNucleusRef[i2]] = fNuclearZ[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
-            fNuclearA[ref2] = fNuclearA[ref1];
-            fNuclearZ[ref2] = fNuclearZ[ref1];
-          } else {
-            // i2 is overwritten with non-nucleus i1
-            fNucleusRef[i2] = -1;                       // flag as non-nucleus
-            fNuclearA.erase(fNuclearA.cbegin() + ref2); // remove data for i2
-            fNuclearZ.erase(fNuclearZ.cbegin() + ref2); // remove data for i2
-            const int n = fNucleusRef.size(); // update fNucleusRef: indices above ref2
-                                              // must be decremented by 1
-            for (int i = 0; i < n; ++i) {
-              if (fNucleusRef[i] > ref2) { fNucleusRef[i] -= 1; }
-            }
-          }
-        }
-      }
-
-      /**
-       *   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
-        InnerStackImpl::Swap(i1, i2);
-        // swap corresponding nuclear reference data
-        std::swap(fNucleusRef[i2], fNucleusRef[i1]);
-      }
-
-      void IncrementSize() {
-        InnerStackImpl::IncrementSize();
-        fNucleusRef.push_back(-1);
-      }
-
-      void DecrementSize() {
-        InnerStackImpl::DecrementSize();
-        if (fNucleusRef.size() > 0) {
-          const int ref = fNucleusRef.back();
-          fNucleusRef.pop_back();
-          if (ref >= 0) {
-            fNuclearA.erase(fNuclearA.begin() + ref);
-            fNuclearZ.erase(fNuclearZ.begin() + ref);
-            const int n = fNucleusRef.size();
-            for (int i = 0; i < n; ++i) {
-              if (fNucleusRef[i] >= ref) { fNucleusRef[i] -= 1; }
-            }
-          }
-        }
-      }
-
-    private:
-      /// the actual memory to store particle data
-
-      std::vector<int> fNucleusRef;
-      std::vector<unsigned short> fNuclearA;
-      std::vector<unsigned short> fNuclearZ;
-
-    }; // end class NuclearStackExtensionImpl
-
-    //    template<typename StackIteratorInterface>
-    // using NuclearParticleInterfaceType<StackIteratorInterface> =
-    // NuclearParticleInterface< ,StackIteratorInterface>
-
-    // works, but requires stupd _PI class
-    // template<typename SS> using TEST =
-    // NuclearParticleInterface<corsika::super_stupid::SuperStupidStack::PIType,
-    // SS>;
-    template <typename InnerStack, template <typename> typename _PI>
-    using NuclearStackExtension =
-        Stack<NuclearStackExtensionImpl<typename InnerStack::StackImpl>, _PI>;
-
-    // ----
-
-    // I'm dont't manage to do this properly.......
-    /*
-    template<typename TT, typename SS> using TESTi = typename
-    NuclearParticleInterface<TT::template PIType, SS>::ExtendedParticleInterface;
-    template<typename TT, typename SS> using TEST1 = TESTi<TT, SS>;
-    template<typename SS> using TEST2 = TEST1<typename
-    corsika::super_stupid::SuperStupidStack, SS>;
-
-    using NuclearStackExtension = Stack<NuclearStackExtensionImpl<typename
-    InnerStack::StackImpl>, TEST2>;
-    */
-    /*
-      // .... this should be fixed ....
-
-    template <typename InnerStack, typename SS=StackIteratorInterface>
-      //using NuclearStackExtension = Stack<NuclearStackExtensionImpl<typename
-    InnerStack::StackImpl>, NuclearParticleInterface<typename InnerStack::template PIType,
-    StackIteratorInterface>::ExtendedParticleInterface>; using NuclearStackExtension =
-    Stack<NuclearStackExtensionImpl<typename InnerStack::StackImpl>, TEST1<typename
-    corsika::super_stupid::SuperStupidStack, SS> >;
-
-    //template <typename InnerStack>
-      //  using NuclearStackExtension = Stack<NuclearStackExtensionImpl<typename
-    InnerStack::StackImpl>, TEST<typename
-    corsika::super_stupid::SuperStupidStack::PIType>>;
-    //using NuclearStackExtension = Stack<NuclearStackExtensionImpl<typename
-    InnerStack::StackImpl>, TEST>;
-    */
-
-  } // namespace nuclear_extension
+/**
+ * @namespace nuclear_extension
+ *
+ * Add A and Z data to existing stack (currently SuperStupidStack) of particle
+ * properties. This is done via inheritance, not via CombinedStack since the nuclear
+ * data is stored ONLY when needed (for nuclei) and not for all particles. Thus, this is
+ * a new, derived Stack object.
+ *
+ * Only for Code::Nucleus particles A and Z are stored, not for all
+ * normal elementary particles.
+ *
+ * Thus in your code, make sure to always check <code>
+ * particle.GetPID()==Code::Nucleus </code> before attempting to
+ * read any nuclear information.
+ *
+ *
+ */
+
+
+/**
+ * @class NuclearParticleInterface
+ *
+ * Define ParticleInterface for NuclearStackExtension Stack derived from
+ * ParticleInterface of Inner stack class
+ */
+template < template <typename> class InnerParticleInterface, typename StackIteratorInterface>
+struct NuclearParticleInterface : public  InnerParticleInterface<StackIteratorInterface>  {
+
+	typedef  InnerParticleInterface<StackIteratorInterface>  super_type;
+
+
+
+public:
+
+
+	typedef std::tuple<
+			corsika::Code, corsika::units::si::HEPEnergyType,
+			momentum_type, corsika::Point,
+		    corsika::units::si::TimeType> particle_data_type;
+
+	typedef std::tuple<
+			corsika::Code, corsika::units::si::HEPEnergyType,
+			momentum_type, corsika::Point,
+			corsika::units::si::TimeType,
+			unsigned short, unsigned short> altenative_particle_data_type;
+
+
+	typedef corsika::Vector<corsika::units::si::hepmomentum_d> momentum_type;
+
+	void setParticleData(particle_data_type const& v) {
+
+		if (std::get<0>(v) == corsika::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
+	}
+
+	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) != corsika::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) == corsika::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);
+
+		if (std::get<0>(v) != corsika::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)});
+	}
+
+	std::string as_string() const {
+		return fmt::format(
+				"{}, nuc({})", super_type::as_string(),
+				(isNucleus() ? fmt::format("A={}, Z={}", getNuclearA(), getNuclearZ())
+						: "n/a"));
+	}
+
+	/**
+	 * @name individual setters
+	 * @{
+	 */
+	void setNuclearA(const unsigned short vA) {
+		super_type::GetStackData().setNuclearA(super_type::GetIndex(), vA);
+	}
+	void setNuclearZ(const unsigned short vZ) {
+		super_type::GetStackData().setNuclearZ(super_type::GetIndex(), vZ);
+	}
+	/// @}
+
+	/**
+	 * @name individual getters
+	 * @{
+	 */
+	int getNuclearA() const { return super_type::GetStackData().getNuclearA(super_type::GetIndex()); }
+	int getNuclearZ() const { return super_type::GetStackData().getNuclearZ(super_type::GetIndex()); }
+	/// @}
+
+	/**
+	 * Overwrite normal GetParticleMass function with nuclear version
+	 */
+	corsika::units::si::HEPMassType getMass() const {
+		if (super_type::GetPID() ==
+				corsika::Code::Nucleus)
+			return corsika::GetNucleusMass(getNuclearA(), getNuclearZ());
+		return super_type::getMass();
+	}
+	/**
+	 * Overwirte normal GetChargeNumber function with nuclear version
+	 **/
+	int16_t getChargeNumber() const {
+		if (super_type::GetPID() ==
+				corsika::Code::Nucleus)
+			return getNuclearZ();
+		return super_type::getChargeNumber();
+	}
+
+	int getNucleusRef() const {
+		return super_type::GetStackData().getNucleusRef(GetIndex());
+	} // LCOV_EXCL_LINE
+
+protected:
+
+	void setNucleusRef(const int vR) {
+		super_type::GetStackData().setNucleusRef(super_type::GetIndex(), vR);
+	}
+
+	bool isNucleus() const {
+		return super_type::GetStackData().isNucleus(super_type::GetIndex());
+	}
+};
+
+/**
+ * @class NuclearStackExtension
+ *
+ * Memory implementation of adding nuclear inforamtion to the
+ * existing particle stack defined in class InnerStackImpl.
+ *
+ * Inside the NuclearStackExtension class there is a dedicated
+ * fNucleusRef index, where fNucleusRef[i] is referring to the
+ * correct A and Z for a specific particle index i. fNucleusRef[i]
+ * == -1 means that this is not a nucleus, and a subsequent call to
+ * GetNucleusA would produce an exception.
+ */
+template <typename InnerStackImpl>
+class NuclearStackExtensionImpl : public InnerStackImpl {
+
+	typedef InnerStackImpl super_type;
+
+public:
+
+	typedef std::vector<int>            nucleus_ref_type;
+	typedef std::vector<unsigned short>   nuclear_a_type;
+	typedef std::vector<unsigned short>   nuclear_z_type;
+
+
+	NuclearStackExtensionImpl()= default;
+
+	NuclearStackExtensionImpl( NuclearStackExtensionImpl<InnerStackImpl> const&)= default;
+
+	NuclearStackExtensionImpl( NuclearStackExtensionImpl<InnerStackImpl> &&)= default;
+
+	NuclearStackExtensionImpl<InnerStackImpl>&
+	operator=( NuclearStackExtensionImpl<InnerStackImpl> const&)= default;
+
+	NuclearStackExtensionImpl<InnerStackImpl>&
+	operator=( NuclearStackExtensionImpl<InnerStackImpl> &&)= default;
+
+	void init() {
+		super_type::init();
+	}
+
+	void dump() {
+		super_type::dump();
+	}
+
+	void clear() {
+		super_type::clear();
+		nucleusRef_.clear();
+		nuclearA_.clear();
+		nuclearZ_.clear();
+	}
+
+	unsigned int getSize() const {
+		return nucleusRef_.size();
+	}
+
+	unsigned int getCapacity() const {
+		return nucleusRef_.capacity();
+	}
+
+	void setNuclearA(const unsigned int i, const unsigned short vA) {
+		nuclearA_[getNucleusRef(i)] = vA;
+	}
+
+	void setNuclearZ(const unsigned int i, const unsigned short vZ) {
+		nuclearZ_[getNucleusRef(i)] = vZ;
+	}
+
+	void setNucleusRef(const unsigned int i, const int v) {
+		nucleusRef_[i] = v;
+	}
+
+	int getNuclearA(const unsigned int i) const {
+		return nuclearA_[getNucleusRef(i)];
+	}
+
+	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 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());
+	}
+
+	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; }
+				}
+			}
+		}
+	}
+
+	/**
+	 *   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 incrementSize() {
+		super_type::incrementSize();
+		nucleusRef_.push_back(-1);
+	}
+
+	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; }
+				}
+			}
+		}
+	}
+
+private:
+	/// the actual memory to store particle data
+
+	nucleus_ref_type nucleusRef_;
+	nuclear_a_type     nuclearA_;
+	nuclear_z_type     nuclearZ_;
+
+}; // end class NuclearStackExtensionImpl
+
+template <typename InnerStack, template <typename> typename _PI>
+using NuclearStackExtension =
+		Stack<NuclearStackExtensionImpl<typename InnerStack::StackImpl>, _PI> ;
+
+//
+template <typename StackIter>
+using ExtendedParticleInterfaceType = NuclearParticleInterface<SuperStupidStack, StackIter>;
+
+// the particle data stack with extra nuclear information:
+using ParticleDataStack = NuclearStackExtension<SuperStupidStack, ExtendedParticleInterfaceType>;
+
+
 } // namespace corsika
diff --git a/corsika/stack/SuperStupidStack.hpp b/corsika/stack/SuperStupidStack.hpp
index d83dda4f3d8cbf3e2026d5d603c144e1d4eebe95..f35b41da77949341b9ee3bf9117f9fe45e819f9c 100644
--- a/corsika/stack/SuperStupidStack.hpp
+++ b/corsika/stack/SuperStupidStack.hpp
@@ -22,193 +22,259 @@
 
 namespace corsika {
 
-  typedef corsika::Vector<hepmomentum_d> MomentumVector;
-
-  namespace super_stupid {
-
-    /**
-     * Example of a particle object on the stack.
-     */
-
-    template <typename StackIteratorInterface>
-    class ParticleInterface : public ParticleBase<StackIteratorInterface> {
-
-    protected:
-      using corsika::ParticleBase<StackIteratorInterface>::GetStack;
-      using corsika::ParticleBase<StackIteratorInterface>::GetStackData;
-
-    public:
-      using corsika::ParticleBase<StackIteratorInterface>::GetIndex;
-
-    public:
-      void SetParticleData(const std::tuple<corsika::Code, HEPEnergyType, MomentumVector,
-                                            corsika::Point, TimeType>& v) {
-        SetPID(std::get<0>(v));
-        SetEnergy(std::get<1>(v));
-        SetMomentum(std::get<2>(v));
-        SetPosition(std::get<3>(v));
-        SetTime(std::get<4>(v));
-      }
-      /*
-    void SetParticleData(const corsika::Code vDataPID,
-                         const HEPEnergyType vDataE,
-                         const MomentumVector& vMomentum,
-                         const corsika::Point& vPosition,
-                         const TimeType vTime) {
-      }*/
-
-      void SetParticleData(ParticleInterface<StackIteratorInterface>&,
-                           const std::tuple<corsika::Code, HEPEnergyType, MomentumVector,
-                                            corsika::Point, TimeType>& v) {
-        SetPID(std::get<0>(v));
-        SetEnergy(std::get<1>(v));
-        SetMomentum(std::get<2>(v));
-        SetPosition(std::get<3>(v));
-        SetTime(std::get<4>(v));
-      }
-      /*      void SetParticleData(ParticleInterface<StackIteratorInterface>&,
-                           const corsika::Code vDataPID,
-                           const HEPEnergyType vDataE,
-                           const MomentumVector& vMomentum,
-                           const corsika::Point& vPosition,
-                           const TimeType vTime) {
-        SetPID(vDataPID);
-        SetEnergy(vDataE);
-        SetMomentum(vMomentum);
-        SetPosition(vPosition);
-        SetTime(vTime);
-      }*/
-
-      /// individual setters
-      void SetPID(const corsika::Code id) { GetStackData().SetPID(GetIndex(), id); }
-      void SetEnergy(const HEPEnergyType& e) { GetStackData().SetEnergy(GetIndex(), e); }
-      void SetMomentum(const MomentumVector& v) {
-        GetStackData().SetMomentum(GetIndex(), v);
-      }
-      void SetPosition(const corsika::Point& v) {
-        GetStackData().SetPosition(GetIndex(), v);
-      }
-      void SetTime(const TimeType& v) { GetStackData().SetTime(GetIndex(), v); }
-
-      /// individual getters
-      corsika::Code GetPID() const { return GetStackData().GetPID(GetIndex()); }
-      HEPEnergyType GetEnergy() const { return GetStackData().GetEnergy(GetIndex()); }
-      MomentumVector GetMomentum() const {
-        return GetStackData().GetMomentum(GetIndex());
-      }
-      corsika::Point GetPosition() const {
-        return GetStackData().GetPosition(GetIndex());
-      }
-      TimeType GetTime() const { return GetStackData().GetTime(GetIndex()); }
-      /**
-       * @name derived quantities
-       *
-       * @{
-       */
-      corsika::Vector<dimensionless_d> GetDirection() const {
-        return GetMomentum() / GetEnergy();
-      }
-      HEPMassType GetMass() const { return corsika::mass(GetPID()); }
-      int16_t GetChargeNumber() const { return corsika::charge_number(GetPID()); }
-      ///@}
-    };
-
-    /**
-     * Memory implementation of the most simple (stupid) particle stack object.
-     */
-
-    class SuperStupidStackImpl {
-
-    public:
-      void Init() {}
-      void Dump() const {}
-
-      void Clear() {
-        fDataPID.clear();
-        fDataE.clear();
-        fMomentum.clear();
-        fPosition.clear();
-        fTime.clear();
-      }
-
-      unsigned int GetSize() const { return fDataPID.size(); }
-      unsigned int GetCapacity() const { return fDataPID.size(); }
-
-      void SetPID(const unsigned int i, const corsika::Code id) { fDataPID[i] = id; }
-      void SetEnergy(const unsigned int i, const HEPEnergyType e) { fDataE[i] = e; }
-      void SetMomentum(const unsigned int i, const MomentumVector& v) {
-        fMomentum[i] = v;
-      }
-      void SetPosition(const unsigned int i, const corsika::Point& v) {
-        fPosition[i] = v;
-      }
-      void SetTime(const unsigned int i, const TimeType& v) { fTime[i] = v; }
-
-      corsika::Code GetPID(const unsigned int i) const { return fDataPID[i]; }
-      HEPEnergyType GetEnergy(const unsigned int i) const { return fDataE[i]; }
-      MomentumVector GetMomentum(const unsigned int i) const { return fMomentum[i]; }
-      corsika::Point GetPosition(const unsigned int i) const { return fPosition[i]; }
-      TimeType GetTime(const unsigned int i) const { return fTime[i]; }
-
-      /**
-       *   Function to copy particle at location i2 in stack to i1
-       */
-      void Copy(const unsigned int i1, const unsigned int i2) {
-        fDataPID[i2] = fDataPID[i1];
-        fDataE[i2] = fDataE[i1];
-        fMomentum[i2] = fMomentum[i1];
-        fPosition[i2] = fPosition[i1];
-        fTime[i2] = fTime[i1];
-      }
-
-      /**
-       *   Function to copy particle at location i2 in stack to i1
-       */
-      void Swap(const unsigned int i1, const unsigned int i2) {
-        std::swap(fDataPID[i2], fDataPID[i1]);
-        std::swap(fDataE[i2], fDataE[i1]);
-        std::swap(fMomentum[i2], fMomentum[i1]);
-        std::swap(fPosition[i2], fPosition[i1]);
-        std::swap(fTime[i2], fTime[i1]);
-      }
-
-      void IncrementSize() {
-        using corsika::Code;
-        using corsika::Point;
-        fDataPID.push_back(Code::Unknown);
-        fDataE.push_back(0 * electronvolt);
-        CoordinateSystem& dummyCS =
-            RootCoordinateSystem::getInstance().GetRootCoordinateSystem();
-        fMomentum.push_back(MomentumVector(
-            dummyCS, {0 * electronvolt, 0 * electronvolt, 0 * electronvolt}));
-        fPosition.push_back(Point(dummyCS, {0 * meter, 0 * meter, 0 * meter}));
-        fTime.push_back(0 * second);
-      }
-
-      void DecrementSize() {
-        if (fDataE.size() > 0) {
-          fDataPID.pop_back();
-          fDataE.pop_back();
-          fMomentum.pop_back();
-          fPosition.pop_back();
-          fTime.pop_back();
-        }
-      }
-
-    private:
-      /// the actual memory to store particle data
-
-      std::vector<corsika::Code> fDataPID;
-      std::vector<HEPEnergyType> fDataE;
-      std::vector<MomentumVector> fMomentum;
-      std::vector<corsika::Point> fPosition;
-      std::vector<TimeType> fTime;
-
-    }; // end class SuperStupidStackImpl
-
-    typedef Stack<SuperStupidStackImpl, ParticleInterface> SuperStupidStack;
-
-  } // namespace super_stupid
+
+/**
+ * Example of a particle object on the stack.
+ */
+
+template <typename StackIteratorInterface>
+struct ParticleInterface : public ParticleBase<StackIteratorInterface> {
+
+private:
+
+	typedef corsika::ParticleBase<StackIteratorInterface> super_type;
+
+public:
+
+	typedef corsika::Vector<corsika::units::si::hepmomentum_d> momentum_vector_type;
+
+	std::string as_string() const {
+		using namespace corsika::units::si;
+		return fmt::format("particle: i={}, PID={}, E={}GeV", super_type::GetIndex(),
+				particles::GetName(this->getPID()), this->getEnergy() / 1_GeV);
+	}
+
+	void setParticleData( std::tuple<corsika::Code, corsika::units::si::HEPEnergyType,
+			momentum_vector_type, corsika::Point, corsika::units::si::TimeType> const& v) {
+		this->setPID(std::get<0>(v));
+		this->setEnergy(std::get<1>(v));
+		this->setMomentum(std::get<2>(v));
+		this->setPosition(std::get<3>(v));
+		this->setTime(std::get<4>(v));
+	}
+
+	void setParticleData( ParticleInterface<StackIteratorInterface> const&,
+			std::tuple<corsika::Code, corsika::units::si::HEPEnergyType,
+			momentum_vector_type, corsika::Point, corsika::units::si::TimeType> const& v) {
+		this->setPID(std::get<0>(v));
+		this->setEnergy(std::get<1>(v));
+		this->setMomentum(std::get<2>(v));
+		this->setPosition(std::get<3>(v));
+		this->setTime(std::get<4>(v));
+	}
+
+	/// individual setters
+	void setPID(const corsika::Code id) {
+		super_type::GetStackData().setPID(super_type::GetIndex(), id);
+	}
+	void setEnergy(const corsika::units::si::HEPEnergyType& e) {
+		super_type::GetStackData().setEnergy(super_type::GetIndex(), e);
+	}
+	void setMomentum(const momentum_vector_type& v) {
+		super_type::GetStackData().setMomentum(super_type::GetIndex(), v);
+	}
+	void setPosition(const corsika::Point& v) {
+		super_type::GetStackData().setPosition(super_type::GetIndex(), v);
+	}
+	void setTime(const corsika::units::si::TimeType& v) {
+		super_type::GetStackData().setTime(super_type::GetIndex(), v);
+	}
+
+	/// individual getters
+	corsika::Code getPID() const {
+		return super_type::GetStackData().getPID(super_type::GetIndex());
+	}
+	corsika::units::si::HEPEnergyType getEnergy() const {
+		return super_type::GetStackData().getEnergy(super_type::GetIndex());
+	}
+	momentum_vector_type getMomentum() const {
+		return super_type::GetStackData().getMomentum(super_type::GetIndex());
+	}
+	corsika::Point getPosition() const {
+		return super_type::GetStackData().getPosition(super_type::GetIndex());
+	}
+	corsika::units::si::TimeType getTime() const {
+		return super_type::GetStackData().getTime(super_type::GetIndex());
+	}
+	/**
+	 * @name derived quantities
+	 *
+	 * @{
+	 */
+	corsika::Vector<corsika::units::si::dimensionless_d> getDirection() const {
+		return  this->getMomentum() /  this->getEnergy();
+	}
+
+	corsika::units::si::HEPMassType getMass() const {
+		return corsika::GetMass(this->getPID());
+	}
+
+	int16_t getChargeNumber() const {
+		return corsika::GetChargeNumber(this->getPID());
+	}
+	///@}
+};
+
+/**
+ * Memory implementation of the most simple (stupid) particle stack object.
+ *
+ */
+
+class SuperStupidStackImpl {
+
+public:
+
+	typedef  corsika::Vector<corsika::units::si::hepmomentum_d>        momentum_type;
+	typedef  std::vector<corsika::Code>                             code_vector_type;
+	typedef  std::vector<corsika::units::si::HEPEnergyType>       energy_vector_type;
+	typedef  std::vector<corsika::Point>                           point_vector_type;
+	typedef  std::vector<corsika::units::si::TimeType>              time_vector_type;
+	typedef  std::vector<momentum_type>                         momentum_vector_type;
+
+	SuperStupidStackImpl()=default;
+
+	SuperStupidStackImpl( SuperStupidStackImpl const& other)=default;
+
+	SuperStupidStackImpl( SuperStupidStackImpl && other)=default;
+
+
+	SuperStupidStackImpl& operator=( SuperStupidStackImpl const& other)=default;
+
+	SuperStupidStackImpl& operator=( SuperStupidStackImpl && other)=default;
+
+
+	void init() {}
+	void dump() const {}
+
+	void clear() {
+		dataPID_.clear();
+		dataE_.clear();
+		momentum_.clear();
+		position_.clear();
+		time_.clear();
+	}
+
+	unsigned int getSize() const { return dataPID_.size(); }
+	unsigned int getCapacity() const { return dataPID_.size(); }
+
+	void setPID(size_t i, const corsika::Code id) {
+		dataPID_[i] = id;
+	}
+	void setEnergy(size_t i,  corsika::units::si::HEPEnergyType  const& e) {
+		dataE_[i] = e;
+	}
+	void setMomentum(size_t i, momentum_type const& v) {
+		momentum_[i] = v;
+	}
+	void setPosition(size_t i, corsika::Point const& v) {
+		position_[i] = v;
+	}
+	void setTime(size_t i, corsika::units::si::TimeType const& v) {
+		time_[i] = v;
+	}
+
+	corsika::Code getPID(size_t i) const {
+		return dataPID_[i];
+	}
+
+	corsika::units::si::HEPEnergyType getEnergy(size_t i) const {
+		return dataE_[i];
+	}
+
+	momentum_type getMomentum(size_t i) const {
+		return momentum_[i];
+	}
+
+	corsika::Point getPosition(size_t i) const {
+		return position_[i];
+	}
+	corsika::units::si::TimeType getTime(size_t i) const {
+		return time_[i];
+	}
+
+	corsika::units::si::HEPEnergyType getDataE(size_t i) const {
+		return dataE_[i];
+	}
+
+	void setDataE(size_t i, corsika::units::si::HEPEnergyType const& dataE) {
+		dataE_[i] = dataE;
+	}
+
+	corsika::Code getDataPid(size_t i) const {
+		return dataPID_;
+	}
+
+	void setDataPid(size_t i, corsika::Code  const& dataPid) {
+		dataPID_[i] = dataPid;
+	}
+	/**
+	 *   Function to copy particle at location i2 in stack to i1
+	 */
+	 void copy(size_t i1, size_t i2) {
+		dataPID_[i2]  = dataPID_[i1];
+		dataE_[i2]    = dataE_[i1];
+		momentum_[i2] = momentum_[i1];
+		position_[i2] = position_[i1];
+		time_[i2]     = time_[i1];
+	 }
+
+
+	 /**
+	  *   FIXME: change to iterators.
+	  *   Function to copy particle at location i2 in stack to i1
+	  */
+	 void swap(size_t i1, size_t i2) {
+		 std::swap(dataPID_[i2] , dataPID_[i1]);
+		 std::swap(dataE_[i2]   , dataE_[i1]);
+		 std::swap(momentum_[i2], momentum_[i1]);
+		 std::swap(position_[i2], position_[i1]);
+		 std::swap(time_[i2]    , time_[i1]);
+	 }
+
+	 void incrementSize() {
+		 using corsika::Point;
+		 using corsika::Code;
+
+		 dataPID_.push_back(Code::Unknown);
+		 dataE_.push_back(0 * corsika::units::si::electronvolt);
+
+		 CoordinateSystem& dummyCS = RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
+
+		 momentum_.push_back(momentum_type( dummyCS,
+				 {0 * corsika::units::si::electronvolt, 0 * corsika::units::si::electronvolt,
+						 0 * corsika::units::si::electronvolt}));
+
+		 position_.push_back(
+				 Point(dummyCS, {0 * corsika::units::si::meter, 0 * corsika::units::si::meter,
+						 0 * corsika::units::si::meter}));
+		 time_.push_back(0 * corsika::units::si::second);
+	 }
+
+	 void decrementSize() {
+		 if (dataE_.size() > 0) {
+			 dataPID_.pop_back();
+			 dataE_.pop_back();
+			 momentum_.pop_back();
+			 position_.pop_back();
+			 time_.pop_back();
+		 }
+	 }
+
+
+private:
+
+	 /// the actual memory to store particle data
+	 code_vector_type dataPID_;
+	 energy_vector_type dataE_;
+	 std::vector<momentum_type> momentum_;
+	 std::vector<corsika::Point> position_;
+	 std::vector<corsika::units::si::TimeType> time_;
+
+}; // end class SuperStupidStackImpl
+
+
+typedef Stack<SuperStupidStackImpl, ParticleInterface> SuperStupidStack;
+
 
 } // namespace corsika