feat(surface): major work on implementing surface constraints in a presciption agnostic manner

This commit is contained in:
2026-08-30 16:41:14 -04:00
parent 36adfa1174
commit 0a7f18c5c7
95 changed files with 30144 additions and 25766 deletions

View File

@@ -164,9 +164,7 @@ TEST_CASE(
CHECK_FALSE(gravityPotentialReport.updatedDisplacement);
CHECK_FALSE(gravityPotentialReport.updatedBernoulliConstant);
CHECK(
context.GetBaseGravityPotentialTrue()(context.GetGravityPotentialMap().true_dof(0)) == gravityPotential(0)
);
CHECK(context.GetBaseGravityPotentialTrue()(context.GetGravityPotentialMap().true_dof(0)) == gravityPotential(0));
++dependencies.bernoulliConstant.revision;

File diff suppressed because it is too large Load Diff

View File

@@ -11,386 +11,370 @@ import mean_field;
import test_helpers;
namespace gravity_displacement_force_analytic_test_utils {
struct AffineCase {
const char *name;
std::array<double, 3> scales;
};
struct AffineCase {
const char *name;
std::array<double, 3> scales;
};
[[nodiscard]] double analytic_sphere_volume(const double radius) {
return (4.0 / 3.0) * std::numbers::pi * radius * radius * radius;
}
[[nodiscard]] double analytic_sphere_volume(const double radius) {
return (4.0 / 3.0) * std::numbers::pi * radius * radius * radius;
}
[[nodiscard]] double determinant(const std::array<double, 3> &scales) {
return scales[0] * scales[1] * scales[2];
}
[[nodiscard]] double determinant(
const std::array<
double,
3> &scales
) {
return scales[0] * scales[1] * scales[2];
}
[[nodiscard]] double relative_scalar_error(const double computed,
const double expected) {
return std::abs(computed - expected) / std::max(std::abs(expected), 1.0e-30);
}
[[nodiscard]] double relative_scalar_error(
const double computed,
const double expected
) {
return std::abs(computed - expected) / std::max(std::abs(expected), 1.0e-30);
}
[[nodiscard]] mfem::Vector make_constant_density(const mean_field::fem::FEM &f,
const double densityValue) {
mfem::ParGridFunction densityField(f.densityFes.get());
mfem::ConstantCoefficient densityCoefficient(densityValue);
densityField.ProjectCoefficient(densityCoefficient);
[[nodiscard]] mfem::Vector make_constant_density(
const mean_field::fem::FEM &f,
const double densityValue
) {
mfem::ParGridFunction densityField(f.densityFes.get());
mfem::ConstantCoefficient densityCoefficient(densityValue);
densityField.ProjectCoefficient(densityCoefficient);
mfem::Vector densityTrue;
densityField.GetTrueDofs(densityTrue);
return densityTrue;
}
mfem::Vector densityTrue;
densityField.GetTrueDofs(densityTrue);
return densityTrue;
}
[[nodiscard]] mfem::Vector
make_reference_gravity(const mean_field::fem::FEM &f,
const std::array<double, 3> &referenceGravity) {
mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
[[nodiscard]] mfem::Vector make_reference_gravity(
const mean_field::fem::FEM &f,
const std::array<
double,
3> &referenceGravity
) {
mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
mfem::VectorFunctionCoefficient gravityCoefficient(
f.mesh->Dimension(),
[referenceGravity](const mfem::Vector &, mfem::Vector &value) {
value.SetSize(3);
mfem::VectorFunctionCoefficient gravityCoefficient(
f.mesh->Dimension(), [referenceGravity](const mfem::Vector &, mfem::Vector &value) {
value.SetSize(3);
for (int component = 0; component < 3; ++component) {
value(component) =
referenceGravity[static_cast<std::size_t>(component)];
}
});
for (int component = 0; component < 3; ++component) {
value(component) = referenceGravity[static_cast<std::size_t>(component)];
}
}
);
gravityField.ProjectCoefficient(gravityCoefficient);
gravityField.ProjectCoefficient(gravityCoefficient);
mfem::Vector gravityTrue;
gravityField.GetTrueDofs(gravityTrue);
return gravityTrue;
}
mfem::Vector gravityTrue;
gravityField.GetTrueDofs(gravityTrue);
return gravityTrue;
}
[[nodiscard]] mfem::Vector make_radial_gravity(const mean_field::fem::FEM &f,
const double radialCoefficient) {
mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
[[nodiscard]] mfem::Vector make_radial_gravity(
const mean_field::fem::FEM &f,
const double radialCoefficient
) {
mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
mfem::VectorFunctionCoefficient gravityCoefficient(
f.mesh->Dimension(),
[radialCoefficient](const mfem::Vector &position, mfem::Vector &value) {
value.SetSize(position.Size());
mfem::VectorFunctionCoefficient gravityCoefficient(
f.mesh->Dimension(), [radialCoefficient](const mfem::Vector &position, mfem::Vector &value) {
value.SetSize(position.Size());
for (int component = 0; component < position.Size(); ++component) {
value(component) = radialCoefficient * position(component);
}
});
for (int component = 0; component < position.Size(); ++component) {
value(component) = radialCoefficient * position(component);
}
}
);
gravityField.ProjectCoefficient(gravityCoefficient);
gravityField.ProjectCoefficient(gravityCoefficient);
mfem::Vector gravityTrue;
gravityField.GetTrueDofs(gravityTrue);
return gravityTrue;
}
mfem::Vector gravityTrue;
gravityField.GetTrueDofs(gravityTrue);
return gravityTrue;
}
[[nodiscard]] mfem::Vector
make_affine_displacement(const mean_field::fem::FEM &f,
const std::array<double, 3> &scales) {
mfem::ParGridFunction displacementField(f.displacementFes.get());
[[nodiscard]] mfem::Vector make_affine_displacement(
const mean_field::fem::FEM &f,
const std::array<
double,
3> &scales
) {
mfem::ParGridFunction displacementField(f.displacementFes.get());
mfem::VectorFunctionCoefficient displacementCoefficient(
f.mesh->Dimension(),
[scales](const mfem::Vector &position, mfem::Vector &value) {
value.SetSize(position.Size());
mfem::VectorFunctionCoefficient displacementCoefficient(
f.mesh->Dimension(), [scales](const mfem::Vector &position, mfem::Vector &value) {
value.SetSize(position.Size());
for (int component = 0; component < position.Size(); ++component) {
value(component) =
(scales[static_cast<std::size_t>(component)] - 1.0) *
position(component);
}
});
for (int component = 0; component < position.Size(); ++component) {
value(component) = (scales[static_cast<std::size_t>(component)] - 1.0) * position(component);
}
}
);
displacementField.ProjectCoefficient(displacementCoefficient);
displacementField.ProjectCoefficient(displacementCoefficient);
mfem::Vector displacementTrue;
displacementField.GetTrueDofs(displacementTrue);
return displacementTrue;
}
mfem::Vector displacementTrue;
displacementField.GetTrueDofs(displacementTrue);
return displacementTrue;
}
[[nodiscard]] mfem::Vector
make_constant_test_direction(const mean_field::fem::FEM &f,
const int selectedComponent) {
mfem::ParGridFunction testField(f.displacementFes.get());
[[nodiscard]] mfem::Vector make_constant_test_direction(
const mean_field::fem::FEM &f,
const int selectedComponent
) {
mfem::ParGridFunction testField(f.displacementFes.get());
mfem::VectorFunctionCoefficient testCoefficient(
f.mesh->Dimension(),
[selectedComponent](const mfem::Vector &position, mfem::Vector &value) {
value.SetSize(position.Size());
value = 0.0;
value(selectedComponent) = 1.0;
});
mfem::VectorFunctionCoefficient testCoefficient(
f.mesh->Dimension(), [selectedComponent](const mfem::Vector &position, mfem::Vector &value) {
value.SetSize(position.Size());
value = 0.0;
value(selectedComponent) = 1.0;
}
);
testField.ProjectCoefficient(testCoefficient);
testField.ProjectCoefficient(testCoefficient);
mfem::Vector testTrue;
testField.GetTrueDofs(testTrue);
return testTrue;
}
mfem::Vector testTrue;
testField.GetTrueDofs(testTrue);
return testTrue;
}
[[nodiscard]] mfem::Vector
make_dilation_test_direction(const mean_field::fem::FEM &f) {
mfem::ParGridFunction testField(f.displacementFes.get());
[[nodiscard]] mfem::Vector make_dilation_test_direction(const mean_field::fem::FEM &f) {
mfem::ParGridFunction testField(f.displacementFes.get());
mfem::VectorFunctionCoefficient testCoefficient(
f.mesh->Dimension(), [](const mfem::Vector &position,
mfem::Vector &value) { value = position; });
mfem::VectorFunctionCoefficient testCoefficient(
f.mesh->Dimension(), [](const mfem::Vector &position, mfem::Vector &value) { value = position; }
);
testField.ProjectCoefficient(testCoefficient);
testField.ProjectCoefficient(testCoefficient);
mfem::Vector testTrue;
testField.GetTrueDofs(testTrue);
return testTrue;
}
mfem::Vector testTrue;
testField.GetTrueDofs(testTrue);
return testTrue;
}
void set_mass_normalized_density(mean_field::fem::FEM &f,
const double targetMass,
mfem::ParGridFunction &densityField) {
const mfem::Vector stellarDensityTrue =
gravity_prepared_test_utils::make_domain_supported_density(f, true);
void set_mass_normalized_density(
mean_field::fem::FEM &f,
const double targetMass,
mfem::ParGridFunction &densityField
) {
const mfem::Vector stellarDensityTrue = gravity_prepared_test_utils::make_domain_supported_density(f, true);
densityField.SetFromTrueDofs(stellarDensityTrue);
densityField.SetFromTrueDofs(stellarDensityTrue);
const double unnormalizedMass =
mean_field::analysis::domain_integrate_grid_function(
f, densityField, mean_field::utils::DOMAINS::STELLAR);
const double unnormalizedMass =
mean_field::analysis::domain_integrate_grid_function(f, densityField, mean_field::utils::DOMAINS::STELLAR);
MFEM_VERIFY(unnormalizedMass > 0.0,
"The analytic gravity-force test obtained non-positive mass.");
MFEM_VERIFY(unnormalizedMass > 0.0, "The analytic gravity-force test obtained non-positive mass.");
densityField *= targetMass / unnormalizedMass;
}
densityField *= targetMass / unnormalizedMass;
}
} // namespace gravity_displacement_force_analytic_test_utils
TEST_CASE("Gravity Displacement Force Matches Analytic Affine Resultants",
tags::gravity &tags::accuracy &tags::analytic_comparison
&tags::integration) {
mean_field::utils::Args args = test_utils::setup_args();
TEST_CASE(
"Gravity Displacement Force Matches Analytic Affine Resultants",
tags::gravity &tags::accuracy &tags::analytic_comparison &tags::integration
) {
mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
REQUIRE(f.okay());
REQUIRE(f.domainMapperStateless != nullptr);
REQUIRE(f.domainMapperStateless != nullptr);
REQUIRE(f.okay());
REQUIRE(f.domainMapperStateless != nullptr);
REQUIRE(f.domainMapperStateless != nullptr);
constexpr double densityValue = 1.37;
constexpr double densityValue = 1.37;
constexpr std::array<double, 3> physicalGravity{0.31, -0.47, 0.22};
constexpr std::array<double, 3> physicalGravity{0.31, -0.47, 0.22};
constexpr std::array<
gravity_displacement_force_analytic_test_utils::AffineCase, 3>
affineCases{{{.name = "identity geometry", .scales = {1.0, 1.0, 1.0}},
{.name = "volume-preserving affine geometry",
.scales = {1.14, 0.93, 1.0 / (1.14 * 0.93)}},
{.name = "volume-changing affine geometry",
.scales = {1.11, 0.96, 1.07}}}};
constexpr std::array<gravity_displacement_force_analytic_test_utils::AffineCase, 3> affineCases{
{{.name = "identity geometry", .scales = {1.0, 1.0, 1.0}},
{.name = "volume-preserving affine geometry", .scales = {1.14, 0.93, 1.0 / (1.14 * 0.93)}},
{.name = "volume-changing affine geometry", .scales = {1.11, 0.96, 1.07}}}
};
const mfem::Vector density =
gravity_displacement_force_analytic_test_utils::make_constant_density(
f, densityValue);
const mfem::Vector density = gravity_displacement_force_analytic_test_utils::make_constant_density(f, densityValue);
const double referenceVolume =
gravity_displacement_force_analytic_test_utils::analytic_sphere_volume(
mean_field::utils::RADIUS);
const double referenceVolume =
gravity_displacement_force_analytic_test_utils::analytic_sphere_volume(mean_field::utils::RADIUS);
constexpr double relativeTolerance = 5.0e-6;
constexpr double relativeTolerance = 5.0e-6;
for (const gravity_displacement_force_analytic_test_utils::AffineCase
&affineCase : affineCases) {
DYNAMIC_SECTION(affineCase.name) {
const double mapDeterminant =
gravity_displacement_force_analytic_test_utils::determinant(
affineCase.scales);
for (const gravity_displacement_force_analytic_test_utils::AffineCase &affineCase : affineCases) {
DYNAMIC_SECTION(affineCase.name) {
const double mapDeterminant =
gravity_displacement_force_analytic_test_utils::determinant(affineCase.scales);
REQUIRE(mapDeterminant > 0.0);
REQUIRE(mapDeterminant > 0.0);
std::array<double, 3> referenceGravity{};
std::array<double, 3> referenceGravity{};
/*
* For x = A X, the H(div) Piola relation is
*
* g_phys = A g_ref / det(A).
*
* Prescribe the RT pullback that represents the requested
* constant physical gravity field exactly.
*/
for (int component = 0; component < 3; ++component) {
referenceGravity[static_cast<std::size_t>(component)] =
mapDeterminant *
physicalGravity[static_cast<std::size_t>(component)] /
affineCase.scales[static_cast<std::size_t>(component)];
}
/*
* For x = A X, the H(div) Piola relation is
*
* g_phys = A g_ref / det(A).
*
* Prescribe the RT pullback that represents the requested
* constant physical gravity field exactly.
*/
for (int component = 0; component < 3; ++component) {
referenceGravity[static_cast<std::size_t>(component)] =
mapDeterminant * physicalGravity[static_cast<std::size_t>(component)] /
affineCase.scales[static_cast<std::size_t>(component)];
}
const mfem::Vector gravityGradient =
gravity_displacement_force_analytic_test_utils::
make_reference_gravity(f, referenceGravity);
const mfem::Vector gravityGradient =
gravity_displacement_force_analytic_test_utils::make_reference_gravity(f, referenceGravity);
const mfem::Vector displacement =
gravity_displacement_force_analytic_test_utils::
make_affine_displacement(f, affineCase.scales);
const mfem::Vector displacement =
gravity_displacement_force_analytic_test_utils::make_affine_displacement(f, affineCase.scales);
mfem::Vector residual;
mfem::Vector residual;
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
f, *f.domainMapperStateless, density, gravityGradient, displacement,
residual);
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
f, *f.domainMapperStateless, density, gravityGradient, displacement, residual
);
for (int component = 0; component < 3; ++component) {
const mfem::Vector testDirection =
gravity_displacement_force_analytic_test_utils::
make_constant_test_direction(f, component);
for (int component = 0; component < 3; ++component) {
const mfem::Vector testDirection =
gravity_displacement_force_analytic_test_utils::make_constant_test_direction(f, component);
const double computedResultant =
gravity_prepared_test_utils::global_dot(residual, testDirection,
f.mesh->GetComm());
const double computedResultant =
gravity_prepared_test_utils::global_dot(residual, testDirection, f.mesh->GetComm());
const double expectedResultant =
densityValue *
physicalGravity[static_cast<std::size_t>(component)] *
mapDeterminant * referenceVolume;
const double expectedResultant = densityValue * physicalGravity[static_cast<std::size_t>(component)] *
mapDeterminant * referenceVolume;
const double relativeError =
gravity_displacement_force_analytic_test_utils::
relative_scalar_error(computedResultant, expectedResultant);
const double relativeError = gravity_displacement_force_analytic_test_utils::relative_scalar_error(
computedResultant, expectedResultant
);
CAPTURE(component);
INFO("Map determinant = " << mapDeterminant);
INFO("Computed resultant = " << computedResultant);
INFO("Analytic resultant = " << expectedResultant);
INFO("Relative resultant error = " << relativeError);
CAPTURE(component);
INFO("Map determinant = " << mapDeterminant);
INFO("Computed resultant = " << computedResultant);
INFO("Analytic resultant = " << expectedResultant);
INFO("Relative resultant error = " << relativeError);
CHECK(relativeError < relativeTolerance);
}
CHECK(relativeError < relativeTolerance);
}
}
}
}
}
TEST_CASE(
"Gravity Displacement Force Reproduces Analytic Homogeneous Sphere Work",
tags::gravity &tags::accuracy &tags::analytic_comparison
&tags::integration) {
mean_field::utils::Args args = test_utils::setup_args();
tags::gravity &tags::accuracy &tags::analytic_comparison &tags::integration
) {
mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
REQUIRE(f.okay());
REQUIRE(f.domainMapperStateless != nullptr);
REQUIRE(f.okay());
REQUIRE(f.domainMapperStateless != nullptr);
const double radius = mean_field::utils::RADIUS;
const double mass = mean_field::utils::MASS;
const double volume =
gravity_displacement_force_analytic_test_utils::analytic_sphere_volume(
radius);
const double radius = mean_field::utils::RADIUS;
const double mass = mean_field::utils::MASS;
const double volume = gravity_displacement_force_analytic_test_utils::analytic_sphere_volume(radius);
const double densityValue = mass / volume;
const double radialGravityCoefficient =
mean_field::utils::G * mass / (radius * radius * radius);
const double densityValue = mass / volume;
const double radialGravityCoefficient = mean_field::utils::G * mass / (radius * radius * radius);
const mfem::Vector density =
gravity_displacement_force_analytic_test_utils::make_constant_density(
f, densityValue);
const mfem::Vector density = gravity_displacement_force_analytic_test_utils::make_constant_density(f, densityValue);
const mfem::Vector gravityGradient =
gravity_displacement_force_analytic_test_utils::make_radial_gravity(
f, radialGravityCoefficient);
const mfem::Vector gravityGradient =
gravity_displacement_force_analytic_test_utils::make_radial_gravity(f, radialGravityCoefficient);
mfem::Vector displacement(f.displacementFes->GetTrueVSize());
displacement = 0.0;
mfem::Vector displacement(f.displacementFes->GetTrueVSize());
displacement = 0.0;
mfem::Vector residual;
mfem::Vector residual;
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
f, *f.domainMapperStateless, density, gravityGradient, displacement,
residual);
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
f, *f.domainMapperStateless, density, gravityGradient, displacement, residual
);
const mfem::Vector dilationDirection =
gravity_displacement_force_analytic_test_utils::
make_dilation_test_direction(f);
const mfem::Vector dilationDirection =
gravity_displacement_force_analytic_test_utils::make_dilation_test_direction(f);
const double computedWork = gravity_prepared_test_utils::global_dot(
residual, dilationDirection, f.mesh->GetComm());
const double computedWork = gravity_prepared_test_utils::global_dot(residual, dilationDirection, f.mesh->GetComm());
const double analyticWork =
(3.0 / 5.0) * mean_field::utils::G * mass * mass / radius;
const double analyticWork = (3.0 / 5.0) * mean_field::utils::G * mass * mass / radius;
const double relativeError =
gravity_displacement_force_analytic_test_utils::relative_scalar_error(
computedWork, analyticWork);
const double relativeError =
gravity_displacement_force_analytic_test_utils::relative_scalar_error(computedWork, analyticWork);
INFO("Computed positive gravity work = " << computedWork);
INFO("Analytic positive gravity work = " << analyticWork);
INFO("Computed gravitational virial = " << -computedWork);
INFO("Analytic binding energy = " << -analyticWork);
INFO("Relative analytic work error = " << relativeError);
INFO("Computed positive gravity work = " << computedWork);
INFO("Analytic positive gravity work = " << analyticWork);
INFO("Computed gravitational virial = " << -computedWork);
INFO("Analytic binding energy = " << -analyticWork);
INFO("Relative analytic work error = " << relativeError);
REQUIRE(computedWork > 0.0);
CHECK(relativeError < 1.0e-5);
REQUIRE(computedWork > 0.0);
CHECK(relativeError < 1.0e-5);
}
TEST_CASE("Solved Homogeneous Sphere Gravity Force Matches Analytic Virial",
tags::gravity &tags::accuracy &tags::analytic_comparison
&tags::integration &tags::initialization) {
mean_field::utils::Args args = test_utils::setup_args();
args.p.rtol = 1.0e-13;
args.p.max_iters = std::max(args.p.max_iters, 1000);
TEST_CASE(
"Solved Homogeneous Sphere Gravity Force Matches Analytic Virial",
tags::gravity &tags::accuracy &tags::analytic_comparison &tags::integration &tags::initialization
) {
mean_field::utils::Args args = test_utils::setup_args();
args.p.rtol = 1.0e-13;
args.p.max_iters = std::max(args.p.max_iters, 1000);
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
REQUIRE(f.okay());
REQUIRE(f.domainMapperStateless != nullptr);
REQUIRE(f.okay());
REQUIRE(f.domainMapperStateless != nullptr);
mfem::ParGridFunction displacementField(f.displacementFes.get());
displacementField = 0.0;
mfem::ParGridFunction displacementField(f.displacementFes.get());
displacementField = 0.0;
REQUIRE(f.domainMapperStateless != nullptr);
*f.displacement = 0.0;
REQUIRE(f.domainMapperStateless != nullptr);
*f.displacement = 0.0;
const double radius = mean_field::utils::RADIUS;
const double mass = mean_field::utils::MASS;
const double radius = mean_field::utils::RADIUS;
const double mass = mean_field::utils::MASS;
mfem::ParGridFunction densityField(f.densityFes.get());
mfem::ParGridFunction densityField(f.densityFes.get());
gravity_displacement_force_analytic_test_utils::set_mass_normalized_density(
f, mass, densityField);
gravity_displacement_force_analytic_test_utils::set_mass_normalized_density(f, mass, densityField);
const mean_field::physics::GravitySolution gravitySolution =
mean_field::physics::solve_gravity_field(f, args, densityField,
displacementField);
const mean_field::physics::GravitySolution gravitySolution =
mean_field::physics::solve_gravity_field(f, args, densityField, displacementField);
mfem::Vector densityTrue;
mfem::Vector gravityGradientTrue;
mfem::Vector displacementTrue;
mfem::Vector densityTrue;
mfem::Vector gravityGradientTrue;
mfem::Vector displacementTrue;
densityField.GetTrueDofs(densityTrue);
gravitySolution.gradPhi.GetTrueDofs(gravityGradientTrue);
displacementField.GetTrueDofs(displacementTrue);
densityField.GetTrueDofs(densityTrue);
gravitySolution.gradPhi.GetTrueDofs(gravityGradientTrue);
displacementField.GetTrueDofs(displacementTrue);
mfem::Vector residual;
mfem::Vector residual;
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
f, *f.domainMapperStateless, densityTrue, gravityGradientTrue,
displacementTrue, residual);
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
f, *f.domainMapperStateless, densityTrue, gravityGradientTrue, displacementTrue, residual
);
const mfem::Vector dilationDirection =
gravity_displacement_force_analytic_test_utils::
make_dilation_test_direction(f);
const mfem::Vector dilationDirection =
gravity_displacement_force_analytic_test_utils::make_dilation_test_direction(f);
const double computedWork = gravity_prepared_test_utils::global_dot(
residual, dilationDirection, f.mesh->GetComm());
const double computedWork = gravity_prepared_test_utils::global_dot(residual, dilationDirection, f.mesh->GetComm());
const double analyticWork =
(3.0 / 5.0) * mean_field::utils::G * mass * mass / radius;
const double analyticWork = (3.0 / 5.0) * mean_field::utils::G * mass * mass / radius;
const double relativeError =
gravity_displacement_force_analytic_test_utils::relative_scalar_error(
computedWork, analyticWork);
const double relativeError =
gravity_displacement_force_analytic_test_utils::relative_scalar_error(computedWork, analyticWork);
INFO("Solved-field positive gravity work = " << computedWork);
INFO("Analytic positive gravity work = " << analyticWork);
INFO("Solved-field gravitational virial = " << -computedWork);
INFO("Analytic homogeneous-sphere binding energy = " << -analyticWork);
INFO("Relative solved-field virial error = " << relativeError);
INFO("Solved-field positive gravity work = " << computedWork);
INFO("Analytic positive gravity work = " << analyticWork);
INFO("Solved-field gravitational virial = " << -computedWork);
INFO("Analytic homogeneous-sphere binding energy = " << -analyticWork);
INFO("Relative solved-field virial error = " << relativeError);
REQUIRE(computedWork > 0.0);
CHECK(relativeError < 1.0e-5);
REQUIRE(computedWork > 0.0);
CHECK(relativeError < 1.0e-5);
}

File diff suppressed because it is too large Load Diff

View File

@@ -72,7 +72,10 @@ TEST_CASE(
constexpr double enthalpyValue = 0.8;
const double densityValue = barotrope.density_from_enthalpy(enthalpyValue);
const double densityValue = mean_field::eos::evaluate<mean_field::eos::quantity::Density>(
barotrope, mean_field::eos::SpecificEnthalpyValue{enthalpyValue}
)
.value();
const mfem::Vector enthalpy = project_constant(*f.enthalpyFes, enthalpyValue);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -242,8 +242,12 @@ namespace prepared_barotropic_closure_test_utils {
const ClosureCondition &condition
) {
mfem::FunctionCoefficient coefficient([&equationOfState, condition](const mfem::Vector &position) {
const double enthalpy = evaluate_enthalpy(position, condition);
return condition.densityFactor * equationOfState.density_from_enthalpy(enthalpy) + condition.densityOffset +
const double enthalpy = evaluate_enthalpy(position, condition);
const double equationOfStateDensity = mean_field::eos::evaluate<mean_field::eos::quantity::Density>(
equationOfState, mean_field::eos::SpecificEnthalpyValue{enthalpy}
)
.value();
return condition.densityFactor * equationOfStateDensity + condition.densityOffset +
condition.densityGradient * (0.40 * position(0) + 0.25 * position(1) - 0.15 * position(2));
});
return project_scalar(*f.densityFes, coefficient);
@@ -784,8 +788,12 @@ namespace prepared_barotropic_closure_test_utils {
const Maps maps(f);
const mean_field::eos::Polytrope equationOfState(3.0, 1.5);
constexpr double enthalpyValue = 1.20;
const double equilibriumDensityValue = equationOfState.density_from_enthalpy(enthalpyValue);
constexpr double enthalpyValue = 1.20;
const double equilibriumDensityValue =
mean_field::eos::evaluate<mean_field::eos::quantity::Density>(
equationOfState, mean_field::eos::SpecificEnthalpyValue{enthalpyValue}
)
.value();
const mfem::Vector enthalpy = reduce(maps.enthalpy, make_constant_field(*f.enthalpyFes, enthalpyValue));
const mfem::Vector equilibriumDensity =

View File

@@ -10,181 +10,157 @@ using namespace mean_field;
using Catch::Matchers::WithinAbs;
namespace prepared_test = gravity_prepared_test_utils;
TEST_CASE("Prepared Mapped Hdiv Mass Matches Stateless Kernel",
tags::gravity_prepared) {
auto args = test_utils::setup_args();
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
TEST_CASE(
"Prepared Mapped Hdiv Mass Matches Stateless Kernel",
tags::gravity_prepared
) {
auto args = test_utils::setup_args();
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
operators::PreparedMappedHDivMassOperator prepared_operator(
f, *f.domainMapperStateless);
REQUIRE(prepared_operator.Width() ==
prepared_operator.GetFluxMap().reduced_size());
REQUIRE(prepared_operator.Height() ==
prepared_operator.GetFluxMap().reduced_size());
operators::PreparedMappedHDivMassOperator prepared_operator(f, *f.domainMapperStateless);
REQUIRE(prepared_operator.Width() == prepared_operator.GetFluxMap().reduced_size());
REQUIRE(prepared_operator.Height() == prepared_operator.GetFluxMap().reduced_size());
const mfem::Vector gravity_gradient_true =
prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(),
0.21);
const mfem::Vector gravity_gradient =
prepared_operator.GetFluxMap().gather(gravity_gradient_true);
const MPI_Comm communicator = f.gravityFluxFes->GetComm();
const mfem::Vector gravity_gradient_true =
prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(), 0.21);
const mfem::Vector gravity_gradient = prepared_operator.GetFluxMap().gather(gravity_gradient_true);
const MPI_Comm communicator = f.gravityFluxFes->GetComm();
mfem::Vector identity_action;
mfem::Vector deformed_action;
mfem::Vector identity_action;
mfem::Vector deformed_action;
for (const double deformation_scale : {0.0, 1.0}) {
const mfem::Vector displacement_true =
prepared_test::make_displacement(f, deformation_scale);
for (const double deformation_scale : {0.0, 1.0}) {
const mfem::Vector displacement_true = prepared_test::make_displacement(f, deformation_scale);
const mfem::Vector displacement = prepared_operator.GetDisplacementMap().gather(displacement_true);
prepared_operator.Prepare(displacement);
mfem::Vector prepared_action;
prepared_operator.Mult(gravity_gradient, prepared_action);
mfem::Vector reference_action_true;
operators::kernels::apply_mapped_hdiv_mass(
f, *f.domainMapperStateless, gravity_gradient_true, displacement_true, reference_action_true
);
const mfem::Vector reference_action = prepared_operator.GetFluxMap().gather(reference_action_true);
const double relative_error = prepared_test::relative_error(prepared_action, reference_action, communicator);
INFO("Deformation scale = " << deformation_scale);
INFO("Prepared action norm = " << prepared_test::global_norm(prepared_action, communicator));
INFO("Reference action norm = " << prepared_test::global_norm(reference_action, communicator));
INFO("Relative prepared-operator error = " << relative_error);
REQUIRE(prepared_operator.IsPrepared());
CHECK_THAT(relative_error, WithinAbs(0.0, 2.0e-11));
if (deformation_scale == 0.0) {
identity_action = prepared_action;
} else {
deformed_action = prepared_action;
}
}
const double geometry_change = prepared_test::relative_error(deformed_action, identity_action, communicator);
INFO("Relative action change under deformation = " << geometry_change);
CHECK(prepared_operator.GetPreparationCount() == 2);
CHECK(geometry_change > 1.0e-5);
}
TEST_CASE(
"Prepared Mapped Hdiv Mass Preserves Operator Identities",
tags::gravity_prepared
) {
auto args = test_utils::setup_args();
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
operators::PreparedMappedHDivMassOperator prepared_operator(f, *f.domainMapperStateless);
REQUIRE(prepared_operator.Width() == prepared_operator.GetFluxMap().reduced_size());
REQUIRE(prepared_operator.Height() == prepared_operator.GetFluxMap().reduced_size());
const mfem::Vector displacement =
prepared_operator.GetDisplacementMap().gather(displacement_true);
prepared_operator.GetDisplacementMap().gather(prepared_test::make_displacement(f, 1.0));
prepared_operator.Prepare(displacement);
mfem::Vector prepared_action;
const mfem::Vector first = prepared_operator.GetFluxMap().gather(
prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(), 0.17)
);
const mfem::Vector second = prepared_operator.GetFluxMap().gather(
prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(), 0.83)
);
const mfem::Vector combination = prepared_test::linear_combination(first, 1.7, second, -0.4);
prepared_operator.Mult(gravity_gradient, prepared_action);
mfem::Vector reference_action_true;
operators::kernels::apply_mapped_hdiv_mass(
f, *f.domainMapperStateless, gravity_gradient_true, displacement_true,
reference_action_true);
const mfem::Vector reference_action =
prepared_operator.GetFluxMap().gather(reference_action_true);
mfem::Vector first_action;
mfem::Vector second_action;
mfem::Vector combination_action;
mfem::Vector zero_action;
const double relative_error = prepared_test::relative_error(
prepared_action, reference_action, communicator);
prepared_operator.Mult(first, first_action);
prepared_operator.Mult(second, second_action);
prepared_operator.Mult(combination, combination_action);
INFO("Deformation scale = " << deformation_scale);
INFO("Prepared action norm = "
<< prepared_test::global_norm(prepared_action, communicator));
INFO("Reference action norm = "
<< prepared_test::global_norm(reference_action, communicator));
INFO("Relative prepared-operator error = " << relative_error);
mfem::Vector expected_combination = prepared_test::linear_combination(first_action, 1.7, second_action, -0.4);
REQUIRE(prepared_operator.IsPrepared());
CHECK_THAT(relative_error, WithinAbs(0.0, 2.0e-11));
mfem::Vector zero(first.Size());
zero = 0.0;
prepared_operator.Mult(zero, zero_action);
if (deformation_scale == 0.0) {
identity_action = prepared_action;
} else {
deformed_action = prepared_action;
const MPI_Comm communicator = f.gravityFluxFes->GetComm();
const double first_second_product = prepared_test::global_dot(first, second_action, communicator);
const double second_first_product = prepared_test::global_dot(second, first_action, communicator);
const double symmetry_error = prepared_test::relative_scalar_error(first_second_product, second_first_product);
const double linearity_error =
prepared_test::relative_error(combination_action, expected_combination, communicator);
const double first_energy = prepared_test::global_dot(first, first_action, communicator);
const double second_energy = prepared_test::global_dot(second, second_action, communicator);
const std::uint64_t preparation_count = prepared_operator.GetPreparationCount();
mfem::Vector repeated_action;
prepared_operator.Mult(first, repeated_action);
INFO("u^T M v = " << first_second_product);
INFO("v^T M u = " << second_first_product);
INFO("Relative symmetry error = " << symmetry_error);
INFO("Relative linearity error = " << linearity_error);
INFO("u^T M u = " << first_energy);
INFO("v^T M v = " << second_energy);
CHECK_THAT(symmetry_error, WithinAbs(0.0, 2.0e-12));
CHECK_THAT(linearity_error, WithinAbs(0.0, 2.0e-12));
CHECK_THAT(prepared_test::global_norm(zero_action, communicator), WithinAbs(0.0, 1.0e-14));
CHECK(first_energy > 0.0);
CHECK(second_energy > 0.0);
CHECK(prepared_test::relative_error(repeated_action, first_action, communicator) < 2.0e-14);
CHECK(prepared_operator.GetPreparationCount() == preparation_count);
}
TEST_CASE(
"Prepared Mapped Hdiv Mass Diagonal Is Positive Across Both Domains",
tags::gravity_prepared
) {
auto args = test_utils::setup_args();
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
operators::PreparedMappedHDivMassOperator prepared_operator(f, *f.domainMapperStateless);
const mfem::Vector displacement =
prepared_operator.GetDisplacementMap().gather(prepared_test::make_displacement(f, 1.0));
prepared_operator.Prepare(displacement);
mfem::Vector diagonal;
mfem::Vector true_diagonal;
prepared_operator.AssembleDiagonal(diagonal);
prepared_operator.AssembleTrueDiagonal(true_diagonal);
REQUIRE(diagonal.Size() == prepared_operator.Height());
REQUIRE(true_diagonal.Size() == prepared_operator.GetFluxMap().full_size());
const mfem::Vector gathered_true_diagonal = prepared_operator.GetFluxMap().gather(true_diagonal);
for (int i = 0; i < diagonal.Size(); ++i) {
REQUIRE(std::isfinite(diagonal(i)));
CHECK(diagonal(i) > 0.0);
CHECK_THAT(diagonal(i), WithinAbs(gathered_true_diagonal(i), 1.0e-14 * std::abs(diagonal(i))));
}
}
const double geometry_change = prepared_test::relative_error(
deformed_action, identity_action, communicator);
INFO("Relative action change under deformation = " << geometry_change);
CHECK(prepared_operator.GetPreparationCount() == 2);
CHECK(geometry_change > 1.0e-5);
}
TEST_CASE("Prepared Mapped Hdiv Mass Preserves Operator Identities",
tags::gravity_prepared) {
auto args = test_utils::setup_args();
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
operators::PreparedMappedHDivMassOperator prepared_operator(
f, *f.domainMapperStateless);
REQUIRE(prepared_operator.Width() ==
prepared_operator.GetFluxMap().reduced_size());
REQUIRE(prepared_operator.Height() ==
prepared_operator.GetFluxMap().reduced_size());
const mfem::Vector displacement =
prepared_operator.GetDisplacementMap().gather(
prepared_test::make_displacement(f, 1.0));
prepared_operator.Prepare(displacement);
const mfem::Vector first = prepared_operator.GetFluxMap().gather(
prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(),
0.17));
const mfem::Vector second = prepared_operator.GetFluxMap().gather(
prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(),
0.83));
const mfem::Vector combination =
prepared_test::linear_combination(first, 1.7, second, -0.4);
mfem::Vector first_action;
mfem::Vector second_action;
mfem::Vector combination_action;
mfem::Vector zero_action;
prepared_operator.Mult(first, first_action);
prepared_operator.Mult(second, second_action);
prepared_operator.Mult(combination, combination_action);
mfem::Vector expected_combination =
prepared_test::linear_combination(first_action, 1.7, second_action, -0.4);
mfem::Vector zero(first.Size());
zero = 0.0;
prepared_operator.Mult(zero, zero_action);
const MPI_Comm communicator = f.gravityFluxFes->GetComm();
const double first_second_product =
prepared_test::global_dot(first, second_action, communicator);
const double second_first_product =
prepared_test::global_dot(second, first_action, communicator);
const double symmetry_error = prepared_test::relative_scalar_error(
first_second_product, second_first_product);
const double linearity_error = prepared_test::relative_error(
combination_action, expected_combination, communicator);
const double first_energy =
prepared_test::global_dot(first, first_action, communicator);
const double second_energy =
prepared_test::global_dot(second, second_action, communicator);
const std::uint64_t preparation_count =
prepared_operator.GetPreparationCount();
mfem::Vector repeated_action;
prepared_operator.Mult(first, repeated_action);
INFO("u^T M v = " << first_second_product);
INFO("v^T M u = " << second_first_product);
INFO("Relative symmetry error = " << symmetry_error);
INFO("Relative linearity error = " << linearity_error);
INFO("u^T M u = " << first_energy);
INFO("v^T M v = " << second_energy);
CHECK_THAT(symmetry_error, WithinAbs(0.0, 2.0e-12));
CHECK_THAT(linearity_error, WithinAbs(0.0, 2.0e-12));
CHECK_THAT(prepared_test::global_norm(zero_action, communicator),
WithinAbs(0.0, 1.0e-14));
CHECK(first_energy > 0.0);
CHECK(second_energy > 0.0);
CHECK(prepared_test::relative_error(repeated_action, first_action,
communicator) < 2.0e-14);
CHECK(prepared_operator.GetPreparationCount() == preparation_count);
}
TEST_CASE("Prepared Mapped Hdiv Mass Diagonal Is Positive Across Both Domains",
tags::gravity_prepared) {
auto args = test_utils::setup_args();
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
operators::PreparedMappedHDivMassOperator prepared_operator(
f, *f.domainMapperStateless);
const mfem::Vector displacement =
prepared_operator.GetDisplacementMap().gather(
prepared_test::make_displacement(f, 1.0));
prepared_operator.Prepare(displacement);
mfem::Vector diagonal;
mfem::Vector true_diagonal;
prepared_operator.AssembleDiagonal(diagonal);
prepared_operator.AssembleTrueDiagonal(true_diagonal);
REQUIRE(diagonal.Size() == prepared_operator.Height());
REQUIRE(true_diagonal.Size() == prepared_operator.GetFluxMap().full_size());
const mfem::Vector gathered_true_diagonal =
prepared_operator.GetFluxMap().gather(true_diagonal);
for (int i = 0; i < diagonal.Size(); ++i) {
REQUIRE(std::isfinite(diagonal(i)));
CHECK(diagonal(i) > 0.0);
CHECK_THAT(diagonal(i), WithinAbs(gathered_true_diagonal(i),
1.0e-14 * std::abs(diagonal(i))));
}
}

View File

@@ -9,430 +9,423 @@ import mean_field;
import test_helpers;
namespace prepared_hydrostatic_analytic_solve_test_utils {
constexpr double bernoulliConstant = 0.83;
constexpr double enthalpyAmplitude = 0.61;
constexpr double bernoulliConstant = 0.83;
constexpr double enthalpyAmplitude = 0.61;
struct AnalyticCase {
const char *name;
struct AnalyticCase {
const char *name;
std::array<double, 3> deformationScale;
std::array<double, 3> angularVelocity;
std::array<double, 3> rotationCenter;
};
std::array<double, 3> deformationScale;
std::array<double, 3> angularVelocity;
std::array<double, 3> rotationCenter;
};
class EnthalpyJacobianOperator final : public mfem::Operator {
public:
EnthalpyJacobianOperator(
const int enthalpySize,
const mean_field::operators::PreparedHydrostaticEquilibriumOperator
&preparedOperator)
: mfem::Operator(enthalpySize), m_preparedOperator(preparedOperator) {}
class EnthalpyJacobianOperator final : public mfem::Operator {
public:
EnthalpyJacobianOperator(
const int enthalpySize,
const mean_field::operators::PreparedHydrostaticEquilibriumOperator &preparedOperator
)
: mfem::Operator(enthalpySize),
m_preparedOperator(preparedOperator) {
}
void Mult(const mfem::Vector &direction,
mfem::Vector &action) const override {
m_preparedOperator.ApplyEnthalpyJacobianAction(direction, action);
}
void Mult(
const mfem::Vector &direction,
mfem::Vector &action
) const override {
m_preparedOperator.ApplyEnthalpyJacobianAction(direction, action);
}
private:
const mean_field::operators::PreparedHydrostaticEquilibriumOperator
&m_preparedOperator;
};
private:
const mean_field::operators::PreparedHydrostaticEquilibriumOperator &m_preparedOperator;
};
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumDependencies
make_dependencies() {
return {.discretization = {.identity = 701, .revision = 2},
.enthalpy = {.identity = 709, .revision = 3},
.gravityPotential = {.identity = 719, .revision = 5},
.displacement = {.identity = 727, .revision = 7},
.rotation = {.identity = 733, .revision = 11},
.bernoulliConstant = {.identity = 739, .revision = 13}};
}
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumDependencies make_dependencies() {
return {
.discretization = {.identity = 701, .revision = 2},
.enthalpy = {.identity = 709, .revision = 3},
.gravityPotential = {.identity = 719, .revision = 5},
.displacement = {.identity = 727, .revision = 7},
.rotation = {.identity = 733, .revision = 11},
.bernoulliConstant = {.identity = 739, .revision = 13}
};
}
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView
make_state(const mfem::Vector &enthalpy, const mfem::Vector &gravityPotential,
const mfem::Vector &displacement) {
return {.enthalpy = enthalpy,
.gravityPotential = gravityPotential,
.displacement = displacement,
.bernoulliConstant = bernoulliConstant};
}
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView make_state(
const mfem::Vector &enthalpy,
const mfem::Vector &gravityPotential,
const mfem::Vector &displacement
) {
return {
.enthalpy = enthalpy,
.gravityPotential = gravityPotential,
.displacement = displacement,
.bernoulliConstant = bernoulliConstant
};
}
mfem::Vector make_vector(const std::array<double, 3> &values) {
mfem::Vector vector(3);
mfem::Vector make_vector(
const std::array<
double,
3> &values
) {
mfem::Vector vector(3);
for (int component = 0; component < 3; ++component) {
vector(component) = values[static_cast<std::size_t>(component)];
}
for (int component = 0; component < 3; ++component) {
vector(component) = values[static_cast<std::size_t>(component)];
}
return vector;
}
return vector;
}
mean_field::physics::RigidRotation
make_rotation(const AnalyticCase &analyticCase) {
return mean_field::physics::RigidRotation(
make_vector(analyticCase.angularVelocity),
make_vector(analyticCase.rotationCenter));
}
mean_field::physics::RigidRotation make_rotation(const AnalyticCase &analyticCase) {
return mean_field::physics::RigidRotation(
make_vector(analyticCase.angularVelocity), make_vector(analyticCase.rotationCenter)
);
}
void map_to_physical(const mfem::Vector &referencePosition,
const AnalyticCase &analyticCase,
mfem::Vector &physicalPosition) {
physicalPosition.SetSize(3);
void map_to_physical(
const mfem::Vector &referencePosition,
const AnalyticCase &analyticCase,
mfem::Vector &physicalPosition
) {
physicalPosition.SetSize(3);
for (int component = 0; component < 3; ++component) {
physicalPosition(component) =
analyticCase.deformationScale[static_cast<std::size_t>(component)] *
referencePosition(component);
}
}
for (int component = 0; component < 3; ++component) {
physicalPosition(component) =
analyticCase.deformationScale[static_cast<std::size_t>(component)] * referencePosition(component);
}
}
double exact_enthalpy_value(const mfem::Vector &referencePosition) {
double normalizedRadiusSquared = 0.0;
double exact_enthalpy_value(const mfem::Vector &referencePosition) {
double normalizedRadiusSquared = 0.0;
for (int component = 0; component < 3; ++component) {
const double normalizedCoordinate =
referencePosition(component) / mean_field::utils::RADIUS;
for (int component = 0; component < 3; ++component) {
const double normalizedCoordinate = referencePosition(component) / mean_field::utils::RADIUS;
normalizedRadiusSquared += normalizedCoordinate * normalizedCoordinate;
}
normalizedRadiusSquared += normalizedCoordinate * normalizedCoordinate;
}
return enthalpyAmplitude * std::max(0.0, 1.0 - normalizedRadiusSquared);
}
return enthalpyAmplitude * std::max(0.0, 1.0 - normalizedRadiusSquared);
}
double
exact_potential_value(const mfem::Vector &referencePosition,
const AnalyticCase &analyticCase,
const mean_field::physics::RigidRotation &rotation) {
mfem::Vector physicalPosition;
double exact_potential_value(
const mfem::Vector &referencePosition,
const AnalyticCase &analyticCase,
const mean_field::physics::RigidRotation &rotation
) {
mfem::Vector physicalPosition;
map_to_physical(referencePosition, analyticCase, physicalPosition);
map_to_physical(referencePosition, analyticCase, physicalPosition);
/*
* Construct Phi so that
*
* h + Phi - Psi_rotation - C = 0
*
* analytically.
*/
return bernoulliConstant + rotation.potential(physicalPosition) -
exact_enthalpy_value(referencePosition);
}
/*
* Construct Phi so that
*
* h + Phi - Psi_rotation - C = 0
*
* analytically.
*/
return bernoulliConstant + rotation.potential(physicalPosition) - exact_enthalpy_value(referencePosition);
}
mfem::Array<int> make_stellar_element_marker(const mean_field::fem::FEM &f) {
mfem::Array<int> stellarElementMarker(f.mesh->GetNE());
mfem::Array<int> make_stellar_element_marker(const mean_field::fem::FEM &f) {
mfem::Array<int> stellarElementMarker(f.mesh->GetNE());
const int vacuumAttribute = field_dof_test_utils::vacuum_material_attribute;
const int vacuumAttribute = field_dof_test_utils::vacuum_material_attribute;
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
stellarElementMarker[elementId] =
f.mesh->GetAttribute(elementId) != vacuumAttribute;
}
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
stellarElementMarker[elementId] = f.mesh->GetAttribute(elementId) != vacuumAttribute;
}
return stellarElementMarker;
}
return stellarElementMarker;
}
} // namespace prepared_hydrostatic_analytic_solve_test_utils
TEST_CASE("Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria",
tags::barotrope_hydrostatic_prepared_analytic &tags::convergence
&tags::accuracy) {
using prepared_hydrostatic_analytic_solve_test_utils::AnalyticCase;
constexpr double deformationX = 1.08;
constexpr double deformationY = 0.96;
/*
* The third scale makes the affine deformation
* volume-preserving:
*
* det(F) = sx * sy * sz = 1.
*/
constexpr double deformationZ = 1.0 / (deformationX * deformationY);
const std::array<AnalyticCase, 3> analyticCases{
{{.name = "spherical nonrotating equilibrium",
.deformationScale = {1.0, 1.0, 1.0},
.angularVelocity = {0.0, 0.0, 0.0},
.rotationCenter = {0.0, 0.0, 0.0}},
{.name = "spherical rotating equilibrium",
.deformationScale = {1.0, 1.0, 1.0},
.angularVelocity = {0.13, -0.09, 0.31},
.rotationCenter = {0.04, -0.03, 0.02}},
{.name = "volume-preserving deformed rotating equilibrium",
.deformationScale = {deformationX, deformationY, deformationZ},
.angularVelocity = {0.17, -0.12, 0.43},
.rotationCenter = {0.031, -0.024, 0.018}}}};
TEST_CASE(
"Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria",
tags::barotrope_hydrostatic_prepared_analytic &tags::convergence &tags::accuracy
) {
using prepared_hydrostatic_analytic_solve_test_utils::AnalyticCase;
auto args = test_utils::setup_args();
constexpr double deformationX = 1.08;
constexpr double deformationY = 0.96;
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
/*
* The third scale makes the affine deformation
* volume-preserving:
*
* det(F) = sx * sy * sz = 1.
*/
constexpr double deformationZ = 1.0 / (deformationX * deformationY);
const MPI_Comm communicator = f.mesh->GetComm();
const std::array<AnalyticCase, 3> analyticCases{
{{.name = "spherical nonrotating equilibrium",
.deformationScale = {1.0, 1.0, 1.0},
.angularVelocity = {0.0, 0.0, 0.0},
.rotationCenter = {0.0, 0.0, 0.0}},
{.name = "spherical rotating equilibrium",
.deformationScale = {1.0, 1.0, 1.0},
.angularVelocity = {0.13, -0.09, 0.31},
.rotationCenter = {0.04, -0.03, 0.02}},
{.name = "volume-preserving deformed rotating equilibrium",
.deformationScale = {deformationX, deformationY, deformationZ},
.angularVelocity = {0.17, -0.12, 0.43},
.rotationCenter = {0.031, -0.024, 0.018}}}
};
const mean_field::field::FieldDofMap enthalpyMap =
field_dof_test_utils::make_map<mean_field::field::Enthalpy>(
*f.enthalpyFes);
auto args = test_utils::setup_args();
const mean_field::field::FieldDofMap gravityPotentialMap =
field_dof_test_utils::make_map<mean_field::field::Gravity>(
*f.gravityPotentialFes);
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
const mean_field::field::FieldDofMap displacementMap =
field_dof_test_utils::make_map<mean_field::field::Displacement>(
*f.displacementFes);
const MPI_Comm communicator = f.mesh->GetComm();
const mfem::Array<int> stellarElementMarker =
prepared_hydrostatic_analytic_solve_test_utils::
make_stellar_element_marker(f);
const mean_field::field::FieldDofMap enthalpyMap =
field_dof_test_utils::make_map<mean_field::field::Enthalpy>(*f.enthalpyFes);
for (const AnalyticCase &analyticCase : analyticCases) {
DYNAMIC_SECTION(analyticCase.name) {
const double deformationDeterminant = analyticCase.deformationScale[0] *
analyticCase.deformationScale[1] *
analyticCase.deformationScale[2];
const mean_field::field::FieldDofMap gravityPotentialMap =
field_dof_test_utils::make_map<mean_field::field::Gravity>(*f.gravityPotentialFes);
REQUIRE(std::abs(deformationDeterminant - 1.0) < 2.0e-14);
const mean_field::field::FieldDofMap displacementMap =
field_dof_test_utils::make_map<mean_field::field::Displacement>(*f.displacementFes);
const mean_field::physics::RigidRotation rotation =
prepared_hydrostatic_analytic_solve_test_utils::make_rotation(
analyticCase);
const mfem::Array<int> stellarElementMarker =
prepared_hydrostatic_analytic_solve_test_utils::make_stellar_element_marker(f);
auto displacementFunction =
[&analyticCase](const mfem::Vector &referencePosition,
mfem::Vector &displacementValue) {
mfem::Vector physicalPosition;
for (const AnalyticCase &analyticCase : analyticCases) {
DYNAMIC_SECTION(analyticCase.name) {
const double deformationDeterminant =
analyticCase.deformationScale[0] * analyticCase.deformationScale[1] * analyticCase.deformationScale[2];
prepared_hydrostatic_analytic_solve_test_utils::map_to_physical(
referencePosition, analyticCase, physicalPosition);
REQUIRE(std::abs(deformationDeterminant - 1.0) < 2.0e-14);
displacementValue.SetSize(3);
displacementValue = physicalPosition;
displacementValue -= referencePosition;
};
const mean_field::physics::RigidRotation rotation =
prepared_hydrostatic_analytic_solve_test_utils::make_rotation(analyticCase);
auto potentialFunction = [&analyticCase, &rotation](
const mfem::Vector &referencePosition) {
return prepared_hydrostatic_analytic_solve_test_utils::
exact_potential_value(referencePosition, analyticCase, rotation);
};
auto displacementFunction =
[&analyticCase](const mfem::Vector &referencePosition, mfem::Vector &displacementValue) {
mfem::Vector physicalPosition;
auto enthalpyFunction = [](const mfem::Vector &referencePosition) {
return prepared_hydrostatic_analytic_solve_test_utils::
exact_enthalpy_value(referencePosition);
};
prepared_hydrostatic_analytic_solve_test_utils::map_to_physical(
referencePosition, analyticCase, physicalPosition
);
mfem::VectorFunctionCoefficient displacementCoefficient(
f.mesh->Dimension(), displacementFunction);
displacementValue.SetSize(3);
displacementValue = physicalPosition;
displacementValue -= referencePosition;
};
mfem::FunctionCoefficient potentialCoefficient(potentialFunction);
auto potentialFunction = [&analyticCase, &rotation](const mfem::Vector &referencePosition) {
return prepared_hydrostatic_analytic_solve_test_utils::exact_potential_value(
referencePosition, analyticCase, rotation
);
};
mfem::FunctionCoefficient exactEnthalpyCoefficient(enthalpyFunction);
auto enthalpyFunction = [](const mfem::Vector &referencePosition) {
return prepared_hydrostatic_analytic_solve_test_utils::exact_enthalpy_value(referencePosition);
};
/*
* Project the prescribed geometry and potential.
*/
mfem::ParGridFunction displacementField(f.displacementFes.get());
mfem::VectorFunctionCoefficient displacementCoefficient(f.mesh->Dimension(), displacementFunction);
mfem::ParGridFunction potentialField(f.gravityPotentialFes.get());
mfem::FunctionCoefficient potentialCoefficient(potentialFunction);
displacementField.ProjectCoefficient(displacementCoefficient);
mfem::FunctionCoefficient exactEnthalpyCoefficient(enthalpyFunction);
potentialField.ProjectCoefficient(potentialCoefficient);
/*
* Project the prescribed geometry and potential.
*/
mfem::ParGridFunction displacementField(f.displacementFes.get());
mfem::Vector displacementTrue;
mfem::Vector gravityPotentialTrue;
mfem::ParGridFunction potentialField(f.gravityPotentialFes.get());
displacementField.GetTrueDofs(displacementTrue);
potentialField.GetTrueDofs(gravityPotentialTrue);
displacementField.ProjectCoefficient(displacementCoefficient);
const mfem::Vector displacement =
displacementMap.gather(displacementTrue);
const mfem::Vector gravityPotential =
gravityPotentialMap.gather(gravityPotentialTrue);
potentialField.ProjectCoefficient(potentialCoefficient);
/*
* This projection is not used as the solution. It gives
* the best directly available representation baseline
* against which the solved field can be compared.
*/
mfem::ParGridFunction projectedEnthalpyField(f.enthalpyFes.get());
mfem::Vector displacementTrue;
mfem::Vector gravityPotentialTrue;
projectedEnthalpyField.ProjectCoefficient(exactEnthalpyCoefficient);
displacementField.GetTrueDofs(displacementTrue);
potentialField.GetTrueDofs(gravityPotentialTrue);
mfem::ParGridFunction zeroEnthalpyField(f.enthalpyFes.get());
const mfem::Vector displacement = displacementMap.gather(displacementTrue);
const mfem::Vector gravityPotential = gravityPotentialMap.gather(gravityPotentialTrue);
zeroEnthalpyField = 0.0;
/*
* This projection is not used as the solution. It gives
* the best directly available representation baseline
* against which the solved field can be compared.
*/
mfem::ParGridFunction projectedEnthalpyField(f.enthalpyFes.get());
const double exactEnthalpyNorm = zeroEnthalpyField.ComputeL2Error(
exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
projectedEnthalpyField.ProjectCoefficient(exactEnthalpyCoefficient);
const double projectionError = projectedEnthalpyField.ComputeL2Error(
exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
mfem::ParGridFunction zeroEnthalpyField(f.enthalpyFes.get());
REQUIRE(exactEnthalpyNorm > 0.0);
zeroEnthalpyField = 0.0;
const double relativeProjectionError =
projectionError / exactEnthalpyNorm;
const double exactEnthalpyNorm =
zeroEnthalpyField.ComputeL2Error(exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
/*
* Begin deliberately far from equilibrium.
*/
mfem::Vector enthalpy(enthalpyMap.reduced_size());
const double projectionError =
projectedEnthalpyField.ComputeL2Error(exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
enthalpy = 0.0;
REQUIRE(exactEnthalpyNorm > 0.0);
auto dependencies =
prepared_hydrostatic_analytic_solve_test_utils::make_dependencies();
const double relativeProjectionError = projectionError / exactEnthalpyNorm;
mean_field::operators::PreparedHydrostaticEquilibriumOperator
preparedOperator(f, *f.domainMapperStateless);
/*
* Begin deliberately far from equilibrium.
*/
mfem::Vector enthalpy(enthalpyMap.reduced_size());
const auto initialReport = preparedOperator.Prepare(
prepared_hydrostatic_analytic_solve_test_utils::make_state(
enthalpy, gravityPotential, displacement),
dependencies, rotation);
enthalpy = 0.0;
REQUIRE(initialReport.preparedResidual);
REQUIRE(initialReport.preparedAlgebraicJacobianBlocks);
auto dependencies = prepared_hydrostatic_analytic_solve_test_utils::make_dependencies();
mfem::Vector initialResidual;
mean_field::operators::PreparedHydrostaticEquilibriumOperator preparedOperator(f, *f.domainMapperStateless);
preparedOperator.BuildResidual(initialResidual);
const auto initialReport = preparedOperator.Prepare(
prepared_hydrostatic_analytic_solve_test_utils::make_state(enthalpy, gravityPotential, displacement),
dependencies, rotation
);
const double initialResidualNorm =
gravity_prepared_test_utils::global_norm(initialResidual,
communicator);
REQUIRE(initialReport.preparedResidual);
REQUIRE(initialReport.preparedAlgebraicJacobianBlocks);
REQUIRE(initialResidualNorm > 1.0e-12);
mfem::Vector initialResidual;
/*
* One discrete Newton step:
*
* M_h delta_h = -R_h.
*
* The full four-block Bernoulli Jacobian is rectangular
* and underdetermined in isolation. Freezing Phi, C,
* rotation, and displacement makes this a well-defined
* enthalpy solve.
*/
prepared_hydrostatic_analytic_solve_test_utils::EnthalpyJacobianOperator
enthalpyJacobian(enthalpyMap.reduced_size(), preparedOperator);
preparedOperator.BuildResidual(initialResidual);
mfem::Vector rightHandSide(initialResidual);
rightHandSide *= -1.0;
const double initialResidualNorm = gravity_prepared_test_utils::global_norm(initialResidual, communicator);
mfem::Vector enthalpyCorrection(enthalpyMap.reduced_size());
REQUIRE(initialResidualNorm > 1.0e-12);
enthalpyCorrection = 0.0;
/*
* One discrete Newton step:
*
* M_h delta_h = -R_h.
*
* The full four-block Bernoulli Jacobian is rectangular
* and underdetermined in isolation. Freezing Phi, C,
* rotation, and displacement makes this a well-defined
* enthalpy solve.
*/
prepared_hydrostatic_analytic_solve_test_utils::EnthalpyJacobianOperator enthalpyJacobian(
enthalpyMap.reduced_size(), preparedOperator
);
/*
* The reduced operator contains only stellar-supported
* enthalpy DOFs and is positive definite. MINRES remains
* appropriate for this symmetric system.
*/
mfem::MINRESSolver linearSolver(communicator);
mfem::Vector rightHandSide(initialResidual);
rightHandSide *= -1.0;
linearSolver.SetOperator(enthalpyJacobian);
mfem::Vector enthalpyCorrection(enthalpyMap.reduced_size());
linearSolver.SetRelTol(1.0e-13);
linearSolver.SetAbsTol(1.0e-14);
linearSolver.SetMaxIter(2000);
linearSolver.SetPrintLevel(0);
enthalpyCorrection = 0.0;
linearSolver.Mult(rightHandSide, enthalpyCorrection);
/*
* The reduced operator contains only stellar-supported
* enthalpy DOFs and is positive definite. MINRES remains
* appropriate for this symmetric system.
*/
mfem::MINRESSolver linearSolver(communicator);
INFO("Linear solver converged = " << linearSolver.GetConverged());
linearSolver.SetOperator(enthalpyJacobian);
INFO("Linear solver iterations = " << linearSolver.GetNumIterations());
linearSolver.SetRelTol(1.0e-13);
linearSolver.SetAbsTol(1.0e-14);
linearSolver.SetMaxIter(2000);
linearSolver.SetPrintLevel(0);
INFO("Linear solver final norm = " << linearSolver.GetFinalNorm());
linearSolver.Mult(rightHandSide, enthalpyCorrection);
REQUIRE(linearSolver.GetConverged());
INFO("Linear solver converged = " << linearSolver.GetConverged());
enthalpy += enthalpyCorrection;
INFO("Linear solver iterations = " << linearSolver.GetNumIterations());
/*
* Only the enthalpy state changed. Geometry, rotation,
* and algebraic Jacobian data must remain reusable.
*/
++dependencies.enthalpy.revision;
INFO("Linear solver final norm = " << linearSolver.GetFinalNorm());
const auto solvedReport = preparedOperator.Prepare(
prepared_hydrostatic_analytic_solve_test_utils::make_state(
enthalpy, gravityPotential, displacement),
dependencies, rotation);
REQUIRE(linearSolver.GetConverged());
CHECK(solvedReport.contextReport.updatedEnthalpy);
enthalpy += enthalpyCorrection;
CHECK(solvedReport.contextReport.preparedBaseState);
/*
* Only the enthalpy state changed. Geometry, rotation,
* and algebraic Jacobian data must remain reusable.
*/
++dependencies.enthalpy.revision;
CHECK_FALSE(solvedReport.contextReport.preparedGeometryState);
const auto solvedReport = preparedOperator.Prepare(
prepared_hydrostatic_analytic_solve_test_utils::make_state(enthalpy, gravityPotential, displacement),
dependencies, rotation
);
CHECK_FALSE(solvedReport.preparedAlgebraicJacobianBlocks);
CHECK(solvedReport.contextReport.updatedEnthalpy);
mfem::Vector solvedResidual;
CHECK(solvedReport.contextReport.preparedBaseState);
preparedOperator.BuildResidual(solvedResidual);
CHECK_FALSE(solvedReport.contextReport.preparedGeometryState);
const double solvedResidualNorm =
gravity_prepared_test_utils::global_norm(solvedResidual,
communicator);
CHECK_FALSE(solvedReport.preparedAlgebraicJacobianBlocks);
const double residualReduction = solvedResidualNorm / initialResidualNorm;
mfem::Vector solvedResidual;
/*
* Compare the solved field with the continuum analytic
* enthalpy over stellar elements only.
*
* All three mappings have determinant one, so this
* normalized L2 error is also unchanged by the physical
* volume transformation.
*/
mfem::ParGridFunction solvedEnthalpyField(f.enthalpyFes.get());
preparedOperator.BuildResidual(solvedResidual);
mfem::Vector enthalpyTrue(enthalpyMap.full_size());
enthalpyMap.scatter(enthalpy, enthalpyTrue);
solvedEnthalpyField.SetFromTrueDofs(enthalpyTrue);
const double solvedResidualNorm = gravity_prepared_test_utils::global_norm(solvedResidual, communicator);
const double solvedAnalyticError = solvedEnthalpyField.ComputeL2Error(
exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
const double residualReduction = solvedResidualNorm / initialResidualNorm;
const double relativeSolvedAnalyticError =
solvedAnalyticError / exactEnthalpyNorm;
/*
* Compare the solved field with the continuum analytic
* enthalpy over stellar elements only.
*
* All three mappings have determinant one, so this
* normalized L2 error is also unchanged by the physical
* volume transformation.
*/
mfem::ParGridFunction solvedEnthalpyField(f.enthalpyFes.get());
INFO("Deformation determinant = " << deformationDeterminant);
mfem::Vector enthalpyTrue(enthalpyMap.full_size());
enthalpyMap.scatter(enthalpy, enthalpyTrue);
solvedEnthalpyField.SetFromTrueDofs(enthalpyTrue);
INFO("Initial weak residual norm = " << initialResidualNorm);
const double solvedAnalyticError =
solvedEnthalpyField.ComputeL2Error(exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
INFO("Solved weak residual norm = " << solvedResidualNorm);
const double relativeSolvedAnalyticError = solvedAnalyticError / exactEnthalpyNorm;
INFO("Weak residual reduction = " << residualReduction);
INFO("Deformation determinant = " << deformationDeterminant);
INFO("Relative analytic projection floor = " << relativeProjectionError);
INFO("Initial weak residual norm = " << initialResidualNorm);
INFO("Relative solved analytic L2 error = "
<< relativeSolvedAnalyticError);
INFO("Solved weak residual norm = " << solvedResidualNorm);
/*
* The discrete Bernoulli equation must be solved essentially
* to the linear-solver floor.
*/
CHECK(residualReduction < 1.0e-10);
INFO("Weak residual reduction = " << residualReduction);
/*
* The directly projected analytic enthalpy provides a lower
* representation bound, but it is not the expected solution
* of the cross-space discrete Bernoulli equation. The latter
* also contains potential-projection and mapped-space
* compatibility errors.
*/
CHECK(relativeSolvedAnalyticError <
std::max(5.0 * relativeProjectionError, 1.25e-4));
INFO("Relative analytic projection floor = " << relativeProjectionError);
/*
* Record that the analytic error remains within one order of
* magnitude of the direct enthalpy projection floor.
*/
CHECK(relativeSolvedAnalyticError / relativeProjectionError < 5.0);
INFO("Relative solved analytic L2 error = " << relativeSolvedAnalyticError);
/*
* The discrete Bernoulli equation must be solved essentially
* to the linear-solver floor.
*/
CHECK(residualReduction < 1.0e-10);
/*
* The directly projected analytic enthalpy provides a lower
* representation bound, but it is not the expected solution
* of the cross-space discrete Bernoulli equation. The latter
* also contains potential-projection and mapped-space
* compatibility errors.
*/
CHECK(relativeSolvedAnalyticError < std::max(5.0 * relativeProjectionError, 1.25e-4));
/*
* Record that the analytic error remains within one order of
* magnitude of the direct enthalpy projection floor.
*/
CHECK(relativeSolvedAnalyticError / relativeProjectionError < 5.0);
}
}
}
}

View File

@@ -138,9 +138,7 @@ TEST_CASE(
mean_field::operators::PreparedHydrostaticEquilibriumOperator preparedOperator(f, *f.domainMapperStateless);
const mfem::Vector enthalpy =
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Enthalpy>(
*f.enthalpyFes, 0.34
);
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Enthalpy>(*f.enthalpyFes, 0.34);
const mfem::Vector gravityPotential =
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Gravity>(
@@ -161,9 +159,7 @@ TEST_CASE(
);
const mfem::Vector enthalpyVariation =
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Enthalpy>(
*f.enthalpyFes, 1.07
);
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Enthalpy>(*f.enthalpyFes, 1.07);
const mfem::Vector gravityPotentialVariation =
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Gravity>(
@@ -236,9 +232,7 @@ TEST_CASE(
mean_field::operators::PreparedHydrostaticEquilibriumOperator preparedOperator(f, *f.domainMapperStateless);
const mfem::Vector enthalpy =
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Enthalpy>(
*f.enthalpyFes, 0.41
);
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Enthalpy>(*f.enthalpyFes, 0.41);
const mfem::Vector gravityPotential =
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Gravity>(
@@ -277,9 +271,7 @@ TEST_CASE(
CHECK(preparedOperator.GetEnthalpyMap().inactive_size() > 0);
const mfem::Vector enthalpyVariation =
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Enthalpy>(
*f.enthalpyFes, 1.12
);
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Enthalpy>(*f.enthalpyFes, 1.12);
const mfem::Vector gravityPotentialVariation =
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Gravity>(

View File

@@ -26,16 +26,13 @@ namespace mass_normalization_test_utils {
);
[[nodiscard]] mean_field::operators::MassNormalizationLayout make_layout(const mean_field::fem::FEM &f) {
const auto densityMap =
field_dof_test_utils::make_map<mean_field::field::Density>(*f.densityFes);
const auto densityMap = field_dof_test_utils::make_map<mean_field::field::Density>(*f.densityFes);
const auto displacementMap =
field_dof_test_utils::make_map<mean_field::field::Displacement>(*f.displacementFes);
const auto gravityFluxMap =
field_dof_test_utils::make_map<mean_field::field::Gravity>(*f.gravityFluxFes);
const auto gravityFluxMap = field_dof_test_utils::make_map<mean_field::field::Gravity>(*f.gravityFluxFes);
const auto gravityPotentialMap =
field_dof_test_utils::make_map<mean_field::field::Gravity>(*f.gravityPotentialFes);
const auto enthalpyMap =
field_dof_test_utils::make_map<mean_field::field::Enthalpy>(*f.enthalpyFes);
const auto enthalpyMap = field_dof_test_utils::make_map<mean_field::field::Enthalpy>(*f.enthalpyFes);
const std::array<int, CoupledForm::value_block_count> valueSizes{
densityMap.reduced_size(), displacementMap.reduced_size(), gravityFluxMap.reduced_size(),
@@ -43,7 +40,7 @@ namespace mass_normalization_test_utils {
};
const std::array<int, CoupledForm::residual_block_count> residualSizes{
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(), densityMap.reduced_size(),
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(), densityMap.reduced_size(),
displacementMap.reduced_size(), enthalpyMap.reduced_size(), 1
};
@@ -345,8 +342,7 @@ TEST_CASE(
mean_field::operators::PreparedMassNormalizationOperator massOperator(f, *f.domainMapperStateless, gravityContext);
massOperator.Prepare({.targetMass = 1.11}, dependencies);
const mfem::Vector reducedDisplacementDirection =
gravityContext.GetDisplacementMap().gather(displacementDirection);
const mfem::Vector reducedDisplacementDirection = gravityContext.GetDisplacementMap().gather(displacementDirection);
mfem::Vector analyticAction;
massOperator.ApplyDisplacementJacobianAction(reducedDisplacementDirection, analyticAction);
@@ -476,9 +472,8 @@ TEST_CASE(
mean_field::operators::PreparedMassNormalizationOperator massOperator(f, *f.domainMapperStateless, gravityContext);
massOperator.Prepare({.targetMass = 1.19}, 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 displacementAction;
@@ -486,9 +481,7 @@ TEST_CASE(
massOperator.ApplyDensityJacobianAction(reducedDensityDirection, densityAction);
massOperator.ApplyDisplacementJacobianAction(reducedDisplacementDirection, displacementAction);
massOperator.ApplyCompleteJacobianAction(
reducedDensityDirection, reducedDisplacementDirection, completeAction
);
massOperator.ApplyCompleteJacobianAction(reducedDensityDirection, reducedDisplacementDirection, completeAction);
CHECK(
mass_normalization_test_utils::relative_error(completeAction(0), densityAction(0) + displacementAction(0)) <
@@ -498,10 +491,7 @@ TEST_CASE(
const auto layout = mass_normalization_test_utils::make_layout(f);
mean_field::operators::PreparedMassNormalizationJacobianOperator adapter(layout, massOperator);
CHECK(
layout.size(mass_normalization_test_utils::densityValue) ==
gravityContext.GetDensityMap().reduced_size()
);
CHECK(layout.size(mass_normalization_test_utils::densityValue) == gravityContext.GetDensityMap().reduced_size());
CHECK(
layout.size(mass_normalization_test_utils::displacementValue) ==
gravityContext.GetDisplacementMap().reduced_size()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff