Add matched frozen-state preconditioner benchmarks

This commit is contained in:
2026-09-17 16:16:46 -04:00
parent 10adf7ec37
commit e557e6b481
2 changed files with 452 additions and 2 deletions

View File

@@ -18,6 +18,7 @@ import mean_field;
#include "plateau_enthalpy_step_diagnostics.hpp"
#include "plateau_physical_diagnostics.hpp"
#include "plateau_preconditioner_diagnostics.hpp"
#include "plateau_preconditioner_benchmark.hpp"
#include "plateau_pressure_obstruction_diagnostics.hpp"
#include "plateau_shape_curvature_diagnostics.hpp"
#include "plateau_numerical_strategy_diagnostics.hpp"
@@ -33,6 +34,8 @@ struct Options {
int maxNewton = 12, maxLinear = 200, restart = 40, probeRestart = 40;
bool probes = false, noCentral = false;
bool strategyProbe = false, positivityProbe = false, positivityCertificate = false;
bool preconditionerProbe = false;
std::string preconditionerVariant = "independent";
std::string strategy = "baseline";
double gravityTolerance = 1e-10;
int gravityIterations = 300, gravityRestart = 80;
@@ -92,6 +95,10 @@ Options Parse(int argc, char **argv) {
o.positivityProbe = true;
else if (a == "--positivity-certificate")
o.positivityCertificate = true;
else if (a == "--preconditioner-probe")
o.preconditionerProbe = true;
else if (a == "--preconditioner-variant")
o.preconditionerVariant = v();
else if (a == "--pressure-probe")
o.pressureProbe = true;
else if (a == "--shape-curvature")
@@ -127,8 +134,17 @@ Options Parse(int argc, char **argv) {
if (o.positivityCertificate && o.probeState.empty())
throw std::invalid_argument("--positivity-certificate requires --probe-state");
if (o.positivityCertificate && (o.probes || o.pressureProbe || o.shapeCurvature || o.surfaceTrace ||
o.strategyProbe || o.positivityProbe || !o.probeDirection.empty() || !o.replayCandidate.empty()))
o.strategyProbe || o.positivityProbe || o.preconditionerProbe || !o.probeDirection.empty() || !o.replayCandidate.empty()))
throw std::invalid_argument("--positivity-certificate is a certificate-only mode; omit other probe options");
if (o.preconditionerProbe) {
experiment::plateau_diagnostics::ParsePreconditionerBenchmarkVariant(o.preconditionerVariant);
if (o.maxNewton != 0 || o.probes || o.pressureProbe || o.shapeCurvature || o.surfaceTrace ||
o.strategyProbe || o.positivityProbe || o.positivityCertificate || !o.probeDirection.empty() ||
!o.replayCandidate.empty() || !o.captureIterations.empty() || o.strategy != "baseline")
throw std::invalid_argument("--preconditioner-probe requires --max-newton 0 and no nonlinear/probe strategies");
if (!std::isfinite(o.linearTolerance) || o.linearTolerance <= 0.0)
throw std::invalid_argument("A preconditioner benchmark requires a finite positive --linear-tol");
}
if ((!o.probeDirection.empty() || !o.replayCandidate.empty()) && o.probeState.empty())
throw std::invalid_argument("A supplied direction or candidate requires --probe-state");
if (o.strategy != "baseline" && o.strategy != "positivity" && o.strategy != "curvature" &&
@@ -397,14 +413,41 @@ template <class Context> int RunContext(Context &context, const Options &o, cons
metadata << "n=" << o.n << "\nK=" << scales.K << "\nrho_c=" << scales.rho << "\nJ=" << o.angularMomentum
<< "\nM=1\nR_reference=1\nxi1=" << scales.xi << "\nmu1=" << scales.mu << "\ncentral_phase=" << !o.noCentral
<< "\nrestart=" << o.restart << "\nprobe_restart=" << o.probeRestart
<< "\nfrozen_probe_backend=" << (o.positivityCertificate ? "polynomial_certificate_no_linear_solve" :
<< "\nfrozen_probe_backend=" << (o.preconditionerProbe ? "direct_mfem_fgmres_preconditioner_benchmark" :
o.positivityCertificate ? "polynomial_certificate_no_linear_solve" :
o.probeDirection.empty() ? "direct_mfem_fgmres_cold" : "supplied_direction_no_linear_solve")
<< "\nlinear_tol=" << o.linearTolerance << "\nmax_linear=" << o.maxLinear
<< "\nmax_newton=" << o.maxNewton << "\natol=" << o.atol << "\nrtol=" << o.rtol
<< "\nstrategy=" << o.strategy << "\ngravity_tolerance=" << o.gravityTolerance
<< "\ngravity_iterations=" << o.gravityIterations << "\ngravity_restart=" << o.gravityRestart
<< "\npreconditioner_probe=" << o.preconditionerProbe
<< "\npreconditioner_variant=" << o.preconditionerVariant
<< "\ncompiler=" << __VERSION__ << "\ncompiled=" << __DATE__ << ' ' << __TIME__ << '\n';
metadata.flush();
if (o.preconditionerProbe) {
metadata << "benchmark_input=" << (o.probeState.empty() ? "lane_emden_seed" : "physical_snapshot") << '\n';
if (!o.probeState.empty()) metadata << "probe_state=" << o.probeState << '\n';
solver::detail::StellarEquilibriumContextDiagnostics::WithState(context, [&](auto &s, const fem::FEM &) {
if (o.probeState.empty()) {
s.trialNormalizedState = s.acceptedNormalizedState;
} else {
const auto snapshot = LoadVector(o.probeState, s.AcceptedPhysicalState().Size());
s.normalizedOperator->NormalizeState(snapshot, s.trialNormalizedState);
}
const auto preparationStart = Clock::now();
if (!s.PrepareTrial())
throw std::runtime_error("Preconditioner benchmark state is inadmissible to the physical operator");
metadata << "benchmark_frozen_preparation_seconds="
<< std::chrono::duration<double>(Clock::now() - preparationStart).count() << '\n';
metadata.flush();
experiment::plateau_diagnostics::BenchmarkPreconditioners(s, o.output,
{.variant = o.preconditionerVariant, .restart = o.restart,
.maximumIterations = o.maxLinear, .relativeTolerance = o.linearTolerance});
});
// A missed linear gate is a measured outcome, not failure to execute
// the experiment. The CSV retains both recurrence and true gate results.
return 0;
}
if (!o.probeState.empty()) {
if (o.maxNewton != 0)
throw std::invalid_argument("--probe-state requires --max-newton 0");