perf(jacobian-action): major updates to jacobian action application by removing redudant quadrature work. ~5x increase in speed
This commit is contained in:
312
libmeanfield/interface/dimensions/quantities.cppm
Normal file
312
libmeanfield/interface/dimensions/quantities.cppm
Normal file
@@ -0,0 +1,312 @@
|
||||
module;
|
||||
|
||||
#include <compare>
|
||||
#include <concepts>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
export module mean_field:dimensions.quantities;
|
||||
|
||||
export namespace mean_field::dimensions {
|
||||
/*
|
||||
* QuantityValue provides semantic strong typing for scalar physical
|
||||
* values expressed in the unit system selected by a model. It does not
|
||||
* perform dimensional algebra or unit conversion.
|
||||
*/
|
||||
struct PhysicalQuantity { };
|
||||
|
||||
struct ThermodynamicQuantity : PhysicalQuantity { };
|
||||
|
||||
template <typename Candidate>
|
||||
concept PhysicalQuantityType =
|
||||
std::same_as<Candidate, std::remove_cv_t<Candidate>> && std::derived_from<Candidate, PhysicalQuantity>;
|
||||
|
||||
template <typename Candidate>
|
||||
concept ThermodynamicQuantityType =
|
||||
PhysicalQuantityType<Candidate> && std::derived_from<Candidate, ThermodynamicQuantity>;
|
||||
|
||||
namespace quantity {
|
||||
struct Dimensionless final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "dimensionless";
|
||||
};
|
||||
|
||||
struct Mass final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "mass";
|
||||
};
|
||||
|
||||
struct Length final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "length";
|
||||
};
|
||||
|
||||
struct Time final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "time";
|
||||
};
|
||||
|
||||
struct Area final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "area";
|
||||
};
|
||||
|
||||
struct Volume final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "volume";
|
||||
};
|
||||
|
||||
struct Density final : ThermodynamicQuantity {
|
||||
static constexpr std::string_view identifier = "density";
|
||||
};
|
||||
|
||||
struct SurfaceDensity final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "surface_density";
|
||||
};
|
||||
|
||||
struct NumberDensity final : ThermodynamicQuantity {
|
||||
static constexpr std::string_view identifier = "number_density";
|
||||
};
|
||||
|
||||
struct Pressure final : ThermodynamicQuantity {
|
||||
static constexpr std::string_view identifier = "pressure";
|
||||
};
|
||||
|
||||
struct Temperature final : ThermodynamicQuantity {
|
||||
static constexpr std::string_view identifier = "temperature";
|
||||
};
|
||||
|
||||
struct Entropy final : ThermodynamicQuantity {
|
||||
static constexpr std::string_view identifier = "entropy";
|
||||
};
|
||||
|
||||
struct SpecificEntropy final : ThermodynamicQuantity {
|
||||
static constexpr std::string_view identifier = "specific_entropy";
|
||||
};
|
||||
|
||||
struct ChemicalPotential final : ThermodynamicQuantity {
|
||||
static constexpr std::string_view identifier = "chemical_potential";
|
||||
};
|
||||
|
||||
struct Energy final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "energy";
|
||||
};
|
||||
|
||||
struct InternalEnergy final : ThermodynamicQuantity {
|
||||
static constexpr std::string_view identifier = "internal_energy";
|
||||
};
|
||||
|
||||
struct SpecificEnergy final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "specific_energy";
|
||||
};
|
||||
|
||||
struct SpecificInternalEnergy final : ThermodynamicQuantity {
|
||||
static constexpr std::string_view identifier = "specific_internal_energy";
|
||||
};
|
||||
|
||||
struct SpecificEnthalpy final : ThermodynamicQuantity {
|
||||
static constexpr std::string_view identifier = "specific_enthalpy";
|
||||
};
|
||||
|
||||
struct EnergyDensity final : ThermodynamicQuantity {
|
||||
static constexpr std::string_view identifier = "energy_density";
|
||||
};
|
||||
|
||||
struct GravitationalPotential final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "gravitational_potential";
|
||||
};
|
||||
|
||||
struct Velocity final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "velocity";
|
||||
};
|
||||
|
||||
struct Acceleration final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "acceleration";
|
||||
};
|
||||
|
||||
struct Frequency final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "frequency";
|
||||
};
|
||||
|
||||
struct AngularVelocity final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "angular_velocity";
|
||||
};
|
||||
|
||||
struct Momentum final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "momentum";
|
||||
};
|
||||
|
||||
struct AngularMomentum final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "angular_momentum";
|
||||
};
|
||||
|
||||
struct MomentOfInertia final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "moment_of_inertia";
|
||||
};
|
||||
|
||||
struct Force final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "force";
|
||||
};
|
||||
|
||||
struct Torque final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "torque";
|
||||
};
|
||||
|
||||
struct Power final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "power";
|
||||
};
|
||||
|
||||
struct Luminosity final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "luminosity";
|
||||
};
|
||||
|
||||
struct MassFlowRate final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "mass_flow_rate";
|
||||
};
|
||||
|
||||
struct Opacity final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "opacity";
|
||||
};
|
||||
|
||||
struct DynamicViscosity final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "dynamic_viscosity";
|
||||
};
|
||||
|
||||
struct KinematicViscosity final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "kinematic_viscosity";
|
||||
};
|
||||
|
||||
struct MagneticFluxDensity final : PhysicalQuantity {
|
||||
static constexpr std::string_view identifier = "magnetic_flux_density";
|
||||
};
|
||||
} // namespace quantity
|
||||
|
||||
template <typename T>
|
||||
concept Numeric = std::integral<T> || std::floating_point<T>;
|
||||
|
||||
template <PhysicalQuantityType Quantity> class QuantityValue final {
|
||||
public:
|
||||
explicit constexpr QuantityValue(const double value) noexcept : m_value(value) {
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr double value() const noexcept {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator==(
|
||||
const QuantityValue &,
|
||||
const QuantityValue &
|
||||
) noexcept = default;
|
||||
|
||||
friend constexpr QuantityValue operator+(
|
||||
const QuantityValue &lhs,
|
||||
const QuantityValue &rhs
|
||||
) noexcept {
|
||||
return QuantityValue{lhs.m_value + rhs.m_value};
|
||||
}
|
||||
|
||||
friend constexpr QuantityValue operator-(
|
||||
const QuantityValue &lhs,
|
||||
const QuantityValue &rhs
|
||||
) noexcept {
|
||||
return QuantityValue{lhs.m_value - rhs.m_value};
|
||||
}
|
||||
|
||||
template <Numeric Scalar>
|
||||
friend constexpr QuantityValue operator*(
|
||||
const QuantityValue &lhs,
|
||||
const Scalar rhs
|
||||
) noexcept {
|
||||
return QuantityValue{lhs.m_value * static_cast<double>(rhs)};
|
||||
}
|
||||
|
||||
template <Numeric Scalar>
|
||||
friend constexpr QuantityValue operator*(
|
||||
const Scalar lhs,
|
||||
const QuantityValue &rhs
|
||||
) noexcept {
|
||||
return QuantityValue{static_cast<double>(lhs) * rhs.m_value};
|
||||
}
|
||||
|
||||
template <Numeric Scalar>
|
||||
friend constexpr QuantityValue operator/(
|
||||
const QuantityValue &lhs,
|
||||
const Scalar rhs
|
||||
) noexcept {
|
||||
return QuantityValue{lhs.m_value / static_cast<double>(rhs)};
|
||||
}
|
||||
|
||||
template <Numeric Scalar>
|
||||
friend constexpr std::partial_ordering operator<=>(
|
||||
const QuantityValue &lhs,
|
||||
const Scalar rhs
|
||||
) noexcept {
|
||||
return lhs.m_value <=> static_cast<double>(rhs);
|
||||
}
|
||||
|
||||
template <Numeric Scalar>
|
||||
friend constexpr std::partial_ordering operator<=>(
|
||||
const Scalar lhs,
|
||||
const QuantityValue &rhs
|
||||
) noexcept {
|
||||
return static_cast<double>(lhs) <=> rhs.m_value;
|
||||
}
|
||||
|
||||
friend constexpr std::partial_ordering operator<=>(
|
||||
const QuantityValue &lhs,
|
||||
const QuantityValue &rhs
|
||||
) noexcept {
|
||||
return lhs.m_value <=> rhs.m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
double m_value;
|
||||
};
|
||||
|
||||
template <typename Candidate> struct IsQuantityValue : std::false_type { };
|
||||
|
||||
template <PhysicalQuantityType Quantity> struct IsQuantityValue<QuantityValue<Quantity>> : std::true_type { };
|
||||
|
||||
template <typename Candidate>
|
||||
concept QuantityValueType = IsQuantityValue<std::remove_cvref_t<Candidate>>::value;
|
||||
|
||||
template <typename Candidate> struct QuantityOf;
|
||||
|
||||
template <PhysicalQuantityType Quantity> struct QuantityOf<QuantityValue<Quantity>> {
|
||||
using Type = Quantity;
|
||||
};
|
||||
|
||||
template <QuantityValueType Value> using QuantityOfT = typename QuantityOf<std::remove_cvref_t<Value>>::Type;
|
||||
|
||||
using DimensionlessValue = QuantityValue<quantity::Dimensionless>;
|
||||
using MassValue = QuantityValue<quantity::Mass>;
|
||||
using LengthValue = QuantityValue<quantity::Length>;
|
||||
using TimeValue = QuantityValue<quantity::Time>;
|
||||
using AreaValue = QuantityValue<quantity::Area>;
|
||||
using VolumeValue = QuantityValue<quantity::Volume>;
|
||||
using DensityValue = QuantityValue<quantity::Density>;
|
||||
using SurfaceDensityValue = QuantityValue<quantity::SurfaceDensity>;
|
||||
using NumberDensityValue = QuantityValue<quantity::NumberDensity>;
|
||||
using PressureValue = QuantityValue<quantity::Pressure>;
|
||||
using TemperatureValue = QuantityValue<quantity::Temperature>;
|
||||
using EntropyValue = QuantityValue<quantity::Entropy>;
|
||||
using SpecificEntropyValue = QuantityValue<quantity::SpecificEntropy>;
|
||||
using ChemicalPotentialValue = QuantityValue<quantity::ChemicalPotential>;
|
||||
using EnergyValue = QuantityValue<quantity::Energy>;
|
||||
using InternalEnergyValue = QuantityValue<quantity::InternalEnergy>;
|
||||
using SpecificEnergyValue = QuantityValue<quantity::SpecificEnergy>;
|
||||
using SpecificInternalEnergyValue = QuantityValue<quantity::SpecificInternalEnergy>;
|
||||
using SpecificEnthalpyValue = QuantityValue<quantity::SpecificEnthalpy>;
|
||||
using EnergyDensityValue = QuantityValue<quantity::EnergyDensity>;
|
||||
using GravitationalPotentialValue = QuantityValue<quantity::GravitationalPotential>;
|
||||
using VelocityValue = QuantityValue<quantity::Velocity>;
|
||||
using AccelerationValue = QuantityValue<quantity::Acceleration>;
|
||||
using FrequencyValue = QuantityValue<quantity::Frequency>;
|
||||
using AngularVelocityValue = QuantityValue<quantity::AngularVelocity>;
|
||||
using MomentumValue = QuantityValue<quantity::Momentum>;
|
||||
using AngularMomentumValue = QuantityValue<quantity::AngularMomentum>;
|
||||
using MomentOfInertiaValue = QuantityValue<quantity::MomentOfInertia>;
|
||||
using ForceValue = QuantityValue<quantity::Force>;
|
||||
using TorqueValue = QuantityValue<quantity::Torque>;
|
||||
using PowerValue = QuantityValue<quantity::Power>;
|
||||
using LuminosityValue = QuantityValue<quantity::Luminosity>;
|
||||
using MassFlowRateValue = QuantityValue<quantity::MassFlowRate>;
|
||||
using OpacityValue = QuantityValue<quantity::Opacity>;
|
||||
using DynamicViscosityValue = QuantityValue<quantity::DynamicViscosity>;
|
||||
using KinematicViscosityValue = QuantityValue<quantity::KinematicViscosity>;
|
||||
using MagneticFluxDensityValue = QuantityValue<quantity::MagneticFluxDensity>;
|
||||
} // namespace mean_field::dimensions
|
||||
@@ -91,10 +91,10 @@ export namespace mean_field::eos {
|
||||
template <typename Candidate>
|
||||
concept BarotropicClosureEquationOfState =
|
||||
EquationOfStateModel<Candidate> && SupportsRelation<Candidate, DensityFromSpecificEnthalpy> &&
|
||||
SupportsPartialDerivative<Candidate, DensityFromSpecificEnthalpy, quantity::SpecificEnthalpy>;
|
||||
SupportsPartialDerivative<Candidate, DensityFromSpecificEnthalpy, dimensions::quantity::SpecificEnthalpy>;
|
||||
|
||||
template <typename Candidate>
|
||||
concept PressureForceEquationOfState =
|
||||
EquationOfStateModel<Candidate> && SupportsRelation<Candidate, PressureFromSpecificEnthalpy> &&
|
||||
SupportsPartialDerivative<Candidate, PressureFromSpecificEnthalpy, quantity::SpecificEnthalpy>;
|
||||
SupportsPartialDerivative<Candidate, PressureFromSpecificEnthalpy, dimensions::quantity::SpecificEnthalpy>;
|
||||
} // namespace mean_field::eos
|
||||
|
||||
@@ -8,6 +8,11 @@ export import :eos.evaluation;
|
||||
export namespace mean_field::eos {
|
||||
class Polytrope final {
|
||||
public:
|
||||
struct Parameters final {
|
||||
double n;
|
||||
double K;
|
||||
};
|
||||
|
||||
using Relations = RelationCatalog<
|
||||
PressureFromDensity,
|
||||
PressureFromSpecificEnthalpy,
|
||||
@@ -15,6 +20,13 @@ export namespace mean_field::eos {
|
||||
SpecificEnthalpyFromPressure,
|
||||
DensityFromSpecificEnthalpy>;
|
||||
|
||||
explicit Polytrope(const Parameters parameters)
|
||||
: Polytrope(
|
||||
parameters.n,
|
||||
parameters.K
|
||||
) {
|
||||
}
|
||||
|
||||
Polytrope(
|
||||
const double polytropic_index,
|
||||
const double polytropic_constant
|
||||
@@ -56,125 +68,131 @@ export namespace mean_field::eos {
|
||||
return m_enthalpy_scale;
|
||||
}
|
||||
|
||||
[[nodiscard]] PressureValue evaluate(
|
||||
[[nodiscard]] dimensions::PressureValue evaluate(
|
||||
PressureFromDensity,
|
||||
const DensityValue density
|
||||
const dimensions::DensityValue density
|
||||
) const {
|
||||
validate_nonnegativity(density.value(), "density");
|
||||
if (density.value() == 0.0) {
|
||||
return PressureValue{0.0};
|
||||
return dimensions::PressureValue{0.0};
|
||||
}
|
||||
|
||||
return PressureValue{m_polytropic_constant * std::pow(density.value(), 1.0 + 1.0 / m_polytropic_index)};
|
||||
return dimensions::PressureValue{
|
||||
m_polytropic_constant * std::pow(density.value(), 1.0 + 1.0 / m_polytropic_index)
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] SpecificEnthalpyValue evaluate(
|
||||
[[nodiscard]] dimensions::SpecificEnthalpyValue evaluate(
|
||||
SpecificEnthalpyFromDensity,
|
||||
const DensityValue density
|
||||
const dimensions::DensityValue density
|
||||
) const {
|
||||
validate_nonnegativity(density.value(), "density");
|
||||
if (density.value() == 0.0) {
|
||||
return SpecificEnthalpyValue{0.0};
|
||||
return dimensions::SpecificEnthalpyValue{0.0};
|
||||
}
|
||||
|
||||
return SpecificEnthalpyValue{m_enthalpy_scale * std::pow(density.value(), 1.0 / m_polytropic_index)};
|
||||
return dimensions::SpecificEnthalpyValue{
|
||||
m_enthalpy_scale * std::pow(density.value(), 1.0 / m_polytropic_index)
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] DensityValue evaluate(
|
||||
[[nodiscard]] dimensions::DensityValue evaluate(
|
||||
DensityFromSpecificEnthalpy,
|
||||
const SpecificEnthalpyValue specificEnthalpy
|
||||
const dimensions::SpecificEnthalpyValue specificEnthalpy
|
||||
) const {
|
||||
validate_finite(specificEnthalpy.value(), "specific enthalpy");
|
||||
|
||||
if (specificEnthalpy.value() <= 0.0) {
|
||||
return DensityValue{0.0};
|
||||
return dimensions::DensityValue{0.0};
|
||||
}
|
||||
|
||||
return DensityValue{std::pow(specificEnthalpy.value() / m_enthalpy_scale, m_polytropic_index)};
|
||||
return dimensions::DensityValue{std::pow(specificEnthalpy.value() / m_enthalpy_scale, m_polytropic_index)};
|
||||
}
|
||||
|
||||
[[nodiscard]] PressureValue evaluate(
|
||||
[[nodiscard]] dimensions::PressureValue evaluate(
|
||||
PressureFromSpecificEnthalpy,
|
||||
const SpecificEnthalpyValue specificEnthalpy
|
||||
const dimensions::SpecificEnthalpyValue specificEnthalpy
|
||||
) const {
|
||||
const DensityValue density = evaluate(DensityFromSpecificEnthalpy{}, specificEnthalpy);
|
||||
const dimensions::DensityValue density = evaluate(DensityFromSpecificEnthalpy{}, specificEnthalpy);
|
||||
|
||||
if (specificEnthalpy.value() <= 0.0) {
|
||||
return PressureValue{0.0};
|
||||
return dimensions::PressureValue{0.0};
|
||||
}
|
||||
|
||||
return PressureValue{density.value() * specificEnthalpy.value() / (m_polytropic_index + 1.0)};
|
||||
return dimensions::PressureValue{density.value() * specificEnthalpy.value() / (m_polytropic_index + 1.0)};
|
||||
}
|
||||
|
||||
[[nodiscard]] SpecificEnthalpyValue evaluate(
|
||||
[[nodiscard]] dimensions::SpecificEnthalpyValue evaluate(
|
||||
SpecificEnthalpyFromPressure,
|
||||
const PressureValue pressure
|
||||
const dimensions::PressureValue pressure
|
||||
) const {
|
||||
validate_nonnegativity(pressure.value(), "pressure");
|
||||
if (pressure.value() == 0.0) {
|
||||
return SpecificEnthalpyValue{0.0};
|
||||
return dimensions::SpecificEnthalpyValue{0.0};
|
||||
}
|
||||
|
||||
const double indexPlusOne = m_polytropic_index + 1.0;
|
||||
|
||||
return SpecificEnthalpyValue{
|
||||
return dimensions::SpecificEnthalpyValue{
|
||||
indexPlusOne * std::pow(m_polytropic_constant, m_polytropic_index / indexPlusOne) *
|
||||
std::pow(pressure.value(), 1.0 / indexPlusOne)
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] PartialDerivative<
|
||||
quantity::Density,
|
||||
quantity::SpecificEnthalpy>
|
||||
dimensions::quantity::Density,
|
||||
dimensions::quantity::SpecificEnthalpy>
|
||||
partialDerivative(
|
||||
DensityFromSpecificEnthalpy,
|
||||
WithRespectTo<quantity::SpecificEnthalpy>,
|
||||
const SpecificEnthalpyValue specificEnthalpy
|
||||
WithRespectTo<dimensions::quantity::SpecificEnthalpy>,
|
||||
const dimensions::SpecificEnthalpyValue specificEnthalpy
|
||||
) const {
|
||||
validate_finite(specificEnthalpy.value(), "specific enthalpy");
|
||||
if (specificEnthalpy.value() < 0.0) {
|
||||
return PartialDerivative<quantity::Density, quantity::SpecificEnthalpy>{0.0};
|
||||
return PartialDerivative<dimensions::quantity::Density, dimensions::quantity::SpecificEnthalpy>{0.0};
|
||||
}
|
||||
|
||||
if (specificEnthalpy.value() == 0.0) {
|
||||
return PartialDerivative<quantity::Density, quantity::SpecificEnthalpy>{
|
||||
return PartialDerivative<dimensions::quantity::Density, dimensions::quantity::SpecificEnthalpy>{
|
||||
m_polytropic_index == 1.0 ? 1.0 / m_enthalpy_scale : 0.0
|
||||
};
|
||||
}
|
||||
|
||||
return PartialDerivative<quantity::Density, quantity::SpecificEnthalpy>{
|
||||
return PartialDerivative<dimensions::quantity::Density, dimensions::quantity::SpecificEnthalpy>{
|
||||
m_polytropic_index / m_enthalpy_scale *
|
||||
std::pow(specificEnthalpy.value() / m_enthalpy_scale, m_polytropic_index - 1.0)
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] PartialDerivative<
|
||||
quantity::Pressure,
|
||||
quantity::SpecificEnthalpy>
|
||||
dimensions::quantity::Pressure,
|
||||
dimensions::quantity::SpecificEnthalpy>
|
||||
partialDerivative(
|
||||
PressureFromSpecificEnthalpy,
|
||||
WithRespectTo<quantity::SpecificEnthalpy>,
|
||||
const SpecificEnthalpyValue specificEnthalpy
|
||||
WithRespectTo<dimensions::quantity::SpecificEnthalpy>,
|
||||
const dimensions::SpecificEnthalpyValue specificEnthalpy
|
||||
) const {
|
||||
const DensityValue density = evaluate(DensityFromSpecificEnthalpy{}, specificEnthalpy);
|
||||
const dimensions::DensityValue density = evaluate(DensityFromSpecificEnthalpy{}, specificEnthalpy);
|
||||
|
||||
return PartialDerivative<quantity::Pressure, quantity::SpecificEnthalpy>{density.value()};
|
||||
return PartialDerivative<dimensions::quantity::Pressure, dimensions::quantity::SpecificEnthalpy>{
|
||||
density.value()
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] PartialDerivative<
|
||||
quantity::Pressure,
|
||||
quantity::Density>
|
||||
dimensions::quantity::Pressure,
|
||||
dimensions::quantity::Density>
|
||||
partialDerivative(
|
||||
PressureFromDensity,
|
||||
WithRespectTo<quantity::Density>,
|
||||
const DensityValue density
|
||||
WithRespectTo<dimensions::quantity::Density>,
|
||||
const dimensions::DensityValue density
|
||||
) const {
|
||||
validate_nonnegativity(density.value(), "density");
|
||||
if (density.value() == 0.0) {
|
||||
return PartialDerivative<quantity::Pressure, quantity::Density>{0.0};
|
||||
return PartialDerivative<dimensions::quantity::Pressure, dimensions::quantity::Density>{0.0};
|
||||
}
|
||||
|
||||
return PartialDerivative<quantity::Pressure, quantity::Density>{
|
||||
return PartialDerivative<dimensions::quantity::Pressure, dimensions::quantity::Density>{
|
||||
m_polytropic_constant * (1.0 + 1.0 / m_polytropic_index) *
|
||||
std::pow(density.value(), 1.0 / m_polytropic_index)
|
||||
};
|
||||
|
||||
@@ -13,10 +13,10 @@ export namespace mean_field::eos {
|
||||
ThermodynamicQuantityType InputQuantity,
|
||||
typename SurfaceState>
|
||||
[[nodiscard]] constexpr auto pressureSurfaceRelationInput(
|
||||
const PressureValue targetPressure,
|
||||
const dimensions::PressureValue targetPressure,
|
||||
const SurfaceState &state
|
||||
) {
|
||||
if constexpr (std::same_as<InputQuantity, quantity::Pressure>) {
|
||||
if constexpr (std::same_as<InputQuantity, dimensions::quantity::Pressure>) {
|
||||
return targetPressure;
|
||||
} else {
|
||||
return state.value(InputQuantity{});
|
||||
@@ -30,9 +30,9 @@ export namespace mean_field::eos {
|
||||
template <
|
||||
typename EquationOfState,
|
||||
typename SurfaceState>
|
||||
[[nodiscard]] static QuantityValue<CarrierQuantity> requiredCarrierValue(
|
||||
[[nodiscard]] static dimensions::QuantityValue<CarrierQuantity> requiredCarrierValue(
|
||||
const EquationOfState &equationOfState,
|
||||
const PressureValue targetPressure,
|
||||
const dimensions::PressureValue targetPressure,
|
||||
const SurfaceState &state
|
||||
) {
|
||||
return evaluate<CarrierQuantity>(
|
||||
@@ -47,11 +47,11 @@ export namespace mean_field::eos {
|
||||
typename SurfaceVariation>
|
||||
[[nodiscard]] static double inputJacobianContribution(
|
||||
const EquationOfState &equationOfState,
|
||||
const PressureValue targetPressure,
|
||||
const dimensions::PressureValue targetPressure,
|
||||
const SurfaceState &state,
|
||||
const SurfaceVariation &variation
|
||||
) {
|
||||
if constexpr (std::same_as<InputQuantity, quantity::Pressure>) {
|
||||
if constexpr (std::same_as<InputQuantity, dimensions::quantity::Pressure>) {
|
||||
return 0.0;
|
||||
} else {
|
||||
const auto derivative = partialDerivative<CarrierQuantity, InputQuantity>(
|
||||
@@ -67,7 +67,7 @@ export namespace mean_field::eos {
|
||||
typename SurfaceVariation>
|
||||
[[nodiscard]] static double carrierCorrectionJacobianAction(
|
||||
const EquationOfState &equationOfState,
|
||||
const PressureValue targetPressure,
|
||||
const dimensions::PressureValue targetPressure,
|
||||
const SurfaceState &state,
|
||||
const SurfaceVariation &variation
|
||||
) {
|
||||
@@ -92,18 +92,18 @@ export namespace mean_field::eos {
|
||||
|
||||
ResolvedPressureSurfaceRelation(
|
||||
const EquationOfState &equationOfState,
|
||||
const PressureValue targetPressure
|
||||
const dimensions::PressureValue targetPressure
|
||||
) noexcept
|
||||
: m_equationOfState(std::addressof(equationOfState)),
|
||||
m_targetPressure(targetPressure) {
|
||||
}
|
||||
|
||||
[[nodiscard]] PressureValue targetPressure() const noexcept {
|
||||
[[nodiscard]] dimensions::PressureValue targetPressure() const noexcept {
|
||||
return m_targetPressure;
|
||||
}
|
||||
|
||||
template <typename SurfaceState>
|
||||
[[nodiscard]] QuantityValue<CarrierQuantity> requiredCarrierValue(const SurfaceState &state) const {
|
||||
[[nodiscard]] dimensions::QuantityValue<CarrierQuantity> requiredCarrierValue(const SurfaceState &state) const {
|
||||
return detail::PressureSurfaceRelationOperations<RelationType>::requiredCarrierValue(
|
||||
*m_equationOfState, m_targetPressure, state
|
||||
);
|
||||
@@ -123,6 +123,6 @@ export namespace mean_field::eos {
|
||||
|
||||
private:
|
||||
const EquationOfState *m_equationOfState;
|
||||
PressureValue m_targetPressure;
|
||||
dimensions::PressureValue m_targetPressure;
|
||||
};
|
||||
} // namespace mean_field::eos
|
||||
|
||||
@@ -2,132 +2,42 @@ module;
|
||||
|
||||
#include <compare>
|
||||
#include <concepts>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
export module mean_field:eos.quantities;
|
||||
export import :dimensions.quantities;
|
||||
|
||||
export namespace mean_field::eos {
|
||||
struct ThermodynamicQuantity { };
|
||||
// Compatibility names for the thermodynamic subset now owned by the
|
||||
// general dimensions partition.
|
||||
using ThermodynamicQuantity = dimensions::ThermodynamicQuantity;
|
||||
|
||||
template <typename Candidate>
|
||||
concept ThermodynamicQuantityType =
|
||||
std::same_as<Candidate, std::remove_cv_t<Candidate>> && std::derived_from<Candidate, ThermodynamicQuantity>;
|
||||
concept ThermodynamicQuantityType = dimensions::ThermodynamicQuantityType<Candidate>;
|
||||
|
||||
namespace quantity {
|
||||
struct Density final : ThermodynamicQuantity {
|
||||
static constexpr std::string_view identifier = "density";
|
||||
};
|
||||
|
||||
struct Pressure final : ThermodynamicQuantity {
|
||||
static constexpr std::string_view identifier = "pressure";
|
||||
};
|
||||
|
||||
struct SpecificEnthalpy final : ThermodynamicQuantity {
|
||||
static constexpr std::string_view identifier = "specific_enthalpy";
|
||||
};
|
||||
using Density = dimensions::quantity::Density;
|
||||
using Pressure = dimensions::quantity::Pressure;
|
||||
using SpecificEnthalpy = dimensions::quantity::SpecificEnthalpy;
|
||||
} // namespace quantity
|
||||
|
||||
template <typename T>
|
||||
concept Numeric = std::integral<T> || std::floating_point<T>;
|
||||
concept Numeric = dimensions::Numeric<T>;
|
||||
|
||||
template <ThermodynamicQuantityType Quantity> class QuantityValue final {
|
||||
public:
|
||||
explicit constexpr QuantityValue(const double value) noexcept : m_value(value) {
|
||||
}
|
||||
template <ThermodynamicQuantityType Quantity> using QuantityValue = dimensions::QuantityValue<Quantity>;
|
||||
|
||||
[[nodiscard]] constexpr double value() const noexcept {
|
||||
return m_value;
|
||||
}
|
||||
using DensityValue = dimensions::DensityValue;
|
||||
using PressureValue = dimensions::PressureValue;
|
||||
using SpecificEnthalpyValue = dimensions::SpecificEnthalpyValue;
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator==(
|
||||
const QuantityValue &,
|
||||
const QuantityValue &
|
||||
) noexcept = default;
|
||||
|
||||
friend constexpr QuantityValue<Quantity> operator+(
|
||||
const QuantityValue<Quantity> &lhs,
|
||||
const QuantityValue<Quantity> &rhs
|
||||
) noexcept {
|
||||
return QuantityValue<Quantity>{lhs.m_value + rhs.m_value};
|
||||
}
|
||||
|
||||
friend constexpr QuantityValue<Quantity> operator-(
|
||||
const QuantityValue<Quantity> &lhs,
|
||||
const QuantityValue<Quantity> &rhs
|
||||
) noexcept {
|
||||
return QuantityValue<Quantity>{lhs.m_value - rhs.m_value};
|
||||
}
|
||||
|
||||
template <Numeric rhsT>
|
||||
friend constexpr QuantityValue<Quantity> operator*(
|
||||
const QuantityValue<Quantity> &lhs,
|
||||
rhsT rhs
|
||||
) noexcept {
|
||||
return QuantityValue<Quantity>{lhs.m_value * static_cast<double>(rhs)};
|
||||
}
|
||||
|
||||
template <Numeric lhsT>
|
||||
friend constexpr QuantityValue<Quantity> operator*(
|
||||
lhsT lhs,
|
||||
const QuantityValue<Quantity> &rhs
|
||||
) noexcept {
|
||||
return QuantityValue<Quantity>{static_cast<double>(lhs) * rhs.m_value};
|
||||
}
|
||||
|
||||
template <Numeric rhsT>
|
||||
friend constexpr QuantityValue<Quantity> operator/(
|
||||
const QuantityValue<Quantity> &lhs,
|
||||
rhsT rhs
|
||||
) noexcept {
|
||||
return QuantityValue<Quantity>{lhs.m_value / static_cast<double>(rhs)};
|
||||
}
|
||||
|
||||
template <Numeric compT>
|
||||
friend constexpr std::partial_ordering operator<=>(
|
||||
const QuantityValue<Quantity> &lhs,
|
||||
compT rhs
|
||||
) noexcept {
|
||||
return lhs.m_value <=> static_cast<double>(rhs);
|
||||
}
|
||||
|
||||
template <Numeric compT>
|
||||
friend constexpr std::partial_ordering operator<=>(
|
||||
compT lhs,
|
||||
const QuantityValue<Quantity> &rhs
|
||||
) noexcept {
|
||||
return static_cast<double>(lhs) <=> rhs.m_value;
|
||||
}
|
||||
|
||||
friend constexpr std::partial_ordering operator<=>(
|
||||
const QuantityValue<Quantity> &lhs,
|
||||
const QuantityValue<Quantity> &rhs
|
||||
) noexcept {
|
||||
return lhs.m_value <=> rhs.m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
double m_value;
|
||||
};
|
||||
|
||||
using DensityValue = QuantityValue<quantity::Density>;
|
||||
using PressureValue = QuantityValue<quantity::Pressure>;
|
||||
using SpecificEnthalpyValue = QuantityValue<quantity::SpecificEnthalpy>;
|
||||
|
||||
template <typename Candidate> struct IsQuantityValue : std::false_type { };
|
||||
|
||||
template <ThermodynamicQuantityType Quantity> struct IsQuantityValue<QuantityValue<Quantity>> : std::true_type { };
|
||||
template <typename Candidate> using IsQuantityValue = dimensions::IsQuantityValue<Candidate>;
|
||||
|
||||
template <typename Candidate>
|
||||
concept QuantityValueType = IsQuantityValue<std::remove_cvref_t<Candidate>>::value;
|
||||
concept QuantityValueType =
|
||||
dimensions::QuantityValueType<Candidate> && ThermodynamicQuantityType<dimensions::QuantityOfT<Candidate>>;
|
||||
|
||||
template <typename Candidate> struct QuantityOf;
|
||||
template <typename Candidate> using QuantityOf = dimensions::QuantityOf<Candidate>;
|
||||
|
||||
template <ThermodynamicQuantityType Quantity> struct QuantityOf<QuantityValue<Quantity>> {
|
||||
using Type = Quantity;
|
||||
};
|
||||
|
||||
template <QuantityValueType Value> using QuantityOfT = typename QuantityOf<std::remove_cvref_t<Value>>::Type;
|
||||
template <QuantityValueType Value> using QuantityOfT = dimensions::QuantityOfT<Value>;
|
||||
|
||||
template <ThermodynamicQuantityType OutputQuantity, ThermodynamicQuantityType InputQuantity>
|
||||
class PartialDerivative final {
|
||||
@@ -163,7 +73,7 @@ export namespace mean_field::eos {
|
||||
InputQuantity> &rhs
|
||||
) noexcept;
|
||||
|
||||
template <Numeric rhsT>
|
||||
template <Numeric Scalar>
|
||||
friend constexpr PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity>
|
||||
@@ -171,21 +81,21 @@ export namespace mean_field::eos {
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &,
|
||||
rhsT
|
||||
Scalar
|
||||
) noexcept;
|
||||
|
||||
template <Numeric lhsT>
|
||||
template <Numeric Scalar>
|
||||
friend constexpr PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity>
|
||||
operator*(
|
||||
lhsT,
|
||||
Scalar,
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &
|
||||
) noexcept;
|
||||
|
||||
template <Numeric rhsT>
|
||||
template <Numeric Scalar>
|
||||
friend constexpr PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity>
|
||||
@@ -193,22 +103,22 @@ export namespace mean_field::eos {
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &,
|
||||
rhsT
|
||||
Scalar
|
||||
) noexcept;
|
||||
|
||||
template <Numeric cmpT>
|
||||
template <Numeric Scalar>
|
||||
friend constexpr std::partial_ordering operator<=>(
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &lhs,
|
||||
cmpT rhs
|
||||
Scalar rhs
|
||||
) noexcept {
|
||||
return lhs.m_value <=> static_cast<double>(rhs);
|
||||
}
|
||||
|
||||
template <Numeric cmpT>
|
||||
template <Numeric Scalar>
|
||||
friend constexpr std::partial_ordering operator<=>(
|
||||
cmpT lhs,
|
||||
Scalar lhs,
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &rhs
|
||||
|
||||
@@ -85,9 +85,11 @@ export namespace mean_field::eos {
|
||||
template <std::size_t Index, ThermodynamicRelationType RelationType>
|
||||
using RelationInputT = typename detail::QuantityAt<Index, typename RelationType::InputQuantities>::Type;
|
||||
|
||||
using PressureFromDensity = Relation<quantity::Pressure, quantity::Density>;
|
||||
using PressureFromSpecificEnthalpy = Relation<quantity::Pressure, quantity::SpecificEnthalpy>;
|
||||
using SpecificEnthalpyFromDensity = Relation<quantity::SpecificEnthalpy, quantity::Density>;
|
||||
using SpecificEnthalpyFromPressure = Relation<quantity::SpecificEnthalpy, quantity::Pressure>;
|
||||
using DensityFromSpecificEnthalpy = Relation<quantity::Density, quantity::SpecificEnthalpy>;
|
||||
using PressureFromDensity = Relation<dimensions::quantity::Pressure, dimensions::quantity::Density>;
|
||||
using PressureFromSpecificEnthalpy =
|
||||
Relation<dimensions::quantity::Pressure, dimensions::quantity::SpecificEnthalpy>;
|
||||
using SpecificEnthalpyFromDensity = Relation<dimensions::quantity::SpecificEnthalpy, dimensions::quantity::Density>;
|
||||
using SpecificEnthalpyFromPressure =
|
||||
Relation<dimensions::quantity::SpecificEnthalpy, dimensions::quantity::Pressure>;
|
||||
using DensityFromSpecificEnthalpy = Relation<dimensions::quantity::Density, dimensions::quantity::SpecificEnthalpy>;
|
||||
} // namespace mean_field::eos
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
module;
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
export module mean_field:equilibrium.stellar_discretization;
|
||||
|
||||
export import :fem;
|
||||
export import :mapping.domain_mapper;
|
||||
|
||||
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.
|
||||
*/
|
||||
class StellarDiscretization final {
|
||||
public:
|
||||
explicit StellarDiscretization(fem::FEM &finiteElementModel)
|
||||
: StellarDiscretization(
|
||||
finiteElementModel,
|
||||
RequireDomainMapper(finiteElementModel)
|
||||
) {
|
||||
}
|
||||
|
||||
StellarDiscretization(
|
||||
fem::FEM &finiteElementModel,
|
||||
const mapping::DomainMapper &domainMapper
|
||||
)
|
||||
: m_finiteElementModel(std::addressof(finiteElementModel)),
|
||||
m_domainMapper(std::addressof(domainMapper)) {
|
||||
if (!finiteElementModel.okay()) {
|
||||
throw std::invalid_argument("A stellar discretization requires a complete finite-element model.");
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] fem::FEM &finiteElementModel() const noexcept {
|
||||
return *m_finiteElementModel;
|
||||
}
|
||||
|
||||
[[nodiscard]] const mapping::DomainMapper &domainMapper() const noexcept {
|
||||
return *m_domainMapper;
|
||||
}
|
||||
|
||||
[[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) {
|
||||
if (finiteElementModel.domainMapperStateless == nullptr) {
|
||||
throw std::invalid_argument("A stellar discretization requires a domain mapper.");
|
||||
}
|
||||
return *finiteElementModel.domainMapperStateless;
|
||||
}
|
||||
|
||||
fem::FEM *m_finiteElementModel;
|
||||
const mapping::DomainMapper *m_domainMapper;
|
||||
};
|
||||
} // namespace mean_field::equilibrium
|
||||
@@ -208,6 +208,9 @@ export namespace mean_field::field {
|
||||
using FormList = TypeList<Form::MeshExtension, Form::GravityForce, Form::CentrifugalForce, Form::ErrorNorm>;
|
||||
};
|
||||
|
||||
// Current realization of MultiplierFor<FixedTotalMass>. This remains a
|
||||
// barotrope-specific field representation: the specification compiler,
|
||||
// rather than the universal state registry, decides when it is present.
|
||||
struct BarotropicConstant {
|
||||
static constexpr std::string_view name = "barotropic_constant";
|
||||
|
||||
@@ -226,6 +229,26 @@ export namespace mean_field::field {
|
||||
static_assert(constraintsAreValid);
|
||||
};
|
||||
|
||||
// Solver border generated by FixedCentralDensity. This is deliberately a
|
||||
// non-spatial numerical coordinate rather than a physical stellar field.
|
||||
struct CentralDensityBorder {
|
||||
static constexpr std::string_view name = "central_density_border";
|
||||
|
||||
using Support = NonSpatialSupport;
|
||||
|
||||
struct Scalar final : GlobalScalarQ {
|
||||
static constexpr std::string_view symbol = "lambda_rho_c";
|
||||
};
|
||||
|
||||
using Quantities = TypeList<Scalar>;
|
||||
using Constraints = TypeList<>;
|
||||
using FormList = TypeList<>;
|
||||
|
||||
static constexpr bool constraintsAreValid = validate_constraints(Constraints{});
|
||||
|
||||
static_assert(constraintsAreValid);
|
||||
};
|
||||
|
||||
// =========================================================================
|
||||
// Specific enthalpy
|
||||
//
|
||||
|
||||
@@ -78,6 +78,7 @@ export namespace mean_field::mapping {
|
||||
int m_dimension;
|
||||
|
||||
mfem::Vector m_shape;
|
||||
mfem::DenseMatrix m_reference_dshape;
|
||||
mfem::DenseMatrix m_mesh_dshape;
|
||||
mfem::Vector m_field_value;
|
||||
mfem::DenseMatrix m_field_jacobian;
|
||||
@@ -178,7 +179,8 @@ export namespace mean_field::mapping {
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace,
|
||||
mfem::Vector &value,
|
||||
mfem::DenseMatrix &jacobian
|
||||
mfem::DenseMatrix &jacobian,
|
||||
const mfem::DenseMatrix *inverse_mesh_jacobian
|
||||
) const;
|
||||
|
||||
[[nodiscard]] MappingStatus EvaluateCompactificationCoordinate(
|
||||
@@ -186,7 +188,19 @@ export namespace mean_field::mapping {
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace,
|
||||
CompactificationPointData &point_data
|
||||
CompactificationPointData &point_data,
|
||||
const mfem::DenseMatrix *inverse_mesh_jacobian
|
||||
) const;
|
||||
|
||||
[[nodiscard]] MappingStatus EvaluatePointVariationImpl(
|
||||
const ElementMappingData &element_data,
|
||||
const ElementDisplacementData &direction,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
const MappingPointContext &base_context,
|
||||
Workspace &workspace,
|
||||
MappingPointVariation &variation,
|
||||
const mfem::DenseMatrix *inverse_mesh_jacobian
|
||||
) const;
|
||||
|
||||
[[nodiscard]] static mfem::ElementTransformation &SelectFaceElementTransformation(
|
||||
|
||||
@@ -25,6 +25,7 @@ export import :integrators.viscosity;
|
||||
export import :quadrature.policy;
|
||||
export import :quadrature.mfem;
|
||||
export import :solver.fields;
|
||||
export import :solver.preconditioning_diagnostics;
|
||||
export import :utils.blocks;
|
||||
export import :operators.gravity_field;
|
||||
export import :operators.gravity_field_jacobian;
|
||||
@@ -51,9 +52,14 @@ export import :operators.context.rotational_displacement_force;
|
||||
export import :operators.kernels.rotational_displacement_force;
|
||||
export import :operators.prepared_rotational_displacement_force;
|
||||
export import :operators.prepared_displacement_residual;
|
||||
export import :dimensions.quantities;
|
||||
export import :model.structure_profile;
|
||||
export import :model.structure.base;
|
||||
export import :model.structure.polytropic;
|
||||
export import :model.specifications;
|
||||
export import :model.typed_stellar;
|
||||
export import :model.compiled_fixed_mass;
|
||||
export import :model.compiled_fixed_central_density;
|
||||
export import :eos.quantities;
|
||||
export import :eos.relations;
|
||||
export import :eos.concepts;
|
||||
@@ -61,6 +67,7 @@ export import :eos.evaluation;
|
||||
export import :eos.pressure_surface;
|
||||
export import :eos.runtime;
|
||||
export import :eos.polytrope;
|
||||
export import :seed.lane_emden;
|
||||
export import :surface.constant;
|
||||
export import :surface.dependencies;
|
||||
export import :surface.compiled;
|
||||
@@ -73,7 +80,15 @@ export import :deformation.vacuum_extension;
|
||||
export import :deformation.radial_extensions;
|
||||
export import :deformation.domain_deformation;
|
||||
export import :model.stellar;
|
||||
export import :operators.root_manifest;
|
||||
export import :operators.prepared_constraint;
|
||||
export import :operators.prepared_mass_normalization;
|
||||
export import :operators.prepared_central_density;
|
||||
export import :operators.prepared_centering_constraint;
|
||||
export import :operators.prepared_surface_constraint;
|
||||
export import :operators.prepared_stellar_equilibrium;
|
||||
export import :operators.prepared_central_density_stellar_equilibrium;
|
||||
export import :equilibrium.stellar_discretization;
|
||||
export import :operators.stellar_equilibrium_problem;
|
||||
export import :seed.stellar_equilibrium_projection;
|
||||
export import :operators.stellar_equilibrium_system;
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
|
||||
export module mean_field:model.compiled_fixed_central_density;
|
||||
|
||||
export import :eos.polytrope;
|
||||
export import :field.registry;
|
||||
export import :model.compiled_fixed_mass;
|
||||
export import :utils.blocks;
|
||||
|
||||
export namespace mean_field::models {
|
||||
struct CentralDensityLayoutRequest final {
|
||||
using SpecificationType = FixedCentralDensity;
|
||||
using GeneratedValueType = BorderFor<FixedCentralDensity>;
|
||||
using GeneratedResidualType = ResidualFor<FixedCentralDensity>;
|
||||
using ValueBlockType = utils::blocks::fixed_central_density::central_value::value;
|
||||
using ResidualBlockType = utils::blocks::fixed_central_density::central_value::residual;
|
||||
using TermType = utils::blocks::fixed_central_density::central_value;
|
||||
using StateValueBlockTypes = ModelTypeList<utils::blocks::enthalpy::specific::value>;
|
||||
|
||||
static constexpr ConstraintRowInjection rowInjection = ConstraintRowInjection::solver_border;
|
||||
static constexpr std::size_t valueArity = GeneratedValueType::scalarArity;
|
||||
static constexpr std::size_t residualArity = GeneratedResidualType::scalarArity;
|
||||
|
||||
template <typename Form> [[nodiscard]] static consteval auto valueBlock() {
|
||||
return utils::blocks::get_value_block<Form>(TermType{});
|
||||
}
|
||||
|
||||
template <typename Form> [[nodiscard]] static consteval auto residualBlock() {
|
||||
return utils::blocks::get_residual_block<Form>(TermType{});
|
||||
}
|
||||
};
|
||||
|
||||
class CompiledFixedCentralDensity final {
|
||||
public:
|
||||
using SpecificationType = FixedCentralDensity;
|
||||
using LayoutRequest = CentralDensityLayoutRequest;
|
||||
using BorderType = typename LayoutRequest::GeneratedValueType;
|
||||
using ResidualType = typename LayoutRequest::GeneratedResidualType;
|
||||
using CarrierField = field::Enthalpy;
|
||||
using BorderField = field::CentralDensityBorder;
|
||||
|
||||
CompiledFixedCentralDensity(
|
||||
const FixedCentralDensity specification,
|
||||
const eos::Polytrope &equationOfState
|
||||
)
|
||||
: m_specification(specification),
|
||||
m_equationOfState(equationOfState),
|
||||
m_targetEnthalpy(
|
||||
eos::evaluate<eos::quantity::SpecificEnthalpy>(
|
||||
m_equationOfState,
|
||||
m_specification.targetDensity()
|
||||
)
|
||||
) {
|
||||
}
|
||||
|
||||
[[nodiscard]] const FixedCentralDensity &specification() const noexcept {
|
||||
return m_specification;
|
||||
}
|
||||
|
||||
[[nodiscard]] dimensions::DensityValue targetDensity() const noexcept {
|
||||
return m_specification.targetDensity();
|
||||
}
|
||||
|
||||
[[nodiscard]] dimensions::SpecificEnthalpyValue targetEnthalpy() const noexcept {
|
||||
return m_targetEnthalpy;
|
||||
}
|
||||
|
||||
[[nodiscard]] dimensions::DensityValue
|
||||
densityFromEnthalpy(const dimensions::SpecificEnthalpyValue enthalpy) const {
|
||||
return eos::evaluate<eos::quantity::Density>(m_equationOfState, enthalpy);
|
||||
}
|
||||
|
||||
[[nodiscard]] static consteval LayoutRequest layoutRequest() noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
FixedCentralDensity m_specification;
|
||||
eos::Polytrope m_equationOfState;
|
||||
dimensions::SpecificEnthalpyValue m_targetEnthalpy;
|
||||
};
|
||||
|
||||
[[nodiscard]] inline CompiledFixedCentralDensity compileConstraint(
|
||||
const FixedCentralDensity specification,
|
||||
const eos::Polytrope &equationOfState
|
||||
) {
|
||||
return {specification, equationOfState};
|
||||
}
|
||||
|
||||
static_assert(ConstraintLayoutRequestType<CentralDensityLayoutRequest>);
|
||||
static_assert(CompiledConstraint<CompiledFixedCentralDensity>);
|
||||
} // namespace mean_field::models
|
||||
115
libmeanfield/interface/models/compiled_fixed_mass.cppm
Normal file
115
libmeanfield/interface/models/compiled_fixed_mass.cppm
Normal file
@@ -0,0 +1,115 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
|
||||
export module mean_field:model.compiled_fixed_mass;
|
||||
|
||||
export import :field.registry;
|
||||
export import :model.specifications;
|
||||
export import :utils.blocks;
|
||||
|
||||
export namespace mean_field::models {
|
||||
enum class ConstraintRowInjection { append, solver_border };
|
||||
|
||||
template <
|
||||
ModelSpecification Specification,
|
||||
typename GeneratedValue,
|
||||
typename GeneratedResidual,
|
||||
typename ValueBlock,
|
||||
typename ResidualBlock,
|
||||
typename Term,
|
||||
typename... StateValueBlocks>
|
||||
struct ConstraintLayoutRequest final {
|
||||
using SpecificationType = Specification;
|
||||
using GeneratedValueType = GeneratedValue;
|
||||
using GeneratedResidualType = GeneratedResidual;
|
||||
using ValueBlockType = ValueBlock;
|
||||
using ResidualBlockType = ResidualBlock;
|
||||
using TermType = Term;
|
||||
using StateValueBlockTypes = ModelTypeList<StateValueBlocks...>;
|
||||
|
||||
static constexpr ConstraintRowInjection rowInjection = ConstraintRowInjection::append;
|
||||
static constexpr std::size_t valueArity = GeneratedValue::scalarArity;
|
||||
static constexpr std::size_t residualArity = GeneratedResidual::scalarArity;
|
||||
|
||||
template <typename Form> [[nodiscard]] static consteval auto valueBlock() {
|
||||
return utils::blocks::get_value_block<Form>(Term{});
|
||||
}
|
||||
|
||||
template <typename Form> [[nodiscard]] static consteval auto residualBlock() {
|
||||
return utils::blocks::get_residual_block<Form>(Term{});
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Candidate>
|
||||
concept ConstraintLayoutRequestType = requires {
|
||||
typename std::remove_cvref_t<Candidate>::SpecificationType;
|
||||
typename std::remove_cvref_t<Candidate>::GeneratedValueType;
|
||||
typename std::remove_cvref_t<Candidate>::GeneratedResidualType;
|
||||
typename std::remove_cvref_t<Candidate>::ValueBlockType;
|
||||
typename std::remove_cvref_t<Candidate>::ResidualBlockType;
|
||||
typename std::remove_cvref_t<Candidate>::StateValueBlockTypes;
|
||||
requires ModelSpecification<typename std::remove_cvref_t<Candidate>::SpecificationType>;
|
||||
requires std::remove_cvref_t<Candidate>::valueArity == std::remove_cvref_t<Candidate>::residualArity;
|
||||
};
|
||||
|
||||
using FixedMassLayoutRequest = ConstraintLayoutRequest<
|
||||
FixedTotalMass,
|
||||
MultiplierFor<FixedTotalMass>,
|
||||
ResidualFor<FixedTotalMass>,
|
||||
utils::blocks::fixed_total_mass::mass_normalization::value,
|
||||
utils::blocks::fixed_total_mass::mass_normalization::residual,
|
||||
utils::blocks::fixed_total_mass::mass_normalization,
|
||||
utils::blocks::density::mass::value,
|
||||
utils::blocks::displacement::geometry::value>;
|
||||
|
||||
class CompiledFixedMass final {
|
||||
public:
|
||||
using SpecificationType = FixedTotalMass;
|
||||
using LayoutRequest = FixedMassLayoutRequest;
|
||||
using MultiplierType = typename LayoutRequest::GeneratedValueType;
|
||||
using ResidualType = typename LayoutRequest::GeneratedResidualType;
|
||||
|
||||
// In the current barotropic formulation, the multiplier generated by
|
||||
// FixedTotalMass is realized by the historical scalar C field.
|
||||
using MultiplierField = field::BarotropicConstant;
|
||||
|
||||
explicit CompiledFixedMass(const FixedTotalMass specification) noexcept : m_specification(specification) {
|
||||
}
|
||||
|
||||
[[nodiscard]] const FixedTotalMass &specification() const noexcept {
|
||||
return m_specification;
|
||||
}
|
||||
|
||||
[[nodiscard]] dimensions::MassValue targetMass() const noexcept {
|
||||
return m_specification.targetMass();
|
||||
}
|
||||
|
||||
[[nodiscard]] static consteval LayoutRequest layoutRequest() noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
FixedTotalMass m_specification;
|
||||
};
|
||||
|
||||
template <typename Candidate>
|
||||
concept CompiledConstraint = requires(const std::remove_cvref_t<Candidate> &constraint) {
|
||||
typename std::remove_cvref_t<Candidate>::SpecificationType;
|
||||
typename std::remove_cvref_t<Candidate>::LayoutRequest;
|
||||
requires ModelSpecification<typename std::remove_cvref_t<Candidate>::SpecificationType>;
|
||||
requires ConstraintLayoutRequestType<typename std::remove_cvref_t<Candidate>::LayoutRequest>;
|
||||
{
|
||||
constraint.specification()
|
||||
} noexcept -> std::same_as<const typename std::remove_cvref_t<Candidate>::SpecificationType &>;
|
||||
{ constraint.layoutRequest() } noexcept -> std::same_as<typename std::remove_cvref_t<Candidate>::LayoutRequest>;
|
||||
};
|
||||
|
||||
[[nodiscard]] inline CompiledFixedMass compileConstraint(const FixedTotalMass specification) noexcept {
|
||||
return CompiledFixedMass{specification};
|
||||
}
|
||||
|
||||
static_assert(ConstraintLayoutRequestType<FixedMassLayoutRequest>);
|
||||
static_assert(CompiledConstraint<CompiledFixedMass>);
|
||||
} // namespace mean_field::models
|
||||
481
libmeanfield/interface/models/specifications.cppm
Normal file
481
libmeanfield/interface/models/specifications.cppm
Normal file
@@ -0,0 +1,481 @@
|
||||
module;
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <compare>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <format>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
export module mean_field:model.specifications;
|
||||
|
||||
export import :eos.polytrope;
|
||||
export import :surface.constant;
|
||||
|
||||
export namespace mean_field::models {
|
||||
enum class SpecificationRole {
|
||||
constitutive_law,
|
||||
boundary_condition,
|
||||
invariant,
|
||||
phase_condition,
|
||||
gauge_choice,
|
||||
rotation_law
|
||||
};
|
||||
|
||||
struct SpecificationKey final {
|
||||
SpecificationRole role;
|
||||
std::size_t ordinal;
|
||||
|
||||
constexpr auto operator<=>(const SpecificationKey &) const = default;
|
||||
};
|
||||
|
||||
struct SpecificationDescriptor final {
|
||||
std::string_view name;
|
||||
SpecificationRole role;
|
||||
SpecificationKey key;
|
||||
std::size_t generatedValueArity;
|
||||
std::size_t generatedResidualArity;
|
||||
|
||||
constexpr bool operator==(const SpecificationDescriptor &) const = default;
|
||||
};
|
||||
|
||||
enum class EquilibriumSystemCompilation {
|
||||
complete_equilibrium_system,
|
||||
equation_contributions_only,
|
||||
|
||||
// Transitional spellings retained while internal solver code is
|
||||
// migrated to physics-facing equilibrium-system terminology.
|
||||
isolated_root = complete_equilibrium_system,
|
||||
assembly_only = equation_contributions_only
|
||||
};
|
||||
|
||||
using ModelCompilationClass = EquilibriumSystemCompilation;
|
||||
|
||||
struct RuntimeSpecificationDescriptor final {
|
||||
SpecificationDescriptor specification;
|
||||
std::size_t canonicalIndex;
|
||||
bool hasRootCompiler;
|
||||
|
||||
constexpr bool operator==(const RuntimeSpecificationDescriptor &) const = default;
|
||||
};
|
||||
|
||||
template <typename Candidate> struct SpecificationTraits;
|
||||
|
||||
template <typename Candidate>
|
||||
concept ModelSpecification = requires {
|
||||
typename std::remove_cvref_t<Candidate>::Parameters;
|
||||
{ SpecificationTraits<std::remove_cvref_t<Candidate>>::name } -> std::convertible_to<std::string_view>;
|
||||
{ SpecificationTraits<std::remove_cvref_t<Candidate>>::role } -> std::convertible_to<SpecificationRole>;
|
||||
{ SpecificationTraits<std::remove_cvref_t<Candidate>>::key } -> std::convertible_to<SpecificationKey>;
|
||||
} && std::constructible_from<std::remove_cvref_t<Candidate>, typename std::remove_cvref_t<Candidate>::Parameters>;
|
||||
|
||||
class FixedTotalMass final {
|
||||
public:
|
||||
struct Parameters final {
|
||||
dimensions::MassValue Mtotal;
|
||||
};
|
||||
|
||||
using TargetValue = dimensions::MassValue;
|
||||
|
||||
explicit FixedTotalMass(const Parameters parameters) : FixedTotalMass(parameters.Mtotal) {
|
||||
}
|
||||
|
||||
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()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] TargetValue targetMass() const noexcept {
|
||||
return m_targetMass;
|
||||
}
|
||||
|
||||
private:
|
||||
TargetValue m_targetMass;
|
||||
};
|
||||
|
||||
class FixedCentralDensity final {
|
||||
public:
|
||||
struct Parameters final {
|
||||
dimensions::DensityValue RhoC;
|
||||
};
|
||||
|
||||
using TargetValue = dimensions::DensityValue;
|
||||
|
||||
explicit FixedCentralDensity(const Parameters parameters) : FixedCentralDensity(parameters.RhoC) {
|
||||
}
|
||||
|
||||
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()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] TargetValue targetDensity() const noexcept {
|
||||
return m_targetDensity;
|
||||
}
|
||||
|
||||
private:
|
||||
TargetValue m_targetDensity;
|
||||
};
|
||||
|
||||
template <> struct SpecificationTraits<eos::Polytrope> {
|
||||
static constexpr std::string_view name = "Polytrope";
|
||||
static constexpr SpecificationRole role = SpecificationRole::constitutive_law;
|
||||
static constexpr SpecificationKey key{role, 0};
|
||||
};
|
||||
|
||||
template <> struct SpecificationTraits<surface::ConstantPressureSurface> {
|
||||
static constexpr std::string_view name = "IsobaricSurface";
|
||||
static constexpr SpecificationRole role = SpecificationRole::boundary_condition;
|
||||
static constexpr SpecificationKey key{role, 0};
|
||||
};
|
||||
|
||||
template <> struct SpecificationTraits<FixedTotalMass> {
|
||||
static constexpr std::string_view name = "FixedTotalMass";
|
||||
static constexpr SpecificationRole role = SpecificationRole::invariant;
|
||||
static constexpr SpecificationKey key{role, 0};
|
||||
};
|
||||
|
||||
template <> struct SpecificationTraits<FixedCentralDensity> {
|
||||
static constexpr std::string_view name = "FixedCentralDensity";
|
||||
static constexpr SpecificationRole role = SpecificationRole::phase_condition;
|
||||
static constexpr SpecificationKey key{role, 0};
|
||||
};
|
||||
|
||||
template <typename... Types> struct ModelTypeList final {
|
||||
static constexpr std::size_t size = sizeof...(Types);
|
||||
};
|
||||
|
||||
template <typename Query, typename List> struct ModelTypeListContains;
|
||||
|
||||
template <typename Query, typename... Types>
|
||||
struct ModelTypeListContains<Query, ModelTypeList<Types...>>
|
||||
: std::bool_constant<(std::same_as<Query, Types> || ...)> { };
|
||||
|
||||
template <typename Query, typename List>
|
||||
inline constexpr bool modelTypeListContains = ModelTypeListContains<Query, List>::value;
|
||||
|
||||
template <ModelSpecification Specification> struct ResidualFor final {
|
||||
using SpecificationType = Specification;
|
||||
|
||||
static constexpr std::size_t scalarArity = 1;
|
||||
};
|
||||
|
||||
template <ModelSpecification Specification> struct MultiplierFor final {
|
||||
using SpecificationType = Specification;
|
||||
|
||||
static constexpr std::size_t scalarArity = 1;
|
||||
};
|
||||
|
||||
template <ModelSpecification Specification> struct BorderFor final {
|
||||
using SpecificationType = Specification;
|
||||
|
||||
static constexpr std::size_t scalarArity = 1;
|
||||
};
|
||||
|
||||
template <ModelSpecification Specification> struct SpecificationContribution {
|
||||
using GeneratedValues = ModelTypeList<>;
|
||||
using GeneratedResiduals = ModelTypeList<>;
|
||||
|
||||
static constexpr bool isDefined = false;
|
||||
static constexpr bool hasRootCompiler = false;
|
||||
};
|
||||
|
||||
template <> struct SpecificationContribution<eos::Polytrope> {
|
||||
using GeneratedValues = ModelTypeList<>;
|
||||
using GeneratedResiduals = ModelTypeList<>;
|
||||
|
||||
static constexpr bool isDefined = true;
|
||||
static constexpr bool hasRootCompiler = true;
|
||||
};
|
||||
|
||||
template <> struct SpecificationContribution<surface::ConstantPressureSurface> {
|
||||
using GeneratedValues = ModelTypeList<>;
|
||||
using GeneratedResiduals = ModelTypeList<>;
|
||||
|
||||
static constexpr bool isDefined = true;
|
||||
static constexpr bool hasRootCompiler = true;
|
||||
};
|
||||
|
||||
template <> struct SpecificationContribution<FixedTotalMass> {
|
||||
using GeneratedValues = ModelTypeList<MultiplierFor<FixedTotalMass>>;
|
||||
using GeneratedResiduals = ModelTypeList<ResidualFor<FixedTotalMass>>;
|
||||
|
||||
static constexpr bool isDefined = true;
|
||||
static constexpr bool hasRootCompiler = true;
|
||||
};
|
||||
|
||||
template <> struct SpecificationContribution<FixedCentralDensity> {
|
||||
using GeneratedValues = ModelTypeList<BorderFor<FixedCentralDensity>>;
|
||||
using GeneratedResiduals = ModelTypeList<ResidualFor<FixedCentralDensity>>;
|
||||
|
||||
static constexpr bool isDefined = true;
|
||||
static constexpr bool hasRootCompiler = true;
|
||||
};
|
||||
|
||||
template <typename Candidate>
|
||||
concept ResolvedModelSpecification =
|
||||
ModelSpecification<Candidate> && SpecificationContribution<std::remove_cvref_t<Candidate>>::isDefined;
|
||||
|
||||
namespace detail {
|
||||
template <typename... Specifications> struct SpecificationSetStorage final {
|
||||
static constexpr std::size_t size = sizeof...(Specifications);
|
||||
};
|
||||
|
||||
template <typename... Lists> struct ConcatenateModelTypeLists;
|
||||
|
||||
template <> struct ConcatenateModelTypeLists<> {
|
||||
using Type = ModelTypeList<>;
|
||||
};
|
||||
|
||||
template <typename... Types> struct ConcatenateModelTypeLists<ModelTypeList<Types...>> {
|
||||
using Type = ModelTypeList<Types...>;
|
||||
};
|
||||
|
||||
template <typename... First, typename... Second, typename... Remaining>
|
||||
struct ConcatenateModelTypeLists<ModelTypeList<First...>, ModelTypeList<Second...>, Remaining...> {
|
||||
using Type = typename ConcatenateModelTypeLists<ModelTypeList<First..., Second...>, Remaining...>::Type;
|
||||
};
|
||||
|
||||
template <ModelSpecification Specification, typename Set> struct InsertSpecification;
|
||||
|
||||
template <ModelSpecification Specification>
|
||||
struct InsertSpecification<Specification, SpecificationSetStorage<>> {
|
||||
using Type = SpecificationSetStorage<Specification>;
|
||||
};
|
||||
|
||||
template <ModelSpecification Specification, ModelSpecification Head, ModelSpecification... Tail>
|
||||
struct InsertSpecification<Specification, SpecificationSetStorage<Head, Tail...>> {
|
||||
private:
|
||||
using InsertedTail = typename InsertSpecification<Specification, SpecificationSetStorage<Tail...>>::Type;
|
||||
|
||||
template <typename First, typename Rest> struct PrependSpecification;
|
||||
|
||||
template <typename First, ModelSpecification... Rest>
|
||||
struct PrependSpecification<First, SpecificationSetStorage<Rest...>> {
|
||||
using Type = SpecificationSetStorage<First, Rest...>;
|
||||
};
|
||||
|
||||
public:
|
||||
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;
|
||||
|
||||
template <typename Set> struct CanonicalizeSpecifications<Set> {
|
||||
using Type = Set;
|
||||
};
|
||||
|
||||
template <typename Set, ModelSpecification Head, ModelSpecification... Tail>
|
||||
struct CanonicalizeSpecifications<Set, Head, Tail...> {
|
||||
using Inserted = typename InsertSpecification<Head, Set>::Type;
|
||||
using Type = typename CanonicalizeSpecifications<Inserted, Tail...>::Type;
|
||||
};
|
||||
|
||||
template <ModelSpecification... Specifications>
|
||||
using CanonicalSpecificationSet =
|
||||
typename CanonicalizeSpecifications<SpecificationSetStorage<>, Specifications...>::Type;
|
||||
|
||||
template <
|
||||
ModelSpecification Head,
|
||||
ModelSpecification... Tail>
|
||||
consteval bool specificationKeyIsUnique() {
|
||||
return ((SpecificationTraits<Head>::key != SpecificationTraits<Tail>::key) && ...);
|
||||
}
|
||||
|
||||
template <ModelSpecification... Specifications> struct SpecificationKeysAreUnique;
|
||||
|
||||
template <> struct SpecificationKeysAreUnique<> : std::true_type { };
|
||||
|
||||
template <ModelSpecification Head, ModelSpecification... Tail>
|
||||
struct SpecificationKeysAreUnique<Head, Tail...>
|
||||
: std::bool_constant<
|
||||
specificationKeyIsUnique<Head, Tail...>() && SpecificationKeysAreUnique<Tail...>::value> { };
|
||||
|
||||
template <SpecificationRole Role, ModelSpecification... Specifications>
|
||||
inline constexpr std::size_t specificationRoleCount =
|
||||
(std::size_t{0} + ... + (SpecificationTraits<Specifications>::role == Role ? 1 : 0));
|
||||
|
||||
template <typename List> struct ModelTypeListScalarArity;
|
||||
|
||||
template <typename... Types>
|
||||
struct ModelTypeListScalarArity<ModelTypeList<Types...>>
|
||||
: 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> || ...);
|
||||
|
||||
template <typename Query, typename... Types>
|
||||
inline constexpr std::size_t typeCount =
|
||||
(std::size_t{0} + ... +
|
||||
(std::same_as<Query, std::remove_cvref_t<Types>> ? std::size_t{1} : std::size_t{0}));
|
||||
|
||||
template <typename CanonicalSet, typename... Arguments> struct ArgumentsMatchCanonicalSpecifications;
|
||||
|
||||
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) && ...)> { };
|
||||
} // namespace detail
|
||||
|
||||
template <ModelSpecification... Specifications>
|
||||
inline constexpr bool specificationKeysAreUnique = detail::SpecificationKeysAreUnique<Specifications...>::value;
|
||||
|
||||
template <typename... Specifications>
|
||||
concept ValidModelSpecificationPack =
|
||||
(ResolvedModelSpecification<Specifications> && ...) && specificationKeysAreUnique<Specifications...> &&
|
||||
detail::specificationRoleCount<SpecificationRole::constitutive_law, Specifications...> == 1;
|
||||
|
||||
template <ModelSpecification... Specifications>
|
||||
requires specificationKeysAreUnique<Specifications...>
|
||||
using SpecificationSet = detail::CanonicalSpecificationSet<Specifications...>;
|
||||
|
||||
template <typename SpecificationSet> struct SpecificationOperatorSignature;
|
||||
|
||||
template <ModelSpecification... Specifications>
|
||||
struct SpecificationOperatorSignature<detail::SpecificationSetStorage<Specifications...>> final {
|
||||
using GeneratedValues = typename detail::ConcatenateModelTypeLists<
|
||||
typename SpecificationContribution<Specifications>::GeneratedValues...>::Type;
|
||||
|
||||
using GeneratedResiduals = typename detail::ConcatenateModelTypeLists<
|
||||
typename SpecificationContribution<Specifications>::GeneratedResiduals...>::Type;
|
||||
|
||||
static constexpr std::size_t generatedValueArity = detail::ModelTypeListScalarArity<GeneratedValues>::value;
|
||||
|
||||
static constexpr std::size_t generatedResidualArity =
|
||||
detail::ModelTypeListScalarArity<GeneratedResiduals>::value;
|
||||
|
||||
static constexpr bool symbolicallySquare = generatedValueArity == generatedResidualArity;
|
||||
};
|
||||
|
||||
template <ResolvedModelSpecification Specification>
|
||||
[[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
|
||||
};
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template <typename Specifications> class SpecifiedModel;
|
||||
|
||||
template <ModelSpecification... Specifications>
|
||||
class SpecifiedModel<SpecificationSetStorage<Specifications...>> final {
|
||||
public:
|
||||
using SpecificationTypes = SpecificationSetStorage<Specifications...>;
|
||||
using OperatorSignature = SpecificationOperatorSignature<SpecificationTypes>;
|
||||
|
||||
static constexpr bool symbolicallySquare = OperatorSignature::symbolicallySquare;
|
||||
static constexpr bool hasCompleteRootCompiler =
|
||||
(SpecificationContribution<Specifications>::hasRootCompiler && ...);
|
||||
static constexpr EquilibriumSystemCompilation compilationClass =
|
||||
symbolicallySquare && hasCompleteRootCompiler
|
||||
? EquilibriumSystemCompilation::complete_equilibrium_system
|
||||
: EquilibriumSystemCompilation::equation_contributions_only;
|
||||
|
||||
template <typename... Arguments>
|
||||
requires ArgumentsMatchCanonicalSpecifications<
|
||||
SpecificationTypes,
|
||||
Arguments...>::value
|
||||
explicit SpecifiedModel(Arguments &&...arguments)
|
||||
: m_specifications(
|
||||
std::get<Specifications>(
|
||||
std::tuple<std::remove_cvref_t<Arguments>...>{std::forward<Arguments>(arguments)...}
|
||||
)...
|
||||
) {
|
||||
}
|
||||
|
||||
template <ModelSpecification Specification>
|
||||
requires isOneOf<
|
||||
Specification,
|
||||
Specifications...>
|
||||
[[nodiscard]] const Specification &specification() const noexcept {
|
||||
return std::get<Specification>(m_specifications);
|
||||
}
|
||||
|
||||
template <ModelSpecification Specification>
|
||||
static constexpr bool containsSpecification = isOneOf<Specification, Specifications...>;
|
||||
|
||||
[[nodiscard]] static constexpr std::span<const RuntimeSpecificationDescriptor>
|
||||
runtimeSpecificationDescriptors() noexcept {
|
||||
return runtimeDescriptors;
|
||||
}
|
||||
|
||||
private:
|
||||
inline static constexpr std::array<RuntimeSpecificationDescriptor, sizeof...(Specifications)>
|
||||
runtimeDescriptors = [] {
|
||||
std::array<RuntimeSpecificationDescriptor, sizeof...(Specifications)> descriptors{};
|
||||
std::size_t index = 0;
|
||||
((descriptors[index] =
|
||||
{.specification = specificationDescriptor<Specifications>(),
|
||||
.canonicalIndex = index,
|
||||
.hasRootCompiler = SpecificationContribution<Specifications>::hasRootCompiler},
|
||||
++index),
|
||||
...);
|
||||
return descriptors;
|
||||
}();
|
||||
|
||||
std::tuple<Specifications...> m_specifications;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <ModelSpecification... Specifications>
|
||||
requires ValidModelSpecificationPack<Specifications...> &&
|
||||
SpecificationOperatorSignature<SpecificationSet<Specifications...>>::symbolicallySquare
|
||||
using Model = detail::SpecifiedModel<SpecificationSet<Specifications...>>;
|
||||
|
||||
template <typename Candidate>
|
||||
concept SpecifiedModelType = requires {
|
||||
typename std::remove_cvref_t<Candidate>::SpecificationTypes;
|
||||
typename std::remove_cvref_t<Candidate>::OperatorSignature;
|
||||
requires std::remove_cvref_t<Candidate>::symbolicallySquare;
|
||||
{ std::remove_cvref_t<Candidate>::compilationClass } -> std::convertible_to<ModelCompilationClass>;
|
||||
{
|
||||
std::remove_cvref_t<Candidate>::runtimeSpecificationDescriptors()
|
||||
} -> std::same_as<std::span<const RuntimeSpecificationDescriptor>>;
|
||||
};
|
||||
|
||||
static_assert(ModelSpecification<eos::Polytrope>);
|
||||
static_assert(ModelSpecification<surface::ConstantPressureSurface>);
|
||||
static_assert(ModelSpecification<FixedTotalMass>);
|
||||
static_assert(ModelSpecification<FixedCentralDensity>);
|
||||
static_assert(ResolvedModelSpecification<eos::Polytrope>);
|
||||
static_assert(ResolvedModelSpecification<surface::ConstantPressureSurface>);
|
||||
static_assert(ResolvedModelSpecification<FixedTotalMass>);
|
||||
static_assert(ResolvedModelSpecification<FixedCentralDensity>);
|
||||
} // namespace mean_field::models
|
||||
|
||||
export namespace mean_field::integral {
|
||||
using FixedTotalMass = models::FixedTotalMass;
|
||||
}
|
||||
|
||||
export namespace mean_field::constraint {
|
||||
using FixedCentralDensity = models::FixedCentralDensity;
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
module;
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:model.structure.polytropic;
|
||||
|
||||
export import :eos.polytrope;
|
||||
export import :model.structure.base;
|
||||
export import :seed.lane_emden;
|
||||
|
||||
import :utils.misc;
|
||||
|
||||
@@ -28,40 +27,6 @@ export namespace mean_field::models::structure {
|
||||
void validate() const;
|
||||
|
||||
private:
|
||||
struct LaneEmdenPoint {
|
||||
double coordinate{0.0};
|
||||
double value{0.0};
|
||||
double derivative{0.0};
|
||||
};
|
||||
|
||||
struct LaneEmdenDerivative {
|
||||
double value{0.0};
|
||||
double derivative{0.0};
|
||||
};
|
||||
|
||||
static void validateSeedRequest(const StructureSeedRequest &request);
|
||||
|
||||
[[nodiscard]] static LaneEmdenDerivative evaluateLaneEmdenRhs(
|
||||
double coordinate,
|
||||
double value,
|
||||
double derivative,
|
||||
double polytropicIndex
|
||||
);
|
||||
|
||||
[[nodiscard]] static LaneEmdenPoint takeLaneEmdenStep(
|
||||
const LaneEmdenPoint &point,
|
||||
double step,
|
||||
double polytropicIndex
|
||||
);
|
||||
|
||||
[[nodiscard]] static std::vector<LaneEmdenPoint> solveLaneEmden(double polytropicIndex);
|
||||
|
||||
[[nodiscard]] static double interpolateLaneEmdenValue(
|
||||
const std::vector<LaneEmdenPoint> &solution,
|
||||
double coordinate,
|
||||
std::size_t &lowerIndex
|
||||
);
|
||||
|
||||
eos::Polytrope m_equationOfState;
|
||||
double m_targetMass;
|
||||
};
|
||||
|
||||
66
libmeanfield/interface/models/typed_stellar_model.cppm
Normal file
66
libmeanfield/interface/models/typed_stellar_model.cppm
Normal file
@@ -0,0 +1,66 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <span>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
export module mean_field:model.typed_stellar;
|
||||
|
||||
export import :model.specifications;
|
||||
|
||||
export namespace mean_field::model {
|
||||
template <typename SpecificationSet> class StellarModel;
|
||||
|
||||
template <models::ModelSpecification... CanonicalSpecifications>
|
||||
class StellarModel<models::detail::SpecificationSetStorage<CanonicalSpecifications...>> final {
|
||||
public:
|
||||
using SpecificationTypes = models::detail::SpecificationSetStorage<CanonicalSpecifications...>;
|
||||
using OperatorSignature = models::SpecificationOperatorSignature<SpecificationTypes>;
|
||||
using Storage = models::Model<CanonicalSpecifications...>;
|
||||
|
||||
static constexpr std::size_t specificationCount = sizeof...(CanonicalSpecifications);
|
||||
static constexpr bool symbolicallySquare = Storage::symbolicallySquare;
|
||||
static constexpr bool hasCompleteEquilibriumCompiler = Storage::hasCompleteRootCompiler;
|
||||
static constexpr models::EquilibriumSystemCompilation compilationClass = Storage::compilationClass;
|
||||
|
||||
template <typename... Arguments>
|
||||
requires std::constructible_from<
|
||||
Storage,
|
||||
Arguments...>
|
||||
explicit StellarModel(Arguments &&...arguments) : m_specifications(std::forward<Arguments>(arguments)...) {
|
||||
}
|
||||
|
||||
template <models::ModelSpecification Specification>
|
||||
requires Storage::template
|
||||
containsSpecification<Specification> [[nodiscard]] const Specification &specification() const noexcept {
|
||||
return m_specifications.template specification<Specification>();
|
||||
}
|
||||
|
||||
template <models::ModelSpecification Specification>
|
||||
static constexpr bool containsSpecification = Storage::template containsSpecification<Specification>;
|
||||
|
||||
[[nodiscard]] static constexpr std::span<const models::RuntimeSpecificationDescriptor>
|
||||
runtimeSpecificationDescriptors() noexcept {
|
||||
return Storage::runtimeSpecificationDescriptors();
|
||||
}
|
||||
|
||||
private:
|
||||
Storage m_specifications;
|
||||
};
|
||||
|
||||
template <models::ResolvedModelSpecification... Specifications>
|
||||
requires models::ValidModelSpecificationPack<std::remove_cvref_t<Specifications>...>
|
||||
StellarModel(Specifications &&...)
|
||||
-> StellarModel<models::SpecificationSet<std::remove_cvref_t<Specifications>...>>;
|
||||
|
||||
namespace detail {
|
||||
template <typename Candidate> struct IsStellarModel : std::false_type { };
|
||||
|
||||
template <typename SpecificationSet> struct IsStellarModel<StellarModel<SpecificationSet>> : std::true_type { };
|
||||
} // namespace detail
|
||||
|
||||
template <typename Candidate>
|
||||
concept StellarModelType = detail::IsStellarModel<std::remove_cvref_t<Candidate>>::value;
|
||||
} // namespace mean_field::model
|
||||
@@ -84,15 +84,24 @@ export namespace mean_field::operators {
|
||||
mfem::Vector &actionTrue
|
||||
) const;
|
||||
|
||||
void ApplyDisplacementActionFull(
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &actionTrue
|
||||
) const;
|
||||
|
||||
struct ElementPAData {
|
||||
int elementId{-1};
|
||||
mfem::Array<int> densityDofs;
|
||||
mfem::Array<int> enthalpyDofs;
|
||||
mfem::Array<int> displacementDofs;
|
||||
|
||||
mfem::DofTransformation *densityDofTransformation{nullptr};
|
||||
mfem::DofTransformation *enthalpyDofTransformation{nullptr};
|
||||
mfem::DofTransformation *displacementDofTransformation{nullptr};
|
||||
|
||||
mfem::DenseMatrix densityBasis;
|
||||
mfem::DenseMatrix enthalpyBasis;
|
||||
mfem::DenseMatrix inverseElementJacobians;
|
||||
|
||||
mfem::Vector weightedResidual;
|
||||
mfem::Vector quadratureWeights;
|
||||
@@ -122,6 +131,14 @@ export namespace mean_field::operators {
|
||||
mutable mfem::Vector m_fullDisplacementAction;
|
||||
mutable mfem::Vector m_fullResidual;
|
||||
|
||||
mutable mfem::Vector m_displacementVariationLocal;
|
||||
mutable mfem::Vector m_localDisplacementAction;
|
||||
mutable mfem::Vector m_elementDisplacementVariation;
|
||||
mutable mfem::Vector m_quadratureDisplacementAction;
|
||||
mutable mfem::Vector m_elementDisplacementAction;
|
||||
mutable mfem::DenseMatrix m_referenceDShape;
|
||||
mutable mfem::DenseMatrix m_referenceDisplacementJacobian;
|
||||
|
||||
std::uint64_t m_preparationCount{0};
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
|
||||
264
libmeanfield/interface/operators/prepared_central_density.cppm
Normal file
264
libmeanfield/interface/operators/prepared_central_density.cppm
Normal file
@@ -0,0 +1,264 @@
|
||||
module;
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.prepared_central_density;
|
||||
|
||||
export import :field.mfem;
|
||||
export import :model.compiled_fixed_central_density;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
struct CentralDensityDependencyStamp final {
|
||||
std::uint64_t identity{0};
|
||||
std::uint64_t revision{0};
|
||||
|
||||
constexpr auto operator<=>(const CentralDensityDependencyStamp &) const = default;
|
||||
};
|
||||
|
||||
struct CentralDensityDependencies final {
|
||||
CentralDensityDependencyStamp enthalpy;
|
||||
|
||||
constexpr auto operator<=>(const CentralDensityDependencies &) const = default;
|
||||
};
|
||||
|
||||
struct PreparedCentralDensityReport final {
|
||||
bool refreshedCentralEnthalpy{false};
|
||||
bool refreshedBorder{false};
|
||||
bool assembledResidual{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return refreshedCentralEnthalpy || refreshedBorder || assembledResidual;
|
||||
}
|
||||
|
||||
constexpr auto operator<=>(const PreparedCentralDensityReport &) const = default;
|
||||
};
|
||||
|
||||
struct CentralDensityConstraintReport final {
|
||||
double targetDensity;
|
||||
double achievedDensity;
|
||||
double targetEnthalpy;
|
||||
double achievedEnthalpy;
|
||||
double enthalpyResidual;
|
||||
double scaledResidual;
|
||||
};
|
||||
|
||||
struct CentralDensityJacobianInput final {
|
||||
const mfem::Vector &enthalpyVariation;
|
||||
double borderVariation;
|
||||
};
|
||||
|
||||
struct CentralDensityJacobianOutput final {
|
||||
mfem::Vector &enthalpyAction;
|
||||
mfem::Vector &phaseAction;
|
||||
};
|
||||
|
||||
struct CentralDensityJacobianTransposeInput final {
|
||||
const mfem::Vector &enthalpyResidualDual;
|
||||
double phaseResidualDual;
|
||||
};
|
||||
|
||||
struct CentralDensityJacobianTransposeOutput final {
|
||||
mfem::Vector &enthalpyDual;
|
||||
mfem::Vector &borderDual;
|
||||
};
|
||||
|
||||
/*
|
||||
* Bordered central-density phase condition
|
||||
*
|
||||
* R_c(h) = h(0) - h(rho_c,target),
|
||||
* R_h <- R_h + lambda_c e_c.
|
||||
*
|
||||
* The point functional e_c selects the unique scalar H1 vertex at the
|
||||
* computational origin. Its coordinate transpose supplies the border
|
||||
* column, so this contribution is algebraically symmetric before any
|
||||
* independent scaling is applied by a solver.
|
||||
*/
|
||||
class PreparedCentralDensityConstraint final {
|
||||
public:
|
||||
PreparedCentralDensityConstraint(
|
||||
field::FieldPointDofMap centerDof,
|
||||
const MPI_Comm communicator
|
||||
)
|
||||
: m_centerDof(std::move(centerDof)),
|
||||
m_communicator(communicator) {
|
||||
}
|
||||
|
||||
PreparedCentralDensityReport Prepare(
|
||||
const models::CompiledFixedCentralDensity &constraint,
|
||||
const mfem::Vector &enthalpy,
|
||||
const double border,
|
||||
const CentralDensityDependencies &dependencies
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
enthalpy.Size() == m_centerDof.field_size(),
|
||||
"The central-density phase received an enthalpy vector with the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(std::isfinite(border), "The central-density phase received a non-finite border value.");
|
||||
|
||||
const bool wasPrepared = m_isPrepared;
|
||||
PreparedCentralDensityReport report;
|
||||
|
||||
if (!wasPrepared || dependencies.enthalpy != m_preparedDependencies.enthalpy) {
|
||||
double localCentralEnthalpy = 0.0;
|
||||
for (const int reducedDof : m_centerDof.reduced_dofs()) {
|
||||
const double value = enthalpy(reducedDof);
|
||||
MFEM_VERIFY(std::isfinite(value), "The central enthalpy is non-finite.");
|
||||
localCentralEnthalpy += value;
|
||||
}
|
||||
m_centralEnthalpy = GlobalSum(localCentralEnthalpy);
|
||||
report.refreshedCentralEnthalpy = true;
|
||||
}
|
||||
|
||||
if (!wasPrepared || border != m_border) {
|
||||
m_border = border;
|
||||
report.refreshedBorder = true;
|
||||
}
|
||||
|
||||
const bool targetChanged =
|
||||
!m_constraint.has_value() || constraint.targetDensity() != m_constraint->targetDensity();
|
||||
if (targetChanged) {
|
||||
m_constraint = constraint;
|
||||
}
|
||||
|
||||
if (report.refreshedCentralEnthalpy || report.refreshedBorder || targetChanged) {
|
||||
m_cachedPhaseResidual = m_centralEnthalpy - m_constraint->targetEnthalpy().value();
|
||||
report.assembledResidual = true;
|
||||
}
|
||||
|
||||
m_preparedDependencies = dependencies;
|
||||
m_isPrepared = true;
|
||||
++m_preparationCount;
|
||||
return report;
|
||||
}
|
||||
|
||||
void AddResidual(
|
||||
mfem::Vector &enthalpyResidual,
|
||||
mfem::Vector &phaseResidual
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
VerifyOutputSizes(enthalpyResidual, phaseResidual);
|
||||
for (const int reducedDof : m_centerDof.reduced_dofs()) {
|
||||
enthalpyResidual(reducedDof) += m_border;
|
||||
}
|
||||
phaseResidual(0) = m_cachedPhaseResidual;
|
||||
}
|
||||
|
||||
void ApplyJacobian(
|
||||
const CentralDensityJacobianInput &input,
|
||||
CentralDensityJacobianOutput output
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
MFEM_VERIFY(
|
||||
input.enthalpyVariation.Size() == m_centerDof.field_size(),
|
||||
"The central-density Jacobian received an enthalpy direction with the wrong size."
|
||||
);
|
||||
VerifyOutputSizes(output.enthalpyAction, output.phaseAction);
|
||||
|
||||
double localPhaseAction = 0.0;
|
||||
for (const int reducedDof : m_centerDof.reduced_dofs()) {
|
||||
output.enthalpyAction(reducedDof) += input.borderVariation;
|
||||
localPhaseAction += input.enthalpyVariation(reducedDof);
|
||||
}
|
||||
output.phaseAction(0) = GlobalSum(localPhaseAction);
|
||||
++m_jacobianApplicationCount;
|
||||
}
|
||||
|
||||
void ApplyJacobianTranspose(
|
||||
const CentralDensityJacobianTransposeInput &input,
|
||||
CentralDensityJacobianTransposeOutput output
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
MFEM_VERIFY(
|
||||
input.enthalpyResidualDual.Size() == m_centerDof.field_size(),
|
||||
"The central-density transpose received an enthalpy residual dual with the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
output.enthalpyDual.Size() == m_centerDof.field_size() && output.borderDual.Size() == 1,
|
||||
"The central-density transpose received output vectors with the wrong size."
|
||||
);
|
||||
|
||||
double localBorderDual = 0.0;
|
||||
for (const int reducedDof : m_centerDof.reduced_dofs()) {
|
||||
output.enthalpyDual(reducedDof) += input.phaseResidualDual;
|
||||
localBorderDual += input.enthalpyResidualDual(reducedDof);
|
||||
}
|
||||
output.borderDual(0) += GlobalSum(localBorderDual);
|
||||
++m_transposeApplicationCount;
|
||||
}
|
||||
|
||||
[[nodiscard]] CentralDensityConstraintReport GetConstraintReport() const {
|
||||
VerifyPrepared();
|
||||
const double targetEnthalpy = m_constraint->targetEnthalpy().value();
|
||||
const double scale = std::max(std::abs(targetEnthalpy), 1.0e-300);
|
||||
return {
|
||||
.targetDensity = m_constraint->targetDensity().value(),
|
||||
.achievedDensity =
|
||||
m_constraint->densityFromEnthalpy(dimensions::SpecificEnthalpyValue{m_centralEnthalpy}).value(),
|
||||
.targetEnthalpy = targetEnthalpy,
|
||||
.achievedEnthalpy = m_centralEnthalpy,
|
||||
.enthalpyResidual = m_cachedPhaseResidual,
|
||||
.scaledResidual = m_cachedPhaseResidual / scale
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept {
|
||||
return m_isPrepared;
|
||||
}
|
||||
|
||||
[[nodiscard]] const field::FieldPointDofMap &GetCenterDof() const noexcept {
|
||||
return m_centerDof;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint64_t GetPreparationCount() const noexcept {
|
||||
return m_preparationCount;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint64_t GetJacobianApplicationCount() const noexcept {
|
||||
return m_jacobianApplicationCount;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint64_t GetTransposeApplicationCount() const noexcept {
|
||||
return m_transposeApplicationCount;
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] double GlobalSum(const double localValue) const {
|
||||
double globalValue = 0.0;
|
||||
MPI_Allreduce(&localValue, &globalValue, 1, MPI_DOUBLE, MPI_SUM, m_communicator);
|
||||
return globalValue;
|
||||
}
|
||||
|
||||
void VerifyOutputSizes(
|
||||
const mfem::Vector &enthalpyOutput,
|
||||
const mfem::Vector &phaseOutput
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
enthalpyOutput.Size() == m_centerDof.field_size() && phaseOutput.Size() == 1,
|
||||
"The central-density phase received output vectors with the wrong size."
|
||||
);
|
||||
}
|
||||
|
||||
void VerifyPrepared() const {
|
||||
MFEM_VERIFY(m_isPrepared, "The central-density phase must be prepared before application.");
|
||||
}
|
||||
|
||||
field::FieldPointDofMap m_centerDof;
|
||||
MPI_Comm m_communicator;
|
||||
std::optional<models::CompiledFixedCentralDensity> m_constraint;
|
||||
CentralDensityDependencies m_preparedDependencies;
|
||||
double m_centralEnthalpy{0.0};
|
||||
double m_border{0.0};
|
||||
double m_cachedPhaseResidual{0.0};
|
||||
std::uint64_t m_preparationCount{0};
|
||||
mutable std::uint64_t m_jacobianApplicationCount{0};
|
||||
mutable std::uint64_t m_transposeApplicationCount{0};
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
@@ -0,0 +1,117 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.prepared_central_density_stellar_equilibrium;
|
||||
|
||||
export import :model.compiled_fixed_central_density;
|
||||
export import :operators.prepared_central_density;
|
||||
export import :operators.prepared_stellar_equilibrium;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
using CentralDensityStellarEquilibriumSpecificationModel = model::StellarModel<
|
||||
models::
|
||||
SpecificationSet<eos::Polytrope, models::FixedTotalMass, surface::Isobaric, models::FixedCentralDensity>>;
|
||||
|
||||
using CentralDensityStellarEquilibriumForm = utils::blocks::central_density_bordered_stellar_equilibrium_form;
|
||||
using CentralDensityStellarEquilibriumJacobianForm =
|
||||
utils::blocks::central_density_bordered_stellar_equilibrium_jacobian_form;
|
||||
using CentralDensityStellarEquilibriumLayout = utils::blocks::form_layout<CentralDensityStellarEquilibriumForm>;
|
||||
using CentralDensityStellarEquilibriumSystemManifest = EquilibriumSystemManifest<
|
||||
CentralDensityStellarEquilibriumSpecificationModel,
|
||||
CentralDensityStellarEquilibriumForm,
|
||||
CentralDensityStellarEquilibriumJacobianForm>;
|
||||
|
||||
using CentralDensityStellarEquilibriumRootManifest = CentralDensityStellarEquilibriumSystemManifest;
|
||||
|
||||
struct PreparedCentralDensityStellarEquilibriumReport final {
|
||||
PreparedStellarEquilibriumReport physical;
|
||||
PreparedCentralDensityReport phase;
|
||||
bool assembledResidual{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return physical.DidAnyWork() || phase.DidAnyWork() || assembledResidual;
|
||||
}
|
||||
};
|
||||
|
||||
class PreparedCentralDensityStellarEquilibriumOperator final : public mfem::Operator {
|
||||
public:
|
||||
PreparedCentralDensityStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
models::CompiledFixedMass fixedMassConstraint,
|
||||
PressureSurfaceConstraintView surfaceConstraint,
|
||||
deformation::PreparedDomainDeformationRuntime domainDeformation,
|
||||
models::CompiledFixedCentralDensity centralDensity
|
||||
)
|
||||
: PreparedCentralDensityStellarEquilibriumOperator(
|
||||
f,
|
||||
std::make_unique<PreparedStellarEquilibriumOperator>(
|
||||
f,
|
||||
domainMapper,
|
||||
equationOfState,
|
||||
std::move(fixedMassConstraint),
|
||||
surfaceConstraint,
|
||||
std::move(domainDeformation)
|
||||
),
|
||||
std::move(centralDensity),
|
||||
MakeCenterDofMap(f)
|
||||
) {
|
||||
}
|
||||
|
||||
PreparedCentralDensityStellarEquilibriumOperator(const PreparedCentralDensityStellarEquilibriumOperator &) =
|
||||
delete;
|
||||
PreparedCentralDensityStellarEquilibriumOperator &
|
||||
operator=(const PreparedCentralDensityStellarEquilibriumOperator &) = delete;
|
||||
PreparedCentralDensityStellarEquilibriumOperator(PreparedCentralDensityStellarEquilibriumOperator &&) = delete;
|
||||
PreparedCentralDensityStellarEquilibriumOperator &
|
||||
operator=(PreparedCentralDensityStellarEquilibriumOperator &&) = delete;
|
||||
|
||||
PreparedCentralDensityStellarEquilibriumReport Prepare(
|
||||
const mfem::Vector &state,
|
||||
const StellarEquilibriumDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
);
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void Mult(
|
||||
const mfem::Vector &direction,
|
||||
mfem::Vector &action
|
||||
) const override;
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
[[nodiscard]] const CentralDensityStellarEquilibriumLayout &GetLayout() const noexcept;
|
||||
[[nodiscard]] const CentralDensityStellarEquilibriumRootManifest &GetRootManifest() const noexcept;
|
||||
[[nodiscard]] const PreparedStellarEquilibriumOperator &GetPhysicalOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedCentralDensityConstraint &GetCentralDensityConstraint() const noexcept;
|
||||
[[nodiscard]] RootConstraintReport GetFixedMassReport() const;
|
||||
[[nodiscard]] CentralDensityConstraintReport GetCentralDensityReport() const;
|
||||
|
||||
private:
|
||||
static field::FieldPointDofMap MakeCenterDofMap(const fem::FEM &f);
|
||||
|
||||
PreparedCentralDensityStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
std::unique_ptr<PreparedStellarEquilibriumOperator> physicalOperator,
|
||||
models::CompiledFixedCentralDensity centralDensity,
|
||||
field::FieldPointDofMap centerDof
|
||||
);
|
||||
|
||||
void AssembleResidual();
|
||||
void VerifyPrepared() const;
|
||||
|
||||
std::unique_ptr<PreparedStellarEquilibriumOperator> m_physicalOperator;
|
||||
models::CompiledFixedCentralDensity m_centralDensity;
|
||||
PreparedCentralDensityConstraint m_phaseConstraint;
|
||||
CentralDensityStellarEquilibriumRootManifest m_rootManifest;
|
||||
mfem::Vector m_cachedResidual;
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
37
libmeanfield/interface/operators/prepared_constraint.cppm
Normal file
37
libmeanfield/interface/operators/prepared_constraint.cppm
Normal file
@@ -0,0 +1,37 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.prepared_constraint;
|
||||
|
||||
export import :model.compiled_fixed_mass;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
template <typename Candidate>
|
||||
concept PreparedConstraint = requires(
|
||||
std::remove_cvref_t<Candidate> &prepared,
|
||||
const std::remove_cvref_t<Candidate> &constPrepared,
|
||||
const typename std::remove_cvref_t<Candidate>::CompiledConstraintType &constraint,
|
||||
const typename std::remove_cvref_t<Candidate>::Dependencies &dependencies,
|
||||
const typename std::remove_cvref_t<Candidate>::JacobianInput &jacobianInput,
|
||||
typename std::remove_cvref_t<Candidate>::JacobianTransposeOutput transposeOutput,
|
||||
const mfem::Vector &residualDual,
|
||||
mfem::Vector &result
|
||||
) {
|
||||
typename std::remove_cvref_t<Candidate>::SpecificationType;
|
||||
typename std::remove_cvref_t<Candidate>::CompiledConstraintType;
|
||||
typename std::remove_cvref_t<Candidate>::Dependencies;
|
||||
typename std::remove_cvref_t<Candidate>::Report;
|
||||
typename std::remove_cvref_t<Candidate>::JacobianInput;
|
||||
typename std::remove_cvref_t<Candidate>::JacobianTransposeOutput;
|
||||
requires models::CompiledConstraint<typename std::remove_cvref_t<Candidate>::CompiledConstraintType>;
|
||||
{ prepared.Prepare(constraint, dependencies) } -> std::same_as<typename std::remove_cvref_t<Candidate>::Report>;
|
||||
{ constPrepared.BuildResidual(result) } -> std::same_as<void>;
|
||||
{ constPrepared.ApplyJacobian(jacobianInput, result) } -> std::same_as<void>;
|
||||
{ constPrepared.ApplyJacobianTranspose(residualDual, transposeOutput) } -> std::same_as<void>;
|
||||
{ constPrepared.IsPrepared() } noexcept -> std::same_as<bool>;
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
@@ -2,6 +2,7 @@ module;
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
@@ -105,6 +106,29 @@ export namespace mean_field::operators {
|
||||
|
||||
private:
|
||||
void VerifyPrepared() const;
|
||||
void PrepareElementData();
|
||||
void ApplyPreparedCompleteJacobianActionTrue(
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
const mfem::Vector &gravityGradientVariationTrue,
|
||||
mfem::Vector &actionTrue
|
||||
) const;
|
||||
|
||||
struct ElementPAData {
|
||||
int elementId{-1};
|
||||
mfem::Array<int> densityDofs;
|
||||
mfem::Array<int> gravityGradientDofs;
|
||||
mfem::Array<int> displacementDofs;
|
||||
mfem::DofTransformation *densityDofTransformation{nullptr};
|
||||
mfem::DofTransformation *gravityGradientDofTransformation{nullptr};
|
||||
mfem::DofTransformation *displacementDofTransformation{nullptr};
|
||||
const mfem::IntegrationRule *integrationRule{nullptr};
|
||||
mfem::DenseMatrix mappingJacobians;
|
||||
mfem::DenseMatrix inverseMeshJacobians;
|
||||
mfem::DenseMatrix baseGravityReferenceValues;
|
||||
mfem::Vector baseDensityValues;
|
||||
mfem::Vector referenceWeights;
|
||||
};
|
||||
|
||||
const fem::FEM &m_fem;
|
||||
const mapping::DomainMapper &m_domainMapper;
|
||||
@@ -112,11 +136,34 @@ export namespace mean_field::operators {
|
||||
|
||||
context::gravity_field::GravityFieldRevisions m_preparedRevisions;
|
||||
mfem::Vector m_cachedResidual;
|
||||
std::vector<ElementPAData> m_elements;
|
||||
|
||||
mutable mfem::Vector m_densityVariationTrue;
|
||||
mutable mfem::Vector m_gravityGradientVariationTrue;
|
||||
mutable mfem::Vector m_displacementVariationTrue;
|
||||
mutable mfem::Vector m_actionTrue;
|
||||
mutable mfem::Vector m_densityVariationLocal;
|
||||
mutable mfem::Vector m_gravityGradientVariationLocal;
|
||||
mutable mfem::Vector m_displacementVariationLocal;
|
||||
mutable mfem::Vector m_localAction;
|
||||
mutable mfem::Vector m_elementDensityVariation;
|
||||
mutable mfem::Vector m_elementGravityGradientVariation;
|
||||
mutable mfem::Vector m_elementDisplacementVariation;
|
||||
mutable mfem::Vector m_elementAction;
|
||||
mutable mfem::Vector m_densityShape;
|
||||
mutable mfem::Vector m_displacementShape;
|
||||
mutable mfem::Vector m_baseGravityReferenceValue;
|
||||
mutable mfem::Vector m_gravityVariationReferenceValue;
|
||||
mutable mfem::Vector m_mappedBaseGravity;
|
||||
mutable mfem::Vector m_mappedGravityVariation;
|
||||
mutable mfem::Vector m_mappedGeometryVariation;
|
||||
mutable mfem::Vector m_forceValue;
|
||||
mutable mfem::DenseMatrix m_gravityGradientShape;
|
||||
mutable mfem::DenseMatrix m_referenceDisplacementDShape;
|
||||
mutable mfem::DenseMatrix m_referenceDisplacementJacobian;
|
||||
mutable mfem::DenseMatrix m_displacementJacobianVariation;
|
||||
mutable mfem::DenseMatrix m_mappingJacobian;
|
||||
mutable mfem::DenseMatrix m_inverseMeshJacobian;
|
||||
|
||||
std::uint64_t m_residualPreparationCount{0};
|
||||
mutable std::uint64_t m_residualApplicationCount{0};
|
||||
|
||||
@@ -22,6 +22,11 @@ export namespace mean_field::operators {
|
||||
const mfem::Vector &density,
|
||||
mfem::Vector &action
|
||||
) const override;
|
||||
void MultDisplacementVariationTrue(
|
||||
const mfem::Vector &densityTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &actionVariationTrue
|
||||
) const;
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
[[nodiscard]] std::uint64_t GetPreparationCount() const noexcept;
|
||||
@@ -41,13 +46,18 @@ export namespace mean_field::operators {
|
||||
|
||||
mfem::Array<int> density_dofs;
|
||||
mfem::Array<int> potential_dofs;
|
||||
mfem::Array<int> displacement_dofs;
|
||||
|
||||
mfem::DofTransformation *density_dof_transformation{nullptr};
|
||||
mfem::DofTransformation *potential_dof_transformation{nullptr};
|
||||
mfem::DofTransformation *displacement_dof_transformation{nullptr};
|
||||
|
||||
const mfem::IntegrationRule *integration_rule{nullptr};
|
||||
|
||||
// Rows are quadrature points; columns are element DOFs.
|
||||
mfem::DenseMatrix density_basis;
|
||||
mfem::DenseMatrix potential_basis;
|
||||
mfem::DenseMatrix inverse_element_jacobians;
|
||||
|
||||
// Contains quadrature weight, mesh Jacobian, mapped Jacobian,
|
||||
// and 4*pi*G.
|
||||
@@ -67,6 +77,15 @@ export namespace mean_field::operators {
|
||||
mutable mfem::Vector m_density_true;
|
||||
mutable mfem::Vector m_potential_true;
|
||||
mutable mfem::Vector m_action_true;
|
||||
mutable mfem::Vector m_density_local;
|
||||
mutable mfem::Vector m_displacement_variation_local;
|
||||
mutable mfem::Vector m_local_variation_action;
|
||||
mutable mfem::Vector m_element_density;
|
||||
mutable mfem::Vector m_element_displacement_variation;
|
||||
mutable mfem::Vector m_quadrature_variation_action;
|
||||
mutable mfem::Vector m_element_variation_action;
|
||||
mutable mfem::DenseMatrix m_reference_displacement_dshape;
|
||||
mutable mfem::DenseMatrix m_reference_displacement_jacobian;
|
||||
mfem::Vector m_displacement_true;
|
||||
|
||||
std::uint64_t m_preparation_count{0};
|
||||
|
||||
@@ -2,6 +2,7 @@ module;
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mfem.hpp>
|
||||
#include <vector>
|
||||
|
||||
export module mean_field:operators.prepared_hdiv_mass;
|
||||
export import :fem;
|
||||
@@ -21,6 +22,11 @@ export namespace mean_field::operators {
|
||||
const mfem::Vector &gravity_gradient,
|
||||
mfem::Vector &action
|
||||
) const override;
|
||||
void MultDisplacementVariationTrue(
|
||||
const mfem::Vector &gravityGradientTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &actionVariationTrue
|
||||
) const;
|
||||
void AssembleDiagonal(mfem::Vector &diagonal) const override;
|
||||
void AssembleTrueDiagonal(mfem::Vector &diagonal) const;
|
||||
|
||||
@@ -31,6 +37,21 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] const field::FieldDofMap &GetDisplacementMap() const noexcept;
|
||||
|
||||
private:
|
||||
struct ElementVariationData {
|
||||
int elementId{-1};
|
||||
mfem::Array<int> gravityGradientDofs;
|
||||
mfem::Array<int> displacementDofs;
|
||||
mfem::Array<int> compactificationDofs;
|
||||
mfem::DofTransformation *gravityGradientDofTransformation{nullptr};
|
||||
mfem::DofTransformation *displacementDofTransformation{nullptr};
|
||||
mfem::Vector baseDisplacement;
|
||||
mfem::Vector compactification;
|
||||
const mfem::IntegrationRule *integrationRule{nullptr};
|
||||
mfem::DenseMatrix frozenMappingData;
|
||||
};
|
||||
|
||||
void PrepareVariationData();
|
||||
|
||||
const fem::FEM &m_fem;
|
||||
const mapping::DomainMapper &m_domain_mapper;
|
||||
|
||||
@@ -48,6 +69,21 @@ export namespace mean_field::operators {
|
||||
mutable mfem::Vector m_action_true;
|
||||
mutable mfem::Vector m_domain_action_true;
|
||||
mfem::Vector m_displacement_true;
|
||||
std::vector<ElementVariationData> m_variationElements;
|
||||
|
||||
mutable mapping::DomainMapper::Workspace m_variationWorkspace;
|
||||
mutable mapping::VolumeMappingContext m_baseMappingContext;
|
||||
mutable mapping::VolumeMappingVariation m_mappingVariation;
|
||||
mutable mfem::Vector m_gravityGradientLocal;
|
||||
mutable mfem::Vector m_displacementVariationLocal;
|
||||
mutable mfem::Vector m_localVariationAction;
|
||||
mutable mfem::Vector m_elementGravityGradient;
|
||||
mutable mfem::Vector m_elementDisplacementVariation;
|
||||
mutable mfem::Vector m_elementVariationAction;
|
||||
mutable mfem::Vector m_gravityGradientValue;
|
||||
mutable mfem::Vector m_massTensorVariationAction;
|
||||
mutable mfem::DenseMatrix m_gravityGradientShape;
|
||||
mutable mfem::DenseMatrix m_massTensorVariation;
|
||||
std::uint64_t m_preparation_count{0};
|
||||
bool m_is_prepared{false};
|
||||
};
|
||||
|
||||
@@ -9,7 +9,9 @@ export module mean_field:operators.prepared_mass_normalization;
|
||||
|
||||
export import :fem;
|
||||
export import :mapping.domain_mapper;
|
||||
export import :model.compiled_fixed_mass;
|
||||
export import :operators.context.gravity_field;
|
||||
export import :operators.prepared_constraint;
|
||||
export import :utils.blocks;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
@@ -33,6 +35,16 @@ export namespace mean_field::operators {
|
||||
double targetMass{0.0};
|
||||
};
|
||||
|
||||
struct FixedMassJacobianInput final {
|
||||
const mfem::Vector &densityVariation;
|
||||
const mfem::Vector &displacementVariation;
|
||||
};
|
||||
|
||||
struct FixedMassJacobianTransposeOutput final {
|
||||
mfem::Vector &densityDual;
|
||||
mfem::Vector &displacementDual;
|
||||
};
|
||||
|
||||
struct PreparedMassNormalizationReport final {
|
||||
bool rebuiltStaticPlan{false};
|
||||
bool refreshedGeometry{false};
|
||||
@@ -43,12 +55,15 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return rebuiltStaticPlan || refreshedGeometry || refreshedDensity || updatedTargetMass || assembledResidual;
|
||||
}
|
||||
|
||||
constexpr auto operator<=>(const PreparedMassNormalizationReport &) const = default;
|
||||
};
|
||||
|
||||
struct PreparedMassNormalizationActionStatistics final {
|
||||
std::uint64_t densityApplications{0};
|
||||
std::uint64_t displacementApplications{0};
|
||||
std::uint64_t completeApplications{0};
|
||||
std::uint64_t transposeApplications{0};
|
||||
|
||||
constexpr auto operator<=>(const PreparedMassNormalizationActionStatistics &) const = default;
|
||||
};
|
||||
@@ -66,6 +81,13 @@ export namespace mean_field::operators {
|
||||
*/
|
||||
class PreparedMassNormalizationOperator final {
|
||||
public:
|
||||
using SpecificationType = models::FixedTotalMass;
|
||||
using CompiledConstraintType = models::CompiledFixedMass;
|
||||
using Dependencies = MassNormalizationDependencies;
|
||||
using Report = PreparedMassNormalizationReport;
|
||||
using JacobianInput = FixedMassJacobianInput;
|
||||
using JacobianTransposeOutput = FixedMassJacobianTransposeOutput;
|
||||
|
||||
PreparedMassNormalizationOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
@@ -82,6 +104,11 @@ export namespace mean_field::operators {
|
||||
const MassNormalizationDependencies &dependencies
|
||||
);
|
||||
|
||||
PreparedMassNormalizationReport Prepare(
|
||||
const models::CompiledFixedMass &constraint,
|
||||
const MassNormalizationDependencies &dependencies
|
||||
);
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void ApplyDensityJacobianAction(
|
||||
@@ -100,6 +127,22 @@ export namespace mean_field::operators {
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyJacobian(
|
||||
const FixedMassJacobianInput &input,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyCompleteJacobianTransposeAction(
|
||||
double residualDual,
|
||||
mfem::Vector &densityDual,
|
||||
mfem::Vector &displacementDual
|
||||
) const;
|
||||
|
||||
void ApplyJacobianTranspose(
|
||||
const mfem::Vector &residualDual,
|
||||
FixedMassJacobianTransposeOutput output
|
||||
) const;
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
[[nodiscard]] double GetCurrentMass() const;
|
||||
[[nodiscard]] double GetTargetMass() const;
|
||||
@@ -145,6 +188,16 @@ export namespace mean_field::operators {
|
||||
|
||||
[[nodiscard]] double EvaluateDisplacementActionLocal(const mfem::Vector &displacementVariation) const;
|
||||
|
||||
void AssembleDensityTransposeAction(
|
||||
double residualDual,
|
||||
mfem::Vector &densityDual
|
||||
) const;
|
||||
|
||||
void AssembleDisplacementTransposeAction(
|
||||
double residualDual,
|
||||
mfem::Vector &displacementDual
|
||||
) const;
|
||||
|
||||
[[nodiscard]] double GlobalSum(double localValue) const;
|
||||
|
||||
const fem::FEM &m_fem;
|
||||
@@ -167,6 +220,10 @@ export namespace mean_field::operators {
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
|
||||
using PreparedFixedMass = PreparedMassNormalizationOperator;
|
||||
|
||||
static_assert(PreparedConstraint<PreparedFixedMass>);
|
||||
|
||||
using MassNormalizationLayout = utils::blocks::form_layout<utils::blocks::barotropic_equilibrium_form>;
|
||||
|
||||
class PreparedMassNormalizationJacobianOperator final : public mfem::Operator {
|
||||
@@ -181,6 +238,11 @@ export namespace mean_field::operators {
|
||||
mfem::Vector &action
|
||||
) const override;
|
||||
|
||||
void MultTranspose(
|
||||
const mfem::Vector &residualDual,
|
||||
mfem::Vector &stateDual
|
||||
) const override;
|
||||
|
||||
[[nodiscard]] const MassNormalizationLayout &GetLayout() const noexcept;
|
||||
|
||||
private:
|
||||
|
||||
@@ -3,6 +3,7 @@ module;
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
@@ -103,6 +104,25 @@ export namespace mean_field::operators {
|
||||
|
||||
private:
|
||||
void VerifyPrepared() const;
|
||||
void PrepareElementData();
|
||||
void ApplyPreparedCompleteJacobianActionTrue(
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &actionTrue
|
||||
) const;
|
||||
|
||||
struct ElementPAData {
|
||||
int elementId{-1};
|
||||
mfem::Array<int> densityDofs;
|
||||
mfem::Array<int> displacementDofs;
|
||||
mfem::DofTransformation *densityDofTransformation{nullptr};
|
||||
mfem::DofTransformation *displacementDofTransformation{nullptr};
|
||||
const mfem::IntegrationRule *integrationRule{nullptr};
|
||||
mfem::DenseMatrix inverseElementJacobians;
|
||||
mfem::DenseMatrix centrifugalAccelerations;
|
||||
mfem::Vector baseDensityValues;
|
||||
mfem::Vector quadratureWeights;
|
||||
};
|
||||
|
||||
const fem::FEM &m_fem;
|
||||
const mapping::DomainMapper &m_domainMapper;
|
||||
@@ -111,9 +131,24 @@ export namespace mean_field::operators {
|
||||
|
||||
std::optional<physics::RigidRotation> m_rotation;
|
||||
mfem::Vector m_cachedResidual;
|
||||
std::vector<ElementPAData> m_elements;
|
||||
mutable mfem::Vector m_densityVariationTrue;
|
||||
mutable mfem::Vector m_displacementVariationTrue;
|
||||
mutable mfem::Vector m_actionTrue;
|
||||
mutable mfem::Vector m_densityVariationLocal;
|
||||
mutable mfem::Vector m_displacementVariationLocal;
|
||||
mutable mfem::Vector m_localAction;
|
||||
mutable mfem::Vector m_elementDensityVariation;
|
||||
mutable mfem::Vector m_elementDisplacementVariation;
|
||||
mutable mfem::Vector m_elementAction;
|
||||
mutable mfem::Vector m_densityShape;
|
||||
mutable mfem::Vector m_displacementShape;
|
||||
mutable mfem::Vector m_physicalPositionVariation;
|
||||
mutable mfem::Vector m_centrifugalAcceleration;
|
||||
mutable mfem::Vector m_centrifugalAccelerationVariation;
|
||||
mutable mfem::Vector m_weightedForce;
|
||||
mutable mfem::DenseMatrix m_referenceDisplacementDShape;
|
||||
mutable mfem::DenseMatrix m_referenceDisplacementJacobian;
|
||||
|
||||
context::rotational_displacement_force::RotationalDisplacementForceDependencies m_preparedDependencies;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ export import :fem;
|
||||
export import :field.mfem;
|
||||
export import :mapping.domain_mapper;
|
||||
export import :model.stellar;
|
||||
export import :model.typed_stellar;
|
||||
export import :operators.context.gravity_field;
|
||||
export import :operators.gravity_field;
|
||||
export import :operators.gravity_field_jacobian;
|
||||
@@ -23,6 +24,7 @@ export import :operators.prepared_displacement_residual;
|
||||
export import :operators.prepared_hydrostatic_equilibrium;
|
||||
export import :operators.prepared_mass_normalization;
|
||||
export import :operators.prepared_surface_constraint;
|
||||
export import :operators.root_manifest;
|
||||
export import :physics.rigid_rotation;
|
||||
export import :utils.blocks;
|
||||
|
||||
@@ -82,6 +84,16 @@ export namespace mean_field::operators {
|
||||
using StellarEquilibriumLayout =
|
||||
utils::blocks::form_layout<utils::blocks::surface_deformed_stellar_equilibrium_form>;
|
||||
|
||||
using StellarEquilibriumSpecificationModel =
|
||||
model::StellarModel<models::SpecificationSet<eos::Polytrope, models::FixedTotalMass, surface::Isobaric>>;
|
||||
|
||||
using StellarEquilibriumSystemManifest = EquilibriumSystemManifest<
|
||||
StellarEquilibriumSpecificationModel,
|
||||
utils::blocks::surface_deformed_stellar_equilibrium_form,
|
||||
utils::blocks::surface_deformed_stellar_equilibrium_jacobian_form>;
|
||||
|
||||
using StellarEquilibriumRootManifest = StellarEquilibriumSystemManifest;
|
||||
|
||||
class PreparedStellarEquilibriumOperator final : public mfem::Operator {
|
||||
public:
|
||||
template <models::StellarModelType Model>
|
||||
@@ -101,12 +113,27 @@ export namespace mean_field::operators {
|
||||
f,
|
||||
domainMapper,
|
||||
stellarModel.equationOfState(),
|
||||
stellarModel.targetMass(),
|
||||
models::compileConstraint(models::FixedTotalMass{dimensions::MassValue{stellarModel.targetMass()}}),
|
||||
PressureSurfaceConstraintView{stellarModel.compiledSurfaceConstraint()},
|
||||
deformation::PreparedDomainDeformationRuntime{stellarModel.compileDomainDeformation(f)}
|
||||
) {
|
||||
}
|
||||
|
||||
/*
|
||||
* Authoritative construction path for a compiled equilibrium system.
|
||||
* The caller owns the EOS and compiled surface constraint for this
|
||||
* operator's lifetime; the remaining compiled contributions are
|
||||
* transferred into the operator.
|
||||
*/
|
||||
PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
models::CompiledFixedMass fixedMassConstraint,
|
||||
PressureSurfaceConstraintView surfaceConstraint,
|
||||
deformation::PreparedDomainDeformationRuntime domainDeformation
|
||||
);
|
||||
|
||||
PreparedStellarEquilibriumOperator(const PreparedStellarEquilibriumOperator &) = delete;
|
||||
PreparedStellarEquilibriumOperator &operator=(const PreparedStellarEquilibriumOperator &) = delete;
|
||||
PreparedStellarEquilibriumOperator(PreparedStellarEquilibriumOperator &&) = delete;
|
||||
@@ -128,6 +155,12 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
[[nodiscard]] double GetTargetMass() const noexcept;
|
||||
[[nodiscard]] const StellarEquilibriumLayout &GetLayout() const noexcept;
|
||||
[[nodiscard]] const StellarEquilibriumRootManifest &GetRootManifest() const noexcept;
|
||||
[[nodiscard]] RootStateView<utils::blocks::surface_deformed_stellar_equilibrium_form>
|
||||
GetRootStateView(const mfem::Vector &state) const;
|
||||
[[nodiscard]] ResidualView<utils::blocks::surface_deformed_stellar_equilibrium_form>
|
||||
GetResidualView(mfem::Vector &residual) const;
|
||||
[[nodiscard]] RootConstraintReport GetFixedMassReport() const;
|
||||
[[nodiscard]] const StellarEquilibriumDependencies &GetDependencies() const;
|
||||
[[nodiscard]] const PreparedStellarEquilibriumStatistics &GetStatistics() const noexcept;
|
||||
|
||||
@@ -160,16 +193,7 @@ export namespace mean_field::operators {
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
double targetMass,
|
||||
PressureSurfaceConstraintView surfaceConstraint,
|
||||
deformation::PreparedDomainDeformationRuntime domainDeformation
|
||||
);
|
||||
|
||||
PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
double targetMass,
|
||||
models::CompiledFixedMass fixedMassConstraint,
|
||||
PressureSurfaceConstraintView surfaceConstraint,
|
||||
ConstructionData constructionData
|
||||
);
|
||||
@@ -177,7 +201,7 @@ export namespace mean_field::operators {
|
||||
void AssembleResidual();
|
||||
void VerifyPrepared() const;
|
||||
|
||||
StellarEquilibriumLayout m_layout;
|
||||
StellarEquilibriumRootManifest m_rootManifest;
|
||||
mfem::Array<int> m_gravityStateOffsets;
|
||||
|
||||
context::gravity_field::GravityFieldLinearizationContext m_gravityContext;
|
||||
@@ -198,7 +222,7 @@ export namespace mean_field::operators {
|
||||
mfem::Vector m_generatedVolumeDisplacement;
|
||||
mfem::Vector m_fullMechanicalResidual;
|
||||
mfem::Vector m_cachedResidual;
|
||||
double m_targetMass{0.0};
|
||||
models::CompiledFixedMass m_fixedMassConstraint;
|
||||
|
||||
mutable PreparedStellarEquilibriumStatistics m_statistics;
|
||||
bool m_isPrepared{false};
|
||||
|
||||
@@ -14,10 +14,10 @@ export import :field.mfem;
|
||||
export import :surface.compiled;
|
||||
|
||||
namespace mean_field::operators::detail {
|
||||
template <eos::ThermodynamicQuantityType Quantity> struct SingleQuantitySurfaceState final {
|
||||
eos::QuantityValue<Quantity> quantityValue;
|
||||
template <dimensions::ThermodynamicQuantityType Quantity> struct SingleQuantitySurfaceState final {
|
||||
dimensions::QuantityValue<Quantity> quantityValue;
|
||||
|
||||
[[nodiscard]] eos::QuantityValue<Quantity> value(Quantity) const noexcept {
|
||||
[[nodiscard]] dimensions::QuantityValue<Quantity> value(Quantity) const noexcept {
|
||||
return quantityValue;
|
||||
}
|
||||
};
|
||||
@@ -37,7 +37,7 @@ export namespace mean_field::operators {
|
||||
typename std::remove_cvref_t<Candidate>::CarrierQuantity;
|
||||
typename std::remove_cvref_t<Candidate>::CarrierField;
|
||||
typename std::remove_cvref_t<Candidate>::SurfaceDependencies;
|
||||
} && std::same_as<typename std::remove_cvref_t<Candidate>::PhysicalQuantity, eos::quantity::Pressure> &&
|
||||
} && std::same_as<typename std::remove_cvref_t<Candidate>::PhysicalQuantity, dimensions::quantity::Pressure> &&
|
||||
std::same_as<
|
||||
typename std::remove_cvref_t<Candidate>::SurfaceDependencies::RowField,
|
||||
typename std::remove_cvref_t<Candidate>::CarrierField> &&
|
||||
@@ -112,7 +112,7 @@ export namespace mean_field::operators {
|
||||
|
||||
for (int surfaceIndex = 0; surfaceIndex < surfaceRows.size(); ++surfaceIndex) {
|
||||
const detail::SingleQuantitySurfaceState<CarrierQuantity> state{
|
||||
eos::QuantityValue<CarrierQuantity>{surfaceState(surfaceIndex)}
|
||||
dimensions::QuantityValue<CarrierQuantity>{surfaceState(surfaceIndex)}
|
||||
};
|
||||
rowResidual(surfaceRows.reduced_dofs()[surfaceIndex]) =
|
||||
static_cast<const Constraint *>(constraint)->residual(state);
|
||||
@@ -132,10 +132,10 @@ export namespace mean_field::operators {
|
||||
for (int surfaceIndex = 0; surfaceIndex < surfaceRows.size(); ++surfaceIndex) {
|
||||
const int reducedDof = surfaceRows.reduced_dofs()[surfaceIndex];
|
||||
const detail::SingleQuantitySurfaceState<CarrierQuantity> state{
|
||||
eos::QuantityValue<CarrierQuantity>{surfaceState(surfaceIndex)}
|
||||
dimensions::QuantityValue<CarrierQuantity>{surfaceState(surfaceIndex)}
|
||||
};
|
||||
const detail::SingleQuantitySurfaceState<CarrierQuantity> variation{
|
||||
eos::QuantityValue<CarrierQuantity>{stateVariation(reducedDof)}
|
||||
dimensions::QuantityValue<CarrierQuantity>{stateVariation(reducedDof)}
|
||||
};
|
||||
rowAction(reducedDof) = static_cast<const Constraint *>(constraint)->jacobianAction(state, variation);
|
||||
}
|
||||
|
||||
601
libmeanfield/interface/operators/root_manifest.cppm
Normal file
601
libmeanfield/interface/operators/root_manifest.cppm
Normal file
@@ -0,0 +1,601 @@
|
||||
module;
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.root_manifest;
|
||||
|
||||
export import :model.compiled_fixed_mass;
|
||||
export import :model.compiled_fixed_central_density;
|
||||
export import :model.specifications;
|
||||
export import :utils.blocks;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
enum class RootBlockKind { value, residual };
|
||||
enum class RootBlockProvenance { physical_operator, model_specification };
|
||||
enum class RootRowInjection { physical_equation, append_global, replace_carrier_rows };
|
||||
enum class RootColumnPolicy { physical_state, existing_physical_multiplier, solver_border, no_column };
|
||||
enum class RootScalePolicy { unscaled, target_relative };
|
||||
|
||||
struct RootBlockDescriptor final {
|
||||
std::string_view stableId;
|
||||
std::string_view symbol;
|
||||
RootBlockKind kind;
|
||||
RootBlockProvenance provenance;
|
||||
std::string_view source;
|
||||
RootRowInjection rowInjection;
|
||||
RootColumnPolicy columnPolicy;
|
||||
RootScalePolicy scalePolicy;
|
||||
int canonicalIndex;
|
||||
int offset;
|
||||
int size;
|
||||
double scale;
|
||||
};
|
||||
|
||||
struct RootRowReplacementDescriptor final {
|
||||
std::string_view stableId;
|
||||
std::string_view sourceSpecification;
|
||||
models::SpecificationRole role;
|
||||
int carrierResidualBlock;
|
||||
int replacedRowCount;
|
||||
};
|
||||
|
||||
struct RootConstraintDescriptor final {
|
||||
std::string_view stableId;
|
||||
models::SpecificationRole role;
|
||||
RootRowInjection rowInjection;
|
||||
RootColumnPolicy columnPolicy;
|
||||
int valueBlock;
|
||||
int residualBlock;
|
||||
int rowArity;
|
||||
int columnArity;
|
||||
double target;
|
||||
std::optional<double> carrierTarget;
|
||||
std::string_view targetUnits;
|
||||
std::string_view residualUnits;
|
||||
double residualScale;
|
||||
};
|
||||
|
||||
struct CentralDensityManifestInput final {
|
||||
double targetDensity;
|
||||
double targetEnthalpy;
|
||||
int centerDofCount;
|
||||
};
|
||||
|
||||
struct RootConstraintReport final {
|
||||
RootConstraintDescriptor descriptor;
|
||||
double achieved;
|
||||
double dimensionalResidual;
|
||||
double scaledResidual;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
struct StaticRootBlockDescriptor final {
|
||||
std::string_view stableId;
|
||||
std::string_view symbol;
|
||||
RootBlockProvenance provenance;
|
||||
std::string_view source;
|
||||
RootRowInjection rowInjection;
|
||||
RootColumnPolicy columnPolicy;
|
||||
RootScalePolicy scalePolicy;
|
||||
};
|
||||
|
||||
template <typename Block> struct RootBlockTraits;
|
||||
|
||||
#define MEAN_FIELD_PHYSICAL_VALUE_BLOCK(BlockType, StableId, Symbol) \
|
||||
template <> struct RootBlockTraits<BlockType> { \
|
||||
static constexpr StaticRootBlockDescriptor descriptor{ \
|
||||
StableId, \
|
||||
Symbol, \
|
||||
RootBlockProvenance::physical_operator, \
|
||||
"stellar_equilibrium", \
|
||||
RootRowInjection::physical_equation, \
|
||||
RootColumnPolicy::physical_state, \
|
||||
RootScalePolicy::unscaled \
|
||||
}; \
|
||||
}
|
||||
|
||||
#define MEAN_FIELD_PHYSICAL_RESIDUAL_BLOCK(BlockType, StableId, Symbol) \
|
||||
template <> struct RootBlockTraits<BlockType> { \
|
||||
static constexpr StaticRootBlockDescriptor descriptor{ \
|
||||
StableId, \
|
||||
Symbol, \
|
||||
RootBlockProvenance::physical_operator, \
|
||||
"stellar_equilibrium", \
|
||||
RootRowInjection::physical_equation, \
|
||||
RootColumnPolicy::no_column, \
|
||||
RootScalePolicy::unscaled \
|
||||
}; \
|
||||
}
|
||||
|
||||
MEAN_FIELD_PHYSICAL_VALUE_BLOCK(
|
||||
utils::blocks::density::mass::value,
|
||||
"density",
|
||||
"rho"
|
||||
);
|
||||
MEAN_FIELD_PHYSICAL_VALUE_BLOCK(
|
||||
utils::blocks::displacement::geometry::value,
|
||||
"volume_displacement",
|
||||
"d"
|
||||
);
|
||||
MEAN_FIELD_PHYSICAL_VALUE_BLOCK(
|
||||
utils::blocks::surface_deformation::parameters::value,
|
||||
"surface_deformation",
|
||||
"q"
|
||||
);
|
||||
MEAN_FIELD_PHYSICAL_VALUE_BLOCK(
|
||||
utils::blocks::gravity::gradient::value,
|
||||
"gravity_gradient",
|
||||
"g"
|
||||
);
|
||||
MEAN_FIELD_PHYSICAL_VALUE_BLOCK(
|
||||
utils::blocks::gravity::poisson::value,
|
||||
"gravity_potential",
|
||||
"Phi"
|
||||
);
|
||||
MEAN_FIELD_PHYSICAL_VALUE_BLOCK(
|
||||
utils::blocks::enthalpy::specific::value,
|
||||
"specific_enthalpy",
|
||||
"h"
|
||||
);
|
||||
|
||||
MEAN_FIELD_PHYSICAL_RESIDUAL_BLOCK(
|
||||
utils::blocks::gravity::gradient::residual,
|
||||
"gravity_gradient_relation",
|
||||
"R_g"
|
||||
);
|
||||
MEAN_FIELD_PHYSICAL_RESIDUAL_BLOCK(
|
||||
utils::blocks::gravity::poisson::residual,
|
||||
"poisson_balance",
|
||||
"R_Phi"
|
||||
);
|
||||
MEAN_FIELD_PHYSICAL_RESIDUAL_BLOCK(
|
||||
utils::blocks::density::mass::residual,
|
||||
"barotropic_closure",
|
||||
"R_rho"
|
||||
);
|
||||
MEAN_FIELD_PHYSICAL_RESIDUAL_BLOCK(
|
||||
utils::blocks::displacement::geometry::residual,
|
||||
"mechanical_balance",
|
||||
"R_d"
|
||||
);
|
||||
MEAN_FIELD_PHYSICAL_RESIDUAL_BLOCK(
|
||||
utils::blocks::surface_deformation::shape_equilibrium::residual,
|
||||
"surface_shape_balance",
|
||||
"R_q"
|
||||
);
|
||||
MEAN_FIELD_PHYSICAL_RESIDUAL_BLOCK(
|
||||
utils::blocks::enthalpy::specific::residual,
|
||||
"hydrostatic_balance",
|
||||
"R_h"
|
||||
);
|
||||
|
||||
#undef MEAN_FIELD_PHYSICAL_VALUE_BLOCK
|
||||
#undef MEAN_FIELD_PHYSICAL_RESIDUAL_BLOCK
|
||||
|
||||
template <> struct RootBlockTraits<utils::blocks::fixed_total_mass::mass_normalization::value> {
|
||||
static constexpr StaticRootBlockDescriptor descriptor{
|
||||
"fixed_total_mass.multiplier",
|
||||
"C",
|
||||
RootBlockProvenance::model_specification,
|
||||
"FixedTotalMass",
|
||||
RootRowInjection::physical_equation,
|
||||
RootColumnPolicy::existing_physical_multiplier,
|
||||
RootScalePolicy::unscaled
|
||||
};
|
||||
};
|
||||
|
||||
template <> struct RootBlockTraits<utils::blocks::fixed_total_mass::mass_normalization::residual> {
|
||||
static constexpr StaticRootBlockDescriptor descriptor{
|
||||
"fixed_total_mass.residual",
|
||||
"R_M",
|
||||
RootBlockProvenance::model_specification,
|
||||
"FixedTotalMass",
|
||||
RootRowInjection::append_global,
|
||||
RootColumnPolicy::no_column,
|
||||
RootScalePolicy::target_relative
|
||||
};
|
||||
};
|
||||
|
||||
template <> struct RootBlockTraits<utils::blocks::fixed_central_density::central_value::value> {
|
||||
static constexpr StaticRootBlockDescriptor descriptor{
|
||||
"fixed_central_density.border",
|
||||
"lambda_rho_c",
|
||||
RootBlockProvenance::model_specification,
|
||||
"FixedCentralDensity",
|
||||
RootRowInjection::physical_equation,
|
||||
RootColumnPolicy::solver_border,
|
||||
RootScalePolicy::unscaled
|
||||
};
|
||||
};
|
||||
|
||||
template <> struct RootBlockTraits<utils::blocks::fixed_central_density::central_value::residual> {
|
||||
static constexpr StaticRootBlockDescriptor descriptor{
|
||||
"fixed_central_density.residual", "R_rho_c",
|
||||
RootBlockProvenance::model_specification, "FixedCentralDensity",
|
||||
RootRowInjection::append_global, RootColumnPolicy::no_column,
|
||||
RootScalePolicy::target_relative
|
||||
};
|
||||
};
|
||||
|
||||
template <typename Block>
|
||||
[[nodiscard]] constexpr double blockScale(
|
||||
const double fixedMassScale,
|
||||
const double centralDensityScale
|
||||
) noexcept {
|
||||
if constexpr (std::same_as<Block, utils::blocks::fixed_total_mass::mass_normalization::residual>) {
|
||||
return fixedMassScale;
|
||||
} else if constexpr (std::same_as<Block, utils::blocks::fixed_central_density::central_value::residual>) {
|
||||
return centralDensityScale;
|
||||
} else {
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
RootBlockKind Kind,
|
||||
typename... Blocks>
|
||||
[[nodiscard]] std::array<
|
||||
RootBlockDescriptor,
|
||||
sizeof...(Blocks)>
|
||||
makeBlockDescriptors(
|
||||
const mfem::Array<int> &offsets,
|
||||
const double fixedMassScale,
|
||||
const double centralDensityScale,
|
||||
utils::blocks::type_list<Blocks...>
|
||||
) {
|
||||
std::array<RootBlockDescriptor, sizeof...(Blocks)> descriptors{};
|
||||
int index = 0;
|
||||
((descriptors[index] =
|
||||
{.stableId = RootBlockTraits<Blocks>::descriptor.stableId,
|
||||
.symbol = RootBlockTraits<Blocks>::descriptor.symbol,
|
||||
.kind = Kind,
|
||||
.provenance = RootBlockTraits<Blocks>::descriptor.provenance,
|
||||
.source = RootBlockTraits<Blocks>::descriptor.source,
|
||||
.rowInjection = RootBlockTraits<Blocks>::descriptor.rowInjection,
|
||||
.columnPolicy = RootBlockTraits<Blocks>::descriptor.columnPolicy,
|
||||
.scalePolicy = RootBlockTraits<Blocks>::descriptor.scalePolicy,
|
||||
.canonicalIndex = index,
|
||||
.offset = offsets[index],
|
||||
.size = offsets[index + 1] - offsets[index],
|
||||
.scale = blockScale<Blocks>(fixedMassScale, centralDensityScale)},
|
||||
++index),
|
||||
...);
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
template <models::SpecifiedModelType Model>
|
||||
inline constexpr bool hasCentralDensity = Model::template containsSpecification<models::FixedCentralDensity>;
|
||||
|
||||
template <models::SpecifiedModelType Model>
|
||||
inline constexpr std::size_t rootConstraintCount = 2 + (hasCentralDensity<Model> ? 1 : 0);
|
||||
|
||||
template <
|
||||
models::SpecifiedModelType Model,
|
||||
typename Form>
|
||||
[[nodiscard]] std::array<
|
||||
RootConstraintDescriptor,
|
||||
rootConstraintCount<Model>>
|
||||
makeConstraintDescriptors(
|
||||
const double targetMass,
|
||||
const double targetSurfacePressure,
|
||||
const double fixedMassScale,
|
||||
const std::optional<CentralDensityManifestInput> centralDensity
|
||||
) {
|
||||
std::array<RootConstraintDescriptor, rootConstraintCount<Model>> descriptors{};
|
||||
descriptors[0] = {
|
||||
.stableId = "FixedTotalMass",
|
||||
.role = models::SpecificationRole::invariant,
|
||||
.rowInjection = RootRowInjection::append_global,
|
||||
.columnPolicy = RootColumnPolicy::existing_physical_multiplier,
|
||||
.valueBlock = models::FixedMassLayoutRequest::valueBlock<Form>().index,
|
||||
.residualBlock = models::FixedMassLayoutRequest::residualBlock<Form>().index,
|
||||
.rowArity = 1,
|
||||
.columnArity = 1,
|
||||
.target = targetMass,
|
||||
.carrierTarget = targetMass,
|
||||
.targetUnits = "mass",
|
||||
.residualUnits = "mass",
|
||||
.residualScale = fixedMassScale
|
||||
};
|
||||
descriptors[1] = {
|
||||
.stableId = "IsobaricSurface",
|
||||
.role = models::SpecificationRole::boundary_condition,
|
||||
.rowInjection = RootRowInjection::replace_carrier_rows,
|
||||
.columnPolicy = RootColumnPolicy::no_column,
|
||||
.valueBlock = -1,
|
||||
.residualBlock =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::enthalpy_field.specific_term).index,
|
||||
.rowArity = 0,
|
||||
.columnArity = 0,
|
||||
.target = targetSurfacePressure,
|
||||
.carrierTarget = std::nullopt,
|
||||
.targetUnits = "pressure",
|
||||
.residualUnits = "specific_enthalpy",
|
||||
.residualScale = 1.0
|
||||
};
|
||||
|
||||
if constexpr (hasCentralDensity<Model>) {
|
||||
if (!centralDensity.has_value()) {
|
||||
throw std::invalid_argument(
|
||||
"A model containing FixedCentralDensity requires central-density manifest metadata."
|
||||
);
|
||||
}
|
||||
descriptors[2] = {
|
||||
.stableId = "FixedCentralDensity",
|
||||
.role = models::SpecificationRole::phase_condition,
|
||||
.rowInjection = RootRowInjection::append_global,
|
||||
.columnPolicy = RootColumnPolicy::solver_border,
|
||||
.valueBlock = models::CentralDensityLayoutRequest::valueBlock<Form>().index,
|
||||
.residualBlock = models::CentralDensityLayoutRequest::residualBlock<Form>().index,
|
||||
.rowArity = 1,
|
||||
.columnArity = 1,
|
||||
.target = centralDensity->targetDensity,
|
||||
.carrierTarget = centralDensity->targetEnthalpy,
|
||||
.targetUnits = "density",
|
||||
.residualUnits = "specific_enthalpy",
|
||||
.residualScale = std::max(std::abs(centralDensity->targetEnthalpy), 1.0e-300)
|
||||
};
|
||||
} else if (centralDensity.has_value()) {
|
||||
throw std::invalid_argument(
|
||||
"Central-density manifest metadata was provided to a model without FixedCentralDensity."
|
||||
);
|
||||
}
|
||||
return descriptors;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <typename Form> class RootStateView final {
|
||||
public:
|
||||
RootStateView(
|
||||
const mfem::Vector &state,
|
||||
const utils::blocks::form_layout<Form> &layout
|
||||
)
|
||||
: m_state(state),
|
||||
m_layout(layout) {
|
||||
if (state.Size() != layout.value_offsets().Last()) {
|
||||
throw std::invalid_argument("RootStateView received a vector with the wrong size.");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Term> [[nodiscard]] mfem::Vector block(const Term &term) const {
|
||||
constexpr auto valueBlock = utils::blocks::get_value_block<Form>(term);
|
||||
return mfem::Vector(
|
||||
const_cast<mfem::real_t *>(m_state.GetData()) + m_layout.offset(valueBlock), m_layout.size(valueBlock)
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::Vector &vector() const noexcept {
|
||||
return m_state;
|
||||
}
|
||||
|
||||
private:
|
||||
const mfem::Vector &m_state;
|
||||
const utils::blocks::form_layout<Form> &m_layout;
|
||||
};
|
||||
|
||||
template <typename Form> class ResidualView final {
|
||||
public:
|
||||
ResidualView(
|
||||
mfem::Vector &residual,
|
||||
const utils::blocks::form_layout<Form> &layout
|
||||
)
|
||||
: m_residual(residual),
|
||||
m_layout(layout) {
|
||||
if (residual.Size() != layout.residual_offsets().Last()) {
|
||||
throw std::invalid_argument("ResidualView received a vector with the wrong size.");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Term> [[nodiscard]] mfem::Vector block(const Term &term) const {
|
||||
constexpr auto residualBlock = utils::blocks::get_residual_block<Form>(term);
|
||||
return mfem::Vector(m_residual.GetData() + m_layout.offset(residualBlock), m_layout.size(residualBlock));
|
||||
}
|
||||
|
||||
template <typename Term>
|
||||
void assign(
|
||||
const Term &term,
|
||||
const mfem::Vector &source
|
||||
) const {
|
||||
mfem::Vector destination = block(term);
|
||||
if (destination.Size() != source.Size()) {
|
||||
throw std::invalid_argument("ResidualView block assignment has the wrong size.");
|
||||
}
|
||||
destination = source;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector &vector() const noexcept {
|
||||
return m_residual;
|
||||
}
|
||||
|
||||
private:
|
||||
mfem::Vector &m_residual;
|
||||
const utils::blocks::form_layout<Form> &m_layout;
|
||||
};
|
||||
|
||||
template <models::SpecifiedModelType Model, typename Form, typename JacobianForm>
|
||||
requires utils::blocks::valid_jacobian_form<Form, JacobianForm>
|
||||
class CompiledRootManifest final {
|
||||
public:
|
||||
using ModelType = Model;
|
||||
using FormType = Form;
|
||||
using JacobianType = JacobianForm;
|
||||
using Layout = utils::blocks::form_layout<Form>;
|
||||
using StateView = RootStateView<Form>;
|
||||
using DirectionView = RootStateView<Form>;
|
||||
using RootResidualView = ResidualView<Form>;
|
||||
|
||||
static constexpr models::ModelCompilationClass compilationClass = Model::compilationClass;
|
||||
static constexpr bool symbolicallySquare = Model::symbolicallySquare;
|
||||
|
||||
CompiledRootManifest(
|
||||
const std::array<
|
||||
int,
|
||||
Form::value_block_count> &valueSizes,
|
||||
const std::array<
|
||||
int,
|
||||
Form::residual_block_count> &residualSizes,
|
||||
const double targetMass,
|
||||
const double targetSurfacePressure,
|
||||
const int replacedSurfaceRowCount,
|
||||
const std::optional<CentralDensityManifestInput> centralDensity = std::nullopt
|
||||
)
|
||||
: m_layout(
|
||||
valueSizes,
|
||||
residualSizes
|
||||
),
|
||||
m_fixedMassScale(
|
||||
std::max(
|
||||
std::abs(targetMass),
|
||||
1.0e-300
|
||||
)
|
||||
),
|
||||
m_centralDensityScale(
|
||||
centralDensity.has_value() ? std::max(
|
||||
std::abs(centralDensity->targetEnthalpy),
|
||||
1.0e-300
|
||||
)
|
||||
: 1.0
|
||||
),
|
||||
m_valueBlocks(
|
||||
detail::makeBlockDescriptors<RootBlockKind::value>(
|
||||
m_layout.value_offsets(),
|
||||
m_fixedMassScale,
|
||||
m_centralDensityScale,
|
||||
typename Form::value_blocks{}
|
||||
)
|
||||
),
|
||||
m_residualBlocks(
|
||||
detail::makeBlockDescriptors<RootBlockKind::residual>(
|
||||
m_layout.residual_offsets(),
|
||||
m_fixedMassScale,
|
||||
m_centralDensityScale,
|
||||
typename Form::residual_blocks{}
|
||||
)
|
||||
),
|
||||
m_replacements{RootRowReplacementDescriptor{
|
||||
.stableId = "isobaric_surface.replacement",
|
||||
.sourceSpecification = "IsobaricSurface",
|
||||
.role = models::SpecificationRole::boundary_condition,
|
||||
.carrierResidualBlock =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::enthalpy_field.specific_term).index,
|
||||
.replacedRowCount = replacedSurfaceRowCount
|
||||
}},
|
||||
m_constraints(
|
||||
detail::makeConstraintDescriptors<
|
||||
Model,
|
||||
Form>(
|
||||
targetMass,
|
||||
targetSurfacePressure,
|
||||
m_fixedMassScale,
|
||||
centralDensity
|
||||
)
|
||||
) {
|
||||
if (replacedSurfaceRowCount < 0) {
|
||||
throw std::invalid_argument(
|
||||
"An equilibrium-system manifest cannot contain a negative replacement-row count."
|
||||
);
|
||||
}
|
||||
if (centralDensity.has_value() && centralDensity->centerDofCount < 0) {
|
||||
throw std::invalid_argument(
|
||||
"An equilibrium-system manifest cannot contain a negative central-DOF count."
|
||||
);
|
||||
}
|
||||
if constexpr (compilationClass == models::EquilibriumSystemCompilation::complete_equilibrium_system) {
|
||||
if (m_layout.value_offsets().Last() != m_layout.residual_offsets().Last()) {
|
||||
throw std::invalid_argument(
|
||||
"A complete equilibrium system must have equal state and equation dimensions."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] const Layout &layout() const noexcept {
|
||||
return m_layout;
|
||||
}
|
||||
|
||||
[[nodiscard]] StateView stateView(const mfem::Vector &state) const {
|
||||
return {state, m_layout};
|
||||
}
|
||||
|
||||
[[nodiscard]] DirectionView directionView(const mfem::Vector &direction) const {
|
||||
return {direction, m_layout};
|
||||
}
|
||||
|
||||
[[nodiscard]] RootResidualView residualView(mfem::Vector &residual) const {
|
||||
return {residual, m_layout};
|
||||
}
|
||||
|
||||
[[nodiscard]] std::span<const RootBlockDescriptor> valueBlocks() const noexcept {
|
||||
return m_valueBlocks;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::span<const RootBlockDescriptor> residualBlocks() const noexcept {
|
||||
return m_residualBlocks;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::span<const RootRowReplacementDescriptor> rowReplacements() const noexcept {
|
||||
return m_replacements;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::span<const RootConstraintDescriptor> constraints() const noexcept {
|
||||
return m_constraints;
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr std::span<const models::RuntimeSpecificationDescriptor>
|
||||
specificationDescriptors() noexcept {
|
||||
return Model::runtimeSpecificationDescriptors();
|
||||
}
|
||||
|
||||
[[nodiscard]] RootConstraintReport fixedMassReport(const double achievedMass) const {
|
||||
const RootConstraintDescriptor &descriptor = m_constraints[0];
|
||||
const double residual = achievedMass - descriptor.target;
|
||||
return {
|
||||
.descriptor = descriptor,
|
||||
.achieved = achievedMass,
|
||||
.dimensionalResidual = residual,
|
||||
.scaledResidual = residual / descriptor.residualScale
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
Layout m_layout;
|
||||
double m_fixedMassScale;
|
||||
double m_centralDensityScale;
|
||||
std::array<RootBlockDescriptor, Form::value_block_count> m_valueBlocks;
|
||||
std::array<RootBlockDescriptor, Form::residual_block_count> m_residualBlocks;
|
||||
std::array<RootRowReplacementDescriptor, 1> m_replacements;
|
||||
std::array<RootConstraintDescriptor, detail::rootConstraintCount<Model>> m_constraints;
|
||||
};
|
||||
|
||||
// Physics-facing names for the public equilibrium-system boundary. The
|
||||
// root-oriented names remain available while existing solver consumers
|
||||
// migrate, but new APIs should expose these aliases.
|
||||
using EquilibriumBlockKind = RootBlockKind;
|
||||
using EquilibriumBlockProvenance = RootBlockProvenance;
|
||||
using EquilibriumEquationInjection = RootRowInjection;
|
||||
using EquilibriumGeneratedVariablePolicy = RootColumnPolicy;
|
||||
using EquilibriumScalePolicy = RootScalePolicy;
|
||||
using EquilibriumBlockDescriptor = RootBlockDescriptor;
|
||||
using EquilibriumEquationReplacementDescriptor = RootRowReplacementDescriptor;
|
||||
using EquilibriumSpecificationDescriptor = RootConstraintDescriptor;
|
||||
using EquilibriumSpecificationReport = RootConstraintReport;
|
||||
|
||||
template <typename Form> using EquilibriumStateView = RootStateView<Form>;
|
||||
|
||||
template <typename Form> using EquilibriumResidualView = ResidualView<Form>;
|
||||
|
||||
template <models::SpecifiedModelType Model, typename Form, typename JacobianForm>
|
||||
requires utils::blocks::valid_jacobian_form<Form, JacobianForm>
|
||||
using EquilibriumSystemManifest = CompiledRootManifest<Model, Form, JacobianForm>;
|
||||
} // namespace mean_field::operators
|
||||
@@ -0,0 +1,210 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.stellar_equilibrium_problem;
|
||||
|
||||
export import :deformation.domain_deformation;
|
||||
export import :equilibrium.stellar_discretization;
|
||||
export import :model.typed_stellar;
|
||||
export import :operators.prepared_central_density_stellar_equilibrium;
|
||||
export import :surface.compiler;
|
||||
|
||||
export namespace mean_field::equilibrium {
|
||||
template <typename Candidate>
|
||||
concept StellarEquilibriumModel = model::StellarModelType<Candidate> && requires {
|
||||
requires std::remove_cvref_t<Candidate>::template containsSpecification<eos::Polytrope>;
|
||||
requires std::remove_cvref_t<Candidate>::template containsSpecification<surface::Isobaric>;
|
||||
requires std::remove_cvref_t<Candidate>::template containsSpecification<models::FixedTotalMass>;
|
||||
requires std::remove_cvref_t<Candidate>::specificationCount ==
|
||||
3 + static_cast<std::size_t>(
|
||||
std::remove_cvref_t<Candidate>::template containsSpecification<models::FixedCentralDensity>
|
||||
);
|
||||
};
|
||||
|
||||
template <StellarEquilibriumModel Model> class StellarEquilibriumProblem final {
|
||||
public:
|
||||
using ModelType = std::remove_cvref_t<Model>;
|
||||
|
||||
static constexpr bool hasFixedCentralDensity =
|
||||
ModelType::template containsSpecification<models::FixedCentralDensity>;
|
||||
static constexpr bool symbolicallySquare = ModelType::symbolicallySquare;
|
||||
|
||||
using PreparedOperatorType = std::conditional_t<
|
||||
hasFixedCentralDensity,
|
||||
operators::PreparedCentralDensityStellarEquilibriumOperator,
|
||||
operators::PreparedStellarEquilibriumOperator>;
|
||||
using CompiledSurfaceConstraintType =
|
||||
surface::CompiledPressureSurfaceConstraintT<surface::BarotropicSurfaceFormulation, eos::Polytrope>;
|
||||
|
||||
StellarEquilibriumProblem(
|
||||
ModelType stellarModel,
|
||||
const StellarDiscretization discretization
|
||||
)
|
||||
requires(!hasFixedCentralDensity)
|
||||
: m_stellarModel(std::move(stellarModel)),
|
||||
m_discretization(discretization),
|
||||
m_compiledSurfaceConstraint(CompileSurfaceConstraint(m_stellarModel)),
|
||||
m_preparedOperator(
|
||||
m_discretization.finiteElementModel(),
|
||||
m_discretization.domainMapper(),
|
||||
m_stellarModel.template specification<eos::Polytrope>(),
|
||||
models::compileConstraint(m_stellarModel.template specification<models::FixedTotalMass>()),
|
||||
operators::PressureSurfaceConstraintView{m_compiledSurfaceConstraint},
|
||||
CompileDefaultDomainDeformation(m_discretization.finiteElementModel())
|
||||
) {
|
||||
VerifyProblem();
|
||||
}
|
||||
|
||||
StellarEquilibriumProblem(
|
||||
ModelType stellarModel,
|
||||
const StellarDiscretization discretization
|
||||
)
|
||||
requires hasFixedCentralDensity
|
||||
: m_stellarModel(std::move(stellarModel)),
|
||||
m_discretization(discretization),
|
||||
m_compiledSurfaceConstraint(CompileSurfaceConstraint(m_stellarModel)),
|
||||
m_preparedOperator(
|
||||
m_discretization.finiteElementModel(),
|
||||
m_discretization.domainMapper(),
|
||||
m_stellarModel.template specification<eos::Polytrope>(),
|
||||
models::compileConstraint(m_stellarModel.template specification<models::FixedTotalMass>()),
|
||||
operators::PressureSurfaceConstraintView{m_compiledSurfaceConstraint},
|
||||
CompileDefaultDomainDeformation(m_discretization.finiteElementModel()),
|
||||
models::compileConstraint(
|
||||
m_stellarModel.template specification<models::FixedCentralDensity>(),
|
||||
m_stellarModel.template specification<eos::Polytrope>()
|
||||
)
|
||||
) {
|
||||
VerifyProblem();
|
||||
}
|
||||
|
||||
StellarEquilibriumProblem(const StellarEquilibriumProblem &) = delete;
|
||||
StellarEquilibriumProblem &operator=(const StellarEquilibriumProblem &) = delete;
|
||||
StellarEquilibriumProblem(StellarEquilibriumProblem &&) = delete;
|
||||
StellarEquilibriumProblem &operator=(StellarEquilibriumProblem &&) = delete;
|
||||
|
||||
[[nodiscard]] const ModelType &GetStellarModel() const noexcept {
|
||||
return m_stellarModel;
|
||||
}
|
||||
|
||||
[[nodiscard]] const StellarDiscretization &GetDiscretization() const noexcept {
|
||||
return m_discretization;
|
||||
}
|
||||
|
||||
[[nodiscard]] const CompiledSurfaceConstraintType &GetCompiledSurfaceConstraint() const noexcept {
|
||||
return m_compiledSurfaceConstraint;
|
||||
}
|
||||
|
||||
[[nodiscard]] PreparedOperatorType &GetPreparedOperator() noexcept {
|
||||
return m_preparedOperator;
|
||||
}
|
||||
|
||||
[[nodiscard]] const PreparedOperatorType &GetPreparedOperator() const noexcept {
|
||||
return m_preparedOperator;
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &GetManifest() const noexcept {
|
||||
return m_preparedOperator.GetRootManifest();
|
||||
}
|
||||
|
||||
[[nodiscard]] const field::FieldBoundaryDofMap &GetPressureSurfaceRows() const noexcept {
|
||||
if constexpr (hasFixedCentralDensity) {
|
||||
return m_preparedOperator.GetPhysicalOperator().GetSurfaceConstraintOperator().GetSurfaceRows();
|
||||
} else {
|
||||
return m_preparedOperator.GetSurfaceConstraintOperator().GetSurfaceRows();
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] int StateSize() const noexcept {
|
||||
return m_preparedOperator.Width();
|
||||
}
|
||||
|
||||
[[nodiscard]] int EquationSize() const noexcept {
|
||||
return m_preparedOperator.Height();
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::Operator &GetLinearizationOperator() const noexcept {
|
||||
return m_preparedOperator;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto Prepare(
|
||||
const mfem::Vector &state,
|
||||
const operators::StellarEquilibriumDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
) {
|
||||
return m_preparedOperator.Prepare(state, dependencies, rotation);
|
||||
}
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const {
|
||||
m_preparedOperator.BuildResidual(residual);
|
||||
}
|
||||
|
||||
void ApplyLinearization(
|
||||
const mfem::Vector &direction,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
m_preparedOperator.Mult(direction, action);
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] static CompiledSurfaceConstraintType CompileSurfaceConstraint(const ModelType &stellarModel) {
|
||||
return surface::compilePressureSurfaceConstraint<surface::BarotropicSurfaceFormulation>(
|
||||
stellarModel.template specification<surface::Isobaric>(),
|
||||
stellarModel.template specification<eos::Polytrope>()
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] static deformation::PreparedDomainDeformationRuntime
|
||||
CompileDefaultDomainDeformation(fem::FEM &finiteElementModel) {
|
||||
MFEM_VERIFY(
|
||||
finiteElementModel.mesh != nullptr,
|
||||
"Default stellar domain-deformation compilation requires a physical mesh."
|
||||
);
|
||||
mfem::Vector referenceCenter(finiteElementModel.mesh->SpaceDimension());
|
||||
referenceCenter = 0.0;
|
||||
|
||||
return deformation::PreparedDomainDeformationRuntime{deformation::compileDomainDeformation(
|
||||
deformation::NodalRadialSurface{std::move(referenceCenter)},
|
||||
deformation::PowerLawRadialInteriorExtension{}, deformation::FixedInfinityRadialVacuumExtension{},
|
||||
finiteElementModel
|
||||
)};
|
||||
}
|
||||
|
||||
void VerifyProblem() const {
|
||||
MFEM_VERIFY(symbolicallySquare, "A stellar equilibrium problem must be symbolically square.");
|
||||
MFEM_VERIFY(
|
||||
StateSize() == EquationSize(),
|
||||
"The discretized stellar equilibrium problem has unequal state and equation dimensions."
|
||||
);
|
||||
MFEM_VERIFY(m_discretization.isCurrent(), "The stellar equilibrium problem has a stale discretization.");
|
||||
}
|
||||
|
||||
ModelType m_stellarModel;
|
||||
StellarDiscretization m_discretization;
|
||||
CompiledSurfaceConstraintType m_compiledSurfaceConstraint;
|
||||
PreparedOperatorType m_preparedOperator;
|
||||
};
|
||||
|
||||
template <StellarEquilibriumModel Model>
|
||||
[[nodiscard]] auto discretize(
|
||||
Model &&stellarModel,
|
||||
const StellarDiscretization discretization
|
||||
) {
|
||||
using ModelType = std::remove_cvref_t<Model>;
|
||||
return StellarEquilibriumProblem<ModelType>{std::forward<Model>(stellarModel), discretization};
|
||||
}
|
||||
|
||||
template <StellarEquilibriumModel Model>
|
||||
[[nodiscard]] auto discretize(
|
||||
Model &&stellarModel,
|
||||
fem::FEM &finiteElementModel
|
||||
) {
|
||||
return discretize(std::forward<Model>(stellarModel), StellarDiscretization{finiteElementModel});
|
||||
}
|
||||
} // namespace mean_field::equilibrium
|
||||
@@ -0,0 +1,26 @@
|
||||
module;
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
export module mean_field:operators.stellar_equilibrium_system;
|
||||
|
||||
export import :operators.stellar_equilibrium_problem;
|
||||
|
||||
export namespace mean_field::equilibrium {
|
||||
// Transitional source-compatible names. New code should use
|
||||
// StellarEquilibriumProblem and equilibrium::discretize.
|
||||
template <typename Candidate>
|
||||
concept CurrentlySupportedStellarModel = StellarEquilibriumModel<Candidate>;
|
||||
|
||||
template <StellarEquilibriumModel Model> using StellarEquilibriumSystem = StellarEquilibriumProblem<Model>;
|
||||
|
||||
template <StellarEquilibriumModel Model>
|
||||
[[nodiscard]] auto makeStellarEquilibriumSystem(
|
||||
fem::FEM &finiteElementModel,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
Model &&stellarModel
|
||||
) {
|
||||
return discretize(std::forward<Model>(stellarModel), StellarDiscretization{finiteElementModel, domainMapper});
|
||||
}
|
||||
} // namespace mean_field::equilibrium
|
||||
@@ -6,6 +6,12 @@ export module mean_field:physics.gravity;
|
||||
export import :fem;
|
||||
|
||||
export namespace mean_field::physics {
|
||||
struct GravitySolveOptions final {
|
||||
double relativeTolerance{1.0e-12};
|
||||
double absoluteTolerance{1.0e-15};
|
||||
int maximumIterations{1000};
|
||||
};
|
||||
|
||||
struct GravitySolution {
|
||||
mfem::ParGridFunction gradPhi;
|
||||
mfem::ParGridFunction phi;
|
||||
@@ -16,6 +22,13 @@ export namespace mean_field::physics {
|
||||
}
|
||||
};
|
||||
|
||||
GravitySolution solve_gravity_field(
|
||||
fem::FEM &f,
|
||||
const GravitySolveOptions &options,
|
||||
const mfem::GridFunction &rho,
|
||||
const mfem::GridFunction &displacement
|
||||
);
|
||||
|
||||
GravitySolution solve_gravity_field(
|
||||
fem::FEM &f,
|
||||
const utils::Args &args,
|
||||
|
||||
120
libmeanfield/interface/seed/lane_emden.cppm
Normal file
120
libmeanfield/interface/seed/lane_emden.cppm
Normal file
@@ -0,0 +1,120 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
#include <concepts>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:seed.lane_emden;
|
||||
|
||||
export import :dimensions.quantities;
|
||||
export import :eos.polytrope;
|
||||
export import :model.typed_stellar;
|
||||
|
||||
export namespace mean_field::seed {
|
||||
struct DimensionlessLaneEmdenSolution final {
|
||||
mfem::Vector coordinate;
|
||||
mfem::Vector theta;
|
||||
mfem::Vector thetaDerivative;
|
||||
std::optional<double> firstZeroCoordinate;
|
||||
};
|
||||
|
||||
/*
|
||||
* Integrate the dimensionless Lane-Emden equation from the regular center
|
||||
* to either the first zero of theta or coordinateLimit, whichever occurs
|
||||
* first. This numerical kernel also supports the n = 0 and n = 5 analytic
|
||||
* benchmark cases even though they do not both define admissible seeds for
|
||||
* the current Polytrope EOS and finite stellar domain.
|
||||
*/
|
||||
[[nodiscard]] DimensionlessLaneEmdenSolution integrateLaneEmden(
|
||||
double polytropicIndex,
|
||||
double coordinateLimit,
|
||||
double integrationStep = 1.0e-3
|
||||
);
|
||||
|
||||
struct RadialProfile final {
|
||||
mfem::Vector radius;
|
||||
mfem::Vector density;
|
||||
mfem::Vector specificEnthalpy;
|
||||
|
||||
dimensions::LengthValue stellarRadius;
|
||||
dimensions::DensityValue centralDensity;
|
||||
dimensions::SpecificEnthalpyValue centralSpecificEnthalpy;
|
||||
};
|
||||
|
||||
class LaneEmden final {
|
||||
public:
|
||||
struct Parameters final {
|
||||
std::optional<dimensions::DensityValue> centralDensity{std::nullopt};
|
||||
int radialSampleCount{512};
|
||||
};
|
||||
|
||||
LaneEmden()
|
||||
: m_centralDensity(std::nullopt),
|
||||
m_radialSampleCount(512) {
|
||||
}
|
||||
|
||||
explicit LaneEmden(const Parameters parameters)
|
||||
: m_centralDensity(parameters.centralDensity),
|
||||
m_radialSampleCount(parameters.radialSampleCount) {
|
||||
if (m_centralDensity.has_value() &&
|
||||
(!std::isfinite(m_centralDensity->value()) || m_centralDensity->value() <= 0.0)) {
|
||||
throw std::invalid_argument("A Lane-Emden seed central density must be finite and positive.");
|
||||
}
|
||||
if (m_radialSampleCount < 2) {
|
||||
throw std::invalid_argument("A Lane-Emden seed requires at least two radial samples.");
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::optional<dimensions::DensityValue> ¢ralDensity() const noexcept {
|
||||
return m_centralDensity;
|
||||
}
|
||||
|
||||
[[nodiscard]] int radialSampleCount() const noexcept {
|
||||
return m_radialSampleCount;
|
||||
}
|
||||
|
||||
private:
|
||||
std::optional<dimensions::DensityValue> m_centralDensity;
|
||||
int m_radialSampleCount;
|
||||
};
|
||||
|
||||
[[nodiscard]] RadialProfile generateLaneEmdenProfile(
|
||||
const eos::Polytrope &equationOfState,
|
||||
dimensions::DensityValue centralDensity,
|
||||
int radialSampleCount
|
||||
);
|
||||
|
||||
template <model::StellarModelType Model>
|
||||
requires std::remove_cvref_t<Model>::template
|
||||
containsSpecification<eos::Polytrope> [[nodiscard]] RadialProfile generateRadialProfile(
|
||||
const Model &stellarModel,
|
||||
const LaneEmden &strategy
|
||||
) {
|
||||
std::optional<dimensions::DensityValue> centralDensity = strategy.centralDensity();
|
||||
|
||||
if (!centralDensity.has_value()) {
|
||||
if constexpr (std::remove_cvref_t<Model>::template containsSpecification<models::FixedCentralDensity>) {
|
||||
centralDensity = stellarModel.template specification<models::FixedCentralDensity>().targetDensity();
|
||||
} else {
|
||||
throw std::invalid_argument(
|
||||
"Lane-Emden seed generation requires either FixedCentralDensity or an explicit seed-only central "
|
||||
"density."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return generateLaneEmdenProfile(
|
||||
stellarModel.template specification<eos::Polytrope>(), *centralDensity, strategy.radialSampleCount()
|
||||
);
|
||||
}
|
||||
|
||||
template <typename Strategy, typename Model>
|
||||
concept RadialSeedStrategyFor = requires(const Model &stellarModel, const Strategy &strategy) {
|
||||
{ generateRadialProfile(stellarModel, strategy) } -> std::same_as<RadialProfile>;
|
||||
};
|
||||
} // namespace mean_field::seed
|
||||
136
libmeanfield/interface/seed/stellar_equilibrium_projection.cppm
Normal file
136
libmeanfield/interface/seed/stellar_equilibrium_projection.cppm
Normal file
@@ -0,0 +1,136 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:seed.stellar_equilibrium_projection;
|
||||
|
||||
export import :operators.stellar_equilibrium_problem;
|
||||
export import :physics.gravity;
|
||||
export import :seed.lane_emden;
|
||||
|
||||
export namespace mean_field::seed {
|
||||
struct StellarEquilibriumProjectionOptions final {
|
||||
physics::GravitySolveOptions gravity{};
|
||||
double surfaceRadiusRelativeTolerance{5.0e-4};
|
||||
};
|
||||
|
||||
template <equilibrium::StellarEquilibriumModel Model> struct ProjectedEquilibriumState final {
|
||||
using ModelType = std::remove_cvref_t<Model>;
|
||||
|
||||
mfem::Vector values;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
struct ProjectedRadialFields final {
|
||||
mfem::Vector density;
|
||||
mfem::Vector gravityGradient;
|
||||
mfem::Vector gravityPotential;
|
||||
mfem::Vector specificEnthalpy;
|
||||
double bernoulliConstant;
|
||||
};
|
||||
|
||||
[[nodiscard]] ProjectedRadialFields projectRadialFields(
|
||||
const equilibrium::StellarDiscretization &discretization,
|
||||
const RadialProfile &profile,
|
||||
dimensions::MassValue targetMass,
|
||||
dimensions::PressureValue targetSurfacePressure,
|
||||
const StellarEquilibriumProjectionOptions &options
|
||||
);
|
||||
|
||||
inline void assignProjectedBlock(
|
||||
mfem::Vector destination,
|
||||
const mfem::Vector &source,
|
||||
const char *name
|
||||
) {
|
||||
if (destination.Size() != source.Size()) {
|
||||
throw std::invalid_argument(name);
|
||||
}
|
||||
destination = source;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <equilibrium::StellarEquilibriumModel Model>
|
||||
[[nodiscard]] ProjectedEquilibriumState<Model> projectRadialProfile(
|
||||
const equilibrium::StellarEquilibriumProblem<Model> &problem,
|
||||
const RadialProfile &profile,
|
||||
const StellarEquilibriumProjectionOptions &options = {}
|
||||
) {
|
||||
const detail::ProjectedRadialFields fields = detail::projectRadialFields(
|
||||
problem.GetDiscretization(), profile,
|
||||
problem.GetStellarModel().template specification<models::FixedTotalMass>().targetMass(),
|
||||
problem.GetStellarModel().template specification<surface::Isobaric>().targetPressure(), options
|
||||
);
|
||||
|
||||
mfem::Vector values(problem.StateSize());
|
||||
values = 0.0;
|
||||
const auto stateView = problem.GetManifest().stateView(values);
|
||||
|
||||
detail::assignProjectedBlock(
|
||||
stateView.block(utils::blocks::density_field.mass_term), fields.density,
|
||||
"The projected density does not match the compiled equilibrium-state block."
|
||||
);
|
||||
stateView.block(utils::blocks::surface_deformation_field.parameters_term) = 0.0;
|
||||
detail::assignProjectedBlock(
|
||||
stateView.block(utils::blocks::gravity_field.gradient_term), fields.gravityGradient,
|
||||
"The projected gravity gradient does not match the compiled equilibrium-state block."
|
||||
);
|
||||
detail::assignProjectedBlock(
|
||||
stateView.block(utils::blocks::gravity_field.poisson_term), fields.gravityPotential,
|
||||
"The projected gravity potential does not match the compiled equilibrium-state block."
|
||||
);
|
||||
detail::assignProjectedBlock(
|
||||
stateView.block(utils::blocks::enthalpy_field.specific_term), fields.specificEnthalpy,
|
||||
"The projected specific enthalpy does not match the compiled equilibrium-state block."
|
||||
);
|
||||
|
||||
/*
|
||||
* Projection of a continuous spherical profile onto a faceted
|
||||
* reference mesh generally leaves a small trace error on the physical
|
||||
* surface. The pressure condition replaces these carrier rows in the
|
||||
* compiled equilibrium problem, so impose its required carrier value
|
||||
* exactly after bulk projection instead of treating that geometric
|
||||
* mismatch as part of the initial residual.
|
||||
*/
|
||||
mfem::Vector enthalpy = stateView.block(utils::blocks::enthalpy_field.specific_term);
|
||||
const dimensions::SpecificEnthalpyValue requiredSurfaceEnthalpy =
|
||||
eos::evaluate<dimensions::quantity::SpecificEnthalpy>(
|
||||
problem.GetStellarModel().template specification<eos::Polytrope>(),
|
||||
problem.GetStellarModel().template specification<surface::Isobaric>().targetPressure()
|
||||
);
|
||||
for (const int surfaceRow : problem.GetPressureSurfaceRows().reduced_dofs()) {
|
||||
enthalpy(surfaceRow) = requiredSurfaceEnthalpy.value();
|
||||
}
|
||||
|
||||
mfem::Vector fixedMassCoordinate =
|
||||
stateView.block(utils::blocks::fixed_total_mass_constraint.mass_normalization_term);
|
||||
if (fixedMassCoordinate.Size() != 1) {
|
||||
throw std::invalid_argument("FixedTotalMass must generate exactly one equilibrium-state coordinate.");
|
||||
}
|
||||
fixedMassCoordinate(0) = fields.bernoulliConstant;
|
||||
|
||||
if constexpr (std::remove_cvref_t<Model>::template containsSpecification<models::FixedCentralDensity>) {
|
||||
stateView.block(utils::blocks::fixed_central_density_phase.central_value_term) = 0.0;
|
||||
}
|
||||
|
||||
return {.values = std::move(values)};
|
||||
}
|
||||
|
||||
template <
|
||||
equilibrium::StellarEquilibriumModel Model,
|
||||
typename Strategy>
|
||||
requires RadialSeedStrategyFor<
|
||||
Strategy,
|
||||
typename equilibrium::StellarEquilibriumProblem<Model>::ModelType>
|
||||
[[nodiscard]] ProjectedEquilibriumState<Model> makeProjectedEquilibriumState(
|
||||
const equilibrium::StellarEquilibriumProblem<Model> &problem,
|
||||
const Strategy &strategy,
|
||||
const StellarEquilibriumProjectionOptions &options = {}
|
||||
) {
|
||||
return projectRadialProfile(problem, generateRadialProfile(problem.GetStellarModel(), strategy), options);
|
||||
}
|
||||
} // namespace mean_field::seed
|
||||
259
libmeanfield/interface/solver/preconditioning_diagnostics.cppm
Normal file
259
libmeanfield/interface/solver/preconditioning_diagnostics.cppm
Normal file
@@ -0,0 +1,259 @@
|
||||
module;
|
||||
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
#include <mpi.h>
|
||||
|
||||
export module mean_field:solver.preconditioning_diagnostics;
|
||||
|
||||
export import :operators.root_manifest;
|
||||
|
||||
export namespace mean_field::solver {
|
||||
struct OperatorApplicationStatistics final {
|
||||
std::uint64_t applications{0};
|
||||
double totalSeconds{0.0};
|
||||
double maximumSeconds{0.0};
|
||||
};
|
||||
|
||||
struct PreconditionerLifecycleStatistics final {
|
||||
std::uint64_t setups{0};
|
||||
std::uint64_t refreshes{0};
|
||||
double setupSeconds{0.0};
|
||||
double refreshSeconds{0.0};
|
||||
};
|
||||
|
||||
/*
|
||||
* A non-owning measurement wrapper. Statistics are local to an MPI rank;
|
||||
* cross-rank wall-clock reductions are performed when a solve report is
|
||||
* assembled. Krylov application is sequential, so counters intentionally
|
||||
* do not impose atomic overhead.
|
||||
*/
|
||||
class InstrumentedOperator final : public mfem::Operator {
|
||||
public:
|
||||
explicit InstrumentedOperator(const mfem::Operator &operation);
|
||||
|
||||
void Mult(
|
||||
const mfem::Vector &input,
|
||||
mfem::Vector &output
|
||||
) const override;
|
||||
|
||||
void ResetStatistics() const noexcept;
|
||||
[[nodiscard]] const OperatorApplicationStatistics &GetStatistics() const noexcept;
|
||||
[[nodiscard]] const mfem::Operator &GetOperation() const noexcept;
|
||||
|
||||
private:
|
||||
const mfem::Operator *m_operation;
|
||||
mutable OperatorApplicationStatistics m_statistics;
|
||||
};
|
||||
|
||||
class InstrumentedPreconditioner final : public mfem::Solver {
|
||||
public:
|
||||
explicit InstrumentedPreconditioner(mfem::Solver &preconditioner);
|
||||
|
||||
void SetOperator(const mfem::Operator &operation) override;
|
||||
|
||||
void Mult(
|
||||
const mfem::Vector &input,
|
||||
mfem::Vector &output
|
||||
) const override;
|
||||
|
||||
void ResetStatistics() const noexcept;
|
||||
[[nodiscard]] const OperatorApplicationStatistics &GetStatistics() const noexcept;
|
||||
[[nodiscard]] const PreconditionerLifecycleStatistics &GetLifecycleStatistics() const noexcept;
|
||||
[[nodiscard]] const mfem::Solver &GetPreconditioner() const noexcept;
|
||||
|
||||
private:
|
||||
mfem::Solver *m_preconditioner;
|
||||
mutable OperatorApplicationStatistics m_statistics;
|
||||
PreconditionerLifecycleStatistics m_lifecycleStatistics;
|
||||
};
|
||||
|
||||
class IdentityPreconditioner final : public mfem::Solver {
|
||||
public:
|
||||
explicit IdentityPreconditioner(int size);
|
||||
|
||||
void SetOperator(const mfem::Operator &operation) override;
|
||||
|
||||
void Mult(
|
||||
const mfem::Vector &input,
|
||||
mfem::Vector &output
|
||||
) const override;
|
||||
};
|
||||
|
||||
/*
|
||||
* If the supplied solver applies M^{-1}, this operator represents the
|
||||
* fixed right-preconditioned product J M^{-1}. It is deliberately
|
||||
* independent of the Krylov implementation used in production.
|
||||
*/
|
||||
class FixedRightPreconditionedOperator final : public mfem::Operator {
|
||||
public:
|
||||
FixedRightPreconditionedOperator(
|
||||
const mfem::Operator &jacobian,
|
||||
const mfem::Solver &inversePreconditioner
|
||||
);
|
||||
|
||||
void Mult(
|
||||
const mfem::Vector &input,
|
||||
mfem::Vector &output
|
||||
) const override;
|
||||
|
||||
[[nodiscard]] const mfem::Operator &GetJacobian() const noexcept;
|
||||
[[nodiscard]] const mfem::Solver &GetInversePreconditioner() const noexcept;
|
||||
|
||||
private:
|
||||
const mfem::Operator *m_jacobian;
|
||||
const mfem::Solver *m_inversePreconditioner;
|
||||
mutable mfem::Vector m_preconditionedDirection;
|
||||
};
|
||||
|
||||
struct IterationResidualMeasurement final {
|
||||
int iteration;
|
||||
double reportedNorm;
|
||||
bool final;
|
||||
};
|
||||
|
||||
class ResidualHistoryMonitor final : public mfem::IterativeSolverMonitor {
|
||||
public:
|
||||
void Reset() override;
|
||||
|
||||
void MonitorResidual(
|
||||
int iteration,
|
||||
double norm,
|
||||
const mfem::Vector &residual,
|
||||
bool final
|
||||
) override;
|
||||
|
||||
[[nodiscard]] const std::vector<IterationResidualMeasurement> &GetHistory() const noexcept;
|
||||
|
||||
private:
|
||||
std::vector<IterationResidualMeasurement> m_history;
|
||||
};
|
||||
|
||||
struct ResidualBlockMeasurement final {
|
||||
std::string stableId;
|
||||
int size{0};
|
||||
double descriptorScale{1.0};
|
||||
double rightHandSideNorm{0.0};
|
||||
double trueResidualNorm{0.0};
|
||||
double blockRelativeResidual{0.0};
|
||||
double scaledRightHandSideNorm{0.0};
|
||||
double scaledTrueResidualNorm{0.0};
|
||||
double contributionToGlobalRelativeResidual{0.0};
|
||||
double fractionOfGlobalSquaredResidualNorm{0.0};
|
||||
};
|
||||
|
||||
struct DirectResidualMeasurement final {
|
||||
double rightHandSideNorm{0.0};
|
||||
double trueResidualNorm{0.0};
|
||||
double relativeResidual{0.0};
|
||||
std::vector<ResidualBlockMeasurement> blocks;
|
||||
};
|
||||
|
||||
[[nodiscard]] DirectResidualMeasurement measureDirectResidual(
|
||||
const mfem::Operator &jacobian,
|
||||
const mfem::Vector &rightHandSide,
|
||||
const mfem::Vector &solution,
|
||||
std::span<const operators::RootBlockDescriptor> residualBlocks,
|
||||
MPI_Comm communicator,
|
||||
double denominatorFloor = 1.0e-300
|
||||
);
|
||||
|
||||
struct LinearSolveMeasurement final {
|
||||
bool solverConverged{false};
|
||||
int outerIterations{0};
|
||||
double solverReportedInitialNorm{0.0};
|
||||
double solverReportedFinalNorm{0.0};
|
||||
double solverReportedResidualReduction{0.0};
|
||||
double trueResidualDigitsReducedPerJacobianApplication{0.0};
|
||||
double solveSecondsMaximumRank{0.0};
|
||||
OperatorApplicationStatistics jacobian;
|
||||
OperatorApplicationStatistics inversePreconditioner;
|
||||
PreconditionerLifecycleStatistics inversePreconditionerLifecycle;
|
||||
DirectResidualMeasurement directResidual;
|
||||
std::vector<IterationResidualMeasurement> reportedResidualHistory;
|
||||
};
|
||||
|
||||
[[nodiscard]] LinearSolveMeasurement measureLinearSolve(
|
||||
const mfem::IterativeSolver &iterativeSolver,
|
||||
const mfem::Operator &jacobian,
|
||||
const mfem::Vector &rightHandSide,
|
||||
const mfem::Vector &solution,
|
||||
std::span<const operators::RootBlockDescriptor> residualBlocks,
|
||||
const OperatorApplicationStatistics &jacobianStatistics,
|
||||
const OperatorApplicationStatistics &inversePreconditionerStatistics,
|
||||
const PreconditionerLifecycleStatistics &inversePreconditionerLifecycle,
|
||||
const ResidualHistoryMonitor &monitor,
|
||||
double localSolveSeconds,
|
||||
MPI_Comm communicator,
|
||||
double denominatorFloor = 1.0e-300
|
||||
);
|
||||
|
||||
struct ArnoldiOptions final {
|
||||
int krylovDimension{40};
|
||||
double breakdownRelativeTolerance{1.0e-13};
|
||||
double ritzConvergenceRelativeTolerance{1.0e-8};
|
||||
bool reorthogonalize{true};
|
||||
};
|
||||
|
||||
struct RitzValueMeasurement final {
|
||||
double realPart{0.0};
|
||||
double imaginaryPart{0.0};
|
||||
double magnitude{0.0};
|
||||
double distanceFromOne{0.0};
|
||||
double residualEstimate{0.0};
|
||||
double relativeResidualEstimate{0.0};
|
||||
bool converged{false};
|
||||
};
|
||||
|
||||
enum class RitzValueOrdering { closest_to_zero, farthest_from_one, smallest_real_part, largest_magnitude };
|
||||
|
||||
struct ArnoldiSpectralMeasurement final {
|
||||
int requestedDimension{0};
|
||||
int achievedDimension{0};
|
||||
bool invariantSubspaceFound{false};
|
||||
std::uint64_t operatorApplications{0};
|
||||
double operatorApplicationSecondsMaximumRank{0.0};
|
||||
double operatorMaximumApplicationSecondsMaximumRank{0.0};
|
||||
double measurementSecondsMaximumRank{0.0};
|
||||
double nonApplicationSecondsMaximumRank{0.0};
|
||||
int convergedRitzValueCount{0};
|
||||
int negativeRealPartCount{0};
|
||||
|
||||
double projectedLargestSingularValue{0.0};
|
||||
double projectedSmallestSingularValue{0.0};
|
||||
double projectedConditionProxy{0.0};
|
||||
double centroidRealPart{0.0};
|
||||
double centroidImaginaryPart{0.0};
|
||||
double rmsDistanceFromOne{0.0};
|
||||
double rmsClusterRadius{0.0};
|
||||
double minimumMagnitude{0.0};
|
||||
double maximumMagnitude{0.0};
|
||||
double minimumRealPart{0.0};
|
||||
double maximumRealPart{0.0};
|
||||
double maximumAbsoluteImaginaryPart{0.0};
|
||||
double conjugatePairDefect{0.0};
|
||||
double projectedDepartureFromNormality{0.0};
|
||||
double projectedFieldOfValuesMinimumRealPart{0.0};
|
||||
double projectedFieldOfValuesMaximumRealPart{0.0};
|
||||
|
||||
std::vector<RitzValueMeasurement> ritzValues;
|
||||
};
|
||||
|
||||
[[nodiscard]] ArnoldiSpectralMeasurement measureArnoldiSpectrum(
|
||||
const mfem::Operator &operation,
|
||||
const mfem::Vector &initialDirection,
|
||||
MPI_Comm communicator,
|
||||
const ArnoldiOptions &options = {}
|
||||
);
|
||||
|
||||
[[nodiscard]] std::vector<RitzValueMeasurement> selectRitzValues(
|
||||
const ArnoldiSpectralMeasurement &measurement,
|
||||
RitzValueOrdering ordering,
|
||||
int count
|
||||
);
|
||||
} // namespace mean_field::solver
|
||||
@@ -15,7 +15,7 @@ export namespace mean_field::surface {
|
||||
class CompiledPressureSurfaceConstraint final {
|
||||
public:
|
||||
using PhysicalCondition = ConstantPressureSurface;
|
||||
using PhysicalQuantity = eos::quantity::Pressure;
|
||||
using PhysicalQuantity = dimensions::quantity::Pressure;
|
||||
using CarrierQuantity = typename Formulation::CarrierQuantity;
|
||||
using CarrierField = typename Formulation::CarrierField;
|
||||
using Relation = SelectedRelation;
|
||||
@@ -32,7 +32,7 @@ export namespace mean_field::surface {
|
||||
) {
|
||||
}
|
||||
|
||||
[[nodiscard]] eos::PressureValue targetPressure() const noexcept {
|
||||
[[nodiscard]] dimensions::PressureValue targetPressure() const noexcept {
|
||||
return m_condition.targetPressure();
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ module;
|
||||
|
||||
export module mean_field:surface.constant;
|
||||
|
||||
export import :eos.quantities;
|
||||
export import :dimensions.quantities;
|
||||
|
||||
export namespace mean_field::surface {
|
||||
struct PressureSurfaceDescriptor final {
|
||||
@@ -22,8 +22,15 @@ export namespace mean_field::surface {
|
||||
*/
|
||||
class ConstantPressureSurface final {
|
||||
public:
|
||||
using PhysicalQuantity = eos::quantity::Pressure;
|
||||
using TargetValue = eos::PressureValue;
|
||||
struct Parameters final {
|
||||
dimensions::PressureValue Psurf;
|
||||
};
|
||||
|
||||
using PhysicalQuantity = dimensions::quantity::Pressure;
|
||||
using TargetValue = dimensions::PressureValue;
|
||||
|
||||
explicit ConstantPressureSurface(const Parameters parameters) : ConstantPressureSurface(parameters.Psurf) {
|
||||
}
|
||||
|
||||
explicit ConstantPressureSurface(const TargetValue targetPressure) : m_targetPressure(targetPressure) {
|
||||
if (!std::isfinite(targetPressure.value())) {
|
||||
|
||||
@@ -6,6 +6,8 @@ module;
|
||||
#include <type_traits>
|
||||
export module mean_field:utils.blocks;
|
||||
|
||||
export import :model.specifications;
|
||||
|
||||
export namespace mean_field::utils::blocks {
|
||||
inline constexpr int dynamic_block_size = -1;
|
||||
|
||||
@@ -19,6 +21,18 @@ export namespace mean_field::utils::blocks {
|
||||
static constexpr int static_block_size = dynamic_block_size;
|
||||
};
|
||||
|
||||
template <typename GeneratedValue> struct generated_value_block final : value_block_base {
|
||||
using GeneratedType = GeneratedValue;
|
||||
|
||||
static constexpr int static_block_size = static_cast<int>(GeneratedValue::scalarArity);
|
||||
};
|
||||
|
||||
template <typename GeneratedResidual> struct generated_residual_block final : residual_block_base {
|
||||
using GeneratedType = GeneratedResidual;
|
||||
|
||||
static constexpr int static_block_size = static_cast<int>(GeneratedResidual::scalarArity);
|
||||
};
|
||||
|
||||
struct term { };
|
||||
struct field { };
|
||||
|
||||
@@ -96,25 +110,44 @@ export namespace mean_field::utils::blocks {
|
||||
static inline constexpr specific specific_term{};
|
||||
};
|
||||
|
||||
struct barotropic_constant final : field {
|
||||
struct mass_normalization final : term {
|
||||
struct value final : value_block_base {
|
||||
static constexpr int static_block_size = 1;
|
||||
};
|
||||
struct fixed_total_mass final : field {
|
||||
using SpecificationType = models::FixedTotalMass;
|
||||
using MultiplierType = models::MultiplierFor<SpecificationType>;
|
||||
using ResidualType = models::ResidualFor<SpecificationType>;
|
||||
|
||||
struct residual final : residual_block_base {
|
||||
static constexpr int static_block_size = 1;
|
||||
};
|
||||
struct mass_normalization final : term {
|
||||
using value = generated_value_block<MultiplierType>;
|
||||
using residual = generated_residual_block<ResidualType>;
|
||||
};
|
||||
|
||||
static inline constexpr mass_normalization mass_normalization_term{};
|
||||
};
|
||||
|
||||
struct fixed_central_density final : field {
|
||||
using SpecificationType = models::FixedCentralDensity;
|
||||
using BorderType = models::BorderFor<SpecificationType>;
|
||||
using ResidualType = models::ResidualFor<SpecificationType>;
|
||||
|
||||
struct central_value final : term {
|
||||
using value = generated_value_block<BorderType>;
|
||||
using residual = generated_residual_block<ResidualType>;
|
||||
};
|
||||
|
||||
static inline constexpr central_value central_value_term{};
|
||||
};
|
||||
|
||||
// Compatibility name for the current barotropic formulation. The scalar
|
||||
// is generated by FixedTotalMass; its realization in this formulation is
|
||||
// the historical C coordinate.
|
||||
using barotropic_constant = fixed_total_mass;
|
||||
|
||||
inline constexpr density density_field{};
|
||||
inline constexpr displacement displacement_field{};
|
||||
inline constexpr surface_deformation surface_deformation_field{};
|
||||
inline constexpr gravity gravity_field{};
|
||||
inline constexpr enthalpy enthalpy_field{};
|
||||
inline constexpr fixed_total_mass fixed_total_mass_constraint{};
|
||||
inline constexpr fixed_central_density fixed_central_density_phase{};
|
||||
inline constexpr barotropic_constant barotropic_constant_field{};
|
||||
|
||||
template <typename... Types> struct type_list {
|
||||
@@ -489,6 +522,61 @@ export namespace mean_field::utils::blocks {
|
||||
density::mass::value,
|
||||
surface_deformation::parameters::value>>;
|
||||
|
||||
// Bordered n=3 family closure. The original stellar coordinates remain a
|
||||
// contiguous prefix and the phase border and row are appended last.
|
||||
using central_density_bordered_stellar_equilibrium_form = block_form<
|
||||
type_list<
|
||||
density::mass::value,
|
||||
surface_deformation::parameters::value,
|
||||
gravity::gradient::value,
|
||||
gravity::poisson::value,
|
||||
enthalpy::specific::value,
|
||||
barotropic_constant::mass_normalization::value,
|
||||
fixed_central_density::central_value::value>,
|
||||
type_list<
|
||||
gravity::gradient::residual,
|
||||
gravity::poisson::residual,
|
||||
density::mass::residual,
|
||||
surface_deformation::shape_equilibrium::residual,
|
||||
enthalpy::specific::residual,
|
||||
barotropic_constant::mass_normalization::residual,
|
||||
fixed_central_density::central_value::residual>>;
|
||||
|
||||
using central_density_bordered_stellar_equilibrium_jacobian_form = type_list<
|
||||
block_row<
|
||||
gravity::gradient::residual,
|
||||
gravity::gradient::value,
|
||||
gravity::poisson::value,
|
||||
surface_deformation::parameters::value>,
|
||||
block_row<
|
||||
gravity::poisson::residual,
|
||||
gravity::gradient::value,
|
||||
density::mass::value,
|
||||
surface_deformation::parameters::value>,
|
||||
block_row<
|
||||
density::mass::residual,
|
||||
density::mass::value,
|
||||
enthalpy::specific::value,
|
||||
surface_deformation::parameters::value>,
|
||||
block_row<
|
||||
surface_deformation::shape_equilibrium::residual,
|
||||
density::mass::value,
|
||||
surface_deformation::parameters::value,
|
||||
gravity::gradient::value,
|
||||
enthalpy::specific::value>,
|
||||
block_row<
|
||||
enthalpy::specific::residual,
|
||||
enthalpy::specific::value,
|
||||
gravity::poisson::value,
|
||||
surface_deformation::parameters::value,
|
||||
barotropic_constant::mass_normalization::value,
|
||||
fixed_central_density::central_value::value>,
|
||||
block_row<
|
||||
barotropic_constant::mass_normalization::residual,
|
||||
density::mass::value,
|
||||
surface_deformation::parameters::value>,
|
||||
block_row<fixed_central_density::central_value::residual, enthalpy::specific::value>>;
|
||||
|
||||
// Columns: [d, h]
|
||||
// Rows: [R_d]
|
||||
using pressure_force_form = block_form<
|
||||
@@ -509,4 +597,8 @@ export namespace mean_field::utils::blocks {
|
||||
static_assert(valid_jacobian_form<
|
||||
surface_deformed_stellar_equilibrium_form,
|
||||
surface_deformed_stellar_equilibrium_jacobian_form>);
|
||||
|
||||
static_assert(valid_jacobian_form<
|
||||
central_density_bordered_stellar_equilibrium_form,
|
||||
central_density_bordered_stellar_equilibrium_jacobian_form>);
|
||||
} // namespace mean_field::utils::blocks
|
||||
|
||||
Reference in New Issue
Block a user