Add bounded fully coupled second-order trial correction and frozen screen

This commit is contained in:
2026-09-18 09:41:00 -04:00
parent 2944dd5019
commit 0d87b8b919
14 changed files with 2442 additions and 35 deletions

View File

@@ -1359,6 +1359,7 @@ export namespace mean_field::solver {
const Clock::time_point lineSearchStart = Clock::now();
try {
for (int trial = 0; trial < options.backtracking.maximumTrials; ++trial) {
strategy.BeginTrial(diagnostics.numericalStrategies);
++lineSearchTrials;
++diagnostics.totalLineSearchTrials;
nonlinear::LineSearchTrialDisposition trialDisposition =
@@ -1497,6 +1498,41 @@ export namespace mean_field::solver {
metricSeconds = std::chrono::duration<double>(Clock::now() - metricStart).count();
timings.metricEvaluationSeconds += metricSeconds;
diagnostics.totalMetricEvaluationSeconds += metricSeconds;
const auto acceptsTrialMetric = [&](const nonlinear::MetricEvaluation &candidateMetric) {
if constexpr (Strategy::altersDirection) {
return candidateMetric.merit <= acceptedMetric.merit +
options.backtracking.sufficientDecrease * stepLength *
diagnostics.numericalStrategies.lastMeritDirectionalDerivative;
} else {
return AcceptStep(acceptedMetric, candidateMetric, stepLength, options.backtracking);
}
};
if constexpr (Strategy::correctsTrial) {
if (detail::AllStellarRanksSatisfy(ValidMetric(trialMetric), communicator)) {
const Clock::time_point correctionStart = Clock::now();
const double previousMetricSeconds = metricSeconds;
strategy.TryImproveTrial(state, stepLength, preparationResult, trialMetric,
diagnostics.numericalStrategies,
[&](const mfem::Vector &residual) {
const Clock::time_point start = Clock::now();
auto metric = EvaluateMetric(residual);
const double seconds = std::chrono::duration<double>(Clock::now() - start).count();
metricSeconds += seconds;
timings.metricEvaluationSeconds += seconds;
diagnostics.totalMetricEvaluationSeconds += seconds;
return metric;
}, acceptsTrialMetric,
[&](const nonlinear::MetricEvaluation &candidate, const nonlinear::MetricEvaluation &original) {
return ValidMetric(candidate) && candidate.merit < original.merit;
});
const double seconds = std::chrono::duration<double>(Clock::now() - correctionStart).count() -
(metricSeconds - previousMetricSeconds);
preparationSeconds += seconds;
timings.trialPreparationSeconds += seconds;
diagnostics.totalTrialPreparationSeconds += seconds;
minimumJacobianDeterminant = preparationResult->minimumJacobianDeterminant;
}
}
observedTrialMetric = trialMetric;
if (!detail::AllStellarRanksSatisfy(ValidMetric(trialMetric), communicator)) {
rejectionReason = StellarEquilibriumFailureReason::non_finite_residual;
@@ -1508,15 +1544,7 @@ export namespace mean_field::solver {
++diagnostics.nonFiniteLineSearchTrials;
} else if (
detail::AllStellarRanksSatisfy(
[&] {
if constexpr (Strategy::altersDirection) {
return trialMetric.merit <= acceptedMetric.merit +
options.backtracking.sufficientDecrease * stepLength *
diagnostics.numericalStrategies.lastMeritDirectionalDerivative;
} else {
return AcceptStep(acceptedMetric, trialMetric, stepLength, options.backtracking);
}
}(),
acceptsTrialMetric(trialMetric),
communicator
)
) {
@@ -1559,7 +1587,8 @@ export namespace mean_field::solver {
timings.observerSeconds += NotifyLineSearchTrial(
iteration, trial, stepLength, trialDisposition, rejectionSource, observedTrialMetric,
minimumJacobianDeterminant, preparationSeconds, metricSeconds, residualAvailable, state
minimumJacobianDeterminant, preparationSeconds, metricSeconds, residualAvailable, state,
diagnostics.numericalStrategies
);
if (accepted) {
@@ -1767,7 +1796,8 @@ export namespace mean_field::solver {
const double preparationSeconds,
const double metricSeconds,
const bool residualAvailable,
const State &state
const State &state,
const nonlinear::NumericalStrategyDiagnostics &numericalStrategies
) {
if constexpr (nonlinear::detail::ObservesLineSearchTrial<ObserverType>) {
const nonlinear::AfterLineSearchTrial event{
@@ -1786,7 +1816,8 @@ export namespace mean_field::solver {
.candidateNormalizedResidual = residualAvailable
? detail::ReadOnlySpan(state.trialNormalizedResidual)
: std::span<const mfem::real_t>{},
.normalizedDirection = detail::ReadOnlySpan(state.normalizedCorrection)
.normalizedDirection = detail::ReadOnlySpan(state.normalizedCorrection),
.numericalStrategies = std::addressof(numericalStrategies)
};
const Clock::time_point observerStart = Clock::now();
nonlinear::detail::InvokeAfterLineSearchTrial(m_observer, event);