Defining your own SEF¶
When the hyperelastic model you need is not in the built-in catalogue
but you can write its strain-energy function (SEF) in closed form,
hyper-surrogate lets you plug it into the framework by subclassing
the Material base class. From there, every downstream capability —
the symbolic Fortran UMAT, the data generator, the surrogate trainer —
works on your model exactly as it does on NeoHooke or
HolzapfelOgden.
This page walks through that workflow end to end.
What Material expects from a subclass¶
A custom material needs three things:
- A
DEFAULT_PARAMSclass attribute listing every parameter the SEF depends on, with default values. - An
__init__that merges the user's overrides onto the defaults and callssuper().__init__(params). - A
sefproperty that returns a single SymPy expression in the standard invariants. The expression is built from the symbolic invariants exposed byself._handlerand the parameter symbols exposed byself._symbols.
That is it. The base class handles symbolic differentiation, batch-tensor evaluation, code generation, and integration with the data pipeline.
Invariants exposed by self._handler¶
| Attribute | Symbol | Description |
|---|---|---|
_handler.isochoric_invariant1 |
\(\bar{I}_1\) | First isochoric invariant of \(\mathbf{C}\) |
_handler.isochoric_invariant2 |
\(\bar{I}_2\) | Second isochoric invariant of \(\mathbf{C}\) |
_handler.invariant3 |
\(I_3 = J^2\) | Determinant of \(\mathbf{C}\) |
For anisotropic models with fiber families you also have access to \(I_4, I_5\) — see Anisotropic materials.
Parameter symbols via self._symbols¶
Every key you list in DEFAULT_PARAMS becomes a SymPy symbol with the
same name, accessible as self._symbols["..."]. Use these inside the
sef expression so that the generated Fortran knows the parameters
are model inputs (eventually surfaced as the PROPS array in the
emitted UMAT).
Example: a one-term Ogden-like SEF¶
The SEF
is not in the built-in catalogue. Here is the full subclass — fewer than fifteen lines:
import sympy as sp
from hyper_surrogate import Material
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
)
Construct it like any built-in material:
material = MyOgdenLike({"mu": 0.8, "alpha": 3.0, "KBULK": 2000.0})
Going to Fortran in one line¶
Hand the instance to UMATHandler and you get a complete analytical
Abaqus-compatible UMAT — Cauchy stress, consistent tangent, Jaumann
correction, all derived symbolically and emitted with common-subexpression
elimination:
from hyper_surrogate import UMATHandler
UMATHandler(material).generate("mysef.f90")
No neural network is involved. No Python runtime is needed at solve
time. The PARAMETER arrays inside the emitted .f90 are determined
entirely by the symbolic expression in your sef property.
A runnable end-to-end version of this example lives at
examples/custom_sef.py.
Driving a surrogate from your custom material¶
If the SEF is expensive to evaluate (multi-scale, homogenisation, or just a heavy SymPy expression), you may prefer to train a neural-network surrogate and emit a hybrid UMAT instead. The pipeline is exactly the same as for the built-in materials:
from hyper_surrogate import (
MLP, Trainer, EnergyStressLoss,
create_datasets, extract_weights, HybridUMATEmitter,
)
train_ds, val_ds, in_norm, energy_norm = create_datasets(
MyOgdenLike(), 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("mysef_hybrid.f90")
See Training surrogates for loss-function choices and architecture options, and Exporting to Fortran for the trade-offs between the analytical and hybrid emitters.
Anisotropic and multi-fiber materials¶
A Material subclass with one or more fiber families needs to declare
the fiber directions and override sef_from_all_invariants instead of
sef. See Anisotropic materials for the
fiber-aware pattern and the PeirlinckArtery source code for a
worked two-fiber example.