Numerical implementation of the time derivatives
Introduction
The time derivatives in PorousFlow are usually lumped to the nodes. In mechanically-coupled systems, there is an added complication of mesh deformation, and this page describes the particular implementation used in PorousFlow. This page is self contained and uses notation that might be different from other PorousFlow documentation.
Consider a volume of porous material, . can be thought of as one finite element, or a region surrounding a node, but it could be any volume. It is "attached" to the porous material (it is in the Lagrangian reference frame): when the porous material moves or deforms, so does , relative to an external observer (in the Eulerian reference frame) who is viewing the movement or deformation from outside the porous material.
The volume contains a mass density of fluid, (measured in kg.m, where the m is the volume of porous material). In PorousFlow, , but that is irrelevant here. (The volume could also contain a heat-energy density of fluid, and the same form of equation follows for it, so could also be thought of as heat-energy density.) The total fluid mass within is
Assume that there are sinks of fluid mass within , and denote these by (measured in kg.m.s). There could also be boundary fluxes of fluid exiting or entering , but those can be handled in the same way as , so can be ignored. The fundamental equation of mass conservation is
(1)
Simple worked example
In a small abuse of notation, denote the volume of the domain by , that is . For simplicity of presentation, consider the situation where , that is, does not vary spatially over the volume . Remember that can vary with . Then the first term of Eq. (1) may be written Consider two times, labelled "old" and "new", with time difference . Then
Connection with PorousFlow
To make the connection with PorousFlow clearer, assume that there is a displacement vector, , that defines the deformation . Then the volume , where is the volume in the undeformed material. The volumetric strain is (at least for small strains), and . Using this notation, substituting into Eq. (1) yields (2) PorousFlow implements this as follows.
The term is PorousFlowMassTimeDerivative or PorousFlowEnergyTimeDerivative. Note that is implemented in the code as which involves the total volumetric strain, as calculated by a TensorMechanics strain calculator, for instance ComputeSmallStrain. It is integrated over the undeformed mesh so should employ
use_displaced_mesh = false
.The term is PorousFlowMassVolumetricExpansion or PorousFlowHeatVolumetricExpansion. The is implemented as , which is the time-derivative of the total volumetric strain as calculated by a TensorMechanics strain calculator, and fed into the PorousFlowVolumetricStrain Material. It also should employ
use_displaced_mesh = false
.
Mass and energy conservation
The PorousFlowFluidMass and PorousFlowHeatEnergy postprocessors use the total volumetric strain as calculated by PorousFlowVolumetricStrain. Because the Kernels and the Postprocessors all use the same approach (assuming the base_name
and use_displaced_mesh
parameters are set correctly) mass and energy conservation is assured.
Use of total strain
The PorousFlow implementation of is to use the total_strain
as calculated by the TensorMechanics strain calculator, which in almost all PorousFlow models is ComputeSmallStrain. Since the TensorMechanics strain calculators have a base_name
input, users should take care to choose the appropriate base_name
(without any typos) since PorousFlow cannot detect errors in this input. PorousFlow simply checks whether a Material property called base_name_total_strain
exists, and if so PorousFlow uses it, otherwise it continues without any total-strain contributions (corresponding to no mechanical coupling).
By far the most common case is where base_name
is not defined in the TensorMechanics strain calculator, so also does not need to be defined in PorousFlow. However, in more complicated situations, some potential use-cases and errors are:
The simulation is mechanically coupled, but the user doesn't defined any TensorMechanics strain material. Solution: define a strain material.
The user desires a simulation to be mechanically coupled, but the user provides a
base_name
to PorousFlow that does not correspond to any TensorMechanics strain material. In this case, PorousFlow simply thinks the user doesn't want mechanical coupling, and blindly continues. Solution: ensurebase_name
is set to a TensorMechanics strain material.The simulation is mechanically coupled, but the user wants to be defined separately from the TensorMechanics kinematics and constitutive law. For instance, TensorMechanics might use finite strain, but PorousFlow might use small strains. Solution: define more than one strain material, each with its own
base_name
, and provide PorousFlow with the appropriatebase_name
.The simulation is not mechanically coupled, but the user has a strain material in the input file that gets inadvertantly used by PorousFlow. This is very difficult to debug, since PorousFlow does not warn it is using the strain material: PorousFlow simply thinks the user desires mechanical coupling. Solution: provide something like
base_name = non_existent
: PorousFlow will detect there is no such strain calculator, and continue assuming no mechanical coupling.