Skip to content

API — Laminate

The cell

MeanFieldHomogenization.Laminates Module
julia
MeanFieldHomogenization.Laminates

Periodic multilayer homogenization: a unit cell of parallel layers of common normal n, with no matrix, no auxiliary Eshelby problem and no reference medium — and an exact analytical solution rather than an estimate.

This is a different kind of microstructure from the one the rest of the package addresses. The schemes of Schemes (bounds, dilute, Mori-Tanaka, Maxwell, Ponte-Castañeda-Willis, self-consistent, differential) all describe random morphologies through the Eshelby auxiliary problem; some of them single out a matrix phase and some do not, but all of them estimate. A laminate is periodic and deterministic, and its effective behavior follows in closed form from two continuity conditions:

  • the traction σ·n is continuous — the out-of-plane stress components;

  • the in-plane strain is continuous and equal to the macroscopic one, so ε_i = E + a_i ⊗ˢ n.

The exported Laminate is therefore an AbstractHomogenizationCell alongside RVE, solved by the Laminated scheme, and it plugs into the multiscale chain like any other cell — including declaratively, as the value of a phase property (see Homogenized).

Provides:

The block algebra itself lives in Core/laminate_algebra.jl, so that the ageing-viscoelastic laminate reuses the very same kernel with the Volterra inversion substituted.

MeanFieldHomogenization.Laminates.Laminate Type
julia
Laminate{T<:Number, B<:TensND.AbstractBasis{3}} <: AbstractHomogenizationCell

Periodic unit cell of parallel layers normal to n — the deterministic, matrix-free counterpart of an RVE, solved exactly by Laminated.

Fields:

  • layer_names::Vector{Symbol} — layers in stacking order;

  • layers::Dict{Symbol,Layer} — per-layer property dicts;

  • thicknesses::Dict{Symbol,Any}h_i, each keeping its own element type (Float64, Dual, Sym, …), exactly as RVE.amounts does;

  • interfaces::Vector{AbstractInterface} — one per layer; entry k is the interface on top of layer k, entry N closing the cell back onto layer 1 by periodicity;

  • basis::B — orthonormal (ℓ, m, n), whose third axis is the layer normal. Storing a basis rather than a bare n fixes (ℓ, m) deterministically, which matters for anisotropic layers;

  • period — cached L = Σ h_i.

Thicknesses rather than fractions are the stored primitive: the physics needs both f_i = h_i/L and L, and one dict gives both. L is what carries the size effect of imperfect interfaces, which enter with weight 1/L — an interface density. With perfect interfaces the result depends on the f_i alone and L is irrelevant.

T is the declared element-type floor, exactly as in RVE: a Dual or symbolic thickness lives happily in a plain Laminate().

Construction is two-step, mirroring RVE:

julia
lam = Laminate(; normal = (0, 0, 1))
add_layer!(lam, :A, Dict(:C => C_A); fraction = 0.4)
add_layer!(lam, :B, Dict(:C => C_B); fraction = 0.6,
           interface = SpringInterface(1.0e3, 5.0e2))
homogenize(lam, Laminated(), :C)

See also add_layer!, layer_property, laminate_period, validate_laminate.

MeanFieldHomogenization.Laminates.Layer Type
julia
Layer(properties)

One layer of a Laminate. Carries only material properties: unlike a Phase, a layer has no inclusion geometry of its own — its geometry is the stacking direction, shared by the whole cell.

A property value may itself be a Homogenized cell + scheme, resolved lazily at homogenize time (declarative multiscale nesting).

MeanFieldHomogenization.Laminates.add_layer! Function
julia
add_layer!(lam, name::Symbol, properties::AbstractDict;
           thickness = nothing, fraction = nothing,
           interface = PerfectInterface())

Append a layer to the top of the stack.

Give exactly one of thickness (an absolute height) or fraction. Both are stored in the same place: a fraction is simply a thickness in a cell whose period is 1, and layer_volume_fraction derives f_i = h_i / L either way. Nothing rescales the stack for you, and nothing checks that the fractions sum to one — that check cannot be made for a symbolic or Dual thickness, and making it only for Real ones would be a rule that silently comes and goes. Mixing the two forms across layers is therefore possible but means what it says: the period is the plain sum, so with imperfect interfaces — which enter with the weight 1/L — read laminate_period before trusting a mixed stack.

interface is the condition on top of this layer; the last layer's interface closes the cell onto the first by periodicity. The five types of LayeredSpheres are reused unchanged: PerfectInterface, SpringInterface, MembraneInterface, KapitzaInterface, SurfaceConductiveInterface.

MeanFieldHomogenization.Laminates.layer_names Function
julia
layer_names(lam) -> Vector{Symbol}

Layer names in stacking order.

MeanFieldHomogenization.Laminates.layer_property Function
julia
layer_property(lam, name::Symbol, key::Symbol)

Property key (:C, :K, …) of layer name.

A stored Homogenized is resolved here — memoized for the duration of the enclosing homogenize call — so the kernel always sees a plain tensor. Use layer_property_raw to inspect the stored value.

MeanFieldHomogenization.Laminates.layer_property_raw Function
julia
layer_property_raw(lam, name::Symbol, key::Symbol)

The value stored under key on layer name, without resolving a Homogenized.

MeanFieldHomogenization.Laminates.layer_thickness Function
julia
layer_thickness(lam, name::Symbol) -> Number

Thickness h_i of layer name.

MeanFieldHomogenization.Laminates.laminate_period Function
julia
laminate_period(lam) -> Number

Period L = Σ h_i of the cell. Imperfect interfaces enter the effective property with weight 1/L, so this is what sets their size effect; with perfect interfaces the result is independent of it.

MeanFieldHomogenization.Laminates.laminate_basis Function
julia
laminate_basis(lam) -> AbstractBasis

The orthonormal frame (ℓ, m, n) of the cell; its third axis is the layer normal.

MeanFieldHomogenization.Laminates.laminate_normal Function
julia
laminate_normal(lam) -> NTuple{3}

The layer normal n, i.e. the third axis of laminate_basis, in canonical components.

MeanFieldHomogenization.Laminates.validate_laminate Function
julia
validate_laminate(lam)

Sanity-check the cell: at least one layer, one interface per layer, non-negative thicknesses and a strictly positive period.

The requirement that validate_rve imposes and this one cannot — a registered matrix phase — is exactly why validate_cell exists. Non-Real thicknesses (symbolic, Dual) skip the inequality checks, as elsewhere in the package.

Anisotropic interfaces

A plane, unlike a sphere, imposes no symmetry on the interface, so a laminate also accepts tensor-valued interface properties.

MeanFieldHomogenization.Laminates.AnisotropicSpringInterface Type
julia
AnisotropicSpringInterface(𝕂)

Imperfect interface of spring type with a full compliance tensor:

the traction staying continuous. Generalizes SpringInterface, whose two stiffnesses describe the isotropic case 𝕂 = n⊗n/kn + (δ − n⊗n)/kt; here 𝕂 is any symmetric second-order compliance, so the normal and the two tangential directions may each have their own compliance and be coupled.

𝕂 is either a 3×3 matrix — read as components in the layer frame (ℓ, m, n), the third axis being the normal — or a TensND second-order tensor carrying its own basis, converted on use.

𝕂 = 0 recovers PerfectInterface.

julia
# a stiffer normal spring than tangential, plus an in-plane texture
𝕂 = [3.0e-3 5.0e-4 0.0; 5.0e-4 8.0e-3 0.0; 0.0 0.0 1.0e-3]
add_layer!(lam, :A, Dict(:C => C_A); thickness = 0.3,
           interface = AnisotropicSpringInterface(𝕂))
MeanFieldHomogenization.Laminates.AnisotropicMembraneInterface Type
julia
AnisotropicMembraneInterface(ℂˢ)

Surface-elastic (Gurtin-Murdoch) interface with a full in-plane surface stiffness, generalizing MembraneInterface — whose two moduli (κs, μs) describe the isotropic case. A 2-D elastic surface has six independent coefficients, all of them available here.

ℂˢ is either a 3×3 matrix — the in-plane Kelvin-Mandel block on the basis (ℓ⊗ℓ, m⊗m, √2 ℓ⊗ˢm), so that its [3,3] entry is 2 C^s_{1212} — or a TensND fourth-order tensor, whose in-plane block in the layer frame is taken.

The interfaces being planar there is no traction jump, so this adds directly to the effective stiffness, in the in-plane block, with the weight 1/L.

julia
# an orthotropic membrane: stiffer along ℓ than along m
ℂˢ = [0.20 0.05 0.0; 0.05 0.09 0.0; 0.0 0.0 0.06]
add_layer!(lam, :A, Dict(:C => C_A); thickness = 0.3,
           interface = AnisotropicMembraneInterface(ℂˢ))
MeanFieldHomogenization.Laminates.AnisotropicSurfaceConductiveInterface Type
julia
AnisotropicSurfaceConductiveInterface(𝐤ˢ)

Highly conductive 2-D surface layer with a full in-plane surface conductivity, generalizing SurfaceConductiveInterface — whose single scalar describes the isotropic case.

𝐤ˢ is either a 3×3 matrix in the layer frame or a TensND second-order tensor; only its in-plane part is used, the surface flux being driven by the in-plane gradient. Adds to the effective conductivity with the weight 1/L.

There is deliberately no anisotropic counterpart of KapitzaInterface: the primal transport condition [T] = ρ q_n relates two scalars, so the single resistance is already fully general.

Fields and per-layer tensors

MeanFieldHomogenization.Laminates.laminate_hill Function
julia
laminate_hill(lam, name::Symbol; property = :C) -> (ℙ, ℚ)

The two Hill tensors of one layer, as TensND tensors in the laminate basis: ℙ = n ⊗ˢ 𝐊⁻¹ ⊗ˢ n — the flat-inclusion limit of the Hill polarization tensor of an ellipsoid embedded in that layer's own material — and ℚ = ℂ − ℂ:ℙ:ℂ, the in-plane Schur complement.

Exposed for the theory page and for checking the identities ℙ:ℂ:ℙ = ℙ and ℚ:ℙ = 0; the kernel itself never materializes them as tensors.

MeanFieldHomogenization.Laminates.layer_strain_localization Function
julia
layer_strain_localization(lam, name::Symbol; property = :C) -> Tens{4,3}

Strain localization tensor 𝔸_i of one layer: ε_i = 𝔸_i : E, with 𝔸_i = 𝕀 + ℙ_i : (ℂ^{hom} − ℂ_i) and Σ_i f_i 𝔸_i = 𝕀.

Its in-plane block is the identity and its in-plane/out-of-plane coupling block vanishes — the macroscopic in-plane strain reaches every layer unchanged, which is the compatibility condition ε_i = E + a_i ⊗ˢ n.

Synonym of strain_strain_loc(lam, name), the package-wide generic name; the two return the same object.

Defined for perfect interfaces; with a primal (spring / Kapitza) interface part of the macroscopic strain is carried by the jumps, so the layer strains no longer average to E — see interface_jump.

MeanFieldHomogenization.Laminates.layer_stress_localization Function
julia
layer_stress_localization(lam, name::Symbol; property = :C) -> Tens{4,3}

Stress localization tensor 𝔹_i of one layer: σ_i = 𝔹_i : Σ, with 𝔹_i = ℂ_i : 𝔸_i : (ℂ^{hom})^{-1} and Σ_i f_i 𝔹_i = 𝕀.

Synonym of stress_stress_loc(lam, name), the package-wide generic name; the two return the same object.

MeanFieldHomogenization.Laminates.layer_gradient_localization Function
julia
layer_gradient_localization(lam, name::Symbol; property = :K) -> Tens{2,3}

Transport counterpart of layer_strain_localization: ∇T_i = 𝐀_i · ∇T, with Σ_i f_i 𝐀_i = 𝟏.

Synonym of gradient_gradient_loc(lam, name).

MeanFieldHomogenization.Laminates.layer_flux_localization Function
julia
layer_flux_localization(lam, name::Symbol; property = :K) -> Tens{2,3}

Transport counterpart of layer_stress_localization: q_i = 𝐁_i · q, with Σ_i f_i 𝐁_i = 𝟏.

Synonym of flux_flux_loc(lam, name).

MeanFieldHomogenization.Laminates.interface_jump Function
julia
interface_jump(lam, k::Integer, E; property = :C) -> NTuple{3}

Displacement jump [u] = 𝕂_k · (Σ · n) across the k-th interface, under the macroscopic strain E, with Σ = ℂ^{hom} : E. Returned in canonical components. Zero for a perfect or a dual (membrane) interface, which produce no jump.

This is the observable that distinguishes a spring interface from a softer layer: the compliance shows up as a discontinuity, not as a strain.

A layer also answers the package-wide localization generics — strain_strain_loc, stress_strain_loc, strain_stress_loc, stress_stress_loc and their four transport twins — called as strain_strain_loc(lam, :LAYER). Since a laminate has neither a matrix nor a reference medium, the layer name takes the place of the (ℂ₁, ℂ₀) pair of the inclusion signature. Two of the eight (𝔸^{σε} and 𝔸^{εσ}) exist only under those names; the other six are synonyms of the layer_* functions above. See API — Localization & contribution.

Parameter lenses

MeanFieldHomogenization.Laminates.ThicknessParameter Type
julia
ThicknessParameter(layer::Symbol)
thickness(layer::Symbol)

Lens on the thickness h_i of one layer of a Laminate.

Differentiating with respect to a thickness is not the same as differentiating with respect to a volume fraction: changing h_i changes both f_i (through L) and the period, hence the 1/L weight of every imperfect interface. With perfect interfaces the two coincide up to the chain rule; with a spring interface the thickness derivative also carries the size effect, which is usually what one wants.

MeanFieldHomogenization.Laminates.thickness Function
julia
thickness(layer::Symbol) -> ThicknessParameter

Convenience constructor for ThicknessParameter, mirroring amount, property and geometry.

MeanFieldHomogenization.Laminates.InterfaceParameter Type
julia
InterfaceParameter(index::Int, field::Symbol)
interface_param(index::Int, field::Symbol)

Lens on one scalar parameter of the index-th interface of a Laminate:kn, :kt (spring stiffnesses) or equivalently :sn, :st (the matching compliances, which is what SpringInterface stores); :κs, :μs (surface moduli); :resistance (Kapitza); :conductance (surface-conductive).

Differentiating with respect to :kn and with respect to :sn are both legitimate and give reciprocal sensitivities; pick the one your model is parameterized on.

Interface index sits on top of layer index in stacking order.

MeanFieldHomogenization.Laminates.interface_param Function
julia
interface_param(index::Int, field::Symbol) -> InterfaceParameter

Convenience constructor for InterfaceParameter.

Ageing viscoelasticity

MeanFieldHomogenization.Viscoelasticity.laminate_alv Function
julia
laminate_alv(lam, ::Val{order}; times, property) -> Matrix

Effective ageing-viscoelastic operator of a periodic multilayer cell, as a (6n × 6n) relaxation matrix (order = 4) or a (3n × 3n) one (order = 2), with n = length(times).

Each layer carries a ViscoLaw under property; the interfaces stay elastic (their compliances are numbers), which covers the usual case of an ageing bulk with a time-independent interface. Reached through homogenize_alv(lam, Laminated(), :C; times = …).

The block algebra

The kernel behind the cell, in Core, so that the ageing-viscoelastic laminate reuses it with the Volterra inversion substituted.

MeanFieldHomogenization.Core.KM_IP Constant
julia
KM_IP

Kelvin-Mandel slots of the in-plane subspace (ℓ⊗ℓ, m⊗m, √2 ℓ⊗ˢm) in a frame whose third axis is the layer normal — the components of the strain that stay continuous across a laminate's interfaces.

MeanFieldHomogenization.Core.KM_OP Constant
julia
KM_OP

Kelvin-Mandel slots of the out-of-plane subspace (n⊗n, √2 m⊗ˢn, √2 n⊗ˢℓ) — the components of the stress that stay continuous across a laminate's interfaces, and the support of the flat-inclusion Hill tensor .

MeanFieldHomogenization.Core.plane_pinv Function
julia
plane_pinv(M) -> SMatrix{6,6}

Moore-Penrose pseudo-inverse of a 6×6 Kelvin-Mandel matrix whose range and kernel are known to coincide with the out-of-plane subspace [KM_OP] of the frame in which it is written — true for ⟨ℙ⟩ and for the out-of-plane part of any stiffness.

Implemented as a 3×3 cofactor inversion of the OP block, embedded back:

Never LinearAlgebra.pinv: its SVD is differentiable by neither ForwardDiff nor a symbolic backend, and would in any case be wasted on an exactly-rank-3 input.

MeanFieldHomogenization.Core.plane_pinv2 Function
julia
plane_pinv2(K3) -> SMatrix{3,3}

Order-2 analog of plane_pinv: the pseudo-inverse of a 3×3 conductivity-like matrix restricted to the out-of-plane direction n, i.e. (1/k_{nn}) n⊗n in a frame whose third axis is n.

MeanFieldHomogenization.Core._inv_km6 Function
julia
_inv_km6(M) -> SMatrix{6,6}

Inverse of a 6×6 Kelvin-Mandel matrix by block elimination on the in-plane / out-of-plane partition: two _inv3 cofactor inversions and a Schur complement, hence no pivoting and no factorization. Stays exact for ForwardDiff.Dual and evaluable for symbolic element types, where the LU fallback behind inv(::SMatrix{6,6}) is not.

Requires the out-of-plane block and the in-plane Schur complement to be invertible — both hold for any stiffness with a positive-definite acoustic tensor.

MeanFieldHomogenization.Core.flat_hill Function
julia
flat_hill(C6) -> SMatrix{6,6}

Hill polarisation tensor ℙ = n ⊗ˢ 𝐊⁻¹ ⊗ˢ n of a flat (layer) inclusion of Kelvin-Mandel stiffness C6, expressed in the layer frame — the limit of the Hill tensor of a flat ellipsoid as its smallest aspect ratio tends to zero.

and plane_pinv of the stiffness are the same object: the out-of-plane block of is the matrix inverse of the out-of-plane block of , and every other block vanishes. Hence also ℙ : ℂ : ℙ = ℙ and ℚ : ℙ = 0 with ℚ = ℂ − ℂ:ℙ:ℂ the second Hill tensor.

MeanFieldHomogenization.Core.acoustic_tensor Function
julia
acoustic_tensor(M) -> SMatrix{3,3}

Acoustic tensor 𝐊 = n · ℂ · n, i.e. K_ab = C_{3a3b}, read off the out-of-plane block of a 6×6 Kelvin-Mandel matrix written in a frame whose third axis is n. Undoes both the Mandel √2 weights and the (3, 2, 1) index reversal documented at the top of this file.

Used for reporting and for the exact out-of-plane oracle of a laminate, (n · ℂ^{hom} · n)^{-1} = Σ_i f_i (n · ℂ_i · n)^{-1} + Σ_j 𝕂_j / L; the laminate kernel itself never materializes 𝐊.

MeanFieldHomogenization.Core.compliance_op_block Function
julia
compliance_op_block(X) -> SMatrix{3,3}

Slot-indexed out-of-plane block of an out-of-plane compliance given by its physical 3×3 matrix X in the frame (ℓ, m, n) — the inverse map of acoustic_tensor on the compliance side:

julia
Ŝ[a, b] = X[π(a), π(b)] / (s_a s_b) ,   π = (3, 2, 1),  s = (1, 2, 2).

Its use is the imperfect interface of spring type, whose compliance 𝕂 produces the added strain (𝕂·(σ·n)) ⊗ˢ n. X may be any symmetric 3×3 compliance — all six entries are used — so an anisotropic interface is handled exactly like an isotropic one; the isotropic case 𝕂 = k_n\,n⊗n + k_t\,(δ − n⊗n) simply gives the block diag(k_n, k_t/2, k_t/2).

Note the factor 2 on the tangential entries: a displacement jump enters the strain through a symmetrized product, so those compliances are halved in Mandel slots — exactly as is 𝐊⁻¹ divided, not multiplied, by the Mandel weights.

MeanFieldHomogenization.Core.laminate_stiffness Function
julia
laminate_stiffness(C6s, f, P_int, C_surf; opinv = plane_pinv,
                   opinv_avg = plane_pinv) -> SMatrix{6,6}

Effective Kelvin-Mandel stiffness of a periodic laminate, in the layer frame:

with ⟨·⟩ = Σ_i f_i (·)_i, ℙ_i = flat_hill(ℂ_i) and ℚ_i = ℂ_i − ℂ_i:ℙ_i:ℂ_i (the in-plane Schur complement of ℂ_i).

Arguments:

  • C6s — per-layer Kelvin-Mandel stiffnesses in the layer frame;

  • f — per-layer volume fractions;

  • P_intΣ_j ℙ^{int}_j / L, the primal (spring / Kapitza) interface term, which is out-of-plane-supported and adds to ⟨ℙ⟩ alone;

  • C_surfΣ_j ℂ^{s}_j / L, the dual (membrane) surface term, which is in-plane-supported and adds to ℂ^{hom} directly, the interfaces being planar (divₛ σˢ = 0, hence no traction jump).

opinv / opinv_avg are the out-of-plane inversion used for a layer stiffness and for the average ⟨ℙ⟩ respectively. They are function arguments so that the ageing-viscoelastic laminate reuses this very kernel with the Volterra block inversion substituted; Julia specializes on the function type, so there is no abstraction cost and only one place where the physics lives.

MeanFieldHomogenization.Core.laminate_conductivity Function
julia
laminate_conductivity(K3s, f, P_int, K_surf; opinv = plane_pinv2,
                      opinv_avg = plane_pinv2) -> SMatrix{3,3}

Effective conductivity (or diffusivity, permeability, …) of a periodic laminate, in the layer frame — the order-2 transposition of laminate_stiffness, with the in-plane gradient continuous and the normal flux continuous. P_int collects Σ_j ρ_j / L · n⊗n (Kapitza) and K_surf collects Σ_j (k^s_j / L)(δ − n⊗n) (highly conductive surface layer).

Because the out-of-plane subspace is one-dimensional, the normal component obeys the exact series law 1/k^{hom}_{nn} = Σ_i f_i / k_{i,nn} + Σ_j ρ_j / L.

MeanFieldHomogenization.Core.laminate_strain_localization Function
julia
laminate_strain_localization(C6, Chom6; opinv = plane_pinv) -> SMatrix{6,6}

Strain localization tensor of one layer, 𝔸_i = 𝕀 + ℙ_i : (ℂ^{hom} − ℂ_i), in Kelvin-Mandel form. Its in-plane block is the identity and its in-plane/out-of-plane coupling block vanishes — the macroscopic in-plane strain is transmitted unchanged to every layer. The fractions weight to the identity, Σ_i f_i 𝔸_i = 𝕀.

MeanFieldHomogenization.Core.laminate_stress_localization Function
julia
laminate_stress_localization(C6, Chom6; opinv = plane_pinv) -> SMatrix{6,6}

Stress localization tensor of one layer, 𝔹_i = ℂ_i : 𝔸_i : (ℂ^{hom})^{-1}, in Kelvin-Mandel form, with Σ_i f_i 𝔹_i = 𝕀. The inverse goes through _inv_km6, so the result stays differentiable and symbolically evaluable.

MeanFieldHomogenization.Core.laminate_stress_strain_localization Function
julia
laminate_stress_strain_localization(C6, Chom6; opinv = plane_pinv) -> SMatrix{6,6}

Mixed localization tensor of one layer, 𝔸^{σε}_i = ℂ_i : 𝔸_i, mapping the macroscopic strain to the layer stress: σ_i = 𝔸^{σε}_i : E. It weights to the effective stiffness, Σ_i f_i 𝔸^{σε}_i = ℂ^{hom}, which is the mean-field identity ℂ^{hom} = ⟨ℂ : 𝔸⟩ written layer by layer.

No inverse of ℂ^{hom} is formed, unlike laminate_stress_localization.

MeanFieldHomogenization.Core.laminate_strain_stress_localization Function
julia
laminate_strain_stress_localization(C6, Chom6; opinv = plane_pinv) -> SMatrix{6,6}

Mixed localization tensor of one layer, 𝔸^{εσ}_i = 𝔸_i : (ℂ^{hom})^{-1}, mapping the macroscopic stress to the layer strain: ε_i = 𝔸^{εσ}_i : Σ, with Σ_i f_i 𝔸^{εσ}_i = (ℂ^{hom})^{-1}. The inverse goes through _inv_km6, never inv(::SMatrix{6,6}).

MeanFieldHomogenization.Core.laminate_gradient_localization Function
julia
laminate_gradient_localization(K3, Khom3; opinv = plane_pinv2) -> SMatrix{3,3}

Gradient localization tensor of one layer in a transport problem, 𝐀_i = 𝟏 + 𝐏_i · (𝐊^{hom} − 𝐊_i), with Σ_i f_i 𝐀_i = 𝟏.

MeanFieldHomogenization.Core.laminate_flux_localization Function
julia
laminate_flux_localization(K3, Khom3; opinv = plane_pinv2) -> SMatrix{3,3}

Flux localization tensor of one layer, 𝐁_i = 𝐊_i · 𝐀_i · (𝐊^{hom})^{-1}, with Σ_i f_i 𝐁_i = 𝟏.

MeanFieldHomogenization.Core.laminate_flux_gradient_localization Function
julia
laminate_flux_gradient_localization(K3, Khom3; opinv = plane_pinv2) -> SMatrix{3,3}

Mixed transport localization of one layer, 𝐀^{qg}_i = 𝐊_i · 𝐀_i, mapping the macroscopic gradient to the layer flux, with Σ_i f_i 𝐀^{qg}_i = 𝐊^{hom}. Order-2 twin of laminate_stress_strain_localization.

MeanFieldHomogenization.Core.laminate_gradient_flux_localization Function
julia
laminate_gradient_flux_localization(K3, Khom3; opinv = plane_pinv2) -> SMatrix{3,3}

Mixed transport localization of one layer, 𝐀^{gq}_i = 𝐀_i · (𝐊^{hom})^{-1}, mapping the macroscopic flux to the layer gradient, with Σ_i f_i 𝐀^{gq}_i = (𝐊^{hom})^{-1}. Order-2 twin of laminate_strain_stress_localization.

The cell contract and declarative nesting

MeanFieldHomogenization.Core.AbstractHomogenizationCell Type
julia
AbstractHomogenizationCell

Supertype of every cell accepted by homogenize: a morphological description of a heterogeneous unit whose effective property can be computed.

Two concrete cells ship with MeanFieldHomogenization:

  • RVE — a matrix phase plus named inclusion phases, the container behind every scheme built on the Eshelby auxiliary problem (Voigt, Reuss, Dilute, MoriTanaka, Maxwell, PonteCastanedaWillis, SelfConsistent, AsymmetricSelfConsistent, DifferentialScheme);

  • Laminate — a periodic unit cell of parallel layers, with no matrix and no reference medium, whose Laminated solution is exact rather than an estimate.

A new cell type must provide

  • validate_cell(cell) — structural sanity check, called by homogenize (optionally validate_cell(cell, scheme) for scheme-dependent requirements);

  • at least one _evaluate(cell, scheme, ::Val{property}; kw...) method;

  • cell_member_names, cell_container_property and cell_set_property to take part in declarative nesting and in the parameter lenses.

See also Homogenized, NestedParameter.

MeanFieldHomogenization.Core.validate_cell Function
julia
validate_cell(cell)
validate_cell(cell, scheme)

Structural sanity check of a homogenization cell, called by homogenize before evaluation. validate_cell(::RVE) forwards to validate_rve, validate_cell(::Laminate) to validate_laminate.

The two-argument form additionally checks what only the scheme can require. A matrix-based estimate cannot be evaluated until one phase is designated as its reference medium, whereas the bounds and the self-consistent scheme distinguish no phase and accept a cell that designates none — so the requirement belongs here, keyed on the scheme, and not in the cell's own structural check. It defaults to the one-argument form, so a cell or a scheme that has nothing extra to say need not implement it.

MeanFieldHomogenization.Core.cell_member_names Function
julia
cell_member_names(cell) -> Vector{Symbol}

Names of the cell's members in insertion order — the phases of an RVE (matrix included), the layers of a Laminate.

MeanFieldHomogenization.Core.cell_container_property Function
julia
cell_container_property(cell, name::Symbol, key::Symbol)

Raw stored value of property key on member name — a Homogenized is returned as such, not resolved. This is what type inspections (has_visco_property) and the nesting lenses must use; the resolving accessors are phase_property / layer_property.

MeanFieldHomogenization.Core.cell_set_property Function
julia
cell_set_property(cell, name::Symbol, key::Symbol, value)

Return a new cell in which property key of member name has been replaced by value, without mutating the original.

MeanFieldHomogenization.Core.Homogenized Type
julia
Homogenized(cell, scheme; property = nothing, kw...)

A property value that is another homogenization problem.

Stored in a phase's or a layer's properties dict under the usual key (:C, :K, …) and resolved lazily, when homogenize needs it — so a whole multiscale chain is one object, ForwardDiff traverses it in a single forward pass, and a symbolic backend sees one expression tree.

property = nothing (the default) means inherit the key it is stored under: the same Homogenized then answers :C with the inner cell's effective stiffness and :K with its effective conductivity. Pass an explicit property to read a different key from the inner cell.

kw... are forwarded to the inner homogenize (method = :nestedquadgk, abstol, …).

julia
inner = Laminate(; normal = (0, 0, 1))
add_layer!(inner, :A, Dict(:C => C_A); fraction = 0.4)
add_layer!(inner, :B, Dict(:C => C_B); fraction = 0.6)

rve = RVE()
add_phase!(rve, :M, Ellipsoid(1.0), Dict(:C => C_matrix); fraction = :rest)
add_phase!(rve, :agg, Ellipsoid(1.0),
           Dict(:C => Homogenized(inner, Laminated())); fraction = 0.3)

homogenize(rve, MoriTanaka(), :C)     # resolves `inner` on the way

Within one homogenize call each (Homogenized, key) pair is evaluated exactly once, however many times a scheme reads the property — an iterative self-consistent loop included. The memoization is task-local and torn down when the call returns, so no value ever leaks from one autodiff step to the next.

Nesting a Homogenized inside an ageing-viscoelastic chain is not supported: the inner result would have to be re-expressible as a ViscoLaw.

See also NestedParameter, resolve_property.

MeanFieldHomogenization.Core.NestedParameter Type
julia
NestedParameter(member, property, inner)
nested(member, property, inner)

Lens addressing a scalar inside a declaratively nested cell. member is the phase (or layer) name in the outer cell, property the key under which a Homogenized is stored, and inner a lens into that Homogenized's cell — itself possibly a NestedParameter, so any depth composes.

To reach the bulk modulus of layer :L1 of the laminate nested under the :C property of phase :agg:

julia
nested(:agg, :C, property(:L1, :C, :bulk))

set_param rebuilds the whole chain immutably — outer cell, Homogenized wrapper and inner cell — so ForwardDiff sees one straight-line computation and the original objects are untouched. This is what lets derivative/gradient/jacobian cross every scale of a multiscale model without any hand-written closure.

MeanFieldHomogenization.Core.nested Function
julia
nested(member, property, inner) -> NestedParameter

Convenience constructor for NestedParameter, which documents the lens itself: member is the phase or layer name in the outer cell, property the key under which a Homogenized is stored, and inner a lens into that Homogenized's cell.

julia
nested(:agg, :C, property(:L1, :C, :bulk))
MeanFieldHomogenization.Core.resolve_property Function
julia
resolve_property(value, key::Symbol)

Resolve a stored property value: the identity on every ordinary value, and the inner effective property for a Homogenized (memoized within the enclosing homogenize call).

This is the single seam through which declarative nesting enters; the resolving accessors phase_property and layer_property call it, so every scheme kernel sees a plain tensor without knowing nesting exists.

MeanFieldHomogenization.Core.has_nested_property Function
julia
has_nested_property(cell, key::Symbol) -> Bool

Whether any member of cell carries a Homogenized under key. Purely introspective — it never resolves anything.

MeanFieldHomogenization.Core.MAX_NESTING Constant
julia
MAX_NESTING

Depth ceiling for declaratively nested cells. Exceeding it raises rather than looping forever — the usual cause is a cell nested inside itself.