Skip to content

Symmetrization showcase: exact average vs best-fit projection ​

The whole page as one script

Every block of this page, in order, is scripts/70_symmetrization_showcase.jl. From a clone of the repository, julia --project=docs -e 'using Pkg; Pkg.instantiate()' once, then julia scripts/70_symmetrization_showcase.jl, runs it end to end.

Before this page

The tutorial Every scheme on the whole porosity range, where IsoSymmetrize() first averages a population of pores over their orientations, and the section Isotropic and transversely isotropic bases of the conventions, where the Walpole coefficients printed below are defined.

This page contrasts the two symmetrization mechanisms that MeanFieldHomogenization provides, as echoes does, on a non-major-symmetric strain-concentration tensor.

  • (B) exact rotation-group average — transverse_isotropify (re-exported from TensND) / IsoSymmetrize / TISymmetrize inside scheme kernels. Preserves the full axially-invariant content (TensTI{4,T,8}):   and the antisymmetric azimuthal couplings , .

  • (A) best-fit projection — best_fit_ti (likewise from TensND). Forces major symmetry (TensTI{4,T,5}), the echoes .paramsym(sym=TI) reporting form.

A concentration tensor

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

julia
using MeanFieldHomogenization
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:

julia
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 = gradient_gradient_concentration(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 ​

julia
A_exact = transverse_isotropify(A, n)         # TensTI{4,T,8}
ℓ = walpole_coeffs(A_exact, Val(8))
@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 ​

julia
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 ​

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

Consequence at scheme level: Mori–Tanaka homogenized stiffness ​

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

julia
function homogenise_with(symmode)
    rve = RVE()
    add_phase!(rve, :M, Ellipsoid(1.0), Dict(:C => C₀); fraction = :rest)
    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" canonical_components(C_iso)[3, 3, 3, 3] canonical_components(C_ti)[3, 3, 3, 3]
println("\nDone.")
MT homogenised C₃₃₃₃ :  iso-average = 47.7484   TI(ez)-average = 50.7195

Done.

The two values of differ because the two averages keep different information about the tilted needle: the full rotation group erases its orientation, whereas the azimuthal average about retains its inclination.

Where to go next ​

The averages above act on whatever concentration tensor an inclusion supplies, including one the library knows nothing about. The custom-inclusion contract: three entry gates, one answer plugs an arbitrary morphology into every scheme and obtains the orientation average without further code. The symmetrization types and the projection functions are listed in Symmetry projections.


This page was generated using Literate.jl.