#include #include #include #include #include #include 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 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(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); } } }