diff --git a/experiments/GRAVITY_DIRECTION_DIAGNOSTICS_2026-09-17.md b/experiments/GRAVITY_DIRECTION_DIAGNOSTICS_2026-09-17.md new file mode 100644 index 0000000..56f67e9 --- /dev/null +++ b/experiments/GRAVITY_DIRECTION_DIAGNOSTICS_2026-09-17.md @@ -0,0 +1,26 @@ +# Gravity work accounting on direction rejection + +The combined polynomial-enthalpy and reduced-gravity strategy checks descent +twice: once when selecting the enthalpy repair, and again after lifting the +repaired retained direction into gravity coordinates. A successful gravity +relift performs real auxiliary work even when the second check rejects the +direction before line search. + +Previously, the successful-relift diagnostics were copied only after the second +check. Its early rejection could therefore leave solve counts, iteration counts, +timings, and the last inner residual at their earlier linear-solve snapshot. +The snapshot now occurs immediately after successful direction capture and +before the final descent check. Linear-report coordinates retain their existing +meaning; this changes no tolerance, direction, state, or acceptance decision. + +The immutable `b21fa40` rotating `positivity-reduced` artifact records 20 gravity +solves and a positive final merit slope. Those counters can omit a successful +last relift: they do not distinguish rejection during enthalpy repair from +rejection after relifting. Its `direction_repairs` count also includes a chosen +positive repair fraction whose candidate subsequently fails descent. Historical +artifacts must retain these qualifications rather than being retroactively +interpreted as complete work counters. + +The regression is a repeat of that bounded combined rotating case, checking +unchanged accepted states and residual history while accounting for all +successful relift work. No extra numerical fixture or solver change is needed. diff --git a/libmeanfield/interface/solver/newton_numerics.cppm b/libmeanfield/interface/solver/newton_numerics.cppm index c5fec12..027d8c9 100644 --- a/libmeanfield/interface/solver/newton_numerics.cppm +++ b/libmeanfield/interface/solver/newton_numerics.cppm @@ -72,6 +72,8 @@ export namespace mean_field::solver::nonlinear { struct NumericalStrategyDiagnostics final { int polynomialChecks{0}; + // Nonzero repair proposals include directions later rejected by a + // descent check; this measures repair activity, not accepted steps. int directionRepairs{0}; double lastRepairFraction{0.0}; double lastMeritDirectionalDerivative{0.0}; diff --git a/libmeanfield/interface/solver/stellar_newton_strategy.cppm b/libmeanfield/interface/solver/stellar_newton_strategy.cppm index abaff16..f1d569d 100644 --- a/libmeanfield/interface/solver/stellar_newton_strategy.cppm +++ b/libmeanfield/interface/solver/stellar_newton_strategy.cppm @@ -440,6 +440,9 @@ namespace mean_field::solver::detail { return false; } } + // A successful gravity relift still belongs in the work report if + // the following full-merit check rejects the repaired direction. + m_gravity.CopyDiagnostics(diagnostics); if constexpr (altersDirection && std::same_as) { mfem::Vector action(state.Problem().EquationSize()); state.normalizedOperator->Mult(state.normalizedCorrection, action); @@ -448,7 +451,6 @@ namespace mean_field::solver::detail { if (!std::isfinite(diagnostics.lastMeritDirectionalDerivative) || diagnostics.lastMeritDirectionalDerivative >= 0.0) { return false; } } - m_gravity.CopyDiagnostics(diagnostics); return true; } auto PrepareTrial(State &state, const double alpha, nonlinear::NumericalStrategyDiagnostics &diagnostics) {