In-memory Python solver

The simcoon.solver package drives the C++ material-point solver directly from Python: the loading path is defined with Block and StepMeca / StepThermomeca objects, and the results come back as numpy arrays — no path.txt, output.dat or result files involved. Since simcoon 2.0 this package is sim.solver; legacy loading files are parsed into loading objects with from_file() (the raw pre-2.0 file-to-file runner remains available as the low-level binding simcoon._core.solver).

Quick start

A uniaxial tension test on an elastic isotropic material:

import numpy as np
from simcoon import solver

step = solver.StepMeca(
    control=['strain'] + ['stress'] * 5,   # E11 driven, lateral stress-free
    value=[0.01, 0, 0, 0, 0, 0],           # targets, Voigt order [11,22,33,12,13,23]
    time=1.0, ninc=100,
)
res = solver.solve(step, "ELISO", [70000., 0.3, 1.E-5], nstatev=1)

stress = res["Stress"]     # Cauchy stress history, shape (6, N)
strain = res["Strain"]     # Green-Lagrange strain history, shape (6, N)

Results follow the fedoo DataSet conventions — components first, one column per increment — so they interoperate directly with fedoo utilities (e.g. fedoo.util.voigt_tensors.StressTensorList(res["Stress"])). Available fields include Stress (Cauchy), Kirchhoff, PKII, Strain, LogStrain, F, R, DR ((3, 3, N)), TangentMatrix ((6, 6, N)), Statev, Wm, Time, Temp and, for thermomechanical runs, Q, r, Wt and the coupled tangents dSdE, dSdT, drdE, drdT.

Loading control

  • control sets each component to 'strain' (kinematically driven) or 'stress' (statically driven); mixed control is solved by Newton-Raphson.

  • Block(control_type=...) selects the strain/stress measures: 'small_strain' (default), 'green_lagrange' (PKII control), 'logarithmic' (Kirchhoff control), 'biot', or the fully kinematic 'F' / 'gradU' (9 components of the deformation gradient).

  • solve(corate=...) selects the objective rate for the finite-strain control types: 'jaumann', 'green_naghdi', 'logarithmic' (default), 'logarithmic_R', 'truesdell', 'logarithmic_F'.

  • mode='sinusoidal' interpolates the step sinusoidally instead of linearly; mode='tabular' follows a user table passed in memory:

t = np.linspace(0.01, 1.0, 100)
e11 = 0.015 * np.sin(np.pi * t)
step = solver.StepMeca(control=['strain'] + ['zero'] * 5,
                       mode='tabular',
                       tabular=np.column_stack([t, e11]))
  • Cyclic loading repeats the steps of a block: Block(steps=[...], ncycle=10). Tabular steps cannot be cycled (their time column is absolute); unroll the cycles into explicit steps instead.

  • A rotation rate can be superimposed on the mixed finite-strain control types through StepMeca(BC_w=...) (3x3 spin matrix).

Thermomechanical loading

StepThermomeca activates the coupled heat equation (block type 2, small strain), with thermal_control set to 'temperature' (ramp to T_final), 'heat_flux' (prescribed Q) or 'convection' (0D convection with coefficient q_conv):

step = solver.StepThermomeca(control=['stress'] * 6, value=[0.] * 6,
                             T_final=340., ninc=50)
# ELISO thermomechanical props: rho, c_p, E, nu, alpha
res = solver.solve(step, "ELISO", [1.E-9, 1., 70000., 0.3, 1.E-5], nstatev=1)

Mechanical steps (StepMeca) also accept T_final: the temperature then ramps as an imposed condition of the mechanical problem (thermal expansion without the heat equation).

Tabular thermomechanical steps support the three thermal controls: with thermal_control='temperature' the table carries a T column when tabular_T=True (constant temperature otherwise); with 'heat_flux' the thermal column is the prescribed flux Q; with 'convection' there is no thermal column and q_conv applies.

For finite-element couplers, the point-wise thermomechanical UMAT batch entry sim.umat_T(...) complements sim.umat(...); it returns (sigma, statev, Wm, Wt, r, dSdE, dSdT, drdE, drdT).

Legacy file formats

Existing path.txt / material.dat inputs are parsed into loading objects — same solve, one entry point:

blocks, T_init = solver.from_file("data", "path.txt")
material = solver.material_from_file("data", "material.dat")
res = solver.solve(blocks, T_init=T_init, **material)

JSON configuration

Materials and loading paths round-trip through JSON (save_material_json(), save_path_json(), load_simulation_json()):

solver.save_material_json("material.json", "ELISO", [70000., 0.3, 1.E-5], 1)
solver.save_path_json("path.json", [solver.Block(steps=[step])], T_init=293.15)
res = solver.solve(**solver.load_simulation_json("material.json", "path.json"))

Results can be persisted with res.save("run.npz") / SolverResults.load("run.npz"), or flattened with res.to_dataframe().

Solver parameters

solve() exposes the numeric controls of the adaptive Newton loop as keyword arguments: precision (default 1e-6), maxiter/miniter, div_tnew_dt/mul_tnew_dt (time-step cut/growth factors), inforce, lambda_solver (penalty stiffness of strain-driven components), plus tangent_mode ('none', 'continuum', 'algorithmic' — default) and solver_type.

API reference

class simcoon.solver.StepMeca(control: str | Sequence[str | int] = 'strain', value: Sequence[float] | None = None, time: float = 1.0, ninc: int = 100, mode: str | int = 'linear', Dn_init: float = 1.0, Dn_mini: float = 0.001, BC_w: Sequence[Sequence[float]] | None = None, T_final: float | None = None, tabular: ndarray | None = None, tabular_T: bool = False)

A mechanical loading step.

Parameters:
  • control (str or sequence of str) – Per-component control: ‘strain’ (kinematic) or ‘stress’ (static); ‘zero’ holds a component at zero for tabular steps. A single string applies to all components.

  • value (array-like, optional) – Absolute target values at the end of the step, per component, in Voigt order (6 components; 9 row-major for control types ‘F’/’gradU’). Not used for tabular steps.

  • time (float) – Duration of the step.

  • ninc (int) – Number of increments (linear and sinusoidal modes).

  • mode (str or int) – ‘linear’, ‘sinusoidal’ or ‘tabular’.

  • Dn_init (float, optional) – Initial and minimal sub-increment fraction of one increment (adaptive stepping). Defaults: 1.0 and 1e-3.

  • Dn_mini (float, optional) – Initial and minimal sub-increment fraction of one increment (adaptive stepping). Defaults: 1.0 and 1e-3.

  • BC_w (array-like, optional) – 3x3 spin matrix (rotation rate) applied during the step, for the mixed finite-strain control types (‘green_lagrange’, ‘logarithmic’, ‘biot’).

  • T_final (float, optional) – Target temperature at the end of the step (temperature ramp applied to the mechanical UMAT). None (default) holds the temperature.

  • tabular (ndarray, optional) – Mode-3 table, one row per increment. Columns: [time, (thermal column, see tabular_T and StepThermomeca.thermal_control), controlled components in Voigt order]. Required when mode=’tabular’. The time column is ABSOLUTE simulation time and must continue from the previous step’s end time (a table restarting at 0 after an earlier step is rejected: it would produce negative time increments). Tabular steps cannot be cycled (Block.ncycle must be 1).

  • tabular_T (bool) – Whether the tabular table contains a temperature column (after the time column). Default False (constant temperature). For thermomechanical heat-flux steps the thermal column is the flux Q and is always required, regardless of this flag.

T_end(T_hold: float) float

Temperature at the end of the step (for chaining T_final=None steps).

to_dict(control_type: int, T_hold: float) dict

Marshal to the dict consumed by simcoon._core.solver_run.

Parameters:
  • control_type (int) – The control type of the enclosing block (drives the 6/9 sizing).

  • T_hold (float) – Temperature to hold when T_final is None (running block value).

class simcoon.solver.StepThermomeca(control: str | Sequence[str | int] = 'strain', value: Sequence[float] | None = None, time: float = 1.0, ninc: int = 100, mode: str | int = 'linear', Dn_init: float = 1.0, Dn_mini: float = 0.001, BC_w: Sequence[Sequence[float]] | None = None, T_final: float | None = None, tabular: ndarray | None = None, tabular_T: bool = False, thermal_control: str = 'temperature', Q: float = 0.0, q_conv: float = 0.0)

A thermomechanical loading step (coupled heat equation).

In addition to the mechanical control of StepMeca:

Parameters:
  • thermal_control (str) – ‘temperature’ (ramp to T_final), ‘heat_flux’ (prescribed flux Q) or ‘convection’ (0D convection Q = -q_conv (T - T_init)).

  • Q (float) – Prescribed heat flux (thermal_control=’heat_flux’).

  • q_conv (float) – Convection coefficient rho*c_p/tau (thermal_control=’convection’).

T_end(T_hold: float) float

Temperature at the end of the step; flux/convection steps leave the chained hold temperature unchanged (the reached T is solution-dependent).

class simcoon.solver.Block(steps: ~typing.List[~simcoon.solver.blocks.StepMeca] = <factory>, control_type: str | int = 'small_strain', ncycle: int = 1)

A loading block: a sequence of steps repeated ncycle times.

Parameters:
  • steps (list of StepMeca / StepThermomeca) – The loading steps. If any step is a StepThermomeca, the block is thermomechanical (coupled heat equation) and all steps must be.

  • control_type (str or int) – Loading control (see CONTROL_TYPES). Thermomechanical blocks only support ‘small_strain’.

  • ncycle (int) – Number of repetitions of the step sequence.

to_dict(T_hold: float) dict

Marshal to the dict consumed by simcoon._core.solver_run.

simcoon.solver.solve(blocks: Block | StepMeca | Sequence[Block | StepMeca], umat_name: str, props: Sequence[float], nstatev: int, T_init: float = 293.15, corate: str | int = 'logarithmic', tangent_mode: str | int = 2, solver_type: int = 0, orientation: Sequence[float] = (0.0, 0.0, 0.0), record_tangent: bool = True, raise_on_abort: bool = True, **params) SolverResults

Solve a homogeneous loading path with the C++ simcoon solver, in memory.

Parameters:
  • blocks (Block, StepMeca or sequence of them) – The loading path. Bare steps are wrapped in a small-strain Block.

  • umat_name (str) – Constitutive model name (5 characters, e.g. ‘ELISO’, ‘EPICP’, ‘MODUL’).

  • props (array-like) – Material properties.

  • nstatev (int) – Number of internal state variables.

  • T_init (float) – Initial temperature.

  • corate (str or int) – Objective rate for the finite-strain control types (see CORATE_TYPES).

  • tangent_mode (str or int) – Tangent operator mode: ‘none’, ‘continuum’, ‘algorithmic’ (default) or ‘closest_point’ (reserved).

  • solver_type (int) – 0 = classic Newton-Raphson (default), 1 = RNL (control_type 1 only).

  • orientation (sequence of 3 floats) – Euler angles (psi, theta, phi) of the material orientation (rad).

  • record_tangent (bool) – Capture the tangent operator history (‘TangentMatrix’ or the coupled thermomechanical tangents).

  • raise_on_abort (bool) – Raise a RuntimeError when the solver aborts early (status != 0) instead of returning the partial history.

  • **params – Numeric solver controls forwarded to the C++ loop: div_tnew_dt, mul_tnew_dt, miniter, maxiter, inforce, precision, lambda_solver (penalty stiffness of the strain-driven components).

Returns:

History of the converged increments (fedoo-style data layout).

Return type:

SolverResults

simcoon.solver.from_file(path_data: str = 'data', pathfile: str = 'path.txt') Tuple[List[Block], float]

Parse a legacy loading path file into Block objects.

Parameters:
  • path_data (str) – Folder containing the path file (and any mode-3 increment files it references).

  • pathfile (str) – Name of the loading path file.

Returns:

  • blocks (list of Block) – The loading path, ready for solve().

  • T_init (float) – The initial temperature declared in the file.

simcoon.solver.material_from_file(path_data: str = 'data', materialfile: str = 'material.dat') dict

Parse a legacy material definition file into solve() keyword arguments.

Returns:

{‘umat_name’, ‘props’, ‘nstatev’, ‘orientation’} — mergeable into solve() like the JSON loader.

Return type:

dict

class simcoon.solver.SolverResults(raw: Dict[str, ndarray])

Dict-like access to the solver history.

scalar_data

Scalar histories, shape (N,): ‘Time’, ‘Temp’, ‘Block’, ‘Cycle’, ‘Step’, ‘Inc’; thermomechanical runs add ‘Q’ (heat flux) and ‘r’ (heat source).

Type:

dict

field_data

Tensor histories, components-first: ‘Stress’ (Cauchy, (6, N)), ‘Kirchhoff’, ‘PKII’, ‘Strain’ (Green-Lagrange), ‘LogStrain’, ‘Statev’ ((nstatev, N)), ‘Wm’ ((4, N)), ‘F’, ‘R’, ‘DR’ ((3, 3, N)); ‘TangentMatrix’ ((6, 6, N)) for mechanical runs; thermomechanical runs add ‘Wt’ ((3, N)) and the coupled tangents ‘dSdE’ ((6, 6, N)), ‘dSdT’ ((6, N)), ‘drdE’ ((6, N)), ‘drdT’ ((N,)).

Type:

dict

status

0 if the simulation ran to completion, 1 on early abort (the recorded history is then partial).

Type:

int

get_data(key: str) ndarray

fedoo-style accessor (alias of __getitem__).

classmethod load(filename: str) SolverResults

Load a SolverResults previously written by save().

save(filename: str) None

Save all histories to a compressed npz archive.

to_dataframe()

Flatten scalar and 6-component histories to a pandas DataFrame.