feat(field-support): added field support system, mid migration

currently the barotope and the pressure force operator are migrated to the new support system
This commit is contained in:
2026-08-23 10:13:53 -04:00
parent dc912fd15e
commit 0f3ca8050b
137 changed files with 29975 additions and 16389 deletions

View File

@@ -44,23 +44,17 @@ namespace {
}
mfem::Vector make_base_density(const mean_field::fem::FEM &f) {
mfem::FunctionCoefficient coefficient(
[](const mfem::Vector &position) {
return 0.55 + 0.025 * position(0) - 0.010 * position(1) +
0.006 * position(2);
}
);
mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) {
return 0.55 + 0.025 * position(0) - 0.010 * position(1) + 0.006 * position(2);
});
return project_scalar_field(*f.densityFes, coefficient);
}
mfem::Vector make_base_enthalpy(const mean_field::fem::FEM &f) {
mfem::FunctionCoefficient coefficient(
[](const mfem::Vector &position) {
return 0.90 + 0.020 * position(0) - 0.010 * position(1) +
0.005 * position(2);
}
);
mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) {
return 0.90 + 0.020 * position(0) - 0.010 * position(1) + 0.005 * position(2);
});
return project_scalar_field(*f.enthalpyFes, coefficient);
}
@@ -69,23 +63,20 @@ namespace {
TEST_CASE(
"Barotropic Closure Vanishes For A Representable Constant State",
tags::hydro &tags::residuals &tags::unit &tags::closure &tags::kernels
&tags::barotrope
tags::hydro &tags::residuals &tags::unit &tags::closure &tags::kernels &tags::barotrope
) {
auto args = test_utils::setup_args();
mean_field::fem::FEM f =
mean_field::fem::setup_fem(args.mesh_file, args, 0);
auto args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
constexpr double enthalpyValue = 0.8;
constexpr double enthalpyValue = 0.8;
const double densityValue = barotrope.density_from_enthalpy(enthalpyValue);
const double densityValue = barotrope.density_from_enthalpy(enthalpyValue);
const mfem::Vector enthalpy =
project_constant(*f.enthalpyFes, enthalpyValue);
const mfem::Vector enthalpy = project_constant(*f.enthalpyFes, enthalpyValue);
const mfem::Vector density = project_constant(*f.densityFes, densityValue);
const mfem::Vector density = project_constant(*f.densityFes, densityValue);
const mfem::Vector displacement = make_zero_displacement(f);
@@ -93,19 +84,17 @@ TEST_CASE(
mfem::Vector scale;
mean_field::operators::kernels::apply_barotropic_closure(
f, *f.domainMapperStateless, barotrope, density, enthalpy, displacement,
residual
f, *f.domainMapperStateless, barotrope, density, enthalpy, displacement, residual
);
mean_field::operators::kernels::apply_barotropic_closure_density_action(
f, *f.domainMapperStateless, barotrope, density, displacement, scale
);
const MPI_Comm communicator = f.mesh->GetComm();
const MPI_Comm communicator = f.mesh->GetComm();
const double relativeResidual =
gravity_prepared_test_utils::global_norm(residual, communicator) /
gravity_prepared_test_utils::global_norm(scale, communicator);
const double relativeResidual = gravity_prepared_test_utils::global_norm(residual, communicator) /
gravity_prepared_test_utils::global_norm(scale, communicator);
INFO("Relative constant-state closure residual = " << relativeResidual);
@@ -114,40 +103,34 @@ TEST_CASE(
TEST_CASE(
"Barotropic Closure Density Action Matches The Stellar Mass Matrix",
tags::hydro &tags::jacobian &tags::unit &tags::closure &tags::kernels
&tags::barotrope
tags::hydro &tags::jacobian &tags::unit &tags::closure &tags::kernels &tags::barotrope
) {
auto args = test_utils::setup_args();
mean_field::fem::FEM f =
mean_field::fem::setup_fem(args.mesh_file, args, 0);
auto args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
const mfem::Vector displacement = make_zero_displacement(f);
const mfem::Vector densityVariation =
gravity_prepared_test_utils::make_deterministic_vector(
f.densityFes->GetTrueVSize(), 0.37
);
gravity_prepared_test_utils::make_deterministic_vector(f.densityFes->GetTrueVSize(), 0.37);
mfem::Vector kernelAction;
mean_field::operators::kernels::apply_barotropic_closure_density_action(
f, *f.domainMapperStateless, barotrope, densityVariation, displacement,
kernelAction
f, *f.domainMapperStateless, barotrope, densityVariation, displacement, kernelAction
);
using Schema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
using Stellar = mean_field::utils::domain::Stellar;
mfem::Array<int> stellarMarker(f.mesh->attributes.Max());
stellarMarker = 0;
const int vacuumAttribute =
f.domainMapperStateless->GetVacuumElementAttribute();
for (int attributeIndex = 0; attributeIndex < f.mesh->attributes.Size();
++attributeIndex) {
for (int attributeIndex = 0; attributeIndex < f.mesh->attributes.Size(); ++attributeIndex) {
const int attribute = f.mesh->attributes[attributeIndex];
if (attribute != vacuumAttribute) {
if (Schema::template attribute_belongs_to<Stellar>(attribute)) {
stellarMarker[attribute - 1] = 1;
}
}
@@ -159,9 +142,7 @@ TEST_CASE(
massForm.Assemble();
massForm.Finalize();
std::unique_ptr<mfem::HypreParMatrix> massMatrix(
massForm.ParallelAssemble()
);
std::unique_ptr<mfem::HypreParMatrix> massMatrix(massForm.ParallelAssemble());
REQUIRE(massMatrix != nullptr);
REQUIRE(massMatrix->Width() == densityVariation.Size());
@@ -170,9 +151,8 @@ TEST_CASE(
referenceAction = 0.0;
massMatrix->Mult(densityVariation, referenceAction);
const double relativeError = gravity_prepared_test_utils::relative_error(
kernelAction, referenceAction, f.mesh->GetComm()
);
const double relativeError =
gravity_prepared_test_utils::relative_error(kernelAction, referenceAction, f.mesh->GetComm());
INFO("Density-action mass-matrix error = " << relativeError);
@@ -181,32 +161,24 @@ TEST_CASE(
TEST_CASE(
"Barotropic Closure Jacobian Matches A Combined Centered Difference",
tags::hydro &tags::jacobian &tags::unit &tags::closure &tags::kernels
&tags::barotrope
tags::hydro &tags::jacobian &tags::unit &tags::closure &tags::kernels &tags::barotrope
) {
auto args = test_utils::setup_args();
mean_field::fem::FEM f =
mean_field::fem::setup_fem(args.mesh_file, args, 0);
auto args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
mfem::FunctionCoefficient densityCoefficient(
[](const mfem::Vector &position) {
return 0.4 + 0.03 * position(0) - 0.01 * position(1);
}
);
mfem::FunctionCoefficient densityCoefficient([](const mfem::Vector &position) {
return 0.4 + 0.03 * position(0) - 0.01 * position(1);
});
mfem::FunctionCoefficient enthalpyCoefficient(
[](const mfem::Vector &position) {
return 0.9 + 0.02 * position(0) - 0.01 * position(1);
}
);
mfem::FunctionCoefficient enthalpyCoefficient([](const mfem::Vector &position) {
return 0.9 + 0.02 * position(0) - 0.01 * position(1);
});
mfem::FunctionCoefficient enthalpyVariationCoefficient(
[](const mfem::Vector &position) {
return 0.07 + 0.015 * position(0) + 0.008 * position(2);
}
);
mfem::FunctionCoefficient enthalpyVariationCoefficient([](const mfem::Vector &position) {
return 0.07 + 0.015 * position(0) + 0.008 * position(2);
});
mfem::ParGridFunction densityField(f.densityFes.get());
mfem::ParGridFunction enthalpyField(f.enthalpyFes.get());
@@ -225,46 +197,33 @@ TEST_CASE(
enthalpyVariationField.GetTrueDofs(enthalpyVariation);
const mfem::Vector densityVariation =
gravity_prepared_test_utils::make_deterministic_vector(
f.densityFes->GetTrueVSize(), 0.63
);
gravity_prepared_test_utils::make_deterministic_vector(f.densityFes->GetTrueVSize(), 0.63);
const mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(f, 1.0);
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 1.0);
constexpr double differenceStep = 1.0e-6;
const mfem::Vector plusDensity =
gravity_prepared_test_utils::linear_combination(
density, 1.0, densityVariation, differenceStep
);
gravity_prepared_test_utils::linear_combination(density, 1.0, densityVariation, differenceStep);
const mfem::Vector minusDensity =
gravity_prepared_test_utils::linear_combination(
density, 1.0, densityVariation, -differenceStep
);
gravity_prepared_test_utils::linear_combination(density, 1.0, densityVariation, -differenceStep);
const mfem::Vector plusEnthalpy =
gravity_prepared_test_utils::linear_combination(
enthalpy, 1.0, enthalpyVariation, differenceStep
);
gravity_prepared_test_utils::linear_combination(enthalpy, 1.0, enthalpyVariation, differenceStep);
const mfem::Vector minusEnthalpy =
gravity_prepared_test_utils::linear_combination(
enthalpy, 1.0, enthalpyVariation, -differenceStep
);
gravity_prepared_test_utils::linear_combination(enthalpy, 1.0, enthalpyVariation, -differenceStep);
mfem::Vector plusResidual;
mfem::Vector minusResidual;
mean_field::operators::kernels::apply_barotropic_closure(
f, *f.domainMapperStateless, barotrope, plusDensity, plusEnthalpy,
displacement, plusResidual
f, *f.domainMapperStateless, barotrope, plusDensity, plusEnthalpy, displacement, plusResidual
);
mean_field::operators::kernels::apply_barotropic_closure(
f, *f.domainMapperStateless, barotrope, minusDensity, minusEnthalpy,
displacement, minusResidual
f, *f.domainMapperStateless, barotrope, minusDensity, minusEnthalpy, displacement, minusResidual
);
mfem::Vector finiteDifference(plusResidual);
@@ -275,21 +234,18 @@ TEST_CASE(
mfem::Vector enthalpyAction;
mean_field::operators::kernels::apply_barotropic_closure_density_action(
f, *f.domainMapperStateless, barotrope, densityVariation, displacement,
densityAction
f, *f.domainMapperStateless, barotrope, densityVariation, displacement, densityAction
);
mean_field::operators::kernels::apply_barotropic_closure_enthalpy_action(
f, *f.domainMapperStateless, barotrope, enthalpy, enthalpyVariation,
displacement, enthalpyAction
f, *f.domainMapperStateless, barotrope, enthalpy, enthalpyVariation, displacement, enthalpyAction
);
mfem::Vector analyticAction(densityAction);
analyticAction += enthalpyAction;
const double relativeError = gravity_prepared_test_utils::relative_error(
analyticAction, finiteDifference, f.mesh->GetComm()
);
const double relativeError =
gravity_prepared_test_utils::relative_error(analyticAction, finiteDifference, f.mesh->GetComm());
INFO("Combined EOS Jacobian error = " << relativeError);
@@ -298,57 +254,45 @@ TEST_CASE(
TEST_CASE(
"Barotropic Closure Density Action Excludes Vacuum And Uses Mapped Volume",
tags::hydro &tags::mapping &tags::unit &tags::closure &tags::barotrope
&tags::kernels
tags::hydro &tags::mapping &tags::unit &tags::closure &tags::barotrope &tags::kernels
) {
auto args = test_utils::setup_args();
mean_field::fem::FEM f =
mean_field::fem::setup_fem(args.mesh_file, args, 0);
auto args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
const mfem::Vector stellarDensity =
gravity_prepared_test_utils::make_domain_supported_density(f, true);
const mfem::Vector stellarDensity = gravity_prepared_test_utils::make_domain_supported_density(f, true);
const mfem::Vector vacuumDensity =
gravity_prepared_test_utils::make_domain_supported_density(f, false);
const mfem::Vector vacuumDensity = gravity_prepared_test_utils::make_domain_supported_density(f, false);
const mfem::Vector identityDisplacement =
gravity_prepared_test_utils::make_displacement(f, 0.0);
const mfem::Vector identityDisplacement = gravity_prepared_test_utils::make_displacement(f, 0.0);
const mfem::Vector deformedDisplacement =
gravity_prepared_test_utils::make_displacement(f, 1.0);
const mfem::Vector deformedDisplacement = gravity_prepared_test_utils::make_displacement(f, 1.0);
mfem::Vector stellarAction;
mfem::Vector vacuumAction;
mfem::Vector deformedAction;
mean_field::operators::kernels::apply_barotropic_closure_density_action(
f, *f.domainMapperStateless, barotrope, stellarDensity,
identityDisplacement, stellarAction
f, *f.domainMapperStateless, barotrope, stellarDensity, identityDisplacement, stellarAction
);
mean_field::operators::kernels::apply_barotropic_closure_density_action(
f, *f.domainMapperStateless, barotrope, vacuumDensity,
identityDisplacement, vacuumAction
f, *f.domainMapperStateless, barotrope, vacuumDensity, identityDisplacement, vacuumAction
);
mean_field::operators::kernels::apply_barotropic_closure_density_action(
f, *f.domainMapperStateless, barotrope, stellarDensity,
deformedDisplacement, deformedAction
f, *f.domainMapperStateless, barotrope, stellarDensity, deformedDisplacement, deformedAction
);
const MPI_Comm communicator = f.mesh->GetComm();
const double stellarNorm =
gravity_prepared_test_utils::global_norm(stellarAction, communicator);
const double stellarNorm = gravity_prepared_test_utils::global_norm(stellarAction, communicator);
const double vacuumNorm =
gravity_prepared_test_utils::global_norm(vacuumAction, communicator);
const double vacuumNorm = gravity_prepared_test_utils::global_norm(vacuumAction, communicator);
const double geometryChange = gravity_prepared_test_utils::relative_error(
deformedAction, stellarAction, communicator
);
const double geometryChange =
gravity_prepared_test_utils::relative_error(deformedAction, stellarAction, communicator);
INFO("Stellar action norm = " << stellarNorm);
INFO("Vacuum action norm = " << vacuumNorm);
@@ -361,37 +305,30 @@ TEST_CASE(
TEST_CASE(
"Barotropic Closure Displacement Action Matches Centered Differences",
tags::barotrope &tags::closure &tags::hydro &tags::integration
&tags::jacobian &tags::mapping &tags::physics
tags::barotrope &tags::closure &tags::hydro &tags::integration &tags::jacobian &tags::mapping &tags::physics
&tags::kernels
) {
auto args = test_utils::setup_args();
auto 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.domainMapperStateless != nullptr);
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
const mfem::Vector baseDensity =
barotropic_closure_geometry_test_utils::make_base_density(f);
const mfem::Vector baseDensity = barotropic_closure_geometry_test_utils::make_base_density(f);
const mfem::Vector baseEnthalpy =
barotropic_closure_geometry_test_utils::make_base_enthalpy(f);
const mfem::Vector baseEnthalpy = barotropic_closure_geometry_test_utils::make_base_enthalpy(f);
const mfem::Vector displacementVariation =
gravity_prepared_test_utils::make_displacement(f, 0.65);
const mfem::Vector displacementVariation = gravity_prepared_test_utils::make_displacement(f, 0.65);
constexpr double differenceStep = 1.0e-5;
constexpr double differenceStep = 1.0e-5;
const MPI_Comm communicator = f.mesh->GetComm();
const MPI_Comm communicator = f.mesh->GetComm();
for (const double deformationScale : {0.0, 1.0}) {
DYNAMIC_SECTION("Base deformation scale = " << deformationScale) {
const mfem::Vector baseDisplacement =
gravity_prepared_test_utils::make_displacement(
f, deformationScale
);
const mfem::Vector baseDisplacement = gravity_prepared_test_utils::make_displacement(f, deformationScale);
mfem::Vector plusDisplacement(baseDisplacement);
@@ -406,50 +343,36 @@ TEST_CASE(
mfem::Vector analyticAction;
mean_field::operators::kernels::apply_barotropic_closure(
f, *f.domainMapperStateless, barotrope, baseDensity,
baseEnthalpy, plusDisplacement, plusResidual
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy, plusDisplacement, plusResidual
);
mean_field::operators::kernels::apply_barotropic_closure(
f, *f.domainMapperStateless, barotrope, baseDensity,
baseEnthalpy, minusDisplacement, minusResidual
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy, minusDisplacement, minusResidual
);
mean_field::operators::kernels::
apply_barotropic_closure_displacement_action(
f, *f.domainMapperStateless, barotrope, baseDensity,
baseEnthalpy, baseDisplacement, displacementVariation,
analyticAction
);
mean_field::operators::kernels::apply_barotropic_closure_displacement_action(
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy, baseDisplacement,
displacementVariation, analyticAction
);
mfem::Vector finiteDifference(plusResidual);
finiteDifference -= minusResidual;
finiteDifference *= 1.0 / (2.0 * differenceStep);
const double analyticNorm =
gravity_prepared_test_utils::global_norm(
analyticAction, communicator
);
const double analyticNorm = gravity_prepared_test_utils::global_norm(analyticAction, communicator);
const double finiteDifferenceNorm =
gravity_prepared_test_utils::global_norm(
finiteDifference, communicator
);
gravity_prepared_test_utils::global_norm(finiteDifference, communicator);
const double relativeError =
gravity_prepared_test_utils::relative_error(
analyticAction, finiteDifference, communicator
);
gravity_prepared_test_utils::relative_error(analyticAction, finiteDifference, communicator);
INFO("Base deformation scale = " << deformationScale);
INFO("Analytic geometry-action norm = " << analyticNorm);
INFO(
"Finite-difference geometry-action norm = "
<< finiteDifferenceNorm
);
INFO("Finite-difference geometry-action norm = " << finiteDifferenceNorm);
INFO("Geometry-action relative error = " << relativeError);
@@ -463,34 +386,26 @@ TEST_CASE(
TEST_CASE(
"Barotropic Closure Displacement Action Is Linear In Its Direction",
tags::barotrope &tags::closure &tags::hydro &tags::jacobian &tags::mapping
&tags::physics &tags::unit
tags::barotrope &tags::closure &tags::hydro &tags::jacobian &tags::mapping &tags::physics &tags::unit &tags::kernels
) {
auto args = test_utils::setup_args();
auto 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.domainMapperStateless != nullptr);
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
const mfem::Vector baseDensity =
barotropic_closure_geometry_test_utils::make_base_density(f);
const mfem::Vector baseDensity = barotropic_closure_geometry_test_utils::make_base_density(f);
const mfem::Vector baseEnthalpy =
barotropic_closure_geometry_test_utils::make_base_enthalpy(f);
const mfem::Vector baseEnthalpy = barotropic_closure_geometry_test_utils::make_base_enthalpy(f);
const mfem::Vector baseDisplacement =
gravity_prepared_test_utils::make_displacement(f, 0.8);
const mfem::Vector baseDisplacement = gravity_prepared_test_utils::make_displacement(f, 0.8);
const mfem::Vector firstDirection =
gravity_prepared_test_utils::make_displacement(f, 0.4);
const mfem::Vector firstDirection = gravity_prepared_test_utils::make_displacement(f, 0.4);
mfem::Vector secondDirection =
gravity_prepared_test_utils::make_deterministic_vector(
f.displacementFes->GetTrueVSize(), 0.91
);
gravity_prepared_test_utils::make_deterministic_vector(f.displacementFes->GetTrueVSize(), 0.91);
secondDirection *= 0.01;
@@ -511,29 +426,23 @@ TEST_CASE(
mfem::Vector combinedAction;
mfem::Vector zeroAction;
mean_field::operators::kernels::
apply_barotropic_closure_displacement_action(
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy,
baseDisplacement, firstDirection, firstAction
);
mean_field::operators::kernels::apply_barotropic_closure_displacement_action(
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy, baseDisplacement, firstDirection, firstAction
);
mean_field::operators::kernels::
apply_barotropic_closure_displacement_action(
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy,
baseDisplacement, secondDirection, secondAction
);
mean_field::operators::kernels::apply_barotropic_closure_displacement_action(
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy, baseDisplacement, secondDirection,
secondAction
);
mean_field::operators::kernels::
apply_barotropic_closure_displacement_action(
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy,
baseDisplacement, combinedDirection, combinedAction
);
mean_field::operators::kernels::apply_barotropic_closure_displacement_action(
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy, baseDisplacement, combinedDirection,
combinedAction
);
mean_field::operators::kernels::
apply_barotropic_closure_displacement_action(
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy,
baseDisplacement, zeroDirection, zeroAction
);
mean_field::operators::kernels::apply_barotropic_closure_displacement_action(
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy, baseDisplacement, zeroDirection, zeroAction
);
mfem::Vector expectedAction(firstAction);
@@ -543,15 +452,12 @@ TEST_CASE(
const MPI_Comm communicator = f.mesh->GetComm();
const double expectedNorm =
gravity_prepared_test_utils::global_norm(expectedAction, communicator);
const double expectedNorm = gravity_prepared_test_utils::global_norm(expectedAction, communicator);
const double linearityError = gravity_prepared_test_utils::relative_error(
combinedAction, expectedAction, communicator
);
const double linearityError =
gravity_prepared_test_utils::relative_error(combinedAction, expectedAction, communicator);
const double zeroActionNorm =
gravity_prepared_test_utils::global_norm(zeroAction, communicator);
const double zeroActionNorm = gravity_prepared_test_utils::global_norm(zeroAction, communicator);
INFO("Expected combined-action norm = " << expectedNorm);
@@ -568,55 +474,45 @@ TEST_CASE(
TEST_CASE(
"Barotropic Closure Displacement Action Excludes Vacuum",
tags::barotrope &tags::closure &tags::hydro &tags::mapping &tags::physics
&tags::unit
tags::barotrope &tags::closure &tags::hydro &tags::mapping &tags::physics &tags::unit &tags::kernels
) {
auto args = test_utils::setup_args();
auto 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.domainMapperStateless != nullptr);
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
const mfem::Vector stellarDensity =
gravity_prepared_test_utils::make_domain_supported_density(f, true);
const mfem::Vector stellarDensity = gravity_prepared_test_utils::make_domain_supported_density(f, true);
const mfem::Vector vacuumDensity =
gravity_prepared_test_utils::make_domain_supported_density(f, false);
const mfem::Vector vacuumDensity = gravity_prepared_test_utils::make_domain_supported_density(f, false);
mfem::Vector zeroEnthalpy(f.enthalpyFes->GetTrueVSize());
zeroEnthalpy = 0.0;
zeroEnthalpy = 0.0;
const mfem::Vector baseDisplacement =
gravity_prepared_test_utils::make_displacement(f, 0.7);
const mfem::Vector baseDisplacement = gravity_prepared_test_utils::make_displacement(f, 0.7);
const mfem::Vector displacementVariation =
gravity_prepared_test_utils::make_displacement(f, 0.5);
const mfem::Vector displacementVariation = gravity_prepared_test_utils::make_displacement(f, 0.5);
mfem::Vector stellarAction;
mfem::Vector vacuumAction;
mean_field::operators::kernels::
apply_barotropic_closure_displacement_action(
f, *f.domainMapperStateless, barotrope, stellarDensity,
zeroEnthalpy, baseDisplacement, displacementVariation, stellarAction
);
mean_field::operators::kernels::apply_barotropic_closure_displacement_action(
f, *f.domainMapperStateless, barotrope, stellarDensity, zeroEnthalpy, baseDisplacement, displacementVariation,
stellarAction
);
mean_field::operators::kernels::
apply_barotropic_closure_displacement_action(
f, *f.domainMapperStateless, barotrope, vacuumDensity, zeroEnthalpy,
baseDisplacement, displacementVariation, vacuumAction
);
mean_field::operators::kernels::apply_barotropic_closure_displacement_action(
f, *f.domainMapperStateless, barotrope, vacuumDensity, zeroEnthalpy, baseDisplacement, displacementVariation,
vacuumAction
);
const MPI_Comm communicator = f.mesh->GetComm();
const double stellarNorm =
gravity_prepared_test_utils::global_norm(stellarAction, communicator);
const double stellarNorm = gravity_prepared_test_utils::global_norm(stellarAction, communicator);
const double vacuumNorm =
gravity_prepared_test_utils::global_norm(vacuumAction, communicator);
const double vacuumNorm = gravity_prepared_test_utils::global_norm(vacuumAction, communicator);
INFO("Stellar geometry-action norm = " << stellarNorm);

View File

@@ -34,8 +34,7 @@ namespace hydrostatic_kernel_test_utils {
mfem::Vector make_enthalpy(const mean_field::fem::FEM &f) {
mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) {
return 1.10 + 0.035 * position(0) - 0.021 * position(1) +
0.014 * position(2);
return 1.10 + 0.035 * position(0) - 0.021 * position(1) + 0.014 * position(2);
});
return project_scalar(*f.enthalpyFes, coefficient);
@@ -43,8 +42,7 @@ namespace hydrostatic_kernel_test_utils {
mfem::Vector make_potential(const mean_field::fem::FEM &f) {
mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) {
return -0.72 + 0.018 * position(0) + 0.011 * position(1) -
0.025 * position(2);
return -0.72 + 0.018 * position(0) + 0.011 * position(1) - 0.025 * position(2);
});
return project_scalar(*f.gravityPotentialFes, coefficient);
@@ -98,23 +96,18 @@ namespace hydrostatic_kernel_test_utils {
mfem::Vector difference(computed);
difference -= reference;
return gravity_prepared_test_utils::global_norm(
difference, communicator
) /
return gravity_prepared_test_utils::global_norm(difference, communicator) /
std::max(normalization, std::numeric_limits<double>::epsilon());
}
mfem::Vector
make_vacuum_supported_potential(const mean_field::fem::FEM &f) {
mfem::Vector make_vacuum_supported_potential(const mean_field::fem::FEM &f) {
mfem::Vector attributeValues(f.mesh->attributes.Max());
attributeValues = 0.0;
attributeValues = 0.0;
const int vacuumAttribute =
f.domainMapperStateless->GetVacuumElementAttribute();
const int vacuumAttribute = f.domainMapperStateless->GetVacuumElementAttribute();
for (int attributeIndex = 0; attributeIndex < f.mesh->attributes.Size();
++attributeIndex) {
for (int attributeIndex = 0; attributeIndex < f.mesh->attributes.Size(); ++attributeIndex) {
const int attribute = f.mesh->attributes[attributeIndex];
if (attribute == vacuumAttribute) {
@@ -144,10 +137,9 @@ namespace hydrostatic_kernel_test_utils {
const mfem::Vector &input,
mfem::Vector &output
) const override {
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_enthalpy_action(
f_, domainMapper_, input, displacementTrue_, output
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_enthalpy_action(
f_, domainMapper_, input, displacementTrue_, output
);
}
private:
@@ -161,10 +153,9 @@ namespace hydrostatic_kernel_test_utils {
TEST_CASE(
"Rigid Rotation Potential Derivative Matches Centered Differences",
tags::barotrope &tags::hydro &tags::jacobian &tags::physics &tags::unit
tags::barotrope &tags::hydro &tags::jacobian &tags::physics &tags::unit &tags::kernels
) {
const mean_field::physics::RigidRotation rotation =
hydrostatic_kernel_test_utils::make_rotation();
const mean_field::physics::RigidRotation rotation = hydrostatic_kernel_test_utils::make_rotation();
mfem::Vector position(3);
mfem::Vector direction(3);
@@ -186,17 +177,12 @@ TEST_CASE(
minusPosition.Add(-epsilon, direction);
const double centeredDerivative =
(rotation.potential(plusPosition) - rotation.potential(minusPosition)) /
(2.0 * epsilon);
(rotation.potential(plusPosition) - rotation.potential(minusPosition)) / (2.0 * epsilon);
const double analyticDerivative =
rotation.potential_directional_derivative(position, direction);
const double analyticDerivative = rotation.potential_directional_derivative(position, direction);
const double relativeError =
std::abs(centeredDerivative - analyticDerivative) /
std::max(
std::abs(analyticDerivative), std::numeric_limits<double>::epsilon()
);
const double relativeError = std::abs(centeredDerivative - analyticDerivative) /
std::max(std::abs(analyticDerivative), std::numeric_limits<double>::epsilon());
INFO("Rigid-rotation derivative error = " << relativeError);
@@ -205,42 +191,31 @@ TEST_CASE(
TEST_CASE(
"Hydrostatic Residual Vanishes For A Manufactured Rotating State",
tags::barotrope &tags::hydro &tags::integration &tags::kernels
&tags::physics &tags::residuals
tags::barotrope &tags::hydro &tags::integration &tags::kernels &tags::physics &tags::residuals
) {
auto args = test_utils::setup_args();
auto 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);
const mean_field::physics::RigidRotation rotation =
hydrostatic_kernel_test_utils::make_rotation();
const mean_field::physics::RigidRotation rotation = hydrostatic_kernel_test_utils::make_rotation();
constexpr double bernoulliConstant = 0.73;
constexpr double potentialValue = -0.21;
constexpr double constantOffset = 0.40;
constexpr double bernoulliConstant = 0.73;
constexpr double potentialValue = -0.21;
constexpr double constantOffset = 0.40;
mfem::FunctionCoefficient enthalpyCoefficient(
[&rotation](const mfem::Vector &position) {
return bernoulliConstant - potentialValue +
rotation.potential(position);
}
);
mfem::FunctionCoefficient enthalpyCoefficient([&rotation](const mfem::Vector &position) {
return bernoulliConstant - potentialValue + rotation.potential(position);
});
const mfem::Vector interpolatedEnthalpy =
hydrostatic_kernel_test_utils::project_scalar(
*f.enthalpyFes, enthalpyCoefficient
);
hydrostatic_kernel_test_utils::project_scalar(*f.enthalpyFes, enthalpyCoefficient);
const mfem::Vector potential =
hydrostatic_kernel_test_utils::make_constant_field(
*f.gravityPotentialFes, potentialValue
);
hydrostatic_kernel_test_utils::make_constant_field(*f.gravityPotentialFes, potentialValue);
const mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(f, 0.0);
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.0);
const MPI_Comm communicator = f.mesh->GetComm();
const MPI_Comm communicator = f.mesh->GetComm();
/*
* First measure the residual of the nodally interpolated
@@ -253,35 +228,26 @@ TEST_CASE(
mfem::Vector interpolatedReferenceResidual;
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, interpolatedEnthalpy, potential,
displacement, bernoulliConstant, interpolatedResidual
f, *f.domainMapperStateless, rotation, interpolatedEnthalpy, potential, displacement, bernoulliConstant,
interpolatedResidual
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, interpolatedEnthalpy, potential,
displacement, bernoulliConstant + constantOffset,
interpolatedReferenceResidual
f, *f.domainMapperStateless, rotation, interpolatedEnthalpy, potential, displacement,
bernoulliConstant + constantOffset, interpolatedReferenceResidual
);
const double interpolatedResidualNorm =
gravity_prepared_test_utils::global_norm(
interpolatedResidual, communicator
);
gravity_prepared_test_utils::global_norm(interpolatedResidual, communicator);
const double interpolatedReferenceNorm =
gravity_prepared_test_utils::global_norm(
interpolatedReferenceResidual, communicator
);
gravity_prepared_test_utils::global_norm(interpolatedReferenceResidual, communicator);
REQUIRE(interpolatedReferenceNorm > 1.0e-12);
const double representationFloor =
interpolatedResidualNorm / interpolatedReferenceNorm;
const double representationFloor = interpolatedResidualNorm / interpolatedReferenceNorm;
INFO(
"Interpolated rotating-state residual norm = "
<< interpolatedResidualNorm
);
INFO("Interpolated rotating-state residual norm = " << interpolatedResidualNorm);
INFO(
"Interpolated rotating-state relative "
@@ -310,8 +276,9 @@ TEST_CASE(
* side is in the range of M_h. Starting CG from zero keeps the
* iteration in the active stellar subspace.
*/
hydrostatic_kernel_test_utils::HydrostaticEnthalpyMassOperator
enthalpyMassOperator(f, *f.domainMapperStateless, displacement);
hydrostatic_kernel_test_utils::HydrostaticEnthalpyMassOperator enthalpyMassOperator(
f, *f.domainMapperStateless, displacement
);
mfem::Vector correctionRightHandSide(interpolatedResidual);
@@ -332,20 +299,11 @@ TEST_CASE(
projectionSolver.Mult(correctionRightHandSide, enthalpyCorrection);
INFO(
"Discrete-equilibrium projection converged = "
<< projectionSolver.GetConverged()
);
INFO("Discrete-equilibrium projection converged = " << projectionSolver.GetConverged());
INFO(
"Discrete-equilibrium projection iterations = "
<< projectionSolver.GetNumIterations()
);
INFO("Discrete-equilibrium projection iterations = " << projectionSolver.GetNumIterations());
INFO(
"Discrete-equilibrium projection final norm = "
<< projectionSolver.GetFinalNorm()
);
INFO("Discrete-equilibrium projection final norm = " << projectionSolver.GetFinalNorm());
REQUIRE(projectionSolver.GetConverged());
@@ -356,9 +314,7 @@ TEST_CASE(
correctionEquationResidual -= correctionRightHandSide;
const double correctionEquationNorm =
gravity_prepared_test_utils::global_norm(
correctionEquationResidual, communicator
);
gravity_prepared_test_utils::global_norm(correctionEquationResidual, communicator);
INFO(
"Discrete-equilibrium correction-equation "
@@ -366,10 +322,7 @@ TEST_CASE(
<< correctionEquationNorm
);
CHECK(
correctionEquationNorm <=
std::max(5.0e-12 * interpolatedResidualNorm, 5.0e-15)
);
CHECK(correctionEquationNorm <= std::max(5.0e-12 * interpolatedResidualNorm, 5.0e-15));
mfem::Vector discreteEnthalpy(interpolatedEnthalpy);
@@ -379,25 +332,20 @@ TEST_CASE(
mfem::Vector referenceResidual;
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, discreteEnthalpy, potential,
displacement, bernoulliConstant, exactResidual
f, *f.domainMapperStateless, rotation, discreteEnthalpy, potential, displacement, bernoulliConstant,
exactResidual
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, discreteEnthalpy, potential,
displacement, bernoulliConstant + constantOffset, referenceResidual
f, *f.domainMapperStateless, rotation, discreteEnthalpy, potential, displacement,
bernoulliConstant + constantOffset, referenceResidual
);
const double exactNorm =
gravity_prepared_test_utils::global_norm(exactResidual, communicator);
const double exactNorm = gravity_prepared_test_utils::global_norm(exactResidual, communicator);
const double referenceNorm = gravity_prepared_test_utils::global_norm(
referenceResidual, communicator
);
const double referenceNorm = gravity_prepared_test_utils::global_norm(referenceResidual, communicator);
const double correctionNorm = gravity_prepared_test_utils::global_norm(
enthalpyCorrection, communicator
);
const double correctionNorm = gravity_prepared_test_utils::global_norm(enthalpyCorrection, communicator);
INFO("Enthalpy representation correction norm = " << correctionNorm);
@@ -412,43 +360,31 @@ TEST_CASE(
TEST_CASE(
"Exact Constant Hydrostatic Equilibrium Remains Zero Under Deformation",
tags::barotrope &tags::hydro &tags::integration &tags::jacobian
&tags::kernels &tags::mapping &tags::physics
tags::barotrope &tags::hydro &tags::integration &tags::jacobian &tags::kernels &tags::mapping &tags::physics
) {
auto args = test_utils::setup_args();
auto 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);
const mean_field::physics::RigidRotation rotation =
hydrostatic_kernel_test_utils::make_zero_rotation();
const mean_field::physics::RigidRotation rotation = hydrostatic_kernel_test_utils::make_zero_rotation();
constexpr double enthalpyValue = 1.20;
constexpr double potentialValue = -0.35;
constexpr double enthalpyValue = 1.20;
constexpr double potentialValue = -0.35;
constexpr double bernoulliConstant = enthalpyValue + potentialValue;
constexpr double bernoulliConstant = enthalpyValue + potentialValue;
const mfem::Vector enthalpy =
hydrostatic_kernel_test_utils::make_constant_field(
*f.enthalpyFes, enthalpyValue
);
const mfem::Vector enthalpy = hydrostatic_kernel_test_utils::make_constant_field(*f.enthalpyFes, enthalpyValue);
const mfem::Vector potential =
hydrostatic_kernel_test_utils::make_constant_field(
*f.gravityPotentialFes, potentialValue
);
hydrostatic_kernel_test_utils::make_constant_field(*f.gravityPotentialFes, potentialValue);
const mfem::Vector displacementVariation =
gravity_prepared_test_utils::make_displacement(f, 0.67);
const mfem::Vector displacementVariation = gravity_prepared_test_utils::make_displacement(f, 0.67);
const MPI_Comm communicator = f.mesh->GetComm();
const MPI_Comm communicator = f.mesh->GetComm();
for (const double deformationScale : {0.0, 0.5, 1.0}) {
DYNAMIC_SECTION("Deformation scale = " << deformationScale) {
const mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(
f, deformationScale
);
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, deformationScale);
mfem::Vector exactResidual;
mfem::Vector referenceResidual;
@@ -456,48 +392,35 @@ TEST_CASE(
mfem::Vector referenceGeometryAction;
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, enthalpy, potential,
displacement, bernoulliConstant, exactResidual
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant,
exactResidual
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, enthalpy, potential,
displacement, bernoulliConstant + 0.50, referenceResidual
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant + 0.50,
referenceResidual
);
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential,
displacement, bernoulliConstant, displacementVariation,
exactGeometryAction
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant,
displacementVariation, exactGeometryAction
);
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential,
displacement, bernoulliConstant + 0.50,
displacementVariation, referenceGeometryAction
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant + 0.50,
displacementVariation, referenceGeometryAction
);
const double exactResidualNorm =
gravity_prepared_test_utils::global_norm(
exactResidual, communicator
);
const double exactResidualNorm = gravity_prepared_test_utils::global_norm(exactResidual, communicator);
const double referenceResidualNorm =
gravity_prepared_test_utils::global_norm(
referenceResidual, communicator
);
gravity_prepared_test_utils::global_norm(referenceResidual, communicator);
const double exactGeometryNorm =
gravity_prepared_test_utils::global_norm(
exactGeometryAction, communicator
);
gravity_prepared_test_utils::global_norm(exactGeometryAction, communicator);
const double referenceGeometryNorm =
gravity_prepared_test_utils::global_norm(
referenceGeometryAction, communicator
);
gravity_prepared_test_utils::global_norm(referenceGeometryAction, communicator);
REQUIRE(referenceResidualNorm > 1.0e-12);
@@ -512,64 +435,49 @@ TEST_CASE(
TEST_CASE(
"Hydrostatic Equilibrium Excludes Vacuum Elements",
tags::barotrope &tags::hydro &tags::kernels &tags::mapping &tags::physics
&tags::unit
tags::barotrope &tags::hydro &tags::kernels &tags::mapping &tags::physics &tags::unit
) {
auto args = test_utils::setup_args();
auto 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);
const mean_field::physics::RigidRotation rotation =
hydrostatic_kernel_test_utils::make_zero_rotation();
const mean_field::physics::RigidRotation rotation = hydrostatic_kernel_test_utils::make_zero_rotation();
const mfem::Vector zeroEnthalpy(f.enthalpyFes->GetTrueVSize());
mfem::Vector enthalpy(zeroEnthalpy);
enthalpy = 0.0;
enthalpy = 0.0;
const mfem::Vector vacuumPotential =
hydrostatic_kernel_test_utils::make_vacuum_supported_potential(f);
const mfem::Vector vacuumPotential = hydrostatic_kernel_test_utils::make_vacuum_supported_potential(f);
const mfem::Vector stellarPotential =
hydrostatic_kernel_test_utils::make_constant_field(
*f.gravityPotentialFes, 1.0
);
hydrostatic_kernel_test_utils::make_constant_field(*f.gravityPotentialFes, 1.0);
const mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(f, 1.0);
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 1.0);
mfem::Vector residual;
mfem::Vector vacuumAction;
mfem::Vector stellarAction;
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, enthalpy, vacuumPotential,
displacement, 0.0, residual
f, *f.domainMapperStateless, rotation, enthalpy, vacuumPotential, displacement, 0.0, residual
);
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_potential_action(
f, *f.domainMapperStateless, vacuumPotential, displacement,
vacuumAction
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_potential_action(
f, *f.domainMapperStateless, vacuumPotential, displacement, vacuumAction
);
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_potential_action(
f, *f.domainMapperStateless, stellarPotential, displacement,
stellarAction
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_potential_action(
f, *f.domainMapperStateless, stellarPotential, displacement, stellarAction
);
const MPI_Comm communicator = f.mesh->GetComm();
const MPI_Comm communicator = f.mesh->GetComm();
const double residualNorm =
gravity_prepared_test_utils::global_norm(residual, communicator);
const double residualNorm = gravity_prepared_test_utils::global_norm(residual, communicator);
const double vacuumActionNorm =
gravity_prepared_test_utils::global_norm(vacuumAction, communicator);
const double vacuumActionNorm = gravity_prepared_test_utils::global_norm(vacuumAction, communicator);
const double stellarActionNorm =
gravity_prepared_test_utils::global_norm(stellarAction, communicator);
const double stellarActionNorm = gravity_prepared_test_utils::global_norm(stellarAction, communicator);
REQUIRE(stellarActionNorm > 1.0e-12);
@@ -580,40 +488,28 @@ TEST_CASE(
TEST_CASE(
"Hydrostatic Jacobian Matches Blocks And Centered Differences",
tags::barotrope &tags::hydro &tags::integration &tags::jacobian
&tags::kernels &tags::mapping &tags::physics
tags::barotrope &tags::hydro &tags::integration &tags::jacobian &tags::kernels &tags::mapping &tags::physics
) {
auto args = test_utils::setup_args();
auto 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);
const mean_field::physics::RigidRotation rotation =
hydrostatic_kernel_test_utils::make_rotation();
const mean_field::physics::RigidRotation rotation = hydrostatic_kernel_test_utils::make_rotation();
const mfem::Vector enthalpy =
hydrostatic_kernel_test_utils::make_enthalpy(f);
const mfem::Vector enthalpy = hydrostatic_kernel_test_utils::make_enthalpy(f);
const mfem::Vector potential =
hydrostatic_kernel_test_utils::make_potential(f);
const mfem::Vector potential = hydrostatic_kernel_test_utils::make_potential(f);
const mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(f, 1.0);
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 1.0);
const mfem::Vector enthalpyVariation =
gravity_prepared_test_utils::make_deterministic_vector(
f.enthalpyFes->GetTrueVSize(), 0.23
);
gravity_prepared_test_utils::make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), 0.23);
const mfem::Vector potentialVariation =
gravity_prepared_test_utils::make_deterministic_vector(
f.gravityPotentialFes->GetTrueVSize(), 0.47
);
gravity_prepared_test_utils::make_deterministic_vector(f.gravityPotentialFes->GetTrueVSize(), 0.47);
const mfem::Vector displacementVariation =
gravity_prepared_test_utils::make_deterministic_vector(
f.displacementFes->GetTrueVSize(), 0.71
);
gravity_prepared_test_utils::make_deterministic_vector(f.displacementFes->GetTrueVSize(), 0.71);
constexpr double bernoulliConstant = 0.41;
constexpr double constantVariation = -0.37;
@@ -625,35 +521,26 @@ TEST_CASE(
mfem::Vector displacementAction;
mfem::Vector completeAction;
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_enthalpy_action(
f, *f.domainMapperStateless, enthalpyVariation, displacement,
enthalpyAction
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_enthalpy_action(
f, *f.domainMapperStateless, enthalpyVariation, displacement, enthalpyAction
);
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_potential_action(
f, *f.domainMapperStateless, potentialVariation, displacement,
potentialAction
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_potential_action(
f, *f.domainMapperStateless, potentialVariation, displacement, potentialAction
);
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_constant_action(
f, *f.domainMapperStateless, constantVariation, displacement,
constantAction
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_constant_action(
f, *f.domainMapperStateless, constantVariation, displacement, constantAction
);
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential,
displacement, bernoulliConstant, displacementVariation,
displacementAction
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant,
displacementVariation, displacementAction
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential,
displacement, bernoulliConstant, enthalpyVariation, potentialVariation,
constantVariation, displacementVariation, completeAction
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant, enthalpyVariation,
potentialVariation, constantVariation, displacementVariation, completeAction
);
mfem::Vector blockAction(enthalpyAction);
@@ -663,25 +550,21 @@ TEST_CASE(
const MPI_Comm communicator = f.mesh->GetComm();
const double blockError = gravity_prepared_test_utils::relative_error(
completeAction, blockAction, communicator
);
const double blockError = gravity_prepared_test_utils::relative_error(completeAction, blockAction, communicator);
INFO("Hydrostatic block reconstruction error = " << blockError);
CHECK(blockError < 5.0e-13);
auto evaluate_residual = [&f, &rotation](
const mfem::Vector &trialEnthalpy,
const mfem::Vector &trialPotential,
const mfem::Vector &trialDisplacement,
const double trialConstant
const mfem::Vector &trialEnthalpy, const mfem::Vector &trialPotential,
const mfem::Vector &trialDisplacement, const double trialConstant
) {
mfem::Vector residual;
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, trialEnthalpy,
trialPotential, trialDisplacement, trialConstant, residual
f, *f.domainMapperStateless, rotation, trialEnthalpy, trialPotential, trialDisplacement, trialConstant,
residual
);
return residual;
@@ -694,16 +577,10 @@ TEST_CASE(
minusEnthalpy.Add(-epsilon, enthalpyVariation);
const mfem::Vector enthalpyDifference =
hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(
plusEnthalpy, potential, displacement, bernoulliConstant
),
evaluate_residual(
minusEnthalpy, potential, displacement, bernoulliConstant
),
epsilon
);
const mfem::Vector enthalpyDifference = hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(plusEnthalpy, potential, displacement, bernoulliConstant),
evaluate_residual(minusEnthalpy, potential, displacement, bernoulliConstant), epsilon
);
mfem::Vector plusPotential(potential);
mfem::Vector minusPotential(potential);
@@ -712,29 +589,15 @@ TEST_CASE(
minusPotential.Add(-epsilon, potentialVariation);
const mfem::Vector potentialDifference =
hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(
enthalpy, plusPotential, displacement, bernoulliConstant
),
evaluate_residual(
enthalpy, minusPotential, displacement, bernoulliConstant
),
epsilon
);
const mfem::Vector potentialDifference = hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(enthalpy, plusPotential, displacement, bernoulliConstant),
evaluate_residual(enthalpy, minusPotential, displacement, bernoulliConstant), epsilon
);
const mfem::Vector constantDifference =
hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(
enthalpy, potential, displacement,
bernoulliConstant + epsilon * constantVariation
),
evaluate_residual(
enthalpy, potential, displacement,
bernoulliConstant - epsilon * constantVariation
),
epsilon
);
const mfem::Vector constantDifference = hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(enthalpy, potential, displacement, bernoulliConstant + epsilon * constantVariation),
evaluate_residual(enthalpy, potential, displacement, bernoulliConstant - epsilon * constantVariation), epsilon
);
mfem::Vector plusDisplacement(displacement);
mfem::Vector minusDisplacement(displacement);
@@ -743,33 +606,22 @@ TEST_CASE(
minusDisplacement.Add(-epsilon, displacementVariation);
const mfem::Vector displacementDifference =
hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(
enthalpy, potential, plusDisplacement, bernoulliConstant
),
evaluate_residual(
enthalpy, potential, minusDisplacement, bernoulliConstant
),
epsilon
);
const double enthalpyError = gravity_prepared_test_utils::relative_error(
enthalpyAction, enthalpyDifference, communicator
const mfem::Vector displacementDifference = hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(enthalpy, potential, plusDisplacement, bernoulliConstant),
evaluate_residual(enthalpy, potential, minusDisplacement, bernoulliConstant), epsilon
);
const double potentialError = gravity_prepared_test_utils::relative_error(
potentialAction, potentialDifference, communicator
);
const double enthalpyError =
gravity_prepared_test_utils::relative_error(enthalpyAction, enthalpyDifference, communicator);
const double constantError = gravity_prepared_test_utils::relative_error(
constantAction, constantDifference, communicator
);
const double potentialError =
gravity_prepared_test_utils::relative_error(potentialAction, potentialDifference, communicator);
const double constantError =
gravity_prepared_test_utils::relative_error(constantAction, constantDifference, communicator);
const double displacementError =
gravity_prepared_test_utils::relative_error(
displacementAction, displacementDifference, communicator
);
gravity_prepared_test_utils::relative_error(displacementAction, displacementDifference, communicator);
INFO("Hydrostatic enthalpy-block error = " << enthalpyError);
@@ -803,35 +655,26 @@ TEST_CASE(
combinedMinusDisplacement.Add(-epsilon, displacementVariation);
const mfem::Vector combinedDifference =
hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(
combinedPlusEnthalpy, combinedPlusPotential,
combinedPlusDisplacement,
bernoulliConstant + epsilon * constantVariation
),
evaluate_residual(
combinedMinusEnthalpy, combinedMinusPotential,
combinedMinusDisplacement,
bernoulliConstant - epsilon * constantVariation
),
epsilon
);
const mfem::Vector combinedDifference = hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(
combinedPlusEnthalpy, combinedPlusPotential, combinedPlusDisplacement,
bernoulliConstant + epsilon * constantVariation
),
evaluate_residual(
combinedMinusEnthalpy, combinedMinusPotential, combinedMinusDisplacement,
bernoulliConstant - epsilon * constantVariation
),
epsilon
);
const double blockNormSum =
gravity_prepared_test_utils::global_norm(enthalpyAction, communicator) +
gravity_prepared_test_utils::global_norm(
potentialAction, communicator
) +
gravity_prepared_test_utils::global_norm(constantAction, communicator) +
gravity_prepared_test_utils::global_norm(
displacementAction, communicator
);
const double blockNormSum = gravity_prepared_test_utils::global_norm(enthalpyAction, communicator) +
gravity_prepared_test_utils::global_norm(potentialAction, communicator) +
gravity_prepared_test_utils::global_norm(constantAction, communicator) +
gravity_prepared_test_utils::global_norm(displacementAction, communicator);
const double simultaneousError =
hydrostatic_kernel_test_utils::sum_normalized_error(
completeAction, combinedDifference, blockNormSum, communicator
);
const double simultaneousError = hydrostatic_kernel_test_utils::sum_normalized_error(
completeAction, combinedDifference, blockNormSum, communicator
);
INFO("Hydrostatic simultaneous Jacobian error = " << simultaneousError);
@@ -840,75 +683,58 @@ TEST_CASE(
TEST_CASE(
"Hydrostatic Displacement Action Is Linear In Its Direction",
tags::barotrope &tags::hydro &tags::integration &tags::jacobian
&tags::mapping &tags::physics &tags::unit
tags::barotrope &tags::hydro &tags::integration &tags::jacobian &tags::mapping &tags::physics &tags::unit
&tags::kernels
) {
auto args = test_utils::setup_args();
auto 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);
const mean_field::physics::RigidRotation rotation =
hydrostatic_kernel_test_utils::make_rotation();
const mean_field::physics::RigidRotation rotation = hydrostatic_kernel_test_utils::make_rotation();
const mfem::Vector enthalpy =
hydrostatic_kernel_test_utils::make_enthalpy(f);
const mfem::Vector enthalpy = hydrostatic_kernel_test_utils::make_enthalpy(f);
const mfem::Vector potential =
hydrostatic_kernel_test_utils::make_potential(f);
const mfem::Vector potential = hydrostatic_kernel_test_utils::make_potential(f);
const mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(f, 1.0);
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 1.0);
const mfem::Vector firstDirection =
gravity_prepared_test_utils::make_deterministic_vector(
f.displacementFes->GetTrueVSize(), 0.31
);
gravity_prepared_test_utils::make_deterministic_vector(f.displacementFes->GetTrueVSize(), 0.31);
const mfem::Vector secondDirection =
gravity_prepared_test_utils::make_deterministic_vector(
f.displacementFes->GetTrueVSize(), 0.83
);
gravity_prepared_test_utils::make_deterministic_vector(f.displacementFes->GetTrueVSize(), 0.83);
constexpr double firstScale = 0.43;
constexpr double secondScale = -0.29;
constexpr double bernoulliConstant = 0.41;
const mfem::Vector combinedDirection =
gravity_prepared_test_utils::linear_combination(
firstDirection, firstScale, secondDirection, secondScale
);
gravity_prepared_test_utils::linear_combination(firstDirection, firstScale, secondDirection, secondScale);
mfem::Vector firstAction;
mfem::Vector secondAction;
mfem::Vector combinedAction;
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential,
displacement, bernoulliConstant, firstDirection, firstAction
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant, firstDirection,
firstAction
);
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential,
displacement, bernoulliConstant, secondDirection, secondAction
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant, secondDirection,
secondAction
);
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential,
displacement, bernoulliConstant, combinedDirection, combinedAction
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant, combinedDirection,
combinedAction
);
const mfem::Vector expectedAction =
gravity_prepared_test_utils::linear_combination(
firstAction, firstScale, secondAction, secondScale
);
gravity_prepared_test_utils::linear_combination(firstAction, firstScale, secondAction, secondScale);
const double linearityError = gravity_prepared_test_utils::relative_error(
combinedAction, expectedAction, f.mesh->GetComm()
);
const double linearityError =
gravity_prepared_test_utils::relative_error(combinedAction, expectedAction, f.mesh->GetComm());
INFO("Hydrostatic displacement-linearity error = " << linearityError);
@@ -917,13 +743,11 @@ TEST_CASE(
TEST_CASE(
"Hydrostatic Residual Is Translationally Invariant On Deformed Geometry",
tags::barotrope &tags::hydro &tags::integration &tags::kernels
&tags::mapping &tags::physics &tags::residuals
tags::barotrope &tags::hydro &tags::integration &tags::kernels &tags::mapping &tags::physics &tags::residuals
) {
auto args = test_utils::setup_args();
auto 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);
mfem::Vector angularVelocity(3);
@@ -946,33 +770,25 @@ TEST_CASE(
mfem::Vector translatedCenter(center);
translatedCenter += translation;
const mean_field::physics::RigidRotation baseRotation(
angularVelocity, center
);
const mean_field::physics::RigidRotation baseRotation(angularVelocity, center);
const mean_field::physics::RigidRotation translatedRotation(
angularVelocity, translatedCenter
);
const mean_field::physics::RigidRotation translatedRotation(angularVelocity, translatedCenter);
const mfem::Vector enthalpy =
hydrostatic_kernel_test_utils::make_enthalpy(f);
const mfem::Vector enthalpy = hydrostatic_kernel_test_utils::make_enthalpy(f);
const mfem::Vector potential =
hydrostatic_kernel_test_utils::make_potential(f);
const mfem::Vector potential = hydrostatic_kernel_test_utils::make_potential(f);
/*
* Use a nontrivially deformed base state so this checks rotation
* and mapped geometry simultaneously. The comparison state adds
* an exactly representable rigid translation to that deformation.
*/
const mfem::Vector baseDisplacement =
gravity_prepared_test_utils::make_displacement(f, 0.73);
const mfem::Vector baseDisplacement = gravity_prepared_test_utils::make_displacement(f, 0.73);
mfem::ParGridFunction translationField(f.displacementFes.get());
mfem::VectorFunctionCoefficient translationCoefficient(
f.mesh->Dimension(),
[&translation](const mfem::Vector &, mfem::Vector &value) {
f.mesh->Dimension(), [&translation](const mfem::Vector &, mfem::Vector &value) {
value.SetSize(translation.Size());
value = translation;
}
@@ -994,13 +810,13 @@ TEST_CASE(
mfem::Vector untranslatedCenterResidual;
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, baseRotation, enthalpy, potential,
baseDisplacement, bernoulliConstant, baseResidual
f, *f.domainMapperStateless, baseRotation, enthalpy, potential, baseDisplacement, bernoulliConstant,
baseResidual
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, translatedRotation, enthalpy, potential,
translatedDisplacement, bernoulliConstant, translatedResidual
f, *f.domainMapperStateless, translatedRotation, enthalpy, potential, translatedDisplacement, bernoulliConstant,
translatedResidual
);
/*
@@ -1008,43 +824,29 @@ TEST_CASE(
* center fixed. This must not agree with the covariant result.
*/
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, baseRotation, enthalpy, potential,
translatedDisplacement, bernoulliConstant, untranslatedCenterResidual
f, *f.domainMapperStateless, baseRotation, enthalpy, potential, translatedDisplacement, bernoulliConstant,
untranslatedCenterResidual
);
const MPI_Comm communicator = f.mesh->GetComm();
const MPI_Comm communicator = f.mesh->GetComm();
const double baseResidualNorm =
gravity_prepared_test_utils::global_norm(baseResidual, communicator);
const double baseResidualNorm = gravity_prepared_test_utils::global_norm(baseResidual, communicator);
const double translatedResidualNorm =
gravity_prepared_test_utils::global_norm(
translatedResidual, communicator
);
const double translatedResidualNorm = gravity_prepared_test_utils::global_norm(translatedResidual, communicator);
const double translationInvarianceError =
gravity_prepared_test_utils::relative_error(
translatedResidual, baseResidual, communicator
);
gravity_prepared_test_utils::relative_error(translatedResidual, baseResidual, communicator);
const double fixedCenterDifference =
gravity_prepared_test_utils::relative_error(
untranslatedCenterResidual, translatedResidual, communicator
);
gravity_prepared_test_utils::relative_error(untranslatedCenterResidual, translatedResidual, communicator);
INFO("Base deformed hydrostatic residual norm = " << baseResidualNorm);
INFO("Translated hydrostatic residual norm = " << translatedResidualNorm);
INFO(
"Mapped-rotation translation invariance error = "
<< translationInvarianceError
);
INFO("Mapped-rotation translation invariance error = " << translationInvarianceError);
INFO(
"Relative change with untranslated rotation center = "
<< fixedCenterDifference
);
INFO("Relative change with untranslated rotation center = " << fixedCenterDifference);
REQUIRE(baseResidualNorm > 1.0e-12);
REQUIRE(translatedResidualNorm > 1.0e-12);

View File

@@ -1,6 +1,6 @@
#include <array>
#include <cmath>
#include <limits>
#include <array>
#include <catch2/catch_test_macros.hpp>
@@ -19,44 +19,34 @@ namespace pressure_force_kernel_test_utils {
for (int index = 0; index < size; ++index) {
const double position = static_cast<double>(index + 1);
vector(index) = 0.71 + 0.19 * std::sin(0.31 * position + phase) +
0.08 * std::cos(0.17 * position - 0.5 * phase);
vector(index) =
0.71 + 0.19 * std::sin(0.31 * position + phase) + 0.08 * std::cos(0.17 * position - 0.5 * phase);
}
return vector;
}
[[nodiscard]] mfem::Vector
make_zero_displacement(const mean_field::fem::FEM &f) {
[[nodiscard]] mfem::Vector make_zero_displacement(const mean_field::fem::FEM &f) {
mfem::Vector displacementTrue(f.displacementFes->GetTrueVSize());
displacementTrue = 0.0;
return displacementTrue;
}
[[nodiscard]] mfem::Vector
make_vacuum_only_enthalpy(const mean_field::fem::FEM &f) {
mfem::Vector enthalpyTrue =
make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), 0.43);
[[nodiscard]] mfem::Vector make_vacuum_only_enthalpy(const mean_field::fem::FEM &f) {
mfem::Vector enthalpyTrue = make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), 0.43);
mfem::Array<int> stellarElementMask;
mean_field::utils::populate_element_mask(
f.mesh.get(), mean_field::utils::DOMAINS::STELLAR,
stellarElementMask
);
mean_field::utils::populate_element_mask(f.mesh.get(), mean_field::utils::DOMAINS::STELLAR, stellarElementMask);
mfem::Array<int> stellarEnthalpyTrueDofs;
mean_field::utils::populate_domain_tdofs(
f.enthalpyFes.get(), stellarElementMask, stellarEnthalpyTrueDofs
);
mean_field::utils::populate_domain_tdofs(f.enthalpyFes.get(), stellarElementMask, stellarEnthalpyTrueDofs);
for (int listIndex = 0; listIndex < stellarEnthalpyTrueDofs.Size();
++listIndex) {
for (int listIndex = 0; listIndex < stellarEnthalpyTrueDofs.Size(); ++listIndex) {
const int trueDof = stellarEnthalpyTrueDofs[listIndex];
MFEM_VERIFY(
trueDof >= 0 && trueDof < enthalpyTrue.Size(),
"The stellar enthalpy true-DOF mask contains an "
"invalid index."
trueDof >= 0 && trueDof < enthalpyTrue.Size(), "The stellar enthalpy true-DOF mask contains an "
"invalid index."
);
enthalpyTrue(trueDof) = 0.0;
@@ -65,11 +55,9 @@ namespace pressure_force_kernel_test_utils {
return enthalpyTrue;
}
[[nodiscard]] mfem::Vector
make_positive_asymmetric_enthalpy(const mean_field::fem::FEM &f) {
[[nodiscard]] mfem::Vector make_positive_asymmetric_enthalpy(const mean_field::fem::FEM &f) {
mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) {
return 1.10 + 0.07 * position(0) - 0.04 * position(1) +
0.03 * position(2);
return 1.10 + 0.07 * position(0) - 0.04 * position(1) + 0.03 * position(2);
});
mfem::ParGridFunction enthalpyField(f.enthalpyFes.get());
@@ -89,15 +77,9 @@ namespace pressure_force_kernel_test_utils {
) {
const int dimension = f.mesh->Dimension();
MFEM_VERIFY(
component >= 0 && component < dimension,
"The requested vector component is invalid."
);
MFEM_VERIFY(component >= 0 && component < dimension, "The requested vector component is invalid.");
MFEM_VERIFY(
coordinate >= -1 && coordinate < dimension,
"The requested coordinate is invalid."
);
MFEM_VERIFY(coordinate >= -1 && coordinate < dimension, "The requested coordinate is invalid.");
/*
* coordinate == -1 gives the rigid translation e_component.
@@ -107,9 +89,7 @@ namespace pressure_force_kernel_test_utils {
* w = x_coordinate e_component.
*/
mfem::VectorFunctionCoefficient coefficient(
dimension,
[component, coordinate,
dimension](const mfem::Vector &position, mfem::Vector &value) {
dimension, [component, coordinate, dimension](const mfem::Vector &position, mfem::Vector &value) {
value.SetSize(dimension);
value = 0.0;
@@ -132,20 +112,192 @@ namespace pressure_force_kernel_test_utils {
const mfem::Vector &right,
MPI_Comm communicator
) {
MFEM_VERIFY(
left.Size() == right.Size(),
"The global dot-product vectors have different sizes."
);
MFEM_VERIFY(left.Size() == right.Size(), "The global dot-product vectors have different sizes.");
const double localDot = left * right;
double globalDot = 0.0;
MPI_Allreduce(
&localDot, &globalDot, 1, MPI_DOUBLE, MPI_SUM, communicator
);
MPI_Allreduce(&localDot, &globalDot, 1, MPI_DOUBLE, MPI_SUM, communicator);
return globalDot;
}
[[nodiscard]] double integrate_pressure(
const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domainMapper,
const mean_field::eos::Polytrope &barotrope,
const mfem::Vector &enthalpyTrue,
const mfem::Vector &displacementTrue
) {
MFEM_VERIFY(
enthalpyTrue.Size() == f.enthalpyFes->GetTrueVSize(),
"The pressure-integral enthalpy vector has the wrong size."
);
MFEM_VERIFY(
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
"The pressure-integral displacement vector has the wrong size."
);
mfem::Vector enthalpyLocal(f.enthalpyFes->GetVSize());
const mfem::Operator *enthalpyProlongation = f.enthalpyFes->GetProlongationMatrix();
if (enthalpyProlongation != nullptr) {
enthalpyProlongation->Mult(enthalpyTrue, enthalpyLocal);
} else {
enthalpyLocal = enthalpyTrue;
}
mfem::Vector displacementLocal(f.displacementFes->GetVSize());
const mfem::Operator *displacementProlongation = f.displacementFes->GetProlongationMatrix();
if (displacementProlongation != nullptr) {
displacementProlongation->Mult(displacementTrue, displacementLocal);
} else {
displacementLocal = displacementTrue;
}
const double pressureExtraOrderValue =
barotrope.polytropic_index() * static_cast<double>(mean_field::field::Enthalpy::Scalar::familyOrder);
MFEM_VERIFY(
std::isfinite(pressureExtraOrderValue) && pressureExtraOrderValue >= 0.0 &&
pressureExtraOrderValue <= static_cast<double>(std::numeric_limits<int>::max()),
"The pressure-integral EOS order is invalid."
);
const int pressureExtraOrder = static_cast<int>(std::ceil(pressureExtraOrderValue));
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
mean_field::mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension());
mean_field::mapping::VolumeMappingContext mappingContext;
mfem::Array<int> enthalpyDofs;
mfem::Array<int> displacementDofs;
mfem::Array<int> compactificationDofs;
mfem::Vector elementEnthalpy;
mfem::Vector elementDisplacement;
mfem::Vector elementCompactification;
mfem::Vector enthalpyShape;
double localPressureIntegral = 0.0;
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
MFEM_VERIFY(
transformation != nullptr, "The pressure-integral reference received a null "
"element transformation."
);
if (transformation->Attribute == vacuumAttribute) {
continue;
}
const mfem::FiniteElement &enthalpyElement = *f.enthalpyFes->GetFE(elementId);
const mfem::FiniteElement &displacementElement = *f.displacementFes->GetFE(elementId);
const mfem::FiniteElement &compactificationElement = *f.compactificationFes->GetFE(elementId);
mfem::DofTransformation *enthalpyDofTransformation = f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
mfem::DofTransformation *displacementDofTransformation =
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
mfem::DofTransformation *compactificationDofTransformation =
f.compactificationFes->GetElementDofs(elementId, compactificationDofs);
enthalpyLocal.GetSubVector(enthalpyDofs, elementEnthalpy);
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
if (enthalpyDofTransformation != nullptr) {
enthalpyDofTransformation->InvTransformPrimal(elementEnthalpy);
}
if (displacementDofTransformation != nullptr) {
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
}
if (compactificationDofTransformation != nullptr) {
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
}
const mean_field::mapping::ElementDisplacementData displacementData =
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
const mean_field::mapping::ElementCompactificationData compactificationData(
compactificationElement, elementCompactification
);
const mean_field::mapping::ElementMappingData mappingData{
.displacement = displacementData, .compactification = compactificationData
};
const mean_field::quadrature::Query query =
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureIntegral>(
mean_field::quadrature::QuadratureRole::diagnostic, transformation->OrderW(),
std::array<int, 1>{pressureExtraOrder}, mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general
);
const mean_field::quadrature::MfemRule rule =
f.quadratureFactory->get(query, transformation->GetGeometryType());
MFEM_VERIFY(rule.integration_rule != nullptr, "The pressure-integral quadrature rule is null.");
enthalpyShape.SetSize(enthalpyElement.GetDof());
for (int quadratureIndex = 0; quadratureIndex < rule.integration_rule->GetNPoints(); ++quadratureIndex) {
const mfem::IntegrationPoint &integrationPoint = rule.integration_rule->IntPoint(quadratureIndex);
transformation->SetIntPoint(&integrationPoint);
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume(
mappingData, *transformation, integrationPoint, workspace, mappingContext
);
MFEM_VERIFY(
mappingStatus == mean_field::mapping::MappingStatus::valid,
"Stateless mapping failed in the "
"independent pressure integral. Element: "
<< elementId << ", attribute: " << transformation->Attribute
<< ", quadrature point: " << quadratureIndex << ", status: " << static_cast<int>(mappingStatus)
);
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
const double enthalpyValue = elementEnthalpy * enthalpyShape;
const double pressureValue = barotrope.pressure_from_enthalpy(enthalpyValue);
const double contribution = pressureValue * mappingContext.quadrature.weight;
MFEM_VERIFY(
std::isfinite(pressureValue) && std::isfinite(contribution), "The independent pressure integral "
"encountered a non-finite value."
);
localPressureIntegral += contribution;
}
}
double globalPressureIntegral = 0.0;
MPI_Allreduce(&localPressureIntegral, &globalPressureIntegral, 1, MPI_DOUBLE, MPI_SUM, f.mesh->GetComm());
return globalPressureIntegral;
}
} // namespace pressure_force_kernel_test_utils
TEST_CASE(
@@ -154,31 +306,26 @@ TEST_CASE(
) {
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());
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 0.25);
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
mfem::Vector enthalpyTrue(f.enthalpyFes->GetTrueVSize());
enthalpyTrue = 0.0;
enthalpyTrue = 0.0;
const mfem::Vector displacementTrue =
pressure_force_kernel_test_utils::make_zero_displacement(f);
const mfem::Vector displacementTrue = pressure_force_kernel_test_utils::make_zero_displacement(f);
mfem::Vector residualTrue;
mean_field::operators::kernels::apply_pressure_force_residual(
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue,
residualTrue
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue, residualTrue
);
REQUIRE(residualTrue.Size() == f.displacementFes->GetTrueVSize());
const double residualNorm = gravity_prepared_test_utils::global_norm(
residualTrue, f.mesh->GetComm()
);
const double residualNorm = gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm());
CHECK(residualNorm == 0.0);
}
@@ -189,19 +336,15 @@ TEST_CASE(
) {
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());
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 0.25);
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
const mfem::Vector enthalpyTrue =
pressure_force_kernel_test_utils::make_vacuum_only_enthalpy(f);
const mfem::Vector enthalpyTrue = pressure_force_kernel_test_utils::make_vacuum_only_enthalpy(f);
const double enthalpyNorm = gravity_prepared_test_utils::global_norm(
enthalpyTrue, f.mesh->GetComm()
);
const double enthalpyNorm = gravity_prepared_test_utils::global_norm(enthalpyTrue, f.mesh->GetComm());
/*
* Ensure this is a real exclusion test rather than another
@@ -209,21 +352,17 @@ TEST_CASE(
*/
REQUIRE(enthalpyNorm > 0.0);
const mfem::Vector displacementTrue =
pressure_force_kernel_test_utils::make_zero_displacement(f);
const mfem::Vector displacementTrue = pressure_force_kernel_test_utils::make_zero_displacement(f);
mfem::Vector residualTrue;
mean_field::operators::kernels::apply_pressure_force_residual(
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue,
residualTrue
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue, residualTrue
);
REQUIRE(residualTrue.Size() == f.displacementFes->GetTrueVSize());
const double residualNorm = gravity_prepared_test_utils::global_norm(
residualTrue, f.mesh->GetComm()
);
const double residualNorm = gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm());
CHECK(residualNorm == 0.0);
}
@@ -234,12 +373,11 @@ TEST_CASE(
) {
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());
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 0.25);
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
/*
* With n = 3 and K = 1/4:
@@ -247,21 +385,17 @@ TEST_CASE(
* P(1) = 1/4.
*/
mfem::Vector enthalpyTrue(f.enthalpyFes->GetTrueVSize());
enthalpyTrue = 1.0;
enthalpyTrue = 1.0;
const mfem::Vector displacementTrue =
pressure_force_kernel_test_utils::make_zero_displacement(f);
const mfem::Vector displacementTrue = pressure_force_kernel_test_utils::make_zero_displacement(f);
mfem::Vector residualTrue;
mean_field::operators::kernels::apply_pressure_force_residual(
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue,
residualTrue
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue, residualTrue
);
const double residualNorm = gravity_prepared_test_utils::global_norm(
residualTrue, f.mesh->GetComm()
);
const double residualNorm = gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm());
INFO("Positive-pressure residual norm = " << residualNorm);
@@ -272,36 +406,29 @@ TEST_CASE(
TEST_CASE(
"Pressure Force Residual Does No Work Against Rigid Translations",
tags::barotrope &tags::pressure &tags::kernels &tags::integration
&tags::accuracy
tags::barotrope &tags::pressure &tags::kernels &tags::integration &tags::accuracy
) {
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.displacementFes->GetOrdering() == mfem::Ordering::byNODES);
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 0.25);
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
const mfem::Vector enthalpyTrue =
pressure_force_kernel_test_utils::make_positive_asymmetric_enthalpy(f);
const mfem::Vector enthalpyTrue = pressure_force_kernel_test_utils::make_positive_asymmetric_enthalpy(f);
const mfem::Vector displacementTrue =
pressure_force_kernel_test_utils::make_zero_displacement(f);
const mfem::Vector displacementTrue = pressure_force_kernel_test_utils::make_zero_displacement(f);
mfem::Vector residualTrue;
mean_field::operators::kernels::apply_pressure_force_residual(
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue,
residualTrue
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue, residualTrue
);
const double residualNorm = gravity_prepared_test_utils::global_norm(
residualTrue, f.mesh->GetComm()
);
const double residualNorm = gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm());
REQUIRE(residualNorm > 0.0);
@@ -309,21 +436,14 @@ TEST_CASE(
for (int component = 0; component < dimension; ++component) {
const mfem::Vector translationTrue =
pressure_force_kernel_test_utils::make_component_test_field(
f, component, -1
);
pressure_force_kernel_test_utils::make_component_test_field(f, component, -1);
const double translationNorm = gravity_prepared_test_utils::global_norm(
translationTrue, f.mesh->GetComm()
);
const double translationNorm = gravity_prepared_test_utils::global_norm(translationTrue, f.mesh->GetComm());
const double translationWork =
pressure_force_kernel_test_utils::global_dot(
translationTrue, residualTrue, f.mesh->GetComm()
);
pressure_force_kernel_test_utils::global_dot(translationTrue, residualTrue, f.mesh->GetComm());
const double dotProductScale =
std::fmax(residualNorm * translationNorm, 1.0);
const double dotProductScale = std::fmax(residualNorm * translationNorm, 1.0);
CAPTURE(component, translationWork, dotProductScale);
@@ -332,32 +452,27 @@ TEST_CASE(
}
TEST_CASE(
"Pressure Force Residual Respects byNODES Component Layout",
tags::barotrope &tags::pressure &tags::kernels &tags::integration
&tags::accuracy
"Pressure Force Residual Matches Independent Pressure Integral",
tags::barotrope &tags::pressure &tags::kernels &tags::integration &tags::accuracy
) {
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.displacementFes->GetOrdering() == mfem::Ordering::byNODES);
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 0.25);
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
const mfem::Vector enthalpyTrue =
pressure_force_kernel_test_utils::make_positive_asymmetric_enthalpy(f);
const mfem::Vector enthalpyTrue = pressure_force_kernel_test_utils::make_positive_asymmetric_enthalpy(f);
const mfem::Vector displacementTrue =
pressure_force_kernel_test_utils::make_zero_displacement(f);
const mfem::Vector displacementTrue = pressure_force_kernel_test_utils::make_zero_displacement(f);
mfem::Vector residualTrue;
mean_field::operators::kernels::apply_pressure_force_residual(
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue,
residualTrue
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue, residualTrue
);
const int dimension = f.mesh->Dimension();
@@ -369,17 +484,21 @@ TEST_CASE(
for (int component = 0; component < dimension; ++component) {
for (int coordinate = 0; coordinate < dimension; ++coordinate) {
const mfem::Vector affineTestTrue =
pressure_force_kernel_test_utils::make_component_test_field(
f, component, coordinate
);
pressure_force_kernel_test_utils::make_component_test_field(f, component, coordinate);
virtualWork(component, coordinate) =
pressure_force_kernel_test_utils::global_dot(
affineTestTrue, residualTrue, f.mesh->GetComm()
);
pressure_force_kernel_test_utils::global_dot(affineTestTrue, residualTrue, f.mesh->GetComm());
}
}
const double pressureIntegral = pressure_force_kernel_test_utils::integrate_pressure(
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue
);
REQUIRE(std::isfinite(pressureIntegral));
REQUIRE(pressureIntegral > 100.0 * std::numeric_limits<double>::epsilon());
double meanDiagonalWork = 0.0;
for (int component = 0; component < dimension; ++component) {
@@ -388,37 +507,173 @@ TEST_CASE(
meanDiagonalWork /= static_cast<double>(dimension);
// INFO(
// "Affine pressure virtual-work tensor:\n"
// << virtualWork
// );
const double comparisonTolerance = 1.0e-6 * std::abs(pressureIntegral);
INFO("Independent pressure integral = " << pressureIntegral);
INFO("Expected diagonal virtual work = " << -pressureIntegral);
INFO("Mean diagonal virtual work = " << meanDiagonalWork);
REQUIRE(
std::abs(meanDiagonalWork) >
100.0 * std::numeric_limits<double>::epsilon()
);
INFO("Comparison tolerance = " << comparisonTolerance);
const double comparisonTolerance = 1.0e-8 * std::abs(meanDiagonalWork);
/*
* This separate mean check gives a compact diagnostic if all three
* diagonal components drift together.
*/
CHECK(std::abs(meanDiagonalWork + pressureIntegral) <= comparisonTolerance);
for (int component = 0; component < dimension; ++component) {
for (int coordinate = 0; coordinate < dimension; ++coordinate) {
const double computedWork = virtualWork(component, coordinate);
CAPTURE(
component, coordinate, computedWork, meanDiagonalWork,
comparisonTolerance
);
const double expectedWork = component == coordinate ? -pressureIntegral : 0.0;
if (component == coordinate) {
CHECK(
std::abs(computedWork - meanDiagonalWork) <=
comparisonTolerance
);
} else {
CHECK(std::abs(computedWork) <= comparisonTolerance);
}
CAPTURE(component, coordinate, computedWork, expectedWork, pressureIntegral, comparisonTolerance);
CHECK(std::abs(computedWork - expectedWork) <= comparisonTolerance);
}
}
}
const double relativeMeanError = std::abs(meanDiagonalWork + pressureIntegral) / std::abs(pressureIntegral);
INFO("Relative mean diagonal error = " << relativeMeanError);
CHECK(relativeMeanError <= 1.0e-6);
}
TEST_CASE(
"Pressure Force Residual Matches Deformed Pressure Volume Variation",
tags::barotrope &tags::pressure &tags::kernels &tags::integration &tags::accuracy
) {
mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
REQUIRE(f.okay());
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
/*
* This field is positive but spatially nonuniform, so the test
* exercises a genuinely nonuniform pressure distribution.
*/
const mfem::Vector enthalpyTrue =
pressure_force_kernel_test_utils::make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), 0.37);
/*
* make_displacement() contains anisotropic diagonal terms and
* quadratic cross terms. A scale of 0.67 therefore provides a
* nonzero, nonspherical, valid base geometry.
*/
const mfem::Vector baseDisplacementTrue = gravity_prepared_test_utils::make_displacement(f, 0.67);
/*
* Differentiate along the same smooth deformation family. Thus
*
* d(epsilon) = (0.67 + epsilon) d_shape.
*
* This gives a controlled geometry path while still evaluating
* the derivative at a genuinely deformed base state.
*/
const mfem::Vector displacementVariationTrue = gravity_prepared_test_utils::make_displacement(f, 1.0);
const double baseDisplacementNorm =
gravity_prepared_test_utils::global_norm(baseDisplacementTrue, f.mesh->GetComm());
const double variationNorm = gravity_prepared_test_utils::global_norm(displacementVariationTrue, f.mesh->GetComm());
REQUIRE(baseDisplacementNorm > 100.0 * std::numeric_limits<double>::epsilon());
REQUIRE(variationNorm > 100.0 * std::numeric_limits<double>::epsilon());
mfem::Vector residualTrue;
mean_field::operators::kernels::apply_pressure_force_residual(
f, *f.domainMapperStateless, barotrope, enthalpyTrue, baseDisplacementTrue, residualTrue
);
REQUIRE(residualTrue.Size() == f.displacementFes->GetTrueVSize());
const double residualWork =
pressure_force_kernel_test_utils::global_dot(displacementVariationTrue, residualTrue, f.mesh->GetComm());
REQUIRE(std::isfinite(residualWork));
REQUIRE(std::abs(residualWork) > 100.0 * std::numeric_limits<double>::epsilon());
/*
* The relatively broad initial sweep lets us see the expected
* centered-difference convergence before reaching the quadrature
* and representation plateau.
*/
constexpr std::array<double, 4> differenceSteps{1.0e-2, 5.0e-3, 2.5e-3, 1.25e-3};
double bestRelativeDiscrepancy = std::numeric_limits<double>::infinity();
for (const double differenceStep : differenceSteps) {
mfem::Vector displacementPlus(baseDisplacementTrue);
mfem::Vector displacementMinus(baseDisplacementTrue);
displacementPlus.Add(differenceStep, displacementVariationTrue);
displacementMinus.Add(-differenceStep, displacementVariationTrue);
const double pressureIntegralPlus = pressure_force_kernel_test_utils::integrate_pressure(
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementPlus
);
const double pressureIntegralMinus = pressure_force_kernel_test_utils::integrate_pressure(
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementMinus
);
REQUIRE(std::isfinite(pressureIntegralPlus));
REQUIRE(std::isfinite(pressureIntegralMinus));
const double pressureVolumeDerivative = (pressureIntegralPlus - pressureIntegralMinus) / (2.0 * differenceStep);
REQUIRE(std::isfinite(pressureVolumeDerivative));
double comparisonScale = std::abs(residualWork);
if (std::abs(pressureVolumeDerivative) > comparisonScale) {
comparisonScale = std::abs(pressureVolumeDerivative);
}
REQUIRE(comparisonScale > 100.0 * std::numeric_limits<double>::epsilon());
const double absoluteDiscrepancy = std::abs(residualWork + pressureVolumeDerivative);
const double relativeDiscrepancy = absoluteDiscrepancy / comparisonScale;
if (relativeDiscrepancy < bestRelativeDiscrepancy) {
bestRelativeDiscrepancy = relativeDiscrepancy;
}
INFO("Difference step = " << differenceStep);
INFO("Pressure residual work = " << residualWork);
INFO("Pressure-volume derivative = " << pressureVolumeDerivative);
INFO("Residual work plus derivative = " << residualWork + pressureVolumeDerivative);
INFO("Relative discrepancy = " << relativeDiscrepancy);
/*
* The signs must be opposite because the implemented pressure
* force is the negative variation of the pressure-volume
* functional.
*/
CHECK(residualWork * pressureVolumeDerivative < 0.0);
}
INFO("Best pressure-volume relative discrepancy = " << bestRelativeDiscrepancy);
/*
* This is intentionally a provisional but meaningful threshold.
* We will tighten it after measuring the convergence plateau.
*/
CHECK(bestRelativeDiscrepancy < 1.0e-8);
}