https://gitlab.opengeosys.org/ogs/ogs.git
Raw File
Tip revision: f804b6cda599b7e432385cf618d8d5d2324801d3 authored by Florian Zill on 27 October 2021, 11:53:58 UTC
[T/HM] shared use of identical project data
Tip revision: f804b6c
ThermoRichardsFlowFEM-impl.h
/**
 * \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 "HydrostaticElasticityModel.h"
#include "MaterialLib/MPL/MaterialSpatialDistributionMap.h"
#include "MaterialLib/MPL/Medium.h"
#include "MaterialLib/MPL/Utils/FormEffectiveThermalConductivity.h"
#include "MaterialLib/MPL/Utils/FormEigenTensor.h"
#include "MaterialLib/MPL/Utils/FormEigenVector.h"
#include "MaterialLib/MPL/Utils/GetLiquidThermalExpansivity.h"
#include "MaterialLib/PhysicalConstant.h"
#include "MaterialLib/SolidModels/SelectSolidConstitutiveRelation.h"
#include "NumLib/Function/Interpolation.h"
#include "ProcessLib/Utils/SetOrGetIntegrationPointData.h"
#include "RigidElasticityModel.h"
#include "UniaxialElasticityModel.h"
#include "UserDefinedElasticityModel.h"

namespace ProcessLib
{
namespace ThermoRichardsFlow
{
template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
ThermoRichardsFlowLocalAssembler<ShapeFunction, IntegrationMethod, GlobalDim>::
    ThermoRichardsFlowLocalAssembler(
        MeshLib::Element const& e,
        std::size_t const /*local_matrix_size*/,
        bool const is_axially_symmetric,
        unsigned const integration_order,
        ThermoRichardsFlowProcessData& 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);

    auto const shape_matrices =
        NumLib::initShapeMatrices<ShapeFunction, ShapeMatricesType, GlobalDim>(
            e, is_axially_symmetric, _integration_method);

    auto const& medium = *_process_data.media_map->getMedium(_element.getID());

    ParameterLib::SpatialPosition x_position;
    x_position.setElementID(_element.getID());
    for (unsigned ip = 0; ip < n_integration_points; ip++)
    {
        auto const& sm = shape_matrices[ip];
        x_position.setIntegrationPoint(ip);
        _ip_data.emplace_back();
        auto& ip_data = _ip_data[ip];
        _ip_data[ip].integration_weight =
            _integration_method.getWeightedPoint(ip).getWeight() *
            sm.integralMeasure * sm.detJ;

        ip_data.N = sm.N;
        ip_data.dNdx = sm.dNdx;

        // Initial porosity. Could be read from integration point data or mesh.
        ip_data.porosity = medium[MPL::porosity].template initialValue<double>(
            x_position,
            std::numeric_limits<double>::quiet_NaN() /* t independent */);
    }
}

template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
std::size_t ThermoRichardsFlowLocalAssembler<
    ShapeFunction, IntegrationMethod,
    GlobalDim>::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 == "saturation_ip")
    {
        return ProcessLib::setIntegrationPointScalarData(values, _ip_data,
                                                         &IpData::saturation);
    }
    if (name == "porosity_ip")
    {
        return ProcessLib::setIntegrationPointScalarData(values, _ip_data,
                                                         &IpData::porosity);
    }
    return 0;
}

template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
void ThermoRichardsFlowLocalAssembler<ShapeFunction, IntegrationMethod,
                                      GlobalDim>::
    setInitialConditionsConcrete(std::vector<double> const& local_x,
                                 double const t,
                                 bool const /*use_monolithic_scheme*/,
                                 int const /*process_id*/)
{
    assert(local_x.size() == temperature_size + pressure_size);

    auto p_L = Eigen::Map<
        typename ShapeMatricesType::template VectorType<pressure_size> const>(
        local_x.data() + pressure_index, pressure_size);

    auto const& medium = *_process_data.media_map->getMedium(_element.getID());
    MPL::VariableArray variables;

    ParameterLib::SpatialPosition x_position;
    x_position.setElementID(_element.getID());

    unsigned const n_integration_points =
        _integration_method.getNumberOfPoints();
    for (unsigned ip = 0; ip < n_integration_points; ip++)
    {
        x_position.setIntegrationPoint(ip);

        auto const& N = _ip_data[ip].N;

        double p_cap_ip;
        NumLib::shapeFunctionInterpolate(-p_L, N, p_cap_ip);

        variables[static_cast<int>(MPL::Variable::capillary_pressure)] =
            p_cap_ip;
        variables[static_cast<int>(MPL::Variable::phase_pressure)] = -p_cap_ip;

        // Note: temperature dependent saturation model is not considered so
        // far.
        _ip_data[ip].saturation_prev =
            medium[MPL::PropertyType::saturation].template value<double>(
                variables, x_position, t,
                std::numeric_limits<double>::quiet_NaN());
    }
}

template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
void ThermoRichardsFlowLocalAssembler<
    ShapeFunction, IntegrationMethod,
    GlobalDim>::assembleWithJacobian(double const t, double const dt,
                                     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_dim = pressure_size + temperature_size;
    assert(local_x.size() == local_matrix_dim);

    auto const T = Eigen::Map<typename ShapeMatricesType::template VectorType<
        temperature_size> const>(local_x.data() + temperature_index,
                                 temperature_size);
    auto const p_L = Eigen::Map<
        typename ShapeMatricesType::template VectorType<pressure_size> const>(
        local_x.data() + pressure_index, pressure_size);

    auto const T_dot =
        Eigen::Map<typename ShapeMatricesType::template VectorType<
            temperature_size> const>(local_xdot.data() + temperature_index,
                                     temperature_size);
    auto const p_L_dot = Eigen::Map<
        typename ShapeMatricesType::template VectorType<pressure_size> const>(
        local_xdot.data() + pressure_index, pressure_size);

    auto local_Jac = MathLib::createZeroedMatrix<
        typename ShapeMatricesType::template MatrixType<local_matrix_dim,
                                                        local_matrix_dim>>(
        local_Jac_data, local_matrix_dim, local_matrix_dim);

    auto local_rhs = MathLib::createZeroedVector<
        typename ShapeMatricesType::template VectorType<local_matrix_dim>>(
        local_rhs_data, local_matrix_dim);

    typename ShapeMatricesType::NodalMatrixType M_TT =
        ShapeMatricesType::NodalMatrixType::Zero(temperature_size,
                                                 temperature_size);
    typename ShapeMatricesType::NodalMatrixType M_Tp =
        ShapeMatricesType::NodalMatrixType::Zero(temperature_size,
                                                 pressure_size);
    typename ShapeMatricesType::NodalMatrixType K_TT =
        ShapeMatricesType::NodalMatrixType::Zero(temperature_size,
                                                 temperature_size);
    typename ShapeMatricesType::NodalMatrixType K_Tp =
        ShapeMatricesType::NodalMatrixType::Zero(temperature_size,
                                                 pressure_size);
    typename ShapeMatricesType::NodalMatrixType M_pT =
        ShapeMatricesType::NodalMatrixType::Zero(pressure_size,
                                                 temperature_size);
    typename ShapeMatricesType::NodalMatrixType laplace_p =
        ShapeMatricesType::NodalMatrixType::Zero(pressure_size, pressure_size);

    typename ShapeMatricesType::NodalMatrixType storage_p_a_p =
        ShapeMatricesType::NodalMatrixType::Zero(pressure_size, pressure_size);

    typename ShapeMatricesType::NodalMatrixType storage_p_a_S_Jpp =
        ShapeMatricesType::NodalMatrixType::Zero(pressure_size, pressure_size);

    typename ShapeMatricesType::NodalMatrixType storage_p_a_S =
        ShapeMatricesType::NodalMatrixType::Zero(pressure_size, pressure_size);

    auto const& medium = *_process_data.media_map->getMedium(_element.getID());
    auto const& liquid_phase = medium.phase("AqueousLiquid");
    auto const& solid_phase = medium.phase("Solid");
    MPL::Phase const* gas_phase =
        medium.hasPhase("Gas") ? &medium.phase("Gas") : nullptr;
    MPL::VariableArray variables;
    MPL::VariableArray variables_prev;

    ParameterLib::SpatialPosition x_position;
    x_position.setElementID(_element.getID());

    unsigned const n_integration_points =
        _integration_method.getNumberOfPoints();
    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;

        double T_ip;
        NumLib::shapeFunctionInterpolate(T, N, T_ip);
        double T_dot_ip;
        NumLib::shapeFunctionInterpolate(T_dot, N, T_dot_ip);

        double p_cap_ip;
        NumLib::shapeFunctionInterpolate(-p_L, N, p_cap_ip);

        double p_cap_dot_ip;
        NumLib::shapeFunctionInterpolate(-p_L_dot, N, p_cap_dot_ip);

        variables[static_cast<int>(MPL::Variable::capillary_pressure)] =
            p_cap_ip;
        variables[static_cast<int>(MPL::Variable::phase_pressure)] = -p_cap_ip;
        variables[static_cast<int>(MPL::Variable::temperature)] = T_ip;

        auto& S_L = _ip_data[ip].saturation;
        auto const S_L_prev = _ip_data[ip].saturation_prev;
        auto const alpha =
            medium[MPL::PropertyType::biot_coefficient].template value<double>(
                variables, x_position, t, dt);

        auto& solid_elasticity = *_process_data.simplified_elasticity;
        // TODO (buchwaldj)
        // is bulk_modulus good name for bulk modulus of solid skeleton?
        auto const beta_S =
            solid_elasticity.bulkCompressibilityFromYoungsModulus(
                solid_phase, variables, x_position, t, dt);
        auto const beta_SR = (1 - alpha) * beta_S;
        variables[static_cast<int>(MPL::Variable::grain_compressibility)] =
            beta_SR;

        auto const rho_LR =
            liquid_phase[MPL::PropertyType::density].template value<double>(
                variables, x_position, t, dt);
        auto const& b = _process_data.specific_body_force;

        double const drho_LR_dp =
            liquid_phase[MPL::PropertyType::density].template dValue<double>(
                variables, MPL::Variable::phase_pressure, x_position, t, dt);
        auto const beta_LR = drho_LR_dp / rho_LR;

        S_L = medium[MPL::PropertyType::saturation].template value<double>(
            variables, x_position, t, dt);
        variables[static_cast<int>(MPL::Variable::liquid_saturation)] = S_L;
        variables_prev[static_cast<int>(MPL::Variable::liquid_saturation)] =
            S_L_prev;

        // tangent derivative for Jacobian
        double const dS_L_dp_cap =
            medium[MPL::PropertyType::saturation].template dValue<double>(
                variables, MPL::Variable::capillary_pressure, x_position, t,
                dt);
        // secant derivative from time discretization for storage
        // use tangent, if secant is not available
        double const DeltaS_L_Deltap_cap =
            (p_cap_dot_ip == 0) ? dS_L_dp_cap
                                : (S_L - S_L_prev) / (dt * p_cap_dot_ip);

        auto chi_S_L = S_L;
        auto chi_S_L_prev = S_L_prev;
        auto dchi_dS_L = 1.0;
        if (medium.hasProperty(MPL::PropertyType::bishops_effective_stress))
        {
            auto const chi = [&medium, x_position, t, dt](double const S_L)
            {
                MPL::VariableArray variables;
                variables[static_cast<int>(MPL::Variable::liquid_saturation)] =
                    S_L;
                return medium[MPL::PropertyType::bishops_effective_stress]
                    .template value<double>(variables, x_position, t, dt);
            };
            chi_S_L = chi(S_L);
            chi_S_L_prev = chi(S_L_prev);

            dchi_dS_L = medium[MPL::PropertyType::bishops_effective_stress]
                            .template dValue<double>(
                                variables, MPL::Variable::liquid_saturation,
                                x_position, t, dt);
        }
        // TODO (buchwaldj)
        // should solid_grain_pressure or effective_pore_pressure remain?
        // double const p_FR = -chi_S_L * p_cap_ip;
        // variables[static_cast<int>(MPL::Variable::solid_grain_pressure)] =
        // p_FR;

        variables[static_cast<int>(MPL::Variable::effective_pore_pressure)] =
            -chi_S_L * p_cap_ip;
        variables_prev[static_cast<int>(
            MPL::Variable::effective_pore_pressure)] =
            -chi_S_L_prev * (p_cap_ip - p_cap_dot_ip * dt);

        auto& phi = _ip_data[ip].porosity;
        {  // Porosity update

            variables_prev[static_cast<int>(MPL::Variable::porosity)] =
                _ip_data[ip].porosity_prev;
            phi = medium[MPL::PropertyType::porosity].template value<double>(
                variables, variables_prev, x_position, t, dt);
            variables[static_cast<int>(MPL::Variable::porosity)] = phi;
        }

        if (alpha < phi)
        {
            OGS_FATAL(
                "ThermoRichardsFlow: Biot-coefficient {} is smaller than "
                "porosity {} in element/integration point {}/{}.",
                alpha, phi, _element.getID(), ip);
        }

        double const k_rel =
            medium[MPL::PropertyType::relative_permeability]
                .template value<double>(variables, x_position, t, dt);
        auto const mu =
            liquid_phase[MPL::PropertyType::viscosity].template value<double>(
                variables, x_position, t, dt);

        auto const K_intrinsic = MPL::formEigenTensor<GlobalDim>(
            medium[MPL::PropertyType::permeability].value(variables, x_position,
                                                          t, dt));

        GlobalDimMatrixType const Ki_over_mu = K_intrinsic / mu;
        GlobalDimMatrixType const rho_Ki_over_mu = rho_LR * Ki_over_mu;

        // Consider anisotropic thermal expansion.
        // Read in 3x3 tensor. 2D case also requires expansion coeff. for z-
        // component.
        Eigen::Matrix<double, 3, 3> const
            solid_linear_thermal_expansion_coefficient =
                MaterialPropertyLib::formEigenTensor<3>(
                    solid_phase
                        [MaterialPropertyLib::PropertyType::thermal_expansivity]
                            .value(variables, x_position, t, dt));

        auto const rho_SR =
            solid_phase[MPL::PropertyType::density].template value<double>(
                variables, x_position, t, dt);

        //
        // pressure equation, pressure part.
        //
        laplace_p.noalias() +=
            dNdx.transpose() * k_rel * rho_Ki_over_mu * dNdx * w;

        const double alphaB_minus_phi = alpha - phi;
        double const a0 = alphaB_minus_phi * beta_SR;
        double const specific_storage_a_p =
            S_L * (phi * beta_LR + S_L * a0 +
                   chi_S_L * alpha * alpha *
                       solid_elasticity.storageContribution(
                           solid_phase, variables, x_position, t, dt));
        double const specific_storage_a_S = phi - p_cap_ip * S_L * a0;

        double const dspecific_storage_a_p_dp_cap =
            dS_L_dp_cap * (phi * beta_LR + 2 * S_L * a0 +
                           alpha * alpha *
                               solid_elasticity.storageContribution(
                                   solid_phase, variables, x_position, t, dt) *
                               (chi_S_L + dchi_dS_L * S_L));
        double const dspecific_storage_a_S_dp_cap =
            -a0 * (S_L + p_cap_ip * dS_L_dp_cap);

        storage_p_a_p.noalias() +=
            N.transpose() * rho_LR * specific_storage_a_p * N * w;

        storage_p_a_S.noalias() -= N.transpose() * rho_LR *
                                   specific_storage_a_S * DeltaS_L_Deltap_cap *
                                   N * w;

        local_Jac
            .template block<pressure_size, pressure_size>(pressure_index,
                                                          pressure_index)
            .noalias() += N.transpose() * p_cap_dot_ip * rho_LR *
                          dspecific_storage_a_p_dp_cap * N * w;

        storage_p_a_S_Jpp.noalias() -=
            N.transpose() * rho_LR *
            ((S_L - S_L_prev) * dspecific_storage_a_S_dp_cap +
             specific_storage_a_S * dS_L_dp_cap) /
            dt * N * w;

        double const dk_rel_dS_L =
            medium[MPL::PropertyType::relative_permeability]
                .template dValue<double>(variables,
                                         MPL::Variable::liquid_saturation,
                                         x_position, t, dt);
        GlobalDimVectorType const grad_p_cap = -dNdx * p_L;
        local_Jac
            .template block<pressure_size, pressure_size>(pressure_index,
                                                          pressure_index)
            .noalias() += dNdx.transpose() * rho_Ki_over_mu * grad_p_cap *
                          dk_rel_dS_L * dS_L_dp_cap * N * w;

        local_Jac
            .template block<pressure_size, pressure_size>(pressure_index,
                                                          pressure_index)
            .noalias() += dNdx.transpose() * rho_LR * rho_Ki_over_mu * b *
                          dk_rel_dS_L * dS_L_dp_cap * N * w;

        local_rhs.template segment<pressure_size>(pressure_index).noalias() +=
            dNdx.transpose() * rho_LR * k_rel * rho_Ki_over_mu * b * w;

        //
        // pressure equation, temperature part.
        //
        double const fluid_volumetric_thermal_expansion_coefficient =
            MPL::getLiquidThermalExpansivity(liquid_phase, variables, rho_LR,
                                             x_position, t, dt);
        const double eff_thermal_expansion =
            S_L * (alphaB_minus_phi *
                       solid_linear_thermal_expansion_coefficient.trace() +
                   phi * fluid_volumetric_thermal_expansion_coefficient +
                   alpha * solid_elasticity.thermalExpansivityContribution(
                               solid_linear_thermal_expansion_coefficient,
                               solid_phase, variables, x_position, t, dt));
        M_pT.noalias() -=
            N.transpose() * rho_LR * eff_thermal_expansion * N * w;

        //
        // temperature equation.
        //
        {
            auto const specific_heat_capacity_fluid =
                liquid_phase[MaterialPropertyLib::specific_heat_capacity]
                    .template value<double>(variables, x_position, t, dt);

            auto const specific_heat_capacity_solid =
                solid_phase
                    [MaterialPropertyLib::PropertyType::specific_heat_capacity]
                        .template value<double>(variables, x_position, t, dt);

            M_TT.noalias() +=
                w *
                (rho_SR * specific_heat_capacity_solid * (1 - phi) +
                 (S_L * rho_LR * specific_heat_capacity_fluid) * phi) *
                N.transpose() * N;

            auto const thermal_conductivity =
                MaterialPropertyLib::formEigenTensor<GlobalDim>(
                    medium[MaterialPropertyLib::PropertyType::
                               thermal_conductivity]
                        .value(variables, x_position, t, dt));

            GlobalDimVectorType const velocity_L = GlobalDimVectorType(
                -Ki_over_mu * k_rel * (dNdx * p_L - rho_LR * b));

            K_TT.noalias() += (dNdx.transpose() * thermal_conductivity * dNdx +
                               N.transpose() * velocity_L.transpose() * dNdx *
                                   rho_LR * specific_heat_capacity_fluid) *
                              w;

            //
            // temperature equation, pressure part
            //
            K_Tp.noalias() -= rho_LR * specific_heat_capacity_fluid *
                              N.transpose() * (dNdx * T).transpose() * k_rel *
                              Ki_over_mu * dNdx * w;

            K_Tp.noalias() -= rho_LR * specific_heat_capacity_fluid *
                              N.transpose() * velocity_L.dot(dNdx * T) / k_rel *
                              dk_rel_dS_L * dS_L_dp_cap * N * w;
        }
        if (liquid_phase.hasProperty(MPL::PropertyType::vapour_diffusion) &&
            S_L < 1.0)
        {
            variables[static_cast<int>(MPL::Variable::density)] = rho_LR;

            double const rho_wv =
                liquid_phase[MaterialPropertyLib::vapour_density]
                    .template value<double>(variables, x_position, t, dt);

            double const drho_wv_dT =
                liquid_phase[MaterialPropertyLib::vapour_density]
                    .template dValue<double>(variables,
                                             MPL::Variable::temperature,
                                             x_position, t, dt);
            double const drho_wv_dp =
                liquid_phase[MaterialPropertyLib::vapour_density]
                    .template dValue<double>(variables,
                                             MPL::Variable::phase_pressure,
                                             x_position, t, dt);
            auto const f_Tv =
                liquid_phase
                    [MPL::PropertyType::thermal_diffusion_enhancement_factor]
                        .template value<double>(variables, x_position, t, dt);

            variables[static_cast<int>(MPL::Variable::porosity)] = phi;
            double const D_v =
                liquid_phase[MPL::PropertyType::vapour_diffusion]
                    .template value<double>(variables, x_position, t, dt);

            double const f_Tv_D_Tv = f_Tv * D_v * drho_wv_dT;
            double const D_pv = D_v * drho_wv_dp;

            if (gas_phase && gas_phase->hasProperty(
                                 MPL::PropertyType::specific_heat_capacity))
            {
                GlobalDimVectorType const grad_T = dNdx * T;
                // Vapour velocity
                GlobalDimVectorType const q_v =
                    -(f_Tv_D_Tv * grad_T - D_pv * grad_p_cap) / rho_LR;
                double const specific_heat_capacity_vapour =
                    gas_phase
                        ->property(MaterialPropertyLib::PropertyType::
                                       specific_heat_capacity)
                        .template value<double>(variables, x_position, t, dt);

                M_TT.noalias() +=
                    w *
                    (rho_wv * specific_heat_capacity_vapour * (1 - S_L) * phi) *
                    N.transpose() * N;

                K_TT.noalias() += N.transpose() * q_v.transpose() * dNdx *
                                  rho_wv * specific_heat_capacity_vapour * w;
            }

            double const storage_coefficient_by_water_vapor =
                phi * (rho_wv * dS_L_dp_cap + (1 - S_L) * drho_wv_dp);

            storage_p_a_p.noalias() +=
                N.transpose() * storage_coefficient_by_water_vapor * N * w;

            double const vapor_expansion_factor = phi * (1 - S_L) * drho_wv_dT;
            M_pT.noalias() += N.transpose() * vapor_expansion_factor * N * w;

            local_Jac
                .template block<pressure_size, temperature_size>(
                    pressure_index, temperature_index)
                .noalias() += dNdx.transpose() * f_Tv_D_Tv * dNdx * w;

            local_rhs.template segment<pressure_size>(pressure_index)
                .noalias() -= f_Tv_D_Tv * dNdx.transpose() * (dNdx * T) * w;

            laplace_p.noalias() += dNdx.transpose() * D_pv * dNdx * w;

            //
            // Latent heat term
            //
            if (liquid_phase.hasProperty(MPL::PropertyType::latent_heat))
            {
                double const factor = phi * (1 - S_L) / rho_LR;
                // The volumetric latent heat of vaporization of liquid water
                double const L0 =
                    liquid_phase[MPL::PropertyType::latent_heat]
                        .template value<double>(variables, x_position, t, dt) *
                    rho_LR;

                double const drho_LR_dT =
                    liquid_phase[MPL::PropertyType::density]
                        .template dValue<double>(variables,
                                                 MPL::Variable::temperature,
                                                 x_position, t, dt);

                double const rho_wv_over_rho_L = rho_wv / rho_LR;
                M_TT.noalias() +=
                    factor * L0 *
                    (drho_wv_dT - rho_wv_over_rho_L * drho_LR_dT) *
                    N.transpose() * N * w;

                M_Tp.noalias() +=
                    (factor * L0 *
                         (drho_wv_dp - rho_wv_over_rho_L * drho_LR_dp) +
                     L0 * phi * rho_wv_over_rho_L * dS_L_dp_cap) *
                    N.transpose() * N * w;

                // temperature equation, temperature part
                K_TT.noalias() +=
                    L0 * f_Tv_D_Tv * dNdx.transpose() * dNdx * w / rho_LR;
                // temperature equation, pressure part
                K_Tp.noalias() +=
                    L0 * D_pv * dNdx.transpose() * dNdx * w / rho_LR;
            }
        }
    }

    if (_process_data.apply_mass_lumping)
    {
        storage_p_a_p = storage_p_a_p.colwise().sum().eval().asDiagonal();
        storage_p_a_S = storage_p_a_S.colwise().sum().eval().asDiagonal();
        storage_p_a_S_Jpp =
            storage_p_a_S_Jpp.colwise().sum().eval().asDiagonal();
    }

    //
    // -- Jacobian
    //
    // temperature equation.
    local_Jac
        .template block<temperature_size, temperature_size>(temperature_index,
                                                            temperature_index)
        .noalias() += M_TT / dt + K_TT;
    // temperature equation, pressure part
    local_Jac
        .template block<temperature_size, pressure_size>(temperature_index,
                                                         pressure_index)
        .noalias() += K_Tp;

    // pressure equation, pressure part.
    local_Jac
        .template block<pressure_size, pressure_size>(pressure_index,
                                                      pressure_index)
        .noalias() += laplace_p + storage_p_a_p / dt + storage_p_a_S_Jpp;

    // pressure equation, temperature part (contributed by thermal expansion).
    local_Jac
        .template block<pressure_size, temperature_size>(pressure_index,
                                                         temperature_index)
        .noalias() += M_pT / dt;

    //
    // -- Residual
    //
    // temperature equation
    local_rhs.template segment<temperature_size>(temperature_index).noalias() -=
        M_TT * T_dot + K_TT * T;

    // pressure equation
    local_rhs.template segment<pressure_size>(pressure_index).noalias() -=
        laplace_p * p_L + (storage_p_a_p + storage_p_a_S) * p_L_dot +
        M_pT * T_dot;
    if (liquid_phase.hasProperty(MPL::PropertyType::vapour_diffusion) &&
        liquid_phase.hasProperty(MPL::PropertyType::latent_heat))
    {
        // Jacobian: temperature equation, pressure part
        local_Jac
            .template block<temperature_size, pressure_size>(temperature_index,
                                                             pressure_index)
            .noalias() += M_Tp / dt;
        // RHS: temperature part
        local_rhs.template segment<temperature_size>(temperature_index)
            .noalias() -= M_Tp * p_L_dot;
    }
}

template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
void ThermoRichardsFlowLocalAssembler<
    ShapeFunction, IntegrationMethod,
    GlobalDim>::assemble(double const t, double const dt,
                         std::vector<double> const& local_x,
                         std::vector<double> const& local_xdot,
                         std::vector<double>& local_M_data,
                         std::vector<double>& local_K_data,
                         std::vector<double>& local_rhs_data)
{
    auto const local_matrix_dim = pressure_size + temperature_size;
    assert(local_x.size() == local_matrix_dim);

    auto const T = Eigen::Map<typename ShapeMatricesType::template VectorType<
        temperature_size> const>(local_x.data() + temperature_index,
                                 temperature_size);
    auto const p_L = Eigen::Map<
        typename ShapeMatricesType::template VectorType<pressure_size> const>(
        local_x.data() + pressure_index, pressure_size);

    auto const T_dot =
        Eigen::Map<typename ShapeMatricesType::template VectorType<
            temperature_size> const>(local_xdot.data() + temperature_index,
                                     temperature_size);
    auto const p_L_dot = Eigen::Map<
        typename ShapeMatricesType::template VectorType<pressure_size> const>(
        local_xdot.data() + pressure_index, pressure_size);

    auto local_K = MathLib::createZeroedMatrix<
        typename ShapeMatricesType::template MatrixType<local_matrix_dim,
                                                        local_matrix_dim>>(
        local_K_data, local_matrix_dim, local_matrix_dim);

    auto local_M = MathLib::createZeroedMatrix<
        typename ShapeMatricesType::template MatrixType<local_matrix_dim,
                                                        local_matrix_dim>>(
        local_M_data, local_matrix_dim, local_matrix_dim);

    auto local_rhs = MathLib::createZeroedVector<
        typename ShapeMatricesType::template VectorType<local_matrix_dim>>(
        local_rhs_data, local_matrix_dim);

    auto const& medium = *_process_data.media_map->getMedium(_element.getID());
    auto const& liquid_phase = medium.phase("AqueousLiquid");
    auto const& solid_phase = medium.phase("Solid");
    MPL::Phase const* gas_phase =
        medium.hasPhase("Gas") ? &medium.phase("Gas") : nullptr;
    MPL::VariableArray variables;
    MPL::VariableArray variables_prev;

    ParameterLib::SpatialPosition x_position;
    x_position.setElementID(_element.getID());

    unsigned const n_integration_points =
        _integration_method.getNumberOfPoints();
    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;

        double T_ip;
        NumLib::shapeFunctionInterpolate(T, N, T_ip);
        double T_dot_ip;
        NumLib::shapeFunctionInterpolate(T_dot, N, T_dot_ip);

        double p_cap_ip;
        NumLib::shapeFunctionInterpolate(-p_L, N, p_cap_ip);

        double p_cap_dot_ip;
        NumLib::shapeFunctionInterpolate(-p_L_dot, N, p_cap_dot_ip);

        variables[static_cast<int>(MPL::Variable::capillary_pressure)] =
            p_cap_ip;
        variables[static_cast<int>(MPL::Variable::phase_pressure)] = -p_cap_ip;
        variables[static_cast<int>(MPL::Variable::temperature)] = T_ip;

        auto& S_L = _ip_data[ip].saturation;
        auto const S_L_prev = _ip_data[ip].saturation_prev;
        auto const alpha =
            medium[MPL::PropertyType::biot_coefficient].template value<double>(
                variables, x_position, t, dt);

        auto& solid_elasticity = *_process_data.simplified_elasticity;
        // TODO (buchwaldj)
        // is bulk_modulus good name for bulk modulus of solid skeleton?
        auto const beta_S =
            solid_elasticity.bulkCompressibilityFromYoungsModulus(
                solid_phase, variables, x_position, t, dt);
        auto const beta_SR = (1 - alpha) * beta_S;
        variables[static_cast<int>(MPL::Variable::grain_compressibility)] =
            beta_SR;

        auto const rho_LR =
            liquid_phase[MPL::PropertyType::density].template value<double>(
                variables, x_position, t, dt);
        auto const& b = _process_data.specific_body_force;

        double const drho_LR_dp =
            liquid_phase[MPL::PropertyType::density].template dValue<double>(
                variables, MPL::Variable::phase_pressure, x_position, t, dt);
        auto const beta_LR = drho_LR_dp / rho_LR;

        S_L = medium[MPL::PropertyType::saturation].template value<double>(
            variables, x_position, t, dt);
        variables[static_cast<int>(MPL::Variable::liquid_saturation)] = S_L;
        variables_prev[static_cast<int>(MPL::Variable::liquid_saturation)] =
            S_L_prev;

        // tangent derivative for Jacobian
        double const dS_L_dp_cap =
            medium[MPL::PropertyType::saturation].template dValue<double>(
                variables, MPL::Variable::capillary_pressure, x_position, t,
                dt);
        // secant derivative from time discretization for storage
        // use tangent, if secant is not available
        double const DeltaS_L_Deltap_cap =
            (p_cap_dot_ip == 0) ? dS_L_dp_cap
                                : (S_L - S_L_prev) / (dt * p_cap_dot_ip);

        auto chi_S_L = S_L;
        auto chi_S_L_prev = S_L_prev;
        if (medium.hasProperty(MPL::PropertyType::bishops_effective_stress))
        {
            auto const chi = [&medium, x_position, t, dt](double const S_L)
            {
                MPL::VariableArray variables;
                variables[static_cast<int>(MPL::Variable::liquid_saturation)] =
                    S_L;
                return medium[MPL::PropertyType::bishops_effective_stress]
                    .template value<double>(variables, x_position, t, dt);
            };
            chi_S_L = chi(S_L);
            chi_S_L_prev = chi(S_L_prev);
        }
        // TODO (buchwaldj)
        // should solid_grain_pressure or effective_pore_pressure remain?
        // double const p_FR = -chi_S_L * p_cap_ip;
        // variables[static_cast<int>(MPL::Variable::solid_grain_pressure)] =
        // p_FR;

        variables[static_cast<int>(MPL::Variable::effective_pore_pressure)] =
            -chi_S_L * p_cap_ip;
        variables_prev[static_cast<int>(
            MPL::Variable::effective_pore_pressure)] =
            -chi_S_L_prev * (p_cap_ip - p_cap_dot_ip * dt);

        auto& phi = _ip_data[ip].porosity;
        {  // Porosity update

            variables_prev[static_cast<int>(MPL::Variable::porosity)] =
                _ip_data[ip].porosity_prev;
            phi = medium[MPL::PropertyType::porosity].template value<double>(
                variables, variables_prev, x_position, t, dt);
            variables[static_cast<int>(MPL::Variable::porosity)] = phi;
        }

        if (alpha < phi)
        {
            OGS_FATAL(
                "ThermoRichardsFlow: Biot-coefficient {} is smaller than "
                "porosity {} in element/integration point {}/{}.",
                alpha, phi, _element.getID(), ip);
        }

        double const k_rel =
            medium[MPL::PropertyType::relative_permeability]
                .template value<double>(variables, x_position, t, dt);
        auto const mu =
            liquid_phase[MPL::PropertyType::viscosity].template value<double>(
                variables, x_position, t, dt);

        auto const K_intrinsic = MPL::formEigenTensor<GlobalDim>(
            medium[MPL::PropertyType::permeability].value(variables, x_position,
                                                          t, dt));

        GlobalDimMatrixType const Ki_over_mu = K_intrinsic / mu;
        GlobalDimMatrixType const rho_Ki_over_mu = rho_LR * Ki_over_mu;

        // Consider anisotropic thermal expansion.
        // Read in 3x3 tensor. 2D case also requires expansion coeff. for z-
        // component.
        Eigen::Matrix<double, 3, 3> const
            solid_linear_thermal_expansion_coefficient =
                MaterialPropertyLib::formEigenTensor<3>(
                    solid_phase
                        [MaterialPropertyLib::PropertyType::thermal_expansivity]
                            .value(variables, x_position, t, dt));

        auto const rho_SR =
            solid_phase[MPL::PropertyType::density].template value<double>(
                variables, x_position, t, dt);

        //
        // pressure equation, pressure part.
        //
        local_K
            .template block<pressure_size, pressure_size>(pressure_index,
                                                          pressure_index)
            .noalias() += dNdx.transpose() * k_rel * rho_Ki_over_mu * dNdx * w;

        const double alphaB_minus_phi = alpha - phi;
        double const a0 = alphaB_minus_phi * beta_SR;
        double const specific_storage_a_p =
            S_L * (phi * beta_LR + S_L * a0 +
                   chi_S_L * alpha * alpha *
                       solid_elasticity.storageContribution(
                           solid_phase, variables, x_position, t, dt));
        double const specific_storage_a_S = phi - p_cap_ip * S_L * a0;

        local_M
            .template block<pressure_size, pressure_size>(pressure_index,
                                                          pressure_index)
            .noalias() += N.transpose() * rho_LR *
                          (specific_storage_a_p -
                           specific_storage_a_S * DeltaS_L_Deltap_cap) *
                          N * w;

        local_rhs.template segment<pressure_size>(pressure_index).noalias() +=
            dNdx.transpose() * rho_LR * k_rel * rho_Ki_over_mu * b * w;

        //
        // pressure equation, temperature part.
        //
        double const fluid_volumetric_thermal_expansion_coefficient =
            MPL::getLiquidThermalExpansivity(liquid_phase, variables, rho_LR,
                                             x_position, t, dt);
        const double eff_thermal_expansion =
            S_L * (alphaB_minus_phi *
                       solid_linear_thermal_expansion_coefficient.trace() +
                   phi * fluid_volumetric_thermal_expansion_coefficient +
                   alpha * solid_elasticity.thermalExpansivityContribution(
                               solid_linear_thermal_expansion_coefficient,
                               solid_phase, variables, x_position, t, dt));

        local_M
            .template block<pressure_size, temperature_size>(pressure_index,
                                                             temperature_index)
            .noalias() -=
            N.transpose() * rho_LR * eff_thermal_expansion * N * w;

        //
        // temperature equation.
        //
        {
            auto const specific_heat_capacity_fluid =
                liquid_phase[MaterialPropertyLib::specific_heat_capacity]
                    .template value<double>(variables, x_position, t, dt);

            auto const specific_heat_capacity_solid =
                solid_phase
                    [MaterialPropertyLib::PropertyType::specific_heat_capacity]
                        .template value<double>(variables, x_position, t, dt);

            local_M
                .template block<temperature_size, temperature_size>(
                    temperature_index, temperature_index)
                .noalias() +=
                w *
                (rho_SR * specific_heat_capacity_solid * (1 - phi) +
                 (S_L * rho_LR * specific_heat_capacity_fluid) * phi) *
                N.transpose() * N;

            auto const thermal_conductivity =
                MaterialPropertyLib::formEigenTensor<GlobalDim>(
                    medium[MaterialPropertyLib::PropertyType::
                               thermal_conductivity]
                        .value(variables, x_position, t, dt));

            GlobalDimVectorType const velocity_L = GlobalDimVectorType(
                -Ki_over_mu * k_rel * (dNdx * p_L - rho_LR * b));

            local_K
                .template block<temperature_size, temperature_size>(
                    temperature_index, temperature_index)
                .noalias() += (dNdx.transpose() * thermal_conductivity * dNdx +
                               N.transpose() * velocity_L.transpose() * dNdx *
                                   rho_LR * specific_heat_capacity_fluid) *
                              w;
        }
        if (liquid_phase.hasProperty(MPL::PropertyType::vapour_diffusion) &&
            S_L < 1.0)
        {
            variables[static_cast<int>(MPL::Variable::density)] = rho_LR;

            double const rho_wv =
                liquid_phase[MaterialPropertyLib::vapour_density]
                    .template value<double>(variables, x_position, t, dt);

            double const drho_wv_dT =
                liquid_phase[MaterialPropertyLib::vapour_density]
                    .template dValue<double>(variables,
                                             MPL::Variable::temperature,
                                             x_position, t, dt);
            double const drho_wv_dp =
                liquid_phase[MaterialPropertyLib::vapour_density]
                    .template dValue<double>(variables,
                                             MPL::Variable::phase_pressure,
                                             x_position, t, dt);
            auto const f_Tv =
                liquid_phase
                    [MPL::PropertyType::thermal_diffusion_enhancement_factor]
                        .template value<double>(variables, x_position, t, dt);

            variables[static_cast<int>(MPL::Variable::porosity)] = phi;
            double const D_v =
                liquid_phase[MPL::PropertyType::vapour_diffusion]
                    .template value<double>(variables, x_position, t, dt);

            double const f_Tv_D_Tv = f_Tv * D_v * drho_wv_dT;
            double const D_pv = D_v * drho_wv_dp;

            if (gas_phase && gas_phase->hasProperty(
                                 MPL::PropertyType::specific_heat_capacity))
            {
                GlobalDimVectorType const grad_T = dNdx * T;
                GlobalDimVectorType const grad_p_cap = -dNdx * p_L;
                // Vapour velocity
                GlobalDimVectorType const q_v =
                    -(f_Tv_D_Tv * grad_T - D_pv * grad_p_cap) / rho_LR;
                double const specific_heat_capacity_vapour =
                    gas_phase
                        ->property(MaterialPropertyLib::PropertyType::
                                       specific_heat_capacity)
                        .template value<double>(variables, x_position, t, dt);

                local_M
                    .template block<temperature_size, temperature_size>(
                        temperature_index, temperature_index)
                    .noalias() +=
                    w *
                    (rho_wv * specific_heat_capacity_vapour * (1 - S_L) * phi) *
                    N.transpose() * N;

                local_K
                    .template block<temperature_size, temperature_size>(
                        temperature_index, temperature_index)
                    .noalias() += N.transpose() * q_v.transpose() * dNdx *
                                  rho_wv * specific_heat_capacity_vapour * w;
            }

            double const storage_coefficient_by_water_vapor =
                phi * (rho_wv * dS_L_dp_cap + (1 - S_L) * drho_wv_dp);
            local_M
                .template block<pressure_size, pressure_size>(pressure_index,
                                                              pressure_index)
                .noalias() +=
                N.transpose() * storage_coefficient_by_water_vapor * N * w;

            double const vapor_expansion_factor = phi * (1 - S_L) * drho_wv_dT;
            local_M
                .template block<pressure_size, temperature_size>(
                    pressure_index, temperature_index)
                .noalias() += N.transpose() * vapor_expansion_factor * N * w;

            local_rhs.template segment<pressure_size>(pressure_index)
                .noalias() -= f_Tv_D_Tv * dNdx.transpose() * (dNdx * T) * w;

            local_K
                .template block<pressure_size, pressure_size>(pressure_index,
                                                              pressure_index)
                .noalias() += dNdx.transpose() * D_pv * dNdx * w;

            //
            // Latent heat term
            //
            if (liquid_phase.hasProperty(MPL::PropertyType::latent_heat))
            {
                double const factor = phi * (1 - S_L) / rho_LR;
                // The volumetric latent heat of vaporization of liquid water
                double const L0 =
                    liquid_phase[MPL::PropertyType::latent_heat]
                        .template value<double>(variables, x_position, t, dt) *
                    rho_LR;

                double const drho_LR_dT =
                    liquid_phase[MPL::PropertyType::density]
                        .template dValue<double>(variables,
                                                 MPL::Variable::temperature,
                                                 x_position, t, dt);

                double const rho_wv_over_rho_L = rho_wv / rho_LR;
                local_M
                    .template block<temperature_size, temperature_size>(
                        temperature_index, temperature_index)
                    .noalias() +=
                    factor * L0 *
                    (drho_wv_dT - rho_wv_over_rho_L * drho_LR_dT) *
                    N.transpose() * N * w;

                local_M
                    .template block<temperature_size, pressure_size>(
                        temperature_index, pressure_index)
                    .noalias() +=
                    (factor * L0 *
                         (drho_wv_dp - rho_wv_over_rho_L * drho_LR_dp) +
                     L0 * phi * rho_wv_over_rho_L * dS_L_dp_cap) *
                    N.transpose() * N * w;

                // temperature equation, temperature part
                local_K
                    .template block<temperature_size, temperature_size>(
                        temperature_index, temperature_index)
                    .noalias() +=
                    L0 * f_Tv_D_Tv * dNdx.transpose() * dNdx * w / rho_LR;
                // temperature equation, pressure part
                local_K
                    .template block<temperature_size, pressure_size>(
                        temperature_index, pressure_index)
                    .noalias() +=
                    L0 * D_pv * dNdx.transpose() * dNdx * w / rho_LR;
            }
        }
    }

    if (_process_data.apply_mass_lumping)
    {
        auto Mpp = local_M.template block<pressure_size, pressure_size>(
            pressure_index, pressure_index);
        Mpp = Mpp.colwise().sum().eval().asDiagonal();
    }
}

template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
std::vector<double> const&
ThermoRichardsFlowLocalAssembler<ShapeFunction, IntegrationMethod, GlobalDim>::
    getIntPtDarcyVelocity(
        const double /*t*/,
        std::vector<GlobalVector*> const& /*x*/,
        std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/,
        std::vector<double>& cache) const
{
    unsigned const n_integration_points =
        _integration_method.getNumberOfPoints();

    cache.clear();
    auto cache_matrix = MathLib::createZeroedMatrix<
        Eigen::Matrix<double, GlobalDim, Eigen::Dynamic, Eigen::RowMajor>>(
        cache, GlobalDim, n_integration_points);

    for (unsigned ip = 0; ip < n_integration_points; ip++)
    {
        cache_matrix.col(ip).noalias() = _ip_data[ip].v_darcy;
    }

    return cache;
}

template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
std::vector<double> ThermoRichardsFlowLocalAssembler<
    ShapeFunction, IntegrationMethod, GlobalDim>::getSaturation() const
{
    std::vector<double> result;
    getIntPtSaturation(0, {}, {}, result);
    return result;
}

template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
std::vector<double> const&
ThermoRichardsFlowLocalAssembler<ShapeFunction, IntegrationMethod, GlobalDim>::
    getIntPtSaturation(
        const double /*t*/,
        std::vector<GlobalVector*> const& /*x*/,
        std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/,
        std::vector<double>& cache) const
{
    return ProcessLib::getIntegrationPointScalarData(
        _ip_data, &IpData::saturation, cache);
}

template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
std::vector<double> ThermoRichardsFlowLocalAssembler<
    ShapeFunction, IntegrationMethod, GlobalDim>::getPorosity() const
{
    std::vector<double> result;
    getIntPtPorosity(0, {}, {}, result);
    return result;
}

template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
std::vector<double> const&
ThermoRichardsFlowLocalAssembler<ShapeFunction, IntegrationMethod, GlobalDim>::
    getIntPtPorosity(
        const double /*t*/,
        std::vector<GlobalVector*> const& /*x*/,
        std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/,
        std::vector<double>& cache) const
{
    return ProcessLib::getIntegrationPointScalarData(_ip_data,
                                                     &IpData::porosity, cache);
}

template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
std::vector<double> const&
ThermoRichardsFlowLocalAssembler<ShapeFunction, IntegrationMethod, GlobalDim>::
    getIntPtDryDensitySolid(
        const double /*t*/,
        std::vector<GlobalVector*> const& /*x*/,
        std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/,
        std::vector<double>& cache) const
{
    return ProcessLib::getIntegrationPointScalarData(
        _ip_data, &IpData::dry_density_solid, cache);
}

template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
void ThermoRichardsFlowLocalAssembler<ShapeFunction, IntegrationMethod,
                                      GlobalDim>::
    computeSecondaryVariableConcrete(double const t, double const dt,
                                     Eigen::VectorXd const& local_x,
                                     Eigen::VectorXd const& local_x_dot)
{
    auto const T =
        local_x.template segment<temperature_size>(temperature_index);

    auto const T_dot =
        local_x_dot.template segment<temperature_size>(temperature_index);

    auto const p_L = local_x.template segment<pressure_size>(pressure_index);

    auto p_L_dot = local_x_dot.template segment<pressure_size>(pressure_index);

    auto const& medium = *_process_data.media_map->getMedium(_element.getID());
    auto const& liquid_phase = medium.phase("AqueousLiquid");
    auto const& solid_phase = medium.phase("Solid");
    MPL::VariableArray variables;
    MPL::VariableArray variables_prev;

    ParameterLib::SpatialPosition x_position;
    x_position.setElementID(_element.getID());

    unsigned const n_integration_points =
        _integration_method.getNumberOfPoints();

    double saturation_avg = 0;
    double porosity_avg = 0;

    for (unsigned ip = 0; ip < n_integration_points; ip++)
    {
        x_position.setIntegrationPoint(ip);
        auto const& N = _ip_data[ip].N;

        double T_ip;
        NumLib::shapeFunctionInterpolate(T, N, T_ip);
        double T_dot_ip;
        NumLib::shapeFunctionInterpolate(T_dot, N, T_dot_ip);

        double p_cap_ip;
        NumLib::shapeFunctionInterpolate(-p_L, N, p_cap_ip);

        double p_cap_dot_ip;
        NumLib::shapeFunctionInterpolate(-p_L_dot, N, p_cap_dot_ip);

        variables[static_cast<int>(MPL::Variable::capillary_pressure)] =
            p_cap_ip;
        variables[static_cast<int>(MPL::Variable::phase_pressure)] = -p_cap_ip;

        variables[static_cast<int>(MPL::Variable::temperature)] = T_ip;

        auto& S_L = _ip_data[ip].saturation;
        auto const S_L_prev = _ip_data[ip].saturation_prev;
        S_L = medium[MPL::PropertyType::saturation].template value<double>(
            variables, x_position, t, dt);
        variables[static_cast<int>(MPL::Variable::liquid_saturation)] = S_L;
        variables_prev[static_cast<int>(MPL::Variable::liquid_saturation)] =
            S_L_prev;

        auto chi_S_L = S_L;
        auto chi_S_L_prev = S_L_prev;
        if (medium.hasProperty(MPL::PropertyType::bishops_effective_stress))
        {
            auto const chi = [&medium, x_position, t, dt](double const S_L)
            {
                MPL::VariableArray variables;
                variables[static_cast<int>(MPL::Variable::liquid_saturation)] =
                    S_L;
                return medium[MPL::PropertyType::bishops_effective_stress]
                    .template value<double>(variables, x_position, t, dt);
            };
            chi_S_L = chi(S_L);
            chi_S_L_prev = chi(S_L_prev);
        }
        variables[static_cast<int>(MPL::Variable::effective_pore_pressure)] =
            -chi_S_L * p_cap_ip;
        variables_prev[static_cast<int>(
            MPL::Variable::effective_pore_pressure)] =
            -chi_S_L_prev * (p_cap_ip - p_cap_dot_ip * dt);

        auto const alpha =
            medium[MPL::PropertyType::biot_coefficient].template value<double>(
                variables, x_position, t, dt);

        auto& solid_elasticity = *_process_data.simplified_elasticity;
        auto const beta_S =
            solid_elasticity.bulkCompressibilityFromYoungsModulus(
                solid_phase, variables, x_position, t, dt);
        auto const beta_SR = (1 - alpha) * beta_S;
        variables[static_cast<int>(MPL::Variable::grain_compressibility)] =
            beta_SR;

        auto& phi = _ip_data[ip].porosity;
        {  // Porosity update
            variables_prev[static_cast<int>(MPL::Variable::porosity)] =
                _ip_data[ip].porosity_prev;
            phi = medium[MPL::PropertyType::porosity].template value<double>(
                variables, variables_prev, x_position, t, dt);
            variables[static_cast<int>(MPL::Variable::porosity)] = phi;
        }

        auto const mu =
            liquid_phase[MPL::PropertyType::viscosity].template value<double>(
                variables, x_position, t, dt);
        auto const rho_LR =
            liquid_phase[MPL::PropertyType::density].template value<double>(
                variables, x_position, t, dt);

        auto const K_intrinsic = MPL::formEigenTensor<GlobalDim>(
            medium[MPL::PropertyType::permeability].value(variables, x_position,
                                                          t, dt));

        double const k_rel =
            medium[MPL::PropertyType::relative_permeability]
                .template value<double>(variables, x_position, t, dt);

        GlobalDimMatrixType const K_over_mu = k_rel * K_intrinsic / mu;

        auto const rho_SR =
            solid_phase[MPL::PropertyType::density].template value<double>(
                variables, x_position, t, dt);
        _ip_data[ip].dry_density_solid = (1 - phi) * rho_SR;

        auto const& b = _process_data.specific_body_force;

        // Compute the velocity
        auto const& dNdx = _ip_data[ip].dNdx;
        _ip_data[ip].v_darcy.noalias() =
            -K_over_mu * dNdx * p_L + rho_LR * K_over_mu * b;

        saturation_avg += S_L;
        porosity_avg += phi;
    }
    saturation_avg /= n_integration_points;
    porosity_avg /= n_integration_points;

    (*_process_data.element_saturation)[_element.getID()] = saturation_avg;
    (*_process_data.element_porosity)[_element.getID()] = porosity_avg;
}

template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
unsigned ThermoRichardsFlowLocalAssembler<
    ShapeFunction, IntegrationMethod, GlobalDim>::getNumberOfIntegrationPoints()
    const
{
    return _integration_method.getNumberOfPoints();
}

}  // namespace ThermoRichardsFlow
}  // namespace ProcessLib
back to top