feat(libmeanfield): centrifugal + pressure

This commit is contained in:
2026-08-04 14:24:55 -04:00
parent 9bc4f2758a
commit dc912fd15e
115 changed files with 260058 additions and 163261 deletions

View File

@@ -0,0 +1,135 @@
module;
#include <cstdint>
#include <mfem.hpp>
module mean_field;
import :operators.context.barotropic_closure_linearization;
namespace mean_field::operators::context::barotropic {
BarotropicClosureLinearizationContext::
BarotropicClosureLinearizationContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope
)
: m_f(f),
m_operator(
f,
domainMapper,
barotrope
) {
MFEM_VERIFY(
m_f.densityFes != nullptr,
"The closure linearization context requires the "
"density finite-element space."
);
MFEM_VERIFY(
m_f.enthalpyFes != nullptr,
"The closure linearization context requires the "
"enthalpy finite-element space."
);
MFEM_VERIFY(
m_f.displacementFes != nullptr,
"The closure linearization context requires the "
"displacement finite-element space."
);
}
void BarotropicClosureLinearizationContext::Prepare(
const mfem::Vector &baseDensityTrue,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &displacementTrue,
const BarotropicClosureRevisions &revisions
) {
MFEM_VERIFY(
baseDensityTrue.Size() == m_f.densityFes->GetTrueVSize(),
"The closure base-density vector has the wrong size."
);
MFEM_VERIFY(
baseEnthalpyTrue.Size() == m_f.enthalpyFes->GetTrueVSize(),
"The closure base-enthalpy vector has the wrong size."
);
MFEM_VERIFY(
displacementTrue.Size() == m_f.displacementFes->GetTrueVSize(),
"The closure displacement vector has the wrong size."
);
if (m_isPrepared && revisions == m_revisions) {
return;
}
m_operator.Prepare(baseDensityTrue, baseEnthalpyTrue, displacementTrue);
m_baseDensityTrue = baseDensityTrue;
m_baseEnthalpyTrue = baseEnthalpyTrue;
m_displacementTrue = displacementTrue;
m_revisions = revisions;
m_isPrepared = true;
++m_preparationCount;
}
bool BarotropicClosureLinearizationContext::IsPrepared() const noexcept {
return m_isPrepared;
}
bool BarotropicClosureLinearizationContext::MatchesRevisions(
const BarotropicClosureRevisions &revisions
) const noexcept {
return m_isPrepared && revisions == m_revisions;
}
std::uint64_t BarotropicClosureLinearizationContext::
GetPreparationCount() const noexcept {
return m_preparationCount;
}
const BarotropicClosureRevisions &
BarotropicClosureLinearizationContext::GetRevisions() const {
VerifyPrepared();
return m_revisions;
}
const mfem::Vector &
BarotropicClosureLinearizationContext::GetBaseDensityTrue() const {
VerifyPrepared();
return m_baseDensityTrue;
}
const mfem::Vector &
BarotropicClosureLinearizationContext::GetBaseEnthalpyTrue() const {
VerifyPrepared();
return m_baseEnthalpyTrue;
}
const mfem::Vector &
BarotropicClosureLinearizationContext::GetDisplacementTrue() const {
VerifyPrepared();
return m_displacementTrue;
}
const PreparedBarotropicClosureOperator &
BarotropicClosureLinearizationContext::GetOperator() const noexcept {
return m_operator;
}
void BarotropicClosureLinearizationContext::BuildResidual(
mfem::Vector &residual
) const {
VerifyPrepared();
m_operator.BuildResidual(residual);
}
void BarotropicClosureLinearizationContext::VerifyPrepared() const {
MFEM_VERIFY(
m_isPrepared, "The barotropic-closure linearization context "
"has not been prepared."
);
}
} // namespace mean_field::operators::context::barotropic

View File

@@ -0,0 +1,442 @@
module;
#include <cmath>
#include <memory>
#include <mfem.hpp>
module mean_field;
import :operators.context.gravity_field;
namespace {
void validate_displacement(
const mean_field::fem::FEM &f,
const mfem::Vector &displacement_true
) {
MFEM_VERIFY(
f.displacementFes != nullptr,
"GravityFieldGeometryContext requires the "
"displacement finite-element space."
);
MFEM_VERIFY(
displacement_true.Size() == f.displacementFes->GetTrueVSize(),
"GravityFieldGeometryContext received a displacement vector with "
"the "
"wrong size."
);
for (int i = 0; i < displacement_true.Size(); ++i) {
MFEM_VERIFY(
std::isfinite(displacement_true(i)),
"GravityFieldGeometryContext received a non-finite "
"displacement "
"value."
);
}
}
void validate_linearization_state(
const mean_field::fem::FEM &f,
const mean_field::operators::context::gravity_field::
GravityFieldStateView &state
) {
MFEM_VERIFY(
f.densityFes != nullptr, "GravityFieldLinearizationContext "
"requires the density finite-element "
"space."
);
MFEM_VERIFY(
f.gravityPotentialFes != nullptr,
"GravityFieldLinearizationContext requires the gravity-potential "
"finite-element space."
);
MFEM_VERIFY(
f.gravityFluxFes != nullptr,
"GravityFieldLinearizationContext requires the "
"gravity-gradient finite-element space."
);
MFEM_VERIFY(
f.displacementFes != nullptr,
"GravityFieldLinearizationContext requires "
"the displacement finite-element space."
);
MFEM_VERIFY(
state.density.Size() == f.densityFes->GetTrueVSize(),
"GravityFieldLinearizationContext received a density vector with "
"the "
"wrong size."
);
MFEM_VERIFY(
state.displacement.Size() == f.displacementFes->GetTrueVSize(),
"GravityFieldLinearizationContext received a displacement vector "
"with "
"the wrong size."
);
MFEM_VERIFY(
state.gravity_gradient.Size() == f.gravityFluxFes->GetTrueVSize(),
"GravityFieldLinearizationContext received a gravity-gradient "
"vector "
"with the wrong size."
);
MFEM_VERIFY(
state.gravity_potential.Size() ==
f.gravityPotentialFes->GetTrueVSize(),
"GravityFieldLinearizationContext received a gravity-potential "
"vector "
"with the wrong size."
);
for (int i = 0; i < state.density.Size(); ++i) {
MFEM_VERIFY(
std::isfinite(state.density(i)),
"GravityFieldLinearizationContext received a non-finite "
"density "
"value."
);
}
for (int i = 0; i < state.displacement.Size(); ++i) {
MFEM_VERIFY(
std::isfinite(state.displacement(i)),
"GravityFieldLinearizationContext received a non-finite "
"displacement "
"value."
);
}
for (int i = 0; i < state.gravity_gradient.Size(); ++i) {
MFEM_VERIFY(
std::isfinite(state.gravity_gradient(i)),
"GravityFieldLinearizationContext received a non-finite "
"gravity-gradient value."
);
}
for (int i = 0; i < state.gravity_potential.Size(); ++i) {
MFEM_VERIFY(
std::isfinite(state.gravity_potential(i)),
"GravityFieldLinearizationContext received a non-finite "
"gravity-potential value."
);
}
}
} // namespace
namespace mean_field::operators::context::gravity_field {
GravityFieldGeometryContext::GravityFieldGeometryContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper
)
: m_fem(f),
m_domain_mapper(domain_mapper) {
MFEM_VERIFY(
f.mesh != nullptr, "GravityFieldGeometryContext requires a mesh."
);
MFEM_VERIFY(
f.gravityFluxFes != nullptr,
"GravityFieldGeometryContext requires the "
"gravity-gradient finite-element space."
);
MFEM_VERIFY(
f.densityFes != nullptr,
"GravityFieldGeometryContext requires the density finite-element "
"space."
);
MFEM_VERIFY(
f.gravityPotentialFes != nullptr,
"GravityFieldGeometryContext requires the gravity-potential "
"finite-element space."
);
MFEM_VERIFY(
f.displacementFes != nullptr,
"GravityFieldGeometryContext requires the "
"displacement finite-element space."
);
MFEM_VERIFY(
f.compactificationFes != nullptr,
"GravityFieldGeometryContext requires the compactification "
"finite-element space."
);
MFEM_VERIFY(
f.compactificationCoordinate != nullptr,
"GravityFieldGeometryContext requires the compactification "
"coordinate."
);
MFEM_VERIFY(
f.quadratureFactory != nullptr,
"GravityFieldGeometryContext requires the quadrature-rule factory."
);
MFEM_VERIFY(
domain_mapper.GetDimension() == f.mesh->Dimension(),
"The stateless domain-mapper dimension does not match the mesh "
"dimension."
);
}
GravityFieldGeometryPreparation GravityFieldGeometryContext::Prepare(
const mfem::Vector &displacement_true,
const DiscretizationRevision discretization_revision,
const DisplacementRevision displacement_revision
) {
validate_displacement(m_fem, displacement_true);
if (m_is_prepared) {
MFEM_VERIFY(
discretization_revision >= m_discretization_revision,
"GravityFieldGeometryContext received an older discretization "
"revision."
);
MFEM_VERIFY(
displacement_revision >= m_displacement_revision,
"GravityFieldGeometryContext received an older displacement "
"revision."
);
}
const bool discretization_changed =
!m_is_prepared ||
discretization_revision != m_discretization_revision;
const bool displacement_changed =
!m_is_prepared || displacement_revision != m_displacement_revision;
GravityFieldGeometryPreparation preparation;
if (!discretization_changed && !displacement_changed) {
return preparation;
}
if (discretization_changed) {
auto mass_operator =
std::make_unique<PreparedMappedHDivMassOperator>(
m_fem, m_domain_mapper
);
auto source_operator =
std::make_unique<PreparedMappedGravitySourceOperator>(
m_fem, m_domain_mapper
);
mass_operator->Prepare(displacement_true);
source_operator->Prepare(displacement_true);
m_mass_operator = std::move(mass_operator);
m_source_operator = std::move(source_operator);
preparation.reconstructed_operators = true;
preparation.rebuilt_mass_operator = true;
preparation.rebuilt_source_operator = true;
} else {
MFEM_VERIFY(
m_mass_operator != nullptr, "GravityFieldGeometryContext has "
"no prepared H(div) mass operator."
);
MFEM_VERIFY(
m_source_operator != nullptr,
"GravityFieldGeometryContext has no prepared gravity source "
"operator."
);
m_mass_operator->Prepare(displacement_true);
m_source_operator->Prepare(displacement_true);
preparation.rebuilt_mass_operator = true;
preparation.rebuilt_source_operator = true;
}
m_displacement_true = displacement_true;
m_discretization_revision = discretization_revision;
m_displacement_revision = displacement_revision;
m_is_prepared = true;
preparation.refreshed_variation_state = true;
return preparation;
}
const PreparedMappedHDivMassOperator &
GravityFieldGeometryContext::GetMassOperator() const {
MFEM_VERIFY(
m_is_prepared,
"GravityFieldGeometryContext must be prepared before "
"accessing its mass operator."
);
MFEM_VERIFY(
m_mass_operator != nullptr,
"GravityFieldGeometryContext has no prepared H(div) mass operator."
);
return *m_mass_operator;
}
const PreparedMappedGravitySourceOperator &
GravityFieldGeometryContext::GetSourceOperator() const {
MFEM_VERIFY(
m_is_prepared,
"GravityFieldGeometryContext must be prepared before "
"accessing its source operator."
);
MFEM_VERIFY(
m_source_operator != nullptr, "GravityFieldGeometryContext has no "
"prepared gravity source operator."
);
return *m_source_operator;
}
const mfem::Vector &GravityFieldGeometryContext::GetDisplacement() const {
MFEM_VERIFY(
m_is_prepared,
"GravityFieldGeometryContext must be prepared before "
"accessing its displacement."
);
return m_displacement_true;
}
DiscretizationRevision
GravityFieldGeometryContext::GetDiscretizationRevision() const noexcept {
return m_discretization_revision;
}
DisplacementRevision
GravityFieldGeometryContext::GetDisplacementRevision() const noexcept {
return m_displacement_revision;
}
bool GravityFieldGeometryContext::IsPrepared() const noexcept {
return m_is_prepared;
}
GravityFieldLinearizationContext::GravityFieldLinearizationContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper
)
: m_fem(f),
m_geometry_context(
f,
domain_mapper
) {
MFEM_VERIFY(
f.densityFes != nullptr, "GravityFieldLinearizationContext "
"requires the density finite-element "
"space."
);
MFEM_VERIFY(
f.gravityPotentialFes != nullptr,
"GravityFieldLinearizationContext requires the gravity-potential "
"finite-element space."
);
MFEM_VERIFY(
f.gravityFluxFes != nullptr,
"GravityFieldLinearizationContext requires the "
"gravity-gradient finite-element space."
);
MFEM_VERIFY(
f.displacementFes != nullptr,
"GravityFieldLinearizationContext requires "
"the displacement finite-element space."
);
}
GravityFieldPreparationReport GravityFieldLinearizationContext::Prepare(
const GravityFieldStateView &state,
const GravityFieldRevisions &revisions
) {
validate_linearization_state(m_fem, state);
if (m_is_prepared) {
MFEM_VERIFY(
revisions.discretization >= m_revisions.discretization,
"GravityFieldLinearizationContext received an older "
"discretization "
"revision."
);
MFEM_VERIFY(
revisions.displacement >= m_revisions.displacement,
"GravityFieldLinearizationContext received an older "
"displacement "
"revision."
);
MFEM_VERIFY(
revisions.density >= m_revisions.density,
"GravityFieldLinearizationContext received an older density "
"revision."
);
MFEM_VERIFY(
revisions.gravity_gradient >= m_revisions.gravity_gradient,
"GravityFieldLinearizationContext received an older "
"gravity-gradient "
"revision."
);
MFEM_VERIFY(
revisions.gravity_potential >= m_revisions.gravity_potential,
"GravityFieldLinearizationContext received an older "
"gravity-potential revision."
);
}
const bool discretization_changed =
!m_is_prepared ||
revisions.discretization != m_revisions.discretization;
const bool density_changed = !m_is_prepared || discretization_changed ||
revisions.density != m_revisions.density;
const bool gravity_gradient_changed =
!m_is_prepared || discretization_changed ||
revisions.gravity_gradient != m_revisions.gravity_gradient;
GravityFieldPreparationReport report;
report.geometry = m_geometry_context.Prepare(
state.displacement, revisions.discretization, revisions.displacement
);
if (density_changed) {
m_density_true = state.density;
report.updated_density = true;
}
if (gravity_gradient_changed) {
m_gravity_gradient_true = state.gravity_gradient;
report.updated_gravity_gradient = true;
}
m_revisions = revisions;
m_is_prepared = true;
return report;
}
const GravityFieldGeometryContext &
GravityFieldLinearizationContext::GetGeometryContext() const {
MFEM_VERIFY(
m_is_prepared, "GravityFieldLinearizationContext must be prepared "
"before accessing its geometry context."
);
return m_geometry_context;
}
const mfem::Vector &GravityFieldLinearizationContext::GetDensity() const {
MFEM_VERIFY(
m_is_prepared, "GravityFieldLinearizationContext must be prepared "
"before accessing its density."
);
return m_density_true;
}
const mfem::Vector &
GravityFieldLinearizationContext::GetGravityGradient() const {
MFEM_VERIFY(
m_is_prepared, "GravityFieldLinearizationContext must be prepared "
"before accessing its gravity gradient."
);
return m_gravity_gradient_true;
}
const GravityFieldRevisions &
GravityFieldLinearizationContext::GetRevisions() const {
MFEM_VERIFY(
m_is_prepared, "GravityFieldLinearizationContext must be prepared "
"before accessing its revisions."
);
return m_revisions;
}
bool GravityFieldLinearizationContext::IsPrepared() const noexcept {
return m_is_prepared;
}
} // namespace mean_field::operators::context::gravity_field

View File

@@ -0,0 +1,309 @@
module;
#include <cmath>
#include <mfem.hpp>
module mean_field;
import :operators.context.hydrostatic_equilibrium;
namespace {
void validate_finite_vector(
const mfem::Vector &vector,
const char *message
) {
for (int i = 0; i < vector.Size(); ++i) {
MFEM_VERIFY(std::isfinite(vector(i)), message);
}
}
void validate_state(
const mean_field::fem::FEM &f,
const mean_field::operators::context::hydrostatic::
HydrostaticEquilibriumStateView &state
) {
MFEM_VERIFY(
f.enthalpyFes != nullptr,
"HydrostaticEquilibriumContext requires the "
"enthalpy finite-element space."
);
MFEM_VERIFY(
f.gravityPotentialFes != nullptr,
"HydrostaticEquilibriumContext requires the "
"gravity-potential finite-element space."
);
MFEM_VERIFY(
f.displacementFes != nullptr,
"HydrostaticEquilibriumContext requires the "
"displacement finite-element space."
);
MFEM_VERIFY(
state.enthalpy.Size() == f.enthalpyFes->GetTrueVSize(),
"HydrostaticEquilibriumContext received an "
"enthalpy vector with the wrong size."
);
MFEM_VERIFY(
state.gravityPotential.Size() ==
f.gravityPotentialFes->GetTrueVSize(),
"HydrostaticEquilibriumContext received a "
"gravity-potential vector with the wrong size."
);
MFEM_VERIFY(
state.displacement.Size() == f.displacementFes->GetTrueVSize(),
"HydrostaticEquilibriumContext received a "
"displacement vector with the wrong size."
);
validate_finite_vector(
state.enthalpy, "HydrostaticEquilibriumContext received a "
"non-finite enthalpy value."
);
validate_finite_vector(
state.gravityPotential, "HydrostaticEquilibriumContext received a "
"non-finite gravity-potential value."
);
validate_finite_vector(
state.displacement, "HydrostaticEquilibriumContext received a "
"non-finite displacement value."
);
MFEM_VERIFY(
std::isfinite(state.bernoulliConstant),
"HydrostaticEquilibriumContext received a "
"non-finite Bernoulli constant."
);
}
template <typename Stamp>
void validate_dependency_transition(
const Stamp &prepared,
const Stamp &requested,
const char *message
) {
MFEM_VERIFY(requested.CanFollow(prepared), message);
}
} // namespace
namespace mean_field::operators::context::hydrostatic {
HydrostaticEquilibriumContext::HydrostaticEquilibriumContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper
)
: m_f(f),
m_domainMapper(domainMapper) {
MFEM_VERIFY(
m_f.mesh != nullptr,
"HydrostaticEquilibriumContext requires a mesh."
);
MFEM_VERIFY(
m_f.enthalpyFes != nullptr,
"HydrostaticEquilibriumContext requires the "
"enthalpy finite-element space."
);
MFEM_VERIFY(
m_f.gravityPotentialFes != nullptr,
"HydrostaticEquilibriumContext requires the "
"gravity-potential finite-element space."
);
MFEM_VERIFY(
m_f.displacementFes != nullptr,
"HydrostaticEquilibriumContext requires the "
"displacement finite-element space."
);
MFEM_VERIFY(
m_domainMapper.GetDimension() == m_f.mesh->Dimension(),
"The hydrostatic context's stateless "
"domain-mapper dimension does not match the mesh "
"dimension."
);
}
HydrostaticPreparationReport HydrostaticEquilibriumContext::Prepare(
const HydrostaticEquilibriumStateView &state,
const HydrostaticEquilibriumDependencies &dependencies
) {
validate_state(m_f, state);
if (m_isPrepared) {
validate_dependency_transition(
m_dependencies.discretization, dependencies.discretization,
"HydrostaticEquilibriumContext received an older "
"discretization revision for the same identity."
);
validate_dependency_transition(
m_dependencies.enthalpy, dependencies.enthalpy,
"HydrostaticEquilibriumContext received an older "
"enthalpy revision for the same identity."
);
validate_dependency_transition(
m_dependencies.gravityPotential, dependencies.gravityPotential,
"HydrostaticEquilibriumContext received an older "
"gravity-potential revision for the same identity."
);
validate_dependency_transition(
m_dependencies.displacement, dependencies.displacement,
"HydrostaticEquilibriumContext received an older "
"displacement revision for the same identity."
);
validate_dependency_transition(
m_dependencies.rotation, dependencies.rotation,
"HydrostaticEquilibriumContext received an older "
"rotation revision for the same identity."
);
validate_dependency_transition(
m_dependencies.bernoulliConstant,
dependencies.bernoulliConstant,
"HydrostaticEquilibriumContext received an older "
"Bernoulli-constant revision for the same identity."
);
}
const bool staticChanged =
!m_isPrepared ||
dependencies.discretization != m_dependencies.discretization;
const bool enthalpyChanged =
!m_isPrepared || dependencies.enthalpy != m_dependencies.enthalpy;
const bool gravityPotentialChanged =
!m_isPrepared ||
dependencies.gravityPotential != m_dependencies.gravityPotential;
const bool displacementChanged =
!m_isPrepared ||
dependencies.displacement != m_dependencies.displacement;
const bool rotationChanged =
!m_isPrepared || dependencies.rotation != m_dependencies.rotation;
const bool bernoulliConstantChanged =
!m_isPrepared ||
dependencies.bernoulliConstant != m_dependencies.bernoulliConstant;
const bool geometryPreparationRequired =
staticChanged || displacementChanged;
const bool rotationPreparationRequired =
geometryPreparationRequired || rotationChanged;
const bool baseStatePreparationRequired =
rotationPreparationRequired || enthalpyChanged ||
gravityPotentialChanged || bernoulliConstantChanged;
HydrostaticPreparationReport report;
report.preparedStaticDependencies = staticChanged;
report.preparedGeometryState = geometryPreparationRequired;
report.preparedRotationDependencies = rotationPreparationRequired;
report.preparedBaseState = baseStatePreparationRequired;
if (staticChanged || enthalpyChanged) {
m_baseEnthalpyTrue = state.enthalpy;
report.updatedEnthalpy = true;
}
if (staticChanged || gravityPotentialChanged) {
m_baseGravityPotentialTrue = state.gravityPotential;
report.updatedGravityPotential = true;
}
if (geometryPreparationRequired) {
m_displacementTrue = state.displacement;
report.updatedDisplacement = true;
}
if (staticChanged || bernoulliConstantChanged) {
m_bernoulliConstant = state.bernoulliConstant;
report.updatedBernoulliConstant = true;
}
if (report.preparedStaticDependencies) {
++m_statistics.staticPreparations;
}
if (report.preparedGeometryState) {
++m_statistics.geometryPreparations;
}
if (report.preparedRotationDependencies) {
++m_statistics.rotationPreparations;
}
if (report.preparedBaseState) {
++m_statistics.baseStatePreparations;
}
m_dependencies = dependencies;
m_isPrepared = true;
return report;
}
bool HydrostaticEquilibriumContext::IsPrepared() const noexcept {
return m_isPrepared;
}
bool HydrostaticEquilibriumContext::MatchesDependencies(
const HydrostaticEquilibriumDependencies &dependencies
) const noexcept {
return m_isPrepared && dependencies == m_dependencies;
}
const HydrostaticEquilibriumDependencies &
HydrostaticEquilibriumContext::GetDependencies() const {
VerifyPrepared();
return m_dependencies;
}
const HydrostaticPreparationStatistics &
HydrostaticEquilibriumContext::GetPreparationStatistics() const noexcept {
return m_statistics;
}
const mfem::Vector &
HydrostaticEquilibriumContext::GetBaseEnthalpyTrue() const {
VerifyPrepared();
return m_baseEnthalpyTrue;
}
const mfem::Vector &
HydrostaticEquilibriumContext::GetBaseGravityPotentialTrue() const {
VerifyPrepared();
return m_baseGravityPotentialTrue;
}
const mfem::Vector &
HydrostaticEquilibriumContext::GetDisplacementTrue() const {
VerifyPrepared();
return m_displacementTrue;
}
double HydrostaticEquilibriumContext::GetBernoulliConstant() const {
VerifyPrepared();
return m_bernoulliConstant;
}
void HydrostaticEquilibriumContext::VerifyPrepared() const {
MFEM_VERIFY(
m_isPrepared, "HydrostaticEquilibriumContext has not been prepared."
);
}
} // namespace mean_field::operators::context::hydrostatic