feat(surface): major work on implementing surface constraints in a presciption agnostic manner
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.prepared_centering_constraint;
|
||||
|
||||
export import :field.mfem;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
struct PreparedCenteringConstraintReport final {
|
||||
bool cachedCenterDisplacement{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return cachedCenterDisplacement;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Strong translational gauge: the material point at the computational
|
||||
* origin has zero displacement. The three corresponding displacement
|
||||
* residual rows replace redundant force-balance rows.
|
||||
*/
|
||||
class PreparedCenteringConstraint final {
|
||||
public:
|
||||
explicit PreparedCenteringConstraint(field::FieldPointDofMap centerRows)
|
||||
: m_centerRows(std::move(centerRows)),
|
||||
m_centerDisplacement(m_centerRows.size()) {
|
||||
}
|
||||
|
||||
[[nodiscard]] PreparedCenteringConstraintReport Prepare(
|
||||
const mfem::Vector &displacement,
|
||||
const bool displacementChanged
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
displacement.Size() == m_centerRows.field_size(),
|
||||
"The centering constraint received a displacement vector with the wrong size."
|
||||
);
|
||||
|
||||
PreparedCenteringConstraintReport report;
|
||||
if (!m_isPrepared || displacementChanged) {
|
||||
for (int centerIndex = 0; centerIndex < m_centerRows.size(); ++centerIndex) {
|
||||
const double value = displacement(m_centerRows.reduced_dofs()[centerIndex]);
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(value), "The centering constraint received a non-finite center displacement."
|
||||
);
|
||||
m_centerDisplacement(centerIndex) = value;
|
||||
}
|
||||
report.cachedCenterDisplacement = true;
|
||||
}
|
||||
|
||||
m_isPrepared = true;
|
||||
return report;
|
||||
}
|
||||
|
||||
void ApplyResidualRows(mfem::Vector &displacementResidual) const {
|
||||
VerifyPrepared();
|
||||
MFEM_VERIFY(
|
||||
displacementResidual.Size() == m_centerRows.field_size(),
|
||||
"The centering constraint received a displacement residual with the wrong size."
|
||||
);
|
||||
|
||||
for (int centerIndex = 0; centerIndex < m_centerRows.size(); ++centerIndex) {
|
||||
displacementResidual(m_centerRows.reduced_dofs()[centerIndex]) = m_centerDisplacement(centerIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyJacobianRows(
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &displacementAction
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
MFEM_VERIFY(
|
||||
displacementVariation.Size() == m_centerRows.field_size() &&
|
||||
displacementAction.Size() == m_centerRows.field_size(),
|
||||
"The centering constraint received a Jacobian vector with the wrong size."
|
||||
);
|
||||
|
||||
for (const int centerRow : m_centerRows.reduced_dofs()) {
|
||||
displacementAction(centerRow) = displacementVariation(centerRow);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept {
|
||||
return m_isPrepared;
|
||||
}
|
||||
|
||||
[[nodiscard]] const field::FieldPointDofMap &GetCenterRows() const noexcept {
|
||||
return m_centerRows;
|
||||
}
|
||||
|
||||
private:
|
||||
void VerifyPrepared() const {
|
||||
MFEM_VERIFY(m_isPrepared, "The centering constraint must be prepared before row application.");
|
||||
}
|
||||
|
||||
field::FieldPointDofMap m_centerRows;
|
||||
mfem::Vector m_centerDisplacement;
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
@@ -1,7 +1,9 @@
|
||||
module;
|
||||
|
||||
#include <compare>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
@@ -16,9 +18,11 @@ export import :operators.context.gravity_field;
|
||||
export import :operators.gravity_field;
|
||||
export import :operators.gravity_field_jacobian;
|
||||
export import :operators.prepared_barotropic_closure;
|
||||
export import :operators.prepared_centering_constraint;
|
||||
export import :operators.prepared_displacement_residual;
|
||||
export import :operators.prepared_hydrostatic_equilibrium;
|
||||
export import :operators.prepared_mass_normalization;
|
||||
export import :operators.prepared_surface_constraint;
|
||||
export import :physics.rigid_rotation;
|
||||
export import :utils.blocks;
|
||||
|
||||
@@ -50,11 +54,14 @@ export namespace mean_field::operators {
|
||||
PreparedHydrostaticEquilibriumReport hydrostatic;
|
||||
PreparedDisplacementResidualReport displacement;
|
||||
PreparedMassNormalizationReport massNormalization;
|
||||
PreparedSurfaceConstraintReport surfaceConstraint;
|
||||
PreparedCenteringConstraintReport centeringConstraint;
|
||||
bool assembledResidual{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyChildWork() const noexcept {
|
||||
return gravity.DidAnyWork() || barotropicClosure.DidAnyWork() || hydrostatic.DidAnyWork() ||
|
||||
displacement.DidAnyWork() || massNormalization.DidAnyWork();
|
||||
displacement.DidAnyWork() || massNormalization.DidAnyWork() || surfaceConstraint.DidAnyWork() ||
|
||||
centeringConstraint.DidAnyWork();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
@@ -74,19 +81,27 @@ export namespace mean_field::operators {
|
||||
|
||||
class PreparedStellarEquilibriumOperator final : public mfem::Operator {
|
||||
public:
|
||||
template <models::StellarModelType Model>
|
||||
requires std::same_as<
|
||||
typename std::remove_cvref_t<Model>::EquationOfStateType,
|
||||
eos::Polytrope> &&
|
||||
SingleFieldPressureSurfaceConstraintFor<
|
||||
typename std::remove_cvref_t<Model>::SurfaceConstraintType,
|
||||
field::Enthalpy> &&
|
||||
std::is_lvalue_reference_v<Model &&>
|
||||
PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
double targetMass
|
||||
);
|
||||
|
||||
PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
const models::StellarModel &stellarModel
|
||||
);
|
||||
Model &&stellarModel
|
||||
)
|
||||
: PreparedStellarEquilibriumOperator(
|
||||
f,
|
||||
domainMapper,
|
||||
stellarModel.equationOfState(),
|
||||
stellarModel.targetMass(),
|
||||
PressureSurfaceConstraintView{stellarModel.compiledSurfaceConstraint()}
|
||||
) {
|
||||
}
|
||||
|
||||
PreparedStellarEquilibriumOperator(const PreparedStellarEquilibriumOperator &) = delete;
|
||||
PreparedStellarEquilibriumOperator &operator=(const PreparedStellarEquilibriumOperator &) = delete;
|
||||
@@ -122,6 +137,8 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] const PreparedHydrostaticEquilibriumOperator &GetHydrostaticOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedDisplacementResidualOperator &GetDisplacementOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedMassNormalizationOperator &GetMassNormalizationOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedPressureSurfaceConstraint &GetSurfaceConstraintOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedCenteringConstraint &GetCenteringConstraintOperator() const noexcept;
|
||||
|
||||
private:
|
||||
struct ConstructionData;
|
||||
@@ -133,6 +150,15 @@ export namespace mean_field::operators {
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
double targetMass,
|
||||
PressureSurfaceConstraintView surfaceConstraint
|
||||
);
|
||||
|
||||
PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
double targetMass,
|
||||
PressureSurfaceConstraintView surfaceConstraint,
|
||||
ConstructionData constructionData
|
||||
);
|
||||
|
||||
@@ -150,6 +176,8 @@ export namespace mean_field::operators {
|
||||
PreparedHydrostaticEquilibriumOperator m_hydrostaticOperator;
|
||||
PreparedDisplacementResidualOperator m_displacementOperator;
|
||||
PreparedMassNormalizationOperator m_massNormalizationOperator;
|
||||
PreparedPressureSurfaceConstraint m_surfaceConstraintOperator;
|
||||
PreparedCenteringConstraint m_centeringConstraintOperator;
|
||||
|
||||
StellarEquilibriumDependencies m_preparedDependencies;
|
||||
mfem::Vector m_cachedResidual;
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
#include <concepts>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.prepared_surface_constraint;
|
||||
|
||||
export import :field.mfem;
|
||||
export import :surface.compiled;
|
||||
|
||||
namespace mean_field::operators::detail {
|
||||
template <eos::ThermodynamicQuantityType Quantity> struct SingleQuantitySurfaceState final {
|
||||
eos::QuantityValue<Quantity> quantityValue;
|
||||
|
||||
[[nodiscard]] eos::QuantityValue<Quantity> value(Quantity) const noexcept {
|
||||
return quantityValue;
|
||||
}
|
||||
};
|
||||
} // namespace mean_field::operators::detail
|
||||
|
||||
export namespace mean_field::operators {
|
||||
/*
|
||||
* Runtime enforcement currently supports a pointwise pressure constraint
|
||||
* whose row field is also its sole state field. The concept is expressed
|
||||
* entirely in compiled-constraint metadata: no thermodynamic carrier or
|
||||
* concrete field is selected by this prepared layer.
|
||||
*/
|
||||
template <typename Candidate>
|
||||
concept SingleFieldPressureSurfaceConstraint =
|
||||
requires {
|
||||
typename std::remove_cvref_t<Candidate>::PhysicalQuantity;
|
||||
typename std::remove_cvref_t<Candidate>::CarrierQuantity;
|
||||
typename std::remove_cvref_t<Candidate>::CarrierField;
|
||||
typename std::remove_cvref_t<Candidate>::SurfaceDependencies;
|
||||
} && std::same_as<typename std::remove_cvref_t<Candidate>::PhysicalQuantity, eos::quantity::Pressure> &&
|
||||
std::same_as<
|
||||
typename std::remove_cvref_t<Candidate>::SurfaceDependencies::RowField,
|
||||
typename std::remove_cvref_t<Candidate>::CarrierField> &&
|
||||
std::same_as<
|
||||
typename std::remove_cvref_t<Candidate>::SurfaceDependencies::StateFieldTypes,
|
||||
field::TypeList<typename std::remove_cvref_t<Candidate>::CarrierField>>;
|
||||
|
||||
template <typename Candidate, typename Field>
|
||||
concept SingleFieldPressureSurfaceConstraintFor =
|
||||
SingleFieldPressureSurfaceConstraint<Candidate> &&
|
||||
std::same_as<typename std::remove_cvref_t<Candidate>::SurfaceDependencies::RowField, Field>;
|
||||
|
||||
/*
|
||||
* Non-owning runtime bridge for a statically compiled pressure constraint.
|
||||
* There is one function-pointer dispatch per complete row application;
|
||||
* the concrete loop remains templated so EOS operations can be inlined.
|
||||
*/
|
||||
class PressureSurfaceConstraintView final {
|
||||
public:
|
||||
template <SingleFieldPressureSurfaceConstraint Constraint>
|
||||
explicit PressureSurfaceConstraintView(const Constraint &constraint) noexcept
|
||||
: m_constraint(std::addressof(constraint)),
|
||||
m_applyResidualRows(&applyResidualRows<Constraint>),
|
||||
m_applyJacobianRows(&applyJacobianRows<Constraint>),
|
||||
m_descriptor(constraint.descriptor()) {
|
||||
}
|
||||
|
||||
void ApplyResidualRows(
|
||||
const mfem::Vector &surfaceState,
|
||||
const field::FieldBoundaryDofMap &surfaceRows,
|
||||
mfem::Vector &rowResidual
|
||||
) const {
|
||||
m_applyResidualRows(m_constraint, surfaceState, surfaceRows, rowResidual);
|
||||
}
|
||||
|
||||
void ApplyJacobianRows(
|
||||
const mfem::Vector &surfaceState,
|
||||
const field::FieldBoundaryDofMap &surfaceRows,
|
||||
const mfem::Vector &stateVariation,
|
||||
mfem::Vector &rowAction
|
||||
) const {
|
||||
m_applyJacobianRows(m_constraint, surfaceState, surfaceRows, stateVariation, rowAction);
|
||||
}
|
||||
|
||||
[[nodiscard]] surface::PressureSurfaceDescriptor descriptor() const noexcept {
|
||||
return m_descriptor;
|
||||
}
|
||||
|
||||
private:
|
||||
using ApplyResidualRowsFunction = void (*)(
|
||||
const void *,
|
||||
const mfem::Vector &,
|
||||
const field::FieldBoundaryDofMap &,
|
||||
mfem::Vector &
|
||||
);
|
||||
using ApplyJacobianRowsFunction = void (*)(
|
||||
const void *,
|
||||
const mfem::Vector &,
|
||||
const field::FieldBoundaryDofMap &,
|
||||
const mfem::Vector &,
|
||||
mfem::Vector &
|
||||
);
|
||||
|
||||
template <SingleFieldPressureSurfaceConstraint Constraint>
|
||||
static void applyResidualRows(
|
||||
const void *constraint,
|
||||
const mfem::Vector &surfaceState,
|
||||
const field::FieldBoundaryDofMap &surfaceRows,
|
||||
mfem::Vector &rowResidual
|
||||
) {
|
||||
using CarrierQuantity = typename Constraint::CarrierQuantity;
|
||||
|
||||
for (int surfaceIndex = 0; surfaceIndex < surfaceRows.size(); ++surfaceIndex) {
|
||||
const detail::SingleQuantitySurfaceState<CarrierQuantity> state{
|
||||
eos::QuantityValue<CarrierQuantity>{surfaceState(surfaceIndex)}
|
||||
};
|
||||
rowResidual(surfaceRows.reduced_dofs()[surfaceIndex]) =
|
||||
static_cast<const Constraint *>(constraint)->residual(state);
|
||||
}
|
||||
}
|
||||
|
||||
template <SingleFieldPressureSurfaceConstraint Constraint>
|
||||
static void applyJacobianRows(
|
||||
const void *constraint,
|
||||
const mfem::Vector &surfaceState,
|
||||
const field::FieldBoundaryDofMap &surfaceRows,
|
||||
const mfem::Vector &stateVariation,
|
||||
mfem::Vector &rowAction
|
||||
) {
|
||||
using CarrierQuantity = typename Constraint::CarrierQuantity;
|
||||
|
||||
for (int surfaceIndex = 0; surfaceIndex < surfaceRows.size(); ++surfaceIndex) {
|
||||
const int reducedDof = surfaceRows.reduced_dofs()[surfaceIndex];
|
||||
const detail::SingleQuantitySurfaceState<CarrierQuantity> state{
|
||||
eos::QuantityValue<CarrierQuantity>{surfaceState(surfaceIndex)}
|
||||
};
|
||||
const detail::SingleQuantitySurfaceState<CarrierQuantity> variation{
|
||||
eos::QuantityValue<CarrierQuantity>{stateVariation(reducedDof)}
|
||||
};
|
||||
rowAction(reducedDof) = static_cast<const Constraint *>(constraint)->jacobianAction(state, variation);
|
||||
}
|
||||
}
|
||||
|
||||
const void *m_constraint;
|
||||
ApplyResidualRowsFunction m_applyResidualRows;
|
||||
ApplyJacobianRowsFunction m_applyJacobianRows;
|
||||
surface::PressureSurfaceDescriptor m_descriptor;
|
||||
};
|
||||
|
||||
struct PreparedSurfaceConstraintReport final {
|
||||
bool cachedSurfaceState{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return cachedSurfaceState;
|
||||
}
|
||||
};
|
||||
|
||||
class PreparedPressureSurfaceConstraint final {
|
||||
public:
|
||||
PreparedPressureSurfaceConstraint(
|
||||
field::FieldBoundaryDofMap surfaceRows,
|
||||
const PressureSurfaceConstraintView constraint
|
||||
)
|
||||
: m_surfaceRows(std::move(surfaceRows)),
|
||||
m_constraint(constraint),
|
||||
m_surfaceState(m_surfaceRows.size()) {
|
||||
}
|
||||
|
||||
[[nodiscard]] PreparedSurfaceConstraintReport Prepare(
|
||||
const mfem::Vector &reducedState,
|
||||
const bool stateChanged
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
reducedState.Size() == m_surfaceRows.field_size(),
|
||||
"The pressure surface constraint received a state vector with the wrong size."
|
||||
);
|
||||
|
||||
PreparedSurfaceConstraintReport report;
|
||||
if (!m_isPrepared || stateChanged) {
|
||||
for (int surfaceIndex = 0; surfaceIndex < m_surfaceRows.size(); ++surfaceIndex) {
|
||||
const double value = reducedState(m_surfaceRows.reduced_dofs()[surfaceIndex]);
|
||||
MFEM_VERIFY(std::isfinite(value), "The pressure surface constraint received non-finite state.");
|
||||
m_surfaceState(surfaceIndex) = value;
|
||||
}
|
||||
report.cachedSurfaceState = true;
|
||||
}
|
||||
|
||||
m_isPrepared = true;
|
||||
return report;
|
||||
}
|
||||
|
||||
void ApplyResidualRows(mfem::Vector &rowResidual) const {
|
||||
VerifyPrepared();
|
||||
MFEM_VERIFY(
|
||||
rowResidual.Size() == m_surfaceRows.field_size(),
|
||||
"The pressure surface constraint received a residual vector with the wrong size."
|
||||
);
|
||||
m_constraint.ApplyResidualRows(m_surfaceState, m_surfaceRows, rowResidual);
|
||||
}
|
||||
|
||||
void ApplyJacobianRows(
|
||||
const mfem::Vector &stateVariation,
|
||||
mfem::Vector &rowAction
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
MFEM_VERIFY(
|
||||
stateVariation.Size() == m_surfaceRows.field_size() && rowAction.Size() == m_surfaceRows.field_size(),
|
||||
"The pressure surface constraint received a Jacobian vector with the wrong size."
|
||||
);
|
||||
m_constraint.ApplyJacobianRows(m_surfaceState, m_surfaceRows, stateVariation, rowAction);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept {
|
||||
return m_isPrepared;
|
||||
}
|
||||
|
||||
[[nodiscard]] const field::FieldBoundaryDofMap &GetSurfaceRows() const noexcept {
|
||||
return m_surfaceRows;
|
||||
}
|
||||
|
||||
[[nodiscard]] surface::PressureSurfaceDescriptor GetPhysicalCondition() const noexcept {
|
||||
return m_constraint.descriptor();
|
||||
}
|
||||
|
||||
private:
|
||||
void VerifyPrepared() const {
|
||||
MFEM_VERIFY(m_isPrepared, "The pressure surface constraint must be prepared before row application.");
|
||||
}
|
||||
|
||||
field::FieldBoundaryDofMap m_surfaceRows;
|
||||
PressureSurfaceConstraintView m_constraint;
|
||||
mfem::Vector m_surfaceState;
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
Reference in New Issue
Block a user