feat(newton): first newton solver implementation

This commit is contained in:
2026-09-08 06:36:39 -04:00
parent 76818f2f82
commit b3c04d507a
98 changed files with 20397 additions and 11040 deletions

View File

@@ -1,8 +1,10 @@
module;
#include <compare>
#include <cstdint>
#include <expected>
#include <memory>
#include <mfem.hpp>
#include <stdexcept>
export module mean_field:operators.context.gravity_field;
export import :fem;
@@ -58,6 +60,23 @@ export namespace mean_field::operators::context::gravity_field {
}
};
enum class GravityFieldPreparationRejectionReason : std::uint8_t { invalid_mapping, non_finite_arithmetic };
struct GravityFieldPreparationRejection final {
GravityFieldPreparationRejectionReason reason{GravityFieldPreparationRejectionReason::invalid_mapping};
mapping::MappingStatus mappingStatus{mapping::MappingStatus::valid};
};
template <typename Report>
using GravityFieldPreparationResult = std::expected<Report, GravityFieldPreparationRejection>;
[[noreturn]] inline void throwGravityFieldPreparationRejection(const GravityFieldPreparationRejection &rejection) {
if (rejection.reason == GravityFieldPreparationRejectionReason::non_finite_arithmetic) {
throw std::domain_error("Prepared gravity-field data contained non-finite arithmetic.");
}
throw std::domain_error("Prepared gravity-field data could not map the candidate geometry.");
}
class GravityFieldGeometryContext {
public:
GravityFieldGeometryContext(
@@ -76,12 +95,24 @@ export namespace mean_field::operators::context::gravity_field {
DisplacementRevision displacement_revision
);
[[nodiscard]] GravityFieldPreparationResult<GravityFieldGeometryPreparation> TryPrepare(
const mfem::Vector &displacement,
DiscretizationRevision discretization_revision,
DisplacementRevision displacement_revision
);
GravityFieldGeometryPreparation PreparePrimal(
const mfem::Vector &displacement,
DiscretizationRevision discretization_revision,
DisplacementRevision displacement_revision
);
[[nodiscard]] GravityFieldPreparationResult<GravityFieldGeometryPreparation> TryPreparePrimal(
const mfem::Vector &displacement,
DiscretizationRevision discretization_revision,
DisplacementRevision displacement_revision
);
[[nodiscard]] const PreparedMappedHDivMassOperator &GetMassOperator() const;
[[nodiscard]] const PreparedMappedGravitySourceOperator &GetSourceOperator() const;
[[nodiscard]] const mfem::Operator &GetDivergenceOperator() const;
@@ -95,7 +126,7 @@ export namespace mean_field::operators::context::gravity_field {
private:
enum class PreparationMode : std::uint8_t { primal, linearization };
GravityFieldGeometryPreparation PrepareImpl(
[[nodiscard]] GravityFieldPreparationResult<GravityFieldGeometryPreparation> TryPrepareImpl(
const mfem::Vector &displacement,
DiscretizationRevision discretization_revision,
DisplacementRevision displacement_revision,
@@ -147,6 +178,11 @@ export namespace mean_field::operators::context::gravity_field {
const GravityFieldRevisions &revisions
);
[[nodiscard]] GravityFieldPreparationResult<GravityFieldPreparationReport> TryPrepare(
const GravityFieldStateView &state,
const GravityFieldRevisions &revisions
);
[[nodiscard]] const GravityFieldGeometryContext &GetGeometryContext() const;
[[nodiscard]] const mfem::Vector &GetDensityTrue() const;
[[nodiscard]] const mfem::Vector &GetGravityGradientTrue() const;

View File

@@ -24,6 +24,13 @@ export namespace mean_field::operators {
const context::gravity_field::GravityFieldRevisions &revisions
);
[[nodiscard]] context::gravity_field::GravityFieldPreparationResult<
context::gravity_field::GravityFieldPreparationReport>
TryPrepare(
const mfem::Vector &state,
const context::gravity_field::GravityFieldRevisions &revisions
);
void Mult(
const mfem::Vector &state,
mfem::Vector &residual

View File

@@ -1,5 +1,8 @@
module;
#include <cstdint>
#include <expected>
#include <mfem.hpp>
export module mean_field:operators.kernels.gravity_displacement_force;
@@ -8,6 +11,24 @@ export import :fem;
export import :mapping.domain_mapper;
export namespace mean_field::operators::kernels {
enum class GravityDisplacementForceRejectionReason : std::uint8_t { invalid_mapping, non_finite_arithmetic };
struct GravityDisplacementForceRejection final {
GravityDisplacementForceRejectionReason reason{GravityDisplacementForceRejectionReason::invalid_mapping};
mapping::MappingStatus mappingStatus{mapping::MappingStatus::valid};
};
using GravityDisplacementForceResult = std::expected<void, GravityDisplacementForceRejection>;
[[nodiscard]] GravityDisplacementForceResult try_apply_gravity_displacement_force_residual(
const fem::FEM &f,
const mapping::DomainMapper &domainMapper,
const mfem::Vector &densityTrue,
const mfem::Vector &gravityGradientTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &residualTrue
);
void apply_gravity_displacement_force_residual(
const fem::FEM &f,
const mapping::DomainMapper &domainMapper,

View File

@@ -1,5 +1,8 @@
module;
#include <cstdint>
#include <expected>
#include <mfem.hpp>
export module mean_field:operators.kernels.rotational_displacement_force;
@@ -9,6 +12,24 @@ export import :mapping.domain_mapper;
export import :physics.rigid_rotation;
export namespace mean_field::operators::kernels {
enum class RotationalDisplacementForceRejectionReason : std::uint8_t { invalid_mapping, non_finite_arithmetic };
struct RotationalDisplacementForceRejection final {
RotationalDisplacementForceRejectionReason reason{RotationalDisplacementForceRejectionReason::invalid_mapping};
mapping::MappingStatus mappingStatus{mapping::MappingStatus::valid};
};
using RotationalDisplacementForceResult = std::expected<void, RotationalDisplacementForceRejection>;
[[nodiscard]] RotationalDisplacementForceResult try_apply_rotational_displacement_force_residual(
const fem::FEM &f,
const mapping::DomainMapper &domainMapper,
const physics::RigidRotation &rotation,
const mfem::Vector &densityTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &residualTrue
);
/*
* Rotational contribution to the displacement row:
*

View File

@@ -2,6 +2,10 @@ module;
#include <compare>
#include <cstdint>
#include <expected>
#include <limits>
#include <optional>
#include <stdexcept>
#include <vector>
#include <mfem.hpp>
@@ -45,6 +49,52 @@ export namespace mean_field::operators {
constexpr auto operator<=>(const PreparedAngularMomentumReport &) const = default;
};
enum class AngularMomentumPreparationRejectionReason : std::uint8_t {
inverted_geometry,
non_finite_geometry,
non_finite_angular_velocity,
non_finite_density,
negative_moment_of_inertia,
non_finite_moment_of_inertia,
non_finite_residual
};
/*
* A trial state can fail to define the angular-momentum invariant without
* violating the operator's structural contract. Keep that distinction in
* a fixed-size value so a line search can reject the candidate without
* constructing or transporting an exception.
*/
struct AngularMomentumPreparationRejection final {
AngularMomentumPreparationRejectionReason reason{AngularMomentumPreparationRejectionReason::inverted_geometry};
mapping::MappingStatus mappingStatus{mapping::MappingStatus::valid};
double momentOfInertia{std::numeric_limits<double>::quiet_NaN()};
};
using AngularMomentumPreparationResult =
std::expected<PreparedAngularMomentumReport, AngularMomentumPreparationRejection>;
[[noreturn]] inline void
throwAngularMomentumPreparationRejection(const AngularMomentumPreparationRejection &rejection) {
switch (rejection.reason) {
case AngularMomentumPreparationRejectionReason::inverted_geometry:
throw std::domain_error("The angular-momentum trial inverts mapped geometry.");
case AngularMomentumPreparationRejectionReason::non_finite_geometry:
throw std::domain_error("The angular-momentum trial produced non-finite mapped geometry.");
case AngularMomentumPreparationRejectionReason::non_finite_angular_velocity:
throw std::domain_error("The angular-momentum trial has a non-finite angular velocity.");
case AngularMomentumPreparationRejectionReason::non_finite_density:
throw std::domain_error("The angular-momentum trial produced a non-finite interpolated density.");
case AngularMomentumPreparationRejectionReason::negative_moment_of_inertia:
throw std::domain_error("The angular-momentum trial produced a negative moment of inertia.");
case AngularMomentumPreparationRejectionReason::non_finite_moment_of_inertia:
throw std::domain_error("The angular-momentum trial produced a non-finite moment of inertia.");
case AngularMomentumPreparationRejectionReason::non_finite_residual:
throw std::domain_error("The angular-momentum trial produced a non-finite residual.");
}
throw std::logic_error("An unknown angular-momentum trial rejection was reported.");
}
struct AngularMomentumConstraintReport final {
double targetAngularMomentum;
double achievedAngularMomentum;
@@ -97,6 +147,11 @@ export namespace mean_field::operators {
const AngularMomentumDependencies &dependencies
);
[[nodiscard]] AngularMomentumPreparationResult TryPrepare(
double angularVelocity,
const AngularMomentumDependencies &dependencies
);
void BuildResidual(mfem::Vector &residual) const;
void ApplyDensityJacobianAction(
@@ -156,9 +211,9 @@ export namespace mean_field::operators {
};
void BuildStaticPlan();
void RefreshGeometry(const mfem::Vector &displacement);
void RefreshDensity(const mfem::Vector &density);
void AssembleResidual();
[[nodiscard]] std::optional<mapping::MappingStatus> RefreshGeometry(const mfem::Vector &displacement);
[[nodiscard]] bool RefreshDensity(const mfem::Vector &density);
[[nodiscard]] std::optional<AngularMomentumPreparationRejection> TryAssembleResidual();
void VerifyPrepared() const;
[[nodiscard]] double EvaluateDensityMomentActionLocal(const mfem::Vector &densityVariation) const;

View File

@@ -1,6 +1,7 @@
module;
#include <cstdint>
#include <expected>
#include <mfem.hpp>
#include <vector>
@@ -22,6 +23,29 @@ export namespace mean_field::operators {
}
};
enum class BarotropicClosurePreparationRejectionReason : std::uint8_t {
mapping_failure,
invalid_quadrature_data,
equation_of_state
};
/*
* A rejected candidate is part of the nonlinear-solver control flow, not
* an exceptional API failure. Keep the payload fixed-size so it can be
* selected deterministically across ranks without allocating. Only the
* detail associated with `reason` is meaningful.
*/
struct BarotropicClosurePreparationRejection final {
BarotropicClosurePreparationRejectionReason reason{
BarotropicClosurePreparationRejectionReason::mapping_failure
};
mapping::MappingStatus mappingStatus{mapping::MappingStatus::non_finite_result};
eos::EvaluationErrorCode equationOfStateError{eos::EvaluationErrorCode::nonfinite_result};
};
using BarotropicClosurePreparationResult =
std::expected<PreparedBarotropicClosureReport, BarotropicClosurePreparationRejection>;
class PreparedBarotropicClosureOperator final : public mfem::Operator {
public:
PreparedBarotropicClosureOperator(
@@ -40,6 +64,11 @@ export namespace mean_field::operators {
const context::barotropic::BarotropicClosureDependencies &dependencies
);
[[nodiscard]] BarotropicClosurePreparationResult TryPrepare(
const context::barotropic::BarotropicClosureStateView &state,
const context::barotropic::BarotropicClosureDependencies &dependencies
);
void Mult(
const mfem::Vector &densityVariation,
const mfem::Vector &enthalpyVariation,

View File

@@ -2,6 +2,8 @@ module;
#include <compare>
#include <cstdint>
#include <expected>
#include <optional>
#include <mfem.hpp>
@@ -18,6 +20,28 @@ export import :physics.rigid_rotation;
export import :utils.blocks;
export namespace mean_field::operators {
enum class DisplacementResidualPreparationRejectionSource : std::uint8_t {
pressure,
gravity,
rotation,
composition
};
enum class DisplacementResidualPreparationRejectionReason : std::uint8_t {
equation_of_state,
invalid_mapping,
non_finite_arithmetic
};
struct DisplacementResidualPreparationRejection final {
DisplacementResidualPreparationRejectionSource source{DisplacementResidualPreparationRejectionSource::pressure};
DisplacementResidualPreparationRejectionReason reason{
DisplacementResidualPreparationRejectionReason::equation_of_state
};
eos::EvaluationErrorCode equationOfStateCode{eos::EvaluationErrorCode::nonfinite_result};
mapping::MappingStatus mappingStatus{mapping::MappingStatus::valid};
};
struct DisplacementResidualDependencyStamp final {
std::uint64_t identity{0};
std::uint64_t revision{0};
@@ -104,6 +128,15 @@ export namespace mean_field::operators {
const physics::RigidRotation &rotation
);
[[nodiscard]] std::expected<
PreparedDisplacementResidualReport,
DisplacementResidualPreparationRejection>
TryPrepare(
const DisplacementResidualStateView &state,
const DisplacementResidualDependencies &dependencies,
const physics::RigidRotation &rotation
);
void BuildResidual(mfem::Vector &residual) const;
void ApplyDensityJacobianAction(
@@ -154,7 +187,7 @@ export namespace mean_field::operators {
GetGravityContext() const noexcept;
private:
void AssembleResidual();
[[nodiscard]] std::optional<DisplacementResidualPreparationRejection> AssembleResidual();
void VerifyPrepared() const;
const fem::FEM &m_fem;

View File

@@ -2,6 +2,7 @@ module;
#include <compare>
#include <cstdint>
#include <expected>
#include <vector>
#include <mfem.hpp>
@@ -11,6 +12,7 @@ export module mean_field:operators.prepared_gravity_displacement_force;
export import :fem;
export import :mapping.domain_mapper;
export import :operators.context.gravity_field;
export import :operators.kernels.gravity_displacement_force;
export import :utils.blocks;
export namespace mean_field::operators {
@@ -57,6 +59,11 @@ export namespace mean_field::operators {
*/
PreparedGravityDisplacementForceReport Prepare();
[[nodiscard]] std::expected<
PreparedGravityDisplacementForceReport,
kernels::GravityDisplacementForceRejection>
TryPrepare();
void BuildResidual(mfem::Vector &residual) const;
void ApplyDensityJacobianAction(
@@ -106,7 +113,10 @@ export namespace mean_field::operators {
private:
void VerifyPrepared() const;
void PrepareElementData();
[[nodiscard]] std::expected<
void,
kernels::GravityDisplacementForceRejection>
TryPrepareElementData();
void ApplyPreparedCompleteJacobianActionTrue(
const mfem::Vector &densityVariationTrue,
const mfem::Vector &displacementVariationTrue,

View File

@@ -1,7 +1,9 @@
module;
#include <cstdint>
#include <expected>
#include <memory>
#include <mfem.hpp>
#include <stdexcept>
#include <vector>
export module mean_field:operators.prepared_gravity_source;
@@ -10,6 +12,23 @@ export import :field.mfem;
export import :mapping.domain_mapper;
export namespace mean_field::operators {
enum class GravitySourcePreparationRejectionReason : std::uint8_t { invalid_mapping, non_finite_arithmetic };
struct GravitySourcePreparationRejection final {
GravitySourcePreparationRejectionReason reason{GravitySourcePreparationRejectionReason::invalid_mapping};
mapping::MappingStatus mappingStatus{mapping::MappingStatus::valid};
};
using GravitySourcePreparationResult = std::expected<void, GravitySourcePreparationRejection>;
[[noreturn]] inline void
throwGravitySourcePreparationRejection(const GravitySourcePreparationRejection &rejection) {
if (rejection.reason == GravitySourcePreparationRejectionReason::non_finite_arithmetic) {
throw std::domain_error("Prepared gravity-source data contained non-finite arithmetic.");
}
throw std::domain_error("Prepared gravity-source data could not map the candidate geometry.");
}
class PreparedMappedGravitySourceOperator final : public mfem::Operator {
public:
PreparedMappedGravitySourceOperator(
@@ -19,6 +38,8 @@ export namespace mean_field::operators {
void Prepare(const mfem::Vector &displacement);
void PreparePrimal(const mfem::Vector &displacement);
[[nodiscard]] GravitySourcePreparationResult TryPrepare(const mfem::Vector &displacement);
[[nodiscard]] GravitySourcePreparationResult TryPreparePrimal(const mfem::Vector &displacement);
void Mult(
const mfem::Vector &density,
mfem::Vector &action
@@ -68,7 +89,7 @@ export namespace mean_field::operators {
mfem::Vector quadrature_data;
};
void PrepareImpl(
[[nodiscard]] GravitySourcePreparationResult TryPrepareImpl(
const mfem::Vector &displacement,
PreparationMode mode
);

View File

@@ -1,7 +1,9 @@
module;
#include <cstdint>
#include <expected>
#include <memory>
#include <mfem.hpp>
#include <stdexcept>
#include <vector>
export module mean_field:operators.prepared_hdiv_mass;
@@ -10,6 +12,22 @@ export import :field.mfem;
export import :mapping.domain_mapper;
export namespace mean_field::operators {
enum class HDivMassPreparationRejectionReason : std::uint8_t { invalid_mapping, non_finite_arithmetic };
struct HDivMassPreparationRejection final {
HDivMassPreparationRejectionReason reason{HDivMassPreparationRejectionReason::invalid_mapping};
mapping::MappingStatus mappingStatus{mapping::MappingStatus::valid};
};
using HDivMassPreparationResult = std::expected<void, HDivMassPreparationRejection>;
[[noreturn]] inline void throwHDivMassPreparationRejection(const HDivMassPreparationRejection &rejection) {
if (rejection.reason == HDivMassPreparationRejectionReason::non_finite_arithmetic) {
throw std::domain_error("Prepared H(div) mass data contained non-finite arithmetic.");
}
throw std::domain_error("Prepared H(div) mass data could not map the candidate geometry.");
}
class PreparedMappedHDivMassOperator final : public mfem::Operator {
public:
PreparedMappedHDivMassOperator(
@@ -19,6 +37,8 @@ export namespace mean_field::operators {
void Prepare(const mfem::Vector &displacement);
void PreparePrimal(const mfem::Vector &displacement);
[[nodiscard]] HDivMassPreparationResult TryPrepare(const mfem::Vector &displacement);
[[nodiscard]] HDivMassPreparationResult TryPreparePrimal(const mfem::Vector &displacement);
void Mult(
const mfem::Vector &gravity_gradient,
mfem::Vector &action
@@ -54,8 +74,8 @@ export namespace mean_field::operators {
mfem::DenseMatrix frozenMappingData;
};
void PrepareVariationData();
void PrepareImpl(
[[nodiscard]] mapping::MappingStatus PrepareVariationData();
[[nodiscard]] HDivMassPreparationResult TryPrepareImpl(
const mfem::Vector &displacement,
PreparationMode mode
);

View File

@@ -3,7 +3,9 @@ module;
#include <compare>
#include <cstddef>
#include <cstdint>
#include <expected>
#include <optional>
#include <stdexcept>
#include <vector>
#include <mfem.hpp>
@@ -30,6 +32,35 @@ export namespace mean_field::operators {
}
};
enum class HydrostaticEquilibriumPreparationRejectionReason : std::uint8_t {
inverted_geometry,
non_finite_geometry,
non_finite_residual
};
struct HydrostaticEquilibriumPreparationRejection final {
HydrostaticEquilibriumPreparationRejectionReason reason{
HydrostaticEquilibriumPreparationRejectionReason::inverted_geometry
};
mapping::MappingStatus mappingStatus{mapping::MappingStatus::valid};
};
using HydrostaticEquilibriumPreparationResult =
std::expected<PreparedHydrostaticEquilibriumReport, HydrostaticEquilibriumPreparationRejection>;
[[noreturn]] inline void
throwHydrostaticEquilibriumPreparationRejection(const HydrostaticEquilibriumPreparationRejection &rejection) {
switch (rejection.reason) {
case HydrostaticEquilibriumPreparationRejectionReason::non_finite_geometry:
throw std::domain_error("Prepared hydrostatic equilibrium encountered non-finite mapped geometry.");
case HydrostaticEquilibriumPreparationRejectionReason::non_finite_residual:
throw std::domain_error("Prepared hydrostatic equilibrium produced a non-finite residual.");
case HydrostaticEquilibriumPreparationRejectionReason::inverted_geometry:
default:
throw std::domain_error("Prepared hydrostatic equilibrium encountered inverted mapped geometry.");
}
}
struct PreparedHydrostaticAlgebraicJacobianStatistics {
std::uint64_t preparations{0};
std::uint64_t enthalpyApplications{0};
@@ -102,6 +133,12 @@ export namespace mean_field::operators {
const physics::RigidRotation &rotation
);
[[nodiscard]] HydrostaticEquilibriumPreparationResult TryPrepare(
const context::hydrostatic::HydrostaticEquilibriumStateView &state,
const context::hydrostatic::HydrostaticEquilibriumDependencies &dependencies,
const physics::RigidRotation &rotation
);
void BuildResidual(mfem::Vector &residual) const;
void ApplyEnthalpyJacobianAction(
@@ -221,12 +258,12 @@ export namespace mean_field::operators {
};
void PrepareStaticPlan();
void PrepareGeometry();
void PrepareAlgebraicJacobianBlocks();
void PrepareRotation();
void PrepareBaseState();
[[nodiscard]] std::optional<mapping::MappingStatus> PrepareGeometry();
[[nodiscard]] bool PrepareAlgebraicJacobianBlocks();
[[nodiscard]] bool PrepareRotation();
[[nodiscard]] bool PrepareBaseState();
void FinalizeDisplacementJacobianPreparation();
void AssembleCachedResidual();
[[nodiscard]] bool AssembleCachedResidual();
void VerifyPrepared() const;
const fem::FEM &m_fem;

View File

@@ -2,7 +2,9 @@ module;
#include <compare>
#include <cstdint>
#include <expected>
#include <mfem.hpp>
#include <optional>
#include <vector>
export module mean_field:operators.prepared_mass_normalization;
@@ -59,6 +61,28 @@ export namespace mean_field::operators {
constexpr auto operator<=>(const PreparedMassNormalizationReport &) const = default;
};
enum class MassNormalizationPreparationRejectionReason : std::uint8_t {
mapping_failure,
non_finite_density_interpolation,
non_finite_assembled_mass
};
/*
* Candidate rejection is deliberately represented without text or owned
* storage. That makes the result cheap to propagate through a line search
* and gives the MPI implementation a deterministic, allocation-free value
* to select on every rank.
*/
struct MassNormalizationPreparationRejection final {
MassNormalizationPreparationRejectionReason reason{
MassNormalizationPreparationRejectionReason::mapping_failure
};
mapping::MappingStatus mappingStatus{mapping::MappingStatus::non_finite_result};
};
using MassNormalizationPreparationResult =
std::expected<PreparedMassNormalizationReport, MassNormalizationPreparationRejection>;
struct PreparedMassNormalizationActionStatistics final {
std::uint64_t densityApplications{0};
std::uint64_t displacementApplications{0};
@@ -109,6 +133,16 @@ export namespace mean_field::operators {
const MassNormalizationDependencies &dependencies
);
[[nodiscard]] MassNormalizationPreparationResult TryPrepare(
const MassNormalizationStateView &state,
const MassNormalizationDependencies &dependencies
);
[[nodiscard]] MassNormalizationPreparationResult TryPrepare(
const models::CompiledFixedMass &constraint,
const MassNormalizationDependencies &dependencies
);
void BuildResidual(mfem::Vector &residual) const;
void ApplyDensityJacobianAction(
@@ -179,9 +213,11 @@ export namespace mean_field::operators {
};
void BuildStaticPlan();
void RefreshGeometry(const mfem::Vector &displacement);
void RefreshDensity(const mfem::Vector &density);
void AssembleResidual();
[[nodiscard]] std::optional<MassNormalizationPreparationRejection>
RefreshGeometry(const mfem::Vector &displacement);
[[nodiscard]] std::optional<MassNormalizationPreparationRejection> RefreshDensity(const mfem::Vector &density);
[[nodiscard]] std::optional<MassNormalizationPreparationRejection> AssembleResidual();
[[nodiscard]] std::optional<MassNormalizationPreparationRejection> UpdateResidualForTargetMass();
void VerifyPrepared() const;
[[nodiscard]] double EvaluateDensityActionLocal(const mfem::Vector &densityVariation) const;

View File

@@ -3,6 +3,7 @@ module;
#include <compare>
#include <cstddef>
#include <cstdint>
#include <expected>
#include <optional>
#include <vector>
@@ -18,6 +19,18 @@ export import :operators.context.pressure_force;
export import :utils.blocks;
export namespace mean_field::operators {
enum class PressureForcePreparationRejectionReason : std::uint8_t {
equation_of_state,
invalid_mapping,
non_finite_arithmetic
};
struct PressureForcePreparationRejection final {
PressureForcePreparationRejectionReason reason{PressureForcePreparationRejectionReason::equation_of_state};
eos::EvaluationErrorCode equationOfStateCode{eos::EvaluationErrorCode::nonfinite_result};
mapping::MappingStatus mappingStatus{mapping::MappingStatus::valid};
};
struct PreparedPressureForceReport final {
context::pressure_force::PressureForcePreparationReport contextReport;
@@ -92,6 +105,14 @@ export namespace mean_field::operators {
const context::pressure_force::PressureForceDependencies &dependencies
);
[[nodiscard]] std::expected<
PreparedPressureForceReport,
PressureForcePreparationRejection>
TryPrepare(
const context::pressure_force::PressureForceStateView &state,
const context::pressure_force::PressureForceDependencies &dependencies
);
void BuildResidual(mfem::Vector &residual) const;
void ApplyEnthalpyJacobianAction(
@@ -206,12 +227,12 @@ export namespace mean_field::operators {
};
void PrepareStaticPlan();
void PrepareGeometry();
void PrepareMaterialState();
[[nodiscard]] std::optional<PressureForcePreparationRejection> PrepareGeometry();
[[nodiscard]] std::optional<PressureForcePreparationRejection> PrepareMaterialState();
void FinalizeDisplacementJacobianPreparation();
void AssembleCachedResidual();
[[nodiscard]] std::optional<PressureForcePreparationRejection> AssembleCachedResidual();
void VerifyPrepared() const;
@@ -301,4 +322,4 @@ export namespace mean_field::operators {
const PreparedPressureForceOperator &m_preparedOperator;
};
} // namespace mean_field::operators
} // namespace mean_field::operators

View File

@@ -2,6 +2,7 @@ module;
#include <compare>
#include <cstdint>
#include <expected>
#include <optional>
#include <vector>
@@ -12,6 +13,7 @@ export module mean_field:operators.prepared_rotational_displacement_force;
export import :fem;
export import :mapping.domain_mapper;
export import :operators.context.rotational_displacement_force;
export import :operators.kernels.rotational_displacement_force;
export import :physics.rigid_rotation;
export import :utils.blocks;
@@ -61,6 +63,15 @@ export namespace mean_field::operators {
const physics::RigidRotation &rotation
);
[[nodiscard]] std::expected<
PreparedRotationalDisplacementForceReport,
kernels::RotationalDisplacementForceRejection>
TryPrepare(
const context::rotational_displacement_force::RotationalDisplacementForceStateView &state,
const context::rotational_displacement_force::RotationalDisplacementForceDependencies &dependencies,
const physics::RigidRotation &rotation
);
void BuildResidual(mfem::Vector &residual) const;
void ApplyDensityJacobianAction(
@@ -104,7 +115,10 @@ export namespace mean_field::operators {
private:
void VerifyPrepared() const;
void PrepareElementData();
[[nodiscard]] std::expected<
void,
kernels::RotationalDisplacementForceRejection>
TryPrepareElementData();
void ApplyPreparedCompleteJacobianActionTrue(
const mfem::Vector &densityVariationTrue,
const mfem::Vector &displacementVariationTrue,

View File

@@ -3,10 +3,14 @@ module;
#include <compare>
#include <concepts>
#include <cstdint>
#include <expected>
#include <limits>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <mfem.hpp>
#include <mpi.h>
export module mean_field:operators.prepared_stellar_equilibrium;
@@ -72,6 +76,70 @@ export namespace mean_field::operators {
}
};
enum class StellarEquilibriumPreparationRejectionReason : std::uint8_t {
inverted_geometry,
non_finite_geometry,
thermodynamic_domain,
non_finite_thermodynamics,
inadmissible_physics,
non_finite_physics
};
enum class StellarEquilibriumPreparationStage : std::uint8_t {
unspecified,
generated_geometry,
gravity,
barotropic_closure,
hydrostatic_equilibrium,
displacement_residual,
pressure_force,
gravity_displacement_force,
rotational_displacement_force,
displacement_composition,
mass_normalization,
model_specification
};
/*
* A candidate state that cannot define a physical mapped domain is an
* expected line-search outcome, not an exceptional program failure. Keep
* this payload fixed-size so every trial can report it without allocating.
*/
struct StellarEquilibriumPreparationRejection final {
StellarEquilibriumPreparationRejectionReason reason{
StellarEquilibriumPreparationRejectionReason::inverted_geometry
};
StellarEquilibriumPreparationStage stage{StellarEquilibriumPreparationStage::unspecified};
double minimumJacobianDeterminant{std::numeric_limits<double>::quiet_NaN()};
eos::EvaluationErrorCode thermodynamicErrorCode{eos::EvaluationErrorCode::nonfinite_result};
};
template <typename Report>
using StellarEquilibriumPreparationResult = std::expected<Report, StellarEquilibriumPreparationRejection>;
[[noreturn]] inline void
throwStellarEquilibriumPreparationRejection(const StellarEquilibriumPreparationRejection &rejection) {
switch (rejection.reason) {
case StellarEquilibriumPreparationRejectionReason::non_finite_geometry:
throw std::domain_error("The prepared domain deformation has non-finite mapped geometry.");
case StellarEquilibriumPreparationRejectionReason::thermodynamic_domain:
throw eos::EvaluationError(
rejection.thermodynamicErrorCode, "The stellar state lies outside the equation-of-state domain."
);
case StellarEquilibriumPreparationRejectionReason::non_finite_thermodynamics:
throw eos::EvaluationError(
rejection.thermodynamicErrorCode, "The stellar state produced non-finite thermodynamic data."
);
case StellarEquilibriumPreparationRejectionReason::inadmissible_physics:
throw std::domain_error("The stellar state is physically inadmissible.");
case StellarEquilibriumPreparationRejectionReason::non_finite_physics:
throw std::domain_error("The stellar state produced non-finite physical data.");
case StellarEquilibriumPreparationRejectionReason::inverted_geometry:
throw std::domain_error("The prepared domain deformation inverts at least one volume element.");
}
throw std::logic_error("Unknown stellar-equilibrium candidate-rejection reason.");
}
struct PreparedStellarEquilibriumStatistics final {
std::uint64_t residualAssemblies{0};
std::uint64_t residualApplications{0};
@@ -159,6 +227,12 @@ export namespace mean_field::operators {
const physics::RigidRotation &rotation
);
[[nodiscard]] StellarEquilibriumPreparationResult<PreparedStellarEquilibriumReport> TryPrepare(
const mfem::Vector &state,
const StellarEquilibriumDependencies &dependencies,
const physics::RigidRotation &rotation
);
void BuildResidual(mfem::Vector &residual) const;
void Mult(
@@ -188,12 +262,9 @@ export namespace mean_field::operators {
[[nodiscard]] const PreparedHydrostaticEquilibriumOperator &GetHydrostaticOperator() const noexcept;
[[nodiscard]] const PreparedDisplacementResidualOperator &GetDisplacementOperator() const noexcept;
[[nodiscard]] const PreparedMassNormalizationOperator &GetMassNormalizationOperator() const noexcept;
[[nodiscard]] double ApplyDensityVolumeIntegralDensityAction(
const mfem::Vector &densityDirection
) const;
[[nodiscard]] double ApplyDensityVolumeIntegralSurfaceShapeAction(
const mfem::Vector &surfaceShapeDirection
) const;
[[nodiscard]] double ApplyDensityVolumeIntegralDensityAction(const mfem::Vector &densityDirection) const;
[[nodiscard]] double
ApplyDensityVolumeIntegralSurfaceShapeAction(const mfem::Vector &surfaceShapeDirection) const;
[[nodiscard]] const PreparedPressureSurfaceConstraint &GetSurfaceConstraintOperator() const noexcept;
[[nodiscard]] const deformation::PreparedDomainDeformationRuntime &GetDomainDeformation() const noexcept;
[[nodiscard]] const mfem::Vector &GetSurfaceDeformationParameters() const;
@@ -222,6 +293,7 @@ export namespace mean_field::operators {
void VerifyPrepared() const;
StellarEquilibriumRootManifest m_rootManifest;
MPI_Comm m_communicator{MPI_COMM_NULL};
mfem::Array<int> m_gravityStateOffsets;
context::gravity_field::GravityFieldLinearizationContext m_gravityContext;

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@ module;
#include <concepts>
#include <cstddef>
#include <cstdint>
#include <expected>
#include <memory>
#include <type_traits>
#include <utility>
@@ -28,12 +29,11 @@ export namespace mean_field::equilibrium {
static constexpr bool complete = false;
};
template <model::StellarModelType Model>
struct StellarSurfaceCompilationAudit<Model, true> {
template <model::StellarModelType Model> struct StellarSurfaceCompilationAudit<Model, true> {
private:
using ModelType = std::remove_cvref_t<Model>;
using EquationOfState = typename ModelType::EquationOfStateType;
using Form = operators::CompiledStellarEquilibriumForm<ModelType>;
using ModelType = std::remove_cvref_t<Model>;
using EquationOfState = typename ModelType::EquationOfStateType;
using Form = operators::CompiledStellarEquilibriumForm<ModelType>;
using AvailableEquations = material::StellarEquilibriumThermodynamicEquations;
static constexpr bool thermodynamicsCompilable =
@@ -46,14 +46,12 @@ export namespace mean_field::equilibrium {
} else {
using ThermodynamicEquations =
material::CompiledThermodynamicEquationsT<EquationOfState, Form, AvailableEquations>;
using Formulation = typename ThermodynamicEquations::PressureSurfaceFormulation;
using CompiledSurface =
surface::CompiledPressureSurfaceConstraintT<Formulation, EquationOfState>;
using Formulation = typename ThermodynamicEquations::PressureSurfaceFormulation;
using CompiledSurface = surface::CompiledPressureSurfaceConstraintT<Formulation, EquationOfState>;
return requires(const ModelType &model) {
{
surface::compilePressureSurfaceConstraint<Formulation>(
model.surfaceCondition(),
model.equationOfState()
model.surfaceCondition(), model.equationOfState()
)
} -> std::same_as<CompiledSurface>;
};
@@ -77,8 +75,7 @@ export namespace mean_field::equilibrium {
requires operators::StellarEquilibriumSystemCompilable<std::remove_cvref_t<Candidate>>;
requires hasStellarEquilibriumSurfaceCompilation<std::remove_cvref_t<Candidate>>;
requires operators::CompilableRootManifestFor<
std::remove_cvref_t<Candidate>,
operators::CompiledStellarEquilibriumForm<std::remove_cvref_t<Candidate>>>;
std::remove_cvref_t<Candidate>, operators::CompiledStellarEquilibriumForm<std::remove_cvref_t<Candidate>>>;
requires operators::hasStellarEquilibriumCoreRuntime<std::remove_cvref_t<Candidate>>;
requires operators::hasCompleteStellarEquilibriumRuntime<std::remove_cvref_t<Candidate>>;
requires operators::stellarEquilibriumRotationProviderCount<std::remove_cvref_t<Candidate>> <= 1;
@@ -105,19 +102,16 @@ export namespace mean_field::equilibrium {
operators::StellarEquilibriumPhysicalCoreType<std::remove_cvref_t<Model>>,
typename std::remove_cvref_t<Model>::SpecificationTypes>> { };
struct StellarEquilibriumProblemFactory;
} // namespace detail
template <
StellarEquilibriumModel Model,
StellarDiscretizationType Discretization = StellarDiscretization>
template <StellarEquilibriumModel Model, StellarDiscretizationType Discretization = StellarDiscretization>
requires detail::StellarEquilibriumModelDiscretizationStructureAudit<
std::remove_cvref_t<Model>,
std::remove_cvref_t<Discretization>>::value
class StellarEquilibriumProblem final {
public:
using ModelType = std::remove_cvref_t<Model>;
using DiscretizationType = std::remove_cvref_t<Discretization>;
using ModelType = std::remove_cvref_t<Model>;
using DiscretizationType = std::remove_cvref_t<Discretization>;
using NormalizationPrescriptionType = typename DiscretizationType::NormalizationPrescriptionType;
static constexpr bool hasFixedCentralDensity =
@@ -128,13 +122,15 @@ export namespace mean_field::equilibrium {
operators::stellarEquilibriumRotationProviderCount<ModelType>;
static constexpr bool symbolicallySquare = ModelType::symbolicallySquare;
using PreparedOperatorType = operators::PreparedVariadicStellarEquilibriumOperator<ModelType>;
using PhysicalCoreType = typename PreparedOperatorType::PhysicalCoreType;
using FormType = operators::CompiledStellarEquilibriumForm<ModelType>;
using JacobianFormType = operators::CompiledStellarEquilibriumJacobianForm<ModelType>;
using ManifestType = operators::EquilibriumSystemManifest<ModelType, FormType, JacobianFormType>;
using EquationOfStateType = model::EquationOfStateType<ModelType>;
using SurfaceConditionType = model::SurfaceConditionType<ModelType>;
using PreparedOperatorType = operators::PreparedVariadicStellarEquilibriumOperator<ModelType>;
using Report = typename PreparedOperatorType::Report;
using PreparationResult = typename PreparedOperatorType::PreparationResult;
using PhysicalCoreType = typename PreparedOperatorType::PhysicalCoreType;
using FormType = operators::CompiledStellarEquilibriumForm<ModelType>;
using JacobianFormType = operators::CompiledStellarEquilibriumJacobianForm<ModelType>;
using ManifestType = operators::EquilibriumSystemManifest<ModelType, FormType, JacobianFormType>;
using EquationOfStateType = model::EquationOfStateType<ModelType>;
using SurfaceConditionType = model::SurfaceConditionType<ModelType>;
using AvailableThermodynamicEquations = material::StellarEquilibriumThermodynamicEquations;
using ThermodynamicEquationsType =
material::CompiledThermodynamicEquationsT<EquationOfStateType, FormType, AvailableThermodynamicEquations>;
@@ -147,17 +143,18 @@ export namespace mean_field::equilibrium {
StellarEquilibriumProblem(
ModelType stellarModel,
DiscretizationType discretization
DiscretizationType discretization,
fem::FEM &finiteElementModel
)
: m_stellarModel(std::make_shared<ModelType>(std::move(stellarModel))),
m_discretization(std::move(discretization)),
m_compiledSurfaceConstraint(CompileSurfaceConstraint(*m_stellarModel)),
m_preparedOperator(
m_discretization.finiteElementModel(),
finiteElementModel,
m_discretization.domainMapper(),
m_stellarModel,
operators::PressureSurfaceConstraintView{m_compiledSurfaceConstraint},
CompileDefaultDomainDeformation(m_discretization.finiteElementModel())
CompileDefaultDomainDeformation(finiteElementModel)
) {
VerifyProblem();
}
@@ -176,6 +173,12 @@ export namespace mean_field::equilibrium {
return m_discretization;
}
[[nodiscard]] MPI_Comm GetCommunicator() const & {
return m_discretization.communicator();
}
[[nodiscard]] MPI_Comm GetCommunicator() const && = delete;
[[nodiscard]] const NormalizationPrescriptionType &GetNormalizationPrescription() const noexcept {
return m_discretization.normalizationPrescription();
}
@@ -236,21 +239,54 @@ export namespace mean_field::equilibrium {
const mfem::Vector &state,
const operators::StellarEquilibriumDependencies &dependencies,
const physics::RigidRotation &rotation
) requires(generatedRotationProviderCount == 0) {
)
requires(generatedRotationProviderCount == 0)
{
auto report = m_preparedOperator.Prepare(state, dependencies, rotation);
++m_preparationGeneration;
return report;
}
[[nodiscard]] PreparationResult TryPrepare(
const mfem::Vector &state,
const operators::StellarEquilibriumDependencies &dependencies,
const physics::RigidRotation &rotation
)
requires(generatedRotationProviderCount == 0)
{
auto result = m_preparedOperator.TryPrepare(state, dependencies, rotation);
if (!result.has_value()) {
return std::unexpected(result.error());
}
++m_preparationGeneration;
return result;
}
[[nodiscard]] auto Prepare(
const mfem::Vector &state,
const operators::StellarEquilibriumDependencies &dependencies
) requires(generatedRotationProviderCount == 1) {
)
requires(generatedRotationProviderCount == 1)
{
auto report = m_preparedOperator.Prepare(state, dependencies);
++m_preparationGeneration;
return report;
}
[[nodiscard]] PreparationResult TryPrepare(
const mfem::Vector &state,
const operators::StellarEquilibriumDependencies &dependencies
)
requires(generatedRotationProviderCount == 1)
{
auto result = m_preparedOperator.TryPrepare(state, dependencies);
if (!result.has_value()) {
return std::unexpected(result.error());
}
++m_preparationGeneration;
return result;
}
void BuildResidual(mfem::Vector &residual) const {
m_preparedOperator.BuildResidual(residual);
}
@@ -266,8 +302,7 @@ export namespace mean_field::equilibrium {
[[nodiscard]] static CompiledSurfaceConstraintType CompileSurfaceConstraint(const ModelType &stellarModel) {
return surface::compilePressureSurfaceConstraint<
typename ThermodynamicEquationsType::PressureSurfaceFormulation>(
stellarModel.surfaceCondition(),
stellarModel.equationOfState()
stellarModel.surfaceCondition(), stellarModel.equationOfState()
);
}
@@ -318,28 +353,25 @@ export namespace mean_field::equilibrium {
template <
typename Model,
typename Discretization,
bool StructurallyCompatible =
StellarEquilibriumModelDiscretizationStructureAudit<
std::remove_cvref_t<Model>,
std::remove_cvref_t<Discretization>>::value>
bool StructurallyCompatible = StellarEquilibriumModelDiscretizationStructureAudit<
std::remove_cvref_t<Model>,
std::remove_cvref_t<Discretization>>::value>
struct StellarEquilibriumModelDiscretizationOperationAudit : std::false_type { };
template <typename Model, typename Discretization>
struct StellarEquilibriumModelDiscretizationOperationAudit<
Model,
Discretization,
true> {
struct StellarEquilibriumModelDiscretizationOperationAudit<Model, Discretization, true> {
private:
using ModelType = std::remove_cvref_t<Model>;
using ModelType = std::remove_cvref_t<Model>;
using DiscretizationType = std::remove_cvref_t<Discretization>;
using Problem = StellarEquilibriumProblem<ModelType, DiscretizationType>;
using Prescription = typename DiscretizationType::NormalizationPrescriptionType;
using Problem = StellarEquilibriumProblem<ModelType, DiscretizationType>;
using Prescription = typename DiscretizationType::NormalizationPrescriptionType;
public:
static constexpr bool value = [] {
if constexpr (
std::same_as<Prescription, normalization::Unnormalized> ||
normalization::PhysicalRieszDiagonalPrescription<Prescription>) {
normalization::PhysicalRieszDiagonalPrescription<Prescription>
) {
return true;
} else {
return normalization::RuntimePreparedNormalizationOperation<Problem>;
@@ -362,46 +394,99 @@ export namespace mean_field::equilibrium {
std::remove_cvref_t<Model>,
std::remove_cvref_t<Discretization>>::value;
namespace detail {
/* The structurally formed problem type is needed to probe the ADL
* operation without a recursive concept. Its constructor remains
* private, and this factory is the single construction authority after
* the complete public compatibility contract has succeeded. */
struct StellarEquilibriumProblemFactory final {
template <StellarEquilibriumModel Model, StellarDiscretizationType Discretization>
requires StellarEquilibriumModelDiscretizationCompatible<Model, Discretization>
[[nodiscard]] static auto Create(
Model &&stellarModel,
Discretization discretization
) {
using ModelType = std::remove_cvref_t<Model>;
using DiscretizationType = std::remove_cvref_t<Discretization>;
return StellarEquilibriumProblem<ModelType, DiscretizationType>{
std::forward<Model>(stellarModel),
std::move(discretization)
};
}
};
} // namespace detail
} // namespace mean_field::equilibrium
template <StellarEquilibriumModel Model, StellarDiscretizationType Discretization>
requires StellarEquilibriumModelDiscretizationCompatible<Model, Discretization>
namespace mean_field::equilibrium::detail {
/* The structurally formed problem type is needed to probe the ADL
* operation without a recursive concept. Its constructor remains
* private, and this factory is the single construction authority after
* the complete public compatibility contract has succeeded. */
struct StellarEquilibriumProblemFactory final {
template <
StellarEquilibriumModel Model,
StellarDiscretizationType Discretization>
requires StellarEquilibriumModelDiscretizationCompatible<
Model,
Discretization>
[[nodiscard]] static auto Create(
Model &&stellarModel,
Discretization discretization
) {
using ModelType = std::remove_cvref_t<Model>;
using DiscretizationType = std::remove_cvref_t<Discretization>;
fem::FEM &finiteElementModel = discretization.MutableFiniteElementModelForAssembly();
return StellarEquilibriumProblem<ModelType, DiscretizationType>{
std::forward<Model>(stellarModel), std::move(discretization), finiteElementModel
};
}
template <
StellarEquilibriumModel Model,
StellarDiscretizationType Discretization>
requires StellarEquilibriumModelDiscretizationCompatible<
Model,
Discretization>
[[nodiscard]] static auto CreateOwned(
Model &&stellarModel,
Discretization discretization
) {
using ModelType = std::remove_cvref_t<Model>;
using DiscretizationType = std::remove_cvref_t<Discretization>;
using ProblemType = StellarEquilibriumProblem<ModelType, DiscretizationType>;
fem::FEM &finiteElementModel = discretization.MutableFiniteElementModelForAssembly();
return std::unique_ptr<ProblemType>{
new ProblemType{std::forward<Model>(stellarModel), std::move(discretization), finiteElementModel}
};
}
template <DiscretizedStellarEquilibriumProblem Problem>
[[nodiscard]] static fem::FEM &MutableFiniteElementModelForProjection(Problem &problem) {
return problem.m_discretization.MutableFiniteElementModelForAssembly();
}
template <DiscretizedStellarEquilibriumProblem Problem>
[[nodiscard]] static const fem::FEM &FiniteElementModel(const Problem &problem) {
return problem.m_discretization.RequireFiniteElementModel();
}
};
} // namespace mean_field::equilibrium::detail
export namespace mean_field::equilibrium {
template <
StellarEquilibriumModel Model,
StellarDiscretizationType Discretization>
requires StellarEquilibriumModelDiscretizationCompatible<
Model,
Discretization>
[[nodiscard]] auto discretize(
Model &&stellarModel,
Discretization discretization
) {
return detail::StellarEquilibriumProblemFactory::Create(
std::forward<Model>(stellarModel),
std::move(discretization)
std::forward<Model>(stellarModel), std::move(discretization)
);
}
template <StellarEquilibriumModel Model>
requires StellarEquilibriumModelDiscretizationCompatible<Model, StellarDiscretization>
requires StellarEquilibriumModelDiscretizationCompatible<
Model,
StellarDiscretization>
[[nodiscard]] auto discretize(
Model &&stellarModel,
fem::FEM &finiteElementModel
fem::FEM &&finiteElementModel
) {
return discretize(std::forward<Model>(stellarModel), StellarDiscretization{finiteElementModel});
return discretize(std::forward<Model>(stellarModel), StellarDiscretization{std::move(finiteElementModel)});
}
template <StellarEquilibriumModel Model>
requires StellarEquilibriumModelDiscretizationCompatible<
Model,
StellarDiscretization>
StellarEquilibriumProblem<
std::remove_cvref_t<Model>,
StellarDiscretization>
discretize(
Model &&,
fem::FEM &
) = delete;
} // namespace mean_field::equilibrium

View File

@@ -17,10 +17,9 @@ export namespace mean_field::equilibrium {
template <StellarEquilibriumModel Model>
[[nodiscard]] auto makeStellarEquilibriumSystem(
fem::FEM &finiteElementModel,
const mapping::DomainMapper &domainMapper,
fem::FEM &&finiteElementModel,
Model &&stellarModel
) {
return discretize(std::forward<Model>(stellarModel), StellarDiscretization{finiteElementModel, domainMapper});
return discretize(std::forward<Model>(stellarModel), std::move(finiteElementModel));
}
} // namespace mean_field::equilibrium