diff --git a/ProcessLib/ThermoMechanics/ThermoMechanicsFEM-impl.h b/ProcessLib/ThermoMechanics/ThermoMechanicsFEM-impl.h
index 3b2ead2e11dacf464353281777bdb0feee977265..18456690f5dcd7fc4dcf2730a0ec9474fa428878 100644
--- a/ProcessLib/ThermoMechanics/ThermoMechanicsFEM-impl.h
+++ b/ProcessLib/ThermoMechanics/ThermoMechanicsFEM-impl.h
@@ -19,6 +19,310 @@ namespace ProcessLib
 {
 namespace ThermoMechanics
 {
+template <typename ShapeFunction, typename IntegrationMethod,
+          int DisplacementDim>
+ThermoMechanicsLocalAssembler<ShapeFunction, IntegrationMethod,
+                              DisplacementDim>::
+    ThermoMechanicsLocalAssembler(
+        MeshLib::Element const& e,
+        std::size_t const /*local_matrix_size*/,
+        bool const is_axially_symmetric,
+        unsigned const integration_order,
+        ThermoMechanicsProcessData<DisplacementDim>& process_data)
+    : _process_data(process_data),
+      _integration_method(integration_order),
+      _element(e),
+      _is_axially_symmetric(is_axially_symmetric)
+{
+    unsigned const n_integration_points =
+        _integration_method.getNumberOfPoints();
+
+    _ip_data.reserve(n_integration_points);
+    _secondary_data.N.resize(n_integration_points);
+
+    auto const shape_matrices =
+        initShapeMatrices<ShapeFunction, ShapeMatricesType, IntegrationMethod,
+                          DisplacementDim>(e, is_axially_symmetric,
+                                           _integration_method);
+
+    auto& solid_material = MaterialLib::Solids::selectSolidConstitutiveRelation(
+        _process_data.solid_materials, _process_data.material_ids, e.getID());
+
+    for (unsigned ip = 0; ip < n_integration_points; ip++)
+    {
+        _ip_data.emplace_back(solid_material);
+        auto& ip_data = _ip_data[ip];
+        ip_data.integration_weight =
+            _integration_method.getWeightedPoint(ip).getWeight() *
+            shape_matrices[ip].integralMeasure * shape_matrices[ip].detJ;
+
+        static const int kelvin_vector_size =
+            MathLib::KelvinVector::KelvinVectorDimensions<
+                DisplacementDim>::value;
+        ip_data.sigma.setZero(kelvin_vector_size);
+        ip_data.sigma_prev.setZero(kelvin_vector_size);
+        ip_data.eps.setZero(kelvin_vector_size);
+        ip_data.eps_prev.setZero(kelvin_vector_size);
+        ip_data.eps_m.setZero(kelvin_vector_size);
+        ip_data.eps_m_prev.setZero(kelvin_vector_size);
+        ParameterLib::SpatialPosition x_position;
+        x_position.setElementID(_element.getID());
+        ip_data.solid_density =
+            _process_data.reference_solid_density(0, x_position)[0];
+        ip_data.solid_density_prev = ip_data.solid_density;
+        ip_data.N = shape_matrices[ip].N;
+        ip_data.dNdx = shape_matrices[ip].dNdx;
+
+        _secondary_data.N[ip] = shape_matrices[ip].N;
+
+        // Initialize current time step values
+        if (_process_data.nonequilibrium_stress)
+        {
+            // Computation of non-equilibrium stress.
+            x_position.setCoordinates(MathLib::Point3d(
+                interpolateCoordinates<ShapeFunction, ShapeMatricesType>(
+                    e, ip_data.N)));
+            std::vector<double> sigma_neq_data = (*_process_data
+                                                       .nonequilibrium_stress)(
+                std::numeric_limits<double>::quiet_NaN() /* time independent */,
+                x_position);
+            ip_data.sigma_neq =
+                Eigen::Map<typename BMatricesType::KelvinVectorType>(
+                    sigma_neq_data.data(),
+                    MathLib::KelvinVector::KelvinVectorDimensions<
+                        DisplacementDim>::value,
+                    1);
+        }
+        // Initialization from non-equilibrium sigma, which is zero by
+        // default, or is set to some value.
+        ip_data.sigma = ip_data.sigma_neq;
+    }
+}
+
+template <typename ShapeFunction, typename IntegrationMethod,
+          int DisplacementDim>
+std::size_t ThermoMechanicsLocalAssembler<
+    ShapeFunction, IntegrationMethod,
+    DisplacementDim>::setIPDataInitialConditions(std::string const& name,
+                                                 double const* values,
+                                                 int const integration_order)
+{
+    if (integration_order !=
+        static_cast<int>(_integration_method.getIntegrationOrder()))
+    {
+        OGS_FATAL(
+            "Setting integration point initial conditions; The integration "
+            "order of the local assembler for element %d is different from "
+            "the integration order in the initial condition.",
+            _element.getID());
+    }
+
+    if (name == "sigma_ip")
+    {
+        return setSigma(values);
+    }
+    if (name == "epsilon_ip")
+    {
+        return setEpsilon(values);
+    }
+    if (name == "epsilon_m_ip")
+    {
+        return setEpsilonMechanical(values);
+    }
+
+    return 0;
+}
+
+template <typename ShapeFunction, typename IntegrationMethod,
+          int DisplacementDim>
+void ThermoMechanicsLocalAssembler<ShapeFunction, IntegrationMethod,
+                                   DisplacementDim>::
+    assembleWithJacobian(double const t, std::vector<double> const& local_x,
+                         std::vector<double> const& local_xdot,
+                         const double /*dxdot_dx*/, const double /*dx_dx*/,
+                         std::vector<double>& /*local_M_data*/,
+                         std::vector<double>& /*local_K_data*/,
+                         std::vector<double>& local_rhs_data,
+                         std::vector<double>& local_Jac_data)
+{
+    auto const local_matrix_size = local_x.size();
+    assert(local_matrix_size == temperature_size + displacement_size);
+
+    auto T = Eigen::Map<typename ShapeMatricesType::template VectorType<
+        temperature_size> const>(local_x.data() + temperature_index,
+                                 temperature_size);
+
+    auto u = Eigen::Map<typename ShapeMatricesType::template VectorType<
+        displacement_size> const>(local_x.data() + displacement_index,
+                                  displacement_size);
+
+    auto T_dot = Eigen::Map<typename ShapeMatricesType::template VectorType<
+        temperature_size> const>(local_xdot.data() + temperature_index,
+                                 temperature_size);
+
+    auto local_Jac = MathLib::createZeroedMatrix<JacobianMatrix>(
+        local_Jac_data, local_matrix_size, local_matrix_size);
+
+    auto local_rhs = MathLib::createZeroedVector<RhsVector>(local_rhs_data,
+                                                            local_matrix_size);
+
+    typename ShapeMatricesType::template MatrixType<displacement_size,
+                                                    temperature_size>
+        KuT;
+    KuT.setZero(displacement_size, temperature_size);
+
+    typename ShapeMatricesType::NodalMatrixType KTT;
+    KTT.setZero(temperature_size, temperature_size);
+
+    typename ShapeMatricesType::NodalMatrixType DTT;
+    DTT.setZero(temperature_size, temperature_size);
+
+    double const& dt = _process_data.dt;
+
+    unsigned const n_integration_points =
+        _integration_method.getNumberOfPoints();
+
+    ParameterLib::SpatialPosition x_position;
+    x_position.setElementID(_element.getID());
+
+    for (unsigned ip = 0; ip < n_integration_points; ip++)
+    {
+        x_position.setIntegrationPoint(ip);
+        auto const& w = _ip_data[ip].integration_weight;
+        auto const& N = _ip_data[ip].N;
+        auto const& dNdx = _ip_data[ip].dNdx;
+
+        auto const x_coord =
+            interpolateXCoordinate<ShapeFunction, ShapeMatricesType>(_element,
+                                                                     N);
+        auto const& B =
+            LinearBMatrix::computeBMatrix<DisplacementDim,
+                                          ShapeFunction::NPOINTS,
+                                          typename BMatricesType::BMatrixType>(
+                dNdx, N, x_coord, _is_axially_symmetric);
+
+        auto& sigma = _ip_data[ip].sigma;
+        auto const& sigma_prev = _ip_data[ip].sigma_prev;
+        auto const& sigma_neq = _ip_data[ip].sigma_neq;
+
+        auto& eps = _ip_data[ip].eps;
+        auto const& eps_prev = _ip_data[ip].eps_prev;
+
+        auto& eps_m = _ip_data[ip].eps_m;
+        auto const& eps_m_prev = _ip_data[ip].eps_m_prev;
+
+        auto& state = _ip_data[ip].material_state_variables;
+
+        double const dT = N.dot(T_dot) * dt;
+        // calculate thermally induced strain
+        // assume isotropic thermal expansion
+        auto const alpha = _process_data.linear_thermal_expansion_coefficient(
+            t, x_position)[0];
+        double const linear_thermal_strain_increment = alpha * dT;
+
+        //
+        // displacement equation, displacement part
+        //
+        eps.noalias() = B * u;
+
+        using Invariants = MathLib::KelvinVector::Invariants<
+            MathLib::KelvinVector::KelvinVectorDimensions<
+                DisplacementDim>::value>;
+
+        // assume isotropic thermal expansion
+        const double T_ip = N.dot(T);  // T at integration point
+        eps_m.noalias() =
+            eps_m_prev + eps - eps_prev -
+            linear_thermal_strain_increment * Invariants::identity2;
+
+        auto&& solution = _ip_data[ip].solid_material.integrateStress(
+            t, x_position, dt, eps_m_prev, eps_m, sigma_prev, *state, T_ip);
+
+        if (!solution)
+        {
+            OGS_FATAL("Computation of local constitutive relation failed.");
+        }
+
+        MathLib::KelvinVector::KelvinMatrixType<DisplacementDim> C;
+        std::tie(sigma, state, C) = std::move(*solution);
+
+        local_Jac
+            .template block<displacement_size, displacement_size>(
+                displacement_index, displacement_index)
+            .noalias() += B.transpose() * C * B * w;
+
+        typename ShapeMatricesType::template MatrixType<DisplacementDim,
+                                                        displacement_size>
+            N_u = ShapeMatricesType::template MatrixType<
+                DisplacementDim, displacement_size>::Zero(DisplacementDim,
+                                                          displacement_size);
+
+        for (int i = 0; i < DisplacementDim; ++i)
+        {
+            N_u.template block<1, displacement_size / DisplacementDim>(
+                   i, i * displacement_size / DisplacementDim)
+                .noalias() = N;
+        }
+
+        // calculate real density
+        // rho_s_{n+1} * (V_{n} + dV) = rho_s_n * V_n
+        // dV = 3 * alpha * dT * V_0
+        // rho_s_{n+1} = rho_s_n / (1 + 3 * alpha * dT )
+        // see reference solid density description for details.
+        auto& rho_s = _ip_data[ip].solid_density;
+        rho_s = _ip_data[ip].solid_density_prev /
+                (1 + 3 * linear_thermal_strain_increment);
+
+        auto const& b = _process_data.specific_body_force;
+        local_rhs.template block<displacement_size, 1>(displacement_index, 0)
+            .noalias() -= (B.transpose() * (sigma - sigma_neq) -
+                           N_u.transpose() * rho_s * b) *
+                          w;
+
+        //
+        // displacement equation, temperature part
+        //
+        KuT.noalias() +=
+            B.transpose() * C * alpha * Invariants::identity2 * N * w;
+        if (_ip_data[ip].solid_material.getConstitutiveModel() ==
+            MaterialLib::Solids::ConstitutiveModel::CreepBGRa)
+        {
+            auto const s =
+                Invariants::deviatoric_projection * (sigma - sigma_neq);
+            double const norm_s = Invariants::FrobeniusNorm(s);
+            const double creep_coefficient =
+                _ip_data[ip].solid_material.getTemperatureRelatedCoefficient(
+                    t, dt, x_position, T_ip, norm_s);
+            KuT.noalias() += creep_coefficient * B.transpose() * s * N * w;
+        }
+
+        //
+        // temperature equation, temperature part;
+        //
+        auto const lambda =
+            _process_data.thermal_conductivity(t, x_position)[0];
+        KTT.noalias() += dNdx.transpose() * lambda * dNdx * w;
+
+        auto const c = _process_data.specific_heat_capacity(t, x_position)[0];
+        DTT.noalias() += N.transpose() * rho_s * c * N * w;
+    }
+
+    // temperature equation, temperature part
+    local_Jac
+        .template block<temperature_size, temperature_size>(temperature_index,
+                                                            temperature_index)
+        .noalias() += KTT + DTT / dt;
+
+    // displacement equation, temperature part
+    local_Jac
+        .template block<displacement_size, temperature_size>(displacement_index,
+                                                             temperature_index)
+        .noalias() -= KuT;
+
+    local_rhs.template block<temperature_size, 1>(temperature_index, 0)
+        .noalias() -= KTT * T + DTT * T_dot;
+}
+
 template <typename ShapeFunction, typename IntegrationMethod,
           int DisplacementDim>
 void ThermoMechanicsLocalAssembler<ShapeFunction, IntegrationMethod,
@@ -274,5 +578,215 @@ void ThermoMechanicsLocalAssembler<ShapeFunction, IntegrationMethod,
     local_rhs.noalias() -= laplace * local_T + mass * local_dT / dt;
 }
 
+template <typename ShapeFunction, typename IntegrationMethod,
+          int DisplacementDim>
+std::size_t
+ThermoMechanicsLocalAssembler<ShapeFunction, IntegrationMethod,
+                              DisplacementDim>::setSigma(double const* values)
+{
+    auto const kelvin_vector_size =
+        MathLib::KelvinVector::KelvinVectorDimensions<DisplacementDim>::value;
+    unsigned const n_integration_points =
+        _integration_method.getNumberOfPoints();
+
+    auto sigma_values =
+        Eigen::Map<Eigen::Matrix<double, kelvin_vector_size, Eigen::Dynamic,
+                                 Eigen::ColMajor> const>(
+            values, kelvin_vector_size, n_integration_points);
+
+    for (unsigned ip = 0; ip < n_integration_points; ++ip)
+    {
+        _ip_data[ip].sigma =
+            MathLib::KelvinVector::symmetricTensorToKelvinVector(
+                sigma_values.col(ip));
+    }
+
+    return n_integration_points;
+}
+
+template <typename ShapeFunction, typename IntegrationMethod,
+          int DisplacementDim>
+std::vector<double> ThermoMechanicsLocalAssembler<
+    ShapeFunction, IntegrationMethod, DisplacementDim>::getSigma() const
+{
+    auto const kelvin_vector_size =
+        MathLib::KelvinVector::KelvinVectorDimensions<DisplacementDim>::value;
+    unsigned const n_integration_points =
+        _integration_method.getNumberOfPoints();
+
+    std::vector<double> ip_sigma_values;
+    auto cache_mat = MathLib::createZeroedMatrix<Eigen::Matrix<
+        double, Eigen::Dynamic, kelvin_vector_size, Eigen::RowMajor>>(
+        ip_sigma_values, n_integration_points, kelvin_vector_size);
+
+    for (unsigned ip = 0; ip < n_integration_points; ++ip)
+    {
+        auto const& sigma = _ip_data[ip].sigma;
+        cache_mat.row(ip) =
+            MathLib::KelvinVector::kelvinVectorToSymmetricTensor(sigma);
+    }
+
+    return ip_sigma_values;
+}
+
+template <typename ShapeFunction, typename IntegrationMethod,
+          int DisplacementDim>
+std::vector<double> const& ThermoMechanicsLocalAssembler<
+    ShapeFunction, IntegrationMethod, DisplacementDim>::
+    getIntPtSigma(const double /*t*/,
+                  GlobalVector const& /*current_solution*/,
+                  NumLib::LocalToGlobalIndexMap const& /*dof_table*/,
+                  std::vector<double>& cache) const
+{
+    static const int kelvin_vector_size =
+        MathLib::KelvinVector::KelvinVectorDimensions<DisplacementDim>::value;
+    unsigned const n_integration_points =
+        _integration_method.getNumberOfPoints();
+
+    cache.clear();
+    auto cache_mat = MathLib::createZeroedMatrix<Eigen::Matrix<
+        double, kelvin_vector_size, Eigen::Dynamic, Eigen::RowMajor>>(
+        cache, kelvin_vector_size, n_integration_points);
+
+    for (unsigned ip = 0; ip < n_integration_points; ++ip)
+    {
+        auto const& sigma = _ip_data[ip].sigma;
+        cache_mat.col(ip) =
+            MathLib::KelvinVector::kelvinVectorToSymmetricTensor(sigma);
+    }
+
+    return cache;
+}
+
+template <typename ShapeFunction, typename IntegrationMethod,
+          int DisplacementDim>
+std::size_t
+ThermoMechanicsLocalAssembler<ShapeFunction, IntegrationMethod,
+                              DisplacementDim>::setEpsilon(double const* values)
+{
+    auto const kelvin_vector_size =
+        MathLib::KelvinVector::KelvinVectorDimensions<DisplacementDim>::value;
+    unsigned const n_integration_points =
+        _integration_method.getNumberOfPoints();
+
+    auto epsilon_values =
+        Eigen::Map<Eigen::Matrix<double, kelvin_vector_size, Eigen::Dynamic,
+                                 Eigen::ColMajor> const>(
+            values, kelvin_vector_size, n_integration_points);
+
+    for (unsigned ip = 0; ip < n_integration_points; ++ip)
+    {
+        _ip_data[ip].eps = MathLib::KelvinVector::symmetricTensorToKelvinVector(
+            epsilon_values.col(ip));
+    }
+
+    return n_integration_points;
+}
+
+template <typename ShapeFunction, typename IntegrationMethod,
+          int DisplacementDim>
+std::vector<double> ThermoMechanicsLocalAssembler<
+    ShapeFunction, IntegrationMethod, DisplacementDim>::getEpsilon() const
+{
+    auto const kelvin_vector_size =
+        MathLib::KelvinVector::KelvinVectorDimensions<DisplacementDim>::value;
+    unsigned const n_integration_points =
+        _integration_method.getNumberOfPoints();
+
+    std::vector<double> ip_epsilon_values;
+    auto cache_mat = MathLib::createZeroedMatrix<Eigen::Matrix<
+        double, Eigen::Dynamic, kelvin_vector_size, Eigen::RowMajor>>(
+        ip_epsilon_values, n_integration_points, kelvin_vector_size);
+
+    for (unsigned ip = 0; ip < n_integration_points; ++ip)
+    {
+        auto const& eps = _ip_data[ip].eps;
+        cache_mat.row(ip) =
+            MathLib::KelvinVector::kelvinVectorToSymmetricTensor(eps);
+    }
+
+    return ip_epsilon_values;
+}
+
+template <typename ShapeFunction, typename IntegrationMethod,
+          int DisplacementDim>
+std::vector<double> const& ThermoMechanicsLocalAssembler<
+    ShapeFunction, IntegrationMethod, DisplacementDim>::
+    getIntPtEpsilon(const double /*t*/,
+                    GlobalVector const& /*current_solution*/,
+                    NumLib::LocalToGlobalIndexMap const& /*dof_table*/,
+                    std::vector<double>& cache) const
+{
+    auto const kelvin_vector_size =
+        MathLib::KelvinVector::KelvinVectorDimensions<DisplacementDim>::value;
+    unsigned const n_integration_points =
+        _integration_method.getNumberOfPoints();
+
+    cache.clear();
+    auto cache_mat = MathLib::createZeroedMatrix<Eigen::Matrix<
+        double, kelvin_vector_size, Eigen::Dynamic, Eigen::RowMajor>>(
+        cache, kelvin_vector_size, n_integration_points);
+
+    for (unsigned ip = 0; ip < n_integration_points; ++ip)
+    {
+        auto const& eps = _ip_data[ip].eps;
+        cache_mat.col(ip) =
+            MathLib::KelvinVector::kelvinVectorToSymmetricTensor(eps);
+    }
+
+    return cache;
+}
+
+template <typename ShapeFunction, typename IntegrationMethod,
+          int DisplacementDim>
+std::size_t ThermoMechanicsLocalAssembler<
+    ShapeFunction, IntegrationMethod,
+    DisplacementDim>::setEpsilonMechanical(double const* values)
+{
+    auto const kelvin_vector_size =
+        MathLib::KelvinVector::KelvinVectorDimensions<DisplacementDim>::value;
+    unsigned const n_integration_points =
+        _integration_method.getNumberOfPoints();
+
+    auto epsilon_m_values =
+        Eigen::Map<Eigen::Matrix<double, kelvin_vector_size, Eigen::Dynamic,
+                                 Eigen::ColMajor> const>(
+            values, kelvin_vector_size, n_integration_points);
+
+    for (unsigned ip = 0; ip < n_integration_points; ++ip)
+    {
+        _ip_data[ip].eps_m =
+            MathLib::KelvinVector::symmetricTensorToKelvinVector(
+                epsilon_m_values.col(ip));
+    }
+
+    return n_integration_points;
+}
+template <typename ShapeFunction, typename IntegrationMethod,
+          int DisplacementDim>
+std::vector<double>
+ThermoMechanicsLocalAssembler<ShapeFunction, IntegrationMethod,
+                              DisplacementDim>::getEpsilonMechanical() const
+{
+    auto const kelvin_vector_size =
+        MathLib::KelvinVector::KelvinVectorDimensions<DisplacementDim>::value;
+    unsigned const n_integration_points =
+        _integration_method.getNumberOfPoints();
+
+    std::vector<double> ip_epsilon_m_values;
+    auto cache_mat = MathLib::createZeroedMatrix<Eigen::Matrix<
+        double, Eigen::Dynamic, kelvin_vector_size, Eigen::RowMajor>>(
+        ip_epsilon_m_values, n_integration_points, kelvin_vector_size);
+
+    for (unsigned ip = 0; ip < n_integration_points; ++ip)
+    {
+        auto const& eps_m = _ip_data[ip].eps_m;
+        cache_mat.row(ip) =
+            MathLib::KelvinVector::kelvinVectorToSymmetricTensor(eps_m);
+    }
+
+    return ip_epsilon_m_values;
+}
+
 }  // namespace ThermoMechanics
 }  // namespace ProcessLib
diff --git a/ProcessLib/ThermoMechanics/ThermoMechanicsFEM.h b/ProcessLib/ThermoMechanics/ThermoMechanicsFEM.h
index 14daca7fd8ca9b35cc9b7c71f90ee892aefea208..b14b1a06cbdd7146021486bec6aaeb1dc4f98c5b 100644
--- a/ProcessLib/ThermoMechanics/ThermoMechanicsFEM.h
+++ b/ProcessLib/ThermoMechanics/ThermoMechanicsFEM.h
@@ -119,113 +119,13 @@ public:
         std::size_t const /*local_matrix_size*/,
         bool const is_axially_symmetric,
         unsigned const integration_order,
-        ThermoMechanicsProcessData<DisplacementDim>& process_data)
-        : _process_data(process_data),
-          _integration_method(integration_order),
-          _element(e),
-          _is_axially_symmetric(is_axially_symmetric)
-    {
-        unsigned const n_integration_points =
-            _integration_method.getNumberOfPoints();
-
-        _ip_data.reserve(n_integration_points);
-        _secondary_data.N.resize(n_integration_points);
-
-        auto const shape_matrices =
-            initShapeMatrices<ShapeFunction, ShapeMatricesType,
-                              IntegrationMethod, DisplacementDim>(
-                e, is_axially_symmetric, _integration_method);
-
-        auto& solid_material =
-            MaterialLib::Solids::selectSolidConstitutiveRelation(
-                _process_data.solid_materials,
-                _process_data.material_ids,
-                e.getID());
-
-        for (unsigned ip = 0; ip < n_integration_points; ip++)
-        {
-            _ip_data.emplace_back(solid_material);
-            auto& ip_data = _ip_data[ip];
-            ip_data.integration_weight =
-                _integration_method.getWeightedPoint(ip).getWeight() *
-                shape_matrices[ip].integralMeasure * shape_matrices[ip].detJ;
-
-            static const int kelvin_vector_size =
-                MathLib::KelvinVector::KelvinVectorDimensions<
-                    DisplacementDim>::value;
-            ip_data.sigma.setZero(kelvin_vector_size);
-            ip_data.sigma_prev.setZero(kelvin_vector_size);
-            ip_data.eps.setZero(kelvin_vector_size);
-            ip_data.eps_prev.setZero(kelvin_vector_size);
-            ip_data.eps_m.setZero(kelvin_vector_size);
-            ip_data.eps_m_prev.setZero(kelvin_vector_size);
-
-            ParameterLib::SpatialPosition x_position;
-            x_position.setElementID(_element.getID());
-            ip_data.solid_density =
-                _process_data.reference_solid_density(0, x_position)[0];
-            ip_data.solid_density_prev = ip_data.solid_density;
-
-            ip_data.N = shape_matrices[ip].N;
-            ip_data.dNdx = shape_matrices[ip].dNdx;
-
-            _secondary_data.N[ip] = shape_matrices[ip].N;
-
-            // Initialize current time step values
-            if (_process_data.nonequilibrium_stress)
-            {
-                // Computation of non-equilibrium stress.
-                x_position.setCoordinates(MathLib::Point3d(
-                    interpolateCoordinates<ShapeFunction, ShapeMatricesType>(
-                        e, ip_data.N)));
-                std::vector<double> sigma_neq_data =
-                    (*_process_data.nonequilibrium_stress)(
-                        std::numeric_limits<
-                            double>::quiet_NaN() /* time independent */,
-                        x_position);
-                ip_data.sigma_neq =
-                    Eigen::Map<typename BMatricesType::KelvinVectorType>(
-                        sigma_neq_data.data(),
-                        MathLib::KelvinVector::KelvinVectorDimensions<
-                            DisplacementDim>::value,
-                        1);
-            }
-            // Initialization from non-equilibrium sigma, which is zero by
-            // default, or is set to some value.
-            ip_data.sigma = ip_data.sigma_neq;
-        }
-    }
+        ThermoMechanicsProcessData<DisplacementDim>& process_data);
 
     /// Returns number of read integration points.
-    std::size_t setIPDataInitialConditions(std::string const& name,
-                                           double const* values,
-                                           int const integration_order) override
-    {
-        if (integration_order !=
-            static_cast<int>(_integration_method.getIntegrationOrder()))
-        {
-            OGS_FATAL(
-                "Setting integration point initial conditions; The integration "
-                "order of the local assembler for element %d is different from "
-                "the integration order in the initial condition.",
-                _element.getID());
-        }
-
-        if (name == "sigma_ip")
-        {
-            return setSigma(values);
-        }
-        if (name == "epsilon_ip")
-        {
-            return setEpsilon(values);
-        }
-        if (name == "epsilon_m_ip")
-        {
-            return setEpsilonMechanical(values);
-        }
-
-        return 0;
-    }
+    std::size_t setIPDataInitialConditions(
+        std::string const& name,
+        double const* values,
+        int const integration_order) override;
 
     void assemble(double const /*t*/, std::vector<double> const& /*local_x*/,
                   std::vector<double>& /*local_M_data*/,
@@ -251,189 +151,7 @@ public:
                               std::vector<double>& /*local_M_data*/,
                               std::vector<double>& /*local_K_data*/,
                               std::vector<double>& local_rhs_data,
-                              std::vector<double>& local_Jac_data) override
-    {
-        auto const local_matrix_size = local_x.size();
-        assert(local_matrix_size == temperature_size + displacement_size);
-
-        auto T = Eigen::Map<typename ShapeMatricesType::template VectorType<
-            temperature_size> const>(local_x.data() + temperature_index,
-                                     temperature_size);
-
-        auto u = Eigen::Map<typename ShapeMatricesType::template VectorType<
-            displacement_size> const>(local_x.data() + displacement_index,
-                                      displacement_size);
-
-        auto T_dot = Eigen::Map<typename ShapeMatricesType::template VectorType<
-            temperature_size> const>(local_xdot.data() + temperature_index,
-                                     temperature_size);
-
-        auto local_Jac = MathLib::createZeroedMatrix<JacobianMatrix>(
-            local_Jac_data, local_matrix_size, local_matrix_size);
-
-        auto local_rhs = MathLib::createZeroedVector<RhsVector>(
-            local_rhs_data, local_matrix_size);
-
-        typename ShapeMatricesType::template MatrixType<displacement_size,
-                                                        temperature_size>
-            KuT;
-        KuT.setZero(displacement_size, temperature_size);
-
-        typename ShapeMatricesType::NodalMatrixType KTT;
-        KTT.setZero(temperature_size, temperature_size);
-
-        typename ShapeMatricesType::NodalMatrixType DTT;
-        DTT.setZero(temperature_size, temperature_size);
-
-        double const& dt = _process_data.dt;
-
-        unsigned const n_integration_points =
-            _integration_method.getNumberOfPoints();
-
-        ParameterLib::SpatialPosition x_position;
-        x_position.setElementID(_element.getID());
-
-        for (unsigned ip = 0; ip < n_integration_points; ip++)
-        {
-            x_position.setIntegrationPoint(ip);
-            auto const& w = _ip_data[ip].integration_weight;
-            auto const& N = _ip_data[ip].N;
-            auto const& dNdx = _ip_data[ip].dNdx;
-
-            auto const x_coord =
-                interpolateXCoordinate<ShapeFunction, ShapeMatricesType>(
-                    _element, N);
-            auto const& B = LinearBMatrix::computeBMatrix<
-                DisplacementDim, ShapeFunction::NPOINTS,
-                typename BMatricesType::BMatrixType>(dNdx, N, x_coord,
-                                                     _is_axially_symmetric);
-
-            auto& sigma = _ip_data[ip].sigma;
-            auto const& sigma_prev = _ip_data[ip].sigma_prev;
-            auto const& sigma_neq = _ip_data[ip].sigma_neq;
-
-            auto& eps = _ip_data[ip].eps;
-            auto const& eps_prev = _ip_data[ip].eps_prev;
-
-            auto& eps_m = _ip_data[ip].eps_m;
-            auto const& eps_m_prev = _ip_data[ip].eps_m_prev;
-
-            auto& state = _ip_data[ip].material_state_variables;
-
-            double const dT = N.dot(T_dot) * dt;
-            // calculate thermally induced strain
-            // assume isotropic thermal expansion
-            auto const alpha =
-                _process_data.linear_thermal_expansion_coefficient(
-                    t, x_position)[0];
-            double const linear_thermal_strain_increment = alpha * dT;
-
-            //
-            // displacement equation, displacement part
-            //
-            eps.noalias() = B * u;
-
-            using Invariants = MathLib::KelvinVector::Invariants<
-                MathLib::KelvinVector::KelvinVectorDimensions<
-                    DisplacementDim>::value>;
-
-            // assume isotropic thermal expansion
-            const double T_ip = N.dot(T);  // T at integration point
-            eps_m.noalias() =
-                eps_m_prev + eps - eps_prev -
-                linear_thermal_strain_increment * Invariants::identity2;
-
-            auto&& solution = _ip_data[ip].solid_material.integrateStress(
-                t, x_position, dt, eps_m_prev, eps_m, sigma_prev, *state, T_ip);
-
-            if (!solution)
-            {
-                OGS_FATAL("Computation of local constitutive relation failed.");
-            }
-
-            MathLib::KelvinVector::KelvinMatrixType<DisplacementDim> C;
-            std::tie(sigma, state, C) = std::move(*solution);
-
-            local_Jac
-                .template block<displacement_size, displacement_size>(
-                    displacement_index, displacement_index)
-                .noalias() += B.transpose() * C * B * w;
-
-            typename ShapeMatricesType::template MatrixType<DisplacementDim,
-                                                            displacement_size>
-                N_u = ShapeMatricesType::template MatrixType<
-                    DisplacementDim,
-                    displacement_size>::Zero(DisplacementDim,
-                                             displacement_size);
-
-            for (int i = 0; i < DisplacementDim; ++i)
-            {
-                N_u.template block<1, displacement_size / DisplacementDim>(
-                       i, i * displacement_size / DisplacementDim)
-                    .noalias() = N;
-            }
-
-            // calculate real density
-            // rho_s_{n+1} * (V_{n} + dV) = rho_s_n * V_n
-            // dV = 3 * alpha * dT * V_0
-            // rho_s_{n+1} = rho_s_n / (1 + 3 * alpha * dT )
-            // see reference solid density description for details.
-            auto& rho_s = _ip_data[ip].solid_density;
-            rho_s = _ip_data[ip].solid_density_prev /
-                    (1 + 3 * linear_thermal_strain_increment);
-
-            auto const& b = _process_data.specific_body_force;
-            local_rhs
-                .template block<displacement_size, 1>(displacement_index, 0)
-                .noalias() -= (B.transpose() * (sigma - sigma_neq) -
-                               N_u.transpose() * rho_s * b) *
-                              w;
-
-            //
-            // displacement equation, temperature part
-            //
-            KuT.noalias() +=
-                B.transpose() * C * alpha * Invariants::identity2 * N * w;
-            if (_ip_data[ip].solid_material.getConstitutiveModel() ==
-                MaterialLib::Solids::ConstitutiveModel::CreepBGRa)
-            {
-                auto const s =
-                    Invariants::deviatoric_projection * (sigma - sigma_neq);
-                double const norm_s = Invariants::FrobeniusNorm(s);
-                const double creep_coefficient =
-                    _ip_data[ip]
-                        .solid_material.getTemperatureRelatedCoefficient(
-                            t, dt, x_position, T_ip, norm_s);
-                KuT.noalias() += creep_coefficient * B.transpose() * s * N * w;
-            }
-
-            //
-            // temperature equation, temperature part;
-            //
-            auto const lambda =
-                _process_data.thermal_conductivity(t, x_position)[0];
-            KTT.noalias() += dNdx.transpose() * lambda * dNdx * w;
-
-            auto const c =
-                _process_data.specific_heat_capacity(t, x_position)[0];
-            DTT.noalias() += N.transpose() * rho_s * c * N * w;
-        }
-
-        // temperature equation, temperature part
-        local_Jac
-            .template block<temperature_size, temperature_size>(
-                temperature_index, temperature_index)
-            .noalias() += KTT + DTT / dt;
-
-        // displacement equation, temperature part
-        local_Jac
-            .template block<displacement_size, temperature_size>(
-                displacement_index, temperature_index)
-            .noalias() -= KuT;
-
-        local_rhs.template block<temperature_size, 1>(temperature_index, 0)
-            .noalias() -= KTT * T + DTT * T_dot;
-    }
+                              std::vector<double>& local_Jac_data) override;
 
     void preTimestepConcrete(std::vector<double> const& /*local_x*/,
                              double const /*t*/,
@@ -524,200 +242,32 @@ private:
         std::vector<double>& local_b_data, std::vector<double>& local_Jac_data,
         LocalCoupledSolutions const& local_coupled_solutions);
 
-    std::size_t setSigma(double const* values)
-    {
-        auto const kelvin_vector_size =
-            MathLib::KelvinVector::KelvinVectorDimensions<
-                DisplacementDim>::value;
-        unsigned const n_integration_points =
-            _integration_method.getNumberOfPoints();
-
-        auto sigma_values =
-            Eigen::Map<Eigen::Matrix<double, kelvin_vector_size, Eigen::Dynamic,
-                                     Eigen::ColMajor> const>(
-                values, kelvin_vector_size, n_integration_points);
-
-        for (unsigned ip = 0; ip < n_integration_points; ++ip)
-        {
-            _ip_data[ip].sigma =
-                MathLib::KelvinVector::symmetricTensorToKelvinVector(
-                    sigma_values.col(ip));
-        }
-
-        return n_integration_points;
-    }
+    std::size_t setSigma(double const* values);
 
     // TODO (naumov) This method is same as getIntPtSigma but for arguments and
     // the ordering of the cache_mat.
     // There should be only one.
-    std::vector<double> getSigma() const override
-    {
-        auto const kelvin_vector_size =
-            MathLib::KelvinVector::KelvinVectorDimensions<
-                DisplacementDim>::value;
-        unsigned const n_integration_points =
-            _integration_method.getNumberOfPoints();
-
-        std::vector<double> ip_sigma_values;
-        auto cache_mat = MathLib::createZeroedMatrix<Eigen::Matrix<
-            double, Eigen::Dynamic, kelvin_vector_size, Eigen::RowMajor>>(
-            ip_sigma_values, n_integration_points, kelvin_vector_size);
-
-        for (unsigned ip = 0; ip < n_integration_points; ++ip)
-        {
-            auto const& sigma = _ip_data[ip].sigma;
-            cache_mat.row(ip) =
-                MathLib::KelvinVector::kelvinVectorToSymmetricTensor(sigma);
-        }
-
-        return ip_sigma_values;
-    }
+    std::vector<double> getSigma() const override;
 
     std::vector<double> const& getIntPtSigma(
         const double /*t*/,
         GlobalVector const& /*current_solution*/,
         NumLib::LocalToGlobalIndexMap const& /*dof_table*/,
-        std::vector<double>& cache) const override
-    {
-        static const int kelvin_vector_size =
-            MathLib::KelvinVector::KelvinVectorDimensions<
-                DisplacementDim>::value;
-        unsigned const n_integration_points =
-            _integration_method.getNumberOfPoints();
-
-        cache.clear();
-        auto cache_mat = MathLib::createZeroedMatrix<Eigen::Matrix<
-            double, kelvin_vector_size, Eigen::Dynamic, Eigen::RowMajor>>(
-            cache, kelvin_vector_size, n_integration_points);
-
-        for (unsigned ip = 0; ip < n_integration_points; ++ip)
-        {
-            auto const& sigma = _ip_data[ip].sigma;
-            cache_mat.col(ip) =
-                MathLib::KelvinVector::kelvinVectorToSymmetricTensor(sigma);
-        }
-
-        return cache;
-    }
+        std::vector<double>& cache) const override;
 
-    std::size_t setEpsilon(double const* values)
-    {
-        auto const kelvin_vector_size =
-            MathLib::KelvinVector::KelvinVectorDimensions<
-                DisplacementDim>::value;
-        unsigned const n_integration_points =
-            _integration_method.getNumberOfPoints();
-
-        auto epsilon_values =
-            Eigen::Map<Eigen::Matrix<double, kelvin_vector_size, Eigen::Dynamic,
-                                     Eigen::ColMajor> const>(
-                values, kelvin_vector_size, n_integration_points);
-
-        for (unsigned ip = 0; ip < n_integration_points; ++ip)
-        {
-            _ip_data[ip].eps =
-                MathLib::KelvinVector::symmetricTensorToKelvinVector(
-                    epsilon_values.col(ip));
-        }
-
-        return n_integration_points;
-    }
-
-    std::vector<double> getEpsilon() const override
-    {
-        auto const kelvin_vector_size =
-            MathLib::KelvinVector::KelvinVectorDimensions<
-                DisplacementDim>::value;
-        unsigned const n_integration_points =
-            _integration_method.getNumberOfPoints();
-
-        std::vector<double> ip_epsilon_values;
-        auto cache_mat = MathLib::createZeroedMatrix<Eigen::Matrix<
-            double, Eigen::Dynamic, kelvin_vector_size, Eigen::RowMajor>>(
-            ip_epsilon_values, n_integration_points, kelvin_vector_size);
-
-        for (unsigned ip = 0; ip < n_integration_points; ++ip)
-        {
-            auto const& eps = _ip_data[ip].eps;
-            cache_mat.row(ip) =
-                MathLib::KelvinVector::kelvinVectorToSymmetricTensor(eps);
-        }
+    std::size_t setEpsilon(double const* values);
 
-        return ip_epsilon_values;
-    }
+    std::vector<double> getEpsilon() const override;
 
     std::vector<double> const& getIntPtEpsilon(
         const double /*t*/,
         GlobalVector const& /*current_solution*/,
         NumLib::LocalToGlobalIndexMap const& /*dof_table*/,
-        std::vector<double>& cache) const override
-    {
-        auto const kelvin_vector_size =
-            MathLib::KelvinVector::KelvinVectorDimensions<
-                DisplacementDim>::value;
-        unsigned const n_integration_points =
-            _integration_method.getNumberOfPoints();
-
-        cache.clear();
-        auto cache_mat = MathLib::createZeroedMatrix<Eigen::Matrix<
-            double, kelvin_vector_size, Eigen::Dynamic, Eigen::RowMajor>>(
-            cache, kelvin_vector_size, n_integration_points);
-
-        for (unsigned ip = 0; ip < n_integration_points; ++ip)
-        {
-            auto const& eps = _ip_data[ip].eps;
-            cache_mat.col(ip) =
-                MathLib::KelvinVector::kelvinVectorToSymmetricTensor(eps);
-        }
-
-        return cache;
-    }
-
-    std::size_t setEpsilonMechanical(double const* values)
-    {
-        auto const kelvin_vector_size =
-            MathLib::KelvinVector::KelvinVectorDimensions<
-                DisplacementDim>::value;
-        unsigned const n_integration_points =
-            _integration_method.getNumberOfPoints();
+        std::vector<double>& cache) const override;
 
-        auto epsilon_m_values =
-            Eigen::Map<Eigen::Matrix<double, kelvin_vector_size, Eigen::Dynamic,
-                                     Eigen::ColMajor> const>(
-                values, kelvin_vector_size, n_integration_points);
+    std::size_t setEpsilonMechanical(double const* values);
 
-        for (unsigned ip = 0; ip < n_integration_points; ++ip)
-        {
-            _ip_data[ip].eps_m =
-                MathLib::KelvinVector::symmetricTensorToKelvinVector(
-                    epsilon_m_values.col(ip));
-        }
-
-        return n_integration_points;
-    }
-
-    std::vector<double> getEpsilonMechanical() const override
-    {
-        auto const kelvin_vector_size =
-            MathLib::KelvinVector::KelvinVectorDimensions<
-                DisplacementDim>::value;
-        unsigned const n_integration_points =
-            _integration_method.getNumberOfPoints();
-
-        std::vector<double> ip_epsilon_m_values;
-        auto cache_mat = MathLib::createZeroedMatrix<Eigen::Matrix<
-            double, Eigen::Dynamic, kelvin_vector_size, Eigen::RowMajor>>(
-            ip_epsilon_m_values, n_integration_points, kelvin_vector_size);
-
-        for (unsigned ip = 0; ip < n_integration_points; ++ip)
-        {
-            auto const& eps_m = _ip_data[ip].eps_m;
-            cache_mat.row(ip) =
-                MathLib::KelvinVector::kelvinVectorToSymmetricTensor(eps_m);
-        }
-
-        return ip_epsilon_m_values;
-    }
+    std::vector<double> getEpsilonMechanical() const override;
 
     ThermoMechanicsProcessData<DisplacementDim>& _process_data;