Distinguish strategy failures and expose the actual trial tangent
This commit is contained in:
@@ -265,6 +265,9 @@ export namespace mean_field::solver::nonlinear {
|
||||
std::span<const mfem::real_t> candidatePhysicalState{};
|
||||
std::span<const mfem::real_t> candidateNormalizedState{};
|
||||
std::span<const mfem::real_t> candidateNormalizedResidual{};
|
||||
// A curved trial path cannot recover its Newton tangent by dividing
|
||||
// the candidate displacement by alpha. Observers borrow it directly.
|
||||
std::span<const mfem::real_t> normalizedDirection{};
|
||||
};
|
||||
|
||||
struct AfterIteration final {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
@@ -22,7 +23,7 @@ export namespace mean_field::solver::nonlinear {
|
||||
struct GravitySolveOptions final {
|
||||
LinearSolveControl control{.relativeTolerance = 1.0e-10, .absoluteTolerance = 1.0e-14,
|
||||
.maximumIterations = 300};
|
||||
linear::FGMRESOptions backend{.restartLength = 80};
|
||||
linear::FGMRESOptions backend{.restartLength = 80, .printLevel = -1};
|
||||
|
||||
void Validate() const { control.Validate(); backend.Validate(); }
|
||||
};
|
||||
@@ -64,14 +65,18 @@ export namespace mean_field::solver::nonlinear {
|
||||
template <typename Candidate>
|
||||
concept NewtonNumericalConfiguration = IsNewtonNumerics<std::remove_cvref_t<Candidate>>::value;
|
||||
|
||||
enum class LinearReportSpace : std::uint8_t { full_system, reduced_system, gravity_subsystem };
|
||||
|
||||
struct NumericalStrategyDiagnostics final {
|
||||
int polynomialChecks{0};
|
||||
int directionRepairs{0};
|
||||
double lastRepairFraction{0.0};
|
||||
double lastMeritDirectionalDerivative{0.0};
|
||||
int gravitySolves{0};
|
||||
int gravityIterations{0};
|
||||
int gravityFailures{0};
|
||||
std::uint64_t gravitySolves{0};
|
||||
std::uint64_t gravityIterations{0};
|
||||
std::uint64_t gravityFailures{0};
|
||||
std::uint64_t gravityPreparedFailures{0};
|
||||
LinearReportSpace lastLinearReportSpace{LinearReportSpace::full_system};
|
||||
double gravitySolveSeconds{0.0};
|
||||
double lastGravityTrueResidual{0.0};
|
||||
double lastGravityCorrectionNorm{0.0};
|
||||
|
||||
@@ -1307,9 +1307,14 @@ export namespace mean_field::solver {
|
||||
}
|
||||
|
||||
if (!strategy.CaptureDirection(state, diagnostics.numericalStrategies)) {
|
||||
NotifyAfter(iteration, nonlinear::IterationDisposition::globalization_failure, false, 0.0, 0,
|
||||
const bool linearFailure = strategy.DirectionSolveFailed();
|
||||
NotifyAfter(iteration, linearFailure ? nonlinear::IterationDisposition::linear_solve_failure :
|
||||
nonlinear::IterationDisposition::globalization_failure, false, 0.0, 0,
|
||||
diagnostics.initialResidualNorm, acceptedMetric, acceptedMetric, linearReport, timings, state);
|
||||
return Failure(state, std::move(diagnostics), StellarEquilibriumFailureReason::globalization_failure,
|
||||
return Failure(state, std::move(diagnostics), linearFailure ?
|
||||
StellarEquilibriumFailureReason::linear_solve_failure :
|
||||
StellarEquilibriumFailureReason::globalization_failure,
|
||||
linearFailure ? "Lifting the repaired direction failed its checked gravity solve." :
|
||||
"No certified enthalpy repair with a decreasing linear merit was available.");
|
||||
}
|
||||
|
||||
@@ -1780,7 +1785,8 @@ export namespace mean_field::solver {
|
||||
.candidateNormalizedState = detail::ReadOnlySpan(state.trialNormalizedState),
|
||||
.candidateNormalizedResidual = residualAvailable
|
||||
? detail::ReadOnlySpan(state.trialNormalizedResidual)
|
||||
: std::span<const mfem::real_t>{}
|
||||
: std::span<const mfem::real_t>{},
|
||||
.normalizedDirection = detail::ReadOnlySpan(state.normalizedCorrection)
|
||||
};
|
||||
const Clock::time_point observerStart = Clock::now();
|
||||
nonlinear::detail::InvokeAfterLineSearchTrial(m_observer, event);
|
||||
|
||||
@@ -118,6 +118,8 @@ namespace mean_field::solver::detail {
|
||||
report.gravitySolves = subsystem.GetSolveCount();
|
||||
report.gravityIterations = subsystem.GetIterationCount();
|
||||
report.gravityFailures = subsystem.GetFailureCount();
|
||||
report.gravityPreparedFailures = preparedFailures;
|
||||
report.lastLinearReportSpace = lastLinearReportSpace;
|
||||
report.gravitySolveSeconds = subsystem.GetSolveSeconds();
|
||||
if (const auto &last = subsystem.GetLastSolveReport(); last.has_value()) {
|
||||
report.lastGravityTrueResidual = last->trueResidualNorm;
|
||||
@@ -174,6 +176,7 @@ namespace mean_field::solver::detail {
|
||||
std::isfinite(targetDefect) && targetDefect <= tolerance;
|
||||
lastPreparedDefect = targetDefect;
|
||||
}
|
||||
if (!preparation || !trialSolveSucceeded) { ++preparedFailures; }
|
||||
return preparation;
|
||||
}
|
||||
|
||||
@@ -186,6 +189,8 @@ namespace mean_field::solver::detail {
|
||||
double lastCorrectionNorm{0.0};
|
||||
double lastPreparedDefect{0.0};
|
||||
double liftedRelativeDefect{0.0};
|
||||
std::uint64_t preparedFailures{0};
|
||||
nonlinear::LinearReportSpace lastLinearReportSpace{nonlinear::LinearReportSpace::full_system};
|
||||
};
|
||||
|
||||
template <typename State>
|
||||
@@ -259,13 +264,18 @@ namespace mean_field::solver::detail {
|
||||
LinearSolveReport Solve(State &state, const LinearSolveControl &control) {
|
||||
this->subsystem.Refresh();
|
||||
try {
|
||||
this->lastLinearReportSpace = nonlinear::LinearReportSpace::gravity_subsystem;
|
||||
auto inner = m_operator.TryCondenseRightHandSide(state.linearRightHandSide, m_rhs);
|
||||
if (!inner.Converged()) { return inner; }
|
||||
this->subsystem.GetPartition().GatherRetainedValues(state.normalizedCorrection, m_correction);
|
||||
this->lastLinearReportSpace = nonlinear::LinearReportSpace::reduced_system;
|
||||
auto report = m_backend.Solve(m_rhs, m_correction, control);
|
||||
if (!report.Converged()) { return report; }
|
||||
inner = m_operator.TryLiftCorrection(m_correction, state.linearRightHandSide, state.normalizedCorrection);
|
||||
if (!inner.Converged()) { return inner; }
|
||||
if (!inner.Converged()) {
|
||||
this->lastLinearReportSpace = nonlinear::LinearReportSpace::gravity_subsystem;
|
||||
return inner;
|
||||
}
|
||||
state.normalizedOperator->Mult(state.normalizedCorrection, this->fullAction);
|
||||
this->fullAction -= state.linearRightHandSide;
|
||||
const auto communicator = this->subsystem.GetCommunicator();
|
||||
@@ -283,6 +293,7 @@ namespace mean_field::solver::detail {
|
||||
}
|
||||
return report;
|
||||
} catch (const GravityReductionFailure &failure) {
|
||||
this->lastLinearReportSpace = nonlinear::LinearReportSpace::gravity_subsystem;
|
||||
return failure.GetReport();
|
||||
}
|
||||
}
|
||||
@@ -368,9 +379,14 @@ namespace mean_field::solver::detail {
|
||||
return report;
|
||||
}
|
||||
bool CaptureDirection(State &state, nonlinear::NumericalStrategyDiagnostics &diagnostics) {
|
||||
m_directionSolveFailed = false;
|
||||
if (!m_enthalpy.Repair(state, diagnostics)) { return false; }
|
||||
if constexpr (!std::same_as<Gravity, nonlinear::EliminatedGravity> || altersDirection) {
|
||||
if (!m_gravity.CaptureDirection(state)) { m_gravity.CopyDiagnostics(diagnostics); return false; }
|
||||
if (!m_gravity.CaptureDirection(state)) {
|
||||
m_directionSolveFailed = true;
|
||||
m_gravity.CopyDiagnostics(diagnostics);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if constexpr (altersDirection && std::same_as<Gravity, nonlinear::EliminatedGravity>) {
|
||||
mfem::Vector action(state.Problem().EquationSize());
|
||||
@@ -389,7 +405,9 @@ namespace mean_field::solver::detail {
|
||||
return preparation;
|
||||
}
|
||||
bool TrialSolveSucceeded() const noexcept { return m_gravity.TrialSolveSucceeded(); }
|
||||
bool DirectionSolveFailed() const noexcept { return m_directionSolveFailed; }
|
||||
private:
|
||||
bool m_directionSolveFailed{false};
|
||||
PreparedEnthalpyStrategy<State, Enthalpy> m_enthalpy;
|
||||
PreparedGravityStrategy<State, Gravity> m_gravity;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user