diff --git a/Environment/BaseExponential.h b/Environment/BaseExponential.h
new file mode 100644
index 0000000000000000000000000000000000000000..6d499b366359e2ca606505dc076050bbe5fe9cbc
--- /dev/null
+++ b/Environment/BaseExponential.h
@@ -0,0 +1,83 @@
+/*
+ * (c) Copyright 2019 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 _include_Environment_BaseExponential_h_
+#define _include_Environment_BaseExponential_h_
+
+#include <corsika/geometry/Line.h>
+#include <corsika/geometry/Point.h>
+#include <corsika/geometry/Trajectory.h>
+#include <corsika/particles/ParticleProperties.h>
+#include <corsika/units/PhysicalUnits.h>
+
+#include <cassert>
+#include <limits>
+
+/**
+ *
+ */
+
+namespace corsika::environment {
+
+  template <class TDerived>
+  class BaseExponential {
+  protected:
+    corsika::units::si::MassDensityType const fRho0;
+    units::si::LengthType const fLambda;
+    units::si::InverseLengthType const fInvLambda;
+    geometry::Point const fP0;
+
+    auto const& GetImplementation() const { return *static_cast<TDerived const*>(this); }
+
+    corsika::units::si::GrammageType IntegratedGrammage(
+        corsika::geometry::Trajectory<corsika::geometry::Line> const& line,
+        corsika::units::si::LengthType pTo,
+        geometry::Vector<units::si::dimensionless_d> const& axis) const {
+      auto const vDotA = line.NormalizedDirection().dot(axis).magnitude();
+
+      if (vDotA == 0) {
+        return pTo * GetImplementation().GetMassDensity(line.GetR0());
+      } else {
+        return GetImplementation().GetMassDensity(line.GetR0()) * (fLambda / vDotA) *
+               (exp(vDotA * pTo / fLambda) - 1);
+      }
+    }
+
+    corsika::units::si::LengthType ArclengthFromGrammage(
+        corsika::geometry::Trajectory<corsika::geometry::Line> const& line,
+        corsika::units::si::GrammageType pGrammage,
+        geometry::Vector<units::si::dimensionless_d> const& axis) const {
+      auto const vDotA = line.NormalizedDirection().dot(axis).magnitude();
+
+      if (vDotA == 0) {
+        return pGrammage / GetImplementation().GetMassDensity(line.GetR0());
+      } else {
+        auto const logArg = pGrammage * vDotA / (fRho0 * fLambda) + 1;
+        if (logArg > 0) {
+          return fLambda / vDotA * log(pGrammage * vDotA / (fRho0 * fLambda) + 1);
+        } else {
+          return std::numeric_limits<typename decltype(
+                     pGrammage)::value_type>::infinity() *
+                 corsika::units::si::meter;
+        }
+      }
+    }
+
+  public:
+    BaseExponential(geometry::Point const& p0, units::si::MassDensityType rho,
+                    units::si::LengthType lambda)
+        : fRho0(rho)
+        , fLambda(lambda)
+        , fInvLambda(1 / lambda)
+        , fP0(p0) {}
+  };
+
+} // namespace corsika::environment
+#endif
diff --git a/Environment/CMakeLists.txt b/Environment/CMakeLists.txt
index 0190d6dc8dadd18dc2b1bd6d0345be23545a3e47..13fd4f1e736d70a00b8380fdb6de6d906349b4a5 100644
--- a/Environment/CMakeLists.txt
+++ b/Environment/CMakeLists.txt
@@ -10,6 +10,7 @@ set (
   DensityFunction.h
   Environment.h
   NameModel.h
+  BaseExponential.h
   FlatExponential.h
   SlidingPlanarExponential.h
   )
diff --git a/Environment/FlatExponential.h b/Environment/FlatExponential.h
index 19e42c725c4b5204dc9c6fc166f9be00f44bdb7a..97fba098e4ee0f09698d24c0b90bbb2829df414f 100644
--- a/Environment/FlatExponential.h
+++ b/Environment/FlatExponential.h
@@ -11,12 +11,12 @@
 #ifndef _include_Environment_FlatExponential_h_
 #define _include_Environment_FlatExponential_h_
 
+#include <corsika/environment/BaseExponential.h>
 #include <corsika/environment/NuclearComposition.h>
 #include <corsika/geometry/Line.h>
 #include <corsika/geometry/Point.h>
 #include <corsika/geometry/Trajectory.h>
 #include <corsika/particles/ParticleProperties.h>
-#include <corsika/random/RNGManager.h>
 #include <corsika/units/PhysicalUnits.h>
 
 #include <cassert>
@@ -29,64 +29,39 @@
 namespace corsika::environment {
 
   template <class T>
-  class FlatExponential : public T {
-    corsika::units::si::MassDensityType const fRho0;
-    units::si::LengthType const fLambda;
-    units::si::InverseLengthType const fInvLambda;
-    NuclearComposition const fNuclComp;
+  class FlatExponential : public BaseExponential<FlatExponential<T>>, public T {
     geometry::Vector<units::si::dimensionless_d> const fAxis;
-    geometry::Point const fP0;
+    NuclearComposition const fNuclComp;
+
+    using Base = BaseExponential<FlatExponential<T>>;
 
   public:
     FlatExponential(geometry::Point const& p0,
                     geometry::Vector<units::si::dimensionless_d> const& axis,
                     units::si::MassDensityType rho, units::si::LengthType lambda,
-                    NuclearComposition pNuclComp)
-        : fRho0(rho)
-        , fLambda(lambda)
-        , fInvLambda(1 / lambda)
-        , fNuclComp(pNuclComp)
+                    NuclearComposition nuclComp)
+        : Base(p0, rho, lambda)
         , fAxis(axis)
-        , fP0(p0) {}
+        , fNuclComp(nuclComp) {}
 
     corsika::units::si::MassDensityType GetMassDensity(
         corsika::geometry::Point const& p) const override {
-      return fRho0 * exp(fInvLambda * (p - fP0).dot(fAxis));
+      return Base::fRho0 * exp(Base::fInvLambda * (p - Base::fP0).dot(fAxis));
     }
+
     NuclearComposition const& GetNuclearComposition() const override { return fNuclComp; }
 
     corsika::units::si::GrammageType IntegratedGrammage(
         corsika::geometry::Trajectory<corsika::geometry::Line> const& line,
         corsika::units::si::LengthType pTo) const override {
-      auto const vDotA = line.NormalizedDirection().dot(fAxis).magnitude();
-
-      if (vDotA == 0) {
-        return pTo * GetMassDensity(line.GetR0());
-      } else {
-        return GetMassDensity(line.GetR0()) * (fLambda / vDotA) *
-               (exp(vDotA * pTo / fLambda) - 1);
-      }
+      return Base::IntegratedGrammage(line, pTo, fAxis);
     }
 
     corsika::units::si::LengthType ArclengthFromGrammage(
         corsika::geometry::Trajectory<corsika::geometry::Line> const& line,
         corsika::units::si::GrammageType pGrammage) const override {
-      auto const vDotA = line.NormalizedDirection().dot(fAxis).magnitude();
-
-      if (vDotA == 0) {
-        return pGrammage / GetMassDensity(line.GetR0());
-      } else {
-        auto const logArg = pGrammage * vDotA / (fRho0 * fLambda) + 1;
-        if (logArg > 0) {
-          return fLambda / vDotA * log(pGrammage * vDotA / (fRho0 * fLambda) + 1);
-        } else {
-          return std::numeric_limits<typename decltype(
-                     pGrammage)::value_type>::infinity() *
-                 corsika::units::si::meter;
-        }
-      }
+      return Base::ArclengthFromGrammage(line, pGrammage, fAxis);
     }
   };
-
 } // namespace corsika::environment
 #endif
diff --git a/Environment/SlidingPlanarExponential.h b/Environment/SlidingPlanarExponential.h
index 3dc05917361eaf39d7fbffd1c0b5ecb20475a3ca..c04c084ec5775f4307004a907c065c9761bb57b5 100644
--- a/Environment/SlidingPlanarExponential.h
+++ b/Environment/SlidingPlanarExponential.h
@@ -11,15 +11,16 @@
 #ifndef _include_Environment_SlidingPlanarExponential_h_
 #define _include_Environment_SlidingPlanarExponential_h_
 
+#include <corsika/environment/FlatExponential.h>
 #include <corsika/environment/NuclearComposition.h>
 #include <corsika/geometry/Line.h>
-#include <corsika/environment/FlatExponential.h>
 #include <corsika/geometry/Point.h>
 #include <corsika/geometry/Trajectory.h>
 #include <corsika/particles/ParticleProperties.h>
 #include <corsika/random/RNGManager.h>
 #include <corsika/units/PhysicalUnits.h>
 
+#include <fenv.h>
 #include <cassert>
 #include <limits>
 
@@ -30,30 +31,22 @@
 namespace corsika::environment {
 
   template <class T>
-  class SlidingPlanarExponential : public T {
-    corsika::units::si::MassDensityType const fRho0;
-    units::si::LengthType const fLambda;
-    units::si::InverseLengthType const fInvLambda;
+  class SlidingPlanarExponential : public BaseExponential<SlidingPlanarExponential<T>>,
+                                   public T {
     NuclearComposition const fNuclComp;
-    geometry::Vector<units::si::dimensionless_d> const fAxis;
-    geometry::Point const fP0;
+
+    using Base = BaseExponential<SlidingPlanarExponential<T>>;
 
   public:
-    SlidingPlanarExponential(geometry::Point const& p0,
-                    geometry::Vector<units::si::dimensionless_d> const& axis,
-                    units::si::MassDensityType rho, units::si::LengthType lambda,
-                    NuclearComposition pNuclComp)
-        : fRho0(rho)
-        , fLambda(lambda)
-        , fInvLambda(1 / lambda)
-        , fNuclComp(pNuclComp)
-        , fAxis(axis)
-        , fP0(p0) {}
+    SlidingPlanarExponential(geometry::Point const& p0, units::si::MassDensityType rho,
+                             units::si::LengthType lambda, NuclearComposition nuclComp)
+        : Base(p0, rho, lambda)
+        , fNuclComp(nuclComp) {}
 
     corsika::units::si::MassDensityType GetMassDensity(
         corsika::geometry::Point const& p) const override {
-      auto const height = (p - fP0).norm();
-      return fRho0 * exp(fInvLambda * height);
+      auto const height = (p - Base::fP0).norm();
+      return Base::fRho0 * exp(Base::fInvLambda * height);
     }
 
     NuclearComposition const& GetNuclearComposition() const override { return fNuclComp; }
@@ -61,36 +54,17 @@ namespace corsika::environment {
     corsika::units::si::GrammageType IntegratedGrammage(
         corsika::geometry::Trajectory<corsika::geometry::Line> const& line,
         corsika::units::si::LengthType pTo) const override {
-
-      auto const axis = (fP0 - line.GetR0()).normalized();
-      auto const vDotA = line.NormalizedDirection().dot(fAxis).magnitude();
-
-      if (vDotA == 0) {
-        return pTo * GetMassDensity(line.GetR0());
-      } else {
-        return GetMassDensity(line.GetR0()) * (fLambda / vDotA) *
-               (exp(vDotA * pTo / fLambda) - 1);
-      }
+      feenableexcept(FE_INVALID);
+      auto const axis = (Base::fP0 - line.GetR0()).normalized();
+      return Base::IntegratedGrammage(line, pTo, axis);
     }
 
     corsika::units::si::LengthType ArclengthFromGrammage(
         corsika::geometry::Trajectory<corsika::geometry::Line> const& line,
         corsika::units::si::GrammageType pGrammage) const override {
-      auto const axis = (fP0 - line.GetR0()).normalized();
-      auto const vDotA = line.NormalizedDirection().dot(fAxis).magnitude();
-
-      if (vDotA == 0) {
-        return pGrammage / GetMassDensity(line.GetR0());
-      } else {
-        auto const logArg = pGrammage * vDotA / (fRho0 * fLambda) + 1;
-        if (logArg > 0) {
-          return fLambda / vDotA * log(pGrammage * vDotA / (fRho0 * fLambda) + 1);
-        } else {
-          return std::numeric_limits<typename decltype(
-                     pGrammage)::value_type>::infinity() *
-                 corsika::units::si::meter;
-        }
-      }
+      feenableexcept(FE_INVALID);
+      auto const axis = (Base::fP0 - line.GetR0()).normalized();
+      return Base::ArclengthFromGrammage(line, pGrammage, axis);
     }
   };
 
diff --git a/Environment/testEnvironment.cc b/Environment/testEnvironment.cc
index 206849e7eb17b6d33a23fffddb4abefbb16900e8..0821a24271c951456a4a7364e6aad241d6fdc6d3 100644
--- a/Environment/testEnvironment.cc
+++ b/Environment/testEnvironment.cc
@@ -19,6 +19,7 @@
 #include <corsika/environment/InhomogeneousMedium.h>
 #include <corsika/environment/LinearApproximationIntegrator.h>
 #include <corsika/environment/NuclearComposition.h>
+#include <corsika/environment/SlidingPlanarExponential.h>
 #include <corsika/environment/VolumeTreeNode.h>
 #include <corsika/geometry/Line.h>
 #include <corsika/geometry/RootCoordinateSystem.h>
@@ -100,6 +101,41 @@ TEST_CASE("FlatExponential") {
   }
 }
 
+TEST_CASE("SlidingPlanarExponential") {
+  NuclearComposition const protonComposition(std::vector<Code>{Code::Proton},
+                                             std::vector<float>{1.f});
+
+  LengthType const lambda = 3_m;
+  auto const rho0 = 1_g / units::si::detail::static_pow<3>(1_cm);
+  auto const tEnd = 5_s;
+
+  SlidingPlanarExponential<IMediumModel> const medium(gOrigin, rho0, lambda,
+                                                      protonComposition);
+
+  SECTION("density") {
+    CHECK(medium.GetMassDensity({gCS, {0_m, 0_m, 3_m}}) /
+              medium.GetMassDensity({gCS, {0_m, 3_m, 0_m}}) ==
+          Approx(1));
+  }
+
+  SECTION("vertical") {
+    Vector const axis(gCS, QuantityVector<dimensionless_d>(0, 0, 1));
+    FlatExponential<IMediumModel> const flat(gOrigin, axis, rho0, lambda,
+                                             protonComposition);
+    Line const line({gCS, {0_m, 0_m, 1_m}},
+                    Vector<SpeedType::dimension_type>(
+                        gCS, {0_m / second, 0_m / second, 5_m / second}));
+    Trajectory<Line> const trajectory(line, tEnd);
+
+    CHECK(medium.GetMassDensity({gCS, {0_mm, 0_m, 3_m}}).magnitude() ==
+          flat.GetMassDensity({gCS, {0_mm, 0_m, 3_m}}).magnitude());
+    CHECK(medium.IntegratedGrammage(trajectory, 2_m).magnitude() ==
+          flat.IntegratedGrammage(trajectory, 2_m).magnitude());
+    CHECK(medium.ArclengthFromGrammage(trajectory, rho0 * 5_m).magnitude() ==
+          flat.ArclengthFromGrammage(trajectory, rho0 * 5_m).magnitude());
+  }
+}
+
 auto constexpr rho0 = 1_kg / 1_m / 1_m / 1_m;
 
 struct Exponential {