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:
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
16
libmeanfield/interface/eos/eos_base.cppm
Normal file
16
libmeanfield/interface/eos/eos_base.cppm
Normal 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
|
||||
170
libmeanfield/interface/eos/polytropic.cppm
Normal file
170
libmeanfield/interface/eos/polytropic.cppm
Normal 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
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
114
libmeanfield/interface/models/stellar_model.cppm
Normal file
114
libmeanfield/interface/models/stellar_model.cppm
Normal 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
|
||||
68
libmeanfield/interface/models/structure/polytropic.cppm
Normal file
68
libmeanfield/interface/models/structure/polytropic.cppm
Normal 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
|
||||
37
libmeanfield/interface/models/structure/structure_base.cppm
Normal file
37
libmeanfield/interface/models/structure/structure_base.cppm
Normal 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
|
||||
146
libmeanfield/interface/models/structure_profile.cppm
Normal file
146
libmeanfield/interface/models/structure_profile.cppm
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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};
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
304
libmeanfield/interface/operators/prepared_pressure_force.cppm
Normal file
304
libmeanfield/interface/operators/prepared_pressure_force.cppm
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
64
libmeanfield/interface/surface/isobaric.cppm
Normal file
64
libmeanfield/interface/surface/isobaric.cppm
Normal 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
|
||||
53
libmeanfield/interface/surface/surface_base.cppm
Normal file
53
libmeanfield/interface/surface/surface_base.cppm
Normal 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
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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{};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user