feat(newton): first newton solver implementation
This commit is contained in:
@@ -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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user