diff --git a/MaterialLib/MPL/PropertyType.h b/MaterialLib/MPL/PropertyType.h
index 98e1d52fc1907d9700e9269c25047cad19589b0a..b975b8fc0dbbfe7367624f780b20ef875c5916b9 100644
--- a/MaterialLib/MPL/PropertyType.h
+++ b/MaterialLib/MPL/PropertyType.h
@@ -77,6 +77,8 @@ enum PropertyType : int
     /// specify retardation factor used in component transport process.
     retardation_factor,
     saturation,
+    /// capillary pressure saturation relationship for microstructure.
+    saturation_micro,
     specific_heat_capacity,
     storage,
     swelling_stress_rate,
@@ -257,6 +259,10 @@ inline PropertyType convertStringToProperty(std::string const& inString)
     {
         return PropertyType::saturation;
     }
+    if (boost::iequals(inString, "saturation_micro"))
+    {
+        return PropertyType::saturation_micro;
+    }
     if (boost::iequals(inString, "specific_heat_capacity"))
     {
         return PropertyType::specific_heat_capacity;
@@ -358,6 +364,7 @@ static const std::array<std::string, PropertyType::number_of_properties>
                              "residual_liquid_saturation",
                              "retardation_factor",
                              "saturation",
+                             "saturation_micro",
                              "specific_heat_capacity",
                              "storage",
                              "swelling_stress_rate",
diff --git a/ProcessLib/RichardsMechanics/ComputeMicroPorosity.h b/ProcessLib/RichardsMechanics/ComputeMicroPorosity.h
new file mode 100644
index 0000000000000000000000000000000000000000..c62d9f10dc7e4f896af0862d3f28a4892be1be28
--- /dev/null
+++ b/ProcessLib/RichardsMechanics/ComputeMicroPorosity.h
@@ -0,0 +1,211 @@
+/**
+ * \file
+ * \copyright
+ * Copyright (c) 2012-2021, OpenGeoSys Community (http://www.opengeosys.org)
+ *            Distributed under a Modified BSD License.
+ *              See accompanying file LICENSE.txt or
+ *              http://www.opengeosys.org/project/license
+ */
+
+#pragma once
+
+#include <cassert>
+#include <ostream>
+
+#include "MaterialLib/MPL/Medium.h"
+#include "MaterialLib/MPL/Utils/FormEigenTensor.h"
+#include "MathLib/KelvinVector.h"
+#include "NumLib/NewtonRaphson.h"
+
+namespace ProcessLib::RichardsMechanics
+{
+template <int DisplacementDim>
+struct MicroPorosityStateSpace
+{
+    double phi_m;
+    double e_sw;
+    double p_L_m;
+    MathLib::KelvinVector::KelvinVectorType<DisplacementDim> sigma_sw;
+
+    MicroPorosityStateSpace& operator += (MicroPorosityStateSpace const& state)
+    {
+        phi_m += state.phi_m;
+        e_sw += state.e_sw;
+        p_L_m += state.p_L_m;
+        sigma_sw += state.sigma_sw;
+        return *this;
+    }
+
+    EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
+};
+
+template <int DisplacementDim>
+std::ostream& operator<<(std::ostream& os,
+                         MicroPorosityStateSpace<DisplacementDim> const& state)
+{
+    return os << "phi_m: " << state.phi_m << ", "
+              << "e_sw: " << state.e_sw << ", "
+              << "p_L_m: " << state.p_L_m << ", "
+              << "sigma_sw: " << state.sigma_sw.transpose();
+}
+
+template <int DisplacementDim>
+MicroPorosityStateSpace<DisplacementDim> computeMicroPorosity(
+    MathLib::KelvinVector::KelvinVectorType<DisplacementDim> const&
+        I_2_C_el_inverse,
+    double const rho_LR_m,  // for simplification equal to rho_LR_M
+    double const mu_LR, double const alpha_bar, double const alpha_B,
+    double const phi_M, double const p_L, double const p_L_m_prev,
+    MaterialPropertyLib::VariableArray const& /*variables_prev*/,
+    double const S_L_m_prev, double const phi_m_prev,
+    ParameterLib::SpatialPosition const pos, double const t, double const dt,
+    MaterialPropertyLib::Property const& saturation_micro,
+    MaterialPropertyLib::Property const& swelling_stress_rate,
+    NumLib::NewtonRaphsonSolverParameters const& nonlinear_solver_parameters)
+
+{
+    namespace MPL = MaterialPropertyLib;
+    static constexpr int kelvin_vector_size =
+        MathLib::KelvinVector::KelvinVectorDimensions<DisplacementDim>::value;
+    // phi_m, e_sw, p_L_m, sigma_sw
+    static constexpr int nls_size = 1 + 1 + 1 + kelvin_vector_size;
+
+    static constexpr int i_phi_m = 0;
+    static constexpr int i_e_sw = 1;
+    static constexpr int i_p_L_m = 2;
+    static constexpr int i_sigma_sw = 3;
+
+    using ResidualVectorType = Eigen::Matrix<double, nls_size, 1>;
+    using JacobianMatrix =
+        Eigen::Matrix<double, nls_size, nls_size, Eigen::RowMajor>;
+
+    Eigen::FullPivLU<Eigen::Matrix<double, nls_size, nls_size, Eigen::RowMajor>>
+        linear_solver;
+
+    JacobianMatrix jacobian;
+
+    // Agglomerated solution vector construction.
+    ResidualVectorType solution = ResidualVectorType::Zero();
+
+    if (p_L >= 0 && p_L_m_prev >= 0)
+    {
+        return {solution[i_phi_m], solution[i_e_sw], solution[i_p_L_m],
+                solution.template segment<kelvin_vector_size>(i_sigma_sw)};
+    }
+
+    auto const update_residual = [&](ResidualVectorType& residual) {
+        double const delta_phi_m = solution[i_phi_m];
+        double const delta_e_sw = solution[i_e_sw];
+        auto const& delta_sigma_sw =
+            solution.template segment<kelvin_vector_size>(i_sigma_sw);
+        double const delta_p_L_m = solution[i_p_L_m];
+
+        double const phi_m = phi_m_prev + delta_phi_m;
+        double const p_L_m = p_L_m_prev + delta_p_L_m;
+        MPL::VariableArray variables_prev;
+        variables_prev[static_cast<int>(MPL::Variable::capillary_pressure)] =
+            -p_L_m_prev;
+        variables_prev[static_cast<int>(MPL::Variable::liquid_saturation)] =
+            S_L_m_prev;
+
+        MPL::VariableArray variables;
+        variables[static_cast<int>(MPL::Variable::capillary_pressure)] = -p_L_m;
+
+        double const S_L_m =
+            saturation_micro.template value<double>(variables, pos, t, dt);
+        variables[static_cast<int>(MPL::Variable::liquid_saturation)] = S_L_m;
+        double const delta_S_L_m = S_L_m - S_L_m_prev;
+
+
+        auto const sigma_sw_dot =
+            MathLib::KelvinVector::tensorToKelvin<DisplacementDim>(
+                swelling_stress_rate.template value<Eigen::Matrix3d>(
+                    variables, variables_prev, pos, t, dt));
+
+        residual[i_phi_m] = delta_phi_m - (alpha_B - phi_M) * delta_e_sw;
+        residual[i_e_sw] = delta_e_sw + I_2_C_el_inverse.dot(delta_sigma_sw);
+        residual.template segment<kelvin_vector_size>(i_sigma_sw).noalias() =
+            delta_sigma_sw - sigma_sw_dot * dt;
+
+        residual[i_p_L_m] =
+            rho_LR_m *
+                (phi_m * delta_S_L_m - (alpha_B - phi_M) * S_L_m * delta_e_sw) +
+            phi_m * S_L_m * rho_LR_m * delta_e_sw -
+            alpha_bar / mu_LR * (p_L - p_L_m) * dt;
+    };
+
+    auto const update_jacobian = [&](JacobianMatrix& jacobian) {
+        jacobian = JacobianMatrix::Identity();
+
+        double const delta_phi_m = solution[i_phi_m];
+        double const delta_e_sw = solution[i_e_sw];
+        double const delta_p_L_m = solution[i_p_L_m];
+
+        double const phi_m = phi_m_prev + delta_phi_m;
+        double const p_L_m = p_L_m_prev + delta_p_L_m;
+        MPL::VariableArray variables_prev;
+        variables_prev[static_cast<int>(MPL::Variable::capillary_pressure)] =
+            -p_L_m_prev;
+        MPL::VariableArray variables;
+        variables[static_cast<int>(MPL::Variable::capillary_pressure)] = -p_L_m;
+
+        double const S_L_m =
+            saturation_micro.template value<double>(variables, pos, t, dt);
+        variables_prev[static_cast<int>(MPL::Variable::liquid_saturation)] =
+            S_L_m_prev;
+        variables[static_cast<int>(MPL::Variable::liquid_saturation)] = S_L_m;
+        double const delta_S_L_m = S_L_m - S_L_m_prev;
+
+        double const dS_L_m_dp_cap_m = saturation_micro.template dValue<double>(
+            variables, MPL::Variable::capillary_pressure, pos, t, dt);
+        auto const dsigma_sw_dS_L_m =
+            MathLib::KelvinVector::tensorToKelvin<DisplacementDim>(
+                swelling_stress_rate.template dValue<Eigen::Matrix3d>(
+                    variables, variables_prev, MPL::Variable::liquid_saturation,
+                    pos, t, dt));
+
+        jacobian(i_phi_m, i_e_sw) = -(alpha_B - phi_M);
+
+        jacobian.template block<1, kelvin_vector_size>(i_e_sw, i_sigma_sw) =
+            I_2_C_el_inverse.transpose();
+
+        jacobian.template block<kelvin_vector_size, 1>(
+            i_sigma_sw, i_p_L_m) = -dsigma_sw_dS_L_m * dS_L_m_dp_cap_m;
+
+
+        jacobian(i_p_L_m, i_phi_m) =
+            rho_LR_m * (delta_S_L_m + phi_m * delta_e_sw);
+
+        jacobian(i_p_L_m, i_e_sw) = -rho_LR_m * S_L_m * (alpha_B - phi_M - phi_m);
+
+        jacobian(i_p_L_m, i_p_L_m) =
+            alpha_bar / mu_LR * dt -
+            rho_LR_m * (phi_m - (alpha_B - phi_M - phi_m) * delta_e_sw) *
+                dS_L_m_dp_cap_m;
+    };
+
+    auto const update_solution = [&](ResidualVectorType const& increment) {
+        solution += increment;
+    };
+
+    auto newton_solver =
+        NumLib::NewtonRaphson<decltype(linear_solver), JacobianMatrix,
+                              decltype(update_jacobian), ResidualVectorType,
+                              decltype(update_residual),
+                              decltype(update_solution)>(
+            linear_solver, update_jacobian, update_residual, update_solution,
+            nonlinear_solver_parameters);
+
+    auto const success_iterations = newton_solver.solve(jacobian);
+
+    if (!success_iterations)
+    {
+        OGS_FATAL(
+            "Could not find solution for local double structure nonlinear "
+            "problem.");
+    }
+
+    return {solution[i_phi_m], solution[i_e_sw], solution[i_p_L_m],
+            solution.template segment<kelvin_vector_size>(i_sigma_sw)};
+}
+}  // namespace ProcessLib::RichardsMechanics
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index 9348e24e705fc9bb1f952a6a5adf4fe9f329be4e..23f0e8f55266f29d961bbcea54714c3bae32888b 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -43,6 +43,9 @@ endif()
 if(OGS_BUILD_PROCESS_LIE)
     append_source_files(TEST_SOURCES ProcessLib/LIE)
 endif()
+if(OGS_BUILD_PROCESS_RichardsMechanics)
+    append_source_files(TEST_SOURCES ProcessLib/RichardsMechanics)
+endif()
 
 if(OGS_USE_PETSC)
     list(REMOVE_ITEM TEST_SOURCES NumLib/TestSerialLinearSolver.cpp)
diff --git a/Tests/ProcessLib/RichardsMechanics/MicroporosityComputation.cpp b/Tests/ProcessLib/RichardsMechanics/MicroporosityComputation.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..611e21b08c55e65878288bb27e19e66ba2e183d7
--- /dev/null
+++ b/Tests/ProcessLib/RichardsMechanics/MicroporosityComputation.cpp
@@ -0,0 +1,262 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2021, 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 <gtest/gtest.h>
+
+#include <Eigen/Eigen>
+#include <cmath>
+#include <memory>
+
+#include "MaterialLib/MPL/Properties/CapillaryPressureSaturation/SaturationVanGenuchten.h"
+#include "MaterialLib/MPL/Properties/SaturationDependentSwelling.h"
+#include "MaterialLib/MPL/Property.h"
+#include "MaterialLib/SolidModels/LinearElasticIsotropic.h"
+#include "MaterialLib/SolidModels/MechanicsBase.h"
+#include "MathLib/InterpolationAlgorithms/PiecewiseLinearInterpolation.h"
+#include "ParameterLib/ConstantParameter.h"
+#include "ProcessLib/RichardsMechanics/ComputeMicroPorosity.h"
+
+using namespace ProcessLib::RichardsMechanics;
+namespace MPL = MaterialPropertyLib;
+
+namespace
+{
+template <int DisplacementDim>
+MathLib::KelvinVector::KelvinMatrixType<DisplacementDim>
+computeElasticTangentStiffness(
+    MaterialLib::Solids::MechanicsBase<DisplacementDim> const& solid_material,
+    double const t, ParameterLib::SpatialPosition const& x_position,
+    double const dt, double const temperature)
+{
+    MPL::VariableArray variable_array;
+    MPL::VariableArray variable_array_prev;
+
+    auto const null_state = solid_material.createMaterialStateVariables();
+
+    using KV = MathLib::KelvinVector::KelvinVectorType<DisplacementDim>;
+
+    variable_array[static_cast<int>(MPL::Variable::stress)].emplace<KV>(
+        KV::Zero());
+    variable_array[static_cast<int>(MPL::Variable::mechanical_strain)]
+        .emplace<KV>(KV::Zero());
+    variable_array[static_cast<int>(MPL::Variable::temperature)]
+        .emplace<double>(temperature);
+
+    variable_array_prev[static_cast<int>(MPL::Variable::stress)].emplace<KV>(
+        KV::Zero());
+    variable_array_prev[static_cast<int>(MPL::Variable::mechanical_strain)]
+        .emplace<KV>(KV::Zero());
+    variable_array_prev[static_cast<int>(MPL::Variable::temperature)]
+        .emplace<double>(temperature);
+
+    auto&& solution = solid_material.integrateStress(
+        variable_array_prev, variable_array, t, x_position, dt, *null_state);
+
+    if (!solution)
+    {
+        OGS_FATAL("Computation of elastic tangent stiffness failed.");
+    }
+
+    MathLib::KelvinVector::KelvinMatrixType<DisplacementDim> C =
+        std::move(std::get<2>(*solution));
+
+    return C;
+}
+
+}  // namespace
+
+TEST(RichardsMechanics, computeMicroPorosity)
+{
+    static constexpr auto eps = 2e-14;
+    constexpr int DisplacementDim = 2;
+
+    static const NumLib::NewtonRaphsonSolverParameters
+        nonlinear_solver_parameters{1000, 1e-8, 1e-15};
+
+    //
+    // Create properties.
+    //
+
+    // Saturation
+    constexpr double residual_liquid_saturation = 0;
+    constexpr double residual_gas_saturation = 0;
+    constexpr double exponent = 0.4;
+    constexpr double p_b = 300e6;
+    MPL::Property const& saturation_micro = MPL::SaturationVanGenuchten{
+        "saturation_micro", residual_liquid_saturation, residual_gas_saturation,
+        exponent, p_b};
+
+    // Swelling
+    constexpr std::array swelling_pressures{1e7, 1e7, 1e7};
+    constexpr std::array swelling_exponents{1.2, 1.2, 1.2};
+    constexpr double lower_saturation_limit = 0;
+    constexpr double upper_saturation_limit = 1;
+    MPL::Property const& swelling_stress_rate =
+        MPL::SaturationDependentSwelling{
+            "swelling_stress_rate", swelling_pressures,     swelling_exponents,
+            lower_saturation_limit, upper_saturation_limit, nullptr};
+
+    // LinearElastic
+    ParameterLib::ConstantParameter<double> E{"YoungsModulus", 150e6};
+    ParameterLib::ConstantParameter<double> nu{"PoissonsRatio", 0.25};
+    auto const solid_material =
+        MaterialLib::Solids::LinearElasticIsotropic<DisplacementDim>{{E, nu}};
+
+    //
+    // Populate constants
+    //
+    ParameterLib::SpatialPosition const pos;
+    constexpr double t0 = 0;
+    constexpr double t_max = 200e3;
+    double const dt = 100;
+    double const T = 293.15;
+
+    auto const& identity2 = MathLib::KelvinVector::Invariants<
+        MathLib::KelvinVector::KelvinVectorDimensions<DisplacementDim>::value>::
+        identity2;
+    auto const C_el =
+        computeElasticTangentStiffness(solid_material, t0, pos, dt, T);
+    MathLib::KelvinVector::KelvinVectorType<DisplacementDim> const&
+        I_2_C_el_inverse = identity2.transpose() * C_el.inverse();
+    double const rho_LR_m = 1e3;
+    double const mu_LR = 1e-3;
+    double const alpha_bar = 1e-14;
+    double const alpha_B = 1;
+    double const phi_M = 0.45;
+
+    auto saturation = [&](double const p_L) {
+        MPL::VariableArray v;
+        v[static_cast<int>(MPL::Variable::capillary_pressure)] = -p_L;
+        return saturation_micro.template value<double>(v, pos, t0, dt);
+    };
+
+    //
+    // Drive the computation by macro pressure. Saturation, steady, and
+    // desaturation.
+    //
+
+    // The time coordinates are simultaneously also the test points.
+    std::vector const t_coords{
+        // saturation is quick to rich maximum,
+        0 * t_max, 0.05 * t_max, 0.2 * t_max, 0.3 * t_max,
+        // desaturation takes much longer.
+        0.35 * t_max, 0.5 * t_max, t_max};
+    std::vector const pressure_M_values{// saturation
+                                        -200e6, -200e6, 0., 0.,
+                                        // desaturation
+                                        0., -200e6, -200e6};
+    MathLib::PiecewiseLinearInterpolation const pressure_M{t_coords,
+                                                           pressure_M_values};
+
+    //
+    // Initial state
+    //
+
+    MicroPorosityStateSpace<DisplacementDim> state_prev = {
+        0.45 - 0.25,  // phi_m
+        0,            // e_sw
+        -200e6,       // p_L_m
+        {0, 0, 0, 0}  // sigma_sw
+    };
+
+    auto state = state_prev;
+
+    std::vector<MicroPorosityStateSpace<DisplacementDim>> results;
+    for (double t = t0 + dt; t <= t_max; t += dt)
+    {
+        double const p_L = pressure_M.getValue(t);
+        double const S_L_m_prev = saturation(state_prev.p_L_m);
+        //auto const [delta_phi_m, delta_e_sw, delta_sigma_sw, delta_p_L_m] =
+        auto const state_increment = computeMicroPorosity<DisplacementDim>(
+            I_2_C_el_inverse,
+            rho_LR_m,  // for simplification equal to rho_LR_M
+            mu_LR, alpha_bar, alpha_B, phi_M, p_L, state_prev.p_L_m,
+            MaterialPropertyLib::VariableArray{}, S_L_m_prev, state_prev.phi_m,
+            pos, t, dt, saturation_micro, swelling_stress_rate,
+            nonlinear_solver_parameters);
+
+        // push back state
+        state_prev = state;
+
+        // update
+        state += state_increment;
+
+        if (std::find_if(begin(t_coords), end(t_coords), [&](auto const value) {
+                return std::abs(t - value) < eps;
+            }) != end(t_coords))
+        {
+            results.push_back(state);
+            /* Keep for possible result updates
+            std::cout << std::setprecision(17)
+                      << "MicroPorosityStateSpace<DisplacementDim>{ "
+                      << state.phi_m << ", " << state.e_sw << ", "
+                      << state.p_L_m << ", {" << state.sigma_sw[0] << ", "
+                      << state.sigma_sw[1] << ", " << state.sigma_sw[2] << ", "
+                      << state.sigma_sw[3] << " }}\n";
+            */
+        }
+    }
+
+    //
+    // Expected results
+    //
+    std::vector expected_results = {
+        MicroPorosityStateSpace<DisplacementDim>{
+            0.20000000000000001, 0, -200000000, {0, 0, 0, 0}},
+        MicroPorosityStateSpace<DisplacementDim>{
+            0.20678794335787565,
+            0.012341715196137344,
+            -87023875.75987792,
+            {-1234171.5196137347, -1234171.5196137347, -1234171.5196137347, 0}},
+        MicroPorosityStateSpace<DisplacementDim>{
+            0.20984996711734061,
+            0.017909031122437243,
+            -4693546.2948520193,
+            {-1790903.1122437208, -1790903.1122437208, -1790903.1122437208, 0}},
+        MicroPorosityStateSpace<DisplacementDim>{
+            0.20987701765630837,
+            0.017958213920560351,
+            23234.984113500199,
+            {-1795821.3920560319, -1795821.3920560319, -1795821.3920560319, 0}},
+        MicroPorosityStateSpace<DisplacementDim>{
+            0.20475472126658667,
+            0.0086449477574298151,
+            -123250534.84079438,
+            {-864494.77574297995, -864494.77574297995, -864494.77574297995, 0}},
+        MicroPorosityStateSpace<DisplacementDim>{
+            0.20005534659406693,
+            0.0001006301710302317,
+            -199863745.41244847,
+            {-10063.017103021764, -10063.017103021764, -10063.017103021764,
+             0}}};
+
+    auto eps_equal = [](double const a, double const b) {
+        return std::abs(a - b) < eps;
+    };
+
+    auto const [mismatch_it_expected, mismatch_it_results] = std::mismatch(
+        begin(expected_results), end(expected_results), begin(results),
+        [&](auto const& a, auto const& b) {
+            EXPECT_TRUE(eps_equal(a.phi_m, b.phi_m)) << "with eps = " << eps;
+            EXPECT_TRUE(eps_equal(a.e_sw, b.e_sw)) << "with eps = " << eps;
+            EXPECT_TRUE(eps_equal(a.p_L_m / 1e9, b.p_L_m / 1e9))
+                << "with eps = " << eps;
+            EXPECT_TRUE((a.sigma_sw - b.sigma_sw).norm() / 1e9 < eps)
+                << "with eps = " << eps;
+            // Divide pressures and stresses by GPa to get approxmately
+            // O(1) numbers.
+            return eps_equal(a.phi_m, b.phi_m) && eps_equal(a.e_sw, b.e_sw) &&
+                   eps_equal(a.p_L_m / 1e9, b.p_L_m / 1e9) &&
+                   (a.sigma_sw - b.sigma_sw).norm() / 1e9 < eps;
+        });
+
+    ASSERT_TRUE(mismatch_it_expected == end(expected_results))
+        << "Mismatch for expected result\n\t" << *mismatch_it_expected
+        << "\n\tfound. Corresponding result is\n\t" << *mismatch_it_results;
+    ASSERT_TRUE(mismatch_it_results == end(results));
+}