# -*- coding: utf-8 -*-
"""
This file is part of FElupe.
FElupe is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
FElupe is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with FElupe. If not, see <http://www.gnu.org/licenses/>.
"""
import numpy as np
from ...math import identity, ravel, reshape, sym
from .._base import ConstitutiveMaterial
[docs]
class MaterialStrain(ConstitutiveMaterial):
"""A strain-based user-defined material definition with a given function
for the stress tensor and the (fourth-order) elasticity tensor.
Take this code-block from the linear-elastic material formulation
.. code-block::
from felupe.math import identity, cdya, dya, trace
def linear_elastic(dε, εn, σn, ζn, λ, μ, **kwargs):
'''3D linear-elastic material formulation.
Arguments
---------
dε : ndarray
Incremental strain tensor.
εn : ndarray
Old strain tensor.
σn : ndarray
Old stress tensor.
ζn : ndarray
Old state variables.
λ : float
First Lamé-constant.
μ : float
Second Lamé-constant (shear modulus).
'''
# change of stress due to change of strain
I = identity(dε)
dσ = 2 * μ * dε + λ * trace(dε) * I
# update stress and evaluate elasticity tensor
σ = σn + dσ
dσdε = 2 * μ * cdya(I, I) + λ * dya(I, I)
# update state variables (not used here)
ζ = ζn
return dσdε, σ, ζ
umat = MaterialStrain(material=linear_elastic, μ=1, λ=2)
or this minimal header as template:
.. code-block::
def fun(dε, εn, σn, ζn, **kwargs):
return dσdε, σ, ζ
umat = MaterialStrain(material=fun, **kwargs)
See Also
--------
linear_elastic : 3D linear-elastic material formulation
linear_elastic_plastic_isotropic_hardening : Linear-elastic-plastic material
formulation with linear isotropic hardening (return mapping algorithm).
LinearElasticPlasticIsotropicHardening : Linear-elastic-plastic material
formulation with linear isotropic hardening (return mapping algorithm).
"""
def __init__(self, material, dim=3, statevars=(0,), **kwargs):
self.material = material
self.statevars_shape = statevars
self.statevars_size = [np.prod(shape) for shape in statevars]
self.statevars_offsets = np.cumsum(self.statevars_size)
self.nstatevars = sum(self.statevars_size)
self.kwargs = {**kwargs, "tangent": None}
self.dim = dim
self.x = [np.eye(dim), np.zeros(2 * dim**2 + self.nstatevars)]
self.stress = self.gradient
self.elasticity = self.hessian
[docs]
def gradient(self, x):
strain_old, dstrain, stress_old, statevars_old = self.extract(x)
self.kwargs["tangent"] = False
dsde, stress_new, statevars_new_list = self.material(
dstrain, strain_old, stress_old, statevars_old, **self.kwargs
)
strain_new_1d = (strain_old + dstrain).reshape(-1, *strain_old.shape[2:])
stress_new_1d = stress_new.reshape(-1, *strain_old.shape[2:])
statevars_new = np.concatenate(
[*[ravel(sv) for sv in statevars_new_list], strain_new_1d, stress_new_1d],
axis=0,
)
return [stress_new, statevars_new]
[docs]
def hessian(self, x):
strain_old, dstrain, stress_old, statevars_old = self.extract(x)
self.kwargs["tangent"] = True
dsde = self.material(
dstrain, strain_old, stress_old, statevars_old, **self.kwargs
)[0]
# ensure minor-symmetric elasticity tensor due to symmetry of strain
dsde = (
dsde
+ np.einsum("ijkl...->jikl...", dsde)
+ np.einsum("ijkl...->ijlk...", dsde)
+ np.einsum("ijkl...->jilk...", dsde)
) / 4
return [dsde]