feat(newton): first newton solver implementation
This commit is contained in:
@@ -6,130 +6,124 @@ module;
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <mpi.h>
|
||||
|
||||
export module mean_field:equilibrium.stellar_discretization;
|
||||
|
||||
export import :fem;
|
||||
export import :mapping.domain_mapper;
|
||||
export import :normalization.physical_riesz;
|
||||
|
||||
namespace mean_field::equilibrium::detail {
|
||||
struct StellarEquilibriumProblemFactory;
|
||||
}
|
||||
|
||||
export namespace mean_field::equilibrium {
|
||||
/*
|
||||
* An explicit, non-owning view of the numerical discretization used by a
|
||||
* stellar equilibrium problem. The referenced FEM and mapper must outlive
|
||||
* every problem and structure that uses this view.
|
||||
*
|
||||
* Ownership cannot move here yet because FEM currently also contains
|
||||
* mutable field workspaces. Separating those workspaces is a prerequisite
|
||||
* for shared discretization ownership by solved Structure objects.
|
||||
* The complete numerical discretization used by a stellar equilibrium
|
||||
* problem. Moving the FEM into stable heap storage lets the problem and a
|
||||
* completed Structure transfer unique ownership without invalidating the
|
||||
* references retained by prepared operators. The mapper is part of that
|
||||
* owned FEM and therefore has the same lifetime.
|
||||
*/
|
||||
template <normalization::NormalizationPrescription Normalization>
|
||||
class StellarDiscretizationFor final {
|
||||
template <normalization::NormalizationPrescription Normalization> class StellarDiscretizationFor final {
|
||||
public:
|
||||
using NormalizationPrescriptionType = std::remove_cvref_t<Normalization>;
|
||||
|
||||
explicit StellarDiscretizationFor(fem::FEM &finiteElementModel)
|
||||
requires std::same_as<NormalizationPrescriptionType, normalization::Unnormalized>
|
||||
explicit StellarDiscretizationFor(fem::FEM &&finiteElementModel)
|
||||
requires std::same_as<
|
||||
NormalizationPrescriptionType,
|
||||
normalization::Unnormalized>
|
||||
: StellarDiscretizationFor(
|
||||
finiteElementModel,
|
||||
RequireDomainMapper(finiteElementModel),
|
||||
std::move(finiteElementModel),
|
||||
normalization::Unnormalized{}
|
||||
) {
|
||||
}
|
||||
|
||||
StellarDiscretizationFor(
|
||||
fem::FEM &finiteElementModel,
|
||||
const mapping::DomainMapper &domainMapper
|
||||
)
|
||||
requires std::same_as<NormalizationPrescriptionType, normalization::Unnormalized>
|
||||
: StellarDiscretizationFor(
|
||||
finiteElementModel,
|
||||
domainMapper,
|
||||
normalization::Unnormalized{}
|
||||
) {
|
||||
}
|
||||
explicit StellarDiscretizationFor(fem::FEM &)
|
||||
requires std::same_as<
|
||||
NormalizationPrescriptionType,
|
||||
normalization::Unnormalized>
|
||||
= delete;
|
||||
|
||||
StellarDiscretizationFor(
|
||||
fem::FEM &,
|
||||
mapping::DomainMapper &&
|
||||
) requires std::same_as<NormalizationPrescriptionType, normalization::Unnormalized> = delete;
|
||||
|
||||
StellarDiscretizationFor(
|
||||
fem::FEM &,
|
||||
const mapping::DomainMapper &&
|
||||
) requires std::same_as<NormalizationPrescriptionType, normalization::Unnormalized> = delete;
|
||||
|
||||
StellarDiscretizationFor(
|
||||
fem::FEM &finiteElementModel,
|
||||
fem::FEM &&finiteElementModel,
|
||||
NormalizationPrescriptionType normalizationPrescription
|
||||
)
|
||||
: StellarDiscretizationFor(
|
||||
finiteElementModel,
|
||||
RequireDomainMapper(finiteElementModel),
|
||||
std::move(normalizationPrescription)
|
||||
) {
|
||||
}
|
||||
|
||||
StellarDiscretizationFor(
|
||||
fem::FEM &finiteElementModel,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
NormalizationPrescriptionType normalizationPrescription
|
||||
)
|
||||
: m_finiteElementModel(std::addressof(finiteElementModel)),
|
||||
m_domainMapper(std::addressof(domainMapper)),
|
||||
: m_finiteElementModel(TakeOwnership(std::move(finiteElementModel))),
|
||||
m_normalizationPrescription(std::move(normalizationPrescription)) {
|
||||
if (!finiteElementModel.okay()) {
|
||||
throw std::invalid_argument("A stellar discretization requires a complete finite-element model.");
|
||||
}
|
||||
}
|
||||
|
||||
StellarDiscretizationFor(
|
||||
fem::FEM &,
|
||||
mapping::DomainMapper &&,
|
||||
NormalizationPrescriptionType
|
||||
) = delete;
|
||||
) = delete;
|
||||
|
||||
StellarDiscretizationFor(
|
||||
fem::FEM &,
|
||||
const mapping::DomainMapper &&,
|
||||
NormalizationPrescriptionType
|
||||
) = delete;
|
||||
StellarDiscretizationFor(const StellarDiscretizationFor &) = delete;
|
||||
StellarDiscretizationFor &operator=(const StellarDiscretizationFor &) = delete;
|
||||
StellarDiscretizationFor(StellarDiscretizationFor &&) = default;
|
||||
StellarDiscretizationFor &operator=(StellarDiscretizationFor &&) = delete;
|
||||
|
||||
[[nodiscard]] fem::FEM &finiteElementModel() const noexcept {
|
||||
return *m_finiteElementModel;
|
||||
}
|
||||
|
||||
[[nodiscard]] const mapping::DomainMapper &domainMapper() const noexcept {
|
||||
return *m_domainMapper;
|
||||
}
|
||||
|
||||
[[nodiscard]] const NormalizationPrescriptionType &normalizationPrescription() const noexcept {
|
||||
return m_normalizationPrescription;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isCurrent() const noexcept {
|
||||
return m_finiteElementModel != nullptr && m_domainMapper != nullptr && m_finiteElementModel->okay();
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] static const mapping::DomainMapper &RequireDomainMapper(const fem::FEM &finiteElementModel) {
|
||||
[[nodiscard]] const mapping::DomainMapper &domainMapper() const & {
|
||||
const auto &finiteElementModel = RequireFiniteElementModel();
|
||||
if (finiteElementModel.domainMapperStateless == nullptr) {
|
||||
throw std::invalid_argument("A stellar discretization requires a domain mapper.");
|
||||
throw std::logic_error("The stellar discretization has no domain mapper.");
|
||||
}
|
||||
return *finiteElementModel.domainMapperStateless;
|
||||
}
|
||||
|
||||
fem::FEM *m_finiteElementModel;
|
||||
const mapping::DomainMapper *m_domainMapper;
|
||||
[[nodiscard]] const mapping::DomainMapper &domainMapper() const && = delete;
|
||||
|
||||
[[nodiscard]] MPI_Comm communicator() const & {
|
||||
const auto &finiteElementModel = RequireFiniteElementModel();
|
||||
if (finiteElementModel.mesh == nullptr) {
|
||||
throw std::logic_error("The stellar discretization has no parallel mesh.");
|
||||
}
|
||||
return finiteElementModel.mesh->GetComm();
|
||||
}
|
||||
|
||||
[[nodiscard]] MPI_Comm communicator() const && = delete;
|
||||
|
||||
[[nodiscard]] const NormalizationPrescriptionType &normalizationPrescription() const & noexcept {
|
||||
return m_normalizationPrescription;
|
||||
}
|
||||
|
||||
[[nodiscard]] const NormalizationPrescriptionType &normalizationPrescription() const && = delete;
|
||||
|
||||
[[nodiscard]] bool isCurrent() const noexcept {
|
||||
return m_finiteElementModel != nullptr && m_finiteElementModel->okay();
|
||||
}
|
||||
|
||||
private:
|
||||
friend struct detail::StellarEquilibriumProblemFactory;
|
||||
|
||||
[[nodiscard]] fem::FEM &MutableFiniteElementModelForAssembly() & {
|
||||
return const_cast<fem::FEM &>(RequireFiniteElementModel());
|
||||
}
|
||||
|
||||
[[nodiscard]] const fem::FEM &RequireFiniteElementModel() const {
|
||||
if (m_finiteElementModel == nullptr) {
|
||||
throw std::logic_error("A moved-from stellar discretization has no finite-element model.");
|
||||
}
|
||||
return *m_finiteElementModel;
|
||||
}
|
||||
|
||||
[[nodiscard]] static std::unique_ptr<fem::FEM> TakeOwnership(fem::FEM &&finiteElementModel) {
|
||||
if (!finiteElementModel.okay()) {
|
||||
throw std::invalid_argument("A stellar discretization requires a complete finite-element model.");
|
||||
}
|
||||
return std::make_unique<fem::FEM>(std::move(finiteElementModel));
|
||||
}
|
||||
|
||||
std::unique_ptr<fem::FEM> m_finiteElementModel;
|
||||
NormalizationPrescriptionType m_normalizationPrescription;
|
||||
};
|
||||
|
||||
template <normalization::NormalizationPrescription Normalization>
|
||||
StellarDiscretizationFor(fem::FEM &, Normalization)
|
||||
-> StellarDiscretizationFor<std::remove_cvref_t<Normalization>>;
|
||||
|
||||
template <normalization::NormalizationPrescription Normalization>
|
||||
StellarDiscretizationFor(fem::FEM &, const mapping::DomainMapper &, Normalization)
|
||||
-> StellarDiscretizationFor<std::remove_cvref_t<Normalization>>;
|
||||
StellarDiscretizationFor(
|
||||
fem::FEM &&,
|
||||
Normalization
|
||||
) -> StellarDiscretizationFor<std::remove_cvref_t<Normalization>>;
|
||||
|
||||
using StellarDiscretization = StellarDiscretizationFor<normalization::Unnormalized>;
|
||||
|
||||
@@ -143,41 +137,17 @@ export namespace mean_field::equilibrium {
|
||||
|
||||
template <normalization::NormalizationPrescription Normalization>
|
||||
[[nodiscard]] auto makeStellarDiscretization(
|
||||
fem::FEM &finiteElementModel,
|
||||
fem::FEM &&finiteElementModel,
|
||||
Normalization normalizationPrescription
|
||||
) {
|
||||
return StellarDiscretizationFor<std::remove_cvref_t<Normalization>>{
|
||||
finiteElementModel,
|
||||
std::move(normalizationPrescription)
|
||||
std::move(finiteElementModel), std::move(normalizationPrescription)
|
||||
};
|
||||
}
|
||||
|
||||
template <normalization::NormalizationPrescription Normalization>
|
||||
[[nodiscard]] auto makeStellarDiscretization(
|
||||
fem::FEM &finiteElementModel,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
Normalization normalizationPrescription
|
||||
) {
|
||||
return StellarDiscretizationFor<std::remove_cvref_t<Normalization>>{
|
||||
finiteElementModel,
|
||||
domainMapper,
|
||||
std::move(normalizationPrescription)
|
||||
};
|
||||
}
|
||||
|
||||
template <normalization::NormalizationPrescription Normalization>
|
||||
StellarDiscretizationFor<std::remove_cvref_t<Normalization>>
|
||||
makeStellarDiscretization(
|
||||
StellarDiscretizationFor<std::remove_cvref_t<Normalization>> makeStellarDiscretization(
|
||||
fem::FEM &,
|
||||
mapping::DomainMapper &&,
|
||||
Normalization
|
||||
) = delete;
|
||||
|
||||
template <normalization::NormalizationPrescription Normalization>
|
||||
StellarDiscretizationFor<std::remove_cvref_t<Normalization>>
|
||||
makeStellarDiscretization(
|
||||
fem::FEM &,
|
||||
const mapping::DomainMapper &&,
|
||||
Normalization
|
||||
) = delete;
|
||||
} // namespace mean_field::equilibrium
|
||||
|
||||
@@ -25,7 +25,10 @@ export import :integrators.viscosity;
|
||||
export import :quadrature.policy;
|
||||
export import :quadrature.mfem;
|
||||
export import :solver.fields;
|
||||
export import :solver.linear_backend;
|
||||
export import :solver.preconditioning_diagnostics;
|
||||
export import :solver.stellar_equilibrium_types;
|
||||
export import :solver.stellar_equilibrium;
|
||||
export import :preconditioning;
|
||||
export import :normalization;
|
||||
export import :utils.blocks;
|
||||
|
||||
@@ -27,8 +27,8 @@ export namespace mean_field::models {
|
||||
|
||||
class CompiledFixedAngularMomentum final {
|
||||
public:
|
||||
using SpecificationType = FixedAngularMomentum;
|
||||
using LayoutRequest = FixedAngularMomentumLayoutRequest;
|
||||
using SpecificationType = FixedAngularMomentum;
|
||||
using LayoutRequest = FixedAngularMomentumLayoutRequest;
|
||||
using AngularVelocityType = typename LayoutRequest::GeneratedValueType;
|
||||
using ResidualType = typename LayoutRequest::GeneratedResidualType;
|
||||
using AngularVelocityField = field::AngularVelocity;
|
||||
@@ -63,9 +63,8 @@ export namespace mean_field::models {
|
||||
FixedAngularMomentum m_specification;
|
||||
};
|
||||
|
||||
[[nodiscard]] inline CompiledFixedAngularMomentum compileConstraint(
|
||||
const FixedAngularMomentum specification
|
||||
) noexcept {
|
||||
[[nodiscard]] inline CompiledFixedAngularMomentum
|
||||
compileConstraint(const FixedAngularMomentum specification) noexcept {
|
||||
return CompiledFixedAngularMomentum{specification};
|
||||
}
|
||||
|
||||
|
||||
@@ -57,11 +57,9 @@ export namespace mean_field::models {
|
||||
static constexpr std::size_t size = sizeof...(Types);
|
||||
};
|
||||
|
||||
template <typename... ValueBlocks>
|
||||
using DependsOn = ModelTypeList<ValueBlocks...>;
|
||||
template <typename... ValueBlocks> using DependsOn = ModelTypeList<ValueBlocks...>;
|
||||
|
||||
template <typename... ResidualBlocks>
|
||||
using Affects = ModelTypeList<ResidualBlocks...>;
|
||||
template <typename... ResidualBlocks> using Affects = ModelTypeList<ResidualBlocks...>;
|
||||
|
||||
/*
|
||||
* Physics vocabulary for declaring how a stellar specification couples to
|
||||
@@ -83,8 +81,7 @@ export namespace mean_field::models {
|
||||
* the physics-facing spelling for coupled global constraints: an
|
||||
* extension names the constraint it reads, never its solver block.
|
||||
*/
|
||||
template <typename Specification>
|
||||
struct GeneratedCoordinateOf final {
|
||||
template <typename Specification> struct GeneratedCoordinateOf final {
|
||||
using SpecificationType = Specification;
|
||||
};
|
||||
|
||||
@@ -108,8 +105,7 @@ export namespace mean_field::models {
|
||||
struct OwnConstraint final { };
|
||||
|
||||
/* The scalar constraint equation owned by another specification. */
|
||||
template <typename Specification>
|
||||
struct ConstraintOf final {
|
||||
template <typename Specification> struct ConstraintOf final {
|
||||
using SpecificationType = Specification;
|
||||
};
|
||||
} // namespace equation
|
||||
@@ -119,8 +115,7 @@ export namespace mean_field::models {
|
||||
* Runtime providers consume this vocabulary without learning backend
|
||||
* row and column block types.
|
||||
*/
|
||||
template <typename Equation, typename State>
|
||||
struct Derivative final {
|
||||
template <typename Equation, typename State> struct Derivative final {
|
||||
using EquationType = Equation;
|
||||
using StateType = State;
|
||||
};
|
||||
@@ -220,11 +215,7 @@ export namespace mean_field::models {
|
||||
concept PhysicalScaleRepresentedQuantity =
|
||||
dimensions::PhysicalQuantityType<Quantity> &&
|
||||
physicalScaleForQuantity<Quantity> != PhysicalScaleLaw::unavailable &&
|
||||
requires {
|
||||
typename std::bool_constant<
|
||||
!static_cast<std::string_view>(
|
||||
Quantity::identifier).empty()>;
|
||||
};
|
||||
requires { typename std::bool_constant<!static_cast<std::string_view>(Quantity::identifier).empty()>; };
|
||||
|
||||
namespace detail {
|
||||
template <typename Candidate> [[nodiscard]] consteval bool declaredCoordinateNormalizationIsAvailable() {
|
||||
@@ -270,8 +261,7 @@ export namespace mean_field::models {
|
||||
case SpecificationRole::boundary_condition:
|
||||
return kind == GeneratedStateKind::none;
|
||||
case SpecificationRole::invariant:
|
||||
return kind == GeneratedStateKind::multiplier ||
|
||||
kind == GeneratedStateKind::physical_coordinate;
|
||||
return kind == GeneratedStateKind::multiplier || kind == GeneratedStateKind::physical_coordinate;
|
||||
case SpecificationRole::phase_condition:
|
||||
case SpecificationRole::gauge_choice:
|
||||
return kind == GeneratedStateKind::solver_border;
|
||||
@@ -304,11 +294,12 @@ export namespace mean_field::models {
|
||||
using UnavailableCoordinateNormalization =
|
||||
CoordinateNormalization<RieszTopology::unavailable, PhysicalScaleLaw::unavailable>;
|
||||
|
||||
template <typename ValueNormalization = UnavailableCoordinateNormalization,
|
||||
typename ResidualNormalization = UnavailableCoordinateNormalization>
|
||||
template <
|
||||
typename ValueNormalization = UnavailableCoordinateNormalization,
|
||||
typename ResidualNormalization = UnavailableCoordinateNormalization>
|
||||
struct GeneratedNormalization final {
|
||||
using Value = ValueNormalization;
|
||||
using Residual = ResidualNormalization;
|
||||
using Value = ValueNormalization;
|
||||
using Residual = ResidualNormalization;
|
||||
|
||||
static constexpr bool available = detail::declaredCoordinateNormalizationIsAvailable<Value>() &&
|
||||
detail::declaredCoordinateNormalizationIsAvailable<Residual>();
|
||||
@@ -321,8 +312,13 @@ export namespace mean_field::models {
|
||||
CoordinateNormalization<RieszTopology::global_scalar, ValueScale>,
|
||||
CoordinateNormalization<RieszTopology::global_scalar, ResidualScale>>;
|
||||
|
||||
template <FixedString ValueStableId = "", FixedString ValueSymbol = "", FixedString ResidualStableId = "",
|
||||
FixedString ResidualSymbol = "", FixedString TargetUnits = "", FixedString ResidualUnits = "">
|
||||
template <
|
||||
FixedString ValueStableId = "",
|
||||
FixedString ValueSymbol = "",
|
||||
FixedString ResidualStableId = "",
|
||||
FixedString ResidualSymbol = "",
|
||||
FixedString TargetUnits = "",
|
||||
FixedString ResidualUnits = "">
|
||||
struct GeneratedManifest final {
|
||||
private:
|
||||
inline static constexpr auto valueStableIdStorage = ValueStableId;
|
||||
@@ -366,21 +362,19 @@ export namespace mean_field::models {
|
||||
FixedString ResidualStableId,
|
||||
FixedString ResidualSymbol>
|
||||
struct DimensionalScalarConstraint final {
|
||||
using TargetQuantity = TargetQuantityT;
|
||||
using GeneratedCoordinateQuantity = GeneratedCoordinateQuantityT;
|
||||
using ConstraintResidualQuantity = ConstraintResidualQuantityT;
|
||||
using TargetValue = dimensions::QuantityValue<TargetQuantity>;
|
||||
static constexpr PhysicalScaleLaw targetScale =
|
||||
physicalScaleForQuantity<TargetQuantity>;
|
||||
using TargetQuantity = TargetQuantityT;
|
||||
using GeneratedCoordinateQuantity = GeneratedCoordinateQuantityT;
|
||||
using ConstraintResidualQuantity = ConstraintResidualQuantityT;
|
||||
using TargetValue = dimensions::QuantityValue<TargetQuantity>;
|
||||
static constexpr PhysicalScaleLaw targetScale = physicalScaleForQuantity<TargetQuantity>;
|
||||
|
||||
struct Normalization final {
|
||||
using TargetQuantity = TargetQuantityT;
|
||||
using GeneratedCoordinateQuantity = GeneratedCoordinateQuantityT;
|
||||
using ConstraintResidualQuantity = ConstraintResidualQuantityT;
|
||||
using TargetValue = dimensions::QuantityValue<TargetQuantity>;
|
||||
static constexpr PhysicalScaleLaw targetScale =
|
||||
physicalScaleForQuantity<TargetQuantity>;
|
||||
using Value = CoordinateNormalization<
|
||||
using TargetQuantity = TargetQuantityT;
|
||||
using GeneratedCoordinateQuantity = GeneratedCoordinateQuantityT;
|
||||
using ConstraintResidualQuantity = ConstraintResidualQuantityT;
|
||||
using TargetValue = dimensions::QuantityValue<TargetQuantity>;
|
||||
static constexpr PhysicalScaleLaw targetScale = physicalScaleForQuantity<TargetQuantity>;
|
||||
using Value = CoordinateNormalization<
|
||||
RieszTopology::global_scalar,
|
||||
physicalScaleForQuantity<GeneratedCoordinateQuantity>>;
|
||||
using Residual = CoordinateNormalization<
|
||||
@@ -408,20 +402,23 @@ export namespace mean_field::models {
|
||||
static constexpr std::string_view residualSymbol = residualSymbolStorage.view();
|
||||
static constexpr std::string_view targetUnits = TargetQuantity::identifier;
|
||||
static constexpr std::string_view residualUnits = ConstraintResidualQuantity::identifier;
|
||||
static constexpr bool available =
|
||||
!valueStableId.empty() && !valueSymbol.empty() &&
|
||||
!residualStableId.empty() && !residualSymbol.empty() &&
|
||||
!targetUnits.empty() && !residualUnits.empty();
|
||||
static constexpr bool available = !valueStableId.empty() && !valueSymbol.empty() &&
|
||||
!residualStableId.empty() && !residualSymbol.empty() &&
|
||||
!targetUnits.empty() && !residualUnits.empty();
|
||||
};
|
||||
|
||||
static constexpr bool dimensionallyTyped = true;
|
||||
};
|
||||
|
||||
template <typename Specification, FixedString StableName, SpecificationRole Role,
|
||||
GeneratedStateKind StateKind = GeneratedStateKind::none, typename DependsOnBlocks = ModelTypeList<>,
|
||||
typename AffectedResidualBlocks = ModelTypeList<>,
|
||||
typename NormalizationDefinition = UnavailableGeneratedNormalization,
|
||||
typename ManifestDefinition = UnavailableGeneratedManifest>
|
||||
template <
|
||||
typename Specification,
|
||||
FixedString StableName,
|
||||
SpecificationRole Role,
|
||||
GeneratedStateKind StateKind = GeneratedStateKind::none,
|
||||
typename DependsOnBlocks = ModelTypeList<>,
|
||||
typename AffectedResidualBlocks = ModelTypeList<>,
|
||||
typename NormalizationDefinition = UnavailableGeneratedNormalization,
|
||||
typename ManifestDefinition = UnavailableGeneratedManifest>
|
||||
struct ModelDefinition final {
|
||||
using SpecificationType = Specification;
|
||||
using DependsOn = DependsOnBlocks;
|
||||
@@ -447,26 +444,56 @@ export namespace mean_field::models {
|
||||
template <typename Specification, FixedString Name>
|
||||
using BoundaryCondition = ModelDefinition<Specification, Name, SpecificationRole::boundary_condition>;
|
||||
|
||||
template <typename Specification, FixedString Name, typename DependsOn = ModelTypeList<>,
|
||||
typename Affects = ModelTypeList<>, typename Normalization = UnavailableGeneratedNormalization,
|
||||
typename Manifest = UnavailableGeneratedManifest>
|
||||
using FixedIntegralWithMultiplier =
|
||||
ModelDefinition<Specification, Name, SpecificationRole::invariant, GeneratedStateKind::multiplier, DependsOn,
|
||||
Affects, Normalization, Manifest>;
|
||||
template <
|
||||
typename Specification,
|
||||
FixedString Name,
|
||||
typename DependsOn = ModelTypeList<>,
|
||||
typename Affects = ModelTypeList<>,
|
||||
typename Normalization = UnavailableGeneratedNormalization,
|
||||
typename Manifest = UnavailableGeneratedManifest>
|
||||
using FixedIntegralWithMultiplier = ModelDefinition<
|
||||
Specification,
|
||||
Name,
|
||||
SpecificationRole::invariant,
|
||||
GeneratedStateKind::multiplier,
|
||||
DependsOn,
|
||||
Affects,
|
||||
Normalization,
|
||||
Manifest>;
|
||||
|
||||
template <typename Specification, FixedString Name, typename DependsOn = ModelTypeList<>,
|
||||
typename Affects = ModelTypeList<>, typename Normalization = UnavailableGeneratedNormalization,
|
||||
typename Manifest = UnavailableGeneratedManifest>
|
||||
using FixedIntegralWithPhysicalCoordinate =
|
||||
ModelDefinition<Specification, Name, SpecificationRole::invariant, GeneratedStateKind::physical_coordinate,
|
||||
DependsOn, Affects, Normalization, Manifest>;
|
||||
template <
|
||||
typename Specification,
|
||||
FixedString Name,
|
||||
typename DependsOn = ModelTypeList<>,
|
||||
typename Affects = ModelTypeList<>,
|
||||
typename Normalization = UnavailableGeneratedNormalization,
|
||||
typename Manifest = UnavailableGeneratedManifest>
|
||||
using FixedIntegralWithPhysicalCoordinate = ModelDefinition<
|
||||
Specification,
|
||||
Name,
|
||||
SpecificationRole::invariant,
|
||||
GeneratedStateKind::physical_coordinate,
|
||||
DependsOn,
|
||||
Affects,
|
||||
Normalization,
|
||||
Manifest>;
|
||||
|
||||
template <typename Specification, FixedString Name, typename DependsOn = ModelTypeList<>,
|
||||
typename Affects = ModelTypeList<>, typename Normalization = UnavailableGeneratedNormalization,
|
||||
typename Manifest = UnavailableGeneratedManifest>
|
||||
using PhaseCondition =
|
||||
ModelDefinition<Specification, Name, SpecificationRole::phase_condition, GeneratedStateKind::solver_border,
|
||||
DependsOn, Affects, Normalization, Manifest>;
|
||||
template <
|
||||
typename Specification,
|
||||
FixedString Name,
|
||||
typename DependsOn = ModelTypeList<>,
|
||||
typename Affects = ModelTypeList<>,
|
||||
typename Normalization = UnavailableGeneratedNormalization,
|
||||
typename Manifest = UnavailableGeneratedManifest>
|
||||
using PhaseCondition = ModelDefinition<
|
||||
Specification,
|
||||
Name,
|
||||
SpecificationRole::phase_condition,
|
||||
GeneratedStateKind::solver_border,
|
||||
DependsOn,
|
||||
Affects,
|
||||
Normalization,
|
||||
Manifest>;
|
||||
|
||||
struct SpecificationKey final {
|
||||
SpecificationRole role;
|
||||
@@ -510,35 +537,42 @@ export namespace mean_field::models {
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <typename Candidate> struct IsModelTypeList : std::false_type {};
|
||||
template <typename Candidate> struct IsModelTypeList : std::false_type { };
|
||||
|
||||
template <typename... Types> struct IsModelTypeList<ModelTypeList<Types...>> : std::true_type {};
|
||||
template <typename... Types> struct IsModelTypeList<ModelTypeList<Types...>> : std::true_type { };
|
||||
|
||||
template <typename Candidate> struct IsModelDefinition : std::false_type {};
|
||||
template <typename Candidate> struct IsModelDefinition : std::false_type { };
|
||||
|
||||
template <typename Specification, FixedString StableName, SpecificationRole Role, GeneratedStateKind StateKind,
|
||||
typename DependsOn, typename Affects, typename Normalization, typename Manifest>
|
||||
template <
|
||||
typename Specification,
|
||||
FixedString StableName,
|
||||
SpecificationRole Role,
|
||||
GeneratedStateKind StateKind,
|
||||
typename DependsOn,
|
||||
typename Affects,
|
||||
typename Normalization,
|
||||
typename Manifest>
|
||||
struct IsModelDefinition<
|
||||
ModelDefinition<Specification, StableName, Role, StateKind, DependsOn, Affects, Normalization, Manifest>>
|
||||
: std::bool_constant<(StableName.view().size() > 0) &&
|
||||
CompatibleSpecificationRoleAndGeneratedState<Role, StateKind> &&
|
||||
IsModelTypeList<DependsOn>::value &&
|
||||
IsModelTypeList<Affects>::value>{};
|
||||
: std::bool_constant<
|
||||
(StableName.view().size() > 0) && CompatibleSpecificationRoleAndGeneratedState<Role, StateKind> &&
|
||||
IsModelTypeList<DependsOn>::value && IsModelTypeList<Affects>::value> { };
|
||||
|
||||
template <typename Definition, typename Candidate, bool = IsModelDefinition<Definition>::value>
|
||||
struct DefinitionDescribesCandidate : std::false_type {};
|
||||
struct DefinitionDescribesCandidate : std::false_type { };
|
||||
|
||||
template <typename Definition, typename Candidate>
|
||||
struct DefinitionDescribesCandidate<Definition, Candidate, true>
|
||||
: std::bool_constant<std::same_as<typename Definition::SpecificationType, Candidate>> {};
|
||||
: std::bool_constant<std::same_as<typename Definition::SpecificationType, Candidate>> { };
|
||||
|
||||
template <typename Candidate, typename = void> struct SpecificationDefinitionFor {
|
||||
static constexpr bool available = false;
|
||||
};
|
||||
|
||||
template <typename Candidate>
|
||||
struct SpecificationDefinitionFor<Candidate,
|
||||
std::void_t<typename std::remove_cvref_t<Candidate>::ModelDefinition>> {
|
||||
struct SpecificationDefinitionFor<
|
||||
Candidate,
|
||||
std::void_t<typename std::remove_cvref_t<Candidate>::ModelDefinition>> {
|
||||
using Type = typename std::remove_cvref_t<Candidate>::ModelDefinition;
|
||||
static constexpr bool available = DefinitionDescribesCandidate<Type, std::remove_cvref_t<Candidate>>::value;
|
||||
};
|
||||
@@ -570,7 +604,7 @@ export namespace mean_field::models {
|
||||
template <typename Candidate>
|
||||
requires detail::SpecificationDefinitionFor<std::remove_cvref_t<Candidate>>::available
|
||||
struct SpecificationTraits<Candidate> {
|
||||
using Definition = ModelDefinitionForT<Candidate>;
|
||||
using Definition = ModelDefinitionForT<Candidate>;
|
||||
|
||||
static constexpr std::string_view name = Definition::name;
|
||||
static constexpr SpecificationRole role = Definition::role;
|
||||
@@ -604,7 +638,8 @@ export namespace mean_field::models {
|
||||
"R_M">;
|
||||
using TargetValue = typename ScalarDescription::TargetValue;
|
||||
using ModelDefinition = FixedIntegralWithMultiplier<
|
||||
FixedTotalMass, "FixedTotalMass",
|
||||
FixedTotalMass,
|
||||
"FixedTotalMass",
|
||||
DependsOn<stellar::state::Density, stellar::state::SurfaceShape>,
|
||||
Affects<stellar::equation::HydrostaticBalance>,
|
||||
typename ScalarDescription::Normalization,
|
||||
@@ -615,9 +650,13 @@ export namespace mean_field::models {
|
||||
|
||||
explicit FixedTotalMass(const TargetValue targetMass) : m_targetMass(targetMass) {
|
||||
if (!std::isfinite(targetMass.value()) || targetMass.value() <= 0.0) {
|
||||
throw std::invalid_argument(std::format("The fixed total mass must be finite and positive. "
|
||||
"Instead M = {} was provided.",
|
||||
targetMass.value()));
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"The fixed total mass must be finite and positive. "
|
||||
"Instead M = {} was provided.",
|
||||
targetMass.value()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -651,29 +690,35 @@ export namespace mean_field::models {
|
||||
"R_J">;
|
||||
using TargetValue = typename ScalarDescription::TargetValue;
|
||||
using ModelDefinition = FixedIntegralWithPhysicalCoordinate<
|
||||
FixedAngularMomentum, "FixedAngularMomentum",
|
||||
DependsOn<
|
||||
stellar::state::Density,
|
||||
stellar::state::SurfaceShape,
|
||||
stellar::state::OwnGeneratedCoordinate>,
|
||||
FixedAngularMomentum,
|
||||
"FixedAngularMomentum",
|
||||
DependsOn<stellar::state::Density, stellar::state::SurfaceShape, stellar::state::OwnGeneratedCoordinate>,
|
||||
Affects<stellar::equation::SurfaceShapeBalance, stellar::equation::HydrostaticBalance>,
|
||||
typename ScalarDescription::Normalization,
|
||||
typename ScalarDescription::Manifest>;
|
||||
|
||||
explicit FixedAngularMomentum(const Parameters parameters)
|
||||
: m_targetAngularMomentum(parameters.Jtotal), m_axis(parameters.axis), m_center(parameters.center) {
|
||||
: m_targetAngularMomentum(parameters.Jtotal),
|
||||
m_axis(parameters.axis),
|
||||
m_center(parameters.center) {
|
||||
if (!std::isfinite(m_targetAngularMomentum.value()) || m_targetAngularMomentum.value() < 0.0) {
|
||||
throw std::invalid_argument(std::format("The fixed total angular momentum must be finite and "
|
||||
"nonnegative. Instead J = {} was "
|
||||
"provided.",
|
||||
m_targetAngularMomentum.value()));
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"The fixed total angular momentum must be finite and "
|
||||
"nonnegative. Instead J = {} was "
|
||||
"provided.",
|
||||
m_targetAngularMomentum.value()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
double axisNormSquared = 0.0;
|
||||
for (std::size_t component = 0; component < m_axis.size(); ++component) {
|
||||
if (!std::isfinite(m_axis[component]) || !std::isfinite(m_center[component])) {
|
||||
throw std::invalid_argument("A fixed-angular-momentum rotation axis and center must contain "
|
||||
"only finite values.");
|
||||
throw std::invalid_argument(
|
||||
"A fixed-angular-momentum rotation axis and center must contain "
|
||||
"only finite values."
|
||||
);
|
||||
}
|
||||
axisNormSquared += m_axis[component] * m_axis[component];
|
||||
}
|
||||
@@ -698,11 +743,17 @@ export namespace mean_field::models {
|
||||
return m_targetAngularMomentum;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::array<double, 3> &axis() const noexcept {
|
||||
[[nodiscard]] const std::array<
|
||||
double,
|
||||
3> &
|
||||
axis() const noexcept {
|
||||
return m_axis;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::array<double, 3> ¢er() const noexcept {
|
||||
[[nodiscard]] const std::array<
|
||||
double,
|
||||
3> &
|
||||
center() const noexcept {
|
||||
return m_center;
|
||||
}
|
||||
|
||||
@@ -728,7 +779,8 @@ export namespace mean_field::models {
|
||||
"R_rho_c">;
|
||||
using TargetValue = typename ScalarDescription::TargetValue;
|
||||
using ModelDefinition = PhaseCondition<
|
||||
FixedCentralDensity, "FixedCentralDensity",
|
||||
FixedCentralDensity,
|
||||
"FixedCentralDensity",
|
||||
DependsOn<stellar::state::SpecificEnthalpy>,
|
||||
Affects<stellar::equation::HydrostaticBalance>,
|
||||
typename ScalarDescription::Normalization,
|
||||
@@ -739,9 +791,13 @@ export namespace mean_field::models {
|
||||
|
||||
explicit FixedCentralDensity(const TargetValue targetDensity) : m_targetDensity(targetDensity) {
|
||||
if (!std::isfinite(targetDensity.value()) || targetDensity.value() <= 0.0) {
|
||||
throw std::invalid_argument(std::format("The fixed central density must be finite and positive. "
|
||||
"Instead rho_c = {} was provided.",
|
||||
targetDensity.value()));
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"The fixed central density must be finite and positive. "
|
||||
"Instead rho_c = {} was provided.",
|
||||
targetDensity.value()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -761,19 +817,19 @@ export namespace mean_field::models {
|
||||
|
||||
template <typename Query, typename... Types>
|
||||
struct ModelTypeListContains<Query, ModelTypeList<Types...>>
|
||||
: std::bool_constant<(std::same_as<Query, Types> || ...)> {};
|
||||
: std::bool_constant<(std::same_as<Query, Types> || ...)> { };
|
||||
|
||||
template <typename Query, typename List>
|
||||
inline constexpr bool modelTypeListContains = ModelTypeListContains<Query, List>::value;
|
||||
|
||||
template <typename Specification> struct ResidualFor final {
|
||||
using SpecificationType = Specification;
|
||||
using SpecificationType = Specification;
|
||||
|
||||
static constexpr std::size_t scalarArity = 1;
|
||||
};
|
||||
|
||||
template <typename Specification> struct MultiplierFor final {
|
||||
using SpecificationType = Specification;
|
||||
using SpecificationType = Specification;
|
||||
|
||||
static constexpr std::size_t scalarArity = 1;
|
||||
};
|
||||
@@ -781,13 +837,13 @@ export namespace mean_field::models {
|
||||
// A generated state variable that participates directly in the physical
|
||||
// equations, rather than serving only as a Lagrange multiplier or border.
|
||||
template <typename Specification> struct PhysicalCoordinateFor final {
|
||||
using SpecificationType = Specification;
|
||||
using SpecificationType = Specification;
|
||||
|
||||
static constexpr std::size_t scalarArity = 1;
|
||||
};
|
||||
|
||||
template <typename Specification> struct BorderFor final {
|
||||
using SpecificationType = Specification;
|
||||
using SpecificationType = Specification;
|
||||
|
||||
static constexpr std::size_t scalarArity = 1;
|
||||
};
|
||||
@@ -881,15 +937,15 @@ export namespace mean_field::models {
|
||||
static constexpr std::size_t generatedValueArity = Definition::generatedValueArity;
|
||||
static constexpr std::size_t generatedResidualArity = Definition::generatedResidualArity;
|
||||
static constexpr bool isDefined = Definition::structurallyAvailable;
|
||||
static constexpr bool hasDeclarativeDefinition = Definition::structurallyAvailable;
|
||||
static constexpr bool hasDeclarativeDefinition = Definition::structurallyAvailable;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <ModelSpecification Specification>
|
||||
[[nodiscard]] consteval bool generatedScalarDimensionsAreCoherent() {
|
||||
using Contribution = SpecificationContribution<Specification>;
|
||||
using Contribution = SpecificationContribution<Specification>;
|
||||
using Normalization = typename Contribution::Normalization;
|
||||
using Manifest = typename Contribution::Manifest;
|
||||
using Manifest = typename Contribution::Manifest;
|
||||
|
||||
if constexpr (Contribution::generatedValueArity == 0) {
|
||||
return true;
|
||||
@@ -899,9 +955,7 @@ export namespace mean_field::models {
|
||||
typename Normalization::GeneratedCoordinateQuantity;
|
||||
typename Normalization::ConstraintResidualQuantity;
|
||||
typename Normalization::TargetValue;
|
||||
{
|
||||
Normalization::targetScale
|
||||
} -> std::convertible_to<PhysicalScaleLaw>;
|
||||
{ Normalization::targetScale } -> std::convertible_to<PhysicalScaleLaw>;
|
||||
};
|
||||
constexpr bool manifestIsTyped = requires {
|
||||
typename Manifest::TargetQuantity;
|
||||
@@ -918,72 +972,53 @@ export namespace mean_field::models {
|
||||
} else if constexpr (!normalizationIsTyped || !manifestIsTyped) {
|
||||
return false;
|
||||
} else {
|
||||
using TargetQuantity = typename Normalization::TargetQuantity;
|
||||
using GeneratedCoordinateQuantity =
|
||||
typename Normalization::GeneratedCoordinateQuantity;
|
||||
using ConstraintResidualQuantity =
|
||||
typename Normalization::ConstraintResidualQuantity;
|
||||
using ValueNormalization = typename Normalization::Value;
|
||||
using ResidualNormalization = typename Normalization::Residual;
|
||||
using TargetQuantity = typename Normalization::TargetQuantity;
|
||||
using GeneratedCoordinateQuantity = typename Normalization::GeneratedCoordinateQuantity;
|
||||
using ConstraintResidualQuantity = typename Normalization::ConstraintResidualQuantity;
|
||||
using ValueNormalization = typename Normalization::Value;
|
||||
using ResidualNormalization = typename Normalization::Residual;
|
||||
|
||||
if constexpr (
|
||||
!PhysicalScaleRepresentedQuantity<TargetQuantity> ||
|
||||
!PhysicalScaleRepresentedQuantity<GeneratedCoordinateQuantity> ||
|
||||
!PhysicalScaleRepresentedQuantity<ConstraintResidualQuantity>) {
|
||||
!PhysicalScaleRepresentedQuantity<ConstraintResidualQuantity>
|
||||
) {
|
||||
return false;
|
||||
} else if constexpr (!requires {
|
||||
typename std::integral_constant<
|
||||
PhysicalScaleLaw,
|
||||
static_cast<PhysicalScaleLaw>(
|
||||
Normalization::targetScale)>;
|
||||
typename std::integral_constant<
|
||||
PhysicalScaleLaw,
|
||||
static_cast<PhysicalScaleLaw>(
|
||||
ValueNormalization::scale)>;
|
||||
typename std::integral_constant<
|
||||
PhysicalScaleLaw,
|
||||
static_cast<PhysicalScaleLaw>(
|
||||
ResidualNormalization::scale)>;
|
||||
typename std::bool_constant<
|
||||
static_cast<std::string_view>(
|
||||
Manifest::targetUnits) ==
|
||||
TargetQuantity::identifier>;
|
||||
typename std::bool_constant<
|
||||
static_cast<std::string_view>(
|
||||
Manifest::residualUnits) ==
|
||||
ConstraintResidualQuantity::identifier>;
|
||||
}) {
|
||||
} else if constexpr (
|
||||
!requires {
|
||||
typename std::integral_constant<
|
||||
PhysicalScaleLaw, static_cast<PhysicalScaleLaw>(Normalization::targetScale)>;
|
||||
typename std::integral_constant<
|
||||
PhysicalScaleLaw, static_cast<PhysicalScaleLaw>(ValueNormalization::scale)>;
|
||||
typename std::integral_constant<
|
||||
PhysicalScaleLaw, static_cast<PhysicalScaleLaw>(ResidualNormalization::scale)>;
|
||||
typename std::bool_constant<
|
||||
static_cast<std::string_view>(Manifest::targetUnits) == TargetQuantity::identifier>;
|
||||
typename std::bool_constant<
|
||||
static_cast<std::string_view>(Manifest::residualUnits) ==
|
||||
ConstraintResidualQuantity::identifier>;
|
||||
}
|
||||
) {
|
||||
return false;
|
||||
} else if constexpr (!requires(const Specification &specification) {
|
||||
specification.target();
|
||||
}) {
|
||||
} else if constexpr (!requires(const Specification &specification) { specification.target(); }) {
|
||||
return false;
|
||||
} else {
|
||||
return
|
||||
std::same_as<
|
||||
typename Normalization::TargetValue,
|
||||
dimensions::QuantityValue<TargetQuantity>> &&
|
||||
Normalization::targetScale ==
|
||||
physicalScaleForQuantity<TargetQuantity> &&
|
||||
std::same_as<TargetQuantity, typename Manifest::TargetQuantity> &&
|
||||
std::same_as<
|
||||
GeneratedCoordinateQuantity,
|
||||
typename Manifest::GeneratedCoordinateQuantity> &&
|
||||
std::same_as<
|
||||
ConstraintResidualQuantity,
|
||||
typename Manifest::ConstraintResidualQuantity> &&
|
||||
ValueNormalization::scale ==
|
||||
physicalScaleForQuantity<GeneratedCoordinateQuantity> &&
|
||||
ResidualNormalization::scale ==
|
||||
physicalScaleForQuantity<ConstraintResidualQuantity> &&
|
||||
static_cast<std::string_view>(Manifest::targetUnits) ==
|
||||
TargetQuantity::identifier &&
|
||||
static_cast<std::string_view>(Manifest::residualUnits) ==
|
||||
ConstraintResidualQuantity::identifier &&
|
||||
std::same_as<
|
||||
std::remove_cvref_t<decltype(
|
||||
std::declval<const Specification &>().target())>,
|
||||
typename Normalization::TargetValue>;
|
||||
return std::same_as<
|
||||
typename Normalization::TargetValue, dimensions::QuantityValue<TargetQuantity>> &&
|
||||
Normalization::targetScale == physicalScaleForQuantity<TargetQuantity> &&
|
||||
std::same_as<TargetQuantity, typename Manifest::TargetQuantity> &&
|
||||
std::same_as<
|
||||
GeneratedCoordinateQuantity, typename Manifest::GeneratedCoordinateQuantity> &&
|
||||
std::same_as<
|
||||
ConstraintResidualQuantity, typename Manifest::ConstraintResidualQuantity> &&
|
||||
ValueNormalization::scale == physicalScaleForQuantity<GeneratedCoordinateQuantity> &&
|
||||
ResidualNormalization::scale == physicalScaleForQuantity<ConstraintResidualQuantity> &&
|
||||
static_cast<std::string_view>(Manifest::targetUnits) == TargetQuantity::identifier &&
|
||||
static_cast<std::string_view>(Manifest::residualUnits) ==
|
||||
ConstraintResidualQuantity::identifier &&
|
||||
std::same_as<
|
||||
std::remove_cvref_t<decltype(std::declval<const Specification &>().target())>,
|
||||
typename Normalization::TargetValue>;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -993,20 +1028,17 @@ export namespace mean_field::models {
|
||||
template <typename Specification>
|
||||
concept CompleteGeneratedScalarDimensionsFor =
|
||||
ModelSpecification<Specification> &&
|
||||
detail::generatedScalarDimensionsAreCoherent<
|
||||
std::remove_cvref_t<Specification>>();
|
||||
detail::generatedScalarDimensionsAreCoherent<std::remove_cvref_t<Specification>>();
|
||||
|
||||
template <typename Specification>
|
||||
concept CompleteGeneratedNormalizationFor =
|
||||
ModelSpecification<Specification> &&
|
||||
CompleteGeneratedScalarDimensionsFor<Specification> &&
|
||||
ModelSpecification<Specification> && CompleteGeneratedScalarDimensionsFor<Specification> &&
|
||||
(SpecificationContribution<std::remove_cvref_t<Specification>>::generatedValueArity == 0 ||
|
||||
SpecificationContribution<std::remove_cvref_t<Specification>>::Normalization::available);
|
||||
|
||||
template <typename Specification>
|
||||
concept CompleteGeneratedManifestFor =
|
||||
ModelSpecification<Specification> &&
|
||||
CompleteGeneratedScalarDimensionsFor<Specification> &&
|
||||
ModelSpecification<Specification> && CompleteGeneratedScalarDimensionsFor<Specification> &&
|
||||
(SpecificationContribution<std::remove_cvref_t<Specification>>::generatedValueArity == 0 ||
|
||||
SpecificationContribution<std::remove_cvref_t<Specification>>::Manifest::available);
|
||||
|
||||
@@ -1035,7 +1067,7 @@ export namespace mean_field::models {
|
||||
};
|
||||
|
||||
template <SpecificationRole Role, typename SpecificationSet> struct SpecificationsForRole {
|
||||
using Type = ModelTypeList<>;
|
||||
using Type = ModelTypeList<>;
|
||||
|
||||
static constexpr bool available = false;
|
||||
static constexpr std::size_t count = 0;
|
||||
@@ -1043,9 +1075,10 @@ export namespace mean_field::models {
|
||||
|
||||
template <SpecificationRole Role, ModelSpecification... Specifications>
|
||||
struct SpecificationsForRole<Role, SpecificationSetStorage<Specifications...>> {
|
||||
using Type = typename ConcatenateModelTypeLists<
|
||||
std::conditional_t<SpecificationTraits<Specifications>::role == Role, ModelTypeList<Specifications>,
|
||||
ModelTypeList<>>...>::Type;
|
||||
using Type = typename ConcatenateModelTypeLists<std::conditional_t<
|
||||
SpecificationTraits<Specifications>::role == Role,
|
||||
ModelTypeList<Specifications>,
|
||||
ModelTypeList<>>...>::Type;
|
||||
|
||||
static constexpr bool available = true;
|
||||
static constexpr std::size_t count = Type::size;
|
||||
@@ -1077,9 +1110,10 @@ export namespace mean_field::models {
|
||||
};
|
||||
|
||||
public:
|
||||
using Type = std::conditional_t<(SpecificationTraits<Specification>::key < SpecificationTraits<Head>::key),
|
||||
SpecificationSetStorage<Specification, Head, Tail...>,
|
||||
typename PrependSpecification<Head, InsertedTail>::Type>;
|
||||
using Type = std::conditional_t<
|
||||
(SpecificationTraits<Specification>::key < SpecificationTraits<Head>::key),
|
||||
SpecificationSetStorage<Specification, Head, Tail...>,
|
||||
typename PrependSpecification<Head, InsertedTail>::Type>;
|
||||
};
|
||||
|
||||
template <typename Set, ModelSpecification... Specifications> struct CanonicalizeSpecifications;
|
||||
@@ -1098,21 +1132,26 @@ export namespace mean_field::models {
|
||||
using CanonicalSpecificationSet =
|
||||
typename CanonicalizeSpecifications<SpecificationSetStorage<>, Specifications...>::Type;
|
||||
|
||||
template <ModelSpecification Head, ModelSpecification... Tail> consteval bool specificationKeyIsUnique() {
|
||||
template <
|
||||
ModelSpecification Head,
|
||||
ModelSpecification... Tail>
|
||||
consteval bool specificationKeyIsUnique() {
|
||||
constexpr auto headKey = SpecificationTraits<Head>::key;
|
||||
return ((headKey.role != SpecificationTraits<Tail>::key.role ||
|
||||
headKey.stableName != SpecificationTraits<Tail>::key.stableName) &&
|
||||
...);
|
||||
return (
|
||||
(headKey.role != SpecificationTraits<Tail>::key.role ||
|
||||
headKey.stableName != SpecificationTraits<Tail>::key.stableName) &&
|
||||
...
|
||||
);
|
||||
}
|
||||
|
||||
template <ModelSpecification... Specifications> struct SpecificationKeysAreUnique;
|
||||
|
||||
template <> struct SpecificationKeysAreUnique<> : std::true_type {};
|
||||
template <> struct SpecificationKeysAreUnique<> : std::true_type { };
|
||||
|
||||
template <ModelSpecification Head, ModelSpecification... Tail>
|
||||
struct SpecificationKeysAreUnique<Head, Tail...>
|
||||
: std::bool_constant<specificationKeyIsUnique<Head, Tail...>() &&
|
||||
SpecificationKeysAreUnique<Tail...>::value> {};
|
||||
: std::bool_constant<
|
||||
specificationKeyIsUnique<Head, Tail...>() && SpecificationKeysAreUnique<Tail...>::value> { };
|
||||
|
||||
template <SpecificationRole Role, ModelSpecification... Specifications>
|
||||
inline constexpr std::size_t specificationRoleCount =
|
||||
@@ -1122,7 +1161,7 @@ export namespace mean_field::models {
|
||||
|
||||
template <typename... Types>
|
||||
struct ModelTypeListScalarArity<ModelTypeList<Types...>>
|
||||
: std::integral_constant<std::size_t, (std::size_t{0} + ... + Types::scalarArity)> {};
|
||||
: std::integral_constant<std::size_t, (std::size_t{0} + ... + Types::scalarArity)> { };
|
||||
|
||||
template <typename Query, typename... Types>
|
||||
inline constexpr bool isOneOf = (std::same_as<Query, Types> || ...);
|
||||
@@ -1136,9 +1175,10 @@ export namespace mean_field::models {
|
||||
|
||||
template <ModelSpecification... CanonicalSpecifications, typename... Arguments>
|
||||
struct ArgumentsMatchCanonicalSpecifications<SpecificationSetStorage<CanonicalSpecifications...>, Arguments...>
|
||||
: std::bool_constant<sizeof...(CanonicalSpecifications) == sizeof...(Arguments) &&
|
||||
(isOneOf<std::remove_cvref_t<Arguments>, CanonicalSpecifications...> && ...) &&
|
||||
((typeCount<CanonicalSpecifications, Arguments...> == 1) && ...)> {};
|
||||
: std::bool_constant<
|
||||
sizeof...(CanonicalSpecifications) == sizeof...(Arguments) &&
|
||||
(isOneOf<std::remove_cvref_t<Arguments>, CanonicalSpecifications...> && ...) &&
|
||||
((typeCount<CanonicalSpecifications, Arguments...> == 1) && ...)> { };
|
||||
} // namespace detail
|
||||
|
||||
template <typename... Specifications>
|
||||
@@ -1150,12 +1190,12 @@ export namespace mean_field::models {
|
||||
concept ValidModelSpecificationPack =
|
||||
(ResolvedModelSpecification<std::remove_cvref_t<Specifications>> && ...) &&
|
||||
specificationKeysAreUnique<std::remove_cvref_t<Specifications>...> &&
|
||||
detail::specificationRoleCount<SpecificationRole::constitutive_law,
|
||||
std::remove_cvref_t<Specifications>...> == 1;
|
||||
detail::specificationRoleCount<SpecificationRole::constitutive_law, std::remove_cvref_t<Specifications>...> ==
|
||||
1;
|
||||
|
||||
template <typename... Specifications>
|
||||
requires(ModelSpecification<std::remove_cvref_t<Specifications>> && ...) &&
|
||||
specificationKeysAreUnique<std::remove_cvref_t<Specifications>...>
|
||||
specificationKeysAreUnique<std::remove_cvref_t<Specifications>...>
|
||||
using SpecificationSet = detail::CanonicalSpecificationSet<std::remove_cvref_t<Specifications>...>;
|
||||
|
||||
template <SpecificationRole Role, typename SpecificationSet>
|
||||
@@ -1203,12 +1243,13 @@ export namespace mean_field::models {
|
||||
[[nodiscard]] consteval SpecificationDescriptor specificationDescriptor() {
|
||||
using Contribution = SpecificationContribution<Specification>;
|
||||
|
||||
return {.name = SpecificationTraits<Specification>::name,
|
||||
.role = SpecificationTraits<Specification>::role,
|
||||
.key = SpecificationTraits<Specification>::key,
|
||||
.generatedValueArity = detail::ModelTypeListScalarArity<typename Contribution::GeneratedValues>::value,
|
||||
.generatedResidualArity =
|
||||
detail::ModelTypeListScalarArity<typename Contribution::GeneratedResiduals>::value};
|
||||
return {
|
||||
.name = SpecificationTraits<Specification>::name,
|
||||
.role = SpecificationTraits<Specification>::role,
|
||||
.key = SpecificationTraits<Specification>::key,
|
||||
.generatedValueArity = detail::ModelTypeListScalarArity<typename Contribution::GeneratedValues>::value,
|
||||
.generatedResidualArity = detail::ModelTypeListScalarArity<typename Contribution::GeneratedResiduals>::value
|
||||
};
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
@@ -1231,14 +1272,12 @@ export namespace mean_field::models {
|
||||
std::tuple<ArgumentTypes...> &&arguments,
|
||||
CanonicalArgumentsTag
|
||||
)
|
||||
: m_specifications(
|
||||
std::get<Specifications>(std::move(arguments))...
|
||||
) {
|
||||
: m_specifications(std::get<Specifications>(std::move(arguments))...) {
|
||||
}
|
||||
|
||||
public:
|
||||
using SpecificationTypes = SpecificationSetStorage<Specifications...>;
|
||||
using OperatorSignature = SpecificationOperatorSignature<SpecificationTypes>;
|
||||
using SpecificationTypes = SpecificationSetStorage<Specifications...>;
|
||||
using OperatorSignature = SpecificationOperatorSignature<SpecificationTypes>;
|
||||
|
||||
static constexpr bool symbolicallySquare = OperatorSignature::symbolicallySquare;
|
||||
|
||||
@@ -1249,22 +1288,27 @@ export namespace mean_field::models {
|
||||
symbolicallySquare && (SpecificationContribution<Specifications>::hasDeclarativeDefinition && ...);
|
||||
|
||||
template <typename... Arguments>
|
||||
requires ArgumentsMatchCanonicalSpecifications<SpecificationTypes, Arguments...>::value &&
|
||||
requires ArgumentsMatchCanonicalSpecifications<
|
||||
SpecificationTypes,
|
||||
Arguments...>::value &&
|
||||
std::constructible_from<
|
||||
std::tuple<std::remove_cvref_t<Arguments>...>,
|
||||
Arguments...> &&
|
||||
(std::constructible_from<Specifications, Specifications &&> && ...)
|
||||
(std::constructible_from<
|
||||
Specifications,
|
||||
Specifications &&> &&
|
||||
...)
|
||||
explicit SpecifiedModel(Arguments &&...arguments)
|
||||
: SpecifiedModel(
|
||||
std::tuple<std::remove_cvref_t<Arguments>...>{
|
||||
std::forward<Arguments>(arguments)...
|
||||
},
|
||||
std::tuple<std::remove_cvref_t<Arguments>...>{std::forward<Arguments>(arguments)...},
|
||||
CanonicalArgumentsTag{}
|
||||
) {
|
||||
}
|
||||
|
||||
template <ModelSpecification Specification>
|
||||
requires isOneOf<Specification, Specifications...>
|
||||
requires isOneOf<
|
||||
Specification,
|
||||
Specifications...>
|
||||
[[nodiscard]] const Specification &specification() const noexcept {
|
||||
return std::get<Specification>(m_specifications);
|
||||
}
|
||||
@@ -1282,10 +1326,11 @@ export namespace mean_field::models {
|
||||
runtimeDescriptors = [] {
|
||||
std::array<RuntimeSpecificationDescriptor, sizeof...(Specifications)> descriptors{};
|
||||
std::size_t index = 0;
|
||||
((descriptors[index] = {.specification = specificationDescriptor<Specifications>(),
|
||||
.canonicalIndex = index,
|
||||
.hasDeclarativeDefinition =
|
||||
SpecificationContribution<Specifications>::hasDeclarativeDefinition},
|
||||
((descriptors[index] =
|
||||
{.specification = specificationDescriptor<Specifications>(),
|
||||
.canonicalIndex = index,
|
||||
.hasDeclarativeDefinition =
|
||||
SpecificationContribution<Specifications>::hasDeclarativeDefinition},
|
||||
++index),
|
||||
...);
|
||||
return descriptors;
|
||||
@@ -1346,27 +1391,23 @@ export namespace mean_field::stellar {
|
||||
} // namespace state
|
||||
|
||||
namespace equation {
|
||||
using GravityGradientDefinition = models::stellar::equation::GravityGradientDefinition;
|
||||
using PoissonEquation = models::stellar::equation::PoissonEquation;
|
||||
using DensityClosure = models::stellar::equation::DensityClosure;
|
||||
using SurfaceShapeBalance = models::stellar::equation::SurfaceShapeBalance;
|
||||
using HydrostaticBalance = models::stellar::equation::HydrostaticBalance;
|
||||
using OwnConstraint = models::stellar::equation::OwnConstraint;
|
||||
using GravityGradientDefinition = models::stellar::equation::GravityGradientDefinition;
|
||||
using PoissonEquation = models::stellar::equation::PoissonEquation;
|
||||
using DensityClosure = models::stellar::equation::DensityClosure;
|
||||
using SurfaceShapeBalance = models::stellar::equation::SurfaceShapeBalance;
|
||||
using HydrostaticBalance = models::stellar::equation::HydrostaticBalance;
|
||||
using OwnConstraint = models::stellar::equation::OwnConstraint;
|
||||
|
||||
template <typename Specification>
|
||||
using ConstraintOf = models::stellar::equation::ConstraintOf<Specification>;
|
||||
template <typename Specification> using ConstraintOf = models::stellar::equation::ConstraintOf<Specification>;
|
||||
} // namespace equation
|
||||
|
||||
template <typename Equation, typename State>
|
||||
using Derivative = models::stellar::Derivative<Equation, State>;
|
||||
template <typename Equation, typename State> using Derivative = models::stellar::Derivative<Equation, State>;
|
||||
|
||||
template <typename... Quantities>
|
||||
using Reads = models::DependsOn<Quantities...>;
|
||||
template <typename... Quantities> using Reads = models::DependsOn<Quantities...>;
|
||||
|
||||
template <typename... Equations>
|
||||
using Changes = models::Affects<Equations...>;
|
||||
template <typename... Equations> using Changes = models::Affects<Equations...>;
|
||||
|
||||
using PhysicalScale = models::PhysicalScaleLaw;
|
||||
using PhysicalScale = models::PhysicalScaleLaw;
|
||||
|
||||
template <
|
||||
models::PhysicalScaleRepresentedQuantity TargetQuantity,
|
||||
@@ -1393,10 +1434,8 @@ export namespace mean_field::stellar {
|
||||
typename std::remove_cvref_t<Candidate>::GeneratedCoordinateQuantity;
|
||||
typename std::remove_cvref_t<Candidate>::ConstraintResidualQuantity;
|
||||
typename std::remove_cvref_t<Candidate>::TargetValue;
|
||||
requires models::GeneratedNormalizationDefinition<
|
||||
typename std::remove_cvref_t<Candidate>::Normalization>;
|
||||
requires models::GeneratedManifestDefinition<
|
||||
typename std::remove_cvref_t<Candidate>::Manifest>;
|
||||
requires models::GeneratedNormalizationDefinition<typename std::remove_cvref_t<Candidate>::Normalization>;
|
||||
requires models::GeneratedManifestDefinition<typename std::remove_cvref_t<Candidate>::Manifest>;
|
||||
requires std::remove_cvref_t<Candidate>::Normalization::available;
|
||||
requires std::remove_cvref_t<Candidate>::Manifest::available;
|
||||
requires std::remove_cvref_t<Candidate>::dimensionallyTyped;
|
||||
@@ -1407,31 +1446,43 @@ export namespace mean_field::integral {
|
||||
using FixedTotalMass = models::FixedTotalMass;
|
||||
using FixedAngularMomentum = models::FixedAngularMomentum;
|
||||
|
||||
template <typename Specification, models::FixedString Name, typename DependsOn = models::ModelTypeList<>,
|
||||
typename Affects = models::ModelTypeList<>,
|
||||
typename Normalization = models::UnavailableGeneratedNormalization,
|
||||
typename Manifest = models::UnavailableGeneratedManifest>
|
||||
template <
|
||||
typename Specification,
|
||||
models::FixedString Name,
|
||||
typename DependsOn = models::ModelTypeList<>,
|
||||
typename Affects = models::ModelTypeList<>,
|
||||
typename Normalization = models::UnavailableGeneratedNormalization,
|
||||
typename Manifest = models::UnavailableGeneratedManifest>
|
||||
using FixedIntegralWithMultiplier =
|
||||
models::FixedIntegralWithMultiplier<Specification, Name, DependsOn, Affects, Normalization, Manifest>;
|
||||
|
||||
template <typename Specification, models::FixedString Name, typename DependsOn = models::ModelTypeList<>,
|
||||
typename Affects = models::ModelTypeList<>,
|
||||
typename Normalization = models::UnavailableGeneratedNormalization,
|
||||
typename Manifest = models::UnavailableGeneratedManifest>
|
||||
template <
|
||||
typename Specification,
|
||||
models::FixedString Name,
|
||||
typename DependsOn = models::ModelTypeList<>,
|
||||
typename Affects = models::ModelTypeList<>,
|
||||
typename Normalization = models::UnavailableGeneratedNormalization,
|
||||
typename Manifest = models::UnavailableGeneratedManifest>
|
||||
using FixedWithMultiplier =
|
||||
FixedIntegralWithMultiplier<Specification, Name, DependsOn, Affects, Normalization, Manifest>;
|
||||
|
||||
template <typename Specification, models::FixedString Name, typename DependsOn = models::ModelTypeList<>,
|
||||
typename Affects = models::ModelTypeList<>,
|
||||
typename Normalization = models::UnavailableGeneratedNormalization,
|
||||
typename Manifest = models::UnavailableGeneratedManifest>
|
||||
template <
|
||||
typename Specification,
|
||||
models::FixedString Name,
|
||||
typename DependsOn = models::ModelTypeList<>,
|
||||
typename Affects = models::ModelTypeList<>,
|
||||
typename Normalization = models::UnavailableGeneratedNormalization,
|
||||
typename Manifest = models::UnavailableGeneratedManifest>
|
||||
using FixedIntegralWithPhysicalCoordinate =
|
||||
models::FixedIntegralWithPhysicalCoordinate<Specification, Name, DependsOn, Affects, Normalization, Manifest>;
|
||||
|
||||
template <typename Specification, models::FixedString Name, typename DependsOn = models::ModelTypeList<>,
|
||||
typename Affects = models::ModelTypeList<>,
|
||||
typename Normalization = models::UnavailableGeneratedNormalization,
|
||||
typename Manifest = models::UnavailableGeneratedManifest>
|
||||
template <
|
||||
typename Specification,
|
||||
models::FixedString Name,
|
||||
typename DependsOn = models::ModelTypeList<>,
|
||||
typename Affects = models::ModelTypeList<>,
|
||||
typename Normalization = models::UnavailableGeneratedNormalization,
|
||||
typename Manifest = models::UnavailableGeneratedManifest>
|
||||
using FixedWithPhysicalCoordinate =
|
||||
FixedIntegralWithPhysicalCoordinate<Specification, Name, DependsOn, Affects, Normalization, Manifest>;
|
||||
|
||||
@@ -1467,10 +1518,13 @@ export namespace mean_field::integral {
|
||||
export namespace mean_field::constraint {
|
||||
using FixedCentralDensity = models::FixedCentralDensity;
|
||||
|
||||
template <typename Specification, models::FixedString Name, typename DependsOn = models::ModelTypeList<>,
|
||||
typename Affects = models::ModelTypeList<>,
|
||||
typename Normalization = models::UnavailableGeneratedNormalization,
|
||||
typename Manifest = models::UnavailableGeneratedManifest>
|
||||
template <
|
||||
typename Specification,
|
||||
models::FixedString Name,
|
||||
typename DependsOn = models::ModelTypeList<>,
|
||||
typename Affects = models::ModelTypeList<>,
|
||||
typename Normalization = models::UnavailableGeneratedNormalization,
|
||||
typename Manifest = models::UnavailableGeneratedManifest>
|
||||
using PhaseCondition = models::PhaseCondition<Specification, Name, DependsOn, Affects, Normalization, Manifest>;
|
||||
|
||||
template <
|
||||
|
||||
@@ -25,10 +25,9 @@ export namespace mean_field::model {
|
||||
using EquationOfStateType =
|
||||
models::SpecificationForRoleT<models::SpecificationRole::constitutive_law, SpecificationTypes>;
|
||||
|
||||
static constexpr std::size_t specificationCount = sizeof...(CanonicalSpecifications);
|
||||
static constexpr bool symbolicallySquare = Storage::symbolicallySquare;
|
||||
static constexpr bool hasCompleteEquilibriumDeclaration =
|
||||
Storage::hasCompleteEquilibriumDeclaration;
|
||||
static constexpr std::size_t specificationCount = sizeof...(CanonicalSpecifications);
|
||||
static constexpr bool symbolicallySquare = Storage::symbolicallySquare;
|
||||
static constexpr bool hasCompleteEquilibriumDeclaration = Storage::hasCompleteEquilibriumDeclaration;
|
||||
|
||||
template <models::SpecificationRole Role>
|
||||
using SpecificationsForRole = models::SpecificationsForRoleT<Role, SpecificationTypes>;
|
||||
@@ -48,7 +47,9 @@ export namespace mean_field::model {
|
||||
models::HasUniqueSpecificationForRole<Role, SpecificationTypes>;
|
||||
|
||||
template <typename... Arguments>
|
||||
requires std::constructible_from<Storage, Arguments...>
|
||||
requires std::constructible_from<
|
||||
Storage,
|
||||
Arguments...>
|
||||
explicit StellarModel(Arguments &&...arguments) : m_specifications(std::forward<Arguments>(arguments)...) {
|
||||
}
|
||||
|
||||
@@ -62,8 +63,12 @@ export namespace mean_field::model {
|
||||
static constexpr bool containsSpecification = Storage::template containsSpecification<Specification>;
|
||||
|
||||
template <models::SpecificationRole Role>
|
||||
requires models::HasUniqueSpecificationForRole<Role, SpecificationTypes>
|
||||
[[nodiscard]] const models::SpecificationForRoleT<Role, SpecificationTypes> &
|
||||
requires models::HasUniqueSpecificationForRole<
|
||||
Role,
|
||||
SpecificationTypes>
|
||||
[[nodiscard]] const models::SpecificationForRoleT<
|
||||
Role,
|
||||
SpecificationTypes> &
|
||||
specificationForRole() const noexcept {
|
||||
using Specification = models::SpecificationForRoleT<Role, SpecificationTypes>;
|
||||
return specification<Specification>();
|
||||
@@ -74,8 +79,9 @@ export namespace mean_field::model {
|
||||
}
|
||||
|
||||
template <typename = void>
|
||||
requires models::HasUniqueSpecificationForRole<models::SpecificationRole::boundary_condition,
|
||||
SpecificationTypes>
|
||||
requires models::HasUniqueSpecificationForRole<
|
||||
models::SpecificationRole::boundary_condition,
|
||||
SpecificationTypes>
|
||||
[[nodiscard]] const auto &surfaceCondition() const noexcept {
|
||||
return specificationForRole<models::SpecificationRole::boundary_condition>();
|
||||
}
|
||||
@@ -95,20 +101,20 @@ export namespace mean_field::model {
|
||||
-> StellarModel<models::SpecificationSet<std::remove_cvref_t<Specifications>...>>;
|
||||
|
||||
namespace detail {
|
||||
template <typename Candidate, typename = void> struct IsStellarModel : std::false_type {};
|
||||
template <typename Candidate, typename = void> struct IsStellarModel : std::false_type { };
|
||||
|
||||
template <typename SpecificationSet>
|
||||
struct IsStellarModel<
|
||||
StellarModel<SpecificationSet>,
|
||||
std::void_t<typename StellarModel<SpecificationSet>::SpecificationTypes,
|
||||
typename StellarModel<SpecificationSet>::OperatorSignature,
|
||||
decltype(StellarModel<SpecificationSet>::specificationCount),
|
||||
decltype(StellarModel<SpecificationSet>::hasCompleteEquilibriumDeclaration)>>
|
||||
: std::true_type {};
|
||||
std::void_t<
|
||||
typename StellarModel<SpecificationSet>::SpecificationTypes,
|
||||
typename StellarModel<SpecificationSet>::OperatorSignature,
|
||||
decltype(StellarModel<SpecificationSet>::specificationCount),
|
||||
decltype(StellarModel<SpecificationSet>::hasCompleteEquilibriumDeclaration)>> : std::true_type { };
|
||||
|
||||
template <models::SpecificationRole Role, typename Candidate, bool = IsStellarModel<Candidate>::value>
|
||||
struct StellarModelRoleSelection {
|
||||
using Types = models::ModelTypeList<>;
|
||||
using Types = models::ModelTypeList<>;
|
||||
|
||||
static constexpr std::size_t count = 0;
|
||||
};
|
||||
|
||||
@@ -39,7 +39,7 @@ export namespace mean_field::normalization {
|
||||
}
|
||||
mfem::Vector state(stateSize);
|
||||
mfem::Vector residual(residualSize);
|
||||
state = 1.0;
|
||||
state = 1.0;
|
||||
residual = 1.0;
|
||||
return {std::move(state), std::move(residual)};
|
||||
}
|
||||
@@ -126,7 +126,7 @@ export namespace mean_field::normalization {
|
||||
}
|
||||
for (int index = 0; index < input.Size(); ++index) {
|
||||
const double value = input(index);
|
||||
output(index) = inverse ? value / factors(index) : factors(index) * value;
|
||||
output(index) = inverse ? value / factors(index) : factors(index) * value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,22 +156,19 @@ export namespace mean_field::normalization {
|
||||
* the policy and is found by ADL, so adding a normalization family does
|
||||
* not edit a library registry or switch. */
|
||||
template <typename Problem>
|
||||
concept RuntimePreparedNormalizationOperation =
|
||||
requires(const std::remove_cvref_t<Problem> &problem) {
|
||||
typename std::remove_cvref_t<Problem>::NormalizationPrescriptionType;
|
||||
typename std::remove_cvref_t<Problem>::FormType;
|
||||
requires RuntimePreparedNormalizationFor<
|
||||
typename std::remove_cvref_t<Problem>::NormalizationPrescriptionType,
|
||||
typename std::remove_cvref_t<Problem>::FormType>;
|
||||
{
|
||||
problem.GetNormalizationPrescription()
|
||||
} -> std::same_as<const typename std::remove_cvref_t<Problem>::NormalizationPrescriptionType &>;
|
||||
{
|
||||
prepareStellarNormalization(
|
||||
problem.GetNormalizationPrescription(),
|
||||
problem)
|
||||
} -> std::same_as<DiagonalNormalization>;
|
||||
};
|
||||
concept RuntimePreparedNormalizationOperation = requires(const std::remove_cvref_t<Problem> &problem) {
|
||||
typename std::remove_cvref_t<Problem>::NormalizationPrescriptionType;
|
||||
typename std::remove_cvref_t<Problem>::FormType;
|
||||
requires RuntimePreparedNormalizationFor<
|
||||
typename std::remove_cvref_t<Problem>::NormalizationPrescriptionType,
|
||||
typename std::remove_cvref_t<Problem>::FormType>;
|
||||
{
|
||||
problem.GetNormalizationPrescription()
|
||||
} -> std::same_as<const typename std::remove_cvref_t<Problem>::NormalizationPrescriptionType &>;
|
||||
{
|
||||
prepareStellarNormalization(problem.GetNormalizationPrescription(), problem)
|
||||
} -> std::same_as<DiagonalNormalization>;
|
||||
};
|
||||
|
||||
template <typename Form>
|
||||
requires utils::blocks::block_form_is_valid_v<Form>
|
||||
@@ -183,16 +180,14 @@ export namespace mean_field::normalization {
|
||||
m_residualFactors(layout.residual_offsets().Last()) {
|
||||
}
|
||||
|
||||
explicit DiagonalNormalizationBuilder(
|
||||
utils::blocks::form_layout<Form> &&
|
||||
) = delete;
|
||||
explicit DiagonalNormalizationBuilder(utils::blocks::form_layout<Form> &&) = delete;
|
||||
|
||||
explicit DiagonalNormalizationBuilder(
|
||||
const utils::blocks::form_layout<Form> &&
|
||||
) = delete;
|
||||
explicit DiagonalNormalizationBuilder(const utils::blocks::form_layout<Form> &&) = delete;
|
||||
|
||||
template <typename Block>
|
||||
requires utils::blocks::contains_type_v<Block, typename Form::value_blocks>
|
||||
requires utils::blocks::contains_type_v<
|
||||
Block,
|
||||
typename Form::value_blocks>
|
||||
void SetValueBlock(
|
||||
const double physicalScale,
|
||||
const mfem::Vector &primalGramDiagonal
|
||||
@@ -200,18 +195,17 @@ export namespace mean_field::normalization {
|
||||
constexpr int block = utils::blocks::type_index_v<Block, typename Form::value_blocks>;
|
||||
RequireUnassigned(m_valueAssigned[block], "value");
|
||||
AssignBlock(
|
||||
m_stateFactors,
|
||||
m_layout->value_offsets()[block],
|
||||
m_layout->value_offsets()[block + 1] - m_layout->value_offsets()[block],
|
||||
physicalScale,
|
||||
primalGramDiagonal,
|
||||
false
|
||||
m_stateFactors, m_layout->value_offsets()[block],
|
||||
m_layout->value_offsets()[block + 1] - m_layout->value_offsets()[block], physicalScale,
|
||||
primalGramDiagonal, false
|
||||
);
|
||||
m_valueAssigned[block] = true;
|
||||
}
|
||||
|
||||
template <typename Block>
|
||||
requires utils::blocks::contains_type_v<Block, typename Form::residual_blocks>
|
||||
requires utils::blocks::contains_type_v<
|
||||
Block,
|
||||
typename Form::residual_blocks>
|
||||
void SetResidualBlock(
|
||||
const double physicalScale,
|
||||
const mfem::Vector &primalGramDiagonal
|
||||
@@ -219,25 +213,26 @@ export namespace mean_field::normalization {
|
||||
constexpr int block = utils::blocks::type_index_v<Block, typename Form::residual_blocks>;
|
||||
RequireUnassigned(m_residualAssigned[block], "residual");
|
||||
AssignBlock(
|
||||
m_residualFactors,
|
||||
m_layout->residual_offsets()[block],
|
||||
m_layout->residual_offsets()[block + 1] - m_layout->residual_offsets()[block],
|
||||
physicalScale,
|
||||
primalGramDiagonal,
|
||||
true
|
||||
m_residualFactors, m_layout->residual_offsets()[block],
|
||||
m_layout->residual_offsets()[block + 1] - m_layout->residual_offsets()[block], physicalScale,
|
||||
primalGramDiagonal, true
|
||||
);
|
||||
m_residualAssigned[block] = true;
|
||||
}
|
||||
|
||||
template <typename Block>
|
||||
requires utils::blocks::contains_type_v<Block, typename Form::value_blocks>
|
||||
requires utils::blocks::contains_type_v<
|
||||
Block,
|
||||
typename Form::value_blocks>
|
||||
void SetValueGlobal(const double physicalScale) {
|
||||
constexpr int block = utils::blocks::type_index_v<Block, typename Form::value_blocks>;
|
||||
SetConstantMetricValueBlock<Block>(physicalScale, BlockSize(m_layout->value_offsets(), block));
|
||||
}
|
||||
|
||||
template <typename Block>
|
||||
requires utils::blocks::contains_type_v<Block, typename Form::residual_blocks>
|
||||
requires utils::blocks::contains_type_v<
|
||||
Block,
|
||||
typename Form::residual_blocks>
|
||||
void SetResidualGlobal(const double physicalScale) {
|
||||
constexpr int block = utils::blocks::type_index_v<Block, typename Form::residual_blocks>;
|
||||
mfem::Vector metric(BlockSize(m_layout->residual_offsets(), block));
|
||||
@@ -246,7 +241,9 @@ export namespace mean_field::normalization {
|
||||
}
|
||||
|
||||
template <typename Block>
|
||||
requires utils::blocks::contains_type_v<Block, typename Form::residual_blocks>
|
||||
requires utils::blocks::contains_type_v<
|
||||
Block,
|
||||
typename Form::residual_blocks>
|
||||
void SetHybridResidualBlock(
|
||||
const double physicalScale,
|
||||
const mfem::Vector &bulkPrimalGramDiagonal,
|
||||
@@ -254,7 +251,7 @@ export namespace mean_field::normalization {
|
||||
const double pointMetric = 1.0
|
||||
) {
|
||||
constexpr int block = utils::blocks::type_index_v<Block, typename Form::residual_blocks>;
|
||||
const int size = BlockSize(m_layout->residual_offsets(), block);
|
||||
const int size = BlockSize(m_layout->residual_offsets(), block);
|
||||
if (bulkPrimalGramDiagonal.Size() != size) {
|
||||
throw std::invalid_argument("The hybrid residual Gram diagonal has the wrong size.");
|
||||
}
|
||||
@@ -273,9 +270,7 @@ export namespace mean_field::normalization {
|
||||
|
||||
mfem::Vector metric(size);
|
||||
for (int row = 0; row < size; ++row) {
|
||||
metric(row) = isPointRow[static_cast<std::size_t>(row)]
|
||||
? pointMetric
|
||||
: bulkPrimalGramDiagonal(row);
|
||||
metric(row) = isPointRow[static_cast<std::size_t>(row)] ? pointMetric : bulkPrimalGramDiagonal(row);
|
||||
}
|
||||
SetResidualBlock<Block>(physicalScale, metric);
|
||||
}
|
||||
@@ -345,9 +340,7 @@ export namespace mean_field::normalization {
|
||||
const double metric = primalGramDiagonal(index);
|
||||
ValidateMetric(metric);
|
||||
const double rieszFactor = std::sqrt(metric);
|
||||
const double factor = dual
|
||||
? 1.0 / (physicalScale * rieszFactor)
|
||||
: rieszFactor / physicalScale;
|
||||
const double factor = dual ? 1.0 / (physicalScale * rieszFactor) : rieszFactor / physicalScale;
|
||||
if (!std::isfinite(factor) || factor <= 0.0) {
|
||||
throw std::overflow_error("A normalization factor is not finite and positive.");
|
||||
}
|
||||
@@ -368,7 +361,10 @@ export namespace mean_field::normalization {
|
||||
const mfem::Operator &physicalJacobian,
|
||||
const DiagonalNormalization &normalization
|
||||
)
|
||||
: mfem::Operator(normalization.ResidualSize(), normalization.StateSize()),
|
||||
: mfem::Operator(
|
||||
normalization.ResidualSize(),
|
||||
normalization.StateSize()
|
||||
),
|
||||
m_physicalJacobian(&physicalJacobian),
|
||||
m_normalization(&normalization),
|
||||
m_physicalDirection(normalization.StateSize()),
|
||||
@@ -421,7 +417,10 @@ export namespace mean_field::normalization {
|
||||
const mfem::Operator &physicalInverse,
|
||||
const DiagonalNormalization &normalization
|
||||
)
|
||||
: mfem::Operator(normalization.StateSize(), normalization.ResidualSize()),
|
||||
: mfem::Operator(
|
||||
normalization.StateSize(),
|
||||
normalization.ResidualSize()
|
||||
),
|
||||
m_physicalInverse(&physicalInverse),
|
||||
m_normalization(&normalization),
|
||||
m_physicalResidual(normalization.ResidualSize()),
|
||||
@@ -548,7 +547,7 @@ export namespace mean_field::normalization {
|
||||
const mfem::Operator &,
|
||||
const mfem::Operator &,
|
||||
const DiagonalNormalization &&
|
||||
) = delete;
|
||||
) = delete;
|
||||
|
||||
ScaledPreconditioner(const ScaledPreconditioner &) = delete;
|
||||
ScaledPreconditioner &operator=(const ScaledPreconditioner &) = delete;
|
||||
@@ -557,9 +556,7 @@ export namespace mean_field::normalization {
|
||||
|
||||
void SetOperator(const mfem::Operator &normalizedJacobian) override {
|
||||
if (normalizedJacobian.Width() != Width() || normalizedJacobian.Height() != Height()) {
|
||||
throw std::invalid_argument(
|
||||
"The scaled preconditioner received an incompatible normalized Jacobian."
|
||||
);
|
||||
throw std::invalid_argument("The scaled preconditioner received an incompatible normalized Jacobian.");
|
||||
}
|
||||
if (&normalizedJacobian != m_expectedNormalizedJacobian) {
|
||||
throw std::invalid_argument(
|
||||
|
||||
@@ -27,10 +27,10 @@ export namespace mean_field::normalization {
|
||||
|
||||
template <
|
||||
RieszGeometryPolicy GeometryPolicy = ReferenceGeometry,
|
||||
ReferenceScalePolicy ScalePolicy = FixedMassBranchReference>
|
||||
ReferenceScalePolicy ScalePolicy = FixedMassBranchReference>
|
||||
class PhysicalRieszDiagonal final : public NormalizationPrescriptionTag {
|
||||
public:
|
||||
using Geometry = GeometryPolicy;
|
||||
using Geometry = GeometryPolicy;
|
||||
using ScaleSource = ScalePolicy;
|
||||
|
||||
explicit PhysicalRieszDiagonal(
|
||||
@@ -62,8 +62,13 @@ export namespace mean_field::normalization {
|
||||
double m_gravitationalConstant;
|
||||
};
|
||||
|
||||
PhysicalRieszDiagonal(dimensions::LengthValue, double = 1.0)
|
||||
-> PhysicalRieszDiagonal<ReferenceGeometry, FixedMassBranchReference>;
|
||||
PhysicalRieszDiagonal(
|
||||
dimensions::LengthValue,
|
||||
double = 1.0
|
||||
)
|
||||
-> PhysicalRieszDiagonal<
|
||||
ReferenceGeometry,
|
||||
FixedMassBranchReference>;
|
||||
|
||||
template <typename Candidate> struct IsPhysicalRieszDiagonal : std::false_type { };
|
||||
|
||||
@@ -71,8 +76,7 @@ export namespace mean_field::normalization {
|
||||
struct IsPhysicalRieszDiagonal<PhysicalRieszDiagonal<Geometry, ScaleSource>> : std::true_type { };
|
||||
|
||||
template <typename Candidate>
|
||||
concept PhysicalRieszDiagonalPrescription =
|
||||
IsPhysicalRieszDiagonal<std::remove_cvref_t<Candidate>>::value;
|
||||
concept PhysicalRieszDiagonalPrescription = IsPhysicalRieszDiagonal<std::remove_cvref_t<Candidate>>::value;
|
||||
|
||||
struct StellarCharacteristicScales final {
|
||||
dimensions::MassValue mass;
|
||||
@@ -93,7 +97,7 @@ export namespace mean_field::normalization {
|
||||
const dimensions::LengthValue radius,
|
||||
const double gravitationalConstant = 1.0
|
||||
) {
|
||||
const double massValue = mass.value();
|
||||
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.");
|
||||
@@ -107,28 +111,19 @@ export namespace mean_field::normalization {
|
||||
);
|
||||
}
|
||||
|
||||
const double radiusSquared = radiusValue * radiusValue;
|
||||
const double radiusCubed = radiusSquared * radiusValue;
|
||||
const double density = massValue / radiusCubed;
|
||||
const double acceleration = gravitationalConstant * massValue / radiusSquared;
|
||||
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 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 force = gravitationalConstant * massValue * massValue / radiusSquared;
|
||||
|
||||
const double derived[] = {
|
||||
density,
|
||||
acceleration,
|
||||
inverseTimeSquared,
|
||||
specificEnergy,
|
||||
pressure,
|
||||
angularVelocity,
|
||||
angularMomentum,
|
||||
force
|
||||
};
|
||||
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.");
|
||||
@@ -136,36 +131,38 @@ export namespace mean_field::normalization {
|
||||
}
|
||||
|
||||
return {
|
||||
.mass = mass,
|
||||
.radius = radius,
|
||||
.mass = mass,
|
||||
.radius = radius,
|
||||
.gravitationalConstant = gravitationalConstant,
|
||||
.density = density,
|
||||
.acceleration = acceleration,
|
||||
.inverseTimeSquared = inverseTimeSquared,
|
||||
.specificEnergy = specificEnergy,
|
||||
.pressure = pressure,
|
||||
.angularVelocity = angularVelocity,
|
||||
.angularMomentum = angularMomentum,
|
||||
.force = force
|
||||
.density = density,
|
||||
.acceleration = acceleration,
|
||||
.inverseTimeSquared = inverseTimeSquared,
|
||||
.specificEnergy = specificEnergy,
|
||||
.pressure = pressure,
|
||||
.angularVelocity = angularVelocity,
|
||||
.angularMomentum = angularMomentum,
|
||||
.force = force
|
||||
};
|
||||
}
|
||||
|
||||
template <RieszGeometryPolicy Geometry, ReferenceScalePolicy ScaleSource, typename Model>
|
||||
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>() } -> 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 PhysicalRieszDiagonal<
|
||||
Geometry,
|
||||
ScaleSource> &prescription,
|
||||
const Model &model
|
||||
) {
|
||||
return deriveStellarCharacteristicScales(
|
||||
model.template specification<models::FixedTotalMass>().targetMass(),
|
||||
prescription.referenceRadius(),
|
||||
model.template specification<models::FixedTotalMass>().targetMass(), prescription.referenceRadius(),
|
||||
prescription.gravitationalConstant()
|
||||
);
|
||||
}
|
||||
@@ -179,14 +176,14 @@ export namespace mean_field::normalization {
|
||||
* normalization plan used by the discretization.
|
||||
*/
|
||||
template <models::RieszTopology Topology> struct DeclaredRieszTopology {
|
||||
static constexpr bool available = false;
|
||||
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; \
|
||||
#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);
|
||||
@@ -199,14 +196,14 @@ export namespace mean_field::normalization {
|
||||
#undef MEAN_FIELD_DECLARED_RIESZ_TOPOLOGY
|
||||
|
||||
template <models::PhysicalScaleLaw Scale> struct DeclaredPhysicalScale {
|
||||
static constexpr bool available = false;
|
||||
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; \
|
||||
#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);
|
||||
@@ -223,9 +220,8 @@ export namespace mean_field::normalization {
|
||||
|
||||
#undef MEAN_FIELD_DECLARED_PHYSICAL_SCALE
|
||||
|
||||
template <typename Declaration, typename = void>
|
||||
struct CompileDeclaredPhysicalRieszCoordinate {
|
||||
using Method = UnsupportedPhysicalRieszCoordinate;
|
||||
template <typename Declaration, typename = void> struct CompileDeclaredPhysicalRieszCoordinate {
|
||||
using Method = UnsupportedPhysicalRieszCoordinate;
|
||||
static constexpr bool registered = false;
|
||||
};
|
||||
|
||||
@@ -246,11 +242,11 @@ export namespace mean_field::normalization {
|
||||
static constexpr models::PhysicalScaleLaw declaredScale =
|
||||
static_cast<models::PhysicalScaleLaw>(Declaration::scale);
|
||||
using Topology = DeclaredRieszTopology<declaredTopology>;
|
||||
using Scale = DeclaredPhysicalScale<declaredScale>;
|
||||
using Scale = DeclaredPhysicalScale<declaredScale>;
|
||||
|
||||
public:
|
||||
static constexpr bool registered = static_cast<bool>(Declaration::available) &&
|
||||
Topology::available && Scale::available;
|
||||
static constexpr bool registered =
|
||||
static_cast<bool>(Declaration::available) && Topology::available && Scale::available;
|
||||
using Method = std::conditional_t<
|
||||
registered,
|
||||
PhysicalRieszCoordinate<Topology::value, Scale::value>,
|
||||
@@ -259,7 +255,7 @@ export namespace mean_field::normalization {
|
||||
|
||||
template <typename Generated, CoordinateKind Kind, typename = void>
|
||||
struct DeclaredGeneratedPhysicalRieszCoordinate {
|
||||
using Method = UnsupportedPhysicalRieszCoordinate;
|
||||
using Method = UnsupportedPhysicalRieszCoordinate;
|
||||
static constexpr bool registered = false;
|
||||
};
|
||||
|
||||
@@ -271,9 +267,8 @@ export namespace mean_field::normalization {
|
||||
typename Generated::SpecificationType,
|
||||
typename models::SpecificationContribution<
|
||||
typename Generated::SpecificationType>::Normalization::Value>>
|
||||
: CompileDeclaredPhysicalRieszCoordinate<
|
||||
typename models::SpecificationContribution<
|
||||
typename Generated::SpecificationType>::Normalization::Value> { };
|
||||
: CompileDeclaredPhysicalRieszCoordinate<typename models::SpecificationContribution<
|
||||
typename Generated::SpecificationType>::Normalization::Value> { };
|
||||
|
||||
template <typename Generated>
|
||||
struct DeclaredGeneratedPhysicalRieszCoordinate<
|
||||
@@ -283,12 +278,10 @@ export namespace mean_field::normalization {
|
||||
typename Generated::SpecificationType,
|
||||
typename models::SpecificationContribution<
|
||||
typename Generated::SpecificationType>::Normalization::Residual>>
|
||||
: CompileDeclaredPhysicalRieszCoordinate<
|
||||
typename models::SpecificationContribution<
|
||||
typename Generated::SpecificationType>::Normalization::Residual> { };
|
||||
: CompileDeclaredPhysicalRieszCoordinate<typename models::SpecificationContribution<
|
||||
typename Generated::SpecificationType>::Normalization::Residual> { };
|
||||
|
||||
template <typename GeneratedValues, typename GeneratedResiduals>
|
||||
struct GeneratedPhysicalRieszCoverage {
|
||||
template <typename GeneratedValues, typename GeneratedResiduals> struct GeneratedPhysicalRieszCoverage {
|
||||
static constexpr bool complete = false;
|
||||
};
|
||||
|
||||
@@ -297,16 +290,12 @@ export namespace mean_field::normalization {
|
||||
models::ModelTypeList<GeneratedValues...>,
|
||||
models::ModelTypeList<GeneratedResiduals...>> {
|
||||
static constexpr bool complete =
|
||||
(DeclaredGeneratedPhysicalRieszCoordinate<
|
||||
GeneratedValues,
|
||||
CoordinateKind::value>::registered && ...) &&
|
||||
(DeclaredGeneratedPhysicalRieszCoordinate<
|
||||
GeneratedResiduals,
|
||||
CoordinateKind::residual>::registered && ...);
|
||||
(DeclaredGeneratedPhysicalRieszCoordinate<GeneratedValues, CoordinateKind::value>::registered && ...) &&
|
||||
(DeclaredGeneratedPhysicalRieszCoordinate<GeneratedResiduals, CoordinateKind::residual>::registered &&
|
||||
...);
|
||||
};
|
||||
|
||||
template <typename Specification, typename = void>
|
||||
struct SpecificationPhysicalRieszCoverage {
|
||||
template <typename Specification, typename = void> struct SpecificationPhysicalRieszCoverage {
|
||||
static constexpr bool complete = false;
|
||||
};
|
||||
|
||||
@@ -355,29 +344,17 @@ export namespace mean_field::normalization {
|
||||
* 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>;
|
||||
};
|
||||
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;
|
||||
using GeneratedPhysicalRieszMethod = typename DeclaredGeneratedPhysicalRieszCoordinate<Generated, Kind>::Method;
|
||||
|
||||
template <typename Generated, CoordinateKind Kind, typename = void>
|
||||
struct GeneratedPhysicalRieszRuntimeCoordinate : std::false_type { };
|
||||
@@ -389,8 +366,7 @@ export namespace mean_field::normalization {
|
||||
std::void_t<decltype(GeneratedPhysicalRieszMethod<Generated, Kind>::topology)>>
|
||||
: std::bool_constant<
|
||||
DeclaredGeneratedPhysicalRieszCoordinate<Generated, Kind>::registered &&
|
||||
GeneratedPhysicalRieszMethod<Generated, Kind>::topology ==
|
||||
RieszTopology::global_scalar> { };
|
||||
GeneratedPhysicalRieszMethod<Generated, Kind>::topology == RieszTopology::global_scalar> { };
|
||||
|
||||
template <typename Specification, typename = void>
|
||||
struct SpecificationPhysicalRieszRuntimeCoverage : std::false_type { };
|
||||
@@ -420,21 +396,18 @@ export namespace mean_field::normalization {
|
||||
struct SpecificationSetPhysicalRieszRuntimeCoverage : std::false_type { };
|
||||
|
||||
template <models::ModelSpecification... Specifications>
|
||||
struct SpecificationSetPhysicalRieszRuntimeCoverage<
|
||||
models::detail::SpecificationSetStorage<Specifications...>>
|
||||
: std::bool_constant<
|
||||
(SpecificationPhysicalRieszRuntimeCoverage<Specifications>::value && ...)> { };
|
||||
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;
|
||||
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; \
|
||||
#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(
|
||||
@@ -490,12 +463,9 @@ export namespace mean_field::normalization {
|
||||
);
|
||||
#undef MEAN_FIELD_PHYSICAL_RIESZ_TRAIT
|
||||
|
||||
template <typename Block>
|
||||
[[nodiscard]] double physicalScale(
|
||||
const StellarCharacteristicScales &scales
|
||||
) {
|
||||
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;
|
||||
using Method = typename PhysicalRieszBlockTraits<Block>::Method;
|
||||
constexpr PhysicalScaleKind scale = Method::scale;
|
||||
if constexpr (scale == PhysicalScaleKind::dimensionless) {
|
||||
return 1.0;
|
||||
@@ -527,9 +497,7 @@ export namespace mean_field::normalization {
|
||||
template <typename Values, typename Residuals> struct MakePhysicalRieszPlan;
|
||||
|
||||
template <typename... Values, typename... Residuals>
|
||||
struct MakePhysicalRieszPlan<
|
||||
utils::blocks::type_list<Values...>,
|
||||
utils::blocks::type_list<Residuals...>> {
|
||||
struct MakePhysicalRieszPlan<utils::blocks::type_list<Values...>, utils::blocks::type_list<Residuals...>> {
|
||||
using Type = NormalizationPlan<
|
||||
CoordinateComponent<
|
||||
CoordinateKind::value,
|
||||
@@ -544,9 +512,8 @@ export namespace mean_field::normalization {
|
||||
|
||||
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;
|
||||
using PhysicalRieszNormalizationPlanFor =
|
||||
typename detail::MakePhysicalRieszPlan<typename Form::value_blocks, typename Form::residual_blocks>::Type;
|
||||
|
||||
/*
|
||||
* Public compile-time extension point for a normalization prescription.
|
||||
@@ -557,11 +524,10 @@ export namespace mean_field::normalization {
|
||||
* actually prepare.
|
||||
*/
|
||||
template <typename Prescription, typename Form> struct NormalizationCompilation {
|
||||
using Plan = NormalizationPlan<>;
|
||||
using Plan = NormalizationPlan<>;
|
||||
static constexpr bool registered = false;
|
||||
|
||||
template <typename PhysicalCore, typename SpecificationTypes>
|
||||
static constexpr bool runtimeAvailableFor = false;
|
||||
template <typename PhysicalCore, typename SpecificationTypes> static constexpr bool runtimeAvailableFor = false;
|
||||
};
|
||||
|
||||
/* Astronomy/numerics-facing package for a policy which prepares one
|
||||
@@ -571,7 +537,7 @@ export namespace mean_field::normalization {
|
||||
template <NormalizationPrescription Prescription, typename Form>
|
||||
requires utils::blocks::block_form_is_valid_v<Form>
|
||||
struct RuntimePreparedNormalizationCompilation {
|
||||
using Plan = RuntimePreparedNormalizationPlanFor<Prescription, Form>;
|
||||
using Plan = RuntimePreparedNormalizationPlanFor<Prescription, Form>;
|
||||
static constexpr bool registered = CompleteNormalizationFor<Plan, Form>;
|
||||
|
||||
template <typename PhysicalCore, typename SpecificationTypes>
|
||||
@@ -581,7 +547,7 @@ export namespace mean_field::normalization {
|
||||
template <typename Form>
|
||||
requires utils::blocks::block_form_is_valid_v<Form>
|
||||
struct NormalizationCompilation<Unnormalized, Form> {
|
||||
using Plan = IdentityNormalizationPlanFor<Form>;
|
||||
using Plan = IdentityNormalizationPlanFor<Form>;
|
||||
static constexpr bool registered = CompleteNormalizationFor<Plan, Form>;
|
||||
|
||||
template <typename PhysicalCore, typename SpecificationTypes>
|
||||
@@ -591,21 +557,18 @@ export namespace mean_field::normalization {
|
||||
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>;
|
||||
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;
|
||||
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<>;
|
||||
template <typename Prescription, typename Form, typename = void> struct NormalizationCompilationAudit {
|
||||
using Plan = NormalizationPlan<>;
|
||||
static constexpr bool registered = false;
|
||||
};
|
||||
|
||||
@@ -616,34 +579,25 @@ export namespace mean_field::normalization {
|
||||
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;
|
||||
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>>;
|
||||
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;
|
||||
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;
|
||||
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
|
||||
@@ -654,16 +608,10 @@ export namespace mean_field::normalization {
|
||||
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>> &&
|
||||
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>>>;
|
||||
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 <
|
||||
@@ -674,35 +622,22 @@ export namespace mean_field::normalization {
|
||||
typename = void>
|
||||
struct StellarNormalizationRuntimeAudit : std::false_type { };
|
||||
|
||||
template <
|
||||
typename Prescription,
|
||||
typename Form,
|
||||
typename PhysicalCore,
|
||||
typename SpecificationTypes>
|
||||
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::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> ||
|
||||
(std::same_as<Prescription, Unnormalized> || PhysicalRieszDiagonalPrescription<Prescription> ||
|
||||
RuntimePreparedNormalizationFor<Prescription, Form>) &&
|
||||
static_cast<bool>(NormalizationCompilation<
|
||||
Prescription,
|
||||
Form>::template runtimeAvailableFor<
|
||||
PhysicalCore,
|
||||
SpecificationTypes>)> { };
|
||||
static_cast<bool>(NormalizationCompilation<Prescription, Form>::
|
||||
template runtimeAvailableFor<PhysicalCore, SpecificationTypes>)> { };
|
||||
} // namespace detail
|
||||
|
||||
/*
|
||||
@@ -714,15 +649,10 @@ export namespace mean_field::normalization {
|
||||
* 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;
|
||||
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
|
||||
|
||||
@@ -11,10 +11,7 @@ export namespace mean_field::normalization {
|
||||
struct NormalizationPrescriptionTag { };
|
||||
|
||||
template <typename Candidate>
|
||||
concept NormalizationPrescription =
|
||||
std::derived_from<
|
||||
std::remove_cvref_t<Candidate>,
|
||||
NormalizationPrescriptionTag>;
|
||||
concept NormalizationPrescription = std::derived_from<std::remove_cvref_t<Candidate>, NormalizationPrescriptionTag>;
|
||||
|
||||
enum class CoordinateKind { value, residual };
|
||||
|
||||
@@ -50,44 +47,36 @@ export namespace mean_field::normalization {
|
||||
* numerical value of that factor. The owner type prevents one policy from
|
||||
* silently presenting another policy's runtime map as its own plan.
|
||||
*/
|
||||
template <NormalizationPrescription Prescription>
|
||||
struct RuntimePreparedCoordinate final {
|
||||
template <NormalizationPrescription Prescription> struct RuntimePreparedCoordinate final {
|
||||
using PrescriptionType = std::remove_cvref_t<Prescription>;
|
||||
};
|
||||
|
||||
template <RieszTopology Topology, PhysicalScaleKind Scale> struct PhysicalRieszCoordinate final {
|
||||
static constexpr RieszTopology topology = Topology;
|
||||
static constexpr PhysicalScaleKind scale = Scale;
|
||||
static constexpr RieszTopology topology = Topology;
|
||||
static constexpr PhysicalScaleKind scale = Scale;
|
||||
};
|
||||
|
||||
struct UnsupportedPhysicalRieszCoordinate final { };
|
||||
|
||||
template <typename Block> struct PhysicalRieszBlockTraits {
|
||||
using Method = UnsupportedPhysicalRieszCoordinate;
|
||||
static constexpr bool registered = false;
|
||||
using Method = UnsupportedPhysicalRieszCoordinate;
|
||||
static constexpr bool registered = false;
|
||||
};
|
||||
|
||||
template <CoordinateKind Kind, typename BlockList, typename MethodType>
|
||||
struct CoordinateComponent final {
|
||||
template <CoordinateKind Kind, typename BlockList, typename MethodType> struct CoordinateComponent final {
|
||||
using Blocks = BlockList;
|
||||
using Method = MethodType;
|
||||
static constexpr CoordinateKind kind = Kind;
|
||||
|
||||
using ValueBlocks = std::conditional_t<
|
||||
Kind == CoordinateKind::value,
|
||||
BlockList,
|
||||
utils::blocks::type_list<>>;
|
||||
using ResidualBlocks = std::conditional_t<
|
||||
Kind == CoordinateKind::residual,
|
||||
BlockList,
|
||||
utils::blocks::type_list<>>;
|
||||
using ValueBlocks = std::conditional_t<Kind == CoordinateKind::value, BlockList, utils::blocks::type_list<>>;
|
||||
using ResidualBlocks =
|
||||
std::conditional_t<Kind == CoordinateKind::residual, BlockList, utils::blocks::type_list<>>;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <typename Candidate> struct IsTypeList : std::false_type { };
|
||||
|
||||
template <typename... Types>
|
||||
struct IsTypeList<utils::blocks::type_list<Types...>> : std::true_type { };
|
||||
template <typename... Types> struct IsTypeList<utils::blocks::type_list<Types...>> : std::true_type { };
|
||||
|
||||
template <typename List, typename Base> struct IsUniqueDerivedBlockList : std::false_type { };
|
||||
|
||||
@@ -102,8 +91,7 @@ export namespace mean_field::normalization {
|
||||
template <> struct IsCoordinateMethod<IdentityCoordinate> : std::true_type { };
|
||||
|
||||
template <NormalizationPrescription Prescription>
|
||||
struct IsCoordinateMethod<RuntimePreparedCoordinate<Prescription>>
|
||||
: std::true_type { };
|
||||
struct IsCoordinateMethod<RuntimePreparedCoordinate<Prescription>> : std::true_type { };
|
||||
|
||||
template <RieszTopology Topology, PhysicalScaleKind Scale>
|
||||
struct IsCoordinateMethod<PhysicalRieszCoordinate<Topology, Scale>> : std::true_type { };
|
||||
@@ -121,10 +109,9 @@ export namespace mean_field::normalization {
|
||||
template <RieszTopology Topology, PhysicalScaleKind Scale, typename Block>
|
||||
struct MethodSupportsBlock<PhysicalRieszCoordinate<Topology, Scale>, Block>
|
||||
: std::bool_constant<
|
||||
PhysicalRieszBlockTraits<Block>::registered &&
|
||||
std::same_as<
|
||||
typename PhysicalRieszBlockTraits<Block>::Method,
|
||||
PhysicalRieszCoordinate<Topology, Scale>>> { };
|
||||
PhysicalRieszBlockTraits<Block>::registered && std::same_as<
|
||||
typename PhysicalRieszBlockTraits<Block>::Method,
|
||||
PhysicalRieszCoordinate<Topology, Scale>>> { };
|
||||
|
||||
template <typename Method, typename List> struct MethodSupportsEveryBlock : std::false_type { };
|
||||
|
||||
@@ -166,8 +153,7 @@ export namespace mean_field::normalization {
|
||||
}();
|
||||
|
||||
static constexpr bool hasCoherentCoordinateLists = [] {
|
||||
if constexpr (!hasValidKind || !IsTypeList<ValueBlocks>::value ||
|
||||
!IsTypeList<ResidualBlocks>::value) {
|
||||
if constexpr (!hasValidKind || !IsTypeList<ValueBlocks>::value || !IsTypeList<ResidualBlocks>::value) {
|
||||
return false;
|
||||
} else if constexpr (Candidate::kind == CoordinateKind::value) {
|
||||
return std::same_as<ValueBlocks, Blocks> &&
|
||||
@@ -182,8 +168,7 @@ export namespace mean_field::normalization {
|
||||
|
||||
static constexpr bool valid = hasValidKind && IsTypeList<Blocks>::value &&
|
||||
IsCoordinateMethod<Method>::value && hasValidBlockList &&
|
||||
hasCoherentCoordinateLists &&
|
||||
MethodSupportsEveryBlock<Method, Blocks>::value;
|
||||
hasCoherentCoordinateLists && MethodSupportsEveryBlock<Method, Blocks>::value;
|
||||
};
|
||||
|
||||
template <typename... Lists> struct Concatenate;
|
||||
@@ -205,23 +190,18 @@ export namespace mean_field::normalization {
|
||||
|
||||
template <typename List, typename Type> struct Append;
|
||||
|
||||
template <typename... Types, typename Appended>
|
||||
struct Append<utils::blocks::type_list<Types...>, Appended> {
|
||||
template <typename... Types, typename Appended> struct Append<utils::blocks::type_list<Types...>, Appended> {
|
||||
using Type = utils::blocks::type_list<Types..., Appended>;
|
||||
};
|
||||
|
||||
template <typename List, typename Type> using AppendT = typename Append<List, Type>::Type;
|
||||
|
||||
template <typename List, typename Type>
|
||||
using AppendUniqueT = std::conditional_t<
|
||||
utils::blocks::contains_type_v<Type, List>,
|
||||
List,
|
||||
AppendT<List, Type>>;
|
||||
using AppendUniqueT = std::conditional_t<utils::blocks::contains_type_v<Type, List>, List, AppendT<List, Type>>;
|
||||
|
||||
template <typename Source, typename Excluded> struct ListDifference;
|
||||
|
||||
template <typename Excluded>
|
||||
struct ListDifference<utils::blocks::type_list<>, Excluded> {
|
||||
template <typename Excluded> struct ListDifference<utils::blocks::type_list<>, Excluded> {
|
||||
using Type = utils::blocks::type_list<>;
|
||||
};
|
||||
|
||||
@@ -260,10 +240,7 @@ export namespace mean_field::normalization {
|
||||
};
|
||||
|
||||
template <typename List>
|
||||
using RepeatedTypesT = typename CollectRepeatedTypes<
|
||||
List,
|
||||
List,
|
||||
utils::blocks::type_list<>>::Type;
|
||||
using RepeatedTypesT = typename CollectRepeatedTypes<List, List, utils::blocks::type_list<>>::Type;
|
||||
|
||||
template <typename Candidate, typename = void> struct PlanTraits {
|
||||
static constexpr bool valid = false;
|
||||
@@ -274,23 +251,20 @@ export namespace mean_field::normalization {
|
||||
concept NormalizationComponent = detail::ComponentTraits<std::remove_cvref_t<Candidate>>::valid;
|
||||
|
||||
template <typename... Components> struct NormalizationPlan final {
|
||||
using ComponentTypes = utils::blocks::type_list<Components...>;
|
||||
using ValueBlocks = detail::ConcatenateT<typename Components::ValueBlocks...>;
|
||||
using ResidualBlocks = detail::ConcatenateT<typename Components::ResidualBlocks...>;
|
||||
using ComponentTypes = utils::blocks::type_list<Components...>;
|
||||
using ValueBlocks = detail::ConcatenateT<typename Components::ValueBlocks...>;
|
||||
using ResidualBlocks = detail::ConcatenateT<typename Components::ResidualBlocks...>;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <typename... Components>
|
||||
struct PlanTraits<NormalizationPlan<Components...>> {
|
||||
template <typename... Components> struct PlanTraits<NormalizationPlan<Components...>> {
|
||||
static constexpr bool valid = (ComponentTraits<Components>::valid && ...);
|
||||
};
|
||||
|
||||
template <typename Values, typename Residuals> struct MakeIdentityPlan;
|
||||
|
||||
template <typename... Values, typename... Residuals>
|
||||
struct MakeIdentityPlan<
|
||||
utils::blocks::type_list<Values...>,
|
||||
utils::blocks::type_list<Residuals...>> {
|
||||
struct MakeIdentityPlan<utils::blocks::type_list<Values...>, utils::blocks::type_list<Residuals...>> {
|
||||
using Type = NormalizationPlan<
|
||||
CoordinateComponent<CoordinateKind::value, utils::blocks::type_list<Values>, IdentityCoordinate>...,
|
||||
CoordinateComponent<
|
||||
@@ -299,30 +273,18 @@ export namespace mean_field::normalization {
|
||||
IdentityCoordinate>...>;
|
||||
};
|
||||
|
||||
template <
|
||||
NormalizationPrescription Prescription,
|
||||
typename Values,
|
||||
typename Residuals>
|
||||
template <NormalizationPrescription Prescription, typename Values, typename Residuals>
|
||||
struct MakeRuntimePreparedPlan;
|
||||
|
||||
template <
|
||||
NormalizationPrescription Prescription,
|
||||
typename... Values,
|
||||
typename... Residuals>
|
||||
template <NormalizationPrescription Prescription, typename... Values, typename... Residuals>
|
||||
struct MakeRuntimePreparedPlan<
|
||||
Prescription,
|
||||
utils::blocks::type_list<Values...>,
|
||||
utils::blocks::type_list<Residuals...>> {
|
||||
using Method = RuntimePreparedCoordinate<Prescription>;
|
||||
using Type = NormalizationPlan<
|
||||
CoordinateComponent<
|
||||
CoordinateKind::value,
|
||||
utils::blocks::type_list<Values>,
|
||||
Method>...,
|
||||
CoordinateComponent<
|
||||
CoordinateKind::residual,
|
||||
utils::blocks::type_list<Residuals>,
|
||||
Method>...>;
|
||||
using Type = NormalizationPlan<
|
||||
CoordinateComponent<CoordinateKind::value, utils::blocks::type_list<Values>, Method>...,
|
||||
CoordinateComponent<CoordinateKind::residual, utils::blocks::type_list<Residuals>, Method>...>;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
@@ -331,46 +293,43 @@ export namespace mean_field::normalization {
|
||||
|
||||
template <typename Form>
|
||||
requires utils::blocks::block_form_is_valid_v<Form>
|
||||
using IdentityNormalizationPlanFor = typename detail::MakeIdentityPlan<
|
||||
typename Form::value_blocks,
|
||||
typename Form::residual_blocks>::Type;
|
||||
using IdentityNormalizationPlanFor =
|
||||
typename detail::MakeIdentityPlan<typename Form::value_blocks, typename Form::residual_blocks>::Type;
|
||||
|
||||
template <NormalizationPrescription Prescription, typename Form>
|
||||
requires utils::blocks::block_form_is_valid_v<Form>
|
||||
using RuntimePreparedNormalizationPlanFor =
|
||||
typename detail::MakeRuntimePreparedPlan<
|
||||
std::remove_cvref_t<Prescription>,
|
||||
typename Form::value_blocks,
|
||||
typename Form::residual_blocks>::Type;
|
||||
using RuntimePreparedNormalizationPlanFor = typename detail::MakeRuntimePreparedPlan<
|
||||
std::remove_cvref_t<Prescription>,
|
||||
typename Form::value_blocks,
|
||||
typename Form::residual_blocks>::Type;
|
||||
|
||||
template <typename Form, typename Plan>
|
||||
requires utils::blocks::block_form_is_valid_v<Form>
|
||||
struct NormalizationCoverage final {
|
||||
using DeclaredValueBlocks = typename Plan::ValueBlocks;
|
||||
using DeclaredValueBlocks = typename Plan::ValueBlocks;
|
||||
using DeclaredResidualBlocks = typename Plan::ResidualBlocks;
|
||||
|
||||
using MissingValueBlocks = detail::ListDifferenceT<typename Form::value_blocks, DeclaredValueBlocks>;
|
||||
using UnexpectedValueBlocks = detail::ListDifferenceT<DeclaredValueBlocks, typename Form::value_blocks>;
|
||||
using RepeatedValueBlocks = detail::RepeatedTypesT<DeclaredValueBlocks>;
|
||||
using MissingValueBlocks = detail::ListDifferenceT<typename Form::value_blocks, DeclaredValueBlocks>;
|
||||
using UnexpectedValueBlocks = detail::ListDifferenceT<DeclaredValueBlocks, typename Form::value_blocks>;
|
||||
using RepeatedValueBlocks = detail::RepeatedTypesT<DeclaredValueBlocks>;
|
||||
|
||||
using MissingResidualBlocks = detail::ListDifferenceT<typename Form::residual_blocks, DeclaredResidualBlocks>;
|
||||
using UnexpectedResidualBlocks = detail::ListDifferenceT<DeclaredResidualBlocks, typename Form::residual_blocks>;
|
||||
using RepeatedResidualBlocks = detail::RepeatedTypesT<DeclaredResidualBlocks>;
|
||||
using MissingResidualBlocks = detail::ListDifferenceT<typename Form::residual_blocks, DeclaredResidualBlocks>;
|
||||
using UnexpectedResidualBlocks =
|
||||
detail::ListDifferenceT<DeclaredResidualBlocks, typename Form::residual_blocks>;
|
||||
using RepeatedResidualBlocks = detail::RepeatedTypesT<DeclaredResidualBlocks>;
|
||||
|
||||
static constexpr bool hasEveryValueBlock = MissingValueBlocks::size == 0;
|
||||
static constexpr bool hasOnlyValueBlocks = UnexpectedValueBlocks::size == 0;
|
||||
static constexpr bool hasUniqueValueOwners = RepeatedValueBlocks::size == 0;
|
||||
static constexpr bool hasEveryResidualBlock = MissingResidualBlocks::size == 0;
|
||||
static constexpr bool hasOnlyResidualBlocks = UnexpectedResidualBlocks::size == 0;
|
||||
static constexpr bool hasEveryValueBlock = MissingValueBlocks::size == 0;
|
||||
static constexpr bool hasOnlyValueBlocks = UnexpectedValueBlocks::size == 0;
|
||||
static constexpr bool hasUniqueValueOwners = RepeatedValueBlocks::size == 0;
|
||||
static constexpr bool hasEveryResidualBlock = MissingResidualBlocks::size == 0;
|
||||
static constexpr bool hasOnlyResidualBlocks = UnexpectedResidualBlocks::size == 0;
|
||||
static constexpr bool hasUniqueResidualOwners = RepeatedResidualBlocks::size == 0;
|
||||
|
||||
static constexpr bool complete = hasEveryValueBlock && hasOnlyValueBlocks && hasUniqueValueOwners &&
|
||||
hasEveryResidualBlock && hasOnlyResidualBlocks &&
|
||||
hasUniqueResidualOwners;
|
||||
hasEveryResidualBlock && hasOnlyResidualBlocks && hasUniqueResidualOwners;
|
||||
};
|
||||
|
||||
template <typename Plan, typename Form>
|
||||
concept CompleteNormalizationFor = utils::blocks::block_form_is_valid_v<Form> &&
|
||||
NormalizationPlanType<Plan> &&
|
||||
concept CompleteNormalizationFor = utils::blocks::block_form_is_valid_v<Form> && NormalizationPlanType<Plan> &&
|
||||
NormalizationCoverage<Form, std::remove_cvref_t<Plan>>::complete;
|
||||
} // namespace mean_field::normalization
|
||||
|
||||
@@ -2,6 +2,7 @@ module;
|
||||
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
@@ -61,7 +62,7 @@ namespace mean_field::normalization::detail {
|
||||
const field::ScalarBoundaryDofMap &surfaceMap
|
||||
) {
|
||||
mfem::Array<int> marker(finiteElements.mesh->bdr_attributes.Max());
|
||||
marker = 0;
|
||||
marker = 0;
|
||||
constexpr int attribute = DomainSchema::template boundary_attribute<utils::domain::StellarSurface>();
|
||||
if (attribute <= 0 || attribute > marker.Size()) {
|
||||
throw std::invalid_argument("The reference mesh does not contain the stellar-surface boundary.");
|
||||
@@ -107,25 +108,19 @@ export namespace mean_field::normalization {
|
||||
* this layer never names a concrete integral or phase constraint.
|
||||
*/
|
||||
namespace detail {
|
||||
template <typename Block>
|
||||
using PhysicalRieszMethodFor = typename PhysicalRieszBlockTraits<Block>::Method;
|
||||
template <typename Block> using PhysicalRieszMethodFor = typename PhysicalRieszBlockTraits<Block>::Method;
|
||||
|
||||
template <typename Block, typename = void>
|
||||
struct IsGlobalGeneratedValueNormalization : std::false_type { };
|
||||
template <typename Block, typename = void> struct IsGlobalGeneratedValueNormalization : std::false_type { };
|
||||
|
||||
template <typename Generated>
|
||||
struct IsGlobalGeneratedValueNormalization<
|
||||
utils::blocks::generated_value_block<Generated>,
|
||||
std::void_t<
|
||||
decltype(PhysicalRieszMethodFor<
|
||||
utils::blocks::generated_value_block<Generated>>::topology),
|
||||
decltype(PhysicalRieszMethodFor<
|
||||
utils::blocks::generated_value_block<Generated>>::scale)>>
|
||||
decltype(PhysicalRieszMethodFor<utils::blocks::generated_value_block<Generated>>::topology),
|
||||
decltype(PhysicalRieszMethodFor<utils::blocks::generated_value_block<Generated>>::scale)>>
|
||||
: std::bool_constant<
|
||||
PhysicalRieszBlockTraits<
|
||||
utils::blocks::generated_value_block<Generated>>::registered &&
|
||||
PhysicalRieszMethodFor<
|
||||
utils::blocks::generated_value_block<Generated>>::topology ==
|
||||
PhysicalRieszBlockTraits<utils::blocks::generated_value_block<Generated>>::registered &&
|
||||
PhysicalRieszMethodFor<utils::blocks::generated_value_block<Generated>>::topology ==
|
||||
RieszTopology::global_scalar> { };
|
||||
|
||||
template <typename Blocks, typename Specification>
|
||||
@@ -139,32 +134,26 @@ export namespace mean_field::normalization {
|
||||
Generated,
|
||||
Specification,
|
||||
std::void_t<typename Generated::SpecificationType>>
|
||||
: std::bool_constant<
|
||||
std::same_as<typename Generated::SpecificationType, Specification>> { };
|
||||
: std::bool_constant<std::same_as<typename Generated::SpecificationType, Specification>> { };
|
||||
|
||||
template <typename Specification, typename... Generated>
|
||||
struct GeneratedValueBlocksBelongToSpecification<
|
||||
utils::blocks::type_list<utils::blocks::generated_value_block<Generated>...>,
|
||||
Specification>
|
||||
: std::bool_constant<
|
||||
(GeneratedCoordinateBelongsToSpecification<Generated, Specification>::value && ...)> { };
|
||||
: std::bool_constant<(GeneratedCoordinateBelongsToSpecification<Generated, Specification>::value && ...)> {
|
||||
};
|
||||
|
||||
template <typename Block, typename = void>
|
||||
struct IsGlobalGeneratedResidualNormalization : std::false_type { };
|
||||
template <typename Block, typename = void> struct IsGlobalGeneratedResidualNormalization : std::false_type { };
|
||||
|
||||
template <typename Generated>
|
||||
struct IsGlobalGeneratedResidualNormalization<
|
||||
utils::blocks::generated_residual_block<Generated>,
|
||||
std::void_t<
|
||||
decltype(PhysicalRieszMethodFor<
|
||||
utils::blocks::generated_residual_block<Generated>>::topology),
|
||||
decltype(PhysicalRieszMethodFor<
|
||||
utils::blocks::generated_residual_block<Generated>>::scale)>>
|
||||
decltype(PhysicalRieszMethodFor<utils::blocks::generated_residual_block<Generated>>::topology),
|
||||
decltype(PhysicalRieszMethodFor<utils::blocks::generated_residual_block<Generated>>::scale)>>
|
||||
: std::bool_constant<
|
||||
PhysicalRieszBlockTraits<
|
||||
utils::blocks::generated_residual_block<Generated>>::registered &&
|
||||
PhysicalRieszMethodFor<
|
||||
utils::blocks::generated_residual_block<Generated>>::topology ==
|
||||
PhysicalRieszBlockTraits<utils::blocks::generated_residual_block<Generated>>::registered &&
|
||||
PhysicalRieszMethodFor<utils::blocks::generated_residual_block<Generated>>::topology ==
|
||||
RieszTopology::global_scalar> { };
|
||||
|
||||
template <typename Blocks, typename Specification>
|
||||
@@ -174,14 +163,13 @@ export namespace mean_field::normalization {
|
||||
struct GeneratedResidualBlocksBelongToSpecification<
|
||||
utils::blocks::type_list<utils::blocks::generated_residual_block<Generated>...>,
|
||||
Specification>
|
||||
: std::bool_constant<
|
||||
(GeneratedCoordinateBelongsToSpecification<Generated, Specification>::value && ...)> { };
|
||||
: std::bool_constant<(GeneratedCoordinateBelongsToSpecification<Generated, Specification>::value && ...)> {
|
||||
};
|
||||
|
||||
template <typename Blocks> struct PrepareGeneratedValueNormalizations {
|
||||
static constexpr bool registered = false;
|
||||
static constexpr bool registered = false;
|
||||
|
||||
template <typename Form>
|
||||
static constexpr bool completeFor = false;
|
||||
template <typename Form> static constexpr bool completeFor = false;
|
||||
|
||||
template <typename Form>
|
||||
static void Apply(
|
||||
@@ -192,14 +180,12 @@ export namespace mean_field::normalization {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... Blocks>
|
||||
struct PrepareGeneratedValueNormalizations<utils::blocks::type_list<Blocks...>> {
|
||||
static constexpr bool registered =
|
||||
(IsGlobalGeneratedValueNormalization<Blocks>::value && ...);
|
||||
template <typename... Blocks> struct PrepareGeneratedValueNormalizations<utils::blocks::type_list<Blocks...>> {
|
||||
static constexpr bool registered = (IsGlobalGeneratedValueNormalization<Blocks>::value && ...);
|
||||
|
||||
template <typename Form>
|
||||
static constexpr bool completeFor = registered &&
|
||||
utils::blocks::block_form_is_valid_v<Form> &&
|
||||
static constexpr bool completeFor =
|
||||
registered && utils::blocks::block_form_is_valid_v<Form> &&
|
||||
(utils::blocks::contains_type_v<Blocks, typename Form::value_blocks> && ...);
|
||||
|
||||
template <typename Form>
|
||||
@@ -220,10 +206,9 @@ export namespace mean_field::normalization {
|
||||
};
|
||||
|
||||
template <typename Blocks> struct PrepareGeneratedResidualNormalizations {
|
||||
static constexpr bool registered = false;
|
||||
static constexpr bool registered = false;
|
||||
|
||||
template <typename Form>
|
||||
static constexpr bool completeFor = false;
|
||||
template <typename Form> static constexpr bool completeFor = false;
|
||||
|
||||
template <typename Form>
|
||||
static void Apply(
|
||||
@@ -236,12 +221,11 @@ export namespace mean_field::normalization {
|
||||
|
||||
template <typename... Blocks>
|
||||
struct PrepareGeneratedResidualNormalizations<utils::blocks::type_list<Blocks...>> {
|
||||
static constexpr bool registered =
|
||||
(IsGlobalGeneratedResidualNormalization<Blocks>::value && ...);
|
||||
static constexpr bool registered = (IsGlobalGeneratedResidualNormalization<Blocks>::value && ...);
|
||||
|
||||
template <typename Form>
|
||||
static constexpr bool completeFor = registered &&
|
||||
utils::blocks::block_form_is_valid_v<Form> &&
|
||||
static constexpr bool completeFor =
|
||||
registered && utils::blocks::block_form_is_valid_v<Form> &&
|
||||
(utils::blocks::contains_type_v<Blocks, typename Form::residual_blocks> && ...);
|
||||
|
||||
template <typename Form>
|
||||
@@ -261,15 +245,13 @@ export namespace mean_field::normalization {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Specification, typename = void>
|
||||
struct CompileStellarSpecificationNormalization {
|
||||
using ValuePreparation = PrepareGeneratedValueNormalizations<void>;
|
||||
using ResidualPreparation = PrepareGeneratedResidualNormalizations<void>;
|
||||
template <typename Specification, typename = void> struct CompileStellarSpecificationNormalization {
|
||||
using ValuePreparation = PrepareGeneratedValueNormalizations<void>;
|
||||
using ResidualPreparation = PrepareGeneratedResidualNormalizations<void>;
|
||||
|
||||
static constexpr bool registered = false;
|
||||
static constexpr bool registered = false;
|
||||
|
||||
template <typename Form>
|
||||
static constexpr bool completeFor = false;
|
||||
template <typename Form> static constexpr bool completeFor = false;
|
||||
|
||||
template <typename Form>
|
||||
static void Apply(
|
||||
@@ -277,8 +259,7 @@ export namespace mean_field::normalization {
|
||||
const StellarCharacteristicScales &
|
||||
) {
|
||||
static_assert(
|
||||
completeFor<Form>,
|
||||
"The specification has no complete generated-coordinate normalization."
|
||||
completeFor<Form>, "The specification has no complete generated-coordinate normalization."
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -287,32 +268,27 @@ export namespace mean_field::normalization {
|
||||
struct CompileStellarSpecificationNormalization<
|
||||
Specification,
|
||||
std::void_t<
|
||||
typename operators::StellarEquilibriumSpecificationCompilation<
|
||||
Specification>::GeneratedValueBlocks,
|
||||
typename operators::StellarEquilibriumSpecificationCompilation<Specification>::GeneratedValueBlocks,
|
||||
typename operators::StellarEquilibriumSpecificationCompilation<
|
||||
Specification>::GeneratedResidualBlocks>> {
|
||||
using OperatorCompilation =
|
||||
operators::StellarEquilibriumSpecificationCompilation<Specification>;
|
||||
using ValuePreparation = PrepareGeneratedValueNormalizations<
|
||||
typename OperatorCompilation::GeneratedValueBlocks>;
|
||||
using ResidualPreparation = PrepareGeneratedResidualNormalizations<
|
||||
typename OperatorCompilation::GeneratedResidualBlocks>;
|
||||
using OperatorCompilation = operators::StellarEquilibriumSpecificationCompilation<Specification>;
|
||||
using ValuePreparation =
|
||||
PrepareGeneratedValueNormalizations<typename OperatorCompilation::GeneratedValueBlocks>;
|
||||
using ResidualPreparation =
|
||||
PrepareGeneratedResidualNormalizations<typename OperatorCompilation::GeneratedResidualBlocks>;
|
||||
|
||||
static constexpr bool registered = OperatorCompilation::complete &&
|
||||
models::CompleteGeneratedNormalizationFor<
|
||||
Specification> &&
|
||||
models::CompleteGeneratedNormalizationFor<Specification> &&
|
||||
GeneratedValueBlocksBelongToSpecification<
|
||||
typename OperatorCompilation::GeneratedValueBlocks,
|
||||
Specification>::value &&
|
||||
GeneratedResidualBlocksBelongToSpecification<
|
||||
typename OperatorCompilation::GeneratedResidualBlocks,
|
||||
Specification>::value &&
|
||||
ValuePreparation::registered &&
|
||||
ResidualPreparation::registered;
|
||||
ValuePreparation::registered && ResidualPreparation::registered;
|
||||
|
||||
template <typename Form>
|
||||
static constexpr bool completeFor = registered &&
|
||||
ValuePreparation::template completeFor<Form> &&
|
||||
static constexpr bool completeFor = registered && ValuePreparation::template completeFor<Form> &&
|
||||
ResidualPreparation::template completeFor<Form>;
|
||||
|
||||
template <typename Form>
|
||||
@@ -366,9 +342,8 @@ export namespace mean_field::normalization {
|
||||
Model,
|
||||
Form,
|
||||
std::void_t<typename std::remove_cvref_t<Model>::SpecificationTypes>>
|
||||
: std::bool_constant<
|
||||
PrepareSpecificationNormalizations<
|
||||
typename std::remove_cvref_t<Model>::SpecificationTypes>::template completeFor<Form>> { };
|
||||
: std::bool_constant<PrepareSpecificationNormalizations<
|
||||
typename std::remove_cvref_t<Model>::SpecificationTypes>::template completeFor<Form>> { };
|
||||
} // namespace detail
|
||||
|
||||
template <typename Specification>
|
||||
@@ -400,9 +375,7 @@ export namespace mean_field::normalization {
|
||||
|
||||
template <typename Model, typename Form>
|
||||
concept CompleteStellarNormalizationFor =
|
||||
detail::StellarModelNormalizationCoverage<
|
||||
std::remove_cvref_t<Model>,
|
||||
std::remove_cvref_t<Form>>::value;
|
||||
detail::StellarModelNormalizationCoverage<std::remove_cvref_t<Model>, std::remove_cvref_t<Form>>::value;
|
||||
|
||||
/*
|
||||
* Physical Riesz preparation is an optional capability of a physical
|
||||
@@ -418,8 +391,7 @@ export namespace mean_field::normalization {
|
||||
|
||||
template <typename Problem>
|
||||
concept PhysicalRieszStellarEquilibriumProblem =
|
||||
equilibrium::DiscretizedStellarEquilibriumProblem<std::remove_cvref_t<Problem>> &&
|
||||
requires {
|
||||
equilibrium::DiscretizedStellarEquilibriumProblem<std::remove_cvref_t<Problem>> && requires {
|
||||
typename std::remove_cvref_t<Problem>::ModelType;
|
||||
typename std::remove_cvref_t<Problem>::FormType;
|
||||
typename std::remove_cvref_t<Problem>::PhysicalCoreType;
|
||||
@@ -430,8 +402,7 @@ export namespace mean_field::normalization {
|
||||
typename std::remove_cvref_t<Problem>::NormalizationPrescriptionType,
|
||||
typename std::remove_cvref_t<Problem>::FormType>;
|
||||
requires CompleteStellarNormalizationFor<
|
||||
typename std::remove_cvref_t<Problem>::ModelType,
|
||||
typename std::remove_cvref_t<Problem>::FormType>;
|
||||
typename std::remove_cvref_t<Problem>::ModelType, typename std::remove_cvref_t<Problem>::FormType>;
|
||||
requires StellarNormalizationRuntimeAvailableFor<
|
||||
typename std::remove_cvref_t<Problem>::NormalizationPrescriptionType,
|
||||
typename std::remove_cvref_t<Problem>::FormType,
|
||||
@@ -450,42 +421,36 @@ export namespace mean_field::normalization {
|
||||
template <PhysicalRieszStellarEquilibriumProblem Problem>
|
||||
[[nodiscard]] DiagonalNormalization prepareNormalization(const Problem &problem) {
|
||||
using ProblemType = std::remove_cvref_t<Problem>;
|
||||
using Form = typename ProblemType::FormType;
|
||||
using Form = typename ProblemType::FormType;
|
||||
|
||||
const fem::FEM &finiteElements = problem.GetDiscretization().finiteElementModel();
|
||||
const fem::FEM &finiteElements =
|
||||
equilibrium::detail::StellarEquilibriumProblemFactory::FiniteElementModel(problem);
|
||||
if (!finiteElements.okay()) {
|
||||
throw std::invalid_argument("Physical Riesz preparation requires a current finite-element model.");
|
||||
}
|
||||
|
||||
const auto &physical = detail::PhysicalOperator(problem);
|
||||
const auto &physical = detail::PhysicalOperator(problem);
|
||||
const auto &gravityContext = physical.GetGravityContext();
|
||||
const auto &enthalpyMap = physical.GetHydrostaticOperator().GetEnthalpyMap();
|
||||
const auto scales = deriveStellarCharacteristicScales(
|
||||
problem.GetNormalizationPrescription(),
|
||||
problem.GetStellarModel()
|
||||
);
|
||||
const auto &enthalpyMap = physical.GetHydrostaticOperator().GetEnthalpyMap();
|
||||
const auto scales =
|
||||
deriveStellarCharacteristicScales(problem.GetNormalizationPrescription(), problem.GetStellarModel());
|
||||
|
||||
mfem::Array<int> stellarMarker =
|
||||
utils::domain::make_attribute_marker<utils::domain::Stellar, detail::DomainSchema>(*finiteElements.mesh);
|
||||
const mfem::Vector densityDiagonal = detail::GatherDiagonal(
|
||||
detail::AssembleScalarMassDiagonal(*finiteElements.densityFes, &stellarMarker),
|
||||
gravityContext.GetDensityMap(),
|
||||
"density"
|
||||
gravityContext.GetDensityMap(), "density"
|
||||
);
|
||||
const mfem::Vector enthalpyDiagonal = detail::GatherDiagonal(
|
||||
detail::AssembleScalarMassDiagonal(*finiteElements.enthalpyFes, &stellarMarker),
|
||||
enthalpyMap,
|
||||
"enthalpy"
|
||||
detail::AssembleScalarMassDiagonal(*finiteElements.enthalpyFes, &stellarMarker), enthalpyMap, "enthalpy"
|
||||
);
|
||||
const mfem::Vector gravityGradientDiagonal = detail::GatherDiagonal(
|
||||
detail::AssembleHDivMassDiagonal(*finiteElements.gravityFluxFes),
|
||||
gravityContext.GetGravityGradientMap(),
|
||||
detail::AssembleHDivMassDiagonal(*finiteElements.gravityFluxFes), gravityContext.GetGravityGradientMap(),
|
||||
"gravity-gradient"
|
||||
);
|
||||
const mfem::Vector gravityPotentialDiagonal = detail::GatherDiagonal(
|
||||
detail::AssembleScalarMassDiagonal(*finiteElements.gravityPotentialFes),
|
||||
gravityContext.GetGravityPotentialMap(),
|
||||
"gravity-potential"
|
||||
gravityContext.GetGravityPotentialMap(), "gravity-potential"
|
||||
);
|
||||
const field::ScalarBoundaryDofMap surfaceMap =
|
||||
field::make_stellar_surface_scalar_dof_map<detail::DomainSchema>(*finiteElements.surfaceDeformationFes);
|
||||
@@ -524,13 +489,11 @@ export namespace mean_field::normalization {
|
||||
);
|
||||
const mfem::Array<int> &surfaceRows = problem.GetPressureSurfaceRows().reduced_dofs();
|
||||
builder.template SetHybridResidualBlock<utils::blocks::enthalpy::specific::residual>(
|
||||
physicalScale<utils::blocks::enthalpy::specific::residual>(scales),
|
||||
enthalpyDiagonal,
|
||||
physicalScale<utils::blocks::enthalpy::specific::residual>(scales), enthalpyDiagonal,
|
||||
std::span<const int>{surfaceRows.GetData(), static_cast<std::size_t>(surfaceRows.Size())}
|
||||
);
|
||||
detail::PrepareSpecificationNormalizations<typename ProblemType::ModelType::SpecificationTypes>::Apply(
|
||||
builder,
|
||||
scales
|
||||
builder, scales
|
||||
);
|
||||
|
||||
return std::move(builder).Build();
|
||||
@@ -548,16 +511,11 @@ export namespace mean_field::normalization {
|
||||
!std::same_as<
|
||||
typename std::remove_cvref_t<Problem>::NormalizationPrescriptionType,
|
||||
Unnormalized> &&
|
||||
!PhysicalRieszDiagonalPrescription<
|
||||
typename std::remove_cvref_t<Problem>::NormalizationPrescriptionType> &&
|
||||
RuntimePreparedNormalizationOperation<Problem>)
|
||||
[[nodiscard]] DiagonalNormalization prepareNormalization(
|
||||
const Problem &problem
|
||||
) {
|
||||
return prepareStellarNormalization(
|
||||
problem.GetNormalizationPrescription(),
|
||||
problem
|
||||
);
|
||||
!PhysicalRieszDiagonalPrescription<typename std::remove_cvref_t<Problem>::NormalizationPrescriptionType> &&
|
||||
RuntimePreparedNormalizationOperation<Problem>
|
||||
)
|
||||
[[nodiscard]] DiagonalNormalization prepareNormalization(const Problem &problem) {
|
||||
return prepareStellarNormalization(problem.GetNormalizationPrescription(), problem);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -571,9 +529,7 @@ export namespace mean_field::normalization {
|
||||
concept NormalizableStellarEquilibriumProblem =
|
||||
equilibrium::DiscretizedStellarEquilibriumProblem<std::remove_cvref_t<Problem>> &&
|
||||
requires(const std::remove_cvref_t<Problem> &problem) {
|
||||
{
|
||||
prepareNormalization(problem)
|
||||
} -> std::same_as<DiagonalNormalization>;
|
||||
{ prepareNormalization(problem) } -> std::same_as<DiagonalNormalization>;
|
||||
};
|
||||
|
||||
struct NormalizedStellarEquilibriumStatistics final {
|
||||
@@ -591,17 +547,14 @@ export namespace mean_field::normalization {
|
||||
* implied.
|
||||
*/
|
||||
template <typename Candidate, typename Problem>
|
||||
concept ProblemBoundStellarInverseFor =
|
||||
NormalizableStellarEquilibriumProblem<std::remove_cvref_t<Problem>> &&
|
||||
std::derived_from<std::remove_cvref_t<Candidate>, mfem::Solver> &&
|
||||
requires(const std::remove_cvref_t<Candidate> &inverse) {
|
||||
{
|
||||
inverse.GetProblem()
|
||||
} -> std::same_as<const std::remove_cvref_t<Problem> &>;
|
||||
{
|
||||
inverse.IsCurrent()
|
||||
} -> std::same_as<bool>;
|
||||
};
|
||||
concept ProblemBoundStellarInverseFor = NormalizableStellarEquilibriumProblem<std::remove_cvref_t<Problem>> &&
|
||||
std::derived_from<std::remove_cvref_t<Candidate>, mfem::Solver> &&
|
||||
requires(const std::remove_cvref_t<Candidate> &inverse) {
|
||||
{
|
||||
inverse.GetProblem()
|
||||
} -> std::same_as<const std::remove_cvref_t<Problem> &>;
|
||||
{ inverse.IsCurrent() } -> std::same_as<bool>;
|
||||
};
|
||||
|
||||
template <NormalizableStellarEquilibriumProblem Problem, typename PhysicalInverse>
|
||||
requires ProblemBoundStellarInverseFor<PhysicalInverse, Problem>
|
||||
@@ -623,11 +576,20 @@ export namespace mean_field::normalization {
|
||||
using ProblemType = std::remove_cvref_t<Problem>;
|
||||
|
||||
public:
|
||||
using Report = typename ProblemType::Report;
|
||||
using PreparationResult = typename ProblemType::PreparationResult;
|
||||
|
||||
explicit NormalizedStellarEquilibriumOperator(ProblemType &problem)
|
||||
: mfem::Operator(problem.EquationSize(), problem.StateSize()),
|
||||
: mfem::Operator(
|
||||
problem.EquationSize(),
|
||||
problem.StateSize()
|
||||
),
|
||||
m_problem(&problem),
|
||||
m_normalization(prepareNormalization(problem)),
|
||||
m_scaledJacobian(problem.GetLinearizationOperator(), m_normalization),
|
||||
m_scaledJacobian(
|
||||
problem.GetLinearizationOperator(),
|
||||
m_normalization
|
||||
),
|
||||
m_physicalState(problem.StateSize()),
|
||||
m_physicalResidual(problem.EquationSize()),
|
||||
m_normalizedResidual(problem.EquationSize()) {
|
||||
@@ -646,7 +608,9 @@ export namespace mean_field::normalization {
|
||||
const mfem::Vector &normalizedState,
|
||||
const operators::StellarEquilibriumDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
) requires(ProblemType::generatedRotationProviderCount == 0) {
|
||||
)
|
||||
requires(ProblemType::generatedRotationProviderCount == 0)
|
||||
{
|
||||
if (normalizedState.Size() != Width()) {
|
||||
throw std::invalid_argument("The normalized stellar state has the wrong size.");
|
||||
}
|
||||
@@ -662,10 +626,37 @@ export namespace mean_field::normalization {
|
||||
return report;
|
||||
}
|
||||
|
||||
[[nodiscard]] PreparationResult TryPrepare(
|
||||
const mfem::Vector &normalizedState,
|
||||
const operators::StellarEquilibriumDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
)
|
||||
requires(ProblemType::generatedRotationProviderCount == 0)
|
||||
{
|
||||
if (normalizedState.Size() != Width()) {
|
||||
throw std::invalid_argument("The normalized stellar state has the wrong size.");
|
||||
}
|
||||
|
||||
m_isPrepared = false;
|
||||
m_normalization.DenormalizeState(normalizedState, m_physicalState);
|
||||
auto result = m_problem->TryPrepare(m_physicalState, dependencies, rotation);
|
||||
if (!result.has_value()) {
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
m_problem->BuildResidual(m_physicalResidual);
|
||||
m_normalization.NormalizeResidual(m_physicalResidual, m_normalizedResidual);
|
||||
m_physicalPreparationGeneration = m_problem->GetPreparationGeneration();
|
||||
m_isPrepared = true;
|
||||
++m_statistics.physicalPreparations;
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto Prepare(
|
||||
const mfem::Vector &normalizedState,
|
||||
const operators::StellarEquilibriumDependencies &dependencies
|
||||
) requires(ProblemType::generatedRotationProviderCount == 1) {
|
||||
)
|
||||
requires(ProblemType::generatedRotationProviderCount == 1)
|
||||
{
|
||||
if (normalizedState.Size() != Width()) {
|
||||
throw std::invalid_argument("The normalized stellar state has the wrong size.");
|
||||
}
|
||||
@@ -681,6 +672,30 @@ export namespace mean_field::normalization {
|
||||
return report;
|
||||
}
|
||||
|
||||
[[nodiscard]] PreparationResult TryPrepare(
|
||||
const mfem::Vector &normalizedState,
|
||||
const operators::StellarEquilibriumDependencies &dependencies
|
||||
)
|
||||
requires(ProblemType::generatedRotationProviderCount == 1)
|
||||
{
|
||||
if (normalizedState.Size() != Width()) {
|
||||
throw std::invalid_argument("The normalized stellar state has the wrong size.");
|
||||
}
|
||||
|
||||
m_isPrepared = false;
|
||||
m_normalization.DenormalizeState(normalizedState, m_physicalState);
|
||||
auto result = m_problem->TryPrepare(m_physicalState, dependencies);
|
||||
if (!result.has_value()) {
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
m_problem->BuildResidual(m_physicalResidual);
|
||||
m_normalization.NormalizeResidual(m_physicalResidual, m_normalizedResidual);
|
||||
m_physicalPreparationGeneration = m_problem->GetPreparationGeneration();
|
||||
m_isPrepared = true;
|
||||
++m_statistics.physicalPreparations;
|
||||
return result;
|
||||
}
|
||||
|
||||
void BuildResidual(mfem::Vector &normalizedResidual) const {
|
||||
VerifyPrepared();
|
||||
normalizedResidual = m_normalizedResidual;
|
||||
@@ -701,8 +716,8 @@ export namespace mean_field::normalization {
|
||||
|
||||
void RefreshNormalization() {
|
||||
DiagonalNormalization refreshed = prepareNormalization(*m_problem);
|
||||
m_normalization = std::move(refreshed);
|
||||
m_isPrepared = false;
|
||||
m_normalization = std::move(refreshed);
|
||||
m_isPrepared = false;
|
||||
++m_statistics.normalizationPreparations;
|
||||
}
|
||||
|
||||
@@ -735,8 +750,12 @@ export namespace mean_field::normalization {
|
||||
}
|
||||
|
||||
template <typename PhysicalInverse>
|
||||
requires ProblemBoundStellarInverseFor<PhysicalInverse, Problem>
|
||||
[[nodiscard]] NormalizedStellarPreconditioner<Problem, std::remove_cvref_t<PhysicalInverse>>
|
||||
requires ProblemBoundStellarInverseFor<
|
||||
PhysicalInverse,
|
||||
Problem>
|
||||
[[nodiscard]] NormalizedStellarPreconditioner<
|
||||
Problem,
|
||||
std::remove_cvref_t<PhysicalInverse>>
|
||||
MakeScaledPreconditioner(PhysicalInverse &physicalInverse) const;
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept {
|
||||
@@ -802,16 +821,15 @@ export namespace mean_field::normalization {
|
||||
requires ProblemBoundStellarInverseFor<PhysicalInverse, Problem>
|
||||
class NormalizedStellarPreconditioner final : public mfem::Solver {
|
||||
private:
|
||||
using ProblemType = std::remove_cvref_t<Problem>;
|
||||
using NormalizedOperator = NormalizedStellarEquilibriumOperator<ProblemType>;
|
||||
using ProblemType = std::remove_cvref_t<Problem>;
|
||||
using NormalizedOperator = NormalizedStellarEquilibriumOperator<ProblemType>;
|
||||
using PhysicalInverseType = std::remove_cvref_t<PhysicalInverse>;
|
||||
|
||||
[[nodiscard]] static PhysicalInverseType &RequireAssociatedPhysicalInverse(
|
||||
const NormalizedOperator &normalizedOperator,
|
||||
PhysicalInverseType &physicalInverse
|
||||
) {
|
||||
if (std::addressof(physicalInverse.GetProblem()) !=
|
||||
std::addressof(normalizedOperator.GetProblem())) {
|
||||
if (std::addressof(physicalInverse.GetProblem()) != std::addressof(normalizedOperator.GetProblem())) {
|
||||
throw std::invalid_argument(
|
||||
"A normalized stellar preconditioner and its physical inverse must belong to the same problem."
|
||||
);
|
||||
@@ -832,7 +850,10 @@ export namespace mean_field::normalization {
|
||||
m_normalizedOperator(&normalizedOperator),
|
||||
m_physicalInverse(&physicalInverse),
|
||||
m_scaled(
|
||||
RequireAssociatedPhysicalInverse(normalizedOperator, physicalInverse),
|
||||
RequireAssociatedPhysicalInverse(
|
||||
normalizedOperator,
|
||||
physicalInverse
|
||||
),
|
||||
normalizedOperator.GetPhysicalJacobian(),
|
||||
normalizedOperator,
|
||||
normalizedOperator.GetNormalization()
|
||||
@@ -863,8 +884,7 @@ export namespace mean_field::normalization {
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsCurrent() const {
|
||||
return m_normalizedOperator->IsPrepared() &&
|
||||
m_physicalInverse->IsCurrent();
|
||||
return m_normalizedOperator->IsPrepared() && m_physicalInverse->IsCurrent();
|
||||
}
|
||||
|
||||
[[nodiscard]] PhysicalInverseType &GetPhysicalInverse() noexcept {
|
||||
@@ -904,14 +924,15 @@ export namespace mean_field::normalization {
|
||||
|
||||
template <NormalizableStellarEquilibriumProblem Problem>
|
||||
template <typename PhysicalInverse>
|
||||
requires ProblemBoundStellarInverseFor<PhysicalInverse, Problem>
|
||||
NormalizedStellarPreconditioner<Problem, std::remove_cvref_t<PhysicalInverse>>
|
||||
requires ProblemBoundStellarInverseFor<
|
||||
PhysicalInverse,
|
||||
Problem>
|
||||
NormalizedStellarPreconditioner<
|
||||
Problem,
|
||||
std::remove_cvref_t<PhysicalInverse>>
|
||||
NormalizedStellarEquilibriumOperator<Problem>::MakeScaledPreconditioner(PhysicalInverse &physicalInverse) const {
|
||||
VerifyPrepared();
|
||||
return NormalizedStellarPreconditioner<Problem, std::remove_cvref_t<PhysicalInverse>>{
|
||||
*this,
|
||||
physicalInverse
|
||||
};
|
||||
return NormalizedStellarPreconditioner<Problem, std::remove_cvref_t<PhysicalInverse>>{*this, physicalInverse};
|
||||
}
|
||||
|
||||
template <NormalizableStellarEquilibriumProblem Problem>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
module;
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
#include <mfem.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
export module mean_field:operators.context.gravity_field;
|
||||
export import :fem;
|
||||
@@ -58,6 +60,23 @@ export namespace mean_field::operators::context::gravity_field {
|
||||
}
|
||||
};
|
||||
|
||||
enum class GravityFieldPreparationRejectionReason : std::uint8_t { invalid_mapping, non_finite_arithmetic };
|
||||
|
||||
struct GravityFieldPreparationRejection final {
|
||||
GravityFieldPreparationRejectionReason reason{GravityFieldPreparationRejectionReason::invalid_mapping};
|
||||
mapping::MappingStatus mappingStatus{mapping::MappingStatus::valid};
|
||||
};
|
||||
|
||||
template <typename Report>
|
||||
using GravityFieldPreparationResult = std::expected<Report, GravityFieldPreparationRejection>;
|
||||
|
||||
[[noreturn]] inline void throwGravityFieldPreparationRejection(const GravityFieldPreparationRejection &rejection) {
|
||||
if (rejection.reason == GravityFieldPreparationRejectionReason::non_finite_arithmetic) {
|
||||
throw std::domain_error("Prepared gravity-field data contained non-finite arithmetic.");
|
||||
}
|
||||
throw std::domain_error("Prepared gravity-field data could not map the candidate geometry.");
|
||||
}
|
||||
|
||||
class GravityFieldGeometryContext {
|
||||
public:
|
||||
GravityFieldGeometryContext(
|
||||
@@ -76,12 +95,24 @@ export namespace mean_field::operators::context::gravity_field {
|
||||
DisplacementRevision displacement_revision
|
||||
);
|
||||
|
||||
[[nodiscard]] GravityFieldPreparationResult<GravityFieldGeometryPreparation> TryPrepare(
|
||||
const mfem::Vector &displacement,
|
||||
DiscretizationRevision discretization_revision,
|
||||
DisplacementRevision displacement_revision
|
||||
);
|
||||
|
||||
GravityFieldGeometryPreparation PreparePrimal(
|
||||
const mfem::Vector &displacement,
|
||||
DiscretizationRevision discretization_revision,
|
||||
DisplacementRevision displacement_revision
|
||||
);
|
||||
|
||||
[[nodiscard]] GravityFieldPreparationResult<GravityFieldGeometryPreparation> TryPreparePrimal(
|
||||
const mfem::Vector &displacement,
|
||||
DiscretizationRevision discretization_revision,
|
||||
DisplacementRevision displacement_revision
|
||||
);
|
||||
|
||||
[[nodiscard]] const PreparedMappedHDivMassOperator &GetMassOperator() const;
|
||||
[[nodiscard]] const PreparedMappedGravitySourceOperator &GetSourceOperator() const;
|
||||
[[nodiscard]] const mfem::Operator &GetDivergenceOperator() const;
|
||||
@@ -95,7 +126,7 @@ export namespace mean_field::operators::context::gravity_field {
|
||||
private:
|
||||
enum class PreparationMode : std::uint8_t { primal, linearization };
|
||||
|
||||
GravityFieldGeometryPreparation PrepareImpl(
|
||||
[[nodiscard]] GravityFieldPreparationResult<GravityFieldGeometryPreparation> TryPrepareImpl(
|
||||
const mfem::Vector &displacement,
|
||||
DiscretizationRevision discretization_revision,
|
||||
DisplacementRevision displacement_revision,
|
||||
@@ -147,6 +178,11 @@ export namespace mean_field::operators::context::gravity_field {
|
||||
const GravityFieldRevisions &revisions
|
||||
);
|
||||
|
||||
[[nodiscard]] GravityFieldPreparationResult<GravityFieldPreparationReport> TryPrepare(
|
||||
const GravityFieldStateView &state,
|
||||
const GravityFieldRevisions &revisions
|
||||
);
|
||||
|
||||
[[nodiscard]] const GravityFieldGeometryContext &GetGeometryContext() const;
|
||||
[[nodiscard]] const mfem::Vector &GetDensityTrue() const;
|
||||
[[nodiscard]] const mfem::Vector &GetGravityGradientTrue() const;
|
||||
|
||||
@@ -24,6 +24,13 @@ export namespace mean_field::operators {
|
||||
const context::gravity_field::GravityFieldRevisions &revisions
|
||||
);
|
||||
|
||||
[[nodiscard]] context::gravity_field::GravityFieldPreparationResult<
|
||||
context::gravity_field::GravityFieldPreparationReport>
|
||||
TryPrepare(
|
||||
const mfem::Vector &state,
|
||||
const context::gravity_field::GravityFieldRevisions &revisions
|
||||
);
|
||||
|
||||
void Mult(
|
||||
const mfem::Vector &state,
|
||||
mfem::Vector &residual
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
module;
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.kernels.gravity_displacement_force;
|
||||
@@ -8,6 +11,24 @@ export import :fem;
|
||||
export import :mapping.domain_mapper;
|
||||
|
||||
export namespace mean_field::operators::kernels {
|
||||
enum class GravityDisplacementForceRejectionReason : std::uint8_t { invalid_mapping, non_finite_arithmetic };
|
||||
|
||||
struct GravityDisplacementForceRejection final {
|
||||
GravityDisplacementForceRejectionReason reason{GravityDisplacementForceRejectionReason::invalid_mapping};
|
||||
mapping::MappingStatus mappingStatus{mapping::MappingStatus::valid};
|
||||
};
|
||||
|
||||
using GravityDisplacementForceResult = std::expected<void, GravityDisplacementForceRejection>;
|
||||
|
||||
[[nodiscard]] GravityDisplacementForceResult try_apply_gravity_displacement_force_residual(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const mfem::Vector &densityTrue,
|
||||
const mfem::Vector &gravityGradientTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &residualTrue
|
||||
);
|
||||
|
||||
void apply_gravity_displacement_force_residual(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
module;
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.kernels.rotational_displacement_force;
|
||||
@@ -9,6 +12,24 @@ export import :mapping.domain_mapper;
|
||||
export import :physics.rigid_rotation;
|
||||
|
||||
export namespace mean_field::operators::kernels {
|
||||
enum class RotationalDisplacementForceRejectionReason : std::uint8_t { invalid_mapping, non_finite_arithmetic };
|
||||
|
||||
struct RotationalDisplacementForceRejection final {
|
||||
RotationalDisplacementForceRejectionReason reason{RotationalDisplacementForceRejectionReason::invalid_mapping};
|
||||
mapping::MappingStatus mappingStatus{mapping::MappingStatus::valid};
|
||||
};
|
||||
|
||||
using RotationalDisplacementForceResult = std::expected<void, RotationalDisplacementForceRejection>;
|
||||
|
||||
[[nodiscard]] RotationalDisplacementForceResult try_apply_rotational_displacement_force_residual(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const physics::RigidRotation &rotation,
|
||||
const mfem::Vector &densityTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &residualTrue
|
||||
);
|
||||
|
||||
/*
|
||||
* Rotational contribution to the displacement row:
|
||||
*
|
||||
|
||||
@@ -2,6 +2,10 @@ module;
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
@@ -45,6 +49,52 @@ export namespace mean_field::operators {
|
||||
constexpr auto operator<=>(const PreparedAngularMomentumReport &) const = default;
|
||||
};
|
||||
|
||||
enum class AngularMomentumPreparationRejectionReason : std::uint8_t {
|
||||
inverted_geometry,
|
||||
non_finite_geometry,
|
||||
non_finite_angular_velocity,
|
||||
non_finite_density,
|
||||
negative_moment_of_inertia,
|
||||
non_finite_moment_of_inertia,
|
||||
non_finite_residual
|
||||
};
|
||||
|
||||
/*
|
||||
* A trial state can fail to define the angular-momentum invariant without
|
||||
* violating the operator's structural contract. Keep that distinction in
|
||||
* a fixed-size value so a line search can reject the candidate without
|
||||
* constructing or transporting an exception.
|
||||
*/
|
||||
struct AngularMomentumPreparationRejection final {
|
||||
AngularMomentumPreparationRejectionReason reason{AngularMomentumPreparationRejectionReason::inverted_geometry};
|
||||
mapping::MappingStatus mappingStatus{mapping::MappingStatus::valid};
|
||||
double momentOfInertia{std::numeric_limits<double>::quiet_NaN()};
|
||||
};
|
||||
|
||||
using AngularMomentumPreparationResult =
|
||||
std::expected<PreparedAngularMomentumReport, AngularMomentumPreparationRejection>;
|
||||
|
||||
[[noreturn]] inline void
|
||||
throwAngularMomentumPreparationRejection(const AngularMomentumPreparationRejection &rejection) {
|
||||
switch (rejection.reason) {
|
||||
case AngularMomentumPreparationRejectionReason::inverted_geometry:
|
||||
throw std::domain_error("The angular-momentum trial inverts mapped geometry.");
|
||||
case AngularMomentumPreparationRejectionReason::non_finite_geometry:
|
||||
throw std::domain_error("The angular-momentum trial produced non-finite mapped geometry.");
|
||||
case AngularMomentumPreparationRejectionReason::non_finite_angular_velocity:
|
||||
throw std::domain_error("The angular-momentum trial has a non-finite angular velocity.");
|
||||
case AngularMomentumPreparationRejectionReason::non_finite_density:
|
||||
throw std::domain_error("The angular-momentum trial produced a non-finite interpolated density.");
|
||||
case AngularMomentumPreparationRejectionReason::negative_moment_of_inertia:
|
||||
throw std::domain_error("The angular-momentum trial produced a negative moment of inertia.");
|
||||
case AngularMomentumPreparationRejectionReason::non_finite_moment_of_inertia:
|
||||
throw std::domain_error("The angular-momentum trial produced a non-finite moment of inertia.");
|
||||
case AngularMomentumPreparationRejectionReason::non_finite_residual:
|
||||
throw std::domain_error("The angular-momentum trial produced a non-finite residual.");
|
||||
}
|
||||
throw std::logic_error("An unknown angular-momentum trial rejection was reported.");
|
||||
}
|
||||
|
||||
struct AngularMomentumConstraintReport final {
|
||||
double targetAngularMomentum;
|
||||
double achievedAngularMomentum;
|
||||
@@ -97,6 +147,11 @@ export namespace mean_field::operators {
|
||||
const AngularMomentumDependencies &dependencies
|
||||
);
|
||||
|
||||
[[nodiscard]] AngularMomentumPreparationResult TryPrepare(
|
||||
double angularVelocity,
|
||||
const AngularMomentumDependencies &dependencies
|
||||
);
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void ApplyDensityJacobianAction(
|
||||
@@ -156,9 +211,9 @@ export namespace mean_field::operators {
|
||||
};
|
||||
|
||||
void BuildStaticPlan();
|
||||
void RefreshGeometry(const mfem::Vector &displacement);
|
||||
void RefreshDensity(const mfem::Vector &density);
|
||||
void AssembleResidual();
|
||||
[[nodiscard]] std::optional<mapping::MappingStatus> RefreshGeometry(const mfem::Vector &displacement);
|
||||
[[nodiscard]] bool RefreshDensity(const mfem::Vector &density);
|
||||
[[nodiscard]] std::optional<AngularMomentumPreparationRejection> TryAssembleResidual();
|
||||
void VerifyPrepared() const;
|
||||
|
||||
[[nodiscard]] double EvaluateDensityMomentActionLocal(const mfem::Vector &densityVariation) const;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
module;
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <mfem.hpp>
|
||||
#include <vector>
|
||||
|
||||
@@ -22,6 +23,29 @@ export namespace mean_field::operators {
|
||||
}
|
||||
};
|
||||
|
||||
enum class BarotropicClosurePreparationRejectionReason : std::uint8_t {
|
||||
mapping_failure,
|
||||
invalid_quadrature_data,
|
||||
equation_of_state
|
||||
};
|
||||
|
||||
/*
|
||||
* A rejected candidate is part of the nonlinear-solver control flow, not
|
||||
* an exceptional API failure. Keep the payload fixed-size so it can be
|
||||
* selected deterministically across ranks without allocating. Only the
|
||||
* detail associated with `reason` is meaningful.
|
||||
*/
|
||||
struct BarotropicClosurePreparationRejection final {
|
||||
BarotropicClosurePreparationRejectionReason reason{
|
||||
BarotropicClosurePreparationRejectionReason::mapping_failure
|
||||
};
|
||||
mapping::MappingStatus mappingStatus{mapping::MappingStatus::non_finite_result};
|
||||
eos::EvaluationErrorCode equationOfStateError{eos::EvaluationErrorCode::nonfinite_result};
|
||||
};
|
||||
|
||||
using BarotropicClosurePreparationResult =
|
||||
std::expected<PreparedBarotropicClosureReport, BarotropicClosurePreparationRejection>;
|
||||
|
||||
class PreparedBarotropicClosureOperator final : public mfem::Operator {
|
||||
public:
|
||||
PreparedBarotropicClosureOperator(
|
||||
@@ -40,6 +64,11 @@ export namespace mean_field::operators {
|
||||
const context::barotropic::BarotropicClosureDependencies &dependencies
|
||||
);
|
||||
|
||||
[[nodiscard]] BarotropicClosurePreparationResult TryPrepare(
|
||||
const context::barotropic::BarotropicClosureStateView &state,
|
||||
const context::barotropic::BarotropicClosureDependencies &dependencies
|
||||
);
|
||||
|
||||
void Mult(
|
||||
const mfem::Vector &densityVariation,
|
||||
const mfem::Vector &enthalpyVariation,
|
||||
|
||||
@@ -2,6 +2,8 @@ module;
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
@@ -18,6 +20,28 @@ export import :physics.rigid_rotation;
|
||||
export import :utils.blocks;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
enum class DisplacementResidualPreparationRejectionSource : std::uint8_t {
|
||||
pressure,
|
||||
gravity,
|
||||
rotation,
|
||||
composition
|
||||
};
|
||||
|
||||
enum class DisplacementResidualPreparationRejectionReason : std::uint8_t {
|
||||
equation_of_state,
|
||||
invalid_mapping,
|
||||
non_finite_arithmetic
|
||||
};
|
||||
|
||||
struct DisplacementResidualPreparationRejection final {
|
||||
DisplacementResidualPreparationRejectionSource source{DisplacementResidualPreparationRejectionSource::pressure};
|
||||
DisplacementResidualPreparationRejectionReason reason{
|
||||
DisplacementResidualPreparationRejectionReason::equation_of_state
|
||||
};
|
||||
eos::EvaluationErrorCode equationOfStateCode{eos::EvaluationErrorCode::nonfinite_result};
|
||||
mapping::MappingStatus mappingStatus{mapping::MappingStatus::valid};
|
||||
};
|
||||
|
||||
struct DisplacementResidualDependencyStamp final {
|
||||
std::uint64_t identity{0};
|
||||
std::uint64_t revision{0};
|
||||
@@ -104,6 +128,15 @@ export namespace mean_field::operators {
|
||||
const physics::RigidRotation &rotation
|
||||
);
|
||||
|
||||
[[nodiscard]] std::expected<
|
||||
PreparedDisplacementResidualReport,
|
||||
DisplacementResidualPreparationRejection>
|
||||
TryPrepare(
|
||||
const DisplacementResidualStateView &state,
|
||||
const DisplacementResidualDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
);
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void ApplyDensityJacobianAction(
|
||||
@@ -154,7 +187,7 @@ export namespace mean_field::operators {
|
||||
GetGravityContext() const noexcept;
|
||||
|
||||
private:
|
||||
void AssembleResidual();
|
||||
[[nodiscard]] std::optional<DisplacementResidualPreparationRejection> AssembleResidual();
|
||||
void VerifyPrepared() const;
|
||||
|
||||
const fem::FEM &m_fem;
|
||||
|
||||
@@ -2,6 +2,7 @@ module;
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
@@ -11,6 +12,7 @@ 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 :operators.kernels.gravity_displacement_force;
|
||||
export import :utils.blocks;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
@@ -57,6 +59,11 @@ export namespace mean_field::operators {
|
||||
*/
|
||||
PreparedGravityDisplacementForceReport Prepare();
|
||||
|
||||
[[nodiscard]] std::expected<
|
||||
PreparedGravityDisplacementForceReport,
|
||||
kernels::GravityDisplacementForceRejection>
|
||||
TryPrepare();
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void ApplyDensityJacobianAction(
|
||||
@@ -106,7 +113,10 @@ export namespace mean_field::operators {
|
||||
|
||||
private:
|
||||
void VerifyPrepared() const;
|
||||
void PrepareElementData();
|
||||
[[nodiscard]] std::expected<
|
||||
void,
|
||||
kernels::GravityDisplacementForceRejection>
|
||||
TryPrepareElementData();
|
||||
void ApplyPreparedCompleteJacobianActionTrue(
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
module;
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
#include <mfem.hpp>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
export module mean_field:operators.prepared_gravity_source;
|
||||
@@ -10,6 +12,23 @@ export import :field.mfem;
|
||||
export import :mapping.domain_mapper;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
enum class GravitySourcePreparationRejectionReason : std::uint8_t { invalid_mapping, non_finite_arithmetic };
|
||||
|
||||
struct GravitySourcePreparationRejection final {
|
||||
GravitySourcePreparationRejectionReason reason{GravitySourcePreparationRejectionReason::invalid_mapping};
|
||||
mapping::MappingStatus mappingStatus{mapping::MappingStatus::valid};
|
||||
};
|
||||
|
||||
using GravitySourcePreparationResult = std::expected<void, GravitySourcePreparationRejection>;
|
||||
|
||||
[[noreturn]] inline void
|
||||
throwGravitySourcePreparationRejection(const GravitySourcePreparationRejection &rejection) {
|
||||
if (rejection.reason == GravitySourcePreparationRejectionReason::non_finite_arithmetic) {
|
||||
throw std::domain_error("Prepared gravity-source data contained non-finite arithmetic.");
|
||||
}
|
||||
throw std::domain_error("Prepared gravity-source data could not map the candidate geometry.");
|
||||
}
|
||||
|
||||
class PreparedMappedGravitySourceOperator final : public mfem::Operator {
|
||||
public:
|
||||
PreparedMappedGravitySourceOperator(
|
||||
@@ -19,6 +38,8 @@ export namespace mean_field::operators {
|
||||
|
||||
void Prepare(const mfem::Vector &displacement);
|
||||
void PreparePrimal(const mfem::Vector &displacement);
|
||||
[[nodiscard]] GravitySourcePreparationResult TryPrepare(const mfem::Vector &displacement);
|
||||
[[nodiscard]] GravitySourcePreparationResult TryPreparePrimal(const mfem::Vector &displacement);
|
||||
void Mult(
|
||||
const mfem::Vector &density,
|
||||
mfem::Vector &action
|
||||
@@ -68,7 +89,7 @@ export namespace mean_field::operators {
|
||||
mfem::Vector quadrature_data;
|
||||
};
|
||||
|
||||
void PrepareImpl(
|
||||
[[nodiscard]] GravitySourcePreparationResult TryPrepareImpl(
|
||||
const mfem::Vector &displacement,
|
||||
PreparationMode mode
|
||||
);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
module;
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
#include <mfem.hpp>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
export module mean_field:operators.prepared_hdiv_mass;
|
||||
@@ -10,6 +12,22 @@ export import :field.mfem;
|
||||
export import :mapping.domain_mapper;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
enum class HDivMassPreparationRejectionReason : std::uint8_t { invalid_mapping, non_finite_arithmetic };
|
||||
|
||||
struct HDivMassPreparationRejection final {
|
||||
HDivMassPreparationRejectionReason reason{HDivMassPreparationRejectionReason::invalid_mapping};
|
||||
mapping::MappingStatus mappingStatus{mapping::MappingStatus::valid};
|
||||
};
|
||||
|
||||
using HDivMassPreparationResult = std::expected<void, HDivMassPreparationRejection>;
|
||||
|
||||
[[noreturn]] inline void throwHDivMassPreparationRejection(const HDivMassPreparationRejection &rejection) {
|
||||
if (rejection.reason == HDivMassPreparationRejectionReason::non_finite_arithmetic) {
|
||||
throw std::domain_error("Prepared H(div) mass data contained non-finite arithmetic.");
|
||||
}
|
||||
throw std::domain_error("Prepared H(div) mass data could not map the candidate geometry.");
|
||||
}
|
||||
|
||||
class PreparedMappedHDivMassOperator final : public mfem::Operator {
|
||||
public:
|
||||
PreparedMappedHDivMassOperator(
|
||||
@@ -19,6 +37,8 @@ export namespace mean_field::operators {
|
||||
|
||||
void Prepare(const mfem::Vector &displacement);
|
||||
void PreparePrimal(const mfem::Vector &displacement);
|
||||
[[nodiscard]] HDivMassPreparationResult TryPrepare(const mfem::Vector &displacement);
|
||||
[[nodiscard]] HDivMassPreparationResult TryPreparePrimal(const mfem::Vector &displacement);
|
||||
void Mult(
|
||||
const mfem::Vector &gravity_gradient,
|
||||
mfem::Vector &action
|
||||
@@ -54,8 +74,8 @@ export namespace mean_field::operators {
|
||||
mfem::DenseMatrix frozenMappingData;
|
||||
};
|
||||
|
||||
void PrepareVariationData();
|
||||
void PrepareImpl(
|
||||
[[nodiscard]] mapping::MappingStatus PrepareVariationData();
|
||||
[[nodiscard]] HDivMassPreparationResult TryPrepareImpl(
|
||||
const mfem::Vector &displacement,
|
||||
PreparationMode mode
|
||||
);
|
||||
|
||||
@@ -3,7 +3,9 @@ module;
|
||||
#include <compare>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
@@ -30,6 +32,35 @@ export namespace mean_field::operators {
|
||||
}
|
||||
};
|
||||
|
||||
enum class HydrostaticEquilibriumPreparationRejectionReason : std::uint8_t {
|
||||
inverted_geometry,
|
||||
non_finite_geometry,
|
||||
non_finite_residual
|
||||
};
|
||||
|
||||
struct HydrostaticEquilibriumPreparationRejection final {
|
||||
HydrostaticEquilibriumPreparationRejectionReason reason{
|
||||
HydrostaticEquilibriumPreparationRejectionReason::inverted_geometry
|
||||
};
|
||||
mapping::MappingStatus mappingStatus{mapping::MappingStatus::valid};
|
||||
};
|
||||
|
||||
using HydrostaticEquilibriumPreparationResult =
|
||||
std::expected<PreparedHydrostaticEquilibriumReport, HydrostaticEquilibriumPreparationRejection>;
|
||||
|
||||
[[noreturn]] inline void
|
||||
throwHydrostaticEquilibriumPreparationRejection(const HydrostaticEquilibriumPreparationRejection &rejection) {
|
||||
switch (rejection.reason) {
|
||||
case HydrostaticEquilibriumPreparationRejectionReason::non_finite_geometry:
|
||||
throw std::domain_error("Prepared hydrostatic equilibrium encountered non-finite mapped geometry.");
|
||||
case HydrostaticEquilibriumPreparationRejectionReason::non_finite_residual:
|
||||
throw std::domain_error("Prepared hydrostatic equilibrium produced a non-finite residual.");
|
||||
case HydrostaticEquilibriumPreparationRejectionReason::inverted_geometry:
|
||||
default:
|
||||
throw std::domain_error("Prepared hydrostatic equilibrium encountered inverted mapped geometry.");
|
||||
}
|
||||
}
|
||||
|
||||
struct PreparedHydrostaticAlgebraicJacobianStatistics {
|
||||
std::uint64_t preparations{0};
|
||||
std::uint64_t enthalpyApplications{0};
|
||||
@@ -102,6 +133,12 @@ export namespace mean_field::operators {
|
||||
const physics::RigidRotation &rotation
|
||||
);
|
||||
|
||||
[[nodiscard]] HydrostaticEquilibriumPreparationResult TryPrepare(
|
||||
const context::hydrostatic::HydrostaticEquilibriumStateView &state,
|
||||
const context::hydrostatic::HydrostaticEquilibriumDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
);
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void ApplyEnthalpyJacobianAction(
|
||||
@@ -221,12 +258,12 @@ export namespace mean_field::operators {
|
||||
};
|
||||
|
||||
void PrepareStaticPlan();
|
||||
void PrepareGeometry();
|
||||
void PrepareAlgebraicJacobianBlocks();
|
||||
void PrepareRotation();
|
||||
void PrepareBaseState();
|
||||
[[nodiscard]] std::optional<mapping::MappingStatus> PrepareGeometry();
|
||||
[[nodiscard]] bool PrepareAlgebraicJacobianBlocks();
|
||||
[[nodiscard]] bool PrepareRotation();
|
||||
[[nodiscard]] bool PrepareBaseState();
|
||||
void FinalizeDisplacementJacobianPreparation();
|
||||
void AssembleCachedResidual();
|
||||
[[nodiscard]] bool AssembleCachedResidual();
|
||||
void VerifyPrepared() const;
|
||||
|
||||
const fem::FEM &m_fem;
|
||||
|
||||
@@ -2,7 +2,9 @@ module;
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <mfem.hpp>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
export module mean_field:operators.prepared_mass_normalization;
|
||||
@@ -59,6 +61,28 @@ export namespace mean_field::operators {
|
||||
constexpr auto operator<=>(const PreparedMassNormalizationReport &) const = default;
|
||||
};
|
||||
|
||||
enum class MassNormalizationPreparationRejectionReason : std::uint8_t {
|
||||
mapping_failure,
|
||||
non_finite_density_interpolation,
|
||||
non_finite_assembled_mass
|
||||
};
|
||||
|
||||
/*
|
||||
* Candidate rejection is deliberately represented without text or owned
|
||||
* storage. That makes the result cheap to propagate through a line search
|
||||
* and gives the MPI implementation a deterministic, allocation-free value
|
||||
* to select on every rank.
|
||||
*/
|
||||
struct MassNormalizationPreparationRejection final {
|
||||
MassNormalizationPreparationRejectionReason reason{
|
||||
MassNormalizationPreparationRejectionReason::mapping_failure
|
||||
};
|
||||
mapping::MappingStatus mappingStatus{mapping::MappingStatus::non_finite_result};
|
||||
};
|
||||
|
||||
using MassNormalizationPreparationResult =
|
||||
std::expected<PreparedMassNormalizationReport, MassNormalizationPreparationRejection>;
|
||||
|
||||
struct PreparedMassNormalizationActionStatistics final {
|
||||
std::uint64_t densityApplications{0};
|
||||
std::uint64_t displacementApplications{0};
|
||||
@@ -109,6 +133,16 @@ export namespace mean_field::operators {
|
||||
const MassNormalizationDependencies &dependencies
|
||||
);
|
||||
|
||||
[[nodiscard]] MassNormalizationPreparationResult TryPrepare(
|
||||
const MassNormalizationStateView &state,
|
||||
const MassNormalizationDependencies &dependencies
|
||||
);
|
||||
|
||||
[[nodiscard]] MassNormalizationPreparationResult TryPrepare(
|
||||
const models::CompiledFixedMass &constraint,
|
||||
const MassNormalizationDependencies &dependencies
|
||||
);
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void ApplyDensityJacobianAction(
|
||||
@@ -179,9 +213,11 @@ export namespace mean_field::operators {
|
||||
};
|
||||
|
||||
void BuildStaticPlan();
|
||||
void RefreshGeometry(const mfem::Vector &displacement);
|
||||
void RefreshDensity(const mfem::Vector &density);
|
||||
void AssembleResidual();
|
||||
[[nodiscard]] std::optional<MassNormalizationPreparationRejection>
|
||||
RefreshGeometry(const mfem::Vector &displacement);
|
||||
[[nodiscard]] std::optional<MassNormalizationPreparationRejection> RefreshDensity(const mfem::Vector &density);
|
||||
[[nodiscard]] std::optional<MassNormalizationPreparationRejection> AssembleResidual();
|
||||
[[nodiscard]] std::optional<MassNormalizationPreparationRejection> UpdateResidualForTargetMass();
|
||||
void VerifyPrepared() const;
|
||||
|
||||
[[nodiscard]] double EvaluateDensityActionLocal(const mfem::Vector &densityVariation) const;
|
||||
|
||||
@@ -3,6 +3,7 @@ module;
|
||||
#include <compare>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
@@ -18,6 +19,18 @@ export import :operators.context.pressure_force;
|
||||
export import :utils.blocks;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
enum class PressureForcePreparationRejectionReason : std::uint8_t {
|
||||
equation_of_state,
|
||||
invalid_mapping,
|
||||
non_finite_arithmetic
|
||||
};
|
||||
|
||||
struct PressureForcePreparationRejection final {
|
||||
PressureForcePreparationRejectionReason reason{PressureForcePreparationRejectionReason::equation_of_state};
|
||||
eos::EvaluationErrorCode equationOfStateCode{eos::EvaluationErrorCode::nonfinite_result};
|
||||
mapping::MappingStatus mappingStatus{mapping::MappingStatus::valid};
|
||||
};
|
||||
|
||||
struct PreparedPressureForceReport final {
|
||||
context::pressure_force::PressureForcePreparationReport contextReport;
|
||||
|
||||
@@ -92,6 +105,14 @@ export namespace mean_field::operators {
|
||||
const context::pressure_force::PressureForceDependencies &dependencies
|
||||
);
|
||||
|
||||
[[nodiscard]] std::expected<
|
||||
PreparedPressureForceReport,
|
||||
PressureForcePreparationRejection>
|
||||
TryPrepare(
|
||||
const context::pressure_force::PressureForceStateView &state,
|
||||
const context::pressure_force::PressureForceDependencies &dependencies
|
||||
);
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void ApplyEnthalpyJacobianAction(
|
||||
@@ -206,12 +227,12 @@ export namespace mean_field::operators {
|
||||
};
|
||||
|
||||
void PrepareStaticPlan();
|
||||
void PrepareGeometry();
|
||||
void PrepareMaterialState();
|
||||
[[nodiscard]] std::optional<PressureForcePreparationRejection> PrepareGeometry();
|
||||
[[nodiscard]] std::optional<PressureForcePreparationRejection> PrepareMaterialState();
|
||||
|
||||
void FinalizeDisplacementJacobianPreparation();
|
||||
|
||||
void AssembleCachedResidual();
|
||||
[[nodiscard]] std::optional<PressureForcePreparationRejection> AssembleCachedResidual();
|
||||
|
||||
void VerifyPrepared() const;
|
||||
|
||||
@@ -301,4 +322,4 @@ export namespace mean_field::operators {
|
||||
|
||||
const PreparedPressureForceOperator &m_preparedOperator;
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
} // namespace mean_field::operators
|
||||
|
||||
@@ -2,6 +2,7 @@ module;
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
@@ -12,6 +13,7 @@ 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 :operators.kernels.rotational_displacement_force;
|
||||
export import :physics.rigid_rotation;
|
||||
export import :utils.blocks;
|
||||
|
||||
@@ -61,6 +63,15 @@ export namespace mean_field::operators {
|
||||
const physics::RigidRotation &rotation
|
||||
);
|
||||
|
||||
[[nodiscard]] std::expected<
|
||||
PreparedRotationalDisplacementForceReport,
|
||||
kernels::RotationalDisplacementForceRejection>
|
||||
TryPrepare(
|
||||
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(
|
||||
@@ -104,7 +115,10 @@ export namespace mean_field::operators {
|
||||
|
||||
private:
|
||||
void VerifyPrepared() const;
|
||||
void PrepareElementData();
|
||||
[[nodiscard]] std::expected<
|
||||
void,
|
||||
kernels::RotationalDisplacementForceRejection>
|
||||
TryPrepareElementData();
|
||||
void ApplyPreparedCompleteJacobianActionTrue(
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
|
||||
@@ -3,10 +3,14 @@ module;
|
||||
#include <compare>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
#include <mpi.h>
|
||||
|
||||
export module mean_field:operators.prepared_stellar_equilibrium;
|
||||
|
||||
@@ -72,6 +76,70 @@ export namespace mean_field::operators {
|
||||
}
|
||||
};
|
||||
|
||||
enum class StellarEquilibriumPreparationRejectionReason : std::uint8_t {
|
||||
inverted_geometry,
|
||||
non_finite_geometry,
|
||||
thermodynamic_domain,
|
||||
non_finite_thermodynamics,
|
||||
inadmissible_physics,
|
||||
non_finite_physics
|
||||
};
|
||||
|
||||
enum class StellarEquilibriumPreparationStage : std::uint8_t {
|
||||
unspecified,
|
||||
generated_geometry,
|
||||
gravity,
|
||||
barotropic_closure,
|
||||
hydrostatic_equilibrium,
|
||||
displacement_residual,
|
||||
pressure_force,
|
||||
gravity_displacement_force,
|
||||
rotational_displacement_force,
|
||||
displacement_composition,
|
||||
mass_normalization,
|
||||
model_specification
|
||||
};
|
||||
|
||||
/*
|
||||
* A candidate state that cannot define a physical mapped domain is an
|
||||
* expected line-search outcome, not an exceptional program failure. Keep
|
||||
* this payload fixed-size so every trial can report it without allocating.
|
||||
*/
|
||||
struct StellarEquilibriumPreparationRejection final {
|
||||
StellarEquilibriumPreparationRejectionReason reason{
|
||||
StellarEquilibriumPreparationRejectionReason::inverted_geometry
|
||||
};
|
||||
StellarEquilibriumPreparationStage stage{StellarEquilibriumPreparationStage::unspecified};
|
||||
double minimumJacobianDeterminant{std::numeric_limits<double>::quiet_NaN()};
|
||||
eos::EvaluationErrorCode thermodynamicErrorCode{eos::EvaluationErrorCode::nonfinite_result};
|
||||
};
|
||||
|
||||
template <typename Report>
|
||||
using StellarEquilibriumPreparationResult = std::expected<Report, StellarEquilibriumPreparationRejection>;
|
||||
|
||||
[[noreturn]] inline void
|
||||
throwStellarEquilibriumPreparationRejection(const StellarEquilibriumPreparationRejection &rejection) {
|
||||
switch (rejection.reason) {
|
||||
case StellarEquilibriumPreparationRejectionReason::non_finite_geometry:
|
||||
throw std::domain_error("The prepared domain deformation has non-finite mapped geometry.");
|
||||
case StellarEquilibriumPreparationRejectionReason::thermodynamic_domain:
|
||||
throw eos::EvaluationError(
|
||||
rejection.thermodynamicErrorCode, "The stellar state lies outside the equation-of-state domain."
|
||||
);
|
||||
case StellarEquilibriumPreparationRejectionReason::non_finite_thermodynamics:
|
||||
throw eos::EvaluationError(
|
||||
rejection.thermodynamicErrorCode, "The stellar state produced non-finite thermodynamic data."
|
||||
);
|
||||
case StellarEquilibriumPreparationRejectionReason::inadmissible_physics:
|
||||
throw std::domain_error("The stellar state is physically inadmissible.");
|
||||
case StellarEquilibriumPreparationRejectionReason::non_finite_physics:
|
||||
throw std::domain_error("The stellar state produced non-finite physical data.");
|
||||
case StellarEquilibriumPreparationRejectionReason::inverted_geometry:
|
||||
throw std::domain_error("The prepared domain deformation inverts at least one volume element.");
|
||||
}
|
||||
throw std::logic_error("Unknown stellar-equilibrium candidate-rejection reason.");
|
||||
}
|
||||
|
||||
struct PreparedStellarEquilibriumStatistics final {
|
||||
std::uint64_t residualAssemblies{0};
|
||||
std::uint64_t residualApplications{0};
|
||||
@@ -159,6 +227,12 @@ export namespace mean_field::operators {
|
||||
const physics::RigidRotation &rotation
|
||||
);
|
||||
|
||||
[[nodiscard]] StellarEquilibriumPreparationResult<PreparedStellarEquilibriumReport> TryPrepare(
|
||||
const mfem::Vector &state,
|
||||
const StellarEquilibriumDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
);
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void Mult(
|
||||
@@ -188,12 +262,9 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] const PreparedHydrostaticEquilibriumOperator &GetHydrostaticOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedDisplacementResidualOperator &GetDisplacementOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedMassNormalizationOperator &GetMassNormalizationOperator() const noexcept;
|
||||
[[nodiscard]] double ApplyDensityVolumeIntegralDensityAction(
|
||||
const mfem::Vector &densityDirection
|
||||
) const;
|
||||
[[nodiscard]] double ApplyDensityVolumeIntegralSurfaceShapeAction(
|
||||
const mfem::Vector &surfaceShapeDirection
|
||||
) const;
|
||||
[[nodiscard]] double ApplyDensityVolumeIntegralDensityAction(const mfem::Vector &densityDirection) const;
|
||||
[[nodiscard]] double
|
||||
ApplyDensityVolumeIntegralSurfaceShapeAction(const mfem::Vector &surfaceShapeDirection) const;
|
||||
[[nodiscard]] const PreparedPressureSurfaceConstraint &GetSurfaceConstraintOperator() const noexcept;
|
||||
[[nodiscard]] const deformation::PreparedDomainDeformationRuntime &GetDomainDeformation() const noexcept;
|
||||
[[nodiscard]] const mfem::Vector &GetSurfaceDeformationParameters() const;
|
||||
@@ -222,6 +293,7 @@ export namespace mean_field::operators {
|
||||
void VerifyPrepared() const;
|
||||
|
||||
StellarEquilibriumRootManifest m_rootManifest;
|
||||
MPI_Comm m_communicator{MPI_COMM_NULL};
|
||||
mfem::Array<int> m_gravityStateOffsets;
|
||||
|
||||
context::gravity_field::GravityFieldLinearizationContext m_gravityContext;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,7 @@ module;
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@@ -28,12 +29,11 @@ export namespace mean_field::equilibrium {
|
||||
static constexpr bool complete = false;
|
||||
};
|
||||
|
||||
template <model::StellarModelType Model>
|
||||
struct StellarSurfaceCompilationAudit<Model, true> {
|
||||
template <model::StellarModelType Model> struct StellarSurfaceCompilationAudit<Model, true> {
|
||||
private:
|
||||
using ModelType = std::remove_cvref_t<Model>;
|
||||
using EquationOfState = typename ModelType::EquationOfStateType;
|
||||
using Form = operators::CompiledStellarEquilibriumForm<ModelType>;
|
||||
using ModelType = std::remove_cvref_t<Model>;
|
||||
using EquationOfState = typename ModelType::EquationOfStateType;
|
||||
using Form = operators::CompiledStellarEquilibriumForm<ModelType>;
|
||||
using AvailableEquations = material::StellarEquilibriumThermodynamicEquations;
|
||||
|
||||
static constexpr bool thermodynamicsCompilable =
|
||||
@@ -46,14 +46,12 @@ export namespace mean_field::equilibrium {
|
||||
} else {
|
||||
using ThermodynamicEquations =
|
||||
material::CompiledThermodynamicEquationsT<EquationOfState, Form, AvailableEquations>;
|
||||
using Formulation = typename ThermodynamicEquations::PressureSurfaceFormulation;
|
||||
using CompiledSurface =
|
||||
surface::CompiledPressureSurfaceConstraintT<Formulation, EquationOfState>;
|
||||
using Formulation = typename ThermodynamicEquations::PressureSurfaceFormulation;
|
||||
using CompiledSurface = surface::CompiledPressureSurfaceConstraintT<Formulation, EquationOfState>;
|
||||
return requires(const ModelType &model) {
|
||||
{
|
||||
surface::compilePressureSurfaceConstraint<Formulation>(
|
||||
model.surfaceCondition(),
|
||||
model.equationOfState()
|
||||
model.surfaceCondition(), model.equationOfState()
|
||||
)
|
||||
} -> std::same_as<CompiledSurface>;
|
||||
};
|
||||
@@ -77,8 +75,7 @@ export namespace mean_field::equilibrium {
|
||||
requires operators::StellarEquilibriumSystemCompilable<std::remove_cvref_t<Candidate>>;
|
||||
requires hasStellarEquilibriumSurfaceCompilation<std::remove_cvref_t<Candidate>>;
|
||||
requires operators::CompilableRootManifestFor<
|
||||
std::remove_cvref_t<Candidate>,
|
||||
operators::CompiledStellarEquilibriumForm<std::remove_cvref_t<Candidate>>>;
|
||||
std::remove_cvref_t<Candidate>, operators::CompiledStellarEquilibriumForm<std::remove_cvref_t<Candidate>>>;
|
||||
requires operators::hasStellarEquilibriumCoreRuntime<std::remove_cvref_t<Candidate>>;
|
||||
requires operators::hasCompleteStellarEquilibriumRuntime<std::remove_cvref_t<Candidate>>;
|
||||
requires operators::stellarEquilibriumRotationProviderCount<std::remove_cvref_t<Candidate>> <= 1;
|
||||
@@ -105,19 +102,16 @@ export namespace mean_field::equilibrium {
|
||||
operators::StellarEquilibriumPhysicalCoreType<std::remove_cvref_t<Model>>,
|
||||
typename std::remove_cvref_t<Model>::SpecificationTypes>> { };
|
||||
|
||||
struct StellarEquilibriumProblemFactory;
|
||||
} // namespace detail
|
||||
|
||||
template <
|
||||
StellarEquilibriumModel Model,
|
||||
StellarDiscretizationType Discretization = StellarDiscretization>
|
||||
template <StellarEquilibriumModel Model, StellarDiscretizationType Discretization = StellarDiscretization>
|
||||
requires detail::StellarEquilibriumModelDiscretizationStructureAudit<
|
||||
std::remove_cvref_t<Model>,
|
||||
std::remove_cvref_t<Discretization>>::value
|
||||
class StellarEquilibriumProblem final {
|
||||
public:
|
||||
using ModelType = std::remove_cvref_t<Model>;
|
||||
using DiscretizationType = std::remove_cvref_t<Discretization>;
|
||||
using ModelType = std::remove_cvref_t<Model>;
|
||||
using DiscretizationType = std::remove_cvref_t<Discretization>;
|
||||
using NormalizationPrescriptionType = typename DiscretizationType::NormalizationPrescriptionType;
|
||||
|
||||
static constexpr bool hasFixedCentralDensity =
|
||||
@@ -128,13 +122,15 @@ export namespace mean_field::equilibrium {
|
||||
operators::stellarEquilibriumRotationProviderCount<ModelType>;
|
||||
static constexpr bool symbolicallySquare = ModelType::symbolicallySquare;
|
||||
|
||||
using PreparedOperatorType = operators::PreparedVariadicStellarEquilibriumOperator<ModelType>;
|
||||
using PhysicalCoreType = typename PreparedOperatorType::PhysicalCoreType;
|
||||
using FormType = operators::CompiledStellarEquilibriumForm<ModelType>;
|
||||
using JacobianFormType = operators::CompiledStellarEquilibriumJacobianForm<ModelType>;
|
||||
using ManifestType = operators::EquilibriumSystemManifest<ModelType, FormType, JacobianFormType>;
|
||||
using EquationOfStateType = model::EquationOfStateType<ModelType>;
|
||||
using SurfaceConditionType = model::SurfaceConditionType<ModelType>;
|
||||
using PreparedOperatorType = operators::PreparedVariadicStellarEquilibriumOperator<ModelType>;
|
||||
using Report = typename PreparedOperatorType::Report;
|
||||
using PreparationResult = typename PreparedOperatorType::PreparationResult;
|
||||
using PhysicalCoreType = typename PreparedOperatorType::PhysicalCoreType;
|
||||
using FormType = operators::CompiledStellarEquilibriumForm<ModelType>;
|
||||
using JacobianFormType = operators::CompiledStellarEquilibriumJacobianForm<ModelType>;
|
||||
using ManifestType = operators::EquilibriumSystemManifest<ModelType, FormType, JacobianFormType>;
|
||||
using EquationOfStateType = model::EquationOfStateType<ModelType>;
|
||||
using SurfaceConditionType = model::SurfaceConditionType<ModelType>;
|
||||
using AvailableThermodynamicEquations = material::StellarEquilibriumThermodynamicEquations;
|
||||
using ThermodynamicEquationsType =
|
||||
material::CompiledThermodynamicEquationsT<EquationOfStateType, FormType, AvailableThermodynamicEquations>;
|
||||
@@ -147,17 +143,18 @@ export namespace mean_field::equilibrium {
|
||||
|
||||
StellarEquilibriumProblem(
|
||||
ModelType stellarModel,
|
||||
DiscretizationType discretization
|
||||
DiscretizationType discretization,
|
||||
fem::FEM &finiteElementModel
|
||||
)
|
||||
: m_stellarModel(std::make_shared<ModelType>(std::move(stellarModel))),
|
||||
m_discretization(std::move(discretization)),
|
||||
m_compiledSurfaceConstraint(CompileSurfaceConstraint(*m_stellarModel)),
|
||||
m_preparedOperator(
|
||||
m_discretization.finiteElementModel(),
|
||||
finiteElementModel,
|
||||
m_discretization.domainMapper(),
|
||||
m_stellarModel,
|
||||
operators::PressureSurfaceConstraintView{m_compiledSurfaceConstraint},
|
||||
CompileDefaultDomainDeformation(m_discretization.finiteElementModel())
|
||||
CompileDefaultDomainDeformation(finiteElementModel)
|
||||
) {
|
||||
VerifyProblem();
|
||||
}
|
||||
@@ -176,6 +173,12 @@ export namespace mean_field::equilibrium {
|
||||
return m_discretization;
|
||||
}
|
||||
|
||||
[[nodiscard]] MPI_Comm GetCommunicator() const & {
|
||||
return m_discretization.communicator();
|
||||
}
|
||||
|
||||
[[nodiscard]] MPI_Comm GetCommunicator() const && = delete;
|
||||
|
||||
[[nodiscard]] const NormalizationPrescriptionType &GetNormalizationPrescription() const noexcept {
|
||||
return m_discretization.normalizationPrescription();
|
||||
}
|
||||
@@ -236,21 +239,54 @@ export namespace mean_field::equilibrium {
|
||||
const mfem::Vector &state,
|
||||
const operators::StellarEquilibriumDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
) requires(generatedRotationProviderCount == 0) {
|
||||
)
|
||||
requires(generatedRotationProviderCount == 0)
|
||||
{
|
||||
auto report = m_preparedOperator.Prepare(state, dependencies, rotation);
|
||||
++m_preparationGeneration;
|
||||
return report;
|
||||
}
|
||||
|
||||
[[nodiscard]] PreparationResult TryPrepare(
|
||||
const mfem::Vector &state,
|
||||
const operators::StellarEquilibriumDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
)
|
||||
requires(generatedRotationProviderCount == 0)
|
||||
{
|
||||
auto result = m_preparedOperator.TryPrepare(state, dependencies, rotation);
|
||||
if (!result.has_value()) {
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
++m_preparationGeneration;
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto Prepare(
|
||||
const mfem::Vector &state,
|
||||
const operators::StellarEquilibriumDependencies &dependencies
|
||||
) requires(generatedRotationProviderCount == 1) {
|
||||
)
|
||||
requires(generatedRotationProviderCount == 1)
|
||||
{
|
||||
auto report = m_preparedOperator.Prepare(state, dependencies);
|
||||
++m_preparationGeneration;
|
||||
return report;
|
||||
}
|
||||
|
||||
[[nodiscard]] PreparationResult TryPrepare(
|
||||
const mfem::Vector &state,
|
||||
const operators::StellarEquilibriumDependencies &dependencies
|
||||
)
|
||||
requires(generatedRotationProviderCount == 1)
|
||||
{
|
||||
auto result = m_preparedOperator.TryPrepare(state, dependencies);
|
||||
if (!result.has_value()) {
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
++m_preparationGeneration;
|
||||
return result;
|
||||
}
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const {
|
||||
m_preparedOperator.BuildResidual(residual);
|
||||
}
|
||||
@@ -266,8 +302,7 @@ export namespace mean_field::equilibrium {
|
||||
[[nodiscard]] static CompiledSurfaceConstraintType CompileSurfaceConstraint(const ModelType &stellarModel) {
|
||||
return surface::compilePressureSurfaceConstraint<
|
||||
typename ThermodynamicEquationsType::PressureSurfaceFormulation>(
|
||||
stellarModel.surfaceCondition(),
|
||||
stellarModel.equationOfState()
|
||||
stellarModel.surfaceCondition(), stellarModel.equationOfState()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -318,28 +353,25 @@ export namespace mean_field::equilibrium {
|
||||
template <
|
||||
typename Model,
|
||||
typename Discretization,
|
||||
bool StructurallyCompatible =
|
||||
StellarEquilibriumModelDiscretizationStructureAudit<
|
||||
std::remove_cvref_t<Model>,
|
||||
std::remove_cvref_t<Discretization>>::value>
|
||||
bool StructurallyCompatible = StellarEquilibriumModelDiscretizationStructureAudit<
|
||||
std::remove_cvref_t<Model>,
|
||||
std::remove_cvref_t<Discretization>>::value>
|
||||
struct StellarEquilibriumModelDiscretizationOperationAudit : std::false_type { };
|
||||
|
||||
template <typename Model, typename Discretization>
|
||||
struct StellarEquilibriumModelDiscretizationOperationAudit<
|
||||
Model,
|
||||
Discretization,
|
||||
true> {
|
||||
struct StellarEquilibriumModelDiscretizationOperationAudit<Model, Discretization, true> {
|
||||
private:
|
||||
using ModelType = std::remove_cvref_t<Model>;
|
||||
using ModelType = std::remove_cvref_t<Model>;
|
||||
using DiscretizationType = std::remove_cvref_t<Discretization>;
|
||||
using Problem = StellarEquilibriumProblem<ModelType, DiscretizationType>;
|
||||
using Prescription = typename DiscretizationType::NormalizationPrescriptionType;
|
||||
using Problem = StellarEquilibriumProblem<ModelType, DiscretizationType>;
|
||||
using Prescription = typename DiscretizationType::NormalizationPrescriptionType;
|
||||
|
||||
public:
|
||||
static constexpr bool value = [] {
|
||||
if constexpr (
|
||||
std::same_as<Prescription, normalization::Unnormalized> ||
|
||||
normalization::PhysicalRieszDiagonalPrescription<Prescription>) {
|
||||
normalization::PhysicalRieszDiagonalPrescription<Prescription>
|
||||
) {
|
||||
return true;
|
||||
} else {
|
||||
return normalization::RuntimePreparedNormalizationOperation<Problem>;
|
||||
@@ -362,46 +394,99 @@ export namespace mean_field::equilibrium {
|
||||
std::remove_cvref_t<Model>,
|
||||
std::remove_cvref_t<Discretization>>::value;
|
||||
|
||||
namespace detail {
|
||||
/* The structurally formed problem type is needed to probe the ADL
|
||||
* operation without a recursive concept. Its constructor remains
|
||||
* private, and this factory is the single construction authority after
|
||||
* the complete public compatibility contract has succeeded. */
|
||||
struct StellarEquilibriumProblemFactory final {
|
||||
template <StellarEquilibriumModel Model, StellarDiscretizationType Discretization>
|
||||
requires StellarEquilibriumModelDiscretizationCompatible<Model, Discretization>
|
||||
[[nodiscard]] static auto Create(
|
||||
Model &&stellarModel,
|
||||
Discretization discretization
|
||||
) {
|
||||
using ModelType = std::remove_cvref_t<Model>;
|
||||
using DiscretizationType = std::remove_cvref_t<Discretization>;
|
||||
return StellarEquilibriumProblem<ModelType, DiscretizationType>{
|
||||
std::forward<Model>(stellarModel),
|
||||
std::move(discretization)
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace mean_field::equilibrium
|
||||
|
||||
template <StellarEquilibriumModel Model, StellarDiscretizationType Discretization>
|
||||
requires StellarEquilibriumModelDiscretizationCompatible<Model, Discretization>
|
||||
namespace mean_field::equilibrium::detail {
|
||||
/* The structurally formed problem type is needed to probe the ADL
|
||||
* operation without a recursive concept. Its constructor remains
|
||||
* private, and this factory is the single construction authority after
|
||||
* the complete public compatibility contract has succeeded. */
|
||||
struct StellarEquilibriumProblemFactory final {
|
||||
template <
|
||||
StellarEquilibriumModel Model,
|
||||
StellarDiscretizationType Discretization>
|
||||
requires StellarEquilibriumModelDiscretizationCompatible<
|
||||
Model,
|
||||
Discretization>
|
||||
[[nodiscard]] static auto Create(
|
||||
Model &&stellarModel,
|
||||
Discretization discretization
|
||||
) {
|
||||
using ModelType = std::remove_cvref_t<Model>;
|
||||
using DiscretizationType = std::remove_cvref_t<Discretization>;
|
||||
fem::FEM &finiteElementModel = discretization.MutableFiniteElementModelForAssembly();
|
||||
return StellarEquilibriumProblem<ModelType, DiscretizationType>{
|
||||
std::forward<Model>(stellarModel), std::move(discretization), finiteElementModel
|
||||
};
|
||||
}
|
||||
|
||||
template <
|
||||
StellarEquilibriumModel Model,
|
||||
StellarDiscretizationType Discretization>
|
||||
requires StellarEquilibriumModelDiscretizationCompatible<
|
||||
Model,
|
||||
Discretization>
|
||||
[[nodiscard]] static auto CreateOwned(
|
||||
Model &&stellarModel,
|
||||
Discretization discretization
|
||||
) {
|
||||
using ModelType = std::remove_cvref_t<Model>;
|
||||
using DiscretizationType = std::remove_cvref_t<Discretization>;
|
||||
using ProblemType = StellarEquilibriumProblem<ModelType, DiscretizationType>;
|
||||
fem::FEM &finiteElementModel = discretization.MutableFiniteElementModelForAssembly();
|
||||
return std::unique_ptr<ProblemType>{
|
||||
new ProblemType{std::forward<Model>(stellarModel), std::move(discretization), finiteElementModel}
|
||||
};
|
||||
}
|
||||
|
||||
template <DiscretizedStellarEquilibriumProblem Problem>
|
||||
[[nodiscard]] static fem::FEM &MutableFiniteElementModelForProjection(Problem &problem) {
|
||||
return problem.m_discretization.MutableFiniteElementModelForAssembly();
|
||||
}
|
||||
|
||||
template <DiscretizedStellarEquilibriumProblem Problem>
|
||||
[[nodiscard]] static const fem::FEM &FiniteElementModel(const Problem &problem) {
|
||||
return problem.m_discretization.RequireFiniteElementModel();
|
||||
}
|
||||
};
|
||||
} // namespace mean_field::equilibrium::detail
|
||||
|
||||
export namespace mean_field::equilibrium {
|
||||
template <
|
||||
StellarEquilibriumModel Model,
|
||||
StellarDiscretizationType Discretization>
|
||||
requires StellarEquilibriumModelDiscretizationCompatible<
|
||||
Model,
|
||||
Discretization>
|
||||
[[nodiscard]] auto discretize(
|
||||
Model &&stellarModel,
|
||||
Discretization discretization
|
||||
) {
|
||||
return detail::StellarEquilibriumProblemFactory::Create(
|
||||
std::forward<Model>(stellarModel),
|
||||
std::move(discretization)
|
||||
std::forward<Model>(stellarModel), std::move(discretization)
|
||||
);
|
||||
}
|
||||
|
||||
template <StellarEquilibriumModel Model>
|
||||
requires StellarEquilibriumModelDiscretizationCompatible<Model, StellarDiscretization>
|
||||
requires StellarEquilibriumModelDiscretizationCompatible<
|
||||
Model,
|
||||
StellarDiscretization>
|
||||
[[nodiscard]] auto discretize(
|
||||
Model &&stellarModel,
|
||||
fem::FEM &finiteElementModel
|
||||
fem::FEM &&finiteElementModel
|
||||
) {
|
||||
return discretize(std::forward<Model>(stellarModel), StellarDiscretization{finiteElementModel});
|
||||
return discretize(std::forward<Model>(stellarModel), StellarDiscretization{std::move(finiteElementModel)});
|
||||
}
|
||||
|
||||
template <StellarEquilibriumModel Model>
|
||||
requires StellarEquilibriumModelDiscretizationCompatible<
|
||||
Model,
|
||||
StellarDiscretization>
|
||||
StellarEquilibriumProblem<
|
||||
std::remove_cvref_t<Model>,
|
||||
StellarDiscretization>
|
||||
discretize(
|
||||
Model &&,
|
||||
fem::FEM &
|
||||
) = delete;
|
||||
} // namespace mean_field::equilibrium
|
||||
|
||||
@@ -17,10 +17,9 @@ export namespace mean_field::equilibrium {
|
||||
|
||||
template <StellarEquilibriumModel Model>
|
||||
[[nodiscard]] auto makeStellarEquilibriumSystem(
|
||||
fem::FEM &finiteElementModel,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
fem::FEM &&finiteElementModel,
|
||||
Model &&stellarModel
|
||||
) {
|
||||
return discretize(std::forward<Model>(stellarModel), StellarDiscretization{finiteElementModel, domainMapper});
|
||||
return discretize(std::forward<Model>(stellarModel), std::move(finiteElementModel));
|
||||
}
|
||||
} // namespace mean_field::equilibrium
|
||||
|
||||
@@ -325,8 +325,14 @@ export namespace mean_field::preconditioning {
|
||||
}
|
||||
}
|
||||
|
||||
PreparedStellarPreconditioner(ProblemType &&, BlockType) = delete;
|
||||
PreparedStellarPreconditioner(const ProblemType &&, BlockType) = delete;
|
||||
PreparedStellarPreconditioner(
|
||||
ProblemType &&,
|
||||
BlockType
|
||||
) = delete;
|
||||
PreparedStellarPreconditioner(
|
||||
const ProblemType &&,
|
||||
BlockType
|
||||
) = delete;
|
||||
|
||||
PreparedStellarPreconditioner(const PreparedStellarPreconditioner &) = delete;
|
||||
PreparedStellarPreconditioner &operator=(const PreparedStellarPreconditioner &) = delete;
|
||||
@@ -399,9 +405,11 @@ export namespace mean_field::preconditioning {
|
||||
equilibrium::DiscretizedStellarEquilibriumProblem Problem,
|
||||
SpecificationBorderBlockType Block>
|
||||
requires EquilibriumCoordinateComponentFor<
|
||||
Block,
|
||||
typename std::remove_cvref_t<Problem>::FormType> &&
|
||||
SpecificationBorderPreparableFor<Problem, Block>
|
||||
Block,
|
||||
typename std::remove_cvref_t<Problem>::FormType> &&
|
||||
SpecificationBorderPreparableFor<
|
||||
Problem,
|
||||
Block>
|
||||
[[nodiscard]] auto prepare(
|
||||
const Problem &problem,
|
||||
Block block
|
||||
@@ -409,17 +417,22 @@ export namespace mean_field::preconditioning {
|
||||
return PreparedStellarPreconditioner<Problem, Block>{problem, std::move(block)};
|
||||
}
|
||||
|
||||
template <typename Problem, SpecificationBorderBlockType Block>
|
||||
requires (!std::is_lvalue_reference_v<Problem>) &&
|
||||
equilibrium::DiscretizedStellarEquilibriumProblem<std::remove_cvref_t<Problem>> &&
|
||||
EquilibriumCoordinateComponentFor<
|
||||
Block,
|
||||
typename std::remove_cvref_t<Problem>::FormType> &&
|
||||
SpecificationBorderPreparableFor<std::remove_cvref_t<Problem>, Block>
|
||||
template <
|
||||
typename Problem,
|
||||
SpecificationBorderBlockType Block>
|
||||
requires(!std::is_lvalue_reference_v<Problem>) &&
|
||||
equilibrium::DiscretizedStellarEquilibriumProblem<std::remove_cvref_t<Problem>> &&
|
||||
EquilibriumCoordinateComponentFor<
|
||||
Block,
|
||||
typename std::remove_cvref_t<Problem>::FormType> &&
|
||||
SpecificationBorderPreparableFor<
|
||||
std::remove_cvref_t<Problem>,
|
||||
Block>
|
||||
[[nodiscard]] auto prepare(
|
||||
Problem &&,
|
||||
Block
|
||||
) -> PreparedStellarPreconditioner<
|
||||
std::remove_cvref_t<Problem>,
|
||||
std::remove_cvref_t<Block>> = delete;
|
||||
)
|
||||
-> PreparedStellarPreconditioner<
|
||||
std::remove_cvref_t<Problem>,
|
||||
std::remove_cvref_t<Block>> = delete;
|
||||
} // namespace mean_field::preconditioning
|
||||
|
||||
@@ -236,15 +236,13 @@ export namespace mean_field::preconditioning {
|
||||
* assembly. The current kernels remain polytropic, but selection no
|
||||
* longer embeds that closed-world type test in the descriptor concept.
|
||||
*/
|
||||
template <typename EquationOfState>
|
||||
struct MaterialSurfaceEquationOfStateBackend {
|
||||
template <typename EquationOfState> struct MaterialSurfaceEquationOfStateBackend {
|
||||
static constexpr bool registered = false;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct MaterialSurfaceEquationOfStateBackend<eos::Polytrope> {
|
||||
template <> struct MaterialSurfaceEquationOfStateBackend<eos::Polytrope> {
|
||||
static constexpr bool registered = true;
|
||||
using CoreType = operators::PreparedStellarEquilibriumOperator;
|
||||
using CoreType = operators::PreparedStellarEquilibriumOperator;
|
||||
};
|
||||
|
||||
template <typename EquationOfState>
|
||||
@@ -252,8 +250,7 @@ export namespace mean_field::preconditioning {
|
||||
{
|
||||
MaterialSurfaceEquationOfStateBackend<std::remove_cvref_t<EquationOfState>>::registered
|
||||
} -> std::convertible_to<bool>;
|
||||
requires MaterialSurfaceEquationOfStateBackend<
|
||||
std::remove_cvref_t<EquationOfState>>::registered;
|
||||
requires MaterialSurfaceEquationOfStateBackend<std::remove_cvref_t<EquationOfState>>::registered;
|
||||
typename MaterialSurfaceEquationOfStateBackend<std::remove_cvref_t<EquationOfState>>::CoreType;
|
||||
};
|
||||
|
||||
@@ -266,13 +263,11 @@ export namespace mean_field::preconditioning {
|
||||
* truthful while the current kernels still consume the legacy physical
|
||||
* core directly.
|
||||
*/
|
||||
template <typename EquationOfState, typename PhysicalCore>
|
||||
struct MaterialSurfaceExecutableRuntime {
|
||||
template <typename EquationOfState, typename PhysicalCore> struct MaterialSurfaceExecutableRuntime {
|
||||
static constexpr bool available = false;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct MaterialSurfaceExecutableRuntime<eos::Polytrope, operators::PreparedStellarEquilibriumOperator> {
|
||||
template <> struct MaterialSurfaceExecutableRuntime<eos::Polytrope, operators::PreparedStellarEquilibriumOperator> {
|
||||
static constexpr bool available = true;
|
||||
};
|
||||
|
||||
@@ -280,33 +275,28 @@ export namespace mean_field::preconditioning {
|
||||
concept ExecutableMaterialSurfaceRuntimeFor = requires {
|
||||
{
|
||||
MaterialSurfaceExecutableRuntime<
|
||||
std::remove_cvref_t<EquationOfState>,
|
||||
std::remove_cvref_t<PhysicalCore>>::available
|
||||
std::remove_cvref_t<EquationOfState>, std::remove_cvref_t<PhysicalCore>>::available
|
||||
} -> std::convertible_to<bool>;
|
||||
requires MaterialSurfaceExecutableRuntime<
|
||||
std::remove_cvref_t<EquationOfState>,
|
||||
std::remove_cvref_t<PhysicalCore>>::available;
|
||||
std::remove_cvref_t<EquationOfState>, std::remove_cvref_t<PhysicalCore>>::available;
|
||||
};
|
||||
|
||||
template <typename Descriptor>
|
||||
concept ImplementedMaterialSurfaceDescriptor =
|
||||
MaterialSurfaceDescriptor<Descriptor> &&
|
||||
ImplementedMaterialSurfaceEquationOfState<
|
||||
typename Descriptor::ThermodynamicEquations::EquationOfStateType>;
|
||||
ImplementedMaterialSurfaceEquationOfState<typename Descriptor::ThermodynamicEquations::EquationOfStateType>;
|
||||
|
||||
template <typename Descriptor, typename PhysicalCore>
|
||||
concept MaterialSurfaceRuntimeFor =
|
||||
ImplementedMaterialSurfaceDescriptor<Descriptor> && requires {
|
||||
concept MaterialSurfaceRuntimeFor = ImplementedMaterialSurfaceDescriptor<Descriptor> && requires {
|
||||
typename MaterialSurfaceEquationOfStateBackend<
|
||||
typename std::remove_cvref_t<Descriptor>::ThermodynamicEquations::EquationOfStateType>::CoreType;
|
||||
requires std::same_as<
|
||||
std::remove_cvref_t<PhysicalCore>,
|
||||
typename MaterialSurfaceEquationOfStateBackend<
|
||||
typename std::remove_cvref_t<Descriptor>::ThermodynamicEquations::EquationOfStateType>::CoreType;
|
||||
requires std::same_as<
|
||||
std::remove_cvref_t<PhysicalCore>,
|
||||
typename MaterialSurfaceEquationOfStateBackend<
|
||||
typename std::remove_cvref_t<Descriptor>::ThermodynamicEquations::EquationOfStateType>::CoreType>;
|
||||
requires ExecutableMaterialSurfaceRuntimeFor<
|
||||
typename std::remove_cvref_t<Descriptor>::ThermodynamicEquations::EquationOfStateType,
|
||||
PhysicalCore>;
|
||||
};
|
||||
typename std::remove_cvref_t<Descriptor>::ThermodynamicEquations::EquationOfStateType>::CoreType>;
|
||||
requires ExecutableMaterialSurfaceRuntimeFor<
|
||||
typename std::remove_cvref_t<Descriptor>::ThermodynamicEquations::EquationOfStateType, PhysicalCore>;
|
||||
};
|
||||
|
||||
template <typename Candidate>
|
||||
concept MaterialSurfacePreconditionerProblem =
|
||||
@@ -778,14 +768,14 @@ export namespace mean_field::preconditioning {
|
||||
fullEnthalpyDirection = enthalpyDirection;
|
||||
|
||||
m_operation->Mult(m_fullDirection, m_fullAction);
|
||||
const auto fullActionView = m_operation->GetRootManifest().residualView(m_fullAction);
|
||||
const auto fullActionView = m_operation->GetRootManifest().residualView(m_fullAction);
|
||||
const auto fullDensityAction = fullActionView.block(utils::blocks::density_field.mass_term);
|
||||
const auto fullSurfaceAction =
|
||||
fullActionView.block(utils::blocks::surface_deformation_field.shape_equilibrium_term);
|
||||
const auto fullEnthalpyAction = fullActionView.block(utils::blocks::enthalpy_field.specific_term);
|
||||
densityAction = fullDensityAction;
|
||||
surfaceAction = fullSurfaceAction;
|
||||
enthalpyAction = fullEnthalpyAction;
|
||||
densityAction = fullDensityAction;
|
||||
surfaceAction = fullSurfaceAction;
|
||||
enthalpyAction = fullEnthalpyAction;
|
||||
}
|
||||
|
||||
void ApplyEnthalpyToDensity(
|
||||
@@ -1121,6 +1111,79 @@ export namespace mean_field::preconditioning {
|
||||
std::uint64_t regularizedEntries{0};
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
[[nodiscard]] inline DiagonalPreparationQuality regularizeMaterialSurfaceDiagonal(
|
||||
mfem::Vector &diagonal,
|
||||
const MaterialSurfaceDiagonalOptions options,
|
||||
const MPI_Comm communicator
|
||||
) {
|
||||
const int localOptionsAreValid = std::isfinite(options.relativeFloor) && options.relativeFloor >= 0.0 &&
|
||||
std::isfinite(options.absoluteFloor) && options.absoluteFloor > 0.0
|
||||
? 1
|
||||
: 0;
|
||||
int globalOptionsAreValid = 0;
|
||||
if (MPI_Allreduce(&localOptionsAreValid, &globalOptionsAreValid, 1, MPI_INT, MPI_MIN, communicator) !=
|
||||
MPI_SUCCESS) {
|
||||
throw std::runtime_error("Material-surface regularization could not validate its options.");
|
||||
}
|
||||
if (globalOptionsAreValid == 0) {
|
||||
throw std::invalid_argument("Material-surface diagonal floors must be finite and nonnegative.");
|
||||
}
|
||||
|
||||
double localMaximum = 0.0;
|
||||
double localMinimum = std::numeric_limits<double>::infinity();
|
||||
int localEntriesAreFinite = 1;
|
||||
for (int index = 0; index < diagonal.Size(); ++index) {
|
||||
if (!std::isfinite(diagonal(index))) {
|
||||
localEntriesAreFinite = 0;
|
||||
continue;
|
||||
}
|
||||
const double magnitude = std::abs(diagonal(index));
|
||||
localMaximum = std::max(localMaximum, magnitude);
|
||||
localMinimum = std::min(localMinimum, magnitude);
|
||||
}
|
||||
|
||||
int globalEntriesAreFinite = 0;
|
||||
if (MPI_Allreduce(&localEntriesAreFinite, &globalEntriesAreFinite, 1, MPI_INT, MPI_MIN, communicator) !=
|
||||
MPI_SUCCESS) {
|
||||
throw std::runtime_error("Material-surface regularization could not validate its diagonal.");
|
||||
}
|
||||
if (globalEntriesAreFinite == 0) {
|
||||
throw std::invalid_argument("A material-surface diagonal contains a non-finite entry.");
|
||||
}
|
||||
|
||||
double globalMaximum = 0.0;
|
||||
double globalMinimum = 0.0;
|
||||
const int maximumStatus =
|
||||
MPI_Allreduce(&localMaximum, &globalMaximum, 1, MPI_DOUBLE, MPI_MAX, communicator);
|
||||
const int minimumStatus =
|
||||
MPI_Allreduce(&localMinimum, &globalMinimum, 1, MPI_DOUBLE, MPI_MIN, communicator);
|
||||
if (maximumStatus != MPI_SUCCESS || minimumStatus != MPI_SUCCESS) {
|
||||
throw std::runtime_error("Material-surface regularization could not reduce diagonal magnitudes.");
|
||||
}
|
||||
const double floor = std::max(options.absoluteFloor, options.relativeFloor * globalMaximum);
|
||||
std::uint64_t localRegularized = 0;
|
||||
for (int index = 0; index < diagonal.Size(); ++index) {
|
||||
if (std::abs(diagonal(index)) < floor) {
|
||||
diagonal(index) = std::copysign(floor, diagonal(index) == 0.0 ? 1.0 : diagonal(index));
|
||||
++localRegularized;
|
||||
}
|
||||
}
|
||||
|
||||
std::uint64_t globalRegularized = 0;
|
||||
if (MPI_Allreduce(&localRegularized, &globalRegularized, 1, MPI_UINT64_T, MPI_SUM, communicator) !=
|
||||
MPI_SUCCESS) {
|
||||
throw std::runtime_error("Material-surface regularization could not count regularized entries.");
|
||||
}
|
||||
return {
|
||||
.minimumAbsoluteEntryBeforeRegularization = globalMinimum,
|
||||
.maximumAbsoluteEntryBeforeRegularization = globalMaximum,
|
||||
.appliedFloor = floor,
|
||||
.regularizedEntries = globalRegularized
|
||||
};
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
struct SurfaceRieszCalibrationReport final {
|
||||
SurfaceRieszCalibrationTarget target{SurfaceRieszCalibrationTarget::none};
|
||||
int probeCount{0};
|
||||
@@ -1522,40 +1585,7 @@ export namespace mean_field::preconditioning {
|
||||
const MaterialSurfaceDiagonalOptions options,
|
||||
const MPI_Comm communicator
|
||||
) {
|
||||
if (!std::isfinite(options.relativeFloor) || options.relativeFloor < 0.0 ||
|
||||
!std::isfinite(options.absoluteFloor) || options.absoluteFloor <= 0.0) {
|
||||
throw std::invalid_argument("Material-surface diagonal floors must be finite and nonnegative.");
|
||||
}
|
||||
double localMaximum = 0.0;
|
||||
double localMinimum = std::numeric_limits<double>::infinity();
|
||||
for (int index = 0; index < diagonal.Size(); ++index) {
|
||||
if (!std::isfinite(diagonal(index))) {
|
||||
throw std::invalid_argument("A material-surface diagonal contains a non-finite entry.");
|
||||
}
|
||||
const double magnitude = std::abs(diagonal(index));
|
||||
localMaximum = std::max(localMaximum, magnitude);
|
||||
localMinimum = std::min(localMinimum, magnitude);
|
||||
}
|
||||
double globalMaximum = 0.0;
|
||||
double globalMinimum = 0.0;
|
||||
MPI_Allreduce(&localMaximum, &globalMaximum, 1, MPI_DOUBLE, MPI_MAX, communicator);
|
||||
MPI_Allreduce(&localMinimum, &globalMinimum, 1, MPI_DOUBLE, MPI_MIN, communicator);
|
||||
const double floor = std::max(options.absoluteFloor, options.relativeFloor * globalMaximum);
|
||||
std::uint64_t localRegularized = 0;
|
||||
for (int index = 0; index < diagonal.Size(); ++index) {
|
||||
if (std::abs(diagonal(index)) < floor) {
|
||||
diagonal(index) = std::copysign(floor, diagonal(index) == 0.0 ? 1.0 : diagonal(index));
|
||||
++localRegularized;
|
||||
}
|
||||
}
|
||||
std::uint64_t globalRegularized = 0;
|
||||
MPI_Allreduce(&localRegularized, &globalRegularized, 1, MPI_UINT64_T, MPI_SUM, communicator);
|
||||
return {
|
||||
.minimumAbsoluteEntryBeforeRegularization = globalMinimum,
|
||||
.maximumAbsoluteEntryBeforeRegularization = globalMaximum,
|
||||
.appliedFloor = floor,
|
||||
.regularizedEntries = globalRegularized
|
||||
};
|
||||
return detail::regularizeMaterialSurfaceDiagonal(diagonal, options, communicator);
|
||||
}
|
||||
|
||||
Block m_block;
|
||||
@@ -2051,40 +2081,7 @@ export namespace mean_field::preconditioning {
|
||||
const MaterialSurfaceDiagonalOptions options,
|
||||
const MPI_Comm communicator
|
||||
) {
|
||||
if (!std::isfinite(options.relativeFloor) || options.relativeFloor < 0.0 ||
|
||||
!std::isfinite(options.absoluteFloor) || options.absoluteFloor <= 0.0) {
|
||||
throw std::invalid_argument("Material-surface diagonal floors must be finite and nonnegative.");
|
||||
}
|
||||
double localMaximum = 0.0;
|
||||
double localMinimum = std::numeric_limits<double>::infinity();
|
||||
for (int index = 0; index < diagonal.Size(); ++index) {
|
||||
if (!std::isfinite(diagonal(index))) {
|
||||
throw std::invalid_argument("A material-surface diagonal contains a non-finite entry.");
|
||||
}
|
||||
const double magnitude = std::abs(diagonal(index));
|
||||
localMaximum = std::max(localMaximum, magnitude);
|
||||
localMinimum = std::min(localMinimum, magnitude);
|
||||
}
|
||||
double globalMaximum = 0.0;
|
||||
double globalMinimum = 0.0;
|
||||
MPI_Allreduce(&localMaximum, &globalMaximum, 1, MPI_DOUBLE, MPI_MAX, communicator);
|
||||
MPI_Allreduce(&localMinimum, &globalMinimum, 1, MPI_DOUBLE, MPI_MIN, communicator);
|
||||
const double floor = std::max(options.absoluteFloor, options.relativeFloor * globalMaximum);
|
||||
std::uint64_t localRegularized = 0;
|
||||
for (int index = 0; index < diagonal.Size(); ++index) {
|
||||
if (std::abs(diagonal(index)) < floor) {
|
||||
diagonal(index) = std::copysign(floor, diagonal(index) == 0.0 ? 1.0 : diagonal(index));
|
||||
++localRegularized;
|
||||
}
|
||||
}
|
||||
std::uint64_t globalRegularized = 0;
|
||||
MPI_Allreduce(&localRegularized, &globalRegularized, 1, MPI_UINT64_T, MPI_SUM, communicator);
|
||||
return {
|
||||
.minimumAbsoluteEntryBeforeRegularization = globalMinimum,
|
||||
.maximumAbsoluteEntryBeforeRegularization = globalMaximum,
|
||||
.appliedFloor = floor,
|
||||
.regularizedEntries = globalRegularized
|
||||
};
|
||||
return detail::regularizeMaterialSurfaceDiagonal(diagonal, options, communicator);
|
||||
}
|
||||
|
||||
Block m_block;
|
||||
@@ -2111,7 +2108,9 @@ export namespace mean_field::preconditioning {
|
||||
template <
|
||||
MaterialSurfaceDescriptor Descriptor,
|
||||
MaterialSurfaceFactorizationPolicy Policy>
|
||||
requires MaterialSurfaceRuntimeFor<Descriptor, operators::PreparedStellarEquilibriumOperator>
|
||||
requires MaterialSurfaceRuntimeFor<
|
||||
Descriptor,
|
||||
operators::PreparedStellarEquilibriumOperator>
|
||||
[[nodiscard]] auto prepare(
|
||||
const operators::PreparedStellarEquilibriumOperator &operation,
|
||||
MaterialSurfaceBlock<
|
||||
@@ -2127,12 +2126,17 @@ export namespace mean_field::preconditioning {
|
||||
equilibrium::StellarEquilibriumModel Model,
|
||||
equilibrium::StellarDiscretizationType Discretization,
|
||||
MaterialSurfaceFactorizationPolicy Policy>
|
||||
requires MaterialSurfacePreconditionerProblem<
|
||||
equilibrium::StellarEquilibriumProblem<Model, Discretization>>
|
||||
requires MaterialSurfacePreconditionerProblem<equilibrium::StellarEquilibriumProblem<
|
||||
Model,
|
||||
Discretization>>
|
||||
[[nodiscard]] auto prepare(
|
||||
const equilibrium::StellarEquilibriumProblem<Model, Discretization> &problem,
|
||||
const equilibrium::StellarEquilibriumProblem<
|
||||
Model,
|
||||
Discretization> &problem,
|
||||
MaterialSurfaceBlock<
|
||||
MaterialSurfaceDescriptorFor<equilibrium::StellarEquilibriumProblem<Model, Discretization>>,
|
||||
MaterialSurfaceDescriptorFor<equilibrium::StellarEquilibriumProblem<
|
||||
Model,
|
||||
Discretization>>,
|
||||
backend::Diagonal,
|
||||
backend::Diagonal,
|
||||
Policy> block
|
||||
@@ -2144,7 +2148,9 @@ export namespace mean_field::preconditioning {
|
||||
MaterialSurfaceDescriptor Descriptor,
|
||||
MaterialSurfaceFactorizationPolicy Policy,
|
||||
backend::ApplicationMode Mode>
|
||||
requires MaterialSurfaceRuntimeFor<Descriptor, operators::PreparedStellarEquilibriumOperator>
|
||||
requires MaterialSurfaceRuntimeFor<
|
||||
Descriptor,
|
||||
operators::PreparedStellarEquilibriumOperator>
|
||||
[[nodiscard]] auto prepare(
|
||||
const operators::PreparedStellarEquilibriumOperator &operation,
|
||||
MaterialSurfaceBlock<
|
||||
@@ -2162,12 +2168,17 @@ export namespace mean_field::preconditioning {
|
||||
equilibrium::StellarDiscretizationType Discretization,
|
||||
MaterialSurfaceFactorizationPolicy Policy,
|
||||
backend::ApplicationMode Mode>
|
||||
requires MaterialSurfacePreconditionerProblem<
|
||||
equilibrium::StellarEquilibriumProblem<Model, Discretization>>
|
||||
requires MaterialSurfacePreconditionerProblem<equilibrium::StellarEquilibriumProblem<
|
||||
Model,
|
||||
Discretization>>
|
||||
[[nodiscard]] auto prepare(
|
||||
const equilibrium::StellarEquilibriumProblem<Model, Discretization> &problem,
|
||||
const equilibrium::StellarEquilibriumProblem<
|
||||
Model,
|
||||
Discretization> &problem,
|
||||
MaterialSurfaceBlock<
|
||||
MaterialSurfaceDescriptorFor<equilibrium::StellarEquilibriumProblem<Model, Discretization>>,
|
||||
MaterialSurfaceDescriptorFor<equilibrium::StellarEquilibriumProblem<
|
||||
Model,
|
||||
Discretization>>,
|
||||
backend::Diagonal,
|
||||
backend::HypreBoomerAMG<Mode>,
|
||||
Policy,
|
||||
|
||||
@@ -9,3 +9,4 @@ export import :preconditioning.stellar_equilibrium;
|
||||
export import :preconditioning.stellar_structure;
|
||||
export import :preconditioning.specification_border;
|
||||
export import :preconditioning.equilibrium_coordinates;
|
||||
export import :preconditioning.stellar_recipe;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -63,7 +63,7 @@ export namespace mean_field::preconditioning {
|
||||
.geometry = prepared.geometry != current.geometry,
|
||||
.equationOfState = prepared.equationOfStateIdentity != current.equationOfStateIdentity,
|
||||
.linearization = prepared.linearization != current.linearization ||
|
||||
prepared.preparedOperatorGeneration != current.preparedOperatorGeneration
|
||||
prepared.preparedOperatorGeneration != current.preparedOperatorGeneration
|
||||
};
|
||||
}
|
||||
|
||||
@@ -97,9 +97,7 @@ export namespace mean_field::preconditioning {
|
||||
static constexpr bool registered = false;
|
||||
};
|
||||
|
||||
template <
|
||||
equilibrium::StellarEquilibriumModel Model,
|
||||
equilibrium::StellarDiscretizationType Discretization>
|
||||
template <equilibrium::StellarEquilibriumModel Model, equilibrium::StellarDiscretizationType Discretization>
|
||||
struct StellarEquilibriumProblemTraits<equilibrium::StellarEquilibriumProblem<Model, Discretization>> {
|
||||
using Problem = equilibrium::StellarEquilibriumProblem<Model, Discretization>;
|
||||
using Form = typename Problem::FormType;
|
||||
@@ -131,10 +129,9 @@ export namespace mean_field::preconditioning {
|
||||
[[nodiscard]] static StellarPreconditionerLifecycleSnapshot Snapshot(const Problem &problem) {
|
||||
const operators::StellarEquilibriumDependencies &dependencies = problem.GetLinearizationDependencies();
|
||||
return {
|
||||
.discretization = dependencies.discretization,
|
||||
.geometry = problem.GetGeometryDependency(),
|
||||
.equationOfStateIdentity =
|
||||
std::addressof(problem.GetStellarModel().equationOfState()),
|
||||
.discretization = dependencies.discretization,
|
||||
.geometry = problem.GetGeometryDependency(),
|
||||
.equationOfStateIdentity = std::addressof(problem.GetStellarModel().equationOfState()),
|
||||
.linearization = dependencies,
|
||||
.preparedOperatorGeneration = problem.GetPreparationGeneration()
|
||||
};
|
||||
@@ -145,26 +142,20 @@ export namespace mean_field::preconditioning {
|
||||
concept StellarPreconditionerProblem = StellarEquilibriumProblemTraits<std::remove_cvref_t<Candidate>>::registered;
|
||||
|
||||
namespace detail {
|
||||
template <typename Block>
|
||||
struct IsGeneratedStellarValueBlock : std::false_type { };
|
||||
template <typename Block> struct IsGeneratedStellarValueBlock : std::false_type { };
|
||||
|
||||
template <typename Generated>
|
||||
struct IsGeneratedStellarValueBlock<utils::blocks::generated_value_block<Generated>>
|
||||
: std::true_type { };
|
||||
struct IsGeneratedStellarValueBlock<utils::blocks::generated_value_block<Generated>> : std::true_type { };
|
||||
|
||||
template <typename Block>
|
||||
struct IsGeneratedStellarResidualBlock : std::false_type { };
|
||||
template <typename Block> struct IsGeneratedStellarResidualBlock : std::false_type { };
|
||||
|
||||
template <typename Generated>
|
||||
struct IsGeneratedStellarResidualBlock<utils::blocks::generated_residual_block<Generated>>
|
||||
: std::true_type { };
|
||||
struct IsGeneratedStellarResidualBlock<utils::blocks::generated_residual_block<Generated>> : std::true_type { };
|
||||
|
||||
template <typename Coupling>
|
||||
inline constexpr bool isPurePhysicalStellarCoupling =
|
||||
!IsGeneratedStellarValueBlock<
|
||||
std::remove_cvref_t<typename Coupling::Value>>::value &&
|
||||
!IsGeneratedStellarResidualBlock<
|
||||
std::remove_cvref_t<typename Coupling::Residual>>::value;
|
||||
!IsGeneratedStellarValueBlock<std::remove_cvref_t<typename Coupling::Value>>::value &&
|
||||
!IsGeneratedStellarResidualBlock<std::remove_cvref_t<typename Coupling::Residual>>::value;
|
||||
|
||||
/* Pure structure contributions owned by a trusted backend are exact
|
||||
* (core, specification, coupling) capabilities. Future cores and new
|
||||
@@ -172,8 +163,7 @@ export namespace mean_field::preconditioning {
|
||||
* be accompanied by an explicit preconditioner decision. Generated-
|
||||
* border terms remain the responsibility of specification-border
|
||||
* machinery. */
|
||||
template <typename PhysicalCore, typename Specification>
|
||||
struct StellarStructureBackendHandledCouplings {
|
||||
template <typename PhysicalCore, typename Specification> struct StellarStructureBackendHandledCouplings {
|
||||
using Type = utils::blocks::type_list<>;
|
||||
};
|
||||
|
||||
@@ -213,17 +203,12 @@ export namespace mean_field::preconditioning {
|
||||
struct StellarStructureBackendHandledCouplings<
|
||||
operators::PreparedStellarEquilibriumOperator,
|
||||
models::FixedCentralDensity> {
|
||||
using Type = utils::blocks::type_list<
|
||||
operators::StellarEquilibriumJacobianCoupling<
|
||||
utils::blocks::enthalpy::specific::residual,
|
||||
utils::blocks::enthalpy::specific::value>>;
|
||||
using Type = utils::blocks::type_list<operators::StellarEquilibriumJacobianCoupling<
|
||||
utils::blocks::enthalpy::specific::residual,
|
||||
utils::blocks::enthalpy::specific::value>>;
|
||||
};
|
||||
|
||||
template <
|
||||
typename Coupling,
|
||||
typename Model,
|
||||
typename PhysicalCore,
|
||||
typename ModelSpecifications>
|
||||
template <typename Coupling, typename Model, typename PhysicalCore, typename ModelSpecifications>
|
||||
struct EveryCouplingContributionHandled;
|
||||
|
||||
template <
|
||||
@@ -235,30 +220,22 @@ export namespace mean_field::preconditioning {
|
||||
Coupling,
|
||||
Model,
|
||||
PhysicalCore,
|
||||
models::detail::SpecificationSetStorage<Specifications...>> final {
|
||||
models::detail::SpecificationSetStorage<Specifications...>>
|
||||
final {
|
||||
private:
|
||||
template <typename Specification>
|
||||
static constexpr bool handled =
|
||||
!utils::blocks::contains_type_v<
|
||||
Coupling,
|
||||
typename operators::StellarEquilibriumSpecificationCompilation<
|
||||
Specification>::JacobianCouplings> ||
|
||||
(operators::stellarEquilibriumBackendRuntimeAuthorized<
|
||||
Specification,
|
||||
Model> &&
|
||||
typename operators::StellarEquilibriumSpecificationCompilation<Specification>::JacobianCouplings> ||
|
||||
(operators::stellarEquilibriumBackendRuntimeAuthorized<Specification, Model> &&
|
||||
utils::blocks::contains_type_v<
|
||||
Coupling,
|
||||
typename StellarStructureBackendHandledCouplings<
|
||||
PhysicalCore,
|
||||
Specification>::Type>) ||
|
||||
operators::stellarEquilibriumSpecificationCouplingIsStructuralZero<
|
||||
Specification,
|
||||
Model,
|
||||
Coupling>;
|
||||
typename StellarStructureBackendHandledCouplings<PhysicalCore, Specification>::Type>) ||
|
||||
operators::stellarEquilibriumSpecificationCouplingIsStructuralZero<Specification, Model, Coupling>;
|
||||
|
||||
public:
|
||||
static constexpr bool value =
|
||||
(handled<Specifications> && ...);
|
||||
static constexpr bool value = (handled<Specifications> && ...);
|
||||
};
|
||||
|
||||
template <
|
||||
@@ -269,11 +246,7 @@ export namespace mean_field::preconditioning {
|
||||
typename Unsupported>
|
||||
struct CollectUnsupportedStellarStructureCouplings;
|
||||
|
||||
template <
|
||||
typename Model,
|
||||
typename PhysicalCore,
|
||||
typename ModelSpecifications,
|
||||
typename Unsupported>
|
||||
template <typename Model, typename PhysicalCore, typename ModelSpecifications, typename Unsupported>
|
||||
struct CollectUnsupportedStellarStructureCouplings<
|
||||
utils::blocks::type_list<>,
|
||||
Model,
|
||||
@@ -299,11 +272,7 @@ export namespace mean_field::preconditioning {
|
||||
private:
|
||||
static constexpr bool supported =
|
||||
!isPurePhysicalStellarCoupling<Head> ||
|
||||
EveryCouplingContributionHandled<
|
||||
Head,
|
||||
Model,
|
||||
PhysicalCore,
|
||||
ModelSpecifications>::value;
|
||||
EveryCouplingContributionHandled<Head, Model, PhysicalCore, ModelSpecifications>::value;
|
||||
using Next = std::conditional_t<
|
||||
supported,
|
||||
utils::blocks::type_list<Unsupported...>,
|
||||
@@ -318,42 +287,36 @@ export namespace mean_field::preconditioning {
|
||||
Next>::Type;
|
||||
};
|
||||
|
||||
template <typename Candidate, typename = void>
|
||||
struct DefaultStellarStructurePhysicalTopologyAudit {
|
||||
using ContributionCouplings = utils::blocks::type_list<>;
|
||||
using UnsupportedCouplings = utils::blocks::type_list<>;
|
||||
template <typename Candidate, typename = void> struct DefaultStellarStructurePhysicalTopologyAudit {
|
||||
using ContributionCouplings = utils::blocks::type_list<>;
|
||||
using UnsupportedCouplings = utils::blocks::type_list<>;
|
||||
|
||||
static constexpr bool supported = false;
|
||||
};
|
||||
|
||||
template <model::StellarModelType Model>
|
||||
requires(
|
||||
operators::StellarEquilibriumSystemCompilable<
|
||||
std::remove_cvref_t<Model>> &&
|
||||
operators::hasStellarEquilibriumCoreRuntime<
|
||||
std::remove_cvref_t<Model>>)
|
||||
operators::StellarEquilibriumSystemCompilable<std::remove_cvref_t<Model>> &&
|
||||
operators::hasStellarEquilibriumCoreRuntime<std::remove_cvref_t<Model>>
|
||||
)
|
||||
struct DefaultStellarStructurePhysicalTopologyAudit<
|
||||
Model,
|
||||
std::void_t<
|
||||
typename operators::CompiledStellarEquilibriumSystem<
|
||||
std::remove_cvref_t<Model>>::ContributionJacobianCouplings,
|
||||
operators::StellarEquilibriumPhysicalCoreType<
|
||||
std::remove_cvref_t<Model>>>> {
|
||||
operators::StellarEquilibriumPhysicalCoreType<std::remove_cvref_t<Model>>>> {
|
||||
private:
|
||||
using Compilation = operators::CompiledStellarEquilibriumSystem<
|
||||
std::remove_cvref_t<Model>>;
|
||||
using PhysicalCore = operators::StellarEquilibriumPhysicalCoreType<
|
||||
std::remove_cvref_t<Model>>;
|
||||
using Compilation = operators::CompiledStellarEquilibriumSystem<std::remove_cvref_t<Model>>;
|
||||
using PhysicalCore = operators::StellarEquilibriumPhysicalCoreType<std::remove_cvref_t<Model>>;
|
||||
|
||||
public:
|
||||
using ContributionCouplings =
|
||||
typename Compilation::ContributionJacobianCouplings;
|
||||
using UnsupportedCouplings =
|
||||
typename CollectUnsupportedStellarStructureCouplings<
|
||||
ContributionCouplings,
|
||||
std::remove_cvref_t<Model>,
|
||||
PhysicalCore,
|
||||
typename std::remove_cvref_t<Model>::SpecificationTypes,
|
||||
utils::blocks::type_list<>>::Type;
|
||||
using ContributionCouplings = typename Compilation::ContributionJacobianCouplings;
|
||||
using UnsupportedCouplings = typename CollectUnsupportedStellarStructureCouplings<
|
||||
ContributionCouplings,
|
||||
std::remove_cvref_t<Model>,
|
||||
PhysicalCore,
|
||||
typename std::remove_cvref_t<Model>::SpecificationTypes,
|
||||
utils::blocks::type_list<>>::Type;
|
||||
|
||||
static constexpr bool supported = UnsupportedCouplings::size == 0;
|
||||
};
|
||||
@@ -369,13 +332,11 @@ export namespace mean_field::preconditioning {
|
||||
* detection-safe and therefore suitable for constraining factories. */
|
||||
template <typename Candidate>
|
||||
struct DefaultStellarStructurePhysicalTopologySupport
|
||||
: detail::DefaultStellarStructurePhysicalTopologyAudit<
|
||||
std::remove_cvref_t<Candidate>> { };
|
||||
: detail::DefaultStellarStructurePhysicalTopologyAudit<std::remove_cvref_t<Candidate>> { };
|
||||
|
||||
template <typename Candidate>
|
||||
inline constexpr bool defaultStellarStructurePhysicalTopologySupported =
|
||||
DefaultStellarStructurePhysicalTopologySupport<
|
||||
std::remove_cvref_t<Candidate>>::supported;
|
||||
DefaultStellarStructurePhysicalTopologySupport<std::remove_cvref_t<Candidate>>::supported;
|
||||
|
||||
template <typename Candidate>
|
||||
concept DefaultStellarStructurePhysicalTopologySupportedFor =
|
||||
@@ -419,9 +380,8 @@ export namespace mean_field::preconditioning {
|
||||
template <typename Form> struct IdentityPlanForForm;
|
||||
|
||||
template <typename... Values, typename... Residuals>
|
||||
struct IdentityPlanForForm<utils::blocks::block_form<
|
||||
utils::blocks::type_list<Values...>,
|
||||
utils::blocks::type_list<Residuals...>>> {
|
||||
struct IdentityPlanForForm<
|
||||
utils::blocks::block_form<utils::blocks::type_list<Values...>, utils::blocks::type_list<Residuals...>>> {
|
||||
static_assert(sizeof...(Values) == sizeof...(Residuals));
|
||||
using Type = PreconditionerPlan<IdentityBlock<Values, Residuals>...>;
|
||||
|
||||
|
||||
83
libmeanfield/interface/preconditioning/stellar_recipe.cppm
Normal file
83
libmeanfield/interface/preconditioning/stellar_recipe.cppm
Normal file
@@ -0,0 +1,83 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:preconditioning.stellar_recipe;
|
||||
|
||||
export import :preconditioning.equilibrium_coordinates;
|
||||
|
||||
export namespace mean_field::preconditioning {
|
||||
/*
|
||||
* A stellar-preconditioner prescription is an unbound, owning value. It
|
||||
* may therefore be created before a problem exists and safely moved into
|
||||
* the eventual user-owned solve context. A prepared inverse is deliberately a
|
||||
* separate, problem-bound object with a stable address.
|
||||
*/
|
||||
struct StellarPreconditionerPrescriptionTag { };
|
||||
|
||||
template <typename Candidate>
|
||||
concept StellarPreconditionerPrescription =
|
||||
std::derived_from<std::remove_cvref_t<Candidate>, StellarPreconditionerPrescriptionTag> &&
|
||||
std::move_constructible<std::remove_cvref_t<Candidate>>;
|
||||
|
||||
struct DefaultStellarPreconditioner final : StellarPreconditionerPrescriptionTag { };
|
||||
|
||||
/*
|
||||
* This overload is the user-facing, problem-independent factory. The
|
||||
* existing makePreconditioner(problem) overload remains the low-level
|
||||
* factory for the typed, unprepared block assembled below.
|
||||
*/
|
||||
[[nodiscard]] constexpr DefaultStellarPreconditioner makePreconditioner() noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename Candidate, typename Problem>
|
||||
concept PreparedStellarInverseFor =
|
||||
equilibrium::DiscretizedStellarEquilibriumProblem<std::remove_cvref_t<Problem>> &&
|
||||
std::derived_from<std::remove_cvref_t<Candidate>, mfem::Solver> &&
|
||||
std::destructible<std::remove_cvref_t<Candidate>> &&
|
||||
requires(std::remove_cvref_t<Candidate> &prepared, const std::remove_cvref_t<Candidate> &constantPrepared) {
|
||||
{ constantPrepared.GetProblem() } -> std::same_as<const std::remove_cvref_t<Problem> &>;
|
||||
{ constantPrepared.IsCurrent() } -> std::same_as<bool>;
|
||||
prepared.Refresh();
|
||||
};
|
||||
|
||||
/*
|
||||
* Built-in preparation is intentionally policy-first. The same spelling
|
||||
* can be supplied beside a third-party prescription and found by ADL,
|
||||
* without adding that prescription to a central registry or switch.
|
||||
* Preparation requires an already-prepared problem because the current
|
||||
* physical inverse assembles state-dependent numerical data.
|
||||
*/
|
||||
template <DefaultStellarPreconditionerAvailableFor Problem>
|
||||
[[nodiscard]] auto prepareStellarPreconditioner(
|
||||
DefaultStellarPreconditioner,
|
||||
const Problem &problem
|
||||
) {
|
||||
return preconditioning::prepare(problem, preconditioning::makePreconditioner(problem));
|
||||
}
|
||||
|
||||
template <typename Prescription, typename Problem>
|
||||
concept StellarPreconditionerRuntimeAvailableFor =
|
||||
StellarPreconditionerPrescription<Prescription> &&
|
||||
equilibrium::DiscretizedStellarEquilibriumProblem<std::remove_cvref_t<Problem>> &&
|
||||
requires(std::remove_cvref_t<Prescription> prescription, const std::remove_cvref_t<Problem> &problem) {
|
||||
requires std::same_as<
|
||||
decltype(prepareStellarPreconditioner(std::move(prescription), problem)),
|
||||
std::remove_cvref_t<decltype(prepareStellarPreconditioner(std::move(prescription), problem))>>;
|
||||
{
|
||||
prepareStellarPreconditioner(std::move(prescription), problem)
|
||||
} -> PreparedStellarInverseFor<std::remove_cvref_t<Problem>>;
|
||||
};
|
||||
|
||||
template <StellarPreconditionerPrescription Prescription, typename Problem>
|
||||
requires StellarPreconditionerRuntimeAvailableFor<Prescription, Problem>
|
||||
using PreparedStellarInverseType = std::remove_cvref_t<decltype(prepareStellarPreconditioner(
|
||||
std::declval<std::remove_cvref_t<Prescription> &&>(),
|
||||
std::declval<const std::remove_cvref_t<Problem> &>()
|
||||
))>;
|
||||
} // namespace mean_field::preconditioning
|
||||
@@ -634,13 +634,11 @@ export namespace mean_field::preconditioning {
|
||||
* Add future cores here only together with matching cross-coupling and
|
||||
* preparation implementations.
|
||||
*/
|
||||
template <typename PhysicalCore>
|
||||
struct StellarStructureExecutableRuntime {
|
||||
template <typename PhysicalCore> struct StellarStructureExecutableRuntime {
|
||||
static constexpr bool available = false;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct StellarStructureExecutableRuntime<operators::PreparedStellarEquilibriumOperator> {
|
||||
template <> struct StellarStructureExecutableRuntime<operators::PreparedStellarEquilibriumOperator> {
|
||||
static constexpr bool available = true;
|
||||
};
|
||||
|
||||
@@ -654,14 +652,12 @@ export namespace mean_field::preconditioning {
|
||||
|
||||
template <typename Descriptor, typename PhysicalCore>
|
||||
concept StellarStructureRuntimeFor =
|
||||
MaterialSurfaceRuntimeFor<Descriptor, PhysicalCore> &&
|
||||
ExecutableStellarStructureRuntimeFor<PhysicalCore>;
|
||||
MaterialSurfaceRuntimeFor<Descriptor, PhysicalCore> && ExecutableStellarStructureRuntimeFor<PhysicalCore>;
|
||||
|
||||
template <typename Candidate>
|
||||
concept StellarStructurePreconditionerProblem =
|
||||
equilibrium::DiscretizedStellarEquilibriumProblem<Candidate> &&
|
||||
DefaultStellarStructurePhysicalTopologySupportedFor<
|
||||
typename std::remove_cvref_t<Candidate>::ModelType> &&
|
||||
DefaultStellarStructurePhysicalTopologySupportedFor<typename std::remove_cvref_t<Candidate>::ModelType> &&
|
||||
requires {
|
||||
requires StellarStructureRuntimeFor<
|
||||
MaterialSurfaceDescriptorFor<std::remove_cvref_t<Candidate>>,
|
||||
@@ -688,8 +684,7 @@ export namespace mean_field::preconditioning {
|
||||
preconditioning::prepare(problem, std::move(materialComponent));
|
||||
preconditioning::prepare(
|
||||
problem.GetPhysicalOperator().GetHydrostaticOperator().GetFEM(),
|
||||
problem.GetPhysicalOperator().GetGravityContext().GetGeometryContext(),
|
||||
std::move(gravityComponent)
|
||||
problem.GetPhysicalOperator().GetGravityContext().GetGeometryContext(), std::move(gravityComponent)
|
||||
);
|
||||
StellarStructureCrossJacobianOperator{problem.GetPhysicalOperator()};
|
||||
};
|
||||
@@ -873,19 +868,30 @@ export namespace mean_field::preconditioning {
|
||||
GravityFactorizationPolicy GravityPolicy,
|
||||
StellarStructureFactorizationPolicy StructurePolicy>
|
||||
requires StellarStructurePreparableFor<
|
||||
equilibrium::StellarEquilibriumProblem<Model, Discretization>,
|
||||
equilibrium::StellarEquilibriumProblem<
|
||||
Model,
|
||||
Discretization>,
|
||||
MaterialComponent,
|
||||
GravityFieldBlock<GravityMassBackend, backend::HypreBoomerAMG<Mode>, GravityPolicy>>
|
||||
GravityFieldBlock<
|
||||
GravityMassBackend,
|
||||
backend::HypreBoomerAMG<Mode>,
|
||||
GravityPolicy>>
|
||||
[[nodiscard]] auto prepare(
|
||||
const equilibrium::StellarEquilibriumProblem<Model, Discretization> &problem,
|
||||
const equilibrium::StellarEquilibriumProblem<
|
||||
Model,
|
||||
Discretization> &problem,
|
||||
StellarStructureBlock<
|
||||
MaterialComponent,
|
||||
GravityFieldBlock<
|
||||
GravityMassBackend,
|
||||
backend::HypreBoomerAMG<Mode>,
|
||||
GravityPolicy>,
|
||||
typename equilibrium::StellarEquilibriumProblem<Model, Discretization>::FormType,
|
||||
typename equilibrium::StellarEquilibriumProblem<Model, Discretization>::JacobianFormType,
|
||||
typename equilibrium::StellarEquilibriumProblem<
|
||||
Model,
|
||||
Discretization>::FormType,
|
||||
typename equilibrium::StellarEquilibriumProblem<
|
||||
Model,
|
||||
Discretization>::JacobianFormType,
|
||||
StructurePolicy> structure
|
||||
) {
|
||||
return PreparedStellarStructureBlock<
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@@ -59,13 +59,14 @@ export namespace mean_field::seed {
|
||||
|
||||
/* An explicit opt-in for a specification that leaves a radial seed unchanged. */
|
||||
struct NoStateChange {
|
||||
static constexpr bool registered = true;
|
||||
static constexpr bool providesRadialMass = false;
|
||||
static constexpr bool registered = true;
|
||||
static constexpr bool providesRadialMass = false;
|
||||
|
||||
template <typename Model>
|
||||
static constexpr bool supports = true;
|
||||
template <typename Model> static constexpr bool supports = true;
|
||||
|
||||
template <typename Specification, typename Model>
|
||||
template <
|
||||
typename Specification,
|
||||
typename Model>
|
||||
static void validate(
|
||||
const Specification &,
|
||||
const Model &,
|
||||
@@ -74,7 +75,9 @@ export namespace mean_field::seed {
|
||||
) noexcept {
|
||||
}
|
||||
|
||||
template <typename Specification, typename Model>
|
||||
template <
|
||||
typename Specification,
|
||||
typename Model>
|
||||
static void initialize(
|
||||
const Specification &,
|
||||
const Model &,
|
||||
@@ -115,19 +118,17 @@ export namespace mean_field::seed {
|
||||
}
|
||||
|
||||
struct UnavailableRadialProjectionPhysics final {
|
||||
static constexpr bool registered = false;
|
||||
static constexpr bool providesRadialMass = false;
|
||||
static constexpr bool registered = false;
|
||||
static constexpr bool providesRadialMass = false;
|
||||
|
||||
template <typename Model>
|
||||
static constexpr bool supports = false;
|
||||
template <typename Model> static constexpr bool supports = false;
|
||||
};
|
||||
|
||||
struct FixedTotalMassRadialProjectionPhysics final {
|
||||
static constexpr bool registered = true;
|
||||
static constexpr bool providesRadialMass = true;
|
||||
static constexpr bool registered = true;
|
||||
static constexpr bool providesRadialMass = true;
|
||||
|
||||
template <typename Model>
|
||||
static constexpr bool supports = true;
|
||||
template <typename Model> static constexpr bool supports = true;
|
||||
|
||||
[[nodiscard]] static dimensions::MassValue targetMass(const models::FixedTotalMass &specification) {
|
||||
return specification.targetMass();
|
||||
@@ -164,10 +165,7 @@ export namespace mean_field::seed {
|
||||
static constexpr bool providesRadialMass = false;
|
||||
|
||||
template <typename Model>
|
||||
static constexpr bool supports = requires(
|
||||
const Model &model,
|
||||
const surface::Isobaric &condition
|
||||
) {
|
||||
static constexpr bool supports = requires(const Model &model, const surface::Isobaric &condition) {
|
||||
{
|
||||
eos::evaluate<dimensions::quantity::SpecificEnthalpy>(
|
||||
model.equationOfState(), condition.targetPressure()
|
||||
@@ -213,11 +211,10 @@ export namespace mean_field::seed {
|
||||
};
|
||||
|
||||
struct FixedCentralDensityRadialProjectionPhysics final {
|
||||
static constexpr bool registered = true;
|
||||
static constexpr bool providesRadialMass = false;
|
||||
static constexpr bool registered = true;
|
||||
static constexpr bool providesRadialMass = false;
|
||||
|
||||
template <typename Model>
|
||||
static constexpr bool supports = true;
|
||||
template <typename Model> static constexpr bool supports = true;
|
||||
|
||||
template <typename Model>
|
||||
static void validate(
|
||||
@@ -246,11 +243,10 @@ export namespace mean_field::seed {
|
||||
};
|
||||
|
||||
struct FixedAngularMomentumRadialProjectionPhysics final {
|
||||
static constexpr bool registered = true;
|
||||
static constexpr bool providesRadialMass = false;
|
||||
static constexpr bool registered = true;
|
||||
static constexpr bool providesRadialMass = false;
|
||||
|
||||
template <typename Model>
|
||||
static constexpr bool supports = true;
|
||||
template <typename Model> static constexpr bool supports = true;
|
||||
|
||||
template <typename Model>
|
||||
static void validate(
|
||||
@@ -316,12 +312,12 @@ export namespace mean_field::seed {
|
||||
};
|
||||
|
||||
template <typename Candidate> struct UnwrapRadialProjectionPhysics {
|
||||
using Type = UnavailableRadialProjectionPhysics;
|
||||
using Type = UnavailableRadialProjectionPhysics;
|
||||
static constexpr bool valid = false;
|
||||
};
|
||||
|
||||
template <typename Physics> struct UnwrapRadialProjectionPhysics<projection::Use<Physics>> {
|
||||
using Type = Physics;
|
||||
using Type = Physics;
|
||||
static constexpr bool valid = true;
|
||||
};
|
||||
|
||||
@@ -358,7 +354,9 @@ export namespace mean_field::seed {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Specification, typename Model>
|
||||
template <
|
||||
typename Specification,
|
||||
typename Model>
|
||||
[[nodiscard]] consteval bool radialProjectionPhysicsIsComplete() {
|
||||
using Physics = typename SelectRadialProjectionPhysics<Specification>::Type;
|
||||
if constexpr (!radialProjectionPhysicsRegistered<Physics>()) {
|
||||
@@ -370,12 +368,9 @@ export namespace mean_field::seed {
|
||||
} else if constexpr (!static_cast<bool>(Physics::template supports<Model>)) {
|
||||
return false;
|
||||
} else if constexpr (!requires(
|
||||
const Specification &specification,
|
||||
const Model &model,
|
||||
const RadialProfile &profile,
|
||||
const StellarEquilibriumProjectionOptions &options,
|
||||
const RadialProjectionScales &scales,
|
||||
RadialProjectionState &state,
|
||||
const Specification &specification, const Model &model,
|
||||
const RadialProfile &profile, const StellarEquilibriumProjectionOptions &options,
|
||||
const RadialProjectionScales &scales, RadialProjectionState &state,
|
||||
mfem::Vector coordinate
|
||||
) {
|
||||
Physics::validate(specification, model, profile, options);
|
||||
@@ -417,21 +412,24 @@ export namespace mean_field::seed {
|
||||
|
||||
static constexpr std::size_t radialMassProviderCount =
|
||||
(std::size_t{0} + ... +
|
||||
(radialProjectionPhysicsProvidesMass<
|
||||
typename SelectRadialProjectionPhysics<Specifications>::Type>()
|
||||
(radialProjectionPhysicsProvidesMass<typename SelectRadialProjectionPhysics<Specifications>::Type>()
|
||||
? std::size_t{1}
|
||||
: std::size_t{0}));
|
||||
static constexpr bool complete = radialMassProviderCount == 1 &&
|
||||
(radialProjectionPhysicsIsComplete<Specifications, ModelType>() && ...);
|
||||
static constexpr bool complete =
|
||||
radialMassProviderCount == 1 && (radialProjectionPhysicsIsComplete<Specifications, ModelType>() && ...);
|
||||
|
||||
[[nodiscard]] static dimensions::MassValue targetMass(const ModelType &model) requires complete {
|
||||
[[nodiscard]] static dimensions::MassValue targetMass(const ModelType &model)
|
||||
requires complete
|
||||
{
|
||||
dimensions::MassValue result{0.0};
|
||||
([&] {
|
||||
using Physics = typename SelectRadialProjectionPhysics<Specifications>::Type;
|
||||
if constexpr (radialProjectionPhysicsProvidesMass<Physics>()) {
|
||||
result = Physics::targetMass(model.template specification<Specifications>());
|
||||
}
|
||||
}(), ...);
|
||||
(
|
||||
[&] {
|
||||
using Physics = typename SelectRadialProjectionPhysics<Specifications>::Type;
|
||||
if constexpr (radialProjectionPhysicsProvidesMass<Physics>()) {
|
||||
result = Physics::targetMass(model.template specification<Specifications>());
|
||||
}
|
||||
}(),
|
||||
...);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -439,11 +437,15 @@ export namespace mean_field::seed {
|
||||
const ModelType &model,
|
||||
const RadialProfile &profile,
|
||||
const StellarEquilibriumProjectionOptions &options
|
||||
) requires complete {
|
||||
([&] {
|
||||
using Physics = typename SelectRadialProjectionPhysics<Specifications>::Type;
|
||||
Physics::validate(model.template specification<Specifications>(), model, profile, options);
|
||||
}(), ...);
|
||||
)
|
||||
requires complete
|
||||
{
|
||||
(
|
||||
[&] {
|
||||
using Physics = typename SelectRadialProjectionPhysics<Specifications>::Type;
|
||||
Physics::validate(model.template specification<Specifications>(), model, profile, options);
|
||||
}(),
|
||||
...);
|
||||
}
|
||||
|
||||
template <typename StateView>
|
||||
@@ -452,27 +454,32 @@ export namespace mean_field::seed {
|
||||
const RadialProjectionScales &scales,
|
||||
RadialProjectionState &state,
|
||||
const StateView &stateView
|
||||
) requires complete {
|
||||
([&] {
|
||||
using Contribution = models::SpecificationContribution<Specifications>;
|
||||
using Physics = typename SelectRadialProjectionPhysics<Specifications>::Type;
|
||||
if constexpr (Contribution::generatedValueArity == 0) {
|
||||
Physics::initialize(
|
||||
model.template specification<Specifications>(), model, scales, state, mfem::Vector{}
|
||||
);
|
||||
} else {
|
||||
static_assert(
|
||||
Contribution::generatedValueArity == 1,
|
||||
"Radial projection currently requires each specification contribution to generate at "
|
||||
"most one scalar coordinate."
|
||||
);
|
||||
using Term = RadialProjectionCoordinateTerm<Specifications, Contribution::generatedStateKind>;
|
||||
Physics::initialize(
|
||||
model.template specification<Specifications>(), model, scales, state,
|
||||
stateView.block(Term{})
|
||||
);
|
||||
}
|
||||
}(), ...);
|
||||
)
|
||||
requires complete
|
||||
{
|
||||
(
|
||||
[&] {
|
||||
using Contribution = models::SpecificationContribution<Specifications>;
|
||||
using Physics = typename SelectRadialProjectionPhysics<Specifications>::Type;
|
||||
if constexpr (Contribution::generatedValueArity == 0) {
|
||||
Physics::initialize(
|
||||
model.template specification<Specifications>(), model, scales, state, mfem::Vector{}
|
||||
);
|
||||
} else {
|
||||
static_assert(
|
||||
Contribution::generatedValueArity == 1,
|
||||
"Radial projection currently requires each specification contribution to generate at "
|
||||
"most one scalar coordinate."
|
||||
);
|
||||
using Term =
|
||||
RadialProjectionCoordinateTerm<Specifications, Contribution::generatedStateKind>;
|
||||
Physics::initialize(
|
||||
model.template specification<Specifications>(), model, scales, state,
|
||||
stateView.block(Term{})
|
||||
);
|
||||
}
|
||||
}(),
|
||||
...);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -494,21 +501,25 @@ export namespace mean_field::seed {
|
||||
concept RadialProfileProjectableModel =
|
||||
model::StellarModelType<Candidate> && radialProjectionIsCompilable<std::remove_cvref_t<Candidate>>;
|
||||
|
||||
template <equilibrium::StellarEquilibriumModel Model, equilibrium::StellarDiscretizationType Discretization>
|
||||
template <
|
||||
equilibrium::StellarEquilibriumModel Model,
|
||||
equilibrium::StellarDiscretizationType Discretization>
|
||||
requires RadialProfileProjectableModel<Model>
|
||||
[[nodiscard]] ProjectedEquilibriumState<Model> projectRadialProfile(
|
||||
const equilibrium::StellarEquilibriumProblem<Model, Discretization> &problem,
|
||||
equilibrium::StellarEquilibriumProblem<
|
||||
Model,
|
||||
Discretization> &problem,
|
||||
const RadialProfile &profile,
|
||||
const StellarEquilibriumProjectionOptions &options = {}
|
||||
) {
|
||||
using Projection = detail::CompileRadialProjection<
|
||||
std::remove_cvref_t<Model>,
|
||||
typename std::remove_cvref_t<Model>::SpecificationTypes>;
|
||||
std::remove_cvref_t<Model>, typename std::remove_cvref_t<Model>::SpecificationTypes>;
|
||||
const auto &stellarModel = problem.GetStellarModel();
|
||||
Projection::validate(stellarModel, profile, options);
|
||||
const dimensions::MassValue targetMass = Projection::targetMass(stellarModel);
|
||||
const dimensions::MassValue targetMass = Projection::targetMass(stellarModel);
|
||||
const detail::ProjectedRadialFields fields = detail::projectRadialFields(
|
||||
problem.GetDiscretization().finiteElementModel(), profile, targetMass, options
|
||||
equilibrium::detail::StellarEquilibriumProblemFactory::MutableFiniteElementModelForProjection(problem),
|
||||
profile, targetMass, options
|
||||
);
|
||||
|
||||
mfem::Vector values(problem.StateSize());
|
||||
@@ -563,11 +574,15 @@ export namespace mean_field::seed {
|
||||
equilibrium::StellarDiscretizationType Discretization,
|
||||
typename Strategy>
|
||||
requires RadialSeedStrategyFor<
|
||||
Strategy,
|
||||
typename equilibrium::StellarEquilibriumProblem<Model, Discretization>::ModelType> &&
|
||||
Strategy,
|
||||
typename equilibrium::StellarEquilibriumProblem<
|
||||
Model,
|
||||
Discretization>::ModelType> &&
|
||||
RadialProfileProjectableModel<Model>
|
||||
[[nodiscard]] ProjectedEquilibriumState<Model> makeProjectedEquilibriumState(
|
||||
const equilibrium::StellarEquilibriumProblem<Model, Discretization> &problem,
|
||||
equilibrium::StellarEquilibriumProblem<
|
||||
Model,
|
||||
Discretization> &problem,
|
||||
const Strategy &strategy,
|
||||
const StellarEquilibriumProjectionOptions &options = {}
|
||||
) {
|
||||
|
||||
868
libmeanfield/interface/solver/linear_backend.cppm
Normal file
868
libmeanfield/interface/solver/linear_backend.cppm
Normal file
@@ -0,0 +1,868 @@
|
||||
module;
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
#include <mpi.h>
|
||||
|
||||
export module mean_field:solver.linear_backend;
|
||||
|
||||
export import :preconditioning.backend;
|
||||
|
||||
export namespace mean_field::solver {
|
||||
enum class LinearSolveStatus : std::uint8_t {
|
||||
converged,
|
||||
maximum_iterations,
|
||||
breakdown,
|
||||
non_finite,
|
||||
backend_failure
|
||||
};
|
||||
|
||||
struct LinearSolveControl final {
|
||||
double relativeTolerance{1.0e-8};
|
||||
double absoluteTolerance{0.0};
|
||||
int maximumIterations{100};
|
||||
|
||||
void Validate() const {
|
||||
if (!std::isfinite(relativeTolerance) || relativeTolerance < 0.0) {
|
||||
throw std::invalid_argument("A linear solve requires a finite, non-negative relative tolerance.");
|
||||
}
|
||||
if (!std::isfinite(absoluteTolerance) || absoluteTolerance < 0.0) {
|
||||
throw std::invalid_argument("A linear solve requires a finite, non-negative absolute tolerance.");
|
||||
}
|
||||
if (maximumIterations <= 0) {
|
||||
throw std::invalid_argument("A linear solve requires at least one permitted iteration.");
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] double ConvergenceThreshold(const double globalRightHandSideNorm) const {
|
||||
Validate();
|
||||
if (!std::isfinite(globalRightHandSideNorm) || globalRightHandSideNorm < 0.0) {
|
||||
throw std::invalid_argument("A linear solve requires a finite, non-negative right-hand-side norm.");
|
||||
}
|
||||
const double relativeThreshold = relativeTolerance * globalRightHandSideNorm;
|
||||
if (!std::isfinite(relativeThreshold)) {
|
||||
throw std::invalid_argument("The linear relative convergence threshold must be finite.");
|
||||
}
|
||||
return absoluteTolerance > relativeThreshold ? absoluteTolerance : relativeThreshold;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Numerical termination is data, not an exception. Implementations throw
|
||||
* for invalid controls, configuration, dimensions, or violated lifetime
|
||||
* contracts. All reported norms are communicator-global Euclidean norms.
|
||||
* Solve uses the incoming correction as its initial guess and overwrites
|
||||
* it with the final correction, so initialResidualNorm is ||b - A x_0||.
|
||||
* Convergence remains relative to the right-hand side rather than the
|
||||
* quality of a particular initial guess:
|
||||
*
|
||||
* ||b - A x|| <= max(absoluteTolerance,
|
||||
* relativeTolerance * ||b||).
|
||||
*
|
||||
* For a zero right-hand side, relativeTrueResidualNorm is zero exactly
|
||||
* when the true residual is zero and positive infinity otherwise. The
|
||||
* true-residual fields are distinct from the backend's recurrence so
|
||||
* callers never have to infer one from the other. This is deliberately
|
||||
* fixed-size: recording a history is an optional backend concern whose
|
||||
* storage must be owned and reserved by the prepared runtime, not allocated
|
||||
* while Solve is active.
|
||||
*/
|
||||
struct LinearSolveReport final {
|
||||
LinearSolveStatus status{LinearSolveStatus::backend_failure};
|
||||
LinearSolveControl control{};
|
||||
int iterations{0};
|
||||
int restarts{0};
|
||||
double rightHandSideNorm{0.0};
|
||||
double initialResidualNorm{0.0};
|
||||
double reportedResidualNorm{0.0};
|
||||
double trueResidualNorm{0.0};
|
||||
double relativeTrueResidualNorm{0.0};
|
||||
// Includes MFEM's initial-guess residual application and the
|
||||
// post-solve application used to verify the true residual.
|
||||
std::uint64_t operatorApplications{0};
|
||||
std::uint64_t inversePreconditionerApplications{0};
|
||||
double solveSeconds{0.0};
|
||||
// These totals include only completed applications. Operator time also
|
||||
// includes the post-solve true-residual verification application.
|
||||
double operatorSeconds{0.0};
|
||||
double inversePreconditionerSeconds{0.0};
|
||||
|
||||
[[nodiscard]] bool Converged() const noexcept {
|
||||
return status == LinearSolveStatus::converged;
|
||||
}
|
||||
};
|
||||
|
||||
struct LinearBackendConfigurationTag { };
|
||||
|
||||
template <typename Candidate>
|
||||
concept LinearBackendConfiguration =
|
||||
std::derived_from<std::remove_cvref_t<Candidate>, LinearBackendConfigurationTag> &&
|
||||
std::move_constructible<std::remove_cvref_t<Candidate>> && requires {
|
||||
requires std::same_as<
|
||||
std::remove_cv_t<decltype(std::remove_cvref_t<Candidate>::supportedPreconditionerContract)>,
|
||||
preconditioning::ApplicationContract>;
|
||||
typename std::integral_constant<
|
||||
preconditioning::ApplicationContract, std::remove_cvref_t<Candidate>::supportedPreconditionerContract>;
|
||||
requires(
|
||||
std::remove_cvref_t<Candidate>::supportedPreconditionerContract ==
|
||||
preconditioning::ApplicationContract::stationary_linear ||
|
||||
std::remove_cvref_t<Candidate>::supportedPreconditionerContract ==
|
||||
preconditioning::ApplicationContract::flexible
|
||||
);
|
||||
};
|
||||
|
||||
} // namespace mean_field::solver
|
||||
|
||||
namespace mean_field::solver::detail {
|
||||
template <typename Candidate>
|
||||
concept StaticPreconditionerContractDeclared = requires { &std::remove_cvref_t<Candidate>::applicationContract; };
|
||||
|
||||
template <typename Candidate>
|
||||
concept ExactStaticPreconditionerContract = requires {
|
||||
requires std::same_as<
|
||||
std::remove_cv_t<decltype(std::remove_cvref_t<Candidate>::applicationContract)>,
|
||||
preconditioning::ApplicationContract>;
|
||||
typename std::integral_constant<
|
||||
preconditioning::ApplicationContract, std::remove_cvref_t<Candidate>::applicationContract>;
|
||||
requires(
|
||||
std::remove_cvref_t<Candidate>::applicationContract ==
|
||||
preconditioning::ApplicationContract::stationary_linear ||
|
||||
std::remove_cvref_t<Candidate>::applicationContract == preconditioning::ApplicationContract::flexible
|
||||
);
|
||||
};
|
||||
|
||||
template <typename Candidate, typename = void> struct StaticPreconditionerContract {
|
||||
static constexpr bool declared = StaticPreconditionerContractDeclared<Candidate>;
|
||||
static constexpr bool registered = false;
|
||||
static constexpr preconditioning::ApplicationContract value =
|
||||
preconditioning::ApplicationContract::stationary_linear;
|
||||
};
|
||||
|
||||
template <typename Candidate>
|
||||
struct StaticPreconditionerContract<Candidate, std::enable_if_t<ExactStaticPreconditionerContract<Candidate>>> {
|
||||
static constexpr bool declared = true;
|
||||
static constexpr auto value = std::remove_cvref_t<Candidate>::applicationContract;
|
||||
static constexpr bool registered = true;
|
||||
};
|
||||
|
||||
template <typename Candidate>
|
||||
concept BackendPreconditionerContractDeclared = requires { typename std::remove_cvref_t<Candidate>::BackendType; };
|
||||
|
||||
template <typename Backend>
|
||||
concept ExactRegisteredBackendPreconditionerContract = requires {
|
||||
requires preconditioning::backend::Registered<std::remove_cvref_t<Backend>>;
|
||||
requires std::same_as<
|
||||
std::remove_cv_t<
|
||||
decltype(preconditioning::backend::Traits<std::remove_cvref_t<Backend>>::applicationContract)>,
|
||||
preconditioning::ApplicationContract>;
|
||||
typename std::integral_constant<
|
||||
preconditioning::ApplicationContract,
|
||||
preconditioning::backend::Traits<std::remove_cvref_t<Backend>>::applicationContract>;
|
||||
requires(
|
||||
preconditioning::backend::Traits<std::remove_cvref_t<Backend>>::applicationContract ==
|
||||
preconditioning::ApplicationContract::stationary_linear ||
|
||||
preconditioning::backend::Traits<std::remove_cvref_t<Backend>>::applicationContract ==
|
||||
preconditioning::ApplicationContract::flexible
|
||||
);
|
||||
};
|
||||
|
||||
template <typename Candidate>
|
||||
concept ExactBackendPreconditionerContract =
|
||||
BackendPreconditionerContractDeclared<Candidate> &&
|
||||
ExactRegisteredBackendPreconditionerContract<typename std::remove_cvref_t<Candidate>::BackendType>;
|
||||
|
||||
template <typename Candidate, typename = void> struct BackendPreconditionerContract {
|
||||
static constexpr bool declared = BackendPreconditionerContractDeclared<Candidate>;
|
||||
static constexpr bool registered = false;
|
||||
static constexpr preconditioning::ApplicationContract value =
|
||||
preconditioning::ApplicationContract::stationary_linear;
|
||||
};
|
||||
|
||||
template <typename Candidate>
|
||||
struct BackendPreconditionerContract<Candidate, std::enable_if_t<ExactBackendPreconditionerContract<Candidate>>> {
|
||||
private:
|
||||
using Backend = typename std::remove_cvref_t<Candidate>::BackendType;
|
||||
|
||||
public:
|
||||
static constexpr bool declared = true;
|
||||
static constexpr bool registered = true;
|
||||
static constexpr preconditioning::ApplicationContract value =
|
||||
preconditioning::backend::Traits<std::remove_cvref_t<Backend>>::applicationContract;
|
||||
};
|
||||
|
||||
template <typename Candidate> struct DirectPreconditionerContractAudit final {
|
||||
private:
|
||||
using StaticContract = StaticPreconditionerContract<Candidate>;
|
||||
using BackendContract = BackendPreconditionerContract<Candidate>;
|
||||
|
||||
public:
|
||||
static constexpr bool declarationsValid = (!StaticContract::declared || StaticContract::registered) &&
|
||||
(!BackendContract::declared || BackendContract::registered);
|
||||
static constexpr bool sourcesAgree = !StaticContract::registered || !BackendContract::registered ||
|
||||
StaticContract::value == BackendContract::value;
|
||||
static constexpr bool registered =
|
||||
declarationsValid && sourcesAgree && (StaticContract::registered || BackendContract::registered);
|
||||
static constexpr preconditioning::ApplicationContract value = [] {
|
||||
if constexpr (StaticContract::registered) {
|
||||
return StaticContract::value;
|
||||
} else if constexpr (BackendContract::registered) {
|
||||
return BackendContract::value;
|
||||
} else {
|
||||
return preconditioning::ApplicationContract::stationary_linear;
|
||||
}
|
||||
}();
|
||||
};
|
||||
|
||||
template <typename Candidate>
|
||||
concept PhysicalInversePreconditionerContractDeclared =
|
||||
requires(const std::remove_cvref_t<Candidate> &candidate) { candidate.GetPhysicalInverse(); };
|
||||
|
||||
template <typename Candidate>
|
||||
using PhysicalInverseType =
|
||||
std::remove_cvref_t<decltype(std::declval<const std::remove_cvref_t<Candidate> &>().GetPhysicalInverse())>;
|
||||
|
||||
template <typename Candidate>
|
||||
concept ExactPhysicalInversePreconditionerContract =
|
||||
PhysicalInversePreconditionerContractDeclared<Candidate> &&
|
||||
DirectPreconditionerContractAudit<PhysicalInverseType<Candidate>>::registered;
|
||||
|
||||
template <typename Candidate, typename = void> struct PhysicalInversePreconditionerContract {
|
||||
static constexpr bool declared = PhysicalInversePreconditionerContractDeclared<Candidate>;
|
||||
static constexpr bool registered = false;
|
||||
static constexpr preconditioning::ApplicationContract value =
|
||||
preconditioning::ApplicationContract::stationary_linear;
|
||||
};
|
||||
|
||||
template <typename Candidate>
|
||||
struct PhysicalInversePreconditionerContract<
|
||||
Candidate,
|
||||
std::enable_if_t<ExactPhysicalInversePreconditionerContract<Candidate>>> {
|
||||
using PhysicalInverse = PhysicalInverseType<Candidate>;
|
||||
using ContractAudit = DirectPreconditionerContractAudit<PhysicalInverse>;
|
||||
|
||||
static constexpr bool declared = true;
|
||||
static constexpr bool registered = true;
|
||||
static constexpr preconditioning::ApplicationContract value = ContractAudit::value;
|
||||
};
|
||||
|
||||
template <typename Candidate> struct LinearPreconditionerContractAudit final {
|
||||
private:
|
||||
using StaticContract = StaticPreconditionerContract<Candidate>;
|
||||
using BackendContract = BackendPreconditionerContract<Candidate>;
|
||||
using PhysicalContract = PhysicalInversePreconditionerContract<Candidate>;
|
||||
|
||||
public:
|
||||
static constexpr bool declarationsValid = (!StaticContract::declared || StaticContract::registered) &&
|
||||
(!BackendContract::declared || BackendContract::registered) &&
|
||||
(!PhysicalContract::declared || PhysicalContract::registered);
|
||||
static constexpr bool sourcesAgree = (!StaticContract::registered || !BackendContract::registered ||
|
||||
StaticContract::value == BackendContract::value) &&
|
||||
(!StaticContract::registered || !PhysicalContract::registered ||
|
||||
StaticContract::value == PhysicalContract::value) &&
|
||||
(!BackendContract::registered || !PhysicalContract::registered ||
|
||||
BackendContract::value == PhysicalContract::value);
|
||||
static constexpr bool registered =
|
||||
declarationsValid && sourcesAgree &&
|
||||
(StaticContract::registered || BackendContract::registered || PhysicalContract::registered);
|
||||
static constexpr preconditioning::ApplicationContract value = [] {
|
||||
if constexpr (StaticContract::registered) {
|
||||
return StaticContract::value;
|
||||
} else if constexpr (BackendContract::registered) {
|
||||
return BackendContract::value;
|
||||
} else if constexpr (PhysicalContract::registered) {
|
||||
return PhysicalContract::value;
|
||||
} else {
|
||||
return preconditioning::ApplicationContract::stationary_linear;
|
||||
}
|
||||
}();
|
||||
};
|
||||
} // namespace mean_field::solver::detail
|
||||
|
||||
export namespace mean_field::solver {
|
||||
template <typename Candidate>
|
||||
concept LinearPreconditionerApplicationContractAvailable =
|
||||
detail::LinearPreconditionerContractAudit<std::remove_cvref_t<Candidate>>::registered;
|
||||
|
||||
template <LinearPreconditionerApplicationContractAvailable Candidate>
|
||||
inline constexpr preconditioning::ApplicationContract linearPreconditionerApplicationContract =
|
||||
detail::LinearPreconditionerContractAudit<std::remove_cvref_t<Candidate>>::value;
|
||||
|
||||
template <typename Configuration, typename Preconditioner>
|
||||
concept LinearBackendPreconditionerCompatible =
|
||||
LinearBackendConfiguration<Configuration> && LinearPreconditionerApplicationContractAvailable<Preconditioner> &&
|
||||
(std::remove_cvref_t<Configuration>::supportedPreconditionerContract ==
|
||||
preconditioning::ApplicationContract::flexible ||
|
||||
linearPreconditionerApplicationContract<std::remove_cvref_t<Preconditioner>> ==
|
||||
preconditioning::ApplicationContract::stationary_linear);
|
||||
|
||||
/*
|
||||
* A prepared backend is bound once to the exact operator and inverse that
|
||||
* its owner keeps at stable addresses and identifies the communicator on
|
||||
* which it operates. The communicator supplied to preparation is borrowed;
|
||||
* a backend may retain it or own a congruent duplicate. The handle returned
|
||||
* by GetCommunicator is borrowed from the backend and must not be freed by
|
||||
* the caller. Every prepared backend must be destroyed before MPI_Finalize.
|
||||
* It owns its numerical workspaces; neither copyability nor movability is
|
||||
* required. Solve treats a caller-provided, correctly sized correction
|
||||
* vector as its initial guess and overwrites it with the final correction.
|
||||
*/
|
||||
template <typename Candidate, typename Operator, typename Preconditioner>
|
||||
concept PreparedLinearBackendFor = std::derived_from<std::remove_cvref_t<Operator>, mfem::Operator> &&
|
||||
std::derived_from<std::remove_cvref_t<Preconditioner>, mfem::Solver> &&
|
||||
std::destructible<std::remove_cvref_t<Candidate>> &&
|
||||
requires(
|
||||
std::remove_cvref_t<Candidate> &prepared,
|
||||
const std::remove_cvref_t<Candidate> &constantPrepared,
|
||||
const mfem::Vector &rightHandSide,
|
||||
mfem::Vector &correction,
|
||||
const LinearSolveControl &control
|
||||
) {
|
||||
{
|
||||
constantPrepared.GetOperator()
|
||||
} -> std::same_as<const std::remove_cvref_t<Operator> &>;
|
||||
{
|
||||
constantPrepared.GetPreconditioner()
|
||||
} -> std::same_as<const std::remove_cvref_t<Preconditioner> &>;
|
||||
{ constantPrepared.GetCommunicator() } -> std::same_as<MPI_Comm>;
|
||||
{ constantPrepared.IsReady() } -> std::same_as<bool>;
|
||||
{ constantPrepared.RightHandSideSize() } -> std::same_as<int>;
|
||||
{ constantPrepared.CorrectionSize() } -> std::same_as<int>;
|
||||
{
|
||||
prepared.Solve(rightHandSide, correction, control)
|
||||
} -> std::same_as<LinearSolveReport>;
|
||||
};
|
||||
|
||||
/*
|
||||
* `prepareLinearBackend` is intentionally unqualified in this detection
|
||||
* boundary. A third-party configuration supplies its overload beside the
|
||||
* configuration type and ADL discovers it without a library registry.
|
||||
*/
|
||||
template <typename Configuration, typename Operator, typename Preconditioner>
|
||||
concept LinearBackendRuntimeAvailableFor =
|
||||
LinearBackendPreconditionerCompatible<Configuration, Preconditioner> &&
|
||||
std::derived_from<std::remove_cvref_t<Operator>, mfem::Operator> &&
|
||||
std::derived_from<std::remove_cvref_t<Preconditioner>, mfem::Solver> &&
|
||||
requires(
|
||||
std::remove_cvref_t<Configuration> configuration,
|
||||
const std::remove_cvref_t<Operator> &operation,
|
||||
std::remove_cvref_t<Preconditioner> &preconditioner,
|
||||
MPI_Comm communicator
|
||||
) {
|
||||
requires std::same_as<
|
||||
decltype(prepareLinearBackend(std::move(configuration), operation, preconditioner, communicator)),
|
||||
std::remove_cvref_t<
|
||||
decltype(prepareLinearBackend(std::move(configuration), operation, preconditioner, communicator))>>;
|
||||
{
|
||||
prepareLinearBackend(std::move(configuration), operation, preconditioner, communicator)
|
||||
} -> PreparedLinearBackendFor<std::remove_cvref_t<Operator>, std::remove_cvref_t<Preconditioner>>;
|
||||
};
|
||||
|
||||
template <LinearBackendConfiguration Configuration, typename Operator, typename Preconditioner>
|
||||
requires LinearBackendRuntimeAvailableFor<Configuration, Operator, Preconditioner>
|
||||
using PreparedLinearBackendType = std::remove_cvref_t<decltype(prepareLinearBackend(
|
||||
std::declval<std::remove_cvref_t<Configuration> &&>(),
|
||||
std::declval<const std::remove_cvref_t<Operator> &>(),
|
||||
std::declval<std::remove_cvref_t<Preconditioner> &>(),
|
||||
std::declval<MPI_Comm>()
|
||||
))>;
|
||||
} // namespace mean_field::solver
|
||||
|
||||
export namespace mean_field::solver::linear {
|
||||
struct FGMRESOptions final {
|
||||
int restartLength{50};
|
||||
int printLevel{-1};
|
||||
|
||||
void Validate() const {
|
||||
if (restartLength <= 0) {
|
||||
throw std::invalid_argument("MFEM FGMRES requires a positive restart length.");
|
||||
}
|
||||
if (printLevel < -1 || printLevel > 3) {
|
||||
throw std::invalid_argument("MFEM FGMRES print level must be between -1 and 3.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class FGMRES final : public LinearBackendConfigurationTag {
|
||||
public:
|
||||
static constexpr preconditioning::ApplicationContract supportedPreconditionerContract =
|
||||
preconditioning::ApplicationContract::flexible;
|
||||
|
||||
FGMRES() = default;
|
||||
|
||||
explicit FGMRES(FGMRESOptions options) : m_options(std::move(options)) {
|
||||
m_options.Validate();
|
||||
}
|
||||
|
||||
[[nodiscard]] const FGMRESOptions &GetOptions() const noexcept {
|
||||
return m_options;
|
||||
}
|
||||
|
||||
private:
|
||||
FGMRESOptions m_options{};
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <typename Operation>
|
||||
requires std::derived_from<std::remove_cvref_t<Operation>, mfem::Operator>
|
||||
class CountedOperator final : public mfem::Operator {
|
||||
public:
|
||||
explicit CountedOperator(const Operation &operation)
|
||||
: mfem::Operator(
|
||||
operation.Height(),
|
||||
operation.Width()
|
||||
),
|
||||
m_operation(std::addressof(operation)) {
|
||||
}
|
||||
|
||||
void Mult(
|
||||
const mfem::Vector &input,
|
||||
mfem::Vector &output
|
||||
) const override {
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
m_operation->Mult(input, output);
|
||||
m_seconds += std::chrono::duration<double>(std::chrono::steady_clock::now() - start).count();
|
||||
++m_applications;
|
||||
}
|
||||
|
||||
void Reset() const noexcept {
|
||||
m_applications = 0;
|
||||
m_seconds = 0.0;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint64_t Applications() const noexcept {
|
||||
return m_applications;
|
||||
}
|
||||
|
||||
[[nodiscard]] double Seconds() const noexcept {
|
||||
return m_seconds;
|
||||
}
|
||||
|
||||
private:
|
||||
const Operation *m_operation;
|
||||
mutable std::uint64_t m_applications{0};
|
||||
mutable double m_seconds{0.0};
|
||||
};
|
||||
|
||||
template <typename Operation, typename Preconditioner>
|
||||
requires std::derived_from<std::remove_cvref_t<Operation>, mfem::Operator> &&
|
||||
std::derived_from<std::remove_cvref_t<Preconditioner>, mfem::Solver>
|
||||
class CountedPreconditioner final : public mfem::Solver {
|
||||
public:
|
||||
CountedPreconditioner(
|
||||
const Operation &operation,
|
||||
Preconditioner &preconditioner,
|
||||
const CountedOperator<Operation> &countedOperation
|
||||
)
|
||||
: mfem::Solver(
|
||||
preconditioner.Height(),
|
||||
preconditioner.Width(),
|
||||
false
|
||||
),
|
||||
m_operation(std::addressof(operation)),
|
||||
m_preconditioner(std::addressof(preconditioner)),
|
||||
m_countedOperation(std::addressof(countedOperation)) {
|
||||
m_preconditioner->iterative_mode = false;
|
||||
}
|
||||
|
||||
void SetOperator(const mfem::Operator &operation) override {
|
||||
if (std::addressof(operation) != m_countedOperation) {
|
||||
throw std::invalid_argument("The MFEM FGMRES preconditioner received an unexpected operator.");
|
||||
}
|
||||
m_preconditioner->SetOperator(*m_operation);
|
||||
if (m_preconditioner->Height() != Height() || m_preconditioner->Width() != Width()) {
|
||||
throw std::invalid_argument(
|
||||
"The MFEM FGMRES preconditioner changed dimensions while binding its operator."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void Mult(
|
||||
const mfem::Vector &input,
|
||||
mfem::Vector &output
|
||||
) const override {
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
m_preconditioner->Mult(input, output);
|
||||
m_seconds += std::chrono::duration<double>(std::chrono::steady_clock::now() - start).count();
|
||||
++m_applications;
|
||||
}
|
||||
|
||||
void Reset() const noexcept {
|
||||
m_applications = 0;
|
||||
m_seconds = 0.0;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint64_t Applications() const noexcept {
|
||||
return m_applications;
|
||||
}
|
||||
|
||||
[[nodiscard]] double Seconds() const noexcept {
|
||||
return m_seconds;
|
||||
}
|
||||
|
||||
private:
|
||||
const Operation *m_operation;
|
||||
Preconditioner *m_preconditioner;
|
||||
const CountedOperator<Operation> *m_countedOperation;
|
||||
mutable std::uint64_t m_applications{0};
|
||||
mutable double m_seconds{0.0};
|
||||
};
|
||||
|
||||
[[nodiscard]] inline bool MpiIsUsable() noexcept {
|
||||
int initialized = 0;
|
||||
int finalized = 0;
|
||||
return MPI_Initialized(&initialized) == MPI_SUCCESS && initialized != 0 &&
|
||||
MPI_Finalized(&finalized) == MPI_SUCCESS && finalized == 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AllRanksAgree(
|
||||
const bool localValue,
|
||||
const MPI_Comm communicator
|
||||
) {
|
||||
int local = localValue ? 1 : 0;
|
||||
int global = 0;
|
||||
if (MPI_Allreduce(&local, &global, 1, MPI_INT, MPI_MIN, communicator) != MPI_SUCCESS) {
|
||||
throw std::runtime_error("MFEM FGMRES could not perform a communicator-wide validity check.");
|
||||
}
|
||||
return global != 0;
|
||||
}
|
||||
|
||||
inline void RequireCollectivelyIdenticalConfiguration(
|
||||
const FGMRESOptions &options,
|
||||
const LinearSolveControl &control,
|
||||
const MPI_Comm communicator
|
||||
) {
|
||||
const std::array<double, 2> localRealValues{control.relativeTolerance, control.absoluteTolerance};
|
||||
std::array<double, 2> minimumRealValues{};
|
||||
std::array<double, 2> maximumRealValues{};
|
||||
const std::array<int, 3> localIntegerValues{
|
||||
control.maximumIterations, options.restartLength, options.printLevel
|
||||
};
|
||||
std::array<int, 3> minimumIntegerValues{};
|
||||
std::array<int, 3> maximumIntegerValues{};
|
||||
|
||||
if (MPI_Allreduce(
|
||||
localRealValues.data(), minimumRealValues.data(), static_cast<int>(localRealValues.size()),
|
||||
MPI_DOUBLE, MPI_MIN, communicator
|
||||
) != MPI_SUCCESS ||
|
||||
MPI_Allreduce(
|
||||
localRealValues.data(), maximumRealValues.data(), static_cast<int>(localRealValues.size()),
|
||||
MPI_DOUBLE, MPI_MAX, communicator
|
||||
) != MPI_SUCCESS ||
|
||||
MPI_Allreduce(
|
||||
localIntegerValues.data(), minimumIntegerValues.data(), static_cast<int>(localIntegerValues.size()),
|
||||
MPI_INT, MPI_MIN, communicator
|
||||
) != MPI_SUCCESS ||
|
||||
MPI_Allreduce(
|
||||
localIntegerValues.data(), maximumIntegerValues.data(), static_cast<int>(localIntegerValues.size()),
|
||||
MPI_INT, MPI_MAX, communicator
|
||||
) != MPI_SUCCESS) {
|
||||
throw std::runtime_error("MFEM FGMRES could not validate its distributed configuration.");
|
||||
}
|
||||
|
||||
if (minimumRealValues != maximumRealValues || minimumIntegerValues != maximumIntegerValues) {
|
||||
throw std::invalid_argument(
|
||||
"MFEM FGMRES requires identical options and solve controls on every communicator rank."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool LocallyFinite(const mfem::Vector &values) {
|
||||
for (int index = 0; index < values.Size(); ++index) {
|
||||
if (!std::isfinite(values(index))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline double GlobalNorm(
|
||||
const mfem::Vector &values,
|
||||
const MPI_Comm communicator
|
||||
) {
|
||||
const double localNorm = values.Norml2();
|
||||
const double localNormSquared = localNorm * localNorm;
|
||||
double globalNormSquared = 0.0;
|
||||
if (MPI_Allreduce(&localNormSquared, &globalNormSquared, 1, MPI_DOUBLE, MPI_SUM, communicator) !=
|
||||
MPI_SUCCESS) {
|
||||
throw std::runtime_error("MFEM FGMRES could not reduce a global vector norm.");
|
||||
}
|
||||
if (!std::isfinite(globalNormSquared) || globalNormSquared < 0.0) {
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
return std::sqrt(globalNormSquared);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline double MaximumRankValue(
|
||||
const double localValue,
|
||||
const MPI_Comm communicator
|
||||
) {
|
||||
double maximumValue = 0.0;
|
||||
if (MPI_Allreduce(&localValue, &maximumValue, 1, MPI_DOUBLE, MPI_MAX, communicator) != MPI_SUCCESS) {
|
||||
throw std::runtime_error("MFEM FGMRES could not reduce a communicator-wide timing measurement.");
|
||||
}
|
||||
return maximumValue;
|
||||
}
|
||||
|
||||
template <typename Candidate> [[nodiscard]] bool RuntimeDependencyIsCurrent(const Candidate &candidate) {
|
||||
if constexpr (requires {
|
||||
{ candidate.IsCurrent() } -> std::same_as<bool>;
|
||||
}) {
|
||||
return candidate.IsCurrent();
|
||||
} else if constexpr (requires {
|
||||
{ candidate.IsPrepared() } -> std::same_as<bool>;
|
||||
}) {
|
||||
return candidate.IsPrepared();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Operation, typename Preconditioner>
|
||||
requires std::derived_from<std::remove_cvref_t<Operation>, mfem::Operator> &&
|
||||
std::derived_from<std::remove_cvref_t<Preconditioner>, mfem::Solver>
|
||||
class PreparedFGMRES final {
|
||||
private:
|
||||
using Clock = std::chrono::steady_clock;
|
||||
|
||||
public:
|
||||
PreparedFGMRES(
|
||||
FGMRESOptions options,
|
||||
const Operation &operation,
|
||||
Preconditioner &preconditioner,
|
||||
const MPI_Comm communicator
|
||||
)
|
||||
: m_options(std::move(options)),
|
||||
m_operation(std::addressof(operation)),
|
||||
m_preconditioner(std::addressof(preconditioner)),
|
||||
m_communicator(communicator),
|
||||
m_countedOperation(operation),
|
||||
m_countedPreconditioner(
|
||||
operation,
|
||||
preconditioner,
|
||||
m_countedOperation
|
||||
),
|
||||
m_solver(communicator),
|
||||
m_rightHandSide(operation.Height()),
|
||||
m_operationAction(operation.Height()),
|
||||
m_trueResidual(operation.Height()) {
|
||||
m_options.Validate();
|
||||
if (!MpiIsUsable()) {
|
||||
throw std::logic_error("MFEM FGMRES requires initialized MPI that has not been finalized.");
|
||||
}
|
||||
if (m_communicator == MPI_COMM_NULL) {
|
||||
throw std::invalid_argument("MFEM FGMRES requires a non-null MPI communicator.");
|
||||
}
|
||||
if (operation.Height() <= 0 || operation.Width() <= 0 || operation.Height() != operation.Width() ||
|
||||
preconditioner.Height() != operation.Width() || preconditioner.Width() != operation.Height()) {
|
||||
throw std::invalid_argument(
|
||||
"MFEM FGMRES requires compatible square operator and preconditioner dimensions."
|
||||
);
|
||||
}
|
||||
|
||||
m_rightHandSide = 0.0;
|
||||
m_operationAction = 0.0;
|
||||
m_trueResidual = 0.0;
|
||||
|
||||
m_solver.SetPreconditioner(m_countedPreconditioner);
|
||||
m_solver.SetOperator(m_countedOperation);
|
||||
m_solver.SetKDim(m_options.restartLength);
|
||||
m_solver.SetPrintLevel(m_options.printLevel);
|
||||
m_solver.iterative_mode = true;
|
||||
}
|
||||
|
||||
PreparedFGMRES(const PreparedFGMRES &) = delete;
|
||||
PreparedFGMRES &operator=(const PreparedFGMRES &) = delete;
|
||||
PreparedFGMRES(PreparedFGMRES &&) = delete;
|
||||
PreparedFGMRES &operator=(PreparedFGMRES &&) = delete;
|
||||
|
||||
[[nodiscard]] const Operation &GetOperator() const noexcept {
|
||||
return *m_operation;
|
||||
}
|
||||
|
||||
[[nodiscard]] const Preconditioner &GetPreconditioner() const noexcept {
|
||||
return *m_preconditioner;
|
||||
}
|
||||
|
||||
[[nodiscard]] MPI_Comm GetCommunicator() const noexcept {
|
||||
return m_communicator;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsReady() const {
|
||||
return m_operation != nullptr && m_preconditioner != nullptr && m_communicator != MPI_COMM_NULL &&
|
||||
m_operation->Height() == m_operation->Width() &&
|
||||
m_preconditioner->Height() == m_operation->Width() &&
|
||||
m_preconditioner->Width() == m_operation->Height() &&
|
||||
m_rightHandSide.Size() == m_operation->Height() &&
|
||||
m_operationAction.Size() == m_operation->Height() &&
|
||||
m_trueResidual.Size() == m_operation->Height() && RuntimeDependencyIsCurrent(*m_operation) &&
|
||||
RuntimeDependencyIsCurrent(*m_preconditioner);
|
||||
}
|
||||
|
||||
[[nodiscard]] int RightHandSideSize() const noexcept {
|
||||
return m_operation->Height();
|
||||
}
|
||||
|
||||
[[nodiscard]] int CorrectionSize() const noexcept {
|
||||
return m_operation->Width();
|
||||
}
|
||||
|
||||
[[nodiscard]] LinearSolveReport Solve(
|
||||
const mfem::Vector &rightHandSide,
|
||||
mfem::Vector &correction,
|
||||
const LinearSolveControl &control
|
||||
) {
|
||||
bool localConfigurationIsValid = true;
|
||||
try {
|
||||
m_options.Validate();
|
||||
control.Validate();
|
||||
} catch (const std::invalid_argument &) {
|
||||
localConfigurationIsValid = false;
|
||||
}
|
||||
if (!AllRanksAgree(localConfigurationIsValid, m_communicator)) {
|
||||
throw std::invalid_argument(
|
||||
"MFEM FGMRES requires valid options and solve controls on every communicator rank."
|
||||
);
|
||||
}
|
||||
if (!localConfigurationIsValid) {
|
||||
throw std::invalid_argument("MFEM FGMRES received invalid options or solve controls.");
|
||||
}
|
||||
RequireCollectivelyIdenticalConfiguration(m_options, control, m_communicator);
|
||||
if (!AllRanksAgree(IsReady(), m_communicator)) {
|
||||
throw std::logic_error(
|
||||
"MFEM FGMRES requires a complete, current prepared runtime on every communicator rank."
|
||||
);
|
||||
}
|
||||
if (!AllRanksAgree(
|
||||
rightHandSide.Size() == RightHandSideSize() && correction.Size() == CorrectionSize(),
|
||||
m_communicator
|
||||
)) {
|
||||
throw std::invalid_argument(
|
||||
"MFEM FGMRES received incompatible linear-system vectors on at least one rank."
|
||||
);
|
||||
}
|
||||
if (!AllRanksAgree(LocallyFinite(rightHandSide), m_communicator) ||
|
||||
!AllRanksAgree(LocallyFinite(correction), m_communicator)) {
|
||||
throw std::invalid_argument(
|
||||
"MFEM FGMRES requires finite right-hand side and initial-guess values."
|
||||
);
|
||||
}
|
||||
|
||||
m_rightHandSide = rightHandSide;
|
||||
const double rightHandSideNorm = GlobalNorm(m_rightHandSide, m_communicator);
|
||||
const double threshold = control.ConvergenceThreshold(rightHandSideNorm);
|
||||
|
||||
m_countedOperation.Reset();
|
||||
m_countedPreconditioner.Reset();
|
||||
m_solver.SetRelTol(0.0);
|
||||
m_solver.SetAbsTol(threshold);
|
||||
m_solver.SetMaxIter(control.maximumIterations);
|
||||
|
||||
const Clock::time_point start = Clock::now();
|
||||
m_solver.Mult(m_rightHandSide, correction);
|
||||
const double solveSeconds =
|
||||
MaximumRankValue(std::chrono::duration<double>(Clock::now() - start).count(), m_communicator);
|
||||
|
||||
const bool correctionIsFinite = AllRanksAgree(LocallyFinite(correction), m_communicator);
|
||||
double trueResidualNorm = std::numeric_limits<double>::quiet_NaN();
|
||||
if (correctionIsFinite) {
|
||||
m_countedOperation.Mult(correction, m_operationAction);
|
||||
m_trueResidual = m_rightHandSide;
|
||||
m_trueResidual -= m_operationAction;
|
||||
if (AllRanksAgree(LocallyFinite(m_trueResidual), m_communicator)) {
|
||||
trueResidualNorm = GlobalNorm(m_trueResidual, m_communicator);
|
||||
}
|
||||
}
|
||||
|
||||
const double initialResidualNorm = m_solver.GetInitialNorm();
|
||||
const double reportedResidualNorm = m_solver.GetFinalNorm();
|
||||
const std::uint64_t krylovIterationCount = m_countedPreconditioner.Applications();
|
||||
if (krylovIterationCount > static_cast<std::uint64_t>(std::numeric_limits<int>::max())) {
|
||||
throw std::overflow_error("MFEM FGMRES reported more Krylov iterations than can be represented.");
|
||||
}
|
||||
const int iterations = static_cast<int>(krylovIterationCount);
|
||||
// A restart is an additional Krylov cycle entered after the
|
||||
// initial cycle, not the residual check at a cycle boundary.
|
||||
const int restarts = iterations > 0 ? (iterations - 1) / m_options.restartLength : 0;
|
||||
const bool numericalValuesAreFinite = correctionIsFinite && std::isfinite(initialResidualNorm) &&
|
||||
std::isfinite(reportedResidualNorm) &&
|
||||
std::isfinite(trueResidualNorm);
|
||||
|
||||
LinearSolveStatus status = LinearSolveStatus::backend_failure;
|
||||
if (!numericalValuesAreFinite) {
|
||||
status = LinearSolveStatus::non_finite;
|
||||
} else if (trueResidualNorm <= threshold) {
|
||||
status = LinearSolveStatus::converged;
|
||||
} else if (!m_solver.GetConverged() && iterations >= control.maximumIterations) {
|
||||
status = LinearSolveStatus::maximum_iterations;
|
||||
}
|
||||
|
||||
const double relativeTrueResidualNorm =
|
||||
rightHandSideNorm > 0.0 ? trueResidualNorm / rightHandSideNorm
|
||||
: (trueResidualNorm == 0.0 ? 0.0 : std::numeric_limits<double>::infinity());
|
||||
const double operatorSeconds = MaximumRankValue(m_countedOperation.Seconds(), m_communicator);
|
||||
const double inversePreconditionerSeconds =
|
||||
MaximumRankValue(m_countedPreconditioner.Seconds(), m_communicator);
|
||||
|
||||
return {
|
||||
.status = status,
|
||||
.control = control,
|
||||
.iterations = iterations,
|
||||
.restarts = restarts,
|
||||
.rightHandSideNorm = rightHandSideNorm,
|
||||
.initialResidualNorm = initialResidualNorm,
|
||||
.reportedResidualNorm = reportedResidualNorm,
|
||||
.trueResidualNorm = trueResidualNorm,
|
||||
.relativeTrueResidualNorm = relativeTrueResidualNorm,
|
||||
.operatorApplications = m_countedOperation.Applications(),
|
||||
.inversePreconditionerApplications = m_countedPreconditioner.Applications(),
|
||||
.solveSeconds = solveSeconds,
|
||||
.operatorSeconds = operatorSeconds,
|
||||
.inversePreconditionerSeconds = inversePreconditionerSeconds
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
FGMRESOptions m_options;
|
||||
const Operation *m_operation;
|
||||
Preconditioner *m_preconditioner;
|
||||
MPI_Comm m_communicator;
|
||||
CountedOperator<Operation> m_countedOperation;
|
||||
CountedPreconditioner<Operation, Preconditioner> m_countedPreconditioner;
|
||||
mfem::FGMRESSolver m_solver;
|
||||
mfem::Vector m_rightHandSide;
|
||||
mfem::Vector m_operationAction;
|
||||
mfem::Vector m_trueResidual;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <
|
||||
typename Operation,
|
||||
typename Preconditioner>
|
||||
requires std::derived_from<
|
||||
std::remove_cvref_t<Operation>,
|
||||
mfem::Operator> &&
|
||||
std::derived_from<
|
||||
std::remove_cvref_t<Preconditioner>,
|
||||
mfem::Solver>
|
||||
[[nodiscard]] auto prepareLinearBackend(
|
||||
FGMRES configuration,
|
||||
const Operation &operation,
|
||||
Preconditioner &preconditioner,
|
||||
const MPI_Comm communicator
|
||||
) {
|
||||
return detail::PreparedFGMRES<Operation, Preconditioner>{
|
||||
configuration.GetOptions(), operation, preconditioner, communicator
|
||||
};
|
||||
}
|
||||
} // namespace mean_field::solver::linear
|
||||
579
libmeanfield/interface/solver/newton.cppm
Normal file
579
libmeanfield/interface/solver/newton.cppm
Normal file
@@ -0,0 +1,579 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
#include <mpi.h>
|
||||
|
||||
export module mean_field:solver.newton;
|
||||
|
||||
export import :solver.linear_backend;
|
||||
|
||||
export namespace mean_field::solver::nonlinear {
|
||||
/*
|
||||
* Backtracking counts the full Newton trial as its first trial. A
|
||||
* contraction is applied only after that candidate has been rejected.
|
||||
*/
|
||||
struct BacktrackingOptions final {
|
||||
double initialStepLength{1.0};
|
||||
double contractionFactor{0.5};
|
||||
double fractionToBoundarySafety{0.9};
|
||||
double sufficientDecrease{1.0e-4};
|
||||
double minimumStepLength{1.0e-8};
|
||||
int maximumTrials{20};
|
||||
|
||||
void Validate() const {
|
||||
if (!std::isfinite(initialStepLength) || initialStepLength <= 0.0) {
|
||||
throw std::invalid_argument("Newton backtracking requires a finite, positive initial step length.");
|
||||
}
|
||||
if (!std::isfinite(contractionFactor) || contractionFactor <= 0.0 || contractionFactor >= 1.0) {
|
||||
throw std::invalid_argument(
|
||||
"Newton backtracking requires a finite contraction factor strictly between zero and one."
|
||||
);
|
||||
}
|
||||
if (!std::isfinite(fractionToBoundarySafety) || fractionToBoundarySafety <= 0.0 ||
|
||||
fractionToBoundarySafety >= 1.0) {
|
||||
throw std::invalid_argument(
|
||||
"Newton backtracking requires a finite fraction-to-boundary safety factor strictly between zero "
|
||||
"and one."
|
||||
);
|
||||
}
|
||||
if (!std::isfinite(sufficientDecrease) || sufficientDecrease <= 0.0 || sufficientDecrease >= 1.0) {
|
||||
throw std::invalid_argument(
|
||||
"Newton backtracking requires a finite sufficient-decrease factor strictly between zero and one."
|
||||
);
|
||||
}
|
||||
if (!std::isfinite(minimumStepLength) || minimumStepLength <= 0.0 ||
|
||||
minimumStepLength > initialStepLength) {
|
||||
throw std::invalid_argument(
|
||||
"Newton backtracking requires a finite, positive minimum step no larger than the initial step."
|
||||
);
|
||||
}
|
||||
if (maximumTrials <= 0) {
|
||||
throw std::invalid_argument("Newton backtracking requires at least one permitted trial.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct NewtonOptions final {
|
||||
double relativeTolerance{1.0e-8};
|
||||
double absoluteTolerance{0.0};
|
||||
int maximumIterations{50};
|
||||
LinearSolveControl linearSolve{};
|
||||
BacktrackingOptions backtracking{};
|
||||
|
||||
void Validate() const {
|
||||
if (!std::isfinite(relativeTolerance) || relativeTolerance < 0.0) {
|
||||
throw std::invalid_argument("A Newton solve requires a finite, non-negative relative tolerance.");
|
||||
}
|
||||
if (!std::isfinite(absoluteTolerance) || absoluteTolerance < 0.0) {
|
||||
throw std::invalid_argument("A Newton solve requires a finite, non-negative absolute tolerance.");
|
||||
}
|
||||
if (maximumIterations <= 0) {
|
||||
throw std::invalid_argument("A Newton solve requires at least one permitted iteration.");
|
||||
}
|
||||
linearSolve.Validate();
|
||||
backtracking.Validate();
|
||||
}
|
||||
|
||||
[[nodiscard]] double ConvergenceThreshold(const double initialResidualNorm) const {
|
||||
Validate();
|
||||
if (!std::isfinite(initialResidualNorm) || initialResidualNorm < 0.0) {
|
||||
throw std::invalid_argument(
|
||||
"A Newton convergence threshold requires a finite, non-negative initial residual norm."
|
||||
);
|
||||
}
|
||||
const double relativeThreshold = relativeTolerance * initialResidualNorm;
|
||||
if (!std::isfinite(relativeThreshold)) {
|
||||
throw std::invalid_argument("The Newton relative convergence threshold must be finite.");
|
||||
}
|
||||
return absoluteTolerance > relativeThreshold ? absoluteTolerance : relativeThreshold;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* The MVP globalization merit is phi(x) = 0.5 ||F_normalized(x)||^2.
|
||||
* A metric customization receives the solve communicator and must return
|
||||
* communicator-consistent values or throw collectively. The Newton engine
|
||||
* reduces every predicate that drives control flow, but it cannot make a
|
||||
* rank-local exception inside an arbitrary callback collective-safe.
|
||||
*/
|
||||
struct NormalizedResidualMetric final { };
|
||||
|
||||
struct MetricEvaluation final {
|
||||
double residualNorm{0.0};
|
||||
double merit{0.0};
|
||||
};
|
||||
|
||||
[[nodiscard]] inline MetricEvaluation getMetric(
|
||||
const NormalizedResidualMetric &,
|
||||
const mfem::Vector &normalizedResidual,
|
||||
const MPI_Comm communicator
|
||||
) {
|
||||
if (communicator == MPI_COMM_NULL) {
|
||||
throw std::invalid_argument("A nonlinear metric requires a valid communicator.");
|
||||
}
|
||||
|
||||
double localSquaredNorm = 0.0;
|
||||
for (int index = 0; index < normalizedResidual.Size(); ++index) {
|
||||
const double value = static_cast<double>(normalizedResidual(index));
|
||||
localSquaredNorm += value * value;
|
||||
}
|
||||
|
||||
double globalSquaredNorm = 0.0;
|
||||
if (MPI_Allreduce(&localSquaredNorm, &globalSquaredNorm, 1, MPI_DOUBLE, MPI_SUM, communicator) != MPI_SUCCESS) {
|
||||
throw std::runtime_error("The nonlinear metric could not reduce the normalized residual norm.");
|
||||
}
|
||||
|
||||
const double residualNorm = std::sqrt(globalSquaredNorm);
|
||||
return {.residualNorm = residualNorm, .merit = 0.5 * residualNorm * residualNorm};
|
||||
}
|
||||
|
||||
template <typename Metric = NormalizedResidualMetric>
|
||||
requires std::move_constructible<std::remove_cvref_t<Metric>>
|
||||
class Newton final {
|
||||
public:
|
||||
using MetricType = std::remove_cvref_t<Metric>;
|
||||
|
||||
Newton()
|
||||
requires std::default_initializable<MetricType>
|
||||
: Newton(
|
||||
NewtonOptions{},
|
||||
MetricType{}
|
||||
) {
|
||||
}
|
||||
|
||||
explicit Newton(NewtonOptions options)
|
||||
requires std::default_initializable<MetricType>
|
||||
: Newton(
|
||||
std::move(options),
|
||||
MetricType{}
|
||||
) {
|
||||
}
|
||||
|
||||
Newton(
|
||||
NewtonOptions options,
|
||||
MetricType metric
|
||||
)
|
||||
: m_options(std::move(options)),
|
||||
m_metric(std::move(metric)) {
|
||||
m_options.Validate();
|
||||
}
|
||||
|
||||
[[nodiscard]] const NewtonOptions &options() const noexcept {
|
||||
return m_options;
|
||||
}
|
||||
|
||||
[[nodiscard]] const MetricType &metric() const noexcept {
|
||||
return m_metric;
|
||||
}
|
||||
|
||||
private:
|
||||
NewtonOptions m_options;
|
||||
[[no_unique_address]] MetricType m_metric;
|
||||
};
|
||||
|
||||
Newton() -> Newton<NormalizedResidualMetric>;
|
||||
Newton(NewtonOptions) -> Newton<NormalizedResidualMetric>;
|
||||
|
||||
template <typename Metric>
|
||||
Newton(
|
||||
NewtonOptions,
|
||||
Metric
|
||||
) -> Newton<std::remove_cvref_t<Metric>>;
|
||||
|
||||
template <typename Candidate> struct IsNewtonConfiguration : std::false_type { };
|
||||
|
||||
template <typename Metric> struct IsNewtonConfiguration<Newton<Metric>> : std::true_type { };
|
||||
|
||||
template <typename Candidate>
|
||||
concept NewtonConfiguration = IsNewtonConfiguration<std::remove_cvref_t<Candidate>>::value;
|
||||
|
||||
enum class IterationDisposition : std::uint8_t {
|
||||
unspecified,
|
||||
accepted,
|
||||
converged,
|
||||
inadmissible_state,
|
||||
non_finite_state,
|
||||
non_finite_residual,
|
||||
linear_solve_failure,
|
||||
globalization_failure,
|
||||
stagnation,
|
||||
iteration_limit
|
||||
};
|
||||
|
||||
/*
|
||||
* Event spans borrow solver workspaces and are valid only for the duration
|
||||
* of the callback. Copy values that must outlive the callback.
|
||||
*/
|
||||
struct BeforeIteration final {
|
||||
int iteration{0};
|
||||
double initialResidualNorm{0.0};
|
||||
double residualNorm{0.0};
|
||||
double relativeResidualNorm{0.0};
|
||||
double merit{0.0};
|
||||
MPI_Comm communicator{MPI_COMM_NULL};
|
||||
std::span<const mfem::real_t> physicalState{};
|
||||
std::span<const mfem::real_t> normalizedState{};
|
||||
std::span<const mfem::real_t> normalizedResidual{};
|
||||
};
|
||||
|
||||
enum class LineSearchTrialDisposition : std::uint8_t {
|
||||
accepted,
|
||||
inadmissible_state,
|
||||
non_finite_state,
|
||||
non_finite_residual,
|
||||
insufficient_decrease
|
||||
};
|
||||
|
||||
struct AfterLineSearchTrial final {
|
||||
int iteration{0};
|
||||
int trial{0};
|
||||
double stepLength{0.0};
|
||||
LineSearchTrialDisposition disposition{LineSearchTrialDisposition::insufficient_decrease};
|
||||
std::string_view rejectionSource{};
|
||||
std::optional<MetricEvaluation> metric{};
|
||||
std::optional<double> minimumJacobianDeterminant{};
|
||||
double preparationSeconds{0.0};
|
||||
double metricSeconds{0.0};
|
||||
MPI_Comm communicator{MPI_COMM_NULL};
|
||||
std::span<const mfem::real_t> candidatePhysicalState{};
|
||||
std::span<const mfem::real_t> candidateNormalizedState{};
|
||||
std::span<const mfem::real_t> candidateNormalizedResidual{};
|
||||
};
|
||||
|
||||
struct AfterIteration final {
|
||||
int iteration{0};
|
||||
IterationDisposition disposition{IterationDisposition::unspecified};
|
||||
bool stepAccepted{false};
|
||||
double acceptedStepLength{0.0};
|
||||
int lineSearchTrials{0};
|
||||
double initialResidualNorm{0.0};
|
||||
double previousResidualNorm{0.0};
|
||||
double residualNorm{0.0};
|
||||
double relativeResidualNorm{0.0};
|
||||
double merit{0.0};
|
||||
double iterationSeconds{0.0};
|
||||
double lineSearchSeconds{0.0};
|
||||
double trialPreparationSeconds{0.0};
|
||||
double metricEvaluationSeconds{0.0};
|
||||
double preconditionerRefreshSeconds{0.0};
|
||||
double rollbackSeconds{0.0};
|
||||
std::optional<LinearSolveReport> linearSolve{};
|
||||
MPI_Comm communicator{MPI_COMM_NULL};
|
||||
std::span<const mfem::real_t> physicalState{};
|
||||
std::span<const mfem::real_t> normalizedState{};
|
||||
std::span<const mfem::real_t> normalizedResidual{};
|
||||
};
|
||||
|
||||
struct NoObserver final { };
|
||||
|
||||
template <typename Candidate>
|
||||
concept BeforeIterationCallback = std::invocable<Candidate &, const BeforeIteration &> &&
|
||||
std::same_as<std::invoke_result_t<Candidate &, const BeforeIteration &>, void>;
|
||||
|
||||
template <typename Candidate>
|
||||
concept AfterIterationCallback = std::invocable<Candidate &, const AfterIteration &> &&
|
||||
std::same_as<std::invoke_result_t<Candidate &, const AfterIteration &>, void>;
|
||||
|
||||
template <typename Candidate>
|
||||
concept LineSearchTrialCallback =
|
||||
std::invocable<Candidate &, const AfterLineSearchTrial &> &&
|
||||
std::same_as<std::invoke_result_t<Candidate &, const AfterLineSearchTrial &>, void>;
|
||||
|
||||
template <BeforeIterationCallback BeforeCallback, AfterIterationCallback AfterCallback>
|
||||
class CallbackObserver final {
|
||||
public:
|
||||
CallbackObserver(
|
||||
BeforeCallback before,
|
||||
AfterCallback after
|
||||
)
|
||||
: m_before(std::move(before)),
|
||||
m_after(std::move(after)) {
|
||||
}
|
||||
|
||||
void beforeIteration(const BeforeIteration &event) noexcept(std::is_nothrow_invocable_v<
|
||||
BeforeCallback &,
|
||||
const BeforeIteration &>) {
|
||||
std::invoke(m_before, event);
|
||||
}
|
||||
|
||||
void afterIteration(const AfterIteration &event) noexcept(std::is_nothrow_invocable_v<
|
||||
AfterCallback &,
|
||||
const AfterIteration &>) {
|
||||
std::invoke(m_after, event);
|
||||
}
|
||||
|
||||
private:
|
||||
[[no_unique_address]] BeforeCallback m_before;
|
||||
[[no_unique_address]] AfterCallback m_after;
|
||||
};
|
||||
|
||||
template <
|
||||
typename BeforeCallback,
|
||||
typename AfterCallback>
|
||||
requires BeforeIterationCallback<std::decay_t<BeforeCallback>> &&
|
||||
AfterIterationCallback<std::decay_t<AfterCallback>> &&
|
||||
std::constructible_from<
|
||||
std::decay_t<BeforeCallback>,
|
||||
BeforeCallback> &&
|
||||
std::constructible_from<
|
||||
std::decay_t<AfterCallback>,
|
||||
AfterCallback>
|
||||
[[nodiscard]] auto makeObserver(
|
||||
BeforeCallback &&before,
|
||||
AfterCallback &&after
|
||||
) {
|
||||
return CallbackObserver<std::decay_t<BeforeCallback>, std::decay_t<AfterCallback>>{
|
||||
std::forward<BeforeCallback>(before), std::forward<AfterCallback>(after)
|
||||
};
|
||||
}
|
||||
|
||||
template <
|
||||
BeforeIterationCallback BeforeCallback,
|
||||
LineSearchTrialCallback TrialCallback,
|
||||
AfterIterationCallback AfterCallback>
|
||||
class DetailedCallbackObserver final {
|
||||
public:
|
||||
DetailedCallbackObserver(
|
||||
BeforeCallback before,
|
||||
TrialCallback trial,
|
||||
AfterCallback after
|
||||
)
|
||||
: m_before(std::move(before)),
|
||||
m_trial(std::move(trial)),
|
||||
m_after(std::move(after)) {
|
||||
}
|
||||
|
||||
void beforeIteration(const BeforeIteration &event) noexcept(std::is_nothrow_invocable_v<
|
||||
BeforeCallback &,
|
||||
const BeforeIteration &>) {
|
||||
std::invoke(m_before, event);
|
||||
}
|
||||
|
||||
void afterLineSearchTrial(const AfterLineSearchTrial &event) noexcept(std::is_nothrow_invocable_v<
|
||||
TrialCallback &,
|
||||
const AfterLineSearchTrial &>) {
|
||||
std::invoke(m_trial, event);
|
||||
}
|
||||
|
||||
void afterIteration(const AfterIteration &event) noexcept(std::is_nothrow_invocable_v<
|
||||
AfterCallback &,
|
||||
const AfterIteration &>) {
|
||||
std::invoke(m_after, event);
|
||||
}
|
||||
|
||||
private:
|
||||
[[no_unique_address]] BeforeCallback m_before;
|
||||
[[no_unique_address]] TrialCallback m_trial;
|
||||
[[no_unique_address]] AfterCallback m_after;
|
||||
};
|
||||
|
||||
template <
|
||||
typename BeforeCallback,
|
||||
typename TrialCallback,
|
||||
typename AfterCallback>
|
||||
requires BeforeIterationCallback<std::decay_t<BeforeCallback>> &&
|
||||
LineSearchTrialCallback<std::decay_t<TrialCallback>> &&
|
||||
AfterIterationCallback<std::decay_t<AfterCallback>> &&
|
||||
std::constructible_from<
|
||||
std::decay_t<BeforeCallback>,
|
||||
BeforeCallback> &&
|
||||
std::constructible_from<
|
||||
std::decay_t<TrialCallback>,
|
||||
TrialCallback> &&
|
||||
std::constructible_from<
|
||||
std::decay_t<AfterCallback>,
|
||||
AfterCallback>
|
||||
[[nodiscard]] auto makeObserver(
|
||||
BeforeCallback &&before,
|
||||
TrialCallback &&trial,
|
||||
AfterCallback &&after
|
||||
) {
|
||||
return DetailedCallbackObserver<
|
||||
std::decay_t<BeforeCallback>, std::decay_t<TrialCallback>, std::decay_t<AfterCallback>>{
|
||||
std::forward<BeforeCallback>(before), std::forward<TrialCallback>(trial), std::forward<AfterCallback>(after)
|
||||
};
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
[[nodiscard]] inline double NextBacktrackingStepLength(
|
||||
const double rejectedStepLength,
|
||||
const double acceptedMinimumJacobianDeterminant,
|
||||
const std::optional<double> rejectedMinimumJacobianDeterminant,
|
||||
const bool rejectedByInvertedGeometry,
|
||||
const BacktrackingOptions &options
|
||||
) noexcept {
|
||||
const double contractedStepLength = rejectedStepLength * options.contractionFactor;
|
||||
if (!rejectedByInvertedGeometry || !rejectedMinimumJacobianDeterminant.has_value() ||
|
||||
!std::isfinite(acceptedMinimumJacobianDeterminant) || acceptedMinimumJacobianDeterminant <= 0.0 ||
|
||||
!std::isfinite(*rejectedMinimumJacobianDeterminant) || *rejectedMinimumJacobianDeterminant > 0.0) {
|
||||
return contractedStepLength;
|
||||
}
|
||||
|
||||
const double determinantChange = acceptedMinimumJacobianDeterminant - *rejectedMinimumJacobianDeterminant;
|
||||
if (!std::isfinite(determinantChange) || determinantChange <= 0.0) {
|
||||
return contractedStepLength;
|
||||
}
|
||||
|
||||
const double estimatedBoundaryStep =
|
||||
rejectedStepLength * acceptedMinimumJacobianDeterminant / determinantChange;
|
||||
const double safeguardedStep = options.fractionToBoundarySafety * estimatedBoundaryStep;
|
||||
if (!std::isfinite(safeguardedStep) || safeguardedStep <= 0.0 || safeguardedStep >= rejectedStepLength) {
|
||||
return contractedStepLength;
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep the configured backtracking ladder intact. The geometry
|
||||
* certificate is used only to skip rungs that its local boundary
|
||||
* estimate says are unsafe; it does not introduce a new trial
|
||||
* length between two rungs. This preserves the candidates that
|
||||
* ordinary backtracking would eventually test while avoiding the
|
||||
* expensive preparation of the skipped, inverted geometries.
|
||||
*/
|
||||
if (contractedStepLength <= safeguardedStep) {
|
||||
return contractedStepLength;
|
||||
}
|
||||
|
||||
const double rung =
|
||||
std::ceil(std::log(safeguardedStep / rejectedStepLength) / std::log(options.contractionFactor));
|
||||
double skippedStep = rejectedStepLength * std::pow(options.contractionFactor, rung);
|
||||
if (!std::isfinite(skippedStep) || skippedStep <= 0.0 || skippedStep >= rejectedStepLength) {
|
||||
return contractedStepLength;
|
||||
}
|
||||
if (skippedStep > safeguardedStep) {
|
||||
skippedStep *= options.contractionFactor;
|
||||
}
|
||||
return skippedStep;
|
||||
}
|
||||
|
||||
template <typename Observer>
|
||||
inline constexpr bool isNoObserver = std::same_as<std::remove_cvref_t<Observer>, NoObserver>;
|
||||
|
||||
template <typename Observer>
|
||||
concept ObservesBeforeIteration =
|
||||
!isNoObserver<Observer> &&
|
||||
requires(std::remove_reference_t<Observer> &observer, const BeforeIteration &event) {
|
||||
{ observer.beforeIteration(event) } -> std::same_as<void>;
|
||||
};
|
||||
|
||||
template <typename Observer>
|
||||
concept ObservesAfterIteration =
|
||||
!isNoObserver<Observer> &&
|
||||
requires(std::remove_reference_t<Observer> &observer, const AfterIteration &event) {
|
||||
{ observer.afterIteration(event) } -> std::same_as<void>;
|
||||
};
|
||||
|
||||
template <typename Observer>
|
||||
concept ObservesLineSearchTrial =
|
||||
!isNoObserver<Observer> &&
|
||||
requires(std::remove_reference_t<Observer> &observer, const AfterLineSearchTrial &event) {
|
||||
{ observer.afterLineSearchTrial(event) } -> std::same_as<void>;
|
||||
};
|
||||
|
||||
template <typename Callback>
|
||||
void InvokeObserverHookCollectively(
|
||||
const MPI_Comm communicator,
|
||||
const char *remoteFailureMessage,
|
||||
Callback &&callback
|
||||
) {
|
||||
std::exception_ptr localFailure;
|
||||
try {
|
||||
std::invoke(std::forward<Callback>(callback));
|
||||
} catch (...) {
|
||||
localFailure = std::current_exception();
|
||||
}
|
||||
|
||||
const int localFailureFlag = localFailure != nullptr ? 1 : 0;
|
||||
int globalFailureFlag = 0;
|
||||
if (MPI_Allreduce(&localFailureFlag, &globalFailureFlag, 1, MPI_INT, MPI_MAX, communicator) !=
|
||||
MPI_SUCCESS) {
|
||||
if (localFailure != nullptr) {
|
||||
std::rethrow_exception(localFailure);
|
||||
}
|
||||
throw std::runtime_error("The nonlinear solver could not synchronize an observer callback.");
|
||||
}
|
||||
|
||||
if (globalFailureFlag != 0) {
|
||||
if (localFailure != nullptr) {
|
||||
std::rethrow_exception(localFailure);
|
||||
}
|
||||
throw std::runtime_error(remoteFailureMessage);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Observer>
|
||||
void InvokeBeforeIteration(
|
||||
Observer &observer,
|
||||
const BeforeIteration &event
|
||||
) {
|
||||
if constexpr (ObservesBeforeIteration<Observer>) {
|
||||
if constexpr (noexcept(observer.beforeIteration(event))) {
|
||||
observer.beforeIteration(event);
|
||||
} else {
|
||||
InvokeObserverHookCollectively(
|
||||
event.communicator, "An observer before-iteration callback failed on another rank.",
|
||||
[&observer, &event] { observer.beforeIteration(event); }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Observer>
|
||||
void InvokeAfterIteration(
|
||||
Observer &observer,
|
||||
const AfterIteration &event
|
||||
) {
|
||||
if constexpr (ObservesAfterIteration<Observer>) {
|
||||
if constexpr (noexcept(observer.afterIteration(event))) {
|
||||
observer.afterIteration(event);
|
||||
} else {
|
||||
InvokeObserverHookCollectively(
|
||||
event.communicator, "An observer after-iteration callback failed on another rank.",
|
||||
[&observer, &event] { observer.afterIteration(event); }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Observer>
|
||||
void InvokeAfterLineSearchTrial(
|
||||
Observer &observer,
|
||||
const AfterLineSearchTrial &event
|
||||
) {
|
||||
if constexpr (ObservesLineSearchTrial<Observer>) {
|
||||
if constexpr (noexcept(observer.afterLineSearchTrial(event))) {
|
||||
observer.afterLineSearchTrial(event);
|
||||
} else {
|
||||
InvokeObserverHookCollectively(
|
||||
event.communicator, "An observer line-search callback failed on another rank.",
|
||||
[&observer, &event] { observer.afterLineSearchTrial(event); }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
/*
|
||||
* Observers run synchronously on every solve rank. Ordinary callback
|
||||
* exceptions are synchronized before the solver proceeds, so all ranks can
|
||||
* unwind together; explicitly noexcept callbacks bypass that synchronization.
|
||||
* A callback must still not enter an MPI collective on only a subset of
|
||||
* ranks. A before/after pair is guaranteed for iterations that finish by
|
||||
* returning an evaluation report. Infrastructure exceptions unwind
|
||||
* immediately and do not promise an after callback.
|
||||
*/
|
||||
template <typename Candidate>
|
||||
concept NewtonObserver = detail::isNoObserver<Candidate> || detail::ObservesBeforeIteration<Candidate> ||
|
||||
detail::ObservesLineSearchTrial<Candidate> || detail::ObservesAfterIteration<Candidate>;
|
||||
} // namespace mean_field::solver::nonlinear
|
||||
1642
libmeanfield/interface/solver/stellar_context.cppm
Normal file
1642
libmeanfield/interface/solver/stellar_context.cppm
Normal file
File diff suppressed because it is too large
Load Diff
4
libmeanfield/interface/solver/stellar_equilibrium.cppm
Normal file
4
libmeanfield/interface/solver/stellar_equilibrium.cppm
Normal file
@@ -0,0 +1,4 @@
|
||||
export module mean_field:solver.stellar_equilibrium;
|
||||
|
||||
export import :solver.stellar_structure;
|
||||
export import :solver.stellar_context;
|
||||
60
libmeanfield/interface/solver/stellar_equilibrium_types.cppm
Normal file
60
libmeanfield/interface/solver/stellar_equilibrium_types.cppm
Normal file
@@ -0,0 +1,60 @@
|
||||
module;
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
export module mean_field:solver.stellar_equilibrium_types;
|
||||
|
||||
export import :solver.linear_backend;
|
||||
|
||||
export namespace mean_field::solver {
|
||||
enum class StellarEquilibriumFailureReason : std::uint8_t {
|
||||
unspecified,
|
||||
inadmissible_state,
|
||||
non_finite_state,
|
||||
non_finite_residual,
|
||||
linear_solve_failure,
|
||||
globalization_failure,
|
||||
stagnation,
|
||||
iteration_limit
|
||||
};
|
||||
|
||||
/*
|
||||
* An owning, backend-neutral account of an expected numerical failure.
|
||||
* Backend-specific measurements may be translated into the message or
|
||||
* future common diagnostics, but are deliberately not part of this stable
|
||||
* result boundary.
|
||||
*/
|
||||
struct StellarEquilibriumFailureReport final {
|
||||
StellarEquilibriumFailureReason reason{StellarEquilibriumFailureReason::unspecified};
|
||||
std::string message;
|
||||
int completedNonlinearIterations{0};
|
||||
std::optional<double> initialResidualNorm;
|
||||
std::optional<double> finalResidualNorm;
|
||||
};
|
||||
|
||||
/*
|
||||
* Fixed-size diagnostics retained by every evaluation report. Detailed
|
||||
* iteration histories belong in an observer so the default solve does not
|
||||
* allocate storage proportional to the iteration count.
|
||||
*/
|
||||
struct StellarEquilibriumEvaluationDiagnostics final {
|
||||
int attemptedNonlinearIterations{0};
|
||||
int acceptedNonlinearIterations{0};
|
||||
int totalLineSearchTrials{0};
|
||||
int inadmissibleLineSearchTrials{0};
|
||||
int nonFiniteLineSearchTrials{0};
|
||||
int insufficientDecreaseTrials{0};
|
||||
double initialResidualNorm{0.0};
|
||||
double finalResidualNorm{0.0};
|
||||
double lastAcceptedStepLength{0.0};
|
||||
double totalLinearSolveSeconds{0.0};
|
||||
double totalLineSearchSeconds{0.0};
|
||||
double totalTrialPreparationSeconds{0.0};
|
||||
double totalMetricEvaluationSeconds{0.0};
|
||||
double totalPreconditionerRefreshSeconds{0.0};
|
||||
double totalRollbackSeconds{0.0};
|
||||
std::optional<LinearSolveReport> lastLinearSolve;
|
||||
};
|
||||
} // namespace mean_field::solver
|
||||
486
libmeanfield/interface/solver/stellar_structure.cppm
Normal file
486
libmeanfield/interface/solver/stellar_structure.cppm
Normal file
@@ -0,0 +1,486 @@
|
||||
module;
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
#include <mpi.h>
|
||||
|
||||
export module mean_field:solver.stellar_structure;
|
||||
|
||||
export import :operators.stellar_equilibrium_problem;
|
||||
export import :solver.stellar_equilibrium_types;
|
||||
|
||||
export namespace mean_field::solver {
|
||||
template <equilibrium::DiscretizedStellarEquilibriumProblem Problem> class StellarEquilibriumEvaluationReport;
|
||||
}
|
||||
|
||||
namespace mean_field::solver::detail {
|
||||
enum class StellarViewCertification : std::uint8_t { unavailable, checkpoint, structure };
|
||||
|
||||
template <typename Problem> struct StellarStructureStorage final {
|
||||
using ProblemType = std::remove_cvref_t<Problem>;
|
||||
|
||||
StellarStructureStorage(
|
||||
std::unique_ptr<ProblemType> ownedProblem,
|
||||
std::unique_ptr<mfem::Vector> acceptedPhysicalState,
|
||||
std::unique_ptr<physics::RigidRotation> prescribedRotation
|
||||
)
|
||||
: problem(std::move(ownedProblem)),
|
||||
physicalState(std::move(acceptedPhysicalState)),
|
||||
rotation(std::move(prescribedRotation)) {
|
||||
}
|
||||
|
||||
std::unique_ptr<ProblemType> problem;
|
||||
std::unique_ptr<mfem::Vector> physicalState;
|
||||
std::unique_ptr<physics::RigidRotation> rotation;
|
||||
std::uint64_t viewGeneration{0};
|
||||
StellarViewCertification certification{StellarViewCertification::unavailable};
|
||||
};
|
||||
|
||||
template <typename Problem>
|
||||
[[nodiscard]] bool IsCurrentView(
|
||||
const std::weak_ptr<const StellarStructureStorage<Problem>> &candidate,
|
||||
const std::uint64_t generation,
|
||||
const bool requireConverged
|
||||
) noexcept {
|
||||
const auto storage = candidate.lock();
|
||||
if (storage == nullptr || storage->problem == nullptr || storage->physicalState == nullptr ||
|
||||
storage->viewGeneration != generation) {
|
||||
return false;
|
||||
}
|
||||
if (requireConverged) {
|
||||
return storage->certification == StellarViewCertification::structure;
|
||||
}
|
||||
return storage->certification == StellarViewCertification::checkpoint ||
|
||||
storage->certification == StellarViewCertification::structure;
|
||||
}
|
||||
|
||||
template <typename Problem>
|
||||
[[nodiscard]] std::shared_ptr<const StellarStructureStorage<Problem>> RequireCurrentView(
|
||||
const std::weak_ptr<const StellarStructureStorage<Problem>> &candidate,
|
||||
const std::uint64_t generation,
|
||||
const bool requireConverged
|
||||
) {
|
||||
auto storage = candidate.lock();
|
||||
if (storage == nullptr || storage->problem == nullptr || storage->physicalState == nullptr ||
|
||||
storage->viewGeneration != generation ||
|
||||
(requireConverged && storage->certification != StellarViewCertification::structure) ||
|
||||
(!requireConverged && storage->certification != StellarViewCertification::checkpoint &&
|
||||
storage->certification != StellarViewCertification::structure)) {
|
||||
throw std::logic_error("The stellar structure view is stale or is not certified for this result.");
|
||||
}
|
||||
return storage;
|
||||
}
|
||||
|
||||
template <typename Vector> [[nodiscard]] std::span<const mfem::real_t> ReadOnlySpan(const Vector &values) noexcept {
|
||||
return {values.GetData(), static_cast<std::size_t>(values.Size())};
|
||||
}
|
||||
|
||||
template <typename Problem> struct StellarEvaluationReportAccess;
|
||||
} // namespace mean_field::solver::detail
|
||||
|
||||
export namespace mean_field::equilibrium {
|
||||
/*
|
||||
* These are the future owning, self-contained values. There is no public
|
||||
* construction path until deep capture and its MPI-independent storage
|
||||
* schema are implemented.
|
||||
*/
|
||||
template <DiscretizedStellarEquilibriumProblem Problem> class StellarStructure final {
|
||||
public:
|
||||
using ProblemType = std::remove_cvref_t<Problem>;
|
||||
|
||||
StellarStructure(const StellarStructure &) = delete;
|
||||
StellarStructure &operator=(const StellarStructure &) = delete;
|
||||
StellarStructure(StellarStructure &&) noexcept = default;
|
||||
StellarStructure &operator=(StellarStructure &&) = delete;
|
||||
~StellarStructure() = default;
|
||||
|
||||
private:
|
||||
StellarStructure() = default;
|
||||
};
|
||||
|
||||
template <DiscretizedStellarEquilibriumProblem Problem> class StellarCheckpoint final {
|
||||
public:
|
||||
using ProblemType = std::remove_cvref_t<Problem>;
|
||||
|
||||
StellarCheckpoint(const StellarCheckpoint &) = delete;
|
||||
StellarCheckpoint &operator=(const StellarCheckpoint &) = delete;
|
||||
StellarCheckpoint(StellarCheckpoint &&) noexcept = default;
|
||||
StellarCheckpoint &operator=(StellarCheckpoint &&) = delete;
|
||||
~StellarCheckpoint() = default;
|
||||
|
||||
private:
|
||||
StellarCheckpoint() = default;
|
||||
};
|
||||
|
||||
/*
|
||||
* Result views weakly observe context-owned storage. valid() remains safe
|
||||
* after that context is destroyed. References and spans extracted from a
|
||||
* valid view remain borrowed: the context must outlive their use, and the
|
||||
* next evaluate() call invalidates them along with their originating view.
|
||||
*/
|
||||
template <DiscretizedStellarEquilibriumProblem Problem> class StellarStructureView final {
|
||||
public:
|
||||
using ProblemType = std::remove_cvref_t<Problem>;
|
||||
using ModelType = typename ProblemType::ModelType;
|
||||
|
||||
[[nodiscard]] bool valid() const noexcept {
|
||||
return solver::detail::IsCurrentView(m_storage, m_generation, true);
|
||||
}
|
||||
|
||||
[[nodiscard]] const ModelType &model() const & {
|
||||
const auto storage = RequireStorage();
|
||||
return storage->problem->GetStellarModel();
|
||||
}
|
||||
|
||||
[[nodiscard]] const ModelType &model() const && = delete;
|
||||
|
||||
[[nodiscard]] MPI_Comm communicator() const & {
|
||||
const auto storage = RequireStorage();
|
||||
return storage->problem->GetCommunicator();
|
||||
}
|
||||
|
||||
[[nodiscard]] MPI_Comm communicator() const && = delete;
|
||||
|
||||
[[nodiscard]] std::span<const mfem::real_t> state() const & {
|
||||
const auto storage = RequireStorage();
|
||||
return solver::detail::ReadOnlySpan(*storage->physicalState);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::span<const mfem::real_t> state() const && = delete;
|
||||
|
||||
[[nodiscard]] std::span<const operators::RootBlockDescriptor> stateDescriptors() const & {
|
||||
const auto storage = RequireStorage();
|
||||
return storage->problem->GetManifest().valueBlocks();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::span<const operators::RootBlockDescriptor> stateDescriptors() const && = delete;
|
||||
|
||||
template <typename Term>
|
||||
requires requires(
|
||||
const typename ProblemType::ManifestType &manifest,
|
||||
const mfem::Vector &physicalState,
|
||||
const Term &term
|
||||
) { manifest.stateView(physicalState).block(term); }
|
||||
[[nodiscard]] std::span<const mfem::real_t> stateBlock(const Term &term) const & {
|
||||
const auto storage = RequireStorage();
|
||||
const auto block = storage->problem->GetManifest().stateView(*storage->physicalState).block(term);
|
||||
return solver::detail::ReadOnlySpan(block);
|
||||
}
|
||||
|
||||
template <typename Term> [[nodiscard]] std::span<const mfem::real_t> stateBlock(const Term &) const && = delete;
|
||||
|
||||
[[nodiscard]] std::optional<physics::RigidRotation> prescribedRotation() const & {
|
||||
const auto storage = RequireStorage();
|
||||
if (storage->rotation == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return *storage->rotation;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<physics::RigidRotation> prescribedRotation() const && = delete;
|
||||
|
||||
[[nodiscard]] physics::RigidRotation rotation() const & {
|
||||
const auto storage = RequireStorage();
|
||||
return storage->problem->GetPreparedOperator().GetRotation();
|
||||
}
|
||||
|
||||
[[nodiscard]] physics::RigidRotation rotation() const && = delete;
|
||||
|
||||
[[nodiscard]] StellarStructure<ProblemType> capture() const {
|
||||
(void)RequireStorage();
|
||||
throw std::logic_error("Capturing a self-contained StellarStructure is not implemented.");
|
||||
}
|
||||
|
||||
private:
|
||||
template <DiscretizedStellarEquilibriumProblem> friend class solver::StellarEquilibriumEvaluationReport;
|
||||
using Storage = solver::detail::StellarStructureStorage<ProblemType>;
|
||||
|
||||
StellarStructureView(
|
||||
std::weak_ptr<const Storage> storage,
|
||||
const std::uint64_t generation
|
||||
) noexcept
|
||||
: m_storage(std::move(storage)),
|
||||
m_generation(generation) {
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<const Storage> RequireStorage() const {
|
||||
return solver::detail::RequireCurrentView(m_storage, m_generation, true);
|
||||
}
|
||||
|
||||
std::weak_ptr<const Storage> m_storage;
|
||||
std::uint64_t m_generation;
|
||||
};
|
||||
|
||||
template <DiscretizedStellarEquilibriumProblem Problem> class StellarCheckpointView final {
|
||||
public:
|
||||
using ProblemType = std::remove_cvref_t<Problem>;
|
||||
using ModelType = typename ProblemType::ModelType;
|
||||
|
||||
[[nodiscard]] bool valid() const noexcept {
|
||||
return solver::detail::IsCurrentView(m_storage, m_generation, false);
|
||||
}
|
||||
|
||||
[[nodiscard]] const ModelType &model() const & {
|
||||
const auto storage = RequireStorage();
|
||||
return storage->problem->GetStellarModel();
|
||||
}
|
||||
|
||||
[[nodiscard]] const ModelType &model() const && = delete;
|
||||
|
||||
[[nodiscard]] MPI_Comm communicator() const & {
|
||||
const auto storage = RequireStorage();
|
||||
return storage->problem->GetCommunicator();
|
||||
}
|
||||
|
||||
[[nodiscard]] MPI_Comm communicator() const && = delete;
|
||||
|
||||
[[nodiscard]] std::span<const mfem::real_t> state() const & {
|
||||
const auto storage = RequireStorage();
|
||||
return solver::detail::ReadOnlySpan(*storage->physicalState);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::span<const mfem::real_t> state() const && = delete;
|
||||
|
||||
[[nodiscard]] std::span<const operators::RootBlockDescriptor> stateDescriptors() const & {
|
||||
const auto storage = RequireStorage();
|
||||
return storage->problem->GetManifest().valueBlocks();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::span<const operators::RootBlockDescriptor> stateDescriptors() const && = delete;
|
||||
|
||||
template <typename Term>
|
||||
requires requires(
|
||||
const typename ProblemType::ManifestType &manifest,
|
||||
const mfem::Vector &physicalState,
|
||||
const Term &term
|
||||
) { manifest.stateView(physicalState).block(term); }
|
||||
[[nodiscard]] std::span<const mfem::real_t> stateBlock(const Term &term) const & {
|
||||
const auto storage = RequireStorage();
|
||||
const auto block = storage->problem->GetManifest().stateView(*storage->physicalState).block(term);
|
||||
return solver::detail::ReadOnlySpan(block);
|
||||
}
|
||||
|
||||
template <typename Term> [[nodiscard]] std::span<const mfem::real_t> stateBlock(const Term &) const && = delete;
|
||||
|
||||
[[nodiscard]] std::optional<physics::RigidRotation> prescribedRotation() const & {
|
||||
const auto storage = RequireStorage();
|
||||
if (storage->rotation == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return *storage->rotation;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<physics::RigidRotation> prescribedRotation() const && = delete;
|
||||
|
||||
[[nodiscard]] physics::RigidRotation rotation() const & {
|
||||
const auto storage = RequireStorage();
|
||||
return storage->problem->GetPreparedOperator().GetRotation();
|
||||
}
|
||||
|
||||
[[nodiscard]] physics::RigidRotation rotation() const && = delete;
|
||||
|
||||
[[nodiscard]] StellarCheckpoint<ProblemType> capture() const {
|
||||
(void)RequireStorage();
|
||||
throw std::logic_error("Capturing a self-contained StellarCheckpoint is not implemented.");
|
||||
}
|
||||
|
||||
private:
|
||||
template <DiscretizedStellarEquilibriumProblem> friend class solver::StellarEquilibriumEvaluationReport;
|
||||
using Storage = solver::detail::StellarStructureStorage<ProblemType>;
|
||||
|
||||
StellarCheckpointView(
|
||||
std::weak_ptr<const Storage> storage,
|
||||
const std::uint64_t generation
|
||||
) noexcept
|
||||
: m_storage(std::move(storage)),
|
||||
m_generation(generation) {
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<const Storage> RequireStorage() const {
|
||||
return solver::detail::RequireCurrentView(m_storage, m_generation, false);
|
||||
}
|
||||
|
||||
std::weak_ptr<const Storage> m_storage;
|
||||
std::uint64_t m_generation;
|
||||
};
|
||||
|
||||
template <DiscretizedStellarEquilibriumProblem Problem>
|
||||
[[noreturn]] void serialize(
|
||||
const StellarStructure<Problem> &,
|
||||
const std::filesystem::path &
|
||||
) {
|
||||
throw std::logic_error("Serializing a StellarStructure is not implemented.");
|
||||
}
|
||||
|
||||
template <DiscretizedStellarEquilibriumProblem Problem>
|
||||
[[noreturn]] void serialize(
|
||||
const StellarStructureView<Problem> &view,
|
||||
const std::filesystem::path &
|
||||
) {
|
||||
(void)view.state();
|
||||
throw std::logic_error("Serializing a StellarStructureView is not implemented.");
|
||||
}
|
||||
|
||||
template <DiscretizedStellarEquilibriumProblem Problem>
|
||||
[[noreturn]] void serialize(
|
||||
const StellarCheckpoint<Problem> &,
|
||||
const std::filesystem::path &
|
||||
) {
|
||||
throw std::logic_error("Serializing a StellarCheckpoint is not implemented.");
|
||||
}
|
||||
|
||||
template <DiscretizedStellarEquilibriumProblem Problem>
|
||||
[[noreturn]] void serialize(
|
||||
const StellarCheckpointView<Problem> &view,
|
||||
const std::filesystem::path &
|
||||
) {
|
||||
(void)view.state();
|
||||
throw std::logic_error("Serializing a StellarCheckpointView is not implemented.");
|
||||
}
|
||||
} // namespace mean_field::equilibrium
|
||||
|
||||
export namespace mean_field::solver {
|
||||
template <equilibrium::DiscretizedStellarEquilibriumProblem Problem>
|
||||
class StellarEquilibriumEvaluationReport final {
|
||||
public:
|
||||
using ProblemType = std::remove_cvref_t<Problem>;
|
||||
using StructureView = equilibrium::StellarStructureView<ProblemType>;
|
||||
using CheckpointView = equilibrium::StellarCheckpointView<ProblemType>;
|
||||
|
||||
StellarEquilibriumEvaluationReport(const StellarEquilibriumEvaluationReport &) = default;
|
||||
StellarEquilibriumEvaluationReport &operator=(const StellarEquilibriumEvaluationReport &) = default;
|
||||
StellarEquilibriumEvaluationReport(StellarEquilibriumEvaluationReport &&) noexcept = default;
|
||||
StellarEquilibriumEvaluationReport &operator=(StellarEquilibriumEvaluationReport &&) noexcept = default;
|
||||
~StellarEquilibriumEvaluationReport() = default;
|
||||
|
||||
[[nodiscard]] bool converged() const noexcept {
|
||||
return m_converged;
|
||||
}
|
||||
|
||||
[[nodiscard]] const StellarEquilibriumEvaluationDiagnostics &diagnostics() const & noexcept {
|
||||
return m_diagnostics;
|
||||
}
|
||||
|
||||
[[nodiscard]] const StellarEquilibriumEvaluationDiagnostics &diagnostics() const && = delete;
|
||||
|
||||
[[nodiscard]] int completedNonlinearIterations() const noexcept {
|
||||
return m_diagnostics.acceptedNonlinearIterations;
|
||||
}
|
||||
|
||||
[[nodiscard]] double initialResidualNorm() const noexcept {
|
||||
return m_diagnostics.initialResidualNorm;
|
||||
}
|
||||
|
||||
[[nodiscard]] double finalResidualNorm() const noexcept {
|
||||
return m_diagnostics.finalResidualNorm;
|
||||
}
|
||||
|
||||
[[nodiscard]] const StellarEquilibriumFailureReport &failure() const & {
|
||||
if (!m_failure.has_value()) {
|
||||
throw std::logic_error("A converged stellar-equilibrium report has no failure record.");
|
||||
}
|
||||
return *m_failure;
|
||||
}
|
||||
|
||||
[[nodiscard]] const StellarEquilibriumFailureReport &failure() const && = delete;
|
||||
|
||||
[[nodiscard]] StructureView structureView() const {
|
||||
if (!m_converged) {
|
||||
throw std::logic_error("A failed stellar-equilibrium report cannot certify a structure view.");
|
||||
}
|
||||
StructureView view{m_storage, m_generation};
|
||||
if (!view.valid()) {
|
||||
throw std::logic_error("The stellar-equilibrium structure view has been invalidated.");
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
[[nodiscard]] CheckpointView checkpointView() const {
|
||||
CheckpointView view{m_storage, m_generation};
|
||||
if (!view.valid()) {
|
||||
throw std::logic_error("The stellar-equilibrium checkpoint view has been invalidated.");
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
[[nodiscard]] CheckpointView lastAcceptedCheckpointView() const {
|
||||
return checkpointView();
|
||||
}
|
||||
|
||||
private:
|
||||
friend struct detail::StellarEvaluationReportAccess<ProblemType>;
|
||||
using Storage = detail::StellarStructureStorage<ProblemType>;
|
||||
|
||||
StellarEquilibriumEvaluationReport(
|
||||
const bool converged,
|
||||
StellarEquilibriumEvaluationDiagnostics diagnostics,
|
||||
std::optional<StellarEquilibriumFailureReport> failure,
|
||||
std::weak_ptr<const Storage> storage,
|
||||
const std::uint64_t generation
|
||||
)
|
||||
: m_converged(converged),
|
||||
m_diagnostics(std::move(diagnostics)),
|
||||
m_failure(std::move(failure)),
|
||||
m_storage(std::move(storage)),
|
||||
m_generation(generation) {
|
||||
}
|
||||
|
||||
bool m_converged;
|
||||
StellarEquilibriumEvaluationDiagnostics m_diagnostics;
|
||||
std::optional<StellarEquilibriumFailureReport> m_failure;
|
||||
std::weak_ptr<const Storage> m_storage;
|
||||
std::uint64_t m_generation;
|
||||
};
|
||||
} // namespace mean_field::solver
|
||||
|
||||
namespace mean_field::solver::detail {
|
||||
template <typename Problem> struct StellarEvaluationReportAccess final {
|
||||
using ProblemType = std::remove_cvref_t<Problem>;
|
||||
using Report = StellarEquilibriumEvaluationReport<ProblemType>;
|
||||
using Storage = StellarStructureStorage<ProblemType>;
|
||||
|
||||
[[nodiscard]] static Report Success(
|
||||
const std::shared_ptr<Storage> &storage,
|
||||
StellarEquilibriumEvaluationDiagnostics diagnostics
|
||||
) {
|
||||
if (storage == nullptr) {
|
||||
throw std::invalid_argument("A stellar-equilibrium report requires owned result storage.");
|
||||
}
|
||||
storage->certification = StellarViewCertification::structure;
|
||||
return Report{true, std::move(diagnostics), std::nullopt, storage, storage->viewGeneration};
|
||||
}
|
||||
|
||||
[[nodiscard]] static Report Failure(
|
||||
const std::shared_ptr<Storage> &storage,
|
||||
StellarEquilibriumEvaluationDiagnostics diagnostics,
|
||||
const StellarEquilibriumFailureReason reason,
|
||||
std::string message
|
||||
) {
|
||||
if (storage == nullptr) {
|
||||
throw std::invalid_argument("A stellar-equilibrium report requires owned result storage.");
|
||||
}
|
||||
storage->certification = StellarViewCertification::checkpoint;
|
||||
StellarEquilibriumFailureReport failure{
|
||||
.reason = reason,
|
||||
.message = std::move(message),
|
||||
.completedNonlinearIterations = diagnostics.acceptedNonlinearIterations,
|
||||
.initialResidualNorm = diagnostics.initialResidualNorm,
|
||||
.finalResidualNorm = diagnostics.finalResidualNorm
|
||||
};
|
||||
return Report{
|
||||
false, std::move(diagnostics), std::optional<StellarEquilibriumFailureReport>{std::move(failure)},
|
||||
storage, storage->viewGeneration
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace mean_field::solver::detail
|
||||
Reference in New Issue
Block a user