Spaces:
Sleeping
Sleeping
| """Lichtenecker power-law mixing rules and Looyenga equation. | |
| Three related closed-form mixing rules express the effective composite | |
| property as a power-mean combination of constituent properties: | |
| 1. **Geometric mean (Lichtenecker, 1926):** The limiting case of the | |
| power-law as the exponent n → 0. No free parameters; lies strictly | |
| between the Reuss and Voigt bounds for P_m ≠ P_f. | |
| 2. **General power-law (Lichtenecker–Rother, 1931):** The exponent | |
| n ∈ [−1, 1] interpolates between the Reuss lower bound (n = −1), | |
| the geometric mean (n → 0), the Looyenga value (n = 1/3), and the | |
| Voigt upper bound (n = 1). The effective property is *monotone | |
| increasing* in n whenever P_f ≠ P_m. | |
| 3. **Looyenga (1965):** The special case n = 1/3, independently derived | |
| from a differential effective medium argument. Performs well for | |
| moderate-contrast mixtures and is the dielectric Bruggeman analogue in | |
| the optical limit. | |
| Mathematical conventions | |
| ------------------------ | |
| Power-law (n ≠ 0): | |
| P_eff^n = (1 − φ)·P_m^n + φ·P_f^n | |
| Geometric mean (n → 0, limiting case): | |
| P_eff = P_m^(1−φ) · P_f^φ = exp[(1−φ)·ln P_m + φ·ln P_f] | |
| Looyenga (n = 1/3): | |
| P_eff^(1/3) = (1−φ)·P_m^(1/3) + φ·P_f^(1/3) | |
| Special values of n | |
| n = −1 → Reuss series bound (exact lower bound) | |
| n → 0 → Geometric mean (Lichtenecker 1926) | |
| n = 1/3 → Looyenga (1965) | |
| n = 1 → Voigt parallel bound (exact upper bound) | |
| References | |
| ---------- | |
| Lichtenecker, K. (1926). Die Dielektrizitätskonstante natürlicher und | |
| künstlicher Mischkörper. *Phys. Z.*, 27, 115–158. | |
| Lichtenecker, K. and Rother, K. (1931). Die Herleitung des | |
| logarithmischen Mischungsgesetzes. *Phys. Z.*, 32, 255–260. | |
| Looyenga, H. (1965). Dielectric constants of heterogeneous mixtures. | |
| *Physica*, 31(3), 401–406. | |
| """ | |
| from __future__ import annotations | |
| import numpy as np | |
| __all__ = [ | |
| "geometric_mean", | |
| "power_law", | |
| "looyenga", | |
| ] | |
| # --------------------------------------------------------------------------- | |
| # Public API | |
| # --------------------------------------------------------------------------- | |
| def geometric_mean( | |
| P_m: float | np.ndarray, | |
| P_f: float | np.ndarray, | |
| phi: float | np.ndarray, | |
| ) -> float | np.ndarray: | |
| """Geometric mean (Lichtenecker, 1926) effective composite property. | |
| The limiting case of the power-law mixing rule as the exponent n → 0: | |
| .. math:: | |
| P_{\\text{eff}} = P_m^{1-\\phi} \\cdot P_f^{\\phi} | |
| Equivalent to ``exp((1−φ)·ln P_m + φ·ln P_f)``. Lies strictly between | |
| the Reuss and Voigt bounds for P_m ≠ P_f. | |
| Parameters | |
| ---------- | |
| P_m : float or np.ndarray | |
| Matrix property (positive). | |
| P_f : float or np.ndarray | |
| Filler property (positive, same units as P_m). | |
| phi : float or np.ndarray | |
| Filler volume fraction ∈ [0, 1]. | |
| Returns | |
| ------- | |
| P_eff : float or np.ndarray | |
| Effective composite property. | |
| Raises | |
| ------ | |
| ValueError | |
| If *phi* is outside [0, 1] or *P_m* / *P_f* is non-positive. | |
| Notes | |
| ----- | |
| * For P_m = P_f the result equals P_m regardless of φ. | |
| * Symmetric under phase swap with complementary fraction: | |
| ``geometric_mean(P_m, P_f, φ) == geometric_mean(P_f, P_m, 1−φ)``. | |
| * The result lies strictly between Reuss and Voigt whenever P_m ≠ P_f | |
| and 0 < φ < 1. | |
| References | |
| ---------- | |
| Lichtenecker, K. (1926). *Phys. Z.*, 27, 115. | |
| Examples | |
| -------- | |
| >>> geometric_mean(1.0, 10.0, 0.3) # 10^0.3 ≈ 1.9953 | |
| 1.9952623149688797 | |
| >>> geometric_mean(1.0, 10.0, 0.0) # phi=0 → P_m | |
| 1.0 | |
| >>> geometric_mean(1.0, 10.0, 1.0) # phi=1 → P_f | |
| 10.0 | |
| """ | |
| P_m = np.asarray(P_m, dtype=float) | |
| P_f = np.asarray(P_f, dtype=float) | |
| phi = np.asarray(phi, dtype=float) | |
| if np.any((phi < 0.0) | (phi > 1.0)): | |
| raise ValueError("phi 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.") | |
| result = np.exp((1.0 - phi) * np.log(P_m) + phi * np.log(P_f)) | |
| return float(result) if result.ndim == 0 else result | |
| def power_law( | |
| P_m: float | np.ndarray, | |
| P_f: float | np.ndarray, | |
| phi: float | np.ndarray, | |
| n: float = 1.0 / 3.0, | |
| ) -> float | np.ndarray: | |
| """General power-law (Lichtenecker–Rother, 1931) effective composite property. | |
| Computes | |
| .. math:: | |
| P_{\\text{eff}} = | |
| \\bigl[(1-\\phi)\\,P_m^n + \\phi\\,P_f^n\\bigr]^{1/n} | |
| for n ≠ 0, with the limit n → 0 giving the geometric mean | |
| ``P_m^(1−φ)·P_f^φ``. | |
| Parameters | |
| ---------- | |
| P_m : float or np.ndarray | |
| Matrix property (positive). | |
| P_f : float or np.ndarray | |
| Filler property (positive, same units as P_m). | |
| phi : float or np.ndarray | |
| Filler volume fraction ∈ [0, 1]. | |
| n : float, optional | |
| Power-law exponent. Must lie in [−1, 1]. Defaults to ``1/3`` | |
| (Looyenga). Special values: | |
| * ``n = −1`` → Reuss series lower bound (exact) | |
| * ``n → 0`` → Geometric mean (Lichtenecker 1926) | |
| * ``n = 1/3`` → Looyenga (1965) | |
| * ``n = 1`` → Voigt parallel upper bound (exact) | |
| Returns | |
| ------- | |
| P_eff : float or np.ndarray | |
| Effective composite property (same units as P_m). | |
| Raises | |
| ------ | |
| ValueError | |
| If *phi* is outside [0, 1], *P_m* / *P_f* is non-positive, or *n* | |
| is outside [−1, 1]. | |
| Notes | |
| ----- | |
| * The mapping n ↦ P_eff(n) is strictly monotone increasing for P_f ≠ P_m | |
| and 0 < φ < 1. Thus Reuss ≤ geometric mean ≤ Looyenga ≤ Voigt. | |
| * For |n| < 1×10⁻⁹ the geometric-mean limit is used directly to | |
| avoid numerical overflow in ``base^(1/n)``. | |
| References | |
| ---------- | |
| Lichtenecker, K. and Rother, K. (1931). *Phys. Z.*, 32, 255. | |
| Examples | |
| -------- | |
| >>> power_law(1.0, 10.0, 0.3, n=1) # Voigt upper bound | |
| 3.7 | |
| >>> power_law(1.0, 10.0, 0.3, n=-1) # Reuss lower bound | |
| 1.3698630136986301 | |
| >>> power_law(1.0, 10.0, 0.3, n=0) # geometric mean | |
| 1.9952623149688797 | |
| >>> power_law(1.0, 8.0, 0.3, n=1/3) # Looyenga: (0.7 + 0.6)^3 = 2.197 | |
| 2.197 | |
| """ | |
| P_m = np.asarray(P_m, dtype=float) | |
| P_f = np.asarray(P_f, dtype=float) | |
| phi = np.asarray(phi, dtype=float) | |
| n = float(n) | |
| if np.any((phi < 0.0) | (phi > 1.0)): | |
| raise ValueError("phi 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.") | |
| if not (-1.0 <= n <= 1.0): | |
| raise ValueError("n must be in [-1, 1].") | |
| if abs(n) < 1e-9: | |
| # Geometric mean: the n→0 limit | |
| result = np.exp((1.0 - phi) * np.log(P_m) + phi * np.log(P_f)) | |
| else: | |
| result = ((1.0 - phi) * P_m**n + phi * P_f**n) ** (1.0 / n) | |
| return float(result) if result.ndim == 0 else result | |
| def looyenga( | |
| P_m: float | np.ndarray, | |
| P_f: float | np.ndarray, | |
| phi: float | np.ndarray, | |
| ) -> float | np.ndarray: | |
| """Looyenga (1965) effective composite property. | |
| The special case n = 1/3 of the general power-law mixing rule: | |
| .. math:: | |
| P_{\\text{eff}}^{1/3} = | |
| (1-\\phi)\\,P_m^{1/3} + \\phi\\,P_f^{1/3} | |
| Independently derived by Looyenga from a differential effective medium | |
| argument. Symmetric in the phase roles; performs well for moderate- | |
| contrast mixtures and is widely used in dielectric mixing. | |
| Parameters | |
| ---------- | |
| P_m : float or np.ndarray | |
| Matrix property (positive). | |
| P_f : float or np.ndarray | |
| Filler property (positive, same units as P_m). | |
| phi : float or np.ndarray | |
| Filler volume fraction ∈ [0, 1]. | |
| Returns | |
| ------- | |
| P_eff : float or np.ndarray | |
| Effective composite property (same units as P_m). | |
| Raises | |
| ------ | |
| ValueError | |
| If *phi* is outside [0, 1] or *P_m* / *P_f* is non-positive. | |
| Notes | |
| ----- | |
| * Algebraically identical to ``power_law(P_m, P_f, phi, n=1/3)``. | |
| * Symmetric under phase swap and complementary fraction: | |
| ``looyenga(P_m, P_f, φ) == looyenga(P_f, P_m, 1−φ)``. | |
| * Lies strictly between the Reuss lower bound and Voigt upper bound, | |
| and strictly above the geometric mean, for P_m ≠ P_f and 0 < φ < 1. | |
| References | |
| ---------- | |
| Looyenga, H. (1965). Dielectric constants of heterogeneous mixtures. | |
| *Physica*, 31(3), 401–406. | |
| Examples | |
| -------- | |
| >>> looyenga(1.0, 8.0, 0.3) # (0.7·1 + 0.3·2)^3 = 1.3^3 = 2.197 | |
| 2.197 | |
| >>> looyenga(1.0, 10.0, 0.0) # phi=0 → P_m | |
| 1.0 | |
| >>> looyenga(1.0, 10.0, 1.0) # phi=1 → P_f | |
| 10.0 | |
| """ | |
| P_m = np.asarray(P_m, dtype=float) | |
| P_f = np.asarray(P_f, dtype=float) | |
| phi = np.asarray(phi, dtype=float) | |
| if np.any((phi < 0.0) | (phi > 1.0)): | |
| raise ValueError("phi 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.") | |
| result = ( | |
| (1.0 - phi) * P_m ** (1.0 / 3.0) + phi * P_f ** (1.0 / 3.0) | |
| ) ** 3.0 | |
| return float(result) if result.ndim == 0 else result | |