CompositePropCalc / compositecalc /core /maxwell_garnett.py
tengfeiluo's picture
Upload folder using huggingface_hub
01a0b26 verified
"""Maxwell–Garnett effective medium model (generalized for ellipsoids).
The Maxwell–Garnett (MG) mixing formula predicts the effective composite
property P_eff for dilute-to-moderate concentrations of ellipsoidal inclusions
embedded in a continuous matrix. It treats the inclusions as non-interacting
dipoles in the matrix background—an asymmetric host/guest assumption that
distinguishes MG from the symmetric Bruggeman approach.
The depolarization factor L encodes filler shape along the measurement axis:
* L = 0 (needle parallel to field) → Voigt (parallel) bound
* L = 1/3 (sphere) → Maxwell (1873); equals Hashin–Shtrikman
lower bound when P_f > P_m
* L = 1 (disk perpendicular to field) → Reuss (series) bound
Mathematical equivalences
-------------------------
For the same L value, the MG formula is **algebraically identical** to the
Mori–Tanaka method (Mori & Tanaka 1973; Benveniste 1987). Both expressions
collapse to the Maxwell formula for spheres (L = 1/3) and equal the
Hashin–Shtrikman lower bound when P_f > P_m.
References
----------
Maxwell, J.C. (1873).
*A Treatise on Electricity and Magnetism*, Oxford.
Maxwell Garnett, J.C. (1904).
*Phil. Trans. Roy. Soc. London* 203, 385.
Mori, T., & Tanaka, K. (1973).
*Acta Metall.* 21, 571.
Benveniste, Y. (1987).
*Mech. Mater.* 6, 147.
"""
from __future__ import annotations
import numpy as np
__all__ = [
"maxwell_garnett",
"maxwell_garnett_anisotropic",
"mori_tanaka",
]
def maxwell_garnett(
P_m: float | np.ndarray,
P_f: float | np.ndarray,
phi: float | np.ndarray,
L: float | np.ndarray = 1.0 / 3.0,
) -> float | np.ndarray:
"""Maxwell–Garnett effective property for ellipsoidal inclusions.
Computes the effective composite property along the axis corresponding to
depolarization factor *L*:
.. math::
P_{\\text{eff}} = P_m \\left[1 +
\\frac{\\phi\\,(P_f - P_m)}{P_m + L\\,(1-\\phi)\\,(P_f - P_m)}
\\right]
Parameters
----------
P_m : float or np.ndarray
Matrix (host) property (positive; any transport property P > 0).
P_f : float or np.ndarray
Filler (inclusion) property (positive, same units as P_m).
phi : float or np.ndarray
Filler volume fraction ∈ [0, 1].
L : float or np.ndarray, optional
Depolarization factor of the inclusion along the measurement axis,
∈ [0, 1]. Defaults to ``1/3`` (sphere).
Returns
-------
P_eff : float or np.ndarray
Effective composite property (same units as P_m).
Raises
------
ValueError
If *phi* is outside [0, 1], *L* outside [0, 1], or *P_m* / *P_f*
is non-positive.
Notes
-----
Special limits:
* ``L = 0`` → Voigt (parallel) bound: P_eff = (1−φ)·P_m + φ·P_f.
* ``L = 1/3`` → Maxwell (1873) for spheres; equals the Hashin–Shtrikman
lower bound when P_f > P_m.
* ``L = 1`` → Reuss (series) bound: 1/P_eff = (1−φ)/P_m + φ/P_f.
The formula is algebraically identical to the Mori–Tanaka method for the
same depolarization factor; ``mori_tanaka`` is provided as an alias.
References
----------
Maxwell Garnett, J.C. (1904). *Phil. Trans. Roy. Soc. London* 203, 385.
Examples
--------
>>> maxwell_garnett(1.0, 10.0, 0.3) # spheres, L = 1/3
1.8709677...
>>> maxwell_garnett(1.0, 10.0, 0.3, L=0.0) # needle → Voigt
3.7
>>> maxwell_garnett(1.0, 10.0, 0.3, L=1.0) # disk → Reuss ≈ 1.3699
1.3698630...
"""
P_m = np.asarray(P_m, dtype=float)
P_f = np.asarray(P_f, dtype=float)
phi = np.asarray(phi, dtype=float)
L = np.asarray(L, dtype=float)
if np.any((phi < 0.0) | (phi > 1.0)):
raise ValueError("phi must be in [0, 1].")
if np.any((L < 0.0) | (L > 1.0)):
raise ValueError("L must be in [0, 1].")
if np.any(P_m <= 0.0):
raise ValueError("P_m must be positive.")
if np.any(P_f <= 0.0):
raise ValueError("P_f must be positive.")
delta = P_f - P_m
denom = P_m + L * (1.0 - phi) * delta
result = P_m * (1.0 + phi * delta / denom)
return float(result) if result.ndim == 0 else result
def maxwell_garnett_anisotropic(
P_m: float,
P_f: float,
phi: float,
L_tuple: tuple[float, float, float],
) -> tuple[float, float, float]:
"""Maxwell–Garnett effective property along each principal axis.
For aligned ellipsoidal inclusions described by three depolarization
factors (L_1, L_2, L_3) with L_1 + L_2 + L_3 = 1, returns the
directional effective properties (P_eff_1, P_eff_2, P_eff_3).
Parameters
----------
P_m : float
Matrix property (positive).
P_f : float
Filler property (positive, same units as P_m).
phi : float
Filler volume fraction ∈ [0, 1].
L_tuple : tuple of three floats
Depolarization factors (L_1, L_2, L_3) along the three principal
axes. Each L_i ∈ [0, 1]; together they must sum to 1 ± 1e-8.
Returns
-------
(P_eff_1, P_eff_2, P_eff_3) : tuple of float
Effective property along each principal axis.
Raises
------
ValueError
If the depolarization factors do not sum to 1, or if any individual
L_i is outside [0, 1], or if the scalar inputs are invalid.
Notes
-----
For a sphere (L_1 = L_2 = L_3 = 1/3), all three components are equal
and recover the isotropic Maxwell result.
The out-of-plane (e.g. L_3) direction for a prolate spheroid (fiber)
has L_3 < 1/3 and gives a higher effective property, reflecting the
enhanced conductance along the fiber axis.
References
----------
Maxwell Garnett, J.C. (1904). *Phil. Trans. Roy. Soc. London* 203, 385.
Mori, T., & Tanaka, K. (1973). *Acta Metall.* 21, 571.
Examples
--------
>>> maxwell_garnett_anisotropic(1.0, 10.0, 0.3, (1/3, 1/3, 1/3))
(1.8709677..., 1.8709677..., 1.8709677...)
"""
L_1, L_2, L_3 = L_tuple
L_sum = L_1 + L_2 + L_3
if abs(L_sum - 1.0) > 1e-8:
raise ValueError(
f"Depolarization factors must sum to 1; got {L_sum:.6g}."
)
P1 = maxwell_garnett(P_m, P_f, phi, L=L_1)
P2 = maxwell_garnett(P_m, P_f, phi, L=L_2)
P3 = maxwell_garnett(P_m, P_f, phi, L=L_3)
return (P1, P2, P3)
#: Alias: Mori–Tanaka for aligned spheroidal inclusions is algebraically
#: identical to Maxwell–Garnett for the same depolarization factor.
mori_tanaka = maxwell_garnett