feat(newton): first newton solver implementation
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -141,55 +141,50 @@ TEST_CASE(
|
||||
tags::model_specification_type_contract
|
||||
) {
|
||||
using namespace mean_field;
|
||||
using Request = models::FixedAngularMomentumLayoutRequest;
|
||||
using Form = operators::CompiledStellarEquilibriumForm<AngularMomentumPolytropicMassModel>;
|
||||
using Jacobian = operators::CompiledStellarEquilibriumJacobianForm<AngularMomentumPolytropicMassModel>;
|
||||
using AngularValue = utils::blocks::fixed_angular_momentum::angular_velocity::value;
|
||||
using Request = models::FixedAngularMomentumLayoutRequest;
|
||||
using Form = operators::CompiledStellarEquilibriumForm<AngularMomentumPolytropicMassModel>;
|
||||
using Jacobian = operators::CompiledStellarEquilibriumJacobianForm<AngularMomentumPolytropicMassModel>;
|
||||
using AngularValue = utils::blocks::fixed_angular_momentum::angular_velocity::value;
|
||||
using AngularResidual = utils::blocks::fixed_angular_momentum::angular_velocity::residual;
|
||||
|
||||
STATIC_CHECK(models::ConstraintLayoutRequestType<Request>);
|
||||
STATIC_CHECK(models::CompiledConstraint<models::CompiledFixedAngularMomentum>);
|
||||
STATIC_CHECK(std::same_as<
|
||||
typename Request::GeneratedValueType,
|
||||
models::PhysicalCoordinateFor<models::FixedAngularMomentum>>);
|
||||
STATIC_CHECK(std::same_as<
|
||||
typename AngularValue::GeneratedType,
|
||||
models::PhysicalCoordinateFor<models::FixedAngularMomentum>>);
|
||||
STATIC_CHECK(std::same_as<
|
||||
typename models::CompiledFixedAngularMomentum::AngularVelocityField,
|
||||
field::AngularVelocity>);
|
||||
STATIC_CHECK(
|
||||
std::same_as<typename Request::GeneratedValueType, models::PhysicalCoordinateFor<models::FixedAngularMomentum>>
|
||||
);
|
||||
STATIC_CHECK(
|
||||
std::same_as<typename AngularValue::GeneratedType, models::PhysicalCoordinateFor<models::FixedAngularMomentum>>
|
||||
);
|
||||
STATIC_CHECK(
|
||||
std::same_as<typename models::CompiledFixedAngularMomentum::AngularVelocityField, field::AngularVelocity>
|
||||
);
|
||||
STATIC_CHECK(Form::value_block_count == 7);
|
||||
STATIC_CHECK(Form::residual_block_count == 7);
|
||||
STATIC_CHECK(Request::valueBlock<Form>().index == 6);
|
||||
STATIC_CHECK(Request::residualBlock<Form>().index == 6);
|
||||
STATIC_CHECK(utils::blocks::valid_jacobian_form<Form, Jacobian>);
|
||||
STATIC_CHECK(utils::blocks::has_jacobian_coupling_v<
|
||||
AngularResidual,
|
||||
utils::blocks::density::mass::value,
|
||||
Jacobian>);
|
||||
STATIC_CHECK(utils::blocks::has_jacobian_coupling_v<
|
||||
AngularResidual,
|
||||
utils::blocks::surface_deformation::parameters::value,
|
||||
Jacobian>);
|
||||
STATIC_CHECK(
|
||||
utils::blocks::has_jacobian_coupling_v<AngularResidual, utils::blocks::density::mass::value, Jacobian>
|
||||
);
|
||||
STATIC_CHECK(
|
||||
utils::blocks::has_jacobian_coupling_v<
|
||||
AngularResidual, utils::blocks::surface_deformation::parameters::value, Jacobian>
|
||||
);
|
||||
STATIC_CHECK(utils::blocks::has_jacobian_coupling_v<AngularResidual, AngularValue, Jacobian>);
|
||||
STATIC_CHECK(utils::blocks::has_jacobian_coupling_v<
|
||||
utils::blocks::surface_deformation::shape_equilibrium::residual,
|
||||
AngularValue,
|
||||
Jacobian>);
|
||||
STATIC_CHECK(utils::blocks::has_jacobian_coupling_v<
|
||||
utils::blocks::enthalpy::specific::residual,
|
||||
AngularValue,
|
||||
Jacobian>);
|
||||
STATIC_CHECK_FALSE(utils::blocks::has_jacobian_coupling_v<
|
||||
utils::blocks::gravity::poisson::residual,
|
||||
AngularValue,
|
||||
Jacobian>);
|
||||
STATIC_CHECK(
|
||||
utils::blocks::has_jacobian_coupling_v<
|
||||
utils::blocks::surface_deformation::shape_equilibrium::residual, AngularValue, Jacobian>
|
||||
);
|
||||
STATIC_CHECK(
|
||||
utils::blocks::has_jacobian_coupling_v<utils::blocks::enthalpy::specific::residual, AngularValue, Jacobian>
|
||||
);
|
||||
STATIC_CHECK_FALSE(
|
||||
utils::blocks::has_jacobian_coupling_v<utils::blocks::gravity::poisson::residual, AngularValue, Jacobian>
|
||||
);
|
||||
|
||||
const integral::FixedAngularMomentum specification({
|
||||
.Jtotal = dimensions::AngularMomentumValue{2.75},
|
||||
.axis = {0.0, 3.0, 4.0},
|
||||
.center = {0.25, -0.5, 0.75}
|
||||
});
|
||||
const integral::FixedAngularMomentum specification(
|
||||
{.Jtotal = dimensions::AngularMomentumValue{2.75}, .axis = {0.0, 3.0, 4.0}, .center = {0.25, -0.5, 0.75}}
|
||||
);
|
||||
const models::CompiledFixedAngularMomentum compiled = models::compileConstraint(specification);
|
||||
CHECK(compiled.targetAngularMomentum() == dimensions::AngularMomentumValue{2.75});
|
||||
CHECK(std::abs(compiled.specification().axis()[0]) < 1.0e-15);
|
||||
@@ -281,9 +276,7 @@ TEST_CASE(
|
||||
) {
|
||||
const mean_field::models::FixedTotalMass mass{mean_field::dimensions::MassValue{1.25}};
|
||||
const mean_field::models::FixedCentralDensity centralDensity{mean_field::eos::DensityValue{2.5}};
|
||||
const mean_field::models::FixedAngularMomentum angularMomentum{
|
||||
mean_field::dimensions::AngularMomentumValue{0.75}
|
||||
};
|
||||
const mean_field::models::FixedAngularMomentum angularMomentum{mean_field::dimensions::AngularMomentumValue{0.75}};
|
||||
|
||||
CHECK(mass.targetMass() == mean_field::dimensions::MassValue{1.25});
|
||||
CHECK(centralDensity.targetDensity() == mean_field::eos::DensityValue{2.5});
|
||||
@@ -308,18 +301,17 @@ TEST_CASE(
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mean_field::models::FixedAngularMomentum({
|
||||
.Jtotal = mean_field::dimensions::AngularMomentumValue{1.0},
|
||||
.axis = {0.0, 0.0, 0.0}
|
||||
}),
|
||||
mean_field::models::FixedAngularMomentum(
|
||||
{.Jtotal = mean_field::dimensions::AngularMomentumValue{1.0}, .axis = {0.0, 0.0, 0.0}}
|
||||
),
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mean_field::models::FixedAngularMomentum({
|
||||
.Jtotal = mean_field::dimensions::AngularMomentumValue{1.0},
|
||||
.axis = {0.0, 0.0, 1.0},
|
||||
.center = {0.0, std::numeric_limits<double>::quiet_NaN(), 0.0}
|
||||
}),
|
||||
mean_field::models::FixedAngularMomentum(
|
||||
{.Jtotal = mean_field::dimensions::AngularMomentumValue{1.0},
|
||||
.axis = {0.0, 0.0, 1.0},
|
||||
.center = {0.0, std::numeric_limits<double>::quiet_NaN(), 0.0}}
|
||||
),
|
||||
std::invalid_argument
|
||||
);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27,16 +27,15 @@ namespace {
|
||||
int marker;
|
||||
};
|
||||
|
||||
using ModelDefinition =
|
||||
mean_field::eos::ConstitutiveLaw<MoveOnlyEquationOfState, "MoveOnlyEquationOfState">;
|
||||
using ModelDefinition = mean_field::eos::ConstitutiveLaw<MoveOnlyEquationOfState, "MoveOnlyEquationOfState">;
|
||||
|
||||
explicit MoveOnlyEquationOfState(const Parameters parameters)
|
||||
: m_marker(std::make_unique<int>(parameters.marker)) {
|
||||
}
|
||||
|
||||
MoveOnlyEquationOfState(const MoveOnlyEquationOfState &) = delete;
|
||||
MoveOnlyEquationOfState &operator=(const MoveOnlyEquationOfState &) = delete;
|
||||
MoveOnlyEquationOfState(MoveOnlyEquationOfState &&) noexcept = default;
|
||||
MoveOnlyEquationOfState(const MoveOnlyEquationOfState &) = delete;
|
||||
MoveOnlyEquationOfState &operator=(const MoveOnlyEquationOfState &) = delete;
|
||||
MoveOnlyEquationOfState(MoveOnlyEquationOfState &&) noexcept = default;
|
||||
MoveOnlyEquationOfState &operator=(MoveOnlyEquationOfState &&) noexcept = default;
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<int> &marker() const noexcept {
|
||||
@@ -60,9 +59,9 @@ namespace {
|
||||
: m_marker(std::make_unique<int>(parameters.marker)) {
|
||||
}
|
||||
|
||||
MoveOnlySurfaceCondition(const MoveOnlySurfaceCondition &) = delete;
|
||||
MoveOnlySurfaceCondition &operator=(const MoveOnlySurfaceCondition &) = delete;
|
||||
MoveOnlySurfaceCondition(MoveOnlySurfaceCondition &&) noexcept = default;
|
||||
MoveOnlySurfaceCondition(const MoveOnlySurfaceCondition &) = delete;
|
||||
MoveOnlySurfaceCondition &operator=(const MoveOnlySurfaceCondition &) = delete;
|
||||
MoveOnlySurfaceCondition(MoveOnlySurfaceCondition &&) noexcept = default;
|
||||
MoveOnlySurfaceCondition &operator=(MoveOnlySurfaceCondition &&) noexcept = default;
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<int> &marker() const noexcept {
|
||||
@@ -87,7 +86,7 @@ namespace {
|
||||
"C_move",
|
||||
"move_only_integral.residual",
|
||||
"R_move">;
|
||||
using TargetValue = typename ScalarDescription::TargetValue;
|
||||
using TargetValue = typename ScalarDescription::TargetValue;
|
||||
using ModelDefinition = mean_field::integral::FixedScalarWithMultiplier<
|
||||
MoveOnlyIntegralConstraint,
|
||||
"MoveOnlyIntegralConstraint",
|
||||
@@ -99,9 +98,9 @@ namespace {
|
||||
: m_marker(std::make_unique<int>(parameters.marker)) {
|
||||
}
|
||||
|
||||
MoveOnlyIntegralConstraint(const MoveOnlyIntegralConstraint &) = delete;
|
||||
MoveOnlyIntegralConstraint &operator=(const MoveOnlyIntegralConstraint &) = delete;
|
||||
MoveOnlyIntegralConstraint(MoveOnlyIntegralConstraint &&) noexcept = default;
|
||||
MoveOnlyIntegralConstraint(const MoveOnlyIntegralConstraint &) = delete;
|
||||
MoveOnlyIntegralConstraint &operator=(const MoveOnlyIntegralConstraint &) = delete;
|
||||
MoveOnlyIntegralConstraint(MoveOnlyIntegralConstraint &&) noexcept = default;
|
||||
MoveOnlyIntegralConstraint &operator=(MoveOnlyIntegralConstraint &&) noexcept = default;
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<int> &marker() const noexcept {
|
||||
@@ -167,20 +166,15 @@ TEST_CASE(
|
||||
tags::stellar_model_specification_api
|
||||
) {
|
||||
using namespace mean_field;
|
||||
using Qualified = models::SpecificationSet<
|
||||
const eos::Polytrope &,
|
||||
volatile surface::Isobaric &&,
|
||||
const integral::FixedTotalMass>;
|
||||
using Plain = models::SpecificationSet<
|
||||
eos::Polytrope,
|
||||
surface::Isobaric,
|
||||
integral::FixedTotalMass>;
|
||||
using Qualified =
|
||||
models::SpecificationSet<const eos::Polytrope &, volatile surface::Isobaric &&, const integral::FixedTotalMass>;
|
||||
using Plain = models::SpecificationSet<eos::Polytrope, surface::Isobaric, integral::FixedTotalMass>;
|
||||
|
||||
STATIC_CHECK(std::same_as<Qualified, Plain>);
|
||||
STATIC_CHECK(models::ValidModelSpecificationPack<
|
||||
const eos::Polytrope &,
|
||||
volatile surface::Isobaric &&,
|
||||
const integral::FixedTotalMass>);
|
||||
STATIC_CHECK(
|
||||
models::ValidModelSpecificationPack<
|
||||
const eos::Polytrope &, volatile surface::Isobaric &&, const integral::FixedTotalMass>
|
||||
);
|
||||
STATIC_CHECK(model::StellarModelType<model::StellarModel<Qualified>>);
|
||||
}
|
||||
|
||||
@@ -226,23 +220,20 @@ TEST_CASE(
|
||||
using namespace mean_field;
|
||||
|
||||
auto stellarModel = model::StellarModel(
|
||||
MoveOnlyIntegralConstraint({.marker = 307}),
|
||||
MoveOnlySurfaceCondition({.marker = 211}),
|
||||
MoveOnlyIntegralConstraint({.marker = 307}), MoveOnlySurfaceCondition({.marker = 211}),
|
||||
MoveOnlyEquationOfState({.marker = 101})
|
||||
);
|
||||
using Model = std::remove_cvref_t<decltype(stellarModel)>;
|
||||
using Expected = model::StellarModel<models::SpecificationSet<
|
||||
MoveOnlyEquationOfState,
|
||||
MoveOnlySurfaceCondition,
|
||||
MoveOnlyIntegralConstraint>>;
|
||||
using Model = std::remove_cvref_t<decltype(stellarModel)>;
|
||||
using Expected = model::StellarModel<
|
||||
models::SpecificationSet<MoveOnlyEquationOfState, MoveOnlySurfaceCondition, MoveOnlyIntegralConstraint>>;
|
||||
|
||||
STATIC_CHECK(std::same_as<Model, Expected>);
|
||||
STATIC_CHECK_FALSE(std::copy_constructible<MoveOnlyEquationOfState>);
|
||||
STATIC_CHECK_FALSE(std::copy_constructible<MoveOnlySurfaceCondition>);
|
||||
STATIC_CHECK_FALSE(std::copy_constructible<MoveOnlyIntegralConstraint>);
|
||||
|
||||
const auto &equationOfState = stellarModel.specification<MoveOnlyEquationOfState>();
|
||||
const auto &surfaceCondition = stellarModel.specification<MoveOnlySurfaceCondition>();
|
||||
const auto &equationOfState = stellarModel.specification<MoveOnlyEquationOfState>();
|
||||
const auto &surfaceCondition = stellarModel.specification<MoveOnlySurfaceCondition>();
|
||||
const auto &integralConstraint = stellarModel.specification<MoveOnlyIntegralConstraint>();
|
||||
|
||||
REQUIRE(equationOfState.marker() != nullptr);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -9,6 +9,28 @@ using namespace mean_field;
|
||||
namespace prepared_test = gravity_prepared_test_utils;
|
||||
namespace gravity_context = operators::context::gravity_field;
|
||||
|
||||
namespace {
|
||||
[[nodiscard]] mfem::Vector makeAffineDisplacement(
|
||||
const fem::FEM &finiteElements,
|
||||
const double scale
|
||||
) {
|
||||
mfem::ParGridFunction field(finiteElements.displacementFes.get());
|
||||
mfem::VectorFunctionCoefficient coefficient(
|
||||
finiteElements.mesh->Dimension(), [scale](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(position.Size());
|
||||
for (int component = 0; component < position.Size(); ++component) {
|
||||
value(component) = scale * position(component);
|
||||
}
|
||||
}
|
||||
);
|
||||
field.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector result;
|
||||
field.GetTrueDofs(result);
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE(
|
||||
"Gravity Field Linearization Context Applies Selective Invalidation",
|
||||
tags::gravity_context
|
||||
@@ -125,6 +147,70 @@ TEST_CASE(
|
||||
CHECK(context.GetGeometryContext().GetSourceOperator().GetPreparationCount() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Gravity Field Contexts Invalidate And Recover After Candidate Rejection",
|
||||
tags::gravity_context &tags::geometry &tags::unit
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
gravity_context::GravityFieldGeometryContext geometryContext(f, *f.domainMapperStateless);
|
||||
const mfem::Vector zeroDisplacement = geometryContext.GetDisplacementMap().gather(makeAffineDisplacement(f, 0.0));
|
||||
const mfem::Vector foldedDisplacement =
|
||||
geometryContext.GetDisplacementMap().gather(makeAffineDisplacement(f, -2.0));
|
||||
|
||||
REQUIRE(geometryContext.TryPrepare(zeroDisplacement, {.value = 0}, {.value = 0}).has_value());
|
||||
REQUIRE(geometryContext.IsPrepared());
|
||||
|
||||
const auto geometryRejection = geometryContext.TryPrepare(foldedDisplacement, {.value = 0}, {.value = 1});
|
||||
REQUIRE_FALSE(geometryRejection.has_value());
|
||||
CHECK(geometryRejection.error().reason == gravity_context::GravityFieldPreparationRejectionReason::invalid_mapping);
|
||||
CHECK(geometryRejection.error().mappingStatus == mapping::MappingStatus::non_positive_determinant);
|
||||
CHECK_FALSE(geometryContext.IsPrepared());
|
||||
|
||||
const auto geometryRecovery = geometryContext.TryPrepare(zeroDisplacement, {.value = 0}, {.value = 1});
|
||||
REQUIRE(geometryRecovery.has_value());
|
||||
CHECK(geometryRecovery->reconstructed_operators);
|
||||
CHECK(geometryContext.IsPrepared());
|
||||
|
||||
gravity_context::GravityFieldLinearizationContext linearizationContext(f, *f.domainMapperStateless);
|
||||
mfem::Vector density =
|
||||
prepared_test::make_deterministic_vector(linearizationContext.GetDensityMap().reduced_size(), 0.17);
|
||||
mfem::Vector gravityGradient =
|
||||
prepared_test::make_deterministic_vector(linearizationContext.GetGravityGradientMap().reduced_size(), 0.41);
|
||||
mfem::Vector gravityPotential =
|
||||
prepared_test::make_deterministic_vector(linearizationContext.GetGravityPotentialMap().reduced_size(), 0.73);
|
||||
gravity_context::GravityFieldRevisions revisions;
|
||||
|
||||
const auto makeState = [&](const mfem::Vector &displacement) {
|
||||
return gravity_context::GravityFieldStateView{
|
||||
.density = density,
|
||||
.displacement = displacement,
|
||||
.gravity_gradient = gravityGradient,
|
||||
.gravity_potential = gravityPotential
|
||||
};
|
||||
};
|
||||
|
||||
REQUIRE(linearizationContext.TryPrepare(makeState(zeroDisplacement), revisions).has_value());
|
||||
REQUIRE(linearizationContext.IsPrepared());
|
||||
|
||||
revisions.displacement.value = 1;
|
||||
const auto linearizationRejection = linearizationContext.TryPrepare(makeState(foldedDisplacement), revisions);
|
||||
REQUIRE_FALSE(linearizationRejection.has_value());
|
||||
CHECK(
|
||||
linearizationRejection.error().reason ==
|
||||
gravity_context::GravityFieldPreparationRejectionReason::invalid_mapping
|
||||
);
|
||||
CHECK(linearizationRejection.error().mappingStatus == mapping::MappingStatus::non_positive_determinant);
|
||||
CHECK_FALSE(linearizationContext.IsPrepared());
|
||||
|
||||
const auto linearizationRecovery = linearizationContext.TryPrepare(makeState(zeroDisplacement), revisions);
|
||||
REQUIRE(linearizationRecovery.has_value());
|
||||
CHECK(linearizationRecovery->geometry.reconstructed_operators);
|
||||
CHECK(linearizationContext.IsPrepared());
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Gravity Field Geometry Context Distinguishes Primal And Linearization Preparation",
|
||||
tags::gravity_context
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
@@ -177,6 +178,26 @@ namespace gravity_displacement_force_test_utils {
|
||||
return direction;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_affine_displacement(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double scale
|
||||
) {
|
||||
mfem::ParGridFunction field(f.displacementFes.get());
|
||||
mfem::VectorFunctionCoefficient coefficient(
|
||||
f.mesh->Dimension(), [scale](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(position.Size());
|
||||
for (int dimension = 0; dimension < position.Size(); ++dimension) {
|
||||
value(dimension) = scale * position(dimension);
|
||||
}
|
||||
}
|
||||
);
|
||||
field.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector result;
|
||||
field.GetTrueDofs(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_vacuum_only_density(const mean_field::fem::FEM &f) {
|
||||
mfem::ParGridFunction densityField(f.densityFes.get());
|
||||
densityField = 0.0;
|
||||
@@ -422,6 +443,85 @@ TEST_CASE(
|
||||
CHECK(gravity_prepared_test_utils::global_norm(vacuumResidual, f.mesh->GetComm()) == 0.0);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Gravity Displacement Force Reports Candidate Mapping And Arithmetic Rejections",
|
||||
tags::gravity_unit
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
mfem::Vector density = gravity_displacement_force_test_utils::make_density(f, 0.21);
|
||||
mfem::Vector gravityGradient = gravity_displacement_force_test_utils::make_gravity_gradient(f, 0.37);
|
||||
mfem::Vector displacement = gravity_displacement_force_test_utils::make_affine_displacement(f, -2.0);
|
||||
mfem::Vector residual;
|
||||
|
||||
const auto invalidMapping = mean_field::operators::kernels::try_apply_gravity_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, density, gravityGradient, displacement, residual
|
||||
);
|
||||
REQUIRE_FALSE(invalidMapping.has_value());
|
||||
CHECK(
|
||||
invalidMapping.error().reason ==
|
||||
mean_field::operators::kernels::GravityDisplacementForceRejectionReason::invalid_mapping
|
||||
);
|
||||
CHECK(invalidMapping.error().mappingStatus == mean_field::mapping::MappingStatus::non_positive_determinant);
|
||||
CHECK_THROWS_AS(
|
||||
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, density, gravityGradient, displacement, residual
|
||||
),
|
||||
std::domain_error
|
||||
);
|
||||
|
||||
displacement = 0.0;
|
||||
density = 1.0e200;
|
||||
gravityGradient = 1.0e200;
|
||||
const auto nonFiniteArithmetic = mean_field::operators::kernels::try_apply_gravity_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, density, gravityGradient, displacement, residual
|
||||
);
|
||||
REQUIRE_FALSE(nonFiniteArithmetic.has_value());
|
||||
CHECK(
|
||||
nonFiniteArithmetic.error().reason ==
|
||||
mean_field::operators::kernels::GravityDisplacementForceRejectionReason::non_finite_arithmetic
|
||||
);
|
||||
|
||||
mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize());
|
||||
gravityPotential = 0.0;
|
||||
auto revisions = gravity_displacement_force_test_utils::make_revisions();
|
||||
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
gravity_displacement_force_test_utils::prepare_gravity_context(
|
||||
gravityContext, density, displacement, gravityGradient, gravityPotential, revisions
|
||||
);
|
||||
mean_field::operators::PreparedGravityDisplacementForceOperator preparedOperator(
|
||||
f, *f.domainMapperStateless, gravityContext
|
||||
);
|
||||
const auto preparedRejection = preparedOperator.TryPrepare();
|
||||
REQUIRE_FALSE(preparedRejection.has_value());
|
||||
CHECK(
|
||||
preparedRejection.error().reason ==
|
||||
mean_field::operators::kernels::GravityDisplacementForceRejectionReason::non_finite_arithmetic
|
||||
);
|
||||
CHECK_FALSE(preparedOperator.IsPrepared());
|
||||
CHECK_THROWS_AS(preparedOperator.Prepare(), std::domain_error);
|
||||
|
||||
density = gravity_displacement_force_test_utils::make_density(f, 0.21);
|
||||
gravityGradient = gravity_displacement_force_test_utils::make_gravity_gradient(f, 0.37);
|
||||
++revisions.density.value;
|
||||
++revisions.gravity_gradient.value;
|
||||
gravity_displacement_force_test_utils::prepare_gravity_context(
|
||||
gravityContext, density, displacement, gravityGradient, gravityPotential, revisions
|
||||
);
|
||||
const auto preparedAccepted = preparedOperator.TryPrepare();
|
||||
REQUIRE(preparedAccepted.has_value());
|
||||
CHECK(preparedOperator.IsPrepared());
|
||||
|
||||
const auto accepted = mean_field::operators::kernels::try_apply_gravity_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, density, gravityGradient, displacement, residual
|
||||
);
|
||||
CHECK(accepted.has_value());
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Gravity Displacement Force Reuses Shared Gravity Revisions",
|
||||
tags::gravity_prepared
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#include <catch2/catch_approx.hpp>
|
||||
@@ -16,22 +17,22 @@ namespace angular_momentum_test_utils {
|
||||
[[nodiscard]] mean_field::operators::AngularMomentumDependencies makeDependencies() {
|
||||
return {
|
||||
.discretization = {.identity = 15013, .revision = 3},
|
||||
.density = {.identity = 15017, .revision = 5},
|
||||
.displacement = {.identity = 15031, .revision = 7},
|
||||
.rotation = {.identity = 15053, .revision = 11}
|
||||
.density = {.identity = 15017, .revision = 5},
|
||||
.displacement = {.identity = 15031, .revision = 7},
|
||||
.rotation = {.identity = 15053, .revision = 11}
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::context::gravity_field::GravityFieldRevisions makeGravityRevisions(
|
||||
const mean_field::operators::AngularMomentumDependencies &dependencies,
|
||||
const std::uint64_t gravityGradientRevision = 13,
|
||||
const std::uint64_t gravityGradientRevision = 13,
|
||||
const std::uint64_t gravityPotentialRevision = 17
|
||||
) {
|
||||
return {
|
||||
.discretization = {.value = dependencies.discretization.revision},
|
||||
.displacement = {.value = dependencies.displacement.revision},
|
||||
.density = {.value = dependencies.density.revision},
|
||||
.gravity_gradient = {.value = gravityGradientRevision},
|
||||
.discretization = {.value = dependencies.discretization.revision},
|
||||
.displacement = {.value = dependencies.displacement.revision},
|
||||
.density = {.value = dependencies.density.revision},
|
||||
.gravity_gradient = {.value = gravityGradientRevision},
|
||||
.gravity_potential = {.value = gravityPotentialRevision}
|
||||
};
|
||||
}
|
||||
@@ -42,17 +43,17 @@ namespace angular_momentum_test_utils {
|
||||
const mfem::Vector &density,
|
||||
const mfem::Vector &displacement,
|
||||
const mean_field::operators::AngularMomentumDependencies &dependencies,
|
||||
const std::uint64_t gravityGradientRevision = 13,
|
||||
const std::uint64_t gravityGradientRevision = 13,
|
||||
const std::uint64_t gravityPotentialRevision = 17
|
||||
) {
|
||||
mfem::Vector gravityGradient(finiteElements.gravityFluxFes->GetTrueVSize());
|
||||
mfem::Vector gravityPotential(finiteElements.gravityPotentialFes->GetTrueVSize());
|
||||
gravityGradient = 0.0;
|
||||
gravityGradient = 0.0;
|
||||
gravityPotential = 0.0;
|
||||
context.Prepare(
|
||||
{.density = context.GetDensityMap().gather(density),
|
||||
.displacement = context.GetDisplacementMap().gather(displacement),
|
||||
.gravity_gradient = context.GetGravityGradientMap().gather(gravityGradient),
|
||||
{.density = context.GetDensityMap().gather(density),
|
||||
.displacement = context.GetDisplacementMap().gather(displacement),
|
||||
.gravity_gradient = context.GetGravityGradientMap().gather(gravityGradient),
|
||||
.gravity_potential = context.GetGravityPotentialMap().gather(gravityPotential)},
|
||||
makeGravityRevisions(dependencies, gravityGradientRevision, gravityPotentialRevision)
|
||||
);
|
||||
@@ -64,8 +65,8 @@ namespace angular_momentum_test_utils {
|
||||
) {
|
||||
mfem::ParGridFunction field(finiteElements.densityFes.get());
|
||||
mfem::FunctionCoefficient coefficient([phase](const mfem::Vector &position) {
|
||||
return 0.94 + 0.08 * std::sin(0.71 * position(0) + phase) +
|
||||
0.05 * std::cos(0.63 * position(1) - phase) + 0.03 * position(2) * position(2);
|
||||
return 0.94 + 0.08 * std::sin(0.71 * position(0) + phase) + 0.05 * std::cos(0.63 * position(1) - phase) +
|
||||
0.03 * position(2) * position(2);
|
||||
});
|
||||
field.ProjectCoefficient(coefficient);
|
||||
mfem::Vector result;
|
||||
@@ -79,8 +80,8 @@ namespace angular_momentum_test_utils {
|
||||
) {
|
||||
mfem::ParGridFunction field(finiteElements.densityFes.get());
|
||||
mfem::FunctionCoefficient coefficient([phase](const mfem::Vector &position) {
|
||||
return 0.17 * std::sin(0.83 * position(0) + phase) -
|
||||
0.12 * std::cos(0.79 * position(1) - phase) + 0.06 * position(2);
|
||||
return 0.17 * std::sin(0.83 * position(0) + phase) - 0.12 * std::cos(0.79 * position(1) - phase) +
|
||||
0.06 * position(2);
|
||||
});
|
||||
field.ProjectCoefficient(coefficient);
|
||||
mfem::Vector result;
|
||||
@@ -110,8 +111,7 @@ namespace angular_momentum_test_utils {
|
||||
) {
|
||||
mfem::ParGridFunction field(finiteElements.displacementFes.get());
|
||||
mfem::VectorFunctionCoefficient coefficient(
|
||||
finiteElements.mesh->Dimension(),
|
||||
[scale](const mfem::Vector &position, mfem::Vector &value) {
|
||||
finiteElements.mesh->Dimension(), [scale](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(position.Size());
|
||||
for (int component = 0; component < position.Size(); ++component) {
|
||||
value(component) = scale * position(component);
|
||||
@@ -130,8 +130,7 @@ namespace angular_momentum_test_utils {
|
||||
) {
|
||||
mfem::ParGridFunction field(finiteElements.displacementFes.get());
|
||||
mfem::VectorFunctionCoefficient coefficient(
|
||||
finiteElements.mesh->Dimension(),
|
||||
[scale](const mfem::Vector &position, mfem::Vector &value) {
|
||||
finiteElements.mesh->Dimension(), [scale](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value(0) = scale * (0.07 * position(0) + 0.018 * position(1) * position(2));
|
||||
value(1) = scale * (-0.05 * position(1) + 0.013 * position(0) * position(2));
|
||||
@@ -151,7 +150,10 @@ namespace angular_momentum_test_utils {
|
||||
return value(0);
|
||||
}
|
||||
|
||||
[[nodiscard]] double relativeError(const double actual, const double expected) {
|
||||
[[nodiscard]] double relativeError(
|
||||
const double actual,
|
||||
const double expected
|
||||
) {
|
||||
return std::abs(actual - expected) /
|
||||
std::max({std::abs(actual), std::abs(expected), 100.0 * std::numeric_limits<double>::epsilon()});
|
||||
}
|
||||
@@ -168,48 +170,34 @@ TEST_CASE(
|
||||
STATIC_CHECK_FALSE(std::is_copy_constructible_v<Operator>);
|
||||
STATIC_CHECK_FALSE(std::is_move_constructible_v<Operator>);
|
||||
|
||||
utils::Args arguments = test_utils::setup_args();
|
||||
utils::Args arguments = test_utils::setup_args();
|
||||
fem::FEM finiteElements = fem::setup_fem(arguments.mesh_file, arguments, 0);
|
||||
REQUIRE(finiteElements.okay());
|
||||
|
||||
constexpr double densityValue = 1.37;
|
||||
constexpr double angularVelocity = 0.73;
|
||||
constexpr double densityValue = 1.37;
|
||||
constexpr double angularVelocity = 0.73;
|
||||
constexpr double targetAngularMomentum = 0.41;
|
||||
mfem::ParGridFunction densityField(finiteElements.densityFes.get());
|
||||
const mfem::Vector density = angular_momentum_test_utils::projectConstantDensity(
|
||||
finiteElements,
|
||||
densityValue,
|
||||
&densityField
|
||||
);
|
||||
const mfem::Vector density =
|
||||
angular_momentum_test_utils::projectConstantDensity(finiteElements, densityValue, &densityField);
|
||||
mfem::Vector displacement(finiteElements.displacementFes->GetTrueVSize());
|
||||
displacement = 0.0;
|
||||
finiteElements.displacement->SetFromTrueDofs(displacement);
|
||||
|
||||
auto dependencies = angular_momentum_test_utils::makeDependencies();
|
||||
operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||
finiteElements,
|
||||
*finiteElements.domainMapperStateless
|
||||
finiteElements, *finiteElements.domainMapperStateless
|
||||
);
|
||||
angular_momentum_test_utils::prepareGravityContext(
|
||||
gravityContext,
|
||||
finiteElements,
|
||||
density,
|
||||
displacement,
|
||||
dependencies
|
||||
gravityContext, finiteElements, density, displacement, dependencies
|
||||
);
|
||||
|
||||
const models::CompiledFixedAngularMomentum originConstraint = models::compileConstraint(
|
||||
integral::FixedAngularMomentum({
|
||||
.Jtotal = dimensions::AngularMomentumValue{targetAngularMomentum},
|
||||
.axis = {0.0, 0.0, 4.0}
|
||||
})
|
||||
);
|
||||
Operator origin(
|
||||
finiteElements,
|
||||
*finiteElements.domainMapperStateless,
|
||||
gravityContext,
|
||||
originConstraint
|
||||
integral::FixedAngularMomentum(
|
||||
{.Jtotal = dimensions::AngularMomentumValue{targetAngularMomentum}, .axis = {0.0, 0.0, 4.0}}
|
||||
)
|
||||
);
|
||||
Operator origin(finiteElements, *finiteElements.domainMapperStateless, gravityContext, originConstraint);
|
||||
const auto initial = origin.Prepare(angularVelocity, dependencies);
|
||||
CHECK(initial.rebuiltStaticPlan);
|
||||
CHECK(initial.refreshedGeometry);
|
||||
@@ -219,10 +207,11 @@ TEST_CASE(
|
||||
|
||||
const double independentMoment = analysis::get_moment_of_inertia(finiteElements, densityField);
|
||||
CHECK(angular_momentum_test_utils::relativeError(origin.GetMomentOfInertia(), independentMoment) < 2.0e-13);
|
||||
CHECK(origin.GetCurrentAngularMomentum() ==
|
||||
Approx(angularVelocity * origin.GetMomentOfInertia()).epsilon(2.0e-15));
|
||||
CHECK(angular_momentum_test_utils::residual(origin) ==
|
||||
Approx(angularVelocity * origin.GetMomentOfInertia() - targetAngularMomentum).epsilon(2.0e-15));
|
||||
CHECK(origin.GetCurrentAngularMomentum() == Approx(angularVelocity * origin.GetMomentOfInertia()).epsilon(2.0e-15));
|
||||
CHECK(
|
||||
angular_momentum_test_utils::residual(origin) ==
|
||||
Approx(angularVelocity * origin.GetMomentOfInertia() - targetAngularMomentum).epsilon(2.0e-15)
|
||||
);
|
||||
|
||||
const auto report = origin.GetConstraintReport();
|
||||
CHECK(report.targetAngularMomentum == targetAngularMomentum);
|
||||
@@ -239,11 +228,7 @@ TEST_CASE(
|
||||
angular_momentum_test_utils::projectAffineDisplacement(finiteElements, affineScale);
|
||||
++dependencies.displacement.revision;
|
||||
angular_momentum_test_utils::prepareGravityContext(
|
||||
gravityContext,
|
||||
finiteElements,
|
||||
density,
|
||||
affineDisplacement,
|
||||
dependencies
|
||||
gravityContext, finiteElements, density, affineDisplacement, dependencies
|
||||
);
|
||||
const auto affine = origin.Prepare(angularVelocity, dependencies);
|
||||
CHECK(affine.refreshedGeometry);
|
||||
@@ -257,38 +242,30 @@ TEST_CASE(
|
||||
displacement = 0.0;
|
||||
++dependencies.displacement.revision;
|
||||
angular_momentum_test_utils::prepareGravityContext(
|
||||
gravityContext,
|
||||
finiteElements,
|
||||
density,
|
||||
displacement,
|
||||
dependencies
|
||||
gravityContext, finiteElements, density, displacement, dependencies
|
||||
);
|
||||
origin.Prepare(angularVelocity, dependencies);
|
||||
constexpr std::array<double, 3> shiftedCenter{0.27, -0.19, 0.31};
|
||||
Operator shifted(
|
||||
finiteElements,
|
||||
*finiteElements.domainMapperStateless,
|
||||
gravityContext,
|
||||
models::compileConstraint(integral::FixedAngularMomentum({
|
||||
.Jtotal = dimensions::AngularMomentumValue{targetAngularMomentum},
|
||||
.axis = {0.0, 0.0, 1.0},
|
||||
.center = shiftedCenter
|
||||
}))
|
||||
finiteElements, *finiteElements.domainMapperStateless, gravityContext,
|
||||
models::compileConstraint(
|
||||
integral::FixedAngularMomentum(
|
||||
{.Jtotal = dimensions::AngularMomentumValue{targetAngularMomentum},
|
||||
.axis = {0.0, 0.0, 1.0},
|
||||
.center = shiftedCenter}
|
||||
)
|
||||
)
|
||||
);
|
||||
shifted.Prepare(angularVelocity, dependencies);
|
||||
|
||||
const double mass = analysis::domain_integrate_grid_function(
|
||||
finiteElements,
|
||||
densityField,
|
||||
utils::DOMAINS::STELLAR,
|
||||
mapping::COORDINATE_SPACE::PHYSICAL
|
||||
finiteElements, densityField, utils::DOMAINS::STELLAR, mapping::COORDINATE_SPACE::PHYSICAL
|
||||
);
|
||||
const mfem::Vector centerOfMass = analysis::get_com(finiteElements, densityField);
|
||||
const double expectedShiftedMoment = origin.GetMomentOfInertia() +
|
||||
mass * (shiftedCenter[0] * shiftedCenter[0] +
|
||||
shiftedCenter[1] * shiftedCenter[1]) -
|
||||
2.0 * mass * (shiftedCenter[0] * centerOfMass(0) +
|
||||
shiftedCenter[1] * centerOfMass(1));
|
||||
const double expectedShiftedMoment =
|
||||
origin.GetMomentOfInertia() +
|
||||
mass * (shiftedCenter[0] * shiftedCenter[0] + shiftedCenter[1] * shiftedCenter[1]) -
|
||||
2.0 * mass * (shiftedCenter[0] * centerOfMass(0) + shiftedCenter[1] * centerOfMass(1));
|
||||
CHECK(angular_momentum_test_utils::relativeError(shifted.GetMomentOfInertia(), expectedShiftedMoment) < 3.0e-13);
|
||||
}
|
||||
|
||||
@@ -298,45 +275,33 @@ TEST_CASE(
|
||||
) {
|
||||
using namespace mean_field;
|
||||
|
||||
utils::Args arguments = test_utils::setup_args();
|
||||
utils::Args arguments = test_utils::setup_args();
|
||||
fem::FEM finiteElements = fem::setup_fem(arguments.mesh_file, arguments, 0);
|
||||
REQUIRE(finiteElements.okay());
|
||||
|
||||
const mfem::Vector density = angular_momentum_test_utils::projectDensity(finiteElements, 0.31);
|
||||
const mfem::Vector densityDirection =
|
||||
angular_momentum_test_utils::projectDensityDirection(finiteElements, 0.67);
|
||||
const mfem::Vector displacement =
|
||||
angular_momentum_test_utils::projectDisplacementDirection(finiteElements, 0.43);
|
||||
const mfem::Vector density = angular_momentum_test_utils::projectDensity(finiteElements, 0.31);
|
||||
const mfem::Vector densityDirection = angular_momentum_test_utils::projectDensityDirection(finiteElements, 0.67);
|
||||
const mfem::Vector displacement = angular_momentum_test_utils::projectDisplacementDirection(finiteElements, 0.43);
|
||||
const mfem::Vector displacementDirection =
|
||||
angular_momentum_test_utils::projectDisplacementDirection(finiteElements, -0.79);
|
||||
constexpr double angularVelocity = 0.63;
|
||||
constexpr double angularVelocity = 0.63;
|
||||
constexpr double angularVelocityDirection = -0.37;
|
||||
|
||||
auto dependencies = angular_momentum_test_utils::makeDependencies();
|
||||
auto dependencies = angular_momentum_test_utils::makeDependencies();
|
||||
operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||
finiteElements,
|
||||
*finiteElements.domainMapperStateless
|
||||
finiteElements, *finiteElements.domainMapperStateless
|
||||
);
|
||||
angular_momentum_test_utils::prepareGravityContext(
|
||||
gravityContext,
|
||||
finiteElements,
|
||||
density,
|
||||
displacement,
|
||||
dependencies
|
||||
gravityContext, finiteElements, density, displacement, dependencies
|
||||
);
|
||||
operators::PreparedAngularMomentumOperator operation(
|
||||
finiteElements,
|
||||
*finiteElements.domainMapperStateless,
|
||||
gravityContext,
|
||||
models::compileConstraint(
|
||||
integral::FixedAngularMomentum({.Jtotal = dimensions::AngularMomentumValue{0.81}})
|
||||
)
|
||||
finiteElements, *finiteElements.domainMapperStateless, gravityContext,
|
||||
models::compileConstraint(integral::FixedAngularMomentum({.Jtotal = dimensions::AngularMomentumValue{0.81}}))
|
||||
);
|
||||
operation.Prepare(angularVelocity, dependencies);
|
||||
|
||||
const mfem::Vector reducedDensityDirection = gravityContext.GetDensityMap().gather(densityDirection);
|
||||
const mfem::Vector reducedDisplacementDirection =
|
||||
gravityContext.GetDisplacementMap().gather(displacementDirection);
|
||||
const mfem::Vector reducedDensityDirection = gravityContext.GetDensityMap().gather(densityDirection);
|
||||
const mfem::Vector reducedDisplacementDirection = gravityContext.GetDisplacementMap().gather(displacementDirection);
|
||||
mfem::Vector densityAction;
|
||||
mfem::Vector geometryAction;
|
||||
mfem::Vector angularVelocityAction;
|
||||
@@ -345,17 +310,17 @@ TEST_CASE(
|
||||
operation.ApplyDisplacementJacobianAction(reducedDisplacementDirection, geometryAction);
|
||||
operation.ApplyAngularVelocityJacobianAction(angularVelocityDirection, angularVelocityAction);
|
||||
operation.ApplyCompleteJacobianAction(
|
||||
reducedDensityDirection,
|
||||
reducedDisplacementDirection,
|
||||
angularVelocityDirection,
|
||||
completeAction
|
||||
reducedDensityDirection, reducedDisplacementDirection, angularVelocityDirection, completeAction
|
||||
);
|
||||
CHECK(
|
||||
angular_momentum_test_utils::relativeError(
|
||||
completeAction(0), densityAction(0) + geometryAction(0) + angularVelocityAction(0)
|
||||
) < 3.0e-15
|
||||
);
|
||||
CHECK(
|
||||
angularVelocityAction(0) ==
|
||||
Catch::Approx(operation.GetMomentOfInertia() * angularVelocityDirection).epsilon(2.0e-15)
|
||||
);
|
||||
CHECK(angular_momentum_test_utils::relativeError(
|
||||
completeAction(0),
|
||||
densityAction(0) + geometryAction(0) + angularVelocityAction(0)
|
||||
) < 3.0e-15);
|
||||
CHECK(angularVelocityAction(0) ==
|
||||
Catch::Approx(operation.GetMomentOfInertia() * angularVelocityDirection).epsilon(2.0e-15));
|
||||
|
||||
constexpr double angularStep = 1.0e-6;
|
||||
++dependencies.rotation.revision;
|
||||
@@ -363,7 +328,7 @@ TEST_CASE(
|
||||
const double angularPlus = angular_momentum_test_utils::residual(operation);
|
||||
++dependencies.rotation.revision;
|
||||
operation.Prepare(angularVelocity - angularStep * angularVelocityDirection, dependencies);
|
||||
const double angularMinus = angular_momentum_test_utils::residual(operation);
|
||||
const double angularMinus = angular_momentum_test_utils::residual(operation);
|
||||
const double angularDifference = (angularPlus - angularMinus) / (2.0 * angularStep);
|
||||
CHECK(angular_momentum_test_utils::relativeError(angularVelocityAction(0), angularDifference) < 2.0e-10);
|
||||
|
||||
@@ -372,11 +337,7 @@ TEST_CASE(
|
||||
densityPlus.Add(densityStep, densityDirection);
|
||||
++dependencies.density.revision;
|
||||
angular_momentum_test_utils::prepareGravityContext(
|
||||
gravityContext,
|
||||
finiteElements,
|
||||
densityPlus,
|
||||
displacement,
|
||||
dependencies
|
||||
gravityContext, finiteElements, densityPlus, displacement, dependencies
|
||||
);
|
||||
operation.Prepare(angularVelocity, dependencies);
|
||||
const double densityPlusResidual = angular_momentum_test_utils::residual(operation);
|
||||
@@ -384,15 +345,11 @@ TEST_CASE(
|
||||
densityMinus.Add(-densityStep, densityDirection);
|
||||
++dependencies.density.revision;
|
||||
angular_momentum_test_utils::prepareGravityContext(
|
||||
gravityContext,
|
||||
finiteElements,
|
||||
densityMinus,
|
||||
displacement,
|
||||
dependencies
|
||||
gravityContext, finiteElements, densityMinus, displacement, dependencies
|
||||
);
|
||||
operation.Prepare(angularVelocity, dependencies);
|
||||
const double densityMinusResidual = angular_momentum_test_utils::residual(operation);
|
||||
const double densityDifference = (densityPlusResidual - densityMinusResidual) / (2.0 * densityStep);
|
||||
const double densityDifference = (densityPlusResidual - densityMinusResidual) / (2.0 * densityStep);
|
||||
CHECK(angular_momentum_test_utils::relativeError(densityAction(0), densityDifference) < 4.0e-8);
|
||||
|
||||
constexpr double geometryStep = 1.0e-6;
|
||||
@@ -401,11 +358,7 @@ TEST_CASE(
|
||||
++dependencies.density.revision;
|
||||
++dependencies.displacement.revision;
|
||||
angular_momentum_test_utils::prepareGravityContext(
|
||||
gravityContext,
|
||||
finiteElements,
|
||||
density,
|
||||
displacementPlus,
|
||||
dependencies
|
||||
gravityContext, finiteElements, density, displacementPlus, dependencies
|
||||
);
|
||||
operation.Prepare(angularVelocity, dependencies);
|
||||
const double geometryPlusResidual = angular_momentum_test_utils::residual(operation);
|
||||
@@ -413,19 +366,19 @@ TEST_CASE(
|
||||
displacementMinus.Add(-geometryStep, displacementDirection);
|
||||
++dependencies.displacement.revision;
|
||||
angular_momentum_test_utils::prepareGravityContext(
|
||||
gravityContext,
|
||||
finiteElements,
|
||||
density,
|
||||
displacementMinus,
|
||||
dependencies
|
||||
gravityContext, finiteElements, density, displacementMinus, dependencies
|
||||
);
|
||||
operation.Prepare(angularVelocity, dependencies);
|
||||
const double geometryMinusResidual = angular_momentum_test_utils::residual(operation);
|
||||
const double geometryDifference = (geometryPlusResidual - geometryMinusResidual) / (2.0 * geometryStep);
|
||||
INFO("Density angular-momentum derivative error = " <<
|
||||
angular_momentum_test_utils::relativeError(densityAction(0), densityDifference));
|
||||
INFO("Geometry angular-momentum derivative error = " <<
|
||||
angular_momentum_test_utils::relativeError(geometryAction(0), geometryDifference));
|
||||
const double geometryDifference = (geometryPlusResidual - geometryMinusResidual) / (2.0 * geometryStep);
|
||||
INFO(
|
||||
"Density angular-momentum derivative error = "
|
||||
<< angular_momentum_test_utils::relativeError(densityAction(0), densityDifference)
|
||||
);
|
||||
INFO(
|
||||
"Geometry angular-momentum derivative error = "
|
||||
<< angular_momentum_test_utils::relativeError(geometryAction(0), geometryDifference)
|
||||
);
|
||||
CHECK(angular_momentum_test_utils::relativeError(geometryAction(0), geometryDifference) < 4.0e-7);
|
||||
}
|
||||
|
||||
@@ -435,53 +388,35 @@ TEST_CASE(
|
||||
) {
|
||||
using namespace mean_field;
|
||||
|
||||
utils::Args arguments = test_utils::setup_args();
|
||||
utils::Args arguments = test_utils::setup_args();
|
||||
fem::FEM finiteElements = fem::setup_fem(arguments.mesh_file, arguments, 0);
|
||||
REQUIRE(finiteElements.okay());
|
||||
mfem::Vector density = angular_momentum_test_utils::projectDensity(finiteElements, 0.29);
|
||||
mfem::Vector displacement =
|
||||
angular_momentum_test_utils::projectDisplacementDirection(finiteElements, 0.41);
|
||||
auto dependencies = angular_momentum_test_utils::makeDependencies();
|
||||
mfem::Vector density = angular_momentum_test_utils::projectDensity(finiteElements, 0.29);
|
||||
mfem::Vector displacement = angular_momentum_test_utils::projectDisplacementDirection(finiteElements, 0.41);
|
||||
auto dependencies = angular_momentum_test_utils::makeDependencies();
|
||||
std::uint64_t gravityPotentialRevision = 17;
|
||||
|
||||
operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||
finiteElements,
|
||||
*finiteElements.domainMapperStateless
|
||||
finiteElements, *finiteElements.domainMapperStateless
|
||||
);
|
||||
angular_momentum_test_utils::prepareGravityContext(
|
||||
gravityContext,
|
||||
finiteElements,
|
||||
density,
|
||||
displacement,
|
||||
dependencies,
|
||||
13,
|
||||
gravityPotentialRevision
|
||||
gravityContext, finiteElements, density, displacement, dependencies, 13, gravityPotentialRevision
|
||||
);
|
||||
operators::PreparedAngularMomentumOperator operation(
|
||||
finiteElements,
|
||||
*finiteElements.domainMapperStateless,
|
||||
gravityContext,
|
||||
models::compileConstraint(
|
||||
integral::FixedAngularMomentum({.Jtotal = dimensions::AngularMomentumValue{0.71}})
|
||||
)
|
||||
finiteElements, *finiteElements.domainMapperStateless, gravityContext,
|
||||
models::compileConstraint(integral::FixedAngularMomentum({.Jtotal = dimensions::AngularMomentumValue{0.71}}))
|
||||
);
|
||||
operation.Prepare(0.52, dependencies);
|
||||
const auto preparationCount = operation.GetPreparationCount();
|
||||
const double moment = operation.GetMomentOfInertia();
|
||||
const double moment = operation.GetMomentOfInertia();
|
||||
|
||||
const auto repeated = operation.Prepare(0.52, dependencies);
|
||||
const auto repeated = operation.Prepare(0.52, dependencies);
|
||||
CHECK_FALSE(repeated.DidAnyWork());
|
||||
CHECK(operation.GetPreparationCount() == preparationCount);
|
||||
|
||||
++gravityPotentialRevision;
|
||||
angular_momentum_test_utils::prepareGravityContext(
|
||||
gravityContext,
|
||||
finiteElements,
|
||||
density,
|
||||
displacement,
|
||||
dependencies,
|
||||
13,
|
||||
gravityPotentialRevision
|
||||
gravityContext, finiteElements, density, displacement, dependencies, 13, gravityPotentialRevision
|
||||
);
|
||||
const auto unrelatedPotential = operation.Prepare(0.52, dependencies);
|
||||
CHECK_FALSE(unrelatedPotential.DidAnyWork());
|
||||
@@ -494,19 +429,15 @@ TEST_CASE(
|
||||
CHECK_FALSE(rotationOnly.refreshedDensity);
|
||||
CHECK_FALSE(rotationOnly.refreshedGeometry);
|
||||
CHECK(operation.GetMomentOfInertia() == moment);
|
||||
CHECK(angular_momentum_test_utils::residual(operation) - residualBeforeRotation ==
|
||||
Catch::Approx((0.81 - 0.52) * moment).epsilon(3.0e-15));
|
||||
CHECK(
|
||||
angular_momentum_test_utils::residual(operation) - residualBeforeRotation ==
|
||||
Catch::Approx((0.81 - 0.52) * moment).epsilon(3.0e-15)
|
||||
);
|
||||
|
||||
density = angular_momentum_test_utils::projectDensity(finiteElements, 0.83);
|
||||
++dependencies.density.revision;
|
||||
angular_momentum_test_utils::prepareGravityContext(
|
||||
gravityContext,
|
||||
finiteElements,
|
||||
density,
|
||||
displacement,
|
||||
dependencies,
|
||||
13,
|
||||
gravityPotentialRevision
|
||||
gravityContext, finiteElements, density, displacement, dependencies, 13, gravityPotentialRevision
|
||||
);
|
||||
const auto densityOnly = operation.Prepare(0.81, dependencies);
|
||||
CHECK(densityOnly.refreshedDensity);
|
||||
@@ -516,16 +447,84 @@ TEST_CASE(
|
||||
displacement = angular_momentum_test_utils::projectDisplacementDirection(finiteElements, 0.87);
|
||||
++dependencies.displacement.revision;
|
||||
angular_momentum_test_utils::prepareGravityContext(
|
||||
gravityContext,
|
||||
finiteElements,
|
||||
density,
|
||||
displacement,
|
||||
dependencies,
|
||||
13,
|
||||
gravityPotentialRevision
|
||||
gravityContext, finiteElements, density, displacement, dependencies, 13, gravityPotentialRevision
|
||||
);
|
||||
const auto geometryOnly = operation.Prepare(0.81, dependencies);
|
||||
CHECK(geometryOnly.refreshedGeometry);
|
||||
CHECK_FALSE(geometryOnly.refreshedDensity);
|
||||
CHECK_FALSE(geometryOnly.updatedAngularVelocity);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Angular Momentum Returns Explicit Candidate Rejections And Recovers",
|
||||
"[fixed-angular-momentum][prepared][trial-outcome]"
|
||||
) {
|
||||
using namespace mean_field;
|
||||
|
||||
STATIC_CHECK(std::is_trivially_copyable_v<operators::AngularMomentumPreparationRejection>);
|
||||
|
||||
utils::Args arguments = test_utils::setup_args();
|
||||
fem::FEM finiteElements = fem::setup_fem(arguments.mesh_file, arguments, 0);
|
||||
REQUIRE(finiteElements.okay());
|
||||
|
||||
constexpr double angularVelocity = 0.52;
|
||||
const mfem::Vector positiveDensity = angular_momentum_test_utils::projectConstantDensity(finiteElements, 0.91);
|
||||
const mfem::Vector negativeDensity = angular_momentum_test_utils::projectConstantDensity(finiteElements, -0.91);
|
||||
mfem::Vector displacement(finiteElements.displacementFes->GetTrueVSize());
|
||||
displacement = 0.0;
|
||||
|
||||
auto dependencies = angular_momentum_test_utils::makeDependencies();
|
||||
operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||
finiteElements, *finiteElements.domainMapperStateless
|
||||
);
|
||||
angular_momentum_test_utils::prepareGravityContext(
|
||||
gravityContext, finiteElements, positiveDensity, displacement, dependencies
|
||||
);
|
||||
operators::PreparedAngularMomentumOperator operation(
|
||||
finiteElements, *finiteElements.domainMapperStateless, gravityContext,
|
||||
models::compileConstraint(integral::FixedAngularMomentum({.Jtotal = dimensions::AngularMomentumValue{0.71}}))
|
||||
);
|
||||
|
||||
const auto initial = operation.TryPrepare(angularVelocity, dependencies);
|
||||
REQUIRE(initial.has_value());
|
||||
const std::uint64_t initialPreparationCount = operation.GetPreparationCount();
|
||||
|
||||
const std::uint64_t successfulPreparations = initialPreparationCount;
|
||||
|
||||
++dependencies.density.revision;
|
||||
angular_momentum_test_utils::prepareGravityContext(
|
||||
gravityContext, finiteElements, negativeDensity, displacement, dependencies
|
||||
);
|
||||
const auto negativeMoment = operation.TryPrepare(angularVelocity, dependencies);
|
||||
REQUIRE_FALSE(negativeMoment.has_value());
|
||||
CHECK(
|
||||
negativeMoment.error().reason ==
|
||||
operators::AngularMomentumPreparationRejectionReason::negative_moment_of_inertia
|
||||
);
|
||||
CHECK(negativeMoment.error().momentOfInertia < 0.0);
|
||||
CHECK(operation.GetPreparationCount() == successfulPreparations);
|
||||
CHECK_FALSE(operation.IsPrepared());
|
||||
REQUIRE_THROWS_AS(operation.Prepare(angularVelocity, dependencies), std::domain_error);
|
||||
|
||||
++dependencies.density.revision;
|
||||
angular_momentum_test_utils::prepareGravityContext(
|
||||
gravityContext, finiteElements, positiveDensity, displacement, dependencies
|
||||
);
|
||||
const auto recovered = operation.TryPrepare(angularVelocity, dependencies);
|
||||
REQUIRE(recovered.has_value());
|
||||
CHECK(operation.IsPrepared());
|
||||
|
||||
++dependencies.rotation.revision;
|
||||
const auto nonFiniteAngularVelocity = operation.TryPrepare(std::numeric_limits<double>::quiet_NaN(), dependencies);
|
||||
REQUIRE_FALSE(nonFiniteAngularVelocity.has_value());
|
||||
CHECK(
|
||||
nonFiniteAngularVelocity.error().reason ==
|
||||
operators::AngularMomentumPreparationRejectionReason::non_finite_angular_velocity
|
||||
);
|
||||
CHECK_FALSE(operation.IsPrepared());
|
||||
REQUIRE_THROWS_AS(operation.Prepare(std::numeric_limits<double>::quiet_NaN(), dependencies), std::domain_error);
|
||||
|
||||
++dependencies.rotation.revision;
|
||||
REQUIRE(operation.TryPrepare(angularVelocity, dependencies).has_value());
|
||||
CHECK(operation.IsPrepared());
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <mfem.hpp>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
import mean_field;
|
||||
@@ -101,6 +102,22 @@ namespace prepared_barotropic_closure_test_utils {
|
||||
return project_scalar(finiteElementSpace, coefficient);
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_folding_displacement(const mean_field::fem::FEM &f) {
|
||||
mfem::ParGridFunction fieldValue(f.displacementFes.get());
|
||||
mfem::VectorFunctionCoefficient coefficient(
|
||||
f.mesh->Dimension(), [](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(position.Size());
|
||||
value = 0.0;
|
||||
value(0) = -2.0 * position(0);
|
||||
}
|
||||
);
|
||||
fieldValue.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector result;
|
||||
fieldValue.GetTrueDofs(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector reduce(
|
||||
const field::FieldDofMap &map,
|
||||
const mfem::Vector &full
|
||||
@@ -263,6 +280,7 @@ namespace prepared_barotropic_closure_test_utils {
|
||||
STATIC_REQUIRE_FALSE(std::is_copy_assignable_v<Operator>);
|
||||
STATIC_REQUIRE_FALSE(std::is_move_constructible_v<Operator>);
|
||||
STATIC_REQUIRE_FALSE(std::is_move_assignable_v<Operator>);
|
||||
STATIC_REQUIRE(std::is_trivially_copyable_v<mean_field::operators::BarotropicClosurePreparationRejection>);
|
||||
|
||||
auto args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
@@ -303,6 +321,115 @@ namespace prepared_barotropic_closure_test_utils {
|
||||
CHECK(globalEnthalpyReduced < globalEnthalpyFull);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Barotropic Closure Reports Expected EOS Rejections Without Unwinding",
|
||||
tags::barotrope &tags::closure &tags::prepared &tags::unit
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const Maps maps(f);
|
||||
const mean_field::eos::Polytrope equationOfState(3.0, 1.5);
|
||||
mean_field::operators::PreparedBarotropicClosureOperator preparedOperator(
|
||||
f, *f.domainMapperStateless, equationOfState
|
||||
);
|
||||
|
||||
mfem::Vector density(maps.density.reduced_size());
|
||||
mfem::Vector enthalpy(maps.enthalpy.reduced_size());
|
||||
mfem::Vector displacement(maps.displacement.reduced_size());
|
||||
density = 0.0;
|
||||
enthalpy = -1.0;
|
||||
displacement = 0.0;
|
||||
auto dependencies = make_dependencies();
|
||||
|
||||
const auto outsideDomain =
|
||||
preparedOperator.TryPrepare(make_state_view(density, enthalpy, displacement), dependencies);
|
||||
REQUIRE_FALSE(outsideDomain.has_value());
|
||||
CHECK(
|
||||
outsideDomain.error().reason ==
|
||||
mean_field::operators::BarotropicClosurePreparationRejectionReason::equation_of_state
|
||||
);
|
||||
CHECK(outsideDomain.error().equationOfStateError == mean_field::eos::EvaluationErrorCode::outside_domain);
|
||||
CHECK_FALSE(preparedOperator.IsPrepared());
|
||||
try {
|
||||
(void)preparedOperator.Prepare(make_state_view(density, enthalpy, displacement), dependencies);
|
||||
FAIL("The compatibility Prepare overload accepted an out-of-domain EOS input.");
|
||||
} catch (const mean_field::eos::EvaluationError &error) {
|
||||
CHECK(error.code() == mean_field::eos::EvaluationErrorCode::outside_domain);
|
||||
}
|
||||
|
||||
// Keep the interpolated input finite while forcing the n = 3
|
||||
// polytropic density evaluation to overflow.
|
||||
enthalpy = 1.0e150;
|
||||
++dependencies.enthalpy.revision;
|
||||
|
||||
const auto rejected =
|
||||
preparedOperator.TryPrepare(make_state_view(density, enthalpy, displacement), dependencies);
|
||||
REQUIRE_FALSE(rejected.has_value());
|
||||
CHECK(
|
||||
rejected.error().reason ==
|
||||
mean_field::operators::BarotropicClosurePreparationRejectionReason::equation_of_state
|
||||
);
|
||||
CHECK(rejected.error().equationOfStateError == mean_field::eos::EvaluationErrorCode::nonfinite_result);
|
||||
CHECK_FALSE(preparedOperator.IsPrepared());
|
||||
try {
|
||||
(void)preparedOperator.Prepare(make_state_view(density, enthalpy, displacement), dependencies);
|
||||
FAIL("The compatibility Prepare overload accepted a non-finite EOS result.");
|
||||
} catch (const mean_field::eos::EvaluationError &error) {
|
||||
CHECK(error.code() == mean_field::eos::EvaluationErrorCode::nonfinite_result);
|
||||
}
|
||||
|
||||
enthalpy = 1.0;
|
||||
++dependencies.enthalpy.revision;
|
||||
const auto accepted =
|
||||
preparedOperator.TryPrepare(make_state_view(density, enthalpy, displacement), dependencies);
|
||||
REQUIRE(accepted.has_value());
|
||||
CHECK(preparedOperator.IsPrepared());
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Barotropic Closure Reports Invalid Candidate Geometry Without Unwinding",
|
||||
tags::barotrope &tags::closure &tags::prepared &tags::geometry &tags::unit
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const Maps maps(f);
|
||||
const mean_field::eos::Polytrope equationOfState(3.0, 1.5);
|
||||
mean_field::operators::PreparedBarotropicClosureOperator preparedOperator(
|
||||
f, *f.domainMapperStateless, equationOfState
|
||||
);
|
||||
|
||||
mfem::Vector density(maps.density.reduced_size());
|
||||
mfem::Vector enthalpy(maps.enthalpy.reduced_size());
|
||||
density = 1.0;
|
||||
enthalpy = 1.0;
|
||||
mfem::Vector displacement = reduce(maps.displacement, make_folding_displacement(f));
|
||||
auto dependencies = make_dependencies();
|
||||
|
||||
const auto rejected =
|
||||
preparedOperator.TryPrepare(make_state_view(density, enthalpy, displacement), dependencies);
|
||||
REQUIRE_FALSE(rejected.has_value());
|
||||
CHECK(
|
||||
rejected.error().reason ==
|
||||
mean_field::operators::BarotropicClosurePreparationRejectionReason::mapping_failure
|
||||
);
|
||||
CHECK(rejected.error().mappingStatus == mean_field::mapping::MappingStatus::non_positive_determinant);
|
||||
CHECK_FALSE(preparedOperator.IsPrepared());
|
||||
CHECK_THROWS_AS(
|
||||
preparedOperator.Prepare(make_state_view(density, enthalpy, displacement), dependencies), std::domain_error
|
||||
);
|
||||
|
||||
displacement = 0.0;
|
||||
++dependencies.displacement.revision;
|
||||
const auto accepted =
|
||||
preparedOperator.TryPrepare(make_state_view(density, enthalpy, displacement), dependencies);
|
||||
REQUIRE(accepted.has_value());
|
||||
CHECK(preparedOperator.IsPrepared());
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Barotropic Closure Matches Full Stateless Kernels Through FieldDof Restriction",
|
||||
tags::barotrope &tags::closure &tags::hydro &tags::prepared &tags::field &tags::integration
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
#include <cmath>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <mfem.hpp>
|
||||
@@ -49,24 +52,30 @@ TEST_CASE(
|
||||
) {
|
||||
using namespace mean_field;
|
||||
|
||||
utils::Args args = test_utils::setup_args();
|
||||
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
utils::Args args = test_utils::setup_args();
|
||||
fem::FEM physicalFiniteElements = fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(physicalFiniteElements.okay());
|
||||
|
||||
fem::FEM borderedFiniteElements = fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(borderedFiniteElements.okay());
|
||||
|
||||
models::StellarModel stellarModel{
|
||||
models::structure::PolytropicStructure{eos::Polytrope{3.0, 0.25}, 1.0},
|
||||
surface::ConstantPressureSurface{dimensions::PressureValue{0.0}}
|
||||
};
|
||||
operators::PreparedStellarEquilibriumOperator physicalOperator(f, *f.domainMapperStateless, stellarModel);
|
||||
operators::PreparedStellarEquilibriumOperator physicalOperator(
|
||||
physicalFiniteElements, *physicalFiniteElements.domainMapperStateless, stellarModel
|
||||
);
|
||||
auto equilibriumProblem = equilibrium::discretize(
|
||||
model::StellarModel(
|
||||
constraint::FixedCentralDensity({.RhoC = dimensions::DensityValue{1.0}}),
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{1.0}}),
|
||||
surface::Isobaric({.Psurf = dimensions::PressureValue{0.0}}), eos::Polytrope({.n = 3.0, .K = 0.25})
|
||||
),
|
||||
equilibrium::StellarDiscretization{f, *f.domainMapperStateless}
|
||||
std::move(borderedFiniteElements)
|
||||
);
|
||||
auto &borderedOperator = equilibriumProblem.GetPreparedOperator();
|
||||
auto &borderedOperator = equilibriumProblem.GetPreparedOperator();
|
||||
const MPI_Comm communicator = equilibriumProblem.GetCommunicator();
|
||||
|
||||
STATIC_CHECK(
|
||||
std::same_as<
|
||||
@@ -87,8 +96,7 @@ TEST_CASE(
|
||||
CHECK(borderedOperator.GetRootManifest().constraints().size() == 3);
|
||||
CHECK(borderedOperator.GetRootManifest().specificationDescriptors().size() == 4);
|
||||
|
||||
const auto ¢ralDescriptor =
|
||||
borderedOperator.GetRootManifest().specification<constraint::FixedCentralDensity>();
|
||||
const auto ¢ralDescriptor = borderedOperator.GetRootManifest().specification<constraint::FixedCentralDensity>();
|
||||
CHECK(centralDescriptor.stableId == "FixedCentralDensity");
|
||||
CHECK(centralDescriptor.role == models::SpecificationRole::phase_condition);
|
||||
CHECK(centralDescriptor.columnPolicy == operators::RootColumnPolicy::solver_border);
|
||||
@@ -99,10 +107,9 @@ TEST_CASE(
|
||||
CHECK(centralDescriptor.residualUnits == "specific_enthalpy");
|
||||
|
||||
mfem::Vector physicalState(physicalOperator.Width());
|
||||
physicalState = 0.0;
|
||||
const auto physicalStateView =
|
||||
physicalOperator.GetRootManifest().stateView(physicalState);
|
||||
physicalStateView.block(utils::blocks::density_field.mass_term) = 1.0;
|
||||
physicalState = 0.0;
|
||||
const auto physicalStateView = physicalOperator.GetRootManifest().stateView(physicalState);
|
||||
physicalStateView.block(utils::blocks::density_field.mass_term) = 1.0;
|
||||
physicalStateView.block(utils::blocks::enthalpy_field.specific_term) = 1.0;
|
||||
|
||||
mfem::Vector borderedState(borderedOperator.Width());
|
||||
@@ -152,7 +159,7 @@ TEST_CASE(
|
||||
localCenterDirection += enthalpyDirection(centerDof);
|
||||
}
|
||||
double globalCenterDirection = 0.0;
|
||||
MPI_Allreduce(&localCenterDirection, &globalCenterDirection, 1, MPI_DOUBLE, MPI_SUM, f.mesh->GetComm());
|
||||
MPI_Allreduce(&localCenterDirection, &globalCenterDirection, 1, MPI_DOUBLE, MPI_SUM, communicator);
|
||||
CHECK(borderedAction(borderedAction.Size() - 1) == globalCenterDirection);
|
||||
|
||||
const auto repeatedReport = borderedOperator.Prepare(borderedState, dependencies, rotation);
|
||||
@@ -181,6 +188,60 @@ TEST_CASE(
|
||||
localBorderEntry += enthalpyAction(centerDof);
|
||||
}
|
||||
double globalBorderEntry = 0.0;
|
||||
MPI_Allreduce(&localBorderEntry, &globalBorderEntry, 1, MPI_DOUBLE, MPI_SUM, f.mesh->GetComm());
|
||||
MPI_Allreduce(&localBorderEntry, &globalBorderEntry, 1, MPI_DOUBLE, MPI_SUM, communicator);
|
||||
CHECK(globalBorderEntry == -0.625);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Central Density Variadic Preparation Rejects A Non-Finite Phase Coordinate Without Unwinding",
|
||||
tags::central_density_phase_integration
|
||||
) {
|
||||
using namespace mean_field;
|
||||
|
||||
utils::Args args = test_utils::setup_args();
|
||||
fem::FEM finiteElements = fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(finiteElements.okay());
|
||||
|
||||
auto equilibriumProblem = equilibrium::discretize(
|
||||
model::StellarModel(
|
||||
constraint::FixedCentralDensity({.RhoC = dimensions::DensityValue{1.0}}),
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{1.0}}),
|
||||
surface::Isobaric({.Psurf = dimensions::PressureValue{0.0}}), eos::Polytrope({.n = 3.0, .K = 0.25})
|
||||
),
|
||||
std::move(finiteElements)
|
||||
);
|
||||
auto &preparedOperator = equilibriumProblem.GetPreparedOperator();
|
||||
|
||||
mfem::Vector state(preparedOperator.Width());
|
||||
state = 0.0;
|
||||
const auto stateView = preparedOperator.GetRootManifest().stateView(state);
|
||||
stateView.block(utils::blocks::density_field.mass_term) = 1.0;
|
||||
stateView.block(utils::blocks::enthalpy_field.specific_term) = 1.0;
|
||||
|
||||
const operators::StellarEquilibriumDependencies dependencies = make_dependencies();
|
||||
const physics::RigidRotation rotation = make_zero_rotation();
|
||||
REQUIRE(equilibriumProblem.TryPrepare(state, dependencies, rotation).has_value());
|
||||
|
||||
int rank = 0;
|
||||
REQUIRE(MPI_Comm_rank(equilibriumProblem.GetCommunicator(), &rank) == MPI_SUCCESS);
|
||||
auto phaseCoordinate = preparedOperator.GetRootManifest().stateView(state).block(
|
||||
utils::blocks::fixed_central_density_phase.central_value_term
|
||||
);
|
||||
REQUIRE(phaseCoordinate.Size() == 1);
|
||||
if (rank == 0) {
|
||||
phaseCoordinate(0) = std::numeric_limits<double>::quiet_NaN();
|
||||
phaseCoordinate.SyncAliasMemory(state);
|
||||
}
|
||||
|
||||
const auto rejected = equilibriumProblem.TryPrepare(state, dependencies, rotation);
|
||||
REQUIRE_FALSE(rejected.has_value());
|
||||
CHECK(rejected.error().reason == operators::StellarEquilibriumPreparationRejectionReason::non_finite_physics);
|
||||
CHECK(rejected.error().stage == operators::StellarEquilibriumPreparationStage::model_specification);
|
||||
CHECK_FALSE(equilibriumProblem.IsPrepared());
|
||||
CHECK_THROWS_AS(equilibriumProblem.Prepare(state, dependencies, rotation), std::domain_error);
|
||||
|
||||
phaseCoordinate(0) = 0.0;
|
||||
phaseCoordinate.SyncAliasMemory(state);
|
||||
REQUIRE(equilibriumProblem.TryPrepare(state, dependencies, rotation).has_value());
|
||||
CHECK(equilibriumProblem.IsPrepared());
|
||||
}
|
||||
|
||||
@@ -415,6 +415,57 @@ TEST_CASE(
|
||||
CHECK(preparedOperator.IsPrepared());
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Displacement Residual Preserves Pressure Rejection Details",
|
||||
tags::barotrope_prepared
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const mfem::Vector density = prepared_displacement_residual_test_utils::make_density(f, 0.31);
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.67);
|
||||
const mfem::Vector gravityGradient = prepared_displacement_residual_test_utils::make_gravity_gradient(f, 0.47);
|
||||
mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize());
|
||||
gravityPotential = 0.0;
|
||||
|
||||
auto dependencies = prepared_displacement_residual_test_utils::make_dependencies();
|
||||
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
prepared_displacement_residual_test_utils::prepare_gravity_context(
|
||||
gravityContext, density, displacement, gravityGradient, gravityPotential, dependencies, 19
|
||||
);
|
||||
|
||||
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
||||
const mean_field::physics::RigidRotation rotation = prepared_displacement_residual_test_utils::make_rotation(0.83);
|
||||
mean_field::operators::PreparedDisplacementResidualOperator preparedOperator(
|
||||
f, *f.domainMapperStateless, equationOfState, gravityContext
|
||||
);
|
||||
|
||||
mfem::Vector enthalpy(prepared_displacement_residual_test_utils::make_enthalpy_map(f).reduced_size());
|
||||
enthalpy = std::numeric_limits<double>::max();
|
||||
|
||||
const auto rejected = preparedOperator.TryPrepare({.enthalpy = enthalpy}, dependencies, rotation);
|
||||
REQUIRE_FALSE(rejected.has_value());
|
||||
CHECK(rejected.error().source == mean_field::operators::DisplacementResidualPreparationRejectionSource::pressure);
|
||||
CHECK(
|
||||
rejected.error().reason ==
|
||||
mean_field::operators::DisplacementResidualPreparationRejectionReason::equation_of_state
|
||||
);
|
||||
CHECK(rejected.error().equationOfStateCode == mean_field::eos::EvaluationErrorCode::nonfinite_result);
|
||||
CHECK_FALSE(preparedOperator.IsPrepared());
|
||||
CHECK_THROWS_AS(
|
||||
preparedOperator.Prepare({.enthalpy = enthalpy}, dependencies, rotation), mean_field::eos::EvaluationError
|
||||
);
|
||||
|
||||
enthalpy = 1.0;
|
||||
++dependencies.enthalpy.revision;
|
||||
const auto accepted = preparedOperator.TryPrepare({.enthalpy = enthalpy}, dependencies, rotation);
|
||||
REQUIRE(accepted.has_value());
|
||||
CHECK(preparedOperator.IsPrepared());
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Displacement Residual Selectively Orchestrates Its Children",
|
||||
tags::barotrope_context_integration
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||
#include <mfem.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
@@ -9,6 +10,56 @@ using namespace mean_field;
|
||||
using Catch::Matchers::WithinAbs;
|
||||
namespace prepared_test = gravity_prepared_test_utils;
|
||||
|
||||
namespace {
|
||||
[[nodiscard]] mfem::Vector make_folding_displacement(const mean_field::fem::FEM &f) {
|
||||
mfem::ParGridFunction field(f.displacementFes.get());
|
||||
mfem::VectorFunctionCoefficient coefficient(
|
||||
f.mesh->Dimension(), [](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(position.Size());
|
||||
for (int dimension = 0; dimension < position.Size(); ++dimension) {
|
||||
value(dimension) = -2.0 * position(dimension);
|
||||
}
|
||||
}
|
||||
);
|
||||
field.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector displacementTrue;
|
||||
field.GetTrueDofs(displacementTrue);
|
||||
return displacementTrue;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Mapped Gravity Source Reports Invalid Candidate Geometry Without Unwinding",
|
||||
tags::gravity_prepared_unit &tags::geometry
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
operators::PreparedMappedGravitySourceOperator preparedOperator(f, *f.domainMapperStateless);
|
||||
mfem::Vector displacement = preparedOperator.GetDisplacementMap().gather(make_folding_displacement(f));
|
||||
|
||||
const auto rejected = preparedOperator.TryPrepare(displacement);
|
||||
REQUIRE_FALSE(rejected.has_value());
|
||||
CHECK(rejected.error().reason == operators::GravitySourcePreparationRejectionReason::invalid_mapping);
|
||||
CHECK(rejected.error().mappingStatus == mapping::MappingStatus::non_positive_determinant);
|
||||
CHECK_FALSE(preparedOperator.IsPrepared());
|
||||
CHECK_FALSE(preparedOperator.HasVariationData());
|
||||
CHECK(preparedOperator.GetPreparationCount() == 0);
|
||||
|
||||
CHECK_THROWS_AS(preparedOperator.Prepare(displacement), std::domain_error);
|
||||
CHECK_FALSE(preparedOperator.IsPrepared());
|
||||
CHECK(preparedOperator.GetPreparationCount() == 0);
|
||||
|
||||
displacement = 0.0;
|
||||
const auto recovered = preparedOperator.TryPrepare(displacement);
|
||||
REQUIRE(recovered.has_value());
|
||||
CHECK(preparedOperator.IsPrepared());
|
||||
CHECK(preparedOperator.HasVariationData());
|
||||
CHECK(preparedOperator.GetPreparationCount() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Mapped Gravity Source Matches Stateless Kernel",
|
||||
tags::gravity_prepared
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||
#include <cmath>
|
||||
#include <mfem.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
@@ -10,6 +11,56 @@ using namespace mean_field;
|
||||
using Catch::Matchers::WithinAbs;
|
||||
namespace prepared_test = gravity_prepared_test_utils;
|
||||
|
||||
namespace {
|
||||
[[nodiscard]] mfem::Vector make_folding_displacement(const mean_field::fem::FEM &f) {
|
||||
mfem::ParGridFunction field(f.displacementFes.get());
|
||||
mfem::VectorFunctionCoefficient coefficient(
|
||||
f.mesh->Dimension(), [](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(position.Size());
|
||||
for (int dimension = 0; dimension < position.Size(); ++dimension) {
|
||||
value(dimension) = -2.0 * position(dimension);
|
||||
}
|
||||
}
|
||||
);
|
||||
field.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector displacementTrue;
|
||||
field.GetTrueDofs(displacementTrue);
|
||||
return displacementTrue;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Mapped Hdiv Mass Reports Invalid Candidate Geometry Without Unwinding",
|
||||
tags::gravity_prepared_unit &tags::geometry
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
operators::PreparedMappedHDivMassOperator preparedOperator(f, *f.domainMapperStateless);
|
||||
mfem::Vector displacement = preparedOperator.GetDisplacementMap().gather(make_folding_displacement(f));
|
||||
|
||||
const auto rejected = preparedOperator.TryPrepare(displacement);
|
||||
REQUIRE_FALSE(rejected.has_value());
|
||||
CHECK(rejected.error().reason == operators::HDivMassPreparationRejectionReason::invalid_mapping);
|
||||
CHECK(rejected.error().mappingStatus == mapping::MappingStatus::non_positive_determinant);
|
||||
CHECK_FALSE(preparedOperator.IsPrepared());
|
||||
CHECK_FALSE(preparedOperator.HasVariationData());
|
||||
CHECK(preparedOperator.GetPreparationCount() == 0);
|
||||
|
||||
CHECK_THROWS_AS(preparedOperator.Prepare(displacement), std::domain_error);
|
||||
CHECK_FALSE(preparedOperator.IsPrepared());
|
||||
CHECK(preparedOperator.GetPreparationCount() == 0);
|
||||
|
||||
displacement = 0.0;
|
||||
const auto recovered = preparedOperator.TryPrepare(displacement);
|
||||
REQUIRE(recovered.has_value());
|
||||
CHECK(preparedOperator.IsPrepared());
|
||||
CHECK(preparedOperator.HasVariationData());
|
||||
CHECK(preparedOperator.GetPreparationCount() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Mapped Hdiv Mass Matches Stateless Kernel",
|
||||
tags::gravity_prepared
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <mfem.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
@@ -288,3 +289,75 @@ TEST_CASE(
|
||||
|
||||
CHECK(displacementEffect > 1.0e-8);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Hydrostatic Equilibrium Reports Candidate Mapping And Arithmetic Failures Without Unwinding",
|
||||
tags::barotrope_hydrostatic_prepared_residual &tags::unit
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator preparedOperator(f, *f.domainMapperStateless);
|
||||
|
||||
mfem::Vector enthalpy = prepared_hydrostatic_test_utils::make_enthalpy(f);
|
||||
mfem::Vector gravityPotential = prepared_hydrostatic_test_utils::make_gravity_potential(f);
|
||||
mfem::Vector displacement = field_dof_test_utils::make_supported_displacement(f, 0.73);
|
||||
displacement *= 1.0e200;
|
||||
|
||||
auto dependencies = prepared_hydrostatic_test_utils::make_dependencies();
|
||||
const auto rotation = prepared_hydrostatic_test_utils::make_rotation();
|
||||
|
||||
const auto mappingRejection = preparedOperator.TryPrepare(
|
||||
prepared_hydrostatic_test_utils::make_state(enthalpy, gravityPotential, displacement, 0.41), dependencies,
|
||||
rotation
|
||||
);
|
||||
REQUIRE_FALSE(mappingRejection.has_value());
|
||||
CHECK(
|
||||
(mappingRejection.error().reason ==
|
||||
mean_field::operators::HydrostaticEquilibriumPreparationRejectionReason::inverted_geometry ||
|
||||
mappingRejection.error().reason ==
|
||||
mean_field::operators::HydrostaticEquilibriumPreparationRejectionReason::non_finite_geometry)
|
||||
);
|
||||
CHECK(mappingRejection.error().mappingStatus != mean_field::mapping::MappingStatus::valid);
|
||||
CHECK_FALSE(preparedOperator.IsPrepared());
|
||||
CHECK_THROWS_AS(
|
||||
preparedOperator.Prepare(
|
||||
prepared_hydrostatic_test_utils::make_state(enthalpy, gravityPotential, displacement, 0.41), dependencies,
|
||||
rotation
|
||||
),
|
||||
std::domain_error
|
||||
);
|
||||
|
||||
displacement = 0.0;
|
||||
++dependencies.displacement.revision;
|
||||
++dependencies.rotation.revision;
|
||||
|
||||
const auto enormousRotation = prepared_hydrostatic_test_utils::make_rotation(1.0e200);
|
||||
const auto arithmeticRejection = preparedOperator.TryPrepare(
|
||||
prepared_hydrostatic_test_utils::make_state(enthalpy, gravityPotential, displacement, 0.41), dependencies,
|
||||
enormousRotation
|
||||
);
|
||||
REQUIRE_FALSE(arithmeticRejection.has_value());
|
||||
CHECK(
|
||||
arithmeticRejection.error().reason ==
|
||||
mean_field::operators::HydrostaticEquilibriumPreparationRejectionReason::non_finite_residual
|
||||
);
|
||||
CHECK_FALSE(preparedOperator.IsPrepared());
|
||||
CHECK_THROWS_AS(
|
||||
preparedOperator.Prepare(
|
||||
prepared_hydrostatic_test_utils::make_state(enthalpy, gravityPotential, displacement, 0.41), dependencies,
|
||||
enormousRotation
|
||||
),
|
||||
std::domain_error
|
||||
);
|
||||
|
||||
++dependencies.rotation.revision;
|
||||
const auto recovered = preparedOperator.TryPrepare(
|
||||
prepared_hydrostatic_test_utils::make_state(enthalpy, gravityPotential, displacement, 0.41), dependencies,
|
||||
rotation
|
||||
);
|
||||
REQUIRE(recovered.has_value());
|
||||
CHECK(recovered->preparedResidual);
|
||||
CHECK(preparedOperator.IsPrepared());
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
@@ -205,6 +206,7 @@ TEST_CASE(
|
||||
STATIC_REQUIRE_FALSE(std::is_copy_assignable_v<Operator>);
|
||||
STATIC_REQUIRE_FALSE(std::is_move_constructible_v<Operator>);
|
||||
STATIC_REQUIRE_FALSE(std::is_move_assignable_v<Operator>);
|
||||
STATIC_REQUIRE(std::is_trivially_copyable_v<mean_field::operators::MassNormalizationPreparationRejection>);
|
||||
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
@@ -271,6 +273,49 @@ TEST_CASE(
|
||||
CHECK(mass_normalization_test_utils::relative_error(measuredScale, expectedScale) < 5e-7);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Mass Normalization Reports Non-Finite Density Interpolation Without Unwinding",
|
||||
tags::barotrope_mass_normalization_context &tags::unit
|
||||
) {
|
||||
using namespace mass_normalization_test_utils;
|
||||
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
mfem::Vector density = make_constant_density(f, -0.5 * std::numeric_limits<double>::max());
|
||||
mfem::Vector displacement(f.displacementFes->GetTrueVSize());
|
||||
displacement = 0.0;
|
||||
auto dependencies = make_dependencies();
|
||||
|
||||
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
prepare_gravity_context(gravityContext, f, density, displacement, dependencies);
|
||||
|
||||
mean_field::operators::PreparedMassNormalizationOperator massOperator(f, *f.domainMapperStateless, gravityContext);
|
||||
|
||||
const auto rejected = massOperator.TryPrepare({.targetMass = std::numeric_limits<double>::max()}, dependencies);
|
||||
REQUIRE_FALSE(rejected.has_value());
|
||||
CHECK(
|
||||
rejected.error().reason ==
|
||||
mean_field::operators::MassNormalizationPreparationRejectionReason::non_finite_density_interpolation
|
||||
);
|
||||
CHECK_FALSE(massOperator.IsPrepared());
|
||||
CHECK_THROWS_AS(
|
||||
massOperator.Prepare({.targetMass = std::numeric_limits<double>::max()}, dependencies), std::domain_error
|
||||
);
|
||||
|
||||
density = make_constant_density(f, 1.0);
|
||||
++dependencies.density.revision;
|
||||
++dependencies.targetMass.revision;
|
||||
prepare_gravity_context(gravityContext, f, density, displacement, dependencies);
|
||||
|
||||
const auto accepted = massOperator.TryPrepare({.targetMass = 1.0}, dependencies);
|
||||
REQUIRE(accepted.has_value());
|
||||
CHECK(massOperator.IsPrepared());
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Mass Normalization Density Jacobian Matches Centered Difference",
|
||||
tags::barotrope_mass_normalization_jacobian &tags::accuracy
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <mfem.hpp>
|
||||
@@ -51,6 +52,26 @@ namespace prepared_pressure_force_test_utils {
|
||||
}
|
||||
};
|
||||
|
||||
[[nodiscard]] mfem::Vector make_affine_displacement(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double scale
|
||||
) {
|
||||
mfem::ParGridFunction field(f.displacementFes.get());
|
||||
mfem::VectorFunctionCoefficient coefficient(
|
||||
f.mesh->Dimension(), [scale](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(position.Size());
|
||||
for (int dimension = 0; dimension < position.Size(); ++dimension) {
|
||||
value(dimension) = scale * position(dimension);
|
||||
}
|
||||
}
|
||||
);
|
||||
field.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector result;
|
||||
field.GetTrueDofs(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
mfem::Vector make_positive_enthalpy_true(
|
||||
const mean_field::fem::FEM &f,
|
||||
@@ -265,6 +286,97 @@ TEST_CASE(
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Pressure Force Reports Expected EOS Rejections Without Unwinding",
|
||||
tags::barotrope &tags::pressure &tags::prepared &tags::unit
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const prepared_pressure_force_test_utils::Maps maps(f);
|
||||
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
||||
mean_field::operators::PreparedPressureForceOperator preparedOperator(f, *f.domainMapperStateless, equationOfState);
|
||||
|
||||
mfem::Vector enthalpy(maps.enthalpy.reduced_size());
|
||||
mfem::Vector displacement(maps.displacement.reduced_size());
|
||||
enthalpy = -1.0;
|
||||
displacement = 0.0;
|
||||
auto dependencies = prepared_pressure_force_test_utils::make_dependencies();
|
||||
|
||||
const auto outsideDomain =
|
||||
preparedOperator.TryPrepare({.enthalpy = enthalpy, .displacement = displacement}, dependencies);
|
||||
REQUIRE_FALSE(outsideDomain.has_value());
|
||||
CHECK(
|
||||
outsideDomain.error().reason ==
|
||||
mean_field::operators::PressureForcePreparationRejectionReason::equation_of_state
|
||||
);
|
||||
CHECK(outsideDomain.error().equationOfStateCode == mean_field::eos::EvaluationErrorCode::outside_domain);
|
||||
CHECK_FALSE(preparedOperator.IsPrepared());
|
||||
try {
|
||||
(void)preparedOperator.Prepare({.enthalpy = enthalpy, .displacement = displacement}, dependencies);
|
||||
FAIL("The compatibility Prepare overload accepted an out-of-domain EOS input.");
|
||||
} catch (const mean_field::eos::EvaluationError &error) {
|
||||
CHECK(error.code() == mean_field::eos::EvaluationErrorCode::outside_domain);
|
||||
}
|
||||
|
||||
enthalpy = std::numeric_limits<double>::max();
|
||||
++dependencies.enthalpy.revision;
|
||||
const auto rejected =
|
||||
preparedOperator.TryPrepare({.enthalpy = enthalpy, .displacement = displacement}, dependencies);
|
||||
REQUIRE_FALSE(rejected.has_value());
|
||||
CHECK(rejected.error().reason == mean_field::operators::PressureForcePreparationRejectionReason::equation_of_state);
|
||||
CHECK(rejected.error().equationOfStateCode == mean_field::eos::EvaluationErrorCode::nonfinite_result);
|
||||
CHECK_FALSE(preparedOperator.IsPrepared());
|
||||
CHECK_THROWS_AS(
|
||||
preparedOperator.Prepare({.enthalpy = enthalpy, .displacement = displacement}, dependencies),
|
||||
mean_field::eos::EvaluationError
|
||||
);
|
||||
|
||||
enthalpy = 1.0;
|
||||
++dependencies.enthalpy.revision;
|
||||
const auto accepted =
|
||||
preparedOperator.TryPrepare({.enthalpy = enthalpy, .displacement = displacement}, dependencies);
|
||||
REQUIRE(accepted.has_value());
|
||||
CHECK(preparedOperator.IsPrepared());
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Pressure Force Reports Invalid Candidate Geometry Without Unwinding",
|
||||
tags::barotrope &tags::pressure &tags::prepared &tags::geometry &tags::unit
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const prepared_pressure_force_test_utils::Maps maps(f);
|
||||
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
||||
mean_field::operators::PreparedPressureForceOperator preparedOperator(f, *f.domainMapperStateless, equationOfState);
|
||||
|
||||
mfem::Vector enthalpy(maps.enthalpy.reduced_size());
|
||||
enthalpy = 1.0;
|
||||
mfem::Vector displacement =
|
||||
maps.displacement.gather(prepared_pressure_force_test_utils::make_affine_displacement(f, -2.0));
|
||||
auto dependencies = prepared_pressure_force_test_utils::make_dependencies();
|
||||
|
||||
const auto rejected =
|
||||
preparedOperator.TryPrepare({.enthalpy = enthalpy, .displacement = displacement}, dependencies);
|
||||
REQUIRE_FALSE(rejected.has_value());
|
||||
CHECK(rejected.error().reason == mean_field::operators::PressureForcePreparationRejectionReason::invalid_mapping);
|
||||
CHECK(rejected.error().mappingStatus == mean_field::mapping::MappingStatus::non_positive_determinant);
|
||||
CHECK_FALSE(preparedOperator.IsPrepared());
|
||||
CHECK_THROWS_AS(
|
||||
preparedOperator.Prepare({.enthalpy = enthalpy, .displacement = displacement}, dependencies), std::domain_error
|
||||
);
|
||||
|
||||
displacement = 0.0;
|
||||
++dependencies.displacement.revision;
|
||||
const auto accepted =
|
||||
preparedOperator.TryPrepare({.enthalpy = enthalpy, .displacement = displacement}, dependencies);
|
||||
REQUIRE(accepted.has_value());
|
||||
CHECK(preparedOperator.IsPrepared());
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Pressure Force Jacobian Matches Full Stateless Columns "
|
||||
"Through FieldDof Restriction",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
@@ -127,6 +128,26 @@ namespace rotational_displacement_force_test_utils {
|
||||
return direction;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_affine_displacement(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double scale
|
||||
) {
|
||||
mfem::ParGridFunction field(f.displacementFes.get());
|
||||
mfem::VectorFunctionCoefficient coefficient(
|
||||
f.mesh->Dimension(), [scale](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(position.Size());
|
||||
for (int dimension = 0; dimension < position.Size(); ++dimension) {
|
||||
value(dimension) = scale * position(dimension);
|
||||
}
|
||||
}
|
||||
);
|
||||
field.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector result;
|
||||
field.GetTrueDofs(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::physics::RigidRotation make_rotation(const double scale = 1.0) {
|
||||
mfem::Vector angularVelocity(3);
|
||||
angularVelocity(0) = scale * 0.17;
|
||||
@@ -377,6 +398,85 @@ TEST_CASE(
|
||||
CHECK(rotational_displacement_force_test_utils::global_norm(zeroRotationResidual, f.mesh->GetComm()) == 0.0);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Rotational Displacement Force Reports Candidate Mapping And Arithmetic Rejections",
|
||||
tags::rotation_prepared_unit
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
mfem::Vector density = rotational_displacement_force_test_utils::make_density(f, 0.29);
|
||||
mfem::Vector displacement = rotational_displacement_force_test_utils::make_affine_displacement(f, -2.0);
|
||||
const mean_field::physics::RigidRotation rotation = rotational_displacement_force_test_utils::make_rotation(0.83);
|
||||
mfem::Vector residual;
|
||||
|
||||
const auto invalidMapping = mean_field::operators::kernels::try_apply_rotational_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, rotation, density, displacement, residual
|
||||
);
|
||||
REQUIRE_FALSE(invalidMapping.has_value());
|
||||
CHECK(
|
||||
invalidMapping.error().reason ==
|
||||
mean_field::operators::kernels::RotationalDisplacementForceRejectionReason::invalid_mapping
|
||||
);
|
||||
CHECK(invalidMapping.error().mappingStatus == mean_field::mapping::MappingStatus::non_positive_determinant);
|
||||
CHECK_THROWS_AS(
|
||||
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, rotation, density, displacement, residual
|
||||
),
|
||||
std::domain_error
|
||||
);
|
||||
|
||||
displacement = 0.0;
|
||||
density = 1.0e200;
|
||||
const mean_field::physics::RigidRotation extremeRotation =
|
||||
rotational_displacement_force_test_utils::make_rotation(1.0e100);
|
||||
const auto nonFiniteArithmetic = mean_field::operators::kernels::try_apply_rotational_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, extremeRotation, density, displacement, residual
|
||||
);
|
||||
REQUIRE_FALSE(nonFiniteArithmetic.has_value());
|
||||
CHECK(
|
||||
nonFiniteArithmetic.error().reason ==
|
||||
mean_field::operators::kernels::RotationalDisplacementForceRejectionReason::non_finite_arithmetic
|
||||
);
|
||||
|
||||
mean_field::operators::PreparedRotationalDisplacementForceOperator preparedOperator(f, *f.domainMapperStateless);
|
||||
const auto &context = preparedOperator.GetContext();
|
||||
auto dependencies = rotational_displacement_force_test_utils::make_dependencies();
|
||||
mfem::Vector reducedDensity = context.GetDensityMap().gather(density);
|
||||
const mfem::Vector reducedDisplacement = context.GetDisplacementMap().gather(displacement);
|
||||
const auto preparedRejection = preparedOperator.TryPrepare(
|
||||
{.density = reducedDensity, .displacement = reducedDisplacement}, dependencies, extremeRotation
|
||||
);
|
||||
REQUIRE_FALSE(preparedRejection.has_value());
|
||||
CHECK(
|
||||
preparedRejection.error().reason ==
|
||||
mean_field::operators::kernels::RotationalDisplacementForceRejectionReason::non_finite_arithmetic
|
||||
);
|
||||
CHECK_FALSE(preparedOperator.IsPrepared());
|
||||
CHECK_THROWS_AS(
|
||||
preparedOperator.Prepare(
|
||||
{.density = reducedDensity, .displacement = reducedDisplacement}, dependencies, extremeRotation
|
||||
),
|
||||
std::domain_error
|
||||
);
|
||||
|
||||
density = rotational_displacement_force_test_utils::make_density(f, 0.29);
|
||||
reducedDensity = context.GetDensityMap().gather(density);
|
||||
++dependencies.density.revision;
|
||||
++dependencies.rotation.revision;
|
||||
const auto preparedAccepted = preparedOperator.TryPrepare(
|
||||
{.density = reducedDensity, .displacement = reducedDisplacement}, dependencies, rotation
|
||||
);
|
||||
REQUIRE(preparedAccepted.has_value());
|
||||
CHECK(preparedOperator.IsPrepared());
|
||||
|
||||
const auto accepted = mean_field::operators::kernels::try_apply_rotational_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, rotation, density, displacement, residual
|
||||
);
|
||||
CHECK(accepted.has_value());
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Rotational Displacement Force Reprepares Selectively",
|
||||
tags::rotation_prepared
|
||||
|
||||
@@ -1505,6 +1505,69 @@ TEST_CASE(
|
||||
CHECK(targetReport.assembledResidual);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Stellar Trial Preparation Reports Folded Geometry Without Unwinding",
|
||||
tags::reduced_stellar_geometry &tags::prepared &tags::geometry &tags::unit
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
|
||||
const auto stellarModel = stellar_equilibrium_test_utils::make_stellar_model(barotrope, 1.15);
|
||||
const auto parameterGeometry = stellarModel.compileDomainDeformation(f);
|
||||
const auto &surface = parameterGeometry.surfaceDeformationPrescription();
|
||||
|
||||
mean_field::operators::PreparedStellarEquilibriumOperator stellarOperator(
|
||||
f, *f.domainMapperStateless, stellarModel
|
||||
);
|
||||
const auto &layout = stellarOperator.GetLayout();
|
||||
mfem::Vector state = stellar_equilibrium_test_utils::make_state(f, layout);
|
||||
auto dependencies = stellar_equilibrium_test_utils::make_dependencies();
|
||||
const auto rotation = stellar_equilibrium_test_utils::make_zero_rotation();
|
||||
|
||||
const double finiteStateValue = state(0);
|
||||
state(0) = std::numeric_limits<double>::quiet_NaN();
|
||||
const auto nonFiniteState = stellarOperator.TryPrepare(state, dependencies, rotation);
|
||||
REQUIRE_FALSE(nonFiniteState.has_value());
|
||||
CHECK(
|
||||
nonFiniteState.error().reason ==
|
||||
mean_field::operators::StellarEquilibriumPreparationRejectionReason::non_finite_physics
|
||||
);
|
||||
CHECK_FALSE(stellarOperator.IsPrepared());
|
||||
CHECK_THROWS_AS(stellarOperator.Prepare(state, dependencies, rotation), std::domain_error);
|
||||
state(0) = finiteStateValue;
|
||||
|
||||
mfem::Vector foldingParameters(surface.parameterCount());
|
||||
for (int parameter = 0; parameter < foldingParameters.Size(); ++parameter) {
|
||||
foldingParameters(parameter) = -1.5 * surface.referenceRadius(parameter);
|
||||
}
|
||||
stellar_equilibrium_test_utils::assign_value_block(
|
||||
state, layout, stellar_equilibrium_test_utils::displacementValue, foldingParameters
|
||||
);
|
||||
|
||||
const auto rejected = stellarOperator.TryPrepare(state, dependencies, rotation);
|
||||
REQUIRE_FALSE(rejected.has_value());
|
||||
CHECK(
|
||||
rejected.error().reason ==
|
||||
mean_field::operators::StellarEquilibriumPreparationRejectionReason::inverted_geometry
|
||||
);
|
||||
CHECK(rejected.error().stage == mean_field::operators::StellarEquilibriumPreparationStage::generated_geometry);
|
||||
CHECK(std::isfinite(rejected.error().minimumJacobianDeterminant));
|
||||
CHECK(rejected.error().minimumJacobianDeterminant <= 0.0);
|
||||
CHECK_FALSE(stellarOperator.IsPrepared());
|
||||
CHECK_THROWS_AS(stellarOperator.Prepare(state, dependencies, rotation), std::domain_error);
|
||||
|
||||
foldingParameters = 0.0;
|
||||
stellar_equilibrium_test_utils::assign_value_block(
|
||||
state, layout, stellar_equilibrium_test_utils::displacementValue, foldingParameters
|
||||
);
|
||||
const auto accepted = stellarOperator.TryPrepare(state, dependencies, rotation);
|
||||
REQUIRE(accepted.has_value());
|
||||
CHECK(accepted->generatedGeometry.isOrientationPreserving());
|
||||
CHECK(stellarOperator.IsPrepared());
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Accepted Reduced Geometries Remain Valid Across Prepared Stellar Physics Quadrature Rules",
|
||||
tags::reduced_stellar_geometry &tags::prepared &tags::geometry &tags::self_consistency
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <concepts>
|
||||
#include <limits>
|
||||
#include <numbers>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@@ -12,8 +13,7 @@ import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace outer_manifest_report_test {
|
||||
template <mean_field::model::StellarModelType Model>
|
||||
class PreparedEarlierMultiplier;
|
||||
template <mean_field::model::StellarModelType Model> class PreparedEarlierMultiplier;
|
||||
|
||||
class EarlierMultiplier final {
|
||||
public:
|
||||
@@ -36,12 +36,9 @@ namespace outer_manifest_report_test {
|
||||
"R_a",
|
||||
"specific_energy",
|
||||
"specific_energy">>;
|
||||
using EquilibriumPhysics =
|
||||
mean_field::operators::SpecificationEquilibriumPhysics<
|
||||
PreparedEarlierMultiplier>;
|
||||
using EquilibriumPhysics = mean_field::operators::SpecificationEquilibriumPhysics<PreparedEarlierMultiplier>;
|
||||
|
||||
explicit EarlierMultiplier(const Parameters parameters) noexcept
|
||||
: m_target(parameters.target) {
|
||||
explicit EarlierMultiplier(const Parameters parameters) noexcept : m_target(parameters.target) {
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::dimensions::SpecificEnergyValue target() const noexcept {
|
||||
@@ -52,20 +49,20 @@ namespace outer_manifest_report_test {
|
||||
mean_field::dimensions::SpecificEnergyValue m_target;
|
||||
};
|
||||
|
||||
template <mean_field::model::StellarModelType Model>
|
||||
class PreparedEarlierMultiplier final {
|
||||
template <mean_field::model::StellarModelType Model> class PreparedEarlierMultiplier final {
|
||||
public:
|
||||
using Report = mean_field::operators::EmptySpecificationPreparationReport;
|
||||
|
||||
explicit PreparedEarlierMultiplier(const EarlierMultiplier &) noexcept {
|
||||
}
|
||||
|
||||
template <typename StateView>
|
||||
[[nodiscard]] Report PrepareAfterPhysical(const StateView &) noexcept {
|
||||
template <typename StateView> [[nodiscard]] Report PrepareAfterPhysical(const StateView &) noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename Equation, typename Row>
|
||||
template <
|
||||
typename Equation,
|
||||
typename Row>
|
||||
[[nodiscard]] mean_field::stellar::StructuralZero AddResidual(
|
||||
Equation,
|
||||
Row &
|
||||
@@ -73,9 +70,15 @@ namespace outer_manifest_report_test {
|
||||
return mean_field::stellar::structuralZero;
|
||||
}
|
||||
|
||||
template <typename Equation, typename State, typename Direction, typename Row>
|
||||
template <
|
||||
typename Equation,
|
||||
typename State,
|
||||
typename Direction,
|
||||
typename Row>
|
||||
[[nodiscard]] mean_field::stellar::StructuralZero AddJacobianAction(
|
||||
mean_field::stellar::Derivative<Equation, State>,
|
||||
mean_field::stellar::Derivative<
|
||||
Equation,
|
||||
State>,
|
||||
const Direction &,
|
||||
Row &
|
||||
) const noexcept {
|
||||
@@ -89,40 +92,38 @@ namespace outer_manifest_report_test {
|
||||
} // namespace outer_manifest_report_test
|
||||
|
||||
namespace {
|
||||
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 CentralDensityModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
using CentralDensityModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
mean_field::eos::Polytrope,
|
||||
mean_field::surface::Isobaric,
|
||||
mean_field::integral::FixedTotalMass,
|
||||
mean_field::constraint::FixedCentralDensity>>;
|
||||
|
||||
using AngularMomentumModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
using AngularMomentumModel = 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 AngularMomentumCentralDensityModel =
|
||||
mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
mean_field::eos::Polytrope,
|
||||
mean_field::surface::Isobaric,
|
||||
mean_field::integral::FixedTotalMass,
|
||||
mean_field::integral::FixedAngularMomentum,
|
||||
mean_field::constraint::FixedCentralDensity>>;
|
||||
using AngularMomentumCentralDensityModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
mean_field::eos::Polytrope,
|
||||
mean_field::surface::Isobaric,
|
||||
mean_field::integral::FixedTotalMass,
|
||||
mean_field::integral::FixedAngularMomentum,
|
||||
mean_field::constraint::FixedCentralDensity>>;
|
||||
|
||||
using IncompleteModel =
|
||||
mean_field::model::StellarModel<mean_field::models::SpecificationSet<mean_field::eos::Polytrope>>;
|
||||
|
||||
using EarlierMultiplierModel =
|
||||
mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
mean_field::eos::Polytrope,
|
||||
mean_field::surface::Isobaric,
|
||||
outer_manifest_report_test::EarlierMultiplier,
|
||||
mean_field::integral::FixedTotalMass>>;
|
||||
using EarlierMultiplierModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
mean_field::eos::Polytrope,
|
||||
mean_field::surface::Isobaric,
|
||||
outer_manifest_report_test::EarlierMultiplier,
|
||||
mean_field::integral::FixedTotalMass>>;
|
||||
|
||||
template <typename Candidate>
|
||||
concept HasLegacyNumericalModelAdapter = requires { typename Candidate::NumericalModelAdapter; };
|
||||
@@ -166,8 +167,8 @@ TEST_CASE(
|
||||
) {
|
||||
using namespace mean_field;
|
||||
|
||||
using BaseProblem = equilibrium::StellarEquilibriumProblem<BaseModel>;
|
||||
using CentralDensityProblem = equilibrium::StellarEquilibriumProblem<CentralDensityModel>;
|
||||
using BaseProblem = equilibrium::StellarEquilibriumProblem<BaseModel>;
|
||||
using CentralDensityProblem = equilibrium::StellarEquilibriumProblem<CentralDensityModel>;
|
||||
using AngularMomentumProblem = equilibrium::StellarEquilibriumProblem<AngularMomentumModel>;
|
||||
using AngularMomentumCentralDensityProblem =
|
||||
equilibrium::StellarEquilibriumProblem<AngularMomentumCentralDensityModel>;
|
||||
@@ -178,14 +179,17 @@ TEST_CASE(
|
||||
STATIC_CHECK(equilibrium::StellarEquilibriumModel<AngularMomentumCentralDensityModel>);
|
||||
STATIC_CHECK(equilibrium::StellarEquilibriumModel<EarlierMultiplierModel>);
|
||||
STATIC_CHECK_FALSE(equilibrium::StellarEquilibriumModel<IncompleteModel>);
|
||||
STATIC_CHECK_FALSE(operators::StellarEquilibriumRuntimeContribution<
|
||||
outer_manifest_report_test::EarlierMultiplier>::registered);
|
||||
STATIC_CHECK_FALSE(operators::stellarEquilibriumBackendRuntimeAuthorized<
|
||||
outer_manifest_report_test::EarlierMultiplier,
|
||||
EarlierMultiplierModel>);
|
||||
STATIC_CHECK(operators::StellarEquilibriumPhysicsAvailableFor<
|
||||
outer_manifest_report_test::EarlierMultiplier,
|
||||
EarlierMultiplierModel>);
|
||||
STATIC_CHECK_FALSE(
|
||||
operators::StellarEquilibriumRuntimeContribution<outer_manifest_report_test::EarlierMultiplier>::registered
|
||||
);
|
||||
STATIC_CHECK_FALSE(
|
||||
operators::stellarEquilibriumBackendRuntimeAuthorized<
|
||||
outer_manifest_report_test::EarlierMultiplier, EarlierMultiplierModel>
|
||||
);
|
||||
STATIC_CHECK(
|
||||
operators::StellarEquilibriumPhysicsAvailableFor<
|
||||
outer_manifest_report_test::EarlierMultiplier, EarlierMultiplierModel>
|
||||
);
|
||||
STATIC_CHECK_FALSE(std::same_as<BaseProblem, CentralDensityProblem>);
|
||||
STATIC_CHECK(BaseProblem::symbolicallySquare);
|
||||
STATIC_CHECK(CentralDensityProblem::symbolicallySquare);
|
||||
@@ -213,20 +217,23 @@ TEST_CASE(
|
||||
typename AngularMomentumProblem::PreparedOperatorType,
|
||||
operators::PreparedVariadicStellarEquilibriumOperator<AngularMomentumModel>>
|
||||
);
|
||||
STATIC_CHECK_FALSE(std::same_as<
|
||||
typename BaseProblem::PreparedOperatorType,
|
||||
typename CentralDensityProblem::PreparedOperatorType>);
|
||||
STATIC_CHECK_FALSE(std::same_as<
|
||||
typename AngularMomentumProblem::PreparedOperatorType,
|
||||
typename AngularMomentumCentralDensityProblem::PreparedOperatorType>);
|
||||
STATIC_CHECK_FALSE(
|
||||
std::same_as<typename BaseProblem::PreparedOperatorType, typename CentralDensityProblem::PreparedOperatorType>
|
||||
);
|
||||
STATIC_CHECK_FALSE(
|
||||
std::same_as<
|
||||
typename AngularMomentumProblem::PreparedOperatorType,
|
||||
typename AngularMomentumCentralDensityProblem::PreparedOperatorType>
|
||||
);
|
||||
STATIC_CHECK(AngularMomentumProblem::FormType::value_block_count == 7);
|
||||
STATIC_CHECK(AngularMomentumCentralDensityProblem::FormType::value_block_count == 8);
|
||||
STATIC_CHECK(std::same_as<
|
||||
typename BaseProblem::FormType,
|
||||
utils::blocks::surface_deformed_stellar_equilibrium_form>);
|
||||
STATIC_CHECK(std::same_as<
|
||||
typename CentralDensityProblem::FormType,
|
||||
utils::blocks::central_density_bordered_stellar_equilibrium_form>);
|
||||
STATIC_CHECK(
|
||||
std::same_as<typename BaseProblem::FormType, utils::blocks::surface_deformed_stellar_equilibrium_form>
|
||||
);
|
||||
STATIC_CHECK(
|
||||
std::same_as<
|
||||
typename CentralDensityProblem::FormType, utils::blocks::central_density_bordered_stellar_equilibrium_form>
|
||||
);
|
||||
STATIC_CHECK(
|
||||
std::same_as<
|
||||
typename BaseProblem::CompiledSurfaceConstraintType,
|
||||
@@ -242,34 +249,29 @@ TEST_CASE(
|
||||
) {
|
||||
using namespace mean_field;
|
||||
|
||||
utils::Args arguments = test_utils::setup_args();
|
||||
utils::Args arguments = test_utils::setup_args();
|
||||
fem::FEM finiteElements = fem::setup_fem(arguments.mesh_file, arguments, 0);
|
||||
REQUIRE(finiteElements.okay());
|
||||
|
||||
constexpr double radius = utils::RADIUS;
|
||||
constexpr double mass = utils::MASS;
|
||||
constexpr double radius = utils::RADIUS;
|
||||
constexpr double mass = utils::MASS;
|
||||
constexpr double targetAngularMomentum = 0.1;
|
||||
const double polytropicConstant = 2.0 * utils::G * radius * radius / std::numbers::pi_v<double>;
|
||||
const double seedCentralDensity =
|
||||
std::numbers::pi_v<double> * mass / (4.0 * radius * radius * radius);
|
||||
auto model = model::StellarModel(
|
||||
const double polytropicConstant = 2.0 * utils::G * radius * radius / std::numbers::pi_v<double>;
|
||||
const double seedCentralDensity = std::numbers::pi_v<double> * mass / (4.0 * radius * radius * radius);
|
||||
auto model = model::StellarModel(
|
||||
eos::Polytrope({.n = 1.0, .K = polytropicConstant}),
|
||||
surface::Isobaric({.Psurf = dimensions::PressureValue{0.0}}),
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{mass}}),
|
||||
integral::FixedAngularMomentum({
|
||||
.Jtotal = dimensions::AngularMomentumValue{targetAngularMomentum},
|
||||
.axis = {0.0, 0.0, 3.0}
|
||||
})
|
||||
integral::FixedAngularMomentum(
|
||||
{.Jtotal = dimensions::AngularMomentumValue{targetAngularMomentum}, .axis = {0.0, 0.0, 3.0}}
|
||||
)
|
||||
);
|
||||
auto problem = equilibrium::discretize(model, finiteElements);
|
||||
auto problem = equilibrium::discretize(model, std::move(finiteElements));
|
||||
auto projected = seed::makeProjectedEquilibriumState(
|
||||
problem,
|
||||
seed::LaneEmden({
|
||||
.centralDensity = dimensions::DensityValue{seedCentralDensity},
|
||||
.radialSampleCount = 1024
|
||||
})
|
||||
seed::LaneEmden({.centralDensity = dimensions::DensityValue{seedCentralDensity}, .radialSampleCount = 1024})
|
||||
);
|
||||
auto dependencies = make_dependencies();
|
||||
auto dependencies = make_dependencies();
|
||||
|
||||
const auto preparation = problem.Prepare(projected.values, dependencies);
|
||||
CHECK(preparation.generatedPhysicalControl);
|
||||
@@ -284,7 +286,7 @@ TEST_CASE(
|
||||
CHECK(std::abs(angularReport.scaledResidual) < 7.0e-4);
|
||||
|
||||
mfem::Vector direction(problem.StateSize());
|
||||
direction = 0.0;
|
||||
direction = 0.0;
|
||||
mfem::Vector angularVelocityDirection = problem.GetManifest().stateView(direction).block(
|
||||
utils::blocks::fixed_angular_momentum_constraint.angular_velocity_term
|
||||
);
|
||||
@@ -309,15 +311,15 @@ TEST_CASE(
|
||||
plusResidual -= minusResidual;
|
||||
plusResidual /= 2.0 * step;
|
||||
|
||||
auto analyticView = problem.GetManifest().residualView(analyticAction);
|
||||
auto differenceView = problem.GetManifest().residualView(plusResidual);
|
||||
auto analyticView = problem.GetManifest().residualView(analyticAction);
|
||||
auto differenceView = problem.GetManifest().residualView(plusResidual);
|
||||
const auto blockError = [&](const auto &term) {
|
||||
const mfem::Vector analytic = analyticView.block(term);
|
||||
const mfem::Vector analytic = analyticView.block(term);
|
||||
const mfem::Vector difference = differenceView.block(term);
|
||||
return relative_difference(analytic, difference);
|
||||
};
|
||||
|
||||
const double surfaceError = blockError(utils::blocks::surface_deformation_field.shape_equilibrium_term);
|
||||
const double surfaceError = blockError(utils::blocks::surface_deformation_field.shape_equilibrium_term);
|
||||
const double enthalpyError = blockError(utils::blocks::enthalpy_field.specific_term);
|
||||
const double angularMomentumError =
|
||||
blockError(utils::blocks::fixed_angular_momentum_constraint.angular_velocity_term);
|
||||
@@ -335,13 +337,48 @@ TEST_CASE(
|
||||
CHECK(analyticView.block(utils::blocks::density_field.mass_term).Norml2() == 0.0);
|
||||
CHECK(analyticView.block(utils::blocks::fixed_total_mass_constraint.mass_normalization_term).Norml2() == 0.0);
|
||||
|
||||
mfem::Vector nonFiniteAngularVelocityState(projected.values);
|
||||
auto nonFiniteAngularVelocity = problem.GetManifest()
|
||||
.stateView(nonFiniteAngularVelocityState)
|
||||
.block(utils::blocks::fixed_angular_momentum_constraint.angular_velocity_term);
|
||||
REQUIRE(nonFiniteAngularVelocity.Size() == 1);
|
||||
nonFiniteAngularVelocity(0) = std::numeric_limits<double>::quiet_NaN();
|
||||
nonFiniteAngularVelocity.SyncAliasMemory(nonFiniteAngularVelocityState);
|
||||
const auto nonFiniteControl = problem.TryPrepare(nonFiniteAngularVelocityState, dependencies);
|
||||
REQUIRE_FALSE(nonFiniteControl.has_value());
|
||||
CHECK(
|
||||
nonFiniteControl.error().reason == operators::StellarEquilibriumPreparationRejectionReason::non_finite_physics
|
||||
);
|
||||
CHECK_FALSE(problem.IsPrepared());
|
||||
REQUIRE(problem.TryPrepare(projected.values, dependencies).has_value());
|
||||
|
||||
mfem::Vector negativeDensityState(projected.values);
|
||||
auto negativeDensity =
|
||||
problem.GetManifest().stateView(negativeDensityState).block(utils::blocks::density_field.mass_term);
|
||||
negativeDensity *= -1.0;
|
||||
negativeDensity.SyncAliasMemory(negativeDensityState);
|
||||
auto negativeDensityDependencies = dependencies;
|
||||
++negativeDensityDependencies.density.revision;
|
||||
const auto inadmissibleMoment = problem.TryPrepare(negativeDensityState, negativeDensityDependencies);
|
||||
REQUIRE_FALSE(inadmissibleMoment.has_value());
|
||||
CHECK(
|
||||
inadmissibleMoment.error().reason ==
|
||||
operators::StellarEquilibriumPreparationRejectionReason::inadmissible_physics
|
||||
);
|
||||
CHECK_FALSE(problem.IsPrepared());
|
||||
++negativeDensityDependencies.density.revision;
|
||||
REQUIRE(problem.TryPrepare(projected.values, negativeDensityDependencies).has_value());
|
||||
CHECK(problem.IsPrepared());
|
||||
|
||||
auto zeroModel = model::StellarModel(
|
||||
eos::Polytrope({.n = 1.0, .K = polytropicConstant}),
|
||||
surface::Isobaric({.Psurf = dimensions::PressureValue{0.0}}),
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{mass}}),
|
||||
integral::FixedAngularMomentum({.Jtotal = dimensions::AngularMomentumValue{0.0}})
|
||||
);
|
||||
auto zeroProblem = equilibrium::discretize(zeroModel, finiteElements);
|
||||
fem::FEM zeroFiniteElements = fem::setup_fem(arguments.mesh_file, arguments, 0);
|
||||
REQUIRE(zeroFiniteElements.okay());
|
||||
auto zeroProblem = equilibrium::discretize(zeroModel, std::move(zeroFiniteElements));
|
||||
mfem::Vector zeroState(projected.values);
|
||||
zeroProblem.GetManifest().stateView(zeroState).block(
|
||||
utils::blocks::fixed_angular_momentum_constraint.angular_velocity_term
|
||||
@@ -361,47 +398,37 @@ TEST_CASE(
|
||||
) {
|
||||
using namespace mean_field;
|
||||
using Form = operators::CompiledStellarEquilibriumForm<EarlierMultiplierModel>;
|
||||
using EarlierValue = utils::blocks::generated_value_block<
|
||||
models::MultiplierFor<outer_manifest_report_test::EarlierMultiplier>>;
|
||||
using EarlierValue =
|
||||
utils::blocks::generated_value_block<models::MultiplierFor<outer_manifest_report_test::EarlierMultiplier>>;
|
||||
using MassValue = utils::blocks::fixed_total_mass::mass_normalization::value;
|
||||
|
||||
STATIC_CHECK(utils::blocks::type_index_v<
|
||||
EarlierValue,
|
||||
typename Form::value_blocks> == 5);
|
||||
STATIC_CHECK(utils::blocks::type_index_v<
|
||||
MassValue,
|
||||
typename Form::value_blocks> == 6);
|
||||
STATIC_CHECK(utils::blocks::type_index_v<EarlierValue, typename Form::value_blocks> == 5);
|
||||
STATIC_CHECK(utils::blocks::type_index_v<MassValue, typename Form::value_blocks> == 6);
|
||||
|
||||
utils::Args arguments = test_utils::setup_args();
|
||||
utils::Args arguments = test_utils::setup_args();
|
||||
fem::FEM finiteElements = fem::setup_fem(arguments.mesh_file, arguments, 0);
|
||||
REQUIRE(finiteElements.okay());
|
||||
|
||||
auto model = model::StellarModel(
|
||||
eos::Polytrope({.n = 1.0, .K = 0.25}),
|
||||
surface::Isobaric({.Psurf = dimensions::PressureValue{0.0}}),
|
||||
outer_manifest_report_test::EarlierMultiplier({
|
||||
.target = dimensions::SpecificEnergyValue{0.75}}),
|
||||
eos::Polytrope({.n = 1.0, .K = 0.25}), surface::Isobaric({.Psurf = dimensions::PressureValue{0.0}}),
|
||||
outer_manifest_report_test::EarlierMultiplier({.target = dimensions::SpecificEnergyValue{0.75}}),
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{1.25}})
|
||||
);
|
||||
auto problem = equilibrium::discretize(model, finiteElements);
|
||||
auto problem = equilibrium::discretize(model, std::move(finiteElements));
|
||||
|
||||
mfem::Vector state(problem.StateSize());
|
||||
state = 0.0;
|
||||
const auto stateView = problem.GetManifest().stateView(state);
|
||||
stateView.block(utils::blocks::density_field.mass_term) = 1.0;
|
||||
state = 0.0;
|
||||
const auto stateView = problem.GetManifest().stateView(state);
|
||||
stateView.block(utils::blocks::density_field.mass_term) = 1.0;
|
||||
stateView.block(utils::blocks::enthalpy_field.specific_term) = 1.0;
|
||||
stateView.block(utils::blocks::fixed_total_mass_constraint.mass_normalization_term) = 0.25;
|
||||
|
||||
const auto preparation = problem.Prepare(
|
||||
state,
|
||||
make_dependencies(),
|
||||
make_zero_rotation()
|
||||
);
|
||||
REQUIRE(preparation.physical.DidAnyWork());
|
||||
const auto preparation = problem.TryPrepare(state, make_dependencies(), make_zero_rotation());
|
||||
REQUIRE(preparation.has_value());
|
||||
REQUIRE(preparation->physical.DidAnyWork());
|
||||
|
||||
const auto report = problem.GetPreparedOperator().GetFixedMassReport();
|
||||
const auto &outerDescriptor =
|
||||
problem.GetManifest().template specification<models::FixedTotalMass>();
|
||||
const auto report = problem.GetPreparedOperator().GetFixedMassReport();
|
||||
const auto &outerDescriptor = problem.GetManifest().template specification<models::FixedTotalMass>();
|
||||
CHECK(report.descriptor.stableId == outerDescriptor.stableId);
|
||||
CHECK(report.descriptor.valueBlock == outerDescriptor.valueBlock);
|
||||
CHECK(report.descriptor.residualBlock == outerDescriptor.residualBlock);
|
||||
@@ -409,8 +436,7 @@ TEST_CASE(
|
||||
CHECK(report.descriptor.residualBlock == 6);
|
||||
CHECK(report.descriptor.target == 1.25);
|
||||
CHECK(report.dimensionalResidual == report.achieved - report.descriptor.target);
|
||||
CHECK(report.scaledResidual ==
|
||||
report.dimensionalResidual / report.descriptor.residualScale);
|
||||
CHECK(report.scaledResidual == report.dimensionalResidual / report.descriptor.residualScale);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
@@ -419,32 +445,38 @@ TEST_CASE(
|
||||
) {
|
||||
using namespace mean_field;
|
||||
|
||||
utils::Args args = test_utils::setup_args();
|
||||
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
utils::Args args = test_utils::setup_args();
|
||||
fem::FEM legacyFiniteElements = fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(legacyFiniteElements.okay());
|
||||
|
||||
fem::FEM modelDrivenFiniteElements = fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(modelDrivenFiniteElements.okay());
|
||||
const MPI_Comm modelDrivenCommunicator = modelDrivenFiniteElements.mesh->GetComm();
|
||||
const mapping::DomainMapper *modelDrivenMapper = modelDrivenFiniteElements.domainMapperStateless.get();
|
||||
|
||||
models::StellarModel legacyModel{
|
||||
models::structure::PolytropicStructure{eos::Polytrope{3.0, 0.25}, 1.25},
|
||||
surface::ConstantPressureSurface{eos::PressureValue{0.0}}
|
||||
};
|
||||
operators::PreparedStellarEquilibriumOperator legacyOperator(f, *f.domainMapperStateless, legacyModel);
|
||||
operators::PreparedStellarEquilibriumOperator legacyOperator(
|
||||
legacyFiniteElements, *legacyFiniteElements.domainMapperStateless, legacyModel
|
||||
);
|
||||
|
||||
const equilibrium::StellarDiscretization discretization{f, *f.domainMapperStateless};
|
||||
auto equilibriumProblem = equilibrium::discretize(
|
||||
model::StellarModel(
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{1.25}}),
|
||||
surface::Isobaric({.Psurf = dimensions::PressureValue{0.0}}), eos::Polytrope({.n = 3.0, .K = 0.25})
|
||||
),
|
||||
discretization
|
||||
std::move(modelDrivenFiniteElements)
|
||||
);
|
||||
auto &modelDrivenOperator = equilibriumProblem.GetPreparedOperator();
|
||||
auto &modelDrivenOperator = equilibriumProblem.GetPreparedOperator();
|
||||
const auto &physicalOperator = equilibriumProblem.GetPhysicalOperator();
|
||||
|
||||
CHECK(equilibriumProblem.StateSize() == legacyOperator.Width());
|
||||
CHECK(equilibriumProblem.EquationSize() == legacyOperator.Height());
|
||||
CHECK(equilibriumProblem.StateSize() == equilibriumProblem.EquationSize());
|
||||
CHECK(&equilibriumProblem.GetDiscretization().finiteElementModel() == &f);
|
||||
CHECK(&equilibriumProblem.GetDiscretization().domainMapper() == f.domainMapperStateless.get());
|
||||
CHECK(equilibriumProblem.GetCommunicator() == modelDrivenCommunicator);
|
||||
CHECK(&equilibriumProblem.GetDiscretization().domainMapper() == modelDrivenMapper);
|
||||
CHECK(equilibriumProblem.GetDiscretization().isCurrent());
|
||||
CHECK(physicalOperator.GetTargetMass() == 1.25);
|
||||
CHECK(physicalOperator.GetSurfaceConstraintOperator().GetPhysicalCondition().targetPressure == 0.0);
|
||||
@@ -455,7 +487,7 @@ TEST_CASE(
|
||||
|
||||
mfem::Vector state(legacyOperator.Width());
|
||||
state = 0.0;
|
||||
const auto stateView = legacyOperator.GetRootManifest().stateView(state);
|
||||
const auto stateView = legacyOperator.GetRootManifest().stateView(state);
|
||||
stateView.block(utils::blocks::density_field.mass_term) = 1.0;
|
||||
stateView.block(utils::blocks::enthalpy_field.specific_term) = 1.0;
|
||||
|
||||
@@ -487,29 +519,28 @@ TEST_CASE(
|
||||
) {
|
||||
using namespace mean_field;
|
||||
|
||||
utils::Args arguments = test_utils::setup_args();
|
||||
utils::Args arguments = test_utils::setup_args();
|
||||
fem::FEM finiteElements = fem::setup_fem(arguments.mesh_file, arguments, 0);
|
||||
REQUIRE(finiteElements.okay());
|
||||
auto model = model::StellarModel(
|
||||
eos::Polytrope({.n = 1.0, .K = 0.25}),
|
||||
surface::Isobaric({.Psurf = dimensions::PressureValue{0.0}}),
|
||||
eos::Polytrope({.n = 1.0, .K = 0.25}), surface::Isobaric({.Psurf = dimensions::PressureValue{0.0}}),
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{1.0}}),
|
||||
integral::FixedAngularMomentum({.Jtotal = dimensions::AngularMomentumValue{0.2}}),
|
||||
constraint::FixedCentralDensity({.RhoC = dimensions::DensityValue{1.0}})
|
||||
);
|
||||
auto problem = equilibrium::discretize(model, finiteElements);
|
||||
auto problem = equilibrium::discretize(model, std::move(finiteElements));
|
||||
using Problem = std::remove_cvref_t<decltype(problem)>;
|
||||
STATIC_CHECK(Problem::FormType::value_block_count == 8);
|
||||
STATIC_CHECK(Problem::FormType::residual_block_count == 8);
|
||||
|
||||
mfem::Vector state(problem.StateSize());
|
||||
state = 0.0;
|
||||
const auto stateView = problem.GetManifest().stateView(state);
|
||||
stateView.block(utils::blocks::density_field.mass_term) = 1.0;
|
||||
state = 0.0;
|
||||
const auto stateView = problem.GetManifest().stateView(state);
|
||||
stateView.block(utils::blocks::density_field.mass_term) = 1.0;
|
||||
stateView.block(utils::blocks::enthalpy_field.specific_term) = 1.0;
|
||||
stateView.block(utils::blocks::fixed_total_mass_constraint.mass_normalization_term) = 0.25;
|
||||
stateView.block(utils::blocks::fixed_total_mass_constraint.mass_normalization_term) = 0.25;
|
||||
stateView.block(utils::blocks::fixed_angular_momentum_constraint.angular_velocity_term) = 0.4;
|
||||
stateView.block(utils::blocks::fixed_central_density_phase.central_value_term) = 0.03;
|
||||
stateView.block(utils::blocks::fixed_central_density_phase.central_value_term) = 0.03;
|
||||
|
||||
const auto report = problem.Prepare(state, make_dependencies());
|
||||
CHECK(report.template specification<models::FixedCentralDensity>().constraint.DidAnyWork());
|
||||
@@ -517,18 +548,18 @@ TEST_CASE(
|
||||
CHECK(problem.IsPrepared());
|
||||
CHECK(problem.StateSize() == problem.GetPhysicalOperator().Width() + 2);
|
||||
REQUIRE(problem.GetManifest().constraints().size() == 4);
|
||||
CHECK(problem.GetManifest().template specification<models::FixedAngularMomentum>().stableId ==
|
||||
"FixedAngularMomentum");
|
||||
CHECK(problem.GetManifest().template specification<models::FixedCentralDensity>().stableId ==
|
||||
"FixedCentralDensity");
|
||||
CHECK(
|
||||
problem.GetManifest().template specification<models::FixedAngularMomentum>().stableId == "FixedAngularMomentum"
|
||||
);
|
||||
CHECK(
|
||||
problem.GetManifest().template specification<models::FixedCentralDensity>().stableId == "FixedCentralDensity"
|
||||
);
|
||||
|
||||
mfem::Vector residual;
|
||||
problem.BuildResidual(residual);
|
||||
REQUIRE(residual.Size() == problem.EquationSize());
|
||||
const auto residualView = problem.GetManifest().residualView(residual);
|
||||
CHECK(std::isfinite(
|
||||
residualView.block(utils::blocks::fixed_angular_momentum_constraint.angular_velocity_term)(0)
|
||||
));
|
||||
CHECK(std::isfinite(residualView.block(utils::blocks::fixed_angular_momentum_constraint.angular_velocity_term)(0)));
|
||||
CHECK(std::isfinite(residualView.block(utils::blocks::fixed_central_density_phase.central_value_term)(0)));
|
||||
|
||||
mfem::Vector direction(problem.StateSize());
|
||||
|
||||
@@ -57,33 +57,26 @@ namespace {
|
||||
blocks::type_list<>,
|
||||
preconditioning::IdentityOperatorCharacteristics,
|
||||
preconditioning::backend::Identity>;
|
||||
using LifetimeModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
using LifetimeModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
mean_field::eos::Polytrope,
|
||||
mean_field::surface::Isobaric,
|
||||
mean_field::models::FixedTotalMass,
|
||||
mean_field::models::FixedCentralDensity>>;
|
||||
using LifetimeProblem = mean_field::equilibrium::StellarEquilibriumProblem<LifetimeModel>;
|
||||
using LifetimeBlock = decltype(preconditioning::makePreconditioner(
|
||||
std::declval<const LifetimeProblem &>()
|
||||
));
|
||||
using LifetimePrepared = preconditioning::PreparedStellarPreconditioner<
|
||||
LifetimeProblem,
|
||||
LifetimeBlock>;
|
||||
using LifetimeProblem = mean_field::equilibrium::StellarEquilibriumProblem<LifetimeModel>;
|
||||
using LifetimeBlock = decltype(preconditioning::makePreconditioner(std::declval<const LifetimeProblem &>()));
|
||||
using LifetimePrepared = preconditioning::PreparedStellarPreconditioner<LifetimeProblem, LifetimeBlock>;
|
||||
|
||||
template <typename Problem, typename Block>
|
||||
concept CanPrepareStellarPreconditioner = requires(const Problem &problem, Block block) {
|
||||
preconditioning::prepare(problem, std::move(block));
|
||||
};
|
||||
concept CanPrepareStellarPreconditioner =
|
||||
requires(const Problem &problem, Block block) { preconditioning::prepare(problem, std::move(block)); };
|
||||
|
||||
template <typename Problem, typename Block>
|
||||
concept CanPrepareStellarPreconditionerFromTemporary = requires(Block block) {
|
||||
preconditioning::prepare(std::declval<Problem &&>(), std::move(block));
|
||||
};
|
||||
concept CanPrepareStellarPreconditionerFromTemporary =
|
||||
requires(Block block) { preconditioning::prepare(std::declval<Problem &&>(), std::move(block)); };
|
||||
|
||||
template <typename Problem, typename Block>
|
||||
concept CanPrepareStellarPreconditionerFromConstTemporary = requires(Block block) {
|
||||
preconditioning::prepare(std::declval<const Problem &&>(), std::move(block));
|
||||
};
|
||||
concept CanPrepareStellarPreconditionerFromConstTemporary =
|
||||
requires(Block block) { preconditioning::prepare(std::declval<const Problem &&>(), std::move(block)); };
|
||||
|
||||
[[nodiscard]] blocks::form_layout<Form> makeUnevenLayout() {
|
||||
return {
|
||||
@@ -221,7 +214,7 @@ TEST_CASE(
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{mass}}),
|
||||
constraint::FixedCentralDensity({.RhoC = dimensions::DensityValue{centralDensity}})
|
||||
);
|
||||
auto problem = equilibrium::discretize(model, finiteElements);
|
||||
auto problem = equilibrium::discretize(model, std::move(finiteElements));
|
||||
auto projected = seed::makeProjectedEquilibriumState(problem, seed::LaneEmden({.radialSampleCount = 512}));
|
||||
problem.Prepare(projected.values, makeDependencies(), zeroRotation());
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <numbers>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
@@ -18,12 +19,12 @@ namespace material_surface_runtime_contract_test {
|
||||
struct RegisteredAlternateEquationOfState final {
|
||||
struct Parameters final { };
|
||||
|
||||
using ModelDefinition = mean_field::models::ConstitutiveLaw<
|
||||
RegisteredAlternateEquationOfState,
|
||||
"RegisteredAlternateMaterialSurfaceEquationOfState">;
|
||||
using ModelDefinition = mean_field::models::
|
||||
ConstitutiveLaw<RegisteredAlternateEquationOfState, "RegisteredAlternateMaterialSurfaceEquationOfState">;
|
||||
using Relations = mean_field::eos::RelationCatalog<mean_field::eos::SpecificEnthalpyFromPressure>;
|
||||
|
||||
explicit RegisteredAlternateEquationOfState(Parameters) noexcept { }
|
||||
explicit RegisteredAlternateEquationOfState(Parameters) noexcept {
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::dimensions::SpecificEnthalpyValue evaluate(
|
||||
mean_field::eos::SpecificEnthalpyFromPressure,
|
||||
@@ -40,7 +41,10 @@ namespace material_surface_runtime_contract_test {
|
||||
|
||||
using mfem::Operator::Operator;
|
||||
|
||||
void Mult(const mfem::Vector &, mfem::Vector &) const override;
|
||||
void Mult(
|
||||
const mfem::Vector &,
|
||||
mfem::Vector &
|
||||
) const override;
|
||||
|
||||
[[nodiscard]] const mean_field::operators::StellarEquilibriumLayout &GetLayout() const noexcept;
|
||||
[[nodiscard]] mean_field::operators::PreparedStellarEquilibriumReport Prepare(
|
||||
@@ -58,8 +62,7 @@ namespace material_surface_runtime_contract_test {
|
||||
GetSurfaceConstraintOperator() const;
|
||||
};
|
||||
|
||||
template <mean_field::model::StellarModelType Model>
|
||||
class AlternateEquationOfStateRuntime final {
|
||||
template <mean_field::model::StellarModelType Model> class AlternateEquationOfStateRuntime final {
|
||||
public:
|
||||
using Report = mean_field::operators::EmptySpecificationPreparationReport;
|
||||
|
||||
@@ -68,10 +71,17 @@ namespace material_surface_runtime_contract_test {
|
||||
const mean_field::mapping::DomainMapper &,
|
||||
AlternatePhysicalCore &,
|
||||
const Model &
|
||||
) noexcept { }
|
||||
) noexcept {
|
||||
}
|
||||
|
||||
template <typename StateView, typename Controls>
|
||||
void ReadPhysicalControls(const StateView &, Controls &) noexcept { }
|
||||
template <
|
||||
typename StateView,
|
||||
typename Controls>
|
||||
void ReadPhysicalControls(
|
||||
const StateView &,
|
||||
Controls &
|
||||
) noexcept {
|
||||
}
|
||||
|
||||
template <typename StateView>
|
||||
[[nodiscard]] Report PrepareAfterPhysical(
|
||||
@@ -82,15 +92,18 @@ namespace material_surface_runtime_contract_test {
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename ResidualView>
|
||||
void AddResidual(const ResidualView &) const noexcept { }
|
||||
template <typename ResidualView> void AddResidual(const ResidualView &) const noexcept {
|
||||
}
|
||||
|
||||
template <typename DirectionView, typename ActionView>
|
||||
template <
|
||||
typename DirectionView,
|
||||
typename ActionView>
|
||||
void AddJacobianAction(
|
||||
const DirectionView &,
|
||||
const ActionView &,
|
||||
const AlternatePhysicalCore &
|
||||
) const noexcept { }
|
||||
) const noexcept {
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool IsPrepared() const noexcept {
|
||||
return true;
|
||||
@@ -100,10 +113,9 @@ namespace material_surface_runtime_contract_test {
|
||||
|
||||
namespace mean_field::operators {
|
||||
template <>
|
||||
struct StellarEquilibriumCoreRuntime<
|
||||
material_surface_runtime_contract_test::RegisteredAlternateEquationOfState> {
|
||||
struct StellarEquilibriumCoreRuntime<material_surface_runtime_contract_test::RegisteredAlternateEquationOfState> {
|
||||
static constexpr bool registered = true;
|
||||
using CoreType = material_surface_runtime_contract_test::AlternatePhysicalCore;
|
||||
using CoreType = material_surface_runtime_contract_test::AlternatePhysicalCore;
|
||||
|
||||
[[nodiscard]] static std::unique_ptr<CoreType> Make(
|
||||
fem::FEM &,
|
||||
@@ -134,7 +146,7 @@ namespace mean_field::preconditioning {
|
||||
struct MaterialSurfaceEquationOfStateBackend<
|
||||
material_surface_runtime_contract_test::RegisteredAlternateEquationOfState> {
|
||||
static constexpr bool registered = true;
|
||||
using CoreType = material_surface_runtime_contract_test::AlternatePhysicalCore;
|
||||
using CoreType = material_surface_runtime_contract_test::AlternatePhysicalCore;
|
||||
};
|
||||
} // namespace mean_field::preconditioning
|
||||
|
||||
@@ -150,12 +162,12 @@ namespace {
|
||||
mean_field::constraint::FixedCentralDensity>>;
|
||||
using PolytropicProblem = mean_field::equilibrium::StellarEquilibriumProblem<PolytropicModel>;
|
||||
using PolytropicMaterialSurfaceDescriptor = preconditioning::MaterialSurfaceDescriptorFor<PolytropicProblem>;
|
||||
using RegisteredAlternateModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
using RegisteredAlternateModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
material_surface_runtime_contract_test::RegisteredAlternateEquationOfState,
|
||||
mean_field::surface::Isobaric,
|
||||
mean_field::integral::FixedTotalMass>>;
|
||||
using RegisteredAlternateProblem = mean_field::equilibrium::StellarEquilibriumProblem<RegisteredAlternateModel>;
|
||||
using MaterialSurfaceDiagonal = preconditioning::MaterialSurfaceBlock<
|
||||
using MaterialSurfaceDiagonal = preconditioning::MaterialSurfaceBlock<
|
||||
PolytropicMaterialSurfaceDescriptor,
|
||||
backend::Diagonal,
|
||||
backend::Diagonal,
|
||||
@@ -174,10 +186,8 @@ namespace {
|
||||
PolytropicMaterialSurfaceDescriptor,
|
||||
preconditioning::ApproximateMaterialSurfaceLDU,
|
||||
backend::FixedCycles>;
|
||||
using RegisteredAlternateGravityComponent = preconditioning::GravityFieldBlock<
|
||||
backend::Diagonal,
|
||||
FixedCycleAMG,
|
||||
preconditioning::GravityApproximateLDU>;
|
||||
using RegisteredAlternateGravityComponent =
|
||||
preconditioning::GravityFieldBlock<backend::Diagonal, FixedCycleAMG, preconditioning::GravityApproximateLDU>;
|
||||
|
||||
using RegisteredAlternateMaterialSurfaceDescriptor =
|
||||
preconditioning::MaterialSurfaceDescriptorFor<RegisteredAlternateProblem>;
|
||||
@@ -185,9 +195,8 @@ namespace {
|
||||
struct DistinctPhysicalCore final { };
|
||||
|
||||
template <typename Problem>
|
||||
concept CanMakeDefaultMaterialSurface = requires(const Problem &problem) {
|
||||
preconditioning::materialSurfaceBlock(problem);
|
||||
};
|
||||
concept CanMakeDefaultMaterialSurface =
|
||||
requires(const Problem &problem) { preconditioning::materialSurfaceBlock(problem); };
|
||||
|
||||
template <typename Problem>
|
||||
concept CanPrepareDefaultMaterialSurface = requires(const Problem &problem) {
|
||||
@@ -195,9 +204,8 @@ namespace {
|
||||
};
|
||||
|
||||
template <typename Problem>
|
||||
concept CanMakeDefaultStellarStructure = requires(const Problem &problem) {
|
||||
preconditioning::stellarStructureBlock(problem);
|
||||
};
|
||||
concept CanMakeDefaultStellarStructure =
|
||||
requires(const Problem &problem) { preconditioning::stellarStructureBlock(problem); };
|
||||
|
||||
class KnownCouplings final {
|
||||
public:
|
||||
@@ -315,41 +323,46 @@ TEST_CASE(
|
||||
STATIC_CHECK(preconditioning::PreconditionerComponent<MaterialSurfaceDiagonal>);
|
||||
STATIC_CHECK(preconditioning::PreconditionerComponent<MaterialSurfaceH1>);
|
||||
STATIC_CHECK(preconditioning::MaterialSurfaceDescriptor<PolytropicMaterialSurfaceDescriptor>);
|
||||
STATIC_CHECK(preconditioning::ImplementedMaterialSurfaceEquationOfState<
|
||||
mean_field::eos::Polytrope>);
|
||||
STATIC_CHECK(preconditioning::ImplementedMaterialSurfaceEquationOfState<mean_field::eos::Polytrope>);
|
||||
STATIC_CHECK_FALSE(preconditioning::ImplementedMaterialSurfaceEquationOfState<int>);
|
||||
STATIC_CHECK(preconditioning::ImplementedMaterialSurfaceDescriptor<PolytropicMaterialSurfaceDescriptor>);
|
||||
STATIC_CHECK(
|
||||
preconditioning::ExecutableMaterialSurfaceRuntimeFor<
|
||||
mean_field::eos::Polytrope, mean_field::operators::PreparedStellarEquilibriumOperator>
|
||||
);
|
||||
STATIC_CHECK(
|
||||
std::same_as<
|
||||
typename preconditioning::MaterialSurfaceEquationOfStateBackend<mean_field::eos::Polytrope>::CoreType,
|
||||
mean_field::operators::PreparedStellarEquilibriumOperator>
|
||||
);
|
||||
STATIC_CHECK(
|
||||
preconditioning::MaterialSurfaceRuntimeFor<
|
||||
PolytropicMaterialSurfaceDescriptor, mean_field::operators::PreparedStellarEquilibriumOperator>
|
||||
);
|
||||
STATIC_CHECK_FALSE(
|
||||
preconditioning::ImplementedMaterialSurfaceEquationOfState<int>);
|
||||
STATIC_CHECK(preconditioning::ImplementedMaterialSurfaceDescriptor<
|
||||
PolytropicMaterialSurfaceDescriptor>);
|
||||
STATIC_CHECK(preconditioning::ExecutableMaterialSurfaceRuntimeFor<
|
||||
mean_field::eos::Polytrope,
|
||||
mean_field::operators::PreparedStellarEquilibriumOperator>);
|
||||
STATIC_CHECK(std::same_as<
|
||||
typename preconditioning::MaterialSurfaceEquationOfStateBackend<
|
||||
mean_field::eos::Polytrope>::CoreType,
|
||||
mean_field::operators::PreparedStellarEquilibriumOperator>);
|
||||
STATIC_CHECK(preconditioning::MaterialSurfaceRuntimeFor<
|
||||
PolytropicMaterialSurfaceDescriptor,
|
||||
mean_field::operators::PreparedStellarEquilibriumOperator>);
|
||||
STATIC_CHECK_FALSE(preconditioning::MaterialSurfaceRuntimeFor<
|
||||
PolytropicMaterialSurfaceDescriptor,
|
||||
DistinctPhysicalCore>);
|
||||
STATIC_CHECK(preconditioning::MaterialSurfaceDescriptor<
|
||||
RegisteredAlternateMaterialSurfaceDescriptor>);
|
||||
preconditioning::MaterialSurfaceRuntimeFor<PolytropicMaterialSurfaceDescriptor, DistinctPhysicalCore>
|
||||
);
|
||||
STATIC_CHECK(preconditioning::MaterialSurfaceDescriptor<RegisteredAlternateMaterialSurfaceDescriptor>);
|
||||
STATIC_CHECK(mean_field::equilibrium::StellarEquilibriumModel<RegisteredAlternateModel>);
|
||||
STATIC_CHECK(std::same_as<
|
||||
typename RegisteredAlternateProblem::PhysicalCoreType,
|
||||
material_surface_runtime_contract_test::AlternatePhysicalCore>);
|
||||
STATIC_CHECK(preconditioning::ImplementedMaterialSurfaceEquationOfState<
|
||||
material_surface_runtime_contract_test::RegisteredAlternateEquationOfState>);
|
||||
STATIC_CHECK(preconditioning::ImplementedMaterialSurfaceDescriptor<
|
||||
RegisteredAlternateMaterialSurfaceDescriptor>);
|
||||
STATIC_CHECK_FALSE(preconditioning::ExecutableMaterialSurfaceRuntimeFor<
|
||||
material_surface_runtime_contract_test::RegisteredAlternateEquationOfState,
|
||||
material_surface_runtime_contract_test::AlternatePhysicalCore>);
|
||||
STATIC_CHECK_FALSE(preconditioning::MaterialSurfaceRuntimeFor<
|
||||
RegisteredAlternateMaterialSurfaceDescriptor,
|
||||
material_surface_runtime_contract_test::AlternatePhysicalCore>);
|
||||
STATIC_CHECK(
|
||||
std::same_as<
|
||||
typename RegisteredAlternateProblem::PhysicalCoreType,
|
||||
material_surface_runtime_contract_test::AlternatePhysicalCore>
|
||||
);
|
||||
STATIC_CHECK(
|
||||
preconditioning::ImplementedMaterialSurfaceEquationOfState<
|
||||
material_surface_runtime_contract_test::RegisteredAlternateEquationOfState>
|
||||
);
|
||||
STATIC_CHECK(preconditioning::ImplementedMaterialSurfaceDescriptor<RegisteredAlternateMaterialSurfaceDescriptor>);
|
||||
STATIC_CHECK_FALSE(
|
||||
preconditioning::ExecutableMaterialSurfaceRuntimeFor<
|
||||
material_surface_runtime_contract_test::RegisteredAlternateEquationOfState,
|
||||
material_surface_runtime_contract_test::AlternatePhysicalCore>
|
||||
);
|
||||
STATIC_CHECK_FALSE(
|
||||
preconditioning::MaterialSurfaceRuntimeFor<
|
||||
RegisteredAlternateMaterialSurfaceDescriptor, material_surface_runtime_contract_test::AlternatePhysicalCore>
|
||||
);
|
||||
STATIC_CHECK(preconditioning::MaterialSurfacePreconditionerProblem<PolytropicProblem>);
|
||||
STATIC_CHECK_FALSE(preconditioning::MaterialSurfacePreconditionerProblem<RegisteredAlternateProblem>);
|
||||
STATIC_CHECK(CanMakeDefaultMaterialSurface<PolytropicProblem>);
|
||||
@@ -357,10 +370,10 @@ TEST_CASE(
|
||||
STATIC_CHECK(CanPrepareDefaultMaterialSurface<PolytropicProblem>);
|
||||
STATIC_CHECK_FALSE(CanPrepareDefaultMaterialSurface<RegisteredAlternateProblem>);
|
||||
STATIC_CHECK_FALSE(preconditioning::StellarStructurePreconditionerProblem<RegisteredAlternateProblem>);
|
||||
STATIC_CHECK_FALSE(preconditioning::StellarStructurePreparableFor<
|
||||
RegisteredAlternateProblem,
|
||||
MaterialSurfaceDiagonal,
|
||||
RegisteredAlternateGravityComponent>);
|
||||
STATIC_CHECK_FALSE(
|
||||
preconditioning::StellarStructurePreparableFor<
|
||||
RegisteredAlternateProblem, MaterialSurfaceDiagonal, RegisteredAlternateGravityComponent>
|
||||
);
|
||||
STATIC_CHECK_FALSE(CanMakeDefaultStellarStructure<RegisteredAlternateProblem>);
|
||||
STATIC_CHECK(
|
||||
mean_field::material::CompiledThermodynamicEquations<typename PolytropicProblem::ThermodynamicEquationsType>
|
||||
@@ -572,9 +585,10 @@ TEST_CASE(
|
||||
const utils::Args arguments = test_utils::setup_args();
|
||||
fem::FEM finiteElements = fem::setup_fem(arguments.mesh_file, arguments, 0);
|
||||
REQUIRE(finiteElements.okay());
|
||||
const mfem::ParFiniteElementSpace *surfaceDeformationSpace = finiteElements.surfaceDeformationFes.get();
|
||||
|
||||
constexpr double radius = utils::RADIUS;
|
||||
constexpr double mass = utils::MASS;
|
||||
constexpr double radius = utils::RADIUS;
|
||||
constexpr double mass = utils::MASS;
|
||||
const double polytropicConstant = 2.0 * utils::G * radius * radius / std::numbers::pi_v<double>;
|
||||
const double centralDensity = std::numbers::pi_v<double> * mass / (4.0 * radius * radius * radius);
|
||||
const auto stellarModel = model::StellarModel(
|
||||
@@ -583,7 +597,7 @@ TEST_CASE(
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{mass}}),
|
||||
constraint::FixedCentralDensity({.RhoC = dimensions::DensityValue{centralDensity}})
|
||||
);
|
||||
auto problem = equilibrium::discretize(stellarModel, finiteElements);
|
||||
auto problem = equilibrium::discretize(stellarModel, std::move(finiteElements));
|
||||
auto projected = seed::makeProjectedEquilibriumState(problem, seed::LaneEmden({.radialSampleCount = 512}));
|
||||
const auto rotation = zeroRotation();
|
||||
problem.Prepare(projected.values, makeDependencies(), rotation);
|
||||
@@ -711,7 +725,7 @@ TEST_CASE(
|
||||
CHECK(surfaceFit.relativeGramDeterminant > 1.0e-12);
|
||||
CHECK(surfaceFit.normalEquations.targetTarget > 0.0);
|
||||
CHECK(frequencyAware.GetSurfaceInverse().Height() == physical.GetDomainDeformation().parameterCount());
|
||||
CHECK(frequencyAware.GetSurfaceSurrogateMatrix().Height() == finiteElements.surfaceDeformationFes->GetTrueVSize());
|
||||
CHECK(frequencyAware.GetSurfaceSurrogateMatrix().Height() == surfaceDeformationSpace->GetTrueVSize());
|
||||
CHECK(frequencyAware.GetSurfaceBackend().GetStatistics().setups == 1);
|
||||
CHECK(frequencyAware.GetStatistics().surfaceJacobianProbes == 4);
|
||||
CHECK(frequencyAware.GetStatistics().surfaceH1Assemblies == 3);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -122,7 +122,7 @@ namespace unsupported_physical_preconditioner_test {
|
||||
mean_field::dimensions::SpecificEnthalpyValue target;
|
||||
};
|
||||
|
||||
using TargetValue = mean_field::dimensions::SpecificEnthalpyValue;
|
||||
using TargetValue = mean_field::dimensions::SpecificEnthalpyValue;
|
||||
using ScalarDescription = mean_field::stellar::ScalarConstraint<
|
||||
mean_field::dimensions::quantity::SpecificEnthalpy,
|
||||
mean_field::dimensions::quantity::Dimensionless,
|
||||
@@ -134,18 +134,13 @@ namespace unsupported_physical_preconditioner_test {
|
||||
using ModelDefinition = mean_field::constraint::ScalarPhaseCondition<
|
||||
Constraint,
|
||||
"UnsupportedPhysicalPreconditionerEdge",
|
||||
mean_field::stellar::Reads<
|
||||
mean_field::stellar::state::SpecificEnthalpy,
|
||||
mean_field::stellar::state::OwnGeneratedCoordinate>,
|
||||
mean_field::stellar::Changes<
|
||||
mean_field::stellar::equation::PoissonEquation>,
|
||||
mean_field::stellar::
|
||||
Reads<mean_field::stellar::state::SpecificEnthalpy, mean_field::stellar::state::OwnGeneratedCoordinate>,
|
||||
mean_field::stellar::Changes<mean_field::stellar::equation::PoissonEquation>,
|
||||
ScalarDescription>;
|
||||
using EquilibriumPhysics =
|
||||
mean_field::operators::LocalSpecificationEquilibriumPhysics<
|
||||
PreparedConstraint>;
|
||||
using EquilibriumPhysics = mean_field::operators::LocalSpecificationEquilibriumPhysics<PreparedConstraint>;
|
||||
|
||||
explicit constexpr Constraint(const Parameters parameters) noexcept
|
||||
: m_target(parameters.target) {
|
||||
explicit constexpr Constraint(const Parameters parameters) noexcept : m_target(parameters.target) {
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr TargetValue target() const noexcept {
|
||||
@@ -167,8 +162,7 @@ namespace unsupported_physical_preconditioner_test {
|
||||
explicit PreparedConstraint(const Constraint &) noexcept {
|
||||
}
|
||||
|
||||
template <typename StateView>
|
||||
[[nodiscard]] Report PrepareAfterPhysical(const StateView &) noexcept {
|
||||
template <typename StateView> [[nodiscard]] Report PrepareAfterPhysical(const StateView &) noexcept {
|
||||
m_isPrepared = true;
|
||||
return {};
|
||||
}
|
||||
@@ -189,7 +183,9 @@ namespace unsupported_physical_preconditioner_test {
|
||||
return mean_field::stellar::structuralZero;
|
||||
}
|
||||
|
||||
template <typename Direction, typename Row>
|
||||
template <
|
||||
typename Direction,
|
||||
typename Row>
|
||||
[[nodiscard]] mean_field::stellar::StructuralZero AddJacobianAction(
|
||||
mean_field::stellar::Derivative<
|
||||
mean_field::stellar::equation::OwnConstraint,
|
||||
@@ -200,7 +196,9 @@ namespace unsupported_physical_preconditioner_test {
|
||||
return mean_field::stellar::zeroDerivative;
|
||||
}
|
||||
|
||||
template <typename Direction, typename Row>
|
||||
template <
|
||||
typename Direction,
|
||||
typename Row>
|
||||
[[nodiscard]] mean_field::stellar::StructuralZero AddJacobianAction(
|
||||
mean_field::stellar::Derivative<
|
||||
mean_field::stellar::equation::OwnConstraint,
|
||||
@@ -211,7 +209,9 @@ namespace unsupported_physical_preconditioner_test {
|
||||
return mean_field::stellar::zeroDerivative;
|
||||
}
|
||||
|
||||
template <typename Direction, typename Row>
|
||||
template <
|
||||
typename Direction,
|
||||
typename Row>
|
||||
[[nodiscard]] mean_field::stellar::StructuralZero AddJacobianAction(
|
||||
mean_field::stellar::Derivative<
|
||||
mean_field::stellar::equation::PoissonEquation,
|
||||
@@ -222,7 +222,9 @@ namespace unsupported_physical_preconditioner_test {
|
||||
return mean_field::stellar::zeroDerivative;
|
||||
}
|
||||
|
||||
template <typename Direction, typename Row>
|
||||
template <
|
||||
typename Direction,
|
||||
typename Row>
|
||||
[[nodiscard]] mean_field::stellar::StructuralZero AddJacobianAction(
|
||||
mean_field::stellar::Derivative<
|
||||
mean_field::stellar::equation::PoissonEquation,
|
||||
@@ -277,42 +279,40 @@ template <> struct mean_field::preconditioning::StellarEquilibriumProblemTraits<
|
||||
};
|
||||
|
||||
namespace {
|
||||
namespace blocks = mean_field::utils::blocks;
|
||||
namespace preconditioning = mean_field::preconditioning;
|
||||
namespace blocks = mean_field::utils::blocks;
|
||||
namespace preconditioning = mean_field::preconditioning;
|
||||
|
||||
using ModelWithoutPhase = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
using ModelWithoutPhase = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
mean_field::eos::Polytrope,
|
||||
mean_field::surface::Isobaric,
|
||||
mean_field::models::FixedTotalMass>>;
|
||||
using CentralDensityModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
using CentralDensityModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
mean_field::eos::Polytrope,
|
||||
mean_field::surface::Isobaric,
|
||||
mean_field::models::FixedTotalMass,
|
||||
mean_field::models::FixedCentralDensity>>;
|
||||
using AngularMomentumModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
using AngularMomentumModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
mean_field::eos::Polytrope,
|
||||
mean_field::surface::Isobaric,
|
||||
mean_field::models::FixedTotalMass,
|
||||
mean_field::models::FixedAngularMomentum>>;
|
||||
using ProvenZeroPhysicalEdgeModel = mean_field::model::StellarModel<
|
||||
mean_field::models::SpecificationSet<
|
||||
mean_field::eos::Polytrope,
|
||||
mean_field::surface::Isobaric,
|
||||
mean_field::models::FixedTotalMass,
|
||||
unsupported_physical_preconditioner_test::Constraint>>;
|
||||
using ProvenZeroPhysicalEdgeModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
mean_field::eos::Polytrope,
|
||||
mean_field::surface::Isobaric,
|
||||
mean_field::models::FixedTotalMass,
|
||||
unsupported_physical_preconditioner_test::Constraint>>;
|
||||
|
||||
using ProblemWithoutPhase = mean_field::equilibrium::StellarEquilibriumProblem<ModelWithoutPhase>;
|
||||
using CentralDensityProblem = mean_field::equilibrium::StellarEquilibriumProblem<CentralDensityModel>;
|
||||
using AngularMomentumProblem = mean_field::equilibrium::StellarEquilibriumProblem<AngularMomentumModel>;
|
||||
using ProblemWithoutPhase = mean_field::equilibrium::StellarEquilibriumProblem<ModelWithoutPhase>;
|
||||
using CentralDensityProblem = mean_field::equilibrium::StellarEquilibriumProblem<CentralDensityModel>;
|
||||
using AngularMomentumProblem = mean_field::equilibrium::StellarEquilibriumProblem<AngularMomentumModel>;
|
||||
using ProvenZeroPhysicalEdgeProblem =
|
||||
mean_field::equilibrium::StellarEquilibriumProblem<ProvenZeroPhysicalEdgeModel>;
|
||||
using PlanWithoutPhase = preconditioning::IdentityPreconditionerPlanFor<ProblemWithoutPhase>;
|
||||
using CentralDensityPlan = preconditioning::IdentityPreconditionerPlanFor<CentralDensityProblem>;
|
||||
using PlanWithoutPhase = preconditioning::IdentityPreconditionerPlanFor<ProblemWithoutPhase>;
|
||||
using CentralDensityPlan = preconditioning::IdentityPreconditionerPlanFor<CentralDensityProblem>;
|
||||
|
||||
template <typename Problem>
|
||||
concept CanMakeDefaultStellarStructureBlock = requires(const Problem &problem) {
|
||||
preconditioning::stellarStructureBlock(problem);
|
||||
};
|
||||
concept CanMakeDefaultStellarStructureBlock =
|
||||
requires(const Problem &problem) { preconditioning::stellarStructureBlock(problem); };
|
||||
|
||||
using RefreshingDensityIdentity = preconditioning::ComponentDeclaration<
|
||||
blocks::type_list<blocks::density::mass::value>,
|
||||
@@ -367,67 +367,51 @@ TEST_CASE(
|
||||
"Default Stellar Structure Availability Distinguishes Proven Zeros From Unhandled Physical Edges",
|
||||
"[preconditioning][stellar_structure][type_contract][compiler]"
|
||||
) {
|
||||
using BaseCompilation =
|
||||
mean_field::operators::CompiledStellarEquilibriumSystem<ModelWithoutPhase>;
|
||||
using BaseSupport =
|
||||
preconditioning::DefaultStellarStructurePhysicalTopologySupport<
|
||||
ModelWithoutPhase>;
|
||||
using BaseCompilation = mean_field::operators::CompiledStellarEquilibriumSystem<ModelWithoutPhase>;
|
||||
using BaseSupport = preconditioning::DefaultStellarStructurePhysicalTopologySupport<ModelWithoutPhase>;
|
||||
using ProvenZeroSupport =
|
||||
preconditioning::DefaultStellarStructurePhysicalTopologySupport<
|
||||
ProvenZeroPhysicalEdgeModel>;
|
||||
using TrustedFixedMassEdge =
|
||||
mean_field::operators::StellarEquilibriumJacobianCoupling<
|
||||
blocks::enthalpy::specific::residual,
|
||||
blocks::density::mass::value>;
|
||||
using ProvenZeroPoissonEnthalpyEdge =
|
||||
mean_field::operators::StellarEquilibriumJacobianCoupling<
|
||||
blocks::gravity::poisson::residual,
|
||||
blocks::enthalpy::specific::value>;
|
||||
preconditioning::DefaultStellarStructurePhysicalTopologySupport<ProvenZeroPhysicalEdgeModel>;
|
||||
using TrustedFixedMassEdge = mean_field::operators::StellarEquilibriumJacobianCoupling<
|
||||
blocks::enthalpy::specific::residual, blocks::density::mass::value>;
|
||||
using ProvenZeroPoissonEnthalpyEdge = mean_field::operators::StellarEquilibriumJacobianCoupling<
|
||||
blocks::gravity::poisson::residual, blocks::enthalpy::specific::value>;
|
||||
|
||||
// FixedTotalMass contributes h <- rho outside the generic five-field base
|
||||
// graph. It remains supported because that specification is explicitly
|
||||
// embedded in the trusted numerical core, not because of a model-pack
|
||||
// special case.
|
||||
STATIC_CHECK_FALSE(mean_field::utils::blocks::contains_type_v<
|
||||
TrustedFixedMassEdge,
|
||||
typename BaseCompilation::BaseJacobianCouplings>);
|
||||
STATIC_CHECK(mean_field::utils::blocks::contains_type_v<
|
||||
TrustedFixedMassEdge,
|
||||
typename BaseCompilation::ContributionJacobianCouplings>);
|
||||
STATIC_CHECK_FALSE(
|
||||
mean_field::utils::blocks::contains_type_v<
|
||||
TrustedFixedMassEdge, typename BaseCompilation::BaseJacobianCouplings>
|
||||
);
|
||||
STATIC_CHECK(
|
||||
mean_field::utils::blocks::contains_type_v<
|
||||
TrustedFixedMassEdge, typename BaseCompilation::ContributionJacobianCouplings>
|
||||
);
|
||||
STATIC_CHECK(BaseSupport::UnsupportedCouplings::size == 0);
|
||||
STATIC_CHECK(preconditioning::DefaultStellarStructurePhysicalTopologySupportedFor<
|
||||
ModelWithoutPhase>);
|
||||
STATIC_CHECK(preconditioning::DefaultStellarStructurePhysicalTopologySupportedFor<
|
||||
CentralDensityModel>);
|
||||
STATIC_CHECK(preconditioning::DefaultStellarStructurePhysicalTopologySupportedFor<
|
||||
AngularMomentumModel>);
|
||||
STATIC_CHECK(preconditioning::StellarStructurePreconditionerProblem<
|
||||
ProblemWithoutPhase>);
|
||||
STATIC_CHECK(preconditioning::DefaultStellarStructurePhysicalTopologySupportedFor<ModelWithoutPhase>);
|
||||
STATIC_CHECK(preconditioning::DefaultStellarStructurePhysicalTopologySupportedFor<CentralDensityModel>);
|
||||
STATIC_CHECK(preconditioning::DefaultStellarStructurePhysicalTopologySupportedFor<AngularMomentumModel>);
|
||||
STATIC_CHECK(preconditioning::StellarStructurePreconditionerProblem<ProblemWithoutPhase>);
|
||||
STATIC_CHECK(CanMakeDefaultStellarStructureBlock<ProblemWithoutPhase>);
|
||||
STATIC_CHECK(preconditioning::DefaultStellarPreconditionerAvailableFor<
|
||||
ProblemWithoutPhase>);
|
||||
STATIC_CHECK(preconditioning::DefaultStellarPreconditionerAvailableFor<ProblemWithoutPhase>);
|
||||
|
||||
// The mock's novel Poisson <- enthalpy edge is absent from the structure
|
||||
// backend, but its exact nested provider returns StructuralZero. That is
|
||||
// a compile-time proof that no preconditioner term is missing; generated-
|
||||
// coordinate edges are handled independently by the inferred border.
|
||||
STATIC_CHECK(mean_field::equilibrium::StellarEquilibriumModel<
|
||||
ProvenZeroPhysicalEdgeModel>);
|
||||
STATIC_CHECK(mean_field::equilibrium::DiscretizedStellarEquilibriumProblem<
|
||||
ProvenZeroPhysicalEdgeProblem>);
|
||||
STATIC_CHECK(mean_field::equilibrium::StellarEquilibriumModel<ProvenZeroPhysicalEdgeModel>);
|
||||
STATIC_CHECK(mean_field::equilibrium::DiscretizedStellarEquilibriumProblem<ProvenZeroPhysicalEdgeProblem>);
|
||||
STATIC_CHECK(ProvenZeroSupport::UnsupportedCouplings::size == 0);
|
||||
STATIC_CHECK(mean_field::utils::blocks::contains_type_v<
|
||||
ProvenZeroPoissonEnthalpyEdge,
|
||||
typename mean_field::operators::CompiledStellarEquilibriumSystem<
|
||||
ProvenZeroPhysicalEdgeModel>::ContributionJacobianCouplings>);
|
||||
STATIC_CHECK(preconditioning::DefaultStellarStructurePhysicalTopologySupportedFor<
|
||||
ProvenZeroPhysicalEdgeModel>);
|
||||
STATIC_CHECK(preconditioning::StellarStructurePreconditionerProblem<
|
||||
ProvenZeroPhysicalEdgeProblem>);
|
||||
STATIC_CHECK(CanMakeDefaultStellarStructureBlock<
|
||||
ProvenZeroPhysicalEdgeProblem>);
|
||||
STATIC_CHECK(preconditioning::DefaultStellarPreconditionerAvailableFor<
|
||||
ProvenZeroPhysicalEdgeProblem>);
|
||||
STATIC_CHECK(
|
||||
mean_field::utils::blocks::contains_type_v<
|
||||
ProvenZeroPoissonEnthalpyEdge, typename mean_field::operators::CompiledStellarEquilibriumSystem<
|
||||
ProvenZeroPhysicalEdgeModel>::ContributionJacobianCouplings>
|
||||
);
|
||||
STATIC_CHECK(preconditioning::DefaultStellarStructurePhysicalTopologySupportedFor<ProvenZeroPhysicalEdgeModel>);
|
||||
STATIC_CHECK(preconditioning::StellarStructurePreconditionerProblem<ProvenZeroPhysicalEdgeProblem>);
|
||||
STATIC_CHECK(CanMakeDefaultStellarStructureBlock<ProvenZeroPhysicalEdgeProblem>);
|
||||
STATIC_CHECK(preconditioning::DefaultStellarPreconditionerAvailableFor<ProvenZeroPhysicalEdgeProblem>);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
|
||||
@@ -108,12 +108,12 @@ namespace {
|
||||
return action;
|
||||
}
|
||||
|
||||
using PolytropicModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
using PolytropicModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
||||
mean_field::eos::Polytrope,
|
||||
mean_field::surface::Isobaric,
|
||||
mean_field::integral::FixedTotalMass,
|
||||
mean_field::constraint::FixedCentralDensity>>;
|
||||
using PolytropicProblem = mean_field::equilibrium::StellarEquilibriumProblem<PolytropicModel>;
|
||||
using PolytropicProblem = mean_field::equilibrium::StellarEquilibriumProblem<PolytropicModel>;
|
||||
using PolytropicMaterialSurfaceDescriptor = preconditioning::MaterialSurfaceDescriptorFor<PolytropicProblem>;
|
||||
using MaterialComponent =
|
||||
decltype(preconditioning::materialSurfaceBlock(std::declval<const PolytropicProblem &>()));
|
||||
@@ -181,20 +181,21 @@ TEST_CASE(
|
||||
preconditioning::Coupling<blocks::enthalpy::specific::residual, blocks::gravity::poisson::value>>;
|
||||
|
||||
STATIC_CHECK(preconditioning::PreconditionerComponent<PolytropicStructure>);
|
||||
STATIC_CHECK(preconditioning::ExecutableStellarStructureRuntimeFor<
|
||||
mean_field::operators::PreparedStellarEquilibriumOperator>);
|
||||
STATIC_CHECK(
|
||||
preconditioning::ExecutableStellarStructureRuntimeFor<mean_field::operators::PreparedStellarEquilibriumOperator>
|
||||
);
|
||||
STATIC_CHECK_FALSE(preconditioning::ExecutableStellarStructureRuntimeFor<DistinctPhysicalCore>);
|
||||
STATIC_CHECK(preconditioning::StellarStructureRuntimeFor<
|
||||
PolytropicMaterialSurfaceDescriptor,
|
||||
mean_field::operators::PreparedStellarEquilibriumOperator>);
|
||||
STATIC_CHECK_FALSE(preconditioning::StellarStructureRuntimeFor<
|
||||
PolytropicMaterialSurfaceDescriptor,
|
||||
DistinctPhysicalCore>);
|
||||
STATIC_CHECK(
|
||||
preconditioning::StellarStructureRuntimeFor<
|
||||
PolytropicMaterialSurfaceDescriptor, mean_field::operators::PreparedStellarEquilibriumOperator>
|
||||
);
|
||||
STATIC_CHECK_FALSE(
|
||||
preconditioning::StellarStructureRuntimeFor<PolytropicMaterialSurfaceDescriptor, DistinctPhysicalCore>
|
||||
);
|
||||
STATIC_CHECK(preconditioning::StellarStructurePreconditionerProblem<PolytropicProblem>);
|
||||
STATIC_CHECK(preconditioning::StellarStructurePreparableFor<
|
||||
PolytropicProblem,
|
||||
MaterialComponent,
|
||||
GravityComponent>);
|
||||
STATIC_CHECK(
|
||||
preconditioning::StellarStructurePreparableFor<PolytropicProblem, MaterialComponent, GravityComponent>
|
||||
);
|
||||
STATIC_CHECK(CanPrepareDefaultStellarStructure<PolytropicProblem>);
|
||||
STATIC_CHECK(std::same_as<typename PolytropicStructure::MaterialToGravityCouplings, ExpectedMaterialToGravity>);
|
||||
STATIC_CHECK(std::same_as<typename PolytropicStructure::GravityToMaterialCouplings, ExpectedGravityToMaterial>);
|
||||
@@ -268,7 +269,7 @@ TEST_CASE(
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{mass}}),
|
||||
constraint::FixedCentralDensity({.RhoC = dimensions::DensityValue{centralDensity}})
|
||||
);
|
||||
auto problem = equilibrium::discretize(stellarModel, finiteElements);
|
||||
auto problem = equilibrium::discretize(stellarModel, std::move(finiteElements));
|
||||
auto projected = seed::makeProjectedEquilibriumState(problem, seed::LaneEmden({.radialSampleCount = 512}));
|
||||
problem.Prepare(projected.values, makeDependencies(), zeroRotation());
|
||||
const auto &physical = problem.GetPreparedOperator().GetPhysicalOperator();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <numbers>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
@@ -14,8 +15,8 @@ import test_helpers;
|
||||
|
||||
namespace {
|
||||
template <typename... Specifications>
|
||||
using ProjectionModelWith = mean_field::model::StellarModel<
|
||||
mean_field::models::SpecificationSet<Specifications...>>;
|
||||
using ProjectionModelWith =
|
||||
mean_field::model::StellarModel<mean_field::models::SpecificationSet<Specifications...>>;
|
||||
|
||||
class UnregisteredProjectionConstraint final {
|
||||
public:
|
||||
@@ -45,11 +46,10 @@ namespace {
|
||||
};
|
||||
|
||||
struct IncompleteProjectionPhysics final {
|
||||
static constexpr bool registered = true;
|
||||
static constexpr bool providesRadialMass = false;
|
||||
static constexpr bool registered = true;
|
||||
static constexpr bool providesRadialMass = false;
|
||||
|
||||
template <typename Model>
|
||||
static constexpr bool supports = true;
|
||||
template <typename Model> static constexpr bool supports = true;
|
||||
};
|
||||
|
||||
class IncompleteProjectionConstraint final {
|
||||
@@ -69,9 +69,8 @@ namespace {
|
||||
class UnregisteredProjectionEquationOfState final {
|
||||
public:
|
||||
struct Parameters final { };
|
||||
using ModelDefinition = mean_field::eos::ConstitutiveLaw<
|
||||
UnregisteredProjectionEquationOfState,
|
||||
"UnregisteredProjectionEquationOfState">;
|
||||
using ModelDefinition = mean_field::eos::
|
||||
ConstitutiveLaw<UnregisteredProjectionEquationOfState, "UnregisteredProjectionEquationOfState">;
|
||||
|
||||
explicit constexpr UnregisteredProjectionEquationOfState(Parameters) noexcept {
|
||||
}
|
||||
@@ -80,27 +79,27 @@ namespace {
|
||||
class UnregisteredProjectionSurface final {
|
||||
public:
|
||||
struct Parameters final { };
|
||||
using ModelDefinition = mean_field::surface::BoundaryCondition<
|
||||
UnregisteredProjectionSurface,
|
||||
"UnregisteredProjectionSurface">;
|
||||
using ModelDefinition =
|
||||
mean_field::surface::BoundaryCondition<UnregisteredProjectionSurface, "UnregisteredProjectionSurface">;
|
||||
|
||||
explicit constexpr UnregisteredProjectionSurface(Parameters) noexcept {
|
||||
}
|
||||
};
|
||||
|
||||
struct SecondRadialMassProjectionPhysics final {
|
||||
static constexpr bool registered = true;
|
||||
static constexpr bool providesRadialMass = true;
|
||||
static constexpr bool registered = true;
|
||||
static constexpr bool providesRadialMass = true;
|
||||
|
||||
template <typename Model>
|
||||
static constexpr bool supports = true;
|
||||
template <typename Model> static constexpr bool supports = true;
|
||||
|
||||
template <typename Specification>
|
||||
[[nodiscard]] static mean_field::dimensions::MassValue targetMass(const Specification &specification) {
|
||||
return specification.targetMass();
|
||||
}
|
||||
|
||||
template <typename Specification, typename Model>
|
||||
template <
|
||||
typename Specification,
|
||||
typename Model>
|
||||
static void validate(
|
||||
const Specification &,
|
||||
const Model &,
|
||||
@@ -109,7 +108,9 @@ namespace {
|
||||
) noexcept {
|
||||
}
|
||||
|
||||
template <typename Specification, typename Model>
|
||||
template <
|
||||
typename Specification,
|
||||
typename Model>
|
||||
static void initialize(
|
||||
const Specification &,
|
||||
const Model &,
|
||||
@@ -128,9 +129,8 @@ namespace {
|
||||
struct Parameters final {
|
||||
mean_field::dimensions::MassValue mass;
|
||||
};
|
||||
using ModelDefinition = mean_field::integral::FixedWithMultiplier<
|
||||
SecondRadialMassConstraint,
|
||||
"SecondRadialMassConstraint">;
|
||||
using ModelDefinition =
|
||||
mean_field::integral::FixedWithMultiplier<SecondRadialMassConstraint, "SecondRadialMassConstraint">;
|
||||
using RadialProjection = mean_field::seed::projection::Use<SecondRadialMassProjectionPhysics>;
|
||||
|
||||
explicit constexpr SecondRadialMassConstraint(Parameters parameters) noexcept : m_mass(parameters.mass) {
|
||||
@@ -178,46 +178,24 @@ TEST_CASE(
|
||||
tags::stellar_seed_projection_type_contract
|
||||
) {
|
||||
using namespace mean_field;
|
||||
using BaseModel = ProjectionModelWith<eos::Polytrope, surface::Isobaric, integral::FixedTotalMass>;
|
||||
using BaseModel = ProjectionModelWith<eos::Polytrope, surface::Isobaric, integral::FixedTotalMass>;
|
||||
using CentralModel = ProjectionModelWith<
|
||||
eos::Polytrope,
|
||||
surface::Isobaric,
|
||||
integral::FixedTotalMass,
|
||||
constraint::FixedCentralDensity>;
|
||||
eos::Polytrope, surface::Isobaric, integral::FixedTotalMass, constraint::FixedCentralDensity>;
|
||||
using AngularModel = ProjectionModelWith<
|
||||
eos::Polytrope,
|
||||
surface::Isobaric,
|
||||
integral::FixedTotalMass,
|
||||
integral::FixedAngularMomentum>;
|
||||
eos::Polytrope, surface::Isobaric, integral::FixedTotalMass, integral::FixedAngularMomentum>;
|
||||
using ExplicitExtensionModel = ProjectionModelWith<
|
||||
eos::Polytrope,
|
||||
surface::Isobaric,
|
||||
integral::FixedTotalMass,
|
||||
ExplicitNoChangeProjectionConstraint>;
|
||||
eos::Polytrope, surface::Isobaric, integral::FixedTotalMass, ExplicitNoChangeProjectionConstraint>;
|
||||
using MissingConstraintRuleModel = ProjectionModelWith<
|
||||
eos::Polytrope,
|
||||
surface::Isobaric,
|
||||
integral::FixedTotalMass,
|
||||
UnregisteredProjectionConstraint>;
|
||||
eos::Polytrope, surface::Isobaric, integral::FixedTotalMass, UnregisteredProjectionConstraint>;
|
||||
using IncompleteConstraintRuleModel = ProjectionModelWith<
|
||||
eos::Polytrope,
|
||||
surface::Isobaric,
|
||||
integral::FixedTotalMass,
|
||||
IncompleteProjectionConstraint>;
|
||||
using MissingEquationOfStateRuleModel = ProjectionModelWith<
|
||||
UnregisteredProjectionEquationOfState,
|
||||
surface::Isobaric,
|
||||
integral::FixedTotalMass>;
|
||||
using MissingSurfaceRuleModel = ProjectionModelWith<
|
||||
eos::Polytrope,
|
||||
UnregisteredProjectionSurface,
|
||||
integral::FixedTotalMass>;
|
||||
eos::Polytrope, surface::Isobaric, integral::FixedTotalMass, IncompleteProjectionConstraint>;
|
||||
using MissingEquationOfStateRuleModel =
|
||||
ProjectionModelWith<UnregisteredProjectionEquationOfState, surface::Isobaric, integral::FixedTotalMass>;
|
||||
using MissingSurfaceRuleModel =
|
||||
ProjectionModelWith<eos::Polytrope, UnregisteredProjectionSurface, integral::FixedTotalMass>;
|
||||
using MissingMassProviderModel = ProjectionModelWith<eos::Polytrope, surface::Isobaric>;
|
||||
using AmbiguousMassProviderModel = ProjectionModelWith<
|
||||
eos::Polytrope,
|
||||
surface::Isobaric,
|
||||
integral::FixedTotalMass,
|
||||
SecondRadialMassConstraint>;
|
||||
using AmbiguousMassProviderModel =
|
||||
ProjectionModelWith<eos::Polytrope, surface::Isobaric, integral::FixedTotalMass, SecondRadialMassConstraint>;
|
||||
|
||||
STATIC_CHECK(seed::RadialProfileProjectableModel<BaseModel>);
|
||||
STATIC_CHECK(seed::RadialProfileProjectableModel<CentralModel>);
|
||||
@@ -272,7 +250,7 @@ TEST_CASE(
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{targetMass}}),
|
||||
constraint::FixedCentralDensity({.RhoC = dimensions::DensityValue{centralDensity}})
|
||||
);
|
||||
auto problem = equilibrium::discretize(stellarModel, finiteElementModel);
|
||||
auto problem = equilibrium::discretize(stellarModel, std::move(finiteElementModel));
|
||||
|
||||
STATIC_CHECK(seed::RadialSeedStrategyFor<seed::LaneEmden, decltype(stellarModel)>);
|
||||
STATIC_CHECK(
|
||||
@@ -346,7 +324,7 @@ TEST_CASE(
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{1.0}}),
|
||||
constraint::FixedCentralDensity({.RhoC = dimensions::DensityValue{1.0}})
|
||||
);
|
||||
auto problem = equilibrium::discretize(stellarModel, finiteElementModel);
|
||||
auto problem = equilibrium::discretize(stellarModel, std::move(finiteElementModel));
|
||||
|
||||
const seed::RadialProfile mismatchedProfile =
|
||||
seed::generateRadialProfile(problem.GetStellarModel(), seed::LaneEmden({.radialSampleCount = 64}));
|
||||
@@ -371,7 +349,7 @@ TEST_CASE(
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{1.0}}),
|
||||
constraint::FixedCentralDensity({.RhoC = dimensions::DensityValue{centralDensity}})
|
||||
);
|
||||
auto problem = equilibrium::discretize(stellarModel, finiteElementModel);
|
||||
auto problem = equilibrium::discretize(stellarModel, std::move(finiteElementModel));
|
||||
const seed::RadialProfile profile =
|
||||
seed::generateRadialProfile(problem.GetStellarModel(), seed::LaneEmden({.radialSampleCount = 64}));
|
||||
|
||||
|
||||
1049
tests/solver/stellar_equilibrium_architecture.cpp
Normal file
1049
tests/solver/stellar_equilibrium_architecture.cpp
Normal file
File diff suppressed because it is too large
Load Diff
319
tests/solver/stellar_equilibrium_architecture_internal.cpp
Normal file
319
tests/solver/stellar_equilibrium_architecture_internal.cpp
Normal file
@@ -0,0 +1,319 @@
|
||||
module;
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <numbers>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <mfem.hpp>
|
||||
#include <mpi.h>
|
||||
|
||||
module mean_field;
|
||||
|
||||
import :solver.stellar_equilibrium;
|
||||
|
||||
namespace solver_internal_architecture_test {
|
||||
struct LifetimeProbe final {
|
||||
const void *problemIdentity{nullptr};
|
||||
bool backendDestroyed{false};
|
||||
bool dependenciesAliveAtBackendDestruction{false};
|
||||
};
|
||||
|
||||
struct InspectingBackend final : mean_field::solver::LinearBackendConfigurationTag {
|
||||
static constexpr mean_field::preconditioning::ApplicationContract supportedPreconditionerContract =
|
||||
mean_field::preconditioning::ApplicationContract::flexible;
|
||||
|
||||
std::shared_ptr<LifetimeProbe> probe;
|
||||
|
||||
explicit InspectingBackend(std::shared_ptr<LifetimeProbe> lifetimeProbe = nullptr)
|
||||
: probe(std::move(lifetimeProbe)) {
|
||||
}
|
||||
};
|
||||
|
||||
struct ThrowingBackend final : mean_field::solver::LinearBackendConfigurationTag {
|
||||
static constexpr mean_field::preconditioning::ApplicationContract supportedPreconditionerContract =
|
||||
mean_field::preconditioning::ApplicationContract::flexible;
|
||||
};
|
||||
|
||||
struct ZeroMetric final { };
|
||||
|
||||
[[nodiscard]] mean_field::solver::nonlinear::MetricEvaluation getMetric(
|
||||
const ZeroMetric &,
|
||||
const mfem::Vector &,
|
||||
MPI_Comm
|
||||
) {
|
||||
return {.residualNorm = 0.0, .merit = 0.0};
|
||||
}
|
||||
|
||||
template <typename Operator, typename Preconditioner> class PreparedBackend final {
|
||||
public:
|
||||
PreparedBackend(
|
||||
const Operator &operation,
|
||||
Preconditioner &preconditioner,
|
||||
const MPI_Comm communicator,
|
||||
std::shared_ptr<LifetimeProbe> probe
|
||||
)
|
||||
: m_operation(&operation),
|
||||
m_preconditioner(&preconditioner),
|
||||
m_communicator(communicator),
|
||||
m_rightHandSide(operation.Height()),
|
||||
m_correction(operation.Width()),
|
||||
m_probe(std::move(probe)) {
|
||||
m_rightHandSide = 0.0;
|
||||
m_correction = 0.0;
|
||||
if (m_probe != nullptr) {
|
||||
m_probe->problemIdentity = std::addressof(operation.GetProblem());
|
||||
}
|
||||
}
|
||||
|
||||
PreparedBackend(const PreparedBackend &) = delete;
|
||||
PreparedBackend &operator=(const PreparedBackend &) = delete;
|
||||
PreparedBackend(PreparedBackend &&) = delete;
|
||||
PreparedBackend &operator=(PreparedBackend &&) = delete;
|
||||
|
||||
~PreparedBackend() {
|
||||
if (m_probe != nullptr) {
|
||||
m_probe->backendDestroyed = true;
|
||||
m_probe->dependenciesAliveAtBackendDestruction =
|
||||
m_operation != nullptr && m_preconditioner != nullptr && m_preconditioner->IsCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] const Operator &GetOperator() const noexcept {
|
||||
return *m_operation;
|
||||
}
|
||||
|
||||
[[nodiscard]] const Preconditioner &GetPreconditioner() const noexcept {
|
||||
return *m_preconditioner;
|
||||
}
|
||||
|
||||
[[nodiscard]] MPI_Comm GetCommunicator() const noexcept {
|
||||
return m_communicator;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsReady() const noexcept {
|
||||
return m_operation != nullptr && m_preconditioner != nullptr && m_communicator != MPI_COMM_NULL &&
|
||||
m_rightHandSide.Size() == m_operation->Height() && m_correction.Size() == m_operation->Width();
|
||||
}
|
||||
|
||||
[[nodiscard]] int RightHandSideSize() const noexcept {
|
||||
return m_rightHandSide.Size();
|
||||
}
|
||||
|
||||
[[nodiscard]] int CorrectionSize() const noexcept {
|
||||
return m_correction.Size();
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::solver::LinearSolveReport Solve(
|
||||
const mfem::Vector &rightHandSide,
|
||||
mfem::Vector &correction,
|
||||
const mean_field::solver::LinearSolveControl &control
|
||||
) {
|
||||
control.Validate();
|
||||
if (rightHandSide.Size() != RightHandSideSize() || correction.Size() != CorrectionSize()) {
|
||||
throw std::invalid_argument("The internal test backend requires preallocated compatible vectors.");
|
||||
}
|
||||
m_rightHandSide = rightHandSide;
|
||||
m_correction = 0.0;
|
||||
correction = m_correction;
|
||||
return {
|
||||
.status = mean_field::solver::LinearSolveStatus::converged,
|
||||
.control = control,
|
||||
.initialResidualNorm = rightHandSide.Norml2(),
|
||||
.reportedResidualNorm = 0.0,
|
||||
.trueResidualNorm = 0.0
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
const Operator *m_operation;
|
||||
Preconditioner *m_preconditioner;
|
||||
MPI_Comm m_communicator;
|
||||
mfem::Vector m_rightHandSide;
|
||||
mfem::Vector m_correction;
|
||||
std::shared_ptr<LifetimeProbe> m_probe;
|
||||
};
|
||||
|
||||
template <
|
||||
typename Operator,
|
||||
typename Preconditioner>
|
||||
[[nodiscard]] auto prepareLinearBackend(
|
||||
InspectingBackend configuration,
|
||||
const Operator &operation,
|
||||
Preconditioner &preconditioner,
|
||||
const MPI_Comm communicator
|
||||
) {
|
||||
return PreparedBackend<Operator, Preconditioner>{
|
||||
operation, preconditioner, communicator, std::move(configuration.probe)
|
||||
};
|
||||
}
|
||||
|
||||
template <
|
||||
typename Operator,
|
||||
typename Preconditioner>
|
||||
[[nodiscard]] auto prepareLinearBackend(
|
||||
ThrowingBackend,
|
||||
const Operator &,
|
||||
Preconditioner &,
|
||||
MPI_Comm
|
||||
)
|
||||
-> PreparedBackend<
|
||||
Operator,
|
||||
Preconditioner> {
|
||||
throw std::runtime_error("The internal test backend rejected restart preparation.");
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::fem::FEM makeFiniteElements() {
|
||||
mean_field::utils::Args arguments;
|
||||
arguments.mesh_file = "sandbox.smesh";
|
||||
arguments.p.rtol = 1.0e-12;
|
||||
arguments.p.atol = 1.0e-12;
|
||||
return mean_field::fem::setup_fem(arguments.mesh_file, arguments, 0);
|
||||
}
|
||||
|
||||
[[nodiscard]] double matchingCentralDensity() {
|
||||
constexpr double stellarRadius = mean_field::utils::RADIUS;
|
||||
constexpr double targetMass = mean_field::utils::MASS;
|
||||
return std::numbers::pi_v<double> * targetMass / (4.0 * stellarRadius * stellarRadius * stellarRadius);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto makeModel() {
|
||||
using namespace mean_field;
|
||||
constexpr double stellarRadius = utils::RADIUS;
|
||||
constexpr double targetMass = utils::MASS;
|
||||
const double polytropicConstant = 2.0 * utils::G * stellarRadius * stellarRadius / std::numbers::pi_v<double>;
|
||||
return model::StellarModel(
|
||||
eos::Polytrope({.n = 1.0, .K = polytropicConstant}),
|
||||
surface::Isobaric({.Psurf = dimensions::PressureValue{0.0}}),
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{targetMass}}),
|
||||
constraint::FixedCentralDensity({.RhoC = dimensions::DensityValue{matchingCentralDensity()}})
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto makeGeneratedRotationModel() {
|
||||
using namespace mean_field;
|
||||
constexpr double stellarRadius = utils::RADIUS;
|
||||
constexpr double targetMass = utils::MASS;
|
||||
const double polytropicConstant = 2.0 * utils::G * stellarRadius * stellarRadius / std::numbers::pi_v<double>;
|
||||
return model::StellarModel(
|
||||
eos::Polytrope({.n = 1.0, .K = polytropicConstant}),
|
||||
surface::Isobaric({.Psurf = dimensions::PressureValue{0.0}}),
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{targetMass}}),
|
||||
integral::FixedAngularMomentum({.Jtotal = dimensions::AngularMomentumValue{0.05}}),
|
||||
constraint::FixedCentralDensity({.RhoC = dimensions::DensityValue{matchingCentralDensity()}})
|
||||
);
|
||||
}
|
||||
|
||||
void checkZeroRotation(const mean_field::physics::RigidRotation &rotation) {
|
||||
REQUIRE(rotation.angular_velocity().Size() == 3);
|
||||
REQUIRE(rotation.center().Size() == 3);
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
CHECK(rotation.angular_velocity()(component) == 0.0);
|
||||
CHECK(rotation.center()(component) == 0.0);
|
||||
}
|
||||
}
|
||||
} // namespace solver_internal_architecture_test
|
||||
|
||||
TEST_CASE(
|
||||
"Report Views Retain Context-Owned Accepted State After Solver Destruction",
|
||||
"[solver][architecture][ownership][report][internal]"
|
||||
) {
|
||||
using namespace mean_field;
|
||||
using namespace solver_internal_architecture_test;
|
||||
|
||||
auto finiteElements = makeFiniteElements();
|
||||
REQUIRE(finiteElements.okay());
|
||||
const MPI_Comm expectedCommunicator = finiteElements.mesh->GetComm();
|
||||
auto probe = std::make_shared<LifetimeProbe>();
|
||||
{
|
||||
auto context = solver::makeContext(
|
||||
makeModel(), equilibrium::StellarDiscretization{std::move(finiteElements)},
|
||||
preconditioning::makePreconditioner(), InspectingBackend{probe}
|
||||
);
|
||||
REQUIRE(context.isReady());
|
||||
REQUIRE(probe->problemIdentity != nullptr);
|
||||
|
||||
auto report = [&] {
|
||||
auto borrowingSolver =
|
||||
solver::make(context, solver::nonlinear::Newton(solver::nonlinear::NewtonOptions{}, ZeroMetric{}));
|
||||
return borrowingSolver.evaluate();
|
||||
}();
|
||||
CHECK_FALSE(context.hasActiveSolver());
|
||||
CHECK_FALSE(probe->backendDestroyed);
|
||||
REQUIRE(report.converged());
|
||||
|
||||
auto structure = report.structureView();
|
||||
REQUIRE(structure.valid());
|
||||
CHECK(
|
||||
structure.model().template specification<constraint::FixedCentralDensity>().targetDensity() ==
|
||||
dimensions::DensityValue{matchingCentralDensity()}
|
||||
);
|
||||
const auto state = structure.state();
|
||||
REQUIRE_FALSE(state.empty());
|
||||
CHECK(std::ranges::all_of(state, [](const mfem::real_t value) { return std::isfinite(value); }));
|
||||
REQUIRE_FALSE(structure.stateDescriptors().empty());
|
||||
REQUIRE_FALSE(structure.stateBlock(utils::blocks::density_field.mass_term).empty());
|
||||
|
||||
int communicatorComparison = MPI_UNEQUAL;
|
||||
REQUIRE(
|
||||
MPI_Comm_compare(structure.communicator(), expectedCommunicator, &communicatorComparison) == MPI_SUCCESS
|
||||
);
|
||||
CHECK((communicatorComparison == MPI_IDENT || communicatorComparison == MPI_CONGRUENT));
|
||||
REQUIRE(structure.prescribedRotation().has_value());
|
||||
checkZeroRotation(*structure.prescribedRotation());
|
||||
checkZeroRotation(structure.rotation());
|
||||
CHECK_THROWS_AS(structure.capture(), std::logic_error);
|
||||
}
|
||||
|
||||
CHECK(probe->backendDestroyed);
|
||||
CHECK(probe->dependenciesAliveAtBackendDestruction);
|
||||
|
||||
auto rejectedFiniteElements = makeFiniteElements();
|
||||
CHECK_THROWS_AS(
|
||||
solver::makeContext(
|
||||
makeModel(), equilibrium::StellarDiscretization{std::move(rejectedFiniteElements)},
|
||||
preconditioning::makePreconditioner(), ThrowingBackend{}
|
||||
),
|
||||
std::runtime_error
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Generated Rotation Is Reported Without A Prescribed Rotation Payload",
|
||||
"[solver][architecture][ownership][rotation][report][internal]"
|
||||
) {
|
||||
using namespace mean_field;
|
||||
using namespace solver_internal_architecture_test;
|
||||
|
||||
auto finiteElements = makeFiniteElements();
|
||||
REQUIRE(finiteElements.okay());
|
||||
auto probe = std::make_shared<LifetimeProbe>();
|
||||
auto context = solver::makeContext(
|
||||
makeGeneratedRotationModel(), equilibrium::StellarDiscretization{std::move(finiteElements)},
|
||||
preconditioning::makePreconditioner(), InspectingBackend{probe}
|
||||
);
|
||||
|
||||
REQUIRE(context.isReady());
|
||||
auto borrowingSolver =
|
||||
solver::make(context, solver::nonlinear::Newton(solver::nonlinear::NewtonOptions{}, ZeroMetric{}));
|
||||
auto report = borrowingSolver.evaluate();
|
||||
auto structure = report.structureView();
|
||||
|
||||
CHECK_FALSE(probe->backendDestroyed);
|
||||
REQUIRE(structure.valid());
|
||||
CHECK_FALSE(structure.prescribedRotation().has_value());
|
||||
const auto rotation = structure.rotation();
|
||||
REQUIRE(rotation.angular_velocity().Size() == 3);
|
||||
REQUIRE(rotation.center().Size() == 3);
|
||||
CHECK(rotation.angular_velocity()(0) == 0.0);
|
||||
CHECK(rotation.angular_velocity()(1) == 0.0);
|
||||
CHECK(rotation.angular_velocity()(2) > 0.0);
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
CHECK(std::isfinite(rotation.angular_velocity()(component)));
|
||||
CHECK(rotation.center()(component) == 0.0);
|
||||
}
|
||||
}
|
||||
1455
tests/solver/stellar_equilibrium_runtime.cpp
Normal file
1455
tests/solver/stellar_equilibrium_runtime.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,7 @@
|
||||
#include <concepts>
|
||||
#include <numbers>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <mfem.hpp>
|
||||
@@ -8,6 +10,16 @@ import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace {
|
||||
struct UserApiZeroMetric final { };
|
||||
|
||||
[[nodiscard]] mean_field::solver::nonlinear::MetricEvaluation getMetric(
|
||||
const UserApiZeroMetric &,
|
||||
const mfem::Vector &,
|
||||
MPI_Comm
|
||||
) {
|
||||
return {.residualNorm = 0.0, .merit = 0.0};
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::fem::FEM makeFiniteElements() {
|
||||
const mean_field::utils::Args arguments = test_utils::setup_args();
|
||||
return mean_field::fem::setup_fem(arguments.mesh_file, arguments, 0);
|
||||
@@ -18,9 +30,7 @@ namespace {
|
||||
Problem &problem,
|
||||
const mfem::Vector &state,
|
||||
const mean_field::operators::StellarEquilibriumDependencies &dependencies
|
||||
) {
|
||||
problem.Prepare(state, dependencies);
|
||||
};
|
||||
) { problem.Prepare(state, dependencies); };
|
||||
|
||||
template <typename Problem>
|
||||
concept PreparesWithPrescribedRotation = requires(
|
||||
@@ -28,9 +38,7 @@ namespace {
|
||||
const mfem::Vector &state,
|
||||
const mean_field::operators::StellarEquilibriumDependencies &dependencies,
|
||||
const mean_field::physics::RigidRotation &rotation
|
||||
) {
|
||||
problem.Prepare(state, dependencies, rotation);
|
||||
};
|
||||
) { problem.Prepare(state, dependencies, rotation); };
|
||||
} // namespace
|
||||
|
||||
TEST_CASE(
|
||||
@@ -47,7 +55,7 @@ TEST_CASE(
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{1.0}})
|
||||
);
|
||||
|
||||
auto problem = equilibrium::discretize(model, finiteElements);
|
||||
auto problem = equilibrium::discretize(model, std::move(finiteElements));
|
||||
auto preconditioner = preconditioning::makePreconditioner(problem);
|
||||
const auto &gravity = preconditioner.structureComponent().gravityComponent();
|
||||
|
||||
@@ -62,6 +70,52 @@ TEST_CASE(
|
||||
CHECK(gravity.potentialSchurBackend().application.cycles == 3);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Complete User API Builds A Context And Evaluates A Borrowing Newton Solver",
|
||||
"[user-api][solver][context][report]"
|
||||
) {
|
||||
using namespace mean_field;
|
||||
|
||||
auto finiteElements = makeFiniteElements();
|
||||
REQUIRE(finiteElements.okay());
|
||||
|
||||
constexpr double radius = utils::RADIUS;
|
||||
const double polytropicConstant = 2.0 * utils::G * radius * radius / std::numbers::pi_v<double>;
|
||||
const double centralDensity = std::numbers::pi_v<double> * utils::MASS / (4.0 * radius * radius * radius);
|
||||
auto stellarModel = model::StellarModel(
|
||||
eos::Polytrope({.n = 1.0, .K = polytropicConstant}),
|
||||
surface::Isobaric({.Psurf = dimensions::PressureValue{0.0}}),
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{utils::MASS}}),
|
||||
constraint::FixedCentralDensity({.RhoC = dimensions::DensityValue{centralDensity}})
|
||||
);
|
||||
auto discretization = equilibrium::makeStellarDiscretization(
|
||||
std::move(finiteElements), normalization::PhysicalRieszDiagonal{dimensions::LengthValue{radius}, utils::G}
|
||||
);
|
||||
auto context = solver::makeContext(
|
||||
std::move(stellarModel), std::move(discretization), preconditioning::makePreconditioner(),
|
||||
solver::linear::FGMRES({.restartLength = 20, .printLevel = -1})
|
||||
);
|
||||
|
||||
int beforeCalls = 0;
|
||||
int afterCalls = 0;
|
||||
auto observer = solver::nonlinear::makeObserver(
|
||||
[&beforeCalls](const solver::nonlinear::BeforeIteration &) { ++beforeCalls; },
|
||||
[&afterCalls](const solver::nonlinear::AfterIteration &) { ++afterCalls; }
|
||||
);
|
||||
auto newton = solver::nonlinear::Newton(solver::nonlinear::NewtonOptions{}, UserApiZeroMetric{});
|
||||
auto equilibriumSolver = solver::make(context, newton, observer);
|
||||
auto report = equilibriumSolver.evaluate();
|
||||
|
||||
REQUIRE(report.converged());
|
||||
CHECK(report.completedNonlinearIterations() == 0);
|
||||
CHECK(beforeCalls == 0);
|
||||
CHECK(afterCalls == 0);
|
||||
auto structure = report.structureView();
|
||||
REQUIRE(structure.valid());
|
||||
CHECK_FALSE(structure.state().empty());
|
||||
CHECK(context.hasActiveSolver());
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Fixed Angular Momentum User API Generates Rotation And Its Composable Solver Border",
|
||||
"[user-api][fixed-angular-momentum][type]"
|
||||
@@ -72,18 +126,14 @@ TEST_CASE(
|
||||
REQUIRE(finiteElements.okay());
|
||||
|
||||
auto model = model::StellarModel(
|
||||
eos::Polytrope({.n = 1.0, .K = 0.25}),
|
||||
surface::Isobaric({.Psurf = dimensions::PressureValue{0.0}}),
|
||||
eos::Polytrope({.n = 1.0, .K = 0.25}), surface::Isobaric({.Psurf = dimensions::PressureValue{0.0}}),
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{1.0}}),
|
||||
integral::FixedAngularMomentum({
|
||||
.Jtotal = dimensions::AngularMomentumValue{0.2},
|
||||
.axis = {0.0, 0.0, 2.0}
|
||||
})
|
||||
integral::FixedAngularMomentum({.Jtotal = dimensions::AngularMomentumValue{0.2}, .axis = {0.0, 0.0, 2.0}})
|
||||
);
|
||||
|
||||
auto problem = equilibrium::discretize(model, finiteElements);
|
||||
auto preconditioner = preconditioning::makePreconditioner(problem);
|
||||
using Problem = std::remove_cvref_t<decltype(problem)>;
|
||||
auto problem = equilibrium::discretize(model, std::move(finiteElements));
|
||||
auto preconditioner = preconditioning::makePreconditioner(problem);
|
||||
using Problem = std::remove_cvref_t<decltype(problem)>;
|
||||
using Preconditioner = std::remove_cvref_t<decltype(preconditioner)>;
|
||||
|
||||
STATIC_CHECK(Problem::hasFixedAngularMomentum);
|
||||
@@ -99,8 +149,9 @@ TEST_CASE(
|
||||
CHECK(problem.StateSize() == problem.EquationSize());
|
||||
CHECK(problem.StateSize() == problem.GetPhysicalOperator().Width() + 1);
|
||||
REQUIRE(problem.GetManifest().constraints().size() == 3);
|
||||
CHECK(problem.GetManifest().template specification<models::FixedAngularMomentum>().stableId ==
|
||||
"FixedAngularMomentum");
|
||||
CHECK(
|
||||
problem.GetManifest().template specification<models::FixedAngularMomentum>().stableId == "FixedAngularMomentum"
|
||||
);
|
||||
CHECK(problem.GetManifest().valueBlocks().back().symbol == "Omega");
|
||||
CHECK(problem.GetManifest().residualBlocks().back().symbol == "R_J");
|
||||
}
|
||||
@@ -119,7 +170,7 @@ TEST_CASE(
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{1.0}}),
|
||||
constraint::FixedCentralDensity({.RhoC = dimensions::DensityValue{1.0}})
|
||||
);
|
||||
auto problem = equilibrium::discretize(model, finiteElements);
|
||||
auto problem = equilibrium::discretize(model, std::move(finiteElements));
|
||||
|
||||
auto material = preconditioning::materialSurfaceBlock(problem);
|
||||
auto gravity = preconditioning::GravityFieldBlock(
|
||||
@@ -151,7 +202,7 @@ TEST_CASE(
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{1.0}}),
|
||||
constraint::FixedCentralDensity({.RhoC = dimensions::DensityValue{1.0}})
|
||||
);
|
||||
auto problem = equilibrium::discretize(model, finiteElements);
|
||||
auto problem = equilibrium::discretize(model, std::move(finiteElements));
|
||||
|
||||
auto material = preconditioning::materialSurfaceBlock(
|
||||
problem, preconditioning::backend::Diagonal{}, preconditioning::backend::Diagonal{},
|
||||
|
||||
Reference in New Issue
Block a user