diff --git a/Applications/CLI/Tests.cmake b/Applications/CLI/Tests.cmake
index d837a53b200fb4c6871649c8e36944ff3f78c9a9..d35f6cee634c14bf9981dbe01d92dd83a4c1bd0b 100644
--- a/Applications/CLI/Tests.cmake
+++ b/Applications/CLI/Tests.cmake
@@ -486,6 +486,47 @@ if(NOT OGS_USE_MPI)
         cube_1e3_expected_pcs_0_ts_101_t_1.000000.vtu cube_1e3_pcs_0_ts_101_t_1.000000.vtu displacement displacement
     )
 
+    # Mechanics; Small deformations, Ehlers (SDE)
+    AddTest(
+        NAME Mechanics_SDE_cube_1e0
+        PATH Mechanics/Ehlers
+        EXECUTABLE ogs
+        EXECUTABLE_ARGS cube_1e0.prj
+        TESTER vtkdiff
+        ABSTOL 1e-15 RELTOL 1e-13
+        DIFF_DATA
+        expected_cube_1e0_pcs_0_ts_101_t_2.550000.vtu cube_1e0_pcs_0_ts_101_t_2.550000.vtu displacement displacement
+        expected_cube_1e0_pcs_0_ts_101_t_2.550000.vtu cube_1e0_pcs_0_ts_101_t_2.550000.vtu sigma_xx sigma_xx
+        expected_cube_1e0_pcs_0_ts_203_t_5.100000.vtu cube_1e0_pcs_0_ts_203_t_5.100000.vtu displacement displacement
+        expected_cube_1e0_pcs_0_ts_203_t_5.100000.vtu cube_1e0_pcs_0_ts_203_t_5.100000.vtu sigma_xx sigma_xx
+    )
+    AddTest(
+        NAME Mechanics_SDE_cube_1e1
+        PATH Mechanics/Ehlers
+        EXECUTABLE ogs
+        EXECUTABLE_ARGS cube_1e1.prj
+        TESTER vtkdiff
+        ABSTOL 1e-15 RELTOL 1e-13
+        DIFF_DATA
+        expected_cube_1e1_pcs_0_ts_101_t_2.550000.vtu cube_1e1_pcs_0_ts_101_t_2.550000.vtu displacement displacement
+        expected_cube_1e1_pcs_0_ts_101_t_2.550000.vtu cube_1e1_pcs_0_ts_101_t_2.550000.vtu sigma_xx sigma_xx
+        expected_cube_1e1_pcs_0_ts_203_t_5.100000.vtu cube_1e1_pcs_0_ts_203_t_5.100000.vtu displacement displacement
+        expected_cube_1e1_pcs_0_ts_203_t_5.100000.vtu cube_1e1_pcs_0_ts_203_t_5.100000.vtu sigma_xx sigma_xx
+    )
+    AddTest(
+        NAME LARGE_Mechanics_SDE_cube_1e3
+        PATH Mechanics/Ehlers
+        EXECUTABLE ogs
+        EXECUTABLE_ARGS cube_1e3.prj
+        TESTER vtkdiff
+        ABSTOL 1e-15 RELTOL 1e-13
+        DIFF_DATA
+        expected_cube_1e3_pcs_0_ts_101_t_2.550000.vtu cube_1e3_pcs_0_ts_101_t_2.550000.vtu displacement displacement
+        expected_cube_1e3_pcs_0_ts_101_t_2.550000.vtu cube_1e3_pcs_0_ts_101_t_2.550000.vtu sigma_xx sigma_xx
+        expected_cube_1e3_pcs_0_ts_203_t_5.100000.vtu cube_1e3_pcs_0_ts_203_t_5.100000.vtu displacement displacement
+        expected_cube_1e3_pcs_0_ts_203_t_5.100000.vtu cube_1e3_pcs_0_ts_203_t_5.100000.vtu sigma_xx sigma_xx
+    )
+
     # SQUARE 1x1 GROUNDWATER FLOW TEST -- AXIALLY SYMMETRIC
     # test results are compared to 3D simulation on a wedge-shaped domain
     AddTest(
diff --git a/Documentation/bibliography.bib b/Documentation/bibliography.bib
index a25118eb8f773d5d1950ea2e1f2a3937e4ec907f..a6e2af3e046e5208b219be9a7347b46df5535808 100644
--- a/Documentation/bibliography.bib
+++ b/Documentation/bibliography.bib
@@ -2,6 +2,17 @@
 % Encoding: UTF-8
 
 
+@Article{Ehlers1995,
+  Title={A single-surface yield function for geomaterials},
+  Author={Ehlers, W},
+  Journal={Archive of Applied Mechanics},
+  Volume={65},
+  Number={4},
+  Pages={246--259},
+  Year={1995},
+  Publisher={Springer}
+}
+
 @Book{Ericson:2004:RCD:1121584,
   Title                    = {Real-Time Collision Detection (The Morgan Kaufmann Series in Interactive 3-D Technology) (The Morgan Kaufmann Series in Interactive 3D Technology)},
   Author                   = {Ericson, Christer},
diff --git a/MaterialLib/SolidModels/CreateEhlers.h b/MaterialLib/SolidModels/CreateEhlers.h
new file mode 100644
index 0000000000000000000000000000000000000000..a03a55b0135d9f912a8cc81ae569eab2e013aef8
--- /dev/null
+++ b/MaterialLib/SolidModels/CreateEhlers.h
@@ -0,0 +1,138 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2016, OpenGeoSys Community (http://www.opengeosys.org)
+ *            Distributed under a Modified BSD License.
+ *              See accompanying file LICENSE.txt or
+ *              http://www.opengeosys.org/project/license
+ *
+ */
+
+#ifndef MATERIALLIB_SOLIDMODELS_CREATEEHLERS_H_
+#define MATERIALLIB_SOLIDMODELS_CREATEEHLERS_H_
+
+#include <logog/include/logog.hpp>
+
+#include "MechanicsBase.h"
+#include "ProcessLib/Utils/ProcessUtils.h"  // required for findParameter
+#include "Ehlers.h"
+
+namespace MaterialLib
+{
+namespace Solids
+{
+template <int DisplacementDim>
+std::unique_ptr<MechanicsBase<DisplacementDim>> createEhlers(
+    std::vector<std::unique_ptr<ProcessLib::ParameterBase>> const& parameters,
+    BaseLib::ConfigTree const& config)
+{
+    config.checkConfigParameter("type", "Ehlers");
+    DBUG("Create Ehlers material");
+
+    auto& shear_modulus = ProcessLib::findParameter<double>(
+        config, "shear_modulus", parameters, 1);
+
+    DBUG("Use \'%s\' as shear modulus parameter.", shear_modulus.name.c_str());
+
+    auto& bulk_modulus = ProcessLib::findParameter<double>(
+        config, "bulk_modulus", parameters, 1);
+
+    DBUG("Use \'%s\' as bulk modulus parameter.", bulk_modulus.name.c_str());
+
+    auto& kappa = ProcessLib::findParameter<double>(
+        config, "kappa", parameters, 1);
+
+    DBUG("Use \'%s\' as kappa.",
+         kappa.name.c_str());
+
+    auto& beta = ProcessLib::findParameter<double>(
+        config, "beta", parameters, 1);
+
+    DBUG("Use \'%s\' as beta.",
+         beta.name.c_str());
+
+    auto& gamma = ProcessLib::findParameter<double>(
+        config, "gamma", parameters, 1);
+
+    DBUG("Use \'%s\' as gamma.",
+         gamma.name.c_str());
+
+    auto& hardening_modulus = ProcessLib::findParameter<double>(
+        config, "hardening_modulus", parameters, 1);
+
+    DBUG("Use \'%s\' as hardening modulus parameter.",
+         hardening_modulus.name.c_str());
+
+    auto& alpha = ProcessLib::findParameter<double>(
+        config, "alpha", parameters, 1);
+
+    DBUG("Use \'%s\' as alpha.",
+         alpha.name.c_str());
+
+    auto& delta = ProcessLib::findParameter<double>(
+        config, "delta", parameters, 1);
+
+    DBUG("Use \'%s\' as delta.",
+         delta.name.c_str());
+
+    auto& eps = ProcessLib::findParameter<double>(
+        config, "eps", parameters, 1);
+
+    DBUG("Use \'%s\' as eps.",
+         eps.name.c_str());
+
+    auto& m = ProcessLib::findParameter<double>(
+        config, "m", parameters, 1);
+
+    DBUG("Use \'%s\' as m.",
+         m.name.c_str());
+
+    auto& alphap = ProcessLib::findParameter<double>(
+        config, "alphap", parameters, 1);
+
+    DBUG("Use \'%s\' as alphap.",
+         alphap.name.c_str());
+
+    auto& deltap = ProcessLib::findParameter<double>(
+        config, "deltap", parameters, 1);
+
+    DBUG("Use \'%s\' as deltap.",
+         deltap.name.c_str());
+
+    auto& epsp = ProcessLib::findParameter<double>(
+        config, "epsp", parameters, 1);
+
+    DBUG("Use \'%s\' as epsp.",
+         epsp.name.c_str());
+
+    auto& paremeter_mp = ProcessLib::findParameter<double>(
+        config, "mp", parameters, 1);
+
+    DBUG("Use \'%s\' as mp.",
+         paremeter_mp.name.c_str());
+
+    auto& betap = ProcessLib::findParameter<double>(
+        config, "betap", parameters, 1);
+
+    DBUG("Use \'%s\' as betap.",
+         betap.name.c_str());
+
+    auto& gammap = ProcessLib::findParameter<double>(
+        config, "gammap", parameters, 1);
+
+    DBUG("Use \'%s\' as gammap.",
+         gammap.name.c_str());
+
+    typename SolidEhlers<DisplacementDim>::MaterialProperties mp{
+        shear_modulus, bulk_modulus, alpha,  beta,
+        gamma,         delta,        eps,    m,
+        alphap,        betap,        gammap, deltap,
+        epsp,          paremeter_mp, kappa,  hardening_modulus};
+
+    return std::unique_ptr<MechanicsBase<DisplacementDim>>{
+        new SolidEhlers<DisplacementDim>{mp}};
+}
+
+}  // namespace Solids
+}  // namespace MaterialLib
+
+#endif  // MATERIALLIB_SOLIDMODELS_CREATEEHLERS_H_
diff --git a/MaterialLib/SolidModels/Ehlers-impl.h b/MaterialLib/SolidModels/Ehlers-impl.h
new file mode 100644
index 0000000000000000000000000000000000000000..15a6e5fbeebc5ba99e1a120e81af0b77825c14f0
--- /dev/null
+++ b/MaterialLib/SolidModels/Ehlers-impl.h
@@ -0,0 +1,666 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2016, OpenGeoSys Community (http://www.opengeosys.org)
+ *            Distributed under a Modified BSD License.
+ *              See accompanying file LICENSE.txt or
+ *              http://www.opengeosys.org/project/license
+ *
+ */
+
+/**
+ * Common convenitions for naming:
+ * x_D              - deviatoric part of tensor x
+ * x_V              - volumetric part of tensor x
+ * x_p              - a variable related to plastic potential
+ * x_prev           - value of x in previous time step
+ *
+ * Variables used in the code:
+ * eps_D            - deviatoric strain
+ * eps_p_D_dot      - deviatoric increment of plastic strain
+ * eps_p_eff_dot    - increment of effective plastic strain
+ * eps_p_V_dot      - volumetric increment of plastic strain
+ * sigma_D_inverse_D - deviatoric part of sigma_D_inverse
+ *
+ * derivation of the flow rule
+ * theta            - J3 / J2^(3 / 2) from yield function
+ * dtheta_dsigma    - derivative of theta
+ * sqrtPhi          - square root of Phi from plastic potential
+ * flow_D           - deviatoric part of flow
+ * flow_V           - volumetric part of flow
+ * lambda_flow_D    - deviatoric increment of plastic strain
+ *
+ */
+#ifndef MATERIALLIB_SOLIDMODELS_EHLERS_IMPL_H_
+#define MATERIALLIB_SOLIDMODELS_EHLERS_IMPL_H_
+
+#include <boost/math/special_functions/pow.hpp>
+#include <logog/include/logog.hpp>
+#include "MaterialLib/SolidModels/KelvinVector.h"
+#include "NewtonRaphson.h"
+
+namespace MaterialLib
+{
+namespace Solids
+{
+template <int DisplacementDim>
+struct PhysicalStressWithInvariants final
+{
+    static int const KelvinVectorSize =
+        ProcessLib::KelvinVectorDimensions<DisplacementDim>::value;
+    using Invariants = MaterialLib::SolidModels::Invariants<KelvinVectorSize>;
+    using KelvinVector = ProcessLib::KelvinVectorType<DisplacementDim>;
+
+    explicit PhysicalStressWithInvariants(KelvinVector const& stress)
+        : value{stress},
+          D{Invariants::deviatoric_projection * stress},
+          I_1{Invariants::trace(stress)},
+          J_2{Invariants::J2(D)},
+          J_3{Invariants::J3(D)}
+    {
+    }
+
+    PhysicalStressWithInvariants(PhysicalStressWithInvariants const&) = default;
+    PhysicalStressWithInvariants& operator=(
+        PhysicalStressWithInvariants const&) = default;
+#if defined(_MSC_VER) && (_MSC_VER >= 1900)
+    PhysicalStressWithInvariants(PhysicalStressWithInvariants&&) = default;
+    PhysicalStressWithInvariants& operator=(PhysicalStressWithInvariants&&) =
+        default;
+#endif  // _MSC_VER
+
+    KelvinVector value;
+    KelvinVector D;
+    double I_1;
+    double J_2;
+    double J_3;
+};
+
+/// Holds powers of 1 + gamma_p*theta to base 0, m_p, and m_p-1.
+struct OnePlusGamma_pTheta final
+{
+    OnePlusGamma_pTheta(double const gamma_p, double const theta,
+                        double const m_p)
+        : value{1 + gamma_p * theta},
+          pow_m_p{std::pow(value, m_p)},
+          pow_m_p1{pow_m_p/value}
+    {
+    }
+
+    double const value;
+    double const pow_m_p;
+    double const pow_m_p1;
+};
+
+template <int DisplacementDim>
+double plasticFlowVolumetricPart(
+    PhysicalStressWithInvariants<DisplacementDim> const& s,
+    double const sqrtPhi, double const alpha_p, double const beta_p,
+    double const delta_p, double const epsilon_p)
+{
+    return 3 * (alpha_p * s.I_1 +
+                4 * boost::math::pow<2>(delta_p) * boost::math::pow<3>(s.I_1)) /
+               (2 * sqrtPhi) +
+           3 * beta_p + 6 * epsilon_p * s.I_1;
+}
+
+template <int DisplacementDim>
+typename SolidEhlers<DisplacementDim>::KelvinVector
+plasticFlowDeviatoricPart(
+    PhysicalStressWithInvariants<DisplacementDim> const& s,
+    OnePlusGamma_pTheta const& one_gt, double const sqrtPhi,
+    typename SolidEhlers<DisplacementDim>::KelvinVector const&
+        dtheta_dsigma,
+    double const gamma_p, double const m_p)
+{
+    return (one_gt.pow_m_p * (s.D +
+            s.J_2 * m_p * gamma_p * dtheta_dsigma / one_gt.value)) /
+           (2 * sqrtPhi);
+}
+template <int DisplacementDim>
+double yieldFunction(
+    PhysicalStressWithInvariants<DisplacementDim> const& s,
+    typename SolidEhlers<DisplacementDim>::MaterialProperties const& mp,
+    double const t, ProcessLib::SpatialPosition const& x)
+{
+    double const alpha = mp.alpha(t, x)[0];
+    double const beta = mp.beta(t, x)[0];
+    double const delta = mp.delta(t, x)[0];
+    double const epsilon = mp.epsilon(t, x)[0];
+    double const gamma = mp.gamma(t, x)[0];
+    double const m = mp.m(t, x)[0];
+
+    double const I_1_squared = boost::math::pow<2>(s.I_1);
+    assert(s.J_2 != 0);
+
+    return std::sqrt(
+               s.J_2 * std::pow(1 +
+                                    gamma * s.J_3 /
+                                        boost::math::pow<3>(std::sqrt(s.J_2)),
+                                m) +
+               alpha / 2. * I_1_squared +
+               boost::math::pow<2>(delta) * boost::math::pow<2>(I_1_squared)) +
+           beta * s.I_1 + epsilon * I_1_squared - mp.k;
+}
+
+template <int DisplacementDim>
+void calculatePlasticResidual(
+    double const t,
+    ProcessLib::SpatialPosition const& x,
+    ProcessLib::KelvinVectorType<DisplacementDim> const& eps_D,
+    double const eps_V,
+    PhysicalStressWithInvariants<DisplacementDim> const& s,
+    ProcessLib::KelvinVectorType<DisplacementDim> const& eps_p_D,
+    ProcessLib::KelvinVectorType<DisplacementDim> const& eps_p_D_dot,
+    double const eps_p_V,
+    double const eps_p_V_dot,
+    double const eps_p_eff_dot,
+    double const lambda,
+    typename SolidEhlers<DisplacementDim>::MaterialProperties const& _mp,
+    typename SolidEhlers<DisplacementDim>::ResidualVectorType& residual)
+{
+    static int const KelvinVectorSize =
+        ProcessLib::KelvinVectorDimensions<DisplacementDim>::value;
+    using Invariants = MaterialLib::SolidModels::Invariants<KelvinVectorSize>;
+    using KelvinVector = ProcessLib::KelvinVectorType<DisplacementDim>;
+
+    auto const& P_dev = Invariants::deviatoric_projection;
+    auto const& identity2 = Invariants::identity2;
+
+    double const G = _mp.G(t, x)[0];
+    double const K = _mp.K(t, x)[0];
+
+    double const theta = s.J_3 / boost::math::pow<3>(std::sqrt(s.J_2));
+
+    // calculate stress residual
+    residual.template segment<KelvinVectorSize>(0).noalias() =
+        s.value / G - 2 * (eps_D - eps_p_D) -
+        K / G * (eps_V - eps_p_V) * identity2;
+
+    // deviatoric plastic strain
+    double const alpha_p = _mp.alpha_p(t, x)[0];
+    double const delta_p = _mp.delta_p(t, x)[0];
+    double const gamma_p = _mp.gamma_p(t, x)[0];
+    double const m_p = _mp.m_p(t, x)[0];
+    KelvinVector const sigma_D_inverse_D =
+        P_dev * MaterialLib::SolidModels::inverse(s.D);
+    KelvinVector const dtheta_dsigma =
+        theta * sigma_D_inverse_D - 3. / 2. * theta / s.J_2 * s.D;
+
+    OnePlusGamma_pTheta const one_gt{gamma_p, theta, m_p};
+    double const sqrtPhi = std::sqrt(
+        s.J_2 * one_gt.pow_m_p + alpha_p / 2. * boost::math::pow<2>(s.I_1) +
+        boost::math::pow<2>(delta_p) * boost::math::pow<4>(s.I_1));
+    KelvinVector const flow_D = plasticFlowDeviatoricPart(
+        s, one_gt, sqrtPhi, dtheta_dsigma, gamma_p, m_p);
+    KelvinVector const lambda_flow_D = lambda * flow_D;
+
+    residual.template segment<KelvinVectorSize>(KelvinVectorSize)
+        .noalias() = eps_p_D_dot - lambda_flow_D;
+
+    // plastic volume strain
+    {
+        double const beta_p = _mp.beta_p(t, x)[0];
+        double const epsilon_p = _mp.epsilon_p(t, x)[0];
+
+        double const flow_V = plasticFlowVolumetricPart<DisplacementDim>(
+            s, sqrtPhi, alpha_p, beta_p, delta_p, epsilon_p);
+        residual(2 * KelvinVectorSize, 0) = eps_p_V_dot - lambda * flow_V;
+    }
+
+    // evolution of plastic equivalent strain
+    residual(2 * KelvinVectorSize + 1) =
+        eps_p_eff_dot -
+        std::sqrt(2. / 3. * lambda_flow_D.transpose() * lambda_flow_D);
+
+    // yield function (for plastic multiplier)
+    residual(2 * KelvinVectorSize + 2) =
+        yieldFunction<DisplacementDim>(s, _mp, t, x) / G;
+}
+
+/// Special product of v with itself: v \odot v
+/// v is given in Kelvin mapping.
+template <int DisplacementDim>
+ProcessLib::KelvinMatrixType<DisplacementDim> s_odot_s(
+    ProcessLib::KelvinVectorType<DisplacementDim> const& v)
+{
+    ProcessLib::KelvinMatrixType<DisplacementDim> result;
+
+    result(0, 0) = v(0) * v(0);
+    result(0, 1) = result(1, 0) = v(3) * v(3) / 2.;
+    result(0, 2) = result(2, 0) = v(5) * v(5) / 2.;
+    result(0, 3) = result(3, 0) = v(0) * v(3);
+    result(0, 4) = result(4, 0) = v(3) * v(5) / std::sqrt(2.);
+    result(0, 5) = result(5, 0) = v(0) * v(5);
+
+    result(1, 1) = v(1) * v(1);
+    result(1, 2) = result(2, 1) = v(4) * v(4) / 2.;
+    result(1, 3) = result(3, 1) = v(3) * v(1);
+    result(1, 4) = result(4, 1) = v(1) * v(4);
+    result(1, 5) = result(5, 1) = v(3) * v(4) / std::sqrt(2.);
+
+    result(2, 2) = v(2) * v(2);
+    result(2, 3) = result(3, 2) = v(5) * v(4) / std::sqrt(2.);
+    result(2, 4) = result(4, 2) = v(4) * v(2);
+    result(2, 5) = result(5, 2) = v(5) * v(2);
+
+    result(3, 3) = v(0) * v(1) + v(3) * v(3) / 2.;
+    result(3, 4) = result(4, 3) =
+        v(3) * v(4) / 2. + v(5) * v(1) / std::sqrt(2.);
+    result(3, 5) = result(5, 3) =
+        v(0) * v(4) / std::sqrt(2.) + v(3) * v(5) / 2.;
+
+    result(4, 4) = v(1) * v(2) + v(4) * v(4) / 2.;
+    result(4, 5) = result(5, 4) =
+        v(3) * v(2) / std::sqrt(2.) + v(5) * v(4) / 2.;
+
+    result(5, 5) = v(0) * v(2) + v(5) * v(5) / 2.;
+    return result;
+}
+
+template <int DisplacementDim>
+void calculatePlasticJacobian(
+    double const dt,
+    double const t,
+    ProcessLib::SpatialPosition const& x,
+    typename SolidEhlers<DisplacementDim>::JacobianMatrix& jacobian,
+    PhysicalStressWithInvariants<DisplacementDim> const& s,
+    double const lambda,
+    typename SolidEhlers<DisplacementDim>::MaterialProperties const& _mp)
+{
+    static int const KelvinVectorSize =
+        ProcessLib::KelvinVectorDimensions<DisplacementDim>::value;
+    using Invariants = MaterialLib::SolidModels::Invariants<KelvinVectorSize>;
+    using KelvinVector = ProcessLib::KelvinVectorType<DisplacementDim>;
+    using KelvinMatrix = ProcessLib::KelvinMatrixType<DisplacementDim>;
+
+    auto const& P_dev = Invariants::deviatoric_projection;
+    auto const& identity2 = Invariants::identity2;
+
+    double const G = _mp.G(t, x)[0];
+    double const K = _mp.K(t, x)[0];
+    double const m = _mp.m(t, x)[0];
+    double const alpha = _mp.alpha(t, x)[0];
+    double const beta = _mp.beta(t, x)[0];
+    double const gamma = _mp.gamma(t, x)[0];
+    double const delta = _mp.delta(t, x)[0];
+
+    double const alpha_p = _mp.alpha_p(t, x)[0];
+    double const beta_p = _mp.beta_p(t, x)[0];
+    double const gamma_p = _mp.gamma_p(t, x)[0];
+    double const delta_p = _mp.delta_p(t, x)[0];
+    double const epsilon_p = _mp.epsilon_p(t, x)[0];
+    double const m_p = _mp.m_p(t, x)[0];
+
+    double const theta = s.J_3 / boost::math::pow<3>(std::sqrt(s.J_2));
+    OnePlusGamma_pTheta const one_gt{gamma_p, theta, m_p};
+
+    // inverse of deviatoric stress tensor
+    if (Invariants::determinant(s.D) == 0)
+    {
+        OGS_FATAL("Determinant is zero. Matrix is non-invertable.");
+    }
+    // inverse of sigma_D
+    KelvinVector const sigma_D_inverse = MaterialLib::SolidModels::inverse(s.D);
+    KelvinVector const sigma_D_inverse_D = P_dev * sigma_D_inverse;
+
+    KelvinVector const dtheta_dsigma =
+        theta * sigma_D_inverse_D - 3. / 2. * theta / s.J_2 * s.D;
+
+    // deviatoric flow
+    double const sqrtPhi = std::sqrt(
+        s.J_2 * one_gt.pow_m_p + alpha_p / 2. * boost::math::pow<2>(s.I_1) +
+        boost::math::pow<2>(delta_p) * boost::math::pow<4>(s.I_1));
+    KelvinVector const flow_D = plasticFlowDeviatoricPart(
+        s, one_gt, sqrtPhi, dtheta_dsigma, gamma_p, m_p);
+    KelvinVector const lambda_flow_D = lambda * flow_D;
+
+    jacobian.setZero();
+
+    // G_11
+    jacobian.template block<KelvinVectorSize, KelvinVectorSize>(0, 0)
+        .noalias() = KelvinMatrix::Identity();
+
+    // G_12
+    jacobian
+        .template block<KelvinVectorSize, KelvinVectorSize>(0, KelvinVectorSize)
+        .noalias() = 2 * KelvinMatrix::Identity();
+
+    // G_13
+    jacobian.template block<KelvinVectorSize, 1>(0, 2 * KelvinVectorSize)
+        .noalias() = K / G * identity2;
+
+    // G_14 and G_15 are zero
+
+    // G_21 -- derivative of deviatoric flow
+
+    double const gm_p = gamma_p * m_p;
+    // intermediate variable for derivative of deviatoric flow
+    KelvinVector const M0 = s.J_2 / one_gt.value * dtheta_dsigma;
+    // derivative of Phi w.r.t. sigma
+    KelvinVector const dPhi_dsigma =
+        one_gt.pow_m_p * (s.D + gm_p * M0) +
+        (alpha_p * s.I_1 +
+         4 * boost::math::pow<2>(delta_p) * boost::math::pow<3>(s.I_1)) *
+            identity2;
+
+    // intermediate variable for derivative of deviatoric flow
+    KelvinMatrix const M1 =
+        one_gt.pow_m_p *
+        (s.D * dPhi_dsigma.transpose() + gm_p * M0 * dPhi_dsigma.transpose());
+    // intermediate variable for derivative of deviatoric flow
+    KelvinMatrix const M2 =
+        one_gt.pow_m_p * (P_dev + s.D * gm_p * M0.transpose());
+    // second derivative of theta
+    KelvinMatrix const d2theta_dsigma2 =
+        theta * P_dev * s_odot_s<DisplacementDim>(sigma_D_inverse) * P_dev +
+        sigma_D_inverse_D * dtheta_dsigma.transpose() -
+        3. / 2. * theta / s.J_2 * P_dev -
+        3. / 2. * dtheta_dsigma / s.J_2 * s.D.transpose() +
+        3. / 2. * theta / boost::math::pow<2>(s.J_2) * s.D * s.D.transpose();
+
+    // intermediate variable for derivative of deviatoric flow
+    KelvinMatrix const M3 =
+        gm_p * one_gt.pow_m_p1 *
+        ((s.D + (gm_p - gamma_p) * M0) * dtheta_dsigma.transpose() +
+         s.J_2 * d2theta_dsigma2);
+
+    // derivative of flow_D w.r.t. sigma
+    KelvinMatrix const dflow_D_dsigma =
+        (-M1 / (4 * boost::math::pow<3>(sqrtPhi)) + (M2 + M3) / (2 * sqrtPhi)) *
+        G;
+    jacobian
+        .template block<KelvinVectorSize, KelvinVectorSize>(KelvinVectorSize, 0)
+        .noalias() = -lambda * dflow_D_dsigma;
+
+    // G_22
+    jacobian
+        .template block<KelvinVectorSize, KelvinVectorSize>(KelvinVectorSize,
+                                                            KelvinVectorSize)
+        .noalias() = KelvinMatrix::Identity() / dt;
+
+    // G_23 and G_24 are zero
+
+    // G_25
+    jacobian
+        .template block<KelvinVectorSize, 1>(KelvinVectorSize,
+                                             2 * KelvinVectorSize + 2)
+        .noalias() = -flow_D;
+
+    // G_31
+    {
+        // derivative of flow_V w.r.t. sigma
+        KelvinVector const dflow_V_dsigma =
+            3 * G *
+            (-(alpha_p * s.I_1 +
+               4 * boost::math::pow<2>(delta_p) * boost::math::pow<3>(s.I_1)) /
+                 (4 * boost::math::pow<3>(sqrtPhi)) * dPhi_dsigma +
+             (alpha_p * identity2 +
+              12 * boost::math::pow<2>(delta_p * s.I_1) * identity2) /
+                 (2 * sqrtPhi) +
+             2 * epsilon_p * identity2);
+
+        jacobian.template block<1, KelvinVectorSize>(2 * KelvinVectorSize, 0)
+            .noalias() = -lambda * dflow_V_dsigma.transpose();
+    }
+
+    // G_32 is zero
+
+    // G_33
+    jacobian(2 * KelvinVectorSize, 2 * KelvinVectorSize) = 1. / dt;
+
+    // G_34 is zero
+
+    // G_35
+    {
+        double const flow_V = plasticFlowVolumetricPart<DisplacementDim>(
+            s, sqrtPhi, alpha_p, beta_p, delta_p, epsilon_p);
+        jacobian(2 * KelvinVectorSize, 2 * KelvinVectorSize + 2) = -flow_V;
+    }
+
+    // increment of effectiv plastic strain
+    double const eff_flow =
+        std::sqrt(2. / 3. * lambda_flow_D.transpose() * lambda_flow_D);
+
+    if (eff_flow > 0)
+    {
+        // intermediate variable for derivative of plastic jacobian
+        KelvinVector const eff_flow23_lambda_flow_D =
+            -2 / 3. / eff_flow * lambda_flow_D;
+        // G_41
+        jacobian
+            .template block<1, KelvinVectorSize>(2 * KelvinVectorSize + 1, 0)
+            .noalias() = lambda * dflow_D_dsigma * eff_flow23_lambda_flow_D;
+        // G_45
+        jacobian(2 * KelvinVectorSize + 1, 2 * KelvinVectorSize + 2) =
+            eff_flow23_lambda_flow_D.transpose() * flow_D;
+    }
+
+    // G_42 and G_43 are zero
+
+    // G_44
+    jacobian(2 * KelvinVectorSize + 1, 2 * KelvinVectorSize + 1) = 1. / dt;
+
+    // G_51
+    {
+        double const one_gt_pow_m = std::pow(one_gt.value, m);
+        double const gm = gamma * m;
+        // derivative of yield function w.r.t. sigma
+        KelvinVector const dF_dsigma =
+            G * (one_gt_pow_m * (s.D + gm * M0) +
+                 (alpha * s.I_1 +
+                  4 * boost::math::pow<2>(delta) * boost::math::pow<3>(s.I_1)) *
+                     identity2) /
+                (2. * sqrtPhi) +
+            G * (beta + 2 * epsilon_p * s.I_1) * identity2;
+
+        jacobian
+            .template block<1, KelvinVectorSize>(2 * KelvinVectorSize + 2, 0)
+            .noalias() = dF_dsigma.transpose() / G;
+    }
+
+    // G_54
+    jacobian(2 * KelvinVectorSize + 2, 2 * KelvinVectorSize + 1) =
+        -_mp.kappa(t, x)[0] * _mp.hardening_coefficient(t, x)[0] / G;
+
+    // G_52, G_53, G_55 are zero
+}
+
+/// Calculates the derivative of the residuals with respect to total
+/// strain. Implementation fully implicit only.
+template <int DisplacementDim>
+ProcessLib::KelvinMatrixType<DisplacementDim> calculateDResidualDEps(
+    double const K, double const G)
+{
+    static int const KelvinVectorSize =
+        ProcessLib::KelvinVectorDimensions<DisplacementDim>::value;
+    using Invariants = MaterialLib::SolidModels::Invariants<KelvinVectorSize>;
+
+    auto const& P_dev = Invariants::deviatoric_projection;
+    auto const& P_sph = Invariants::spherical_projection;
+    auto const& I = ProcessLib::KelvinMatrixType<DisplacementDim>::Identity();
+
+    return -2. * I * P_dev - 3. * K / G * I * P_sph;
+}
+
+template <int DisplacementDim>
+void SolidEhlers<DisplacementDim>::MaterialProperties::
+    calculateIsotropicHardening(double const t,
+                                ProcessLib::SpatialPosition const& x,
+                                double const eps_p_eff)
+{
+    k = kappa(t, x)[0] * (1. + eps_p_eff * hardening_coefficient(t, x)[0]);
+}
+
+template <int DisplacementDim>
+typename SolidEhlers<DisplacementDim>::KelvinVector predict_sigma(
+    double const G, double const K,
+    typename SolidEhlers<DisplacementDim>::KelvinVector const&
+        sigma_prev,
+    typename SolidEhlers<DisplacementDim>::KelvinVector const& eps,
+    typename SolidEhlers<DisplacementDim>::KelvinVector const& eps_prev,
+    double const eps_V)
+{
+    static int const KelvinVectorSize =
+        ProcessLib::KelvinVectorDimensions<DisplacementDim>::value;
+    using Invariants = MaterialLib::SolidModels::Invariants<KelvinVectorSize>;
+    auto const& P_dev = Invariants::deviatoric_projection;
+
+    // dimensionless initial hydrostatic pressure
+    double const pressure_prev = Invariants::trace(sigma_prev) / (-3. * G);
+    // initial strain invariant
+    double const e_prev = Invariants::trace(eps_prev);
+    // dimensioness hydrostatic stress increment
+    double const pressure = pressure_prev - K / G * (eps_V - e_prev);
+    // dimensionless deviatoric initial stress
+    typename SolidEhlers<DisplacementDim>::KelvinVector const
+        sigma_D_prev = P_dev * sigma_prev / G;
+    // dimensionless deviatoric stress
+    typename SolidEhlers<DisplacementDim>::KelvinVector const sigma_D =
+        sigma_D_prev + 2 * P_dev * (eps - eps_prev);
+    return sigma_D - pressure * Invariants::identity2;
+}
+
+template <int DisplacementDim>
+bool SolidEhlers<DisplacementDim>::computeConstitutiveRelation(
+    double const t,
+    ProcessLib::SpatialPosition const& x,
+    double const dt,
+    KelvinVector const& eps_prev,
+    KelvinVector const& eps,
+    KelvinVector const& sigma_prev,
+    KelvinVector& sigma_final,
+    KelvinMatrix& C,
+    typename MechanicsBase<DisplacementDim>::MaterialStateVariables&
+        material_state_variables)
+{
+    assert(dynamic_cast<MaterialStateVariables*>(&material_state_variables) !=
+           nullptr);
+    MaterialStateVariables& _state =
+        static_cast<MaterialStateVariables&>(material_state_variables);
+    _state.loadState();
+
+    using Invariants = MaterialLib::SolidModels::Invariants<KelvinVectorSize>;
+    C.setZero();
+
+    // volumetric strain
+    double const eps_V = Invariants::trace(eps);
+
+    auto const& P_dev = Invariants::deviatoric_projection;
+    // deviatoric strain
+    KelvinVector const eps_D = P_dev * eps;
+
+    // dimensionless stress/hydrostatic pressure
+    double const G = _mp.G(t, x)[0];
+    double const K = _mp.K(t, x)[0];
+
+    KelvinVector sigma =
+        predict_sigma<DisplacementDim>(G, K, sigma_prev, eps, eps_prev, eps_V);
+
+    // update parameter
+    _mp.calculateIsotropicHardening(t, x, _state.eps_p_eff);
+
+    PhysicalStressWithInvariants<DisplacementDim> s{G * sigma};
+    // Quit early if sigma is zero (nothing to do) or if we are still in elastic
+    // zone.
+    if (sigma.squaredNorm() == 0 ||
+        yieldFunction<DisplacementDim>(s, _mp, t, x) < 0)
+    {
+        C.setZero();
+        C.template topLeftCorner<3, 3>().setConstant(K - 2. / 3 * G);
+        C.noalias() += 2 * G * KelvinMatrix::Identity();
+    }
+    else
+    {
+        JacobianMatrix jacobian;
+
+        // Linear solver for the newton loop is required after the loop with the
+        // same matrix. This saves one decomposition.
+        Eigen::FullPivLU<JacobianMatrix> linear_solver;
+
+        {  // Newton loop for return mapping calculation.
+            auto const update_residual = [&](ResidualVectorType& residual) {
+
+                KelvinVector const eps_p_D_dot =
+                    (_state.eps_p_D - _state.eps_p_D_prev) / dt;
+                double const eps_p_V_dot =
+                    (_state.eps_p_V - _state.eps_p_V_prev) / dt;
+                double const eps_p_eff_dot =
+                    (_state.eps_p_eff - _state.eps_p_eff_prev) / dt;
+                calculatePlasticResidual<DisplacementDim>(
+                    t, x, eps_D, eps_V, s, _state.eps_p_D, eps_p_D_dot,
+                    _state.eps_p_V, eps_p_V_dot, eps_p_eff_dot, _state.lambda,
+                    _mp, residual);
+            };
+
+            auto const update_jacobian = [&](JacobianMatrix& jacobian) {
+                calculatePlasticJacobian<DisplacementDim>(dt, t, x, jacobian, s,
+                                                          _state.lambda, _mp);
+            };
+
+            auto const update_solution = [&](
+                ResidualVectorType const& increment) {
+                sigma.noalias() +=
+                    increment.template segment<KelvinVectorSize>(
+                        KelvinVectorSize * 0);
+                s = PhysicalStressWithInvariants<DisplacementDim>{G * sigma};
+                _state.eps_p_D.noalias() +=
+                    increment.template segment<KelvinVectorSize>(
+                        KelvinVectorSize * 1);
+                _state.eps_p_V += increment(KelvinVectorSize * 2);
+                _state.eps_p_eff += increment(KelvinVectorSize * 2 + 1);
+                _state.lambda += increment(KelvinVectorSize * 2 + 2);
+
+                _mp.calculateIsotropicHardening(t, x, _state.eps_p_eff);
+            };
+
+            // TODO Make the following choice of maximum iterations and
+            // convergence criteria available from the input file configuration:
+            int const maximum_iterations(100);
+            double const tolerance(1e-14);
+
+            auto newton_solver =
+                NewtonRaphson<decltype(linear_solver), JacobianMatrix,
+                              decltype(update_jacobian), ResidualVectorType,
+                              decltype(update_residual),
+                              decltype(update_solution)>(
+                    linear_solver, update_jacobian, update_residual,
+                    update_solution, maximum_iterations, tolerance);
+
+            auto const success_iterations = newton_solver.solve(jacobian);
+
+            if (!success_iterations)
+                return false;
+
+            // If the Newton loop didn't run, the linear solver will not be
+            // initialized.
+            // This happens usually for the first iteration of the first
+            // timestep.
+            if (*success_iterations == 0)
+                linear_solver.compute(jacobian);
+        }
+
+        // Calculate residual derivative w.r.t. strain
+        Eigen::Matrix<double, JacobianResidualSize, KelvinVectorSize,
+                      Eigen::RowMajor>
+            dresidual_deps =
+                Eigen::Matrix<double, JacobianResidualSize, KelvinVectorSize,
+                              Eigen::RowMajor>::Zero();
+        dresidual_deps.template block<KelvinVectorSize, KelvinVectorSize>(0, 0)
+            .noalias() = calculateDResidualDEps<DisplacementDim>(K, G);
+
+        // Extract consistent tangent.
+        C.noalias() =
+            _mp.G(t, x)[0] *
+            linear_solver.solve(-dresidual_deps)
+                .template block<KelvinVectorSize, KelvinVectorSize>(0, 0);
+    }
+
+    // Update sigma.
+    sigma_final.noalias() = G * sigma;
+    return true;
+}
+
+}  // namespace Solids
+}  // namespace MaterialLib
+
+#endif  // MATERIALLIB_SOLIDMODELS_EHLERS_IMPL_H_
diff --git a/MaterialLib/SolidModels/Ehlers.cpp b/MaterialLib/SolidModels/Ehlers.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d494ce535768ad13b336451b5fea8368eaae5f92
--- /dev/null
+++ b/MaterialLib/SolidModels/Ehlers.cpp
@@ -0,0 +1,20 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2016, OpenGeoSys Community (http://www.opengeosys.org)
+ *            Distributed under a Modified BSD License.
+ *              See accompanying file LICENSE.txt or
+ *              http://www.opengeosys.org/project/license
+ *
+ */
+
+#include "Ehlers.h"
+
+namespace MaterialLib
+{
+namespace Solids
+{
+template class SolidEhlers<2>;
+template class SolidEhlers<3>;
+
+}  // namespace Solids
+}  // namespace MaterialLib
diff --git a/MaterialLib/SolidModels/Ehlers.h b/MaterialLib/SolidModels/Ehlers.h
new file mode 100644
index 0000000000000000000000000000000000000000..b6054564208f0f632f8145b6714fa4c392578b31
--- /dev/null
+++ b/MaterialLib/SolidModels/Ehlers.h
@@ -0,0 +1,217 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2016, OpenGeoSys Community (http://www.opengeosys.org)
+ *            Distributed under a Modified BSD License.
+ *              See accompanying file LICENSE.txt or
+ *              http://www.opengeosys.org/project/license
+ *
+ */
+
+/*
+ * Implementation of Ehler's single-surface model.
+ * see Ehler's paper "A single-surface yield function for geomaterials" for more
+ * details. \cite{Ehlers1995}
+ *
+ * Refer to "Single-surface benchmark of OpenGeoSys documentation
+ * (https://docs.opengeosys.org/docs/benchmarks/small-deformations/mechanics-plasticity-single-surface)"
+ * for more details for the tests.
+ */
+
+#ifndef MATERIALLIB_SOLIDMODELS_EHLERS_H_
+#define MATERIALLIB_SOLIDMODELS_EHLERS_H_
+
+#include <cfloat>
+#include <memory>
+#ifndef NDEBUG
+#include <ostream>
+#endif
+
+#include <Eigen/Dense>
+#include <logog/include/logog.hpp>
+
+#include "BaseLib/Error.h"
+#include "KelvinVector.h"
+#include "MechanicsBase.h"
+
+namespace MaterialLib
+{
+namespace Solids
+{
+
+template <int DisplacementDim>
+class SolidEhlers final : public MechanicsBase<DisplacementDim>
+{
+public:
+    static int const KelvinVectorSize =
+        ProcessLib::KelvinVectorDimensions<DisplacementDim>::value;
+    static int const JacobianResidualSize =
+        2 * KelvinVectorSize + 3;  // 2 is the number of components in the
+                                   // jacobian/residual, not the space
+                                   // dimension. And 3 is for additional
+                                   // variables.
+    using ResidualVectorType = Eigen::Matrix<double, JacobianResidualSize, 1>;
+    using JacobianMatrix = Eigen::Matrix<double, JacobianResidualSize,
+                                         JacobianResidualSize, Eigen::RowMajor>;
+
+public:
+    //
+    // Variables specific to the material model.
+    //
+    struct MaterialProperties
+    {
+        using P = ProcessLib::Parameter<double>;
+
+        using KelvinVector = ProcessLib::KelvinVectorType<DisplacementDim>;
+        using KelvinMatrix = ProcessLib::KelvinMatrixType<DisplacementDim>;
+
+        /// material parameters in relation to Ehler's single-surface model
+        /// see Ehler's paper "A single-surface yield function for geomaterials"
+        /// for more details.
+        MaterialProperties(P const& G_, P const& K_, P const& alpha_,
+                           P const& beta_, P const& gamma_, P const& delta_,
+                           P const& epsilon_, P const& m_, P const& alpha_p_,
+                           P const& beta_p_, P const& gamma_p_,
+                           P const& delta_p_, P const& epsilon_p_,
+                           P const& m_p_, P const& kappa_,
+                           P const& hardening_coefficient_)
+            : G(G_),
+              K(K_),
+              alpha(alpha_),
+              beta(beta_),
+              gamma(gamma_),
+              delta(delta_),
+              epsilon(epsilon_),
+              m(m_),
+              alpha_p(alpha_p_),
+              beta_p(beta_p_),
+              gamma_p(gamma_p_),
+              delta_p(delta_p_),
+              epsilon_p(epsilon_p_),
+              m_p(m_p_),
+              kappa(kappa_),
+              hardening_coefficient(hardening_coefficient_)
+        {
+        }
+        // basic material parameters
+        P const& G;  ///< shear modulus
+        P const& K;  ///< bulk modulus
+
+        P const& alpha;
+        P const& beta;
+        P const& gamma;
+        P const& delta;
+        P const& epsilon;
+        P const& m;
+
+        P const& alpha_p;
+        P const& beta_p;
+        P const& gamma_p;
+        P const& delta_p;
+        P const& epsilon_p;
+        P const& m_p;
+
+        P const& kappa;
+        P const& hardening_coefficient;
+        // Drucker-Prager: Import kappa and beta in terms of Drucker-Prager
+        // criterion solution dependent values
+        double k;
+
+        void calculateIsotropicHardening(double const t,
+                                         ProcessLib::SpatialPosition const& x,
+                                         const double e_pv_curr);
+    };
+
+    struct MaterialStateVariables
+        : public MechanicsBase<DisplacementDim>::MaterialStateVariables
+    {
+        MaterialStateVariables()
+            : eps_p_D(KelvinVector::Zero()), eps_p_D_prev(KelvinVector::Zero())
+        {
+        }
+
+        void loadState()
+        {
+            eps_p_D = eps_p_D_prev;
+            eps_p_V = eps_p_V_prev;
+            eps_p_eff = eps_p_eff_prev;
+            lambda = 0;
+        }
+
+        void pushBackState() override
+        {
+            eps_p_D_prev = eps_p_D;
+            eps_p_V_prev = eps_p_V;
+            eps_p_eff_prev = eps_p_eff;  // effective part of trace(eps_p)
+            lambda = 0;
+        }
+
+        using KelvinVector = ProcessLib::KelvinVectorType<DisplacementDim>;
+
+        KelvinVector eps_p_D;  ///< deviatoric plastic strain
+        double eps_p_V = 0;    ///< volumetric strain
+        double eps_p_eff = 0;  ///< effective plastic strain
+
+        // Initial values from previous timestep
+        KelvinVector eps_p_D_prev;  ///< \copydoc eps_p_D
+        double eps_p_V_prev = 0;    ///< \copydoc eps_p_V
+        double eps_p_eff_prev = 0;  ///< \copydoc eps_p_eff
+
+        double lambda = 0;  ///< plastic multiplier
+
+#ifndef NDEBUG
+        friend std::ostream& operator<<(std::ostream& os,
+                                        MaterialStateVariables const& m)
+        {
+            os << "State:\n"
+               << "eps_p_D: " << m.eps_p_D << "\n"
+               << "e_p: " << m.e_p << "\n"
+               << "eps_p_eff: " << m.eps_p_eff << "\n"
+               << "eps_p_D_prev: " << m.eps_p_D_prev << "\n"
+               << "e_p_prev: " << m.e_p_prev << "\n"
+               << "eps_p_eff_prev: " << m.eps_p_eff_prev << "\n"
+               << "lambda: " << m.lambda << "\n";
+            return os;
+        }
+#endif  // NDEBUG
+    };
+
+    std::unique_ptr<
+        typename MechanicsBase<DisplacementDim>::MaterialStateVariables>
+    createMaterialStateVariables() override
+    {
+        return std::unique_ptr<
+            typename MechanicsBase<DisplacementDim>::MaterialStateVariables>{
+            new MaterialStateVariables};
+    }
+
+public:
+    using KelvinVector = ProcessLib::KelvinVectorType<DisplacementDim>;
+    using KelvinMatrix = ProcessLib::KelvinMatrixType<DisplacementDim>;
+
+public:
+    explicit SolidEhlers(MaterialProperties const& material_properties)
+        : _mp(material_properties)
+    {
+    }
+
+    bool computeConstitutiveRelation(
+        double const t,
+        ProcessLib::SpatialPosition const& x,
+        double const dt,
+        KelvinVector const& eps_prev,
+        KelvinVector const& eps,
+        KelvinVector const& sigma_prev,
+        KelvinVector& sigma,
+        KelvinMatrix& C,
+        typename MechanicsBase<DisplacementDim>::MaterialStateVariables&
+            material_state_variables) override;
+
+private:
+    MaterialProperties _mp;
+};
+
+}  // namespace Solids
+}  // namespace MaterialLib
+#include "Ehlers-impl.h"
+
+#endif  // MATERIALLIB_SOLIDMODELS_EHLERS_H_
diff --git a/ProcessLib/SmallDeformation/CreateSmallDeformationProcess.cpp b/ProcessLib/SmallDeformation/CreateSmallDeformationProcess.cpp
index c2a74a9cb5ad11cc651f147fa9243f578123e622..4f8a1b4633d4fd31c1082eb35b4d1641c0dd07d4 100644
--- a/ProcessLib/SmallDeformation/CreateSmallDeformationProcess.cpp
+++ b/ProcessLib/SmallDeformation/CreateSmallDeformationProcess.cpp
@@ -13,6 +13,7 @@
 
 #include "MaterialLib/SolidModels/CreateLinearElasticIsotropic.h"
 #include "MaterialLib/SolidModels/CreateLubby2.h"
+#include "MaterialLib/SolidModels/CreateEhlers.h"
 #include "ProcessLib/Utils/ParseSecondaryVariables.h"
 
 #include "SmallDeformationProcess.h"
@@ -74,7 +75,12 @@ createSmallDeformationProcess(
 
     std::unique_ptr<MaterialLib::Solids::MechanicsBase<DisplacementDim>>
         material = nullptr;
-    if (type == "LinearElasticIsotropic")
+    if (type == "Ehlers")
+    {
+        material = MaterialLib::Solids::createEhlers<DisplacementDim>(
+            parameters, constitutive_relation_config);
+    }
+    else if (type == "LinearElasticIsotropic")
     {
         material =
             MaterialLib::Solids::createLinearElasticIsotropic<DisplacementDim>(
diff --git a/ProcessLib/SmallDeformation/SmallDeformationFEM.h b/ProcessLib/SmallDeformation/SmallDeformationFEM.h
index 18d3593d1ca889532428210f4cd18254746539dd..ace169d9adf2e6efaccf5e38e299cb0150cffb7f 100644
--- a/ProcessLib/SmallDeformation/SmallDeformationFEM.h
+++ b/ProcessLib/SmallDeformation/SmallDeformationFEM.h
@@ -112,6 +112,24 @@ struct SmallDeformationLocalAssemblerInterface
 
     virtual std::vector<double> const& getIntPtSigmaYZ(
         std::vector<double>& cache) const = 0;
+
+    virtual std::vector<double> const& getIntPtEpsilonXX(
+        std::vector<double>& cache) const = 0;
+
+    virtual std::vector<double> const& getIntPtEpsilonYY(
+        std::vector<double>& cache) const = 0;
+
+    virtual std::vector<double> const& getIntPtEpsilonZZ(
+        std::vector<double>& cache) const = 0;
+
+    virtual std::vector<double> const& getIntPtEpsilonXY(
+        std::vector<double>& cache) const = 0;
+
+    virtual std::vector<double> const& getIntPtEpsilonXZ(
+        std::vector<double>& cache) const = 0;
+
+    virtual std::vector<double> const& getIntPtEpsilonYZ(
+        std::vector<double>& cache) const = 0;
 };
 
 template <typename ShapeFunction, typename IntegrationMethod,
@@ -318,6 +336,44 @@ public:
         return getIntPtSigma(cache, 5);
     }
 
+    std::vector<double> const& getIntPtEpsilonXX(
+        std::vector<double>& cache) const override
+    {
+        return getIntPtEpsilon(cache, 0);
+    }
+
+    std::vector<double> const& getIntPtEpsilonYY(
+        std::vector<double>& cache) const override
+    {
+        return getIntPtEpsilon(cache, 1);
+    }
+
+    std::vector<double> const& getIntPtEpsilonZZ(
+        std::vector<double>& cache) const override
+    {
+        return getIntPtEpsilon(cache, 2);
+    }
+
+    std::vector<double> const& getIntPtEpsilonXY(
+        std::vector<double>& cache) const override
+    {
+        return getIntPtEpsilon(cache, 3);
+    }
+
+    std::vector<double> const& getIntPtEpsilonXZ(
+        std::vector<double>& cache) const override
+    {
+        assert(DisplacementDim == 3);
+        return getIntPtEpsilon(cache, 4);
+    }
+
+    std::vector<double> const& getIntPtEpsilonYZ(
+        std::vector<double>& cache) const override
+    {
+        assert(DisplacementDim == 3);
+        return getIntPtEpsilon(cache, 5);
+    }
+
 private:
     std::vector<double> const& getIntPtSigma(std::vector<double>& cache,
                                              std::size_t const component) const
@@ -335,6 +391,20 @@ private:
         return cache;
     }
 
+    std::vector<double> const& getIntPtEpsilon(
+        std::vector<double>& cache, std::size_t const component) const
+    {
+        cache.clear();
+        cache.reserve(_ip_data.size());
+
+        for (auto const& ip_data : _ip_data) {
+            cache.push_back(ip_data._eps[component]);
+        }
+
+        return cache;
+    }
+
+
     SmallDeformationProcessData<DisplacementDim>& _process_data;
 
     std::vector<IntegrationPointData<BMatricesType, DisplacementDim>> _ip_data;
diff --git a/ProcessLib/SmallDeformation/SmallDeformationProcess.h b/ProcessLib/SmallDeformation/SmallDeformationProcess.h
index d21af9415860c629c5da91af1cc9c9ca85d4224d..59ebb0b628eded912339440a5d02e79aaa011b3a 100644
--- a/ProcessLib/SmallDeformation/SmallDeformationProcess.h
+++ b/ProcessLib/SmallDeformation/SmallDeformationProcess.h
@@ -116,6 +116,30 @@ private:
                     getExtrapolator(), _local_assemblers,
                     &SmallDeformationLocalAssemblerInterface::getIntPtSigmaYZ));
         }
+
+        Base::_secondary_variables.addSecondaryVariable(
+            "epsilon_xx", 1,
+            makeExtrapolator(
+                getExtrapolator(), _local_assemblers,
+                &SmallDeformationLocalAssemblerInterface::getIntPtEpsilonXX));
+
+        Base::_secondary_variables.addSecondaryVariable(
+            "epsilon_yy", 1,
+            makeExtrapolator(
+                getExtrapolator(), _local_assemblers,
+                &SmallDeformationLocalAssemblerInterface::getIntPtEpsilonYY));
+
+        Base::_secondary_variables.addSecondaryVariable(
+            "epsilon_zz", 1,
+            makeExtrapolator(
+                getExtrapolator(), _local_assemblers,
+                &SmallDeformationLocalAssemblerInterface::getIntPtEpsilonZZ));
+
+        Base::_secondary_variables.addSecondaryVariable(
+            "epsilon_xy", 1,
+            makeExtrapolator(
+                getExtrapolator(), _local_assemblers,
+                &SmallDeformationLocalAssemblerInterface::getIntPtEpsilonXY));
     }
 
     void assembleConcreteProcess(const double t, GlobalVector const& x,
diff --git a/Tests/Data b/Tests/Data
index 6ffd7ec67ebd30d3c83754cf3127fd885e255528..74db8c483fcdd3231b40215a78a9357865a80f5e 160000
--- a/Tests/Data
+++ b/Tests/Data
@@ -1 +1 @@
-Subproject commit 6ffd7ec67ebd30d3c83754cf3127fd885e255528
+Subproject commit 74db8c483fcdd3231b40215a78a9357865a80f5e