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

@@ -12,7 +12,8 @@ export namespace mean_field::analysis {
const fem::FEM &fem,
const mfem::GridFunction &gf,
utils::DOMAINS domain = utils::DOMAINS::ALL,
mapping::COORDINATE_SPACE coord_space = mapping::COORDINATE_SPACE::PHYSICAL
mapping::COORDINATE_SPACE coord_space =
mapping::COORDINATE_SPACE::PHYSICAL
);
mfem::Vector get_com(
@@ -32,10 +33,9 @@ export namespace mean_field::analysis {
);
double get_mesh_volume(
const fem::FEM& fem,
mapping::COORDINATE_SPACE coordinate_space = mapping::COORDINATE_SPACE::PHYSICAL,
const fem::FEM &fem,
mapping::COORDINATE_SPACE coordinate_space =
mapping::COORDINATE_SPACE::PHYSICAL,
utils::DOMAINS domain = utils::DOMAINS::STELLAR
);
}
} // namespace mean_field::analysis

View File

@@ -5,20 +5,19 @@ export module mean_field:boundary.contexts;
export namespace mean_field::boundary {
struct BoundaryContext {
mfem::Array<int> inf_bounds;
mfem::Array<int> stellar_bounds;
mfem::Array<int> inf_bounds;
mfem::Array<int> stellar_bounds;
};
enum class Boundaries : uint8_t {
STELLAR_SURFACE = 1,
INF_SURFACE = 2
};
enum class Boundaries : uint8_t { STELLAR_SURFACE = 1, INF_SURFACE = 2 };
int operator-(
Boundaries b,
const int a
) {
return static_cast<int>(static_cast<uint8_t>(b) - static_cast<uint8_t>(a));
return static_cast<int>(
static_cast<uint8_t>(b) - static_cast<uint8_t>(a)
);
}
struct Bounds {
@@ -26,9 +25,6 @@ export namespace mean_field::boundary {
double r_inf_ref;
};
enum BoundsError : uint8_t {
CANNOT_FIND_VACUUM
};
enum BoundsError : uint8_t { CANNOT_FIND_VACUUM };
}
} // namespace mean_field::boundary

View File

@@ -1,95 +1,183 @@
module;
#include <stroid/stroid.h>
#include <memory>
#include <string>
#include <mfem.hpp>
#include <stroid/stroid.h>
export module mean_field:fem;
export import :physics.contexts;
export import :boundary.contexts;
export import :mapping.domain_mapper;
export import :utils.misc;
export import :utils.user;
export import :quadrature.mfem;
export import :field.mfem;
export namespace mean_field::fem {
using GravityField = field::Field<field::Gravity>;
using DisplacementField = field::Field<field::Displacement>;
using DensityField = field::Field<field::Density>;
using EnthalpyField = field::Field<field::Enthalpy>;
struct FEM {
// =====================================================================
// Mesh
// =====================================================================
stroid::StroidMesh smesh;
std::unique_ptr<mfem::ParMesh> mesh;
// =====================================
// 2. Finite Element Collections
// =====================================
// H1 (Continuous): For Gravitational Potential (Phi) and Velocity (v)
std::unique_ptr<mfem::FiniteElementCollection> H1_fec;
// =====================================================================
// Compile-time field descriptors
// =====================================================================
// L2 (Discontinuous): For Density (rho) to fix O-grid boundary scalloping
std::unique_ptr<mfem::FiniteElementCollection> L2_fec;
GravityField gravityField;
DisplacementField displacementField;
DensityField densityField;
EnthalpyField enthalpyField;
// H(div)/RT space for gravitational field
std::unique_ptr<mfem::RT_FECollection> RT_fec;
// =====================================================================
// Gravity field
//
// Collection members are declared before their corresponding spaces so
// that the spaces are destroyed first.
// =====================================================================
std::unique_ptr<mfem::FiniteElementCollection> gravityPotentialFec;
// =====================================
// 3. Finite Element Spaces
// =====================================
std::unique_ptr<mfem::ParFiniteElementSpace> H1_fes; // Scalar continuous (Gravity)
std::unique_ptr<mfem::ParFiniteElementSpace> Vec_H1_fes; // Vector continuous (Velocity field)
std::unique_ptr<mfem::ParFiniteElementSpace> L2_fes; // Scalar discontinuous (Density)
std::unique_ptr<mfem::ParFiniteElementSpace> RT_fes; // H(div)/RT space for gravitational field
std::unique_ptr<mfem::ParFiniteElementSpace> gravityPotentialFes;
// Preconditioning for Gravity
std::unique_ptr<mfem::ParLORDiscretization> H1_lor_disc;
const mfem::ParFiniteElementSpace *H1_lor_fes{nullptr};
std::unique_ptr<mfem::FiniteElementCollection> gravityFluxFec;
std::unique_ptr<mfem::ParFiniteElementSpace> gravityFluxFes;
// =====================================================================
// Displacement field
// =====================================================================
std::unique_ptr<mfem::FiniteElementCollection> displacementFec;
std::unique_ptr<mfem::ParFiniteElementSpace> displacementFes;
std::unique_ptr<mfem::ParGridFunction> displacement;
// =====================================================================
// Density field
// =====================================================================
std::unique_ptr<mfem::FiniteElementCollection> densityFec;
std::unique_ptr<mfem::ParFiniteElementSpace> densityFes;
// =====================================================================
// Specific-enthalpy field
// =====================================================================
std::unique_ptr<mfem::FiniteElementCollection> enthalpyFec;
std::unique_ptr<mfem::ParFiniteElementSpace> enthalpyFes;
// =====================================================================
// Compactification coordinate
// =====================================================================
std::unique_ptr<mfem::H1_FECollection> compactificationFec;
std::unique_ptr<mfem::ParFiniteElementSpace> compactificationFes;
std::unique_ptr<mfem::ParGridFunction> compactificationCoordinate;
// =====================================================================
// Domain mapping
//
// These are declared after displacement so that they are destroyed
// before the displacement grid function to which mapping may refer.
// DomainMapper is retained only for legacy integrators. New operators
// use DomainMapperStateless exclusively.
// =====================================================================
// =====================================
// 4. Domain Mapping
// =====================================
std::unique_ptr<mapping::DomainMapper> mapping;
// =====================================
// 5. Global System Tracking
// =====================================
// [ Velocity | Density | Mapping Parameters (Surface) ]
mfem::Array<int> block_true_offsets;
std::unique_ptr<mapping::DomainMapperStateless> domainMapperStateless;
mfem::Array<int> gravity_block_true_offsets;
// =====================================================================
// Block layouts
//
// These arrays are retained only for legacy code. Canonical operator
// layouts are defined by the compile-time forms in :utils.blocks.
//
// Main system: [Displacement | Density]
// Gravity system: [Flux | Potential]
// =====================================================================
// Essential Boundary Conditions for the fluid (e.g., surface stress-free)
mfem::Array<int> ess_v_tdofs;
mfem::Array<int> blockTrueOffsets;
mfem::Array<int> gravityBlockTrueOffsets;
// Elements entirely in the vacuum domain where fluid equations are not solved
mfem::Array<int> vacuum_tdof_rho;
mfem::Array<int> vacuum_tdof_v;
// =====================================================================
// Boundary conditions and domain masks
// =====================================================================
mfem::Array<int> essentialDisplacementTdofs;
mfem::Array<int> vacuumDensityTdofs;
mfem::Array<int> vacuumEnthalpyTdofs;
mfem::Array<int> vacuumDisplacementTdofs;
// =====================================================================
// Global diagnostics
// =====================================================================
// =====================================
// 6. Multiphysics State & Integration
// =====================================
mfem::Vector com;
mfem::DenseMatrix Q;
int int_order{3};
std::unique_ptr<mfem::IntegrationRule> int_rule;
// =====================================================================
// Physics and boundary contexts
// =====================================================================
physics::GravityContext gravity_context;
boundary::BoundaryContext boundary_context;
physics::GravityContext gravityContext;
boundary::BoundaryContext boundaryContext;
std::unique_ptr<quadrature::RuleFactory> quadrature_factory;
std::unique_ptr<quadrature::RuleFactory> quadratureFactory;
// =====================================================================
// Validation
// =====================================================================
// =====================================
// 7. Utilities
// =====================================
[[nodiscard]] bool okay() const {
return (mesh != nullptr) &&
(H1_fec != nullptr) && (L2_fec != nullptr) && (RT_fec != nullptr) &&
(H1_fes != nullptr) && (Vec_H1_fes != nullptr) && (L2_fes != nullptr) && (RT_fes != nullptr);
return mesh != nullptr &&
gravityPotentialFec != nullptr &&
gravityPotentialFes != nullptr &&
gravityFluxFec != nullptr && gravityFluxFes != nullptr &&
displacementFec != nullptr && displacementFes != nullptr &&
displacement != nullptr &&
densityFec != nullptr && densityFes != nullptr &&
enthalpyFec != nullptr && enthalpyFes != nullptr &&
compactificationFec != nullptr &&
compactificationFes != nullptr &&
compactificationCoordinate != nullptr &&
mapping != nullptr && domainMapperStateless != nullptr &&
quadratureFactory != nullptr &&
blockTrueOffsets.Size() == 3 &&
gravityBlockTrueOffsets.Size() == 3;
}
[[nodiscard]] bool has_mapping() const { return mapping != nullptr; }
[[nodiscard]] bool has_mapping() const {
return mapping != nullptr;
}
};
FEM setup_fem(const std::string &filename, const utils::Args &args, int extra_refine = 0);
}
FEM setup_fem(
const std::string &filename,
const utils::Args &args,
int extraRefine = 0
);
} // namespace mean_field::fem

View File

@@ -0,0 +1,356 @@
module;
#include <concepts>
#include <cstddef>
#include <string_view>
#include <type_traits>
export module mean_field:field.base;
export namespace mean_field::field {
template <typename... Ts> struct TypeList { };
template <typename T, typename ListT> struct TypeListContains;
template <typename T, typename... Ts>
struct TypeListContains<T, TypeList<Ts...>>
: std::bool_constant<(std::same_as<T, Ts> || ...)> { };
template <typename T, typename ListT>
inline constexpr bool typeListContains = TypeListContains<T, ListT>::value;
enum class StorageKind { finite_element, global_scalar };
inline constexpr int dynamicBlockSize = -1;
// -------------------------------------------------------------------------
// Function-space tags
// -------------------------------------------------------------------------
struct L2 {
static constexpr std::string_view name = "L2";
};
struct H1 {
static constexpr std::string_view name = "H1";
};
struct RT {
static constexpr std::string_view name = "RT";
};
struct ND {
static constexpr std::string_view name = "ND";
};
template <typename SpaceT>
concept SpaceTag = std::same_as<SpaceT, L2> || std::same_as<SpaceT, H1> ||
std::same_as<SpaceT, RT> || std::same_as<SpaceT, ND>;
template <SpaceTag SpaceT, int RankV>
inline constexpr bool spaceSupportsRank =
(std::same_as<SpaceT, H1> && (RankV == 0 || RankV == 1)) ||
(std::same_as<SpaceT, L2> && (RankV == 0 || RankV == 1)) ||
(std::same_as<SpaceT, RT> && RankV == 1) ||
(std::same_as<SpaceT, ND> && RankV == 1);
// -------------------------------------------------------------------------
// Discretization descriptors
//
// familyOrder is the order passed to the backend's FE collection
// constructor. It is deliberately not called polynomialOrder because those
// values differ for some spaces, notably Raviart-Thomas elements in MFEM.
// -------------------------------------------------------------------------
template <SpaceTag SpaceT, int FamilyOrderV> struct Disc {
using Space = SpaceT;
static constexpr int familyOrder = FamilyOrderV;
static_assert(
FamilyOrderV >= 0,
"Finite-element family order must be non-negative."
);
};
template <typename T>
concept DiscretizationTag = requires {
typename T::Space;
{ T::familyOrder } -> std::convertible_to<int>;
} && SpaceTag<typename T::Space>;
// -------------------------------------------------------------------------
// Physical relations between quantities
// -------------------------------------------------------------------------
struct FieldRelation {
struct Independent { };
template <typename SourceT> struct Gradient {
using Source = SourceT;
};
template <typename SourceT> struct Divergence {
using Source = SourceT;
};
template <typename SourceT> struct Curl {
using Source = SourceT;
};
};
template <typename T> struct IsGradient : std::false_type { };
template <typename T> struct IsDivergence : std::false_type { };
template <typename T> struct IsCurl : std::false_type { };
template <typename SourceT>
struct IsGradient<FieldRelation::Gradient<SourceT>> : std::true_type { };
template <typename SourceT>
struct IsDivergence<FieldRelation::Divergence<SourceT>> : std::true_type {
};
template <typename SourceT>
struct IsCurl<FieldRelation::Curl<SourceT>> : std::true_type { };
template <typename RelationT>
concept ValidRelation =
std::same_as<RelationT, FieldRelation::Independent> ||
IsGradient<RelationT>::value || IsDivergence<RelationT>::value ||
IsCurl<RelationT>::value;
template <typename RelationT> struct RelationTarget {
using Type = void;
};
template <typename SourceT>
struct RelationTarget<FieldRelation::Gradient<SourceT>> {
using Type = SourceT;
};
template <typename SourceT>
struct RelationTarget<FieldRelation::Divergence<SourceT>> {
using Type = SourceT;
};
template <typename SourceT>
struct RelationTarget<FieldRelation::Curl<SourceT>> {
using Type = SourceT;
};
template <typename QuantityT>
using RelationTargetT =
typename RelationTarget<typename QuantityT::Relation>::Type;
// -------------------------------------------------------------------------
// Field quantities
// -------------------------------------------------------------------------
template <int RankV, ValidRelation RelationT, DiscretizationTag DiscT>
struct Quantity {
using Relation = RelationT;
using Discretization = DiscT;
using Space = typename DiscT::Space;
static constexpr int rankValue = RankV;
static constexpr int familyOrder = DiscT::familyOrder;
static constexpr StorageKind storageKind = StorageKind::finite_element;
static constexpr int staticBlockSize = dynamicBlockSize;
static_assert(
RankV >= 0,
"A field quantity cannot have a negative tensor rank."
);
static_assert(
spaceSupportsRank<
Space,
RankV>,
"This function space cannot represent a quantity of this rank."
);
};
template <ValidRelation RelationT, DiscretizationTag DiscT>
using ScalarQ = Quantity<0, RelationT, DiscT>;
template <ValidRelation RelationT, DiscretizationTag DiscT>
using VectorQ = Quantity<1, RelationT, DiscT>;
struct GlobalScalarQ {
using Relation = FieldRelation::Independent;
static constexpr int rankValue = 0;
static constexpr StorageKind storageKind = StorageKind::global_scalar;
static constexpr int staticBlockSize = 1;
};
template <typename T>
concept FieldQuantity =
requires {
typename T::Relation;
typename T::Discretization;
typename T::Space;
{ T::rankValue } -> std::convertible_to<int>;
{ T::familyOrder } -> std::convertible_to<int>;
{ T::storageKind } -> std::convertible_to<StorageKind>;
{ T::staticBlockSize } -> std::convertible_to<int>;
} && SpaceTag<typename T::Space> &&
T::storageKind == StorageKind::finite_element;
template <typename T>
concept GlobalScalarQuantity =
requires {
typename T::Relation;
{ T::rankValue } -> std::convertible_to<int>;
{ T::storageKind } -> std::convertible_to<StorageKind>;
{ T::staticBlockSize } -> std::convertible_to<int>;
} && T::rankValue == 0 &&
T::storageKind == StorageKind::global_scalar && T::staticBlockSize == 1;
template <typename T>
concept RegisteredQuantity = FieldQuantity<T> || GlobalScalarQuantity<T>;
template <typename QuantityT>
concept DerivedQuantity = FieldQuantity<QuantityT> &&
(!std::same_as<RelationTargetT<QuantityT>, void>);
// -------------------------------------------------------------------------
// Compile-time discretization constraints
// -------------------------------------------------------------------------
template <FieldQuantity FluxT, FieldQuantity PotentialT>
struct RtL2StablePair {
static consteval void validate() {
static_assert(
std::same_as<typename FluxT::Space, RT>,
"The flux in an RT/L2 pair must use Raviart-Thomas elements."
);
static_assert(
std::same_as<typename PotentialT::Space, L2>,
"The potential in an RT/L2 pair must use L2 elements."
);
static_assert(
FluxT::rankValue == 1,
"The flux in an RT/L2 pair must be vector-valued."
);
static_assert(
PotentialT::rankValue == 0,
"The potential in an RT/L2 pair must be scalar-valued."
);
static_assert(
FluxT::familyOrder == PotentialT::familyOrder,
"The MFEM RT and L2 family orders must match."
);
}
};
template <typename... ConstraintTs>
consteval bool validate_constraints(TypeList<ConstraintTs...>) {
(ConstraintTs::validate(), ...);
return true;
}
// -------------------------------------------------------------------------
// Operations applied to quantities inside weak forms
//
// These describe the mathematics. Backend-specific polynomial-order rules
// are provided by field.mfem.
// -------------------------------------------------------------------------
struct FieldOperation {
struct Value { };
struct Gradient { };
struct Divergence { };
struct Curl { };
struct NormalTrace { };
};
template <typename OperationT>
concept FieldOperationTag =
std::same_as<OperationT, FieldOperation::Value> ||
std::same_as<OperationT, FieldOperation::Gradient> ||
std::same_as<OperationT, FieldOperation::Divergence> ||
std::same_as<OperationT, FieldOperation::Curl> ||
std::same_as<OperationT, FieldOperation::NormalTrace>;
template <
RegisteredQuantity QuantityT,
FieldOperationTag OperationT = FieldOperation::Value>
struct Operand {
using Quantity = QuantityT;
using Operation = OperationT;
static_assert(
FieldQuantity<QuantityT> || std::same_as<
OperationT,
FieldOperation::Value>,
"Global scalar quantities support only the value operation."
);
};
template <typename T>
concept FieldOperand =
requires {
typename T::Quantity;
typename T::Operation;
} && RegisteredQuantity<typename T::Quantity> &&
FieldOperationTag<typename T::Operation>;
// -------------------------------------------------------------------------
// Weak-form descriptions
//
// PolicyKeyV associates the form with a runtime quadrature-policy key.
//
// DynamicOrderCountV is the number of polynomial-order contributions that
// cannot yet be derived from registered quantities. For example, a source
// coefficient supplied at runtime contributes one dynamic order.
// -------------------------------------------------------------------------
template <
auto PolicyKeyV,
std::size_t DynamicOrderCountV,
FieldOperand... OperandTs>
struct FormSpec {
static constexpr auto policyKey = PolicyKeyV;
static constexpr std::size_t dynamicOrderCount = DynamicOrderCountV;
using Operands = TypeList<OperandTs...>;
};
template <typename T>
concept FieldForm = requires {
typename T::Operands;
T::policyKey;
{ T::dynamicOrderCount } -> std::convertible_to<std::size_t>;
};
template <typename ListT>
struct IsRegisteredQuantityList : std::false_type { };
template <RegisteredQuantity... QuantityTs>
struct IsRegisteredQuantityList<TypeList<QuantityTs...>> : std::true_type {
};
template <typename ListT>
inline constexpr bool isRegisteredQuantityList =
IsRegisteredQuantityList<ListT>::value;
template <typename ListT> struct IsFieldFormList : std::false_type { };
template <FieldForm... FormTs>
struct IsFieldFormList<TypeList<FormTs...>> : std::true_type { };
template <typename ListT>
inline constexpr bool isFieldFormList = IsFieldFormList<ListT>::value;
} // namespace mean_field::field

View File

@@ -0,0 +1,380 @@
module;
#include <array>
#include <concepts>
#include <cstddef>
#include <memory>
#include <stdexcept>
#include <mfem.hpp>
export module mean_field:field.mfem;
export import :field.registry;
namespace mean_field::field::detail {
template <typename T> inline constexpr bool alwaysFalse = false;
// -------------------------------------------------------------------------
// MFEM finite-element collection construction
// -------------------------------------------------------------------------
template <typename SpaceT> struct FecFor;
template <> struct FecFor<L2> {
static std::unique_ptr<mfem::FiniteElementCollection> make(
int familyOrder,
int dimension
) {
return std::make_unique<mfem::L2_FECollection>(
familyOrder, dimension
);
}
};
template <> struct FecFor<H1> {
static std::unique_ptr<mfem::FiniteElementCollection> make(
int familyOrder,
int dimension
) {
return std::make_unique<mfem::H1_FECollection>(
familyOrder, dimension
);
}
};
template <> struct FecFor<RT> {
static std::unique_ptr<mfem::FiniteElementCollection> make(
int familyOrder,
int dimension
) {
return std::make_unique<mfem::RT_FECollection>(
familyOrder, dimension
);
}
};
template <> struct FecFor<ND> {
static std::unique_ptr<mfem::FiniteElementCollection> make(
int familyOrder,
int dimension
) {
return std::make_unique<mfem::ND_FECollection>(
familyOrder, dimension
);
}
};
// -------------------------------------------------------------------------
// MFEM polynomial-order interpretation
//
// familyOrder is the collection constructor argument.
//
// For RT_p:
// value order = p + 1
// divergence order = p
// normal-trace order = p
//
// This distinction is what allows Disc<RT, p> and Disc<L2, p> to form a
// compatible pair while still giving different value-shape orders.
// -------------------------------------------------------------------------
template <typename OperandT> struct MfemOperandOrder;
template <RegisteredQuantity QuantityT, FieldOperationTag OperationT>
struct MfemOperandOrder<Operand<QuantityT, OperationT>> {
static constexpr int orderValue = []() consteval {
if constexpr (GlobalScalarQuantity<QuantityT>) {
static_assert(
std::same_as<OperationT, FieldOperation::Value>,
"Global scalars support only the value operation."
);
return 0;
} else {
using Space = typename QuantityT::Space;
constexpr int familyOrder = QuantityT::familyOrder;
if constexpr (std::same_as<OperationT, FieldOperation::Value>) {
if constexpr (std::same_as<Space, RT>) {
return familyOrder + 1;
} else {
return familyOrder;
}
} else if constexpr (
std::same_as<OperationT, FieldOperation::Divergence>
) {
static_assert(
std::same_as<Space, RT>,
"Only RT quantities currently support the divergence "
"polynomial-order rule."
);
return familyOrder;
} else if constexpr (
std::same_as<OperationT, FieldOperation::Gradient>
) {
static_assert(
std::same_as<Space, H1>,
"Only H1 quantities currently support the gradient "
"polynomial-order rule."
);
return familyOrder > 0 ? familyOrder - 1 : 0;
} else if constexpr (
std::same_as<OperationT, FieldOperation::Curl>
) {
static_assert(
std::same_as<Space, ND>,
"Only ND quantities currently support the curl "
"polynomial-order rule."
);
return familyOrder > 0 ? familyOrder - 1 : 0;
} else if constexpr (
std::same_as<OperationT, FieldOperation::NormalTrace>
) {
static_assert(
std::same_as<Space, RT>,
"Only RT quantities currently support the normal-trace "
"polynomial-order rule."
);
return familyOrder;
} else {
static_assert(
alwaysFalse<OperationT>,
"Unsupported MFEM field operation."
);
}
}
}();
};
// -------------------------------------------------------------------------
// Static polynomial-order contribution of an entire form
// -------------------------------------------------------------------------
template <typename FormT> struct MfemFormOrder;
template <
auto PolicyKeyV,
std::size_t DynamicOrderCountV,
FieldOperand... OperandTs>
struct MfemFormOrder<
FormSpec<PolicyKeyV, DynamicOrderCountV, OperandTs...>> {
static constexpr int staticOrder =
(MfemOperandOrder<OperandTs>::orderValue + ... + 0);
};
// -------------------------------------------------------------------------
// MFEM vector-dimension and ordering rules
//
// Vector H1/L2 fields are represented using multiple copies of a scalar
// finite-element space. RT and ND elements are intrinsically vector-valued
// and therefore use vdim = 1.
// -------------------------------------------------------------------------
template <FieldQuantity QuantityT> int get_vdim(int spaceDimension) {
if (spaceDimension <= 0) {
throw std::invalid_argument("Space dimension must be positive.");
}
if constexpr (QuantityT::rankValue == 0) {
return 1;
} else if constexpr (
std::same_as<typename QuantityT::Space, H1> ||
std::same_as<typename QuantityT::Space, L2>
) {
return spaceDimension;
} else {
return 1;
}
}
template <FieldQuantity QuantityT>
constexpr mfem::Ordering::Type get_ordering() {
if constexpr (
QuantityT::rankValue == 1 &&
(std::same_as<typename QuantityT::Space, H1> ||
std::same_as<typename QuantityT::Space, L2>)
) {
return mfem::Ordering::byVDIM;
} else {
return mfem::Ordering::byNODES;
}
}
// -------------------------------------------------------------------------
// Quantity-specific MFEM realization
//
// Backend choices that are part of a field definition live here rather
// than leaking into FEM setup or call sites.
// -------------------------------------------------------------------------
template <FieldQuantity QuantityT> struct MfemQuantityTraits {
static std::unique_ptr<mfem::FiniteElementCollection>
make_fec(int dimension) {
return FecFor<typename QuantityT::Space>::make(
QuantityT::familyOrder, dimension
);
}
static constexpr mfem::Ordering::Type ordering =
get_ordering<QuantityT>();
};
template <> struct MfemQuantityTraits<Gravity::Flux> {
static std::unique_ptr<mfem::FiniteElementCollection>
make_fec(int dimension) {
return std::make_unique<mfem::RT_FECollection>(
Gravity::Flux::familyOrder, dimension,
mfem::BasisType::GaussLobatto, mfem::BasisType::IntegratedGLL
);
}
static constexpr mfem::Ordering::Type ordering =
mfem::Ordering::byNODES;
};
template <> struct MfemQuantityTraits<Displacement::Vector> {
static std::unique_ptr<mfem::FiniteElementCollection>
make_fec(int dimension) {
return FecFor<H1>::make(
Displacement::Vector::familyOrder, dimension
);
}
static constexpr mfem::Ordering::Type ordering =
mfem::Ordering::byNODES;
};
} // namespace mean_field::field::detail
export namespace mean_field::field {
// -------------------------------------------------------------------------
// User-facing field type
//
// The object itself is currently a zero-cost compile-time descriptor:
//
// Field<Gravity> gravityField;
//
// MFEM construction and typed quadrature-query generation are provided as
// static operations. Runtime ownership can later be added without changing
// Gravity, Displacement, or their form definitions.
// -------------------------------------------------------------------------
template <FieldTag TagT> class Field {
public:
using Tag = TagT;
// ---------------------------------------------------------------------
// MFEM finite-element collection construction
// ---------------------------------------------------------------------
template <FieldQuantity QuantityT>
requires typeListContains<
QuantityT,
typename TagT::Quantities>
static std::unique_ptr<mfem::FiniteElementCollection>
make_fec(int dimension) {
if (dimension <= 0) {
throw std::invalid_argument("Mesh dimension must be positive.");
}
return detail::MfemQuantityTraits<QuantityT>::make_fec(dimension);
}
// ---------------------------------------------------------------------
// MFEM parallel finite-element space construction
//
// The finite-element collection must outlive the returned space.
// ---------------------------------------------------------------------
template <FieldQuantity QuantityT>
requires typeListContains<
QuantityT,
typename TagT::Quantities>
static std::unique_ptr<mfem::ParFiniteElementSpace> make_fespace(
mfem::ParMesh &mesh,
mfem::FiniteElementCollection &finiteElementCollection
) {
return std::make_unique<mfem::ParFiniteElementSpace>(
&mesh, &finiteElementCollection,
detail::get_vdim<QuantityT>(mesh.SpaceDimension()),
detail::MfemQuantityTraits<QuantityT>::ordering
);
}
// ---------------------------------------------------------------------
// Typed quadrature-query construction
//
// geometryWeightOrder is supplied at runtime because it depends on the
// actual element transformation.
//
// dynamicOrders contains the form-specific polynomial orders that are
// not represented by registered compile-time quantities.
//
// Examples:
//
// Density::Form::CenterOfMass:
// { positionOrder }
//
// Gravity source forms need no dynamic orders because density and
// potential are both registered quantities.
//
// The completed base order is stored in Query::base_order, so Policy
// does not need to understand divergence, RT conventions, or individual
// field layouts.
// ---------------------------------------------------------------------
template <FieldForm FormT>
requires typeListContains<
FormT,
typename TagT::FormList>
static constexpr quadrature::Query make_query(
quadrature::QuadratureRole role,
int geometryWeightOrder,
std::array<
int,
FormT::dynamicOrderCount> dynamicOrders = {},
utils::DOMAINS domain = utils::DOMAINS::ALL,
quadrature::MappingKind mapping = quadrature::MappingKind::none
) {
if (geometryWeightOrder < 0) {
throw std::invalid_argument(
"Geometry weight order cannot be negative."
);
}
int baseOrder =
detail::MfemFormOrder<FormT>::staticOrder + geometryWeightOrder;
for (const int dynamicOrder : dynamicOrders) {
if (dynamicOrder < 0) {
throw std::invalid_argument(
"Dynamic polynomial orders cannot be negative."
);
}
baseOrder += dynamicOrder;
}
return {
.term = FormT::policyKey,
.role = role,
.domain = domain,
.mapping = mapping,
.trial_order = 0,
.test_order = 0,
.coefficient_order = 0,
.geometry_weight_order = geometryWeightOrder,
.base_order = baseOrder
};
}
};
static_assert(FieldTag<Gravity>);
static_assert(FieldTag<Displacement>);
static_assert(FieldTag<Density>);
static_assert(FieldTag<BarotropicConstant>);
} // namespace mean_field::field

View File

@@ -0,0 +1,379 @@
module;
#include <concepts>
#include <string_view>
export module mean_field:field.registry;
export import :field.base;
export import :quadrature.policy;
export namespace mean_field::field {
// =========================================================================
// Density
// =========================================================================
struct Density {
static constexpr std::string_view name = "density";
static constexpr int scalarOrder = 2;
struct Scalar final
: ScalarQ<FieldRelation::Independent, Disc<L2, scalarOrder>> {
static constexpr std::string_view symbol = "ρ";
};
using Quantities = TypeList<Scalar>;
using Constraints = TypeList<>;
static constexpr bool constraintsAreValid =
validate_constraints(Constraints{});
static_assert(constraintsAreValid);
struct Form {
// Density-space mass matrix: (rho, q).
using ProjectionMass = FormSpec<
quadrature::Term::density_projection,
0,
Operand<Scalar>,
Operand<Scalar>>;
// Projection RHS with one runtime coefficient order.
using ProjectionSource = FormSpec<
quadrature::Term::density_projection,
1,
Operand<Scalar>>;
// Density-space contribution to the barotropic EOS closure:
// (rho, q_rho).
using EosClosureMass = FormSpec<
quadrature::Term::eos_closure,
0,
Operand<Scalar>,
Operand<Scalar>>;
// Integral of density over the physical volume.
using MassConservation = FormSpec<
quadrature::Term::mass_conservation,
0,
Operand<Scalar>>;
// The same physical integral used as a nonlinear normalization
// constraint. It has a distinct policy key so solver assembly and
// diagnostics can be overintegrated independently.
using MassNormalization = FormSpec<
quadrature::Term::mass_normalization,
0,
Operand<Scalar>>;
// Integral of rho * x. The combined position-coefficient order is
// supplied as one dynamic order.
using CenterOfMass =
FormSpec<quadrature::Term::center_of_mass, 1, Operand<Scalar>>;
// Integral of rho times the quadratic position tensor. The
// combined tensor-coefficient order is supplied dynamically.
using Quadrupole =
FormSpec<quadrature::Term::quadrupole, 1, Operand<Scalar>>;
using ErrorNorm = FormSpec<
quadrature::Term::error_norm,
0,
Operand<Scalar>,
Operand<Scalar>>;
};
using FormList = TypeList<
Form::ProjectionMass,
Form::ProjectionSource,
Form::EosClosureMass,
Form::MassConservation,
Form::MassNormalization,
Form::CenterOfMass,
Form::Quadrupole,
Form::ErrorNorm>;
};
// =========================================================================
// Gravity
// =========================================================================
struct Gravity {
static constexpr std::string_view name = "gravity";
static constexpr int potentialOrder = 2;
static constexpr int fluxOrder = 2;
struct Potential final
: ScalarQ<FieldRelation::Independent, Disc<L2, potentialOrder>> {
static constexpr std::string_view symbol = "φ";
};
struct Flux final
: VectorQ<FieldRelation::Gradient<Potential>, Disc<RT, fluxOrder>> {
static constexpr std::string_view symbol = "∇φ";
};
using Quantities = TypeList<Potential, Flux>;
using Constraints = TypeList<RtL2StablePair<Flux, Potential>>;
static constexpr bool constraintsAreValid =
validate_constraints(Constraints{});
static_assert(constraintsAreValid);
struct Form {
using HDivMass = FormSpec<
quadrature::Term::gravity_hdiv_mass,
0,
Operand<Flux>,
Operand<Flux>>;
using DivergenceCoupling = FormSpec<
quadrature::Term::gravity_divergence,
0,
Operand<Flux, FieldOperation::Divergence>,
Operand<Potential>>;
using Boundary = FormSpec<
quadrature::Term::gravity_boundary,
0,
Operand<Flux, FieldOperation::NormalTrace>,
Operand<Flux, FieldOperation::NormalTrace>>;
// Density is a registered coefficient field and potential is the
// test field, so the full polynomial order is compile-time data.
using SourceLinear = FormSpec<
quadrature::Term::gravity_source,
0,
Operand<Density::Scalar>,
Operand<Potential>>;
// Mixed density-to-potential projection. Both trial and test
// orders are registered quantities.
using SourceProjection = FormSpec<
quadrature::Term::gravity_source,
0,
Operand<Density::Scalar>,
Operand<Potential>>;
using PotentialErrorNorm = FormSpec<
quadrature::Term::error_norm,
0,
Operand<Potential>,
Operand<Potential>>;
using FluxErrorNorm = FormSpec<
quadrature::Term::error_norm,
0,
Operand<Flux>,
Operand<Flux>>;
};
using FormList = TypeList<
Form::HDivMass,
Form::DivergenceCoupling,
Form::Boundary,
Form::SourceLinear,
Form::SourceProjection,
Form::PotentialErrorNorm,
Form::FluxErrorNorm>;
};
// =========================================================================
// Displacement
// =========================================================================
struct Displacement {
static constexpr std::string_view name = "displacement";
static constexpr int vectorOrder = 3;
struct Vector final
: VectorQ<FieldRelation::Independent, Disc<H1, vectorOrder>> {
static constexpr std::string_view symbol = "d";
};
using Quantities = TypeList<Vector>;
using Constraints = TypeList<>;
static constexpr bool constraintsAreValid =
validate_constraints(Constraints{});
static_assert(constraintsAreValid);
struct Form {
// Harmonic or pseudoelastic interior mesh extension. For the
// initial Laplacian model this is (grad d, grad w).
using MeshExtension = FormSpec<
quadrature::Term::mesh_extension,
0,
Operand<Vector, FieldOperation::Gradient>,
Operand<Vector, FieldOperation::Gradient>>;
using ErrorNorm = FormSpec<
quadrature::Term::error_norm,
0,
Operand<Vector>,
Operand<Vector>>;
};
using FormList = TypeList<Form::MeshExtension, Form::ErrorNorm>;
};
struct BarotropicConstant {
static constexpr std::string_view name = "barotropic_constant";
struct Scalar final : GlobalScalarQ {
static constexpr std::string_view symbol = "C";
};
using Quantities = TypeList<Scalar>;
using Constraints = TypeList<>;
using FormList = TypeList<>;
static constexpr bool constraintsAreValid =
validate_constraints(Constraints{});
static_assert(constraintsAreValid);
};
// =========================================================================
// Specific enthalpy
//
// Pressure is deliberately not registered as an independent field. For a
// barotrope it is derived from h through the EOS, while h supplies the
// continuous H1 trace used to define the isobaric stellar surface.
// =========================================================================
struct Enthalpy {
static constexpr std::string_view name = "specific_enthalpy";
static constexpr int scalarOrder = 3;
struct Scalar final
: ScalarQ<FieldRelation::Independent, Disc<H1, scalarOrder>> {
static constexpr std::string_view symbol = "h";
};
using Quantities = TypeList<Scalar>;
using Constraints = TypeList<>;
static constexpr bool constraintsAreValid =
validate_constraints(Constraints{});
static_assert(constraintsAreValid);
struct Form {
// EOS source contribution (rho(h), q_rho). The dynamic order is
// the extra polynomial order introduced by the nonlinear EOS
// beyond the registered order of h. For an n=3 polytrope this is
// 2 * hOrder, making rho(h) cubic in h.
using EosClosureSource = FormSpec<
quadrature::Term::eos_closure,
1,
Operand<Scalar>,
Operand<Density::Scalar>>;
// (h, q_h) contribution to
// h + phi - Psi_rotation - C = 0.
using EquilibriumEnthalpy = FormSpec<
quadrature::Term::hydrostatic_equilibrium,
0,
Operand<Scalar>,
Operand<Scalar>>;
// (phi, q_h) contribution to hydrostatic equilibrium.
using EquilibriumGravity = FormSpec<
quadrature::Term::hydrostatic_equilibrium,
0,
Operand<Gravity::Potential>,
Operand<Scalar>>;
// (Psi_rotation, q_h). The rotation-potential order is supplied
// dynamically because it belongs to runtime rotation data.
using EquilibriumRotation = FormSpec<
quadrature::Term::hydrostatic_equilibrium,
1,
Operand<Scalar>>;
// (C, q_h), where C is spatially constant.
using EquilibriumConstant = FormSpec<
quadrature::Term::hydrostatic_equilibrium,
0,
Operand<BarotropicConstant::Scalar>,
Operand<Scalar>>;
// Boundary trace form available for weak enforcement, testing, or
// a future multiplier formulation of h|Gamma_star = 0.
using IsobaricSurface = FormSpec<
quadrature::Term::isobaric_surface,
0,
Operand<Scalar>,
Operand<Scalar>>;
// Integral of P(h). The dynamic order is the extra EOS order
// beyond the registered order of h.
using PressureIntegral = FormSpec<
quadrature::Term::pressure_integral,
1,
Operand<Scalar>>;
// Weak pressure force in the displacement test space:
//
// -int P(h) I : grad(w) dV
//
// which is equivalent to -int P(h) div(w) dV. The dynamic order
// is the extra EOS order beyond the registered order of h. For an
// n=3 polytrope this is 3 * hOrder, making P(h) quartic in h.
using PressureForce = FormSpec<
quadrature::Term::pressure_force,
1,
Operand<Scalar>,
Operand<Displacement::Vector, FieldOperation::Gradient>>;
using ErrorNorm = FormSpec<
quadrature::Term::error_norm,
0,
Operand<Scalar>,
Operand<Scalar>>;
};
using FormList = TypeList<
Form::EosClosureSource,
Form::EquilibriumEnthalpy,
Form::EquilibriumGravity,
Form::EquilibriumRotation,
Form::EquilibriumConstant,
Form::IsobaricSurface,
Form::PressureIntegral,
Form::PressureForce,
Form::ErrorNorm>;
};
// =========================================================================
// Field definition concept
// =========================================================================
template <typename T>
concept FieldTag =
requires {
typename T::Quantities;
typename T::Constraints;
typename T::FormList;
{ T::name } -> std::convertible_to<std::string_view>;
} && isRegisteredQuantityList<typename T::Quantities> &&
isFieldFormList<typename T::FormList>;
static_assert(FieldTag<Gravity>);
static_assert(FieldTag<Displacement>);
static_assert(FieldTag<Density>);
static_assert(FieldTag<Enthalpy>);
static_assert(FieldTag<BarotropicConstant>);
static_assert(DerivedQuantity<Gravity::Flux>);
static_assert(std::same_as<
RelationTargetT<Gravity::Flux>,
Gravity::Potential>);
} // namespace mean_field::field

View File

@@ -25,4 +25,4 @@ export namespace mean_field::integrators {
private:
const mapping::DomainMapper &m_map;
};
}
} // namespace mean_field::integrators

View File

@@ -1,14 +1,19 @@
module;
#include <mfem.hpp>
export module mean_field:integrators.centrifugal;
import :mapping.domain_mapper;
export import :mapping.domain_mapper;
export namespace mean_field::integrators {
class CentrifugalForceIntegrator : public mfem::BlockNonlinearFormIntegrator {
class CentrifugalForceIntegrator
: public mfem::BlockNonlinearFormIntegrator {
public:
CentrifugalForceIntegrator(const mapping::DomainMapper& map, const mfem::Vector& omega);
CentrifugalForceIntegrator(
const mapping::DomainMapper &map,
const mfem::Vector &omega
);
void SetOmega(const mfem::Vector& omega);
void SetOmega(const mfem::Vector &omega);
void SetIntegrationRule(const mfem::IntegrationRule &ir);
void AssembleElementVector(
const mfem::Array<const mfem::FiniteElement *> &el,
@@ -25,8 +30,9 @@ export namespace mean_field::integrators {
) override;
private:
const mapping::DomainMapper& m_map;
const mapping::DomainMapper &m_map;
mfem::Vector m_omega;
const mfem::IntegrationRule *m_ir = nullptr;
};
}
} // namespace mean_field::integrators

View File

@@ -3,26 +3,32 @@ module;
export module mean_field:integrators.coriolis;
import :mapping.domain_mapper;
export namespace mean_field::integrators {
class CoriolisIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
CoriolisIntegrator(const mapping::DomainMapper& map, const mfem::Vector& omega);
CoriolisIntegrator(
const mapping::DomainMapper &map,
const mfem::Vector &omega
);
void AssembleElementVector(const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvec) override;
void AssembleElementVector(
const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvec
) override;
void AssembleElementGrad(const mfem::Array<const mfem::FiniteElement*> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats) override;
void AssembleElementGrad(
const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) override;
private:
const mapping::DomainMapper& m_map;
const mapping::DomainMapper &m_map;
mfem::Vector m_omega;
mfem::DenseMatrix m_omega_mat;
};
}
} // namespace mean_field::integrators

View File

@@ -1,14 +1,29 @@
module;
#include <cstdint>
#include <mfem.hpp>
export module mean_field:integrators.gravity;
import :mapping.domain_mapper;
export namespace mean_field::integrators {
class GravityForceIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
GravityForceIntegrator(const mapping::DomainMapper& map, const mfem::GridFunction& phi);
enum class GravityForceJacobianMode : std::uint8_t {
minimal,
field_coupled,
exact
};
void SetPotential(const mfem::GridFunction& phi);
class GravityMomentumIntegrator
: public mfem::BlockNonlinearFormIntegrator {
public:
explicit GravityMomentumIntegrator(
const mapping::DomainMapper &map,
GravityForceJacobianMode jacobian_mode =
GravityForceJacobianMode::field_coupled
);
void SetJacobianMode(GravityForceJacobianMode jacobian_mode);
void SetIntegrationRule(const mfem::IntegrationRule &integration_rule);
[[nodiscard]] GravityForceJacobianMode GetJacobianMode() const;
void AssembleElementVector(
const mfem::Array<const mfem::FiniteElement *> &el,
@@ -16,7 +31,6 @@ export namespace mean_field::integrators {
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvec
) override;
void AssembleElementGrad(
const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
@@ -24,9 +38,9 @@ export namespace mean_field::integrators {
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) override;
private:
const mapping::DomainMapper& m_map;
const mfem::GridFunction* m_phi;
const mapping::DomainMapper &m_map;
GravityForceJacobianMode m_jacobian_mode;
const mfem::IntegrationRule *m_integration_rule{nullptr};
};
}
} // namespace mean_field::integrators

View File

@@ -4,9 +4,10 @@ export module mean_field:integrators.mass_continuity;
import :mapping.domain_mapper;
export namespace mean_field::integrators {
class ContinuityVolumeIntegrator : public mfem::BlockNonlinearFormIntegrator {
class ContinuityVolumeIntegrator
: public mfem::BlockNonlinearFormIntegrator {
public:
explicit ContinuityVolumeIntegrator(const mapping::DomainMapper& map);
explicit ContinuityVolumeIntegrator(const mapping::DomainMapper &map);
void AssembleElementVector(
const mfem::Array<const mfem::FiniteElement *> &el,
@@ -21,13 +22,14 @@ export namespace mean_field::integrators {
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) override;
private:
const mapping::DomainMapper& m_map;
const mapping::DomainMapper &m_map;
};
class ContinuityFaceIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
explicit ContinuityFaceIntegrator(const mapping::DomainMapper& map);
explicit ContinuityFaceIntegrator(const mapping::DomainMapper &map);
void AssembleFaceVector(
const mfem::Array<const mfem::FiniteElement *> &el1,
@@ -44,19 +46,20 @@ export namespace mean_field::integrators {
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) override;
private:
static bool skip_face(const mfem::FaceElementTransformations& Tr);
static bool skip_face(const mfem::FaceElementTransformations &Tr);
static double compute_u_n(
const mfem::Vector& v_dofs,
const mfem::Vector& shape_v_minus,
const mfem::Vector& n_unit,
const mfem::Vector &v_dofs,
const mfem::Vector &shape_v_minus,
const mfem::Vector &n_unit,
int dof_v_minus,
int dim
);
private:
const mapping::DomainMapper& m_map;
const mapping::DomainMapper &m_map;
};
}
} // namespace mean_field::integrators

View File

@@ -1,36 +1,47 @@
module;
#include <mfem.hpp>
#include "xad_promote_polyfill.h"
#include <XAD/XAD.hpp>
#include <mfem.hpp>
export module mean_field:integrators.pressure_gradient;
import :mapping.domain_mapper;
import :utils.misc;
export namespace mean_field::integrators {
template <utils::is_xad EOS_T>
class PressureGradientIntegrator : public mfem::BlockNonlinearFormIntegrator {
class PressureGradientIntegrator
: public mfem::BlockNonlinearFormIntegrator {
public:
PressureGradientIntegrator(const mapping::DomainMapper& map, utils::EOS_P<EOS_T> eos);
PressureGradientIntegrator(
const mapping::DomainMapper &map,
utils::EOS_P<EOS_T> eos
);
void AssembleElementVector(
const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvec
) override;
void AssembleElementGrad(
const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) override;
void AssembleElementVector(const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvec) override;
void AssembleElementGrad(const mfem::Array<const mfem::FiniteElement*> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats) override;
private:
const mapping::DomainMapper& m_map;
const mapping::DomainMapper &m_map;
utils::EOS_P<EOS_T> m_eos;
};
template <utils::is_xad EOS_T>
PressureGradientIntegrator<EOS_T>::PressureGradientIntegrator(
const mapping::DomainMapper& map,
const mapping::DomainMapper &map,
utils::EOS_P<EOS_T> eos
) : m_map(map), m_eos(std::move(eos)) {}
)
: m_map(map),
m_eos(std::move(eos)) {
}
template <utils::is_xad EOS_T>
void PressureGradientIntegrator<EOS_T>::AssembleElementVector(
@@ -43,16 +54,16 @@ export namespace mean_field::integrators {
return;
}
const mfem::FiniteElement* fe_v = el[0];
const mfem::FiniteElement* fe_rho = el[1];
const mfem::FiniteElement *fe_v = el[0];
const mfem::FiniteElement *fe_rho = el[1];
const int dof_v = fe_v->GetDof();
const int dof_rho = fe_rho->GetDof();
const int dim = Tr.GetSpaceDim();
const int dof_v = fe_v->GetDof();
const int dof_rho = fe_rho->GetDof();
const int dim = Tr.GetSpaceDim();
const mfem::Vector& rho_dofs = *elfun[1];
const mfem::Vector &rho_dofs = *elfun[1];
mfem::Vector& r_v = *elvec[0];
mfem::Vector &r_v = *elvec[0];
r_v.SetSize(dof_v * dim);
r_v = 0.0;
if (elvec[1]) {
@@ -63,10 +74,11 @@ export namespace mean_field::integrators {
mfem::DenseMatrix dshape_v_ref(dof_v, dim), dshape_v_phys(dof_v, dim);
mfem::Vector shape_rho(dof_rho);
const mfem::IntegrationRule* ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
const mfem::IntegrationRule *ir =
&mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
for (int q = 0; q < ir->GetNPoints(); ++q) {
const mfem::IntegrationPoint& ip = ir->IntPoint(q);
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
Tr.SetIntPoint(&ip);
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
@@ -76,13 +88,15 @@ export namespace mean_field::integrators {
fe_rho->CalcShape(ip, shape_rho);
double rho_val = 0.0;
for (int i = 0; i < dof_rho; ++i) rho_val += rho_dofs(i) * shape_rho(i);
for (int i = 0; i < dof_rho; ++i)
rho_val += rho_dofs(i) * shape_rho(i);
// Guard against negative density from Newton solver overshoots
if (rho_val < 1e-15) rho_val = 1e-15;
if (rho_val < 1e-15)
rho_val = 1e-15;
// Evaluate the exact Equation of State Pressure
EOS_T x_rho = rho_val;
EOS_T x_rho = rho_val;
double P_val = m_eos(x_rho, EOS_T(0.0)).value();
for (int i = 0; i < dof_v; ++i) {
@@ -95,36 +109,40 @@ export namespace mean_field::integrators {
template <utils::is_xad EOS_T>
void PressureGradientIntegrator<EOS_T>::AssembleElementGrad(
const mfem::Array<const mfem::FiniteElement*> &el,
const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) {
const mfem::FiniteElement* fe_v = el[0];
const mfem::FiniteElement* fe_rho = el[1];
const mfem::FiniteElement *fe_v = el[0];
const mfem::FiniteElement *fe_rho = el[1];
const int dof_v = fe_v->GetDof();
const int dof_rho = fe_rho->GetDof();
const int dim = Tr.GetSpaceDim();
const int dof_v = fe_v->GetDof();
const int dof_rho = fe_rho->GetDof();
const int dim = Tr.GetSpaceDim();
const mfem::Vector& rho_dofs = *elfun[1];
const mfem::Vector &rho_dofs = *elfun[1];
mfem::DenseMatrix* dv_dv = elmats(0, 0);
mfem::DenseMatrix* dv_drho = elmats(0, 1);
mfem::DenseMatrix *dv_dv = elmats(0, 0);
mfem::DenseMatrix *dv_drho = elmats(0, 1);
if (dv_dv) *dv_dv = 0.0;
if (dv_drho) *dv_drho = 0.0;
if (!dv_drho) return;
if (dv_dv)
*dv_dv = 0.0;
if (dv_drho)
*dv_drho = 0.0;
if (!dv_drho)
return;
mfem::DenseMatrix dshape_v_ref(dof_v, dim), dshape_v_phys(dof_v, dim);
mfem::Vector shape_rho(dof_rho);
const mfem::IntegrationRule* ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
const mfem::IntegrationRule *ir =
&mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
for (int q = 0; q < ir->GetNPoints(); ++q) {
using Scalar = EOS_T::value_type;
xad::Tape<Scalar> tape;
const mfem::IntegrationPoint& ip = ir->IntPoint(q);
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
Tr.SetIntPoint(&ip);
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
@@ -140,29 +158,32 @@ export namespace mean_field::integrators {
for (int i = 0; i < dof_rho; ++i) {
x_rho += rho_dofs(i) * shape_rho(i);
}
if (x_rho < 1e-15) x_rho = EOS_T(1e-15);
if (x_rho < 1e-15)
x_rho = EOS_T(1e-15);
EOS_T x_P = m_eos(x_rho, EOS_T(0.0));
tape.registerOutput(x_P);
x_P.setAdjoint(1.0);
tape.computeAdjoints();
double dP_drho = x_rho.getAdjoint();
double dP_drho = x_rho.getAdjoint();
double debug_K = 1.5;
double debug_n = 3.0;
double analytic_dp = debug_K * (1.0 + 1.0 / debug_n) * std::pow(xad::value(x_rho), 1.0 / debug_n);
double debug_K = 1.5;
double debug_n = 3.0;
double analytic_dp = debug_K * (1.0 + 1.0 / debug_n) *
std::pow(xad::value(x_rho), 1.0 / debug_n);
double ad_err = std::abs(dP_drho - analytic_dp);
double ad_err = std::abs(dP_drho - analytic_dp);
for (int i = 0; i < dof_v; ++i) {
for (int c = 0; c < dim; ++c) {
int row = i + c * dof_v;
for (int j = 0; j < dof_rho; ++j) {
int col = j;
double term = dshape_v_phys(i, c) * dP_drho * shape_rho(j);
double term =
dshape_v_phys(i, c) * dP_drho * shape_rho(j);
(*dv_drho)(row, col) -= term * weight;
}
}
}
}
}
}
} // namespace mean_field::integrators

View File

@@ -4,9 +4,13 @@ export module mean_field:integrators.viscosity;
import :mapping.domain_mapper;
export namespace mean_field::integrators {
class ViscosityIntegrator : public mfem::BlockNonlinearFormIntegrator {
class ViscosityIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
ViscosityIntegrator(const mapping::DomainMapper& map, double mu, int quad_boost);
ViscosityIntegrator(
const mapping::DomainMapper &map,
double mu,
int quad_boost
);
void SetMu(const double mu);
@@ -23,10 +27,11 @@ export namespace mean_field::integrators {
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) override;
private:
const mapping::DomainMapper& m_map;
const mapping::DomainMapper &m_map;
double m_mu;
int m_quad_boost;
};
}
} // namespace mean_field::integrators

View File

@@ -10,7 +10,7 @@ export namespace mean_field::mapping {
public:
MappedScalarCoefficient(
const DomainMapper &map,
mfem::Coefficient &coeff,
Coefficient &coeff,
COORDINATE_SPACE coord_space = COORDINATE_SPACE::PHYSICAL
);
@@ -21,14 +21,14 @@ export namespace mean_field::mapping {
private:
static double eval_at_point(
mfem::Coefficient &c,
Coefficient &c,
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip
);
private:
const DomainMapper &m_map;
mfem::Coefficient &m_coeff;
Coefficient &m_coeff;
COORDINATE_SPACE m_coord_space;
};
@@ -42,29 +42,37 @@ export namespace mean_field::mapping {
MappedDiffusionCoefficient(
const DomainMapper &map,
mfem::MatrixCoefficient &sigma
MatrixCoefficient &sigma
);
void Eval(mfem::DenseMatrix &K, mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) override;
void Eval(
mfem::DenseMatrix &K,
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip
) override;
private:
const DomainMapper &m_map;
mfem::Coefficient *m_scalar;
mfem::MatrixCoefficient *m_tensor;
MatrixCoefficient *m_tensor;
};
class MappedVectorCoefficient : public mfem::VectorCoefficient {
public:
MappedVectorCoefficient(
const DomainMapper &map,
mfem::VectorCoefficient &coeff
VectorCoefficient &coeff
);
void Eval(mfem::Vector &V, mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) override;
void Eval(
mfem::Vector &V,
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip
) override;
private:
const DomainMapper &m_map;
mfem::VectorCoefficient &m_coeff;
VectorCoefficient &m_coeff;
};
class PhysicalPositionFunctionCoefficient : public mfem::Coefficient {
@@ -76,7 +84,10 @@ export namespace mean_field::mapping {
Func f
);
double Eval(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) override;
double Eval(
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip
) override;
private:
Func m_f;
@@ -85,11 +96,18 @@ export namespace mean_field::mapping {
class MappedHDivMassCoefficient final : public mfem::MatrixCoefficient {
public:
MappedHDivMassCoefficient(const DomainMapper& map, const int dim);
MappedHDivMassCoefficient(
const DomainMapper &map,
const int dim
);
void Eval(
mfem::DenseMatrix &matrix,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point
) override;
void Eval(mfem::DenseMatrix& matrix, mfem::ElementTransformation& transformation, const mfem::IntegrationPoint& integration_point) override;
private:
const DomainMapper& m_map;
const DomainMapper &m_map;
};
}
} // namespace mean_field::mapping

View File

@@ -0,0 +1,46 @@
module;
#include <mfem.hpp>
export module mean_field:mapping.compactification;
export import :mapping.types;
export namespace mean_field::mapping::compactification {
struct ExteriorMapInput {
const mfem::Vector &reference_position;
const mfem::Vector &displaced_position;
const mfem::DenseMatrix &displacement_jacobian;
double compactification_coordinate;
const mfem::Vector &compactification_coordinate_gradient;
};
struct ExteriorMapResult {
mfem::Vector physical_position;
mfem::DenseMatrix mapping_jacobian;
};
struct ExteriorMapDirection {
const mfem::Vector &displaced_position_variation;
const mfem::DenseMatrix &displacement_jacobian_variation;
};
struct ExteriorMapVariation {
mfem::Vector physical_position_variation;
mfem::DenseMatrix mapping_jacobian_variation;
};
class ExteriorDomainMap {
public:
virtual ~ExteriorDomainMap() = default;
[[nodiscard]] virtual MappingStatus Evaluate(
const ExteriorMapInput &input,
ExteriorMapResult &result
) const = 0;
[[nodiscard]] virtual MappingStatus EvaluateVariation(
const ExteriorMapInput &input,
const ExteriorMapResult &result,
const ExteriorMapDirection &direction,
ExteriorMapVariation &variation
) const = 0;
[[nodiscard]] virtual std::string_view GetName() const noexcept = 0;
};
} // namespace mean_field::mapping::compactification

View File

@@ -0,0 +1,50 @@
module;
#include <mfem.hpp>
export module mean_field:mapping.kelvin;
export import :mapping.compactification;
export import :mapping.types;
export import :mapping.compactification.options;
export namespace mean_field::mapping::compactification {
class KelvinCompactification final : public ExteriorDomainMap {
public:
explicit KelvinCompactification(
options::KelvinCompactificationOptions options
);
[[nodiscard]] MappingStatus Evaluate(
const ExteriorMapInput &input,
ExteriorMapResult &result
) const override;
[[nodiscard]] MappingStatus EvaluateVariation(
const ExteriorMapInput &input,
const ExteriorMapResult &result,
const ExteriorMapDirection &direction,
ExteriorMapVariation &variation
) const override;
[[nodiscard]] std::string_view GetName() const noexcept override;
[[nodiscard]] double GetReferenceStellarRadius() const noexcept;
[[nodiscard]] double GetReferenceInfinityRadius() const noexcept;
[[nodiscard]] double GetCoordinateTolerance() const noexcept;
private:
struct RadialFactors {
double coordinate;
double computational_radius;
double scale;
double scale_derivative;
};
[[nodiscard]] MappingStatus ComputeRadialFactors(
double compactification_coordinate,
RadialFactors &factors
) const;
options::KelvinCompactificationOptions m_options;
};
} // namespace mean_field::mapping::compactification

View File

@@ -0,0 +1,9 @@
export module mean_field:mapping.compactification.options;
export namespace mean_field::mapping::compactification::options {
struct KelvinCompactificationOptions {
double r_star_ref{1.0};
double r_inf_ref{2.0};
double coordinate_tolerance{1.0e-12};
};
} // namespace mean_field::mapping::compactification::options

View File

@@ -3,51 +3,302 @@ module;
#include "mean_field.h"
export module mean_field:mapping.domain_mapper;
export import :mapping.types;
import :mapping.compactification;
import :utils.user;
export namespace mean_field::mapping {
enum class FaceElementSide : uint8_t { element_1, element_2 };
class ElementDisplacementData {
public:
ElementDisplacementData(
const mfem::FiniteElement &element,
const mfem::Vector &displacement_dofs,
mfem::Ordering::Type ordering = mfem::Ordering::byNODES
);
[[nodiscard]] const mfem::FiniteElement &GetElement() const noexcept;
[[nodiscard]] const mfem::DenseMatrix &GetDofMatrix() const noexcept;
[[nodiscard]] int GetDimension() const noexcept;
[[nodiscard]] int GetDofCount() const noexcept;
[[nodiscard]] mfem::Ordering::Type GetOrdering() const noexcept;
private:
const mfem::FiniteElement *m_element;
mfem::DenseMatrix m_dof_matrix;
int m_dimension;
mfem::Ordering::Type m_ordering;
};
struct CompactificationPointData {
double coordinate{0.0};
mfem::Vector coordinate_gradient;
};
[[nodiscard]] ElementDisplacementData
ElementDisplacementDataFromElementVDofs(
const mfem::FiniteElement &element,
const mfem::Vector &displacement_dofs
);
class ElementCompactificationData {
public:
ElementCompactificationData(
const mfem::FiniteElement &element,
const mfem::Vector &dofs
);
[[nodiscard]] const mfem::FiniteElement &GetElement() const noexcept;
[[nodiscard]] const mfem::Vector &GetDofs() const noexcept;
[[nodiscard]] int GetDofCount() const noexcept;
private:
const mfem::FiniteElement *m_element;
mfem::Vector m_dofs;
};
struct ElementMappingData {
const ElementDisplacementData &displacement;
const ElementCompactificationData &compactification;
};
class DomainMapperStateless {
public:
class Workspace {
public:
explicit Workspace(int dimension = 3);
void SetDimension(int dimension);
[[nodiscard]] int GetDimension() const noexcept;
private:
friend class DomainMapperStateless;
int m_dimension;
mfem::Vector m_shape;
mfem::DenseMatrix m_mesh_dshape;
mfem::Vector m_field_value;
mfem::DenseMatrix m_field_jacobian;
mfem::Vector m_compactification_shape;
mfem::DenseMatrix m_compactification_dshape;
CompactificationPointData m_compactification_point;
mfem::Vector m_reference_normal;
mfem::Vector m_mapped_normal;
mfem::DenseMatrix m_full_element_jacobian;
mfem::Vector m_vector_temp;
mfem::DenseMatrix m_matrix_temp_1;
mfem::DenseMatrix m_matrix_temp_2;
compactification::ExteriorMapResult m_exterior_result;
compactification::ExteriorMapVariation m_exterior_variation;
};
public:
DomainMapperStateless(
utils::DomainMapperStatelessOptions options,
std::unique_ptr<const compactification::ExteriorDomainMap>
exterior_map
);
DomainMapperStateless(const DomainMapperStateless &) = delete;
DomainMapperStateless &
operator=(const DomainMapperStateless &) = delete;
DomainMapperStateless(DomainMapperStateless &&) = default;
DomainMapperStateless &operator=(DomainMapperStateless &&) = default;
[[nodiscard]] MappingStatus EvaluatePoint(
const ElementMappingData &element_data,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
MappingPointContext &context
) const;
[[nodiscard]] MappingStatus EvaluateVolume(
const ElementMappingData &element_data,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
VolumeMappingContext &context
) const;
[[nodiscard]] MappingStatus EvaluateFace(
const ElementMappingData &element_data,
mfem::FaceElementTransformations &transformation,
FaceElementSide side,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
FaceMappingContext &context
) const;
[[nodiscard]] MappingStatus EvaluatePointVariation(
const ElementMappingData &element_data,
const ElementDisplacementData &direction,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
const MappingPointContext &base_context,
Workspace &workspace,
MappingPointVariation &variation
) const;
[[nodiscard]] MappingStatus EvaluateVolumeVariation(
const ElementMappingData &element_data,
const ElementDisplacementData &direction,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
const VolumeMappingContext &base_context,
Workspace &workspace,
VolumeMappingVariation &variation
) const;
[[nodiscard]] MappingStatus EvaluateFaceVariation(
const ElementMappingData &element_data,
const ElementDisplacementData &direction,
mfem::FaceElementTransformations &transformation,
FaceElementSide side,
const mfem::IntegrationPoint &integration_point,
const FaceMappingContext &base_context,
Workspace &workspace,
FaceMappingVariation &variation
) const;
[[nodiscard]] bool IsCompactifiedElement(
const mfem::ElementTransformation &transformation
) const noexcept;
[[nodiscard]] int GetDimension() const noexcept;
[[nodiscard]] int GetVacuumElementAttribute() const noexcept;
[[nodiscard]] const compactification::ExteriorDomainMap &
GetExteriorMap() const noexcept;
private:
void ValidateElementData(const ElementMappingData &element_data) const;
void EvaluateField(
const ElementDisplacementData &field,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
mfem::Vector &value,
mfem::DenseMatrix &jacobian
) const;
[[nodiscard]] MappingStatus EvaluateCompactificationCoordinate(
const ElementCompactificationData &compactification,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
CompactificationPointData &point_data
) const;
[[nodiscard]] static mfem::ElementTransformation &
SelectFaceElementTransformation(
mfem::FaceElementTransformations &transformation,
FaceElementSide side
);
[[nodiscard]] static const mfem::IntegrationPoint &
SelectFaceElementIntegrationPoint(
mfem::FaceElementTransformations &transformation,
FaceElementSide side
);
utils::DomainMapperStatelessOptions m_options;
std::unique_ptr<const compactification::ExteriorDomainMap>
m_exterior_map;
};
class DomainMapper {
public:
struct VolumeQuadratureContext {
mfem::DenseMatrix J_inv;
double detJ;
double weight;
};
struct FaceQuadratureContext {
mfem::Vector normal;
double ds;
double v_dot_n_scale;
};
public:
explicit DomainMapper(const double r_star_ref, const double r_inf_ref);
explicit DomainMapper(
const double r_star_ref,
const double r_inf_ref
);
explicit DomainMapper(const mfem::GridFunction &d, const double r_star_ref, const double r_inf_ref);
explicit DomainMapper(
const mfem::GridFunction &d,
const double r_star_ref,
const double r_inf_ref
);
[[nodiscard]] bool is_vacuum(const mfem::ElementTransformation &T) const;
[[nodiscard]] bool
is_vacuum(const mfem::ElementTransformation &T) const;
void SetDisplacement(const mfem::GridFunction &d);
[[nodiscard]] bool IsIdentity() const;
[[nodiscard]] bool HasCompactification() const noexcept;
[[nodiscard]] bool HasDisplacementField() const noexcept;
[[nodiscard]] bool CalcIsIdentity() const;
void ResetDisplacement();
void ComputeJacobian(mfem::ElementTransformation &T, mfem::DenseMatrix &J) const;
void ComputeJacobian(
mfem::ElementTransformation &T,
mfem::DenseMatrix &J
) const;
double ComputeDetJ(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) const;
double ComputeDetJ(
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip
) const;
void ComputeMappedDiffusionTensor(mfem::ElementTransformation &T, mfem::DenseMatrix &D) const;
void ComputeMappedDiffusionTensor(
mfem::ElementTransformation &T,
mfem::DenseMatrix &D
) const;
void ComputeInverseJacobian(mfem::ElementTransformation &T, mfem::DenseMatrix &JInv) const;
void ComputeInverseJacobian(
mfem::ElementTransformation &T,
mfem::DenseMatrix &JInv
) const;
VolumeQuadratureContext GetQuadratureContext(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) const;
VolumeQuadratureContext GetQuadratureContext(
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip
) const;
FaceQuadratureContext GetFaceQuadratureContext(mfem::FaceElementTransformations &T, const mfem::IntegrationPoint &ip) const;
FaceQuadratureContext GetFaceQuadratureContext(
mfem::FaceElementTransformations &T,
const mfem::IntegrationPoint &ip
) const;
void GetPhysicalPoint(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip, mfem::Vector &x_phys) const;
void GetPhysicalPoint(
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip,
mfem::Vector &x_phys
) const;
void GetVectorValue(const int i, const mfem::IntegrationPoint &ip, mfem::Vector &val) const;
void GetVectorValue(
const int i,
const mfem::IntegrationPoint &ip,
mfem::Vector &val
) const;
void MapHDivFluxToPhysical(
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
const mfem::Vector &reference_flux,
mfem::Vector &physical_flux
) const;
void MapPhysicalFluxToHDivReference(
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
const mfem::Vector &physical_flux,
mfem::Vector &reference_flux
) const;
void MapReferenceGradientToPhysical(
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
const mfem::Vector &reference_gradient,
mfem::Vector &physical_gradient
) const;
[[nodiscard]] const mfem::GridFunction *GetDisplacement() const;
[[nodiscard]] double GetPhysInfRadius() const;
@@ -63,9 +314,17 @@ export namespace mean_field::mapping {
private:
void InitAllScratchSpaces() const;
void ApplyKelvinMapping(const mfem::Vector &x_ref, mfem::Vector &x_phys) const;
void ApplyKelvinMapping(
const mfem::Vector &x_ref,
mfem::Vector &x_phys
) const;
void ComputeKelvinJacobian(const mfem::Vector &x_ref, const mfem::Vector &x_disp, const mfem::DenseMatrix &J_D, mfem::DenseMatrix &J) const;
void ComputeKelvinJacobian(
const mfem::Vector &x_ref,
const mfem::Vector &x_disp,
const mfem::DenseMatrix &J_D,
mfem::DenseMatrix &J
) const;
void InvalidateCache() const;
@@ -98,6 +357,8 @@ export namespace mean_field::mapping {
mutable mfem::Vector m_x_ref;
mutable mfem::Vector m_x_disp;
mutable mfem::Vector m_d_val;
bool m_displacement_is_identity{true};
};
}
} // namespace mean_field::mapping

View File

@@ -0,0 +1,87 @@
module;
#include <mfem.hpp>
export module mean_field:mapping.transformations;
export import :mapping.types;
export namespace mean_field::mapping {
void MapHDivFluxToPhysical(
const MappingPointContext &context,
const mfem::Vector &reference_flux,
mfem::Vector &physical_flux
);
void MapPhysicalFluxToHDivReference(
const MappingPointContext &context,
const mfem::Vector &physical_flux,
mfem::Vector &reference_flux
);
void MapReferenceGradientToPhysical(
const MappingPointContext &context,
const mfem::Vector &reference_gradient,
mfem::Vector &physical_gradient
);
void MapPhysicalGradientToReference(
const MappingPointContext &context,
const mfem::Vector &physical_gradient,
mfem::Vector &reference_gradient
);
void MapReferenceVectorGradientToPhysical(
const MappingPointContext &context,
const mfem::DenseMatrix &reference_gradient,
mfem::DenseMatrix &physical_gradient
);
void MapPhysicalVectorGradientToReference(
const MappingPointContext &context,
const mfem::DenseMatrix &physical_gradient,
mfem::DenseMatrix &reference_gradient
);
[[nodiscard]] double MapHDivDivergenceToPhysical(
const MappingPointContext &context,
double reference_divergence
);
void ComputeHDivMassTensor(
const MappingPointContext &context,
mfem::DenseMatrix &mass_tensor
);
void ComputeScalarDiffusionTensor(
const MappingPointContext &context,
mfem::DenseMatrix &diffusion_tensor
);
void MapHCurlFieldToPhysical(
const MappingPointContext &context,
const mfem::Vector &reference_field,
mfem::Vector &physical_field
);
void MapPhysicalFieldToHCurlReference(
const MappingPointContext &context,
const mfem::Vector &physical_field,
mfem::Vector &reference_field
);
void MapHCurlCurlToPhysical(
const MappingPointContext &context,
const mfem::Vector &reference_curl,
mfem::Vector &physical_curl
);
void MapPhysicalCurlToHCurlReference(
const MappingPointContext &context,
const mfem::Vector &physical_curl,
mfem::Vector &reference_curl
);
void ComputeHCurlMassTensor(
const MappingPointContext &context,
mfem::DenseMatrix &mass_tensor
);
void ComputeHCurlCurlTensor(
const MappingPointContext &context,
mfem::DenseMatrix &curl_tensor
);
void ComputeHDivMassTensorVariation(
const MappingPointContext &context,
const MappingPointVariation &variation,
mfem::DenseMatrix &mass_tensor_variation
);
} // namespace mean_field::mapping

View File

@@ -1,10 +1,77 @@
module;
#include <cstdint>
#include <mfem.hpp>
export module mean_field:mapping.types;
namespace mean_field::mapping {
enum class COORDINATE_SPACE : uint8_t {
PHYSICAL,
REFERENCE
export namespace mean_field::mapping {
enum class COORDINATE_SPACE : uint8_t { PHYSICAL, REFERENCE };
enum class MappingStatus : uint8_t {
valid,
invalid_dimension,
non_finite_input,
invalid_reference_radius,
at_compactified_infinity,
outside_reference_domain,
non_finite_result,
non_positive_determinant
};
}
struct VolumeQuadratureContext {
mfem::DenseMatrix J_inv;
double detJ;
double weight;
};
struct FaceQuadratureContext {
mfem::Vector normal;
double ds;
double v_dot_n_scale;
};
struct MappingPointContext {
mfem::Vector reference_position;
mfem::Vector displaced_position;
mfem::Vector physical_position;
mfem::DenseMatrix displacement_jacobian;
mfem::DenseMatrix mapping_jacobian;
mfem::DenseMatrix inverse_mapping_jacobian;
double mapping_determinant{0.0};
bool compactified{false};
};
struct VolumeMappingContext {
MappingPointContext mapping;
VolumeQuadratureContext quadrature;
};
struct FaceMappingContext {
MappingPointContext mapping;
FaceQuadratureContext quadrature;
mfem::Vector reference_normal;
double reference_surface_weight{0.0};
double physical_surface_weight{0.0};
};
struct MappingPointVariation {
mfem::Vector displacement_variation;
mfem::Vector physical_position_variation;
mfem::DenseMatrix displacement_jacobian_variation;
mfem::DenseMatrix mapping_jacobian_variation;
mfem::DenseMatrix inverse_mapping_jacobian_variation;
double mapping_determinant_variation{0.0};
};
struct VolumeMappingVariation {
MappingPointVariation mapping;
mfem::DenseMatrix inverse_element_jacobian_variation;
double weight_variation{0.0};
};
struct FaceMappingVariation {
MappingPointVariation mapping;
mfem::Vector physical_normal_variation;
double physical_surface_weight_variation{0.0};
double normal_flux_scale_variation{0.0};
};
} // namespace mean_field::mapping

View File

@@ -5,11 +5,18 @@ export import :utils.misc;
export import :utils.user;
export import :utils.domain;
export import :physics.gravity;
export import :physics.solid_body;
export import :physics.barotrope;
export import :physics.contexts;
export import :boundary.contexts;
export import :analysis.integral;
export import :mapping.domain_mapper;
export import :mapping.coefficients;
export import :mapping.compactification;
export import :mapping.kelvin;
export import :mapping.transformations;
export import :mapping.types;
export import :mapping.compactification.options;
export import :integrators.advection;
export import :integrators.centrifugal;
export import :integrators.gravity;
@@ -19,3 +26,22 @@ export import :integrators.pressure_gradient;
export import :integrators.viscosity;
export import :quadrature.policy;
export import :quadrature.mfem;
export import :solver.fields;
export import :utils.blocks;
export import :operators.gravity_field;
export import :operators.gravity_field_jacobian;
export import :operators.kernels.gravity_field;
export import :operators.prepared_gravity_source;
export import :operators.prepared_hdiv_mass;
export import :operators.context.gravity_field;
export import :field.base;
export import :field.registry;
export import :field.mfem;
export import :operators.kernels.barotropic_closure;
export import :operators.prepared_barotropic_closure;
export import :operators.context.barotropic_closure_linearization;
export import :physics.rigid_rotation;
export import :operators.kernels.hydrostatic_equilibrium;
export import :operators.context.hydrostatic_equilibrium;
export import :operators.prepared_hydrostatic_equilibrium;
export import :operators.kernels.pressure_force;

View File

@@ -0,0 +1,76 @@
module;
#include <cstdint>
#include <mfem.hpp>
export module mean_field:operators.context.barotropic_closure_linearization;
export import :fem;
export import :mapping.domain_mapper;
export import :operators.prepared_barotropic_closure;
export import :physics.barotrope;
export namespace mean_field::operators::context::barotropic {
struct BarotropicClosureRevisions final {
std::uint64_t density = 0;
std::uint64_t enthalpy = 0;
std::uint64_t displacement = 0;
[[nodiscard]] bool
operator==(const BarotropicClosureRevisions &) const noexcept = default;
};
class BarotropicClosureLinearizationContext final {
public:
BarotropicClosureLinearizationContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope
);
void Prepare(
const mfem::Vector &baseDensityTrue,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &displacementTrue,
const BarotropicClosureRevisions &revisions
);
[[nodiscard]] bool IsPrepared() const noexcept;
[[nodiscard]] bool MatchesRevisions(
const BarotropicClosureRevisions &revisions
) const noexcept;
[[nodiscard]] std::uint64_t GetPreparationCount() const noexcept;
[[nodiscard]] const BarotropicClosureRevisions &GetRevisions() const;
[[nodiscard]] const mfem::Vector &GetBaseDensityTrue() const;
[[nodiscard]] const mfem::Vector &GetBaseEnthalpyTrue() const;
[[nodiscard]] const mfem::Vector &GetDisplacementTrue() const;
[[nodiscard]]
const PreparedBarotropicClosureOperator &GetOperator() const noexcept;
void BuildResidual(mfem::Vector &residual) const;
private:
void VerifyPrepared() const;
const fem::FEM &m_f;
PreparedBarotropicClosureOperator m_operator;
mfem::Vector m_baseDensityTrue;
mfem::Vector m_baseEnthalpyTrue;
mfem::Vector m_displacementTrue;
BarotropicClosureRevisions m_revisions;
std::uint64_t m_preparationCount = 0;
bool m_isPrepared = false;
};
} // namespace mean_field::operators::context::barotropic

View File

@@ -0,0 +1,157 @@
module;
#include <compare>
#include <cstdint>
#include <memory>
#include <mfem.hpp>
export module mean_field:operators.context.gravity_field;
export import :fem;
export import :mapping.domain_mapper;
export import :operators.prepared_gravity_source;
export import :operators.prepared_hdiv_mass;
export namespace mean_field::operators::context::gravity_field {
template <typename Tag> struct Revision {
std::uint64_t value{0};
constexpr auto operator<=>(const Revision &) const = default;
};
struct DiscretizationRevisionTag { };
struct DisplacementRevisionTag { };
struct DensityRevisionTag { };
struct GravityGradientRevisionTag { };
struct GravityPotentialRevisionTag { };
using DiscretizationRevision = Revision<DiscretizationRevisionTag>;
using DisplacementRevision = Revision<DisplacementRevisionTag>;
using DensityRevision = Revision<DensityRevisionTag>;
using GravityGradientRevision = Revision<GravityGradientRevisionTag>;
using GravityPotentialRevision = Revision<GravityPotentialRevisionTag>;
struct GravityFieldRevisions {
DiscretizationRevision discretization;
DisplacementRevision displacement;
DensityRevision density;
GravityGradientRevision gravity_gradient;
GravityPotentialRevision gravity_potential;
};
struct GravityFieldStateView {
const mfem::Vector &density;
const mfem::Vector &displacement;
const mfem::Vector &gravity_gradient;
const mfem::Vector &gravity_potential;
};
struct GravityFieldGeometryPreparation {
bool reconstructed_operators{false};
bool rebuilt_mass_operator{false};
bool rebuilt_source_operator{false};
bool refreshed_variation_state{false};
[[nodiscard]] bool DidAnyWork() const noexcept {
return reconstructed_operators || rebuilt_mass_operator ||
rebuilt_source_operator || refreshed_variation_state;
}
};
class GravityFieldGeometryContext {
public:
GravityFieldGeometryContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper
);
GravityFieldGeometryContext(const GravityFieldGeometryContext &) =
delete;
GravityFieldGeometryContext &
operator=(const GravityFieldGeometryContext &) = delete;
GravityFieldGeometryContext(GravityFieldGeometryContext &&) = delete;
GravityFieldGeometryContext &
operator=(GravityFieldGeometryContext &&) = delete;
GravityFieldGeometryPreparation Prepare(
const mfem::Vector &displacement_true,
DiscretizationRevision discretization_revision,
DisplacementRevision displacement_revision
);
[[nodiscard]] const PreparedMappedHDivMassOperator &
GetMassOperator() const;
[[nodiscard]] const PreparedMappedGravitySourceOperator &
GetSourceOperator() const;
[[nodiscard]] const mfem::Vector &GetDisplacement() const;
[[nodiscard]] DiscretizationRevision
GetDiscretizationRevision() const noexcept;
[[nodiscard]] DisplacementRevision
GetDisplacementRevision() const noexcept;
[[nodiscard]] bool IsPrepared() const noexcept;
private:
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domain_mapper;
std::unique_ptr<PreparedMappedHDivMassOperator> m_mass_operator;
std::unique_ptr<PreparedMappedGravitySourceOperator> m_source_operator;
mfem::Vector m_displacement_true;
DiscretizationRevision m_discretization_revision;
DisplacementRevision m_displacement_revision;
bool m_is_prepared{false};
};
struct GravityFieldPreparationReport {
GravityFieldGeometryPreparation geometry;
bool updated_density{false};
bool updated_gravity_gradient{false};
[[nodiscard]] bool DidAnyWork() const noexcept {
return geometry.DidAnyWork() || updated_density ||
updated_gravity_gradient;
}
};
class GravityFieldLinearizationContext {
public:
GravityFieldLinearizationContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper
);
GravityFieldLinearizationContext(
const GravityFieldLinearizationContext &
) = delete;
GravityFieldLinearizationContext &
operator=(const GravityFieldLinearizationContext &) = delete;
GravityFieldLinearizationContext(GravityFieldLinearizationContext &&) =
delete;
GravityFieldLinearizationContext &
operator=(GravityFieldLinearizationContext &&) = delete;
GravityFieldPreparationReport Prepare(
const GravityFieldStateView &state,
const GravityFieldRevisions &revisions
);
[[nodiscard]] const GravityFieldGeometryContext &
GetGeometryContext() const;
[[nodiscard]] const mfem::Vector &GetDensity() const;
[[nodiscard]] const mfem::Vector &GetGravityGradient() const;
[[nodiscard]] const GravityFieldRevisions &GetRevisions() const;
[[nodiscard]] bool IsPrepared() const noexcept;
private:
const fem::FEM &m_fem;
GravityFieldGeometryContext m_geometry_context;
mfem::Vector m_density_true;
mfem::Vector m_gravity_gradient_true;
GravityFieldRevisions m_revisions;
bool m_is_prepared{false};
};
} // namespace mean_field::operators::context::gravity_field

View File

@@ -0,0 +1,154 @@
module;
#include <compare>
#include <cstdint>
#include <mfem.hpp>
export module mean_field:operators.context.hydrostatic_equilibrium;
export import :fem;
export import :mapping.domain_mapper;
export namespace mean_field::operators::context::hydrostatic {
template <typename Tag> struct DependencyStamp {
std::uint64_t identity{0};
std::uint64_t revision{0};
[[nodiscard]] constexpr bool
CanFollow(const DependencyStamp &prepared) const noexcept {
return identity != prepared.identity ||
revision >= prepared.revision;
}
constexpr auto operator<=>(const DependencyStamp &) const = default;
};
struct DiscretizationDependencyTag { };
struct EnthalpyDependencyTag { };
struct GravityPotentialDependencyTag { };
struct DisplacementDependencyTag { };
struct RotationDependencyTag { };
struct BernoulliConstantDependencyTag { };
using DiscretizationDependency =
DependencyStamp<DiscretizationDependencyTag>;
using EnthalpyDependency = DependencyStamp<EnthalpyDependencyTag>;
using GravityPotentialDependency =
DependencyStamp<GravityPotentialDependencyTag>;
using DisplacementDependency = DependencyStamp<DisplacementDependencyTag>;
using RotationDependency = DependencyStamp<RotationDependencyTag>;
using BernoulliConstantDependency =
DependencyStamp<BernoulliConstantDependencyTag>;
struct HydrostaticEquilibriumDependencies {
DiscretizationDependency discretization;
EnthalpyDependency enthalpy;
GravityPotentialDependency gravityPotential;
DisplacementDependency displacement;
RotationDependency rotation;
BernoulliConstantDependency bernoulliConstant;
constexpr auto
operator<=>(const HydrostaticEquilibriumDependencies &) const = default;
};
struct HydrostaticEquilibriumStateView {
const mfem::Vector &enthalpy;
const mfem::Vector &gravityPotential;
const mfem::Vector &displacement;
double bernoulliConstant{0.0};
};
struct HydrostaticPreparationReport {
bool preparedStaticDependencies{false};
bool preparedGeometryState{false};
bool preparedRotationDependencies{false};
bool preparedBaseState{false};
bool updatedEnthalpy{false};
bool updatedGravityPotential{false};
bool updatedDisplacement{false};
bool updatedBernoulliConstant{false};
[[nodiscard]] bool DidAnyWork() const noexcept {
return preparedStaticDependencies || preparedGeometryState ||
preparedRotationDependencies || preparedBaseState;
}
};
struct HydrostaticPreparationStatistics {
std::uint64_t staticPreparations{0};
std::uint64_t geometryPreparations{0};
std::uint64_t rotationPreparations{0};
std::uint64_t baseStatePreparations{0};
constexpr auto
operator<=>(const HydrostaticPreparationStatistics &) const = default;
};
class HydrostaticEquilibriumContext {
public:
HydrostaticEquilibriumContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper
);
HydrostaticEquilibriumContext(const HydrostaticEquilibriumContext &) =
delete;
HydrostaticEquilibriumContext &
operator=(const HydrostaticEquilibriumContext &) = delete;
HydrostaticEquilibriumContext(HydrostaticEquilibriumContext &&) =
delete;
HydrostaticEquilibriumContext &
operator=(HydrostaticEquilibriumContext &&) = delete;
HydrostaticPreparationReport Prepare(
const HydrostaticEquilibriumStateView &state,
const HydrostaticEquilibriumDependencies &dependencies
);
[[nodiscard]] bool IsPrepared() const noexcept;
[[nodiscard]] bool MatchesDependencies(
const HydrostaticEquilibriumDependencies &dependencies
) const noexcept;
[[nodiscard]] const HydrostaticEquilibriumDependencies &
GetDependencies() const;
[[nodiscard]] const HydrostaticPreparationStatistics &
GetPreparationStatistics() const noexcept;
[[nodiscard]] const mfem::Vector &GetBaseEnthalpyTrue() const;
[[nodiscard]] const mfem::Vector &GetBaseGravityPotentialTrue() const;
[[nodiscard]] const mfem::Vector &GetDisplacementTrue() const;
[[nodiscard]] double GetBernoulliConstant() const;
private:
void VerifyPrepared() const;
const fem::FEM &m_f;
const mapping::DomainMapperStateless &m_domainMapper;
mfem::Vector m_baseEnthalpyTrue;
mfem::Vector m_baseGravityPotentialTrue;
mfem::Vector m_displacementTrue;
double m_bernoulliConstant{0.0};
HydrostaticEquilibriumDependencies m_dependencies;
HydrostaticPreparationStatistics m_statistics;
bool m_isPrepared{false};
};
} // namespace mean_field::operators::context::hydrostatic

View File

@@ -0,0 +1,147 @@
module;
#include <cstdint>
#include <memory>
#include <mfem.hpp>
export module mean_field:operators.gravity_field;
export import :fem;
export import :mapping.domain_mapper;
export import :operators.gravity_field_jacobian;
export import :operators.context.gravity_field;
export namespace mean_field::operators {
enum class GravityResidualBlock : std::uint8_t {
gradient_equation = 0,
poisson_equation = 1,
count = 2
};
constexpr int
gravity_residual_block_index(const GravityResidualBlock block) noexcept {
return static_cast<int>(block);
}
inline constexpr int gravity_residual_block_count =
gravity_residual_block_index(GravityResidualBlock::count);
class GravityFieldOperator final : public mfem::Operator {
public:
GravityFieldOperator(
fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper,
context::gravity_field::GravityFieldLinearizationContext
&linearization_context,
const mfem::Array<int> &state_true_offsets,
GravityFieldJacobianOperator &jacobian
);
context::gravity_field::GravityFieldPreparationReport Prepare(
const mfem::Vector &state,
const context::gravity_field::GravityFieldRevisions &revisions
);
void Mult(
const mfem::Vector &state,
mfem::Vector &residual
) const override;
Operator &GetGradient(const mfem::Vector &state) const override;
[[nodiscard]] const mfem::Array<int> &
GetStateTrueOffsets() const noexcept;
[[nodiscard]] const mfem::Array<int> &
GetResidualTrueOffsets() const noexcept;
[[nodiscard]] context::gravity_field::GravityFieldLinearizationContext &
GetLinearizationContext() noexcept;
[[nodiscard]] const context::gravity_field::
GravityFieldLinearizationContext &
GetLinearizationContext() const noexcept;
void ApplyGravityUnknowns(
const mfem::Vector &gravity_gradient,
const mfem::Vector &gravity_potential,
const context::gravity_field::GravityFieldGeometryContext
&geometry_context,
mfem::Vector &action
) const;
void ApplyDensitySource(
const mfem::Vector &density,
const context::gravity_field::GravityFieldGeometryContext
&geometry_context,
mfem::Vector &action
) const;
private:
fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domain_mapper;
context::gravity_field::GravityFieldLinearizationContext
&m_linearization_context;
mfem::Array<int> m_state_true_offsets;
mfem::Array<int> m_residual_true_offsets;
GravityFieldJacobianOperator &m_jacobian;
};
class ReducedGravityFieldOperator final : public mfem::Operator {
public:
ReducedGravityFieldOperator(
GravityFieldOperator &gravity_field_operator,
context::gravity_field::GravityFieldGeometryContext
&gravity_field_geometry_context,
const mfem::Vector &displacement
);
ReducedGravityFieldOperator(const ReducedGravityFieldOperator &) =
delete;
ReducedGravityFieldOperator &
operator=(const ReducedGravityFieldOperator &) = delete;
ReducedGravityFieldOperator(ReducedGravityFieldOperator &&) = delete;
ReducedGravityFieldOperator &
operator=(ReducedGravityFieldOperator &&) = delete;
void SetDisplacement(const mfem::Vector &displacement);
[[nodiscard]] const mfem::Vector &GetDisplacement() const;
void BuildRightHandSide(
const mfem::Vector &density,
mfem::Vector &right_hand_side
) const;
void Mult(
const mfem::Vector &gravity_state,
mfem::Vector &action
) const override;
[[nodiscard]] GravityFieldOperator &GetGravityFieldOperator() noexcept;
[[nodiscard]] const GravityFieldOperator &
GetGravityFieldOperator() const noexcept;
[[nodiscard]] context::gravity_field::GravityFieldGeometryContext &
GetGeometryContext() noexcept;
[[nodiscard]] const context::gravity_field::
GravityFieldGeometryContext &
GetGeometryContext() const noexcept;
[[nodiscard]] const mfem::Array<int> &
GetGravityTrueOffsets() const noexcept;
private:
void ValidateDisplacement(const mfem::Vector &displacement) const;
void ValidateDensity(const mfem::Vector &density) const;
void ValidateGravityState(const mfem::Vector &gravity_state) const;
private:
GravityFieldOperator &m_gravity_field_operator;
mfem::Array<int> m_gravity_true_offsets;
context::gravity_field::GravityFieldGeometryContext
&m_gravity_field_geometry_context;
};
} // namespace mean_field::operators

View File

@@ -0,0 +1,38 @@
module;
#include <mfem.hpp>
export module mean_field:operators.gravity_field_jacobian;
export import :fem;
export import :mapping.domain_mapper;
export import :operators.context.gravity_field;
export namespace mean_field::operators {
class GravityFieldJacobianOperator final : public mfem::Operator {
public:
GravityFieldJacobianOperator(
fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper,
const context::gravity_field::GravityFieldLinearizationContext
&linearization_context,
const mfem::Array<int> &state_true_offsets,
const mfem::Array<int> &residual_true_offsets
);
void Mult(
const mfem::Vector &direction,
mfem::Vector &action
) const override;
[[nodiscard]] const context::gravity_field::
GravityFieldLinearizationContext &
GetLinearizationContext() const noexcept;
private:
fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domain_mapper;
const context::gravity_field::GravityFieldLinearizationContext
&m_linearization_context;
mfem::Array<int> m_state_true_offsets;
mfem::Array<int> m_residual_true_offsets;
};
} // namespace mean_field::operators

View File

@@ -0,0 +1,51 @@
module;
#include <mfem.hpp>
export module mean_field:operators.kernels.barotropic_closure;
export import :fem;
export import :mapping.domain_mapper;
export import :physics.barotrope;
export namespace mean_field::operators::kernels {
void apply_barotropic_closure(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope,
const mfem::Vector &densityTrue,
const mfem::Vector &enthalpyTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &residual
);
void apply_barotropic_closure_density_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope,
const mfem::Vector &densityVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &action
);
void apply_barotropic_closure_enthalpy_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &enthalpyVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &action
);
void apply_barotropic_closure_displacement_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope,
const mfem::Vector &baseDensityTrue,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &displacementTrue,
const mfem::Vector &displacementVariationTrue,
mfem::Vector &action
);
} // namespace mean_field::operators::kernels

View File

@@ -0,0 +1,42 @@
module;
#include <mfem.hpp>
export module mean_field:operators.kernels.gravity_field;
export import :mapping.domain_mapper;
export import :fem;
export namespace mean_field::operators::kernels {
void apply_mapped_hdiv_mass(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper,
const mfem::Vector &gravity_gradient_true,
const mfem::Vector &displacement_true,
mfem::Vector &action
);
void apply_mapped_source(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper,
const mfem::Vector &density_true,
const mfem::Vector &displacement_true,
mfem::Vector &action
);
void apply_mapped_hdiv_mass_variation(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper,
const mfem::Vector &gravity_gradient_true,
const mfem::Vector &displacement_true,
const mfem::Vector &displacement_variation_true,
mfem::Vector &action
);
void apply_mapped_source_variation(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper,
const mfem::Vector &density_true,
const mfem::Vector &displacement_true,
const mfem::Vector &displacement_variation_true,
mfem::Vector &action_variation
);
} // namespace mean_field::operators::kernels

View File

@@ -0,0 +1,73 @@
module;
#include <mfem.hpp>
export module mean_field:operators.kernels.hydrostatic_equilibrium;
export import :fem;
export import :mapping.domain_mapper;
export import :physics.rigid_rotation;
export namespace mean_field::operators::kernels {
void apply_hydrostatic_equilibrium(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::RigidRotation &rotation,
const mfem::Vector &enthalpyTrue,
const mfem::Vector &potentialTrue,
const mfem::Vector &displacementTrue,
double bernoulliConstant,
mfem::Vector &residual
);
void apply_hydrostatic_equilibrium_enthalpy_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &enthalpyVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &action
);
void apply_hydrostatic_equilibrium_potential_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &potentialVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &action
);
void apply_hydrostatic_equilibrium_constant_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
double constantVariation,
const mfem::Vector &displacementTrue,
mfem::Vector &action
);
void apply_hydrostatic_equilibrium_displacement_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::RigidRotation &rotation,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &basePotentialTrue,
const mfem::Vector &baseDisplacementTrue,
double baseBernoulliConstant,
const mfem::Vector &displacementVariationTrue,
mfem::Vector &action
);
void apply_hydrostatic_equilibrium_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::RigidRotation &rotation,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &basePotentialTrue,
const mfem::Vector &baseDisplacementTrue,
double baseBernoulliConstant,
const mfem::Vector &enthalpyVariationTrue,
const mfem::Vector &potentialVariationTrue,
double constantVariation,
const mfem::Vector &displacementVariationTrue,
mfem::Vector &action
);
} // namespace mean_field::operators::kernels

View File

@@ -0,0 +1,20 @@
module;
#include <mfem.hpp>
export module mean_field:operators.kernels.pressure_force;
export import :fem;
export import :mapping.domain_mapper;
export import :physics.barotrope;
export namespace mean_field::operators::kernels {
void apply_pressure_force_residual(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope,
const mfem::Vector &enthalpyTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &residualTrue
);
} // namespace mean_field::operators::kernels

View File

@@ -0,0 +1,90 @@
module;
#include <cstdint>
#include <mfem.hpp>
#include <vector>
export module mean_field:operators.prepared_barotropic_closure;
export import :fem;
export import :mapping.domain_mapper;
export import :physics.barotrope;
export namespace mean_field::operators {
class PreparedBarotropicClosureOperator final : public mfem::Operator {
public:
PreparedBarotropicClosureOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope
);
void Prepare(
const mfem::Vector &baseDensityTrue,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &displacementTrue
);
void Mult(
const mfem::Vector &densityVariationTrue,
const mfem::Vector &enthalpyVariationTrue,
const mfem::Vector &displacementVariationTrue,
mfem::Vector &action
) const;
void Mult(
const mfem::Vector &combinedVariation,
mfem::Vector &action
) const override;
void BuildResidual(mfem::Vector &residual) const;
[[nodiscard]] bool IsPrepared() const noexcept;
[[nodiscard]] std::uint64_t GetPreparationCount() const noexcept;
[[nodiscard]] int GetDensitySize() const noexcept;
[[nodiscard]] int GetEnthalpySize() const noexcept;
private:
void VerifyPrepared() const;
void Mult(
const mfem::Vector &densityVariationTrue,
const mfem::Vector &enthalpyVariationTrue,
mfem::Vector &action
) const;
struct ElementPAData {
mfem::Array<int> densityDofs;
mfem::Array<int> enthalpyDofs;
mfem::DofTransformation *densityDofTransformation{nullptr};
mfem::DofTransformation *enthalpyDofTransformation{nullptr};
mfem::DenseMatrix densityBasis;
mfem::DenseMatrix enthalpyBasis;
mfem::Vector weightedResidual;
mfem::Vector quadratureWeights;
mfem::Vector weightedEnthalpyDerivative;
};
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domainMapper;
const physics::PolytropicBarotrope &m_barotrope;
std::vector<ElementPAData> m_elements;
mfem::Vector m_baseDensityTrue;
mfem::Vector m_baseEnthalpyTrue;
mfem::Vector m_baseDisplacementTrue;
int m_densitySize{0};
int m_enthalpySize{0};
std::uint64_t m_preparationCount{0};
bool m_isPrepared{false};
};
} // namespace mean_field::operators

View File

@@ -0,0 +1,61 @@
module;
#include <cstdint>
#include <memory>
#include <mfem.hpp>
#include <vector>
export module mean_field:operators.prepared_gravity_source;
export import :fem;
export import :mapping.domain_mapper;
export namespace mean_field::operators {
class PreparedMappedGravitySourceOperator final : public mfem::Operator {
public:
PreparedMappedGravitySourceOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper
);
void Prepare(const mfem::Vector &displacement_true);
void Mult(
const mfem::Vector &density_true,
mfem::Vector &action
) const override;
[[nodiscard]] bool IsPrepared() const noexcept;
[[nodiscard]] std::uint64_t GetPreparationCount() const noexcept;
void MultTranspose(
const mfem::Vector &potential_true,
mfem::Vector &action
) const override;
private:
struct ElementPAData {
int element_id{-1};
mfem::Array<int> density_dofs;
mfem::Array<int> potential_dofs;
mfem::DofTransformation *density_dof_transformation{nullptr};
mfem::DofTransformation *potential_dof_transformation{nullptr};
// Rows are quadrature points; columns are element DOFs.
mfem::DenseMatrix density_basis;
mfem::DenseMatrix potential_basis;
// Contains quadrature weight, mesh Jacobian, mapped Jacobian,
// and 4*pi*G.
mfem::Vector quadrature_data;
};
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domain_mapper;
mfem::Array<int> m_stellar_marker;
std::vector<ElementPAData> m_elements;
std::uint64_t m_preparation_count{0};
bool m_is_prepared{false};
};
} // namespace mean_field::operators

View File

@@ -0,0 +1,40 @@
module;
#include <cstdint>
#include <memory>
#include <mfem.hpp>
export module mean_field:operators.prepared_hdiv_mass;
export import :fem;
export import :mapping.domain_mapper;
export namespace mean_field::operators {
class PreparedMappedHDivMassOperator final : public mfem::Operator {
public:
PreparedMappedHDivMassOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper
);
void Prepare(const mfem::Vector &displacement_true);
void Mult(
const mfem::Vector &gravity_gradient_true,
mfem::Vector &action
) const override;
[[nodiscard]] bool IsPrepared() const noexcept;
[[nodiscard]] std::uint64_t GetPreparationCount() const noexcept;
private:
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domain_mapper;
mfem::Array<int> m_stellar_marker;
mfem::Array<int> m_vacuum_marker;
std::unique_ptr<mfem::MatrixCoefficient> m_stellar_mass_coefficient;
std::unique_ptr<mfem::MatrixCoefficient> m_vacuum_mass_coefficient;
std::unique_ptr<mfem::ParBilinearForm> m_mass_form;
std::uint64_t m_preparation_count{0};
bool m_is_prepared{false};
};
} // namespace mean_field::operators

View File

@@ -0,0 +1,278 @@
module;
#include <compare>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <vector>
#include <mfem.hpp>
export module mean_field:operators.prepared_hydrostatic_equilibrium;
export import :fem;
export import :mapping.domain_mapper;
export import :operators.context.hydrostatic_equilibrium;
export import :physics.rigid_rotation;
export namespace mean_field::operators {
struct PreparedHydrostaticEquilibriumReport {
context::hydrostatic::HydrostaticPreparationReport contextReport;
bool updatedRotation{false};
bool preparedAlgebraicJacobianBlocks{false};
bool preparedDisplacementJacobianData{false};
bool preparedResidual{false};
[[nodiscard]] bool DidAnyWork() const noexcept {
return contextReport.DidAnyWork() || updatedRotation ||
preparedAlgebraicJacobianBlocks ||
preparedDisplacementJacobianData || preparedResidual;
}
};
struct PreparedHydrostaticAlgebraicJacobianStatistics {
std::uint64_t preparations{0};
std::uint64_t enthalpyApplications{0};
std::uint64_t gravityPotentialApplications{0};
std::uint64_t bernoulliConstantApplications{0};
std::uint64_t combinedApplications{0};
constexpr auto operator<=>(
const PreparedHydrostaticAlgebraicJacobianStatistics &
) const = default;
};
struct PreparedHydrostaticDisplacementJacobianStatistics {
std::uint64_t preparations{0};
std::uint64_t applications{0};
constexpr auto operator<=>(
const PreparedHydrostaticDisplacementJacobianStatistics &
) const = default;
};
struct PreparedHydrostaticCompleteJacobianStatistics {
std::uint64_t applications{0};
constexpr auto operator<=>(
const PreparedHydrostaticCompleteJacobianStatistics &
) const = default;
};
enum class HydrostaticJacobianInputBlock : int {
enthalpy = 0,
gravityPotential = 1,
bernoulliConstant = 2,
displacement = 3
};
class HydrostaticJacobianBlockLayout final {
public:
explicit HydrostaticJacobianBlockLayout(const fem::FEM &f);
[[nodiscard]] int Offset(HydrostaticJacobianInputBlock block) const;
[[nodiscard]] int Size(HydrostaticJacobianInputBlock block) const;
[[nodiscard]] int GetTotalSize() const noexcept;
[[nodiscard]] int GetResidualSize() const noexcept;
private:
int m_enthalpySize{0};
int m_gravityPotentialSize{0};
int m_displacementSize{0};
int m_totalSize{0};
int m_residualSize{0};
};
class PreparedHydrostaticEquilibriumOperator final {
public:
PreparedHydrostaticEquilibriumOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper
);
PreparedHydrostaticEquilibriumOperator(
const PreparedHydrostaticEquilibriumOperator &
) = delete;
PreparedHydrostaticEquilibriumOperator &
operator=(const PreparedHydrostaticEquilibriumOperator &) = delete;
PreparedHydrostaticEquilibriumOperator(
PreparedHydrostaticEquilibriumOperator &&
) = delete;
PreparedHydrostaticEquilibriumOperator &
operator=(PreparedHydrostaticEquilibriumOperator &&) = delete;
PreparedHydrostaticEquilibriumReport Prepare(
const context::hydrostatic::HydrostaticEquilibriumStateView &state,
const context::hydrostatic::HydrostaticEquilibriumDependencies
&dependencies,
const physics::RigidRotation &rotation
);
void BuildResidual(mfem::Vector &residual) const;
void ApplyEnthalpyJacobianAction(
const mfem::Vector &enthalpyVariation,
mfem::Vector &action
) const;
void ApplyGravityPotentialJacobianAction(
const mfem::Vector &gravityPotentialVariation,
mfem::Vector &action
) const;
void ApplyBernoulliConstantJacobianAction(
double bernoulliConstantVariation,
mfem::Vector &action
) const;
void ApplyAlgebraicJacobianAction(
const mfem::Vector &enthalpyVariation,
const mfem::Vector &gravityPotentialVariation,
double bernoulliConstantVariation,
mfem::Vector &action
) const;
void ApplyDisplacementJacobianAction(
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const;
void ApplyCompleteJacobianAction(
const mfem::Vector &enthalpyVariation,
const mfem::Vector &gravityPotentialVariation,
double bernoulliConstantVariation,
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const;
[[nodiscard]] bool IsPrepared() const noexcept;
[[nodiscard]] const context::hydrostatic::
HydrostaticPreparationStatistics &
GetContextPreparationStatistics() const noexcept;
[[nodiscard]] std::uint64_t
GetResidualPreparationCount() const noexcept;
[[nodiscard]] std::uint64_t
GetResidualApplicationCount() const noexcept;
[[nodiscard]] const PreparedHydrostaticAlgebraicJacobianStatistics &
GetAlgebraicJacobianStatistics() const noexcept;
[[nodiscard]] const PreparedHydrostaticDisplacementJacobianStatistics &
GetDisplacementJacobianStatistics() const noexcept;
[[nodiscard]] const PreparedHydrostaticCompleteJacobianStatistics &
GetCompleteJacobianStatistics() const noexcept;
[[nodiscard]] std::size_t GetStellarElementCount() const noexcept;
[[nodiscard]] const fem::FEM &GetFEM() const noexcept;
private:
struct ElementPAData {
int elementId{-1};
mfem::Array<int> enthalpyDofs;
mfem::Array<int> gravityPotentialDofs;
mfem::Array<int> displacementDofs;
mfem::DofTransformation *enthalpyDofTransformation{nullptr};
mfem::DofTransformation *gravityPotentialDofTransformation{nullptr};
mfem::DofTransformation *displacementDofTransformation{nullptr};
const mfem::IntegrationRule *integrationRule{nullptr};
// Rows are quadrature points and columns are element DOFs.
mfem::DenseMatrix enthalpyBasis;
mfem::DenseMatrix gravityPotentialBasis;
// Rows are quadrature points and columns are physical components.
mfem::DenseMatrix physicalPositions;
mfem::Vector quadratureWeights;
std::vector<mapping::VolumeMappingContext> baseMappingContexts;
std::optional<mapping::ElementDisplacementData>
baseDisplacementData;
std::optional<mapping::ElementCompactificationData>
compactificationData;
mfem::Vector rotationPotential;
mfem::DenseMatrix rotationGradient;
mfem::Vector hydrostaticImbalance;
mfem::Vector weightedResidual;
// Geometry-dependent algebraic Jacobian blocks.
mfem::DenseMatrix enthalpyJacobian;
mfem::DenseMatrix gravityPotentialJacobian;
mfem::Vector bernoulliConstantJacobian;
};
void PrepareStaticPlan();
void PrepareGeometry();
void PrepareAlgebraicJacobianBlocks();
void PrepareRotation();
void PrepareBaseState();
void FinalizeDisplacementJacobianPreparation();
void AssembleCachedResidual();
void VerifyPrepared() const;
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domainMapper;
context::hydrostatic::HydrostaticEquilibriumContext m_context;
std::optional<physics::RigidRotation> m_rotation;
std::vector<ElementPAData> m_elements;
mfem::Vector m_cachedResidual;
std::uint64_t m_residualPreparationCount{0};
mutable std::uint64_t m_residualApplicationCount{0};
mutable PreparedHydrostaticAlgebraicJacobianStatistics
m_algebraicJacobianStatistics;
mutable PreparedHydrostaticDisplacementJacobianStatistics
m_displacementJacobianStatistics;
mutable PreparedHydrostaticCompleteJacobianStatistics
m_completeJacobianStatistics;
bool m_isPrepared{false};
};
class PreparedHydrostaticEquilibriumJacobianOperator final
: public mfem::Operator {
public:
PreparedHydrostaticEquilibriumJacobianOperator(
const fem::FEM &f,
const PreparedHydrostaticEquilibriumOperator &preparedOperator
);
void Mult(
const mfem::Vector &direction,
mfem::Vector &action
) const override;
[[nodiscard]] const HydrostaticJacobianBlockLayout &
GetLayout() const noexcept;
private:
HydrostaticJacobianBlockLayout m_layout;
const PreparedHydrostaticEquilibriumOperator &m_preparedOperator;
};
} // namespace mean_field::operators

View File

@@ -0,0 +1,173 @@
module;
#include <cmath>
#include <format>
#include <stdexcept>
export module mean_field:physics.barotrope;
export namespace mean_field::physics {
class PolytropicBarotrope final {
public:
PolytropicBarotrope(
const double polytropic_index,
const double polytropic_constant
)
: m_polytropic_index(polytropic_index),
m_polytropic_constant(polytropic_constant),
m_enthalpy_scale((polytropic_index + 1.0) * polytropic_constant) {
if (!std::isfinite(polytropic_index) || polytropic_index < 1.0) {
throw std::invalid_argument(
std::format(
"The differentiable polytropic closure requires a "
"finite polytropic index greater than or equal to one. "
"Instead a value of {} has been provided",
polytropic_index
)
);
}
if (!std::isfinite(polytropic_constant) ||
polytropic_constant <= 0.0) {
throw std::invalid_argument(
std::format(
"The polytropic constant must be finite and positive. "
"Instead a value of {} has been provided",
polytropic_constant
)
);
}
};
[[nodiscard]] double polytropic_index() const noexcept {
return m_polytropic_index;
}
[[nodiscard]] double polytropic_constant() const noexcept {
return m_polytropic_constant;
}
[[nodiscard]] double enthalpy_scale() const noexcept {
return m_enthalpy_scale;
}
[[nodiscard]] double pressure_from_density(const double density) const {
validate_nonnegativity(density, "density");
if (density == 0.0) {
return 0.0;
}
return m_polytropic_constant *
std::pow(density, 1.0 + 1.0 / m_polytropic_index);
}
[[nodiscard]] double enthalpy_from_density(const double density) const {
validate_nonnegativity(density, "density");
if (density == 0.0) {
return 0.0;
}
return m_enthalpy_scale *
std::pow(density, 1.0 / m_polytropic_index);
}
[[nodiscard]] double
density_from_enthalpy(const double enthalpy) const {
validate_finite(enthalpy, "enthalpy");
if (enthalpy <= 0.0) {
return 0.0;
}
return std::pow(enthalpy / m_enthalpy_scale, m_polytropic_index);
}
[[nodiscard]] double
pressure_from_enthalpy(const double enthalpy) const {
validate_finite(enthalpy, "enthalpy");
if (enthalpy <= 0.0) {
return 0.0;
}
return density_from_enthalpy(enthalpy) * enthalpy /
(m_polytropic_index + 1.0);
}
[[nodiscard]] double
density_derivative_from_enthalpy(const double enthalpy) const {
validate_finite(enthalpy, "enthalpy");
if (enthalpy < 0.0) {
return 0.0;
}
if (enthalpy == 0.0) {
return m_polytropic_index == 1.0 ? 1.0 / m_enthalpy_scale : 0.0;
}
return m_polytropic_index / m_enthalpy_scale *
std::pow(
enthalpy / m_enthalpy_scale, m_polytropic_index - 1.0
);
}
[[nodiscard]] double
pressure_derivative_from_enthalpy(const double enthalpy) const {
validate_finite(enthalpy, "enthalpy");
if (enthalpy <= 0.0) {
return 0.0;
}
return density_from_enthalpy(enthalpy);
}
[[nodiscard]] double
pressure_derivative_from_density(const double density) const {
validate_nonnegativity(density, "density");
if (density == 0.0) {
return 0.0;
}
return m_polytropic_constant * (1.0 + 1.0 / m_polytropic_index) *
std::pow(density, 1.0 / m_polytropic_index);
}
private:
static void validate_finite(
const double value,
const char *quantity
) {
if (!std::isfinite(value)) {
throw std::domain_error(
std::format(
"The {} must be finite. Instead a value of {} has been "
"provided",
quantity, value
)
);
}
}
static void validate_nonnegativity(
const double value,
const char *quantity
) {
validate_finite(value, quantity);
if (value < 0.0) {
throw std::domain_error(
std::format(
"The {} must be non-negative. Instead a value of {} "
"has been "
"provided",
quantity, value
)
);
}
}
double m_polytropic_index;
double m_polytropic_constant;
double m_enthalpy_scale;
};
} // namespace mean_field::physics

View File

@@ -1,4 +1,5 @@
module;
#include <memory>
#include <mfem.hpp>
export module mean_field:physics.contexts;
@@ -23,6 +24,6 @@ export namespace mean_field::physics {
std::unique_ptr<mfem::HypreParMatrix> Schur;
std::unique_ptr<mfem::MatrixCoefficient> mapped_hdiv_mass_coeff;
std::unique_ptr<mfem::Operator> source_form;
};
}
} // namespace mean_field::physics

View File

@@ -10,7 +10,10 @@ export namespace mean_field::physics {
mfem::ParGridFunction gradPhi;
mfem::ParGridFunction phi;
explicit GravitySolution(fem::FEM& fem): gradPhi(fem.RT_fes.get()), phi(fem.L2_fes.get()) {}
explicit GravitySolution(fem::FEM &fem)
: gradPhi(fem.gravityFluxFes.get()),
phi(fem.gravityPotentialFes.get()) {
}
};
GravitySolution grav_potential(
@@ -20,6 +23,13 @@ export namespace mean_field::physics {
bool phi_warm = false
);
GravitySolution grav_potential_new(
fem::FEM &f,
const utils::Args &args,
const mfem::GridFunction &rho,
const mfem::GridFunction &displacement
);
mfem::GridFunction get_potential(
fem::FEM &fem,
const utils::Args &args,
@@ -40,6 +50,4 @@ export namespace mean_field::physics {
);
void update_stiffness_matrix(fem::FEM &fem);
}
} // namespace mean_field::physics

View File

@@ -0,0 +1,127 @@
module;
#include <cmath>
#include <mfem.hpp>
export module mean_field:physics.rigid_rotation;
export namespace mean_field::physics {
class RigidRotation final {
public:
RigidRotation(
const mfem::Vector &angularVelocity,
const mfem::Vector &center
)
: m_angularVelocity(angularVelocity),
m_center(center) {
MFEM_VERIFY(
m_angularVelocity.Size() == 3,
"RigidRotation requires a three-dimensional "
"angular-velocity vector."
);
MFEM_VERIFY(
m_center.Size() == 3,
"RigidRotation requires a three-dimensional center."
);
for (int component = 0; component < 3; ++component) {
MFEM_VERIFY(
std::isfinite(m_angularVelocity(component)),
"RigidRotation received a non-finite "
"angular-velocity component."
);
MFEM_VERIFY(
std::isfinite(m_center(component)),
"RigidRotation received a non-finite center component."
);
}
}
[[nodiscard]] double
potential(const mfem::Vector &physicalPosition) const {
MFEM_VERIFY(
physicalPosition.Size() == 3,
"RigidRotation::potential requires a "
"three-dimensional position."
);
const double relativeX = physicalPosition(0) - m_center(0);
const double relativeY = physicalPosition(1) - m_center(1);
const double relativeZ = physicalPosition(2) - m_center(2);
const double crossX = m_angularVelocity(1) * relativeZ -
m_angularVelocity(2) * relativeY;
const double crossY = m_angularVelocity(2) * relativeX -
m_angularVelocity(0) * relativeZ;
const double crossZ = m_angularVelocity(0) * relativeY -
m_angularVelocity(1) * relativeX;
return 0.5 * (crossX * crossX + crossY * crossY + crossZ * crossZ);
}
[[nodiscard]] double potential_directional_derivative(
const mfem::Vector &physicalPosition,
const mfem::Vector &physicalPositionVariation
) const {
MFEM_VERIFY(
physicalPosition.Size() == 3,
"RigidRotation derivative requires a "
"three-dimensional position."
);
MFEM_VERIFY(
physicalPositionVariation.Size() == 3,
"RigidRotation derivative requires a "
"three-dimensional direction."
);
double angularVelocitySquared = 0.0;
double angularVelocityDotPosition = 0.0;
for (int component = 0; component < 3; ++component) {
const double relativePosition =
physicalPosition(component) - m_center(component);
angularVelocitySquared +=
m_angularVelocity(component) * m_angularVelocity(component);
angularVelocityDotPosition +=
m_angularVelocity(component) * relativePosition;
}
double derivative = 0.0;
for (int component = 0; component < 3; ++component) {
const double relativePosition =
physicalPosition(component) - m_center(component);
const double gradientComponent =
angularVelocitySquared * relativePosition -
angularVelocityDotPosition * m_angularVelocity(component);
derivative +=
gradientComponent * physicalPositionVariation(component);
}
return derivative;
}
[[nodiscard]] const mfem::Vector &angular_velocity() const noexcept {
return m_angularVelocity;
}
[[nodiscard]] const mfem::Vector &center() const noexcept {
return m_center;
}
private:
mfem::Vector m_angularVelocity;
mfem::Vector m_center;
};
} // namespace mean_field::physics

View File

@@ -5,5 +5,8 @@ export module mean_field:physics.solid_body;
export import :fem;
export namespace mean_field::physics {
double compute_moment_of_inertia(const fem::FEM &fem, const mfem::GridFunction &rho_ref);
double compute_moment_of_inertia(
const fem::FEM &fem,
const mfem::GridFunction &rho_ref
);
}

View File

@@ -4,93 +4,116 @@ module;
export module mean_field:quadrature.mfem;
export import :quadrature.policy;
export import :integrators.centrifugal;
import :field.mfem;
export namespace mean_field::quadrature {
struct MfemRule {
Resolution resolution;
const mfem::IntegrationRule* integration_rule;
const mfem::IntegrationRule *integration_rule;
};
class RuleFactory {
public:
explicit RuleFactory(
Policy policy
);
MfemRule get(
const Query& query,
mfem::Geometry::Type geometry
) const;
MfemRule get(
Term term,
explicit RuleFactory(Policy policy);
MfemRule
get(const Query &query,
mfem::Geometry::Type geometry) const;
MfemRule
get(Term term,
QuadratureRole role,
mfem::Geometry::Type geometry,
int base_order,
utils::DOMAINS domain = utils::DOMAINS::ALL,
MappingKind mapping = MappingKind::none
) const;
MappingKind mapping = MappingKind::none) const;
Resolution configure_gravity_hdiv_mass(
mfem::VectorFEMassIntegrator& integrator,
mfem::VectorFEMassIntegrator &integrator,
QuadratureRole role,
const mfem::FiniteElement& element,
const mfem::ElementTransformation& transformation,
const mfem::FiniteElement &element,
const mfem::ElementTransformation &transformation,
utils::DOMAINS domain = utils::DOMAINS::ALL,
MappingKind mapping = MappingKind::none
MappingKind mapping = MappingKind::none
) const;
Resolution configure_gravity_divergence(
mfem::VectorFEDivergenceIntegrator& integrator,
mfem::VectorFEDivergenceIntegrator &integrator,
QuadratureRole role,
const mfem::FiniteElement& trial_element,
const mfem::FiniteElement& test_element,
const mfem::ElementTransformation& transformation,
const mfem::FiniteElement &trial_element,
const mfem::FiniteElement &test_element,
const mfem::ElementTransformation &transformation,
utils::DOMAINS domain = utils::DOMAINS::ALL,
MappingKind mapping = MappingKind::none
MappingKind mapping = MappingKind::none
) const;
Resolution configure_gravity_boundary(
mfem::VectorFEBoundaryFluxLFIntegrator& integrator,
mfem::VectorFEBoundaryFluxLFIntegrator &integrator,
QuadratureRole role,
const mfem::FiniteElement& boundary_element,
const mfem::FiniteElement &boundary_element,
utils::DOMAINS domain = utils::DOMAINS::VACUUM,
MappingKind mapping = MappingKind::none
MappingKind mapping = MappingKind::none
) const;
Resolution configure_gravity_source(
mfem::DomainLFIntegrator& integrator,
mfem::DomainLFIntegrator &integrator,
QuadratureRole role,
const mfem::FiniteElement& test_element,
const mfem::ElementTransformation& transformation,
const mfem::FiniteElement &test_element,
const mfem::ElementTransformation &transformation,
int coefficient_order,
utils::DOMAINS domain = utils::DOMAINS::STELLAR,
MappingKind mapping = MappingKind::none
MappingKind mapping = MappingKind::none
) const;
template<typename IntegratorType>
Resolution configure_gravity_source(
mfem::MixedScalarMassIntegrator &integrator,
QuadratureRole role,
const mfem::FiniteElement &trial_element,
const mfem::FiniteElement &test_element,
const mfem::ElementTransformation &transformation,
int coefficient_order,
utils::DOMAINS domain = utils::DOMAINS::STELLAR,
MappingKind mapping = MappingKind::none
) const;
Resolution configure_centrifugal(
integrators::CentrifugalForceIntegrator &integrator,
QuadratureRole role,
const mfem::FiniteElement &density_element,
const mfem::FiniteElement &velocity_element,
const mfem::ElementTransformation &transformation,
int position_order,
utils::DOMAINS domain = utils::DOMAINS::STELLAR,
MappingKind mapping = MappingKind::none
) const;
template <typename IntegratorType>
Resolution configure(
IntegratorType& integrator,
IntegratorType &integrator,
Term term,
QuadratureRole role,
mfem::Geometry::Type geometry,
int base_order,
utils::DOMAINS domain = utils::DOMAINS::ALL,
MappingKind mapping = MappingKind::none
MappingKind mapping = MappingKind::none
) const;
private:
Policy policy;
};
RuleFactory::RuleFactory(Policy policy) : policy(std::move(policy)) {}
RuleFactory::RuleFactory(Policy policy) : policy(std::move(policy)) {
}
MfemRule RuleFactory::get(
const Query& query,
const Query &query,
const mfem::Geometry::Type geometry
) const {
const Resolution resolution = policy.resolve(query);
const mfem::IntegrationRule& integration_rule = mfem::IntRules.Get(geometry, resolution.order);
return {.resolution = resolution, .integration_rule = &integration_rule};
const mfem::IntegrationRule &integration_rule =
mfem::IntRules.Get(geometry, resolution.order);
return {
.resolution = resolution, .integration_rule = &integration_rule
};
}
MfemRule RuleFactory::get(
@@ -102,87 +125,197 @@ export namespace mean_field::quadrature {
const MappingKind mapping
) const {
Query query{.term = term};
query.domain = domain;
query.mapping = mapping;
query.role = role;
query.domain = domain;
query.mapping = mapping;
query.role = role;
query.base_order = base_order;
return get(query, geometry);
}
Resolution RuleFactory::configure_gravity_hdiv_mass(
mfem::VectorFEMassIntegrator& integrator,
mfem::VectorFEMassIntegrator &integrator,
const QuadratureRole role,
const mfem::FiniteElement& element,
const mfem::ElementTransformation& transformation,
const mfem::FiniteElement &element,
const mfem::ElementTransformation &transformation,
const utils::DOMAINS domain,
const MappingKind mapping
) const {
const int base_order = 2 * element.GetOrder() + transformation.OrderW();
return configure(integrator, Term::gravity_hdiv_mass, role, element.GetGeomType(), base_order, domain, mapping);
using GravityField = field::Field<field::Gravity>;
MFEM_VERIFY(
element.GetOrder() == field::Gravity::Flux::familyOrder + 1,
"The H(div) element order does not match the registered gravity "
"flux."
);
const Query query =
GravityField::make_query<field::Gravity::Form::HDivMass>(
role, transformation.OrderW(), {}, domain, mapping
);
const auto [resolution, integration_rule] =
get(query, element.GetGeomType());
integrator.SetIntegrationRule(*integration_rule);
return resolution;
}
Resolution RuleFactory::configure_gravity_divergence(
mfem::VectorFEDivergenceIntegrator& integrator,
mfem::VectorFEDivergenceIntegrator &integrator,
const QuadratureRole role,
const mfem::FiniteElement& trial_element,
const mfem::FiniteElement& test_element,
const mfem::ElementTransformation& transformation,
const mfem::FiniteElement &trial_element,
const mfem::FiniteElement &test_element,
const mfem::ElementTransformation &transformation,
const utils::DOMAINS domain,
const MappingKind mapping
) const {
const Query query = {
.term = Term::gravity_divergence,
.role = role,
.domain = domain,
.mapping = mapping,
.trial_order = trial_element.GetOrder(),
.test_order = test_element.GetOrder(),
.geometry_weight_order = transformation.OrderW()
};
using GravityField = field::Field<field::Gravity>;
MFEM_VERIFY(
trial_element.GetOrder() == field::Gravity::Flux::familyOrder + 1,
"The divergence trial element does not match the registered "
"gravity flux."
);
MFEM_VERIFY(
test_element.GetOrder() == field::Gravity::Potential::familyOrder,
"The divergence test element does not match the registered "
"gravity potential."
);
const Query query =
GravityField::make_query<field::Gravity::Form::DivergenceCoupling>(
role, transformation.OrderW(), {}, domain, mapping
);
const auto [resolution, integration_rule] = get(query, trial_element.GetGeomType());
const auto [resolution, integration_rule] =
get(query, trial_element.GetGeomType());
integrator.SetIntegrationRule(*integration_rule);
return resolution;
}
Resolution RuleFactory::configure_gravity_boundary(
mfem::VectorFEBoundaryFluxLFIntegrator& integrator,
mfem::VectorFEBoundaryFluxLFIntegrator &integrator,
const QuadratureRole role,
const mfem::FiniteElement& boundary_element,
const mfem::FiniteElement &boundary_element,
const utils::DOMAINS domain,
const MappingKind mapping
) const {
const int base_order = 2 * boundary_element.GetOrder();
return configure(integrator, Term::gravity_boundary, role, boundary_element.GetGeomType(), base_order, domain, mapping);
}
Resolution RuleFactory::configure_gravity_source(
mfem::DomainLFIntegrator& integrator,
const QuadratureRole role,
const mfem::FiniteElement& test_element,
const mfem::ElementTransformation& transformation,
const int coefficient_order,
const utils::DOMAINS domain,
const MappingKind mapping
) const {
const Query query = {
.term = Term::gravity_source,
.role = role,
.domain = domain,
.mapping = mapping,
.test_order = test_element.GetOrder(),
.coefficient_order = coefficient_order,
.geometry_weight_order = transformation.OrderW()
};
const auto [resolution, integration_rule] = get(query, test_element.GetGeomType());
using GravityField = field::Field<field::Gravity>;
MFEM_VERIFY(
boundary_element.GetOrder() == field::Gravity::Flux::familyOrder,
"The boundary element does not match the registered gravity-flux "
"normal trace."
);
const Query query =
GravityField::make_query<field::Gravity::Form::Boundary>(
role, 0, {}, domain, mapping
);
const auto [resolution, integration_rule] =
get(query, boundary_element.GetGeomType());
integrator.SetIntegrationRule(*integration_rule);
return resolution;
}
template<typename IntegratorType>
Resolution RuleFactory::configure_gravity_source(
mfem::DomainLFIntegrator &integrator,
const QuadratureRole role,
const mfem::FiniteElement &test_element,
const mfem::ElementTransformation &transformation,
const int coefficient_order,
const utils::DOMAINS domain,
const MappingKind mapping
) const {
using GravityField = field::Field<field::Gravity>;
MFEM_VERIFY(
test_element.GetOrder() == field::Gravity::Potential::familyOrder,
"The gravity-source test element does not match the registered "
"gravity potential."
);
MFEM_VERIFY(
coefficient_order == field::Density::Scalar::familyOrder,
"The gravity-source coefficient order does not match the "
"registered density field."
);
const Query query =
GravityField::make_query<field::Gravity::Form::SourceLinear>(
role, transformation.OrderW(), {}, domain, mapping
);
const auto [resolution, integration_rule] =
get(query, test_element.GetGeomType());
integrator.SetIntegrationRule(*integration_rule);
return resolution;
}
Resolution RuleFactory::configure_gravity_source(
mfem::MixedScalarMassIntegrator &integrator,
QuadratureRole role,
const mfem::FiniteElement &trial_element,
const mfem::FiniteElement &test_element,
const mfem::ElementTransformation &transformation,
int coefficient_order,
utils::DOMAINS domain,
MappingKind mapping
) const {
MFEM_VERIFY(
trial_element.GetGeomType() == test_element.GetGeomType(),
"Gravity source trial and test elements must use the same geometry."
);
MFEM_VERIFY(
trial_element.GetGeomType() == transformation.GetGeometryType(),
"Gravity source element and transformation geometries must agree."
);
using GravityField = field::Field<field::Gravity>;
MFEM_VERIFY(
trial_element.GetOrder() == field::Density::Scalar::familyOrder,
"The gravity-source trial element does not match the registered "
"density field."
);
MFEM_VERIFY(
test_element.GetOrder() == field::Gravity::Potential::familyOrder,
"The gravity-source test element does not match the registered "
"gravity potential."
);
MFEM_VERIFY(
coefficient_order == 0,
"The mapped gravity-source coefficient order must be zero; "
"density order is supplied by the registered trial field."
);
const Query query =
GravityField::make_query<field::Gravity::Form::SourceProjection>(
role, transformation.OrderW(), {}, domain, mapping
);
const auto [resolution, integration_rule] =
get(query, transformation.GetGeometryType());
integrator.SetIntRule(integration_rule);
return resolution;
}
Resolution RuleFactory::configure_centrifugal(
integrators::CentrifugalForceIntegrator &integrator,
const QuadratureRole role,
const mfem::FiniteElement &density_element,
const mfem::FiniteElement &velocity_element,
const mfem::ElementTransformation &transformation,
const int position_order,
const utils::DOMAINS domain,
const MappingKind mapping
) const {
const Query query = {
.term = Term::centrifugal,
.role = role,
.domain = domain,
.mapping = mapping,
.trial_order = density_element.GetOrder(),
.test_order = velocity_element.GetOrder(),
.coefficient_order = position_order,
.geometry_weight_order = transformation.OrderW()
};
const auto [resolution, integration_rule] =
get(query, velocity_element.GetGeomType());
integrator.SetIntegrationRule(*integration_rule);
return resolution;
}
template <typename IntegratorType>
Resolution RuleFactory::configure(
IntegratorType& integrator,
IntegratorType &integrator,
const Term term,
const QuadratureRole role,
const mfem::Geometry::Type geometry,
@@ -190,9 +323,10 @@ export namespace mean_field::quadrature {
const utils::DOMAINS domain,
const MappingKind mapping
) const {
const auto [resolution, integration_rule] = get(term, role, geometry, base_order, domain, mapping);
const auto [resolution, integration_rule] =
get(term, role, geometry, base_order, domain, mapping);
integrator.SetIntegrationRule(*integration_rule);
return resolution;
}
}
} // namespace mean_field::quadrature

View File

@@ -2,7 +2,9 @@ module;
#include <algorithm>
#include <optional>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
export module mean_field:quadrature.policy;
export import :utils.misc;
@@ -13,11 +15,19 @@ export namespace mean_field::quadrature {
gravity_divergence,
gravity_source,
gravity_boundary,
centrifugal,
density_projection,
eos_closure,
hydrostatic_equilibrium,
isobaric_surface,
mesh_extension,
mass_conservation,
mass_normalization,
center_of_mass,
quadrupole,
gravitational_energy,
pressure_integral,
pressure_force,
virial,
error_norm
};
@@ -29,19 +39,9 @@ export namespace mean_field::quadrature {
projection
};
enum class MappingKind {
none,
affine,
general,
kelvin
};
enum class MappingKind { none, affine, general, kelvin };
enum class Mode {
fast,
production,
reference,
convergence
};
enum class Mode { fast, production, reference, convergence };
struct RuleControl {
std::optional<int> fixed_order;
@@ -55,17 +55,24 @@ export namespace mean_field::quadrature {
RuleControl projection;
};
struct RuleSet {
RuleControl gravity_hdiv_mass;
RuleControl gravity_divergence;
RuleControl gravity_source;
RuleControl gravity_boundary;
RuleControl centrifugal;
RuleControl density_projection;
RuleControl eos_closure;
RuleControl hydrostatic_equilibrium;
RuleControl isobaric_surface;
RuleControl mesh_extension;
RuleControl mass_conservation;
RuleControl mass_normalization;
RuleControl center_of_mass;
RuleControl quadrupole;
RuleControl gravitational_energy;
RuleControl pressure_integral;
RuleControl pressure_force;
RuleControl virial;
RuleControl error_norm;
RoleControls roles;
@@ -74,12 +81,12 @@ export namespace mean_field::quadrature {
struct Query {
Term term;
QuadratureRole role = QuadratureRole::discretization;
utils::DOMAINS domain = utils::DOMAINS::ALL;
MappingKind mapping = MappingKind::none;
int trial_order = 0;
int test_order = 0;
int coefficient_order = 0;
QuadratureRole role = QuadratureRole::discretization;
utils::DOMAINS domain = utils::DOMAINS::ALL;
MappingKind mapping = MappingKind::none;
int trial_order = 0;
int test_order = 0;
int coefficient_order = 0;
int geometry_weight_order = 0;
std::optional<int> base_order;
};
@@ -96,16 +103,16 @@ export namespace mean_field::quadrature {
};
struct QuadratureManifestOptions {
bool enabled = false;
bool enabled = false;
bool include_repeated_queries = false;
std::optional<std::string> output_file;
};
struct QuadratureValidationOptions {
bool require_explicit_base_order = false;
bool require_explicit_mfem_rule = false;
bool reject_negative_boosts = true;
bool report_unused_overrides = true;
bool require_explicit_mfem_rule = false;
bool reject_negative_boosts = true;
bool report_unused_overrides = true;
};
struct QuadratureRoleOptions {
@@ -116,7 +123,7 @@ export namespace mean_field::quadrature {
};
struct QuadratureOptions {
Mode mode = Mode::production;
Mode mode = Mode::production;
int global_boost = 0;
std::optional<int> fallback_fixed_order;
@@ -124,11 +131,19 @@ export namespace mean_field::quadrature {
QuadratureTermOptions gravity_divergence;
QuadratureTermOptions gravity_source;
QuadratureTermOptions gravity_boundary;
QuadratureTermOptions centrifugal;
QuadratureTermOptions density_projection;
QuadratureTermOptions eos_closure;
QuadratureTermOptions hydrostatic_equilibrium;
QuadratureTermOptions isobaric_surface;
QuadratureTermOptions mesh_extension;
QuadratureTermOptions mass_conservation;
QuadratureTermOptions mass_normalization;
QuadratureTermOptions center_of_mass;
QuadratureTermOptions quadrupole;
QuadratureTermOptions gravitational_energy;
QuadratureTermOptions pressure_integral;
QuadratureTermOptions pressure_force;
QuadratureTermOptions virial;
QuadratureTermOptions error_norm;
@@ -139,44 +154,51 @@ export namespace mean_field::quadrature {
QuadratureValidationOptions validation;
};
RuleSet make_rule_set(Mode mode, int global_boost = 0);
RuleSet make_rule_set(
Mode mode,
int global_boost = 0
);
class Policy {
public:
explicit Policy(RuleSet rule_set);
Resolution resolve(const Query& query) const;
Resolution resolve(const Query &query) const;
private:
const RuleControl& get_control(Term term) const;
static int compute_base_order(const Query& query) ;
const RuleControl& get_role_control(QuadratureRole role) const;
const RuleControl &get_control(Term term) const;
static int compute_base_order(const Query &query);
const RuleControl &get_role_control(QuadratureRole role) const;
RuleSet rule_set;
};
RuleSet make_rule_set(const Mode mode, const int global_boost) {
RuleSet make_rule_set(
const Mode mode,
const int global_boost
) {
RuleSet rule_set;
switch (mode) {
case Mode::fast:
case Mode::production:
case Mode::convergence:
rule_set.fallback.boost = global_boost;
break;
case Mode::reference:
rule_set.fallback.boost = global_boost + 8;
break;
case Mode::fast:
case Mode::production:
case Mode::convergence:
rule_set.fallback.boost = global_boost;
break;
case Mode::reference:
rule_set.fallback.boost = global_boost + 8;
break;
}
return rule_set;
}
Policy::Policy(RuleSet rule_set) : rule_set(std::move(rule_set)) {}
Policy::Policy(RuleSet rule_set) : rule_set(std::move(rule_set)) {
}
Resolution Policy::resolve(const Query& query) const {
const int base_order = compute_base_order(query);
const RuleControl& term_control = get_control(query.term);
const RuleControl& role_control = get_role_control(query.role);
Resolution Policy::resolve(const Query &query) const {
const int base_order = compute_base_order(query);
const RuleControl &term_control = get_control(query.term);
const RuleControl &role_control = get_role_control(query.role);
std::optional<int> fixed_order;
if (term_control.fixed_order.has_value()) {
@@ -189,49 +211,96 @@ export namespace mean_field::quadrature {
if (fixed_order.has_value()) {
if (*fixed_order < 0) {
throw std::invalid_argument("Quadrature fixed order cannot be negative.");
throw std::invalid_argument(
"Quadrature fixed order cannot be negative."
);
}
return {.base_order = base_order, .boost = 0, .order = *fixed_order, .used_fixed_order = true};
return {
.base_order = base_order,
.boost = 0,
.order = *fixed_order,
.used_fixed_order = true
};
}
const int boost = rule_set.fallback.boost + role_control.boost + term_control.boost;
const int boost =
rule_set.fallback.boost + role_control.boost + term_control.boost;
const int order = base_order + boost;
if (order < 0) {
throw std::invalid_argument("Resolved quadrature order cannot be negative.");
throw std::invalid_argument(
"Resolved quadrature order cannot be negative."
);
}
return {.base_order = base_order, .boost = boost, .order = order, .used_fixed_order = false};
return {
.base_order = base_order,
.boost = boost,
.order = order,
.used_fixed_order = false
};
}
const RuleControl& Policy::get_control(const Term term) const {
const RuleControl &Policy::get_control(const Term term) const {
switch (term) {
case Term::gravity_hdiv_mass: return rule_set.gravity_hdiv_mass;
case Term::gravity_divergence: return rule_set.gravity_divergence;
case Term::gravity_source: return rule_set.gravity_source;
case Term::gravity_boundary: return rule_set.gravity_boundary;
case Term::density_projection: return rule_set.density_projection;
case Term::mass_conservation: return rule_set.mass_conservation;
case Term::center_of_mass: return rule_set.center_of_mass;
case Term::quadrupole: return rule_set.quadrupole;
case Term::gravitational_energy: return rule_set.gravitational_energy;
case Term::virial: return rule_set.virial;
case Term::error_norm: return rule_set.error_norm;
case Term::gravity_hdiv_mass:
return rule_set.gravity_hdiv_mass;
case Term::gravity_divergence:
return rule_set.gravity_divergence;
case Term::gravity_source:
return rule_set.gravity_source;
case Term::gravity_boundary:
return rule_set.gravity_boundary;
case Term::centrifugal:
return rule_set.centrifugal;
case Term::density_projection:
return rule_set.density_projection;
case Term::eos_closure:
return rule_set.eos_closure;
case Term::hydrostatic_equilibrium:
return rule_set.hydrostatic_equilibrium;
case Term::isobaric_surface:
return rule_set.isobaric_surface;
case Term::mesh_extension:
return rule_set.mesh_extension;
case Term::mass_conservation:
return rule_set.mass_conservation;
case Term::mass_normalization:
return rule_set.mass_normalization;
case Term::center_of_mass:
return rule_set.center_of_mass;
case Term::quadrupole:
return rule_set.quadrupole;
case Term::gravitational_energy:
return rule_set.gravitational_energy;
case Term::pressure_integral:
return rule_set.pressure_integral;
case Term::pressure_force:
return rule_set.pressure_force;
case Term::virial:
return rule_set.virial;
case Term::error_norm:
return rule_set.error_norm;
}
throw std::logic_error("Unknown quadrature term.");
}
int Policy::compute_base_order(const Query& query) {
int Policy::compute_base_order(const Query &query) {
if (query.base_order.has_value()) {
if (*query.base_order < 0) {
throw std::invalid_argument("Quadrature base order cannot be negative.");
throw std::invalid_argument(
"Quadrature base order cannot be negative."
);
}
return *query.base_order;
}
if (query.trial_order < 0 || query.test_order < 0 || query.coefficient_order < 0 || query.geometry_weight_order < 0) {
throw std::invalid_argument("Quadrature query orders cannot be negative.");
if (query.trial_order < 0 || query.test_order < 0 ||
query.coefficient_order < 0 || query.geometry_weight_order < 0) {
throw std::invalid_argument(
"Quadrature query orders cannot be negative."
);
}
int trial_order = query.trial_order;
@@ -239,18 +308,24 @@ export namespace mean_field::quadrature {
trial_order = std::max(0, trial_order - 1);
}
return trial_order + query.test_order + query.coefficient_order + query.geometry_weight_order;
return trial_order + query.test_order + query.coefficient_order +
query.geometry_weight_order;
}
const RuleControl& Policy::get_role_control(const QuadratureRole role) const {
const RuleControl &
Policy::get_role_control(const QuadratureRole role) const {
switch (role) {
case QuadratureRole::discretization: return rule_set.roles.discretization;
case QuadratureRole::preconditioner: return rule_set.roles.preconditioner;
case QuadratureRole::diagnostic: return rule_set.roles.diagnostic;
case QuadratureRole::projection: return rule_set.roles.projection;
case QuadratureRole::discretization:
return rule_set.roles.discretization;
case QuadratureRole::preconditioner:
return rule_set.roles.preconditioner;
case QuadratureRole::diagnostic:
return rule_set.roles.diagnostic;
case QuadratureRole::projection:
return rule_set.roles.projection;
}
throw std::logic_error("Unknown quadrature role.");
}
}
} // namespace mean_field::quadrature

View File

@@ -0,0 +1,29 @@
module;
#include <cstdint>
export module mean_field:solver.fields;
export namespace mean_field::solver {
enum class FieldBlock : std::uint8_t {
velocity = 0,
density = 1,
gravity_gradient = 2,
gravity_potential = 3,
displacement = 4,
count = 5
};
[[nodiscard]] constexpr int block_index(const FieldBlock field) noexcept {
return static_cast<int>(field);
}
inline constexpr int field_block_count = block_index(FieldBlock::count);
static_assert(block_index(FieldBlock::velocity) == 0);
static_assert(block_index(FieldBlock::density) == 1);
static_assert(block_index(FieldBlock::gravity_gradient) == 2);
static_assert(block_index(FieldBlock::gravity_potential) == 3);
static_assert(block_index(FieldBlock::displacement) == 4);
static_assert(field_block_count == 5);
} // namespace mean_field::solver

View File

@@ -0,0 +1,493 @@
module;
#include <array>
#include <mfem.hpp>
#include <stdexcept>
#include <tuple>
#include <type_traits>
export module mean_field:utils.blocks;
export namespace mean_field::utils::blocks {
inline constexpr int dynamic_block_size = -1;
struct block { };
struct residual_block_base : block {
static constexpr int static_block_size = dynamic_block_size;
};
struct value_block_base : block {
static constexpr int static_block_size = dynamic_block_size;
};
struct term { };
struct field { };
template <typename Residual, typename... Values> struct block_row { };
template <int index_value>
struct residual_block final : residual_block_base {
static constexpr int index = index_value;
// ReSharper disable once CppNonExplicitConversionOperator
constexpr operator int() const noexcept {
return index;
}
};
template <int index_value> struct value_block final : value_block_base {
static constexpr int index = index_value;
// ReSharper disable once CppNonExplicitConversionOperator
constexpr operator int() const noexcept {
return index;
}
};
struct density final : field {
struct mass final : term {
struct value final : value_block_base { };
struct residual final : residual_block_base { };
};
static inline constexpr mass mass_term{};
};
struct displacement final : field {
struct geometry final : term {
struct value final : value_block_base { };
struct residual final : residual_block_base { };
};
static inline constexpr geometry geometry_term{};
};
struct gravity final : field {
struct gradient final : term {
struct value final : value_block_base { };
struct residual final : residual_block_base { };
};
struct poisson final : term {
struct value final : value_block_base { };
struct residual final : residual_block_base { };
};
static inline constexpr gradient gradient_term{};
static inline constexpr poisson poisson_term{};
};
struct enthalpy final : field {
struct specific final : term {
struct value final : value_block_base { };
struct residual final : residual_block_base { };
};
static inline constexpr specific specific_term{};
};
struct barotropic_constant final : field {
struct mass_normalization final : term {
struct value final : value_block_base {
static constexpr int static_block_size = 1;
};
struct residual final : residual_block_base {
static constexpr int static_block_size = 1;
};
};
static inline constexpr mass_normalization mass_normalization_term{};
};
inline constexpr density density_field{};
inline constexpr displacement displacement_field{};
inline constexpr gravity gravity_field{};
inline constexpr enthalpy enthalpy_field{};
inline constexpr barotropic_constant barotropic_constant_field{};
template <typename... Types> struct type_list {
static constexpr int size = sizeof...(Types);
};
template <typename Query, typename List> struct contains_type;
template <typename Query>
struct contains_type<Query, type_list<>> : std::false_type { };
template <typename Query, typename Head, typename... Tail>
struct contains_type<Query, type_list<Head, Tail...>>
: std::conditional_t<
std::is_same_v<Query, Head>,
std::true_type,
contains_type<Query, type_list<Tail...>>> { };
template <typename Query, typename List>
inline constexpr bool contains_type_v = contains_type<Query, List>::value;
template <typename Query, typename List> struct type_count;
template <typename Query>
struct type_count<Query, type_list<>> : std::integral_constant<int, 0> { };
template <typename Query, typename Head, typename... Tail>
struct type_count<Query, type_list<Head, Tail...>>
: std::integral_constant<
int,
(std::is_same_v<Query, Head> ? 1 : 0) +
type_count<Query, type_list<Tail...>>::value> { };
template <typename Query, typename List>
inline constexpr int type_count_v = type_count<Query, List>::value;
template <typename List> struct types_are_unique;
template <typename... Types>
struct types_are_unique<type_list<Types...>>
: std::bool_constant<
((type_count_v<Types, type_list<Types...>> == 1) && ...)> { };
template <typename List>
inline constexpr bool types_are_unique_v = types_are_unique<List>::value;
template <typename Row> struct block_row_traits {
using residual = void;
using values = type_list<>;
static constexpr int value_count = 0;
static constexpr bool is_block_row = false;
};
template <typename Residual, typename... Values>
struct block_row_traits<block_row<Residual, Values...>> {
using residual = Residual;
using values = type_list<Values...>;
static constexpr int value_count = sizeof...(Values);
static constexpr bool is_block_row = true;
};
template <typename Query, typename List> struct type_index;
template <typename Query, typename... Tail>
struct type_index<Query, type_list<Query, Tail...>> {
static constexpr int value = 0;
};
template <typename Query, typename Head, typename... Tail>
struct type_index<Query, type_list<Head, Tail...>> {
static constexpr int value =
1 + type_index<Query, type_list<Tail...>>::value;
};
template <typename Query, typename List>
inline constexpr int type_index_v = type_index<Query, List>::value;
template <typename ValueBlocks, typename ResidualBlocks> struct block_form {
using value_blocks = ValueBlocks;
using residual_blocks = ResidualBlocks;
static constexpr int value_block_count = ValueBlocks::size;
static constexpr int residual_block_count = ResidualBlocks::size;
};
template <typename Form> struct block_form_is_valid : std::false_type { };
template <typename... Values, typename... Residuals>
struct block_form_is_valid<
block_form<type_list<Values...>, type_list<Residuals...>>>
: std::bool_constant<
(std::is_base_of_v<value_block_base, Values> && ...) &&
(std::is_base_of_v<residual_block_base, Residuals> && ...) &&
types_are_unique_v<type_list<Values...>> &&
types_are_unique_v<type_list<Residuals...>>> { };
template <typename Form>
inline constexpr bool block_form_is_valid_v =
block_form_is_valid<Form>::value;
template <typename Row, typename ValueBlocks, typename ResidualBlocks>
struct block_row_is_valid : std::false_type { };
template <
typename Residual,
typename... Values,
typename ValueBlocks,
typename ResidualBlocks>
struct block_row_is_valid<
block_row<Residual, Values...>,
ValueBlocks,
ResidualBlocks>
: std::bool_constant<
std::is_base_of_v<residual_block_base, Residual> &&
contains_type_v<Residual, ResidualBlocks> &&
((std::is_base_of_v<value_block_base, Values> &&
contains_type_v<Values, ValueBlocks>) &&
...) &&
types_are_unique_v<type_list<Values...>>> { };
template <typename Rows> struct row_residual_list;
template <typename... Rows> struct row_residual_list<type_list<Rows...>> {
using type = type_list<typename block_row_traits<Rows>::residual...>;
};
template <typename Rows>
using row_residual_list_t = typename row_residual_list<Rows>::type;
template <typename Form, typename JacobianForm>
struct jacobian_form_is_valid : std::false_type { };
template <typename... Values, typename... Residuals, typename... Rows>
struct jacobian_form_is_valid<
block_form<type_list<Values...>, type_list<Residuals...>>,
type_list<Rows...>> {
using form_type =
block_form<type_list<Values...>, type_list<Residuals...>>;
using value_blocks = type_list<Values...>;
using residual_blocks = type_list<Residuals...>;
using rows = type_list<Rows...>;
static constexpr bool value =
block_form_is_valid_v<form_type> &&
(block_row_is_valid<Rows, value_blocks, residual_blocks>::value &&
...) &&
std::is_same_v<row_residual_list_t<rows>, residual_blocks>;
};
template <typename Form, typename JacobianForm>
inline constexpr bool jacobian_form_is_valid_v =
jacobian_form_is_valid<Form, JacobianForm>::value;
template <typename Form, typename JacobianForm>
concept valid_jacobian_form = jacobian_form_is_valid_v<Form, JacobianForm>;
template <typename Residual, typename Value, typename JacobianForm>
struct has_jacobian_coupling;
template <typename Residual, typename Value>
struct has_jacobian_coupling<Residual, Value, type_list<>>
: std::false_type { };
template <
typename Residual,
typename Value,
typename RowResidual,
typename... RowValues,
typename... RemainingRows>
struct has_jacobian_coupling<
Residual,
Value,
type_list<block_row<RowResidual, RowValues...>, RemainingRows...>>
: std::conditional_t<
std::is_same_v<Residual, RowResidual>,
std::bool_constant<(std::is_same_v<Value, RowValues> || ...)>,
has_jacobian_coupling<
Residual,
Value,
type_list<RemainingRows...>>> { };
template <typename Residual, typename Value, typename JacobianForm>
inline constexpr bool has_jacobian_coupling_v =
has_jacobian_coupling<Residual, Value, JacobianForm>::value;
template <
typename Form,
typename Term>
consteval auto get_value_block(const Term &) {
using value_type = typename Term::value;
constexpr int index =
type_index_v<value_type, typename Form::value_blocks>;
return value_block<index>{};
}
template <
typename Form,
typename Term>
consteval auto get_residual_block(const Term &) {
using residual_type = typename Term::residual;
constexpr int index =
type_index_v<residual_type, typename Form::residual_blocks>;
return residual_block<index>{};
}
template <typename Form> class form_layout {
public:
form_layout(
const std::array<
int,
Form::value_block_count> &value_sizes,
const std::array<
int,
Form::residual_block_count> &residual_sizes
) {
build_offsets(
m_value_offsets, value_sizes, typename Form::value_blocks{}
);
build_offsets(
m_residual_offsets, residual_sizes,
typename Form::residual_blocks{}
);
}
template <int index> [[nodiscard]] int size(value_block<index>) const {
return m_value_offsets[index + 1] - m_value_offsets[index];
}
template <int index>
[[nodiscard]] int size(residual_block<index>) const {
return m_residual_offsets[index + 1] - m_residual_offsets[index];
}
template <int index>
[[nodiscard]] int offset(value_block<index>) const {
return m_value_offsets[index];
}
template <int index>
[[nodiscard]] int offset(residual_block<index>) const {
return m_residual_offsets[index];
}
[[nodiscard]]
const mfem::Array<int> &value_offsets() const noexcept {
return m_value_offsets;
}
[[nodiscard]]
const mfem::Array<int> &residual_offsets() const noexcept {
return m_residual_offsets;
}
private:
template <typename BlockType>
[[nodiscard]]
static int resolve_block_size(const int requested_size) {
if constexpr (BlockType::static_block_size == dynamic_block_size) {
return requested_size;
} else {
if (requested_size != BlockType::static_block_size) {
throw std::invalid_argument(
"A statically sized block was given an "
"incompatible runtime size."
);
}
return BlockType::static_block_size;
}
}
template <typename... BlockTypes>
static void build_offsets(
mfem::Array<int> &offsets,
const std::array<
int,
sizeof...(BlockTypes)> &requested_sizes,
type_list<BlockTypes...>
) {
offsets.SetSize(sizeof...(BlockTypes) + 1);
offsets[0] = 0;
int block_index = 0;
((offsets[block_index + 1] =
offsets[block_index] +
resolve_block_size<BlockTypes>(requested_sizes[block_index]),
++block_index),
...);
}
mfem::Array<int> m_value_offsets;
mfem::Array<int> m_residual_offsets;
};
using gravity_field_form = block_form<
type_list<
density::mass::value,
displacement::geometry::value,
gravity::gradient::value,
gravity::poisson::value>,
type_list<gravity::gradient::residual, gravity::poisson::residual>>;
using gravity_jacobian_form = type_list<
block_row<
gravity::gradient::residual,
gravity::gradient::value,
gravity::poisson::value,
displacement::geometry::value>,
block_row<
gravity::poisson::residual,
gravity::gradient::value,
density::mass::value,
displacement::geometry::value>>;
// Columns:
// [rho, d, g, Phi, h, C]
//
// Rows:
// [R_g, R_Phi, R_rho, R_d, R_h, R_M]
using barotropic_equilibrium_form = block_form<
type_list<
density::mass::value,
displacement::geometry::value,
gravity::gradient::value,
gravity::poisson::value,
enthalpy::specific::value,
barotropic_constant::mass_normalization::value>,
type_list<
gravity::gradient::residual,
gravity::poisson::residual,
density::mass::residual,
displacement::geometry::residual,
enthalpy::specific::residual,
barotropic_constant::mass_normalization::residual>>;
using barotropic_equilibrium_jacobian_form = type_list<
// R_g(g, Phi, d)
block_row<
gravity::gradient::residual,
gravity::gradient::value,
gravity::poisson::value,
displacement::geometry::value>,
// R_Phi(g, rho, d)
block_row<
gravity::poisson::residual,
gravity::gradient::value,
density::mass::value,
displacement::geometry::value>,
// R_rho(rho, h, d)
block_row<
density::mass::residual,
density::mass::value,
enthalpy::specific::value,
displacement::geometry::value>,
// R_d(d, h)
block_row<
displacement::geometry::residual,
displacement::geometry::value,
enthalpy::specific::value>,
// R_h(h, Phi, d, C)
block_row<
enthalpy::specific::residual,
enthalpy::specific::value,
gravity::poisson::value,
displacement::geometry::value,
barotropic_constant::mass_normalization::value>,
// R_M(rho, d)
block_row<
barotropic_constant::mass_normalization::residual,
density::mass::value,
displacement::geometry::value>>;
static_assert(valid_jacobian_form<
gravity_field_form,
gravity_jacobian_form>);
static_assert(valid_jacobian_form<
barotropic_equilibrium_form,
barotropic_equilibrium_jacobian_form>);
} // namespace mean_field::utils::blocks

View File

@@ -21,4 +21,4 @@ export namespace mean_field::utils {
mapping::COORDINATE_SPACE vspace = mapping::COORDINATE_SPACE::REFERENCE,
mapping::COORDINATE_SPACE rspace = mapping::COORDINATE_SPACE::PHYSICAL
);
}
} // namespace mean_field::utils

View File

@@ -1,7 +1,7 @@
module;
#include <string_view>
#include <functional>
#include <expected>
#include <functional>
#include <string_view>
#include <mfem.hpp>
@@ -14,8 +14,10 @@ import :boundary.contexts;
export namespace mean_field::utils {
constexpr double APPROX_MAX_ACCEPTABLE_POTENTIAL_ERROR_SI_BURNING = 1e-4;
bool is_vacuum(const mfem::ElementTransformation &Tr, mfem::Array<mfem::Vector*> elvec) {
bool is_vacuum(
const mfem::ElementTransformation &Tr,
mfem::Array<mfem::Vector *> elvec
) {
if (Tr.Attribute == 3) {
const int size_elvec = elvec.Size();
for (int i = 0; i < size_elvec; i++) {
@@ -28,45 +30,60 @@ export namespace mean_field::utils {
return false;
}
constexpr std::string_view ANSI_GREEN = "\033[32m";
constexpr std::string_view ANSI_RED = "\033[31m";
constexpr std::string_view ANSI_YELLOW = "\033[33m";
constexpr std::string_view ANSI_BLUE = "\033[34m";
constexpr std::string_view ANSI_MAGENTA = "\033[35m";
constexpr std::string_view ANSI_CYAN = "\033[36m";
constexpr std::string_view ANSI_RESET = "\033[0m";
constexpr std::string_view ANSI_BCYAN = "\033[1;36m";
bool is_vacuum(
const mfem::ElementTransformation &Tr,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) {
if (Tr.Attribute == 3) {
const int cols = elmats.NumCols();
const int rows = elmats.NumRows();
for (int rowID = 0; rowID < rows; rowID++) {
for (int colID = 0; colID < cols; colID++) {
if (elmats(rowID, colID)) {
*elmats(rowID, colID) = 0.0;
}
}
}
return true;
}
return false;
}
constexpr std::string_view ANSI_GREEN = "\033[32m";
constexpr std::string_view ANSI_RED = "\033[31m";
constexpr std::string_view ANSI_YELLOW = "\033[33m";
constexpr std::string_view ANSI_BLUE = "\033[34m";
constexpr std::string_view ANSI_MAGENTA = "\033[35m";
constexpr std::string_view ANSI_CYAN = "\033[36m";
constexpr std::string_view ANSI_RESET = "\033[0m";
constexpr std::string_view ANSI_BCYAN = "\033[1;36m";
constexpr double G = 1.0;
constexpr double MASS = 1.0;
constexpr double RADIUS = 1.0;
constexpr double G = 1.0;
constexpr double MASS = 1.0;
constexpr double RADIUS = 1.0;
[[maybe_unused]] constexpr char HOST[10] = "localhost";
[[maybe_unused]] constexpr int PORT = 19916;
[[maybe_unused]] constexpr int PORT = 19916;
template <typename T>
concept is_xad = std::is_same_v<T, xad::AReal<long double>> ||
std::is_same_v<T, xad::AReal<double>> ||
std::is_same_v<T, xad::AReal<float>>;
template<typename T>
concept is_xad =
std::is_same_v<T, xad::AReal<long double> >
|| std::is_same_v<T, xad::AReal<double> >
|| std::is_same_v<T, xad::AReal<float> >;
template<typename T>
template <typename T>
concept is_real = std::is_floating_point_v<T> || is_xad<T>;
template<is_real T>
using EOS_P = std::function<T(const T& rho, const T& temp)>;
template <is_real T>
using EOS_P = std::function<T(const T &rho, const T &temp)>;
enum class DOMAINS : uint8_t {
CORE = 1 << 0,
CORE = 1 << 0,
ENVELOPE = 1 << 1,
VACUUM = 1 << 2,
STELLAR = CORE | ENVELOPE,
ALL = CORE | ENVELOPE | VACUUM
VACUUM = 1 << 2,
STELLAR = CORE | ENVELOPE,
ALL = CORE | ENVELOPE | VACUUM
};
DOMAINS operator|(
DOMAINS lhs,
DOMAINS rhs
@@ -78,7 +95,7 @@ export namespace mean_field::utils {
);
void populate_element_mask(
const mfem::Mesh* mesh,
const mfem::Mesh *mesh,
DOMAINS domain,
mfem::Array<int> &mask
);
@@ -89,13 +106,14 @@ export namespace mean_field::utils {
mfem::Array<int> &ess_tdof
);
std::expected<boundary::Bounds, boundary::BoundsError> discover_bounds(
std::expected<
boundary::Bounds,
boundary::BoundsError>
discover_bounds(
const mfem::Mesh *mesh,
int vacuum_attr
);
int get_mesh_order(
const mfem::Mesh &mesh
);
int get_mesh_order(const mfem::Mesh &mesh);
}
} // namespace mean_field::utils

View File

@@ -3,6 +3,8 @@ module;
export module mean_field:utils.user;
export import :quadrature.policy;
export import :mapping.compactification.options;
export namespace mean_field::utils {
struct potential {
double rtol;
@@ -16,6 +18,11 @@ export namespace mean_field::utils {
double L;
};
struct DomainMapperStatelessOptions {
int dimension{3};
int vacuum_element_attribute{3};
};
struct Args {
std::string mesh_file;
potential p{};
@@ -24,13 +31,13 @@ export namespace mean_field::utils {
double index{};
double mass{};
double c{};
int quad_boost{0};
DomainMapperStatelessOptions domain_mapper_options{};
mapping::compactification::options::KelvinCompactificationOptions
kelvin_options{};
int max_iters{};
double tol{};
quadrature::QuadratureOptions quadrature{};
};
}
} // namespace mean_field::utils