Add bounded fully coupled second-order trial correction and frozen screen
This commit is contained in:
@@ -43,8 +43,9 @@ namespace mean_field::solver::detail {
|
||||
void RequireCollectivelyIdenticalNumerics(const Recipe &recipe, const MPI_Comm communicator) {
|
||||
using Enthalpy = typename Recipe::EnthalpyType;
|
||||
using Gravity = typename Recipe::GravityType;
|
||||
std::array<int, 8> integers{};
|
||||
std::array<double, 2> reals{};
|
||||
using Trial = typename Recipe::TrialType;
|
||||
std::array<int, 13> integers{};
|
||||
std::array<double, 5> reals{};
|
||||
integers[0] = std::same_as<Enthalpy, nonlinear::PolynomialEnthalpy>;
|
||||
integers[1] = std::same_as<Gravity, nonlinear::GravityCurvatureCorrection> ? 1 :
|
||||
std::same_as<Gravity, nonlinear::EliminatedGravity> ? 2 : 0;
|
||||
@@ -57,15 +58,27 @@ namespace mean_field::solver::detail {
|
||||
integers[4] = options.control.maximumIterations;
|
||||
integers[5] = options.backend.restartLength;
|
||||
integers[6] = options.backend.printLevel;
|
||||
reals = {options.control.relativeTolerance, options.control.absoluteTolerance};
|
||||
reals[0] = options.control.relativeTolerance;
|
||||
reals[1] = options.control.absoluteTolerance;
|
||||
}
|
||||
integers[7] = std::isfinite(reals[0]) && std::isfinite(reals[1]);
|
||||
std::array<int, 8> minimumIntegers{}, maximumIntegers{};
|
||||
std::array<double, 2> minimumReals{}, maximumReals{};
|
||||
if (MPI_Allreduce(integers.data(), minimumIntegers.data(), 8, MPI_INT, MPI_MIN, communicator) != MPI_SUCCESS ||
|
||||
MPI_Allreduce(integers.data(), maximumIntegers.data(), 8, MPI_INT, MPI_MAX, communicator) != MPI_SUCCESS ||
|
||||
MPI_Allreduce(reals.data(), minimumReals.data(), 2, MPI_DOUBLE, MPI_MIN, communicator) != MPI_SUCCESS ||
|
||||
MPI_Allreduce(reals.data(), maximumReals.data(), 2, MPI_DOUBLE, MPI_MAX, communicator) != MPI_SUCCESS) {
|
||||
integers[8] = std::same_as<Trial, nonlinear::CoupledCurvatureCorrection>;
|
||||
if constexpr (std::same_as<Trial, nonlinear::CoupledCurvatureCorrection>) {
|
||||
const auto &options = recipe.trial.correction;
|
||||
integers[9] = options.control.maximumIterations;
|
||||
integers[10] = options.backend.restartLength;
|
||||
integers[11] = options.backend.printLevel;
|
||||
integers[12] = recipe.trial.maximumAttemptsPerIteration;
|
||||
reals[2] = options.control.relativeTolerance;
|
||||
reals[3] = options.control.absoluteTolerance;
|
||||
reals[4] = options.maximumCorrectionRatio;
|
||||
}
|
||||
integers[7] = std::all_of(reals.begin(), reals.end(), [](double value) { return std::isfinite(value); });
|
||||
std::array<int, 13> minimumIntegers{}, maximumIntegers{};
|
||||
std::array<double, 5> minimumReals{}, maximumReals{};
|
||||
if (MPI_Allreduce(integers.data(), minimumIntegers.data(), 13, MPI_INT, MPI_MIN, communicator) != MPI_SUCCESS ||
|
||||
MPI_Allreduce(integers.data(), maximumIntegers.data(), 13, MPI_INT, MPI_MAX, communicator) != MPI_SUCCESS ||
|
||||
MPI_Allreduce(reals.data(), minimumReals.data(), 5, MPI_DOUBLE, MPI_MIN, communicator) != MPI_SUCCESS ||
|
||||
MPI_Allreduce(reals.data(), maximumReals.data(), 5, MPI_DOUBLE, MPI_MAX, communicator) != MPI_SUCCESS) {
|
||||
throw std::runtime_error("Newton could not compare numerical strategies across ranks.");
|
||||
}
|
||||
if (minimumIntegers != maximumIntegers || minimumReals != maximumReals ||
|
||||
@@ -73,6 +86,7 @@ namespace mean_field::solver::detail {
|
||||
throw std::invalid_argument("Newton numerical strategies and controls must agree on every rank.");
|
||||
}
|
||||
if constexpr (!std::same_as<Gravity, nonlinear::MonolithicGravity>) { recipe.gravity.gravity.Validate(); }
|
||||
if constexpr (std::same_as<Trial, nonlinear::CoupledCurvatureCorrection>) { recipe.trial.Validate(); }
|
||||
}
|
||||
|
||||
template <typename State, typename Policy> class PreparedGravityStrategy;
|
||||
@@ -91,7 +105,9 @@ namespace mean_field::solver::detail {
|
||||
void CopyDiagnostics(nonlinear::NumericalStrategyDiagnostics &) const { }
|
||||
};
|
||||
|
||||
template <typename State>
|
||||
// Export the prepared workspace so bounded diagnostics can exercise this
|
||||
// same gravity completion and arithmetic gate without reproducing them.
|
||||
export template <typename State>
|
||||
class StellarGravityWorkspace {
|
||||
public:
|
||||
using Operation = std::remove_cvref_t<decltype(*std::declval<State>().normalizedOperator)>;
|
||||
@@ -406,15 +422,34 @@ namespace mean_field::solver::detail {
|
||||
PreparedStellarEnthalpyStrategy<State> m_prepared;
|
||||
};
|
||||
|
||||
template <typename State, typename Policy> struct PreparedTrialPath;
|
||||
template <typename State> struct PreparedTrialPath<State, nonlinear::UncorrectedTrial> {
|
||||
PreparedTrialPath(State &, const nonlinear::UncorrectedTrial &) { }
|
||||
};
|
||||
template <typename State> struct PreparedTrialPath<State, nonlinear::CoupledCurvatureCorrection> {
|
||||
PreparedTrialPath(State &state, const nonlinear::CoupledCurvatureCorrection &policy)
|
||||
: transaction(state, policy.correction, policy.maximumAttemptsPerIteration) { }
|
||||
PreparedCoupledTrialTransaction<State> transaction;
|
||||
};
|
||||
|
||||
template <typename State, nonlinear::NewtonNumericalConfiguration Recipe>
|
||||
class PreparedNewtonStrategy final : public NewtonStrategyStorage {
|
||||
public:
|
||||
using Enthalpy = typename Recipe::EnthalpyType;
|
||||
using Gravity = typename Recipe::GravityType;
|
||||
using Trial = typename Recipe::TrialType;
|
||||
static constexpr bool altersDirection = std::same_as<Enthalpy, nonlinear::PolynomialEnthalpy>;
|
||||
static constexpr bool correctsTrial = std::same_as<Trial, nonlinear::CoupledCurvatureCorrection>;
|
||||
|
||||
PreparedNewtonStrategy(State &state, const Recipe &recipe)
|
||||
: m_enthalpy(state, recipe.enthalpy), m_gravity(state, recipe.gravity) { }
|
||||
: m_enthalpy(state, recipe.enthalpy), m_gravity(state, recipe.gravity), m_trial(state, recipe.trial) { }
|
||||
|
||||
void BeginTrial(nonlinear::NumericalStrategyDiagnostics &diagnostics) {
|
||||
if constexpr (correctsTrial) {
|
||||
m_trial.transaction.BeginTrial();
|
||||
diagnostics.coupledTrial = m_trial.transaction.GetDiagnostics();
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckState(const mfem::Vector &state, nonlinear::NumericalStrategyDiagnostics &diagnostics) {
|
||||
return m_enthalpy.Check(state, diagnostics);
|
||||
@@ -451,6 +486,12 @@ namespace mean_field::solver::detail {
|
||||
if (!std::isfinite(diagnostics.lastMeritDirectionalDerivative) ||
|
||||
diagnostics.lastMeritDirectionalDerivative >= 0.0) { return false; }
|
||||
}
|
||||
if constexpr (correctsTrial) {
|
||||
// Repair has finished and the accepted Jacobian is still current.
|
||||
// Every later trial, including a gravity-corrected one, uses this
|
||||
// same full affine target and the original-direction norm cap.
|
||||
m_trial.transaction.CaptureBase(state.acceptedNormalizedResidual, state.normalizedCorrection);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
auto PrepareTrial(State &state, const double alpha, nonlinear::NumericalStrategyDiagnostics &diagnostics) {
|
||||
@@ -460,9 +501,19 @@ namespace mean_field::solver::detail {
|
||||
}
|
||||
bool TrialSolveSucceeded() const noexcept { return m_gravity.TrialSolveSucceeded(); }
|
||||
bool DirectionSolveFailed() const noexcept { return m_directionSolveFailed; }
|
||||
template <typename Preparation, typename Metric, typename Evaluate, typename Accepts, typename Better>
|
||||
void TryImproveTrial(State &state, double alpha, Preparation &preparation, Metric &metric,
|
||||
nonlinear::NumericalStrategyDiagnostics &diagnostics,
|
||||
Evaluate &&evaluate, Accepts &&accepts, Better &&better) requires(correctsTrial) {
|
||||
m_trial.transaction.TryImprove(state, alpha, preparation, metric,
|
||||
std::forward<Evaluate>(evaluate), std::forward<Accepts>(accepts), std::forward<Better>(better),
|
||||
[&](const mfem::Vector &candidate) { return m_enthalpy.Check(candidate, diagnostics); });
|
||||
diagnostics.coupledTrial = m_trial.transaction.GetDiagnostics();
|
||||
}
|
||||
private:
|
||||
bool m_directionSolveFailed{false};
|
||||
PreparedEnthalpyStrategy<State, Enthalpy> m_enthalpy;
|
||||
PreparedGravityStrategy<State, Gravity> m_gravity;
|
||||
[[no_unique_address]] PreparedTrialPath<State, Trial> m_trial;
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user