Bounds and classical schemes
The dilute and Mori–Tanaka estimates of the previous tutorial are two points in a much larger family. This page places every classical scheme on a single graph, bracketed by the two estimates that must bound any physically admissible effective stiffness: the Voigt and Reuss bounds.
Rigorous bounds
For a composite with phases $\mathbb{C}_i$ at volume fractions $f_i$, the Voigt bound assumes a uniform strain throughout the RVE, and the Reuss bound a uniform stress:
\[\mathbb{C}_V = \sum_i f_i\,\mathbb{C}_i, \qquad \mathbb{C}_R = \Big(\sum_i f_i\,\mathbb{C}_i^{-1}\Big)^{-1}.\]
Both are exact bounds — no assumption on the microstructure's geometry is needed — and order every physically realizable effective bulk modulus [4], [23]:
\[k_R \le k_{\text{eff}} \le k_V.\]
Estimates between the bounds
Self-consistent
SelfConsistent drops the "matrix" altogether: every phase is embedded directly in the effective medium itself, and the effective stiffness must satisfy the implicit condition
\[\mathbb{C}_{\text{eff}} = \sum_i f_i\,\mathbb{C}_i:\mathbb{A}_i(\mathbb{C}_{\text{eff}}),\]
[38], solved by a damped Picard iteration internally (abstol, maxiters control its convergence). This is the natural model for an interpenetrating, polycrystal-like microstructure where no phase plays the role of a continuous matrix.
Differential
DifferentialScheme builds the composite incrementally, re-homogenizing after each infinitesimal addition of inclusions — see the dedicated tutorial for the full picture.
Putting them on one graph
using MeanFieldHom
using TensND
using LinearAlgebra
using Plots
gr() # headless backend; GKSwstype is set to "100" in make.jl
C_m = iso_stiffness(30.0, 10.0) # matrix
C_i = iso_stiffness(60.0, 20.0) # stiffer inclusion
function build(f)
r = RVE(:M)
add_matrix!(r, Ellipsoid(1.0), Dict(:C => C_m))
f > 0 && add_phase!(r, :I, Ellipsoid(1.0), Dict(:C => C_i); fraction = f)
return r
end
fs = collect(range(0.0, 0.6; length = 25))
schemes = [
(Voigt(), "Voigt", :red, :dash),
(Reuss(), "Reuss", :red, :dot),
(Dilute(), "Dilute", :blue, :solid),
(MoriTanaka(), "MoriTanaka", :green, :solid),
(SelfConsistent(; abstol = 1.0e-10, maxiters = 200), "SelfConsistent", :purple, :solid),
(DifferentialScheme(; nsteps = 100), "Differential", :orange, :solid),
]
plt = plot(;
xlabel = "inclusion volume fraction f", ylabel = "k_eff",
legend = :topleft, framestyle = :box, size = (760, 480),
)
for (sch, label, color, ls) in schemes
ks = [k_mu(homogenize(build(f), sch, :C))[1] for f in fs]
plot!(plt, fs, ks; label = label, color = color, linestyle = ls, lw = 2)
end
pltAt f = 0.3, the numbers behind the plot:
rve = build(0.3)
for (sch, label, _, _) in schemes
k, _ = k_mu(homogenize(rve, sch, :C))
println(rpad(label, 16), round(k, digits = 4))
endVoigt 39.0
Reuss 35.2941
Dilute 35.3182
MoriTanaka 36.0622
SelfConsistent 36.1975
Differential 36.1276Every estimate lies between the Voigt and Reuss bounds — Voigt above, Reuss below, everything else in between. This is the central point of mean-field homogenization: the choice of scheme is not a numerical detail but a modeling decision about the microstructure's topology (matrix-inclusion vs. interpenetrating, dilute vs. dense, aligned vs. random).