Record reproducible plateau campaigns and selected-direction probes

This commit is contained in:
2026-09-16 14:42:41 -04:00
parent 80cc3d83d7
commit 9bd4af6996
4 changed files with 274 additions and 17 deletions

View File

@@ -23,7 +23,7 @@ namespace {
using Clock = std::chrono::steady_clock;
struct Options {
double n = 1., angularMomentum = 0., atol = 0., rtol = 1e-8, linearTolerance = .03;
int maxNewton = 12, maxLinear = 200, restart = 40;
int maxNewton = 12, maxLinear = 200, restart = 40, probeRestart = 40;
bool probes = false, noCentral = false;
std::string mesh = "sandbox.smesh";
std::filesystem::path output, probeState;
@@ -51,6 +51,8 @@ Options Parse(int argc, char **argv) {
o.maxLinear = std::stoi(v());
else if (a == "--restart")
o.restart = std::stoi(v());
else if (a == "--probe-restart")
o.probeRestart = std::stoi(v());
else if (a == "--linear-tol")
o.linearTolerance = std::stod(v());
else if (a == "--atol")
@@ -68,6 +70,8 @@ Options Parse(int argc, char **argv) {
}
if (o.output.empty() || o.n < 1 || o.n >= 5 || o.maxNewton < 0 || o.maxLinear < 1 || o.restart < 1)
throw std::invalid_argument("Invalid options");
if (o.probeRestart != 40 && o.probeRestart != 160)
throw std::invalid_argument("--probe-restart must be 40 or 160");
if (std::filesystem::exists(o.output))
throw std::runtime_error("Output directory already exists");
std::filesystem::create_directories(o.output);
@@ -130,7 +134,9 @@ void FrozenProbes(State &s, const fem::FEM &f, const Options &o, const mfem::Vec
histories << "restart,tolerance,iteration,reported_norm,final\n";
auto blocks = File(o.output / "frozen_blocks.csv");
blocks << "restart,tolerance,block,F_norm,Jp_plus_F_norm,global_correction_norm\n";
mfem::Vector best, baselineAction(base.Size());
mfem::Vector selectedCorrection, baselineAction(base.Size());
// These are two cold direct MFEM solves, not the configured production
// backend or any recycled Krylov history.
for (int restart : {40, 160}) {
mfem::FGMRESSolver linear(MPI_COMM_WORLD);
solver::ResidualHistoryMonitor monitor;
@@ -164,17 +170,18 @@ void FrozenProbes(State &s, const fem::FEM &f, const Options &o, const mfem::Vec
<< ',' << Norm(action, r.offset, r.size) << ',' << p.Norml2() << '\n';
blocks.flush();
std::cout << "Frozen restart=" << restart << " true=" << rel << " seconds=" << seconds << std::endl;
if (restart == 40)
best = p;
if (restart == o.probeRestart)
selectedCorrection = p;
}
s.normalizedCorrection = best;
s.normalizedOperator->Mult(best, baselineAction);
std::cout << "Frozen probe direction restart=" << o.probeRestart << std::endl;
s.normalizedCorrection = selectedCorrection;
s.normalizedOperator->Mult(selectedCorrection, baselineAction);
auto positivity = File(o.output / "enthalpy_step_bound.csv");
positivity << "metric,value\n";
for (const auto &[key, value] : experiment::plateau_diagnostics::MeasureEnthalpyStep(s, f, o.n, physicalBase))
positivity << key << ',' << value << '\n';
positivity.flush();
SaveVector(o.output / "probe_normalized_correction.txt", best);
SaveVector(o.output / "probe_normalized_correction.txt", selectedCorrection);
auto fd = File(o.output / "finite_differences.csv");
fd << "epsilon,admissible,block,absolute_error,relative_to_Jp,action_norm\n";
const auto safe = s.EstimateLargestSafeStepSize(1., .9);
@@ -186,7 +193,7 @@ void FrozenProbes(State &s, const fem::FEM &f, const Options &o, const mfem::Vec
for (double a : {1., .5, .1, .01, .001, .0001, .00001}) {
const double epsilon = std::min(1., safe.stepSize) * a;
s.trialNormalizedState = frozenState;
s.trialNormalizedState.Add(epsilon, best);
s.trialNormalizedState.Add(epsilon, selectedCorrection);
const auto prep = s.PrepareTrial();
if (!prep) {
trials << epsilon << ",0," << int(prep.error().reason) << ",nan,nan\n";
@@ -217,7 +224,9 @@ template <class Context> int RunContext(Context &context, const Options &o, cons
auto metadata = File(o.output / "metadata.txt");
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 << "\nlinear_tol=" << o.linearTolerance << "\nmax_linear=" << o.maxLinear
<< "\nrestart=" << o.restart << "\nprobe_restart=" << o.probeRestart
<< "\nfrozen_probe_backend=direct_mfem_fgmres_cold"
<< "\nlinear_tol=" << o.linearTolerance << "\nmax_linear=" << o.maxLinear
<< "\nmax_newton=" << o.maxNewton << "\natol=" << o.atol << "\nrtol=" << o.rtol
<< "\ncompiler=" << __VERSION__ << "\ncompiled=" << __DATE__ << ' ' << __TIME__ << '\n';
metadata.flush();
@@ -289,7 +298,8 @@ template <class Context> int RunContext(Context &context, const Options &o, cons
});
auto history = File(o.output / "newton.csv");
history << "iteration,accepted,F,alpha,trials,linear_status,linear_iterations,linear_true,seconds,linear_seconds,"
"prepare_seconds,refresh_seconds,geometry_limited,geometry_safe_step,central_border,unbordered_F\n";
"prepare_seconds,refresh_seconds,geometry_limited,geometry_safe_step,central_border,unbordered_F,"
"linear_initial_residual,linear_rhs_norm,linear_initial_over_rhs\n";
auto blocklog = File(o.output / "blocks.csv");
blocklog << "iteration,stage,block,normalized_norm,physical_norm,max_abs_normalized\n";
auto triallog = File(o.output / "trials.csv");
@@ -327,6 +337,13 @@ template <class Context> int RunContext(Context &context, const Options &o, cons
unbordered(i) = e.normalizedResidual[i];
for (int i : centralRows)
unbordered(i) -= residualFactors(i) * border;
const double linearInitial = e.linearSolve ? e.linearSolve->initialResidualNorm : NAN;
const double linearRhs = e.linearSolve ? e.linearSolve->rightHandSideNorm : NAN;
double linearInitialRelative = NAN;
if (linearRhs > 0.)
linearInitialRelative = linearInitial / linearRhs;
else if (linearRhs == 0. && std::isfinite(linearInitial))
linearInitialRelative = linearInitial == 0. ? 0. : std::numeric_limits<double>::infinity();
history << e.iteration << ',' << e.stepAccepted << ',' << e.residualNorm << ',' << e.acceptedStepLength
<< ',' << e.lineSearchTrials << ',' << (e.linearSolve ? int(e.linearSolve->status) : -1) << ','
<< (e.linearSolve ? e.linearSolve->iterations : 0) << ','
@@ -335,7 +352,8 @@ template <class Context> int RunContext(Context &context, const Options &o, cons
<< ',' << e.preconditionerRefreshSeconds << ','
<< (e.geometryPreflight ? e.geometryPreflight->limitedByGeometry : false) << ','
<< (e.geometryPreflight ? e.geometryPreflight->stepSize : NAN) << ',' << border << ','
<< unbordered.Norml2() << '\n';
<< unbordered.Norml2() << ',' << linearInitial << ',' << linearRhs << ','
<< linearInitialRelative << '\n';
history.flush();
std::cout << " alpha=" << e.acceptedStepLength << " F=" << e.residualNorm
<< " linear=" << (e.linearSolve ? e.linearSolve->relativeTrueResidualNorm : NAN)