Files
MeanField/tests/operators/prepared_barotropic_closure.cpp

995 lines
33 KiB
C++

#include <algorithm>
#include <array>
#include <catch2/catch_test_macros.hpp>
#include <cstddef>
#include <cstdint>
#include <mfem.hpp>
import mean_field;
import test_helpers;
namespace {
mfem::Vector project_scalar(
mfem::ParFiniteElementSpace &finiteElementSpace,
mfem::Coefficient &coefficient
) {
mfem::ParGridFunction field(&finiteElementSpace);
field.ProjectCoefficient(coefficient);
mfem::Vector trueVector;
field.GetTrueDofs(trueVector);
return trueVector;
}
mfem::Vector make_base_density(const mean_field::fem::FEM &f) {
mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) {
return 0.42 + 0.025 * position(0) - 0.012 * position(1) +
0.007 * position(2);
});
return project_scalar(*f.densityFes, coefficient);
}
mfem::Vector make_base_enthalpy(const mean_field::fem::FEM &f) {
mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) {
return 0.92 + 0.018 * position(0) - 0.011 * position(1) +
0.006 * position(2);
});
return project_scalar(*f.enthalpyFes, coefficient);
}
mfem::Vector make_enthalpy_variation(const mean_field::fem::FEM &f) {
mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) {
return 0.065 + 0.014 * position(0) + 0.009 * position(2);
});
return project_scalar(*f.enthalpyFes, coefficient);
}
mfem::Vector make_combined_variation(
const mfem::Vector &densityVariation,
const mfem::Vector &enthalpyVariation
) {
mfem::Vector combinedVariation(
densityVariation.Size() + enthalpyVariation.Size()
);
for (int densityDof = 0; densityDof < densityVariation.Size();
++densityDof) {
combinedVariation(densityDof) = densityVariation(densityDof);
}
for (int enthalpyDof = 0; enthalpyDof < enthalpyVariation.Size();
++enthalpyDof) {
combinedVariation(densityVariation.Size() + enthalpyDof) =
enthalpyVariation(enthalpyDof);
}
return combinedVariation;
}
struct ClosureCondition {
const char *name;
double polytropicIndex;
double polytropicConstant;
double enthalpyOffset;
double enthalpyGradient;
double densityFactor;
double densityOffset;
double densityGradient;
double deformationScale;
double directionPhase;
};
inline constexpr std::array<ClosureCondition, 3> conditions{
{{.name = "Linear barotrope on identity geometry",
.polytropicIndex = 1.0,
.polytropicConstant = 0.8,
.enthalpyOffset = 0.65,
.enthalpyGradient = 0.06,
.densityFactor = 0.80,
.densityOffset = 0.015,
.densityGradient = 0.004,
.deformationScale = 0.0,
.directionPhase = 0.31},
{.name = "Fractional barotrope on moderate deformation",
.polytropicIndex = 1.5,
.polytropicConstant = 1.2,
.enthalpyOffset = 0.90,
.enthalpyGradient = 0.09,
.densityFactor = 1.15,
.densityOffset = -0.003,
.densityGradient = 0.003,
.deformationScale = 0.45,
.directionPhase = 0.53},
{.name = "Target n=3 barotrope on strong deformation",
.polytropicIndex = 3.0,
.polytropicConstant = 1.5,
.enthalpyOffset = 1.20,
.enthalpyGradient = 0.12,
.densityFactor = 1.40,
.densityOffset = 0.006,
.densityGradient = 0.002,
.deformationScale = 1.0,
.directionPhase = 0.79}}
};
double evaluate_enthalpy(
const mfem::Vector &position,
const ClosureCondition &condition
) {
return condition.enthalpyOffset +
condition.enthalpyGradient *
(0.50 * position(0) - 0.30 * position(1) +
0.20 * position(2));
}
mfem::Vector project_scalar_field(
mfem::ParFiniteElementSpace &finiteElementSpace,
mfem::Coefficient &coefficient
) {
mfem::ParGridFunction field(&finiteElementSpace);
field.ProjectCoefficient(coefficient);
mfem::Vector trueVector;
field.GetTrueDofs(trueVector);
return trueVector;
}
mfem::Vector make_enthalpy(
const mean_field::fem::FEM &f,
const ClosureCondition &condition
) {
mfem::FunctionCoefficient coefficient(
[condition](const mfem::Vector &position) {
return evaluate_enthalpy(position, condition);
}
);
return project_scalar_field(*f.enthalpyFes, coefficient);
}
mfem::Vector make_density(
const mean_field::fem::FEM &f,
const mean_field::physics::PolytropicBarotrope &barotrope,
const ClosureCondition &condition
) {
mfem::FunctionCoefficient coefficient(
[&barotrope, condition](const mfem::Vector &position) {
const double enthalpy = evaluate_enthalpy(position, condition);
return condition.densityFactor *
barotrope.density_from_enthalpy(enthalpy) +
condition.densityOffset +
condition.densityGradient *
(0.40 * position(0) + 0.25 * position(1) -
0.15 * position(2));
}
);
return project_scalar_field(*f.densityFes, coefficient);
}
mfem::Vector make_constant_field(
mfem::ParFiniteElementSpace &finiteElementSpace,
const double value
) {
mfem::ConstantCoefficient coefficient(value);
return project_scalar_field(finiteElementSpace, coefficient);
}
} // namespace
TEST_CASE(
"Prepared Barotropic Closure Matches Stateless Kernels",
tags::hydro &tags::prepared &tags::unit
) {
auto args = test_utils::setup_args();
mean_field::fem::FEM f =
mean_field::fem::setup_fem(args.mesh_file, args, 0);
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
mean_field::operators::PreparedBarotropicClosureOperator preparedOperator(
f, *f.domainMapperStateless, barotrope
);
REQUIRE_FALSE(preparedOperator.IsPrepared());
REQUIRE(preparedOperator.Height() == f.densityFes->GetTrueVSize());
REQUIRE(
preparedOperator.Width() ==
f.densityFes->GetTrueVSize() + f.enthalpyFes->GetTrueVSize()
);
REQUIRE(preparedOperator.GetDensitySize() == f.densityFes->GetTrueVSize());
REQUIRE(
preparedOperator.GetEnthalpySize() == f.enthalpyFes->GetTrueVSize()
);
const mfem::Vector baseDensity = make_base_density(f);
const mfem::Vector baseEnthalpy = make_base_enthalpy(f);
const mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(f, 1.0);
const mfem::Vector densityVariation =
gravity_prepared_test_utils::make_deterministic_vector(
f.densityFes->GetTrueVSize(), 0.43
);
const mfem::Vector displacementVariation =
gravity_prepared_test_utils::make_displacement(f, 0.63);
const mfem::Vector enthalpyVariation = make_enthalpy_variation(f);
mfem::Vector zeroDensity(f.densityFes->GetTrueVSize());
zeroDensity = 0.0;
mfem::Vector zeroEnthalpy(f.enthalpyFes->GetTrueVSize());
zeroEnthalpy = 0.0;
preparedOperator.Prepare(baseDensity, baseEnthalpy, displacement);
mfem::Vector preparedResidual;
mfem::Vector preparedDensityAction;
mfem::Vector preparedEnthalpyAction;
mfem::Vector preparedSplitAction;
mfem::Vector preparedCombinedAction;
preparedOperator.BuildResidual(preparedResidual);
preparedOperator.Mult(
densityVariation, zeroEnthalpy, displacementVariation,
preparedDensityAction
);
preparedOperator.Mult(
zeroDensity, enthalpyVariation, displacementVariation,
preparedEnthalpyAction
);
preparedOperator.Mult(
densityVariation, enthalpyVariation, displacementVariation,
preparedSplitAction
);
const mfem::Vector combinedVariation =
make_combined_variation(densityVariation, enthalpyVariation);
preparedOperator.Mult(combinedVariation, preparedCombinedAction);
mfem::Vector referenceResidual;
mfem::Vector referenceDensityAction;
mfem::Vector referenceEnthalpyAction;
mfem::Vector referenceDisplacementAction;
mean_field::operators::kernels::apply_barotropic_closure(
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy,
displacement, referenceResidual
);
mean_field::operators::kernels::apply_barotropic_closure_density_action(
f, *f.domainMapperStateless, barotrope, densityVariation, displacement,
referenceDensityAction
);
mean_field::operators::kernels::apply_barotropic_closure_enthalpy_action(
f, *f.domainMapperStateless, barotrope, baseEnthalpy, enthalpyVariation,
displacement, referenceEnthalpyAction
);
mean_field::operators::kernels::
apply_barotropic_closure_displacement_action(
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy,
displacement, displacementVariation, referenceDisplacementAction
);
mfem::Vector referenceCombinedAction(referenceDensityAction);
referenceCombinedAction += referenceEnthalpyAction;
referenceCombinedAction += referenceDisplacementAction;
const MPI_Comm communicator = f.mesh->GetComm();
const double residualError = gravity_prepared_test_utils::relative_error(
preparedResidual, referenceResidual, communicator
);
const double densityError = gravity_prepared_test_utils::relative_error(
preparedDensityAction, referenceDensityAction, communicator
);
const double enthalpyError = gravity_prepared_test_utils::relative_error(
preparedEnthalpyAction, referenceEnthalpyAction, communicator
);
const double splitError = gravity_prepared_test_utils::relative_error(
preparedSplitAction, referenceCombinedAction, communicator
);
const double combinedError = gravity_prepared_test_utils::relative_error(
preparedCombinedAction, referenceCombinedAction, communicator
);
INFO("Prepared residual error = " << residualError);
INFO("Prepared density-action error = " << densityError);
INFO("Prepared enthalpy-action error = " << enthalpyError);
INFO("Prepared split-action error = " << splitError);
INFO("Prepared combined-action error = " << combinedError);
CHECK(preparedOperator.IsPrepared());
CHECK(preparedOperator.GetPreparationCount() == 1);
CHECK(residualError < 2.0e-12);
CHECK(densityError < 2.0e-12);
CHECK(enthalpyError < 2.0e-12);
CHECK(splitError < 2.0e-12);
CHECK(combinedError < 2.0e-12);
}
TEST_CASE(
"Prepared Barotropic Closure Jacobian Matches Centered Difference",
tags::hydro &tags::jacobian &tags::prepared &tags::unit
) {
auto args = test_utils::setup_args();
mean_field::fem::FEM f =
mean_field::fem::setup_fem(args.mesh_file, args, 0);
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
mfem::Vector baseDensity = make_base_density(f);
mfem::Vector baseEnthalpy = make_base_enthalpy(f);
const mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(f, 1.0);
const mfem::Vector densityVariation =
gravity_prepared_test_utils::make_deterministic_vector(
f.densityFes->GetTrueVSize(), 0.71
);
const mfem::Vector displacementVariation =
gravity_prepared_test_utils::make_displacement(f, 0.63);
const mfem::Vector enthalpyVariation = make_enthalpy_variation(f);
mean_field::operators::PreparedBarotropicClosureOperator preparedOperator(
f, *f.domainMapperStateless, barotrope
);
preparedOperator.Prepare(baseDensity, baseEnthalpy, displacement);
mfem::Vector analyticAction;
preparedOperator.Mult(
densityVariation, enthalpyVariation, displacementVariation,
analyticAction
);
constexpr double differenceStep = 1.0e-6;
const mfem::Vector plusDensity =
gravity_prepared_test_utils::linear_combination(
baseDensity, 1.0, densityVariation, differenceStep
);
const mfem::Vector minusDensity =
gravity_prepared_test_utils::linear_combination(
baseDensity, 1.0, densityVariation, -differenceStep
);
const mfem::Vector plusEnthalpy =
gravity_prepared_test_utils::linear_combination(
baseEnthalpy, 1.0, enthalpyVariation, differenceStep
);
const mfem::Vector minusEnthalpy =
gravity_prepared_test_utils::linear_combination(
baseEnthalpy, 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
);
mean_field::operators::kernels::apply_barotropic_closure(
f, *f.domainMapperStateless, barotrope, minusDensity, minusEnthalpy,
displacement, minusResidual
);
mfem::Vector finiteDifference(plusResidual);
finiteDifference -= minusResidual;
finiteDifference *= 1.0 / (2.0 * differenceStep);
const double relativeError = gravity_prepared_test_utils::relative_error(
analyticAction, finiteDifference, f.mesh->GetComm()
);
INFO("Prepared EOS centered-difference error = " << relativeError);
CHECK(relativeError < 2.0e-8);
}
TEST_CASE(
"Prepared Barotropic Closure Reuses Frozen Data",
tags::hydro &tags::prepared &tags::unit
) {
auto args = test_utils::setup_args();
mean_field::fem::FEM f =
mean_field::fem::setup_fem(args.mesh_file, args, 0);
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
mean_field::operators::PreparedBarotropicClosureOperator preparedOperator(
f, *f.domainMapperStateless, barotrope
);
CHECK_FALSE(preparedOperator.IsPrepared());
CHECK(preparedOperator.GetPreparationCount() == 0);
mfem::Vector baseDensity = make_base_density(f);
mfem::Vector baseEnthalpy = make_base_enthalpy(f);
mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(f, 1.0);
const mfem::Vector displacementVariation =
gravity_prepared_test_utils::make_displacement(f, 0.63);
const mfem::Vector densityVariation =
gravity_prepared_test_utils::make_deterministic_vector(
f.densityFes->GetTrueVSize(), 0.31
);
const mfem::Vector enthalpyVariation = make_enthalpy_variation(f);
preparedOperator.Prepare(baseDensity, baseEnthalpy, displacement);
REQUIRE(preparedOperator.IsPrepared());
REQUIRE(preparedOperator.GetPreparationCount() == 1);
mfem::Vector firstResidual;
mfem::Vector firstAction;
preparedOperator.BuildResidual(firstResidual);
preparedOperator.Mult(
densityVariation, enthalpyVariation, displacementVariation, firstAction
);
baseDensity = 7.0;
baseEnthalpy = 3.0;
displacement *= -4.0;
const std::uint64_t preparationCount =
preparedOperator.GetPreparationCount();
mfem::Vector repeatedResidual;
mfem::Vector repeatedAction;
preparedOperator.BuildResidual(repeatedResidual);
preparedOperator.Mult(
densityVariation, enthalpyVariation, displacementVariation,
repeatedAction
);
const MPI_Comm communicator = f.mesh->GetComm();
const double residualReuseError =
gravity_prepared_test_utils::relative_error(
repeatedResidual, firstResidual, communicator
);
const double actionReuseError = gravity_prepared_test_utils::relative_error(
repeatedAction, firstAction, communicator
);
INFO("Frozen residual reuse error = " << residualReuseError);
INFO("Frozen action reuse error = " << actionReuseError);
CHECK(residualReuseError < 2.0e-14);
CHECK(actionReuseError < 2.0e-14);
CHECK(preparedOperator.GetPreparationCount() == preparationCount);
}
TEST_CASE(
"Prepared Barotropic Closure Reprepares For New Geometry",
tags::hydro &tags::mapping &tags::prepared &tags::unit
) {
auto args = test_utils::setup_args();
mean_field::fem::FEM f =
mean_field::fem::setup_fem(args.mesh_file, args, 0);
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
const mfem::Vector baseDensity = make_base_density(f);
const mfem::Vector baseEnthalpy = make_base_enthalpy(f);
const mfem::Vector densityVariation =
gravity_prepared_test_utils::make_deterministic_vector(
f.densityFes->GetTrueVSize(), 0.59
);
mfem::Vector zeroEnthalpy(f.enthalpyFes->GetTrueVSize());
zeroEnthalpy = 0.0;
mean_field::operators::PreparedBarotropicClosureOperator preparedOperator(
f, *f.domainMapperStateless, barotrope
);
mfem::Vector identityAction;
mfem::Vector deformedAction;
for (const double deformationScale : {0.0, 1.0}) {
const mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(f, deformationScale);
const mfem::Vector displacementVariation =
gravity_prepared_test_utils::make_displacement(f, 0.63);
preparedOperator.Prepare(baseDensity, baseEnthalpy, displacement);
mfem::Vector preparedResidual;
mfem::Vector preparedAction;
mfem::Vector referenceResidual;
mfem::Vector referenceAction;
preparedOperator.BuildResidual(preparedResidual);
preparedOperator.Mult(
densityVariation, zeroEnthalpy, displacementVariation,
preparedAction
);
mean_field::operators::kernels::apply_barotropic_closure(
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy,
displacement, referenceResidual
);
mean_field::operators::kernels::apply_barotropic_closure_density_action(
f, *f.domainMapperStateless, barotrope, densityVariation,
displacement, referenceAction
);
const MPI_Comm communicator = f.mesh->GetComm();
const double residualError =
gravity_prepared_test_utils::relative_error(
preparedResidual, referenceResidual, communicator
);
const double actionError = gravity_prepared_test_utils::relative_error(
preparedAction, referenceAction, communicator
);
INFO("Deformation scale = " << deformationScale);
INFO("Reprepared residual error = " << residualError);
INFO("Reprepared action error = " << actionError);
CHECK(residualError < 2.0e-12);
CHECK(actionError < 2.0e-12);
if (deformationScale == 0.0) {
identityAction = preparedAction;
} else {
deformedAction = preparedAction;
}
}
const double geometryChange = gravity_prepared_test_utils::relative_error(
deformedAction, identityAction, f.mesh->GetComm()
);
INFO("Prepared closure geometry change = " << geometryChange);
CHECK(preparedOperator.GetPreparationCount() == 2);
CHECK(geometryChange > 1.0e-5);
}
TEST_CASE(
"Complete Barotropic Closure Matches Blocks And Centered Differences "
"Across Conditions",
tags::barotrope &tags::closure &tags::hydro &tags::integration
&tags::jacobian &tags::mapping &tags::physics &tags::prepared
) {
auto args = test_utils::setup_args();
mean_field::fem::FEM f =
mean_field::fem::setup_fem(args.mesh_file, args, 0);
REQUIRE(f.domainMapperStateless != nullptr);
const MPI_Comm communicator = f.mesh->GetComm();
constexpr double differenceStep = 1.0e-5;
for (std::size_t conditionIndex = 0; conditionIndex <
conditions.size();
++conditionIndex) {
const auto &condition =
conditions[conditionIndex];
DYNAMIC_SECTION(condition.name) {
const mean_field::physics::PolytropicBarotrope barotrope(
condition.polytropicIndex, condition.polytropicConstant
);
const mfem::Vector baseDensity =
make_density(f, barotrope, condition);
const mfem::Vector baseEnthalpy =
make_enthalpy(f, condition);
const mfem::Vector baseDisplacement =
gravity_prepared_test_utils::make_displacement(
f, condition.deformationScale
);
mfem::Vector densityVariation =
gravity_prepared_test_utils::make_deterministic_vector(
f.densityFes->GetTrueVSize(), condition.directionPhase
);
mfem::Vector enthalpyVariation =
gravity_prepared_test_utils::make_deterministic_vector(
f.enthalpyFes->GetTrueVSize(),
condition.directionPhase + 0.27
);
mfem::Vector displacementVariation =
gravity_prepared_test_utils::make_displacement(
f, condition.directionPhase
);
mfem::Vector densityAction;
mfem::Vector enthalpyAction;
mfem::Vector displacementAction;
mean_field::operators::kernels::
apply_barotropic_closure_density_action(
f, *f.domainMapperStateless, barotrope, densityVariation,
baseDisplacement, densityAction
);
mean_field::operators::kernels::
apply_barotropic_closure_enthalpy_action(
f, *f.domainMapperStateless, barotrope, baseEnthalpy,
enthalpyVariation, baseDisplacement, enthalpyAction
);
mean_field::operators::kernels::
apply_barotropic_closure_displacement_action(
f, *f.domainMapperStateless, barotrope, baseDensity,
baseEnthalpy, baseDisplacement, displacementVariation,
displacementAction
);
double densityActionNorm = gravity_prepared_test_utils::global_norm(
densityAction, communicator
);
double enthalpyActionNorm =
gravity_prepared_test_utils::global_norm(
enthalpyAction, communicator
);
double displacementActionNorm =
gravity_prepared_test_utils::global_norm(
displacementAction, communicator
);
REQUIRE(densityActionNorm > 1.0e-12);
REQUIRE(enthalpyActionNorm > 1.0e-12);
REQUIRE(displacementActionNorm > 1.0e-12);
const double targetActionNorm = std::min(
{densityActionNorm, enthalpyActionNorm, displacementActionNorm}
);
const double densityScale = targetActionNorm / densityActionNorm;
const double enthalpyScale = targetActionNorm / enthalpyActionNorm;
const double displacementScale =
targetActionNorm / displacementActionNorm;
densityVariation *= densityScale;
densityAction *= densityScale;
enthalpyVariation *= enthalpyScale;
enthalpyAction *= enthalpyScale;
displacementVariation *= displacementScale;
displacementAction *= displacementScale;
densityActionNorm = gravity_prepared_test_utils::global_norm(
densityAction, communicator
);
enthalpyActionNorm = gravity_prepared_test_utils::global_norm(
enthalpyAction, communicator
);
displacementActionNorm = gravity_prepared_test_utils::global_norm(
displacementAction, communicator
);
mean_field::operators::context::barotropic::
BarotropicClosureLinearizationContext context(
f, *f.domainMapperStateless, barotrope
);
const std::uint64_t revisionBase =
100 + static_cast<std::uint64_t>(10 * conditionIndex);
const mean_field::operators::context::barotropic::
BarotropicClosureRevisions revisions{
.density = revisionBase + 1,
.enthalpy = revisionBase + 2,
.displacement = revisionBase + 3
};
context.Prepare(
baseDensity, baseEnthalpy, baseDisplacement, revisions
);
mfem::Vector preparedResidual;
mfem::Vector referenceResidual;
context.BuildResidual(preparedResidual);
mean_field::operators::kernels::apply_barotropic_closure(
f, *f.domainMapperStateless, barotrope, baseDensity,
baseEnthalpy, baseDisplacement, referenceResidual
);
const double residualEvaluationError =
gravity_prepared_test_utils::relative_error(
preparedResidual, referenceResidual, communicator
);
mfem::Vector preparedAction;
context.GetOperator().Mult(
densityVariation, enthalpyVariation, displacementVariation,
preparedAction
);
mfem::Vector blockSum(densityAction);
blockSum += enthalpyAction;
blockSum += displacementAction;
const double blockAssemblyError =
gravity_prepared_test_utils::relative_error(
preparedAction, blockSum, communicator
);
mfem::Vector plusDensity(baseDensity);
mfem::Vector minusDensity(baseDensity);
mfem::Vector plusEnthalpy(baseEnthalpy);
mfem::Vector minusEnthalpy(baseEnthalpy);
mfem::Vector plusDisplacement(baseDisplacement);
mfem::Vector minusDisplacement(baseDisplacement);
plusDensity.Add(differenceStep, densityVariation);
minusDensity.Add(-differenceStep, densityVariation);
plusEnthalpy.Add(differenceStep, enthalpyVariation);
minusEnthalpy.Add(-differenceStep, enthalpyVariation);
plusDisplacement.Add(differenceStep, displacementVariation);
minusDisplacement.Add(-differenceStep, displacementVariation);
mfem::Vector plusResidual;
mfem::Vector minusResidual;
mean_field::operators::kernels::apply_barotropic_closure(
f, *f.domainMapperStateless, barotrope, plusDensity,
plusEnthalpy, plusDisplacement, plusResidual
);
mean_field::operators::kernels::apply_barotropic_closure(
f, *f.domainMapperStateless, barotrope, minusDensity,
minusEnthalpy, minusDisplacement, minusResidual
);
mfem::Vector finiteDifference(plusResidual);
finiteDifference -= minusResidual;
finiteDifference *= 1.0 / (2.0 * differenceStep);
mfem::Vector finiteDifferenceError(preparedAction);
finiteDifferenceError -= finiteDifference;
const double finiteDifferenceErrorNorm =
gravity_prepared_test_utils::global_norm(
finiteDifferenceError, communicator
);
const double blockNormSum =
densityActionNorm + enthalpyActionNorm + displacementActionNorm;
const double blockScaledDifferenceError =
finiteDifferenceErrorNorm / blockNormSum;
const double completeRelativeError =
gravity_prepared_test_utils::relative_error(
preparedAction, finiteDifference, communicator
);
INFO("Condition = " << condition.name);
INFO("Polytropic index = " << condition.polytropicIndex);
INFO("Deformation scale = " << condition.deformationScale);
INFO("Prepared residual error = " << residualEvaluationError);
INFO("Complete block-assembly error = " << blockAssemblyError);
INFO("Density-action norm = " << densityActionNorm);
INFO("Enthalpy-action norm = " << enthalpyActionNorm);
INFO("Displacement-action norm = " << displacementActionNorm);
INFO(
"Complete centered-difference relative error = "
<< completeRelativeError
);
INFO(
"Block-scaled centered-difference error = "
<< blockScaledDifferenceError
);
CHECK(context.GetPreparationCount() == 1);
CHECK(residualEvaluationError < 5.0e-12);
CHECK(blockAssemblyError < 5.0e-12);
CHECK(blockScaledDifferenceError < 2.0e-7);
}
}
}
TEST_CASE(
"Exact Constant Barotropic Closure Remains Zero Under Deformation",
tags::barotrope &tags::closure &tags::hydro &tags::integration
&tags::jacobian &tags::mapping &tags::physics
) {
auto args = test_utils::setup_args();
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);
constexpr double enthalpyValue = 1.20;
const double equilibriumDensityValue =
barotrope.density_from_enthalpy(enthalpyValue);
const mfem::Vector enthalpy =
make_constant_field(*f.enthalpyFes, enthalpyValue);
const mfem::Vector equilibriumDensity =
make_constant_field(*f.densityFes, equilibriumDensityValue);
const mfem::Vector referenceDensity =
make_constant_field(*f.densityFes, equilibriumDensityValue + 1.0);
const mfem::Vector displacementVariation =
gravity_prepared_test_utils::make_displacement(f, 0.67);
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
);
mfem::Vector exactResidual;
mfem::Vector referenceResidual;
mean_field::operators::kernels::apply_barotropic_closure(
f, *f.domainMapperStateless, barotrope, equilibriumDensity,
enthalpy, displacement, exactResidual
);
mean_field::operators::kernels::apply_barotropic_closure(
f, *f.domainMapperStateless, barotrope, referenceDensity,
enthalpy, displacement, referenceResidual
);
mfem::Vector exactGeometryAction;
mfem::Vector referenceGeometryAction;
mean_field::operators::kernels::
apply_barotropic_closure_displacement_action(
f, *f.domainMapperStateless, barotrope, equilibriumDensity,
enthalpy, displacement, displacementVariation,
exactGeometryAction
);
mean_field::operators::kernels::
apply_barotropic_closure_displacement_action(
f, *f.domainMapperStateless, barotrope, referenceDensity,
enthalpy, displacement, displacementVariation,
referenceGeometryAction
);
const double exactResidualNorm =
gravity_prepared_test_utils::global_norm(
exactResidual, communicator
);
const double referenceResidualNorm =
gravity_prepared_test_utils::global_norm(
referenceResidual, communicator
);
const double exactGeometryNorm =
gravity_prepared_test_utils::global_norm(
exactGeometryAction, communicator
);
const double referenceGeometryNorm =
gravity_prepared_test_utils::global_norm(
referenceGeometryAction, communicator
);
INFO("Deformation scale = " << deformationScale);
INFO("Exact-closure residual norm = " << exactResidualNorm);
INFO("Reference residual norm = " << referenceResidualNorm);
INFO("Exact-closure geometry-action norm = " << exactGeometryNorm);
INFO("Reference geometry-action norm = " << referenceGeometryNorm);
REQUIRE(referenceResidualNorm > 1.0e-12);
REQUIRE(referenceGeometryNorm > 1.0e-14);
CHECK(exactResidualNorm <= 5.0e-12 * referenceResidualNorm);
CHECK(exactGeometryNorm <= 5.0e-12 * referenceGeometryNorm);
}
}
}