We have been working with the linear, isotropic elasticity model in the previous tutorials. We started with that example because it is arguably the simplest possible material model in the context of solid mechanics. It is simple not just because of the simplicity in the description of the material behavior, but also due to the fact that its mathematical formulation only involves one linear equation.
Much more complicated, nonlinear models can be created using NEML2.
Using a Perzyna-type viscoplasticity model as an example, it can be formulated as
The above formulation makes a series of constitutive choices:
The strain is small and can be additively decomposed into elastic and plastic strains.
The elastic response is linear and isotropic.
The plastic flow is isochoric.
There is no isotropic hardening associated with plasticity.
There is no kinematic hardening associated with plasticity.
There is no back stress associated with plasticity.
The plastic flow associative.
The plastic rate-sensitivity follows a power-law relation.
Any change in one of the constitutive choices will result in a new material model. Suppose there are a total of constitutive choices, each having variants, the total number of possible material models would be .
In other words, the number of possible material models grows exponentially with the number of constitutive choices, and implementing all combinations is practically infeasible.
To address such challenge, NEML2 introduces a model composition mechanism which allows multiple models to be "stitched" together in a flexible, modular manner.
This tutorial demonstrates model composition using a much simplified model (without loss of generality). The model can be written as
where and are symmetric second order tensors.
Writing the input file
Let us first search for available models describing this set of equations:
b_rate = eq3.value({"forces/a": a, "state/b": b})["state/b_rate"]
print("b_rate:")
print(b_rate)
Output:
b_rate:
22.6184
7.7092
-1.9855
3.8519
3.9188
1.1109
[ CPUDoubleType{6} ]
<Tensor of shape [][6]>
Running the models: the easy way
We were able to successfully calculate by
calculating by evaluating ,
calculating by evaluating ,
setting the two coefficients of to be and respectively,
calculating by evaluating .
However, that is not ideal because we had to
Manually evaluate the equations and figure out the evaluation order, and
Manually set the parameters in as outputs from .
This manual method is not scalable when the number of equations, variables, and parameters increase.
Using NEML2's model composition capability can address these issues without sacrificing modularity. ComposedModel allows us to compose a new model from the three existing models:
[Models]
[eq1]
type = SR2Invariant
tensor = 'forces/a'
invariant = 'state/a_bar'
invariant_type = I1
[]
[eq2]
type = SR2Invariant
tensor = 'state/b'
invariant = 'state/b_bar'
invariant_type = VONMISES
[]
[eq3]
type = SR2LinearCombination
from_var = 'forces/a state/b'
to_var = 'state/b_rate'
coefficients = 'eq2 eq1'
coefficient_as_parameter = true
[]
[eq]
type = ComposedModel
models = 'eq1 eq2 eq3'
[]
[]
Note
The names of the other two models are used to specify the coefficients of , i.e. ‘coefficients = 'eq2 eq1’`. This syntax is different from what was covered in the previous tutorial on model parameters and will be explained in more details in the next tutorial.
Let us first inspect the composed model and compare it against the three sub-models: