Constitutive Laws

Testing Constitutive Laws

When writing new constitutive laws, the ability to evaluate the stress-strain behaviour and the tangent moduli is convenient, but µSpectre’s material model makes it cumbersome to isolate and execute the evaluate_stress() and evaluate_stress_tangent() methods than any daughter class of MaterialMuSpectre must implement (e.g., evaluate_stress()). As a helper object, µSpectre offers the class MaterialEvaluator to facilitate precisely this:

A MaterialEvaluator object can be constructed with a shared pointer to a MaterialBase and exposes functions to evaluate just the stress, both the stress and tangent moduli, or a numerical approximation to the tangent moduli. For materials with internal history variables, MaterialEvaluator also exposes the MaterialBase::save_history_variables() method. As a convenience function, all daughter classes of MaterialMuSpectre have the static factory function make_evaluator() to create a material and its evaluator at once. See the Reference for the full class description.

Python Usage Example

import numpy as np
from muSpectre import material
from muSpectre import Formulation

# MaterialLinearElastic1 is standard linear elasticity while
# MaterialLinearElastic2 has a per pixel eigenstrain which needs to be set
LinMat1, LinMat2 = (material.MaterialLinearElastic1_2d,
                    material.MaterialLinearElastic2_2d)

young, poisson = 210e9, .33

# the factory returns a material and it's corresponding evaluator
material1, evaluator1 = LinMat1.make_evaluator(young, poisson)

# the material is empty (i.e., does not have any pixel/voxel), so a pixel
# needs to be added. The coordinates are irrelevant, there just needs to
# be one pixel.
material1.add_pixel([0,0])

# the stress and tangent can be evaluated for finite strain
F = np.array([[1., .01],[0, 1.0]])
P, K = evaluator1.evaluate_stress_tangent(F, Formulation.finite_strain)
# or small strain
eps = .5 * ((F-np.eye(2)) + (F-np.eye(2)).T)
sigma, C = evaluator1.evaluate_stress_tangent(eps, Formulation.small_strain)

# and the tangent can be checked against a numerical approximation
Delta_x = 1e-6
num_C = evaluator1.estimate_tangent(eps, Formulation.small_strain, Delta_x)


# Materials with per-pixel data behave similarly: the factory returns a
# material and it's corresponding evaluator like before
material2, evaluator2 = LinMat2.make_evaluator(young, poisson)

# when adding the pixel, we now need to specify also the per-pixel data:
eigenstrain = np.array([[.01, .002], [.002, 0.]])
material2.add_pixel([0,0], eigenstrain)

C++ Usage Example

#include"materials/material_linear_elastic2.hh"
#include "materials/material_evaluator.hh"
#include <libmugrid/T4_map_proxy.hh>

#include "Eigen/Dense"

using Mat_t = MaterialLinearElastic2<twoD, twoD>;

constexpr Real Young{210e9};
constexpr Real Poisson{.33};

auto mat_eval{Mat_t::make_evaluator(Young, Poisson)};
auto & mat{*std::get<0>(mat_eval)};
auto & evaluator{std::get<1>(mat_eval)};


using T2_t = Eigen::Matrix<Real, twoD, twoD>;
using T4_t = T4Mat<Real, twoD>;
const T2_t F{(T2_t::Random() - (T2_t::Ones() * .5)) * 1e-4 +
             T2_t::Identity()};

T2_t eigen_strain{[](auto x) {
  return 1e-4 * (x + x.transpose());
}(T2_t::Random() - T2_t::Ones() * .5)};

mat.add_pixel({}, eigen_strain);

T2_t P{};
T4_t K{};

std::tie(P, K) =
  evaluator.evaluate_stress_tangent(F, Formulation::finite_strain);