Repository navigation
Conversation
…to feature/issue-214
#214) material_base::converges(residual, tolerance, max_passes) declares a fixed point. The engine forms one group per declaration from the callbacks between a local edge's consumer and the residual, splits the callback list into stages and reruns each group until the residual meets its tolerance, or throws cutback_error after max_passes or on NaN. The nonlinear mean-field composite declares its residual; material_point's residual_source, the UMAT residual_property and the report "point" residual keys are deprecated.
…nverges() arguments (#214) form_groups seeds a group only from a local edge P->Q whose producer is downstream of its consumer and upstream of the residual, so solver-internal pairs (both edges local) are no longer iterated. A loop that runs through the group of another residual is rejected at finalize, naming both residuals. iterate() treats a non-finite residual as not converged before the tolerance test and rechecks the settings on every update. The nonlinear composite keeps a NaN residual (std::max dropped it). converges() and declare_converges() delete the overloads that take a temporary tolerance or max_passes.
Member
Author
|
Review findings and how each was addressed (fix commit on top of this branch, then main merged in).
PR description updated: composite defaults change the behaviour against the host Gate after the merge of main: GCC 14 -O1, BUILD=0 TEST=0, 0 warnings. |
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 #214 (part of epic #221).
Merge after #222 and #223. This branch contains both. Its own change is the last commit.
What and why
Until now,
material_point::evaluate()ran the fixed point of a local edge by rerunning the whole graph. Every callback outside the loop ran once per pass: recorders, plots and steppers. The loop also worked only in hosts that setresidual_source. This PR moves the fixed point intoupdate().material_base::converges(residual, tolerance, max_passes)takes the residual output and references to the two parameters, which are read on every update. Engine API:material_interface::converging_outputs().finalize(),property_engine::form_groups()builds one iteration group per declaration. Alocaledge P→Q (Q reads P's previous value; the typed edges of Property engine: typed edges, graph export (DOT/JSON) and a named cycle (#213, #188) #223) seeds the group only if it closes a loop: P is downstream of Q through non-local edges and upstream of the residual (upstream_of()). The group is the callbacks on the paths from such a Q to the residual. Solver-internal pairs whose both edges are local (j2_plasticityand its hardening law) never seed a group. The callback list is then split into stages. Before each group come the callbacks it reads that have not run yet. Then the group itself. Then everything else.update(). It runs the stages in order. A group reruns until|residual| <= tolerance, and a residual that is not finite (NaN or infinity) never converges, even with an infinite tolerance. Aftermax_passes, or at a non-finite residual, it throwsnumsim::materials::cutback_error:'mt::residual' did not reach 1e-10 in 200 passes (last …).retire()andverify()are unchanged. The active callbacks are redistributed into their stages whenever the active list changes (reactivate(),retire()).update_property()on a group member runs its upstream once, without iterating. The code is unchanged: it usesm_activein topological order.finalize(), each naming the node:converges()with no closed local loop behind the residual (logic_error);stateful()callback inside a group (logic_error);logic_error, "one residual per group");max_passesbelow one (invalid_argument);logic_error, names both residuals);logic_error).mean_field_composite_local_nonlineardeclaresconverges(residual). Its new parameters aretolerance(default 1e-10) andmax_passes(default 200), matching what the reports and tests used.orientation_phasespasses both through to the generated nonlinear composite and rejects them for a linear one.material_point'sresidual_source,residual_toleranceandmax_passesstill work. With a converging composite, they find the residual converged after the one update and do nothing. They are marked deprecated in the comments and docs. I did not use[[deprecated]]: these are runtime parameters, and existing tests still use them.config::residual_propertyand the report"point"keys are deprecated the same way and still parsed."tolerance"/"max_passes"on the composite (on theorientation_phasesentry forshort_fibre_damage), not on"point".writing_a_material.md§6,mean_field.md§5, README core API,uvwmaterial-mapping.md.Decisions and deviations from the issue
finalize()throws instead and names both residuals. Nested support is out of scope.mean_field_composite_local_nonlinearnow converges insideupdate()with its owntolerance(default 1e-10) andmax_passes(default 200). The host'sresidual_tolerance/max_passesno longer govern it: with a converging composite they find the residual converged after the one update and do nothing. A deck that relied on a looser host tolerance or a different pass count must settolerance/max_passeson the composite.toleranceis compared with the residual as is. The composite's residual is the largest relative change of a phase tangent,||C - C_used|| / ||C||, so 1e-10 is a relative bound. A customconverges()residual must be scaled the same way. A zero or NaN tangent gives a NaN residual (0/0) and throwscutback_errorinstead of passing.converges()keeps pointers totoleranceandmax_passes. The overloads that take a temporary (converges(r, 1e-10, 100)) are deleted, so the call is ill-formed instead of dangling.downstream(consumer) ∩ upstream(residual), a group that reads the converged output of another group would contain that group too, and finalize would report an overlap. Now the upstream group converges first and the downstream group iterates only its own callbacks. A second residual over the same loop is still an error, which is the issue's "one residual per group". Groups are disjoint by construction.prefix / groups / suffix, and with one group they are exactly that. A callback that neither the group nor anything before it reads goes after the group. Such a callback runs once, on the converged values.finalize(), as the issue asks.test_mean_field_damage's all-global comparison model (build(false)) therefore usesmean_field_composite_local_linear.cutback_erroris the host-neutralnumsim::materials::cutback_errorof Host-neutral errors.h and external_state_source.h out of umat/ (#212) #222, so theumat::cutback_erroralias catches it too.form_groups(),distribute(),iterate(),run(),m_stages,m_stage_of) plus three call sites.compute_execution_order()is untouched, to keep the conflict with Property engine: partial derivatives per callback, chain rule by forward accumulation #217 small.Tests
tests/test_iteration_groups.cpp(new): aguessnode readsrelax::xover a local edge, andrelaxerdeclaresconverges(residual). A statefulprobesits upstream.update()iterates to the residual.property_recorderafter the group writes one row per update holding the converged value. This failed before the change: the host loop wrote one row per pass.residual_sourceonmaterial_pointstill works and adds no rows.max_passes = 3throwscutback_errornamingrelax::residual. The context is failed untilrevert(), andmax_passesis reread afterset_parameter().finalize()(negative tolerance,max_passes0) throwinvalid_argumentat the next update.update_property()on a member runs one pass.set_parameter().converges()without a local edge, a local edge whose producer is outside the group or behind the residual, astateful()member, an overlapping second residual, nested loops, a loop through another group, and invalid settings each throw at finalize, naming the node.converges()anddeclare_converges()with a temporary tolerance ormax_passesare rejected bystatic_assert.tests/test_mean_field_damage.cpp:TheEngineIteratesBitwiseAsTheMaterialPointDid: 24 strain steps of loading and unloading, under strain and under uniaxial-stress control. The engine-owned loop and the old host loop give identical stress, tangent, damage, residual and driver residuals (EXPECT_EQ). The host loop is reproduced exactly: composite tolerance ∞, so one pass per update, plusresidual_source, which repeats the whole update.TheUmatEvaluatorConvergesWithoutAResidualProperty: the UMAT evaluator withoutresidual_propertyconverges every call. It failed before the change (residual 0.96 after one pass).cutback_error.ResidualDecreasesOverThePassesOfAStepnow sets the composite tolerance to ∞ to observe single passes.tests/test_orientation_phases.cpp:toleranceandmax_passespass through to the nonlinear composite and are rejected for a linear one.Reports: I ran every
docs/materials/*/report.jsonwithmaterial_reportbefore the change (old decks withresidual_source) and after it (new decks, engine-owned loop). All 208 CSVs of the 14 reports are bitwise identical, includingmean_field_damage,mean_field_reuss_plasticandshort_fibre_damage, and the driver iteration logs.Gate
GCC 14, full build and ctest: BUILD=0 TEST=0, 0 warnings, 1538/1538 tests passed (after merging main).