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 sim.solver.load_path_json or built directly (see In-memory Python solver)

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); load_path_json returns the value read from the path file

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 Rotation or its Euler angles (psi, theta, phi) in degrees (see In-memory Python solver, mean-field composites, for the convention)

phases

sequence, optional

Sub-phases of a mean-field model (MIHEN, MIMTN, MISCN, MIPLN), passed in memory: Ellipsoid / Layer objects (numbered by position) or the dicts to_phase_dicts() makes of them. Their geometry must be the one the model builds, the concentrations must sum to 1, and a sub-phase that is itself a mean-field model carries its own sub-phases in its phases attribute

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 — Lt stays the elastic operator (explicit integration schemes)

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 MODUL compositions (see the note below); approximate otherwise (this was mode 1 before the 2.0 renumbering)

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 Strain output is the Almansi strain, not the logarithmic one: read LogStrain for \(\ln\mathbf{V}\) (see Read the results). "logarithmic" control still prescribes \(\ln\mathbf{V}\), rebuilt from the stored strain as \(-\frac12\ln(\mathbf{I} - 2\mathbf{e}_A)\). Internal variables are transported with their variance for the kernels that declare them (see Constitutive model (UMAT) catalog)

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

"small_strain" (1)

No

Infinitesimal strains/stress (small deformations)

"green_lagrange" (2)

Yes

Finite deformation with Lagrangian control (Green-Lagrange strain \(\mathbf{E}\) / 2nd Piola-Kirchhoff stress \(\mathbf{S}\))

"logarithmic" (3)

Yes

Finite deformation with logarithmic (true) strain \(\boldsymbol{\varepsilon}\) / Kirchhoff stress \(\boldsymbol{\tau}\)

"biot" (4)

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 1.08

"F" (5)

Yes

Finite deformation with deformation gradient \(\mathbf{F}\) control (Eulerian velocity L)

"gradU" (6)

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, null to 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.0 for 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).