Skip to content

incremental_linear_elasticity: hypoelastic update with the stress as history - #134

Merged
petlenz merged 38 commits into
mainfrom
feature/incremental-elasticity
Oct 27, 2026
Merged

petlenz merged 38 commits into
mainfrom
feature/incremental-elasticity

Conversation

@petlenz

@petlenz petlenz commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

Closes #111. Part of the uvwmaterial port (#94); stacked on #133.

materials/incremental_linear_elasticity.h (incremental_linear_elasticity): σₙ₊₁ = σₙ + C : (εₙ₊₁ − εₙ) with stress as a history property and the tangent read from tangent_source (any material providing one), so a tangent that changes along the path — cure, damage, the linear-comparison composites to come — integrates incrementally. Outputs stress, tangent. Replaces incremental_linear_elasticity and incremental_solid_material; the finite-strain incremental_saint_venant_kirchhoff is left for the Hencky/finite-strain items.

Driver fix found by the report: under stress control the stress passes exactly through zero at a load reversal, and the hypoelastic sum leaves 1e-13 of round-off there; the drivers' purely relative criterion (|P:(σ − σ*)| ≤ tol·max(|σ|, |P:σ*|)) could then never be met. Both drivers now also scale with the step's own stress scale, |C|·|ε − εₙ| (|A|·|F − Fₙ| at finite strain). Covered by UniaxialStressCycleThroughZeroConverges.

Tests (test_incremental_elasticity, 4): equals the total form at every step of a load–unload path with shear and closes the cycle, with one history tensor in STATEV; the uniaxial-stress cycle through zero converges; a repeated trial from the same state does not accumulate; creation from JSON.

Report docs/materials/incremental_linear_elasticity/report.json: the standard cases plus a closed tension–shear–unload path; checks: equals linear_elasticity on every path, E under uniaxial stress, zero stress after the closed path; a step-size study showing the update exact for any step with a constant tangent.

754/754 tests pass (GCC 14, reports on).

JSON input

New material types: incremental_linear_elasticity. A json_model document that loads (the FromJson test of the PR):

{"materials": [
  {"type": "external_strain_source", "name": "strain"},
  {"type": "linear_elasticity", "name": "total", "strain_producer_name": "strain", "K": 100.0, "G": 40.0},
  {"type": "incremental_linear_elasticity", "name": "inc", "strain_source": "strain", "tangent_source": "total"}
]}

tangent_source is any producer of a tangent (here a constant one; a cure- or damage-dependent stiffness is the point of the material); outputs stress (history) and tangent.

@petlenz

petlenz commented Oct 17, 2026

Copy link
Copy Markdown
Member Author

Review findings (design, tmech expressions instead of component loops, modern C++, comments, tests, correctness). Severity: must = wrong result, hang, build break or unreachable feature; should = design, duplication, accuracy; nit = style.

incremental_linear_elasticity — needs changes (small)

  • [should] tangent reported as C_{n+1} only; for strain-dependent C the consistent tangent needs (∂C/∂ε):Δε. State "exact for strain-independent C" and add a case where tangent_source actually varies — currently the feature is exercised nowhere (report uses constant total::tangent, test compares against a constant tangent).
  • [should] F-driver convergence-scale hunk untested.
  • [nit] void update() silently overrides the dead virtual without override (others use compute); unnecessary tensor2{…} temporary; no graph-level revert() test.

@petlenz
petlenz force-pushed the feature/short-fibre-composite branch from 2e38cdd to 3805b49 Compare October 17, 2026 14:09
@petlenz
petlenz force-pushed the feature/incremental-elasticity branch 2 times, most recently from 865a597 to ab93cbb Compare October 17, 2026 14:31
@petlenz
petlenz force-pushed the feature/short-fibre-composite branch from 3805b49 to 7208e62 Compare October 17, 2026 15:48
@petlenz
petlenz force-pushed the feature/incremental-elasticity branch from ab93cbb to 0f0130a Compare October 17, 2026 15:48
@petlenz
petlenz force-pushed the feature/short-fibre-composite branch from 7208e62 to 90289e1 Compare October 17, 2026 16:48
@petlenz
petlenz force-pushed the feature/incremental-elasticity branch from 0f0130a to b110fb5 Compare October 17, 2026 16:48
@petlenz
petlenz force-pushed the feature/short-fibre-composite branch from 90289e1 to f444ebe Compare October 17, 2026 21:19
@petlenz
petlenz force-pushed the feature/incremental-elasticity branch from b110fb5 to 5e03c07 Compare October 17, 2026 21:26
@petlenz
petlenz force-pushed the feature/short-fibre-composite branch from f444ebe to ef6e567 Compare October 17, 2026 22:46
@petlenz
petlenz force-pushed the feature/incremental-elasticity branch from 5e03c07 to 40a9c01 Compare October 17, 2026 22:46
@petlenz
petlenz force-pushed the feature/short-fibre-composite branch 2 times, most recently from 5b014b8 to 7058b57 Compare October 18, 2026 12:02
@petlenz
petlenz force-pushed the feature/incremental-elasticity branch from 40a9c01 to 78698ad Compare October 18, 2026 12:13
@petlenz
petlenz force-pushed the feature/short-fibre-composite branch from 7058b57 to 557b163 Compare October 18, 2026 13:38
@petlenz
petlenz force-pushed the feature/incremental-elasticity branch from 78698ad to 3a67d63 Compare October 18, 2026 13:38
@petlenz
petlenz force-pushed the feature/short-fibre-composite branch from 557b163 to 40204f6 Compare October 18, 2026 20:03
@petlenz
petlenz force-pushed the feature/incremental-elasticity branch from 3a67d63 to 81931b5 Compare October 18, 2026 20:03
@petlenz
petlenz force-pushed the feature/short-fibre-composite branch from 40204f6 to d65215c Compare October 18, 2026 20:22
@petlenz
petlenz force-pushed the feature/incremental-elasticity branch from 81931b5 to 9bb4ac2 Compare October 18, 2026 20:22
load_driver runs one material point under mixed strain/stress control
(uniaxial stress solves the five lateral strains with the material tangent)
and records the Newton residuals; material_report runs the load cases of a
case.json and writes CSV; reports/make_report.py turns them into figures and
the results section of docs/materials/<material>/README.md.
The correction solves (P:C:P + Isym - P + Iskw) : d_eps = -P:(sigma - sigma*)
with tmech::invf instead of a hand-written 6x6 elimination; Iskw makes the
operator regular on the full 9x9 space. Strain, stress and tangent are
tensors, converted to and from the evaluator buffers with tensor_conversion.
A report document holds the model graph, the material point, recorded
columns wired to any graph property ("material::property[i,j]"), the load
cases, the figures and the checks. Includes merge shared cases and figures;
"$name" strings take parameters. material_report writes resolved.json,
graph.json (materials, parameters, produced and consumed properties) and
CSV per case; make_report.py evaluates the checks against closed forms,
draws the figures and writes the results with the wiring diagram. Every
report is a ctest (checks only).
C++: load_control (stress/strain projectors), load_path (steps as a view),
report_json (parsing into value types), case_runner (material_point and
run_case over a step_sink concept), csv_sink, graph_description; the tool
is now an entry point. load_driver keeps no per-step state: a step_result
carries its start state, so tangent_error works for any earlier step.
Produced properties are listed by name (the registry orders by address).

Python: dataclasses for runs, cases and check results; expressions, checks,
panel renderers (registry by kind) and Markdown sections as separate
functions; unit tests in reports/test_make_report.py (ctest report_script).
umat/deformation_gradient_driver.h drives a material point by F under
mixed F / P control, Newton on the full nine components with dP/dF;
deformation_control names them (21, 31, 32 included). A report whose point
has a deformation_gradient_source records F and P with nine components,
the Cauchy stress as stress_11..23 and J; tensors in cases may have nine
components. Reports for neo_hooke and saint_venant_kirchhoff with exact
checks (S = F^-1 P under uniaxial stress, simple shear, volumetric).
core/eshelby.h: sphere in closed form from the projectors; elliptic and
circular cylinders along any axis or direction (Mura 11.21); ellipsoids
with any semi-axes from Mura's I-integrals through Carlson's R_D, the
degenerate pairs (spheroid, sphere) from the sum rules, an infinite axis
dispatching to the cylinder. Tests against the closed forms, the limits,
uvwmaterial's spheroid formula and the identity S_iikk = (1 + nu)/(1 - nu).
…ses, projectors of the controls, load path accessors
…n tensor, float-safe rotation_to

The elliptic integrals come from Boost.Math where they are first used (the
Eshelby tensors). rotation() builds [axis]_x as -(epsilon . axis); rotation_to()
uses 8 eps of the scalar type instead of 1e-14, which did not compile for float
and would have been c > 1 there. [[nodiscard]] on the pure helpers.
…per shape

eshelby_sphere, eshelby_cylinder, eshelby_spheroid and eshelby_ellipsoid consume
nu_source / nu_property and publish the output "tensor"; the engine orders and
re-evaluates them with nu. Two scalar producers feed them: poisson_ratio_from_moduli
(K_source, G_source -> value) and isotropic_moduli (tangent_source -> K, G,
poisson_ratio through isotropize, rejecting an anisotropic tangent). Composites
hold a node through eshelby_tensor_interface (tensor() at the wired nu, tensor(nu)
for the self-consistent iterate). core/eshelby.h keeps the closed forms: the
integrals come from Boost.Math (ellint_rd), the tensors are assembled on the
eigenprojectors, equal axes are detected at sqrt(eps).
core/isotropize.h: K = Pvol::C / Dim, G = Pdev::C / (2 (Dim(Dim+1)/2 - 1)),
and the isotropic part built from them. core/lebedev.h with generated
tables (tools/extract_lebedev.py from uvwmaterial's rule, precisions 3 to
23, up to 194 points): spherical means and integrals of tensor-valued
functions of the direction. eshelby::depolarisation for ellipsoids via
Carlson's R_J, the elliptic cylinder in closed form, and the cylinder along
a direction.
Phases are materials providing a tangent, with volume fractions; outputs
the effective tangent, the stress and the strain concentration tensors
(sum c_i A_i = Isym). Report with the closed forms of the three kernels
and their ordering. Report generator: derived values resolve in
dependency order, independent of the key order.
…ent producer over phases

The composite consumes the phases' produced tangents, fractions and Eshelby
tensors and produces the effective tangent and the concentrations; the stress
is a linear_stress on the tangent. It reaches into no other node: no
material_ref, no eshelby(nu), no isotropisation of the matrix.

Self-consistent is a graph cycle closed by one local edge: isotropic_moduli
on the composite tangent produces nu of the effective medium, the Eshelby
nodes read it with nu_edge = local (from nu_initial on the first pass), and
one update() is one relaxed Picard step with "residual" as its error. The
matrix is a phase with its own Eshelby node, so its shape is no longer a
hard-coded sphere. material_point_evaluator closes the fixed point per host
call (residual_property, residual_tolerance, max_passes; the report point
names them). isotropic_moduli with a non-positive tolerance projects without
the isotropy check, which the effective medium needs.
…ution

Spheroidal fibres in an isotropic matrix with the second-order orientation
tensor: Mori-Tanaka with the orientation-averaged dilute concentration, or
the averaged unidirectional stiffness. Eshelby tensors per direction are
cached for the matrix Poisson ratio. Report with aligned, injection-moulded
and random states against the unidirectional composite and isotropy.
core/orientation.h: fit_acg solves a_i = R_D(b_j, b_k, b_i) / 3 with
det b = 1 by Newton in the principal frame of the orientation tensor (any
A, not only diagonal), the aligned case as a single direction, planar
cases by an eigenvalue floor; nodes maps the Lebedev rule through B^-1/2;
average integrates a function of the direction.
… in the fibre frame

Both phases come in as produced properties (tangent, fraction, the fibre's
Eshelby tensor along x1); the composite consumes them, forms the dilute
concentration once in the fibre frame, rotates onto the quadrature directions
with otimesu(R, R) fixed per direction, and produces the tangent and the two
concentrations. The stress is a linear_stress on the tangent. No material_ref.
…history

sigma_{n+1} = sigma_n + C : (eps_{n+1} - eps_n) with the tangent read from
another material, for path-dependent tangents. Report: equals the total
form on every path, a closed path returns to zero, exact for any step.
Drivers: the convergence test also scales with |C| |d eps| (|A| |dF|), so
a hypoelastic stress crossing zero under stress control converges.
@petlenz
petlenz force-pushed the feature/short-fibre-composite branch from d65215c to 02524f8 Compare October 19, 2026 04:06
@petlenz
petlenz force-pushed the feature/incremental-elasticity branch from 9bb4ac2 to 108389e Compare October 19, 2026 04:06
Main has the reviewed split of this branch (#199-#203), so main's version is
taken everywhere; the branch no longer differs from main.
…ls-and-constants

# Conflicts:
#	tests/CMakeLists.txt
@petlenz
petlenz changed the base branch from feature/short-fibre-composite to main October 24, 2026 21:16
…eshelby-tensors

# Conflicts:
#	tests/CMakeLists.txt
…v-depolarisation

# Conflicts:
#	tests/CMakeLists.txt
…/rule-of-mixture

# Conflicts:
#	tests/CMakeLists.txt
# Conflicts:
#	include/numsim-materials/report/report_json.h
#	tests/CMakeLists.txt
residual_source, residual_tolerance and max_passes are driver parameters
(material_point's defaults) and keys of the report's "point"; the mean-field
tangent report uses them for the self-consistent fixed point.
…averaging

# Conflicts:
#	tests/CMakeLists.txt
…-composite

# Conflicts:
#	tests/CMakeLists.txt
…-elasticity

# Conflicts:
#	include/numsim-materials/umat/deformation_gradient_driver.h
#	include/numsim-materials/umat/material_point_driver.h
#	tests/CMakeLists.txt
@petlenz
petlenz marked this pull request as ready for review October 27, 2026 17:50
@petlenz
petlenz merged commit 58d2aed into main Oct 27, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

incremental_linear_elasticity and incremental Saint Venant-Kirchhoff

1 participant