feat(libmeanfield): centrifugal + pressure

This commit is contained in:
2026-08-04 14:24:55 -04:00
parent 9bc4f2758a
commit dc912fd15e
115 changed files with 260058 additions and 163261 deletions

View File

@@ -0,0 +1,297 @@
#include <cstdint>
#include <catch2/catch_test_macros.hpp>
#include <mfem.hpp>
import mean_field;
import test_helpers;
namespace barotropic_closure_context_test_utils {
mfem::Vector project_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_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);
});
return project_field(*f.densityFes, coefficient);
}
mfem::Vector make_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);
});
return project_field(*f.enthalpyFes, coefficient);
}
} // namespace barotropic_closure_context_test_utils
TEST_CASE(
"Barotropic Closure Context Tracks Independent Revisions",
tags::barotrope &tags::closure &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::context::barotropic::
BarotropicClosureLinearizationContext context(
f, *f.domainMapperStateless, barotrope
);
mfem::Vector density =
barotropic_closure_context_test_utils::make_density(f);
mfem::Vector enthalpy =
barotropic_closure_context_test_utils::make_enthalpy(f);
mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(f, 0.0);
mean_field::operators::context::barotropic::BarotropicClosureRevisions
revisions{.density = 3, .enthalpy = 5, .displacement = 7};
CHECK_FALSE(context.IsPrepared());
CHECK(context.GetPreparationCount() == 0);
context.Prepare(density, enthalpy, displacement, revisions);
REQUIRE(context.IsPrepared());
CHECK(context.MatchesRevisions(revisions));
CHECK(context.GetRevisions() == revisions);
CHECK(context.GetPreparationCount() == 1);
CHECK(context.GetOperator().GetPreparationCount() == 1);
context.Prepare(density, enthalpy, displacement, revisions);
CHECK(context.GetPreparationCount() == 1);
const double frozenDensityValue = context.GetBaseDensityTrue()(0);
density(0) += 0.125;
CHECK(context.GetBaseDensityTrue()(0) == frozenDensityValue);
context.Prepare(density, enthalpy, displacement, revisions);
CHECK(context.GetPreparationCount() == 1);
CHECK(context.GetBaseDensityTrue()(0) == frozenDensityValue);
++revisions.density;
context.Prepare(density, enthalpy, displacement, revisions);
CHECK(context.GetPreparationCount() == 2);
CHECK(context.GetBaseDensityTrue()(0) == density(0));
enthalpy(0) += 0.050;
++revisions.enthalpy;
context.Prepare(density, enthalpy, displacement, revisions);
CHECK(context.GetPreparationCount() == 3);
CHECK(context.GetBaseEnthalpyTrue()(0) == enthalpy(0));
displacement = gravity_prepared_test_utils::make_displacement(f, 1.0);
++revisions.displacement;
context.Prepare(density, enthalpy, displacement, revisions);
CHECK(context.GetPreparationCount() == 4);
CHECK(context.GetRevisions() == revisions);
CHECK(context.MatchesRevisions(revisions));
CHECK(context.GetOperator().GetPreparationCount() == 4);
}
TEST_CASE(
"Barotropic Closure Context Reprepares A Consistent Frozen State",
tags::barotrope &tags::closure &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::context::barotropic::
BarotropicClosureLinearizationContext context(
f, *f.domainMapperStateless, barotrope
);
const mfem::Vector density =
barotropic_closure_context_test_utils::make_density(f);
const mfem::Vector enthalpy =
barotropic_closure_context_test_utils::make_enthalpy(f);
const mfem::Vector identityDisplacement =
gravity_prepared_test_utils::make_displacement(f, 0.0);
const mfem::Vector densityVariation =
gravity_prepared_test_utils::make_deterministic_vector(
f.densityFes->GetTrueVSize(), 0.37
);
const mfem::Vector enthalpyVariation =
gravity_prepared_test_utils::make_deterministic_vector(
f.enthalpyFes->GetTrueVSize(), 0.71
);
const mfem::Vector displacementVariation =
gravity_prepared_test_utils::make_deterministic_vector(
f.displacementFes->GetTrueVSize(), 0.37
);
mean_field::operators::context::barotropic::BarotropicClosureRevisions
revisions{.density = 11, .enthalpy = 13, .displacement = 17};
context.Prepare(density, enthalpy, identityDisplacement, revisions);
mfem::Vector initialResidual;
mfem::Vector initialAction;
context.BuildResidual(initialResidual);
context.GetOperator().Mult(
densityVariation, enthalpyVariation, displacementVariation,
initialAction
);
mfem::Vector changedDensity(density);
changedDensity.Add(0.025, densityVariation);
mfem::Vector changedEnthalpy(enthalpy);
changedEnthalpy.Add(0.015, enthalpyVariation);
const mfem::Vector deformedDisplacement =
gravity_prepared_test_utils::make_displacement(f, 1.0);
context.Prepare(
changedDensity, changedEnthalpy, deformedDisplacement, revisions
);
mfem::Vector unchangedResidual;
mfem::Vector unchangedAction;
context.BuildResidual(unchangedResidual);
context.GetOperator().Mult(
densityVariation, enthalpyVariation, displacementVariation,
unchangedAction
);
const MPI_Comm communicator = f.mesh->GetComm();
CHECK(context.GetPreparationCount() == 1);
CHECK(
gravity_prepared_test_utils::relative_error(
unchangedResidual, initialResidual, communicator
) < 5.0e-15
);
CHECK(
gravity_prepared_test_utils::relative_error(
unchangedAction, initialAction, communicator
) < 5.0e-15
);
++revisions.density;
++revisions.enthalpy;
++revisions.displacement;
context.Prepare(
changedDensity, changedEnthalpy, deformedDisplacement, revisions
);
mfem::Vector preparedResidual;
mfem::Vector preparedAction;
context.BuildResidual(preparedResidual);
context.GetOperator().Mult(
densityVariation, enthalpyVariation, displacementVariation,
preparedAction
);
mfem::Vector referenceResidual;
mfem::Vector referenceDensityAction;
mfem::Vector referenceEnthalpyAction;
mfem::Vector referenceDisplacementAction;
mean_field::operators::kernels::apply_barotropic_closure(
f, *f.domainMapperStateless, barotrope, changedDensity, changedEnthalpy,
deformedDisplacement, referenceResidual
);
mean_field::operators::kernels::apply_barotropic_closure_density_action(
f, *f.domainMapperStateless, barotrope, densityVariation,
deformedDisplacement, referenceDensityAction
);
mean_field::operators::kernels::apply_barotropic_closure_enthalpy_action(
f, *f.domainMapperStateless, barotrope, changedEnthalpy,
enthalpyVariation, deformedDisplacement, referenceEnthalpyAction
);
mean_field::operators::kernels::
apply_barotropic_closure_displacement_action(
f, *f.domainMapperStateless, barotrope, changedDensity,
changedEnthalpy, deformedDisplacement, displacementVariation,
referenceDisplacementAction
);
mfem::Vector referenceAction(referenceDensityAction);
referenceAction += referenceEnthalpyAction;
referenceAction += referenceDisplacementAction;
const double residualError = gravity_prepared_test_utils::relative_error(
preparedResidual, referenceResidual, communicator
);
const double actionError = gravity_prepared_test_utils::relative_error(
preparedAction, referenceAction, communicator
);
const double residualChange = gravity_prepared_test_utils::relative_error(
preparedResidual, initialResidual, communicator
);
const double actionChange = gravity_prepared_test_utils::relative_error(
preparedAction, initialAction, communicator
);
INFO("Prepared-context residual error = " << residualError);
INFO("Prepared-context Jacobian error = " << actionError);
INFO("Residual change after valid revision = " << residualChange);
INFO("Jacobian change after valid revision = " << actionChange);
CHECK(context.GetPreparationCount() == 2);
CHECK(residualError < 5.0e-12);
CHECK(actionError < 5.0e-12);
CHECK(residualChange > 1.0e-6);
CHECK(actionChange > 1.0e-6);
}

View File

@@ -0,0 +1,306 @@
#include <catch2/catch_test_macros.hpp>
#include <mfem.hpp>
import mean_field;
import test_helpers;
using namespace mean_field;
namespace prepared_test = gravity_prepared_test_utils;
namespace gravity_context = operators::context::gravity_field;
TEST_CASE(
"Gravity Field Linearization Context Applies Selective Invalidation",
tags::integration &tags::gravity &tags::contexts
) {
auto args = test_utils::setup_args();
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
gravity_context::GravityFieldLinearizationContext context(
f, *f.domainMapperStateless
);
mfem::Vector density = prepared_test::make_deterministic_vector(
f.densityFes->GetTrueVSize(), 0.11
);
mfem::Vector displacement = prepared_test::make_displacement(f, 0.0);
mfem::Vector gravity_gradient = prepared_test::make_deterministic_vector(
f.gravityFluxFes->GetTrueVSize(), 0.37
);
mfem::Vector gravity_potential = prepared_test::make_deterministic_vector(
f.gravityPotentialFes->GetTrueVSize(), 0.63
);
gravity_context::GravityFieldRevisions revisions;
auto make_state = [&]() {
return gravity_context::GravityFieldStateView{
.density = density,
.displacement = displacement,
.gravity_gradient = gravity_gradient,
.gravity_potential = gravity_potential
};
};
REQUIRE_FALSE(context.IsPrepared());
const gravity_context::GravityFieldPreparationReport initial_report =
context.Prepare(make_state(), revisions);
REQUIRE(context.IsPrepared());
CHECK(initial_report.geometry.reconstructed_operators);
CHECK(initial_report.geometry.rebuilt_mass_operator);
CHECK(initial_report.geometry.rebuilt_source_operator);
CHECK(initial_report.geometry.refreshed_variation_state);
CHECK(initial_report.updated_density);
CHECK(initial_report.updated_gravity_gradient);
CHECK(initial_report.DidAnyWork());
const auto initial_mass_preparations =
context.GetGeometryContext().GetMassOperator().GetPreparationCount();
const auto initial_source_preparations =
context.GetGeometryContext().GetSourceOperator().GetPreparationCount();
const gravity_context::GravityFieldPreparationReport repeated_report =
context.Prepare(make_state(), revisions);
CHECK_FALSE(repeated_report.DidAnyWork());
CHECK(
context.GetGeometryContext().GetMassOperator().GetPreparationCount() ==
initial_mass_preparations
);
CHECK(
context.GetGeometryContext()
.GetSourceOperator()
.GetPreparationCount() == initial_source_preparations
);
gravity_potential(0) += 0.25;
++revisions.gravity_potential.value;
const gravity_context::GravityFieldPreparationReport potential_report =
context.Prepare(make_state(), revisions);
CHECK_FALSE(potential_report.DidAnyWork());
CHECK(
context.GetRevisions().gravity_potential == revisions.gravity_potential
);
density(0) += 0.5;
++revisions.density.value;
const gravity_context::GravityFieldPreparationReport density_report =
context.Prepare(make_state(), revisions);
CHECK(density_report.updated_density);
CHECK_FALSE(density_report.updated_gravity_gradient);
CHECK_FALSE(density_report.geometry.DidAnyWork());
CHECK(context.GetDensity()(0) == density(0));
gravity_gradient(0) -= 0.4;
++revisions.gravity_gradient.value;
const gravity_context::GravityFieldPreparationReport gradient_report =
context.Prepare(make_state(), revisions);
CHECK_FALSE(gradient_report.updated_density);
CHECK(gradient_report.updated_gravity_gradient);
CHECK_FALSE(gradient_report.geometry.DidAnyWork());
CHECK(context.GetGravityGradient()(0) == gravity_gradient(0));
displacement = prepared_test::make_displacement(f, 1.0);
++revisions.displacement.value;
const gravity_context::GravityFieldPreparationReport displacement_report =
context.Prepare(make_state(), revisions);
CHECK_FALSE(displacement_report.geometry.reconstructed_operators);
CHECK(displacement_report.geometry.rebuilt_mass_operator);
CHECK(displacement_report.geometry.rebuilt_source_operator);
CHECK(displacement_report.geometry.refreshed_variation_state);
CHECK_FALSE(displacement_report.updated_density);
CHECK_FALSE(displacement_report.updated_gravity_gradient);
CHECK(
context.GetGeometryContext().GetMassOperator().GetPreparationCount() ==
initial_mass_preparations + 1
);
CHECK(
context.GetGeometryContext()
.GetSourceOperator()
.GetPreparationCount() == initial_source_preparations + 1
);
++revisions.discretization.value;
const gravity_context::GravityFieldPreparationReport discretization_report =
context.Prepare(make_state(), revisions);
CHECK(discretization_report.geometry.reconstructed_operators);
CHECK(discretization_report.geometry.rebuilt_mass_operator);
CHECK(discretization_report.geometry.rebuilt_source_operator);
CHECK(discretization_report.updated_density);
CHECK(discretization_report.updated_gravity_gradient);
CHECK(
context.GetGeometryContext().GetMassOperator().GetPreparationCount() ==
1
);
CHECK(
context.GetGeometryContext()
.GetSourceOperator()
.GetPreparationCount() == 1
);
}
TEST_CASE(
"Gravity Field Linearization Context Owns Frozen Base Fields",
tags::integration &tags::gravity &tags::contexts
) {
auto args = test_utils::setup_args();
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
gravity_context::GravityFieldLinearizationContext context(
f, *f.domainMapperStateless
);
mfem::Vector density = prepared_test::make_deterministic_vector(
f.densityFes->GetTrueVSize(), 0.13
);
mfem::Vector displacement = prepared_test::make_displacement(f, 0.4);
mfem::Vector gravity_gradient = prepared_test::make_deterministic_vector(
f.gravityFluxFes->GetTrueVSize(), 0.47
);
mfem::Vector gravity_potential = prepared_test::make_deterministic_vector(
f.gravityPotentialFes->GetTrueVSize(), 0.71
);
gravity_context::GravityFieldRevisions revisions;
context.Prepare(
{.density = density,
.displacement = displacement,
.gravity_gradient = gravity_gradient,
.gravity_potential = gravity_potential},
revisions
);
const mfem::Vector frozen_density = context.GetDensity();
const mfem::Vector frozen_displacement =
context.GetGeometryContext().GetDisplacement();
const mfem::Vector frozen_gravity_gradient = context.GetGravityGradient();
density = 0.0;
displacement = 0.0;
gravity_gradient = 0.0;
gravity_potential = 0.0;
CHECK(
prepared_test::relative_error(
context.GetDensity(), frozen_density, f.mesh->GetComm()
) == 0.0
);
CHECK(
prepared_test::relative_error(
context.GetGeometryContext().GetDisplacement(), frozen_displacement,
f.displacementFes->GetComm()
) == 0.0
);
CHECK(
prepared_test::relative_error(
context.GetGravityGradient(), frozen_gravity_gradient,
f.gravityFluxFes->GetComm()
) == 0.0
);
const gravity_context::GravityFieldPreparationReport
unchanged_revision_report = context.Prepare(
{.density = density,
.displacement = displacement,
.gravity_gradient = gravity_gradient,
.gravity_potential = gravity_potential},
revisions
);
CHECK_FALSE(unchanged_revision_report.DidAnyWork());
CHECK(
prepared_test::relative_error(
context.GetDensity(), frozen_density, f.mesh->GetComm()
) == 0.0
);
CHECK(
prepared_test::relative_error(
context.GetGeometryContext().GetDisplacement(), frozen_displacement,
f.displacementFes->GetComm()
) == 0.0
);
CHECK(
prepared_test::relative_error(
context.GetGravityGradient(), frozen_gravity_gradient,
f.gravityFluxFes->GetComm()
) == 0.0
);
}
TEST_CASE(
"Gravity Field Geometry Contexts Have Independent Prepared State",
tags::integration &tags::gravity &tags::contexts
) {
auto args = test_utils::setup_args();
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
gravity_context::GravityFieldGeometryContext first_context(
f, *f.domainMapperStateless
);
gravity_context::GravityFieldGeometryContext second_context(
f, *f.domainMapperStateless
);
const mfem::Vector first_displacement =
prepared_test::make_displacement(f, 0.0);
const mfem::Vector second_displacement =
prepared_test::make_displacement(f, 1.0);
const mfem::Vector gravity_gradient =
prepared_test::make_deterministic_vector(
f.gravityFluxFes->GetTrueVSize(), 0.35
);
first_context.Prepare(first_displacement, {.value = 0}, {.value = 0});
second_context.Prepare(second_displacement, {.value = 0}, {.value = 0});
mfem::Vector first_action;
mfem::Vector second_action_before;
mfem::Vector second_action_after;
first_context.GetMassOperator().Mult(gravity_gradient, first_action);
second_context.GetMassOperator().Mult(
gravity_gradient, second_action_before
);
const mfem::Vector updated_first_displacement =
prepared_test::make_displacement(f, 0.6);
first_context.Prepare(
updated_first_displacement, {.value = 0}, {.value = 1}
);
second_context.GetMassOperator().Mult(
gravity_gradient, second_action_after
);
const MPI_Comm communicator = f.gravityFluxFes->GetComm();
const double independent_context_error = prepared_test::relative_error(
second_action_after, second_action_before, communicator
);
const double distinct_geometry_difference = prepared_test::relative_error(
first_action, second_action_before, communicator
);
INFO(
"Second-context change after preparing first context = "
<< independent_context_error
);
INFO(
"Difference between independently prepared geometries = "
<< distinct_geometry_difference
);
CHECK(independent_context_error < 2.0e-14);
CHECK(distinct_geometry_difference > 1.0e-5);
}

View File

@@ -0,0 +1,382 @@
#include <catch2/catch_test_macros.hpp>
#include <mfem.hpp>
import mean_field;
import test_helpers;
namespace hydrostatic_context_test_utils {
mean_field::operators::context::hydrostatic::
HydrostaticEquilibriumDependencies
make_dependencies() {
return {
.discretization = {.identity = 101, .revision = 2},
.enthalpy = {.identity = 103, .revision = 3},
.gravityPotential = {.identity = 107, .revision = 5},
.displacement = {.identity = 109, .revision = 7},
.rotation = {.identity = 113, .revision = 11},
.bernoulliConstant = {.identity = 127, .revision = 13}
};
}
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView
make_state(
const mfem::Vector &enthalpy,
const mfem::Vector &gravityPotential,
const mfem::Vector &displacement,
const double bernoulliConstant
) {
return {
.enthalpy = enthalpy,
.gravityPotential = gravityPotential,
.displacement = displacement,
.bernoulliConstant = bernoulliConstant
};
}
void check_base_only(
const mean_field::operators::context::hydrostatic::
HydrostaticPreparationReport &report
) {
CHECK_FALSE(report.preparedStaticDependencies);
CHECK_FALSE(report.preparedGeometryState);
CHECK_FALSE(report.preparedRotationDependencies);
CHECK(report.preparedBaseState);
}
} // namespace hydrostatic_context_test_utils
TEST_CASE(
"Hydrostatic Context Applies Selective Invalidation",
tags::barotrope &tags::contexts &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);
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumContext
context(f, *f.domainMapperStateless);
mfem::Vector enthalpy =
gravity_prepared_test_utils::make_deterministic_vector(
f.enthalpyFes->GetTrueVSize(), 0.17
);
mfem::Vector gravityPotential =
gravity_prepared_test_utils::make_deterministic_vector(
f.gravityPotentialFes->GetTrueVSize(), 0.31
);
mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(f, 0.35);
double bernoulliConstant = 0.73;
mean_field::operators::context::hydrostatic::
HydrostaticEquilibriumDependencies dependencies =
hydrostatic_context_test_utils::make_dependencies();
CHECK_FALSE(context.IsPrepared());
CHECK_FALSE(context.MatchesDependencies(dependencies));
const auto initialStatistics = context.GetPreparationStatistics();
CHECK(initialStatistics.staticPreparations == 0);
CHECK(initialStatistics.geometryPreparations == 0);
CHECK(initialStatistics.rotationPreparations == 0);
CHECK(initialStatistics.baseStatePreparations == 0);
const auto initialReport = context.Prepare(
hydrostatic_context_test_utils::make_state(
enthalpy, gravityPotential, displacement, bernoulliConstant
),
dependencies
);
REQUIRE(context.IsPrepared());
CHECK(context.MatchesDependencies(dependencies));
CHECK(context.GetDependencies() == dependencies);
CHECK(initialReport.preparedStaticDependencies);
CHECK(initialReport.preparedGeometryState);
CHECK(initialReport.preparedRotationDependencies);
CHECK(initialReport.preparedBaseState);
CHECK(initialReport.updatedEnthalpy);
CHECK(initialReport.updatedGravityPotential);
CHECK(initialReport.updatedDisplacement);
CHECK(initialReport.updatedBernoulliConstant);
CHECK(initialReport.DidAnyWork());
const mfem::Vector frozenEnthalpy = context.GetBaseEnthalpyTrue();
const mfem::Vector frozenGravityPotential =
context.GetBaseGravityPotentialTrue();
const mfem::Vector frozenDisplacement = context.GetDisplacementTrue();
const double frozenBernoulliConstant = context.GetBernoulliConstant();
enthalpy(0) += 0.125;
gravityPotential(0) -= 0.075;
displacement(0) += 0.050;
bernoulliConstant += 0.20;
const auto repeatedReport = context.Prepare(
hydrostatic_context_test_utils::make_state(
enthalpy, gravityPotential, displacement, bernoulliConstant
),
dependencies
);
CHECK_FALSE(repeatedReport.DidAnyWork());
CHECK_FALSE(repeatedReport.updatedEnthalpy);
CHECK_FALSE(repeatedReport.updatedGravityPotential);
CHECK_FALSE(repeatedReport.updatedDisplacement);
CHECK_FALSE(repeatedReport.updatedBernoulliConstant);
const MPI_Comm communicator = f.mesh->GetComm();
CHECK(
gravity_prepared_test_utils::relative_error(
context.GetBaseEnthalpyTrue(), frozenEnthalpy, communicator
) == 0.0
);
CHECK(
gravity_prepared_test_utils::relative_error(
context.GetBaseGravityPotentialTrue(), frozenGravityPotential,
communicator
) == 0.0
);
CHECK(
gravity_prepared_test_utils::relative_error(
context.GetDisplacementTrue(), frozenDisplacement, communicator
) == 0.0
);
CHECK(context.GetBernoulliConstant() == frozenBernoulliConstant);
++dependencies.enthalpy.revision;
const auto enthalpyReport = context.Prepare(
hydrostatic_context_test_utils::make_state(
enthalpy, gravityPotential, displacement, bernoulliConstant
),
dependencies
);
hydrostatic_context_test_utils::check_base_only(enthalpyReport);
CHECK(enthalpyReport.updatedEnthalpy);
CHECK_FALSE(enthalpyReport.updatedGravityPotential);
CHECK_FALSE(enthalpyReport.updatedDisplacement);
CHECK_FALSE(enthalpyReport.updatedBernoulliConstant);
CHECK(context.GetBaseEnthalpyTrue()(0) == enthalpy(0));
++dependencies.gravityPotential.revision;
const auto gravityPotentialReport = context.Prepare(
hydrostatic_context_test_utils::make_state(
enthalpy, gravityPotential, displacement, bernoulliConstant
),
dependencies
);
hydrostatic_context_test_utils::check_base_only(gravityPotentialReport);
CHECK_FALSE(gravityPotentialReport.updatedEnthalpy);
CHECK(gravityPotentialReport.updatedGravityPotential);
CHECK_FALSE(gravityPotentialReport.updatedDisplacement);
CHECK_FALSE(gravityPotentialReport.updatedBernoulliConstant);
CHECK(context.GetBaseGravityPotentialTrue()(0) == gravityPotential(0));
++dependencies.bernoulliConstant.revision;
const auto bernoulliReport = context.Prepare(
hydrostatic_context_test_utils::make_state(
enthalpy, gravityPotential, displacement, bernoulliConstant
),
dependencies
);
hydrostatic_context_test_utils::check_base_only(bernoulliReport);
CHECK_FALSE(bernoulliReport.updatedEnthalpy);
CHECK_FALSE(bernoulliReport.updatedGravityPotential);
CHECK_FALSE(bernoulliReport.updatedDisplacement);
CHECK(bernoulliReport.updatedBernoulliConstant);
CHECK(context.GetBernoulliConstant() == bernoulliConstant);
++dependencies.rotation.revision;
const auto rotationReport = context.Prepare(
hydrostatic_context_test_utils::make_state(
enthalpy, gravityPotential, displacement, bernoulliConstant
),
dependencies
);
CHECK_FALSE(rotationReport.preparedStaticDependencies);
CHECK_FALSE(rotationReport.preparedGeometryState);
CHECK(rotationReport.preparedRotationDependencies);
CHECK(rotationReport.preparedBaseState);
CHECK_FALSE(rotationReport.updatedEnthalpy);
CHECK_FALSE(rotationReport.updatedGravityPotential);
CHECK_FALSE(rotationReport.updatedDisplacement);
CHECK_FALSE(rotationReport.updatedBernoulliConstant);
++dependencies.displacement.revision;
const auto displacementReport = context.Prepare(
hydrostatic_context_test_utils::make_state(
enthalpy, gravityPotential, displacement, bernoulliConstant
),
dependencies
);
CHECK_FALSE(displacementReport.preparedStaticDependencies);
CHECK(displacementReport.preparedGeometryState);
CHECK(displacementReport.preparedRotationDependencies);
CHECK(displacementReport.preparedBaseState);
CHECK_FALSE(displacementReport.updatedEnthalpy);
CHECK_FALSE(displacementReport.updatedGravityPotential);
CHECK(displacementReport.updatedDisplacement);
CHECK_FALSE(displacementReport.updatedBernoulliConstant);
CHECK(context.GetDisplacementTrue()(0) == displacement(0));
++dependencies.discretization.revision;
const auto discretizationReport = context.Prepare(
hydrostatic_context_test_utils::make_state(
enthalpy, gravityPotential, displacement, bernoulliConstant
),
dependencies
);
CHECK(discretizationReport.preparedStaticDependencies);
CHECK(discretizationReport.preparedGeometryState);
CHECK(discretizationReport.preparedRotationDependencies);
CHECK(discretizationReport.preparedBaseState);
CHECK(discretizationReport.updatedEnthalpy);
CHECK(discretizationReport.updatedGravityPotential);
CHECK(discretizationReport.updatedDisplacement);
CHECK(discretizationReport.updatedBernoulliConstant);
const auto finalStatistics = context.GetPreparationStatistics();
CHECK(finalStatistics.staticPreparations == 2);
CHECK(finalStatistics.geometryPreparations == 3);
CHECK(finalStatistics.rotationPreparations == 4);
CHECK(finalStatistics.baseStatePreparations == 7);
}
TEST_CASE(
"Hydrostatic Context Uses Identity In Every Dependency",
tags::barotrope &tags::contexts &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);
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumContext
context(f, *f.domainMapperStateless);
mfem::Vector enthalpy =
gravity_prepared_test_utils::make_deterministic_vector(
f.enthalpyFes->GetTrueVSize(), 0.23
);
const mfem::Vector gravityPotential =
gravity_prepared_test_utils::make_deterministic_vector(
f.gravityPotentialFes->GetTrueVSize(), 0.41
);
const mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(f, 0.60);
constexpr double bernoulliConstant = 0.81;
mean_field::operators::context::hydrostatic::
HydrostaticEquilibriumDependencies dependencies =
hydrostatic_context_test_utils::make_dependencies();
const auto preparedEnthalpyDependency = dependencies.enthalpy;
auto olderSameIdentity = preparedEnthalpyDependency;
--olderSameIdentity.revision;
auto resetNewIdentity = preparedEnthalpyDependency;
++resetNewIdentity.identity;
resetNewIdentity.revision = 0;
CHECK_FALSE(olderSameIdentity.CanFollow(preparedEnthalpyDependency));
CHECK(resetNewIdentity.CanFollow(preparedEnthalpyDependency));
context.Prepare(
hydrostatic_context_test_utils::make_state(
enthalpy, gravityPotential, displacement, bernoulliConstant
),
dependencies
);
const mfem::Vector firstFrozenEnthalpy = context.GetBaseEnthalpyTrue();
enthalpy(0) += 0.33;
const auto sameStampReport = context.Prepare(
hydrostatic_context_test_utils::make_state(
enthalpy, gravityPotential, displacement, bernoulliConstant
),
dependencies
);
CHECK_FALSE(sameStampReport.DidAnyWork());
CHECK(context.GetBaseEnthalpyTrue()(0) == firstFrozenEnthalpy(0));
++dependencies.enthalpy.identity;
dependencies.enthalpy.revision = 0;
const auto newIdentityReport = context.Prepare(
hydrostatic_context_test_utils::make_state(
enthalpy, gravityPotential, displacement, bernoulliConstant
),
dependencies
);
hydrostatic_context_test_utils::check_base_only(newIdentityReport);
CHECK(newIdentityReport.updatedEnthalpy);
CHECK(context.GetBaseEnthalpyTrue()(0) == enthalpy(0));
CHECK(context.MatchesDependencies(dependencies));
++dependencies.rotation.identity;
dependencies.rotation.revision = 0;
const auto newRotationIdentityReport = context.Prepare(
hydrostatic_context_test_utils::make_state(
enthalpy, gravityPotential, displacement, bernoulliConstant
),
dependencies
);
CHECK_FALSE(newRotationIdentityReport.preparedStaticDependencies);
CHECK_FALSE(newRotationIdentityReport.preparedGeometryState);
CHECK(newRotationIdentityReport.preparedRotationDependencies);
CHECK(newRotationIdentityReport.preparedBaseState);
const auto statistics = context.GetPreparationStatistics();
CHECK(statistics.staticPreparations == 1);
CHECK(statistics.geometryPreparations == 1);
CHECK(statistics.rotationPreparations == 2);
CHECK(statistics.baseStatePreparations == 3);
}