feat(field-support): added field support system, mid migration

currently the barotope and the pressure force operator are migrated to the new support system
This commit is contained in:
2026-08-23 10:13:53 -04:00
parent dc912fd15e
commit 0f3ca8050b
137 changed files with 29975 additions and 16389 deletions

View File

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

View File

@@ -15,9 +15,7 @@ export namespace mean_field::boundary {
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 {

View File

@@ -0,0 +1,16 @@
export module mean_field:eos.base;
export namespace mean_field::eos {
class EquationOfState {
public:
virtual ~EquationOfState() = default;
[[nodiscard]] virtual double pressure_from_density(double density) const = 0;
[[nodiscard]] virtual double pressure_from_enthalpy(double enthalpy) const = 0;
[[nodiscard]] virtual double enthalpy_from_density(double density) const = 0;
[[nodiscard]] virtual double enthalpy_from_pressure(double pressure) const = 0;
[[nodiscard]] virtual double density_from_enthalpy(double enthalpy) const = 0;
[[nodiscard]] virtual double density_derivative_from_enthalpy(double enthalpy) const = 0;
[[nodiscard]] virtual double pressure_derivative_from_enthalpy(double enthalpy) const = 0;
[[nodiscard]] virtual double pressure_derivative_from_density(double density) const = 0;
};
} // namespace mean_field::eos

View File

@@ -0,0 +1,170 @@
module;
#include <cmath>
#include <format>
#include <stdexcept>
export module mean_field:eos.polytrope;
export import :eos.base;
export namespace mean_field::eos {
class Polytrope final : public EquationOfState {
public:
Polytrope(
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 override {
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 override {
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 override {
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 override {
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 override {
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 override {
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 override {
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);
}
[[nodiscard]] double enthalpy_from_pressure(double pressure) const override {
validate_nonnegativity(pressure, "pressure");
const double np1 = m_polytropic_index + 1;
return np1 * std::pow(m_polytropic_constant, m_polytropic_index / np1) * std::pow(pressure, 1.0 / np1);
}
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
)
);
}
}
public:
private:
double m_polytropic_index;
double m_polytropic_constant;
double m_enthalpy_scale;
};
} // namespace mean_field::eos

View File

@@ -148,26 +148,21 @@ export namespace mean_field::fem {
[[nodiscard]] bool okay() const {
return mesh != nullptr &&
gravityPotentialFec != nullptr &&
gravityPotentialFes != nullptr &&
gravityFluxFec != nullptr && gravityFluxFes != nullptr &&
gravityPotentialFec != nullptr && gravityPotentialFes != nullptr && gravityFluxFec != nullptr &&
gravityFluxFes != nullptr &&
displacementFec != nullptr && displacementFes != nullptr &&
displacement != nullptr &&
displacementFec != nullptr && displacementFes != nullptr && displacement != nullptr &&
densityFec != nullptr && densityFes != nullptr &&
enthalpyFec != nullptr && enthalpyFes != nullptr &&
compactificationFec != nullptr &&
compactificationFes != nullptr &&
compactificationFec != nullptr && compactificationFes != nullptr &&
compactificationCoordinate != nullptr &&
mapping != nullptr && domainMapperStateless != nullptr &&
quadratureFactory != nullptr &&
mapping != nullptr && domainMapperStateless != nullptr && quadratureFactory != nullptr &&
blockTrueOffsets.Size() == 3 &&
gravityBlockTrueOffsets.Size() == 3;
blockTrueOffsets.Size() == 3 && gravityBlockTrueOffsets.Size() == 3;
}
[[nodiscard]] bool has_mapping() const {

View File

@@ -6,6 +6,7 @@ module;
#include <type_traits>
export module mean_field:field.base;
export import :utils.domain;
export namespace mean_field::field {
template <typename... Ts> struct TypeList { };
@@ -13,11 +14,9 @@ export namespace mean_field::field {
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> || ...)> { };
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;
template <typename T, typename ListT> inline constexpr bool typeListContains = TypeListContains<T, ListT>::value;
enum class StorageKind { finite_element, global_scalar };
@@ -44,14 +43,13 @@ export namespace mean_field::field {
};
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>;
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, L2> && (RankV == 0 || RankV == 1)) || (std::same_as<SpaceT, RT> && RankV == 1) ||
(std::same_as<SpaceT, ND> && RankV == 1);
// -------------------------------------------------------------------------
@@ -105,51 +103,39 @@ export namespace mean_field::field {
template <typename T> struct IsCurl : std::false_type { };
template <typename SourceT>
struct IsGradient<FieldRelation::Gradient<SourceT>> : std::true_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 IsDivergence<FieldRelation::Divergence<SourceT>> : std::true_type { };
template <typename SourceT>
struct IsCurl<FieldRelation::Curl<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;
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>> {
template <typename SourceT> struct RelationTarget<FieldRelation::Gradient<SourceT>> {
using Type = SourceT;
};
template <typename SourceT>
struct RelationTarget<FieldRelation::Divergence<SourceT>> {
template <typename SourceT> struct RelationTarget<FieldRelation::Divergence<SourceT>> {
using Type = SourceT;
};
template <typename SourceT>
struct RelationTarget<FieldRelation::Curl<SourceT>> {
template <typename SourceT> struct RelationTarget<FieldRelation::Curl<SourceT>> {
using Type = SourceT;
};
template <typename QuantityT>
using RelationTargetT =
typename RelationTarget<typename QuantityT::Relation>::Type;
template <typename QuantityT> using RelationTargetT = typename RelationTarget<typename QuantityT::Relation>::Type;
// -------------------------------------------------------------------------
// Field quantities
// -------------------------------------------------------------------------
template <int RankV, ValidRelation RelationT, DiscretizationTag DiscT>
struct Quantity {
template <int RankV, ValidRelation RelationT, DiscretizationTag DiscT> struct Quantity {
using Relation = RelationT;
using Discretization = DiscT;
using Space = typename DiscT::Space;
@@ -173,11 +159,9 @@ export namespace mean_field::field {
);
};
template <ValidRelation RelationT, DiscretizationTag DiscT>
using ScalarQ = Quantity<0, RelationT, DiscT>;
template <ValidRelation RelationT, DiscretizationTag DiscT> using ScalarQ = Quantity<0, RelationT, DiscT>;
template <ValidRelation RelationT, DiscretizationTag DiscT>
using VectorQ = Quantity<1, RelationT, DiscT>;
template <ValidRelation RelationT, DiscretizationTag DiscT> using VectorQ = Quantity<1, RelationT, DiscT>;
struct GlobalScalarQ {
using Relation = FieldRelation::Independent;
@@ -188,73 +172,57 @@ export namespace mean_field::field {
};
template <typename T>
concept FieldQuantity =
requires {
typename T::Relation;
typename T::Discretization;
typename T::Space;
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;
{ 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;
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;
{ 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>);
concept DerivedQuantity = FieldQuantity<QuantityT> && (!std::same_as<RelationTargetT<QuantityT>, void>);
// -------------------------------------------------------------------------
// Compile-time discretization constraints
// -------------------------------------------------------------------------
template <FieldQuantity FluxT, FieldQuantity PotentialT>
struct RtL2StablePair {
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."
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."
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(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(
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."
FluxT::familyOrder == PotentialT::familyOrder, "The MFEM RT and L2 family orders must match."
);
}
};
template <typename... ConstraintTs>
consteval bool validate_constraints(TypeList<ConstraintTs...>) {
template <typename... ConstraintTs> consteval bool validate_constraints(TypeList<ConstraintTs...>) {
(ConstraintTs::validate(), ...);
return true;
}
@@ -276,16 +244,11 @@ export namespace mean_field::field {
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::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 {
template <RegisteredQuantity QuantityT, FieldOperationTag OperationT = FieldOperation::Value> struct Operand {
using Quantity = QuantityT;
using Operation = OperationT;
@@ -298,12 +261,10 @@ export namespace mean_field::field {
};
template <typename T>
concept FieldOperand =
requires {
typename T::Quantity;
typename T::Operation;
} && RegisteredQuantity<typename T::Quantity> &&
FieldOperationTag<typename T::Operation>;
concept FieldOperand = requires {
typename T::Quantity;
typename T::Operation;
} && RegisteredQuantity<typename T::Quantity> && FieldOperationTag<typename T::Operation>;
// -------------------------------------------------------------------------
// Weak-form descriptions
@@ -315,11 +276,7 @@ export namespace mean_field::field {
// coefficient supplied at runtime contributes one dynamic order.
// -------------------------------------------------------------------------
template <
auto PolicyKeyV,
std::size_t DynamicOrderCountV,
FieldOperand... OperandTs>
struct FormSpec {
template <auto PolicyKeyV, std::size_t DynamicOrderCountV, FieldOperand... OperandTs> struct FormSpec {
static constexpr auto policyKey = PolicyKeyV;
static constexpr std::size_t dynamicOrderCount = DynamicOrderCountV;
@@ -335,22 +292,46 @@ export namespace mean_field::field {
{ T::dynamicOrderCount } -> std::convertible_to<std::size_t>;
};
template <typename ListT>
struct IsRegisteredQuantityList : std::false_type { };
template <typename ListT> struct IsRegisteredQuantityList : std::false_type { };
template <RegisteredQuantity... QuantityTs>
struct IsRegisteredQuantityList<TypeList<QuantityTs...>> : std::true_type {
};
struct IsRegisteredQuantityList<TypeList<QuantityTs...>> : std::true_type { };
template <typename ListT>
inline constexpr bool isRegisteredQuantityList =
IsRegisteredQuantityList<ListT>::value;
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 <FieldForm... FormTs> struct IsFieldFormList<TypeList<FormTs...>> : std::true_type { };
template <typename ListT> inline constexpr bool isFieldFormList = IsFieldFormList<ListT>::value;
struct FieldSupport { };
template <utils::domain::IsDomainOrSet DomainT> struct DomainSupport final : FieldSupport {
using Domain = DomainT;
};
struct NonSpatialSupport final : FieldSupport { };
template <typename T> constexpr bool isDomainSupportV = false;
template <utils::domain::IsDomainOrSet DomainT> constexpr bool isDomainSupportV<DomainSupport<DomainT>> = true;
template <typename T>
concept IsDomainSupport = isDomainSupportV<T>;
template <typename T>
concept IsFieldSupport = std::derived_from<T, FieldSupport>;
template <typename FieldT> using FieldSupportT = typename FieldT::Support;
template <typename FieldT>
concept DomainSupportedField = requires { typename FieldT::Support; } && IsDomainSupport<FieldSupportT<FieldT>>;
template <typename FieldT>
concept NonSpatialField =
requires { typename FieldT::Support; } && std::same_as<FieldSupportT<FieldT>, NonSpatialSupport>;
template <DomainSupportedField FieldT> using FieldDomainT = typename FieldSupportT<FieldT>::Domain;
template <typename ListT>
inline constexpr bool isFieldFormList = IsFieldFormList<ListT>::value;
} // namespace mean_field::field

View File

@@ -26,9 +26,7 @@ namespace mean_field::field::detail {
int familyOrder,
int dimension
) {
return std::make_unique<mfem::L2_FECollection>(
familyOrder, dimension
);
return std::make_unique<mfem::L2_FECollection>(familyOrder, dimension);
}
};
@@ -37,9 +35,7 @@ namespace mean_field::field::detail {
int familyOrder,
int dimension
) {
return std::make_unique<mfem::H1_FECollection>(
familyOrder, dimension
);
return std::make_unique<mfem::H1_FECollection>(familyOrder, dimension);
}
};
@@ -48,9 +44,7 @@ namespace mean_field::field::detail {
int familyOrder,
int dimension
) {
return std::make_unique<mfem::RT_FECollection>(
familyOrder, dimension
);
return std::make_unique<mfem::RT_FECollection>(familyOrder, dimension);
}
};
@@ -59,9 +53,7 @@ namespace mean_field::field::detail {
int familyOrder,
int dimension
) {
return std::make_unique<mfem::ND_FECollection>(
familyOrder, dimension
);
return std::make_unique<mfem::ND_FECollection>(familyOrder, dimension);
}
};
@@ -86,8 +78,7 @@ namespace mean_field::field::detail {
static constexpr int orderValue = []() consteval {
if constexpr (GlobalScalarQuantity<QuantityT>) {
static_assert(
std::same_as<OperationT, FieldOperation::Value>,
"Global scalars support only the value operation."
std::same_as<OperationT, FieldOperation::Value>, "Global scalars support only the value operation."
);
return 0;
@@ -101,51 +92,36 @@ namespace mean_field::field::detail {
} else {
return familyOrder;
}
} else if constexpr (
std::same_as<OperationT, FieldOperation::Divergence>
) {
} 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."
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>
) {
} 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."
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>
) {
} 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."
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>
) {
} 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."
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_assert(alwaysFalse<OperationT>, "Unsupported MFEM field operation.");
}
}
}();
@@ -157,14 +133,9 @@ namespace mean_field::field::detail {
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);
template <auto PolicyKeyV, std::size_t DynamicOrderCountV, FieldOperand... OperandTs>
struct MfemFormOrder<FormSpec<PolicyKeyV, DynamicOrderCountV, OperandTs...>> {
static constexpr int staticOrder = (MfemOperandOrder<OperandTs>::orderValue + ... + 0);
};
// -------------------------------------------------------------------------
@@ -183,8 +154,7 @@ namespace mean_field::field::detail {
if constexpr (QuantityT::rankValue == 0) {
return 1;
} else if constexpr (
std::same_as<typename QuantityT::Space, H1> ||
std::same_as<typename QuantityT::Space, L2>
std::same_as<typename QuantityT::Space, H1> || std::same_as<typename QuantityT::Space, L2>
) {
return spaceDimension;
} else {
@@ -192,12 +162,10 @@ namespace mean_field::field::detail {
}
}
template <FieldQuantity QuantityT>
constexpr mfem::Ordering::Type get_ordering() {
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>)
(std::same_as<typename QuantityT::Space, H1> || std::same_as<typename QuantityT::Space, L2>)
) {
return mfem::Ordering::byVDIM;
} else {
@@ -213,40 +181,29 @@ namespace mean_field::field::detail {
// -------------------------------------------------------------------------
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 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>();
static constexpr mfem::Ordering::Type ordering = get_ordering<QuantityT>();
};
template <> struct MfemQuantityTraits<Gravity::Flux> {
static std::unique_ptr<mfem::FiniteElementCollection>
make_fec(int dimension) {
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
Gravity::Flux::familyOrder, dimension, mfem::BasisType::GaussLobatto, mfem::BasisType::IntegratedGLL
);
}
static constexpr mfem::Ordering::Type ordering =
mfem::Ordering::byNODES;
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 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;
static constexpr mfem::Ordering::Type ordering = mfem::Ordering::byNODES;
};
} // namespace mean_field::field::detail
@@ -275,8 +232,7 @@ export namespace mean_field::field {
requires typeListContains<
QuantityT,
typename TagT::Quantities>
static std::unique_ptr<mfem::FiniteElementCollection>
make_fec(int dimension) {
static std::unique_ptr<mfem::FiniteElementCollection> make_fec(int dimension) {
if (dimension <= 0) {
throw std::invalid_argument("Mesh dimension must be positive.");
}
@@ -299,8 +255,7 @@ export namespace mean_field::field {
mfem::FiniteElementCollection &finiteElementCollection
) {
return std::make_unique<mfem::ParFiniteElementSpace>(
&mesh, &finiteElementCollection,
detail::get_vdim<QuantityT>(mesh.SpaceDimension()),
&mesh, &finiteElementCollection, detail::get_vdim<QuantityT>(mesh.SpaceDimension()),
detail::MfemQuantityTraits<QuantityT>::ordering
);
}
@@ -338,22 +293,17 @@ export namespace mean_field::field {
int,
FormT::dynamicOrderCount> dynamicOrders = {},
utils::DOMAINS domain = utils::DOMAINS::ALL,
quadrature::MappingKind mapping = quadrature::MappingKind::none
quadrature::MappingKind mapping = quadrature::MappingKind::none
) {
if (geometryWeightOrder < 0) {
throw std::invalid_argument(
"Geometry weight order cannot be negative."
);
throw std::invalid_argument("Geometry weight order cannot be negative.");
}
int baseOrder =
detail::MfemFormOrder<FormT>::staticOrder + geometryWeightOrder;
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."
);
throw std::invalid_argument("Dynamic polynomial orders cannot be negative.");
}
baseOrder += dynamicOrder;
@@ -377,4 +327,580 @@ export namespace mean_field::field {
static_assert(FieldTag<Displacement>);
static_assert(FieldTag<Density>);
static_assert(FieldTag<BarotropicConstant>);
/*
* Field-support realization onto MFEM element and DOF indices.
*
* A field's compile-time Support is declared in field.registry.
* These utilities resolve that semantic support through a DomainSchema
* onto a concrete MFEM finite-element space.
*
* Important:
*
* active DOFs = union of DOFs touched by supported elements
*
* This is deliberately NOT implemented as "remove every DOF touched by
* an unsupported element". For continuous spaces such as H1, a DOF on
* the Stellar/Vacuum interface is shared by elements on both sides and
* remains an active stellar-field DOF.
*/
struct FieldLocalDofSupport {
/*
* Marker in local/vector-DOF numbering.
*
* Size == finiteElementSpace.GetVSize().
* Entries are 1 for active DOFs and 0 otherwise.
*/
mfem::Array<int> activeVDofMarker;
/*
* Sorted MFEM local/vector DOF indices.
*/
mfem::Array<int> activeVDofs;
mfem::Array<int> inactiveVDofs;
};
struct FieldDofSupport {
/*
* Local/vector-DOF information.
*
* For a ParFiniteElementSpace the marker is synchronized across
* neighboring ranks before these lists are constructed, so a shared
* DOF is active on every rank carrying it if any rank has a supported
* element touching it.
*/
mfem::Array<int> activeVDofMarker;
mfem::Array<int> activeVDofs;
mfem::Array<int> inactiveVDofs;
/*
* True-DOF information owned by this MPI rank.
*
* Size of activeTrueDofMarker == GetTrueVSize().
*/
mfem::Array<int> activeTrueDofMarker;
mfem::Array<int> activeTrueDofs;
mfem::Array<int> inactiveTrueDofs;
};
template <typename FieldT>
concept MfemDomainField = FieldTag<FieldT> && DomainSupportedField<FieldT>;
template <
MfemDomainField FieldT,
utils::domain::IsSchema SchemaT>
[[nodiscard]]
bool element_is_in_field_support(
const mfem::Mesh &mesh,
const int elementId
) {
using DomainT = FieldDomainT<FieldT>;
static_assert(
SchemaT::template contains_domain<DomainT>(), "The field support is not completely registered in the "
"supplied DomainSchema."
);
MFEM_VERIFY(
elementId >= 0 && elementId < mesh.GetNE(), "The requested field-support element ID is outside the mesh."
);
return SchemaT::template attribute_belongs_to<DomainT>(mesh.GetAttribute(elementId));
}
namespace detail {
inline void build_marker_lists(
const mfem::Array<int> &activeMarker,
mfem::Array<int> &activeDofs,
mfem::Array<int> &inactiveDofs
) {
mfem::FiniteElementSpace::MarkerToList(activeMarker, activeDofs);
mfem::Array<int> inactiveMarker(activeMarker.Size());
for (int dofId = 0; dofId < activeMarker.Size(); ++dofId) {
inactiveMarker[dofId] = activeMarker[dofId] == 0 ? 1 : 0;
}
mfem::FiniteElementSpace::MarkerToList(inactiveMarker, inactiveDofs);
}
template <
MfemDomainField FieldT,
utils::domain::IsSchema SchemaT>
[[nodiscard]]
mfem::Array<int> build_local_active_vdof_marker(const mfem::FiniteElementSpace &finiteElementSpace) {
using DomainT = FieldDomainT<FieldT>;
static_assert(
SchemaT::template contains_domain<DomainT>(), "The field support is not completely registered in the "
"supplied DomainSchema."
);
const mfem::Mesh *mesh = finiteElementSpace.GetMesh();
MFEM_VERIFY(mesh != nullptr, "Field-support DOF resolution requires an MFEM mesh.");
MFEM_VERIFY(
finiteElementSpace.GetNE() == mesh->GetNE(), "The finite-element space and mesh have incompatible "
"element counts."
);
mfem::Array<int> activeMarker(finiteElementSpace.GetVSize());
activeMarker = 0;
mfem::Array<int> elementVDofs;
for (int elementId = 0; elementId < mesh->GetNE(); ++elementId) {
const int materialId = mesh->GetAttribute(elementId);
if (!SchemaT::template attribute_belongs_to<DomainT>(materialId)) {
continue;
}
finiteElementSpace.GetElementVDofs(elementId, elementVDofs);
for (int localIndex = 0; localIndex < elementVDofs.Size(); ++localIndex) {
/*
* MFEM can encode orientation in a DOF index by using a
* negative value. DecodeDof removes that orientation sign
* and returns the actual local/vector DOF index.
*/
const int vdof = mfem::FiniteElementSpace::DecodeDof(elementVDofs[localIndex]);
MFEM_VERIFY(
vdof >= 0 && vdof < finiteElementSpace.GetVSize(),
"MFEM returned an invalid element vector DOF."
);
activeMarker[vdof] = 1;
}
}
return activeMarker;
}
} // namespace detail
/*
* Serial/local support resolution.
*
* This works with any mfem::FiniteElementSpace and is particularly
* useful for topology/unit tests.
*
* The returned indices use MFEM local/vector-DOF numbering, not
* true-DOF numbering.
*/
template <
MfemDomainField FieldT,
utils::domain::IsSchema SchemaT>
[[nodiscard]]
FieldLocalDofSupport resolve_field_local_dof_support(const mfem::FiniteElementSpace &finiteElementSpace) {
FieldLocalDofSupport result;
result.activeVDofMarker = detail::build_local_active_vdof_marker<FieldT, SchemaT>(finiteElementSpace);
detail::build_marker_lists(result.activeVDofMarker, result.activeVDofs, result.inactiveVDofs);
return result;
}
/*
* Parallel production support resolution.
*
* This additionally converts the field support to the locally-owned
* true-DOF numbering used by nonlinear vectors and operators.
*
* For now this intentionally requires a conforming ParFiniteElementSpace.
* MFEM's nonconforming spaces require an additional constraint/conforming-
* DOF projection step; silently treating their local DOFs as ordinary
* true DOFs would be incorrect.
*/
template <
MfemDomainField FieldT,
utils::domain::IsSchema SchemaT>
[[nodiscard]]
FieldDofSupport resolve_field_dof_support(const mfem::ParFiniteElementSpace &finiteElementSpace) {
FieldDofSupport result;
MFEM_VERIFY(
!finiteElementSpace.Nonconforming(), "Field-support true-DOF resolution currently requires a "
"conforming mfem::ParFiniteElementSpace."
);
result.activeVDofMarker = detail::build_local_active_vdof_marker<FieldT, SchemaT>(finiteElementSpace);
/*
* Shared H1/RT DOFs can lie on an MPI partition boundary.
*
* If a supported element exists on one rank and the shared DOF also
* exists on a neighboring rank whose local elements are unsupported,
* that DOF must nevertheless be active globally.
*
* MFEM Synchronize performs the required OR-like synchronization of
* the marker across shared local DOFs.
*/
finiteElementSpace.Synchronize(result.activeVDofMarker);
detail::build_marker_lists(result.activeVDofMarker, result.activeVDofs, result.inactiveVDofs);
result.activeTrueDofMarker.SetSize(finiteElementSpace.GetTrueVSize());
result.activeTrueDofMarker = 0;
for (int vdof = 0; vdof < result.activeVDofMarker.Size(); ++vdof) {
if (result.activeVDofMarker[vdof] == 0) {
continue;
}
/*
* GetLocalTDofNumber returns the locally-owned true-DOF index
* for this local/vector DOF, or -1 when this rank does not own
* the shared true DOF.
*
* Because activeVDofMarker was synchronized first, the owning
* rank will also see the active marker.
*/
const int trueDof = finiteElementSpace.GetLocalTDofNumber(vdof);
if (trueDof < 0) {
continue;
}
MFEM_VERIFY(trueDof < result.activeTrueDofMarker.Size(), "MFEM returned an invalid local true DOF.");
result.activeTrueDofMarker[trueDof] = 1;
}
detail::build_marker_lists(result.activeTrueDofMarker, result.activeTrueDofs, result.inactiveTrueDofs);
return result;
}
/*
* Canonical correspondence between a dense reduced field vector and
* the selected MFEM true DOFs representing that field.
*
* The map contains no field, domain, mesh, or solver policy. It is an
* immutable indexing object once constructed:
*
* reduced index i
* |
* v
* reducedToTrue[i]
* |
* v
* MFEM true DOF
*
* trueToReduced supplies the inverse map. Unsupported true DOFs carry
* the sentinel -1.
*
* The reduced-to-true list is required to be strictly increasing.
* This makes reduced ordering deterministic and agrees with the
* canonical ordering produced by MFEM MarkerToList().
*/
class FieldDofMap {
public:
FieldDofMap() = default;
FieldDofMap(
const int fullTrueDofSize,
const mfem::Array<int> &reducedToTrue
) {
if (fullTrueDofSize < 0) {
throw std::invalid_argument("FieldDofMap requires a non-negative full true-DOF size.");
}
m_fullTrueDofSize = fullTrueDofSize;
m_reducedToTrue.SetSize(reducedToTrue.Size());
m_trueToReduced.SetSize(m_fullTrueDofSize);
m_trueToReduced = -1;
int previousTrueDof = -1;
for (int reducedDof = 0; reducedDof < reducedToTrue.Size(); ++reducedDof) {
const int trueDof = reducedToTrue[reducedDof];
if (trueDof < 0 || trueDof >= m_fullTrueDofSize) {
throw std::invalid_argument(
"FieldDofMap contains a true DOF outside the full "
"true-DOF space."
);
}
if (reducedDof > 0 && trueDof <= previousTrueDof) {
throw std::invalid_argument(
"FieldDofMap reduced-to-true indices must be "
"strictly increasing and unique."
);
}
m_reducedToTrue[reducedDof] = trueDof;
m_trueToReduced[trueDof] = reducedDof;
previousTrueDof = trueDof;
}
}
/*
* Construct directly from the support result produced by
* resolve_field_dof_support().
*
* The marker is checked against the active true-DOF list so that
* an internally inconsistent FieldDofSupport cannot silently
* produce a solver map.
*/
explicit FieldDofMap(const FieldDofSupport &support)
: FieldDofMap(
support.activeTrueDofMarker.Size(),
support.activeTrueDofs
) {
for (int trueDof = 0; trueDof < m_fullTrueDofSize; ++trueDof) {
const bool markerSaysActive = support.activeTrueDofMarker[trueDof] != 0;
const bool mapSaysActive = m_trueToReduced[trueDof] >= 0;
if (markerSaysActive != mapSaysActive) {
throw std::invalid_argument(
"FieldDofSupport active marker and active true-DOF "
"list are inconsistent."
);
}
}
}
[[nodiscard]]
int full_size() const noexcept {
return m_fullTrueDofSize;
}
[[nodiscard]]
int reduced_size() const noexcept {
return m_reducedToTrue.Size();
}
[[nodiscard]]
int inactive_size() const noexcept {
return full_size() - reduced_size();
}
/*
* Because reducedToTrue is strictly increasing, a map containing
* every true DOF necessarily has
*
* reducedToTrue[i] == i.
*/
[[nodiscard]]
bool is_identity() const noexcept {
return reduced_size() == full_size();
}
[[nodiscard]]
const mfem::Array<int> &reduced_to_true() const noexcept {
return m_reducedToTrue;
}
/*
* Values are:
*
* >= 0 reduced DOF index
* -1 unsupported/inactive true DOF
*/
[[nodiscard]]
const mfem::Array<int> &true_to_reduced() const noexcept {
return m_trueToReduced;
}
[[nodiscard]]
bool contains_true_dof(const int trueDof) const {
validate_true_dof(trueDof);
return m_trueToReduced[trueDof] >= 0;
}
[[nodiscard]]
int true_dof(const int reducedDof) const {
if (reducedDof < 0 || reducedDof >= reduced_size()) {
throw std::out_of_range("Reduced DOF index is outside FieldDofMap.");
}
return m_reducedToTrue[reducedDof];
}
[[nodiscard]]
std::optional<int> reduced_dof(const int trueDof) const {
validate_true_dof(trueDof);
const int reducedDof = m_trueToReduced[trueDof];
if (reducedDof < 0) {
return std::nullopt;
}
return reducedDof;
}
/*
* Gather:
*
* full MFEM true vector
* |
* v
* dense reduced solver vector
*/
void gather(
const mfem::Vector &full,
mfem::Vector &reduced
) const {
require_full_size(full);
require_reduced_size(reduced);
for (int reducedDof = 0; reducedDof < reduced_size(); ++reducedDof) {
reduced(reducedDof) = full(m_reducedToTrue[reducedDof]);
}
}
[[nodiscard]]
mfem::Vector gather(const mfem::Vector &full) const {
mfem::Vector reduced(reduced_size());
gather(full, reduced);
return reduced;
}
/*
* Scatter with projection semantics.
*
* All unsupported true DOFs are explicitly zeroed.
*
* This is the normal operation for constructing a complete MFEM
* representation of a supported field from the reduced nonlinear
* state.
*
* The output vector is NOT resized. This is intentional: callers
* may provide an mfem::Vector view into an mfem::BlockVector.
*/
void scatter(
const mfem::Vector &reduced,
mfem::Vector &full
) const {
require_reduced_size(reduced);
require_full_size(full);
full = 0.0;
scatter_into(reduced, full);
}
[[nodiscard]]
mfem::Vector scatter(const mfem::Vector &reduced) const {
mfem::Vector full(full_size());
scatter(reduced, full);
return full;
}
/*
* Scatter while preserving unsupported values already present in
* the full vector.
*
* This is distinct from scatter() because future constrained field
* representations may need to preserve prescribed values outside
* the current reduced/free set.
*/
void scatter_into(
const mfem::Vector &reduced,
mfem::Vector &full
) const {
require_reduced_size(reduced);
require_full_size(full);
for (int reducedDof = 0; reducedDof < reduced_size(); ++reducedDof) {
full(m_reducedToTrue[reducedDof]) = reduced(reducedDof);
}
}
/*
* Add a reduced vector into the selected true DOFs.
*
* Unsupported true DOFs are untouched.
*/
void scatter_add(
const mfem::Vector &reduced,
mfem::Vector &full,
const double scale = 1.0
) const {
require_reduced_size(reduced);
require_full_size(full);
for (int reducedDof = 0; reducedDof < reduced_size(); ++reducedDof) {
full(m_reducedToTrue[reducedDof]) += scale * reduced(reducedDof);
}
}
private:
void validate_true_dof(const int trueDof) const {
if (trueDof < 0 || trueDof >= full_size()) {
throw std::out_of_range("True DOF index is outside FieldDofMap.");
}
}
void require_full_size(const mfem::Vector &vector) const {
if (vector.Size() != full_size()) {
throw std::invalid_argument("FieldDofMap full vector has an incompatible size.");
}
}
void require_reduced_size(const mfem::Vector &vector) const {
if (vector.Size() != reduced_size()) {
throw std::invalid_argument("FieldDofMap reduced vector has an incompatible size.");
}
}
int m_fullTrueDofSize{0};
/*
* Canonical forward mapping:
*
* reduced -> MFEM true
*/
mfem::Array<int> m_reducedToTrue;
/*
* Inverse mapping:
*
* MFEM true -> reduced
*
* Unsupported true DOFs are -1.
*/
mfem::Array<int> m_trueToReduced;
};
/*
* Construct the canonical solver map for a registered spatial field.
*
* Field and domain semantics are used only while constructing the map.
* Consumers receive a plain FieldDofMap and therefore do not need to
* understand DomainSchema or field-support types.
*/
template <
MfemDomainField FieldT,
utils::domain::IsSchema SchemaT>
[[nodiscard]]
FieldDofMap make_field_dof_map(const mfem::ParFiniteElementSpace &finiteElementSpace) {
const FieldDofSupport support = resolve_field_dof_support<FieldT, SchemaT>(finiteElementSpace);
return FieldDofMap(support);
}
} // namespace mean_field::field

View File

@@ -7,6 +7,7 @@ export module mean_field:field.registry;
export import :field.base;
export import :quadrature.policy;
export import :utils.domain;
export namespace mean_field::field {
// =========================================================================
@@ -17,70 +18,47 @@ export namespace mean_field::field {
static constexpr std::string_view name = "density";
static constexpr int scalarOrder = 2;
struct Scalar final
: ScalarQ<FieldRelation::Independent, Disc<L2, scalarOrder>> {
using Support = DomainSupport<utils::domain::Stellar>;
struct Scalar final : ScalarQ<FieldRelation::Independent, Disc<L2, scalarOrder>> {
static constexpr std::string_view symbol = "ρ";
};
using Quantities = TypeList<Scalar>;
using Constraints = TypeList<>;
using Quantities = TypeList<Scalar>;
using Constraints = TypeList<>;
static constexpr bool constraintsAreValid =
validate_constraints(Constraints{});
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>>;
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>>;
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>>;
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>>;
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>>;
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>>;
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 Quadrupole = FormSpec<quadrature::Term::quadrupole, 1, Operand<Scalar>>;
using ErrorNorm = FormSpec<
quadrature::Term::error_norm,
0,
Operand<Scalar>,
Operand<Scalar>>;
using ErrorNorm = FormSpec<quadrature::Term::error_norm, 0, Operand<Scalar>, Operand<Scalar>>;
};
using FormList = TypeList<
@@ -104,31 +82,26 @@ export namespace mean_field::field {
static constexpr int potentialOrder = 2;
static constexpr int fluxOrder = 2;
struct Potential final
: ScalarQ<FieldRelation::Independent, Disc<L2, potentialOrder>> {
using Support = DomainSupport<utils::domain::All>;
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>> {
struct Flux final : VectorQ<FieldRelation::Gradient<Potential>, Disc<RT, fluxOrder>> {
static constexpr std::string_view symbol = "∇φ";
};
using Quantities = TypeList<Potential, Flux>;
using Quantities = TypeList<Potential, Flux>;
using Constraints = TypeList<RtL2StablePair<Flux, Potential>>;
using Constraints = TypeList<RtL2StablePair<Flux, Potential>>;
static constexpr bool constraintsAreValid =
validate_constraints(Constraints{});
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 HDivMass = FormSpec<quadrature::Term::gravity_hdiv_mass, 0, Operand<Flux>, Operand<Flux>>;
using DivergenceCoupling = FormSpec<
quadrature::Term::gravity_divergence,
@@ -144,31 +117,18 @@ export namespace mean_field::field {
// 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>>;
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 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 PotentialErrorNorm =
FormSpec<quadrature::Term::error_norm, 0, Operand<Potential>, Operand<Potential>>;
using FluxErrorNorm = FormSpec<
quadrature::Term::error_norm,
0,
Operand<Flux>,
Operand<Flux>>;
using FluxErrorNorm = FormSpec<quadrature::Term::error_norm, 0, Operand<Flux>, Operand<Flux>>;
};
using FormList = TypeList<
@@ -189,16 +149,16 @@ export namespace mean_field::field {
static constexpr std::string_view name = "displacement";
static constexpr int vectorOrder = 3;
struct Vector final
: VectorQ<FieldRelation::Independent, Disc<H1, vectorOrder>> {
using Support = DomainSupport<utils::domain::All>;
struct Vector final : VectorQ<FieldRelation::Independent, Disc<H1, vectorOrder>> {
static constexpr std::string_view symbol = "d";
};
using Quantities = TypeList<Vector>;
using Constraints = TypeList<>;
using Quantities = TypeList<Vector>;
using Constraints = TypeList<>;
static constexpr bool constraintsAreValid =
validate_constraints(Constraints{});
static constexpr bool constraintsAreValid = validate_constraints(Constraints{});
static_assert(constraintsAreValid);
@@ -211,29 +171,50 @@ export namespace mean_field::field {
Operand<Vector, FieldOperation::Gradient>,
Operand<Vector, FieldOperation::Gradient>>;
using ErrorNorm = FormSpec<
quadrature::Term::error_norm,
// Positive gravitational contribution to the displacement row:
//
// int rho grad(phi) . w dV.
//
// Both the base geometry Jacobian and the displacement test
// function contribute to the polynomial order. The RT flux is
// mapped to physical space by the contravariant Piola map.
using GravityForce = FormSpec<
quadrature::Term::gravity_force,
0,
Operand<Vector>,
Operand<Density::Scalar>,
Operand<Gravity::Flux>,
Operand<Vector, FieldOperation::Gradient>,
Operand<Vector>>;
// Rigid-rotation contribution to the displacement row:
//
// -int rho grad(Psi_rotation) . w dV.
//
// grad(Psi_rotation) is linear in physical position, so its
// polynomial order is supplied as one runtime contribution.
using CentrifugalForce =
FormSpec<quadrature::Term::centrifugal, 1, Operand<Density::Scalar>, Operand<Vector>>;
using ErrorNorm = FormSpec<quadrature::Term::error_norm, 0, Operand<Vector>, Operand<Vector>>;
};
using FormList = TypeList<Form::MeshExtension, Form::ErrorNorm>;
using FormList = TypeList<Form::MeshExtension, Form::GravityForce, Form::CentrifugalForce, Form::ErrorNorm>;
};
struct BarotropicConstant {
static constexpr std::string_view name = "barotropic_constant";
using Support = NonSpatialSupport;
struct Scalar final : GlobalScalarQ {
static constexpr std::string_view symbol = "C";
};
using Quantities = TypeList<Scalar>;
using Constraints = TypeList<>;
using FormList = TypeList<>;
using Quantities = TypeList<Scalar>;
using Constraints = TypeList<>;
using FormList = TypeList<>;
static constexpr bool constraintsAreValid =
validate_constraints(Constraints{});
static constexpr bool constraintsAreValid = validate_constraints(Constraints{});
static_assert(constraintsAreValid);
};
@@ -250,16 +231,16 @@ export namespace mean_field::field {
static constexpr std::string_view name = "specific_enthalpy";
static constexpr int scalarOrder = 3;
struct Scalar final
: ScalarQ<FieldRelation::Independent, Disc<H1, scalarOrder>> {
using Support = DomainSupport<utils::domain::Stellar>;
struct Scalar final : ScalarQ<FieldRelation::Independent, Disc<H1, scalarOrder>> {
static constexpr std::string_view symbol = "h";
};
using Quantities = TypeList<Scalar>;
using Constraints = TypeList<>;
using Quantities = TypeList<Scalar>;
using Constraints = TypeList<>;
static constexpr bool constraintsAreValid =
validate_constraints(Constraints{});
static constexpr bool constraintsAreValid = validate_constraints(Constraints{});
static_assert(constraintsAreValid);
@@ -268,33 +249,21 @@ export namespace mean_field::field {
// 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>>;
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>>;
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>>;
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>>;
using EquilibriumRotation = FormSpec<quadrature::Term::hydrostatic_equilibrium, 1, Operand<Scalar>>;
// (C, q_h), where C is spatially constant.
using EquilibriumConstant = FormSpec<
@@ -305,18 +274,11 @@ export namespace mean_field::field {
// 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>>;
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>>;
using PressureIntegral = FormSpec<quadrature::Term::pressure_integral, 1, Operand<Scalar>>;
// Weak pressure force in the displacement test space:
//
@@ -325,17 +287,13 @@ export namespace mean_field::field {
// 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<
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 ErrorNorm = FormSpec<quadrature::Term::error_norm, 0, Operand<Scalar>, Operand<Scalar>>;
};
using FormList = TypeList<
@@ -360,9 +318,10 @@ export namespace mean_field::field {
typename T::Quantities;
typename T::Constraints;
typename T::FormList;
typename T::Support;
{ T::name } -> std::convertible_to<std::string_view>;
} && isRegisteredQuantityList<typename T::Quantities> &&
} && IsFieldSupport<typename T::Support> && isRegisteredQuantityList<typename T::Quantities> &&
isFieldFormList<typename T::FormList>;
static_assert(FieldTag<Gravity>);
@@ -376,4 +335,22 @@ export namespace mean_field::field {
static_assert(std::same_as<
RelationTargetT<Gravity::Flux>,
Gravity::Potential>);
static_assert(std::same_as<
FieldDomainT<Density>,
utils::domain::Stellar>);
static_assert(std::same_as<
FieldDomainT<Enthalpy>,
utils::domain::Stellar>);
static_assert(std::same_as<
FieldDomainT<Gravity>,
utils::domain::All>);
static_assert(std::same_as<
FieldDomainT<Displacement>,
utils::domain::All>);
static_assert(NonSpatialField<BarotropicConstant>);
} // namespace mean_field::field

View File

@@ -4,8 +4,7 @@ export module mean_field:integrators.centrifugal;
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,

View File

@@ -5,19 +5,13 @@ export module mean_field:integrators.gravity;
import :mapping.domain_mapper;
export namespace mean_field::integrators {
enum class GravityForceJacobianMode : std::uint8_t {
minimal,
field_coupled,
exact
};
enum class GravityForceJacobianMode : std::uint8_t { minimal, field_coupled, exact };
class GravityMomentumIntegrator
: public mfem::BlockNonlinearFormIntegrator {
class GravityMomentumIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
explicit GravityMomentumIntegrator(
const mapping::DomainMapper &map,
GravityForceJacobianMode jacobian_mode =
GravityForceJacobianMode::field_coupled
GravityForceJacobianMode jacobian_mode = GravityForceJacobianMode::field_coupled
);
void SetJacobianMode(GravityForceJacobianMode jacobian_mode);

View File

@@ -4,8 +4,7 @@ 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);

View File

@@ -7,9 +7,7 @@ import :mapping.domain_mapper;
import :utils.misc;
export namespace mean_field::integrators {
template <utils::is_xad EOS_T>
class PressureGradientIntegrator
: public mfem::BlockNonlinearFormIntegrator {
template <utils::is_xad EOS_T> class PressureGradientIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
PressureGradientIntegrator(
const mapping::DomainMapper &map,
@@ -74,8 +72,7 @@ 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);
@@ -136,8 +133,7 @@ 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) {
using Scalar = EOS_T::value_type;
@@ -168,8 +164,7 @@ export namespace mean_field::integrators {
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 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);
@@ -177,9 +172,8 @@ export namespace mean_field::integrators {
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);
int col = j;
double term = dshape_v_phys(i, c) * dP_drho * shape_rho(j);
(*dv_drho)(row, col) -= term * weight;
}
}

View File

@@ -10,9 +10,7 @@ export namespace mean_field::mapping::compactification {
class KelvinCompactification final : public ExteriorDomainMap {
public:
explicit KelvinCompactification(
options::KelvinCompactificationOptions options
);
explicit KelvinCompactification(options::KelvinCompactificationOptions options);
[[nodiscard]] MappingStatus Evaluate(
const ExteriorMapInput &input,

View File

@@ -36,8 +36,7 @@ export namespace mean_field::mapping {
mfem::Vector coordinate_gradient;
};
[[nodiscard]] ElementDisplacementData
ElementDisplacementDataFromElementVDofs(
[[nodiscard]] ElementDisplacementData ElementDisplacementDataFromElementVDofs(
const mfem::FiniteElement &element,
const mfem::Vector &displacement_dofs
);
@@ -102,15 +101,13 @@ export namespace mean_field::mapping {
public:
DomainMapperStateless(
utils::DomainMapperStatelessOptions options,
std::unique_ptr<const compactification::ExteriorDomainMap>
exterior_map
std::unique_ptr<const compactification::ExteriorDomainMap> exterior_map
);
DomainMapperStateless(const DomainMapperStateless &) = delete;
DomainMapperStateless &
operator=(const DomainMapperStateless &) = delete;
DomainMapperStateless(DomainMapperStateless &&) = default;
DomainMapperStateless &operator=(DomainMapperStateless &&) = default;
DomainMapperStateless(const DomainMapperStateless &) = delete;
DomainMapperStateless &operator=(const DomainMapperStateless &) = delete;
DomainMapperStateless(DomainMapperStateless &&) = default;
DomainMapperStateless &operator=(DomainMapperStateless &&) = default;
[[nodiscard]] MappingStatus EvaluatePoint(
const ElementMappingData &element_data,
@@ -168,13 +165,10 @@ export namespace mean_field::mapping {
FaceMappingVariation &variation
) const;
[[nodiscard]] bool IsCompactifiedElement(
const mfem::ElementTransformation &transformation
) const noexcept;
[[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;
[[nodiscard]] const compactification::ExteriorDomainMap &GetExteriorMap() const noexcept;
private:
void ValidateElementData(const ElementMappingData &element_data) const;
@@ -196,21 +190,18 @@ export namespace mean_field::mapping {
CompactificationPointData &point_data
) const;
[[nodiscard]] static mfem::ElementTransformation &
SelectFaceElementTransformation(
[[nodiscard]] static mfem::ElementTransformation &SelectFaceElementTransformation(
mfem::FaceElementTransformations &transformation,
FaceElementSide side
);
[[nodiscard]] static const mfem::IntegrationPoint &
SelectFaceElementIntegrationPoint(
[[nodiscard]] static const mfem::IntegrationPoint &SelectFaceElementIntegrationPoint(
mfem::FaceElementTransformations &transformation,
FaceElementSide side
);
utils::DomainMapperStatelessOptions m_options;
std::unique_ptr<const compactification::ExteriorDomainMap>
m_exterior_map;
std::unique_ptr<const compactification::ExteriorDomainMap> m_exterior_map;
};
class DomainMapper {
@@ -226,8 +217,7 @@ export namespace mean_field::mapping {
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);

View File

@@ -45,3 +45,21 @@ export import :operators.kernels.hydrostatic_equilibrium;
export import :operators.context.hydrostatic_equilibrium;
export import :operators.prepared_hydrostatic_equilibrium;
export import :operators.kernels.pressure_force;
export import :operators.context.pressure_force;
export import :operators.prepared_pressure_force;
export import :operators.kernels.gravity_displacement_force;
export import :operators.prepared_gravity_displacement_force;
export import :operators.context.rotational_displacement_force;
export import :operators.kernels.rotational_displacement_force;
export import :operators.prepared_rotational_displacement_force;
export import :operators.prepared_displacement_residual;
export import :model.structure_profile;
export import :model.structure.base;
export import :model.structure.polytropic;
export import :eos.base;
export import :eos.polytrope;
export import :surface.base;
export import :surface.isobaric;
export import :model.stellar;
export import :operators.prepared_mass_normalization;
export import :operators.prepared_stellar_equilibrium;

View File

@@ -0,0 +1,114 @@
module;
#include <concepts>
#include <memory>
#include <type_traits>
#include <utility>
export module mean_field:model.stellar;
export import :eos.base;
export import :model.structure.base;
export import :surface.base;
export namespace mean_field::models {
template <typename Candidate>
concept StructurePrescription =
std::derived_from<std::remove_cvref_t<Candidate>, mean_field::models::structure::StructureBase>;
template <typename Candidate>
concept SurfacePrescription = std::derived_from<std::remove_cvref_t<Candidate>, mean_field::surface::SurfaceBase>;
/*
* Public ownership facade for a physical structure prescription and its
* stellar-surface prescription.
*
* The concrete prescriptions are allocated once at construction. Their
* stable addresses allow future prepared operators and contexts to borrow
* references without making ownership part of the user-facing API.
*/
class StellarModel final {
public:
template <
StructurePrescription StructureType,
SurfacePrescription SurfaceType>
explicit StellarModel(
StructureType &&structurePrescription,
SurfaceType &&surfacePrescription
)
: StellarModel(
std::make_unique<std::remove_cvref_t<StructureType>>(
std::forward<StructureType>(structurePrescription)
),
std::make_unique<std::remove_cvref_t<SurfaceType>>(std::forward<SurfaceType>(surfacePrescription))
) {
}
~StellarModel() = default;
StellarModel(const StellarModel &) = delete;
StellarModel &operator=(const StellarModel &) = delete;
StellarModel(StellarModel &&) noexcept = default;
StellarModel &operator=(StellarModel &&) noexcept = default;
[[nodiscard]] const mean_field::models::structure::StructureBase &structurePrescription() const noexcept {
return *m_structurePrescription;
}
[[nodiscard]] const mean_field::surface::SurfaceBase &surfacePrescription() const noexcept {
return *m_surfacePrescription;
}
[[nodiscard]] const mean_field::eos::EquationOfState &equationOfState() const noexcept {
return m_structurePrescription->equationOfState();
}
[[nodiscard]] double targetMass() const noexcept {
return m_structurePrescription->targetMass();
}
[[nodiscard]] mean_field::models::structure::StructureSeed
makeInitialSeed(const mean_field::models::structure::StructureSeedRequest &request) const {
return m_structurePrescription->makeInitialSeed(request);
}
[[nodiscard]] const mean_field::surface::ResolvedSurfaceCondition &resolvedSurfaceCondition() const noexcept {
return m_resolvedSurfaceCondition;
}
private:
explicit StellarModel(
std::unique_ptr<mean_field::models::structure::StructureBase> structurePrescription,
std::unique_ptr<mean_field::surface::SurfaceBase> surfacePrescription
)
: m_structurePrescription(std::move(structurePrescription)),
m_surfacePrescription(std::move(surfacePrescription)),
m_resolvedSurfaceCondition(validateAndResolve(
*m_structurePrescription,
*m_surfacePrescription
)) {
}
[[nodiscard]] static mean_field::surface::ResolvedSurfaceCondition validateAndResolve(
const mean_field::models::structure::StructureBase &structurePrescription,
const mean_field::surface::SurfaceBase &surfacePrescription
) {
structurePrescription.validate();
const mean_field::eos::EquationOfState &equationOfState = structurePrescription.equationOfState();
surfacePrescription.validate(equationOfState);
return surfacePrescription.resolve(equationOfState);
}
std::unique_ptr<mean_field::models::structure::StructureBase> m_structurePrescription;
std::unique_ptr<mean_field::surface::SurfaceBase> m_surfacePrescription;
mean_field::surface::ResolvedSurfaceCondition m_resolvedSurfaceCondition;
};
} // namespace mean_field::models

View File

@@ -0,0 +1,68 @@
module;
#include <vector>
#include <mfem.hpp>
export module mean_field:model.structure.polytropic;
export import :eos.polytrope;
export import :model.structure.base;
import :utils.misc;
export namespace mean_field::models::structure {
class PolytropicStructure final : public StructureBase {
public:
explicit PolytropicStructure(
eos::Polytrope equationOfState,
double targetMass
);
[[nodiscard]] const eos::EquationOfState &equationOfState() const noexcept override;
[[nodiscard]] double targetMass() const noexcept override;
[[nodiscard]] StructureSeed makeInitialSeed(const StructureSeedRequest &request) const override;
void validate() const override;
private:
struct LaneEmdenPoint {
double coordinate{0.0};
double value{0.0};
double derivative{0.0};
};
struct LaneEmdenDerivative {
double value{0.0};
double derivative{0.0};
};
static void validateSeedRequest(const StructureSeedRequest &request);
[[nodiscard]] static LaneEmdenDerivative evaluateLaneEmdenRhs(
double coordinate,
double value,
double derivative,
double polytropicIndex
);
[[nodiscard]] static LaneEmdenPoint takeLaneEmdenStep(
const LaneEmdenPoint &point,
double step,
double polytropicIndex
);
[[nodiscard]] static std::vector<LaneEmdenPoint> solveLaneEmden(double polytropicIndex);
[[nodiscard]] static double interpolateLaneEmdenValue(
const std::vector<LaneEmdenPoint> &solution,
double coordinate,
std::size_t &lowerIndex
);
eos::Polytrope m_equationOfState;
double m_targetMass;
};
} // namespace mean_field::models::structure

View File

@@ -0,0 +1,37 @@
module;
#include <mfem.hpp>
export module mean_field:model.structure.base;
export import :eos.base;
export namespace mean_field::models::structure {
struct StructureSeed {
mfem::Vector radius;
mfem::Vector density;
mfem::Vector enthalpy;
double stellarRadius;
double centralDensity;
double centralEnthalpy;
};
struct StructureSeedRequest {
double centralDensity;
int radialSampleCount{512};
};
class StructureBase {
public:
virtual ~StructureBase() = default;
[[nodiscard]] virtual const eos::EquationOfState &equationOfState() const noexcept = 0;
[[nodiscard]] virtual double targetMass() const noexcept = 0;
[[nodiscard]] virtual StructureSeed makeInitialSeed(const StructureSeedRequest &request) const = 0;
virtual void validate() const = 0;
protected:
StructureBase() = default;
};
} // namespace mean_field::models::structure

View File

@@ -0,0 +1,146 @@
module;
#include <mfem.hpp>
export module mean_field:model.structure_profile;
export import :fem;
export namespace mean_field::models {
constexpr double DEFAULT_COLATITUDE_DEGREES = 90;
constexpr double DEFAULT_LONGITUDE_DEGREES = 0;
struct RadialDirection {
double coLatitudeDegrees{DEFAULT_COLATITUDE_DEGREES};
double longitudeDegrees{DEFAULT_LONGITUDE_DEGREES};
[[nodiscard]] mfem::Vector toUnitCartesian() const;
};
struct RadialProfile {
double coLatitudeDegrees;
double longitudeDegrees;
mfem::Vector radius;
mfem::Vector param;
};
struct SliceProfile {
double radius;
std::array<double, 2> normal;
mfem::GridFunction param;
};
class StructureProfile {
public:
explicit StructureProfile(
const fem::FEM &fem,
mfem::Vector state
);
// Profiles
mfem::GridFunction pressureProfile();
mfem::GridFunction densityProfile();
mfem::GridFunction gravitationalPotentialProfile();
mfem::GridFunction gravitationalFieldProfile();
mfem::GridFunction enthalpyProfile();
mfem::GridFunction entropyProfile();
mfem::GridFunction temperatureProfile();
mfem::GridFunction internalEnergyProfile();
// Local Evaluation
double pressureAt(const mfem::Vector &position);
double densityAt(const mfem::Vector &position);
double gravitationalPotentialAt(const mfem::Vector &position);
mfem::Vector gravitationalFieldAt(const mfem::Vector &position);
double enthalpyAt(const mfem::Vector &position);
double entropyAt(const mfem::Vector &position);
double temperatureAt(const mfem::Vector &position);
double internalEnergyAt(const mfem::Vector &position);
// Radial helpers
mfem::Vector radius(RadialDirection direction = {});
RadialProfile radialPressureProfile(RadialDirection direction = {});
RadialProfile radialDensityProfile(RadialDirection direction = {});
RadialProfile radialGravitationalPotentialProfile(RadialDirection direction = {});
RadialProfile radialGravitationalFieldProfile(RadialDirection direction = {});
RadialProfile radialEnthalpyProfile(RadialDirection direction = {});
RadialProfile radialEntropyProfile(RadialDirection direction = {});
RadialProfile radialTemperatureProfile(RadialDirection direction = {});
RadialProfile radialInternalEnergyProfile(RadialDirection direction = {});
// Ellipsoidal Slices
SliceProfile slicePressureProfile(
double radius,
const std::array<
double,
2> &normal
);
SliceProfile sliceDensityProfile(
double radius,
const std::array<
double,
2> &normal
);
SliceProfile sliceGravitationalPotentialProfile(
double radius,
const std::array<
double,
2> &normal
);
SliceProfile sliceGravitationalFieldProfile(
double radius,
const std::array<
double,
2> &normal
);
SliceProfile sliceEnthalpyProfile(
double radius,
const std::array<
double,
2> &normal
);
SliceProfile sliceEntropyProfile(
double radius,
const std::array<
double,
2> &normal
);
SliceProfile sliceTemperatureProfile(
double radius,
const std::array<
double,
2> &normal
);
SliceProfile sliceInternalEnergyProfile(
double radius,
const std::array<
double,
2> &normal
);
// Integral Constraints
double totalMass();
double virialRatio();
// Diagnostics
bool isBound();
// IO
void radialToCSV(
const std::string &filename,
RadialDirection direction = {}
);
void radialToBIN(
const std::string &filename,
RadialDirection direction = {}
);
void toBIN(const std::string &filename);
private:
const fem::FEM &m_fem;
const mfem::Vector m_state;
};
StructureProfile StructureProfileFromBIN(const std::string &filename);
} // namespace mean_field::models

View File

@@ -1,5 +1,6 @@
module;
#include <compare>
#include <cstdint>
#include <mfem.hpp>
@@ -7,18 +8,66 @@ module;
export module mean_field:operators.context.barotropic_closure_linearization;
export import :fem;
export import :field.mfem;
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;
template <typename Tag> struct DependencyStamp {
std::uint64_t identity{0};
std::uint64_t revision{0};
[[nodiscard]] bool
operator==(const BarotropicClosureRevisions &) const noexcept = default;
[[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 DensityDependencyTag { };
struct EnthalpyDependencyTag { };
struct DisplacementDependencyTag { };
using DiscretizationDependency = DependencyStamp<DiscretizationDependencyTag>;
using DensityDependency = DependencyStamp<DensityDependencyTag>;
using EnthalpyDependency = DependencyStamp<EnthalpyDependencyTag>;
using DisplacementDependency = DependencyStamp<DisplacementDependencyTag>;
struct BarotropicClosureDependencies final {
DiscretizationDependency discretization;
DensityDependency density;
EnthalpyDependency enthalpy;
DisplacementDependency displacement;
constexpr auto operator<=>(const BarotropicClosureDependencies &) const = default;
};
struct BarotropicClosureStateView final {
const mfem::Vector &density;
const mfem::Vector &enthalpy;
const mfem::Vector &displacement;
};
struct BarotropicClosurePreparationReport final {
bool preparedStaticDependencies{false};
bool preparedGeometryState{false};
bool preparedBaseState{false};
bool updatedDensity{false};
bool updatedEnthalpy{false};
bool updatedDisplacement{false};
[[nodiscard]] bool DidAnyWork() const noexcept {
return preparedStaticDependencies || preparedGeometryState || preparedBaseState;
}
};
struct BarotropicClosurePreparationStatistics final {
std::uint64_t staticPreparations{0};
std::uint64_t geometryPreparations{0};
std::uint64_t baseStatePreparations{0};
constexpr auto operator<=>(const BarotropicClosurePreparationStatistics &) const = default;
};
class BarotropicClosureLinearizationContext final {
@@ -26,51 +75,46 @@ export namespace mean_field::operators::context::barotropic {
BarotropicClosureLinearizationContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope
const field::FieldDofMap &densityMap,
const field::FieldDofMap &enthalpyMap,
const field::FieldDofMap &displacementMap
);
void Prepare(
const mfem::Vector &baseDensityTrue,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &displacementTrue,
const BarotropicClosureRevisions &revisions
BarotropicClosureLinearizationContext(const BarotropicClosureLinearizationContext &) = delete;
BarotropicClosureLinearizationContext &operator=(const BarotropicClosureLinearizationContext &) = delete;
BarotropicClosureLinearizationContext(BarotropicClosureLinearizationContext &&) = delete;
BarotropicClosureLinearizationContext &operator=(BarotropicClosureLinearizationContext &&) = delete;
BarotropicClosurePreparationReport Prepare(
const BarotropicClosureStateView &state,
const BarotropicClosureDependencies &dependencies
);
[[nodiscard]] bool IsPrepared() const noexcept;
[[nodiscard]] bool MatchesDependencies(const BarotropicClosureDependencies &dependencies) const noexcept;
[[nodiscard]] const BarotropicClosureDependencies &GetDependencies() const;
[[nodiscard]] const BarotropicClosurePreparationStatistics &GetPreparationStatistics() 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;
[[nodiscard]] const mfem::Vector &GetBaseDensity() const;
[[nodiscard]] const mfem::Vector &GetBaseEnthalpy() const;
[[nodiscard]] const mfem::Vector &GetDisplacement() const;
private:
void VerifyPrepared() const;
const fem::FEM &m_f;
const mapping::DomainMapperStateless &m_domainMapper;
PreparedBarotropicClosureOperator m_operator;
int m_densitySize{0};
int m_enthalpySize{0};
int m_displacementSize{0};
mfem::Vector m_baseDensityTrue;
mfem::Vector m_baseEnthalpyTrue;
mfem::Vector m_displacementTrue;
mfem::Vector m_baseDensity;
mfem::Vector m_baseEnthalpy;
mfem::Vector m_displacement;
BarotropicClosureRevisions m_revisions;
std::uint64_t m_preparationCount = 0;
bool m_isPrepared = false;
BarotropicClosureDependencies m_dependencies;
BarotropicClosurePreparationStatistics m_statistics;
bool m_isPrepared{false};
};
} // namespace mean_field::operators::context::barotropic
} // namespace mean_field::operators::context::barotropic

View File

@@ -51,8 +51,8 @@ export namespace mean_field::operators::context::gravity_field {
bool refreshed_variation_state{false};
[[nodiscard]] bool DidAnyWork() const noexcept {
return reconstructed_operators || rebuilt_mass_operator ||
rebuilt_source_operator || refreshed_variation_state;
return reconstructed_operators || rebuilt_mass_operator || rebuilt_source_operator ||
refreshed_variation_state;
}
};
@@ -63,13 +63,10 @@ export namespace mean_field::operators::context::gravity_field {
const mapping::DomainMapperStateless &domain_mapper
);
GravityFieldGeometryContext(const GravityFieldGeometryContext &) =
delete;
GravityFieldGeometryContext &
operator=(const GravityFieldGeometryContext &) = delete;
GravityFieldGeometryContext(GravityFieldGeometryContext &&) = delete;
GravityFieldGeometryContext &
operator=(GravityFieldGeometryContext &&) = delete;
GravityFieldGeometryContext(const GravityFieldGeometryContext &) = delete;
GravityFieldGeometryContext &operator=(const GravityFieldGeometryContext &) = delete;
GravityFieldGeometryContext(GravityFieldGeometryContext &&) = delete;
GravityFieldGeometryContext &operator=(GravityFieldGeometryContext &&) = delete;
GravityFieldGeometryPreparation Prepare(
const mfem::Vector &displacement_true,
@@ -77,15 +74,11 @@ export namespace mean_field::operators::context::gravity_field {
DisplacementRevision displacement_revision
);
[[nodiscard]] const PreparedMappedHDivMassOperator &
GetMassOperator() const;
[[nodiscard]] const PreparedMappedGravitySourceOperator &
GetSourceOperator() const;
[[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]] DiscretizationRevision GetDiscretizationRevision() const noexcept;
[[nodiscard]] DisplacementRevision GetDisplacementRevision() const noexcept;
[[nodiscard]] bool IsPrepared() const noexcept;
private:
@@ -109,8 +102,7 @@ export namespace mean_field::operators::context::gravity_field {
bool updated_gravity_gradient{false};
[[nodiscard]] bool DidAnyWork() const noexcept {
return geometry.DidAnyWork() || updated_density ||
updated_gravity_gradient;
return geometry.DidAnyWork() || updated_density || updated_gravity_gradient;
}
};
@@ -121,23 +113,17 @@ export namespace mean_field::operators::context::gravity_field {
const mapping::DomainMapperStateless &domain_mapper
);
GravityFieldLinearizationContext(
const GravityFieldLinearizationContext &
) = delete;
GravityFieldLinearizationContext &
operator=(const GravityFieldLinearizationContext &) = delete;
GravityFieldLinearizationContext(GravityFieldLinearizationContext &&) =
delete;
GravityFieldLinearizationContext &
operator=(GravityFieldLinearizationContext &&) = delete;
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 GravityFieldGeometryContext &GetGeometryContext() const;
[[nodiscard]] const mfem::Vector &GetDensity() const;
[[nodiscard]] const mfem::Vector &GetGravityGradient() const;
[[nodiscard]] const GravityFieldRevisions &GetRevisions() const;

View File

@@ -15,10 +15,8 @@ export namespace mean_field::operators::context::hydrostatic {
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;
[[nodiscard]] constexpr bool CanFollow(const DependencyStamp &prepared) const noexcept {
return identity != prepared.identity || revision >= prepared.revision;
}
constexpr auto operator<=>(const DependencyStamp &) const = default;
@@ -31,20 +29,17 @@ export namespace mean_field::operators::context::hydrostatic {
struct RotationDependencyTag { };
struct BernoulliConstantDependencyTag { };
using DiscretizationDependency =
DependencyStamp<DiscretizationDependencyTag>;
using DiscretizationDependency = DependencyStamp<DiscretizationDependencyTag>;
using EnthalpyDependency = DependencyStamp<EnthalpyDependencyTag>;
using EnthalpyDependency = DependencyStamp<EnthalpyDependencyTag>;
using GravityPotentialDependency =
DependencyStamp<GravityPotentialDependencyTag>;
using GravityPotentialDependency = DependencyStamp<GravityPotentialDependencyTag>;
using DisplacementDependency = DependencyStamp<DisplacementDependencyTag>;
using DisplacementDependency = DependencyStamp<DisplacementDependencyTag>;
using RotationDependency = DependencyStamp<RotationDependencyTag>;
using RotationDependency = DependencyStamp<RotationDependencyTag>;
using BernoulliConstantDependency =
DependencyStamp<BernoulliConstantDependencyTag>;
using BernoulliConstantDependency = DependencyStamp<BernoulliConstantDependencyTag>;
struct HydrostaticEquilibriumDependencies {
DiscretizationDependency discretization;
@@ -54,8 +49,7 @@ export namespace mean_field::operators::context::hydrostatic {
RotationDependency rotation;
BernoulliConstantDependency bernoulliConstant;
constexpr auto
operator<=>(const HydrostaticEquilibriumDependencies &) const = default;
constexpr auto operator<=>(const HydrostaticEquilibriumDependencies &) const = default;
};
struct HydrostaticEquilibriumStateView {
@@ -77,8 +71,8 @@ export namespace mean_field::operators::context::hydrostatic {
bool updatedBernoulliConstant{false};
[[nodiscard]] bool DidAnyWork() const noexcept {
return preparedStaticDependencies || preparedGeometryState ||
preparedRotationDependencies || preparedBaseState;
return preparedStaticDependencies || preparedGeometryState || preparedRotationDependencies ||
preparedBaseState;
}
};
@@ -88,8 +82,7 @@ export namespace mean_field::operators::context::hydrostatic {
std::uint64_t rotationPreparations{0};
std::uint64_t baseStatePreparations{0};
constexpr auto
operator<=>(const HydrostaticPreparationStatistics &) const = default;
constexpr auto operator<=>(const HydrostaticPreparationStatistics &) const = default;
};
class HydrostaticEquilibriumContext {
@@ -99,17 +92,13 @@ export namespace mean_field::operators::context::hydrostatic {
const mapping::DomainMapperStateless &domainMapper
);
HydrostaticEquilibriumContext(const HydrostaticEquilibriumContext &) =
delete;
HydrostaticEquilibriumContext(const HydrostaticEquilibriumContext &) = delete;
HydrostaticEquilibriumContext &
operator=(const HydrostaticEquilibriumContext &) = delete;
HydrostaticEquilibriumContext &operator=(const HydrostaticEquilibriumContext &) = delete;
HydrostaticEquilibriumContext(HydrostaticEquilibriumContext &&) =
delete;
HydrostaticEquilibriumContext(HydrostaticEquilibriumContext &&) = delete;
HydrostaticEquilibriumContext &
operator=(HydrostaticEquilibriumContext &&) = delete;
HydrostaticEquilibriumContext &operator=(HydrostaticEquilibriumContext &&) = delete;
HydrostaticPreparationReport Prepare(
const HydrostaticEquilibriumStateView &state,
@@ -118,15 +107,11 @@ export namespace mean_field::operators::context::hydrostatic {
[[nodiscard]] bool IsPrepared() const noexcept;
[[nodiscard]] bool MatchesDependencies(
const HydrostaticEquilibriumDependencies &dependencies
) const noexcept;
[[nodiscard]] bool MatchesDependencies(const HydrostaticEquilibriumDependencies &dependencies) const noexcept;
[[nodiscard]] const HydrostaticEquilibriumDependencies &
GetDependencies() const;
[[nodiscard]] const HydrostaticEquilibriumDependencies &GetDependencies() const;
[[nodiscard]] const HydrostaticPreparationStatistics &
GetPreparationStatistics() const noexcept;
[[nodiscard]] const HydrostaticPreparationStatistics &GetPreparationStatistics() const noexcept;
[[nodiscard]] const mfem::Vector &GetBaseEnthalpyTrue() const;

View File

@@ -0,0 +1,128 @@
module;
#include <compare>
#include <cstdint>
#include <mfem.hpp>
export module mean_field:operators.context.pressure_force;
export import :fem;
export import :field.mfem;
export import :mapping.domain_mapper;
export namespace mean_field::operators::context::pressure_force {
template <typename Tag> struct DependencyStamp final {
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 final { };
struct EnthalpyDependencyTag final { };
struct DisplacementDependencyTag final { };
using DiscretizationDependency = DependencyStamp<DiscretizationDependencyTag>;
using EnthalpyDependency = DependencyStamp<EnthalpyDependencyTag>;
using DisplacementDependency = DependencyStamp<DisplacementDependencyTag>;
struct PressureForceDependencies final {
DiscretizationDependency discretization;
EnthalpyDependency enthalpy;
DisplacementDependency displacement;
constexpr auto operator<=>(const PressureForceDependencies &) const = default;
};
/*
* Frozen solver-facing state.
*
* Both vectors use their registered FieldDof coordinates.
*
* Under the current registry:
*
* enthalpy -> Stellar -> reduced
* displacement -> All -> identity/full
*/
struct PressureForceStateView final {
const mfem::Vector &enthalpy;
const mfem::Vector &displacement;
};
struct PressureForcePreparationReport final {
bool preparedStaticDependencies{false};
bool preparedGeometryState{false};
bool preparedMaterialState{false};
bool updatedEnthalpy{false};
bool updatedDisplacement{false};
[[nodiscard]] bool DidAnyWork() const noexcept {
return preparedStaticDependencies || preparedGeometryState || preparedMaterialState;
}
};
struct PressureForcePreparationStatistics final {
std::uint64_t staticPreparations{0};
std::uint64_t geometryPreparations{0};
std::uint64_t materialPreparations{0};
constexpr auto operator<=>(const PressureForcePreparationStatistics &) const = default;
};
class PressureForceLinearizationContext final {
public:
PressureForceLinearizationContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const field::FieldDofMap &enthalpyMap,
const field::FieldDofMap &displacementMap
);
PressureForceLinearizationContext(const PressureForceLinearizationContext &) = delete;
PressureForceLinearizationContext &operator=(const PressureForceLinearizationContext &) = delete;
PressureForceLinearizationContext(PressureForceLinearizationContext &&) = delete;
PressureForceLinearizationContext &operator=(PressureForceLinearizationContext &&) = delete;
PressureForcePreparationReport Prepare(
const PressureForceStateView &state,
const PressureForceDependencies &dependencies
);
[[nodiscard]] bool IsPrepared() const noexcept;
[[nodiscard]] bool MatchesDependencies(const PressureForceDependencies &dependencies) const noexcept;
[[nodiscard]] const PressureForceDependencies &GetDependencies() const;
[[nodiscard]] const PressureForcePreparationStatistics &GetPreparationStatistics() const noexcept;
[[nodiscard]] const mfem::Vector &GetBaseEnthalpy() const;
[[nodiscard]] const mfem::Vector &GetDisplacement() const;
private:
void VerifyPrepared() const;
int m_enthalpySize{0};
int m_displacementSize{0};
mfem::Vector m_baseEnthalpy;
mfem::Vector m_displacement;
PressureForcePreparationStatistics m_statistics;
PressureForceDependencies m_dependencies;
bool m_isPrepared{false};
};
} // namespace mean_field::operators::context::pressure_force

View File

@@ -0,0 +1,124 @@
module;
#include <compare>
#include <cstdint>
#include <mfem.hpp>
export module mean_field:operators.context.rotational_displacement_force;
export import :fem;
export import :mapping.domain_mapper;
export namespace mean_field::operators::context::rotational_displacement_force {
template <typename Tag> struct DependencyStamp final {
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 final { };
struct DensityDependencyTag final { };
struct DisplacementDependencyTag final { };
struct RotationDependencyTag final { };
using DiscretizationDependency = DependencyStamp<DiscretizationDependencyTag>;
using DensityDependency = DependencyStamp<DensityDependencyTag>;
using DisplacementDependency = DependencyStamp<DisplacementDependencyTag>;
using RotationDependency = DependencyStamp<RotationDependencyTag>;
struct RotationalDisplacementForceDependencies final {
DiscretizationDependency discretization;
DensityDependency density;
DisplacementDependency displacement;
RotationDependency rotation;
constexpr auto operator<=>(const RotationalDisplacementForceDependencies &) const = default;
};
struct RotationalDisplacementForceStateView final {
const mfem::Vector &density;
const mfem::Vector &displacement;
};
struct RotationalDisplacementForcePreparationReport final {
bool preparedStaticDependencies{false};
bool preparedGeometryState{false};
bool preparedRotationDependencies{false};
bool preparedBaseState{false};
bool updatedDensity{false};
bool updatedDisplacement{false};
[[nodiscard]] bool DidAnyWork() const noexcept {
return preparedStaticDependencies || preparedGeometryState || preparedRotationDependencies ||
preparedBaseState;
}
};
struct RotationalDisplacementForcePreparationStatistics final {
std::uint64_t staticPreparations{0};
std::uint64_t geometryPreparations{0};
std::uint64_t rotationPreparations{0};
std::uint64_t baseStatePreparations{0};
constexpr auto operator<=>(const RotationalDisplacementForcePreparationStatistics &) const = default;
};
class RotationalDisplacementForceLinearizationContext final {
public:
RotationalDisplacementForceLinearizationContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper
);
RotationalDisplacementForceLinearizationContext(const RotationalDisplacementForceLinearizationContext &) =
delete;
RotationalDisplacementForceLinearizationContext &
operator=(const RotationalDisplacementForceLinearizationContext &) = delete;
RotationalDisplacementForceLinearizationContext(RotationalDisplacementForceLinearizationContext &&) = delete;
RotationalDisplacementForceLinearizationContext &
operator=(RotationalDisplacementForceLinearizationContext &&) = delete;
RotationalDisplacementForcePreparationReport Prepare(
const RotationalDisplacementForceStateView &state,
const RotationalDisplacementForceDependencies &dependencies
);
[[nodiscard]] bool IsPrepared() const noexcept;
[[nodiscard]] bool
MatchesDependencies(const RotationalDisplacementForceDependencies &dependencies) const noexcept;
[[nodiscard]] const RotationalDisplacementForceDependencies &GetDependencies() const;
[[nodiscard]] const RotationalDisplacementForcePreparationStatistics &GetPreparationStatistics() const noexcept;
[[nodiscard]] const mfem::Vector &GetBaseDensityTrue() const;
[[nodiscard]] const mfem::Vector &GetDisplacementTrue() const;
private:
void VerifyPrepared() const;
const fem::FEM &m_f;
mfem::Vector m_baseDensityTrue;
mfem::Vector m_displacementTrue;
RotationalDisplacementForceDependencies m_dependencies;
RotationalDisplacementForcePreparationStatistics m_statistics;
bool m_isPrepared{false};
};
} // namespace mean_field::operators::context::rotational_displacement_force

View File

@@ -10,27 +10,20 @@ 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
};
enum class GravityResidualBlock : std::uint8_t { gradient_equation = 0, poisson_equation = 1, count = 2 };
constexpr int
gravity_residual_block_index(const GravityResidualBlock block) noexcept {
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);
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,
context::gravity_field::GravityFieldLinearizationContext &linearization_context,
const mfem::Array<int> &state_true_offsets,
GravityFieldJacobianOperator &jacobian
);
@@ -47,39 +40,32 @@ export namespace mean_field::operators {
Operator &GetGradient(const mfem::Vector &state) const override;
[[nodiscard]] const mfem::Array<int> &
GetStateTrueOffsets() const noexcept;
[[nodiscard]] const mfem::Array<int> &GetStateTrueOffsets() const noexcept;
[[nodiscard]] const mfem::Array<int> &
GetResidualTrueOffsets() const noexcept;
[[nodiscard]] const mfem::Array<int> &GetResidualTrueOffsets() const noexcept;
[[nodiscard]] context::gravity_field::GravityFieldLinearizationContext &
GetLinearizationContext() noexcept;
[[nodiscard]] context::gravity_field::GravityFieldLinearizationContext &GetLinearizationContext() noexcept;
[[nodiscard]] const context::gravity_field::
GravityFieldLinearizationContext &
GetLinearizationContext() const 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,
const context::gravity_field::GravityFieldGeometryContext &geometry_context,
mfem::Vector &action
) const;
void ApplyDensitySource(
const mfem::Vector &density,
const context::gravity_field::GravityFieldGeometryContext
&geometry_context,
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;
context::gravity_field::GravityFieldLinearizationContext &m_linearization_context;
mfem::Array<int> m_state_true_offsets;
mfem::Array<int> m_residual_true_offsets;
GravityFieldJacobianOperator &m_jacobian;
@@ -89,18 +75,14 @@ export namespace mean_field::operators {
public:
ReducedGravityFieldOperator(
GravityFieldOperator &gravity_field_operator,
context::gravity_field::GravityFieldGeometryContext
&gravity_field_geometry_context,
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;
ReducedGravityFieldOperator(const ReducedGravityFieldOperator &) = delete;
ReducedGravityFieldOperator &operator=(const ReducedGravityFieldOperator &) = delete;
ReducedGravityFieldOperator(ReducedGravityFieldOperator &&) = delete;
ReducedGravityFieldOperator &operator=(ReducedGravityFieldOperator &&) = delete;
void SetDisplacement(const mfem::Vector &displacement);
@@ -118,18 +100,13 @@ export namespace mean_field::operators {
[[nodiscard]] GravityFieldOperator &GetGravityFieldOperator() noexcept;
[[nodiscard]] const GravityFieldOperator &
GetGravityFieldOperator() const noexcept;
[[nodiscard]] const GravityFieldOperator &GetGravityFieldOperator() const noexcept;
[[nodiscard]] context::gravity_field::GravityFieldGeometryContext &
GetGeometryContext() noexcept;
[[nodiscard]] context::gravity_field::GravityFieldGeometryContext &GetGeometryContext() noexcept;
[[nodiscard]] const context::gravity_field::
GravityFieldGeometryContext &
GetGeometryContext() const noexcept;
[[nodiscard]] const context::gravity_field::GravityFieldGeometryContext &GetGeometryContext() const noexcept;
[[nodiscard]] const mfem::Array<int> &
GetGravityTrueOffsets() const noexcept;
[[nodiscard]] const mfem::Array<int> &GetGravityTrueOffsets() const noexcept;
private:
void ValidateDisplacement(const mfem::Vector &displacement) const;
@@ -141,7 +118,6 @@ export namespace mean_field::operators {
private:
GravityFieldOperator &m_gravity_field_operator;
mfem::Array<int> m_gravity_true_offsets;
context::gravity_field::GravityFieldGeometryContext
&m_gravity_field_geometry_context;
context::gravity_field::GravityFieldGeometryContext &m_gravity_field_geometry_context;
};
} // namespace mean_field::operators

View File

@@ -12,8 +12,7 @@ export namespace mean_field::operators {
GravityFieldJacobianOperator(
fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper,
const context::gravity_field::GravityFieldLinearizationContext
&linearization_context,
const context::gravity_field::GravityFieldLinearizationContext &linearization_context,
const mfem::Array<int> &state_true_offsets,
const mfem::Array<int> &residual_true_offsets
);
@@ -23,15 +22,13 @@ export namespace mean_field::operators {
mfem::Vector &action
) const override;
[[nodiscard]] const context::gravity_field::
GravityFieldLinearizationContext &
GetLinearizationContext() const noexcept;
[[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;
const context::gravity_field::GravityFieldLinearizationContext &m_linearization_context;
mfem::Array<int> m_state_true_offsets;
mfem::Array<int> m_residual_true_offsets;
};

View File

@@ -4,15 +4,26 @@ module;
export module mean_field:operators.kernels.barotropic_closure;
export import :eos.polytrope;
export import :fem;
export import :mapping.domain_mapper;
export import :physics.barotrope;
export namespace mean_field::operators::kernels {
/*
* Stateless full-MFEM reference kernels for
*
* R_rho = \int_{Omega_star} (rho - rho_EOS(h)) q_rho dV.
*
* These functions intentionally remain expressed in complete MFEM true
* vectors. Solver/prepared-facing support reduction belongs to
* PreparedBarotropicClosureOperator through FieldDofMap. Keeping this
* layer full-space preserves an independent reference implementation for
* R K P tests of the reduced prepared operator.
*/
void apply_barotropic_closure(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope,
const eos::Polytrope &equationOfState,
const mfem::Vector &densityTrue,
const mfem::Vector &enthalpyTrue,
const mfem::Vector &displacementTrue,
@@ -22,7 +33,7 @@ export namespace mean_field::operators::kernels {
void apply_barotropic_closure_density_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope,
const eos::Polytrope &equationOfState,
const mfem::Vector &densityVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &action
@@ -31,7 +42,7 @@ export namespace mean_field::operators::kernels {
void apply_barotropic_closure_enthalpy_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope,
const eos::Polytrope &equationOfState,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &enthalpyVariationTrue,
const mfem::Vector &displacementTrue,
@@ -41,11 +52,11 @@ export namespace mean_field::operators::kernels {
void apply_barotropic_closure_displacement_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope,
const eos::Polytrope &equationOfState,
const mfem::Vector &baseDensityTrue,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &displacementTrue,
const mfem::Vector &displacementVariationTrue,
mfem::Vector &action
);
} // namespace mean_field::operators::kernels
} // namespace mean_field::operators::kernels

View File

@@ -0,0 +1,59 @@
module;
#include <mfem.hpp>
export module mean_field:operators.kernels.gravity_displacement_force;
export import :fem;
export import :mapping.domain_mapper;
export namespace mean_field::operators::kernels {
void apply_gravity_displacement_force_residual(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &densityTrue,
const mfem::Vector &gravityGradientTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &residualTrue
);
void apply_gravity_displacement_force_density_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &densityVariationTrue,
const mfem::Vector &baseGravityGradientTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &actionTrue
);
void apply_gravity_displacement_force_gradient_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &baseDensityTrue,
const mfem::Vector &gravityGradientVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &actionTrue
);
void apply_gravity_displacement_force_displacement_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &baseDensityTrue,
const mfem::Vector &baseGravityGradientTrue,
const mfem::Vector &displacementVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &actionTrue
);
void apply_gravity_displacement_force_complete_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &baseDensityTrue,
const mfem::Vector &densityVariationTrue,
const mfem::Vector &baseGravityGradientTrue,
const mfem::Vector &gravityGradientVariationTrue,
const mfem::Vector &displacementVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &actionTrue
);
} // namespace mean_field::operators::kernels

View File

@@ -6,15 +6,35 @@ export module mean_field:operators.kernels.pressure_force;
export import :fem;
export import :mapping.domain_mapper;
export import :physics.barotrope;
export import :eos.polytrope;
export namespace mean_field::operators::kernels {
void apply_pressure_force_residual(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope,
const eos::Polytrope &barotrope,
const mfem::Vector &enthalpyTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &residualTrue
);
void apply_pressure_force_enthalpy_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const eos::Polytrope &barotrope,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &enthalpyVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &actionTrue
);
void apply_pressure_force_displacement_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const eos::Polytrope &barotrope,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &displacementVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &actionTrue
);
} // namespace mean_field::operators::kernels

View File

@@ -0,0 +1,64 @@
module;
#include <mfem.hpp>
export module mean_field:operators.kernels.rotational_displacement_force;
export import :fem;
export import :mapping.domain_mapper;
export import :physics.rigid_rotation;
export namespace mean_field::operators::kernels {
/*
* Rotational contribution to the displacement row:
*
* R_d^rotation(w)
* = -int_{Omega_star} rho grad(Psi_rotation) . w dV
* = int_{Omega_star}
* rho [Omega x (Omega x (x - x_0))] . w dV.
*
* RigidRotation stores the positive potential
*
* Psi_rotation = 0.5 |Omega x (x - x_0)|^2.
*
* Vacuum elements are excluded exactly.
*/
void apply_rotational_displacement_force_residual(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::RigidRotation &rotation,
const mfem::Vector &densityTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &residualTrue
);
void apply_rotational_displacement_force_density_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::RigidRotation &rotation,
const mfem::Vector &densityVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &actionTrue
);
void apply_rotational_displacement_force_displacement_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::RigidRotation &rotation,
const mfem::Vector &baseDensityTrue,
const mfem::Vector &displacementVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &actionTrue
);
void apply_rotational_displacement_force_complete_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::RigidRotation &rotation,
const mfem::Vector &baseDensityTrue,
const mfem::Vector &densityVariationTrue,
const mfem::Vector &displacementVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &actionTrue
);
} // namespace mean_field::operators::kernels

View File

@@ -6,29 +6,44 @@ module;
export module mean_field:operators.prepared_barotropic_closure;
export import :eos.polytrope;
export import :fem;
export import :field.mfem;
export import :mapping.domain_mapper;
export import :physics.barotrope;
export import :operators.context.barotropic_closure_linearization;
export namespace mean_field::operators {
struct PreparedBarotropicClosureReport final {
context::barotropic::BarotropicClosurePreparationReport contextReport;
bool preparedElementData{false};
[[nodiscard]] bool DidAnyWork() const noexcept {
return contextReport.DidAnyWork() || preparedElementData;
}
};
class PreparedBarotropicClosureOperator final : public mfem::Operator {
public:
PreparedBarotropicClosureOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope
const eos::Polytrope &equationOfState
);
void Prepare(
const mfem::Vector &baseDensityTrue,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &displacementTrue
PreparedBarotropicClosureOperator(const PreparedBarotropicClosureOperator &) = delete;
PreparedBarotropicClosureOperator &operator=(const PreparedBarotropicClosureOperator &) = delete;
PreparedBarotropicClosureOperator(PreparedBarotropicClosureOperator &&) = delete;
PreparedBarotropicClosureOperator &operator=(PreparedBarotropicClosureOperator &&) = delete;
PreparedBarotropicClosureReport Prepare(
const context::barotropic::BarotropicClosureStateView &state,
const context::barotropic::BarotropicClosureDependencies &dependencies
);
void Mult(
const mfem::Vector &densityVariationTrue,
const mfem::Vector &enthalpyVariationTrue,
const mfem::Vector &displacementVariationTrue,
const mfem::Vector &densityVariation,
const mfem::Vector &enthalpyVariation,
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const;
@@ -40,20 +55,33 @@ export namespace mean_field::operators {
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;
[[nodiscard]] int GetDisplacementSize() const noexcept;
[[nodiscard]] const context::barotropic::BarotropicClosureLinearizationContext &GetContext() const noexcept;
[[nodiscard]] const context::barotropic::BarotropicClosurePreparationStatistics &
GetContextPreparationStatistics() const noexcept;
private:
struct ConstructionData;
[[nodiscard]] static ConstructionData MakeConstructionData(const fem::FEM &f);
PreparedBarotropicClosureOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const eos::Polytrope &equationOfState,
ConstructionData constructionData
);
void VerifyPrepared() const;
void Mult(
void ApplyThermodynamicActionFull(
const mfem::Vector &densityVariationTrue,
const mfem::Vector &enthalpyVariationTrue,
mfem::Vector &action
mfem::Vector &actionTrue
) const;
struct ElementPAData {
@@ -73,7 +101,13 @@ export namespace mean_field::operators {
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domainMapper;
const physics::PolytropicBarotrope &m_barotrope;
const eos::Polytrope &m_equationOfState;
field::FieldDofMap m_densityMap;
field::FieldDofMap m_enthalpyMap;
field::FieldDofMap m_displacementMap;
context::barotropic::BarotropicClosureLinearizationContext m_context;
std::vector<ElementPAData> m_elements;
@@ -81,8 +115,12 @@ export namespace mean_field::operators {
mfem::Vector m_baseEnthalpyTrue;
mfem::Vector m_baseDisplacementTrue;
int m_densitySize{0};
int m_enthalpySize{0};
mutable mfem::Vector m_densityVariationTrue;
mutable mfem::Vector m_enthalpyVariationTrue;
mutable mfem::Vector m_displacementVariationTrue;
mutable mfem::Vector m_fullThermodynamicAction;
mutable mfem::Vector m_fullDisplacementAction;
mutable mfem::Vector m_fullResidual;
std::uint64_t m_preparationCount{0};
bool m_isPrepared{false};

View File

@@ -0,0 +1,198 @@
module;
#include <compare>
#include <cstdint>
#include <mfem.hpp>
export module mean_field:operators.prepared_displacement_residual;
export import :fem;
export import :mapping.domain_mapper;
export import :operators.context.gravity_field;
export import :operators.prepared_gravity_displacement_force;
export import :operators.prepared_pressure_force;
export import :operators.prepared_rotational_displacement_force;
export import :eos.polytrope;
export import :physics.rigid_rotation;
export import :utils.blocks;
export namespace mean_field::operators {
struct DisplacementResidualDependencyStamp final {
std::uint64_t identity{0};
std::uint64_t revision{0};
constexpr auto operator<=>(const DisplacementResidualDependencyStamp &) const = default;
};
struct DisplacementResidualDependencies final {
DisplacementResidualDependencyStamp discretization;
DisplacementResidualDependencyStamp density;
DisplacementResidualDependencyStamp displacement;
DisplacementResidualDependencyStamp gravityGradient;
DisplacementResidualDependencyStamp enthalpy;
DisplacementResidualDependencyStamp rotation;
constexpr auto operator<=>(const DisplacementResidualDependencies &) const = default;
};
/*
* Density, displacement, and gravity gradient deliberately do not appear
* here. They are obtained from the shared, already-prepared gravity-field
* linearization context so every mechanical-force contribution consumes
* the same frozen density and geometry as the gravity equations.
*/
struct DisplacementResidualStateView final {
const mfem::Vector &enthalpy;
};
struct PreparedDisplacementResidualReport final {
PreparedPressureForceReport pressure;
PreparedGravityDisplacementForceReport gravity;
PreparedRotationalDisplacementForceReport rotation;
bool assembledResidual{false};
[[nodiscard]] bool DidAnyChildWork() const noexcept {
return pressure.DidAnyWork() || gravity.DidAnyWork() || rotation.DidAnyWork();
}
[[nodiscard]] bool DidAnyWork() const noexcept {
return DidAnyChildWork() || assembledResidual;
}
};
struct PreparedDisplacementResidualActionStatistics final {
std::uint64_t densityApplications{0};
std::uint64_t displacementApplications{0};
std::uint64_t gravityGradientApplications{0};
std::uint64_t enthalpyApplications{0};
std::uint64_t completeApplications{0};
constexpr auto operator<=>(const PreparedDisplacementResidualActionStatistics &) const = default;
};
/*
* Row-level composer for
*
* R_d = R_d^pressure + R_d^gravity + R_d^rotation.
*
* This class owns the three prepared contributors and only orchestrates
* their existing residual and Jacobian APIs. It contains no force kernel
* and no independent copy of the gravity linearization context.
*/
class PreparedDisplacementResidualOperator final {
public:
PreparedDisplacementResidualOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const eos::Polytrope &barotrope,
const context::gravity_field::GravityFieldLinearizationContext &gravityContext
);
PreparedDisplacementResidualOperator(const PreparedDisplacementResidualOperator &) = delete;
PreparedDisplacementResidualOperator &operator=(const PreparedDisplacementResidualOperator &) = delete;
PreparedDisplacementResidualOperator(PreparedDisplacementResidualOperator &&) = delete;
PreparedDisplacementResidualOperator &operator=(PreparedDisplacementResidualOperator &&) = delete;
PreparedDisplacementResidualReport Prepare(
const DisplacementResidualStateView &state,
const DisplacementResidualDependencies &dependencies,
const physics::RigidRotation &rotation
);
void BuildResidual(mfem::Vector &residual) const;
void ApplyDensityJacobianAction(
const mfem::Vector &densityVariation,
mfem::Vector &action
) const;
void ApplyDisplacementJacobianAction(
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const;
void ApplyGravityGradientJacobianAction(
const mfem::Vector &gravityGradientVariation,
mfem::Vector &action
) const;
void ApplyEnthalpyJacobianAction(
const mfem::Vector &enthalpyVariation,
mfem::Vector &action
) const;
void ApplyCompleteJacobianAction(
const mfem::Vector &densityVariation,
const mfem::Vector &displacementVariation,
const mfem::Vector &gravityGradientVariation,
const mfem::Vector &enthalpyVariation,
mfem::Vector &action
) const;
[[nodiscard]] bool IsPrepared() const noexcept;
[[nodiscard]] std::uint64_t GetResidualPreparationCount() const noexcept;
[[nodiscard]] std::uint64_t GetResidualApplicationCount() const noexcept;
[[nodiscard]] const PreparedDisplacementResidualActionStatistics &GetActionStatistics() const noexcept;
[[nodiscard]] const PreparedPressureForceOperator &GetPressureOperator() const noexcept;
[[nodiscard]] const PreparedGravityDisplacementForceOperator &GetGravityOperator() const noexcept;
[[nodiscard]] const PreparedRotationalDisplacementForceOperator &GetRotationalOperator() const noexcept;
[[nodiscard]] const fem::FEM &GetFEM() const noexcept;
[[nodiscard]] const context::gravity_field::GravityFieldLinearizationContext &
GetGravityContext() const noexcept;
private:
void AssembleResidual();
void VerifyPrepared() const;
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domainMapper;
const context::gravity_field::GravityFieldLinearizationContext &m_gravityContext;
PreparedPressureForceOperator m_pressureOperator;
PreparedGravityDisplacementForceOperator m_gravityOperator;
PreparedRotationalDisplacementForceOperator m_rotationalOperator;
DisplacementResidualDependencies m_preparedDependencies;
mfem::Vector m_cachedResidual;
std::uint64_t m_residualPreparationCount{0};
mutable std::uint64_t m_residualApplicationCount{0};
mutable PreparedDisplacementResidualActionStatistics m_actionStatistics;
bool m_isPrepared{false};
};
using DisplacementResidualLayout = utils::blocks::form_layout<utils::blocks::barotropic_equilibrium_form>;
class PreparedDisplacementResidualJacobianOperator final : public mfem::Operator {
public:
PreparedDisplacementResidualJacobianOperator(
const DisplacementResidualLayout &layout,
const PreparedDisplacementResidualOperator &preparedOperator
);
void Mult(
const mfem::Vector &direction,
mfem::Vector &action
) const override;
[[nodiscard]] const DisplacementResidualLayout &GetLayout() const noexcept;
private:
DisplacementResidualLayout m_layout;
const PreparedDisplacementResidualOperator &m_preparedOperator;
};
} // namespace mean_field::operators

View File

@@ -0,0 +1,150 @@
module;
#include <compare>
#include <cstdint>
#include <mfem.hpp>
export module mean_field:operators.prepared_gravity_displacement_force;
export import :fem;
export import :mapping.domain_mapper;
export import :operators.context.gravity_field;
export import :utils.blocks;
export namespace mean_field::operators {
struct PreparedGravityDisplacementForceReport final {
bool preparedResidual{false};
[[nodiscard]] bool DidAnyWork() const noexcept {
return preparedResidual;
}
};
struct PreparedGravityDisplacementForceColumnStatistics final {
std::uint64_t applications{0};
constexpr auto operator<=>(const PreparedGravityDisplacementForceColumnStatistics &) const = default;
};
struct PreparedGravityDisplacementForceCompleteStatistics final {
std::uint64_t applications{0};
constexpr auto operator<=>(const PreparedGravityDisplacementForceCompleteStatistics &) const = default;
};
class PreparedGravityDisplacementForceOperator final {
public:
PreparedGravityDisplacementForceOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const context::gravity_field::GravityFieldLinearizationContext &gravityContext
);
PreparedGravityDisplacementForceOperator(const PreparedGravityDisplacementForceOperator &) = delete;
PreparedGravityDisplacementForceOperator &operator=(const PreparedGravityDisplacementForceOperator &) = delete;
PreparedGravityDisplacementForceOperator(PreparedGravityDisplacementForceOperator &&) = delete;
PreparedGravityDisplacementForceOperator &operator=(PreparedGravityDisplacementForceOperator &&) = delete;
/*
* Freeze the already-prepared shared gravity context. The caller must
* first prepare GravityFieldLinearizationContext for the desired
* state. Repeated calls with unchanged revisions do no work.
*/
PreparedGravityDisplacementForceReport Prepare();
void BuildResidual(mfem::Vector &residual) const;
void ApplyDensityJacobianAction(
const mfem::Vector &densityVariation,
mfem::Vector &action
) const;
void ApplyGravityGradientJacobianAction(
const mfem::Vector &gravityGradientVariation,
mfem::Vector &action
) const;
void ApplyDisplacementJacobianAction(
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const;
void ApplyCompleteJacobianAction(
const mfem::Vector &densityVariation,
const mfem::Vector &displacementVariation,
const mfem::Vector &gravityGradientVariation,
mfem::Vector &action
) const;
[[nodiscard]] bool IsPrepared() const noexcept;
[[nodiscard]] std::uint64_t GetResidualPreparationCount() const noexcept;
[[nodiscard]] std::uint64_t GetResidualApplicationCount() const noexcept;
[[nodiscard]] const PreparedGravityDisplacementForceColumnStatistics &
GetDensityJacobianStatistics() const noexcept;
[[nodiscard]] const PreparedGravityDisplacementForceColumnStatistics &
GetGravityGradientJacobianStatistics() const noexcept;
[[nodiscard]] const PreparedGravityDisplacementForceColumnStatistics &
GetDisplacementJacobianStatistics() const noexcept;
[[nodiscard]] const PreparedGravityDisplacementForceCompleteStatistics &
GetCompleteJacobianStatistics() const noexcept;
[[nodiscard]] const fem::FEM &GetFEM() const noexcept;
[[nodiscard]] const context::gravity_field::GravityFieldLinearizationContext &
GetGravityContext() const noexcept;
private:
void VerifyPrepared() const;
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domainMapper;
const context::gravity_field::GravityFieldLinearizationContext &m_gravityContext;
context::gravity_field::GravityFieldRevisions m_preparedRevisions;
mfem::Vector m_cachedResidual;
std::uint64_t m_residualPreparationCount{0};
mutable std::uint64_t m_residualApplicationCount{0};
mutable PreparedGravityDisplacementForceColumnStatistics m_densityJacobianStatistics;
mutable PreparedGravityDisplacementForceColumnStatistics m_gravityGradientJacobianStatistics;
mutable PreparedGravityDisplacementForceColumnStatistics m_displacementJacobianStatistics;
mutable PreparedGravityDisplacementForceCompleteStatistics m_completeJacobianStatistics;
bool m_isPrepared{false};
};
using GravityDisplacementForceLayout = utils::blocks::form_layout<utils::blocks::barotropic_equilibrium_form>;
class PreparedGravityDisplacementForceJacobianOperator final : public mfem::Operator {
public:
PreparedGravityDisplacementForceJacobianOperator(
const GravityDisplacementForceLayout &layout,
const PreparedGravityDisplacementForceOperator &preparedOperator
);
void Mult(
const mfem::Vector &direction,
mfem::Vector &action
) const override;
[[nodiscard]] const GravityDisplacementForceLayout &GetLayout() const noexcept;
private:
GravityDisplacementForceLayout m_layout;
const PreparedGravityDisplacementForceOperator &m_preparedOperator;
};
} // namespace mean_field::operators

View File

@@ -25,8 +25,7 @@ export namespace mean_field::operators {
bool preparedResidual{false};
[[nodiscard]] bool DidAnyWork() const noexcept {
return contextReport.DidAnyWork() || updatedRotation ||
preparedAlgebraicJacobianBlocks ||
return contextReport.DidAnyWork() || updatedRotation || preparedAlgebraicJacobianBlocks ||
preparedDisplacementJacobianData || preparedResidual;
}
};
@@ -38,26 +37,20 @@ export namespace mean_field::operators {
std::uint64_t bernoulliConstantApplications{0};
std::uint64_t combinedApplications{0};
constexpr auto operator<=>(
const PreparedHydrostaticAlgebraicJacobianStatistics &
) const = default;
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;
constexpr auto operator<=>(const PreparedHydrostaticDisplacementJacobianStatistics &) const = default;
};
struct PreparedHydrostaticCompleteJacobianStatistics {
std::uint64_t applications{0};
constexpr auto operator<=>(
const PreparedHydrostaticCompleteJacobianStatistics &
) const = default;
constexpr auto operator<=>(const PreparedHydrostaticCompleteJacobianStatistics &) const = default;
};
enum class HydrostaticJacobianInputBlock : int {
@@ -94,24 +87,17 @@ export namespace mean_field::operators {
const mapping::DomainMapperStateless &domainMapper
);
PreparedHydrostaticEquilibriumOperator(
const PreparedHydrostaticEquilibriumOperator &
) = delete;
PreparedHydrostaticEquilibriumOperator(const PreparedHydrostaticEquilibriumOperator &) = delete;
PreparedHydrostaticEquilibriumOperator &
operator=(const PreparedHydrostaticEquilibriumOperator &) = delete;
PreparedHydrostaticEquilibriumOperator &operator=(const PreparedHydrostaticEquilibriumOperator &) = delete;
PreparedHydrostaticEquilibriumOperator(
PreparedHydrostaticEquilibriumOperator &&
) = delete;
PreparedHydrostaticEquilibriumOperator(PreparedHydrostaticEquilibriumOperator &&) = delete;
PreparedHydrostaticEquilibriumOperator &
operator=(PreparedHydrostaticEquilibriumOperator &&) = delete;
PreparedHydrostaticEquilibriumOperator &operator=(PreparedHydrostaticEquilibriumOperator &&) = delete;
PreparedHydrostaticEquilibriumReport Prepare(
const context::hydrostatic::HydrostaticEquilibriumStateView &state,
const context::hydrostatic::HydrostaticEquilibriumDependencies
&dependencies,
const context::hydrostatic::HydrostaticEquilibriumDependencies &dependencies,
const physics::RigidRotation &rotation
);
@@ -154,15 +140,12 @@ export namespace mean_field::operators {
[[nodiscard]] bool IsPrepared() const noexcept;
[[nodiscard]] const context::hydrostatic::
HydrostaticPreparationStatistics &
GetContextPreparationStatistics() const noexcept;
[[nodiscard]] const context::hydrostatic::HydrostaticPreparationStatistics &
GetContextPreparationStatistics() const noexcept;
[[nodiscard]] std::uint64_t
GetResidualPreparationCount() const noexcept;
[[nodiscard]] std::uint64_t GetResidualPreparationCount() const noexcept;
[[nodiscard]] std::uint64_t
GetResidualApplicationCount() const noexcept;
[[nodiscard]] std::uint64_t GetResidualApplicationCount() const noexcept;
[[nodiscard]] const PreparedHydrostaticAlgebraicJacobianStatistics &
GetAlgebraicJacobianStatistics() const noexcept;
@@ -203,11 +186,9 @@ export namespace mean_field::operators {
mfem::Vector quadratureWeights;
std::vector<mapping::VolumeMappingContext> baseMappingContexts;
std::optional<mapping::ElementDisplacementData>
baseDisplacementData;
std::optional<mapping::ElementDisplacementData> baseDisplacementData;
std::optional<mapping::ElementCompactificationData>
compactificationData;
std::optional<mapping::ElementCompactificationData> compactificationData;
mfem::Vector rotationPotential;
mfem::DenseMatrix rotationGradient;
@@ -242,20 +223,16 @@ export namespace mean_field::operators {
mutable std::uint64_t m_residualApplicationCount{0};
mutable PreparedHydrostaticAlgebraicJacobianStatistics
m_algebraicJacobianStatistics;
mutable PreparedHydrostaticAlgebraicJacobianStatistics m_algebraicJacobianStatistics;
mutable PreparedHydrostaticDisplacementJacobianStatistics
m_displacementJacobianStatistics;
mutable PreparedHydrostaticDisplacementJacobianStatistics m_displacementJacobianStatistics;
mutable PreparedHydrostaticCompleteJacobianStatistics
m_completeJacobianStatistics;
mutable PreparedHydrostaticCompleteJacobianStatistics m_completeJacobianStatistics;
bool m_isPrepared{false};
};
class PreparedHydrostaticEquilibriumJacobianOperator final
: public mfem::Operator {
class PreparedHydrostaticEquilibriumJacobianOperator final : public mfem::Operator {
public:
PreparedHydrostaticEquilibriumJacobianOperator(
const fem::FEM &f,
@@ -267,8 +244,7 @@ export namespace mean_field::operators {
mfem::Vector &action
) const override;
[[nodiscard]] const HydrostaticJacobianBlockLayout &
GetLayout() const noexcept;
[[nodiscard]] const HydrostaticJacobianBlockLayout &GetLayout() const noexcept;
private:
HydrostaticJacobianBlockLayout m_layout;

View File

@@ -0,0 +1,185 @@
module;
#include <compare>
#include <cstdint>
#include <mfem.hpp>
#include <vector>
export module mean_field:operators.prepared_mass_normalization;
export import :fem;
export import :mapping.domain_mapper;
export import :operators.context.gravity_field;
export import :utils.blocks;
export namespace mean_field::operators {
struct MassNormalizationDependencyStamp final {
std::uint64_t identity{0};
std::uint64_t revision{0};
constexpr auto operator<=>(const MassNormalizationDependencyStamp &) const = default;
};
struct MassNormalizationDependencies final {
MassNormalizationDependencyStamp discretization;
MassNormalizationDependencyStamp density;
MassNormalizationDependencyStamp displacement;
MassNormalizationDependencyStamp targetMass;
constexpr auto operator<=>(const MassNormalizationDependencies &) const = default;
};
struct MassNormalizationStateView final {
double targetMass{0.0};
};
struct PreparedMassNormalizationReport final {
bool rebuiltStaticPlan{false};
bool refreshedGeometry{false};
bool refreshedDensity{false};
bool updatedTargetMass{false};
bool assembledResidual{false};
[[nodiscard]] bool DidAnyWork() const noexcept {
return rebuiltStaticPlan || refreshedGeometry || refreshedDensity || updatedTargetMass || assembledResidual;
}
};
struct PreparedMassNormalizationActionStatistics final {
std::uint64_t densityApplications{0};
std::uint64_t displacementApplications{0};
std::uint64_t completeApplications{0};
constexpr auto operator<=>(const PreparedMassNormalizationActionStatistics &) const = default;
};
/*
* Prepared scalar row
*
* R_M(rho, d) = integral_{Omega_star(d)} rho dV - M_target.
*
* Density and displacement are borrowed from the shared gravity-field
* linearization context. This keeps the mass row on exactly the same
* frozen state and geometry as the gravity and mechanical rows.
*/
class PreparedMassNormalizationOperator final {
public:
PreparedMassNormalizationOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const context::gravity_field::GravityFieldLinearizationContext &gravityContext
);
PreparedMassNormalizationOperator(const PreparedMassNormalizationOperator &) = delete;
PreparedMassNormalizationOperator &operator=(const PreparedMassNormalizationOperator &) = delete;
PreparedMassNormalizationOperator(PreparedMassNormalizationOperator &&) = delete;
PreparedMassNormalizationOperator &operator=(PreparedMassNormalizationOperator &&) = delete;
PreparedMassNormalizationReport Prepare(
const MassNormalizationStateView &state,
const MassNormalizationDependencies &dependencies
);
void BuildResidual(mfem::Vector &residual) const;
void ApplyDensityJacobianAction(
const mfem::Vector &densityVariation,
mfem::Vector &action
) const;
void ApplyDisplacementJacobianAction(
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const;
void ApplyCompleteJacobianAction(
const mfem::Vector &densityVariation,
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const;
[[nodiscard]] bool IsPrepared() const noexcept;
[[nodiscard]] double GetCurrentMass() const;
[[nodiscard]] double GetTargetMass() const;
[[nodiscard]] std::uint64_t GetPreparationCount() const noexcept;
[[nodiscard]] std::uint64_t GetResidualApplicationCount() const noexcept;
[[nodiscard]] const PreparedMassNormalizationActionStatistics &GetActionStatistics() const noexcept;
[[nodiscard]] const fem::FEM &GetFEM() const noexcept;
[[nodiscard]] const context::gravity_field::GravityFieldLinearizationContext &
GetGravityContext() const noexcept;
private:
struct QuadraturePointData final {
mfem::IntegrationPoint integrationPoint;
mfem::Vector densityShape;
mapping::VolumeMappingContext mappingContext;
double density{0.0};
};
struct ElementPAData final {
int elementId{-1};
mfem::Array<int> densityDofs;
mfem::Array<int> displacementDofs;
mfem::Array<int> compactificationDofs;
mfem::DofTransformation *densityDofTransformation{nullptr};
mfem::DofTransformation *displacementDofTransformation{nullptr};
mfem::DofTransformation *compactificationDofTransformation{nullptr};
mfem::Vector baseDisplacement;
mfem::Vector compactification;
std::vector<QuadraturePointData> quadraturePoints;
};
void BuildStaticPlan();
void RefreshGeometry(const mfem::Vector &displacement);
void RefreshDensity(const mfem::Vector &density);
void AssembleResidual();
void VerifyPrepared() const;
[[nodiscard]] double EvaluateDensityActionLocal(const mfem::Vector &densityVariation) const;
[[nodiscard]] double EvaluateDisplacementActionLocal(const mfem::Vector &displacementVariation) const;
[[nodiscard]] double GlobalSum(double localValue) const;
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domainMapper;
const context::gravity_field::GravityFieldLinearizationContext &m_gravityContext;
std::vector<ElementPAData> m_elements;
MassNormalizationDependencies m_preparedDependencies;
mfem::Vector m_cachedResidual;
double m_currentMass{0.0};
double m_targetMass{0.0};
std::uint64_t m_preparationCount{0};
mutable std::uint64_t m_residualApplicationCount{0};
mutable PreparedMassNormalizationActionStatistics m_actionStatistics;
bool m_isPrepared{false};
};
using MassNormalizationLayout = utils::blocks::form_layout<utils::blocks::barotropic_equilibrium_form>;
class PreparedMassNormalizationJacobianOperator final : public mfem::Operator {
public:
PreparedMassNormalizationJacobianOperator(
const MassNormalizationLayout &layout,
const PreparedMassNormalizationOperator &preparedOperator
);
void Mult(
const mfem::Vector &direction,
mfem::Vector &action
) const override;
[[nodiscard]] const MassNormalizationLayout &GetLayout() const noexcept;
private:
MassNormalizationLayout m_layout;
const PreparedMassNormalizationOperator &m_preparedOperator;
};
} // namespace mean_field::operators

View File

@@ -0,0 +1,304 @@
module;
#include <compare>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <vector>
#include <mfem.hpp>
export module mean_field:operators.prepared_pressure_force;
export import :eos.polytrope;
export import :fem;
export import :field.mfem;
export import :mapping.domain_mapper;
export import :operators.context.pressure_force;
export import :utils.blocks;
export namespace mean_field::operators {
struct PreparedPressureForceReport final {
context::pressure_force::PressureForcePreparationReport contextReport;
bool preparedEnthalpyJacobianData{false};
bool preparedDisplacementJacobianData{false};
bool preparedResidual{false};
[[nodiscard]]
bool DidAnyWork() const noexcept {
return contextReport.DidAnyWork() || preparedEnthalpyJacobianData || preparedDisplacementJacobianData ||
preparedResidual;
}
};
struct PreparedPressureForceEnthalpyJacobianStatistics final {
std::uint64_t preparations{0};
std::uint64_t applications{0};
constexpr auto operator<=>(const PreparedPressureForceEnthalpyJacobianStatistics &) const = default;
};
struct PreparedPressureForceDisplacementJacobianStatistics final {
std::uint64_t preparations{0};
std::uint64_t applications{0};
constexpr auto operator<=>(const PreparedPressureForceDisplacementJacobianStatistics &) const = default;
};
struct PreparedPressureForceCompleteJacobianStatistics final {
std::uint64_t applications{0};
constexpr auto operator<=>(const PreparedPressureForceCompleteJacobianStatistics &) const = default;
};
/*
* Prepared pressure contribution
*
* R_d^P(w)
* =
* - integral_{Omega_star(d)}
* P(h) div(w) dV.
*
* Public state and Jacobian directions are expressed in FieldDof
* coordinates.
*
* Current registry:
*
* h -> Stellar -> reduced
* d -> All -> identity/full
* R_d -> All -> identity/full
*
* Full MFEM true/local vectors are private implementation details.
*/
class PreparedPressureForceOperator final {
public:
PreparedPressureForceOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const eos::Polytrope &equationOfState
);
PreparedPressureForceOperator(const PreparedPressureForceOperator &) = delete;
PreparedPressureForceOperator &operator=(const PreparedPressureForceOperator &) = delete;
PreparedPressureForceOperator(PreparedPressureForceOperator &&) = delete;
PreparedPressureForceOperator &operator=(PreparedPressureForceOperator &&) = delete;
PreparedPressureForceReport Prepare(
const context::pressure_force::PressureForceStateView &state,
const context::pressure_force::PressureForceDependencies &dependencies
);
void BuildResidual(mfem::Vector &residual) const;
void ApplyEnthalpyJacobianAction(
const mfem::Vector &enthalpyVariation,
mfem::Vector &action
) const;
void ApplyDisplacementJacobianAction(
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const;
void ApplyCompleteJacobianAction(
const mfem::Vector &enthalpyVariation,
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const;
[[nodiscard]]
bool IsPrepared() const noexcept;
[[nodiscard]]
int GetEnthalpySize() const noexcept;
[[nodiscard]]
int GetDisplacementSize() const noexcept;
[[nodiscard]]
const context::pressure_force::PressureForceLinearizationContext &GetContext() const noexcept;
[[nodiscard]]
const context::pressure_force::PressureForcePreparationStatistics &
GetContextPreparationStatistics() const noexcept;
[[nodiscard]]
std::uint64_t GetResidualPreparationCount() const noexcept;
[[nodiscard]]
std::uint64_t GetResidualApplicationCount() const noexcept;
[[nodiscard]]
const PreparedPressureForceEnthalpyJacobianStatistics &GetEnthalpyJacobianStatistics() const noexcept;
[[nodiscard]]
const PreparedPressureForceDisplacementJacobianStatistics &GetDisplacementJacobianStatistics() const noexcept;
[[nodiscard]]
const PreparedPressureForceCompleteJacobianStatistics &GetCompleteJacobianStatistics() const noexcept;
[[nodiscard]]
std::size_t GetStellarElementCount() const noexcept;
[[nodiscard]]
const fem::FEM &GetFEM() const noexcept;
private:
struct ConstructionData;
[[nodiscard]]
static ConstructionData MakeConstructionData(const fem::FEM &f);
PreparedPressureForceOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const eos::Polytrope &equationOfState,
ConstructionData constructionData
);
struct ElementPAData final {
int elementId{-1};
mfem::Array<int> enthalpyDofs;
mfem::Array<int> displacementDofs;
mfem::Array<int> compactificationDofs;
mfem::DofTransformation *enthalpyDofTransformation{nullptr};
mfem::DofTransformation *displacementDofTransformation{nullptr};
mfem::DofTransformation *compactificationDofTransformation{nullptr};
const mfem::IntegrationRule *integrationRule{nullptr};
/*
* Rows are quadrature points and columns are enthalpy DOFs.
*/
mfem::DenseMatrix enthalpyBasis;
/*
* Each entry is:
*
* scalar displacement DOF
* x
* physical dimension.
*/
std::vector<mfem::DenseMatrix> referenceTestGradients;
std::vector<mfem::DenseMatrix> physicalTestGradients;
std::vector<mapping::VolumeMappingContext> baseMappingContexts;
std::optional<mapping::ElementDisplacementData> baseDisplacementData;
std::optional<mapping::ElementCompactificationData> compactificationData;
mfem::Vector quadratureWeights;
mfem::Vector pressure;
mfem::Vector pressureDerivative;
mfem::Vector elementResidual;
mfem::DenseMatrix enthalpyJacobian;
};
void PrepareStaticPlan();
void PrepareGeometry();
void PrepareMaterialState();
void FinalizeDisplacementJacobianPreparation();
void AssembleCachedResidual();
void VerifyPrepared() const;
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domainMapper;
const eos::Polytrope &m_equationOfState;
field::FieldDofMap m_enthalpyMap;
field::FieldDofMap m_displacementMap;
context::pressure_force::PressureForceLinearizationContext m_context;
std::vector<ElementPAData> m_elements;
/*
* Canonical full-MFEM expansion of the frozen FieldDof state.
*/
mfem::Vector m_baseEnthalpyTrue;
mfem::Vector m_baseDisplacementTrue;
/*
* Reusable Krylov work storage.
*/
mutable mfem::Vector m_enthalpyVariationTrue;
mutable mfem::Vector m_displacementVariationTrue;
mutable mfem::Vector m_fullDisplacementAction;
/*
* Solver-facing cached residual in Displacement FieldDof
* coordinates.
*/
mfem::Vector m_cachedResidual;
std::uint64_t m_residualPreparationCount{0};
mutable std::uint64_t m_residualApplicationCount{0};
mutable PreparedPressureForceEnthalpyJacobianStatistics m_enthalpyJacobianStatistics;
mutable PreparedPressureForceDisplacementJacobianStatistics m_displacementJacobianStatistics;
mutable PreparedPressureForceCompleteJacobianStatistics m_completeJacobianStatistics;
bool m_isPrepared{false};
};
using BarotropicEquilibriumLayout = utils::blocks::form_layout<utils::blocks::barotropic_equilibrium_form>;
/*
* Coupled-layout adapter around the modern prepared pressure-force
* Jacobian.
*
* Reads:
*
* delta d
* delta h
*
* Writes:
*
* R_d
*
* It deliberately imposes no raw-FES-size assumptions on unrelated
* coupled blocks.
*/
class PreparedPressureForceJacobianOperator final : public mfem::Operator {
public:
PreparedPressureForceJacobianOperator(
const BarotropicEquilibriumLayout &layout,
const PreparedPressureForceOperator &preparedOperator
);
void Mult(
const mfem::Vector &direction,
mfem::Vector &action
) const override;
[[nodiscard]]
const BarotropicEquilibriumLayout &GetLayout() const noexcept;
private:
BarotropicEquilibriumLayout m_layout;
const PreparedPressureForceOperator &m_preparedOperator;
};
} // namespace mean_field::operators

View File

@@ -0,0 +1,149 @@
module;
#include <compare>
#include <cstdint>
#include <optional>
#include <mfem.hpp>
export module mean_field:operators.prepared_rotational_displacement_force;
export import :fem;
export import :mapping.domain_mapper;
export import :operators.context.rotational_displacement_force;
export import :physics.rigid_rotation;
export import :utils.blocks;
export namespace mean_field::operators {
struct PreparedRotationalDisplacementForceReport final {
context::rotational_displacement_force::RotationalDisplacementForcePreparationReport contextReport;
bool updatedRotation{false};
bool preparedResidual{false};
[[nodiscard]] bool DidAnyWork() const noexcept {
return contextReport.DidAnyWork() || updatedRotation || preparedResidual;
}
};
struct PreparedRotationalDisplacementForceColumnStatistics final {
std::uint64_t applications{0};
constexpr auto operator<=>(const PreparedRotationalDisplacementForceColumnStatistics &) const = default;
};
struct PreparedRotationalDisplacementForceCompleteStatistics final {
std::uint64_t applications{0};
constexpr auto operator<=>(const PreparedRotationalDisplacementForceCompleteStatistics &) const = default;
};
class PreparedRotationalDisplacementForceOperator final {
public:
PreparedRotationalDisplacementForceOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper
);
PreparedRotationalDisplacementForceOperator(const PreparedRotationalDisplacementForceOperator &) = delete;
PreparedRotationalDisplacementForceOperator &
operator=(const PreparedRotationalDisplacementForceOperator &) = delete;
PreparedRotationalDisplacementForceOperator(PreparedRotationalDisplacementForceOperator &&) = delete;
PreparedRotationalDisplacementForceOperator &operator=(PreparedRotationalDisplacementForceOperator &&) = delete;
PreparedRotationalDisplacementForceReport Prepare(
const context::rotational_displacement_force::RotationalDisplacementForceStateView &state,
const context::rotational_displacement_force::RotationalDisplacementForceDependencies &dependencies,
const physics::RigidRotation &rotation
);
void BuildResidual(mfem::Vector &residual) const;
void ApplyDensityJacobianAction(
const mfem::Vector &densityVariation,
mfem::Vector &action
) const;
void ApplyDisplacementJacobianAction(
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const;
void ApplyCompleteJacobianAction(
const mfem::Vector &densityVariation,
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const;
[[nodiscard]] bool IsPrepared() const noexcept;
[[nodiscard]] const context::rotational_displacement_force::RotationalDisplacementForcePreparationStatistics &
GetContextPreparationStatistics() const noexcept;
[[nodiscard]] std::uint64_t GetResidualPreparationCount() const noexcept;
[[nodiscard]] std::uint64_t GetResidualApplicationCount() const noexcept;
[[nodiscard]] const PreparedRotationalDisplacementForceColumnStatistics &
GetDensityJacobianStatistics() const noexcept;
[[nodiscard]] const PreparedRotationalDisplacementForceColumnStatistics &
GetDisplacementJacobianStatistics() const noexcept;
[[nodiscard]] const PreparedRotationalDisplacementForceCompleteStatistics &
GetCompleteJacobianStatistics() const noexcept;
[[nodiscard]] const fem::FEM &GetFEM() const noexcept;
[[nodiscard]] const context::rotational_displacement_force::RotationalDisplacementForceLinearizationContext &
GetContext() const noexcept;
private:
void VerifyPrepared() const;
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domainMapper;
context::rotational_displacement_force::RotationalDisplacementForceLinearizationContext m_context;
std::optional<physics::RigidRotation> m_rotation;
mfem::Vector m_cachedResidual;
context::rotational_displacement_force::RotationalDisplacementForceDependencies m_preparedDependencies;
std::uint64_t m_residualPreparationCount{0};
mutable std::uint64_t m_residualApplicationCount{0};
mutable PreparedRotationalDisplacementForceColumnStatistics m_densityJacobianStatistics;
mutable PreparedRotationalDisplacementForceColumnStatistics m_displacementJacobianStatistics;
mutable PreparedRotationalDisplacementForceCompleteStatistics m_completeJacobianStatistics;
bool m_isPrepared{false};
};
using RotationalDisplacementForceLayout = utils::blocks::form_layout<utils::blocks::barotropic_equilibrium_form>;
class PreparedRotationalDisplacementForceJacobianOperator final : public mfem::Operator {
public:
PreparedRotationalDisplacementForceJacobianOperator(
const RotationalDisplacementForceLayout &layout,
const PreparedRotationalDisplacementForceOperator &preparedOperator
);
void Mult(
const mfem::Vector &direction,
mfem::Vector &action
) const override;
[[nodiscard]] const RotationalDisplacementForceLayout &GetLayout() const noexcept;
private:
RotationalDisplacementForceLayout m_layout;
const PreparedRotationalDisplacementForceOperator &m_preparedOperator;
};
} // namespace mean_field::operators

View File

@@ -0,0 +1,177 @@
module;
#include <compare>
#include <cstdint>
#include <mfem.hpp>
export module mean_field:operators.prepared_stellar_equilibrium;
export import :eos.polytrope;
export import :fem;
export import :field.mfem;
export import :mapping.domain_mapper;
export import :model.stellar;
export import :operators.context.gravity_field;
export import :operators.gravity_field;
export import :operators.gravity_field_jacobian;
export import :operators.prepared_barotropic_closure;
export import :operators.prepared_displacement_residual;
export import :operators.prepared_hydrostatic_equilibrium;
export import :operators.prepared_mass_normalization;
export import :physics.rigid_rotation;
export import :utils.blocks;
export namespace mean_field::operators {
struct StellarEquilibriumDependencyStamp final {
std::uint64_t identity{0};
std::uint64_t revision{0};
constexpr auto operator<=>(const StellarEquilibriumDependencyStamp &) const = default;
};
struct StellarEquilibriumDependencies final {
StellarEquilibriumDependencyStamp discretization;
StellarEquilibriumDependencyStamp density;
StellarEquilibriumDependencyStamp displacement;
StellarEquilibriumDependencyStamp gravityGradient;
StellarEquilibriumDependencyStamp gravityPotential;
StellarEquilibriumDependencyStamp enthalpy;
StellarEquilibriumDependencyStamp bernoulliConstant;
StellarEquilibriumDependencyStamp rotation;
StellarEquilibriumDependencyStamp targetMass;
constexpr auto operator<=>(const StellarEquilibriumDependencies &) const = default;
};
struct PreparedStellarEquilibriumReport final {
context::gravity_field::GravityFieldPreparationReport gravity;
PreparedBarotropicClosureReport barotropicClosure;
PreparedHydrostaticEquilibriumReport hydrostatic;
PreparedDisplacementResidualReport displacement;
PreparedMassNormalizationReport massNormalization;
bool assembledResidual{false};
[[nodiscard]] bool DidAnyChildWork() const noexcept {
return gravity.DidAnyWork() || barotropicClosure.DidAnyWork() || hydrostatic.DidAnyWork() ||
displacement.DidAnyWork() || massNormalization.DidAnyWork();
}
[[nodiscard]] bool DidAnyWork() const noexcept {
return DidAnyChildWork() || assembledResidual;
}
};
struct PreparedStellarEquilibriumStatistics final {
std::uint64_t residualAssemblies{0};
std::uint64_t residualApplications{0};
std::uint64_t jacobianApplications{0};
constexpr auto operator<=>(const PreparedStellarEquilibriumStatistics &) const = default;
};
using StellarEquilibriumLayout = utils::blocks::form_layout<utils::blocks::barotropic_equilibrium_form>;
class PreparedStellarEquilibriumOperator final : public mfem::Operator {
public:
PreparedStellarEquilibriumOperator(
fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const eos::Polytrope &equationOfState,
double targetMass
);
PreparedStellarEquilibriumOperator(
fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const eos::Polytrope &equationOfState,
const models::StellarModel &stellarModel
);
PreparedStellarEquilibriumOperator(const PreparedStellarEquilibriumOperator &) = delete;
PreparedStellarEquilibriumOperator &operator=(const PreparedStellarEquilibriumOperator &) = delete;
PreparedStellarEquilibriumOperator(PreparedStellarEquilibriumOperator &&) = delete;
PreparedStellarEquilibriumOperator &operator=(PreparedStellarEquilibriumOperator &&) = delete;
PreparedStellarEquilibriumReport Prepare(
const mfem::Vector &state,
const StellarEquilibriumDependencies &dependencies,
const physics::RigidRotation &rotation
);
void BuildResidual(mfem::Vector &residual) const;
void Mult(
const mfem::Vector &direction,
mfem::Vector &action
) const override;
[[nodiscard]] bool IsPrepared() const noexcept;
[[nodiscard]] double GetTargetMass() const noexcept;
[[nodiscard]] const StellarEquilibriumLayout &GetLayout() const noexcept;
[[nodiscard]] const StellarEquilibriumDependencies &GetDependencies() const;
[[nodiscard]] const PreparedStellarEquilibriumStatistics &GetStatistics() const noexcept;
[[nodiscard]] const context::gravity_field::GravityFieldLinearizationContext &
GetGravityContext() const noexcept;
[[nodiscard]] const GravityFieldOperator &GetGravityOperator() const noexcept;
[[nodiscard]] const GravityFieldJacobianOperator &GetGravityJacobianOperator() const noexcept;
[[nodiscard]] const PreparedBarotropicClosureOperator &GetBarotropicClosureOperator() const noexcept;
[[nodiscard]] const context::barotropic::BarotropicClosureLinearizationContext &
GetBarotropicClosureContext() const noexcept;
[[nodiscard]] const PreparedHydrostaticEquilibriumOperator &GetHydrostaticOperator() const noexcept;
[[nodiscard]] const PreparedDisplacementResidualOperator &GetDisplacementOperator() const noexcept;
[[nodiscard]] const PreparedMassNormalizationOperator &GetMassNormalizationOperator() const noexcept;
private:
struct ConstructionData;
static ConstructionData MakeConstructionData(fem::FEM &f);
PreparedStellarEquilibriumOperator(
fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const eos::Polytrope &equationOfState,
double targetMass,
ConstructionData constructionData
);
void AssembleResidual();
void VerifyPrepared() const;
StellarEquilibriumLayout m_layout;
mfem::Array<int> m_gravityStateOffsets;
context::gravity_field::GravityFieldLinearizationContext m_gravityContext;
GravityFieldJacobianOperator m_gravityJacobianOperator;
GravityFieldOperator m_gravityOperator;
PreparedBarotropicClosureOperator m_barotropicClosureOperator;
PreparedHydrostaticEquilibriumOperator m_hydrostaticOperator;
PreparedDisplacementResidualOperator m_displacementOperator;
PreparedMassNormalizationOperator m_massNormalizationOperator;
StellarEquilibriumDependencies m_preparedDependencies;
mfem::Vector m_cachedResidual;
double m_targetMass{0.0};
mutable PreparedStellarEquilibriumStatistics m_statistics;
bool m_isPrepared{false};
field::FieldDofMap m_densityMap;
field::FieldDofMap m_displacementMap;
field::FieldDofMap m_gravityFluxMap;
field::FieldDofMap m_gravityPotentialMap;
field::FieldDofMap m_enthalpyMap;
mfem::Vector m_fullDensity;
mfem::Vector m_fullEnthalpy;
mfem::Vector m_fullGravityState;
mutable mfem::Vector m_fullDensityVariation;
mutable mfem::Vector m_fullEnthalpyVariation;
mutable mfem::Vector m_fullGravityDirection;
mutable mfem::Vector m_fullEnthalpyAction;
};
} // namespace mean_field::operators

View File

@@ -27,8 +27,7 @@ export namespace mean_field::physics {
);
}
if (!std::isfinite(polytropic_constant) ||
polytropic_constant <= 0.0) {
if (!std::isfinite(polytropic_constant) || polytropic_constant <= 0.0) {
throw std::invalid_argument(
std::format(
"The polytropic constant must be finite and positive. "
@@ -57,8 +56,7 @@ export namespace mean_field::physics {
return 0.0;
}
return m_polytropic_constant *
std::pow(density, 1.0 + 1.0 / m_polytropic_index);
return m_polytropic_constant * std::pow(density, 1.0 + 1.0 / m_polytropic_index);
}
[[nodiscard]] double enthalpy_from_density(const double density) const {
@@ -67,12 +65,10 @@ export namespace mean_field::physics {
return 0.0;
}
return m_enthalpy_scale *
std::pow(density, 1.0 / m_polytropic_index);
return m_enthalpy_scale * std::pow(density, 1.0 / m_polytropic_index);
}
[[nodiscard]] double
density_from_enthalpy(const double enthalpy) const {
[[nodiscard]] double density_from_enthalpy(const double enthalpy) const {
validate_finite(enthalpy, "enthalpy");
if (enthalpy <= 0.0) {
@@ -82,20 +78,17 @@ export namespace mean_field::physics {
return std::pow(enthalpy / m_enthalpy_scale, m_polytropic_index);
}
[[nodiscard]] double
pressure_from_enthalpy(const double enthalpy) const {
[[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);
return density_from_enthalpy(enthalpy) * enthalpy / (m_polytropic_index + 1.0);
}
[[nodiscard]] double
density_derivative_from_enthalpy(const double enthalpy) const {
[[nodiscard]] double density_derivative_from_enthalpy(const double enthalpy) const {
validate_finite(enthalpy, "enthalpy");
if (enthalpy < 0.0) {
return 0.0;
@@ -106,13 +99,10 @@ export namespace mean_field::physics {
}
return m_polytropic_index / m_enthalpy_scale *
std::pow(
enthalpy / m_enthalpy_scale, m_polytropic_index - 1.0
);
std::pow(enthalpy / m_enthalpy_scale, m_polytropic_index - 1.0);
}
[[nodiscard]] double
pressure_derivative_from_enthalpy(const double enthalpy) const {
[[nodiscard]] double pressure_derivative_from_enthalpy(const double enthalpy) const {
validate_finite(enthalpy, "enthalpy");
if (enthalpy <= 0.0) {
@@ -122,8 +112,7 @@ export namespace mean_field::physics {
return density_from_enthalpy(enthalpy);
}
[[nodiscard]] double
pressure_derivative_from_density(const double density) const {
[[nodiscard]] double pressure_derivative_from_density(const double density) const {
validate_nonnegativity(density, "density");
if (density == 0.0) {
return 0.0;

View File

@@ -15,36 +15,28 @@ export namespace mean_field::physics {
: m_angularVelocity(angularVelocity),
m_center(center) {
MFEM_VERIFY(
m_angularVelocity.Size() == 3,
"RigidRotation requires a three-dimensional "
"angular-velocity vector."
m_angularVelocity.Size() == 3, "RigidRotation requires a three-dimensional "
"angular-velocity vector."
);
MFEM_VERIFY(
m_center.Size() == 3,
"RigidRotation requires a three-dimensional center."
);
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."
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."
std::isfinite(m_center(component)), "RigidRotation received a non-finite center component."
);
}
}
[[nodiscard]] double
potential(const mfem::Vector &physicalPosition) const {
[[nodiscard]] double potential(const mfem::Vector &physicalPosition) const {
MFEM_VERIFY(
physicalPosition.Size() == 3,
"RigidRotation::potential requires a "
"three-dimensional position."
physicalPosition.Size() == 3, "RigidRotation::potential requires a "
"three-dimensional position."
);
const double relativeX = physicalPosition(0) - m_center(0);
@@ -53,14 +45,11 @@ export namespace mean_field::physics {
const double relativeZ = physicalPosition(2) - m_center(2);
const double crossX = m_angularVelocity(1) * relativeZ -
m_angularVelocity(2) * relativeY;
const double crossX = m_angularVelocity(1) * relativeZ - m_angularVelocity(2) * relativeY;
const double crossY = m_angularVelocity(2) * relativeX -
m_angularVelocity(0) * relativeZ;
const double crossY = m_angularVelocity(2) * relativeX - m_angularVelocity(0) * relativeZ;
const double crossZ = m_angularVelocity(0) * relativeY -
m_angularVelocity(1) * relativeX;
const double crossZ = m_angularVelocity(0) * relativeY - m_angularVelocity(1) * relativeX;
return 0.5 * (crossX * crossX + crossY * crossY + crossZ * crossZ);
}
@@ -70,48 +59,101 @@ export namespace mean_field::physics {
const mfem::Vector &physicalPositionVariation
) const {
MFEM_VERIFY(
physicalPosition.Size() == 3,
"RigidRotation derivative requires a "
"three-dimensional position."
physicalPosition.Size() == 3, "RigidRotation derivative requires a "
"three-dimensional position."
);
MFEM_VERIFY(
physicalPositionVariation.Size() == 3,
"RigidRotation derivative requires a "
"three-dimensional direction."
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);
const double relativePosition = physicalPosition(component) - m_center(component);
angularVelocitySquared +=
m_angularVelocity(component) * m_angularVelocity(component);
angularVelocitySquared += m_angularVelocity(component) * m_angularVelocity(component);
angularVelocityDotPosition +=
m_angularVelocity(component) * relativePosition;
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 relativePosition = physicalPosition(component) - m_center(component);
const double gradientComponent =
angularVelocitySquared * relativePosition -
angularVelocityDotPosition * m_angularVelocity(component);
const double gradientComponent = angularVelocitySquared * relativePosition -
angularVelocityDotPosition * m_angularVelocity(component);
derivative +=
gradientComponent * physicalPositionVariation(component);
derivative += gradientComponent * physicalPositionVariation(component);
}
return derivative;
}
/*
* Gradient of the positive rigid-rotation potential
*
* Psi = 0.5 |Omega x (x - x_0)|^2.
*
* This points away from the rotation axis. The rotational
* displacement residual uses its negative.
*/
void potential_gradient(
const mfem::Vector &physicalPosition,
mfem::Vector &gradient
) const {
MFEM_VERIFY(
physicalPosition.Size() == 3, "RigidRotation::potential_gradient requires a "
"three-dimensional position."
);
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;
}
gradient.SetSize(3);
for (int component = 0; component < 3; ++component) {
const double relativePosition = physicalPosition(component) - m_center(component);
gradient(component) = angularVelocitySquared * relativePosition -
angularVelocityDotPosition * m_angularVelocity(component);
}
}
/*
* Hessian action of Psi. The Hessian is constant for rigid
* rotation, so only the physical-position direction is required.
*/
void potential_gradient_directional_derivative(
const mfem::Vector &physicalPositionVariation,
mfem::Vector &gradientVariation
) const {
MFEM_VERIFY(
physicalPositionVariation.Size() == 3, "RigidRotation gradient derivative requires a "
"three-dimensional direction."
);
const double angularVelocitySquared = m_angularVelocity * m_angularVelocity;
const double angularVelocityDotVariation = m_angularVelocity * physicalPositionVariation;
gradientVariation.SetSize(3);
gradientVariation = physicalPositionVariation;
gradientVariation *= angularVelocitySquared;
gradientVariation.Add(-angularVelocityDotVariation, m_angularVelocity);
}
[[nodiscard]] const mfem::Vector &angular_velocity() const noexcept {
return m_angularVelocity;
}

View File

@@ -108,12 +108,9 @@ export namespace mean_field::quadrature {
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 Resolution resolution = policy.resolve(query);
const mfem::IntegrationRule &integration_rule = mfem::IntRules.Get(geometry, resolution.order);
return {.resolution = resolution, .integration_rule = &integration_rule};
}
MfemRule RuleFactory::get(
@@ -146,12 +143,10 @@ export namespace mean_field::quadrature {
"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());
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;
}
@@ -176,13 +171,11 @@ export namespace mean_field::quadrature {
"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 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;
}
@@ -200,12 +193,8 @@ export namespace mean_field::quadrature {
"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());
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;
}
@@ -230,13 +219,11 @@ export namespace mean_field::quadrature {
"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 Query query = GravityField::make_query<field::Gravity::Form::SourceLinear>(
role, transformation.OrderW(), {}, domain, mapping
);
const auto [resolution, integration_rule] =
get(query, test_element.GetGeomType());
const auto [resolution, integration_rule] = get(query, test_element.GetGeomType());
integrator.SetIntegrationRule(*integration_rule);
return resolution;
}
@@ -271,17 +258,14 @@ export namespace mean_field::quadrature {
"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."
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 Query query =
GravityField::make_query<field::Gravity::Form::SourceProjection>(
role, transformation.OrderW(), {}, domain, mapping
);
const auto [resolution, integration_rule] =
get(query, transformation.GetGeometryType());
const auto [resolution, integration_rule] = get(query, transformation.GetGeometryType());
integrator.SetIntRule(integration_rule);
return resolution;
}
@@ -307,8 +291,7 @@ export namespace mean_field::quadrature {
.geometry_weight_order = transformation.OrderW()
};
const auto [resolution, integration_rule] =
get(query, velocity_element.GetGeomType());
const auto [resolution, integration_rule] = get(query, velocity_element.GetGeomType());
integrator.SetIntegrationRule(*integration_rule);
return resolution;
}
@@ -323,8 +306,7 @@ 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;
}

View File

@@ -14,6 +14,7 @@ export namespace mean_field::quadrature {
gravity_hdiv_mass,
gravity_divergence,
gravity_source,
gravity_force,
gravity_boundary,
centrifugal,
density_projection,
@@ -32,12 +33,7 @@ export namespace mean_field::quadrature {
error_norm
};
enum class QuadratureRole {
discretization,
preconditioner,
diagnostic,
projection
};
enum class QuadratureRole { discretization, preconditioner, diagnostic, projection };
enum class MappingKind { none, affine, general, kelvin };
@@ -59,6 +55,7 @@ export namespace mean_field::quadrature {
RuleControl gravity_hdiv_mass;
RuleControl gravity_divergence;
RuleControl gravity_source;
RuleControl gravity_force;
RuleControl gravity_boundary;
RuleControl centrifugal;
RuleControl density_projection;
@@ -130,6 +127,7 @@ export namespace mean_field::quadrature {
QuadratureTermOptions gravity_hdiv_mass;
QuadratureTermOptions gravity_divergence;
QuadratureTermOptions gravity_source;
QuadratureTermOptions gravity_force;
QuadratureTermOptions gravity_boundary;
QuadratureTermOptions centrifugal;
QuadratureTermOptions density_projection;
@@ -211,35 +209,20 @@ 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 {
switch (term) {
@@ -249,6 +232,8 @@ export namespace mean_field::quadrature {
return rule_set.gravity_divergence;
case Term::gravity_source:
return rule_set.gravity_source;
case Term::gravity_force:
return rule_set.gravity_force;
case Term::gravity_boundary:
return rule_set.gravity_boundary;
case Term::centrifugal:
@@ -289,18 +274,14 @@ export namespace mean_field::quadrature {
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;
@@ -308,12 +289,10 @@ 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;

View File

@@ -0,0 +1,64 @@
module;
#include <cmath>
#include <format>
#include <stdexcept>
export module mean_field:surface.isobaric;
export import :surface.base;
export namespace mean_field::surface {
class Isobaric final : public SurfaceBase {
public:
explicit Isobaric(const double targetPressure = 0.0) : m_targetPressure(targetPressure) {
validateTargetPressure();
}
[[nodiscard]] double targetPressure() const noexcept {
return m_targetPressure;
}
[[nodiscard]] ResolvedSurfaceCondition
resolve(const mean_field::eos::EquationOfState &equationOfState) const override {
return ResolvedSurfaceCondition{resolveTargetEnthalpy(equationOfState)};
}
void validate(const mean_field::eos::EquationOfState &equationOfState) const override {
static_cast<void>(resolveTargetEnthalpy(equationOfState));
}
private:
[[nodiscard]] double resolveTargetEnthalpy(const mean_field::eos::EquationOfState &equationOfState) const {
validateTargetPressure();
const double targetEnthalpy = equationOfState.enthalpy_from_pressure(m_targetPressure);
if (!std::isfinite(targetEnthalpy) || targetEnthalpy < 0.0) {
throw std::domain_error(
std::format(
"The equation of state resolved the isobaric "
"target P = {} to the invalid enthalpy h = {}.",
m_targetPressure, targetEnthalpy
)
);
}
return targetEnthalpy;
}
void validateTargetPressure() const {
if (!std::isfinite(m_targetPressure) || m_targetPressure < 0.0) {
throw std::invalid_argument(
std::format(
"The target surface pressure must be finite and "
"non-negative. Instead P = {} was provided.",
m_targetPressure
)
);
}
}
double m_targetPressure;
};
} // namespace mean_field::surface

View File

@@ -0,0 +1,53 @@
module;
#include <cmath>
#include <stdexcept>
export module mean_field:surface.base;
export import :eos.base;
export namespace mean_field::surface {
struct ResolvedSurfaceCondition final {
double targetEnthalpy{0.0};
explicit ResolvedSurfaceCondition(const double requestedTargetEnthalpy)
: targetEnthalpy(requestedTargetEnthalpy) {
if (!std::isfinite(targetEnthalpy) || targetEnthalpy < 0.0) {
throw std::invalid_argument(
"A resolved surface enthalpy must be finite and "
"non-negative."
);
}
}
[[nodiscard]] double residual(const double enthalpy) const {
if (!std::isfinite(enthalpy)) {
throw std::invalid_argument("A surface enthalpy value must be finite.");
}
return enthalpy - targetEnthalpy;
}
[[nodiscard]] static double jacobianAction(const double enthalpyVariation) {
if (!std::isfinite(enthalpyVariation)) {
throw std::invalid_argument("A surface enthalpy variation must be finite.");
}
return enthalpyVariation;
}
};
class SurfaceBase {
public:
virtual ~SurfaceBase() = default;
[[nodiscard]] virtual ResolvedSurfaceCondition
resolve(const mean_field::eos::EquationOfState &equationOfState) const = 0;
virtual void validate(const mean_field::eos::EquationOfState &equationOfState) const = 0;
protected:
SurfaceBase() = default;
};
} // namespace mean_field::surface

View File

@@ -23,8 +23,7 @@ export namespace mean_field::utils::blocks {
struct field { };
template <typename Residual, typename... Values> struct block_row { };
template <int index_value>
struct residual_block final : residual_block_base {
template <int index_value> struct residual_block final : residual_block_base {
static constexpr int index = index_value;
// ReSharper disable once CppNonExplicitConversionOperator
@@ -110,43 +109,33 @@ export namespace mean_field::utils::blocks {
template <typename Query, typename List> struct contains_type;
template <typename Query>
struct contains_type<Query, type_list<>> : std::false_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...>>> { };
: 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> 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> 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> { };
(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 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) && ...)> { };
: 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 List> inline constexpr bool types_are_unique_v = types_are_unique<List>::value;
template <typename Row> struct block_row_traits {
using residual = void;
@@ -156,8 +145,7 @@ export namespace mean_field::utils::blocks {
static constexpr bool is_block_row = false;
};
template <typename Residual, typename... Values>
struct block_row_traits<block_row<Residual, Values...>> {
template <typename Residual, typename... Values> struct block_row_traits<block_row<Residual, Values...>> {
using residual = Residual;
using values = type_list<Values...>;
@@ -167,19 +155,15 @@ export namespace mean_field::utils::blocks {
template <typename Query, typename List> struct type_index;
template <typename Query, typename... Tail>
struct type_index<Query, type_list<Query, Tail...>> {
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 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 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;
@@ -192,36 +176,22 @@ export namespace mean_field::utils::blocks {
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...>>>
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...>> &&
(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 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>
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>) &&
...) &&
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;
@@ -230,73 +200,50 @@ export namespace mean_field::utils::blocks {
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 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 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...>>;
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...>;
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>;
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;
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, typename JacobianForm> struct has_jacobian_coupling;
template <typename Residual, typename Value>
struct has_jacobian_coupling<Residual, Value, type_list<>>
: std::false_type { };
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...>>
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...>>> { };
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;
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>;
using value_type = typename Term::value;
constexpr int index = type_index_v<value_type, typename Form::value_blocks>;
return value_block<index>{};
}
@@ -305,8 +252,7 @@ export namespace mean_field::utils::blocks {
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>;
constexpr int index = type_index_v<residual_type, typename Form::residual_blocks>;
return residual_block<index>{};
}
@@ -320,32 +266,24 @@ export namespace mean_field::utils::blocks {
int,
Form::residual_block_count> &residual_sizes
) {
build_offsets(
m_value_offsets, value_sizes, typename Form::value_blocks{}
);
build_offsets(m_value_offsets, value_sizes, typename Form::value_blocks{});
build_offsets(
m_residual_offsets, residual_sizes,
typename Form::residual_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 {
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 {
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 {
template <int index> [[nodiscard]] int offset(residual_block<index>) const {
return m_residual_offsets[index];
}
@@ -391,8 +329,7 @@ export namespace mean_field::utils::blocks {
int block_index = 0;
((offsets[block_index + 1] =
offsets[block_index] +
resolve_block_size<BlockTypes>(requested_sizes[block_index]),
offsets[block_index] + resolve_block_size<BlockTypes>(requested_sizes[block_index]),
++block_index),
...);
}
@@ -463,10 +400,12 @@ export namespace mean_field::utils::blocks {
enthalpy::specific::value,
displacement::geometry::value>,
// R_d(d, h)
// R_d(rho, d, g, h)
block_row<
displacement::geometry::residual,
density::mass::value,
displacement::geometry::value,
gravity::gradient::value,
enthalpy::specific::value>,
// R_h(h, Phi, d, C)
@@ -483,6 +422,15 @@ export namespace mean_field::utils::blocks {
density::mass::value,
displacement::geometry::value>>;
// Columns: [d, h]
// Rows: [R_d]
using pressure_force_form = block_form<
type_list<displacement::geometry::value, enthalpy::specific::value>,
type_list<displacement::geometry::residual>>;
using pressure_force_jacobian_form = type_list<
block_row<displacement::geometry::residual, displacement::geometry::value, enthalpy::specific::value>>;
static_assert(valid_jacobian_form<
gravity_field_form,
gravity_jacobian_form>);

File diff suppressed because it is too large Load Diff

View File

@@ -66,15 +66,13 @@ export namespace mean_field::utils {
[[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>> ||
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_real = std::is_floating_point_v<T> || is_xad<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,

View File

@@ -32,8 +32,7 @@ export namespace mean_field::utils {
double mass{};
double c{};
DomainMapperStatelessOptions domain_mapper_options{};
mapping::compactification::options::KelvinCompactificationOptions
kelvin_options{};
mapping::compactification::options::KelvinCompactificationOptions kelvin_options{};
int max_iters{};
double tol{};