Repository navigation
thermal_eigenstrain and an eigenstrain_source for linear_elasticity - #135
Merged
Merged
Conversation
This was referenced Oct 16, 2026
petlenz
force-pushed
the
feature/incremental-elasticity
branch
from
October 16, 2026 23:43
6579831 to
69465a8
Compare
petlenz
force-pushed
the
feature/thermal-eigenstrain
branch
from
October 16, 2026 23:43
81327cc to
891c53f
Compare
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. thermal_eigenstrain — ready with nits
|
petlenz
force-pushed
the
feature/incremental-elasticity
branch
from
October 17, 2026 14:21
69465a8 to
865a597
Compare
petlenz
force-pushed
the
feature/thermal-eigenstrain
branch
2 times, most recently
from
October 17, 2026 14:31
b3e882d to
41afa98
Compare
petlenz
force-pushed
the
feature/incremental-elasticity
branch
2 times, most recently
from
October 17, 2026 15:48
ab93cbb to
0f0130a
Compare
petlenz
force-pushed
the
feature/thermal-eigenstrain
branch
from
October 17, 2026 15:48
41afa98 to
c667526
Compare
petlenz
force-pushed
the
feature/incremental-elasticity
branch
from
October 17, 2026 16:48
0f0130a to
b110fb5
Compare
petlenz
force-pushed
the
feature/thermal-eigenstrain
branch
from
October 17, 2026 16:57
c667526 to
829454b
Compare
Member
Author
|
Producer/consumer pass: the expansion coefficient is a consumed property, |
petlenz
force-pushed
the
feature/incremental-elasticity
branch
from
October 17, 2026 21:26
b110fb5 to
5e03c07
Compare
petlenz
force-pushed
the
feature/thermal-eigenstrain
branch
from
October 17, 2026 21:26
829454b to
0c29804
Compare
petlenz
force-pushed
the
feature/incremental-elasticity
branch
from
October 17, 2026 22:46
5e03c07 to
40a9c01
Compare
petlenz
force-pushed
the
feature/thermal-eigenstrain
branch
from
October 17, 2026 22:46
0c29804 to
ba5606b
Compare
petlenz
force-pushed
the
feature/incremental-elasticity
branch
from
October 18, 2026 12:13
40a9c01 to
78698ad
Compare
petlenz
force-pushed
the
feature/thermal-eigenstrain
branch
from
October 18, 2026 12:23
ba5606b to
ca687fc
Compare
petlenz
force-pushed
the
feature/incremental-elasticity
branch
from
October 18, 2026 13:38
78698ad to
3a67d63
Compare
petlenz
force-pushed
the
feature/thermal-eigenstrain
branch
from
October 18, 2026 13:38
ca687fc to
c32b2f9
Compare
petlenz
force-pushed
the
feature/incremental-elasticity
branch
from
October 18, 2026 20:03
3a67d63 to
81931b5
Compare
petlenz
force-pushed
the
feature/thermal-eigenstrain
branch
from
October 18, 2026 20:03
c32b2f9 to
b075d06
Compare
petlenz
force-pushed
the
feature/incremental-elasticity
branch
from
October 18, 2026 20:22
81931b5 to
9bb4ac2
Compare
petlenz
force-pushed
the
feature/thermal-eigenstrain
branch
from
October 18, 2026 20:22
b075d06 to
031b4e7
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).
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.
eps_th = alpha (T - T_ref) I from a scalar temperature source; linear elasticity subtracts an optional eigenstrain: sigma = C : (eps - eps_eig).
petlenz
force-pushed
the
feature/incremental-elasticity
branch
from
October 19, 2026 04:06
9bb4ac2 to
108389e
Compare
petlenz
force-pushed
the
feature/thermal-eigenstrain
branch
from
October 19, 2026 04:06
031b4e7 to
25909f3
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
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 #114. Part of the uvwmaterial port (#94); stacked on #134.
materials/thermal_eigenstrain.h(thermal_eigenstrain): ε_th = α (T − T_ref) I with the temperature from anexternal_scalar_source(historystate, default nametemperature). Outputeigenstrain. This is the pattern for every eigenstrain (the chemical shrinkage of #115 follows it): a producer ofeigenstrain, and elastic materials subtracting it.linear_elasticity: optionaleigenstrain_source(default none); with it σ = C : (ε − ε_eig), the tangent unchanged. Replaceseigenstrain_temperature_material_base,eigenstrain_material_base,thermo_mechanical_elasticand the thermo-mechanical bases of uvwmaterial as a graph wiring instead of an inheritance tree.Tests (
test_thermal_eigenstrain, 4): free expansion is stress free; constrained heating gives −3Kα ΔT on the diagonal; superposition with a mechanical strain, and no temperature change gives plain elasticity; creation from JSON.No report yet: the report runner has no temperature path (listed in #126); the unit tests carry the closed forms.
762/762 tests pass (GCC 14, reports on).
JSON input
New material types:
thermal_eigenstrain. Ajson_modeldocument that loads (theFromJsontest of the PR):{"materials": [ {"type": "external_strain_source", "name": "strain"}, {"type": "external_scalar_source", "name": "temperature"}, {"type": "constant_scalar", "name": "alpha", "value": 1e-5}, {"type": "thermal_eigenstrain", "name": "thermal", "alpha_source": "alpha::value", "temperature_source": "temperature", "reference_temperature": 20.0}, {"type": "linear_elasticity", "name": "elastic", "strain_producer_name": "strain", "eigenstrain_source": "thermal", "K": 100.0, "G": 40.0} ]}alpha_sourceasmaterial::property(a cure-dependent producer for the chemo variants);temperature_sourcea history source publishingstate(default nametemperature); outputeigenstrain, consumed bylinear_elasticity.eigenstrain_source(this PR) andhencky_elasticity.eigenstrain_source(#141).