659 lines
32 KiB
C++
659 lines
32 KiB
C++
module;
|
|
|
|
#include <cmath>
|
|
#include <concepts>
|
|
#include <stdexcept>
|
|
#include <type_traits>
|
|
|
|
export module mean_field:normalization.physical_riesz;
|
|
|
|
export import :dimensions.quantities;
|
|
export import :field.mfem;
|
|
export import :model.specifications;
|
|
export import :normalization.plan;
|
|
|
|
export namespace mean_field::normalization {
|
|
struct Unnormalized final : NormalizationPrescriptionTag { };
|
|
|
|
struct ReferenceGeometry final { };
|
|
|
|
struct FixedMassBranchReference final { };
|
|
|
|
template <typename Candidate>
|
|
concept RieszGeometryPolicy = std::same_as<std::remove_cvref_t<Candidate>, ReferenceGeometry>;
|
|
|
|
template <typename Candidate>
|
|
concept ReferenceScalePolicy = std::same_as<std::remove_cvref_t<Candidate>, FixedMassBranchReference>;
|
|
|
|
template <
|
|
RieszGeometryPolicy GeometryPolicy = ReferenceGeometry,
|
|
ReferenceScalePolicy ScalePolicy = FixedMassBranchReference>
|
|
class PhysicalRieszDiagonal final : public NormalizationPrescriptionTag {
|
|
public:
|
|
using Geometry = GeometryPolicy;
|
|
using ScaleSource = ScalePolicy;
|
|
|
|
explicit PhysicalRieszDiagonal(
|
|
const dimensions::LengthValue referenceRadius,
|
|
const double gravitationalConstant = 1.0
|
|
)
|
|
: m_referenceRadius(referenceRadius),
|
|
m_gravitationalConstant(gravitationalConstant) {
|
|
if (!std::isfinite(referenceRadius.value()) || referenceRadius.value() <= 0.0) {
|
|
throw std::invalid_argument("Physical Riesz normalization requires a finite, positive branch radius.");
|
|
}
|
|
if (!std::isfinite(gravitationalConstant) || gravitationalConstant <= 0.0) {
|
|
throw std::invalid_argument(
|
|
"Physical Riesz normalization requires a finite, positive gravitational constant."
|
|
);
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] dimensions::LengthValue referenceRadius() const noexcept {
|
|
return m_referenceRadius;
|
|
}
|
|
|
|
[[nodiscard]] double gravitationalConstant() const noexcept {
|
|
return m_gravitationalConstant;
|
|
}
|
|
|
|
private:
|
|
dimensions::LengthValue m_referenceRadius;
|
|
double m_gravitationalConstant;
|
|
};
|
|
|
|
PhysicalRieszDiagonal(
|
|
dimensions::LengthValue,
|
|
double = 1.0
|
|
)
|
|
-> PhysicalRieszDiagonal<
|
|
ReferenceGeometry,
|
|
FixedMassBranchReference>;
|
|
|
|
template <typename Candidate> struct IsPhysicalRieszDiagonal : std::false_type { };
|
|
|
|
template <RieszGeometryPolicy Geometry, ReferenceScalePolicy ScaleSource>
|
|
struct IsPhysicalRieszDiagonal<PhysicalRieszDiagonal<Geometry, ScaleSource>> : std::true_type { };
|
|
|
|
template <typename Candidate>
|
|
concept PhysicalRieszDiagonalPrescription = IsPhysicalRieszDiagonal<std::remove_cvref_t<Candidate>>::value;
|
|
|
|
struct StellarCharacteristicScales final {
|
|
dimensions::MassValue mass;
|
|
dimensions::LengthValue radius;
|
|
double gravitationalConstant;
|
|
double density;
|
|
double acceleration;
|
|
double inverseTimeSquared;
|
|
double specificEnergy;
|
|
double pressure;
|
|
double angularVelocity;
|
|
double angularMomentum;
|
|
double force;
|
|
};
|
|
|
|
[[nodiscard]] inline StellarCharacteristicScales deriveStellarCharacteristicScales(
|
|
const dimensions::MassValue mass,
|
|
const dimensions::LengthValue radius,
|
|
const double gravitationalConstant = 1.0
|
|
) {
|
|
const double massValue = mass.value();
|
|
const double radiusValue = radius.value();
|
|
if (!std::isfinite(massValue) || massValue <= 0.0) {
|
|
throw std::invalid_argument("Characteristic stellar scales require a finite, positive mass.");
|
|
}
|
|
if (!std::isfinite(radiusValue) || radiusValue <= 0.0) {
|
|
throw std::invalid_argument("Characteristic stellar scales require a finite, positive radius.");
|
|
}
|
|
if (!std::isfinite(gravitationalConstant) || gravitationalConstant <= 0.0) {
|
|
throw std::invalid_argument(
|
|
"Characteristic stellar scales require a finite, positive gravitational constant."
|
|
);
|
|
}
|
|
|
|
const double radiusSquared = radiusValue * radiusValue;
|
|
const double radiusCubed = radiusSquared * radiusValue;
|
|
const double density = massValue / radiusCubed;
|
|
const double acceleration = gravitationalConstant * massValue / radiusSquared;
|
|
const double inverseTimeSquared = gravitationalConstant * massValue / radiusCubed;
|
|
const double specificEnergy = gravitationalConstant * massValue / radiusValue;
|
|
const double pressure = gravitationalConstant * massValue * massValue / (radiusSquared * radiusSquared);
|
|
const double angularVelocity = std::sqrt(inverseTimeSquared);
|
|
const double angularMomentum = massValue * std::sqrt(gravitationalConstant * massValue * radiusValue);
|
|
const double force = gravitationalConstant * massValue * massValue / radiusSquared;
|
|
|
|
const double derived[] = {density, acceleration, inverseTimeSquared, specificEnergy,
|
|
pressure, angularVelocity, angularMomentum, force};
|
|
for (const double value : derived) {
|
|
if (!std::isfinite(value) || value <= 0.0) {
|
|
throw std::overflow_error("A derived characteristic stellar scale is not finite and positive.");
|
|
}
|
|
}
|
|
|
|
return {
|
|
.mass = mass,
|
|
.radius = radius,
|
|
.gravitationalConstant = gravitationalConstant,
|
|
.density = density,
|
|
.acceleration = acceleration,
|
|
.inverseTimeSquared = inverseTimeSquared,
|
|
.specificEnergy = specificEnergy,
|
|
.pressure = pressure,
|
|
.angularVelocity = angularVelocity,
|
|
.angularMomentum = angularMomentum,
|
|
.force = force
|
|
};
|
|
}
|
|
|
|
template <
|
|
RieszGeometryPolicy Geometry,
|
|
ReferenceScalePolicy ScaleSource,
|
|
typename Model>
|
|
requires requires(const Model &model) {
|
|
{ model.template specification<models::FixedTotalMass>() } -> std::same_as<const models::FixedTotalMass &>;
|
|
{
|
|
model.template specification<models::FixedTotalMass>().targetMass()
|
|
} -> std::same_as<dimensions::MassValue>;
|
|
}
|
|
[[nodiscard]] StellarCharacteristicScales deriveStellarCharacteristicScales(
|
|
const PhysicalRieszDiagonal<
|
|
Geometry,
|
|
ScaleSource> &prescription,
|
|
const Model &model
|
|
) {
|
|
return deriveStellarCharacteristicScales(
|
|
model.template specification<models::FixedTotalMass>().targetMass(), prescription.referenceRadius(),
|
|
prescription.gravitationalConstant()
|
|
);
|
|
}
|
|
|
|
namespace detail {
|
|
/*
|
|
* Model definitions live below the numerical normalization layer so
|
|
* that a physics component can describe its generated coordinates
|
|
* without importing solver machinery. These two translations are the
|
|
* deliberately small boundary between that neutral declaration and the
|
|
* normalization plan used by the discretization.
|
|
*/
|
|
template <models::RieszTopology Topology> struct DeclaredRieszTopology {
|
|
static constexpr bool available = false;
|
|
static constexpr RieszTopology value = RieszTopology::identity;
|
|
};
|
|
|
|
#define MEAN_FIELD_DECLARED_RIESZ_TOPOLOGY(Name) \
|
|
template <> struct DeclaredRieszTopology<models::RieszTopology::Name> { \
|
|
static constexpr bool available = true; \
|
|
static constexpr RieszTopology value = RieszTopology::Name; \
|
|
}
|
|
|
|
MEAN_FIELD_DECLARED_RIESZ_TOPOLOGY(identity);
|
|
MEAN_FIELD_DECLARED_RIESZ_TOPOLOGY(scalar_volume_l2);
|
|
MEAN_FIELD_DECLARED_RIESZ_TOPOLOGY(vector_volume_l2);
|
|
MEAN_FIELD_DECLARED_RIESZ_TOPOLOGY(scalar_boundary_l2);
|
|
MEAN_FIELD_DECLARED_RIESZ_TOPOLOGY(hybrid_scalar_volume_point_rows);
|
|
MEAN_FIELD_DECLARED_RIESZ_TOPOLOGY(global_scalar);
|
|
|
|
#undef MEAN_FIELD_DECLARED_RIESZ_TOPOLOGY
|
|
|
|
template <models::PhysicalScaleLaw Scale> struct DeclaredPhysicalScale {
|
|
static constexpr bool available = false;
|
|
static constexpr PhysicalScaleKind value = PhysicalScaleKind::dimensionless;
|
|
};
|
|
|
|
#define MEAN_FIELD_DECLARED_PHYSICAL_SCALE(Name) \
|
|
template <> struct DeclaredPhysicalScale<models::PhysicalScaleLaw::Name> { \
|
|
static constexpr bool available = true; \
|
|
static constexpr PhysicalScaleKind value = PhysicalScaleKind::Name; \
|
|
}
|
|
|
|
MEAN_FIELD_DECLARED_PHYSICAL_SCALE(dimensionless);
|
|
MEAN_FIELD_DECLARED_PHYSICAL_SCALE(density);
|
|
MEAN_FIELD_DECLARED_PHYSICAL_SCALE(length);
|
|
MEAN_FIELD_DECLARED_PHYSICAL_SCALE(acceleration);
|
|
MEAN_FIELD_DECLARED_PHYSICAL_SCALE(inverse_time_squared);
|
|
MEAN_FIELD_DECLARED_PHYSICAL_SCALE(specific_energy);
|
|
MEAN_FIELD_DECLARED_PHYSICAL_SCALE(pressure);
|
|
MEAN_FIELD_DECLARED_PHYSICAL_SCALE(mass);
|
|
MEAN_FIELD_DECLARED_PHYSICAL_SCALE(force);
|
|
MEAN_FIELD_DECLARED_PHYSICAL_SCALE(angular_velocity);
|
|
MEAN_FIELD_DECLARED_PHYSICAL_SCALE(angular_momentum);
|
|
|
|
#undef MEAN_FIELD_DECLARED_PHYSICAL_SCALE
|
|
|
|
template <typename Declaration, typename = void> struct CompileDeclaredPhysicalRieszCoordinate {
|
|
using Method = UnsupportedPhysicalRieszCoordinate;
|
|
static constexpr bool registered = false;
|
|
};
|
|
|
|
template <typename Declaration>
|
|
struct CompileDeclaredPhysicalRieszCoordinate<
|
|
Declaration,
|
|
std::void_t<
|
|
decltype(std::integral_constant<
|
|
models::RieszTopology,
|
|
static_cast<models::RieszTopology>(Declaration::topology)>{}),
|
|
decltype(std::integral_constant<
|
|
models::PhysicalScaleLaw,
|
|
static_cast<models::PhysicalScaleLaw>(Declaration::scale)>{}),
|
|
decltype(std::bool_constant<static_cast<bool>(Declaration::available)>{})>> {
|
|
private:
|
|
static constexpr models::RieszTopology declaredTopology =
|
|
static_cast<models::RieszTopology>(Declaration::topology);
|
|
static constexpr models::PhysicalScaleLaw declaredScale =
|
|
static_cast<models::PhysicalScaleLaw>(Declaration::scale);
|
|
using Topology = DeclaredRieszTopology<declaredTopology>;
|
|
using Scale = DeclaredPhysicalScale<declaredScale>;
|
|
|
|
public:
|
|
static constexpr bool registered =
|
|
static_cast<bool>(Declaration::available) && Topology::available && Scale::available;
|
|
using Method = std::conditional_t<
|
|
registered,
|
|
PhysicalRieszCoordinate<Topology::value, Scale::value>,
|
|
UnsupportedPhysicalRieszCoordinate>;
|
|
};
|
|
|
|
template <typename Generated, CoordinateKind Kind, typename = void>
|
|
struct DeclaredGeneratedPhysicalRieszCoordinate {
|
|
using Method = UnsupportedPhysicalRieszCoordinate;
|
|
static constexpr bool registered = false;
|
|
};
|
|
|
|
template <typename Generated>
|
|
struct DeclaredGeneratedPhysicalRieszCoordinate<
|
|
Generated,
|
|
CoordinateKind::value,
|
|
std::void_t<
|
|
typename Generated::SpecificationType,
|
|
typename models::SpecificationContribution<
|
|
typename Generated::SpecificationType>::Normalization::Value>>
|
|
: CompileDeclaredPhysicalRieszCoordinate<typename models::SpecificationContribution<
|
|
typename Generated::SpecificationType>::Normalization::Value> { };
|
|
|
|
template <typename Generated>
|
|
struct DeclaredGeneratedPhysicalRieszCoordinate<
|
|
Generated,
|
|
CoordinateKind::residual,
|
|
std::void_t<
|
|
typename Generated::SpecificationType,
|
|
typename models::SpecificationContribution<
|
|
typename Generated::SpecificationType>::Normalization::Residual>>
|
|
: CompileDeclaredPhysicalRieszCoordinate<typename models::SpecificationContribution<
|
|
typename Generated::SpecificationType>::Normalization::Residual> { };
|
|
|
|
template <typename GeneratedValues, typename GeneratedResiduals> struct GeneratedPhysicalRieszCoverage {
|
|
static constexpr bool complete = false;
|
|
};
|
|
|
|
template <typename... GeneratedValues, typename... GeneratedResiduals>
|
|
struct GeneratedPhysicalRieszCoverage<
|
|
models::ModelTypeList<GeneratedValues...>,
|
|
models::ModelTypeList<GeneratedResiduals...>> {
|
|
static constexpr bool complete =
|
|
(DeclaredGeneratedPhysicalRieszCoordinate<GeneratedValues, CoordinateKind::value>::registered && ...) &&
|
|
(DeclaredGeneratedPhysicalRieszCoordinate<GeneratedResiduals, CoordinateKind::residual>::registered &&
|
|
...);
|
|
};
|
|
|
|
template <typename Specification, typename = void> struct SpecificationPhysicalRieszCoverage {
|
|
static constexpr bool complete = false;
|
|
};
|
|
|
|
template <models::ModelSpecification Specification>
|
|
struct SpecificationPhysicalRieszCoverage<
|
|
Specification,
|
|
std::void_t<
|
|
typename models::SpecificationContribution<Specification>::GeneratedValues,
|
|
typename models::SpecificationContribution<Specification>::GeneratedResiduals>>
|
|
: GeneratedPhysicalRieszCoverage<
|
|
typename models::SpecificationContribution<Specification>::GeneratedValues,
|
|
typename models::SpecificationContribution<Specification>::GeneratedResiduals> { };
|
|
} // namespace detail
|
|
|
|
/*
|
|
* All generated blocks are normalized from their generating physics
|
|
* specification. Adding another constraint therefore does not add a
|
|
* normalization specialization: its public ModelDefinition is the single
|
|
* source of both the value and residual Riesz laws.
|
|
*/
|
|
template <typename Generated>
|
|
struct PhysicalRieszBlockTraits<utils::blocks::generated_value_block<Generated>>
|
|
: detail::DeclaredGeneratedPhysicalRieszCoordinate<Generated, CoordinateKind::value> { };
|
|
|
|
template <typename Generated>
|
|
struct PhysicalRieszBlockTraits<utils::blocks::generated_residual_block<Generated>>
|
|
: detail::DeclaredGeneratedPhysicalRieszCoordinate<Generated, CoordinateKind::residual> { };
|
|
|
|
template <typename Generated>
|
|
concept GeneratedValuePhysicalRieszNormalizable =
|
|
detail::DeclaredGeneratedPhysicalRieszCoordinate<Generated, CoordinateKind::value>::registered;
|
|
|
|
template <typename Generated>
|
|
concept GeneratedResidualPhysicalRieszNormalizable =
|
|
detail::DeclaredGeneratedPhysicalRieszCoordinate<Generated, CoordinateKind::residual>::registered;
|
|
|
|
template <typename Specification>
|
|
concept CompleteGeneratedPhysicalRieszNormalizationFor =
|
|
detail::SpecificationPhysicalRieszCoverage<std::remove_cvref_t<Specification>>::complete;
|
|
|
|
/*
|
|
* Runtime Physical Riesz assembly needs more than a symbolically complete
|
|
* plan: it must be able to recover the finite-element maps owned by the
|
|
* selected physical core. Keep that structural capability in this low
|
|
* normalization module so both problem formation and the solver-facing
|
|
* adapter can consult the same authority without importing one another.
|
|
*/
|
|
template <typename Candidate>
|
|
concept PhysicalRieszCoreRuntime = requires(const std::remove_cvref_t<Candidate> &core) {
|
|
{ core.GetGravityContext().GetDensityMap() } -> std::same_as<const field::FieldDofMap &>;
|
|
{ core.GetGravityContext().GetGravityGradientMap() } -> std::same_as<const field::FieldDofMap &>;
|
|
{ core.GetGravityContext().GetGravityPotentialMap() } -> std::same_as<const field::FieldDofMap &>;
|
|
{ core.GetHydrostaticOperator().GetEnthalpyMap() } -> std::same_as<const field::FieldDofMap &>;
|
|
{ core.GetDomainDeformation().parameterCount() } -> std::same_as<int>;
|
|
};
|
|
|
|
namespace detail {
|
|
template <typename Generated, CoordinateKind Kind>
|
|
using GeneratedPhysicalRieszMethod = typename DeclaredGeneratedPhysicalRieszCoordinate<Generated, Kind>::Method;
|
|
|
|
template <typename Generated, CoordinateKind Kind, typename = void>
|
|
struct GeneratedPhysicalRieszRuntimeCoordinate : std::false_type { };
|
|
|
|
template <typename Generated, CoordinateKind Kind>
|
|
struct GeneratedPhysicalRieszRuntimeCoordinate<
|
|
Generated,
|
|
Kind,
|
|
std::void_t<decltype(GeneratedPhysicalRieszMethod<Generated, Kind>::topology)>>
|
|
: std::bool_constant<
|
|
DeclaredGeneratedPhysicalRieszCoordinate<Generated, Kind>::registered &&
|
|
GeneratedPhysicalRieszMethod<Generated, Kind>::topology == RieszTopology::global_scalar> { };
|
|
|
|
template <typename Specification, typename = void>
|
|
struct SpecificationPhysicalRieszRuntimeCoverage : std::false_type { };
|
|
|
|
template <typename Values, typename Residuals>
|
|
struct GeneratedPhysicalRieszRuntimeCoverage : std::false_type { };
|
|
|
|
template <typename... Values, typename... Residuals>
|
|
struct GeneratedPhysicalRieszRuntimeCoverage<
|
|
models::ModelTypeList<Values...>,
|
|
models::ModelTypeList<Residuals...>>
|
|
: std::bool_constant<
|
|
(GeneratedPhysicalRieszRuntimeCoordinate<Values, CoordinateKind::value>::value && ...) &&
|
|
(GeneratedPhysicalRieszRuntimeCoordinate<Residuals, CoordinateKind::residual>::value && ...)> { };
|
|
|
|
template <models::ModelSpecification Specification>
|
|
struct SpecificationPhysicalRieszRuntimeCoverage<
|
|
Specification,
|
|
std::void_t<
|
|
typename models::SpecificationContribution<Specification>::GeneratedValues,
|
|
typename models::SpecificationContribution<Specification>::GeneratedResiduals>>
|
|
: GeneratedPhysicalRieszRuntimeCoverage<
|
|
typename models::SpecificationContribution<Specification>::GeneratedValues,
|
|
typename models::SpecificationContribution<Specification>::GeneratedResiduals> { };
|
|
|
|
template <typename SpecificationTypes>
|
|
struct SpecificationSetPhysicalRieszRuntimeCoverage : std::false_type { };
|
|
|
|
template <models::ModelSpecification... Specifications>
|
|
struct SpecificationSetPhysicalRieszRuntimeCoverage<models::detail::SpecificationSetStorage<Specifications...>>
|
|
: std::bool_constant<(SpecificationPhysicalRieszRuntimeCoverage<Specifications>::value && ...)> { };
|
|
} // namespace detail
|
|
|
|
template <typename Specification>
|
|
concept CompleteGeneratedPhysicalRieszRuntimeNormalizationFor =
|
|
detail::SpecificationPhysicalRieszRuntimeCoverage<std::remove_cvref_t<Specification>>::value;
|
|
|
|
#define MEAN_FIELD_PHYSICAL_RIESZ_TRAIT(BlockType, TopologyValue, ScaleValue) \
|
|
template <> struct PhysicalRieszBlockTraits<BlockType> { \
|
|
using Method = PhysicalRieszCoordinate<RieszTopology::TopologyValue, PhysicalScaleKind::ScaleValue>; \
|
|
static constexpr bool registered = true; \
|
|
}
|
|
|
|
MEAN_FIELD_PHYSICAL_RIESZ_TRAIT(
|
|
utils::blocks::density::mass::value,
|
|
scalar_volume_l2,
|
|
density
|
|
);
|
|
MEAN_FIELD_PHYSICAL_RIESZ_TRAIT(
|
|
utils::blocks::surface_deformation::parameters::value,
|
|
scalar_boundary_l2,
|
|
length
|
|
);
|
|
MEAN_FIELD_PHYSICAL_RIESZ_TRAIT(
|
|
utils::blocks::gravity::gradient::value,
|
|
vector_volume_l2,
|
|
acceleration
|
|
);
|
|
MEAN_FIELD_PHYSICAL_RIESZ_TRAIT(
|
|
utils::blocks::gravity::poisson::value,
|
|
scalar_volume_l2,
|
|
specific_energy
|
|
);
|
|
MEAN_FIELD_PHYSICAL_RIESZ_TRAIT(
|
|
utils::blocks::enthalpy::specific::value,
|
|
scalar_volume_l2,
|
|
specific_energy
|
|
);
|
|
|
|
MEAN_FIELD_PHYSICAL_RIESZ_TRAIT(
|
|
utils::blocks::gravity::gradient::residual,
|
|
vector_volume_l2,
|
|
acceleration
|
|
);
|
|
MEAN_FIELD_PHYSICAL_RIESZ_TRAIT(
|
|
utils::blocks::gravity::poisson::residual,
|
|
scalar_volume_l2,
|
|
inverse_time_squared
|
|
);
|
|
MEAN_FIELD_PHYSICAL_RIESZ_TRAIT(
|
|
utils::blocks::density::mass::residual,
|
|
scalar_volume_l2,
|
|
density
|
|
);
|
|
MEAN_FIELD_PHYSICAL_RIESZ_TRAIT(
|
|
utils::blocks::surface_deformation::shape_equilibrium::residual,
|
|
scalar_boundary_l2,
|
|
force
|
|
);
|
|
MEAN_FIELD_PHYSICAL_RIESZ_TRAIT(
|
|
utils::blocks::enthalpy::specific::residual,
|
|
hybrid_scalar_volume_point_rows,
|
|
specific_energy
|
|
);
|
|
#undef MEAN_FIELD_PHYSICAL_RIESZ_TRAIT
|
|
|
|
template <typename Block> [[nodiscard]] double physicalScale(const StellarCharacteristicScales &scales) {
|
|
static_assert(PhysicalRieszBlockTraits<Block>::registered, "The block has no Physical Riesz normalization.");
|
|
using Method = typename PhysicalRieszBlockTraits<Block>::Method;
|
|
constexpr PhysicalScaleKind scale = Method::scale;
|
|
if constexpr (scale == PhysicalScaleKind::dimensionless) {
|
|
return 1.0;
|
|
} else if constexpr (scale == PhysicalScaleKind::density) {
|
|
return scales.density;
|
|
} else if constexpr (scale == PhysicalScaleKind::length) {
|
|
return scales.radius.value();
|
|
} else if constexpr (scale == PhysicalScaleKind::acceleration) {
|
|
return scales.acceleration;
|
|
} else if constexpr (scale == PhysicalScaleKind::inverse_time_squared) {
|
|
return scales.inverseTimeSquared;
|
|
} else if constexpr (scale == PhysicalScaleKind::specific_energy) {
|
|
return scales.specificEnergy;
|
|
} else if constexpr (scale == PhysicalScaleKind::pressure) {
|
|
return scales.pressure;
|
|
} else if constexpr (scale == PhysicalScaleKind::mass) {
|
|
return scales.mass.value();
|
|
} else if constexpr (scale == PhysicalScaleKind::force) {
|
|
return scales.force;
|
|
} else if constexpr (scale == PhysicalScaleKind::angular_velocity) {
|
|
return scales.angularVelocity;
|
|
} else {
|
|
static_assert(scale == PhysicalScaleKind::angular_momentum);
|
|
return scales.angularMomentum;
|
|
}
|
|
}
|
|
|
|
namespace detail {
|
|
template <typename Values, typename Residuals> struct MakePhysicalRieszPlan;
|
|
|
|
template <typename... Values, typename... Residuals>
|
|
struct MakePhysicalRieszPlan<utils::blocks::type_list<Values...>, utils::blocks::type_list<Residuals...>> {
|
|
using Type = NormalizationPlan<
|
|
CoordinateComponent<
|
|
CoordinateKind::value,
|
|
utils::blocks::type_list<Values>,
|
|
typename PhysicalRieszBlockTraits<Values>::Method>...,
|
|
CoordinateComponent<
|
|
CoordinateKind::residual,
|
|
utils::blocks::type_list<Residuals>,
|
|
typename PhysicalRieszBlockTraits<Residuals>::Method>...>;
|
|
};
|
|
} // namespace detail
|
|
|
|
template <typename Form>
|
|
requires utils::blocks::block_form_is_valid_v<Form>
|
|
using PhysicalRieszNormalizationPlanFor =
|
|
typename detail::MakePhysicalRieszPlan<typename Form::value_blocks, typename Form::residual_blocks>::Type;
|
|
|
|
/*
|
|
* Public compile-time extension point for a normalization prescription.
|
|
* A specialization owns both the complete coordinate plan and the
|
|
* low-level runtime compatibility predicate used before a discretized
|
|
* problem type is formed. Keeping those declarations together prevents a
|
|
* policy from compiling a plan which the selected stellar core cannot
|
|
* actually prepare.
|
|
*/
|
|
template <typename Prescription, typename Form> struct NormalizationCompilation {
|
|
using Plan = NormalizationPlan<>;
|
|
static constexpr bool registered = false;
|
|
|
|
template <typename PhysicalCore, typename SpecificationTypes> static constexpr bool runtimeAvailableFor = false;
|
|
};
|
|
|
|
/* Astronomy/numerics-facing package for a policy which prepares one
|
|
* runtime diagonal over the complete inferred form and needs no private
|
|
* facility of a particular stellar core. The generated plan truthfully
|
|
* labels every coordinate as runtime-prepared by this exact policy. */
|
|
template <NormalizationPrescription Prescription, typename Form>
|
|
requires utils::blocks::block_form_is_valid_v<Form>
|
|
struct RuntimePreparedNormalizationCompilation {
|
|
using Plan = RuntimePreparedNormalizationPlanFor<Prescription, Form>;
|
|
static constexpr bool registered = CompleteNormalizationFor<Plan, Form>;
|
|
|
|
template <typename PhysicalCore, typename SpecificationTypes>
|
|
static constexpr bool runtimeAvailableFor = registered;
|
|
};
|
|
|
|
template <typename Form>
|
|
requires utils::blocks::block_form_is_valid_v<Form>
|
|
struct NormalizationCompilation<Unnormalized, Form> {
|
|
using Plan = IdentityNormalizationPlanFor<Form>;
|
|
static constexpr bool registered = CompleteNormalizationFor<Plan, Form>;
|
|
|
|
template <typename PhysicalCore, typename SpecificationTypes>
|
|
static constexpr bool runtimeAvailableFor = registered;
|
|
};
|
|
|
|
template <RieszGeometryPolicy Geometry, ReferenceScalePolicy ScaleSource, typename Form>
|
|
requires utils::blocks::block_form_is_valid_v<Form>
|
|
struct NormalizationCompilation<PhysicalRieszDiagonal<Geometry, ScaleSource>, Form> {
|
|
using Plan = PhysicalRieszNormalizationPlanFor<Form>;
|
|
static constexpr bool registered = CompleteNormalizationFor<Plan, Form>;
|
|
|
|
template <typename PhysicalCore, typename SpecificationTypes>
|
|
static constexpr bool runtimeAvailableFor =
|
|
registered && PhysicalRieszCoreRuntime<std::remove_cvref_t<PhysicalCore>> &&
|
|
detail::SpecificationSetPhysicalRieszRuntimeCoverage<std::remove_cvref_t<SpecificationTypes>>::value;
|
|
};
|
|
|
|
namespace detail {
|
|
template <typename Prescription, typename Form, typename = void> struct NormalizationCompilationAudit {
|
|
using Plan = NormalizationPlan<>;
|
|
static constexpr bool registered = false;
|
|
};
|
|
|
|
template <typename Prescription, typename Form>
|
|
requires NormalizationPrescription<std::remove_cvref_t<Prescription>> &&
|
|
utils::blocks::block_form_is_valid_v<std::remove_cvref_t<Form>>
|
|
struct NormalizationCompilationAudit<
|
|
Prescription,
|
|
Form,
|
|
std::void_t<
|
|
typename NormalizationCompilation<std::remove_cvref_t<Prescription>, std::remove_cvref_t<Form>>::Plan,
|
|
decltype(std::bool_constant<static_cast<bool>(NormalizationCompilation<
|
|
std::remove_cvref_t<Prescription>,
|
|
std::remove_cvref_t<Form>>::registered)>{})>> {
|
|
using Compilation = NormalizationCompilation<std::remove_cvref_t<Prescription>, std::remove_cvref_t<Form>>;
|
|
using Plan = typename Compilation::Plan;
|
|
|
|
static constexpr bool registered =
|
|
static_cast<bool>(Compilation::registered) && CompleteNormalizationFor<Plan, std::remove_cvref_t<Form>>;
|
|
};
|
|
} // namespace detail
|
|
|
|
template <NormalizationPrescription Prescription, typename Form>
|
|
using NormalizationPlanFor =
|
|
typename detail::NormalizationCompilationAudit<std::remove_cvref_t<Prescription>, Form>::Plan;
|
|
|
|
template <typename Prescription, typename Form>
|
|
concept CompilableNormalizationFor =
|
|
detail::NormalizationCompilationAudit<std::remove_cvref_t<Prescription>, std::remove_cvref_t<Form>>::registered;
|
|
|
|
/* The public runtime-preparation adapter is intentionally narrower than
|
|
* an arbitrary complete plan: every coordinate must name the exact policy
|
|
* which supplies its runtime factor. This prevents a custom policy from
|
|
* advertising IdentityCoordinate (or another policy's method) while
|
|
* silently installing a different diagonal at runtime. */
|
|
template <typename Prescription, typename Form>
|
|
concept RuntimePreparedNormalizationFor =
|
|
NormalizationPrescription<std::remove_cvref_t<Prescription>> &&
|
|
utils::blocks::block_form_is_valid_v<std::remove_cvref_t<Form>> &&
|
|
CompilableNormalizationFor<std::remove_cvref_t<Prescription>, std::remove_cvref_t<Form>> &&
|
|
std::same_as<
|
|
NormalizationPlanFor<std::remove_cvref_t<Prescription>, std::remove_cvref_t<Form>>,
|
|
RuntimePreparedNormalizationPlanFor<std::remove_cvref_t<Prescription>, std::remove_cvref_t<Form>>>;
|
|
|
|
namespace detail {
|
|
template <
|
|
typename Prescription,
|
|
typename Form,
|
|
typename PhysicalCore,
|
|
typename SpecificationTypes,
|
|
typename = void>
|
|
struct StellarNormalizationRuntimeAudit : std::false_type { };
|
|
|
|
template <typename Prescription, typename Form, typename PhysicalCore, typename SpecificationTypes>
|
|
struct StellarNormalizationRuntimeAudit<
|
|
Prescription,
|
|
Form,
|
|
PhysicalCore,
|
|
SpecificationTypes,
|
|
std::void_t<
|
|
std::enable_if_t<NormalizationCompilationAudit<Prescription, Form>::registered>,
|
|
decltype(std::bool_constant<
|
|
static_cast<bool>(NormalizationCompilation<Prescription, Form>::
|
|
template runtimeAvailableFor<PhysicalCore, SpecificationTypes>)>{})>>
|
|
: std::bool_constant<
|
|
(std::same_as<Prescription, Unnormalized> || PhysicalRieszDiagonalPrescription<Prescription> ||
|
|
RuntimePreparedNormalizationFor<Prescription, Form>) &&
|
|
static_cast<bool>(NormalizationCompilation<Prescription, Form>::
|
|
template runtimeAvailableFor<PhysicalCore, SpecificationTypes>)> { };
|
|
} // namespace detail
|
|
|
|
/*
|
|
* Single detection-safe authority for pairing a compiled stellar form,
|
|
* its selected physical core, and a runtime normalization prescription.
|
|
* Each public NormalizationCompilation specialization declares this
|
|
* compatibility alongside its plan. The identity policy needs only a
|
|
* complete plan. Physical Riesz also requires every map consumed during
|
|
* assembly and global-scalar runtime preparation for every generated
|
|
* coordinate in the specification pack.
|
|
*/
|
|
template <typename Prescription, typename Form, typename PhysicalCore, typename SpecificationTypes>
|
|
concept StellarNormalizationRuntimeAvailableFor = detail::StellarNormalizationRuntimeAudit<
|
|
std::remove_cvref_t<Prescription>,
|
|
std::remove_cvref_t<Form>,
|
|
std::remove_cvref_t<PhysicalCore>,
|
|
std::remove_cvref_t<SpecificationTypes>>::value;
|
|
} // namespace mean_field::normalization
|