Use the solver
The Simcoon solver allows you to simulate the mechanical or thermomechanical response of materials under various loading conditions. This page documents the loading path, kept in a path.json file, and how to drive the solver with it. Since simcoon 2.0 the C++ engine reads no file at all: sim.solver.load_path_json reads the path in Python into loading objects, and sim.solver.solve runs them and returns the whole history as numpy arrays. See In-memory Python solver to build the same loading objects directly, without any file, and to convert the pre-2.0 path.txt / material.dat inputs.
Elastic tensile test
Probably the first thing you would like to do with Simcoon is to simulate the mechanical response corresponding to a simple tension test, considering an elastic isotropic material.
We first import simcoon (the Python simulation module of simcoon) and numpy:
import os
import numpy as np
import simcoon as sim
Next we shall define the material constitutive law to be utilized and the associated material properties. We will pass them as a numpy array:
umat_name = 'ELISO' # This is the 5 character code for the elastic-isotropic subroutine
nstatev = 1 # The number of scalar state variables required
E = 700000. # The Young modulus
nu = 0.2 # The Poisson coefficient
alpha = 1.E-5 # The coefficient of thermal expansion
# Three Euler angles to represent the material orientation with respect to the reference basis
psi_rve = 0.
theta_rve = 0.
phi_rve = 0.
# Solver parameters
solver_type = 0 # Solver strategy (0 for Newton-Raphson)
corate_type = 3 # Corotational spin rate type (0: Jaumann, 1: Green-Naghdi, 2: logarithmic/XBM, 3: logarithmic_R — recommended default)
props = np.array([E, nu, alpha])
We shall then define the location of the loading path file:
path_data = 'data'
pathfile = 'path.json'
The last part is to define the loading path. Create a folder data and a file named path.json with the following content:
{
"initial_temperature": 293.5,
"corate": "logarithmic_R",
"blocks": [
{
"control_type": "small_strain",
"ncycle": 1,
"steps": [
{
"thermomechanical": false,
"mode": "linear",
"time": 30.0,
"ninc": 100,
"Dn_init": 1.0,
"Dn_mini": 0.1,
"control": ["strain", "stress", "stress", "stress", "stress", "stress"],
"value": [0.01, 0.0, 0.0, 0.0, 0.0, 0.0],
"T_final": 293.5
}
]
}
]
}
This corresponds to a pure strain-controlled tension test in direction 1 up to 1% strain, at 293.5K, in 100 increments. The file is what sim.solver.save_path_json writes for a Block of one StepMeca; the pre-2.0 text format is converted to it once with scripts/legacy_to_json.py (see In-memory Python solver).
Finally, read the path file and run the solver:
blocks, T_init, _ = sim.solver.load_path_json(os.path.join(path_data, pathfile))
res = sim.solver.solve(
blocks,
umat_name,
props,
nstatev,
T_init=T_init,
solver_type=solver_type,
corate=corate_type,
orientation=(psi_rve, theta_rve, phi_rve),
)
No result file is written: res holds the whole history in memory, as numpy
arrays of shape (6, N) for the tensor quantities:
e11, e22, e33, e12, e13, e23 = res["Strain"] # total strain
s11, s22, s33, s12, s13, s23 = res["Stress"] # Cauchy stress
time, T = res["Time"], res["Temp"]
Wm, Wm_r, Wm_ir, Wm_d = res["Wm"]
See In-memory Python solver for the full list of available keys.
Solver parameters
sim.solver.solve takes the following parameters:
Parameter |
Type |
Description |
|---|---|---|
blocks |
Block, StepMeca, or a sequence of them |
The loading path, as returned by |
umat_name |
string or callable |
5-character code identifying the constitutive law (e.g., ‘ELISO’, ‘EPICP’, ‘EPKCP’), or a constitutive law written in Python |
props |
numpy array |
Material properties array |
nstatev |
int |
Number of internal state variables |
T_init |
float |
Initial temperature in Kelvin (default 293.15); |
corate |
int or string |
Corotational spin rate type (see below) |
tangent_mode |
int |
Tangent-operator mode (default 2): see below |
solver_type |
int |
Solver strategy (0: Newton-Raphson) |
orientation |
simcoon.Rotation or 3 floats |
Orientation of the material frame: a |
phases |
sequence, optional |
Sub-phases of a mean-field model (MIHEN, MIMTN, MISCN, MIPLN), passed in memory: |
record_tangent |
bool |
Whether to record the tangent operator in the results (default True) |
Tangent-operator modes
The tangent_mode keyword selects the tangent operator returned by the
constitutive models (also exposed as named constants:
sim.tangent_none, sim.tangent_continuum, sim.tangent_algorithmic,
sim.tangent_closest_point):
Value |
Name |
Description |
|---|---|---|
0 |
none |
No tangent assembly — |
1 |
continuum |
Continuum elasto-(visco)plastic operator (this was mode 0 before the 2.0 renumbering) |
2 |
algorithmic |
Simo–Hughes consistent (algorithmic) operator — default; the exact
Jacobian of the discrete update, hence Q-quadratic global convergence,
for von Mises plasticity with isotropic hardening and for the linear
viscoelastic and damage models and their |
3 |
closest-point |
Reserved for the closest-point-projection exact operator (future release); currently raises an error |
Note
Scope of the algorithmic tangent (2.1). The plastic return mapping is a cutting-plane scheme: the plastic strain accumulates along the flow direction of each Newton iterate. The direction stays fixed during the step only for von Mises with isotropic hardening (radial return); with kinematic hardening (Prager, Armstrong–Frederick, Chaboche) or a Hill, DFA, Drucker or Tresca criterion it rotates between iterates, the update depends on the iteration path, and no tangent can be its exact derivative: the algorithmic operator is then a close approximation (about \(10^{-4}\) to \(10^{-2}\) relative, more for Tresca), which slows the global Newton iteration but leaves the converged response unchanged. The closest-point integrator that makes it exact (mode 3) is planned for the next version.
Note
2.0 renumbering. Pre-2.0, tangent_mode 0 meant continuum and
1 meant algorithmic; there was no “none” mode. The converged
response is identical in every mode (the tangent steers the global
Newton iteration, not the residual) — only iteration counts and run
time change. Scripts passing explicit values should shift them by +1;
scripts relying on the default silently upgrade from continuum to the
(faster) algorithmic operator.
Corotational spin rate types
The corate parameter controls the corotational formulation used in finite deformation problems:
Value |
Type |
Description |
|---|---|---|
0 |
Jaumann |
Uses the spin tensor \(\mathbf{W}\) from the velocity gradient |
1 |
Green-Naghdi |
Uses the spin from the polar decomposition \(\dot{\mathbf{R}}\mathbf{R}^T\) |
2 |
Logarithmic |
Uses the logarithmic (Xiao–Bruhns–Meyers) spin rate |
3 |
Logarithmic_R (log_R) |
Logarithmic strain transported by the exact polar rotation increment \(\Delta\mathbf{R} = \mathbf{R}_1\mathbf{R}_0^T\) (recommended default: exact tangent transport, including with rotated internal-variable history) |
4 |
Truesdell |
Convected (Truesdell / Oldroyd) rate, \(\Delta\mathbf{F} = \mathbf{F}_1\mathbf{F}_0^{-1}\):
Kirchhoff stress transported upper-convected, strain lower-convected, so the
accumulated strain is the Almansi strain \(\frac12(\mathbf{I} - \mathbf{b}^{-1})\)
and the box tangent is the Lie tangent. Under this rate the default |
5 |
Logarithmic_F (log_F) |
Convected logarithmic rate (pure \(\mathbf{F}\) transport) |
Use "logarithmic_R" (3) for production work. The Jaumann (0), Green-Naghdi (1) and
"logarithmic_F" (5) rates are provided for research and for comparing objective rates:
under combined stretching and rotation they are different constitutive assumptions and give
different responses. Under Jaumann and Green-Naghdi, Strain is the integral of
\(\mathbf{D}\) along the rate’s spin, path dependent and equal to \(\ln\mathbf{V}\) only
when the principal axes do not rotate; LogStrain and GreenLagrange are computed from \(\mathbf{F}\)
and exact for every rate. At small strain ("small_strain" blocks) the rate plays no role.
The loading path file
path.json is what sim.solver.save_path_json writes for a list of
Block objects, and what sim.solver.load_path_json
reads back. Every name below is a keyword of Block,
StepMeca or StepThermomeca, so
a path built in Python and a path read from the file are the same objects (see
In-memory Python solver). Enumerated entries (control_type, mode,
control, thermal_control, corate) accept the names given here or the
integer codes of the C++ solver; save_path_json always writes the names.
General structure
{
"initial_temperature": 293.15,
"corate": "logarithmic_R",
"blocks": [
{
"control_type": "small_strain",
"ncycle": 1,
"steps": [
{ "...": "step definitions" }
]
}
]
}
Path and block parameters
initial_temperature: initial temperature of the simulation (Kelvin); load_path_json returns it as T_init for solve.
corate: objective rate of the finite-strain control types ("jaumann", "green_naghdi", "logarithmic", "logarithmic_R", "truesdell", "logarithmic_F", see the table above); returned by load_path_json for solve(corate=...).
blocks: the loading blocks, run in order. A block is mechanical or thermomechanical (coupled heat equation) according to its steps: all the steps of a block are StepMeca ("thermomechanical": false) or all are StepThermomeca ("thermomechanical": true).
control_type: kinematic framework and control variables of the block. NLGEOM (non-linear geometry) is activated from "green_lagrange" on; thermomechanical blocks only support "small_strain":
Name (code) |
NLGEOM |
Description |
|---|---|---|
|
No |
Infinitesimal strains/stress (small deformations) |
|
Yes |
Finite deformation with Lagrangian control (Green-Lagrange strain \(\mathbf{E}\) / 2nd Piola-Kirchhoff stress \(\mathbf{S}\)) |
|
Yes |
Finite deformation with logarithmic (true) strain \(\boldsymbol{\varepsilon}\) / Kirchhoff stress \(\boldsymbol{\tau}\) |
|
Yes |
Finite deformation with the right stretch \(\mathbf{U}\) / Biot stress \(\mathbf{T}_B = \frac{1}{2}(\mathbf{R}^T\mathbf{P} + \mathbf{P}^T\mathbf{R})\). The kinematic targets are the components of \(\mathbf{U}\) itself, not of the Biot strain \(\mathbf{U} - \mathbf{I}\): the undeformed state is 1 on the diagonal, and a 8 % stretch is |
|
Yes |
Finite deformation with deformation gradient \(\mathbf{F}\) control (Eulerian velocity L) |
|
Yes |
Finite deformation with displacement gradient \(\nabla\mathbf{u}\) control |
Important
A run is either entirely small strain or entirely finite strain: do not mix
"small_strain" blocks with NLGEOM blocks. A small-strain block never updates
\(\mathbf{F}\) (it stays \(\mathbf{I}\)), so a finite-strain block that follows it
restarts its kinematics from \(\mathbf{F} = \mathbf{I}\) and the strain accumulated
before is not carried into \(\mathbf{F}\) (under "truesdell", rebuilt from
\(\mathbf{F}\), it is lost outright). The NLGEOM control types can be mixed freely.
Note
"logarithmic" control rebuilds the deformation gradient from the controlled strain as
\(\mathbf{F} = \exp(\boldsymbol{\varepsilon})\,\mathbf{R}\), i.e. it takes the
accumulated strain to be the Hencky strain \(\ln \mathbf{V}\), carried between
increments by the polar rotation. The stored strain follows the chosen corate, so the two
coincide exactly for the polar corates, "green_naghdi" and "logarithmic_R", and on
any path without simultaneous stretching and rotation (a superposed rigid spin included).
With "jaumann", "logarithmic" (XBM) or "logarithmic_F" under combined
stretching and rotation, strain-controlled components are approximate and a difference
is expected; this is one more reason to run with "logarithmic_R" (see the corate
table above).
ncycle: number of times the step sequence of the block is repeated (cyclic loading). A block holding a tabular step cannot be cycled.
steps: the steps of the block, in order.
Step definitions
mode: evolution of the prescribed components over the step:
"linear"(1): linear ramp to the target values"sinusoidal"(2): sinusoidal evolution to the target values"tabular"(3): table of increments read from a CSV file
Linear and sinusoidal steps
{
"thermomechanical": false,
"mode": "linear",
"time": 30.0,
"ninc": 100,
"Dn_init": 1.0,
"Dn_mini": 0.1,
"control": ["strain", "stress", "stress", "stress", "stress", "stress"],
"value": [0.01, 0.0, 0.0, 0.0, 0.0, 0.0],
"T_final": 293.5
}
Parameters:
time: duration of the step \(\Delta t\)
ninc: number of increments; the time increment is \(\delta t = \Delta t / n_{inc}\)
Dn_init: initial size of the first sub-increment, as a fraction of one increment (usually 1.0)
Dn_mini: minimal sub-increment fraction the adaptive stepping may cut down to before giving up
control, value: the prescribed mechanical state (below)
T_final: temperature at the end of the step,
nullto hold the current one (mechanical steps: imposed temperature, thermal expansion without the heat equation)
Mechanical state specification
control names the prescribed quantity of each component and value its target at the end of the step, in Voigt order:
11 22 33 12 13 23
"strain" (or "E") prescribes the kinematic quantity of the control type (strain component), "stress" (or "S") the static one (stress component); a single string applies to all six components. For example "control": ["strain", "stress", "stress", "stress", "stress", "stress"] with "value": [0.01, 0, 0, 0, 0, 0] is a uniaxial tension test to 1 % strain in direction 1, the other components being stress-free. Targets are absolute values, reached at the end of the step whatever the state at its start.
For "F" and "gradU" control types the state is the full tensor, 9 components row-major:
11 12 13 21 22 23 31 32 33
control is then "strain" for all of them and value holds the target \(\mathbf{F}\) (or \(\nabla\mathbf{u}\)).
For the mixed finite-strain control types ("green_lagrange", "logarithmic", "biot") a rotation rate can be superimposed with BC_w, a 3x3 spin matrix applied during the step:
"BC_w": [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
Thermal state specification
Mechanical steps only carry T_final (above). Thermomechanical steps ("thermomechanical": true) solve the heat equation and take thermal_control:
"temperature"(T): imposed temperature, ramped to T_final"heat_flux"(Q): imposed heat flux Q on the RVE (0.0for adiabatic conditions)"convection"(C): 0D convection \(Q = -q_{conv}\,(T - T_{init})\) with coefficient q_conv
{
"thermomechanical": true,
"mode": "linear",
"time": 1.0,
"ninc": 100,
"Dn_init": 1.0,
"Dn_mini": 1.0,
"control": ["strain", "stress", "stress", "stress", "stress", "stress"],
"value": [0.02, 0.0, 0.0, 0.0, 0.0, 0.0],
"thermal_control": "heat_flux",
"Q": 0.0
}
Tabular steps
A tabular step follows a table of increments instead of a linear ramp. The
table is the one input that is not JSON: it lives in its own CSV file next to
path.json (<stem>_tab<k>.csv, k numbering the tabular steps of the
path from 1), and the step references it by name:
{
"thermomechanical": false,
"mode": "tabular",
"tabular": "path_tab1.csv",
"Dn_init": 1.0,
"Dn_mini": 0.01,
"control": ["strain", "zero", "zero", "zero", "zero", "zero"],
"tabular_T": false
}
The control list says which components the table drives:
"strain": strain-controlled component (a column of the table)"stress": stress-controlled component (a column of the table)"zero": component held at zero (no column)
tabular_T: false if the temperature is constant, true if the table
carries a temperature column.
The table, one row per increment, comma- or whitespace-separated, # lines
ignored:
# time, E11
0.01, 0.0005
0.02, 0.0010
0.03, 0.0015
...
Columns: time (absolute simulation time, continuing from the previous step),
T if tabular_T is set (Q for a heat-flux thermomechanical step),
then the controlled components in Voigt order 11, 22, 33, 12, 13, 23.
sim.solver.save_path_json writes this file (with the header) from the
tabular array of a StepMeca; the pre-2.0 #File
increment tables (leading increment number, time restarting at 0) are converted
to it by scripts/legacy_to_json.py. A tabular step cannot be cycled.
Examples
Cyclic loading (plasticity)
Stress-controlled tension/compression cycle, 1000 increments per step:
{
"initial_temperature": 293.15,
"corate": "logarithmic_R",
"blocks": [
{
"control_type": "small_strain",
"ncycle": 1,
"steps": [
{
"thermomechanical": false,
"mode": "linear",
"time": 300.0,
"ninc": 1000,
"Dn_init": 1.0,
"Dn_mini": 1.0,
"control": ["stress", "stress", "stress", "stress", "stress", "stress"],
"value": [1000.0, 0.0, 0.0, 0.0, 0.0, 0.0],
"T_final": 293.15
},
{
"thermomechanical": false,
"mode": "linear",
"time": 300.0,
"ninc": 1000,
"Dn_init": 1.0,
"Dn_mini": 1.0,
"control": ["stress", "stress", "stress", "stress", "stress", "stress"],
"value": [-1100.0, 0.0, 0.0, 0.0, 0.0, 0.0],
"T_final": 293.15
}
]
}
]
}
(additional steps, or "ncycle": 10 on the block, for cyclic loading; the
files are written exactly like this by sim.solver.save_path_json)
Hyperelasticity with deformation gradient control
{
"initial_temperature": 293.5,
"corate": "logarithmic_R",
"blocks": [
{
"control_type": "F",
"ncycle": 1,
"steps": [
{
"thermomechanical": false,
"mode": "linear",
"time": 5.0,
"ninc": 10,
"Dn_init": 1.0,
"Dn_mini": 1.0,
"control": ["strain", "strain", "strain",
"strain", "strain", "strain",
"strain", "strain", "strain"],
"value": [5.0, 0.0, 0.0,
0.0, 0.4472135955, 0.0,
0.0, 0.0, 0.4472135955],
"T_final": 290.0
}
]
}
]
}
This applies a uniaxial stretch with \(\lambda_1 = 5\) and \(\lambda_2 = \lambda_3 = 1/\sqrt{5}\) (incompressible).
Finite deformation with spin (logarithmic strain)
{
"initial_temperature": 290.0,
"corate": "logarithmic_R",
"blocks": [
{
"control_type": "logarithmic",
"ncycle": 1,
"steps": [
{
"thermomechanical": false,
"mode": "linear",
"time": 30.0,
"ninc": 100,
"Dn_init": 1.0,
"Dn_mini": 1.0,
"control": ["stress", "stress", "stress", "stress", "stress", "stress"],
"value": [3.0, 0.0, 0.0, 0.0, 0.0, 0.0],
"BC_w": [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]],
"T_final": 293.5
}
]
}
]
}
Thermomechanical loading
Adiabatic strain-controlled loading/unloading (heat equation on, no heat flux):
{
"initial_temperature": 290.0,
"corate": "logarithmic_R",
"blocks": [
{
"control_type": "small_strain",
"ncycle": 1,
"steps": [
{
"thermomechanical": true,
"mode": "linear",
"time": 1.0,
"ninc": 100,
"Dn_init": 1.0,
"Dn_mini": 1.0,
"control": ["strain", "stress", "stress", "stress", "stress", "stress"],
"value": [0.02, 0.0, 0.0, 0.0, 0.0, 0.0],
"thermal_control": "heat_flux",
"Q": 0.0
},
{
"thermomechanical": true,
"mode": "linear",
"time": 1.0,
"ninc": 100,
"Dn_init": 1.0,
"Dn_mini": 1.0,
"control": ["strain", "stress", "stress", "stress", "stress", "stress"],
"value": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
"thermal_control": "heat_flux",
"Q": 0.0
}
]
}
]
}
Pre-2.0 text inputs
Before 2.0 the loading path was a text file (path.txt, with #Mode /
#prescribed_mechanical_state blocks and #File increment tables). Nothing
in simcoon reads that format any more: scripts/legacy_to_json.py converts a
data directory once into the files described here (see In-memory Python solver).