Guard coarse exactness diagnostics and report complete setup work
This commit is contained in:
@@ -253,8 +253,8 @@ void BenchmarkPreconditioners(State &state, const std::filesystem::path &output,
|
||||
reference << "context_inverse_application_seconds," << detail::Seconds(start)
|
||||
<< "\ncontext_inverse_correction_norm," << referenceCorrection.Norml2() << '\n';
|
||||
reference.flush();
|
||||
if (!detail::Finite(referenceCorrection))
|
||||
throw std::runtime_error("The context reference inverse action is nonfinite.");
|
||||
if (!detail::Finite(referenceCorrection) || !std::isfinite(referenceCorrection.Norml2()))
|
||||
throw std::runtime_error("The context reference inverse action or its norm is nonfinite.");
|
||||
detail::Save(output / "context_reference_normalized_correction.txt", referenceCorrection);
|
||||
verify("context_reference");
|
||||
|
||||
@@ -336,6 +336,8 @@ void BenchmarkPreconditioners(State &state, const std::filesystem::path &output,
|
||||
metric("setup_jacobian_applications", stats.setupOperatorApplications);
|
||||
metric("setup_inverse_applications", stats.setupInverseApplications);
|
||||
metric("setup_seconds", stats.lastSetupSeconds);
|
||||
metric("setup_jacobian_seconds", stats.lastSetupOperatorSeconds);
|
||||
metric("setup_inverse_seconds", stats.lastSetupInverseSeconds);
|
||||
metric("persistent_vector_bytes", stats.persistentVectorBytes);
|
||||
metric("applications", stats.applications);
|
||||
metric("inverse_applications", stats.inverseApplications);
|
||||
@@ -346,9 +348,19 @@ void BenchmarkPreconditioners(State &state, const std::filesystem::path &output,
|
||||
sectionStart = detail::Clock::now();
|
||||
const auto modes = MakeCoupledShapeModes(state.Problem(), physical);
|
||||
const double basisSeconds = detail::Seconds(sectionStart);
|
||||
recordWork("after_basis");
|
||||
// Direct basis calls bypass the structure-factorization counters.
|
||||
// The material inverse also counts its usual internal couplings.
|
||||
for (const char *metric : {"basis_explicit_surface_to_material_applications",
|
||||
"basis_material_inverse_applications",
|
||||
"basis_material_to_gravity_applications",
|
||||
"basis_gravity_inverse_applications"})
|
||||
coarseReport << role << ',' << variant << ",constructed," << metric << ',' << modes.size() << '\n';
|
||||
const auto coarseStart = detail::Clock::now();
|
||||
coarse = std::make_unique<Coarse>(*state.normalizedOperator, normalized, modes,
|
||||
state.Problem().GetCommunicator());
|
||||
const double completeCoarseSeconds = detail::Seconds(sectionStart);
|
||||
const double completeCoarseSeconds = basisSeconds + detail::Seconds(coarseStart);
|
||||
recordWork("after_coarse_setup");
|
||||
coarseReport << role << ',' << variant << ",constructed,physical_inverse_construction_seconds,"
|
||||
<< constructionSeconds << '\n';
|
||||
constructionSeconds += completeCoarseSeconds;
|
||||
@@ -366,10 +378,20 @@ void BenchmarkPreconditioners(State &state, const std::filesystem::path &output,
|
||||
state.normalizedOperator->Mult(value, image);
|
||||
coarse->Mult(image, recovered);
|
||||
recovered -= value;
|
||||
maximumRelativeError = std::max(maximumRelativeError,
|
||||
detail::Relative(recovered.Norml2(), value.Norml2()));
|
||||
const double valueNorm = value.Norml2(), imageNorm = image.Norml2(), errorNorm = recovered.Norml2();
|
||||
if (!detail::Finite(value) || !detail::Finite(image) || !detail::Finite(recovered) ||
|
||||
!std::isfinite(valueNorm) || valueNorm <= 0.0 || !std::isfinite(imageNorm) ||
|
||||
!std::isfinite(errorNorm))
|
||||
throw std::runtime_error("The actual FEM coarse identity produced a nonfinite or null reference.");
|
||||
const double relativeError = errorNorm / valueNorm;
|
||||
// std::max can discard NaN, so validate each observation first.
|
||||
if (!std::isfinite(relativeError))
|
||||
throw std::runtime_error("The actual FEM coarse relative identity error is nonfinite.");
|
||||
maximumRelativeError = std::max(maximumRelativeError, relativeError);
|
||||
}
|
||||
coarseReport << role << ',' << variant << ",validation,maximum_exact_coarse_relative_error,"
|
||||
coarseReport << role << ',' << variant << ",validation,exact_coarse_modes_checked,"
|
||||
<< coarse->GetSourceBasis().size() << '\n'
|
||||
<< role << ',' << variant << ",validation,maximum_exact_coarse_relative_error,"
|
||||
<< maximumRelativeError << '\n';
|
||||
coarseReport.flush();
|
||||
if (!std::isfinite(maximumRelativeError) || maximumRelativeError > 1.e-8)
|
||||
@@ -380,11 +402,15 @@ void BenchmarkPreconditioners(State &state, const std::filesystem::path &output,
|
||||
sectionStart = detail::Clock::now();
|
||||
applicationInverse->Mult(rhs, inverseAction);
|
||||
const double applicationSeconds = detail::Seconds(sectionStart);
|
||||
if (!detail::Finite(inverseAction) || !std::isfinite(inverseAction.Norml2()))
|
||||
throw std::runtime_error("The diagnostic inverse action or its norm is nonfinite.");
|
||||
detail::Save(output / (role + "_inverse_rhs_action.txt"), inverseAction);
|
||||
sectionStart = detail::Clock::now();
|
||||
state.normalizedOperator->Mult(inverseAction, inverseJacobianAction);
|
||||
const double actionSeconds = detail::Seconds(sectionStart);
|
||||
const double actionNorm = inverseJacobianAction.Norml2();
|
||||
if (!detail::Finite(inverseJacobianAction) || !std::isfinite(actionNorm))
|
||||
throw std::runtime_error("The diagnostic inverse Jacobian image or its norm is nonfinite.");
|
||||
inverseJacobianAction -= rhs;
|
||||
mfem::Vector referenceDifference(inverseAction);
|
||||
referenceDifference -= referenceCorrection;
|
||||
@@ -518,12 +544,14 @@ void BenchmarkPreconditioners(State &state, const std::filesystem::path &output,
|
||||
<< "The fresh independent inverse must match the existing context inverse on that RHS.\n"
|
||||
<< "construction_seconds includes physical inverse and any selected coarse/basis construction.\n"
|
||||
<< "Coarse exact-subspace validation is diagnostic work outside setup/solve totals.\n"
|
||||
<< "An inactive coarse fallback has exact_coarse_modes_checked=0; its zero maximum error is vacuous.\n"
|
||||
<< "Stellar-level inverse/cross counters exclude direct basis calls, recorded separately; gravity leaf counters include them.\n"
|
||||
<< "unchanged_state_refresh_seconds measures an immediate Refresh; it is not a changed-state refresh benchmark.\n"
|
||||
<< "Each candidate receives one diagnostic inverse/Jv application before the timed cold Krylov solve.\n"
|
||||
<< "Application timers/counts in the summary include only Krylov work; the independent true check is separate.\n"
|
||||
<< "setup_plus_solve_seconds includes construction, normalization binding, Krylov setup, and Krylov solve.\n"
|
||||
<< "setup_solve_and_true_check_seconds adds the separately measured original-J true-residual gate.\n"
|
||||
<< "measured_candidate_seconds also includes diagnostic refresh and the inverse/Jv comparison.\n"
|
||||
<< "measured_candidate_seconds also includes diagnostic refresh, coarse exactness checks, and inverse/Jv comparison.\n"
|
||||
<< "Common FEM/context construction and frozen-state preparation occur before this helper.\n"
|
||||
<< "The helper performs no trial preparation or nonlinear update; WithState restores its accepted seed afterward.\n"
|
||||
<< "Block norms are coefficient norms in the stated normalized/physical coordinates, not spatial error norms.\n"
|
||||
|
||||
Reference in New Issue
Block a user