995 lines
33 KiB
C++
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);
|
|
}
|
|
}
|
|
} |