Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
7b1dfc4
Add simcoon.modular to recipe test imports
chemiskyy Jan 28, 2026
421e5d7
Add scalar DamageMechanism (CDM)
chemiskyy Jan 28, 2026
d6920d2
Add PlasticityMechanism for modular UMAT
chemiskyy Jan 28, 2026
41c07a0
Add ElasticityModule for modular UMAT
chemiskyy Jan 28, 2026
f317339
Add modular isotropic and kinematic hardening
chemiskyy Jan 28, 2026
888b0d2
Add InternalVariable for UMAT modular
chemiskyy Jan 28, 2026
72c3b15
Add InternalVariableCollection class
chemiskyy Jan 28, 2026
4a7bd66
Add Modular UMAT orchestrator
chemiskyy Jan 28, 2026
adeecd1
Add StrainMechanism base class
chemiskyy Jan 28, 2026
23d1372
Add ViscoelasticMechanism (Prony series)
chemiskyy Jan 28, 2026
18e13fd
Add YieldCriterion module for UMAT
chemiskyy Jan 28, 2026
f31f615
Add modular UMAT material module
chemiskyy Jan 28, 2026
2316451
Import modular in package __init__
chemiskyy Jan 28, 2026
b0bd8d7
Remove UMEXT results test files
chemiskyy Jan 28, 2026
ba5e133
Add unit tests for modular UMAT components
chemiskyy Jan 28, 2026
3ab718f
Add Modular UMAT integration
chemiskyy Jan 28, 2026
c9c270a
Merge feature/modular into feature/Tensor
chemiskyy Apr 20, 2026
e6cddcb
modular: add Tensor4-typed accessors to ElasticityModule
chemiskyy Apr 20, 2026
43ca56c
modular: add Tensor2/Tensor4 views to InternalVariable
chemiskyy Apr 20, 2026
628e261
modular: YieldCriterion accepts and returns Tensor2
chemiskyy Apr 20, 2026
f522fe4
modular: Prony_Nfast viscoelastic port + cross-coupled FB Jacobian + …
chemiskyy Apr 23, 2026
5c247de
modular: Python API update, MODUL example, pytest regression
chemiskyy Apr 23, 2026
ba4fa3f
Merge feature/Tensor into feature/modular-tensor
chemiskyy Jul 3, 2026
f9ddf2f
modular: relax L_tensor round-trip tolerance for Mandel storage
chemiskyy Jul 3, 2026
65ff007
modular: fix viscoelastic consistent tangent (missing 1/DTime in Bhat)
chemiskyy Jul 3, 2026
6b438f1
modular: type dPhi_dsigma/kappa interface as tensor2 + hoist kappa fetch
chemiskyy Jul 3, 2026
8bebfb5
modular: cache typed L/M tensor4 in ElasticityModule
chemiskyy Jul 3, 2026
a12e058
modular: typed hardening accessors; keep plasticity FB core raw (benc…
chemiskyy Jul 3, 2026
6daeea2
modular: native tensor2 fast path for von Mises yield
chemiskyy Jul 3, 2026
9697cd5
modular: apply CDM stiffness reduction to stress; damage integration …
chemiskyy Jul 3, 2026
246a684
modular: doc-cpp convention pass
chemiskyy Jul 3, 2026
0549a38
ci: build conda package from the checked-out tree, not the default br…
chemiskyy Jul 3, 2026
98be29b
Merge remote-tracking branch 'origin/master' into feature/modular-tensor
chemiskyy Jul 4, 2026
dc23147
modular: tangent_mode 0/1 support; fix FB Jacobian hardening sign
chemiskyy Jul 4, 2026
aef449c
docs: fix plastic_chaboche_ccp.hpp to match the implementation
chemiskyy Jul 4, 2026
ce01640
modular: fix orthotropic L_ortho argument order; typed 6x6 IV rotatio…
chemiskyy Jul 4, 2026
d82928f
modular: validate elasticity at configuration (SPD + valid axis)
chemiskyy Jul 5, 2026
bab70d0
test: modular Hill yield matches reference EPHIL bit-for-bit
chemiskyy Jul 5, 2026
9638602
modular: fix kinematic-hardening backstress shear convention (24.7% e…
chemiskyy Jul 5, 2026
5876757
modular: remove dead StrainMechanism interface (active flag, compute_…
chemiskyy Jul 5, 2026
f058585
modular: clarify viscoelastic diagonal Jacobian; clean up damage dead…
chemiskyy Jul 5, 2026
f36ecf1
modular: request a step cut on non-finite return-mapping result
chemiskyy Jul 6, 2026
18a8872
modular: guard required_nstatev() against drift from the registered size
chemiskyy Jul 6, 2026
4575f04
test: lock the Python ArmstrongFrederick path (distinct C++ class) un…
chemiskyy Jul 6, 2026
f90a3e8
Validate elasticity inputs and add tests
chemiskyy Jul 10, 2026
444d144
Remove damaged stiffness helpers
chemiskyy Jul 10, 2026
c46176a
Use tensor2 for kinematic hardening API
chemiskyy Jul 10, 2026
3b9db76
Rename IVar accessors to raw_voigt/raw_mat
chemiskyy Jul 10, 2026
d84d515
Refactor internal variables: objective + start views
chemiskyy Jul 10, 2026
f8e29d1
Refactor: mechanism-owned internal variables
chemiskyy Jul 10, 2026
b1a8f02
Rename solver setter to return-mapping params
chemiskyy Jul 10, 2026
a8b8b73
Pin scikit-build-core in CI and fix M_PI usage
chemiskyy Jul 10, 2026
c35241f
Per-branch kinematic hardening refactor
chemiskyy Jul 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,15 @@ jobs:
- name: Editable install
run: |
uv venv
uv pip install scikit-build-core pybind11 numpy
uv pip install -e .[dev]
# scikit-build-core pinned <1: the 1.x editable install changed where
# the compiled extension lands, which breaks the Windows DLL-vendoring
# step below (green on 0.12.2, DLL load failure on 1.0.2). Lift the pin
# together with a rework of that step. --no-build-isolation so the
# pinned backend in the venv is the one that actually builds the
# editable wheel (isolation would re-resolve pyproject's
# scikit-build-core>=0.11 to 1.x).
uv pip install "scikit-build-core<1" pybind11 numpy
uv pip install -e .[dev] --no-build-isolation

- name: Vendor conda BLAS/LAPACK runtime next to the extension (Windows)
if: runner.os == 'Windows'
Expand Down
2 changes: 1 addition & 1 deletion conda.recipe/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,4 @@ test:
imports:
- simcoon
- simcoon._core

- simcoon.modular
68 changes: 68 additions & 0 deletions examples/data/MODUL_path.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#Initial_temperature
293
#Number_of_blocks
2

#Block
1
#Loading_type
1
#Control_type(NLGEOM)
1
#Repeat
1
#Steps
1

#Mode
1
#Dn_init 1.
#Dn_mini 1.
#Dn_inc 0.005
#time
1
#prescribed_mechanical_state
E 0.02
S 0 S 0
S 0 S 0 S 0
#prescribed_temperature_state
T 293

#Block
2
#Loading_type
1
#Control_type(NLGEOM)
1
#Repeat
2
#Steps
2

#Mode
1
#Dn_init 1.
#Dn_mini 1.
#Dn_inc 0.01
#time
1
#prescribed_mechanical_state
E -0.02
S 0 S 0
S 0 S 0 S 0
#prescribed_temperature_state
T 293

#Mode
1
#Dn_init 1.
#Dn_mini 1.
#Dn_inc 0.01
#time
1
#prescribed_mechanical_state
E 0.02
S 0 S 0
S 0 S 0 S 0
#prescribed_temperature_state
T 293
29 changes: 29 additions & 0 deletions examples/data/SHEAR_path.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#Initial_temperature
293.5
#Number_of_blocks
1

#Block
1
#Loading_type
1
#Control_type(NLGEOM)
1
#Repeat
1
#Steps
1

#Mode
1
#Dn_init 1.
#Dn_mini 1.
#Dn_inc 0.005
#time
1
#prescribed_mechanical_state
S 0
E 0.03 S 0
S 0 S 0 S 0
#prescribed_temperature_state
T 293.5
133 changes: 133 additions & 0 deletions examples/mechanical/MODUL.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
"""
Modular UMAT Example — Composable Elasto-Plasticity
====================================================

Demonstrates the ``simcoon.modular`` high-level Python interface that composes a
constitutive model declaratively and runs it through the standard ``sim.solver``
load-path driver. The C++ ``ModularUMAT`` infrastructure (ElasticityModule,
YieldCriterion, hardening, ...) is internal — the user only builds a
``ModularMaterial`` and hands its ``.props`` / ``.nstatev`` to the ``"MODUL"``
UMAT code registered in simcoon's UMAT table.

The path file applies a monotonic tensile ramp to 2% strain, then two
strain-controlled cycles between -2% and +2%, exposing isotropic-hardening
growth and the initial yield plateau.
"""

import os
import numpy as np
import matplotlib.pyplot as plt
import simcoon as sim
from simcoon.modular import (
ModularMaterial,
IsotropicElasticity,
Plasticity,
VonMisesYield,
VoceHardening,
)

plt.rcParams["figure.figsize"] = (14, 6)

###################################################################################
# 1. Compose the constitutive model
# ----------------------------------
# Isotropic elasticity + von Mises yield + Voce isotropic hardening.
# Parameters: E=210 GPa, nu=0.3, sigma_Y=300 MPa, Q=200 MPa, b=10.

mat = ModularMaterial(
elasticity=IsotropicElasticity(C1=210000.0, C2=0.3, alpha=1.2e-5),
mechanisms=[
Plasticity(
sigma_Y=300.0,
yield_criterion=VonMisesYield(),
isotropic_hardening=VoceHardening(Q=200.0, b=10.0),
),
],
)

print(mat.summary())

###################################################################################
# 2. Run the solver
# ------------------
# ``mat.umat_name`` is ``"MODUL"``, the UMAT code registered at
# ``umat_smart.cpp:316`` (id 200). ``mat.props`` serializes the composition into
# the flat array that the C++ ``umat_modular`` deserializes.

umat_name = mat.umat_name
props = mat.props
nstatev = mat.nstatev

psi_rve = 0.0
theta_rve = 0.0
phi_rve = 0.0
solver_type = 0
corate_type = 1

path_data = "../data"
path_results = "results"
pathfile = "MODUL_path.txt"
outputfile = "results_MODUL.txt"

os.makedirs(path_results, exist_ok=True)

sim.solver(
umat_name,
props,
nstatev,
psi_rve,
theta_rve,
phi_rve,
solver_type,
corate_type,
path_data,
path_results,
pathfile,
outputfile,
)

###################################################################################
# 3. Plot the stress-strain curve
# --------------------------------

outputfile_macro = os.path.join(path_results, "results_MODUL_global-0.txt")

e11, e22, e33, e12, e13, e23, s11, s22, s33, s12, s13, s23 = np.loadtxt(
outputfile_macro,
usecols=(8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19),
unpack=True,
)
time, T, Q_out, r = np.loadtxt(outputfile_macro, usecols=(4, 5, 6, 7), unpack=True)
Wm, Wm_r, Wm_ir, Wm_d = np.loadtxt(
outputfile_macro, usecols=(20, 21, 22, 23), unpack=True
)

fig = plt.figure()

# Stress-strain curve
ax1 = fig.add_subplot(1, 2, 1)
plt.grid(True)
plt.tick_params(axis="both", which="major", labelsize=13)
plt.xlabel(r"Strain $\varepsilon_{11}$", size=14)
plt.ylabel(r"Stress $\sigma_{11}$ (MPa)", size=14)
plt.plot(e11, s11, c="royalblue", lw=1.5, label="MODUL: iso-elastic + VM + Voce")
plt.axhline(y=300.0, color="0.6", linestyle="--", lw=0.8, label=r"initial $\sigma_Y$")
plt.axhline(y=-300.0, color="0.6", linestyle="--", lw=0.8)
plt.legend(loc="best")
plt.title("Stress-strain response")

# Work terms vs time
ax2 = fig.add_subplot(1, 2, 2)
plt.grid(True)
plt.tick_params(axis="both", which="major", labelsize=13)
plt.xlabel("time (s)", size=14)
plt.ylabel("Work (MPa)", size=14)
plt.plot(time, Wm, c="black", label=r"$W_m$ (total)")
plt.plot(time, Wm_r, c="green", label=r"$W_m^r$ (recoverable)")
plt.plot(time, Wm_ir, c="blue", label=r"$W_m^{ir}$ (irreversible)")
plt.legend(loc="best")
plt.title("Energy decomposition")

plt.tight_layout()
plt.savefig("MODUL_stress_strain.png", dpi=120)
plt.show()
Loading
Loading