Repository navigation
Damage laws: power, saturation and Peerlings; isotropic_damage report - #138
Merged
Merged
Conversation
petlenz
force-pushed
the
feature/curing-eigenstrain
branch
from
October 16, 2026 23:43
c660cef to
4a060f0
Compare
petlenz
force-pushed
the
feature/damage-laws
branch
from
October 16, 2026 23:43
bc4db85 to
37b8050
Compare
This was referenced Oct 16, 2026
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. damage laws — needs changes
|
petlenz
force-pushed
the
feature/curing-eigenstrain
branch
from
October 17, 2026 14:40
4a060f0 to
bd0004d
Compare
petlenz
force-pushed
the
feature/damage-laws
branch
from
October 17, 2026 14:48
37b8050 to
747415f
Compare
petlenz
force-pushed
the
feature/curing-eigenstrain
branch
from
October 17, 2026 16:05
bd0004d to
6eff511
Compare
petlenz
force-pushed
the
feature/damage-laws
branch
from
October 17, 2026 16:14
747415f to
30ae6c6
Compare
petlenz
force-pushed
the
feature/curing-eigenstrain
branch
from
October 17, 2026 17:12
6eff511 to
30fbcba
Compare
petlenz
force-pushed
the
feature/damage-laws
branch
from
October 17, 2026 17:20
30ae6c6 to
ac4fc2a
Compare
petlenz
force-pushed
the
feature/curing-eigenstrain
branch
from
October 17, 2026 21:36
30fbcba to
bc568df
Compare
petlenz
force-pushed
the
feature/damage-laws
branch
from
October 17, 2026 21:44
ac4fc2a to
90bf8c3
Compare
petlenz
force-pushed
the
feature/curing-eigenstrain
branch
from
October 17, 2026 22:46
bc568df to
f964442
Compare
petlenz
force-pushed
the
feature/damage-laws
branch
from
October 17, 2026 22:46
90bf8c3 to
80652e8
Compare
petlenz
force-pushed
the
feature/curing-eigenstrain
branch
from
October 18, 2026 12:51
f964442 to
a4bd728
Compare
petlenz
force-pushed
the
feature/damage-laws
branch
from
October 18, 2026 13:01
80652e8 to
28d4b5e
Compare
petlenz
force-pushed
the
feature/curing-eigenstrain
branch
from
October 18, 2026 13:38
a4bd728 to
4c1aac1
Compare
petlenz
force-pushed
the
feature/damage-laws
branch
2 times, most recently
from
October 18, 2026 18:55
13fd875 to
485fd9b
Compare
petlenz
force-pushed
the
feature/curing-eigenstrain
branch
2 times, most recently
from
October 18, 2026 20:03
93a893f to
8a07c8c
Compare
petlenz
force-pushed
the
feature/damage-laws
branch
from
October 18, 2026 20:03
485fd9b to
44f79fd
Compare
petlenz
force-pushed
the
feature/curing-eigenstrain
branch
from
October 18, 2026 20:22
8a07c8c to
e1819c4
Compare
petlenz
force-pushed
the
feature/damage-laws
branch
from
October 18, 2026 20:22
44f79fd to
056f7b1
Compare
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).
…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.
eps_th = alpha (T - T_ref) I from a scalar temperature source; linear elasticity subtracts an optional eigenstrain: sigma = C : (eps - eps_eig).
dz/dt = (k1 + k2 z^m) (1 - z)^n with Arrhenius rate constants, as a rate law for rk_integrator next to curing_rate (the autocatalytic term alone; nth order is curing_rate with m = 0).
inclusion takes a fraction_source ("material::property") so the volume
fraction can follow the degree of cure; the chemical shrinkage
beta xi I is thermal_eigenstrain with the cure state as its source.
Three propagation laws of uvwmaterial next to the linear and exponential ones (damage_exponential2 is the existing exponential law). Report of isotropic_damage with every law recorded; the report point takes stress_property and tangent_property; named entries of an include can be replaced, so the tangent check skips the steps on the damage surface.
petlenz
force-pushed
the
feature/curing-eigenstrain
branch
from
October 19, 2026 04:07
e1819c4 to
9af23f2
Compare
petlenz
force-pushed
the
feature/damage-laws
branch
from
October 19, 2026 04:07
056f7b1 to
fc0f56b
Compare
…ls-and-constants # Conflicts: # tests/CMakeLists.txt
…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
…genstrain # Conflicts: # tests/CMakeLists.txt
…-rate # Conflicts: # tests/CMakeLists.txt
…rain # Conflicts: # tests/CMakeLists.txt
# Conflicts: # include/numsim-materials/report/report_json.h # tests/CMakeLists.txt # tests/test_report_case.cpp
The report's "point" names them for materials that publish e.g. damaged_stress / damaged_tangent (isotropic_damage report).
petlenz
marked this pull request as ready for review
October 27, 2026 17:50
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #119. Part of the uvwmaterial port (#94); stacked on #137.
Of uvwmaterial's propagation laws,
linearanddamage_exponential2already exist (linear_damage_law,exponential_damage_law). Added, all readingkappaof ayield_sourceand producingdamage,d_damage:power_damage_law: d = K (κ − κ₀)ᵐ, capped atd_max(0.999).saturation_damage_law: d = K (1 − e^(−m(κ − κ₀))) (exponential_lawof the original).peerlings_damage_law: d = 1 − κ₀/κ (1 − α + α e^(β(κ₀ − κ))), capped atd_max(damage_exponential3, without its hard-coded 0.99 factor).damage_exponential1(1 − e^(β(κ₀ − κ))) is not carried over; it is Peerlings' law without the κ₀/κ factor and was unused.Tests (
test_damage_laws, 6): each law's value and derivative against the formula and against finite differences of the real chain (strain → von Mises state → threshold yield → law), the threshold, the cap and the saturation limit; creation from JSON.Report
docs/materials/isotropic_damage/report.json: the full damage chain with the exponential law as the point and all laws recorded; checks: σ = (1 − d) σ_elastic, κ monotone, no damage below the threshold, every law monotone in κ, Peerlings and saturation against their formulas, and the tangent check. The report point now takesstress_property/tangent_property(damaged_stress,damaged_tangent).What the report found: the consistent tangent of
isotropic_damageis correct (2e-11 against central differences on damaging and elastic steps; the existing test only verified elastic steps), but a central difference straddles the kink wherever the strain state lies exactly on the damage surface — the first threshold step and the load reversals of a symmetric cycle. The tangent check now skips those steps (|ε_eq − κₙ| > 1e-9). To make that possible an include's named entry (check, case, figure) can be replaced by a local entry of the same name instead of being appended.Review follow-up: the plumbing lives once in the CRTP
detail::scalar_damage_law<Derived>— κ fromyield_source,kappa_0,d_max(default 0.999, validated in (0, 1]), the threshold, the cap with a zero slope, the two outputs; a law is itsparameters(), its own checks andevaluate(κ) → {d, dd}. The two older laws (exponential_damage_law,linear_damage_law) sit on the same base, so the cap isd_maxeverywhere (their 0.999 was hard-coded) and the derivative is zero at it. Parameters are validated: power K > 0, m > 0; saturation 0 < K ≤ 1 (K is the final damage — K > 1 gave negative stiffness), m > 0; Peerlings κ₀ > 0, 0 ≤ α ≤ 1, β ≥ 0; exponential κ₀ > 0, β ≥ 0; linear κ_f ≥ κ₀ (equal stays legal: full damage right past the threshold, as the existing test pins).report_spec.hreadsnamewithat(); the report's tangent check excludes loading steps through the recordedyield::is_yielding(report columns now read int flags) and the on-surface steps — threshold step and path reversals, where the central-difference stencil straddles the C⁰ kink — through eq ≠ κ. New tests: every rejection, the cap's zero slope, the older laws on the shared cap.945/945 tests pass (GCC 14, reports on).
JSON input
New material types:
power_damage_law,saturation_damage_law,peerlings_damage_law. Ajson_modeldocument that loads (theFromJsontest of the PR):{"materials": [ {"type": "external_strain_source", "name": "strain"}, {"type": "von_mises_state_function", "name": "state", "strain_source": "strain"}, {"type": "strain_threshold_yield", "name": "yield", "state_source": "state", "kappa_critical": 0.002}, {"type": "power_damage_law", "name": "pow", "yield_source": "yield", "K": 200.0, "m": 1.5, "kappa_0": 0.002}, {"type": "saturation_damage_law", "name": "sat", "yield_source": "yield", "K": 0.9, "m": 300.0, "kappa_0": 0.002}, {"type": "peerlings_damage_law", "name": "peer", "yield_source": "yield", "kappa_0": 0.002, "alpha": 0.95, "beta": 200.0} ]}Each law consumes
kappaof itsyield_sourceand producesdamage,d_damageforisotropic_damage("damage_source": "pow"). Power: D = K (κ − κ₀)ᵐ; saturation: D = K (1 − exp(−m (κ − κ₀))); Peerlings: D = 1 − κ₀/κ (1 − α + α exp(−β (κ − κ₀)));d_maxcaps (default 0.999).