Symmetrization showcase: exact average vs best-fit projection

The didactic centerpiece of the symmetrization overhaul: the difference between the two mechanisms echoes (and now MeanFieldHom) provide, on a non-major-symmetric strain-concentration tensor.

  • (B) exact rotation-group averageMeanFieldHom.Core.transverse_isotropify / IsoSymmetrize / TISymmetrize inside scheme kernels. Preserves the full axially-invariant content (TensTI{4,T,8}): $\ell_3 \neq \ell_4$ and the antisymmetric azimuthal couplings $\ell_7$, $\ell_8$.
  • (A) best-fit projectionbest_fit_ti. Forces major symmetry (TensTI{4,T,5}), the echoes .paramsym(sym=TI) reporting form.

A concentration tensor

\[\mathbb{A} = \big[\mathbb{I} + \mathbb{P}:(\mathbb{C}_1-\mathbb{C}_0)\big]^{-1}\]

generally lacks major symmetry; using (A) where (B) is required silently discards physical couplings. This script quantifies the gap.

using MeanFieldHom
using TensND
using Printf
using LinearAlgebra

A genuinely non-major-symmetric tensor

The dilute strain concentration of a stiff prolate spheroid in a softer isotropic matrix:

C₀ = TensISO{3}(3 * 20.0, 2 * 12.0)          # matrix
C₁ = TensISO{3}(3 * 90.0, 2 * 60.0)          # stiff inclusion
incl = Spheroid(6.0; euler_angles = (deg2rad(35.0), 0.0, 0.0))   # tilted needle
A = strain_strain_loc(incl, C₁, C₀)
arrA = get_array(A)

ismaj = maximum(abs, arrA .- permutedims(arrA, (3, 4, 1, 2)))
@printf "‖A − Aᵀ_major‖∞ = %.4e  (A is NOT major-symmetric)\n\n" ismaj

n = (0.0, 0.0, 1.0)
(0.0, 0.0, 1.0)

(B) Exact azimuthal average about $e_z$

A_exact = transverse_isotropify(A, n)         # TensTI{4,T,8}
ℓ = get_ℓ8(A_exact)
@printf "(B) exact TI average → TensTI{4,T,8}\n"
@printf "    ℓ₁..ℓ₈ = %s\n" join((@sprintf("%.4f", x) for x in ℓ), ", ")
@printf "    ℓ₃ − ℓ₄ (major-asymmetry) = %.4e\n" (ℓ[3] - ℓ[4])
@printf "    ℓ₇, ℓ₈  (antisym. couplings) = %.4e, %.4e\n\n" ℓ[7] ℓ[8]
(B) exact TI average → TensTI{4,T,8}
    ℓ₁..ℓ₈ = 0.5006, 0.3059, 0.0561, -0.0172, 0.3112, 0.4283, 0.0000, 0.0000
    ℓ₃ − ℓ₄ (major-asymmetry) = 7.3221e-02
    ℓ₇, ℓ₈  (antisym. couplings) = 0.0000e+00, 0.0000e+00

(A) Best-fit major-symmetric projection

A_fit = best_fit_ti(A, n)                     # TensTI{4,T,5}
@printf "(A) best-fit TI projection → TensTI{4,T,5}\n"
@printf "    (ℓ₁, ℓ₂, ℓ₃, ℓ₅, ℓ₆) = %s\n\n" join((@sprintf("%.4f", x) for x in get_data(A_fit)), ", ")
(A) best-fit TI projection → TensTI{4,T,5}
    (ℓ₁, ℓ₂, ℓ₃, ℓ₅, ℓ₆) = 0.5006, 0.3059, 0.0194, 0.3112, 0.4283

The gap: what best-fit discards

gap = maximum(abs, get_array(A_exact) .- get_array(A_fit))
@printf "‖exact − best-fit‖∞ = %.4e  (content dropped by the projection)\n" gap
@printf "  → components lost : ℓ₃≠ℓ₄ split + the ℓ₇/ℓ₈ azimuthal couplings.\n\n"
‖exact − best-fit‖∞ = 2.5887e-02  (content dropped by the projection)
  → components lost : ℓ₃≠ℓ₄ split + the ℓ₇/ℓ₈ azimuthal couplings.

Consequence at scheme level: Mori–Tanaka homogenised stiffness

Two RVEs identical except for the symmetrization mechanism applied to the tilted-needle phase. The exact average is what the scheme kernels use.

function homogenise_with(symmode)
    rve = RVE(:M)
    add_matrix!(rve, Ellipsoid(1.0), Dict(:C => C₀))
    add_phase!(
        rve, :I, incl, Dict(:C => C₁);
        fraction = 0.2, symmetrize = symmode
    )
    return homogenize(rve, MoriTanaka(), :C)
end

C_iso = homogenise_with(:iso)                       # full SO(3) average
C_ti = homogenise_with(TISymmetrize(n))             # exact azimuthal average
@printf "MT homogenised C₃₃₃₃ :  iso-average = %.4f   TI(ez)-average = %.4f\n" get_array(C_iso)[3, 3, 3, 3] get_array(C_ti)[3, 3, 3, 3]
println("\nDone.")
MT homogenised C₃₃₃₃ :  iso-average = 47.7484   TI(ez)-average = 50.7195

Done.

This page was generated using Literate.jl.