Integrate typed Newton strategies with checked trial gravity and feasibility
This commit is contained in:
@@ -32,6 +32,7 @@ export import :seed.stellar_equilibrium_projection;
|
||||
export import :solver.linear_backend;
|
||||
export import :solver.newton;
|
||||
export import :solver.stellar_structure;
|
||||
export import :solver.stellar_newton_strategy;
|
||||
|
||||
export namespace mean_field::solver {
|
||||
template <typename Model, typename Discretization, typename PreconditionerPrescription, typename LinearBackend>
|
||||
@@ -523,6 +524,10 @@ export namespace mean_field::solver {
|
||||
std::unique_ptr<PhysicalInverseType> physicalInverse;
|
||||
std::unique_ptr<NormalizedInverseType> normalizedInverse;
|
||||
std::unique_ptr<LinearBackendType> linearBackend;
|
||||
// Only destruction is erased: strategy construction and all
|
||||
// numerical calls retain their statically checked concrete types.
|
||||
// Declaring this last keeps borrowed dependencies alive on teardown.
|
||||
std::unique_ptr<detail::NewtonStrategyStorage> numericalStrategyStorage;
|
||||
|
||||
private:
|
||||
[[nodiscard]] static ProblemType *ProblemPointer(const std::shared_ptr<Storage> &candidate) noexcept {
|
||||
@@ -1209,9 +1214,37 @@ export namespace mean_field::solver {
|
||||
const auto &options = m_newton.options();
|
||||
options.Validate();
|
||||
detail::RequireCollectivelyIdenticalNewtonOptions(options, communicator);
|
||||
detail::RequireCollectivelyIdenticalNumerics(m_newton.numerics(), communicator);
|
||||
state.BeginEvaluation();
|
||||
using Strategy = detail::PreparedNewtonStrategy<std::remove_reference_t<decltype(state)>,
|
||||
typename NewtonType::NumericsType>;
|
||||
static_assert(!Strategy::altersDirection ||
|
||||
std::same_as<typename NewtonType::MetricType, nonlinear::NormalizedResidualMetric>,
|
||||
"Polynomial direction repair currently requires the normalized residual merit.");
|
||||
state.numericalStrategyStorage.reset();
|
||||
auto preparedStrategy = std::make_unique<Strategy>(state, m_newton.numerics());
|
||||
auto &strategy = *preparedStrategy;
|
||||
state.numericalStrategyStorage = std::move(preparedStrategy);
|
||||
|
||||
StellarEquilibriumEvaluationDiagnostics diagnostics{};
|
||||
if (!strategy.CheckState(state.acceptedNormalizedState, diagnostics.numericalStrategies)) {
|
||||
const auto initial = EvaluateMetric(state.acceptedNormalizedResidual);
|
||||
diagnostics.initialResidualNorm = diagnostics.finalResidualNorm = initial.residualNorm;
|
||||
return Failure(state, std::move(diagnostics), StellarEquilibriumFailureReason::inadmissible_state,
|
||||
"The initial enthalpy polynomial could not be certified nonnegative.");
|
||||
}
|
||||
try {
|
||||
if (!strategy.Initialize(state, diagnostics.numericalStrategies)) {
|
||||
state.RestoreAccepted();
|
||||
const auto initial = EvaluateMetric(state.acceptedNormalizedResidual);
|
||||
diagnostics.initialResidualNorm = diagnostics.finalResidualNorm = initial.residualNorm;
|
||||
return Failure(state, std::move(diagnostics), StellarEquilibriumFailureReason::linear_solve_failure,
|
||||
"Initial gravity completion failed its checked inner solve or prepared residual.");
|
||||
}
|
||||
} catch (...) {
|
||||
state.RestoreAccepted();
|
||||
throw;
|
||||
}
|
||||
nonlinear::MetricEvaluation acceptedMetric = EvaluateMetric(state.acceptedNormalizedResidual);
|
||||
diagnostics.initialResidualNorm = acceptedMetric.residualNorm;
|
||||
diagnostics.finalResidualNorm = acceptedMetric.residualNorm;
|
||||
@@ -1240,9 +1273,7 @@ export namespace mean_field::solver {
|
||||
|
||||
state.linearRightHandSide = state.acceptedNormalizedResidual;
|
||||
state.linearRightHandSide *= -1.0;
|
||||
LinearSolveReport linearReport = state.linearBackend->Solve(
|
||||
state.linearRightHandSide, state.normalizedCorrection, options.linearSolve
|
||||
);
|
||||
LinearSolveReport linearReport = strategy.Solve(state, options.linearSolve, diagnostics.numericalStrategies);
|
||||
diagnostics.lastLinearSolve = linearReport;
|
||||
diagnostics.totalLinearSolveSeconds += linearReport.solveSeconds;
|
||||
|
||||
@@ -1275,6 +1306,13 @@ export namespace mean_field::solver {
|
||||
);
|
||||
}
|
||||
|
||||
if (!strategy.CaptureDirection(state, diagnostics.numericalStrategies)) {
|
||||
NotifyAfter(iteration, nonlinear::IterationDisposition::globalization_failure, false, 0.0, 0,
|
||||
diagnostics.initialResidualNorm, acceptedMetric, acceptedMetric, linearReport, timings, state);
|
||||
return Failure(state, std::move(diagnostics), StellarEquilibriumFailureReason::globalization_failure,
|
||||
"No certified enthalpy repair with a decreasing linear merit was available.");
|
||||
}
|
||||
|
||||
const nonlinear::MetricEvaluation previousMetric = acceptedMetric;
|
||||
if (options.backtracking.geometryPreflight) {
|
||||
const Clock::time_point geometryPreflightStart = Clock::now();
|
||||
@@ -1345,9 +1383,15 @@ export namespace mean_field::solver {
|
||||
trialDisposition = nonlinear::LineSearchTrialDisposition::non_finite_state;
|
||||
rejectionSource = "candidate state";
|
||||
++diagnostics.nonFiniteLineSearchTrials;
|
||||
} else if (!strategy.CheckState(state.trialNormalizedState, diagnostics.numericalStrategies)) {
|
||||
rejectionReason = StellarEquilibriumFailureReason::inadmissible_state;
|
||||
rejectionMessage = "The candidate enthalpy polynomial could not be certified nonnegative.";
|
||||
trialDisposition = nonlinear::LineSearchTrialDisposition::inadmissible_state;
|
||||
rejectionSource = "enthalpy polynomial";
|
||||
++diagnostics.inadmissibleLineSearchTrials;
|
||||
} else {
|
||||
const Clock::time_point preparationStart = Clock::now();
|
||||
auto preparationResult = state.PrepareTrial();
|
||||
auto preparationResult = strategy.PrepareTrial(state, stepLength, diagnostics.numericalStrategies);
|
||||
preparationSeconds = std::chrono::duration<double>(Clock::now() - preparationStart).count();
|
||||
timings.trialPreparationSeconds += preparationSeconds;
|
||||
diagnostics.totalTrialPreparationSeconds += preparationSeconds;
|
||||
@@ -1422,6 +1466,12 @@ export namespace mean_field::solver {
|
||||
minimumJacobianDeterminant = preparationRejection.minimumJacobianDeterminant;
|
||||
}
|
||||
}
|
||||
} else if (!strategy.TrialSolveSucceeded()) {
|
||||
rejectionReason = StellarEquilibriumFailureReason::linear_solve_failure;
|
||||
rejectionMessage = "The candidate gravity correction failed its checked solve or prepared residual.";
|
||||
trialDisposition = nonlinear::LineSearchTrialDisposition::auxiliary_solve_failure;
|
||||
rejectionSource = "gravity correction";
|
||||
minimumJacobianDeterminant = preparationResult->minimumJacobianDeterminant;
|
||||
} else if (!detail::AllStellarRanksSatisfy(
|
||||
AllFinite(state.trialNormalizedResidual), communicator
|
||||
)) {
|
||||
@@ -1453,7 +1503,15 @@ export namespace mean_field::solver {
|
||||
++diagnostics.nonFiniteLineSearchTrials;
|
||||
} else if (
|
||||
detail::AllStellarRanksSatisfy(
|
||||
AcceptStep(acceptedMetric, trialMetric, stepLength, options.backtracking),
|
||||
[&] {
|
||||
if constexpr (Strategy::altersDirection) {
|
||||
return trialMetric.merit <= acceptedMetric.merit +
|
||||
options.backtracking.sufficientDecrease * stepLength *
|
||||
diagnostics.numericalStrategies.lastMeritDirectionalDerivative;
|
||||
} else {
|
||||
return AcceptStep(acceptedMetric, trialMetric, stepLength, options.backtracking);
|
||||
}
|
||||
}(),
|
||||
communicator
|
||||
)
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user