Reject unsupported pressure traces before polynomial state certification
This commit is contained in:
@@ -21,3 +21,5 @@ Tests cover nodal-positive/interior-negative cubics, a positive polynomial with
|
||||
A standalone extraction of the scalar/tensor certification engine was compiled with the system C++20 compiler and exercised before module integration. It certified a strictly positive quadratic with negative coarse coefficients and a three-dimensional zero-face bubble; it proved a negative pocket of depth 1e-10; it returned unresolved for an exact tangential zero. Production module compilation and MPI execution remain part of the integration campaign.
|
||||
|
||||
Arithmetic requirements are enforced at compilation: `__FAST_MATH__` and a positive `__FINITE_MATH_ONLY__` are rejected, and static assertions require IEC 559 binary64 with subnormal support. Runtime floating-point modes must retain gradual underflow; the application must not enable flush-to-zero after construction. The existing Release build was inspected: `-O3 -g -DNDEBUG -std=c++23 -flto=thin -arch arm64`; no unsafe floating-point flags were present in the generated build commands.
|
||||
|
||||
The stellar strategy explicitly supports only state-independent pressure-to-specific-enthalpy surface relations. That dependency is checked from the compiled relation type. Its constructor collectively rejects nonzero prescribed surface pressure with an unsupported-prescription error before checking seed coefficients, and verifies that the EOS's enthalpy reference satisfies h(P=0)=0. This prevents a valid positive-pressure model from being mislabeled an invalid numerical base. The focused regression validates zero pressure and collective rejection when only one MPI rank supplies positive pressure.
|
||||
|
||||
@@ -18,6 +18,44 @@ export import :solver.enthalpy_admissibility;
|
||||
export import :normalization.stellar_equilibrium;
|
||||
export import :utils.domain;
|
||||
|
||||
export namespace mean_field::solver::detail {
|
||||
template <class SurfaceConstraint>
|
||||
concept PolynomialEnthalpySurfaceConstraint = requires {
|
||||
typename SurfaceConstraint::CarrierQuantity;
|
||||
typename SurfaceConstraint::Relation;
|
||||
requires std::same_as<typename SurfaceConstraint::CarrierQuantity,dimensions::quantity::SpecificEnthalpy>;
|
||||
requires std::same_as<typename SurfaceConstraint::Relation,eos::SpecificEnthalpyFromPressure>;
|
||||
};
|
||||
|
||||
// The polynomial engine fixes homogeneous trace rows. Check the physical
|
||||
// prescription before inspecting coefficients, so an unsupported surface
|
||||
// condition is never reported as an invalid numerical starting state.
|
||||
template <PolynomialEnthalpySurfaceConstraint SurfaceConstraint, class EquationOfState>
|
||||
void RequirePolynomialEnthalpySurface(const SurfaceConstraint &surface,
|
||||
const EquationOfState &equationOfState, MPI_Comm communicator) {
|
||||
int localUnsupported = surface.targetPressure().value() == 0.0 ? 0 : 1;
|
||||
int globalUnsupported = 0;
|
||||
if (MPI_Allreduce(&localUnsupported,&globalUnsupported,1,MPI_INT,MPI_MAX,communicator) != MPI_SUCCESS)
|
||||
throw std::runtime_error("Could not validate the polynomial enthalpy surface prescription.");
|
||||
if (globalUnsupported)
|
||||
throw std::invalid_argument("PolynomialEnthalpy supports only zero prescribed surface pressure (Psurf=0).");
|
||||
bool homogeneous = false;
|
||||
try {
|
||||
const double trace = eos::evaluate<dimensions::quantity::SpecificEnthalpy>(
|
||||
equationOfState,surface.targetPressure()).value();
|
||||
homogeneous = std::isfinite(trace) && trace == 0.0;
|
||||
} catch (...) {
|
||||
// A rank-local EOS exception must not strand other ranks in the
|
||||
// collective compatibility check that follows.
|
||||
}
|
||||
localUnsupported = homogeneous ? 0 : 1;
|
||||
if (MPI_Allreduce(&localUnsupported,&globalUnsupported,1,MPI_INT,MPI_MAX,communicator) != MPI_SUCCESS)
|
||||
throw std::runtime_error("Could not validate the zero-pressure enthalpy reference.");
|
||||
if (globalUnsupported)
|
||||
throw std::invalid_argument("PolynomialEnthalpy requires the EOS to prescribe h(Psurf=0)=0.");
|
||||
}
|
||||
}
|
||||
|
||||
export namespace mean_field::solver {
|
||||
/*
|
||||
* The solver owns this workspace only when a positivity policy is selected.
|
||||
@@ -28,6 +66,8 @@ export namespace mean_field::solver {
|
||||
class PreparedStellarEnthalpyStrategy final {
|
||||
using Problem = std::remove_cvref_t<decltype(std::declval<State &>().Problem())>;
|
||||
using Form = typename Problem::FormType;
|
||||
static_assert(detail::PolynomialEnthalpySurfaceConstraint<typename Problem::CompiledSurfaceConstraintType>,
|
||||
"PolynomialEnthalpy requires a state-independent specific-enthalpy pressure surface relation.");
|
||||
using Schema = utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
using Domain = field::FieldDomainT<field::Enthalpy>;
|
||||
static constexpr int enthalpyIndex = utils::blocks::type_index_v<
|
||||
@@ -39,6 +79,8 @@ export namespace mean_field::solver {
|
||||
: m_state(&state),
|
||||
m_offset(state.Problem().GetManifest().layout().offset(enthalpyBlock)),
|
||||
m_size(state.Problem().GetManifest().layout().size(enthalpyBlock)) {
|
||||
detail::RequirePolynomialEnthalpySurface(state.Problem().GetCompiledSurfaceConstraint(),
|
||||
state.Problem().GetStellarModel().equationOfState(),state.Problem().GetCommunicator());
|
||||
const auto &fem = equilibrium::detail::StellarEquilibriumProblemFactory::FiniteElementModel(state.Problem());
|
||||
if (!fem.mesh || !fem.enthalpyFes)
|
||||
throw std::invalid_argument("Stellar enthalpy certification requires a complete enthalpy FE space.");
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <span>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/matchers/catch_matchers.hpp>
|
||||
#include <mfem.hpp>
|
||||
#include <mpi.h>
|
||||
|
||||
@@ -199,3 +201,18 @@ TEST_CASE("Feasible direction repair enforces exact homogeneous trace and reject
|
||||
CHECK(prepared.MinimumRepairFraction(invalidTrace,anchor,direction).status ==
|
||||
EnthalpyDirectionRepairStatus::invalid_base);
|
||||
}
|
||||
|
||||
TEST_CASE("Polynomial enthalpy validates the physical surface prescription before inspecting a state",
|
||||
"[solver][enthalpy_admissibility][unit][parallel]") {
|
||||
using namespace mean_field;
|
||||
const eos::Polytrope equationOfState({.n=1.0,.K=1.0});
|
||||
const auto zero = surface::compilePressureSurfaceConstraint<surface::BarotropicSurfaceFormulation>(
|
||||
surface::Isobaric({.Psurf=dimensions::PressureValue{0.0}}),equationOfState);
|
||||
STATIC_CHECK(solver::detail::PolynomialEnthalpySurfaceConstraint<std::remove_cvref_t<decltype(zero)>>);
|
||||
REQUIRE_NOTHROW(solver::detail::RequirePolynomialEnthalpySurface(zero,equationOfState,MPI_COMM_WORLD));
|
||||
int rank = 0; MPI_Comm_rank(MPI_COMM_WORLD,&rank);
|
||||
const auto positive = surface::compilePressureSurfaceConstraint<surface::BarotropicSurfaceFormulation>(
|
||||
surface::Isobaric({.Psurf=dimensions::PressureValue{rank == 0 ? 0.01 : 0.0}}),equationOfState);
|
||||
CHECK_THROWS_WITH(solver::detail::RequirePolynomialEnthalpySurface(positive,equationOfState,MPI_COMM_WORLD),
|
||||
"PolynomialEnthalpy supports only zero prescribed surface pressure (Psurf=0).");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user