Skip to content

API — Poromechanics

MeanFieldHomogenization.Poromechanics Module
julia
MeanFieldHomogenization.Poromechanics

Poroelastic upscaling: the Biot tensor , the Biot modulus , the drained ↔ undrained conversion and the Skempton tensor of a saturated porous or fractured medium whose solid phase is homogeneous.

The sub-module is a post-processor, not a scheme. It takes a drained homogenized stiffness C_hom — from any cell and any scheme, or from elsewhere entirely — together with the solid stiffness C_s and the Lagrangian porosity φ, and closes the poroelastic constitutive law

using the classical poroelastic relations ([70]) in the form quoted as eq. (2) of [71]. No additional Eshelby problem is solved: everything follows from C_hom, C_s and φ.

Entry points

FunctionReturns
biot_tensor    
inverse_biot_modulus    
biot_modulus (Inf for an incompressible solid)
poroelastic_parametersall three at once, inverting once
undrained_stiffness / drained_stiffness     and back
skempton_tensor such that     undrained
terzaghi_stress / biot_effective_stressthe two effective-stress measures
pore_volume_fraction summed over declared pore phases

Everything is plain TensND algebra, hence type-generic (Float64, BigFloat, ForwardDiff.Dual, ComplexF64, SymPy.Sym) at no cost.

Homogeneous solid phase

The relations above hold when the solid phase has uniform elastic properties — the case of a rock matrix with pores or fractures. A medium built from two distinct solid constituents needs the general Levin/eigenstrain route, and C_s is then not defined.

Biot parameters

MeanFieldHomogenization.Poromechanics.biot_tensor Function
julia
biot_tensor(C_hom, C_s) -> Tens{2,3}

Biot tensor of a porous medium with a homogeneous solid phase of stiffness C_s, from its drained homogenized stiffness C_hom:

\boldsymbol{B} is the tensor appearing in the poroelastic law     ; it is generally anisotropic even when the solid is isotropic, because the pore space is not.

Two limits are worth remembering as sanity checks: a homogeneous medium (C_hom == C_s) gives  , and a vanishing drained stiffness gives  . For an isotropic medium the tensor collapses to   with the familiar   .

Homogeneous solid phase only

These relations rest on the solid phase having uniform elastic properties. A medium with two distinct solid constituents needs the full Levin/eigenstrain route instead, and C_s is then not defined.

Pass the solid stiffness, not its compliance — the inverse is taken internally. Computing several poroelastic parameters at once is cheaper through poroelastic_parameters, which inverts C_s only once.

See also inverse_biot_modulus, undrained_stiffness, terzaghi_stress.

julia
biot_tensor(rve::RVE, C_hom; solid = nothing, property = :C) -> Tens{2,3}

biot_tensor with the skeleton stiffness read from the phase that plays the homogeneous solid of microporomechanics.

solid names that phase. Left unset it is the one taking up the volume complement, which is the usual reading of a porous RVE; an RVE that designates none has to be told, because no container can know which of its phases is the skeleton.

MeanFieldHomogenization.Poromechanics.inverse_biot_modulus Function
julia
inverse_biot_modulus(C_s, B, φ) -> Number

Inverse Biot modulus (also written ) of a porous medium with a homogeneous solid phase:

φ is the Lagrangian porosity of the connected pore space in the reference configuration — the volume fraction actually occupied by the fluid, which for a fractured medium is the crack volume fraction and therefore depends on the current apertures rather than on the RVE alone (see pore_volume_fraction).

For an isotropic medium this reduces to the textbook   .

Incompressible saturating fluid

This expression assumes the pore fluid is incompressible, which is the setting of [71]. A fluid of finite bulk modulus adds the storage term :

Add it yourself if the fluid compressibility matters — the function does not, since it has no way of knowing k_f. The distinction is not cosmetic: with an incompressible fluid and a compressible solid the fluid is effectively stiffer than the grains, and the Skempton coefficient then exceeds one (see skempton_tensor).

The inverse is the primitive rather than itself because it is the quantity that stays finite: an incompressible solid phase gives  , i.e.  . biot_modulus returns Inf in that case, which is correct but not something to feed to a linear solver.

See also biot_tensor, biot_modulus, poroelastic_parameters.

MeanFieldHomogenization.Poromechanics.biot_modulus Function
julia
biot_modulus(C_s, B, φ) -> Number

Biot modulus  , the reciprocal of inverse_biot_modulus.

Returns Inf for an incompressible solid phase ( ). Prefer inverse_biot_modulus wherever the value is assembled into a system matrix.

MeanFieldHomogenization.Poromechanics.poroelastic_parameters Function
julia
poroelastic_parameters(C_hom, C_s, φ) -> NamedTuple

Every poroelastic parameter of a saturated medium with a homogeneous solid phase, in one pass:

julia
(; B, inverse_modulus, modulus)

with B the biot_tensor, inverse_modulus the inverse_biot_modulus and modulus its reciprocal .

C_s is inverted once, which is why this is the entry point the Gauss-point materials use rather than the three functions separately.

julia
julia> using MeanFieldHomogenization, TensND

julia> C_s = TensISO{3}(3 * 20.0, 2 * 12.0);   # k_s = 20, μ_s = 12

julia> rve = RVE();

julia> add_phase!(rve, :M, Ellipsoid(1.0), Dict(:C => C_s); fraction = :rest);

julia> add_phase!(rve, :P, Ellipsoid(1.0), Dict(:C => TensISO{3}(1.0e-9, 1.0e-9));
                  fraction = 0.2);

julia> par = poroelastic_parameters(homogenize(rve, MoriTanaka()), C_s, 0.2);

julia> round(par.B[1, 1], digits = 4)   # b = 1 - k_MT/k_s = 1 - 12.8/20
0.36
julia
poroelastic_parameters(rve::RVE, C_hom, φ; solid = nothing, property = :C) -> NamedTuple

poroelastic_parameters with the skeleton stiffness read from the phase that plays the homogeneous solid — see biot_tensor for solid.

Drained ↔ undrained

MeanFieldHomogenization.Poromechanics.undrained_stiffness Function
julia
undrained_stiffness(C_hom, B, M) -> Tens{4,3}

Undrained stiffness     .

It is the tangent stiffness of the closed system, obtained by eliminating p from the poroelastic law under the undrained condition  . M = Inf (incompressible solid and fluid) makes the result infinite, which is the correct statement that no volume change is possible.

See also drained_stiffness, skempton_tensor.

MeanFieldHomogenization.Poromechanics.drained_stiffness Function
julia
drained_stiffness(C_u, B, M) -> Tens{4,3}

Inverse of undrained_stiffness: recover the drained stiffness      from an undrained measurement.

MeanFieldHomogenization.Poromechanics.skempton_tensor Function
julia
skempton_tensor(C_hom, B, M) -> Tens{2,3}

Skempton tensor     ( ), which gives the pore pressure built up by an undrained stress increment:

Under isotropic compression     this yields    , so   plays the role of the classical scalar Skempton coefficient. In the isotropic case it reduces to , equivalently

The bound B ≤ 1 does not hold here

The familiar    assumes a fluid no stiffer than the solid grains,  . inverse_biot_modulus assumes an incompressible fluid ( ), so with a compressible solid the last term above is negative and  : the pore pressure exceeds the applied mean stress, because the pore volume is held fixed while the grains themselves compress. This is a genuine consequence of the assumption, not a numerical artifact. Strong anisotropy of the pore space, aligned cracks for instance, likewise removes any scalar bound.

Effective stresses and porosity

MeanFieldHomogenization.Poromechanics.terzaghi_stress Function
julia
terzaghi_stress(Σ, p) -> Tens{2,3}

Terzaghi effective stress    .

This is the loading measure that drives the microstructure: for a porous or fractured medium whose solid phase has uniform elastic properties, the problem defined by splits into

  1. a dry problem (no fluid pressure) under the macroscopic stress   , during which pores and fractures may open or close, and

  2. a superimposed loading    whose solution is the uniform pair    ,     .

Step 2 carries no strain singularity, so it cannot change the aperture of a flat crack. All the information about the evolution of the pore space is therefore contained in step 1, i.e. it depends on the Terzaghi effective stress alone. This is the argument of [71] § 1.1 and the reason why the constitutive laws of MeanFieldHomogenization.Constitutive drive their internal state with terzaghi_stress rather than with \boldsymbol{\Sigma}.

Do not confuse it with biot_effective_stress, which is the measure that makes the macroscopic constitutive law take its drained form.

See also biot_tensor.

MeanFieldHomogenization.Poromechanics.biot_effective_stress Function
julia
biot_effective_stress(Σ, p, B) -> Tens{2,3}

Biot effective stress   , the measure for which the poroelastic law      reduces to the drained relation     .

It coincides with terzaghi_stress only when  , i.e. for an incompressible solid phase.

See also biot_tensor, terzaghi_stress.

MeanFieldHomogenization.Poromechanics.pore_volume_fraction Function
julia
pore_volume_fraction(rve::RVE, names) -> Number

Sum of the volume fractions of the phases listed in names — the Lagrangian porosity of the connected pore space, for use as the φ argument of inverse_biot_modulus and poroelastic_parameters.

names is any iterable of phase symbols; a single Symbol is accepted too. Passing the pore phases explicitly is deliberate: an RVE has no way of knowing which of its soft inclusions are fluid-filled and connected, and guessing would silently produce a wrong Biot modulus.

Crack phases carry no volume

A CrackDensity phase contributes zero here, because a flat crack has no volume: its fraction     depends on the aspect ratio, which the crack geometry does not carry and which evolves during a simulation. For a fractured medium the porosity must therefore be assembled from the current apertures — that is what MeanFieldHomogenization.Constitutive does — and passed to poroelastic_parameters directly.