$$ \newcommand{\C}{{\mathbb{{C}}}} \newcommand{\R}{{\mathbb{{R}}}} \newcommand{\Q}{{\mathbb{{Q}}}} \newcommand{\Z}{{\mathbb{{Z}}}} \newcommand{\N}{{\mathbb{{N}}}} \newcommand{\uu}[1]{{\boldsymbol{{#1}}}} \newcommand{\uuuu}[1]{{\symbb{{#1}}}} \newcommand{\uv}[1]{{\underline{{#1}}}} \newcommand{\ve}[1]{{\uv{{e}}_{{#1}}}} \newcommand{\x}{{\uv{{x}}}} \newcommand{\n}{{\uv{{n}}}} \newcommand{\eps}{{\uu{{\varepsilon}}}} \newcommand{\E}{{\uu{{E}}}} \newcommand{\sig}{{\uu{{\sigma}}}} \newcommand{\Sig}{{\uu{{\Sigma}}}} \newcommand{\cod}{{\uv{{\symscr{b}}}}} \newcommand{\trans}[1]{{{}^{t}{#1}}} \newcommand{\sotimes}{{\stackrel{s}{\otimes}}} \newcommand{\sboxtimes}{\stackrel{s}{\boxtimes}} \newcommand{\norm}[1]{{\lVert{{#1}}\rVert}} \newcommand{\ud}{{\,\mathrm{d}}} \newcommand{\mat}{\mathsf} \DeclareMathOperator{\arcosh}{arcosh} \DeclareMathOperator{\divz}{div} \DeclareMathOperator{\divu}{\uv{div}} \DeclareMathOperator{\hess}{hess} \DeclareMathOperator{\gradu}{\uv{grad}} \DeclareMathOperator{\graduu}{\uu{grad}} \DeclareMathOperator{\Mat}{Mat} \DeclareMathOperator{\tr}{tr} \DeclareMathOperator{\ISO}{ISO} \newcommand{\volt}[1]{{#1}^{-1\circ}} \newcommand{\dcirc}{\overset{\circ}{:}} \newcommand{\jump}[1]{\mathopen{[\![}\,#1\,\mathclose{]\!]}} $$

10  User-defined inclusions

Important Objectives

This tutorial introduces the user_inclusion class, which allows defining an arbitrary inclusion type by providing the concentration tensors analytically (or numerically). It shows how to subclass user_inclusion, implement the build_all() method, and integrate the result into standard homogenization schemes.

import numpy as np
from echoes import *

np.set_printoptions(precision=8, suppress=True)

Motivation

The ellipsoid class presented in Chapter 7 covers ellipsoidal inclusions for which Eshelby’s uniformity result holds: the four concentration tensors derive solely from the Hill polarization tensor \(\uuuu{P}\), computed automatically by Echoes for a wide range of matrix symmetries. The sphere_nlayers class extends coverage to concentrically layered spheres through the analytical solution of (Hervé and Zaoui, 1993).

Beyond these two built-in types, one may need to handle:

  • An inclusion whose shape is not ellipsoidal and not a concentric sphere (e.g. a composite granule, a non-convex shape, a polyhedral grain).
  • A case where the concentration tensors have been determined externally, for instance by a finite-element simulation of the auxiliary problem, by a dedicated semi-analytical formula, or by a numerical integration.
  • An approximate analytical formula hand-crafted for a specific geometry or interface type.

The user_inclusion class covers all these situations. The user subclasses it and overrides build_all() to supply the four concentration tensors by any means. The resulting object is a first-class citizen in any Echoes homogenization scheme.

Class interface

Defining a subclass

A user_inclusion is created by inheriting from it, registering properties in the constructor, and implementing build_all():

class MyInclusion(user_inclusion):
    def __init__(self, prop, fraction=0., symmetrize=[]):
        super().__init__(fraction, symmetrize)
        for key, val in prop.items():
            user_inclusion.set_prop(self, key, val)   # note: explicit self

    def build_all(self):
        # compute and return the four concentration tensors
        ...
        return {"eE": eE, "eS": eS, "sE": sE, "sS": sS}

The constructor arguments mirror those of ellipsoid:

Argument Description
fraction Volume fraction in the RVE (can also be set later via .fraction)
symmetrize List of symmetry classes to project the concentration tensors onto (e.g. [ISO]), or empty list

Properties are registered with user_inclusion.set_prop(self, key, value). The explicit self is necessary because of Python’s method-resolution order when calling a parent method from within a subclass.

Attributes available inside build_all()

When the homogenization scheme calls build_all(), the following attributes are set on self:

Attribute Type Description
self.ref tensor_param Reference medium (the embedding matrix)
self.refname str Property name (e.g. "C")
self.prop(self.refname) tensor_param Property tensor of this inclusion

Both self.ref and self.prop(key) are tensor_param objects exposing the same fields as any other property tensor in Echoes (.array, .param, .kmu, …).

Required return value

build_all() must return a dictionary with the four \(6\times6\) concentration matrices (in Kelvin-Mandel representation):

Key Tensor Relation
"eE" \(\uuuu{A}^E\) \(\langle\eps\rangle = \uuuu{A}^E:\E\)
"eS" \(\uuuu{A}^S\) \(\langle\eps\rangle = \uuuu{A}^S:\Sig\)
"sE" \(\uuuu{B}^E\) \(\langle\sig\rangle = \uuuu{B}^E:\E\)
"sS" \(\uuuu{B}^S\) \(\langle\sig\rangle = \uuuu{B}^S:\Sig\)

These follow the conventions of Section 7.3:

\[ \uuuu{A}^S = \uuuu{A}^E:\uuuu{C}_0^{-1}, \qquad \uuuu{B}^E = \uuuu{C}^\mathcal{I}:\uuuu{A}^E, \qquad \uuuu{B}^S = \uuuu{C}^\mathcal{I}:\uuuu{A}^S \tag{10.1}\]

Example: sphere in an isotropic matrix

As a concrete worked example, we re-implement the standard spherical inhomogeneity by hand, using the analytical formula for the Hill polarization tensor.

Hill polarization tensor for a sphere

For a spherical inclusion embedded in an isotropic matrix \(\uuuu{C}_0 = 3k_0\,\mathbb{J}+2\mu_0\,\mathbb{K}\), the Hill polarization tensor reduces to an isotropic tensor with closed-form scalar coefficients (Eshelby, 1957; Hill, 1965):

\[ \uuuu{P} = \frac{1}{3k_0+4\mu_0} \left(\mathbb{J} + \frac{3(k_0+2\mu_0)}{5\mu_0}\,\mathbb{K}\right) \tag{10.2}\]

or equivalently, using the notation \(p_J = \frac{1}{3(3k_0+4\mu_0)}\) for the volumetric coefficient and \(p_K = \frac{3(k_0+2\mu_0)}{5\mu_0(3k_0+4\mu_0)}\) for the deviatoric one.

Once \(\uuuu{P}\) is known, the four concentration tensors follow directly from 7.2 and 10.1 with \(\delta\uuuu{C} = \uuuu{C}^\mathcal{I}-\uuuu{C}_0\):

\[ \uuuu{A}^E = \left(\uuuu{I}+\uuuu{P}:\delta\uuuu{C}\right)^{-1} \tag{10.3}\]

Implementation

The J4, K4, and Id4 constants exported by Echoes are the \(6\times6\) Kelvin-Mandel representations of the volumetric projector \(\mathbb{J}\), deviatoric projector \(\mathbb{K}\), and symmetric identity \(\mathbb{I}\), respectively.

class SphereInISO(user_inclusion):
    """Spherical inhomogeneity in an isotropic matrix.
    Concentration tensors computed analytically via the Hill
    polarization tensor formula (Eq. eq-P-sphere-iso)."""

    def __init__(self, prop, fraction=0., symmetrize=[]):
        super().__init__(fraction, symmetrize)
        for key, val in prop.items():
            user_inclusion.set_prop(self, key, val)

    def build_all(self):
        # --- reference medium ---
        C0      = self.ref.array
        S0      = np.linalg.inv(C0)
        k0, mu0 = self.ref.kmu
        # --- inclusion stiffness ---
        Ci = self.prop(self.refname).array
        # --- Hill tensor for sphere in isotropic matrix (@eq-P-sphere-iso) ---
        P  = 1. / (3*k0 + 4*mu0) * (J4 + 3.*(k0 + 2*mu0) / (5*mu0) * K4)
        # --- concentration tensors ---
        eE = np.linalg.inv(Id4 + P.dot(Ci - C0))
        eS = eE.dot(S0)
        sE = Ci.dot(eE)
        sS = Ci.dot(eS)
        return {"eE": eE, "eS": eS, "sE": sE, "sS": sS}

Verification against ellipsoid

For a spherical inclusion, SphereInISO must reproduce the same concentration tensors as ellipsoid(shape=spherical), whose Hill tensor is computed automatically by Echoes:

Ci = stiff_Enu(5., 0.25)    # inclusion: E=5, nu=0.25
C0 = stiff_kmu(3., 2.)      # matrix: k=3, mu=2

# Built-in ellipsoid (Echoes computes P internally)
ell = ellipsoid(shape=spherical, prop={"C": Ci})
ell.set_ref("C", C0)

# User-defined sphere (P computed by hand in build_all)
usr = SphereInISO(prop={"C": Ci})
usr.set_ref("C", C0)

for name, ref, usr_val in [
        ("eE", ell.eE, usr.eE),
        ("eS", ell.eS, usr.eS),
        ("sE", ell.sE, usr.sE),
        ("sS", ell.sS, usr.sS)]:
    err = np.max(np.abs(ref - usr_val))
    print(f"Max |ell.{name} - usr.{name}| = {err:.2e}")
Max |ell.eE - usr.eE| = 1.11e-16
Max |ell.eS - usr.eS| = 2.78e-17
Max |ell.sE - usr.sE| = 8.88e-16
Max |ell.sS - usr.sS| = 2.22e-16

\(\,\)