From 9c975422c1033092c790430066e09252b0a62e83 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Thu, 17 Sep 2026 08:35:49 -0400 Subject: [PATCH] experiment: inspect surface carrier and isolate closure bound crossing --- experiments/plateau_diagnostic.cpp | 11 +++-- experiments/plateau_diagnostic_README.md | 15 +++++++ ...plateau_supplemental_cases_2026-09-17.json | 42 +++++++++++++++++++ 3 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 experiments/plateau_supplemental_cases_2026-09-17.json diff --git a/experiments/plateau_diagnostic.cpp b/experiments/plateau_diagnostic.cpp index d0d8bcf..bb6bf58 100644 --- a/experiments/plateau_diagnostic.cpp +++ b/experiments/plateau_diagnostic.cpp @@ -28,7 +28,7 @@ struct Options { double n = 1., angularMomentum = 0., atol = 0., rtol = 1e-8, linearTolerance = .03; int maxNewton = 12, maxLinear = 200, restart = 40, probeRestart = 40; bool probes = false, noCentral = false; - bool pressureProbe = false, shapeCurvature = false; + bool pressureProbe = false, shapeCurvature = false, surfaceTrace = false; std::vector captureIterations; std::string mesh = "sandbox.smesh"; std::filesystem::path output, probeState, probeDirection, replayCandidate; @@ -74,6 +74,8 @@ Options Parse(int argc, char **argv) { o.pressureProbe = true; else if (a == "--shape-curvature") o.shapeCurvature = true; + else if (a == "--surface-trace") + o.surfaceTrace = true; else if (a == "--probes") o.probes = true; else if (a == "--no-central") @@ -98,7 +100,7 @@ Options Parse(int argc, char **argv) { 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 ((o.pressureProbe || o.shapeCurvature) && (o.probeState.empty() || o.probeDirection.empty())) + if ((o.pressureProbe || o.shapeCurvature || o.surfaceTrace) && (o.probeState.empty() || o.probeDirection.empty())) throw std::invalid_argument("Focused probes require --probe-state and --probe-direction"); if ((!o.probeDirection.empty() || !o.replayCandidate.empty()) && o.probeState.empty()) throw std::invalid_argument("A supplied direction or candidate requires --probe-state"); @@ -225,6 +227,8 @@ void FocusedProbes(State &s, const fem::FEM &f, const Options &o, const mfem::Ve for (const auto &[key, value] : experiment::plateau_diagnostics::MeasureEnthalpyStep(s, f, o.n, &physicalBase)) bounds << key << ',' << value << '\n'; bounds.flush(); + if (o.surfaceTrace) + experiment::plateau_diagnostics::ProbeSurfaceTrace(s, f, o.n, base, direction, o.output); if (o.pressureProbe) experiment::plateau_diagnostics::ProbePressureObstruction(s, f, o.n, base, direction, o.output); if (o.shapeCurvature) @@ -363,7 +367,8 @@ template int RunContext(Context &context, const Options &o, cons metadata << "probe_direction=" << o.probeDirection << '\n'; if (!o.replayCandidate.empty()) metadata << "replay_candidate=" << o.replayCandidate << '\n'; - metadata << "pressure_probe=" << o.pressureProbe << "\nshape_curvature=" << o.shapeCurvature << '\n'; + metadata << "pressure_probe=" << o.pressureProbe << "\nshape_curvature=" << o.shapeCurvature + << "\nsurface_trace=" << o.surfaceTrace << '\n'; metadata.flush(); solver::detail::StellarEquilibriumContextDiagnostics::WithState(context, [&](auto &s, const fem::FEM &f) { const auto snapshot = LoadVector(o.probeState, s.AcceptedPhysicalState().Size()); diff --git a/experiments/plateau_diagnostic_README.md b/experiments/plateau_diagnostic_README.md index 9b493a1..cbcf446 100644 --- a/experiments/plateau_diagnostic_README.md +++ b/experiments/plateau_diagnostic_README.md @@ -178,3 +178,18 @@ agreement, and final restoration error. The two methodology notes describe the pressure-bound crossings and common-step vector Taylor decomposition. Every candidate remains uncommitted. The flags may be used separately to limit work. These are diagnostic outcomes, not new equilibrium-convergence or virial claims. + +`--surface-trace` exports the actual compiled stellar-surface carrier row indices, +physical base enthalpy and direction coefficients. At each production-rule +limiting point it separates the full enthalpy polynomial into contributions from +surface coefficients and all remaining coefficients. The former is a polynomial +contribution at an interior point, not the value of the physical surface trace +there. This inspection adds no trial preparations, Jacobian evaluations or linear +solves, and restores the saved correction. A surface coefficient sign or zero +trace alone does not certify interior polynomial positivity. + +`plateau_supplemental_cases_2026-09-17.json` replays one onset candidate strictly +between the closure and pressure enthalpy bounds and compares surface traces at +onset and late plateau. The candidate is generated from captured normalized base +and direction at the arithmetic midpoint of those two measured bounds; its +derivation and hash are retained with the supplemental input. diff --git a/experiments/plateau_supplemental_cases_2026-09-17.json b/experiments/plateau_supplemental_cases_2026-09-17.json new file mode 100644 index 0000000..5800f95 --- /dev/null +++ b/experiments/plateau_supplemental_cases_2026-09-17.json @@ -0,0 +1,42 @@ +[ + { + "id": "n1_J0p10_onset_between_bounds", + "arguments": [ + "--index", + "1", + "--angular-momentum", + "0.1", + "--no-central", + "--max-newton", + "0", + "--probe-state", + "/Users/tboudreaux/.codex/.chatgpt-projects/g-p-6a6b3f2194ec8191bebcd59075897f1d/diagnostics/pressure_shape_2026-09-17/captures/n1_J0p10_capture/capture_2/physical_base.txt", + "--probe-direction", + "/Users/tboudreaux/.codex/.chatgpt-projects/g-p-6a6b3f2194ec8191bebcd59075897f1d/diagnostics/pressure_shape_2026-09-17/captures/n1_J0p10_capture/capture_2/normalized_direction.txt", + "--replay-candidate", + "/Users/tboudreaux/.codex/.chatgpt-projects/g-p-6a6b3f2194ec8191bebcd59075897f1d/diagnostics/pressure_shape_2026-09-17/supplemental_inputs/n1_J0p10_onset_between_bounds.txt", + "--surface-trace" + ], + "notes": "Candidate at alpha=0.073032369116392076, strictly between exact sampled closure and pressure h boundaries. No new Newton/Krylov solve. Surface carrier and boundary-coefficient contributions are inspected without preparing extra trials." + }, + { + "id": "n1_J0p10_late_surface_trace", + "arguments": [ + "--index", + "1", + "--angular-momentum", + "0.1", + "--no-central", + "--max-newton", + "0", + "--probe-state", + "/Users/tboudreaux/.codex/.chatgpt-projects/g-p-6a6b3f2194ec8191bebcd59075897f1d/diagnostics/pressure_shape_2026-09-17/captures/n1_J0p10_capture/capture_11/physical_base.txt", + "--probe-direction", + "/Users/tboudreaux/.codex/.chatgpt-projects/g-p-6a6b3f2194ec8191bebcd59075897f1d/diagnostics/pressure_shape_2026-09-17/captures/n1_J0p10_capture/capture_11/normalized_direction.txt", + "--replay-candidate", + "/Users/tboudreaux/.codex/.chatgpt-projects/g-p-6a6b3f2194ec8191bebcd59075897f1d/diagnostics/pressure_shape_2026-09-17/captures/n1_J0p10_capture/capture_11/normalized_candidate.txt", + "--surface-trace" + ], + "notes": "Exact late candidate replay plus surface carrier and limiting-point boundary-coefficient contributions. No new Newton/Krylov solve." + } +]