hyper-surrogate
Define hyperelastic materials in Python, deploy them in your finite element solver.
- Github repository: https://github.com/jpsferreira/hyper-surrogate/
- Documentation: https://jpsferreira.github.io/hyper-surrogate/
Table of Contents
Why hyper-surrogate?
Finite element solvers need constitutive models. Writing and maintaining those models — especially user-material subroutines for Abaqus, FEAP and similar Fortran-based solvers — is repetitive: kinematics, second Piola–Kirchhoff stress, consistent tangent, Jaumann correction, reference-configuration freedom, all in Fortran 77 or 90.
hyper-surrogate lets you define your material once in Python and emit a self-contained Fortran 90 user subroutine deployable in any Fortran-based FE solver. You choose how the material is defined:
- Built-in symbolic SEF — one of ten classical isotropic and anisotropic models (Neo-Hooke, Mooney-Rivlin, Yeoh, Holzapfel-Ogden, Gasser-Ogden-Holzapfel, …).
- Custom symbolic SEF — subclass
Materialwith asefproperty inSymPyand the framework derives stress and tangent automatically. - Data-driven surrogate — train an MLP, ICNN, or polyconvex ICNN on energy and stress samples from any ground-truth source and emit a hybrid UMAT whose energy is the trained network and whose stress and tangent are computed analytically in Fortran.
All three paths produce the same artefact: a .f90 with Cauchy stress,
analytical consistent tangent, and stress-freedom at the reference
configuration enforced exactly at emission time. No Python runtime
is required at solve time.
Architecture
Material ─> DeformationGenerator ─> Dataset ─> MLP / ICNN ─> HybridUMATEmitter ─> .f90
│ │
│ (symbolic SEF) │ (trained NN weights)
│ │
└──── UMATHandler ─────────────────────────────┘
(analytical Fortran via SymPy CSE)
Features
- Three entry points, one output — built-in SEF, custom SymPy SEF, or trained surrogate, all emit a structurally identical Fortran UMAT.
- Symbolic mechanics — automatic PK2 stress and stiffness tensor derivation via SymPy; analytical Cauchy push-forward and Jaumann correction emitted with common-subexpression elimination.
- NN architectures — MLP, Input-Convex NN, and polyconvex ICNN, trained with an energy-plus-stress dual loss that backpropagates the gradient target through autograd.
- Hybrid UMAT — neural-network strain energy with closed-form analytical kinematics, stress, and consistent tangent in Fortran; layer-additive Hessian backpropagation verified against autograd at $10^{-5}$ tolerance.
- Stress freedom at reference — enforced exactly at emission time by subtracting a compile-time offset; no surprise residual stress at $\mathbf{C} = \mathbf{I}$.
- Anisotropic support — fiber pseudo-invariants $I_4, I_5$ for one or two fiber families with arbitrary orientations.
Quick Start
Three short pipelines, mirroring the three ways to define a material:
Path A — built-in symbolic SEF
from hyper_surrogate import NeoHooke, UMATHandler
UMATHandler(NeoHooke({"C10": 0.5, "KBULK": 1000.0})).generate("neohooke.f90")
Path B — custom user-defined SEF
import sympy as sp
from hyper_surrogate import Material, UMATHandler
class MyOgdenLike(Material):
DEFAULT_PARAMS = {"mu": 1.0, "alpha": 2.0, "KBULK": 1000.0}
def __init__(self, parameters=None):
super().__init__({**self.DEFAULT_PARAMS, **(parameters or {})})
@property
def sef(self):
h = self._handler
mu, alpha, K = (self._symbols[k] for k in ("mu", "alpha", "KBULK"))
return ((mu / alpha) * (h.isochoric_invariant1 ** (alpha / 2) - 3)
+ 0.5 * K * (sp.sqrt(h.invariant3) - 1) ** 2)
UMATHandler(MyOgdenLike()).generate("mysef.f90")
Path C — data-driven surrogate
from hyper_surrogate import (
NeoHooke, MLP, Trainer, EnergyStressLoss,
create_datasets, extract_weights, HybridUMATEmitter,
)
material = NeoHooke({"C10": 0.5, "KBULK": 1000.0})
train_ds, val_ds, in_norm, energy_norm = create_datasets(
material, n_samples=4000, input_type="invariants",
target_type="energy", deformation_mode="combined_compressible",
)
model = MLP(input_dim=3, output_dim=1, hidden_dims=[64, 64, 64], activation="softplus")
result = Trainer(model, train_ds, val_ds,
loss_fn=EnergyStressLoss(alpha=1.0, beta=1.0),
max_epochs=2000, patience=200).fit()
exported = extract_weights(result.model, in_norm, energy_norm)
HybridUMATEmitter(exported).write("neohooke_hybrid.f90")
See the documentation for the full tutorial set, including custom materials, anisotropic models, and the export-path decision tree.
Installation
pip install hyper-surrogate # core (NumPy, SymPy)
pip install hyper-surrogate[ml] # with PyTorch for ML surrogates
From source with uv:
git clone https://github.com/jpsferreira/hyper-surrogate.git
cd hyper-surrogate
uv sync --all-groups --extra ml
Examples
Runnable scripts are in the examples/ directory:
| Script | Path | Description |
|---|---|---|
analytical_umat.py |
A | Built-in NeoHooke → analytical UMAT via UMATHandler |
custom_sef.py |
B | Custom Ogden-like SEF → analytical UMAT |
export_hybrid_umat.py |
C | End-to-end train + HybridUMATEmitter export |
train_neohooke_sef.py |
C | Train MLP on NeoHooke SEF with hybrid inference |
train_icnn_energy.py |
C | Train ICNN with EnergyStressLoss |
train_polyconvex.py |
C | Train PolyconvexICNN with per-invariant convexity |
train_holzapfel_ogden.py |
C | Anisotropic fiber-reinforced training pipeline |
Run any example with:
uv run python examples/<script>.py
Contributing
Contributions are welcome! See CONTRIBUTING.md for setup instructions and guidelines.
Release files for hyper-surrogate 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| hyper_surrogate-0.4.0.tar.gz | 310.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hyper_surrogate-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 372.3 kB
Release files / hyper_surrogate-0.4.0.tar.gz
| Download URL | hyper_surrogate-0.4.0.tar.gz |
|---|---|
| Size | 310.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
df7ee90e17b7c841b791c728df76e6e3dfdca8e211a1e5c5248ba3fed3f92cfd
|
|
BLAKE2b-256 checksum How to use checksums |
5300bc450bb455ee37c941b9b34bed03e444b538296b162edb1c3f6e47bc4fbf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / hyper_surrogate-0.4.0-py3-none-any.whl
| Download URL | hyper_surrogate-0.4.0-py3-none-any.whl |
|---|---|
| Size | 62.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
33637e243bc081ee900ac1bd7e0aa4702262df397f88b8c5567a18255301da45
|
|
BLAKE2b-256 checksum How to use checksums |
8a1888f06d4bde57ae492e7d07f973e3e22758d1483f3c9c692d1da3f00b3bfa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|