feat(newton): first newton solver implementation
This commit is contained in:
@@ -15,34 +15,28 @@ import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace {
|
||||
namespace blocks = mean_field::utils::blocks;
|
||||
namespace blocks = mean_field::utils::blocks;
|
||||
namespace normalization = mean_field::normalization;
|
||||
namespace models = mean_field::models;
|
||||
namespace models = mean_field::models;
|
||||
|
||||
struct ModelWithoutFixedTotalMass final { };
|
||||
|
||||
template <typename Model>
|
||||
concept SupportsModelDerivedStellarScales = requires(
|
||||
const normalization::PhysicalRieszDiagonal<> &policy,
|
||||
const Model &model
|
||||
) {
|
||||
{
|
||||
normalization::deriveStellarCharacteristicScales(policy, model)
|
||||
} -> std::same_as<normalization::StellarCharacteristicScales>;
|
||||
};
|
||||
concept SupportsModelDerivedStellarScales =
|
||||
requires(const normalization::PhysicalRieszDiagonal<> &policy, const Model &model) {
|
||||
{
|
||||
normalization::deriveStellarCharacteristicScales(policy, model)
|
||||
} -> std::same_as<normalization::StellarCharacteristicScales>;
|
||||
};
|
||||
|
||||
struct TestValue final : blocks::value_block_base { };
|
||||
struct TestResidual final : blocks::residual_block_base { };
|
||||
using TestForm = blocks::block_form<
|
||||
blocks::type_list<TestValue>,
|
||||
blocks::type_list<TestResidual>>;
|
||||
using TestForm = blocks::block_form<blocks::type_list<TestValue>, blocks::type_list<TestResidual>>;
|
||||
|
||||
using GlobalSpecificEnergyNormalization = models::CoordinateNormalization<
|
||||
models::RieszTopology::global_scalar,
|
||||
models::PhysicalScaleLaw::specific_energy>;
|
||||
using VolumeSpecificEnergyNormalization = models::CoordinateNormalization<
|
||||
models::RieszTopology::scalar_volume_l2,
|
||||
models::PhysicalScaleLaw::specific_energy>;
|
||||
using GlobalSpecificEnergyNormalization = models::
|
||||
CoordinateNormalization<models::RieszTopology::global_scalar, models::PhysicalScaleLaw::specific_energy>;
|
||||
using VolumeSpecificEnergyNormalization = models::
|
||||
CoordinateNormalization<models::RieszTopology::scalar_volume_l2, models::PhysicalScaleLaw::specific_energy>;
|
||||
|
||||
class SelfDescribingMagneticSpecificEnergy final {
|
||||
public:
|
||||
@@ -98,9 +92,7 @@ namespace {
|
||||
"NormalizationMockVolumeCoordinate",
|
||||
models::DependsOn<blocks::density::mass::value>,
|
||||
models::Affects<blocks::enthalpy::specific::residual>,
|
||||
models::GeneratedNormalization<
|
||||
VolumeSpecificEnergyNormalization,
|
||||
GlobalSpecificEnergyNormalization>>;
|
||||
models::GeneratedNormalization<VolumeSpecificEnergyNormalization, GlobalSpecificEnergyNormalization>>;
|
||||
|
||||
explicit constexpr GeneratedVolumeCoordinateWithoutMetricSource(const Parameters parameters) noexcept
|
||||
: m_target(parameters.target) {
|
||||
@@ -134,39 +126,36 @@ namespace {
|
||||
};
|
||||
|
||||
template <typename Specification>
|
||||
using GeneratedValueBlock = blocks::generated_value_block<
|
||||
models::PhysicalCoordinateFor<Specification>>;
|
||||
using GeneratedValueBlock = blocks::generated_value_block<models::PhysicalCoordinateFor<Specification>>;
|
||||
|
||||
template <typename Specification>
|
||||
using GeneratedMultiplierBlock = blocks::generated_value_block<
|
||||
models::MultiplierFor<Specification>>;
|
||||
using GeneratedMultiplierBlock = blocks::generated_value_block<models::MultiplierFor<Specification>>;
|
||||
|
||||
template <typename Specification>
|
||||
using GeneratedResidualBlock = blocks::generated_residual_block<
|
||||
models::ResidualFor<Specification>>;
|
||||
using GeneratedResidualBlock = blocks::generated_residual_block<models::ResidualFor<Specification>>;
|
||||
|
||||
using SelfDescribingValue = GeneratedValueBlock<SelfDescribingMagneticSpecificEnergy>;
|
||||
using SelfDescribingValue = GeneratedValueBlock<SelfDescribingMagneticSpecificEnergy>;
|
||||
using SelfDescribingResidual = GeneratedResidualBlock<SelfDescribingMagneticSpecificEnergy>;
|
||||
using SelfDescribingForm = blocks::block_form<
|
||||
blocks::type_list<SelfDescribingValue>,
|
||||
blocks::type_list<SelfDescribingResidual>>;
|
||||
using SelfDescribingForm =
|
||||
blocks::block_form<blocks::type_list<SelfDescribingValue>, blocks::type_list<SelfDescribingResidual>>;
|
||||
|
||||
using MissingValue = GeneratedMultiplierBlock<MissingGeneratedNormalization>;
|
||||
using MissingValue = GeneratedMultiplierBlock<MissingGeneratedNormalization>;
|
||||
using MissingResidual = GeneratedResidualBlock<MissingGeneratedNormalization>;
|
||||
using MissingNormalizationForm = blocks::block_form<
|
||||
blocks::type_list<MissingValue>,
|
||||
blocks::type_list<MissingResidual>>;
|
||||
using MissingNormalizationForm =
|
||||
blocks::block_form<blocks::type_list<MissingValue>, blocks::type_list<MissingResidual>>;
|
||||
|
||||
using UnpreparedVolumeValue = GeneratedValueBlock<GeneratedVolumeCoordinateWithoutMetricSource>;
|
||||
using UnpreparedVolumeValue = GeneratedValueBlock<GeneratedVolumeCoordinateWithoutMetricSource>;
|
||||
using UnpreparedVolumeResidual = GeneratedResidualBlock<GeneratedVolumeCoordinateWithoutMetricSource>;
|
||||
using UnpreparedVolumeForm = blocks::block_form<
|
||||
blocks::type_list<UnpreparedVolumeValue>,
|
||||
blocks::type_list<UnpreparedVolumeResidual>>;
|
||||
using UnpreparedVolumeForm =
|
||||
blocks::block_form<blocks::type_list<UnpreparedVolumeValue>, blocks::type_list<UnpreparedVolumeResidual>>;
|
||||
|
||||
class DenseOperator final : public mfem::Operator {
|
||||
public:
|
||||
explicit DenseOperator(const mfem::DenseMatrix &matrix)
|
||||
: mfem::Operator(matrix.Height(), matrix.Width()),
|
||||
: mfem::Operator(
|
||||
matrix.Height(),
|
||||
matrix.Width()
|
||||
),
|
||||
m_matrix(matrix) {
|
||||
}
|
||||
|
||||
@@ -184,7 +173,10 @@ namespace {
|
||||
class DenseInverseSolver final : public mfem::Solver {
|
||||
public:
|
||||
explicit DenseInverseSolver(const mfem::DenseMatrix &inverse)
|
||||
: mfem::Solver(inverse.Height(), inverse.Width()),
|
||||
: mfem::Solver(
|
||||
inverse.Height(),
|
||||
inverse.Width()
|
||||
),
|
||||
m_inverse(inverse) {
|
||||
}
|
||||
|
||||
@@ -247,75 +239,54 @@ TEST_CASE(
|
||||
) {
|
||||
using Map = normalization::DiagonalNormalization;
|
||||
|
||||
STATIC_CHECK(std::constructible_from<
|
||||
normalization::ScaledJacobianOperator,
|
||||
const DenseOperator &,
|
||||
const Map &>);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<
|
||||
normalization::ScaledJacobianOperator,
|
||||
DenseOperator &&,
|
||||
const Map &>);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<
|
||||
normalization::ScaledJacobianOperator,
|
||||
const DenseOperator &&,
|
||||
const Map &>);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<
|
||||
normalization::ScaledJacobianOperator,
|
||||
const DenseOperator &,
|
||||
Map &&>);
|
||||
STATIC_CHECK(std::constructible_from<normalization::ScaledJacobianOperator, const DenseOperator &, const Map &>);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<normalization::ScaledJacobianOperator, DenseOperator &&, const Map &>);
|
||||
STATIC_CHECK_FALSE(
|
||||
std::constructible_from<normalization::ScaledJacobianOperator, const DenseOperator &&, const Map &>
|
||||
);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<normalization::ScaledJacobianOperator, const DenseOperator &, Map &&>);
|
||||
|
||||
STATIC_CHECK(std::constructible_from<
|
||||
normalization::ScaledInverseOperator,
|
||||
const DenseOperator &,
|
||||
const Map &>);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<
|
||||
normalization::ScaledInverseOperator,
|
||||
DenseOperator &&,
|
||||
const Map &>);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<
|
||||
normalization::ScaledInverseOperator,
|
||||
const DenseOperator &,
|
||||
Map &&>);
|
||||
STATIC_CHECK(std::constructible_from<normalization::ScaledInverseOperator, const DenseOperator &, const Map &>);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<normalization::ScaledInverseOperator, DenseOperator &&, const Map &>);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<normalization::ScaledInverseOperator, const DenseOperator &, Map &&>);
|
||||
|
||||
STATIC_CHECK(std::constructible_from<
|
||||
normalization::ScaledPreconditioner,
|
||||
DenseInverseSolver &,
|
||||
const DenseOperator &,
|
||||
const DenseOperator &,
|
||||
const Map &>);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<
|
||||
normalization::ScaledPreconditioner,
|
||||
DenseInverseSolver &&,
|
||||
const DenseOperator &,
|
||||
const DenseOperator &,
|
||||
const Map &>);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<
|
||||
normalization::ScaledPreconditioner,
|
||||
DenseInverseSolver &,
|
||||
DenseOperator &&,
|
||||
const DenseOperator &,
|
||||
const Map &>);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<
|
||||
normalization::ScaledPreconditioner,
|
||||
DenseInverseSolver &,
|
||||
const DenseOperator &,
|
||||
DenseOperator &&,
|
||||
const Map &>);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<
|
||||
normalization::ScaledPreconditioner,
|
||||
DenseInverseSolver &,
|
||||
const DenseOperator &,
|
||||
const DenseOperator &,
|
||||
Map &&>);
|
||||
STATIC_CHECK(
|
||||
std::constructible_from<
|
||||
normalization::ScaledPreconditioner, DenseInverseSolver &, const DenseOperator &, const DenseOperator &,
|
||||
const Map &>
|
||||
);
|
||||
STATIC_CHECK_FALSE(
|
||||
std::constructible_from<
|
||||
normalization::ScaledPreconditioner, DenseInverseSolver &&, const DenseOperator &, const DenseOperator &,
|
||||
const Map &>
|
||||
);
|
||||
STATIC_CHECK_FALSE(
|
||||
std::constructible_from<
|
||||
normalization::ScaledPreconditioner, DenseInverseSolver &, DenseOperator &&, const DenseOperator &,
|
||||
const Map &>
|
||||
);
|
||||
STATIC_CHECK_FALSE(
|
||||
std::constructible_from<
|
||||
normalization::ScaledPreconditioner, DenseInverseSolver &, const DenseOperator &, DenseOperator &&,
|
||||
const Map &>
|
||||
);
|
||||
STATIC_CHECK_FALSE(
|
||||
std::constructible_from<
|
||||
normalization::ScaledPreconditioner, DenseInverseSolver &, const DenseOperator &, const DenseOperator &,
|
||||
Map &&>
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE("Characteristic Stellar Scales Satisfy Gravity Virial And Rotation Identities", "[normalization][physics]") {
|
||||
TEST_CASE(
|
||||
"Characteristic Stellar Scales Satisfy Gravity Virial And Rotation Identities",
|
||||
"[normalization][physics]"
|
||||
) {
|
||||
using namespace mean_field;
|
||||
|
||||
constexpr double mass = 7.0;
|
||||
constexpr double radius = 3.0;
|
||||
constexpr double mass = 7.0;
|
||||
constexpr double radius = 3.0;
|
||||
constexpr double gravity = 5.0;
|
||||
const auto scales = normalization::deriveStellarCharacteristicScales(
|
||||
const auto scales = normalization::deriveStellarCharacteristicScales(
|
||||
dimensions::MassValue{mass}, dimensions::LengthValue{radius}, gravity
|
||||
);
|
||||
|
||||
@@ -332,66 +303,87 @@ TEST_CASE("Characteristic Stellar Scales Satisfy Gravity Virial And Rotation Ide
|
||||
CHECK(virial == Catch::Approx(scales.force * radius).epsilon(2.0e-15));
|
||||
|
||||
// Omega_0 is the Kepler/break-up scale and J_0 = M R^2 Omega_0.
|
||||
CHECK(scales.angularVelocity * scales.angularVelocity * radius ==
|
||||
Catch::Approx(scales.acceleration).epsilon(2.0e-15));
|
||||
CHECK(scales.angularMomentum ==
|
||||
Catch::Approx(mass * radius * radius * scales.angularVelocity).epsilon(2.0e-15));
|
||||
CHECK(
|
||||
scales.angularVelocity * scales.angularVelocity * radius == Catch::Approx(scales.acceleration).epsilon(2.0e-15)
|
||||
);
|
||||
CHECK(scales.angularMomentum == Catch::Approx(mass * radius * radius * scales.angularVelocity).epsilon(2.0e-15));
|
||||
}
|
||||
|
||||
TEST_CASE("Characteristic Scales Obey The Expected Stellar Homology Exponents", "[normalization][physics]") {
|
||||
TEST_CASE(
|
||||
"Characteristic Scales Obey The Expected Stellar Homology Exponents",
|
||||
"[normalization][physics]"
|
||||
) {
|
||||
using namespace mean_field;
|
||||
|
||||
const auto reference = normalization::deriveStellarCharacteristicScales(
|
||||
dimensions::MassValue{2.5}, dimensions::LengthValue{4.0}, 3.0
|
||||
);
|
||||
constexpr double massFactor = 11.0;
|
||||
constexpr double radiusFactor = 0.2;
|
||||
const auto reference =
|
||||
normalization::deriveStellarCharacteristicScales(dimensions::MassValue{2.5}, dimensions::LengthValue{4.0}, 3.0);
|
||||
constexpr double massFactor = 11.0;
|
||||
constexpr double radiusFactor = 0.2;
|
||||
constexpr double gravityFactor = 7.0;
|
||||
const auto transformed = normalization::deriveStellarCharacteristicScales(
|
||||
dimensions::MassValue{2.5 * massFactor},
|
||||
dimensions::LengthValue{4.0 * radiusFactor},
|
||||
3.0 * gravityFactor
|
||||
const auto transformed = normalization::deriveStellarCharacteristicScales(
|
||||
dimensions::MassValue{2.5 * massFactor}, dimensions::LengthValue{4.0 * radiusFactor}, 3.0 * gravityFactor
|
||||
);
|
||||
|
||||
CHECK(transformed.density / reference.density ==
|
||||
Catch::Approx(massFactor / std::pow(radiusFactor, 3)).epsilon(4.0e-15));
|
||||
CHECK(transformed.acceleration / reference.acceleration ==
|
||||
Catch::Approx(gravityFactor * massFactor / std::pow(radiusFactor, 2)).epsilon(4.0e-15));
|
||||
CHECK(transformed.inverseTimeSquared / reference.inverseTimeSquared ==
|
||||
Catch::Approx(gravityFactor * massFactor / std::pow(radiusFactor, 3)).epsilon(4.0e-15));
|
||||
CHECK(transformed.specificEnergy / reference.specificEnergy ==
|
||||
Catch::Approx(gravityFactor * massFactor / radiusFactor).epsilon(4.0e-15));
|
||||
CHECK(transformed.pressure / reference.pressure ==
|
||||
Catch::Approx(gravityFactor * massFactor * massFactor / std::pow(radiusFactor, 4)).epsilon(4.0e-15));
|
||||
CHECK(transformed.angularVelocity / reference.angularVelocity == Catch::Approx(
|
||||
std::sqrt(gravityFactor * massFactor / std::pow(radiusFactor, 3))
|
||||
).epsilon(4.0e-15));
|
||||
CHECK(transformed.angularMomentum / reference.angularMomentum == Catch::Approx(
|
||||
massFactor * std::sqrt(gravityFactor * massFactor * radiusFactor)
|
||||
).epsilon(4.0e-15));
|
||||
CHECK(
|
||||
transformed.density / reference.density ==
|
||||
Catch::Approx(massFactor / std::pow(radiusFactor, 3)).epsilon(4.0e-15)
|
||||
);
|
||||
CHECK(
|
||||
transformed.acceleration / reference.acceleration ==
|
||||
Catch::Approx(gravityFactor * massFactor / std::pow(radiusFactor, 2)).epsilon(4.0e-15)
|
||||
);
|
||||
CHECK(
|
||||
transformed.inverseTimeSquared / reference.inverseTimeSquared ==
|
||||
Catch::Approx(gravityFactor * massFactor / std::pow(radiusFactor, 3)).epsilon(4.0e-15)
|
||||
);
|
||||
CHECK(
|
||||
transformed.specificEnergy / reference.specificEnergy ==
|
||||
Catch::Approx(gravityFactor * massFactor / radiusFactor).epsilon(4.0e-15)
|
||||
);
|
||||
CHECK(
|
||||
transformed.pressure / reference.pressure ==
|
||||
Catch::Approx(gravityFactor * massFactor * massFactor / std::pow(radiusFactor, 4)).epsilon(4.0e-15)
|
||||
);
|
||||
CHECK(
|
||||
transformed.angularVelocity / reference.angularVelocity ==
|
||||
Catch::Approx(std::sqrt(gravityFactor * massFactor / std::pow(radiusFactor, 3))).epsilon(4.0e-15)
|
||||
);
|
||||
CHECK(
|
||||
transformed.angularMomentum / reference.angularMomentum ==
|
||||
Catch::Approx(massFactor * std::sqrt(gravityFactor * massFactor * radiusFactor)).epsilon(4.0e-15)
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE("Physical Block Scales Distinguish Invariants From Numerical Phase Conditions", "[normalization][physics]") {
|
||||
TEST_CASE(
|
||||
"Physical Block Scales Distinguish Invariants From Numerical Phase Conditions",
|
||||
"[normalization][physics]"
|
||||
) {
|
||||
using namespace mean_field;
|
||||
|
||||
const auto scales = normalization::deriveStellarCharacteristicScales(
|
||||
dimensions::MassValue{9.0}, dimensions::LengthValue{2.0}, 4.0
|
||||
);
|
||||
const auto scales =
|
||||
normalization::deriveStellarCharacteristicScales(dimensions::MassValue{9.0}, dimensions::LengthValue{2.0}, 4.0);
|
||||
CHECK(normalization::physicalScale<blocks::density::mass::value>(scales) == scales.density);
|
||||
CHECK(normalization::physicalScale<blocks::gravity::gradient::value>(scales) == scales.acceleration);
|
||||
CHECK(normalization::physicalScale<blocks::gravity::poisson::residual>(scales) == scales.inverseTimeSquared);
|
||||
CHECK(normalization::physicalScale<blocks::fixed_total_mass::mass_normalization::residual>(scales) == 9.0);
|
||||
CHECK(normalization::physicalScale<blocks::fixed_angular_momentum::angular_velocity::value>(scales) ==
|
||||
scales.angularVelocity);
|
||||
CHECK(normalization::physicalScale<blocks::fixed_angular_momentum::angular_velocity::residual>(scales) ==
|
||||
scales.angularMomentum);
|
||||
CHECK(
|
||||
normalization::physicalScale<blocks::fixed_angular_momentum::angular_velocity::value>(scales) ==
|
||||
scales.angularVelocity
|
||||
);
|
||||
CHECK(
|
||||
normalization::physicalScale<blocks::fixed_angular_momentum::angular_velocity::residual>(scales) ==
|
||||
scales.angularMomentum
|
||||
);
|
||||
|
||||
// The central-density condition is implemented as h(0)-h_target, so its residual scale is energy/mass,
|
||||
// despite the physical target being expressed as a density.
|
||||
CHECK(normalization::physicalScale<blocks::fixed_central_density::central_value::residual>(scales) ==
|
||||
scales.specificEnergy);
|
||||
CHECK(normalization::physicalScale<blocks::fixed_central_density::central_value::residual>(scales) !=
|
||||
scales.density);
|
||||
CHECK(
|
||||
normalization::physicalScale<blocks::fixed_central_density::central_value::residual>(scales) ==
|
||||
scales.specificEnergy
|
||||
);
|
||||
CHECK(
|
||||
normalization::physicalScale<blocks::fixed_central_density::central_value::residual>(scales) != scales.density
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
@@ -399,39 +391,40 @@ TEST_CASE(
|
||||
"[normalization][type][extension]"
|
||||
) {
|
||||
using namespace mean_field;
|
||||
using ValueTraits = normalization::PhysicalRieszBlockTraits<SelfDescribingValue>;
|
||||
using ValueTraits = normalization::PhysicalRieszBlockTraits<SelfDescribingValue>;
|
||||
using ResidualTraits = normalization::PhysicalRieszBlockTraits<SelfDescribingResidual>;
|
||||
|
||||
STATIC_CHECK(models::SelfDescribingModelSpecification<SelfDescribingMagneticSpecificEnergy>);
|
||||
STATIC_CHECK(models::CompleteGeneratedNormalizationFor<SelfDescribingMagneticSpecificEnergy>);
|
||||
STATIC_CHECK(operators::StellarEquilibriumSpecificationCompilable<SelfDescribingMagneticSpecificEnergy>);
|
||||
STATIC_CHECK(normalization::GeneratedValuePhysicalRieszNormalizable<
|
||||
models::PhysicalCoordinateFor<SelfDescribingMagneticSpecificEnergy>>);
|
||||
STATIC_CHECK(normalization::GeneratedResidualPhysicalRieszNormalizable<
|
||||
models::ResidualFor<SelfDescribingMagneticSpecificEnergy>>);
|
||||
STATIC_CHECK(normalization::CompleteGeneratedPhysicalRieszNormalizationFor<
|
||||
SelfDescribingMagneticSpecificEnergy>);
|
||||
STATIC_CHECK(normalization::CompilableNormalizationFor<
|
||||
normalization::PhysicalRieszDiagonal<>,
|
||||
SelfDescribingForm>);
|
||||
STATIC_CHECK(normalization::RegisteredStellarSpecificationNormalization<
|
||||
SelfDescribingMagneticSpecificEnergy>);
|
||||
STATIC_CHECK(normalization::CompleteStellarSpecificationNormalizationFor<
|
||||
SelfDescribingMagneticSpecificEnergy,
|
||||
SelfDescribingForm>);
|
||||
STATIC_CHECK(
|
||||
normalization::GeneratedValuePhysicalRieszNormalizable<
|
||||
models::PhysicalCoordinateFor<SelfDescribingMagneticSpecificEnergy>>
|
||||
);
|
||||
STATIC_CHECK(
|
||||
normalization::GeneratedResidualPhysicalRieszNormalizable<
|
||||
models::ResidualFor<SelfDescribingMagneticSpecificEnergy>>
|
||||
);
|
||||
STATIC_CHECK(normalization::CompleteGeneratedPhysicalRieszNormalizationFor<SelfDescribingMagneticSpecificEnergy>);
|
||||
STATIC_CHECK(normalization::CompilableNormalizationFor<normalization::PhysicalRieszDiagonal<>, SelfDescribingForm>);
|
||||
STATIC_CHECK(normalization::RegisteredStellarSpecificationNormalization<SelfDescribingMagneticSpecificEnergy>);
|
||||
STATIC_CHECK(
|
||||
normalization::CompleteStellarSpecificationNormalizationFor<
|
||||
SelfDescribingMagneticSpecificEnergy, SelfDescribingForm>
|
||||
);
|
||||
|
||||
STATIC_CHECK(ValueTraits::Method::topology == normalization::RieszTopology::global_scalar);
|
||||
STATIC_CHECK(ValueTraits::Method::scale == normalization::PhysicalScaleKind::dimensionless);
|
||||
STATIC_CHECK(ResidualTraits::Method::topology == normalization::RieszTopology::global_scalar);
|
||||
STATIC_CHECK(ResidualTraits::Method::scale == normalization::PhysicalScaleKind::specific_energy);
|
||||
|
||||
const auto scales = normalization::deriveStellarCharacteristicScales(
|
||||
dimensions::MassValue{9.0}, dimensions::LengthValue{2.0}, 4.0
|
||||
);
|
||||
const auto scales =
|
||||
normalization::deriveStellarCharacteristicScales(dimensions::MassValue{9.0}, dimensions::LengthValue{2.0}, 4.0);
|
||||
const blocks::form_layout<SelfDescribingForm> layout({1}, {1});
|
||||
normalization::DiagonalNormalizationBuilder<SelfDescribingForm> builder(layout);
|
||||
normalization::StellarSpecificationNormalizationContribution<
|
||||
SelfDescribingMagneticSpecificEnergy>::Apply(builder, scales);
|
||||
normalization::StellarSpecificationNormalizationContribution<SelfDescribingMagneticSpecificEnergy>::Apply(
|
||||
builder, scales
|
||||
);
|
||||
const normalization::DiagonalNormalization map = std::move(builder).Build();
|
||||
|
||||
REQUIRE(map.StateFactors().Size() == 1);
|
||||
@@ -452,47 +445,51 @@ TEST_CASE(
|
||||
|
||||
STATIC_CHECK(models::ModelSpecification<MissingGeneratedNormalization>);
|
||||
STATIC_CHECK_FALSE(models::CompleteGeneratedNormalizationFor<MissingGeneratedNormalization>);
|
||||
STATIC_CHECK_FALSE(normalization::CompleteGeneratedPhysicalRieszNormalizationFor<
|
||||
MissingGeneratedNormalization>);
|
||||
STATIC_CHECK_FALSE(normalization::CompilableNormalizationFor<
|
||||
normalization::PhysicalRieszDiagonal<>,
|
||||
MissingNormalizationForm>);
|
||||
STATIC_CHECK_FALSE(normalization::RegisteredStellarSpecificationNormalization<
|
||||
MissingGeneratedNormalization>);
|
||||
STATIC_CHECK_FALSE(normalization::CompleteStellarSpecificationNormalizationFor<
|
||||
MissingGeneratedNormalization,
|
||||
MissingNormalizationForm>);
|
||||
STATIC_CHECK_FALSE(normalization::CompleteGeneratedPhysicalRieszNormalizationFor<MissingGeneratedNormalization>);
|
||||
STATIC_CHECK_FALSE(
|
||||
normalization::CompilableNormalizationFor<normalization::PhysicalRieszDiagonal<>, MissingNormalizationForm>
|
||||
);
|
||||
STATIC_CHECK_FALSE(normalization::RegisteredStellarSpecificationNormalization<MissingGeneratedNormalization>);
|
||||
STATIC_CHECK_FALSE(
|
||||
normalization::CompleteStellarSpecificationNormalizationFor<
|
||||
MissingGeneratedNormalization, MissingNormalizationForm>
|
||||
);
|
||||
|
||||
STATIC_CHECK(models::ModelSpecification<MalformedGeneratedNormalizationConstraint>);
|
||||
STATIC_CHECK_FALSE(models::CompleteGeneratedNormalizationFor<
|
||||
MalformedGeneratedNormalizationConstraint>);
|
||||
STATIC_CHECK_FALSE(normalization::CompleteGeneratedPhysicalRieszNormalizationFor<
|
||||
MalformedGeneratedNormalizationConstraint>);
|
||||
STATIC_CHECK_FALSE(normalization::RegisteredStellarSpecificationNormalization<
|
||||
MalformedGeneratedNormalizationConstraint>);
|
||||
STATIC_CHECK_FALSE(models::CompleteGeneratedNormalizationFor<MalformedGeneratedNormalizationConstraint>);
|
||||
STATIC_CHECK_FALSE(
|
||||
normalization::CompleteGeneratedPhysicalRieszNormalizationFor<MalformedGeneratedNormalizationConstraint>
|
||||
);
|
||||
STATIC_CHECK_FALSE(
|
||||
normalization::RegisteredStellarSpecificationNormalization<MalformedGeneratedNormalizationConstraint>
|
||||
);
|
||||
|
||||
// The declaration itself is a valid Riesz law, but runtime stellar
|
||||
// preparation has no finite-element Gram source for a generated volume
|
||||
// field. The stronger runtime concept must therefore reject it.
|
||||
STATIC_CHECK(normalization::CompleteGeneratedPhysicalRieszNormalizationFor<
|
||||
GeneratedVolumeCoordinateWithoutMetricSource>);
|
||||
STATIC_CHECK(normalization::CompilableNormalizationFor<
|
||||
normalization::PhysicalRieszDiagonal<>,
|
||||
UnpreparedVolumeForm>);
|
||||
STATIC_CHECK_FALSE(normalization::RegisteredStellarSpecificationNormalization<
|
||||
GeneratedVolumeCoordinateWithoutMetricSource>);
|
||||
STATIC_CHECK_FALSE(normalization::CompleteStellarSpecificationNormalizationFor<
|
||||
GeneratedVolumeCoordinateWithoutMetricSource,
|
||||
UnpreparedVolumeForm>);
|
||||
STATIC_CHECK(
|
||||
normalization::CompleteGeneratedPhysicalRieszNormalizationFor<GeneratedVolumeCoordinateWithoutMetricSource>
|
||||
);
|
||||
STATIC_CHECK(
|
||||
normalization::CompilableNormalizationFor<normalization::PhysicalRieszDiagonal<>, UnpreparedVolumeForm>
|
||||
);
|
||||
STATIC_CHECK_FALSE(
|
||||
normalization::RegisteredStellarSpecificationNormalization<GeneratedVolumeCoordinateWithoutMetricSource>
|
||||
);
|
||||
STATIC_CHECK_FALSE(
|
||||
normalization::CompleteStellarSpecificationNormalizationFor<
|
||||
GeneratedVolumeCoordinateWithoutMetricSource, UnpreparedVolumeForm>
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE("Characteristic Scale Construction Rejects Invalid Or Overflowing References", "[normalization][validation]") {
|
||||
TEST_CASE(
|
||||
"Characteristic Scale Construction Rejects Invalid Or Overflowing References",
|
||||
"[normalization][validation]"
|
||||
) {
|
||||
using namespace mean_field;
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
normalization::deriveStellarCharacteristicScales(
|
||||
dimensions::MassValue{0.0}, dimensions::LengthValue{1.0}, 1.0
|
||||
),
|
||||
normalization::deriveStellarCharacteristicScales(dimensions::MassValue{0.0}, dimensions::LengthValue{1.0}, 1.0),
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
@@ -502,8 +499,7 @@ TEST_CASE("Characteristic Scale Construction Rejects Invalid Or Overflowing Refe
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
(normalization::PhysicalRieszDiagonal{dimensions::LengthValue{1.0},
|
||||
std::numeric_limits<double>::quiet_NaN()}),
|
||||
(normalization::PhysicalRieszDiagonal{dimensions::LengthValue{1.0}, std::numeric_limits<double>::quiet_NaN()}),
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
@@ -514,25 +510,26 @@ TEST_CASE("Characteristic Scale Construction Rejects Invalid Or Overflowing Refe
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE("Diagonal Riesz Maps Reproduce Primal And Dual Norms Across Extreme Metrics", "[normalization][math]") {
|
||||
TEST_CASE(
|
||||
"Diagonal Riesz Maps Reproduce Primal And Dual Norms Across Extreme Metrics",
|
||||
"[normalization][math]"
|
||||
) {
|
||||
const blocks::form_layout<TestForm> layout({3}, {3});
|
||||
normalization::DiagonalNormalizationBuilder<TestForm> builder(layout);
|
||||
const mfem::Vector gram = vector({1.0e-20, 4.0, 9.0e20});
|
||||
constexpr double stateScale = 10.0;
|
||||
const mfem::Vector gram = vector({1.0e-20, 4.0, 9.0e20});
|
||||
constexpr double stateScale = 10.0;
|
||||
constexpr double residualScale = 0.25;
|
||||
builder.SetValueBlock<TestValue>(stateScale, gram);
|
||||
builder.SetResidualBlock<TestResidual>(residualScale, gram);
|
||||
const normalization::DiagonalNormalization map = std::move(builder).Build();
|
||||
|
||||
const mfem::Vector state = vector({3.0e10, -2.0, 4.0e-10});
|
||||
const mfem::Vector residual = vector({2.0e-10, -3.0, 5.0e10});
|
||||
double expectedPrimalNormSquared = 0.0;
|
||||
double expectedDualNormSquared = 0.0;
|
||||
const mfem::Vector state = vector({3.0e10, -2.0, 4.0e-10});
|
||||
const mfem::Vector residual = vector({2.0e-10, -3.0, 5.0e10});
|
||||
double expectedPrimalNormSquared = 0.0;
|
||||
double expectedDualNormSquared = 0.0;
|
||||
for (int index = 0; index < gram.Size(); ++index) {
|
||||
expectedPrimalNormSquared += gram(index) * state(index) * state(index) /
|
||||
(stateScale * stateScale);
|
||||
expectedDualNormSquared += residual(index) * residual(index) /
|
||||
(gram(index) * residualScale * residualScale);
|
||||
expectedPrimalNormSquared += gram(index) * state(index) * state(index) / (stateScale * stateScale);
|
||||
expectedDualNormSquared += residual(index) * residual(index) / (gram(index) * residualScale * residualScale);
|
||||
}
|
||||
CHECK(map.LocalStateNormSquared(state) == Catch::Approx(expectedPrimalNormSquared).epsilon(3.0e-15));
|
||||
CHECK(map.LocalResidualNormSquared(residual) == Catch::Approx(expectedDualNormSquared).epsilon(3.0e-15));
|
||||
@@ -549,10 +546,12 @@ TEST_CASE("Diagonal Riesz Maps Reproduce Primal And Dual Norms Across Extreme Me
|
||||
checkVector(recoveredResidual, residual, 3.0e-15);
|
||||
}
|
||||
|
||||
TEST_CASE("Hybrid Riesz Rows Replace Missing Volume Metrics With Point Metrics", "[normalization][math]") {
|
||||
TEST_CASE(
|
||||
"Hybrid Riesz Rows Replace Missing Volume Metrics With Point Metrics",
|
||||
"[normalization][math]"
|
||||
) {
|
||||
using HybridForm = blocks::block_form<
|
||||
blocks::type_list<blocks::enthalpy::specific::value>,
|
||||
blocks::type_list<blocks::enthalpy::specific::residual>>;
|
||||
blocks::type_list<blocks::enthalpy::specific::value>, blocks::type_list<blocks::enthalpy::specific::residual>>;
|
||||
const blocks::form_layout<HybridForm> layout({4}, {4});
|
||||
normalization::DiagonalNormalizationBuilder<HybridForm> builder(layout);
|
||||
builder.SetValueBlock<blocks::enthalpy::specific::value>(2.0, vector({2.0, 3.0, 5.0, 7.0}));
|
||||
@@ -580,7 +579,10 @@ TEST_CASE("Hybrid Riesz Rows Replace Missing Volume Metrics With Point Metrics",
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE("Runtime Normalization Assembly Rejects Missing Duplicate And Invalid Data", "[normalization][validation]") {
|
||||
TEST_CASE(
|
||||
"Runtime Normalization Assembly Rejects Missing Duplicate And Invalid Data",
|
||||
"[normalization][validation]"
|
||||
) {
|
||||
const blocks::form_layout<TestForm> layout({2}, {2});
|
||||
|
||||
normalization::DiagonalNormalizationBuilder<TestForm> missing(layout);
|
||||
@@ -603,8 +605,11 @@ TEST_CASE("Runtime Normalization Assembly Rejects Missing Duplicate And Invalid
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE("Scaled Jacobian And Inverse Implement The Exact Coordinate Change", "[normalization][linear-algebra]") {
|
||||
const mfem::Vector stateFactors = vector({1.0e-9, 2.0e7});
|
||||
TEST_CASE(
|
||||
"Scaled Jacobian And Inverse Implement The Exact Coordinate Change",
|
||||
"[normalization][linear-algebra]"
|
||||
) {
|
||||
const mfem::Vector stateFactors = vector({1.0e-9, 2.0e7});
|
||||
const mfem::Vector residualFactors = vector({5.0e8, 3.0e-6});
|
||||
const normalization::DiagonalNormalization map(stateFactors, residualFactors);
|
||||
|
||||
@@ -617,10 +622,8 @@ TEST_CASE("Scaled Jacobian And Inverse Implement The Exact Coordinate Change", "
|
||||
mfem::DenseMatrix inverse(2);
|
||||
for (int row = 0; row < 2; ++row) {
|
||||
for (int column = 0; column < 2; ++column) {
|
||||
matrix(row, column) = normalizedMatrix[row][column] * stateFactors(column) /
|
||||
residualFactors(row);
|
||||
inverse(row, column) = normalizedInverse[row][column] * residualFactors(column) /
|
||||
stateFactors(row);
|
||||
matrix(row, column) = normalizedMatrix[row][column] * stateFactors(column) / residualFactors(row);
|
||||
inverse(row, column) = normalizedInverse[row][column] * residualFactors(column) / stateFactors(row);
|
||||
}
|
||||
}
|
||||
const DenseOperator physicalJacobian(matrix);
|
||||
@@ -641,8 +644,11 @@ TEST_CASE("Scaled Jacobian And Inverse Implement The Exact Coordinate Change", "
|
||||
checkVector(recovered, direction, 2.0e-13);
|
||||
}
|
||||
|
||||
TEST_CASE("Scaled Preconditioning Routes An Exact Physical Inverse Through FGMRES", "[normalization][solver]") {
|
||||
const mfem::Vector stateFactors = vector({1.0e-9, 2.0e7});
|
||||
TEST_CASE(
|
||||
"Scaled Preconditioning Routes An Exact Physical Inverse Through FGMRES",
|
||||
"[normalization][solver]"
|
||||
) {
|
||||
const mfem::Vector stateFactors = vector({1.0e-9, 2.0e7});
|
||||
const mfem::Vector residualFactors = vector({5.0e8, 3.0e-6});
|
||||
const normalization::DiagonalNormalization map(stateFactors, residualFactors);
|
||||
|
||||
@@ -652,19 +658,16 @@ TEST_CASE("Scaled Preconditioning Routes An Exact Physical Inverse Through FGMRE
|
||||
mfem::DenseMatrix physicalInverseMatrix(2);
|
||||
for (int row = 0; row < 2; ++row) {
|
||||
for (int column = 0; column < 2; ++column) {
|
||||
physicalMatrix(row, column) = normalizedMatrix[row][column] * stateFactors(column) /
|
||||
residualFactors(row);
|
||||
physicalInverseMatrix(row, column) = normalizedInverse[row][column] * residualFactors(column) /
|
||||
stateFactors(row);
|
||||
physicalMatrix(row, column) = normalizedMatrix[row][column] * stateFactors(column) / residualFactors(row);
|
||||
physicalInverseMatrix(row, column) =
|
||||
normalizedInverse[row][column] * residualFactors(column) / stateFactors(row);
|
||||
}
|
||||
}
|
||||
|
||||
const DenseOperator physicalJacobian(physicalMatrix);
|
||||
const normalization::ScaledJacobianOperator scaledJacobian(physicalJacobian, map);
|
||||
DenseInverseSolver physicalInverse(physicalInverseMatrix);
|
||||
normalization::ScaledPreconditioner scaledPreconditioner(
|
||||
physicalInverse, physicalJacobian, scaledJacobian, map
|
||||
);
|
||||
normalization::ScaledPreconditioner scaledPreconditioner(physicalInverse, physicalJacobian, scaledJacobian, map);
|
||||
|
||||
CHECK(physicalInverse.BoundOperator() == &physicalJacobian);
|
||||
CHECK(&scaledPreconditioner.GetPhysicalJacobian() == &physicalJacobian);
|
||||
@@ -697,18 +700,18 @@ TEST_CASE("Scaled Preconditioning Routes An Exact Physical Inverse Through FGMRE
|
||||
CHECK(scaledPreconditioner.GetStatistics().applications >= 2);
|
||||
|
||||
mfem::IdentityOperator differentNormalizedJacobian(2);
|
||||
CHECK_THROWS_AS(
|
||||
scaledPreconditioner.SetOperator(differentNormalizedJacobian),
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(scaledPreconditioner.SetOperator(differentNormalizedJacobian), std::invalid_argument);
|
||||
mfem::IdentityOperator wrongSize(3);
|
||||
CHECK_THROWS_AS(scaledPreconditioner.SetOperator(wrongSize), std::invalid_argument);
|
||||
mfem::Vector wrongCorrection(1);
|
||||
CHECK_THROWS_AS(scaledPreconditioner.Mult(rightHandSide, wrongCorrection), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST_CASE("Physical Riesz Scaling Collapses A Forty-Eight-Decade Diagonal Imbalance", "[normalization][numerics]") {
|
||||
const mfem::Vector stateFactors = vector({1.0e-12, 1.0, 1.0e12});
|
||||
TEST_CASE(
|
||||
"Physical Riesz Scaling Collapses A Forty-Eight-Decade Diagonal Imbalance",
|
||||
"[normalization][numerics]"
|
||||
) {
|
||||
const mfem::Vector stateFactors = vector({1.0e-12, 1.0, 1.0e12});
|
||||
const mfem::Vector residualFactors = vector({1.0e12, 1.0, 1.0e-12});
|
||||
const normalization::DiagonalNormalization map(stateFactors, residualFactors);
|
||||
|
||||
@@ -727,38 +730,38 @@ TEST_CASE("Physical Riesz Scaling Collapses A Forty-Eight-Decade Diagonal Imbala
|
||||
checkVector(action, direction, 4.0e-15);
|
||||
}
|
||||
|
||||
TEST_CASE("A Compiled Stellar Problem Prepares Reference Physical Riesz Coordinates", "[normalization][integration]") {
|
||||
TEST_CASE(
|
||||
"A Compiled Stellar Problem Prepares Reference Physical Riesz Coordinates",
|
||||
"[normalization][integration]"
|
||||
) {
|
||||
using namespace mean_field;
|
||||
|
||||
utils::Args args = test_utils::setup_args();
|
||||
utils::Args args = test_utils::setup_args();
|
||||
fem::FEM finiteElements = fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(finiteElements.okay());
|
||||
|
||||
constexpr double targetMass = 2.0;
|
||||
constexpr double referenceRadius = 1.25;
|
||||
constexpr double targetMass = 2.0;
|
||||
constexpr double referenceRadius = 1.25;
|
||||
constexpr double gravitationalConstant = 3.0;
|
||||
const normalization::PhysicalRieszDiagonal policy{
|
||||
dimensions::LengthValue{referenceRadius}, gravitationalConstant
|
||||
};
|
||||
const auto discretization = equilibrium::makeStellarDiscretization(finiteElements, policy);
|
||||
auto problem = equilibrium::discretize(
|
||||
const normalization::PhysicalRieszDiagonal policy{dimensions::LengthValue{referenceRadius}, gravitationalConstant};
|
||||
auto discretization = equilibrium::makeStellarDiscretization(std::move(finiteElements), policy);
|
||||
auto problem = equilibrium::discretize(
|
||||
model::StellarModel(
|
||||
eos::Polytrope({.n = 3.0, .K = 0.25}),
|
||||
surface::Isobaric({.Psurf = dimensions::PressureValue{0.0}}),
|
||||
eos::Polytrope({.n = 3.0, .K = 0.25}), surface::Isobaric({.Psurf = dimensions::PressureValue{0.0}}),
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{targetMass}}),
|
||||
constraint::FixedCentralDensity({.RhoC = dimensions::DensityValue{1.0}})
|
||||
),
|
||||
discretization
|
||||
std::move(discretization)
|
||||
);
|
||||
|
||||
using ProblemType = std::remove_cvref_t<decltype(problem)>;
|
||||
using Form = typename ProblemType::FormType;
|
||||
using ModelType = std::remove_cvref_t<decltype(problem.GetStellarModel())>;
|
||||
using Form = typename ProblemType::FormType;
|
||||
using ModelType = std::remove_cvref_t<decltype(problem.GetStellarModel())>;
|
||||
STATIC_CHECK(SupportsModelDerivedStellarScales<ModelType>);
|
||||
STATIC_CHECK_FALSE(SupportsModelDerivedStellarScales<ModelWithoutFixedTotalMass>);
|
||||
STATIC_CHECK(std::same_as<
|
||||
typename ProblemType::NormalizationPrescriptionType,
|
||||
std::remove_cvref_t<decltype(policy)>>);
|
||||
STATIC_CHECK(
|
||||
std::same_as<typename ProblemType::NormalizationPrescriptionType, std::remove_cvref_t<decltype(policy)>>
|
||||
);
|
||||
CHECK(problem.GetNormalizationPrescription().referenceRadius() == dimensions::LengthValue{referenceRadius});
|
||||
|
||||
const normalization::DiagonalNormalization map = normalization::prepareNormalization(problem);
|
||||
@@ -773,39 +776,41 @@ TEST_CASE("A Compiled Stellar Problem Prepares Reference Physical Riesz Coordina
|
||||
CHECK(map.ResidualFactors()(index) > 0.0);
|
||||
}
|
||||
|
||||
const auto scales = normalization::deriveStellarCharacteristicScales(policy, problem.GetStellarModel());
|
||||
const auto scales = normalization::deriveStellarCharacteristicScales(policy, problem.GetStellarModel());
|
||||
const auto &layout = problem.GetManifest().layout();
|
||||
constexpr int massValueBlock = blocks::type_index_v<
|
||||
blocks::fixed_total_mass::mass_normalization::value,
|
||||
typename Form::value_blocks>;
|
||||
constexpr int massResidualBlock = blocks::type_index_v<
|
||||
blocks::fixed_total_mass::mass_normalization::residual,
|
||||
typename Form::residual_blocks>;
|
||||
constexpr int phaseValueBlock = blocks::type_index_v<
|
||||
blocks::fixed_central_density::central_value::value,
|
||||
typename Form::value_blocks>;
|
||||
constexpr int phaseResidualBlock = blocks::type_index_v<
|
||||
blocks::fixed_central_density::central_value::residual,
|
||||
typename Form::residual_blocks>;
|
||||
constexpr int enthalpyResidualBlock = blocks::type_index_v<
|
||||
blocks::enthalpy::specific::residual,
|
||||
typename Form::residual_blocks>;
|
||||
constexpr int massValueBlock =
|
||||
blocks::type_index_v<blocks::fixed_total_mass::mass_normalization::value, typename Form::value_blocks>;
|
||||
constexpr int massResidualBlock =
|
||||
blocks::type_index_v<blocks::fixed_total_mass::mass_normalization::residual, typename Form::residual_blocks>;
|
||||
constexpr int phaseValueBlock =
|
||||
blocks::type_index_v<blocks::fixed_central_density::central_value::value, typename Form::value_blocks>;
|
||||
constexpr int phaseResidualBlock =
|
||||
blocks::type_index_v<blocks::fixed_central_density::central_value::residual, typename Form::residual_blocks>;
|
||||
constexpr int enthalpyResidualBlock =
|
||||
blocks::type_index_v<blocks::enthalpy::specific::residual, typename Form::residual_blocks>;
|
||||
|
||||
CHECK(map.StateFactors()(layout.value_offsets()[massValueBlock]) ==
|
||||
Catch::Approx(1.0 / scales.specificEnergy).epsilon(2.0e-15));
|
||||
CHECK(map.ResidualFactors()(layout.residual_offsets()[massResidualBlock]) ==
|
||||
Catch::Approx(1.0 / targetMass).epsilon(2.0e-15));
|
||||
CHECK(map.StateFactors()(layout.value_offsets()[phaseValueBlock]) ==
|
||||
Catch::Approx(1.0 / scales.specificEnergy).epsilon(2.0e-15));
|
||||
CHECK(map.ResidualFactors()(layout.residual_offsets()[phaseResidualBlock]) ==
|
||||
Catch::Approx(1.0 / scales.specificEnergy).epsilon(2.0e-15));
|
||||
CHECK(
|
||||
map.StateFactors()(layout.value_offsets()[massValueBlock]) ==
|
||||
Catch::Approx(1.0 / scales.specificEnergy).epsilon(2.0e-15)
|
||||
);
|
||||
CHECK(
|
||||
map.ResidualFactors()(layout.residual_offsets()[massResidualBlock]) ==
|
||||
Catch::Approx(1.0 / targetMass).epsilon(2.0e-15)
|
||||
);
|
||||
CHECK(
|
||||
map.StateFactors()(layout.value_offsets()[phaseValueBlock]) ==
|
||||
Catch::Approx(1.0 / scales.specificEnergy).epsilon(2.0e-15)
|
||||
);
|
||||
CHECK(
|
||||
map.ResidualFactors()(layout.residual_offsets()[phaseResidualBlock]) ==
|
||||
Catch::Approx(1.0 / scales.specificEnergy).epsilon(2.0e-15)
|
||||
);
|
||||
|
||||
const auto &surfaceRows = problem.GetPressureSurfaceRows().reduced_dofs();
|
||||
REQUIRE(surfaceRows.Size() > 0);
|
||||
for (const int row : surfaceRows) {
|
||||
const int rootRow = layout.residual_offsets()[enthalpyResidualBlock] + row;
|
||||
CHECK(map.ResidualFactors()(rootRow) ==
|
||||
Catch::Approx(1.0 / scales.specificEnergy).epsilon(2.0e-15));
|
||||
CHECK(map.ResidualFactors()(rootRow) == Catch::Approx(1.0 / scales.specificEnergy).epsilon(2.0e-15));
|
||||
}
|
||||
|
||||
mfem::Vector physicalState(problem.StateSize());
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
import mean_field;
|
||||
|
||||
namespace {
|
||||
namespace blocks = mean_field::utils::blocks;
|
||||
namespace blocks = mean_field::utils::blocks;
|
||||
namespace normalization = mean_field::normalization;
|
||||
|
||||
using PhysicalForm = blocks::surface_deformed_stellar_equilibrium_form;
|
||||
using PhaseForm = blocks::central_density_bordered_stellar_equilibrium_form;
|
||||
using PhysicalPlan = normalization::PhysicalRieszNormalizationPlanFor<PhysicalForm>;
|
||||
using PhasePlan = normalization::PhysicalRieszNormalizationPlanFor<PhaseForm>;
|
||||
using PhysicalForm = blocks::surface_deformed_stellar_equilibrium_form;
|
||||
using PhaseForm = blocks::central_density_bordered_stellar_equilibrium_form;
|
||||
using PhysicalPlan = normalization::PhysicalRieszNormalizationPlanFor<PhysicalForm>;
|
||||
using PhasePlan = normalization::PhysicalRieszNormalizationPlanFor<PhaseForm>;
|
||||
|
||||
struct ValueA final : blocks::value_block_base { };
|
||||
struct ValueB final : blocks::value_block_base { };
|
||||
@@ -22,9 +22,7 @@ namespace {
|
||||
struct ForeignValue final : blocks::value_block_base { };
|
||||
struct ForeignResidual final : blocks::residual_block_base { };
|
||||
|
||||
using SmallForm = blocks::block_form<
|
||||
blocks::type_list<ValueA, ValueB>,
|
||||
blocks::type_list<ResidualA, ResidualB>>;
|
||||
using SmallForm = blocks::block_form<blocks::type_list<ValueA, ValueB>, blocks::type_list<ResidualA, ResidualB>>;
|
||||
using ValueAIdentity = normalization::CoordinateComponent<
|
||||
normalization::CoordinateKind::value,
|
||||
blocks::type_list<ValueA>,
|
||||
@@ -50,21 +48,11 @@ namespace {
|
||||
blocks::type_list<ForeignResidual>,
|
||||
normalization::IdentityCoordinate>;
|
||||
|
||||
using CompleteSmallPlan = normalization::NormalizationPlan<
|
||||
ValueAIdentity,
|
||||
ValueBIdentity,
|
||||
ResidualAIdentity,
|
||||
ResidualBIdentity>;
|
||||
using MissingSmallPlan = normalization::NormalizationPlan<
|
||||
ValueAIdentity,
|
||||
ResidualAIdentity,
|
||||
ResidualBIdentity>;
|
||||
using DuplicateSmallPlan = normalization::NormalizationPlan<
|
||||
ValueAIdentity,
|
||||
ValueAIdentity,
|
||||
ValueBIdentity,
|
||||
ResidualAIdentity,
|
||||
ResidualBIdentity>;
|
||||
using CompleteSmallPlan =
|
||||
normalization::NormalizationPlan<ValueAIdentity, ValueBIdentity, ResidualAIdentity, ResidualBIdentity>;
|
||||
using MissingSmallPlan = normalization::NormalizationPlan<ValueAIdentity, ResidualAIdentity, ResidualBIdentity>;
|
||||
using DuplicateSmallPlan = normalization::
|
||||
NormalizationPlan<ValueAIdentity, ValueAIdentity, ValueBIdentity, ResidualAIdentity, ResidualBIdentity>;
|
||||
using ForeignSmallPlan = normalization::NormalizationPlan<
|
||||
ValueAIdentity,
|
||||
ValueBIdentity,
|
||||
@@ -79,11 +67,11 @@ namespace {
|
||||
};
|
||||
|
||||
struct IncoherentComponent final {
|
||||
using Blocks = blocks::type_list<ValueA>;
|
||||
using Method = normalization::IdentityCoordinate;
|
||||
using ValueBlocks = blocks::type_list<ValueB>;
|
||||
using ResidualBlocks = blocks::type_list<>;
|
||||
static constexpr auto kind = normalization::CoordinateKind::value;
|
||||
using Blocks = blocks::type_list<ValueA>;
|
||||
using Method = normalization::IdentityCoordinate;
|
||||
using ValueBlocks = blocks::type_list<ValueB>;
|
||||
using ResidualBlocks = blocks::type_list<>;
|
||||
static constexpr auto kind = normalization::CoordinateKind::value;
|
||||
};
|
||||
|
||||
using WrongDensityTopology = normalization::CoordinateComponent<
|
||||
@@ -95,40 +83,34 @@ namespace {
|
||||
|
||||
struct FutureInvariantValue final : blocks::value_block_base { };
|
||||
struct FutureInvariantResidual final : blocks::residual_block_base { };
|
||||
using UnregisteredFutureForm = blocks::block_form<
|
||||
blocks::type_list<FutureInvariantValue>,
|
||||
blocks::type_list<FutureInvariantResidual>>;
|
||||
using UnregisteredFutureForm =
|
||||
blocks::block_form<blocks::type_list<FutureInvariantValue>, blocks::type_list<FutureInvariantResidual>>;
|
||||
|
||||
using BaseModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
using BaseModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
mean_field::eos::Polytrope,
|
||||
mean_field::surface::Isobaric,
|
||||
mean_field::integral::FixedTotalMass>>;
|
||||
using RieszPolicy = normalization::PhysicalRieszDiagonal<>;
|
||||
using RieszPolicy = normalization::PhysicalRieszDiagonal<>;
|
||||
using RieszDiscretization = mean_field::equilibrium::StellarDiscretizationFor<RieszPolicy>;
|
||||
using BaselineProblem = mean_field::equilibrium::StellarEquilibriumProblem<BaseModel>;
|
||||
using RieszProblem = mean_field::equilibrium::StellarEquilibriumProblem<BaseModel, RieszDiscretization>;
|
||||
using AngularModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
using BaselineProblem = mean_field::equilibrium::StellarEquilibriumProblem<BaseModel>;
|
||||
using RieszProblem = mean_field::equilibrium::StellarEquilibriumProblem<BaseModel, RieszDiscretization>;
|
||||
using AngularModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
mean_field::eos::Polytrope,
|
||||
mean_field::surface::Isobaric,
|
||||
mean_field::integral::FixedTotalMass,
|
||||
mean_field::integral::FixedAngularMomentum>>;
|
||||
using AngularForm = mean_field::operators::CompiledStellarEquilibriumForm<AngularModel>;
|
||||
using AngularForm = mean_field::operators::CompiledStellarEquilibriumForm<AngularModel>;
|
||||
|
||||
template <typename Mapper>
|
||||
concept CanMakeRieszDiscretization = requires(
|
||||
mean_field::fem::FEM &finiteElements,
|
||||
Mapper &&mapper,
|
||||
RieszPolicy policy
|
||||
) {
|
||||
mean_field::equilibrium::makeStellarDiscretization(
|
||||
finiteElements,
|
||||
std::forward<Mapper>(mapper),
|
||||
policy
|
||||
);
|
||||
template <typename FiniteElements>
|
||||
concept CanMakeRieszDiscretization = requires(FiniteElements &&finiteElements, RieszPolicy policy) {
|
||||
mean_field::equilibrium::makeStellarDiscretization(std::forward<FiniteElements>(finiteElements), policy);
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Normalization Plans Prove Exact Ownership Of Every Compiled Coordinate", "[normalization][type]") {
|
||||
TEST_CASE(
|
||||
"Normalization Plans Prove Exact Ownership Of Every Compiled Coordinate",
|
||||
"[normalization][type]"
|
||||
) {
|
||||
STATIC_CHECK(normalization::NormalizationPlanType<PhysicalPlan>);
|
||||
STATIC_CHECK(normalization::CompleteNormalizationFor<PhysicalPlan, PhysicalForm>);
|
||||
STATIC_CHECK(normalization::CompleteNormalizationFor<PhasePlan, PhaseForm>);
|
||||
@@ -136,17 +118,19 @@ TEST_CASE("Normalization Plans Prove Exact Ownership Of Every Compiled Coordinat
|
||||
STATIC_CHECK(normalization::CompilableNormalizationFor<RieszPolicy, PhysicalForm>);
|
||||
STATIC_CHECK(normalization::CompilableNormalizationFor<RieszPolicy, PhaseForm>);
|
||||
STATIC_CHECK(normalization::CompilableNormalizationFor<RieszPolicy, AngularForm>);
|
||||
STATIC_CHECK(normalization::StellarSpecificationNormalizationContribution<
|
||||
mean_field::integral::FixedAngularMomentum>::registered);
|
||||
STATIC_CHECK(
|
||||
normalization::StellarSpecificationNormalizationContribution<
|
||||
mean_field::integral::FixedAngularMomentum>::registered
|
||||
);
|
||||
|
||||
STATIC_CHECK(normalization::CompleteNormalizationFor<CompleteSmallPlan, SmallForm>);
|
||||
STATIC_CHECK_FALSE(normalization::CompleteNormalizationFor<MissingSmallPlan, SmallForm>);
|
||||
STATIC_CHECK_FALSE(normalization::CompleteNormalizationFor<DuplicateSmallPlan, SmallForm>);
|
||||
STATIC_CHECK_FALSE(normalization::CompleteNormalizationFor<ForeignSmallPlan, SmallForm>);
|
||||
|
||||
using Missing = normalization::NormalizationCoverage<SmallForm, MissingSmallPlan>;
|
||||
using Missing = normalization::NormalizationCoverage<SmallForm, MissingSmallPlan>;
|
||||
using Duplicate = normalization::NormalizationCoverage<SmallForm, DuplicateSmallPlan>;
|
||||
using Foreign = normalization::NormalizationCoverage<SmallForm, ForeignSmallPlan>;
|
||||
using Foreign = normalization::NormalizationCoverage<SmallForm, ForeignSmallPlan>;
|
||||
STATIC_CHECK(Missing::MissingValueBlocks::size == 1);
|
||||
STATIC_CHECK(blocks::contains_type_v<ValueB, typename Missing::MissingValueBlocks>);
|
||||
STATIC_CHECK(Duplicate::RepeatedValueBlocks::size == 1);
|
||||
@@ -155,72 +139,54 @@ TEST_CASE("Normalization Plans Prove Exact Ownership Of Every Compiled Coordinat
|
||||
STATIC_CHECK(Foreign::UnexpectedResidualBlocks::size == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("Physical Riesz Methods Reject Incompatible Or Unregistered Field Topologies", "[normalization][type]") {
|
||||
TEST_CASE(
|
||||
"Physical Riesz Methods Reject Incompatible Or Unregistered Field Topologies",
|
||||
"[normalization][type]"
|
||||
) {
|
||||
STATIC_CHECK_FALSE(normalization::NormalizationComponent<MalformedComponent>);
|
||||
STATIC_CHECK_FALSE(normalization::NormalizationComponent<IncoherentComponent>);
|
||||
STATIC_CHECK_FALSE(normalization::NormalizationComponent<WrongDensityTopology>);
|
||||
STATIC_CHECK_FALSE(normalization::CompilableNormalizationFor<RieszPolicy, UnregisteredFutureForm>);
|
||||
STATIC_CHECK(normalization::CompilableNormalizationFor<normalization::Unnormalized, UnregisteredFutureForm>);
|
||||
|
||||
using Density = normalization::PhysicalRieszBlockTraits<blocks::density::mass::value>;
|
||||
using Gravity = normalization::PhysicalRieszBlockTraits<blocks::gravity::gradient::value>;
|
||||
using Surface = normalization::PhysicalRieszBlockTraits<blocks::surface_deformation::parameters::value>;
|
||||
using Density = normalization::PhysicalRieszBlockTraits<blocks::density::mass::value>;
|
||||
using Gravity = normalization::PhysicalRieszBlockTraits<blocks::gravity::gradient::value>;
|
||||
using Surface = normalization::PhysicalRieszBlockTraits<blocks::surface_deformation::parameters::value>;
|
||||
using EnthalpyResidual = normalization::PhysicalRieszBlockTraits<blocks::enthalpy::specific::residual>;
|
||||
using MassResidual = normalization::PhysicalRieszBlockTraits<
|
||||
blocks::fixed_total_mass::mass_normalization::residual>;
|
||||
using MassResidual =
|
||||
normalization::PhysicalRieszBlockTraits<blocks::fixed_total_mass::mass_normalization::residual>;
|
||||
|
||||
STATIC_CHECK(Density::Method::topology == normalization::RieszTopology::scalar_volume_l2);
|
||||
STATIC_CHECK(Gravity::Method::topology == normalization::RieszTopology::vector_volume_l2);
|
||||
STATIC_CHECK(Surface::Method::topology == normalization::RieszTopology::scalar_boundary_l2);
|
||||
STATIC_CHECK(
|
||||
EnthalpyResidual::Method::topology == normalization::RieszTopology::hybrid_scalar_volume_point_rows
|
||||
);
|
||||
STATIC_CHECK(EnthalpyResidual::Method::topology == normalization::RieszTopology::hybrid_scalar_volume_point_rows);
|
||||
STATIC_CHECK(MassResidual::Method::topology == normalization::RieszTopology::global_scalar);
|
||||
STATIC_CHECK(MassResidual::Method::scale == normalization::PhysicalScaleKind::mass);
|
||||
}
|
||||
|
||||
TEST_CASE("Normalization Is Part Of The Compile-Time Discretization And Problem Type", "[normalization][type]") {
|
||||
TEST_CASE(
|
||||
"Normalization Is Part Of The Compile-Time Discretization And Problem Type",
|
||||
"[normalization][type]"
|
||||
) {
|
||||
STATIC_CHECK(mean_field::equilibrium::StellarDiscretizationType<RieszDiscretization>);
|
||||
STATIC_CHECK_FALSE(std::same_as<RieszDiscretization, mean_field::equilibrium::StellarDiscretization>);
|
||||
STATIC_CHECK_FALSE(std::same_as<RieszProblem, BaselineProblem>);
|
||||
STATIC_CHECK(std::same_as<typename BaselineProblem::NormalizationPrescriptionType, normalization::Unnormalized>);
|
||||
STATIC_CHECK(std::same_as<typename RieszProblem::NormalizationPrescriptionType, RieszPolicy>);
|
||||
STATIC_CHECK(mean_field::equilibrium::DiscretizedStellarEquilibriumProblem<RieszProblem>);
|
||||
STATIC_CHECK(std::constructible_from<
|
||||
RieszDiscretization,
|
||||
mean_field::fem::FEM &,
|
||||
const mean_field::mapping::DomainMapper &,
|
||||
RieszPolicy>);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<
|
||||
RieszDiscretization,
|
||||
mean_field::fem::FEM &,
|
||||
mean_field::mapping::DomainMapper &&,
|
||||
RieszPolicy>);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<
|
||||
RieszDiscretization,
|
||||
mean_field::fem::FEM &,
|
||||
const mean_field::mapping::DomainMapper &&,
|
||||
RieszPolicy>);
|
||||
STATIC_CHECK(std::constructible_from<
|
||||
mean_field::equilibrium::StellarDiscretization,
|
||||
mean_field::fem::FEM &,
|
||||
const mean_field::mapping::DomainMapper &>);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<
|
||||
mean_field::equilibrium::StellarDiscretization,
|
||||
mean_field::fem::FEM &,
|
||||
mean_field::mapping::DomainMapper &&>);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<
|
||||
mean_field::equilibrium::StellarDiscretization,
|
||||
mean_field::fem::FEM &,
|
||||
const mean_field::mapping::DomainMapper &&>);
|
||||
STATIC_CHECK(CanMakeRieszDiscretization<
|
||||
mean_field::mapping::DomainMapper &>);
|
||||
STATIC_CHECK_FALSE(CanMakeRieszDiscretization<
|
||||
mean_field::mapping::DomainMapper>);
|
||||
STATIC_CHECK_FALSE(CanMakeRieszDiscretization<
|
||||
const mean_field::mapping::DomainMapper>);
|
||||
STATIC_CHECK(std::constructible_from<RieszDiscretization, mean_field::fem::FEM &&, RieszPolicy>);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<RieszDiscretization, mean_field::fem::FEM &, RieszPolicy>);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<RieszDiscretization, const mean_field::fem::FEM &, RieszPolicy>);
|
||||
STATIC_CHECK(std::constructible_from<mean_field::equilibrium::StellarDiscretization, mean_field::fem::FEM &&>);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<mean_field::equilibrium::StellarDiscretization, mean_field::fem::FEM &>);
|
||||
STATIC_CHECK_FALSE(
|
||||
std::constructible_from<mean_field::equilibrium::StellarDiscretization, const mean_field::fem::FEM &>
|
||||
);
|
||||
STATIC_CHECK(CanMakeRieszDiscretization<mean_field::fem::FEM>);
|
||||
STATIC_CHECK_FALSE(CanMakeRieszDiscretization<mean_field::fem::FEM &>);
|
||||
STATIC_CHECK_FALSE(CanMakeRieszDiscretization<const mean_field::fem::FEM &>);
|
||||
|
||||
using SmallLayout = blocks::form_layout<SmallForm>;
|
||||
using SmallLayout = blocks::form_layout<SmallForm>;
|
||||
using SmallBuilder = normalization::DiagonalNormalizationBuilder<SmallForm>;
|
||||
STATIC_CHECK(std::constructible_from<SmallBuilder, const SmallLayout &>);
|
||||
STATIC_CHECK_FALSE(std::constructible_from<SmallBuilder, SmallLayout &&>);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user