feat(field-support): added field support system, mid migration
currently the barotope and the pressure force operator are migrated to the new support system
This commit is contained in:
414
tests/field/field_base.cpp
Normal file
414
tests/field/field_base.cpp
Normal file
@@ -0,0 +1,414 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace field_base_test_utils {
|
||||
namespace field = mean_field::field;
|
||||
namespace domain = mean_field::utils::domain;
|
||||
|
||||
using IndependentL2Scalar = field::ScalarQ<field::FieldRelation::Independent, field::Disc<field::L2, 2>>;
|
||||
|
||||
using IndependentH1Scalar = field::ScalarQ<field::FieldRelation::Independent, field::Disc<field::H1, 3>>;
|
||||
|
||||
using IndependentH1Vector = field::VectorQ<field::FieldRelation::Independent, field::Disc<field::H1, 3>>;
|
||||
|
||||
using IndependentL2Vector = field::VectorQ<field::FieldRelation::Independent, field::Disc<field::L2, 2>>;
|
||||
|
||||
using Potential = field::ScalarQ<field::FieldRelation::Independent, field::Disc<field::L2, 2>>;
|
||||
|
||||
using Flux = field::VectorQ<field::FieldRelation::Gradient<Potential>, field::Disc<field::RT, 2>>;
|
||||
|
||||
using CurlSource = field::VectorQ<field::FieldRelation::Independent, field::Disc<field::ND, 2>>;
|
||||
|
||||
using CurlQuantity = field::VectorQ<field::FieldRelation::Curl<CurlSource>, field::Disc<field::ND, 2>>;
|
||||
|
||||
using SampleOperand = field::Operand<IndependentH1Scalar>;
|
||||
|
||||
using SampleGradientOperand = field::Operand<IndependentH1Scalar, field::FieldOperation::Gradient>;
|
||||
|
||||
using SampleForm = field::FormSpec<17, 2, SampleOperand, SampleGradientOperand>;
|
||||
|
||||
struct StellarSupportedObject {
|
||||
using Support = field::DomainSupport<domain::Stellar>;
|
||||
};
|
||||
|
||||
struct AllSupportedObject {
|
||||
using Support = field::DomainSupport<domain::All>;
|
||||
};
|
||||
|
||||
struct NonSpatialObject {
|
||||
using Support = field::NonSpatialSupport;
|
||||
};
|
||||
} // namespace field_base_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base Type Lists Track Compile Time Membership",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
using List = field::TypeList<int, double, char>;
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<int, List>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<double, List>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<char, List>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::typeListContains<float, List>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::typeListContains<int, field::TypeList<>>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base Function Space Tags Encode Supported Tensor Ranks",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
STATIC_REQUIRE(field::SpaceTag<field::L2>);
|
||||
|
||||
STATIC_REQUIRE(field::SpaceTag<field::H1>);
|
||||
|
||||
STATIC_REQUIRE(field::SpaceTag<field::RT>);
|
||||
|
||||
STATIC_REQUIRE(field::SpaceTag<field::ND>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::SpaceTag<int>);
|
||||
|
||||
STATIC_REQUIRE(field::spaceSupportsRank<field::L2, 0>);
|
||||
|
||||
STATIC_REQUIRE(field::spaceSupportsRank<field::L2, 1>);
|
||||
|
||||
STATIC_REQUIRE(field::spaceSupportsRank<field::H1, 0>);
|
||||
|
||||
STATIC_REQUIRE(field::spaceSupportsRank<field::H1, 1>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::spaceSupportsRank<field::RT, 0>);
|
||||
|
||||
STATIC_REQUIRE(field::spaceSupportsRank<field::RT, 1>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::spaceSupportsRank<field::ND, 0>);
|
||||
|
||||
STATIC_REQUIRE(field::spaceSupportsRank<field::ND, 1>);
|
||||
|
||||
CHECK(field::L2::name == std::string_view{"L2"});
|
||||
|
||||
CHECK(field::H1::name == std::string_view{"H1"});
|
||||
|
||||
CHECK(field::RT::name == std::string_view{"RT"});
|
||||
|
||||
CHECK(field::ND::name == std::string_view{"ND"});
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base Discretization Descriptors Preserve Space And Family Order",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
using L2Disc = field::Disc<field::L2, 2>;
|
||||
|
||||
using H1Disc = field::Disc<field::H1, 4>;
|
||||
|
||||
using RTDisc = field::Disc<field::RT, 1>;
|
||||
|
||||
using NDDisc = field::Disc<field::ND, 3>;
|
||||
|
||||
STATIC_REQUIRE(field::DiscretizationTag<L2Disc>);
|
||||
|
||||
STATIC_REQUIRE(field::DiscretizationTag<H1Disc>);
|
||||
|
||||
STATIC_REQUIRE(field::DiscretizationTag<RTDisc>);
|
||||
|
||||
STATIC_REQUIRE(field::DiscretizationTag<NDDisc>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename L2Disc::Space, field::L2>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename H1Disc::Space, field::H1>);
|
||||
|
||||
STATIC_REQUIRE(L2Disc::familyOrder == 2);
|
||||
|
||||
STATIC_REQUIRE(H1Disc::familyOrder == 4);
|
||||
|
||||
STATIC_REQUIRE(RTDisc::familyOrder == 1);
|
||||
|
||||
STATIC_REQUIRE(NDDisc::familyOrder == 3);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base Relations Preserve Their Source Quantities",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
using Source = field_base_test_utils::IndependentL2Scalar;
|
||||
|
||||
using Gradient = field::FieldRelation::Gradient<Source>;
|
||||
|
||||
using Divergence = field::FieldRelation::Divergence<Source>;
|
||||
|
||||
using Curl = field::FieldRelation::Curl<Source>;
|
||||
|
||||
STATIC_REQUIRE(field::ValidRelation<field::FieldRelation::Independent>);
|
||||
|
||||
STATIC_REQUIRE(field::ValidRelation<Gradient>);
|
||||
|
||||
STATIC_REQUIRE(field::ValidRelation<Divergence>);
|
||||
|
||||
STATIC_REQUIRE(field::ValidRelation<Curl>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::ValidRelation<int>);
|
||||
|
||||
STATIC_REQUIRE(field::IsGradient<Gradient>::value);
|
||||
|
||||
STATIC_REQUIRE(field::IsDivergence<Divergence>::value);
|
||||
|
||||
STATIC_REQUIRE(field::IsCurl<Curl>::value);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::RelationTarget<Gradient>::Type, Source>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::RelationTarget<Divergence>::Type, Source>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::RelationTarget<Curl>::Type, Source>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::RelationTarget<field::FieldRelation::Independent>::Type, void>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base Finite Element Quantities Preserve Rank Storage Space And Order",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
using Scalar = field_base_test_utils::IndependentL2Scalar;
|
||||
|
||||
using Vector = field_base_test_utils::IndependentH1Vector;
|
||||
|
||||
STATIC_REQUIRE(field::FieldQuantity<Scalar>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldQuantity<Vector>);
|
||||
|
||||
STATIC_REQUIRE(field::RegisteredQuantity<Scalar>);
|
||||
|
||||
STATIC_REQUIRE(field::RegisteredQuantity<Vector>);
|
||||
|
||||
STATIC_REQUIRE(Scalar::rankValue == 0);
|
||||
|
||||
STATIC_REQUIRE(Vector::rankValue == 1);
|
||||
|
||||
STATIC_REQUIRE(Scalar::familyOrder == 2);
|
||||
|
||||
STATIC_REQUIRE(Vector::familyOrder == 3);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename Scalar::Space, field::L2>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename Vector::Space, field::H1>);
|
||||
|
||||
STATIC_REQUIRE(Scalar::storageKind == field::StorageKind::finite_element);
|
||||
|
||||
STATIC_REQUIRE(Vector::storageKind == field::StorageKind::finite_element);
|
||||
|
||||
STATIC_REQUIRE(Scalar::staticBlockSize == field::dynamicBlockSize);
|
||||
|
||||
STATIC_REQUIRE(Vector::staticBlockSize == field::dynamicBlockSize);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base Global Scalars Are Registered But Are Not Finite Element Quantities",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
STATIC_REQUIRE(field::GlobalScalarQuantity<field::GlobalScalarQ>);
|
||||
|
||||
STATIC_REQUIRE(field::RegisteredQuantity<field::GlobalScalarQ>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::FieldQuantity<field::GlobalScalarQ>);
|
||||
|
||||
STATIC_REQUIRE(field::GlobalScalarQ::rankValue == 0);
|
||||
|
||||
STATIC_REQUIRE(field::GlobalScalarQ::storageKind == field::StorageKind::global_scalar);
|
||||
|
||||
STATIC_REQUIRE(field::GlobalScalarQ::staticBlockSize == 1);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::GlobalScalarQ::Relation, field::FieldRelation::Independent>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base Derived Quantity Detection Follows Physical Relations",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
using Independent = field_base_test_utils::IndependentL2Scalar;
|
||||
|
||||
using Flux = field_base_test_utils::Flux;
|
||||
|
||||
using CurlQuantity = field_base_test_utils::CurlQuantity;
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::DerivedQuantity<Independent>);
|
||||
|
||||
STATIC_REQUIRE(field::DerivedQuantity<Flux>);
|
||||
|
||||
STATIC_REQUIRE(field::DerivedQuantity<CurlQuantity>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<field::RelationTargetT<Flux>, field_base_test_utils::Potential>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<field::RelationTargetT<CurlQuantity>, field_base_test_utils::CurlSource>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base RT L2 Constraint Accepts The Registered Stable Pair Contract",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
using Potential = field_base_test_utils::Potential;
|
||||
|
||||
using Flux = field_base_test_utils::Flux;
|
||||
|
||||
using Constraint = field::RtL2StablePair<Flux, Potential>;
|
||||
|
||||
STATIC_REQUIRE(field::validate_constraints(field::TypeList<Constraint>{}));
|
||||
|
||||
STATIC_REQUIRE(field::validate_constraints(field::TypeList<>{}));
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base Operations And Operands Preserve Mathematical Intent",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
using Quantity = field_base_test_utils::IndependentH1Scalar;
|
||||
|
||||
using ValueOperand = field::Operand<Quantity>;
|
||||
|
||||
using GradientOperand = field::Operand<Quantity, field::FieldOperation::Gradient>;
|
||||
|
||||
STATIC_REQUIRE(field::FieldOperationTag<field::FieldOperation::Value>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldOperationTag<field::FieldOperation::Gradient>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldOperationTag<field::FieldOperation::Divergence>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldOperationTag<field::FieldOperation::Curl>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldOperationTag<field::FieldOperation::NormalTrace>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::FieldOperationTag<int>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldOperand<ValueOperand>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldOperand<GradientOperand>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename ValueOperand::Quantity, Quantity>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename ValueOperand::Operation, field::FieldOperation::Value>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename GradientOperand::Operation, field::FieldOperation::Gradient>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base Form Specifications Preserve Policy Dynamic Orders And Operands",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
using Form = field_base_test_utils::SampleForm;
|
||||
|
||||
STATIC_REQUIRE(field::FieldForm<Form>);
|
||||
|
||||
STATIC_REQUIRE(Form::policyKey == 17);
|
||||
|
||||
STATIC_REQUIRE(Form::dynamicOrderCount == 2);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename Form::Operands,
|
||||
field::TypeList<field_base_test_utils::SampleOperand, field_base_test_utils::SampleGradientOperand>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
field::isRegisteredQuantityList<field::TypeList<
|
||||
field_base_test_utils::IndependentL2Scalar, field_base_test_utils::IndependentH1Vector,
|
||||
field::GlobalScalarQ>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::isRegisteredQuantityList<field::TypeList<int>>);
|
||||
|
||||
STATIC_REQUIRE(field::isFieldFormList<field::TypeList<Form>>);
|
||||
|
||||
STATIC_REQUIRE(field::isFieldFormList<field::TypeList<>>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::isFieldFormList<field::TypeList<int>>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base Support Types Distinguish Domain And Non Spatial Fields",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
namespace domain = mean_field::utils::domain;
|
||||
|
||||
using StellarSupport = field::DomainSupport<domain::Stellar>;
|
||||
|
||||
using AllSupport = field::DomainSupport<domain::All>;
|
||||
|
||||
STATIC_REQUIRE(field::IsFieldSupport<StellarSupport>);
|
||||
|
||||
STATIC_REQUIRE(field::IsFieldSupport<AllSupport>);
|
||||
|
||||
STATIC_REQUIRE(field::IsFieldSupport<field::NonSpatialSupport>);
|
||||
|
||||
STATIC_REQUIRE(field::IsDomainSupport<StellarSupport>);
|
||||
|
||||
STATIC_REQUIRE(field::IsDomainSupport<AllSupport>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::IsDomainSupport<field::NonSpatialSupport>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename StellarSupport::Domain, domain::Stellar>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename AllSupport::Domain, domain::All>);
|
||||
|
||||
STATIC_REQUIRE(field::DomainSupportedField<field_base_test_utils::StellarSupportedObject>);
|
||||
|
||||
STATIC_REQUIRE(field::DomainSupportedField<field_base_test_utils::AllSupportedObject>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::DomainSupportedField<field_base_test_utils::NonSpatialObject>);
|
||||
|
||||
STATIC_REQUIRE(field::NonSpatialField<field_base_test_utils::NonSpatialObject>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::NonSpatialField<field_base_test_utils::StellarSupportedObject>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<field::FieldSupportT<field_base_test_utils::StellarSupportedObject>, StellarSupport>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<field::FieldDomainT<field_base_test_utils::StellarSupportedObject>, domain::Stellar>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
724
tests/field/field_dof_map.cpp
Normal file
724
tests/field/field_dof_map.cpp
Normal file
@@ -0,0 +1,724 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <cstddef>
|
||||
#include <mfem.hpp>
|
||||
#include <mpi.h>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace field_dof_map_test_utils {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
namespace domain = mean_field::utils::domain;
|
||||
|
||||
using Schema = domain::CoreEnvelopeVacuumDomainSchema;
|
||||
|
||||
[[nodiscard]]
|
||||
mfem::Array<int> make_array(const std::initializer_list<int> values) {
|
||||
mfem::Array<int> result(static_cast<int>(values.size()));
|
||||
|
||||
int index = 0;
|
||||
|
||||
for (const int value : values) {
|
||||
result[index++] = value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
mfem::Mesh make_split_mesh(
|
||||
const int stellarAttribute = 2,
|
||||
const int vacuumAttribute = 3
|
||||
) {
|
||||
int communicatorSize = 1;
|
||||
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &communicatorSize);
|
||||
|
||||
/*
|
||||
* Ensure there are enough cells that every reasonable MPI test
|
||||
* configuration has useful work available.
|
||||
*/
|
||||
const int xElementCount = std::max(4, 2 * communicatorSize);
|
||||
|
||||
constexpr int yElementCount = 2;
|
||||
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian2D(
|
||||
xElementCount, yElementCount, mfem::Element::QUADRILATERAL, true, static_cast<double>(xElementCount),
|
||||
static_cast<double>(yElementCount)
|
||||
);
|
||||
|
||||
for (int elementId = 0; elementId < mesh.GetNE(); ++elementId) {
|
||||
const int xIndex = elementId % xElementCount;
|
||||
|
||||
mesh.GetElement(elementId)->SetAttribute(xIndex < xElementCount / 2 ? stellarAttribute : vacuumAttribute);
|
||||
}
|
||||
|
||||
mesh.SetAttributes();
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
long long global_sum(const int localValue) {
|
||||
const long long local = static_cast<long long>(localValue);
|
||||
|
||||
long long global = 0;
|
||||
|
||||
MPI_Allreduce(&local, &global, 1, MPI_LONG_LONG, MPI_SUM, MPI_COMM_WORLD);
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
template <typename FieldT>
|
||||
concept CanMakeFieldDofMap =
|
||||
requires(const mfem::ParFiniteElementSpace &space) { field::make_field_dof_map<FieldT, Schema>(space); };
|
||||
|
||||
using AlternateSchema = domain::DomainSchema<
|
||||
domain::MaterialList<
|
||||
domain::Material<domain::Core, 11>,
|
||||
domain::Material<domain::Envelope, 17>,
|
||||
domain::Material<domain::Vacuum, 29>>,
|
||||
domain::BoundaryList<>,
|
||||
domain::RelationList<>>;
|
||||
} // namespace field_dof_map_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Preserves Canonical Bidirectional Indexing",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const mfem::Array<int> active = field_dof_map_test_utils::make_array({0, 2, 5, 7});
|
||||
|
||||
const field::FieldDofMap map(9, active);
|
||||
|
||||
CHECK(map.full_size() == 9);
|
||||
|
||||
CHECK(map.reduced_size() == 4);
|
||||
|
||||
CHECK(map.inactive_size() == 5);
|
||||
|
||||
CHECK_FALSE(map.is_identity());
|
||||
|
||||
CHECK(map.true_dof(0) == 0);
|
||||
|
||||
CHECK(map.true_dof(1) == 2);
|
||||
|
||||
CHECK(map.true_dof(2) == 5);
|
||||
|
||||
CHECK(map.true_dof(3) == 7);
|
||||
|
||||
REQUIRE(map.reduced_dof(0).has_value());
|
||||
|
||||
REQUIRE(map.reduced_dof(2).has_value());
|
||||
|
||||
REQUIRE(map.reduced_dof(5).has_value());
|
||||
|
||||
REQUIRE(map.reduced_dof(7).has_value());
|
||||
|
||||
CHECK(*map.reduced_dof(0) == 0);
|
||||
|
||||
CHECK(*map.reduced_dof(2) == 1);
|
||||
|
||||
CHECK(*map.reduced_dof(5) == 2);
|
||||
|
||||
CHECK(*map.reduced_dof(7) == 3);
|
||||
|
||||
CHECK_FALSE(map.reduced_dof(1).has_value());
|
||||
|
||||
CHECK_FALSE(map.reduced_dof(3).has_value());
|
||||
|
||||
CHECK(map.contains_true_dof(0));
|
||||
|
||||
CHECK(map.contains_true_dof(2));
|
||||
|
||||
CHECK_FALSE(map.contains_true_dof(1));
|
||||
|
||||
const mfem::Array<int> &forward = map.reduced_to_true();
|
||||
|
||||
const mfem::Array<int> &inverse = map.true_to_reduced();
|
||||
|
||||
REQUIRE(forward.Size() == 4);
|
||||
|
||||
REQUIRE(inverse.Size() == 9);
|
||||
|
||||
CHECK(forward[0] == 0);
|
||||
CHECK(forward[1] == 2);
|
||||
CHECK(forward[2] == 5);
|
||||
CHECK(forward[3] == 7);
|
||||
|
||||
CHECK(inverse[0] == 0);
|
||||
CHECK(inverse[1] == -1);
|
||||
CHECK(inverse[2] == 1);
|
||||
CHECK(inverse[3] == -1);
|
||||
CHECK(inverse[4] == -1);
|
||||
CHECK(inverse[5] == 2);
|
||||
CHECK(inverse[6] == -1);
|
||||
CHECK(inverse[7] == 3);
|
||||
CHECK(inverse[8] == -1);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Rejects Invalid Canonical Mappings",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const mfem::Array<int> empty;
|
||||
|
||||
CHECK_THROWS_AS((field::FieldDofMap(-1, empty)), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS((field::FieldDofMap(4, field_dof_map_test_utils::make_array({-1, 2}))), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS((field::FieldDofMap(4, field_dof_map_test_utils::make_array({1, 4}))), std::invalid_argument);
|
||||
|
||||
/*
|
||||
* Duplicate true DOF.
|
||||
*/
|
||||
CHECK_THROWS_AS((field::FieldDofMap(5, field_dof_map_test_utils::make_array({1, 1, 3}))), std::invalid_argument);
|
||||
|
||||
/*
|
||||
* Non-canonical unsorted ordering.
|
||||
*/
|
||||
CHECK_THROWS_AS((field::FieldDofMap(5, field_dof_map_test_utils::make_array({1, 3, 2}))), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Rejects Out Of Range Index Queries",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const field::FieldDofMap map(5, field_dof_map_test_utils::make_array({1, 3}));
|
||||
|
||||
CHECK_THROWS_AS(map.true_dof(-1), std::out_of_range);
|
||||
|
||||
CHECK_THROWS_AS(map.true_dof(2), std::out_of_range);
|
||||
|
||||
CHECK_THROWS_AS(map.reduced_dof(-1), std::out_of_range);
|
||||
|
||||
CHECK_THROWS_AS(map.reduced_dof(5), std::out_of_range);
|
||||
|
||||
CHECK_THROWS_AS(map.contains_true_dof(-1), std::out_of_range);
|
||||
|
||||
CHECK_THROWS_AS(map.contains_true_dof(5), std::out_of_range);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Gather Selects Exactly The Active True DOFs",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const field::FieldDofMap map(6, field_dof_map_test_utils::make_array({1, 3, 5}));
|
||||
|
||||
mfem::Vector full(6);
|
||||
|
||||
for (int trueDof = 0; trueDof < full.Size(); ++trueDof) {
|
||||
full(trueDof) = 10.0 + static_cast<double>(trueDof);
|
||||
}
|
||||
|
||||
const mfem::Vector reduced = map.gather(full);
|
||||
|
||||
REQUIRE(reduced.Size() == 3);
|
||||
|
||||
CHECK(reduced(0) == 11.0);
|
||||
|
||||
CHECK(reduced(1) == 13.0);
|
||||
|
||||
CHECK(reduced(2) == 15.0);
|
||||
|
||||
mfem::Vector output(3);
|
||||
|
||||
map.gather(full, output);
|
||||
|
||||
CHECK(output(0) == 11.0);
|
||||
|
||||
CHECK(output(1) == 13.0);
|
||||
|
||||
CHECK(output(2) == 15.0);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Scatter Produces The Canonical Supported Projection",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const field::FieldDofMap map(6, field_dof_map_test_utils::make_array({1, 3, 5}));
|
||||
|
||||
mfem::Vector reduced(3);
|
||||
|
||||
reduced(0) = 2.0;
|
||||
reduced(1) = 4.0;
|
||||
reduced(2) = 6.0;
|
||||
|
||||
const mfem::Vector full = map.scatter(reduced);
|
||||
|
||||
REQUIRE(full.Size() == 6);
|
||||
|
||||
CHECK(full(0) == 0.0);
|
||||
CHECK(full(1) == 2.0);
|
||||
CHECK(full(2) == 0.0);
|
||||
CHECK(full(3) == 4.0);
|
||||
CHECK(full(4) == 0.0);
|
||||
CHECK(full(5) == 6.0);
|
||||
|
||||
const mfem::Vector roundTrip = map.gather(full);
|
||||
|
||||
REQUIRE(roundTrip.Size() == reduced.Size());
|
||||
|
||||
for (int index = 0; index < reduced.Size(); ++index) {
|
||||
CHECK(roundTrip(index) == reduced(index));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Gather Scatter Projects A Full Vector Onto Field Support",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const field::FieldDofMap map(7, field_dof_map_test_utils::make_array({0, 2, 3, 6}));
|
||||
|
||||
mfem::Vector original(7);
|
||||
|
||||
for (int index = 0; index < original.Size(); ++index) {
|
||||
original(index) = 0.25 + static_cast<double>(index);
|
||||
}
|
||||
|
||||
const mfem::Vector reduced = map.gather(original);
|
||||
|
||||
const mfem::Vector projected = map.scatter(reduced);
|
||||
|
||||
for (int trueDof = 0; trueDof < original.Size(); ++trueDof) {
|
||||
CAPTURE(trueDof);
|
||||
|
||||
if (map.contains_true_dof(trueDof)) {
|
||||
CHECK(projected(trueDof) == original(trueDof));
|
||||
} else {
|
||||
CHECK(projected(trueDof) == 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Scatter Into Preserves Unsupported True DOFs",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const field::FieldDofMap map(6, field_dof_map_test_utils::make_array({1, 4}));
|
||||
|
||||
mfem::Vector reduced(2);
|
||||
|
||||
reduced(0) = 7.0;
|
||||
reduced(1) = 9.0;
|
||||
|
||||
mfem::Vector full(6);
|
||||
|
||||
full = -3.0;
|
||||
|
||||
map.scatter_into(reduced, full);
|
||||
|
||||
CHECK(full(0) == -3.0);
|
||||
CHECK(full(1) == 7.0);
|
||||
CHECK(full(2) == -3.0);
|
||||
CHECK(full(3) == -3.0);
|
||||
CHECK(full(4) == 9.0);
|
||||
CHECK(full(5) == -3.0);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Scatter Add Accumulates Only Onto Active True DOFs",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const field::FieldDofMap map(5, field_dof_map_test_utils::make_array({0, 2, 4}));
|
||||
|
||||
mfem::Vector reduced(3);
|
||||
|
||||
reduced(0) = 1.0;
|
||||
reduced(1) = 2.0;
|
||||
reduced(2) = 3.0;
|
||||
|
||||
mfem::Vector full(5);
|
||||
|
||||
full = 10.0;
|
||||
|
||||
map.scatter_add(reduced, full, 2.0);
|
||||
|
||||
CHECK(full(0) == 12.0);
|
||||
CHECK(full(1) == 10.0);
|
||||
CHECK(full(2) == 14.0);
|
||||
CHECK(full(3) == 10.0);
|
||||
CHECK(full(4) == 16.0);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Operations Support MFEM Vector Views Without Resizing",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const field::FieldDofMap map(5, field_dof_map_test_utils::make_array({1, 3}));
|
||||
|
||||
mfem::Vector storage(9);
|
||||
|
||||
storage = -8.0;
|
||||
|
||||
/*
|
||||
* View [2, 7) of the parent vector.
|
||||
*/
|
||||
mfem::Vector fullView(storage.GetData() + 2, 5);
|
||||
|
||||
mfem::Vector reduced(2);
|
||||
|
||||
reduced(0) = 4.0;
|
||||
reduced(1) = 6.0;
|
||||
|
||||
map.scatter_into(reduced, fullView);
|
||||
|
||||
/*
|
||||
* Storage outside the view must remain untouched.
|
||||
*/
|
||||
CHECK(storage(0) == -8.0);
|
||||
CHECK(storage(1) == -8.0);
|
||||
CHECK(storage(7) == -8.0);
|
||||
CHECK(storage(8) == -8.0);
|
||||
|
||||
/*
|
||||
* Within the view, only active true DOFs change.
|
||||
*/
|
||||
CHECK(storage(2) == -8.0);
|
||||
CHECK(storage(3) == 4.0);
|
||||
CHECK(storage(4) == -8.0);
|
||||
CHECK(storage(5) == 6.0);
|
||||
CHECK(storage(6) == -8.0);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Operations Reject Incompatible Vector Sizes",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const field::FieldDofMap map(5, field_dof_map_test_utils::make_array({1, 3}));
|
||||
|
||||
mfem::Vector correctFull(5);
|
||||
mfem::Vector wrongFull(4);
|
||||
|
||||
mfem::Vector correctReduced(2);
|
||||
mfem::Vector wrongReduced(3);
|
||||
|
||||
CHECK_THROWS_AS(map.gather(wrongFull), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS(map.gather(correctFull, wrongReduced), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS(map.scatter(wrongReduced), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS(map.scatter(correctReduced, wrongFull), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS(map.scatter_into(wrongReduced, correctFull), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS(map.scatter_add(correctReduced, wrongFull), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Identity Mapping Is An Exact Vector Identity",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const field::FieldDofMap map(4, field_dof_map_test_utils::make_array({0, 1, 2, 3}));
|
||||
|
||||
REQUIRE(map.is_identity());
|
||||
|
||||
REQUIRE(map.inactive_size() == 0);
|
||||
|
||||
mfem::Vector full(4);
|
||||
|
||||
full(0) = 0.1;
|
||||
full(1) = -0.2;
|
||||
full(2) = 3.7;
|
||||
full(3) = 8.1;
|
||||
|
||||
const mfem::Vector reduced = map.gather(full);
|
||||
|
||||
const mfem::Vector restored = map.scatter(reduced);
|
||||
|
||||
for (int index = 0; index < full.Size(); ++index) {
|
||||
CHECK(reduced(index) == full(index));
|
||||
|
||||
CHECK(restored(index) == full(index));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Validates Field DOF Support Consistency",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
field::FieldDofSupport support;
|
||||
|
||||
support.activeTrueDofMarker.SetSize(5);
|
||||
|
||||
support.activeTrueDofMarker = 0;
|
||||
|
||||
support.activeTrueDofMarker[1] = 1;
|
||||
|
||||
support.activeTrueDofMarker[3] = 1;
|
||||
|
||||
support.activeTrueDofs = field_dof_map_test_utils::make_array({1, 3});
|
||||
|
||||
const field::FieldDofMap validMap(support);
|
||||
|
||||
CHECK(validMap.full_size() == 5);
|
||||
|
||||
CHECK(validMap.reduced_size() == 2);
|
||||
|
||||
/*
|
||||
* Make the marker disagree with the list.
|
||||
*/
|
||||
support.activeTrueDofMarker[3] = 0;
|
||||
|
||||
CHECK_THROWS_AS((field::FieldDofMap(support)), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Factory Is Available Only For Spatial Registered Fields",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
STATIC_REQUIRE(field_dof_map_test_utils::CanMakeFieldDofMap<field::Density>);
|
||||
|
||||
STATIC_REQUIRE(field_dof_map_test_utils::CanMakeFieldDofMap<field::Enthalpy>);
|
||||
|
||||
STATIC_REQUIRE(field_dof_map_test_utils::CanMakeFieldDofMap<field::Gravity>);
|
||||
|
||||
STATIC_REQUIRE(field_dof_map_test_utils::CanMakeFieldDofMap<field::Displacement>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field_dof_map_test_utils::CanMakeFieldDofMap<field::BarotropicConstant>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Factory Exactly Preserves Density Support",
|
||||
tags::integration &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
mfem::Mesh serialMesh = field_dof_map_test_utils::make_split_mesh();
|
||||
|
||||
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
||||
|
||||
auto fec = field::Field<field::Density>::make_fec<field::Density::Scalar>(2);
|
||||
|
||||
auto finiteElementSpace = field::Field<field::Density>::make_fespace<field::Density::Scalar>(mesh, *fec);
|
||||
|
||||
REQUIRE(finiteElementSpace != nullptr);
|
||||
|
||||
const field::FieldDofSupport support =
|
||||
field::resolve_field_dof_support<field::Density, field_dof_map_test_utils::Schema>(*finiteElementSpace);
|
||||
|
||||
const field::FieldDofMap map =
|
||||
field::make_field_dof_map<field::Density, field_dof_map_test_utils::Schema>(*finiteElementSpace);
|
||||
|
||||
REQUIRE(map.full_size() == finiteElementSpace->GetTrueVSize());
|
||||
|
||||
REQUIRE(map.reduced_size() == support.activeTrueDofs.Size());
|
||||
|
||||
REQUIRE(map.full_size() == support.activeTrueDofMarker.Size());
|
||||
|
||||
for (int reducedDof = 0; reducedDof < map.reduced_size(); ++reducedDof) {
|
||||
CAPTURE(reducedDof);
|
||||
|
||||
CHECK(map.true_dof(reducedDof) == support.activeTrueDofs[reducedDof]);
|
||||
}
|
||||
|
||||
for (int trueDof = 0; trueDof < map.full_size(); ++trueDof) {
|
||||
CAPTURE(trueDof);
|
||||
|
||||
CHECK(map.contains_true_dof(trueDof) == (support.activeTrueDofMarker[trueDof] != 0));
|
||||
}
|
||||
|
||||
const long long globalFullSize = field_dof_map_test_utils::global_sum(map.full_size());
|
||||
|
||||
const long long globalReducedSize = field_dof_map_test_utils::global_sum(map.reduced_size());
|
||||
|
||||
/*
|
||||
* L2 density has independent vacuum element DOFs, so removing vacuum
|
||||
* support must genuinely reduce the global nonlinear block.
|
||||
*/
|
||||
CHECK(globalReducedSize > 0);
|
||||
|
||||
CHECK(globalReducedSize < globalFullSize);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Factory Exactly Preserves H1 Enthalpy Support",
|
||||
tags::integration &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
mfem::Mesh serialMesh = field_dof_map_test_utils::make_split_mesh();
|
||||
|
||||
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
||||
|
||||
auto fec = field::Field<field::Enthalpy>::make_fec<field::Enthalpy::Scalar>(2);
|
||||
|
||||
auto finiteElementSpace = field::Field<field::Enthalpy>::make_fespace<field::Enthalpy::Scalar>(mesh, *fec);
|
||||
|
||||
REQUIRE(finiteElementSpace != nullptr);
|
||||
|
||||
const field::FieldDofSupport support =
|
||||
field::resolve_field_dof_support<field::Enthalpy, field_dof_map_test_utils::Schema>(*finiteElementSpace);
|
||||
|
||||
const field::FieldDofMap map =
|
||||
field::make_field_dof_map<field::Enthalpy, field_dof_map_test_utils::Schema>(*finiteElementSpace);
|
||||
|
||||
REQUIRE(map.reduced_size() == support.activeTrueDofs.Size());
|
||||
|
||||
for (int reducedDof = 0; reducedDof < map.reduced_size(); ++reducedDof) {
|
||||
CHECK(map.true_dof(reducedDof) == support.activeTrueDofs[reducedDof]);
|
||||
}
|
||||
|
||||
/*
|
||||
* The separate field_mfem support tests already establish that shared
|
||||
* Stellar/Vacuum H1 trace DOFs are active. This test establishes that
|
||||
* FieldDofMap preserves that active set exactly, rather than applying
|
||||
* a second reduction or reinterpretation.
|
||||
*/
|
||||
for (int trueDof = 0; trueDof < map.full_size(); ++trueDof) {
|
||||
CHECK(map.contains_true_dof(trueDof) == (support.activeTrueDofMarker[trueDof] != 0));
|
||||
}
|
||||
|
||||
const long long globalFullSize = field_dof_map_test_utils::global_sum(map.full_size());
|
||||
|
||||
const long long globalReducedSize = field_dof_map_test_utils::global_sum(map.reduced_size());
|
||||
|
||||
CHECK(globalReducedSize > 0);
|
||||
|
||||
CHECK(globalReducedSize < globalFullSize);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Factory Produces Identity Maps For All Supported Fields",
|
||||
tags::integration &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
mfem::Mesh serialMesh = field_dof_map_test_utils::make_split_mesh();
|
||||
|
||||
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
||||
|
||||
auto fec = field::Field<field::Displacement>::make_fec<field::Displacement::Vector>(2);
|
||||
|
||||
auto finiteElementSpace = field::Field<field::Displacement>::make_fespace<field::Displacement::Vector>(mesh, *fec);
|
||||
|
||||
REQUIRE(finiteElementSpace != nullptr);
|
||||
|
||||
const field::FieldDofMap map =
|
||||
field::make_field_dof_map<field::Displacement, field_dof_map_test_utils::Schema>(*finiteElementSpace);
|
||||
|
||||
CHECK(map.is_identity());
|
||||
|
||||
CHECK(map.full_size() == finiteElementSpace->GetTrueVSize());
|
||||
|
||||
CHECK(map.reduced_size() == finiteElementSpace->GetTrueVSize());
|
||||
|
||||
CHECK(map.inactive_size() == 0);
|
||||
|
||||
for (int trueDof = 0; trueDof < map.full_size(); ++trueDof) {
|
||||
CHECK(map.true_dof(trueDof) == trueDof);
|
||||
|
||||
CHECK(map.contains_true_dof(trueDof));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Factory Uses Schema Material Bindings Rather Than Numeric Conventions",
|
||||
tags::integration &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
mfem::Mesh serialMesh = field_dof_map_test_utils::make_split_mesh(17, 29);
|
||||
|
||||
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
||||
|
||||
auto fec = field::Field<field::Density>::make_fec<field::Density::Scalar>(2);
|
||||
|
||||
auto finiteElementSpace = field::Field<field::Density>::make_fespace<field::Density::Scalar>(mesh, *fec);
|
||||
|
||||
REQUIRE(finiteElementSpace != nullptr);
|
||||
|
||||
const field::FieldDofMap map =
|
||||
field::make_field_dof_map<field::Density, field_dof_map_test_utils::AlternateSchema>(*finiteElementSpace);
|
||||
|
||||
const field::FieldDofSupport support =
|
||||
field::resolve_field_dof_support<field::Density, field_dof_map_test_utils::AlternateSchema>(
|
||||
*finiteElementSpace
|
||||
);
|
||||
|
||||
CHECK(map.full_size() == support.activeTrueDofMarker.Size());
|
||||
|
||||
CHECK(map.reduced_size() == support.activeTrueDofs.Size());
|
||||
|
||||
for (int trueDof = 0; trueDof < map.full_size(); ++trueDof) {
|
||||
CHECK(map.contains_true_dof(trueDof) == (support.activeTrueDofMarker[trueDof] != 0));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Reduced Vectors Round Trip Through Real Field Support",
|
||||
tags::integration &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
mfem::Mesh serialMesh = field_dof_map_test_utils::make_split_mesh();
|
||||
|
||||
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
||||
|
||||
auto fec = field::Field<field::Enthalpy>::make_fec<field::Enthalpy::Scalar>(2);
|
||||
|
||||
auto finiteElementSpace = field::Field<field::Enthalpy>::make_fespace<field::Enthalpy::Scalar>(mesh, *fec);
|
||||
|
||||
REQUIRE(finiteElementSpace != nullptr);
|
||||
|
||||
const field::FieldDofMap map =
|
||||
field::make_field_dof_map<field::Enthalpy, field_dof_map_test_utils::Schema>(*finiteElementSpace);
|
||||
|
||||
mfem::Vector reduced(map.reduced_size());
|
||||
|
||||
for (int reducedDof = 0; reducedDof < reduced.Size(); ++reducedDof) {
|
||||
reduced(reducedDof) = 0.125 + 0.031 * static_cast<double>(reducedDof + 1);
|
||||
}
|
||||
|
||||
const mfem::Vector full = map.scatter(reduced);
|
||||
|
||||
const mfem::Vector recovered = map.gather(full);
|
||||
|
||||
REQUIRE(recovered.Size() == reduced.Size());
|
||||
|
||||
for (int reducedDof = 0; reducedDof < reduced.Size(); ++reducedDof) {
|
||||
CAPTURE(reducedDof);
|
||||
|
||||
CHECK(recovered(reducedDof) == reduced(reducedDof));
|
||||
}
|
||||
|
||||
for (int trueDof = 0; trueDof < full.Size(); ++trueDof) {
|
||||
if (!map.contains_true_dof(trueDof)) {
|
||||
CHECK(full(trueDof) == 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
778
tests/field/field_mfem.cpp
Normal file
778
tests/field/field_mfem.cpp
Normal file
@@ -0,0 +1,778 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <mpi.h>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace field_mfem_test_utils {
|
||||
namespace field = mean_field::field;
|
||||
namespace domain = mean_field::utils::domain;
|
||||
namespace quadrature = mean_field::quadrature;
|
||||
|
||||
using Schema = domain::CoreEnvelopeVacuumDomainSchema;
|
||||
|
||||
struct VectorL2Field {
|
||||
static constexpr std::string_view name = "test_vector_l2";
|
||||
|
||||
using Support = field::DomainSupport<domain::All>;
|
||||
|
||||
struct Vector final : field::VectorQ<field::FieldRelation::Independent, field::Disc<field::L2, 2>> { };
|
||||
|
||||
using Quantities = field::TypeList<Vector>;
|
||||
|
||||
using Constraints = field::TypeList<>;
|
||||
|
||||
using FormList = field::TypeList<>;
|
||||
|
||||
static constexpr bool constraintsAreValid = field::validate_constraints(Constraints{});
|
||||
|
||||
static_assert(constraintsAreValid);
|
||||
};
|
||||
|
||||
struct NdField {
|
||||
static constexpr std::string_view name = "test_nd";
|
||||
|
||||
using Support = field::DomainSupport<domain::All>;
|
||||
|
||||
struct Vector final : field::VectorQ<field::FieldRelation::Independent, field::Disc<field::ND, 2>> { };
|
||||
|
||||
using Quantities = field::TypeList<Vector>;
|
||||
|
||||
using Constraints = field::TypeList<>;
|
||||
|
||||
using FormList = field::TypeList<>;
|
||||
|
||||
static constexpr bool constraintsAreValid = field::validate_constraints(Constraints{});
|
||||
|
||||
static_assert(constraintsAreValid);
|
||||
};
|
||||
|
||||
using AlternateSchema = domain::DomainSchema<
|
||||
domain::MaterialList<
|
||||
domain::Material<domain::Core, 11>,
|
||||
domain::Material<domain::Envelope, 17>,
|
||||
domain::Material<domain::Vacuum, 29>>,
|
||||
domain::BoundaryList<>,
|
||||
domain::RelationList<>>;
|
||||
|
||||
template <typename FieldT>
|
||||
concept CanResolveLocalSupport = requires(const mfem::FiniteElementSpace &space) {
|
||||
field::resolve_field_local_dof_support<FieldT, Schema>(space);
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
mfem::Mesh make_two_domain_mesh(
|
||||
const int leftAttribute = 2,
|
||||
const int rightAttribute = 3
|
||||
) {
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian2D(2, 1, mfem::Element::QUADRILATERAL, true, 2.0, 1.0);
|
||||
|
||||
mesh.GetElement(0)->SetAttribute(leftAttribute);
|
||||
|
||||
mesh.GetElement(1)->SetAttribute(rightAttribute);
|
||||
|
||||
mesh.SetAttributes();
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
mfem::Mesh make_parallel_split_mesh() {
|
||||
constexpr int xElementCount = 4;
|
||||
constexpr int yElementCount = 2;
|
||||
|
||||
mfem::Mesh mesh =
|
||||
mfem::Mesh::MakeCartesian2D(xElementCount, yElementCount, mfem::Element::QUADRILATERAL, true, 4.0, 2.0);
|
||||
|
||||
for (int elementId = 0; elementId < mesh.GetNE(); ++elementId) {
|
||||
const int xIndex = elementId % xElementCount;
|
||||
|
||||
const int attribute = xIndex < 2 ? 2 : 3;
|
||||
|
||||
mesh.GetElement(elementId)->SetAttribute(attribute);
|
||||
}
|
||||
|
||||
mesh.SetAttributes();
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::vector<int> decoded_element_vdofs(
|
||||
const mfem::FiniteElementSpace &space,
|
||||
const int elementId
|
||||
) {
|
||||
mfem::Array<int> signedVDofs;
|
||||
|
||||
space.GetElementVDofs(elementId, signedVDofs);
|
||||
|
||||
std::vector<int> result;
|
||||
|
||||
result.reserve(static_cast<std::size_t>(signedVDofs.Size()));
|
||||
|
||||
for (int index = 0; index < signedVDofs.Size(); ++index) {
|
||||
result.push_back(mfem::FiniteElementSpace::DecodeDof(signedVDofs[index]));
|
||||
}
|
||||
|
||||
std::ranges::sort(result);
|
||||
|
||||
result.erase(std::unique(result.begin(), result.end()), result.end());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
bool contains(
|
||||
const mfem::Array<int> &values,
|
||||
const int value
|
||||
) {
|
||||
for (int index = 0; index < values.Size(); ++index) {
|
||||
if (values[index] == value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::vector<int> intersection(
|
||||
const std::vector<int> &first,
|
||||
const std::vector<int> &second
|
||||
) {
|
||||
std::vector<int> result;
|
||||
|
||||
std::set_intersection(first.begin(), first.end(), second.begin(), second.end(), std::back_inserter(result));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::vector<int> difference(
|
||||
const std::vector<int> &first,
|
||||
const std::vector<int> &second
|
||||
) {
|
||||
std::vector<int> result;
|
||||
|
||||
std::set_difference(first.begin(), first.end(), second.begin(), second.end(), std::back_inserter(result));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
long long global_sum(const int localValue) {
|
||||
const long long local = static_cast<long long>(localValue);
|
||||
|
||||
long long global = 0;
|
||||
|
||||
MPI_Allreduce(&local, &global, 1, MPI_LONG_LONG, MPI_SUM, MPI_COMM_WORLD);
|
||||
|
||||
return global;
|
||||
}
|
||||
} // namespace field_mfem_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM Support Resolution Is Available Only For Domain Supported Fields",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
STATIC_REQUIRE(field::MfemDomainField<field::Density>);
|
||||
|
||||
STATIC_REQUIRE(field::MfemDomainField<field::Enthalpy>);
|
||||
|
||||
STATIC_REQUIRE(field::MfemDomainField<field::Gravity>);
|
||||
|
||||
STATIC_REQUIRE(field::MfemDomainField<field::Displacement>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::MfemDomainField<field::BarotropicConstant>);
|
||||
|
||||
STATIC_REQUIRE(field_mfem_test_utils::CanResolveLocalSupport<field::Density>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field_mfem_test_utils::CanResolveLocalSupport<field::BarotropicConstant>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM Creates The Registered Finite Element Collection Families",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
using DensityField = field::Field<field::Density>;
|
||||
|
||||
using GravityField = field::Field<field::Gravity>;
|
||||
|
||||
using DisplacementField = field::Field<field::Displacement>;
|
||||
|
||||
using EnthalpyField = field::Field<field::Enthalpy>;
|
||||
|
||||
auto densityCollection = DensityField::make_fec<field::Density::Scalar>(3);
|
||||
|
||||
auto potentialCollection = GravityField::make_fec<field::Gravity::Potential>(3);
|
||||
|
||||
auto fluxCollection = GravityField::make_fec<field::Gravity::Flux>(3);
|
||||
|
||||
auto displacementCollection = DisplacementField::make_fec<field::Displacement::Vector>(3);
|
||||
|
||||
auto enthalpyCollection = EnthalpyField::make_fec<field::Enthalpy::Scalar>(3);
|
||||
|
||||
auto vectorL2Collection =
|
||||
field::Field<field_mfem_test_utils::VectorL2Field>::make_fec<field_mfem_test_utils::VectorL2Field::Vector>(3);
|
||||
|
||||
auto ndCollection =
|
||||
field::Field<field_mfem_test_utils::NdField>::make_fec<field_mfem_test_utils::NdField::Vector>(3);
|
||||
|
||||
REQUIRE(densityCollection != nullptr);
|
||||
|
||||
REQUIRE(potentialCollection != nullptr);
|
||||
|
||||
REQUIRE(fluxCollection != nullptr);
|
||||
|
||||
REQUIRE(displacementCollection != nullptr);
|
||||
|
||||
REQUIRE(enthalpyCollection != nullptr);
|
||||
|
||||
REQUIRE(vectorL2Collection != nullptr);
|
||||
|
||||
REQUIRE(ndCollection != nullptr);
|
||||
|
||||
CHECK(dynamic_cast<mfem::L2_FECollection *>(densityCollection.get()) != nullptr);
|
||||
|
||||
CHECK(dynamic_cast<mfem::L2_FECollection *>(potentialCollection.get()) != nullptr);
|
||||
|
||||
CHECK(dynamic_cast<mfem::RT_FECollection *>(fluxCollection.get()) != nullptr);
|
||||
|
||||
CHECK(dynamic_cast<mfem::H1_FECollection *>(displacementCollection.get()) != nullptr);
|
||||
|
||||
CHECK(dynamic_cast<mfem::H1_FECollection *>(enthalpyCollection.get()) != nullptr);
|
||||
|
||||
CHECK(dynamic_cast<mfem::L2_FECollection *>(vectorL2Collection.get()) != nullptr);
|
||||
|
||||
CHECK(dynamic_cast<mfem::ND_FECollection *>(ndCollection.get()) != nullptr);
|
||||
|
||||
CHECK_THROWS_AS((DensityField::make_fec<field::Density::Scalar>(0)), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS((GravityField::make_fec<field::Gravity::Flux>(-1)), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM Creates Parallel Spaces With Registered Dimensions Orders And Ordering",
|
||||
tags::integration &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
mfem::Mesh serialMesh = mfem::Mesh::MakeCartesian2D(4, 2, mfem::Element::QUADRILATERAL, true, 4.0, 2.0);
|
||||
|
||||
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
||||
|
||||
auto densityFec = field::Field<field::Density>::make_fec<field::Density::Scalar>(2);
|
||||
|
||||
auto potentialFec = field::Field<field::Gravity>::make_fec<field::Gravity::Potential>(2);
|
||||
|
||||
auto fluxFec = field::Field<field::Gravity>::make_fec<field::Gravity::Flux>(2);
|
||||
|
||||
auto displacementFec = field::Field<field::Displacement>::make_fec<field::Displacement::Vector>(2);
|
||||
|
||||
auto enthalpyFec = field::Field<field::Enthalpy>::make_fec<field::Enthalpy::Scalar>(2);
|
||||
|
||||
auto vectorL2Fec =
|
||||
field::Field<field_mfem_test_utils::VectorL2Field>::make_fec<field_mfem_test_utils::VectorL2Field::Vector>(2);
|
||||
|
||||
auto ndFec = field::Field<field_mfem_test_utils::NdField>::make_fec<field_mfem_test_utils::NdField::Vector>(2);
|
||||
|
||||
auto densitySpace = field::Field<field::Density>::make_fespace<field::Density::Scalar>(mesh, *densityFec);
|
||||
|
||||
auto potentialSpace = field::Field<field::Gravity>::make_fespace<field::Gravity::Potential>(mesh, *potentialFec);
|
||||
|
||||
auto fluxSpace = field::Field<field::Gravity>::make_fespace<field::Gravity::Flux>(mesh, *fluxFec);
|
||||
|
||||
auto displacementSpace =
|
||||
field::Field<field::Displacement>::make_fespace<field::Displacement::Vector>(mesh, *displacementFec);
|
||||
|
||||
auto enthalpySpace = field::Field<field::Enthalpy>::make_fespace<field::Enthalpy::Scalar>(mesh, *enthalpyFec);
|
||||
|
||||
auto vectorL2Space =
|
||||
field::Field<field_mfem_test_utils::VectorL2Field>::make_fespace<field_mfem_test_utils::VectorL2Field::Vector>(
|
||||
mesh, *vectorL2Fec
|
||||
);
|
||||
|
||||
auto ndSpace = field::Field<field_mfem_test_utils::NdField>::make_fespace<field_mfem_test_utils::NdField::Vector>(
|
||||
mesh, *ndFec
|
||||
);
|
||||
|
||||
REQUIRE(densitySpace != nullptr);
|
||||
REQUIRE(potentialSpace != nullptr);
|
||||
REQUIRE(fluxSpace != nullptr);
|
||||
REQUIRE(displacementSpace != nullptr);
|
||||
REQUIRE(enthalpySpace != nullptr);
|
||||
REQUIRE(vectorL2Space != nullptr);
|
||||
REQUIRE(ndSpace != nullptr);
|
||||
|
||||
CHECK(densitySpace->GetVDim() == 1);
|
||||
|
||||
CHECK(potentialSpace->GetVDim() == 1);
|
||||
|
||||
CHECK(fluxSpace->GetVDim() == 1);
|
||||
|
||||
CHECK(enthalpySpace->GetVDim() == 1);
|
||||
|
||||
CHECK(displacementSpace->GetVDim() == mesh.SpaceDimension());
|
||||
|
||||
CHECK(vectorL2Space->GetVDim() == mesh.SpaceDimension());
|
||||
|
||||
CHECK(ndSpace->GetVDim() == 1);
|
||||
|
||||
CHECK(densitySpace->GetOrdering() == mfem::Ordering::byNODES);
|
||||
|
||||
CHECK(potentialSpace->GetOrdering() == mfem::Ordering::byNODES);
|
||||
|
||||
CHECK(fluxSpace->GetOrdering() == mfem::Ordering::byNODES);
|
||||
|
||||
CHECK(enthalpySpace->GetOrdering() == mfem::Ordering::byNODES);
|
||||
|
||||
/*
|
||||
* Displacement deliberately overrides the generic
|
||||
* vector-H1 rule and is part of the project's block/indexing
|
||||
* contract.
|
||||
*/
|
||||
CHECK(displacementSpace->GetOrdering() == mfem::Ordering::byNODES);
|
||||
|
||||
/*
|
||||
* A generic vector L2 quantity retains the ordinary backend
|
||||
* realization, demonstrating that the displacement behavior is
|
||||
* an intentional specialization rather than a global accident.
|
||||
*/
|
||||
CHECK(vectorL2Space->GetOrdering() == mfem::Ordering::byVDIM);
|
||||
|
||||
CHECK(ndSpace->GetOrdering() == mfem::Ordering::byNODES);
|
||||
|
||||
CHECK(densitySpace->GetMaxElementOrder() == field::Density::Scalar::familyOrder);
|
||||
|
||||
CHECK(potentialSpace->GetMaxElementOrder() == field::Gravity::Potential::familyOrder);
|
||||
|
||||
CHECK(fluxSpace->GetMaxElementOrder() == field::Gravity::Flux::familyOrder + 1);
|
||||
|
||||
CHECK(displacementSpace->GetMaxElementOrder() == field::Displacement::Vector::familyOrder);
|
||||
|
||||
CHECK(enthalpySpace->GetMaxElementOrder() == field::Enthalpy::Scalar::familyOrder);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM Typed Queries Preserve Backend Polynomial Order Semantics",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
namespace quadrature = mean_field::quadrature;
|
||||
namespace utils = mean_field::utils;
|
||||
|
||||
using DensityField = field::Field<field::Density>;
|
||||
|
||||
using GravityField = field::Field<field::Gravity>;
|
||||
|
||||
using EnthalpyField = field::Field<field::Enthalpy>;
|
||||
|
||||
const quadrature::Query densitySource = DensityField::make_query<field::Density::Form::ProjectionSource>(
|
||||
quadrature::QuadratureRole::projection, 3, std::array<int, 1>{4}, utils::DOMAINS::STELLAR,
|
||||
quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
REQUIRE(densitySource.base_order.has_value());
|
||||
|
||||
/*
|
||||
* L2_2 value order 2
|
||||
* + geometry order 3
|
||||
* + dynamic coefficient order 4.
|
||||
*/
|
||||
CHECK(*densitySource.base_order == 9);
|
||||
|
||||
CHECK(densitySource.term == quadrature::Term::density_projection);
|
||||
|
||||
CHECK(densitySource.role == quadrature::QuadratureRole::projection);
|
||||
|
||||
CHECK(densitySource.domain == utils::DOMAINS::STELLAR);
|
||||
|
||||
CHECK(densitySource.mapping == quadrature::MappingKind::general);
|
||||
|
||||
CHECK(densitySource.geometry_weight_order == 3);
|
||||
|
||||
const quadrature::Query hdivMass =
|
||||
GravityField::make_query<field::Gravity::Form::HDivMass>(quadrature::QuadratureRole::discretization, 2);
|
||||
|
||||
REQUIRE(hdivMass.base_order.has_value());
|
||||
|
||||
/*
|
||||
* RT_2 value order is 3, hence
|
||||
* 3 + 3 + geometry 2 = 8.
|
||||
*/
|
||||
CHECK(*hdivMass.base_order == 8);
|
||||
|
||||
const quadrature::Query divergence = GravityField::make_query<field::Gravity::Form::DivergenceCoupling>(
|
||||
quadrature::QuadratureRole::discretization, 2
|
||||
);
|
||||
|
||||
REQUIRE(divergence.base_order.has_value());
|
||||
|
||||
/*
|
||||
* div(RT_2) order 2
|
||||
* + L2_2 order 2
|
||||
* + geometry 2.
|
||||
*/
|
||||
CHECK(*divergence.base_order == 6);
|
||||
|
||||
const quadrature::Query pressureForce = EnthalpyField::make_query<field::Enthalpy::Form::PressureForce>(
|
||||
quadrature::QuadratureRole::discretization, 2, std::array<int, 1>{9}, utils::DOMAINS::STELLAR,
|
||||
quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
REQUIRE(pressureForce.base_order.has_value());
|
||||
|
||||
/*
|
||||
* h value order 3
|
||||
* + grad(d) order 2
|
||||
* + geometry 2
|
||||
* + n=3 pressure extra order 9
|
||||
* = 16.
|
||||
*/
|
||||
CHECK(*pressureForce.base_order == 16);
|
||||
|
||||
const quadrature::Query equilibriumConstant = EnthalpyField::make_query<field::Enthalpy::Form::EquilibriumConstant>(
|
||||
quadrature::QuadratureRole::discretization, 2
|
||||
);
|
||||
|
||||
REQUIRE(equilibriumConstant.base_order.has_value());
|
||||
|
||||
/*
|
||||
* Global scalar C contributes zero polynomial order,
|
||||
* h contributes 3, and geometry contributes 2.
|
||||
*/
|
||||
CHECK(*equilibriumConstant.base_order == 5);
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
(DensityField::make_query<field::Density::Form::ProjectionMass>(quadrature::QuadratureRole::projection, -1)),
|
||||
std::invalid_argument
|
||||
);
|
||||
|
||||
const std::array<int, 1> negativeDynamicOrder{-1};
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
(EnthalpyField::make_query<field::Enthalpy::Form::PressureForce>(
|
||||
quadrature::QuadratureRole::discretization, 2, negativeDynamicOrder
|
||||
)),
|
||||
std::invalid_argument
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM Element Support Resolves Semantic Domains Through The Schema",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const mfem::Mesh mesh = field_mfem_test_utils::make_two_domain_mesh();
|
||||
|
||||
CHECK((field::element_is_in_field_support<field::Density, field_mfem_test_utils::Schema>(mesh, 0)));
|
||||
|
||||
CHECK_FALSE((field::element_is_in_field_support<field::Density, field_mfem_test_utils::Schema>(mesh, 1)));
|
||||
|
||||
CHECK((field::element_is_in_field_support<field::Enthalpy, field_mfem_test_utils::Schema>(mesh, 0)));
|
||||
|
||||
CHECK_FALSE((field::element_is_in_field_support<field::Enthalpy, field_mfem_test_utils::Schema>(mesh, 1)));
|
||||
|
||||
CHECK((field::element_is_in_field_support<field::Gravity, field_mfem_test_utils::Schema>(mesh, 0)));
|
||||
|
||||
CHECK((field::element_is_in_field_support<field::Gravity, field_mfem_test_utils::Schema>(mesh, 1)));
|
||||
|
||||
CHECK((field::element_is_in_field_support<field::Displacement, field_mfem_test_utils::Schema>(mesh, 0)));
|
||||
|
||||
CHECK((field::element_is_in_field_support<field::Displacement, field_mfem_test_utils::Schema>(mesh, 1)));
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM L2 Stellar Support Selects Exactly Stellar Element DOFs",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
mfem::Mesh mesh = field_mfem_test_utils::make_two_domain_mesh();
|
||||
|
||||
auto fec = field::Field<field::Density>::make_fec<field::Density::Scalar>(2);
|
||||
|
||||
mfem::FiniteElementSpace space(&mesh, fec.get());
|
||||
|
||||
const auto support = field::resolve_field_local_dof_support<field::Density, field_mfem_test_utils::Schema>(space);
|
||||
|
||||
const std::vector<int> stellarVDofs = field_mfem_test_utils::decoded_element_vdofs(space, 0);
|
||||
|
||||
const std::vector<int> vacuumVDofs = field_mfem_test_utils::decoded_element_vdofs(space, 1);
|
||||
|
||||
REQUIRE_FALSE(stellarVDofs.empty());
|
||||
|
||||
REQUIRE_FALSE(vacuumVDofs.empty());
|
||||
|
||||
CHECK(support.activeVDofMarker.Size() == space.GetVSize());
|
||||
|
||||
CHECK(support.activeVDofs.Size() + support.inactiveVDofs.Size() == space.GetVSize());
|
||||
|
||||
for (const int vdof : stellarVDofs) {
|
||||
CAPTURE(vdof);
|
||||
|
||||
CHECK(support.activeVDofMarker[vdof] == 1);
|
||||
|
||||
CHECK(field_mfem_test_utils::contains(support.activeVDofs, vdof));
|
||||
|
||||
CHECK_FALSE(field_mfem_test_utils::contains(support.inactiveVDofs, vdof));
|
||||
}
|
||||
|
||||
for (const int vdof : vacuumVDofs) {
|
||||
CAPTURE(vdof);
|
||||
|
||||
CHECK(support.activeVDofMarker[vdof] == 0);
|
||||
|
||||
CHECK_FALSE(field_mfem_test_utils::contains(support.activeVDofs, vdof));
|
||||
|
||||
CHECK(field_mfem_test_utils::contains(support.inactiveVDofs, vdof));
|
||||
}
|
||||
|
||||
CHECK(support.activeVDofs.Size() == static_cast<int>(stellarVDofs.size()));
|
||||
|
||||
CHECK(support.inactiveVDofs.Size() == static_cast<int>(vacuumVDofs.size()));
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM H1 Stellar Support Keeps Shared Stellar Vacuum Trace DOFs Active",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
mfem::Mesh mesh = field_mfem_test_utils::make_two_domain_mesh();
|
||||
|
||||
auto fec = field::Field<field::Enthalpy>::make_fec<field::Enthalpy::Scalar>(2);
|
||||
|
||||
mfem::FiniteElementSpace space(&mesh, fec.get());
|
||||
|
||||
const auto support = field::resolve_field_local_dof_support<field::Enthalpy, field_mfem_test_utils::Schema>(space);
|
||||
|
||||
const std::vector<int> stellarVDofs = field_mfem_test_utils::decoded_element_vdofs(space, 0);
|
||||
|
||||
const std::vector<int> vacuumVDofs = field_mfem_test_utils::decoded_element_vdofs(space, 1);
|
||||
|
||||
const std::vector<int> interfaceVDofs = field_mfem_test_utils::intersection(stellarVDofs, vacuumVDofs);
|
||||
|
||||
const std::vector<int> vacuumOnlyVDofs = field_mfem_test_utils::difference(vacuumVDofs, stellarVDofs);
|
||||
|
||||
REQUIRE_FALSE(interfaceVDofs.empty());
|
||||
|
||||
REQUIRE_FALSE(vacuumOnlyVDofs.empty());
|
||||
|
||||
for (const int vdof : stellarVDofs) {
|
||||
CAPTURE(vdof);
|
||||
|
||||
CHECK(support.activeVDofMarker[vdof] == 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the central support invariant:
|
||||
*
|
||||
* shared interface DOFs are active because they are touched
|
||||
* by a supported stellar element, even though they are also
|
||||
* touched by a vacuum element.
|
||||
*/
|
||||
for (const int vdof : interfaceVDofs) {
|
||||
CAPTURE(vdof);
|
||||
|
||||
CHECK(support.activeVDofMarker[vdof] == 1);
|
||||
|
||||
CHECK(field_mfem_test_utils::contains(support.activeVDofs, vdof));
|
||||
}
|
||||
|
||||
for (const int vdof : vacuumOnlyVDofs) {
|
||||
CAPTURE(vdof);
|
||||
|
||||
CHECK(support.activeVDofMarker[vdof] == 0);
|
||||
|
||||
CHECK(field_mfem_test_utils::contains(support.inactiveVDofs, vdof));
|
||||
}
|
||||
|
||||
CHECK(support.activeVDofs.Size() + support.inactiveVDofs.Size() == space.GetVSize());
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM All Domain Support Activates Every L2 And RT DOF",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
mfem::Mesh mesh = field_mfem_test_utils::make_two_domain_mesh();
|
||||
|
||||
auto potentialFec = field::Field<field::Gravity>::make_fec<field::Gravity::Potential>(2);
|
||||
|
||||
mfem::FiniteElementSpace potentialSpace(&mesh, potentialFec.get());
|
||||
|
||||
const auto potentialSupport =
|
||||
field::resolve_field_local_dof_support<field::Gravity, field_mfem_test_utils::Schema>(potentialSpace);
|
||||
|
||||
CHECK(potentialSupport.activeVDofs.Size() == potentialSpace.GetVSize());
|
||||
|
||||
CHECK(potentialSupport.inactiveVDofs.Size() == 0);
|
||||
|
||||
for (int vdof = 0; vdof < potentialSupport.activeVDofMarker.Size(); ++vdof) {
|
||||
CHECK(potentialSupport.activeVDofMarker[vdof] == 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Exercise signed/oriented MFEM element VDofs through RT as
|
||||
* well. The support resolver must DecodeDof() correctly.
|
||||
*/
|
||||
auto fluxFec = field::Field<field::Gravity>::make_fec<field::Gravity::Flux>(2);
|
||||
|
||||
mfem::FiniteElementSpace fluxSpace(&mesh, fluxFec.get());
|
||||
|
||||
const auto fluxSupport =
|
||||
field::resolve_field_local_dof_support<field::Gravity, field_mfem_test_utils::Schema>(fluxSpace);
|
||||
|
||||
CHECK(fluxSupport.activeVDofs.Size() == fluxSpace.GetVSize());
|
||||
|
||||
CHECK(fluxSupport.inactiveVDofs.Size() == 0);
|
||||
|
||||
for (int vdof = 0; vdof < fluxSupport.activeVDofMarker.Size(); ++vdof) {
|
||||
CHECK(fluxSupport.activeVDofMarker[vdof] == 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM Support Resolution Uses Schema Material Bindings Rather Than Hard Coded IDs",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
mfem::Mesh mesh = field_mfem_test_utils::make_two_domain_mesh(17, 29);
|
||||
|
||||
auto fec = field::Field<field::Density>::make_fec<field::Density::Scalar>(2);
|
||||
|
||||
mfem::FiniteElementSpace space(&mesh, fec.get());
|
||||
|
||||
const auto support =
|
||||
field::resolve_field_local_dof_support<field::Density, field_mfem_test_utils::AlternateSchema>(space);
|
||||
|
||||
const std::vector<int> stellarVDofs = field_mfem_test_utils::decoded_element_vdofs(space, 0);
|
||||
|
||||
const std::vector<int> vacuumVDofs = field_mfem_test_utils::decoded_element_vdofs(space, 1);
|
||||
|
||||
for (const int vdof : stellarVDofs) {
|
||||
CHECK(support.activeVDofMarker[vdof] == 1);
|
||||
}
|
||||
|
||||
for (const int vdof : vacuumVDofs) {
|
||||
CHECK(support.activeVDofMarker[vdof] == 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM Parallel Stellar Support Produces Consistent Local And True DOF Partitions",
|
||||
tags::integration &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
mfem::Mesh serialMesh = field_mfem_test_utils::make_parallel_split_mesh();
|
||||
|
||||
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
||||
|
||||
auto fec = field::Field<field::Enthalpy>::make_fec<field::Enthalpy::Scalar>(2);
|
||||
|
||||
auto space = field::Field<field::Enthalpy>::make_fespace<field::Enthalpy::Scalar>(mesh, *fec);
|
||||
|
||||
REQUIRE(space != nullptr);
|
||||
|
||||
const auto support = field::resolve_field_dof_support<field::Enthalpy, field_mfem_test_utils::Schema>(*space);
|
||||
|
||||
CHECK(support.activeVDofMarker.Size() == space->GetVSize());
|
||||
|
||||
CHECK(support.activeVDofs.Size() + support.inactiveVDofs.Size() == space->GetVSize());
|
||||
|
||||
CHECK(support.activeTrueDofMarker.Size() == space->GetTrueVSize());
|
||||
|
||||
CHECK(support.activeTrueDofs.Size() + support.inactiveTrueDofs.Size() == space->GetTrueVSize());
|
||||
|
||||
/*
|
||||
* Every local DOF touched by a supported element must be active
|
||||
* after shared-DOF synchronization.
|
||||
*/
|
||||
for (int elementId = 0; elementId < mesh.GetNE(); ++elementId) {
|
||||
const int materialId = mesh.GetAttribute(elementId);
|
||||
|
||||
const bool stellar =
|
||||
field_mfem_test_utils::Schema::template attribute_belongs_to<mean_field::utils::domain::Stellar>(
|
||||
materialId
|
||||
);
|
||||
|
||||
if (!stellar) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::vector<int> vdofs = field_mfem_test_utils::decoded_element_vdofs(*space, elementId);
|
||||
|
||||
for (const int vdof : vdofs) {
|
||||
CAPTURE(elementId, vdof);
|
||||
|
||||
CHECK(support.activeVDofMarker[vdof] == 1);
|
||||
}
|
||||
}
|
||||
|
||||
const long long globalActiveTrueDofs = field_mfem_test_utils::global_sum(support.activeTrueDofs.Size());
|
||||
|
||||
const long long globalInactiveTrueDofs = field_mfem_test_utils::global_sum(support.inactiveTrueDofs.Size());
|
||||
|
||||
/*
|
||||
* The split mesh contains a finite stellar region and a finite
|
||||
* vacuum region with order-three H1 structure, so both categories
|
||||
* must genuinely exist globally.
|
||||
*/
|
||||
CHECK(globalActiveTrueDofs > 0);
|
||||
|
||||
CHECK(globalInactiveTrueDofs > 0);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM Parallel All Support Activates Every True Displacement DOF",
|
||||
tags::integration &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
mfem::Mesh serialMesh = field_mfem_test_utils::make_parallel_split_mesh();
|
||||
|
||||
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
||||
|
||||
auto fec = field::Field<field::Displacement>::make_fec<field::Displacement::Vector>(2);
|
||||
|
||||
auto space = field::Field<field::Displacement>::make_fespace<field::Displacement::Vector>(mesh, *fec);
|
||||
|
||||
REQUIRE(space != nullptr);
|
||||
|
||||
const auto support = field::resolve_field_dof_support<field::Displacement, field_mfem_test_utils::Schema>(*space);
|
||||
|
||||
CHECK(support.inactiveVDofs.Size() == 0);
|
||||
|
||||
CHECK(support.activeVDofs.Size() == space->GetVSize());
|
||||
|
||||
CHECK(support.inactiveTrueDofs.Size() == 0);
|
||||
|
||||
CHECK(support.activeTrueDofs.Size() == space->GetTrueVSize());
|
||||
|
||||
for (int index = 0; index < support.activeTrueDofMarker.Size(); ++index) {
|
||||
CHECK(support.activeTrueDofMarker[index] == 1);
|
||||
}
|
||||
}
|
||||
427
tests/field/field_registry.cpp
Normal file
427
tests/field/field_registry.cpp
Normal file
@@ -0,0 +1,427 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace field_registry_test_utils {
|
||||
namespace field = mean_field::field;
|
||||
namespace domain = mean_field::utils::domain;
|
||||
namespace quadrature = mean_field::quadrature;
|
||||
|
||||
template <typename ListT> struct TypeListSize;
|
||||
|
||||
template <typename... Ts>
|
||||
struct TypeListSize<field::TypeList<Ts...>> : std::integral_constant<std::size_t, sizeof...(Ts)> { };
|
||||
|
||||
template <typename ListT> inline constexpr std::size_t typeListSize = TypeListSize<ListT>::value;
|
||||
|
||||
struct MissingSupportField {
|
||||
static constexpr std::string_view name = "missing_support";
|
||||
|
||||
using Quantities = field::TypeList<field::GlobalScalarQ>;
|
||||
|
||||
using Constraints = field::TypeList<>;
|
||||
|
||||
using FormList = field::TypeList<>;
|
||||
};
|
||||
|
||||
struct InvalidSupportField {
|
||||
static constexpr std::string_view name = "invalid_support";
|
||||
|
||||
struct InvalidSupport { };
|
||||
|
||||
using Support = InvalidSupport;
|
||||
|
||||
using Quantities = field::TypeList<field::GlobalScalarQ>;
|
||||
|
||||
using Constraints = field::TypeList<>;
|
||||
|
||||
using FormList = field::TypeList<>;
|
||||
};
|
||||
|
||||
struct InvalidQuantityListField {
|
||||
static constexpr std::string_view name = "invalid_quantity_list";
|
||||
|
||||
using Support = field::NonSpatialSupport;
|
||||
|
||||
using Quantities = field::TypeList<int>;
|
||||
|
||||
using Constraints = field::TypeList<>;
|
||||
|
||||
using FormList = field::TypeList<>;
|
||||
};
|
||||
|
||||
struct InvalidFormListField {
|
||||
static constexpr std::string_view name = "invalid_form_list";
|
||||
|
||||
using Support = field::NonSpatialSupport;
|
||||
|
||||
using Quantities = field::TypeList<field::GlobalScalarQ>;
|
||||
|
||||
using Constraints = field::TypeList<>;
|
||||
|
||||
using FormList = field::TypeList<int>;
|
||||
};
|
||||
} // namespace field_registry_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
"Field Registry Recognizes Every Production Field And Rejects Incomplete Definitions",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
STATIC_REQUIRE(field::FieldTag<field::Density>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldTag<field::Gravity>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldTag<field::Displacement>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldTag<field::Enthalpy>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldTag<field::BarotropicConstant>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::FieldTag<field_registry_test_utils::MissingSupportField>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::FieldTag<field_registry_test_utils::InvalidSupportField>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::FieldTag<field_registry_test_utils::InvalidQuantityListField>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::FieldTag<field_registry_test_utils::InvalidFormListField>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Registry Assigns The Intended Semantic Support To Every Production Field",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
namespace domain = mean_field::utils::domain;
|
||||
|
||||
STATIC_REQUIRE(field::DomainSupportedField<field::Density>);
|
||||
|
||||
STATIC_REQUIRE(field::DomainSupportedField<field::Enthalpy>);
|
||||
|
||||
STATIC_REQUIRE(field::DomainSupportedField<field::Gravity>);
|
||||
|
||||
STATIC_REQUIRE(field::DomainSupportedField<field::Displacement>);
|
||||
|
||||
STATIC_REQUIRE(field::NonSpatialField<field::BarotropicConstant>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<field::FieldDomainT<field::Density>, domain::Stellar>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<field::FieldDomainT<field::Enthalpy>, domain::Stellar>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<field::FieldDomainT<field::Gravity>, domain::All>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<field::FieldDomainT<field::Displacement>, domain::All>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<field::FieldSupportT<field::BarotropicConstant>, field::NonSpatialSupport>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Density Registry Definition Is Complete And Self Consistent",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
namespace quadrature = mean_field::quadrature;
|
||||
|
||||
CHECK(field::Density::name == std::string_view{"density"});
|
||||
|
||||
CHECK(field::Density::Scalar::symbol == std::string_view{"ρ"});
|
||||
|
||||
STATIC_REQUIRE(field::Density::scalarOrder == 2);
|
||||
|
||||
STATIC_REQUIRE(field::Density::Scalar::rankValue == 0);
|
||||
|
||||
STATIC_REQUIRE(field::Density::Scalar::familyOrder == field::Density::scalarOrder);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::Density::Scalar::Space, field::L2>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::Density::Scalar::Relation, field::FieldRelation::Independent>);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::Density::Quantities> == 1);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::Density::Constraints> == 0);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::Density::FormList> == 8);
|
||||
|
||||
STATIC_REQUIRE(field::Density::constraintsAreValid);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Density::Form::ProjectionMass, field::Density::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Density::Form::ProjectionSource, field::Density::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Density::Form::EosClosureMass, field::Density::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Density::Form::MassConservation, field::Density::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Density::Form::MassNormalization, field::Density::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Density::Form::CenterOfMass, field::Density::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Density::Form::Quadrupole, field::Density::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Density::Form::ErrorNorm, field::Density::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::Density::Form::ProjectionMass::policyKey == quadrature::Term::density_projection);
|
||||
|
||||
STATIC_REQUIRE(field::Density::Form::ProjectionMass::dynamicOrderCount == 0);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Density::Form::ProjectionMass::Operands,
|
||||
field::TypeList<field::Operand<field::Density::Scalar>, field::Operand<field::Density::Scalar>>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(field::Density::Form::ProjectionSource::dynamicOrderCount == 1);
|
||||
|
||||
STATIC_REQUIRE(field::Density::Form::EosClosureMass::policyKey == quadrature::Term::eos_closure);
|
||||
|
||||
STATIC_REQUIRE(field::Density::Form::MassConservation::policyKey == quadrature::Term::mass_conservation);
|
||||
|
||||
STATIC_REQUIRE(field::Density::Form::MassNormalization::policyKey == quadrature::Term::mass_normalization);
|
||||
|
||||
STATIC_REQUIRE(field::Density::Form::CenterOfMass::dynamicOrderCount == 1);
|
||||
|
||||
STATIC_REQUIRE(field::Density::Form::Quadrupole::dynamicOrderCount == 1);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Gravity Registry Defines A Stable Mixed RT L2 Pair And All Registered Forms",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
namespace quadrature = mean_field::quadrature;
|
||||
|
||||
CHECK(field::Gravity::name == std::string_view{"gravity"});
|
||||
|
||||
CHECK(field::Gravity::Potential::symbol == std::string_view{"φ"});
|
||||
|
||||
CHECK(field::Gravity::Flux::symbol == std::string_view{"∇φ"});
|
||||
|
||||
STATIC_REQUIRE(field::Gravity::potentialOrder == 2);
|
||||
|
||||
STATIC_REQUIRE(field::Gravity::fluxOrder == 2);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::Gravity::Potential::Space, field::L2>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::Gravity::Flux::Space, field::RT>);
|
||||
|
||||
STATIC_REQUIRE(field::Gravity::Potential::rankValue == 0);
|
||||
|
||||
STATIC_REQUIRE(field::Gravity::Flux::rankValue == 1);
|
||||
|
||||
STATIC_REQUIRE(field::DerivedQuantity<field::Gravity::Flux>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<field::RelationTargetT<field::Gravity::Flux>, field::Gravity::Potential>);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::Gravity::Quantities> == 2);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::Gravity::Constraints> == 1);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::Gravity::FormList> == 7);
|
||||
|
||||
STATIC_REQUIRE(field::Gravity::constraintsAreValid);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Gravity::Form::HDivMass, field::Gravity::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Gravity::Form::DivergenceCoupling, field::Gravity::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Gravity::Form::Boundary, field::Gravity::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Gravity::Form::SourceLinear, field::Gravity::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Gravity::Form::SourceProjection, field::Gravity::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Gravity::Form::PotentialErrorNorm, field::Gravity::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Gravity::Form::FluxErrorNorm, field::Gravity::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::Gravity::Form::HDivMass::policyKey == quadrature::Term::gravity_hdiv_mass);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Gravity::Form::HDivMass::Operands,
|
||||
field::TypeList<field::Operand<field::Gravity::Flux>, field::Operand<field::Gravity::Flux>>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Gravity::Form::DivergenceCoupling::Operands,
|
||||
field::TypeList<
|
||||
field::Operand<field::Gravity::Flux, field::FieldOperation::Divergence>,
|
||||
field::Operand<field::Gravity::Potential>>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Gravity::Form::Boundary::Operands,
|
||||
field::TypeList<
|
||||
field::Operand<field::Gravity::Flux, field::FieldOperation::NormalTrace>,
|
||||
field::Operand<field::Gravity::Flux, field::FieldOperation::NormalTrace>>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Gravity::Form::SourceLinear::Operands,
|
||||
field::TypeList<field::Operand<field::Density::Scalar>, field::Operand<field::Gravity::Potential>>>
|
||||
);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Displacement Registry Preserves Vector H1 Geometry And Force Forms",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
namespace quadrature = mean_field::quadrature;
|
||||
|
||||
CHECK(field::Displacement::name == std::string_view{"displacement"});
|
||||
|
||||
CHECK(field::Displacement::Vector::symbol == std::string_view{"d"});
|
||||
|
||||
STATIC_REQUIRE(field::Displacement::vectorOrder == 3);
|
||||
|
||||
STATIC_REQUIRE(field::Displacement::Vector::rankValue == 1);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::Displacement::Vector::Space, field::H1>);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::Displacement::FormList> == 4);
|
||||
|
||||
STATIC_REQUIRE(field::Displacement::constraintsAreValid);
|
||||
|
||||
STATIC_REQUIRE(field::Displacement::Form::MeshExtension::policyKey == quadrature::Term::mesh_extension);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Displacement::Form::MeshExtension::Operands,
|
||||
field::TypeList<
|
||||
field::Operand<field::Displacement::Vector, field::FieldOperation::Gradient>,
|
||||
field::Operand<field::Displacement::Vector, field::FieldOperation::Gradient>>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Displacement::Form::GravityForce::Operands,
|
||||
field::TypeList<
|
||||
field::Operand<field::Density::Scalar>, field::Operand<field::Gravity::Flux>,
|
||||
field::Operand<field::Displacement::Vector, field::FieldOperation::Gradient>,
|
||||
field::Operand<field::Displacement::Vector>>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(field::Displacement::Form::CentrifugalForce::dynamicOrderCount == 1);
|
||||
|
||||
STATIC_REQUIRE(field::Displacement::Form::CentrifugalForce::policyKey == quadrature::Term::centrifugal);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Enthalpy Registry Preserves Continuous Stellar Field And Coupled Forms",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
namespace quadrature = mean_field::quadrature;
|
||||
|
||||
CHECK(field::Enthalpy::name == std::string_view{"specific_enthalpy"});
|
||||
|
||||
CHECK(field::Enthalpy::Scalar::symbol == std::string_view{"h"});
|
||||
|
||||
STATIC_REQUIRE(field::Enthalpy::scalarOrder == 3);
|
||||
|
||||
STATIC_REQUIRE(field::Enthalpy::Scalar::rankValue == 0);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::Enthalpy::Scalar::Space, field::H1>);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::Enthalpy::FormList> == 9);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Enthalpy::Form::EosClosureSource, field::Enthalpy::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Enthalpy::Form::EquilibriumEnthalpy, field::Enthalpy::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Enthalpy::Form::EquilibriumGravity, field::Enthalpy::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Enthalpy::Form::EquilibriumRotation, field::Enthalpy::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Enthalpy::Form::EquilibriumConstant, field::Enthalpy::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Enthalpy::Form::IsobaricSurface, field::Enthalpy::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Enthalpy::Form::PressureIntegral, field::Enthalpy::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Enthalpy::Form::PressureForce, field::Enthalpy::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Enthalpy::Form::ErrorNorm, field::Enthalpy::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::Enthalpy::Form::EosClosureSource::dynamicOrderCount == 1);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Enthalpy::Form::EosClosureSource::Operands,
|
||||
field::TypeList<field::Operand<field::Enthalpy::Scalar>, field::Operand<field::Density::Scalar>>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Enthalpy::Form::EquilibriumGravity::Operands,
|
||||
field::TypeList<field::Operand<field::Gravity::Potential>, field::Operand<field::Enthalpy::Scalar>>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Enthalpy::Form::EquilibriumConstant::Operands,
|
||||
field::TypeList<field::Operand<field::BarotropicConstant::Scalar>, field::Operand<field::Enthalpy::Scalar>>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(field::Enthalpy::Form::IsobaricSurface::policyKey == quadrature::Term::isobaric_surface);
|
||||
|
||||
STATIC_REQUIRE(field::Enthalpy::Form::PressureIntegral::policyKey == quadrature::Term::pressure_integral);
|
||||
|
||||
STATIC_REQUIRE(field::Enthalpy::Form::PressureForce::policyKey == quadrature::Term::pressure_force);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Enthalpy::Form::PressureForce::Operands,
|
||||
field::TypeList<
|
||||
field::Operand<field::Enthalpy::Scalar>,
|
||||
field::Operand<field::Displacement::Vector, field::FieldOperation::Gradient>>>
|
||||
);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Barotropic Constant Registry Is A Non Spatial Unit Sized Scalar",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
CHECK(field::BarotropicConstant::name == std::string_view{"barotropic_constant"});
|
||||
|
||||
CHECK(field::BarotropicConstant::Scalar::symbol == std::string_view{"C"});
|
||||
|
||||
STATIC_REQUIRE(field::GlobalScalarQuantity<field::BarotropicConstant::Scalar>);
|
||||
|
||||
STATIC_REQUIRE(field::BarotropicConstant::Scalar::staticBlockSize == 1);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::BarotropicConstant::Quantities> == 1);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::BarotropicConstant::Constraints> == 0);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::BarotropicConstant::FormList> == 0);
|
||||
|
||||
STATIC_REQUIRE(field::BarotropicConstant::constraintsAreValid);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,21 +17,14 @@ TEST_CASE(
|
||||
constexpr double jacobian_tolerance = 1.0e-8;
|
||||
constexpr double zero_tolerance = 1.0e-14;
|
||||
|
||||
constexpr int velocity_block =
|
||||
solver::block_index(solver::FieldBlock::velocity);
|
||||
constexpr int density_block =
|
||||
solver::block_index(solver::FieldBlock::density);
|
||||
constexpr int gravity_gradient_block =
|
||||
solver::block_index(solver::FieldBlock::gravity_gradient);
|
||||
constexpr int gravity_potential_block =
|
||||
solver::block_index(solver::FieldBlock::gravity_potential);
|
||||
constexpr int displacement_block =
|
||||
solver::block_index(solver::FieldBlock::displacement);
|
||||
constexpr int block_count = solver::field_block_count;
|
||||
constexpr int velocity_block = solver::block_index(solver::FieldBlock::velocity);
|
||||
constexpr int density_block = solver::block_index(solver::FieldBlock::density);
|
||||
constexpr int gravity_gradient_block = solver::block_index(solver::FieldBlock::gravity_gradient);
|
||||
constexpr int gravity_potential_block = solver::block_index(solver::FieldBlock::gravity_potential);
|
||||
constexpr int displacement_block = solver::block_index(solver::FieldBlock::displacement);
|
||||
constexpr int block_count = solver::field_block_count;
|
||||
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian3D(
|
||||
1, 1, 1, mfem::Element::HEXAHEDRON, 1.0, 1.0, 1.0
|
||||
);
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian3D(1, 1, 1, mfem::Element::HEXAHEDRON, 1.0, 1.0, 1.0);
|
||||
|
||||
mfem::H1_FECollection velocity_fec(2, dim);
|
||||
mfem::L2_FECollection density_fec(1, dim);
|
||||
@@ -39,54 +32,35 @@ TEST_CASE(
|
||||
mfem::L2_FECollection gravity_potential_fec(1, dim);
|
||||
mfem::H1_FECollection displacement_fec(2, dim);
|
||||
|
||||
mfem::FiniteElementSpace velocity_fes(
|
||||
&mesh, &velocity_fec, dim, mfem::Ordering::byVDIM
|
||||
);
|
||||
mfem::FiniteElementSpace velocity_fes(&mesh, &velocity_fec, dim, mfem::Ordering::byVDIM);
|
||||
mfem::FiniteElementSpace density_fes(&mesh, &density_fec);
|
||||
mfem::FiniteElementSpace gravity_gradient_fes(&mesh, &gravity_gradient_fec);
|
||||
mfem::FiniteElementSpace gravity_potential_fes(
|
||||
&mesh, &gravity_potential_fec
|
||||
);
|
||||
mfem::FiniteElementSpace displacement_fes(
|
||||
&mesh, &displacement_fec, dim, mfem::Ordering::byVDIM
|
||||
);
|
||||
mfem::FiniteElementSpace gravity_potential_fes(&mesh, &gravity_potential_fec);
|
||||
mfem::FiniteElementSpace displacement_fes(&mesh, &displacement_fec, dim, mfem::Ordering::byVDIM);
|
||||
|
||||
mfem::GridFunction displacement(&displacement_fes);
|
||||
displacement = 0.0;
|
||||
|
||||
mapping::DomainMapper domain_mapper(displacement, 1.0, 2.0);
|
||||
INFO(
|
||||
std::format(
|
||||
"Domain mapping is has displacement field: {}",
|
||||
domain_mapper.HasDisplacementField()
|
||||
)
|
||||
);
|
||||
INFO(
|
||||
std::format(
|
||||
"Domain mapping is identity: {}", domain_mapper.CalcIsIdentity()
|
||||
)
|
||||
);
|
||||
INFO(std::format("Domain mapping is has displacement field: {}", domain_mapper.HasDisplacementField()));
|
||||
INFO(std::format("Domain mapping is identity: {}", domain_mapper.CalcIsIdentity()));
|
||||
|
||||
REQUIRE(domain_mapper.CalcIsIdentity());
|
||||
|
||||
const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0);
|
||||
const mfem::FiniteElement *density_element = density_fes.GetFE(0);
|
||||
const mfem::FiniteElement *gravity_gradient_element =
|
||||
gravity_gradient_fes.GetFE(0);
|
||||
const mfem::FiniteElement *gravity_potential_element =
|
||||
gravity_potential_fes.GetFE(0);
|
||||
const mfem::FiniteElement *displacement_element = displacement_fes.GetFE(0);
|
||||
mfem::ElementTransformation *transformation =
|
||||
mesh.GetElementTransformation(0);
|
||||
const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0);
|
||||
const mfem::FiniteElement *density_element = density_fes.GetFE(0);
|
||||
const mfem::FiniteElement *gravity_gradient_element = gravity_gradient_fes.GetFE(0);
|
||||
const mfem::FiniteElement *gravity_potential_element = gravity_potential_fes.GetFE(0);
|
||||
const mfem::FiniteElement *displacement_element = displacement_fes.GetFE(0);
|
||||
mfem::ElementTransformation *transformation = mesh.GetElementTransformation(0);
|
||||
|
||||
const int velocity_dofs_count = velocity_element->GetDof();
|
||||
const int density_dofs_count = density_element->GetDof();
|
||||
const int gravity_gradient_dofs_count = gravity_gradient_element->GetDof();
|
||||
const int gravity_potential_dofs_count =
|
||||
gravity_potential_element->GetDof();
|
||||
const int displacement_dofs_count = displacement_element->GetDof();
|
||||
const int velocity_size = dim * velocity_dofs_count;
|
||||
const int displacement_size = dim * displacement_dofs_count;
|
||||
const int velocity_dofs_count = velocity_element->GetDof();
|
||||
const int density_dofs_count = density_element->GetDof();
|
||||
const int gravity_gradient_dofs_count = gravity_gradient_element->GetDof();
|
||||
const int gravity_potential_dofs_count = gravity_potential_element->GetDof();
|
||||
const int displacement_dofs_count = displacement_element->GetDof();
|
||||
const int velocity_size = dim * velocity_dofs_count;
|
||||
const int displacement_size = dim * displacement_dofs_count;
|
||||
|
||||
mfem::Vector velocity_dofs(velocity_size);
|
||||
mfem::Vector density_dofs(density_dofs_count);
|
||||
@@ -116,9 +90,8 @@ TEST_CASE(
|
||||
}
|
||||
|
||||
for (int i = 0; i < gravity_gradient_dofs_count; ++i) {
|
||||
const double sign = i % 3 == 0 ? -1.0 : 1.0;
|
||||
gravity_gradient_direction(i) =
|
||||
sign * (0.03 + 0.005 * static_cast<double>(i));
|
||||
const double sign = i % 3 == 0 ? -1.0 : 1.0;
|
||||
gravity_gradient_direction(i) = sign * (0.03 + 0.005 * static_cast<double>(i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < velocity_size; ++i) {
|
||||
@@ -161,14 +134,10 @@ TEST_CASE(
|
||||
);
|
||||
|
||||
const int maximum_order = std::max(
|
||||
velocity_element->GetOrder(),
|
||||
std::max(
|
||||
density_element->GetOrder(), gravity_gradient_element->GetOrder()
|
||||
)
|
||||
);
|
||||
const mfem::IntegrationRule &integration_rule = mfem::IntRules.Get(
|
||||
velocity_element->GetGeomType(), 2 * maximum_order + 8
|
||||
velocity_element->GetOrder(), std::max(density_element->GetOrder(), gravity_gradient_element->GetOrder())
|
||||
);
|
||||
const mfem::IntegrationRule &integration_rule =
|
||||
mfem::IntRules.Get(velocity_element->GetGeomType(), 2 * maximum_order + 8);
|
||||
integrator.SetIntegrationRule(integration_rule);
|
||||
|
||||
mfem::DenseMatrix dv_dv(velocity_size, velocity_size);
|
||||
@@ -180,9 +149,7 @@ TEST_CASE(
|
||||
dv_dgrad_phi = 1.0;
|
||||
dv_ddisplacement = 1.0;
|
||||
|
||||
mfem::Array2D<mfem::DenseMatrix *> element_matrices(
|
||||
block_count, block_count
|
||||
);
|
||||
mfem::Array2D<mfem::DenseMatrix *> element_matrices(block_count, block_count);
|
||||
|
||||
for (int row = 0; row < block_count; ++row) {
|
||||
for (int column = 0; column < block_count; ++column) {
|
||||
@@ -193,25 +160,19 @@ TEST_CASE(
|
||||
element_matrices(velocity_block, velocity_block) = &dv_dv;
|
||||
element_matrices(velocity_block, density_block) = &dv_drho;
|
||||
element_matrices(velocity_block, gravity_gradient_block) = &dv_dgrad_phi;
|
||||
element_matrices(velocity_block, displacement_block) = &dv_ddisplacement;
|
||||
element_matrices(velocity_block, displacement_block) = &dv_ddisplacement;
|
||||
|
||||
integrator.AssembleElementGrad(
|
||||
elements, *transformation, element_state, element_matrices
|
||||
);
|
||||
integrator.AssembleElementGrad(elements, *transformation, element_state, element_matrices);
|
||||
|
||||
auto assemble_velocity_residual =
|
||||
[&](const mfem::Vector &density_state,
|
||||
const mfem::Vector &gravity_gradient_state) {
|
||||
element_state[density_block] = &density_state;
|
||||
element_state[gravity_gradient_block] = &gravity_gradient_state;
|
||||
integrator.AssembleElementVector(
|
||||
elements, *transformation, element_state, element_residual
|
||||
);
|
||||
return mfem::Vector(velocity_residual);
|
||||
};
|
||||
auto assemble_velocity_residual = [&](const mfem::Vector &density_state,
|
||||
const mfem::Vector &gravity_gradient_state) {
|
||||
element_state[density_block] = &density_state;
|
||||
element_state[gravity_gradient_block] = &gravity_gradient_state;
|
||||
integrator.AssembleElementVector(elements, *transformation, element_state, element_residual);
|
||||
return mfem::Vector(velocity_residual);
|
||||
};
|
||||
|
||||
auto relative_error = [](mfem::Vector computed,
|
||||
const mfem::Vector &reference) {
|
||||
auto relative_error = [](mfem::Vector computed, const mfem::Vector &reference) {
|
||||
computed -= reference;
|
||||
return computed.Norml2() / std::max(reference.Norml2(), 1.0e-30);
|
||||
};
|
||||
@@ -221,10 +182,8 @@ TEST_CASE(
|
||||
density_plus.Add(finite_difference_step, density_direction);
|
||||
density_minus.Add(-finite_difference_step, density_direction);
|
||||
|
||||
mfem::Vector density_residual_plus =
|
||||
assemble_velocity_residual(density_plus, gravity_gradient_dofs);
|
||||
mfem::Vector density_residual_minus =
|
||||
assemble_velocity_residual(density_minus, gravity_gradient_dofs);
|
||||
mfem::Vector density_residual_plus = assemble_velocity_residual(density_plus, gravity_gradient_dofs);
|
||||
mfem::Vector density_residual_minus = assemble_velocity_residual(density_minus, gravity_gradient_dofs);
|
||||
mfem::Vector density_finite_difference(density_residual_plus);
|
||||
density_finite_difference -= density_residual_minus;
|
||||
density_finite_difference *= 0.5 / finite_difference_step;
|
||||
@@ -234,17 +193,11 @@ TEST_CASE(
|
||||
|
||||
mfem::Vector gravity_gradient_plus(gravity_gradient_dofs);
|
||||
mfem::Vector gravity_gradient_minus(gravity_gradient_dofs);
|
||||
gravity_gradient_plus.Add(
|
||||
finite_difference_step, gravity_gradient_direction
|
||||
);
|
||||
gravity_gradient_minus.Add(
|
||||
-finite_difference_step, gravity_gradient_direction
|
||||
);
|
||||
gravity_gradient_plus.Add(finite_difference_step, gravity_gradient_direction);
|
||||
gravity_gradient_minus.Add(-finite_difference_step, gravity_gradient_direction);
|
||||
|
||||
mfem::Vector gravity_residual_plus =
|
||||
assemble_velocity_residual(density_dofs, gravity_gradient_plus);
|
||||
mfem::Vector gravity_residual_minus =
|
||||
assemble_velocity_residual(density_dofs, gravity_gradient_minus);
|
||||
mfem::Vector gravity_residual_plus = assemble_velocity_residual(density_dofs, gravity_gradient_plus);
|
||||
mfem::Vector gravity_residual_minus = assemble_velocity_residual(density_dofs, gravity_gradient_minus);
|
||||
mfem::Vector gravity_finite_difference(gravity_residual_plus);
|
||||
gravity_finite_difference -= gravity_residual_minus;
|
||||
gravity_finite_difference *= 0.5 / finite_difference_step;
|
||||
@@ -258,19 +211,11 @@ TEST_CASE(
|
||||
mfem::Vector combined_gravity_minus(gravity_gradient_dofs);
|
||||
combined_density_plus.Add(finite_difference_step, density_direction);
|
||||
combined_density_minus.Add(-finite_difference_step, density_direction);
|
||||
combined_gravity_plus.Add(
|
||||
finite_difference_step, gravity_gradient_direction
|
||||
);
|
||||
combined_gravity_minus.Add(
|
||||
-finite_difference_step, gravity_gradient_direction
|
||||
);
|
||||
combined_gravity_plus.Add(finite_difference_step, gravity_gradient_direction);
|
||||
combined_gravity_minus.Add(-finite_difference_step, gravity_gradient_direction);
|
||||
|
||||
mfem::Vector combined_residual_plus = assemble_velocity_residual(
|
||||
combined_density_plus, combined_gravity_plus
|
||||
);
|
||||
mfem::Vector combined_residual_minus = assemble_velocity_residual(
|
||||
combined_density_minus, combined_gravity_minus
|
||||
);
|
||||
mfem::Vector combined_residual_plus = assemble_velocity_residual(combined_density_plus, combined_gravity_plus);
|
||||
mfem::Vector combined_residual_minus = assemble_velocity_residual(combined_density_minus, combined_gravity_minus);
|
||||
mfem::Vector combined_finite_difference(combined_residual_plus);
|
||||
combined_finite_difference -= combined_residual_minus;
|
||||
combined_finite_difference *= 0.5 / finite_difference_step;
|
||||
@@ -283,39 +228,19 @@ TEST_CASE(
|
||||
dv_dv.Mult(velocity_direction, inactive_velocity_action);
|
||||
dv_ddisplacement.Mult(displacement_direction, inactive_displacement_action);
|
||||
|
||||
const double density_relative_error =
|
||||
relative_error(density_finite_difference, density_jacobian_action);
|
||||
const double gravity_relative_error =
|
||||
relative_error(gravity_finite_difference, gravity_jacobian_action);
|
||||
const double combined_relative_error =
|
||||
relative_error(combined_finite_difference, combined_jacobian_action);
|
||||
const double density_relative_error = relative_error(density_finite_difference, density_jacobian_action);
|
||||
const double gravity_relative_error = relative_error(gravity_finite_difference, gravity_jacobian_action);
|
||||
const double combined_relative_error = relative_error(combined_finite_difference, combined_jacobian_action);
|
||||
|
||||
INFO("Density Jacobian relative error = " << density_relative_error);
|
||||
INFO(
|
||||
"Gravity-gradient Jacobian relative error = " << gravity_relative_error
|
||||
);
|
||||
INFO("Gravity-gradient Jacobian relative error = " << gravity_relative_error);
|
||||
INFO("Combined Jacobian relative error = " << combined_relative_error);
|
||||
|
||||
CHECK_THAT(
|
||||
density_relative_error,
|
||||
Catch::Matchers::WithinAbs(0.0, jacobian_tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
gravity_relative_error,
|
||||
Catch::Matchers::WithinAbs(0.0, jacobian_tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
combined_relative_error,
|
||||
Catch::Matchers::WithinAbs(0.0, jacobian_tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
inactive_velocity_action.Norml2(),
|
||||
Catch::Matchers::WithinAbs(0.0, zero_tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
inactive_displacement_action.Norml2(),
|
||||
Catch::Matchers::WithinAbs(0.0, zero_tolerance)
|
||||
);
|
||||
CHECK_THAT(density_relative_error, Catch::Matchers::WithinAbs(0.0, jacobian_tolerance));
|
||||
CHECK_THAT(gravity_relative_error, Catch::Matchers::WithinAbs(0.0, jacobian_tolerance));
|
||||
CHECK_THAT(combined_relative_error, Catch::Matchers::WithinAbs(0.0, jacobian_tolerance));
|
||||
CHECK_THAT(inactive_velocity_action.Norml2(), Catch::Matchers::WithinAbs(0.0, zero_tolerance));
|
||||
CHECK_THAT(inactive_displacement_action.Norml2(), Catch::Matchers::WithinAbs(0.0, zero_tolerance));
|
||||
|
||||
mfem::Vector field_coupled_density_action(density_jacobian_action);
|
||||
|
||||
@@ -326,68 +251,46 @@ TEST_CASE(
|
||||
dv_ddisplacement = 1.0;
|
||||
element_state[density_block] = &density_dofs;
|
||||
element_state[gravity_gradient_block] = &gravity_gradient_dofs;
|
||||
integrator.AssembleElementGrad(
|
||||
elements, *transformation, element_state, element_matrices
|
||||
);
|
||||
integrator.AssembleElementGrad(elements, *transformation, element_state, element_matrices);
|
||||
|
||||
mfem::Vector minimal_density_action(velocity_size);
|
||||
mfem::Vector minimal_gravity_action(velocity_size);
|
||||
dv_drho.Mult(density_direction, minimal_density_action);
|
||||
dv_dgrad_phi.Mult(gravity_gradient_direction, minimal_gravity_action);
|
||||
|
||||
const double minimal_density_difference =
|
||||
relative_error(minimal_density_action, field_coupled_density_action);
|
||||
const double minimal_density_difference = relative_error(minimal_density_action, field_coupled_density_action);
|
||||
|
||||
INFO(
|
||||
"Minimal-mode density-block difference = " << minimal_density_difference
|
||||
);
|
||||
INFO("Minimal-mode density-block difference = " << minimal_density_difference);
|
||||
|
||||
CHECK_THAT(
|
||||
minimal_density_difference,
|
||||
Catch::Matchers::WithinAbs(0.0, zero_tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
minimal_gravity_action.Norml2(),
|
||||
Catch::Matchers::WithinAbs(0.0, zero_tolerance)
|
||||
);
|
||||
CHECK_THAT(minimal_density_difference, Catch::Matchers::WithinAbs(0.0, zero_tolerance));
|
||||
CHECK_THAT(minimal_gravity_action.Norml2(), Catch::Matchers::WithinAbs(0.0, zero_tolerance));
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Gravity Force Integrator Matches Manufactured Cartesian Load",
|
||||
tags::unit &tags::solver &tags::integrator &tags::gravity
|
||||
) {
|
||||
constexpr int dim = 3;
|
||||
constexpr double tolerance = 1.0e-12;
|
||||
constexpr int dim = 3;
|
||||
constexpr double tolerance = 1.0e-12;
|
||||
|
||||
constexpr int velocity_block =
|
||||
solver::block_index(solver::FieldBlock::velocity);
|
||||
constexpr int density_block =
|
||||
solver::block_index(solver::FieldBlock::density);
|
||||
constexpr int gravity_gradient_block =
|
||||
solver::block_index(solver::FieldBlock::gravity_gradient);
|
||||
constexpr int gravity_potential_block =
|
||||
solver::block_index(solver::FieldBlock::gravity_potential);
|
||||
constexpr int displacement_block =
|
||||
solver::block_index(solver::FieldBlock::displacement);
|
||||
constexpr int block_count = solver::field_block_count;
|
||||
constexpr int velocity_block = solver::block_index(solver::FieldBlock::velocity);
|
||||
constexpr int density_block = solver::block_index(solver::FieldBlock::density);
|
||||
constexpr int gravity_gradient_block = solver::block_index(solver::FieldBlock::gravity_gradient);
|
||||
constexpr int gravity_potential_block = solver::block_index(solver::FieldBlock::gravity_potential);
|
||||
constexpr int displacement_block = solver::block_index(solver::FieldBlock::displacement);
|
||||
constexpr int block_count = solver::field_block_count;
|
||||
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian3D(
|
||||
1, 1, 1, mfem::Element::HEXAHEDRON, 1.0, 1.0, 1.0
|
||||
);
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian3D(1, 1, 1, mfem::Element::HEXAHEDRON, 1.0, 1.0, 1.0);
|
||||
|
||||
mfem::H1_FECollection velocity_fec(1, dim);
|
||||
mfem::L2_FECollection density_fec(1, dim);
|
||||
mfem::RT_FECollection gravity_gradient_fec(0, dim);
|
||||
mfem::H1_FECollection displacement_fec(1, dim);
|
||||
|
||||
mfem::FiniteElementSpace velocity_fes(
|
||||
&mesh, &velocity_fec, dim, mfem::Ordering::byVDIM
|
||||
);
|
||||
mfem::FiniteElementSpace velocity_fes(&mesh, &velocity_fec, dim, mfem::Ordering::byVDIM);
|
||||
mfem::FiniteElementSpace density_fes(&mesh, &density_fec);
|
||||
mfem::FiniteElementSpace gravity_gradient_fes(&mesh, &gravity_gradient_fec);
|
||||
mfem::FiniteElementSpace displacement_fes(
|
||||
&mesh, &displacement_fec, dim, mfem::Ordering::byVDIM
|
||||
);
|
||||
mfem::FiniteElementSpace displacement_fes(&mesh, &displacement_fec, dim, mfem::Ordering::byVDIM);
|
||||
|
||||
mfem::GridFunction displacement(&displacement_fes);
|
||||
displacement = 0.0;
|
||||
@@ -396,10 +299,9 @@ TEST_CASE(
|
||||
|
||||
REQUIRE(domain_mapper.CalcIsIdentity());
|
||||
|
||||
auto reference_density = [](const mfem::Vector &x) { return 1.0 + x(0); };
|
||||
auto reference_density = [](const mfem::Vector &x) { return 1.0 + x(0); };
|
||||
|
||||
auto reference_gravity_gradient = [](const mfem::Vector &x,
|
||||
mfem::Vector &gradient) {
|
||||
auto reference_gravity_gradient = [](const mfem::Vector &x, mfem::Vector &gradient) {
|
||||
gradient.SetSize(3);
|
||||
gradient(0) = 2.0 * x(0);
|
||||
gradient(1) = 3.0 * x(1);
|
||||
@@ -407,29 +309,25 @@ TEST_CASE(
|
||||
};
|
||||
|
||||
mfem::FunctionCoefficient density_coefficient(reference_density);
|
||||
mfem::VectorFunctionCoefficient gravity_gradient_coefficient(
|
||||
dim, reference_gravity_gradient
|
||||
);
|
||||
mfem::VectorFunctionCoefficient gravity_gradient_coefficient(dim, reference_gravity_gradient);
|
||||
|
||||
mfem::GridFunction density(&density_fes);
|
||||
mfem::GridFunction gravity_gradient(&gravity_gradient_fes);
|
||||
density.ProjectCoefficient(density_coefficient);
|
||||
gravity_gradient.ProjectCoefficient(gravity_gradient_coefficient);
|
||||
|
||||
const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0);
|
||||
const mfem::FiniteElement *density_element = density_fes.GetFE(0);
|
||||
const mfem::FiniteElement *gravity_gradient_element =
|
||||
gravity_gradient_fes.GetFE(0);
|
||||
const mfem::FiniteElement *displacement_element = displacement_fes.GetFE(0);
|
||||
mfem::ElementTransformation *transformation =
|
||||
mesh.GetElementTransformation(0);
|
||||
const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0);
|
||||
const mfem::FiniteElement *density_element = density_fes.GetFE(0);
|
||||
const mfem::FiniteElement *gravity_gradient_element = gravity_gradient_fes.GetFE(0);
|
||||
const mfem::FiniteElement *displacement_element = displacement_fes.GetFE(0);
|
||||
mfem::ElementTransformation *transformation = mesh.GetElementTransformation(0);
|
||||
|
||||
const int velocity_dofs_count = velocity_element->GetDof();
|
||||
const int density_dofs_count = density_element->GetDof();
|
||||
const int gravity_gradient_dofs_count = gravity_gradient_element->GetDof();
|
||||
const int displacement_dofs_count = displacement_element->GetDof();
|
||||
const int velocity_size = dim * velocity_dofs_count;
|
||||
const int displacement_size = dim * displacement_dofs_count;
|
||||
const int velocity_dofs_count = velocity_element->GetDof();
|
||||
const int density_dofs_count = density_element->GetDof();
|
||||
const int gravity_gradient_dofs_count = gravity_gradient_element->GetDof();
|
||||
const int displacement_dofs_count = displacement_element->GetDof();
|
||||
const int velocity_size = dim * velocity_dofs_count;
|
||||
const int displacement_size = dim * displacement_dofs_count;
|
||||
|
||||
mfem::Array<int> density_dof_indices;
|
||||
mfem::Array<int> gravity_gradient_dof_indices;
|
||||
@@ -438,9 +336,7 @@ TEST_CASE(
|
||||
density_fes.GetElementDofs(0, density_dof_indices);
|
||||
gravity_gradient_fes.GetElementVDofs(0, gravity_gradient_dof_indices);
|
||||
density.GetSubVector(density_dof_indices, density_dofs);
|
||||
gravity_gradient.GetSubVector(
|
||||
gravity_gradient_dof_indices, gravity_gradient_dofs
|
||||
);
|
||||
gravity_gradient.GetSubVector(gravity_gradient_dof_indices, gravity_gradient_dofs);
|
||||
|
||||
REQUIRE(density_dofs.Size() == density_dofs_count);
|
||||
REQUIRE(gravity_gradient_dofs.Size() == gravity_gradient_dofs_count);
|
||||
@@ -483,23 +379,18 @@ TEST_CASE(
|
||||
domain_mapper, integrators::GravityForceJacobianMode::field_coupled
|
||||
);
|
||||
|
||||
const mfem::IntegrationRule &integration_rule =
|
||||
mfem::IntRules.Get(velocity_element->GetGeomType(), 8);
|
||||
const mfem::IntegrationRule &integration_rule = mfem::IntRules.Get(velocity_element->GetGeomType(), 8);
|
||||
integrator.SetIntegrationRule(integration_rule);
|
||||
integrator.AssembleElementVector(
|
||||
elements, *transformation, element_state, element_residual
|
||||
);
|
||||
integrator.AssembleElementVector(elements, *transformation, element_state, element_residual);
|
||||
|
||||
mfem::Vector reference_velocity_residual(velocity_residual);
|
||||
|
||||
auto residual_action = [&](const int component,
|
||||
const int coordinate_weight) {
|
||||
auto residual_action = [&](const int component, const int coordinate_weight) {
|
||||
mfem::Vector test_dofs(velocity_size);
|
||||
mfem::Vector x_physical(dim);
|
||||
test_dofs = 0.0;
|
||||
test_dofs = 0.0;
|
||||
|
||||
const mfem::IntegrationRule &velocity_nodes =
|
||||
velocity_element->GetNodes();
|
||||
const mfem::IntegrationRule &velocity_nodes = velocity_element->GetNodes();
|
||||
|
||||
for (int i = 0; i < velocity_dofs_count; ++i) {
|
||||
const mfem::IntegrationPoint &node = velocity_nodes.IntPoint(i);
|
||||
@@ -512,106 +403,62 @@ TEST_CASE(
|
||||
return test_dofs * velocity_residual;
|
||||
};
|
||||
|
||||
CHECK_THAT(
|
||||
residual_action(0, -1), Catch::Matchers::WithinAbs(5.0 / 3.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
residual_action(1, -1), Catch::Matchers::WithinAbs(9.0 / 4.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
residual_action(2, -1), Catch::Matchers::WithinAbs(3.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
residual_action(0, 0), Catch::Matchers::WithinAbs(7.0 / 6.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
residual_action(1, 1), Catch::Matchers::WithinAbs(3.0 / 2.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
residual_action(2, 2), Catch::Matchers::WithinAbs(2.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
residual_action(1, 0), Catch::Matchers::WithinAbs(5.0 / 4.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(residual_action(0, -1), Catch::Matchers::WithinAbs(5.0 / 3.0, tolerance));
|
||||
CHECK_THAT(residual_action(1, -1), Catch::Matchers::WithinAbs(9.0 / 4.0, tolerance));
|
||||
CHECK_THAT(residual_action(2, -1), Catch::Matchers::WithinAbs(3.0, tolerance));
|
||||
CHECK_THAT(residual_action(0, 0), Catch::Matchers::WithinAbs(7.0 / 6.0, tolerance));
|
||||
CHECK_THAT(residual_action(1, 1), Catch::Matchers::WithinAbs(3.0 / 2.0, tolerance));
|
||||
CHECK_THAT(residual_action(2, 2), Catch::Matchers::WithinAbs(2.0, tolerance));
|
||||
CHECK_THAT(residual_action(1, 0), Catch::Matchers::WithinAbs(5.0 / 4.0, tolerance));
|
||||
|
||||
CHECK_THAT(
|
||||
density_residual.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
gravity_gradient_residual.Norml2(),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
gravity_potential_residual.Norml2(),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
displacement_residual.Norml2(),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(density_residual.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(gravity_gradient_residual.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(gravity_potential_residual.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(displacement_residual.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
|
||||
integrator.SetJacobianMode(integrators::GravityForceJacobianMode::minimal);
|
||||
integrator.AssembleElementVector(
|
||||
elements, *transformation, element_state, element_residual
|
||||
);
|
||||
integrator.AssembleElementVector(elements, *transformation, element_state, element_residual);
|
||||
|
||||
mfem::Vector minimal_difference(velocity_residual);
|
||||
minimal_difference -= reference_velocity_residual;
|
||||
|
||||
integrator.SetJacobianMode(integrators::GravityForceJacobianMode::exact);
|
||||
integrator.AssembleElementVector(
|
||||
elements, *transformation, element_state, element_residual
|
||||
);
|
||||
integrator.AssembleElementVector(elements, *transformation, element_state, element_residual);
|
||||
|
||||
mfem::Vector exact_difference(velocity_residual);
|
||||
exact_difference -= reference_velocity_residual;
|
||||
|
||||
CHECK_THAT(
|
||||
minimal_difference.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
exact_difference.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(minimal_difference.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(exact_difference.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
}
|
||||
TEST_CASE(
|
||||
"Gravity Force Integrator Preserves Gravity Identities",
|
||||
tags::unit &tags::solver &tags::integrator &tags::gravity
|
||||
) {
|
||||
constexpr int dim = 3;
|
||||
constexpr double density_value = 1.7;
|
||||
constexpr double gravity_scale = 2.4;
|
||||
constexpr double density_scale = 0.6;
|
||||
constexpr double tolerance = 1.0e-12;
|
||||
constexpr int dim = 3;
|
||||
constexpr double density_value = 1.7;
|
||||
constexpr double gravity_scale = 2.4;
|
||||
constexpr double density_scale = 0.6;
|
||||
constexpr double tolerance = 1.0e-12;
|
||||
|
||||
constexpr int velocity_block =
|
||||
solver::block_index(solver::FieldBlock::velocity);
|
||||
constexpr int density_block =
|
||||
solver::block_index(solver::FieldBlock::density);
|
||||
constexpr int gravity_gradient_block =
|
||||
solver::block_index(solver::FieldBlock::gravity_gradient);
|
||||
constexpr int gravity_potential_block =
|
||||
solver::block_index(solver::FieldBlock::gravity_potential);
|
||||
constexpr int displacement_block =
|
||||
solver::block_index(solver::FieldBlock::displacement);
|
||||
constexpr int block_count = solver::field_block_count;
|
||||
constexpr int velocity_block = solver::block_index(solver::FieldBlock::velocity);
|
||||
constexpr int density_block = solver::block_index(solver::FieldBlock::density);
|
||||
constexpr int gravity_gradient_block = solver::block_index(solver::FieldBlock::gravity_gradient);
|
||||
constexpr int gravity_potential_block = solver::block_index(solver::FieldBlock::gravity_potential);
|
||||
constexpr int displacement_block = solver::block_index(solver::FieldBlock::displacement);
|
||||
constexpr int block_count = solver::field_block_count;
|
||||
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian3D(
|
||||
1, 1, 1, mfem::Element::HEXAHEDRON, 1.0, 1.0, 1.0
|
||||
);
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian3D(1, 1, 1, mfem::Element::HEXAHEDRON, 1.0, 1.0, 1.0);
|
||||
|
||||
mfem::H1_FECollection velocity_fec(1, dim);
|
||||
mfem::L2_FECollection density_fec(0, dim);
|
||||
mfem::RT_FECollection gravity_gradient_fec(0, dim);
|
||||
mfem::H1_FECollection displacement_fec(1, dim);
|
||||
|
||||
mfem::FiniteElementSpace velocity_fes(
|
||||
&mesh, &velocity_fec, dim, mfem::Ordering::byVDIM
|
||||
);
|
||||
mfem::FiniteElementSpace velocity_fes(&mesh, &velocity_fec, dim, mfem::Ordering::byVDIM);
|
||||
mfem::FiniteElementSpace density_fes(&mesh, &density_fec);
|
||||
mfem::FiniteElementSpace gravity_gradient_fes(&mesh, &gravity_gradient_fec);
|
||||
mfem::FiniteElementSpace displacement_fes(
|
||||
&mesh, &displacement_fec, dim, mfem::Ordering::byVDIM
|
||||
);
|
||||
mfem::FiniteElementSpace displacement_fes(&mesh, &displacement_fec, dim, mfem::Ordering::byVDIM);
|
||||
|
||||
mfem::GridFunction displacement(&displacement_fes);
|
||||
displacement = 0.0;
|
||||
@@ -635,20 +482,18 @@ TEST_CASE(
|
||||
density.ProjectCoefficient(density_coefficient);
|
||||
gravity_gradient.ProjectCoefficient(gravity_coefficient);
|
||||
|
||||
const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0);
|
||||
const mfem::FiniteElement *density_element = density_fes.GetFE(0);
|
||||
const mfem::FiniteElement *gravity_gradient_element =
|
||||
gravity_gradient_fes.GetFE(0);
|
||||
const mfem::FiniteElement *displacement_element = displacement_fes.GetFE(0);
|
||||
mfem::ElementTransformation *transformation =
|
||||
mesh.GetElementTransformation(0);
|
||||
const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0);
|
||||
const mfem::FiniteElement *density_element = density_fes.GetFE(0);
|
||||
const mfem::FiniteElement *gravity_gradient_element = gravity_gradient_fes.GetFE(0);
|
||||
const mfem::FiniteElement *displacement_element = displacement_fes.GetFE(0);
|
||||
mfem::ElementTransformation *transformation = mesh.GetElementTransformation(0);
|
||||
|
||||
const int velocity_dofs_count = velocity_element->GetDof();
|
||||
const int density_dofs_count = density_element->GetDof();
|
||||
const int gravity_gradient_dofs_count = gravity_gradient_element->GetDof();
|
||||
const int displacement_dofs_count = displacement_element->GetDof();
|
||||
const int velocity_size = dim * velocity_dofs_count;
|
||||
const int displacement_size = dim * displacement_dofs_count;
|
||||
const int velocity_dofs_count = velocity_element->GetDof();
|
||||
const int density_dofs_count = density_element->GetDof();
|
||||
const int gravity_gradient_dofs_count = gravity_gradient_element->GetDof();
|
||||
const int displacement_dofs_count = displacement_element->GetDof();
|
||||
const int velocity_size = dim * velocity_dofs_count;
|
||||
const int displacement_size = dim * displacement_dofs_count;
|
||||
|
||||
mfem::Array<int> density_dof_indices;
|
||||
mfem::Array<int> gravity_gradient_dof_indices;
|
||||
@@ -657,9 +502,7 @@ TEST_CASE(
|
||||
density_fes.GetElementDofs(0, density_dof_indices);
|
||||
gravity_gradient_fes.GetElementVDofs(0, gravity_gradient_dof_indices);
|
||||
density.GetSubVector(density_dof_indices, density_dofs);
|
||||
gravity_gradient.GetSubVector(
|
||||
gravity_gradient_dof_indices, gravity_gradient_dofs
|
||||
);
|
||||
gravity_gradient.GetSubVector(gravity_gradient_dof_indices, gravity_gradient_dofs);
|
||||
|
||||
mfem::Vector zero_density(density_dofs_count);
|
||||
mfem::Vector zero_gravity(gravity_gradient_dofs_count);
|
||||
@@ -703,76 +546,54 @@ TEST_CASE(
|
||||
domain_mapper, integrators::GravityForceJacobianMode::field_coupled
|
||||
);
|
||||
|
||||
const mfem::IntegrationRule &integration_rule =
|
||||
mfem::IntRules.Get(velocity_element->GetGeomType(), 8);
|
||||
const mfem::IntegrationRule &integration_rule = mfem::IntRules.Get(velocity_element->GetGeomType(), 8);
|
||||
integrator.SetIntegrationRule(integration_rule);
|
||||
|
||||
auto assemble_velocity_residual = [&](const mfem::Vector &density_state,
|
||||
const mfem::Vector &gravity_state) {
|
||||
auto assemble_velocity_residual = [&](const mfem::Vector &density_state, const mfem::Vector &gravity_state) {
|
||||
element_state[density_block] = &density_state;
|
||||
element_state[gravity_gradient_block] = &gravity_state;
|
||||
integrator.AssembleElementVector(
|
||||
elements, *transformation, element_state, element_residual
|
||||
);
|
||||
integrator.AssembleElementVector(elements, *transformation, element_state, element_residual);
|
||||
return mfem::Vector(velocity_residual);
|
||||
};
|
||||
|
||||
auto scaled_difference_norm = [](mfem::Vector computed,
|
||||
const mfem::Vector &reference,
|
||||
const double scale) {
|
||||
auto scaled_difference_norm = [](mfem::Vector computed, const mfem::Vector &reference, const double scale) {
|
||||
computed.Add(-scale, reference);
|
||||
return computed.Norml2();
|
||||
};
|
||||
|
||||
const mfem::Vector base_residual =
|
||||
assemble_velocity_residual(density_dofs, gravity_gradient_dofs);
|
||||
const mfem::Vector base_residual = assemble_velocity_residual(density_dofs, gravity_gradient_dofs);
|
||||
|
||||
REQUIRE(base_residual.Norml2() > tolerance);
|
||||
|
||||
const mfem::Vector zero_field_residual =
|
||||
assemble_velocity_residual(density_dofs, zero_gravity);
|
||||
const mfem::Vector zero_field_residual = assemble_velocity_residual(density_dofs, zero_gravity);
|
||||
|
||||
mfem::Vector reversed_gravity(gravity_gradient_dofs);
|
||||
reversed_gravity *= -1.0;
|
||||
const mfem::Vector reversed_residual =
|
||||
assemble_velocity_residual(density_dofs, reversed_gravity);
|
||||
const mfem::Vector reversed_residual = assemble_velocity_residual(density_dofs, reversed_gravity);
|
||||
|
||||
mfem::Vector scaled_gravity(gravity_gradient_dofs);
|
||||
scaled_gravity *= gravity_scale;
|
||||
const mfem::Vector gravity_scaled_residual =
|
||||
assemble_velocity_residual(density_dofs, scaled_gravity);
|
||||
const mfem::Vector gravity_scaled_residual = assemble_velocity_residual(density_dofs, scaled_gravity);
|
||||
|
||||
mfem::Vector scaled_density(density_dofs);
|
||||
scaled_density *= density_scale;
|
||||
const mfem::Vector density_scaled_residual =
|
||||
assemble_velocity_residual(scaled_density, gravity_gradient_dofs);
|
||||
const mfem::Vector jointly_scaled_residual =
|
||||
assemble_velocity_residual(scaled_density, scaled_gravity);
|
||||
const mfem::Vector density_scaled_residual = assemble_velocity_residual(scaled_density, gravity_gradient_dofs);
|
||||
const mfem::Vector jointly_scaled_residual = assemble_velocity_residual(scaled_density, scaled_gravity);
|
||||
|
||||
CHECK_THAT(zero_field_residual.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(
|
||||
zero_field_residual.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
scaled_difference_norm(reversed_residual, base_residual, -1.0), Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
scaled_difference_norm(reversed_residual, base_residual, -1.0),
|
||||
scaled_difference_norm(gravity_scaled_residual, base_residual, gravity_scale),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
scaled_difference_norm(
|
||||
gravity_scaled_residual, base_residual, gravity_scale
|
||||
),
|
||||
scaled_difference_norm(density_scaled_residual, base_residual, density_scale),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
scaled_difference_norm(
|
||||
density_scaled_residual, base_residual, density_scale
|
||||
),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
scaled_difference_norm(
|
||||
jointly_scaled_residual, base_residual,
|
||||
density_scale * gravity_scale
|
||||
),
|
||||
scaled_difference_norm(jointly_scaled_residual, base_residual, density_scale * gravity_scale),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
|
||||
@@ -781,10 +602,9 @@ TEST_CASE(
|
||||
mfem::Vector x_physical(dim);
|
||||
mfem::Vector centered_position(dim);
|
||||
mfem::Vector test_value(dim);
|
||||
test_dofs = 0.0;
|
||||
test_dofs = 0.0;
|
||||
|
||||
const mfem::IntegrationRule &velocity_nodes =
|
||||
velocity_element->GetNodes();
|
||||
const mfem::IntegrationRule &velocity_nodes = velocity_element->GetNodes();
|
||||
|
||||
for (int i = 0; i < velocity_dofs_count; ++i) {
|
||||
const mfem::IntegrationPoint &node = velocity_nodes.IntPoint(i);
|
||||
@@ -798,87 +618,63 @@ TEST_CASE(
|
||||
test_function(centered_position, test_value);
|
||||
|
||||
for (int component = 0; component < dim; ++component) {
|
||||
test_dofs(i + component * velocity_dofs_count) =
|
||||
test_value(component);
|
||||
test_dofs(i + component * velocity_dofs_count) = test_value(component);
|
||||
}
|
||||
}
|
||||
|
||||
return test_dofs;
|
||||
};
|
||||
|
||||
const mfem::Vector force_x_test =
|
||||
make_test_dofs([](const mfem::Vector &, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value = 0.0;
|
||||
value(0) = 1.0;
|
||||
});
|
||||
const mfem::Vector force_x_test = make_test_dofs([](const mfem::Vector &, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value = 0.0;
|
||||
value(0) = 1.0;
|
||||
});
|
||||
|
||||
const mfem::Vector force_y_test =
|
||||
make_test_dofs([](const mfem::Vector &, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value = 0.0;
|
||||
value(1) = 1.0;
|
||||
});
|
||||
const mfem::Vector force_y_test = make_test_dofs([](const mfem::Vector &, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value = 0.0;
|
||||
value(1) = 1.0;
|
||||
});
|
||||
|
||||
const mfem::Vector force_z_test =
|
||||
make_test_dofs([](const mfem::Vector &, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value = 0.0;
|
||||
value(2) = 1.0;
|
||||
});
|
||||
const mfem::Vector force_z_test = make_test_dofs([](const mfem::Vector &, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value = 0.0;
|
||||
value(2) = 1.0;
|
||||
});
|
||||
|
||||
const mfem::Vector torque_x_test =
|
||||
make_test_dofs([](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value(0) = 0.0;
|
||||
value(1) = -position(2);
|
||||
value(2) = position(1);
|
||||
});
|
||||
const mfem::Vector torque_x_test = make_test_dofs([](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value(0) = 0.0;
|
||||
value(1) = -position(2);
|
||||
value(2) = position(1);
|
||||
});
|
||||
|
||||
const mfem::Vector torque_y_test =
|
||||
make_test_dofs([](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value(0) = position(2);
|
||||
value(1) = 0.0;
|
||||
value(2) = -position(0);
|
||||
});
|
||||
const mfem::Vector torque_y_test = make_test_dofs([](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value(0) = position(2);
|
||||
value(1) = 0.0;
|
||||
value(2) = -position(0);
|
||||
});
|
||||
|
||||
const mfem::Vector torque_z_test =
|
||||
make_test_dofs([](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value(0) = -position(1);
|
||||
value(1) = position(0);
|
||||
value(2) = 0.0;
|
||||
});
|
||||
const mfem::Vector torque_z_test = make_test_dofs([](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value(0) = -position(1);
|
||||
value(1) = position(0);
|
||||
value(2) = 0.0;
|
||||
});
|
||||
|
||||
CHECK_THAT(
|
||||
force_x_test * base_residual, Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
force_y_test * base_residual, Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
force_z_test * base_residual, Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
torque_x_test * base_residual,
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
torque_y_test * base_residual,
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
torque_z_test * base_residual,
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(force_x_test * base_residual, Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(force_y_test * base_residual, Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(force_z_test * base_residual, Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(torque_x_test * base_residual, Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(torque_y_test * base_residual, Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(torque_z_test * base_residual, Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
|
||||
mfem::DenseMatrix dv_drho(velocity_size, density_dofs_count);
|
||||
mfem::DenseMatrix dv_dgrad_phi(velocity_size, gravity_gradient_dofs_count);
|
||||
|
||||
mfem::Array2D<mfem::DenseMatrix *> element_matrices(
|
||||
block_count, block_count
|
||||
);
|
||||
mfem::Array2D<mfem::DenseMatrix *> element_matrices(block_count, block_count);
|
||||
|
||||
for (int row = 0; row < block_count; ++row) {
|
||||
for (int column = 0; column < block_count; ++column) {
|
||||
@@ -891,19 +687,14 @@ TEST_CASE(
|
||||
|
||||
element_state[density_block] = &density_dofs;
|
||||
element_state[gravity_gradient_block] = &zero_gravity;
|
||||
integrator.AssembleElementGrad(
|
||||
elements, *transformation, element_state, element_matrices
|
||||
);
|
||||
integrator.AssembleElementGrad(elements, *transformation, element_state, element_matrices);
|
||||
|
||||
mfem::Vector zero_gravity_density_action(velocity_size);
|
||||
mfem::Vector zero_gravity_field_action(velocity_size);
|
||||
dv_drho.Mult(density_dofs, zero_gravity_density_action);
|
||||
dv_dgrad_phi.Mult(gravity_gradient_dofs, zero_gravity_field_action);
|
||||
|
||||
CHECK_THAT(
|
||||
zero_gravity_density_action.Norml2(),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(zero_gravity_density_action.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(
|
||||
scaled_difference_norm(zero_gravity_field_action, base_residual, 1.0),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
@@ -911,9 +702,7 @@ TEST_CASE(
|
||||
|
||||
element_state[density_block] = &zero_density;
|
||||
element_state[gravity_gradient_block] = &gravity_gradient_dofs;
|
||||
integrator.AssembleElementGrad(
|
||||
elements, *transformation, element_state, element_matrices
|
||||
);
|
||||
integrator.AssembleElementGrad(elements, *transformation, element_state, element_matrices);
|
||||
|
||||
mfem::Vector zero_density_density_action(velocity_size);
|
||||
mfem::Vector zero_density_field_action(velocity_size);
|
||||
@@ -924,8 +713,5 @@ TEST_CASE(
|
||||
scaled_difference_norm(zero_density_density_action, base_residual, 1.0),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
zero_density_field_action.Norml2(),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(zero_density_field_action.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
}
|
||||
@@ -72,16 +72,9 @@ namespace {
|
||||
) {
|
||||
const double radial_extent = r_inf - r_star;
|
||||
const double computational_radius = r_star + coordinate * radial_extent;
|
||||
const double scale =
|
||||
r_star / (computational_radius * (1.0 - coordinate));
|
||||
const double scale_derivative =
|
||||
scale *
|
||||
(1.0 / (1.0 - coordinate) - radial_extent / computational_radius);
|
||||
return {
|
||||
.computational_radius = computational_radius,
|
||||
.scale = scale,
|
||||
.scale_derivative = scale_derivative
|
||||
};
|
||||
const double scale = r_star / (computational_radius * (1.0 - coordinate));
|
||||
const double scale_derivative = scale * (1.0 / (1.0 - coordinate) - radial_extent / computational_radius);
|
||||
return {.computational_radius = computational_radius, .scale = scale, .scale_derivative = scale_derivative};
|
||||
}
|
||||
|
||||
mapping::MappingStatus evaluate_affine_map(
|
||||
@@ -98,12 +91,11 @@ namespace {
|
||||
displaced_position += offset;
|
||||
|
||||
const mapping::compactification::ExteriorMapInput input{
|
||||
.reference_position = reference_position,
|
||||
.displaced_position = displaced_position,
|
||||
.displacement_jacobian = affine_jacobian,
|
||||
.compactification_coordinate = compactification_coordinate,
|
||||
.compactification_coordinate_gradient =
|
||||
compactification_coordinate_gradient
|
||||
.reference_position = reference_position,
|
||||
.displaced_position = displaced_position,
|
||||
.displacement_jacobian = affine_jacobian,
|
||||
.compactification_coordinate = compactification_coordinate,
|
||||
.compactification_coordinate_gradient = compactification_coordinate_gradient
|
||||
};
|
||||
|
||||
return exterior_map.Evaluate(input, result);
|
||||
@@ -114,47 +106,28 @@ TEST_CASE(
|
||||
"Kelvin Compactification Validates Its Configuration",
|
||||
tags::unit &tags::mapping &tags::kelvin
|
||||
) {
|
||||
CHECK_NOTHROW(
|
||||
mapping::compactification::KelvinCompactification(
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0}
|
||||
)
|
||||
CHECK_NOTHROW(mapping::compactification::KelvinCompactification({.r_star_ref = 1.0, .r_inf_ref = 4.0}));
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification({.r_star_ref = 0.0, .r_inf_ref = 4.0}), std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification({.r_star_ref = -1.0, .r_inf_ref = 4.0}), std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification({.r_star_ref = 2.0, .r_inf_ref = 2.0}), std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification({.r_star_ref = 3.0, .r_inf_ref = 2.0}), std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification(
|
||||
{.r_star_ref = 0.0, .r_inf_ref = 4.0}
|
||||
{.r_star_ref = 1.0, .r_inf_ref = std::numeric_limits<double>::infinity()}
|
||||
),
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification(
|
||||
{.r_star_ref = -1.0, .r_inf_ref = 4.0}
|
||||
),
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification(
|
||||
{.r_star_ref = 2.0, .r_inf_ref = 2.0}
|
||||
),
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification(
|
||||
{.r_star_ref = 3.0, .r_inf_ref = 2.0}
|
||||
),
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification(
|
||||
{.r_star_ref = 1.0,
|
||||
.r_inf_ref = std::numeric_limits<double>::infinity()}
|
||||
),
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification(
|
||||
{.r_star_ref = 1.0,
|
||||
.r_inf_ref = 4.0,
|
||||
.coordinate_tolerance = -1.0e-12}
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0, .coordinate_tolerance = -1.0e-12}
|
||||
),
|
||||
std::invalid_argument
|
||||
);
|
||||
@@ -166,9 +139,7 @@ TEST_CASE(
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification(
|
||||
{.r_star_ref = 1.0,
|
||||
.r_inf_ref = 4.0,
|
||||
.coordinate_tolerance = std::numeric_limits<double>::quiet_NaN()}
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0, .coordinate_tolerance = std::numeric_limits<double>::quiet_NaN()}
|
||||
),
|
||||
std::invalid_argument
|
||||
);
|
||||
@@ -180,22 +151,13 @@ TEST_CASE(
|
||||
) {
|
||||
constexpr double coordinate_tolerance = 3.0e-11;
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = 1.25,
|
||||
.r_inf_ref = 5.5,
|
||||
.coordinate_tolerance = coordinate_tolerance}
|
||||
{.r_star_ref = 1.25, .r_inf_ref = 5.5, .coordinate_tolerance = coordinate_tolerance}
|
||||
);
|
||||
|
||||
CHECK(compactification.GetName() == "KelvinCompactification");
|
||||
CHECK_THAT(
|
||||
compactification.GetReferenceStellarRadius(), WithinAbs(1.25, 0.0)
|
||||
);
|
||||
CHECK_THAT(
|
||||
compactification.GetReferenceInfinityRadius(), WithinAbs(5.5, 0.0)
|
||||
);
|
||||
CHECK_THAT(
|
||||
compactification.GetCoordinateTolerance(),
|
||||
WithinAbs(coordinate_tolerance, 0.0)
|
||||
);
|
||||
CHECK_THAT(compactification.GetReferenceStellarRadius(), WithinAbs(1.25, 0.0));
|
||||
CHECK_THAT(compactification.GetReferenceInfinityRadius(), WithinAbs(5.5, 0.0));
|
||||
CHECK_THAT(compactification.GetCoordinateTolerance(), WithinAbs(coordinate_tolerance, 0.0));
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
@@ -206,42 +168,28 @@ TEST_CASE(
|
||||
constexpr double tolerance = 0.0;
|
||||
constexpr double coordinate = 0.37;
|
||||
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0}
|
||||
);
|
||||
mapping::compactification::KelvinCompactification compactification({.r_star_ref = 1.0, .r_inf_ref = 4.0});
|
||||
const mfem::DenseMatrix displacement_jacobian = make_identity();
|
||||
const mfem::Vector displaced_position = make_vector(1.4, -0.2, 0.3);
|
||||
const mfem::Vector coordinate_gradient = make_vector(0.2, -0.1, 0.05);
|
||||
const mfem::Vector reference_a = make_vector(0.2, 0.1, -0.1);
|
||||
const mfem::Vector reference_b = make_vector(12.0, -7.0, 4.0);
|
||||
const mfem::Vector coordinate_gradient = make_vector(0.2, -0.1, 0.05);
|
||||
const mfem::Vector reference_a = make_vector(0.2, 0.1, -0.1);
|
||||
const mfem::Vector reference_b = make_vector(12.0, -7.0, 4.0);
|
||||
|
||||
const mapping::compactification::ExteriorMapInput input_a{
|
||||
reference_a, displaced_position, displacement_jacobian, coordinate,
|
||||
coordinate_gradient
|
||||
reference_a, displaced_position, displacement_jacobian, coordinate, coordinate_gradient
|
||||
};
|
||||
const mapping::compactification::ExteriorMapInput input_b{
|
||||
reference_b, displaced_position, displacement_jacobian, coordinate,
|
||||
coordinate_gradient
|
||||
reference_b, displaced_position, displacement_jacobian, coordinate, coordinate_gradient
|
||||
};
|
||||
|
||||
mapping::compactification::ExteriorMapResult result_a;
|
||||
mapping::compactification::ExteriorMapResult result_b;
|
||||
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input_a, result_a) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input_b, result_b) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(compactification.Evaluate(input_a, result_a) == mapping::MappingStatus::valid);
|
||||
REQUIRE(compactification.Evaluate(input_b, result_b) == mapping::MappingStatus::valid);
|
||||
|
||||
check_vector(
|
||||
result_a.physical_position, result_b.physical_position, tolerance
|
||||
);
|
||||
check_matrix(
|
||||
result_a.mapping_jacobian, result_b.mapping_jacobian, tolerance
|
||||
);
|
||||
check_vector(result_a.physical_position, result_b.physical_position, tolerance);
|
||||
check_matrix(result_a.mapping_jacobian, result_b.mapping_jacobian, tolerance);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
@@ -253,43 +201,28 @@ TEST_CASE(
|
||||
constexpr double radial_extent = r_inf - r_star;
|
||||
constexpr double tolerance = 2.0e-12;
|
||||
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = r_star, .r_inf_ref = r_inf}
|
||||
);
|
||||
const mfem::DenseMatrix identity = make_identity();
|
||||
const mfem::Vector coordinate_gradient =
|
||||
make_vector(1.0 / radial_extent, 0.0, 0.0);
|
||||
mapping::compactification::KelvinCompactification compactification({.r_star_ref = r_star, .r_inf_ref = r_inf});
|
||||
const mfem::DenseMatrix identity = make_identity();
|
||||
const mfem::Vector coordinate_gradient = make_vector(1.0 / radial_extent, 0.0, 0.0);
|
||||
|
||||
for (const double computational_radius :
|
||||
std::array{1.0, 1.25, 2.0, 3.0, 3.75}) {
|
||||
for (const double computational_radius : std::array{1.0, 1.25, 2.0, 3.0, 3.75}) {
|
||||
CAPTURE(computational_radius);
|
||||
|
||||
const double coordinate =
|
||||
(computational_radius - r_star) / radial_extent;
|
||||
const mfem::Vector reference_position =
|
||||
make_vector(computational_radius, 0.0, 0.0);
|
||||
const double coordinate = (computational_radius - r_star) / radial_extent;
|
||||
const mfem::Vector reference_position = make_vector(computational_radius, 0.0, 0.0);
|
||||
const mfem::Vector displaced_position(reference_position);
|
||||
const mapping::compactification::ExteriorMapInput input{
|
||||
reference_position, displaced_position, identity, coordinate,
|
||||
coordinate_gradient
|
||||
reference_position, displaced_position, identity, coordinate, coordinate_gradient
|
||||
};
|
||||
mapping::compactification::ExteriorMapResult result;
|
||||
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input, result) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(compactification.Evaluate(input, result) == mapping::MappingStatus::valid);
|
||||
|
||||
const double expected_radius =
|
||||
r_star * radial_extent / (r_inf - computational_radius);
|
||||
const double expected_radial_derivative =
|
||||
r_star * radial_extent /
|
||||
std::pow(r_inf - computational_radius, 2.0);
|
||||
const double expected_tangential_scale =
|
||||
expected_radius / computational_radius;
|
||||
const double expected_radius = r_star * radial_extent / (r_inf - computational_radius);
|
||||
const double expected_radial_derivative = r_star * radial_extent / std::pow(r_inf - computational_radius, 2.0);
|
||||
const double expected_tangential_scale = expected_radius / computational_radius;
|
||||
|
||||
const mfem::Vector expected_position =
|
||||
make_vector(expected_radius, 0.0, 0.0);
|
||||
const mfem::Vector expected_position = make_vector(expected_radius, 0.0, 0.0);
|
||||
mfem::DenseMatrix expected_jacobian(dimension);
|
||||
expected_jacobian = 0.0;
|
||||
expected_jacobian(0, 0) = expected_radial_derivative;
|
||||
@@ -311,9 +244,7 @@ TEST_CASE(
|
||||
constexpr double coordinate = 0.42;
|
||||
constexpr double tolerance = 1.0e-12;
|
||||
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = r_star, .r_inf_ref = r_inf}
|
||||
);
|
||||
mapping::compactification::KelvinCompactification compactification({.r_star_ref = r_star, .r_inf_ref = r_inf});
|
||||
const mfem::Vector reference_position = make_vector(0.8, 0.4, -0.2);
|
||||
const mfem::Vector displaced_position = make_vector(1.1, 0.5, -0.1);
|
||||
const mfem::Vector coordinate_gradient = make_vector(0.20, -0.10, 0.05);
|
||||
@@ -330,18 +261,13 @@ TEST_CASE(
|
||||
displacement_jacobian(2, 2) = 1.05;
|
||||
|
||||
const mapping::compactification::ExteriorMapInput input{
|
||||
reference_position, displaced_position, displacement_jacobian,
|
||||
coordinate, coordinate_gradient
|
||||
reference_position, displaced_position, displacement_jacobian, coordinate, coordinate_gradient
|
||||
};
|
||||
mapping::compactification::ExteriorMapResult result;
|
||||
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input, result) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(compactification.Evaluate(input, result) == mapping::MappingStatus::valid);
|
||||
|
||||
const AnalyticFactors factors =
|
||||
compute_analytic_factors(r_star, r_inf, coordinate);
|
||||
const AnalyticFactors factors = compute_analytic_factors(r_star, r_inf, coordinate);
|
||||
mfem::Vector expected_position(displaced_position);
|
||||
expected_position *= factors.scale;
|
||||
|
||||
@@ -350,9 +276,7 @@ TEST_CASE(
|
||||
|
||||
for (int i = 0; i < dimension; ++i) {
|
||||
for (int j = 0; j < dimension; ++j)
|
||||
expected_jacobian(i, j) += displaced_position(i) *
|
||||
factors.scale_derivative *
|
||||
coordinate_gradient(j);
|
||||
expected_jacobian(i, j) += displaced_position(i) * factors.scale_derivative * coordinate_gradient(j);
|
||||
}
|
||||
|
||||
check_vector(result.physical_position, expected_position, tolerance);
|
||||
@@ -367,9 +291,7 @@ TEST_CASE(
|
||||
constexpr double difference_step = 1.0e-6;
|
||||
constexpr double tolerance = 3.0e-9;
|
||||
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0}
|
||||
);
|
||||
mapping::compactification::KelvinCompactification compactification({.r_star_ref = 1.0, .r_inf_ref = 4.0});
|
||||
|
||||
mfem::DenseMatrix affine_jacobian(dimension);
|
||||
affine_jacobian(0, 0) = 1.05;
|
||||
@@ -389,8 +311,8 @@ TEST_CASE(
|
||||
mapping::compactification::ExteriorMapResult base_result;
|
||||
REQUIRE(
|
||||
evaluate_affine_map(
|
||||
compactification, reference_position, affine_jacobian, offset,
|
||||
base_coordinate, coordinate_gradient, base_result
|
||||
compactification, reference_position, affine_jacobian, offset, base_coordinate, coordinate_gradient,
|
||||
base_result
|
||||
) == mapping::MappingStatus::valid
|
||||
);
|
||||
|
||||
@@ -400,39 +322,30 @@ TEST_CASE(
|
||||
reference_plus(coordinate) += difference_step;
|
||||
reference_minus(coordinate) -= difference_step;
|
||||
|
||||
const double compactification_plus =
|
||||
base_coordinate + difference_step * coordinate_gradient(coordinate);
|
||||
const double compactification_minus =
|
||||
base_coordinate - difference_step * coordinate_gradient(coordinate);
|
||||
const double compactification_plus = base_coordinate + difference_step * coordinate_gradient(coordinate);
|
||||
const double compactification_minus = base_coordinate - difference_step * coordinate_gradient(coordinate);
|
||||
|
||||
mapping::compactification::ExteriorMapResult result_plus;
|
||||
mapping::compactification::ExteriorMapResult result_minus;
|
||||
|
||||
REQUIRE(
|
||||
evaluate_affine_map(
|
||||
compactification, reference_plus, affine_jacobian, offset,
|
||||
compactification_plus, coordinate_gradient, result_plus
|
||||
compactification, reference_plus, affine_jacobian, offset, compactification_plus, coordinate_gradient,
|
||||
result_plus
|
||||
) == mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(
|
||||
evaluate_affine_map(
|
||||
compactification, reference_minus, affine_jacobian, offset,
|
||||
compactification_minus, coordinate_gradient, result_minus
|
||||
compactification, reference_minus, affine_jacobian, offset, compactification_minus, coordinate_gradient,
|
||||
result_minus
|
||||
) == mapping::MappingStatus::valid
|
||||
);
|
||||
|
||||
for (int component = 0; component < dimension; ++component) {
|
||||
const double finite_difference =
|
||||
(result_plus.physical_position(component) -
|
||||
result_minus.physical_position(component)) /
|
||||
(result_plus.physical_position(component) - result_minus.physical_position(component)) /
|
||||
(2.0 * difference_step);
|
||||
CHECK_THAT(
|
||||
finite_difference,
|
||||
WithinAbs(
|
||||
base_result.mapping_jacobian(component, coordinate),
|
||||
tolerance
|
||||
)
|
||||
);
|
||||
CHECK_THAT(finite_difference, WithinAbs(base_result.mapping_jacobian(component, coordinate), tolerance));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -445,9 +358,7 @@ TEST_CASE(
|
||||
constexpr double difference_step = 1.0e-6;
|
||||
constexpr double tolerance = 2.0e-10;
|
||||
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0}
|
||||
);
|
||||
mapping::compactification::KelvinCompactification compactification({.r_star_ref = 1.0, .r_inf_ref = 4.0});
|
||||
const mfem::Vector reference_position = make_vector(1.3, -0.2, 0.4);
|
||||
const mfem::Vector displaced_position = make_vector(1.4, -0.1, 0.35);
|
||||
const mfem::Vector coordinate_gradient = make_vector(0.12, -0.04, 0.08);
|
||||
@@ -470,24 +381,16 @@ TEST_CASE(
|
||||
jacobian_direction(2, 2) = 0.01;
|
||||
|
||||
const mapping::compactification::ExteriorMapInput input{
|
||||
reference_position, displaced_position, displacement_jacobian,
|
||||
coordinate, coordinate_gradient
|
||||
};
|
||||
const mapping::compactification::ExteriorMapDirection direction{
|
||||
position_direction, jacobian_direction
|
||||
reference_position, displaced_position, displacement_jacobian, coordinate, coordinate_gradient
|
||||
};
|
||||
const mapping::compactification::ExteriorMapDirection direction{position_direction, jacobian_direction};
|
||||
|
||||
mapping::compactification::ExteriorMapResult base_result;
|
||||
mapping::compactification::ExteriorMapVariation variation;
|
||||
|
||||
REQUIRE(compactification.Evaluate(input, base_result) == mapping::MappingStatus::valid);
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input, base_result) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(
|
||||
compactification.EvaluateVariation(
|
||||
input, base_result, direction, variation
|
||||
) == mapping::MappingStatus::valid
|
||||
compactification.EvaluateVariation(input, base_result, direction, variation) == mapping::MappingStatus::valid
|
||||
);
|
||||
|
||||
mfem::Vector displaced_plus(displaced_position);
|
||||
@@ -501,45 +404,27 @@ TEST_CASE(
|
||||
jacobian_minus.Add(-difference_step, jacobian_direction);
|
||||
|
||||
const mapping::compactification::ExteriorMapInput input_plus{
|
||||
reference_position, displaced_plus, jacobian_plus, coordinate,
|
||||
coordinate_gradient
|
||||
reference_position, displaced_plus, jacobian_plus, coordinate, coordinate_gradient
|
||||
};
|
||||
const mapping::compactification::ExteriorMapInput input_minus{
|
||||
reference_position, displaced_minus, jacobian_minus, coordinate,
|
||||
coordinate_gradient
|
||||
reference_position, displaced_minus, jacobian_minus, coordinate, coordinate_gradient
|
||||
};
|
||||
|
||||
mapping::compactification::ExteriorMapResult result_plus;
|
||||
mapping::compactification::ExteriorMapResult result_minus;
|
||||
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input_plus, result_plus) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input_minus, result_minus) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(compactification.Evaluate(input_plus, result_plus) == mapping::MappingStatus::valid);
|
||||
REQUIRE(compactification.Evaluate(input_minus, result_minus) == mapping::MappingStatus::valid);
|
||||
|
||||
for (int i = 0; i < dimension; ++i) {
|
||||
const double position_finite_difference =
|
||||
(result_plus.physical_position(i) -
|
||||
result_minus.physical_position(i)) /
|
||||
(2.0 * difference_step);
|
||||
CHECK_THAT(
|
||||
position_finite_difference,
|
||||
WithinAbs(variation.physical_position_variation(i), tolerance)
|
||||
);
|
||||
(result_plus.physical_position(i) - result_minus.physical_position(i)) / (2.0 * difference_step);
|
||||
CHECK_THAT(position_finite_difference, WithinAbs(variation.physical_position_variation(i), tolerance));
|
||||
|
||||
for (int j = 0; j < dimension; ++j) {
|
||||
const double jacobian_finite_difference =
|
||||
(result_plus.mapping_jacobian(i, j) -
|
||||
result_minus.mapping_jacobian(i, j)) /
|
||||
(2.0 * difference_step);
|
||||
CHECK_THAT(
|
||||
jacobian_finite_difference,
|
||||
WithinAbs(variation.mapping_jacobian_variation(i, j), tolerance)
|
||||
);
|
||||
(result_plus.mapping_jacobian(i, j) - result_minus.mapping_jacobian(i, j)) / (2.0 * difference_step);
|
||||
CHECK_THAT(jacobian_finite_difference, WithinAbs(variation.mapping_jacobian_variation(i, j), tolerance));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -551,9 +436,7 @@ TEST_CASE(
|
||||
constexpr double coordinate = 0.31;
|
||||
constexpr double tolerance = 1.0e-12;
|
||||
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0}
|
||||
);
|
||||
mapping::compactification::KelvinCompactification compactification({.r_star_ref = 1.0, .r_inf_ref = 4.0});
|
||||
const mfem::Vector reference_position = make_vector(1.3, 0.4, -0.2);
|
||||
const mfem::Vector displaced_position = make_vector(1.4, 0.2, -0.1);
|
||||
const mfem::Vector coordinate_gradient = make_vector(0.16, -0.08, 0.03);
|
||||
@@ -564,14 +447,10 @@ TEST_CASE(
|
||||
displacement_jacobian(2, 1) = 0.03;
|
||||
|
||||
const mapping::compactification::ExteriorMapInput input{
|
||||
reference_position, displaced_position, displacement_jacobian,
|
||||
coordinate, coordinate_gradient
|
||||
reference_position, displaced_position, displacement_jacobian, coordinate, coordinate_gradient
|
||||
};
|
||||
mapping::compactification::ExteriorMapResult result;
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input, result) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(compactification.Evaluate(input, result) == mapping::MappingStatus::valid);
|
||||
|
||||
mfem::DenseMatrix rotation(dimension);
|
||||
rotation = 0.0;
|
||||
@@ -592,14 +471,10 @@ TEST_CASE(
|
||||
mfem::MultABt(temporary, rotation, rotated_displacement_jacobian);
|
||||
|
||||
const mapping::compactification::ExteriorMapInput rotated_input{
|
||||
rotated_reference, rotated_displaced, rotated_displacement_jacobian,
|
||||
coordinate, rotated_coordinate_gradient
|
||||
rotated_reference, rotated_displaced, rotated_displacement_jacobian, coordinate, rotated_coordinate_gradient
|
||||
};
|
||||
mapping::compactification::ExteriorMapResult rotated_result;
|
||||
REQUIRE(
|
||||
compactification.Evaluate(rotated_input, rotated_result) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(compactification.Evaluate(rotated_input, rotated_result) == mapping::MappingStatus::valid);
|
||||
|
||||
mfem::Vector expected_position(dimension);
|
||||
rotation.Mult(result.physical_position, expected_position);
|
||||
@@ -608,9 +483,7 @@ TEST_CASE(
|
||||
mfem::Mult(rotation, result.mapping_jacobian, temporary);
|
||||
mfem::MultABt(temporary, rotation, expected_jacobian);
|
||||
|
||||
check_vector(
|
||||
rotated_result.physical_position, expected_position, tolerance
|
||||
);
|
||||
check_vector(rotated_result.physical_position, expected_position, tolerance);
|
||||
check_matrix(rotated_result.mapping_jacobian, expected_jacobian, tolerance);
|
||||
}
|
||||
|
||||
@@ -620,12 +493,8 @@ TEST_CASE(
|
||||
) {
|
||||
constexpr double tolerance = 1.0e-14;
|
||||
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0}
|
||||
);
|
||||
const mfem::Vector reference_position = make_vector(
|
||||
-0.5260553366425769, 0.5260553366425769, -0.6553163792879153
|
||||
);
|
||||
mapping::compactification::KelvinCompactification compactification({.r_star_ref = 1.0, .r_inf_ref = 4.0});
|
||||
const mfem::Vector reference_position = make_vector(-0.5260553366425769, 0.5260553366425769, -0.6553163792879153);
|
||||
const mfem::Vector displaced_position = make_vector(-0.55, 0.51, -0.63);
|
||||
const mfem::Vector coordinate_gradient = make_vector(-0.18, 0.18, -0.22);
|
||||
const mfem::DenseMatrix displacement_jacobian = make_identity();
|
||||
@@ -633,15 +502,11 @@ TEST_CASE(
|
||||
REQUIRE(reference_position.Norml2() < 1.0);
|
||||
|
||||
const mapping::compactification::ExteriorMapInput input{
|
||||
reference_position, displaced_position, displacement_jacobian, 0.0,
|
||||
coordinate_gradient
|
||||
reference_position, displaced_position, displacement_jacobian, 0.0, coordinate_gradient
|
||||
};
|
||||
mapping::compactification::ExteriorMapResult result;
|
||||
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input, result) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(compactification.Evaluate(input, result) == mapping::MappingStatus::valid);
|
||||
check_vector(result.physical_position, displaced_position, tolerance);
|
||||
CHECK(result.mapping_jacobian.Det() > 0.0);
|
||||
}
|
||||
@@ -658,35 +523,23 @@ TEST_CASE(
|
||||
constexpr double tolerance = 2.0e-11;
|
||||
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = r_star,
|
||||
.r_inf_ref = r_inf,
|
||||
.coordinate_tolerance = coordinate_tolerance}
|
||||
{.r_star_ref = r_star, .r_inf_ref = r_inf, .coordinate_tolerance = coordinate_tolerance}
|
||||
);
|
||||
const mfem::DenseMatrix identity = make_identity();
|
||||
const mfem::Vector coordinate_gradient =
|
||||
make_vector(1.0 / radial_extent, 0.0, 0.0);
|
||||
const mfem::DenseMatrix identity = make_identity();
|
||||
const mfem::Vector coordinate_gradient = make_vector(1.0 / radial_extent, 0.0, 0.0);
|
||||
|
||||
for (const double coordinate :
|
||||
std::array{0.0, 0.25, 0.75, 0.95, 0.99, 0.999}) {
|
||||
for (const double coordinate : std::array{0.0, 0.25, 0.75, 0.95, 0.99, 0.999}) {
|
||||
CAPTURE(coordinate);
|
||||
|
||||
const double computational_radius = r_star + coordinate * radial_extent;
|
||||
const mfem::Vector reference_position =
|
||||
make_vector(computational_radius, 0.0, 0.0);
|
||||
const double computational_radius = r_star + coordinate * radial_extent;
|
||||
const mfem::Vector reference_position = make_vector(computational_radius, 0.0, 0.0);
|
||||
const mapping::compactification::ExteriorMapInput input{
|
||||
reference_position, reference_position, identity, coordinate,
|
||||
coordinate_gradient
|
||||
reference_position, reference_position, identity, coordinate, coordinate_gradient
|
||||
};
|
||||
mapping::compactification::ExteriorMapResult result;
|
||||
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input, result) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
CHECK_THAT(
|
||||
result.physical_position.Norml2() * (1.0 - coordinate),
|
||||
WithinAbs(r_star, tolerance)
|
||||
);
|
||||
REQUIRE(compactification.Evaluate(input, result) == mapping::MappingStatus::valid);
|
||||
CHECK_THAT(result.physical_position.Norml2() * (1.0 - coordinate), WithinAbs(r_star, tolerance));
|
||||
}
|
||||
|
||||
const mfem::Vector reference_position = make_vector(r_inf, 0.0, 0.0);
|
||||
@@ -694,46 +547,37 @@ TEST_CASE(
|
||||
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, reference_position, identity, 1.0,
|
||||
coordinate_gradient},
|
||||
{reference_position, reference_position, identity, 1.0, coordinate_gradient}, result
|
||||
) == mapping::MappingStatus::at_compactified_infinity
|
||||
);
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, reference_position, identity, 1.0 - 0.5 * coordinate_tolerance, coordinate_gradient},
|
||||
result
|
||||
) == mapping::MappingStatus::at_compactified_infinity
|
||||
);
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, reference_position, identity,
|
||||
1.0 - 0.5 * coordinate_tolerance, coordinate_gradient},
|
||||
{reference_position, reference_position, identity, 1.0 + 0.5 * coordinate_tolerance, coordinate_gradient},
|
||||
result
|
||||
) == mapping::MappingStatus::at_compactified_infinity
|
||||
);
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, reference_position, identity,
|
||||
1.0 + 0.5 * coordinate_tolerance, coordinate_gradient},
|
||||
result
|
||||
) == mapping::MappingStatus::at_compactified_infinity
|
||||
);
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, reference_position, identity,
|
||||
1.0 + 2.0 * coordinate_tolerance, coordinate_gradient},
|
||||
{reference_position, reference_position, identity, 1.0 + 2.0 * coordinate_tolerance, coordinate_gradient},
|
||||
result
|
||||
) == mapping::MappingStatus::outside_reference_domain
|
||||
);
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, reference_position, identity,
|
||||
-2.0 * coordinate_tolerance, coordinate_gradient},
|
||||
result
|
||||
{reference_position, reference_position, identity, -2.0 * coordinate_tolerance, coordinate_gradient}, result
|
||||
) == mapping::MappingStatus::outside_reference_domain
|
||||
);
|
||||
|
||||
const mfem::Vector surface_position = make_vector(0.97, 0.0, 0.0);
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{surface_position, surface_position, identity,
|
||||
-0.5 * coordinate_tolerance, coordinate_gradient},
|
||||
result
|
||||
{surface_position, surface_position, identity, -0.5 * coordinate_tolerance, coordinate_gradient}, result
|
||||
) == mapping::MappingStatus::valid
|
||||
);
|
||||
check_vector(result.physical_position, surface_position, tolerance);
|
||||
@@ -743,9 +587,7 @@ TEST_CASE(
|
||||
"Kelvin Compactification Rejects Invalid Inputs And Inverted Maps",
|
||||
tags::unit &tags::mapping &tags::kelvin
|
||||
) {
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0}
|
||||
);
|
||||
mapping::compactification::KelvinCompactification compactification({.r_star_ref = 1.0, .r_inf_ref = 4.0});
|
||||
|
||||
const mfem::Vector reference_position = make_vector(2.0, 0.0, 0.0);
|
||||
const mfem::Vector displaced_position(reference_position);
|
||||
@@ -758,16 +600,12 @@ TEST_CASE(
|
||||
wrong_dimension = 1.0;
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{wrong_dimension, displaced_position, identity, 1.0 / 3.0,
|
||||
coordinate_gradient},
|
||||
result
|
||||
{wrong_dimension, displaced_position, identity, 1.0 / 3.0, coordinate_gradient}, result
|
||||
) == mapping::MappingStatus::invalid_dimension
|
||||
);
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, displaced_position, identity, 1.0 / 3.0,
|
||||
wrong_dimension},
|
||||
result
|
||||
{reference_position, displaced_position, identity, 1.0 / 3.0, wrong_dimension}, result
|
||||
) == mapping::MappingStatus::invalid_dimension
|
||||
);
|
||||
|
||||
@@ -775,9 +613,7 @@ TEST_CASE(
|
||||
non_finite_position(1) = std::numeric_limits<double>::quiet_NaN();
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{non_finite_position, displaced_position, identity, 1.0 / 3.0,
|
||||
coordinate_gradient},
|
||||
result
|
||||
{non_finite_position, displaced_position, identity, 1.0 / 3.0, coordinate_gradient}, result
|
||||
) == mapping::MappingStatus::non_finite_input
|
||||
);
|
||||
|
||||
@@ -785,15 +621,13 @@ TEST_CASE(
|
||||
non_finite_gradient(2) = std::numeric_limits<double>::infinity();
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, displaced_position, identity, 1.0 / 3.0,
|
||||
non_finite_gradient},
|
||||
result
|
||||
{reference_position, displaced_position, identity, 1.0 / 3.0, non_finite_gradient}, result
|
||||
) == mapping::MappingStatus::non_finite_input
|
||||
);
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, displaced_position, identity,
|
||||
std::numeric_limits<double>::quiet_NaN(), coordinate_gradient},
|
||||
{reference_position, displaced_position, identity, std::numeric_limits<double>::quiet_NaN(),
|
||||
coordinate_gradient},
|
||||
result
|
||||
) == mapping::MappingStatus::non_finite_input
|
||||
);
|
||||
@@ -802,9 +636,7 @@ TEST_CASE(
|
||||
singular_displacement_jacobian = 0.0;
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, displaced_position,
|
||||
singular_displacement_jacobian, 1.0 / 3.0, zero_gradient},
|
||||
result
|
||||
{reference_position, displaced_position, singular_displacement_jacobian, 1.0 / 3.0, zero_gradient}, result
|
||||
) == mapping::MappingStatus::non_positive_determinant
|
||||
);
|
||||
|
||||
@@ -812,9 +644,7 @@ TEST_CASE(
|
||||
inverted_displacement_jacobian(0, 0) = -1.0;
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, displaced_position,
|
||||
inverted_displacement_jacobian, 1.0 / 3.0, zero_gradient},
|
||||
result
|
||||
{reference_position, displaced_position, inverted_displacement_jacobian, 1.0 / 3.0, zero_gradient}, result
|
||||
) == mapping::MappingStatus::non_positive_determinant
|
||||
);
|
||||
}
|
||||
@@ -823,44 +653,34 @@ TEST_CASE(
|
||||
"Kelvin Compactification Variation Rejects Invalid Inputs",
|
||||
tags::unit &tags::mapping &tags::kelvin
|
||||
) {
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0}
|
||||
);
|
||||
mapping::compactification::KelvinCompactification compactification({.r_star_ref = 1.0, .r_inf_ref = 4.0});
|
||||
const mfem::Vector reference_position = make_vector(2.0, 0.0, 0.0);
|
||||
const mfem::Vector displaced_position(reference_position);
|
||||
const mfem::Vector coordinate_gradient = make_vector(1.0 / 3.0, 0.0, 0.0);
|
||||
const mfem::DenseMatrix identity = make_identity();
|
||||
const mapping::compactification::ExteriorMapInput input{
|
||||
reference_position, displaced_position, identity, 1.0 / 3.0,
|
||||
coordinate_gradient
|
||||
reference_position, displaced_position, identity, 1.0 / 3.0, coordinate_gradient
|
||||
};
|
||||
|
||||
mapping::compactification::ExteriorMapResult result;
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input, result) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(compactification.Evaluate(input, result) == mapping::MappingStatus::valid);
|
||||
|
||||
const mfem::Vector valid_position_direction =
|
||||
make_vector(0.01, -0.02, 0.03);
|
||||
const mfem::Vector valid_position_direction = make_vector(0.01, -0.02, 0.03);
|
||||
const mfem::DenseMatrix valid_jacobian_direction = make_identity();
|
||||
mapping::compactification::ExteriorMapVariation variation;
|
||||
|
||||
mfem::Vector wrong_dimension(2);
|
||||
wrong_dimension = 0.0;
|
||||
CHECK(
|
||||
compactification.EvaluateVariation(
|
||||
input, result, {wrong_dimension, valid_jacobian_direction},
|
||||
variation
|
||||
) == mapping::MappingStatus::invalid_dimension
|
||||
compactification.EvaluateVariation(input, result, {wrong_dimension, valid_jacobian_direction}, variation) ==
|
||||
mapping::MappingStatus::invalid_dimension
|
||||
);
|
||||
|
||||
mfem::DenseMatrix wrong_jacobian_dimension(2);
|
||||
wrong_jacobian_dimension = 0.0;
|
||||
CHECK(
|
||||
compactification.EvaluateVariation(
|
||||
input, result, {valid_position_direction, wrong_jacobian_dimension},
|
||||
variation
|
||||
input, result, {valid_position_direction, wrong_jacobian_dimension}, variation
|
||||
) == mapping::MappingStatus::invalid_dimension
|
||||
);
|
||||
|
||||
@@ -868,8 +688,7 @@ TEST_CASE(
|
||||
non_finite_direction(0) = std::numeric_limits<double>::quiet_NaN();
|
||||
CHECK(
|
||||
compactification.EvaluateVariation(
|
||||
input, result, {non_finite_direction, valid_jacobian_direction},
|
||||
variation
|
||||
input, result, {non_finite_direction, valid_jacobian_direction}, variation
|
||||
) == mapping::MappingStatus::non_finite_input
|
||||
);
|
||||
}
|
||||
@@ -880,43 +699,24 @@ TEST_CASE(
|
||||
) {
|
||||
constexpr double tolerance = 0.0;
|
||||
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0}
|
||||
);
|
||||
mapping::compactification::KelvinCompactification compactification({.r_star_ref = 1.0, .r_inf_ref = 4.0});
|
||||
const mfem::DenseMatrix identity = make_identity();
|
||||
const mfem::Vector gradient_a = make_vector(0.12, 0.03, -0.02);
|
||||
const mfem::Vector gradient_b = make_vector(-0.04, 0.15, 0.01);
|
||||
const mfem::Vector reference_a = make_vector(1.5, 0.2, 0.1);
|
||||
const mfem::Vector reference_b = make_vector(2.5, -0.3, 0.4);
|
||||
|
||||
const mapping::compactification::ExteriorMapInput input_a{
|
||||
reference_a, reference_a, identity, 0.25, gradient_a
|
||||
};
|
||||
const mapping::compactification::ExteriorMapInput input_b{
|
||||
reference_b, reference_b, identity, 0.70, gradient_b
|
||||
};
|
||||
const mapping::compactification::ExteriorMapInput input_a{reference_a, reference_a, identity, 0.25, gradient_a};
|
||||
const mapping::compactification::ExteriorMapInput input_b{reference_b, reference_b, identity, 0.70, gradient_b};
|
||||
|
||||
mapping::compactification::ExteriorMapResult first_a;
|
||||
mapping::compactification::ExteriorMapResult result_b;
|
||||
mapping::compactification::ExteriorMapResult second_a;
|
||||
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input_a, first_a) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input_b, result_b) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input_a, second_a) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(compactification.Evaluate(input_a, first_a) == mapping::MappingStatus::valid);
|
||||
REQUIRE(compactification.Evaluate(input_b, result_b) == mapping::MappingStatus::valid);
|
||||
REQUIRE(compactification.Evaluate(input_a, second_a) == mapping::MappingStatus::valid);
|
||||
|
||||
check_vector(
|
||||
first_a.physical_position, second_a.physical_position, tolerance
|
||||
);
|
||||
check_matrix(
|
||||
first_a.mapping_jacobian, second_a.mapping_jacobian, tolerance
|
||||
);
|
||||
check_vector(first_a.physical_position, second_a.physical_position, tolerance);
|
||||
check_matrix(first_a.mapping_jacobian, second_a.mapping_jacobian, tolerance);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,8 +38,7 @@ namespace {
|
||||
return identity;
|
||||
}
|
||||
|
||||
mapping::MappingPointContext
|
||||
make_context(const mfem::DenseMatrix &jacobian) {
|
||||
mapping::MappingPointContext make_context(const mfem::DenseMatrix &jacobian) {
|
||||
mapping::MappingPointContext context;
|
||||
context.mapping_jacobian = jacobian;
|
||||
context.mapping_determinant = jacobian.Det();
|
||||
@@ -70,9 +69,8 @@ namespace {
|
||||
const mfem::DenseMatrix &jacobian_variation
|
||||
) {
|
||||
mapping::MappingPointVariation variation;
|
||||
variation.mapping_jacobian_variation = jacobian_variation;
|
||||
variation.mapping_determinant_variation =
|
||||
determinant_variation(jacobian, jacobian_variation);
|
||||
variation.mapping_jacobian_variation = jacobian_variation;
|
||||
variation.mapping_determinant_variation = determinant_variation(jacobian, jacobian_variation);
|
||||
variation.physical_position_variation.SetSize(dimension);
|
||||
variation.physical_position_variation = 0.0;
|
||||
return variation;
|
||||
@@ -99,11 +97,7 @@ namespace {
|
||||
mfem::DenseMatrix difference(computed);
|
||||
difference -= reference;
|
||||
|
||||
return matrix_norm(difference) /
|
||||
std::max(
|
||||
matrix_norm(reference),
|
||||
std::numeric_limits<double>::epsilon()
|
||||
);
|
||||
return matrix_norm(difference) / std::max(matrix_norm(reference), std::numeric_limits<double>::epsilon());
|
||||
}
|
||||
|
||||
double matrix_asymmetry(const mfem::DenseMatrix &matrix) {
|
||||
@@ -111,8 +105,7 @@ namespace {
|
||||
|
||||
for (int row = 0; row < matrix.Height(); ++row) {
|
||||
for (int column = 0; column < matrix.Width(); ++column) {
|
||||
const double difference =
|
||||
matrix(row, column) - matrix(column, row);
|
||||
const double difference = matrix(row, column) - matrix(column, row);
|
||||
asymmetry_squared += difference * difference;
|
||||
}
|
||||
}
|
||||
@@ -130,20 +123,16 @@ namespace {
|
||||
|
||||
for (int row = 0; row < dimension; ++row) {
|
||||
for (int column = 0; column < dimension; ++column) {
|
||||
plus_jacobian(row, column) +=
|
||||
step * jacobian_variation(row, column);
|
||||
minus_jacobian(row, column) -=
|
||||
step * jacobian_variation(row, column);
|
||||
plus_jacobian(row, column) += step * jacobian_variation(row, column);
|
||||
minus_jacobian(row, column) -= step * jacobian_variation(row, column);
|
||||
}
|
||||
}
|
||||
|
||||
REQUIRE(plus_jacobian.Det() > 0.0);
|
||||
REQUIRE(minus_jacobian.Det() > 0.0);
|
||||
|
||||
const mapping::MappingPointContext plus_context =
|
||||
make_context(plus_jacobian);
|
||||
const mapping::MappingPointContext minus_context =
|
||||
make_context(minus_jacobian);
|
||||
const mapping::MappingPointContext plus_context = make_context(plus_jacobian);
|
||||
const mapping::MappingPointContext minus_context = make_context(minus_jacobian);
|
||||
|
||||
mfem::DenseMatrix plus_tensor;
|
||||
mfem::DenseMatrix minus_tensor;
|
||||
@@ -184,22 +173,17 @@ TEST_CASE(
|
||||
);
|
||||
|
||||
cases.push_back(
|
||||
{"anisotropic stretch",
|
||||
make_matrix({1.20, 0.00, 0.00, 0.00, 0.85, 0.00, 0.00, 0.00, 1.10}),
|
||||
{"anisotropic stretch", make_matrix({1.20, 0.00, 0.00, 0.00, 0.85, 0.00, 0.00, 0.00, 1.10}),
|
||||
make_matrix({0.08, 0.01, -0.03, 0.02, -0.05, 0.04, 0.01, -0.02, 0.07})}
|
||||
);
|
||||
|
||||
cases.push_back(
|
||||
{"sheared mapping",
|
||||
make_matrix({1.10, 0.20, -0.05, 0.04, 0.90, 0.12, -0.03, 0.08, 1.15}),
|
||||
make_matrix(
|
||||
{0.06, -0.04, 0.02, 0.03, 0.05, -0.07, -0.01, 0.04, -0.02}
|
||||
)}
|
||||
{"sheared mapping", make_matrix({1.10, 0.20, -0.05, 0.04, 0.90, 0.12, -0.03, 0.08, 1.15}),
|
||||
make_matrix({0.06, -0.04, 0.02, 0.03, 0.05, -0.07, -0.01, 0.04, -0.02})}
|
||||
);
|
||||
|
||||
cases.push_back(
|
||||
{"strong general mapping",
|
||||
make_matrix({1.35, 0.31, -0.18, -0.12, 0.78, 0.22, 0.09, -0.16, 1.27}),
|
||||
{"strong general mapping", make_matrix({1.35, 0.31, -0.18, -0.12, 0.78, 0.22, 0.09, -0.16, 1.27}),
|
||||
make_matrix({-0.11, 0.08, 0.05, 0.07, 0.09, -0.04, -0.06, 0.03, 0.12})}
|
||||
);
|
||||
|
||||
@@ -207,37 +191,22 @@ TEST_CASE(
|
||||
DYNAMIC_SECTION(test_case.name) {
|
||||
REQUIRE(test_case.jacobian.Det() > 0.0);
|
||||
|
||||
const mapping::MappingPointContext context =
|
||||
make_context(test_case.jacobian);
|
||||
const mapping::MappingPointVariation variation = make_variation(
|
||||
test_case.jacobian, test_case.jacobian_variation
|
||||
);
|
||||
const mapping::MappingPointContext context = make_context(test_case.jacobian);
|
||||
const mapping::MappingPointVariation variation =
|
||||
make_variation(test_case.jacobian, test_case.jacobian_variation);
|
||||
|
||||
mfem::DenseMatrix analytic_variation;
|
||||
mapping::ComputeHDivMassTensorVariation(
|
||||
context, variation, analytic_variation
|
||||
);
|
||||
mapping::ComputeHDivMassTensorVariation(context, variation, analytic_variation);
|
||||
|
||||
const mfem::DenseMatrix finite_difference =
|
||||
centered_mass_tensor_difference(
|
||||
test_case.jacobian, test_case.jacobian_variation, 1.0e-6
|
||||
);
|
||||
const double relative_error =
|
||||
relative_matrix_error(analytic_variation, finite_difference);
|
||||
const double asymmetry = matrix_asymmetry(analytic_variation);
|
||||
centered_mass_tensor_difference(test_case.jacobian, test_case.jacobian_variation, 1.0e-6);
|
||||
const double relative_error = relative_matrix_error(analytic_variation, finite_difference);
|
||||
const double asymmetry = matrix_asymmetry(analytic_variation);
|
||||
|
||||
INFO("Mapping determinant = " << context.mapping_determinant);
|
||||
INFO(
|
||||
"Determinant variation = "
|
||||
<< variation.mapping_determinant_variation
|
||||
);
|
||||
INFO(
|
||||
"Analytic variation norm = " << matrix_norm(analytic_variation)
|
||||
);
|
||||
INFO(
|
||||
"Finite-difference variation norm = "
|
||||
<< matrix_norm(finite_difference)
|
||||
);
|
||||
INFO("Determinant variation = " << variation.mapping_determinant_variation);
|
||||
INFO("Analytic variation norm = " << matrix_norm(analytic_variation));
|
||||
INFO("Finite-difference variation norm = " << matrix_norm(finite_difference));
|
||||
INFO("Relative tensor-variation error = " << relative_error);
|
||||
INFO("Tensor-variation asymmetry = " << asymmetry);
|
||||
|
||||
@@ -252,31 +221,24 @@ TEST_CASE(
|
||||
"Convergence",
|
||||
tags::unit &tags::transformations &tags::convergence
|
||||
) {
|
||||
const mfem::DenseMatrix jacobian =
|
||||
make_matrix({1.18, 0.17, -0.09, -0.04, 0.92, 0.14, 0.07, -0.11, 1.23});
|
||||
const mfem::DenseMatrix jacobian = make_matrix({1.18, 0.17, -0.09, -0.04, 0.92, 0.14, 0.07, -0.11, 1.23});
|
||||
|
||||
const mfem::DenseMatrix jacobian_variation =
|
||||
make_matrix({0.09, -0.06, 0.04, 0.03, 0.07, -0.05, -0.02, 0.08, -0.03});
|
||||
|
||||
const mapping::MappingPointContext context = make_context(jacobian);
|
||||
const mapping::MappingPointVariation variation =
|
||||
make_variation(jacobian, jacobian_variation);
|
||||
const mapping::MappingPointContext context = make_context(jacobian);
|
||||
const mapping::MappingPointVariation variation = make_variation(jacobian, jacobian_variation);
|
||||
|
||||
mfem::DenseMatrix analytic_variation;
|
||||
mapping::ComputeHDivMassTensorVariation(
|
||||
context, variation, analytic_variation
|
||||
);
|
||||
mapping::ComputeHDivMassTensorVariation(context, variation, analytic_variation);
|
||||
|
||||
const std::array<double, 3> steps{4.0e-2, 2.0e-2, 1.0e-2};
|
||||
std::array<double, 3> errors{};
|
||||
|
||||
for (int i = 0; i < static_cast<int>(steps.size()); ++i) {
|
||||
const mfem::DenseMatrix finite_difference =
|
||||
centered_mass_tensor_difference(
|
||||
jacobian, jacobian_variation, steps[i]
|
||||
);
|
||||
errors[i] =
|
||||
relative_matrix_error(finite_difference, analytic_variation);
|
||||
centered_mass_tensor_difference(jacobian, jacobian_variation, steps[i]);
|
||||
errors[i] = relative_matrix_error(finite_difference, analytic_variation);
|
||||
INFO("Step = " << steps[i] << ", relative error = " << errors[i]);
|
||||
}
|
||||
|
||||
@@ -295,24 +257,20 @@ TEST_CASE(
|
||||
"Hdiv Mass Tensor Variation Vanishes For Translation",
|
||||
tags::unit &tags::transformations
|
||||
) {
|
||||
const mfem::DenseMatrix jacobian =
|
||||
make_matrix({1.12, 0.08, -0.03, 0.02, 0.94, 0.07, -0.01, 0.05, 1.09});
|
||||
const mfem::DenseMatrix jacobian = make_matrix({1.12, 0.08, -0.03, 0.02, 0.94, 0.07, -0.01, 0.05, 1.09});
|
||||
|
||||
mfem::DenseMatrix zero_jacobian_variation(dimension);
|
||||
zero_jacobian_variation = 0.0;
|
||||
zero_jacobian_variation = 0.0;
|
||||
|
||||
mapping::MappingPointContext context = make_context(jacobian);
|
||||
mapping::MappingPointVariation variation =
|
||||
make_variation(jacobian, zero_jacobian_variation);
|
||||
mapping::MappingPointContext context = make_context(jacobian);
|
||||
mapping::MappingPointVariation variation = make_variation(jacobian, zero_jacobian_variation);
|
||||
variation.physical_position_variation.SetSize(dimension);
|
||||
variation.physical_position_variation(0) = 0.7;
|
||||
variation.physical_position_variation(1) = -0.4;
|
||||
variation.physical_position_variation(2) = 0.9;
|
||||
|
||||
mfem::DenseMatrix tensor_variation;
|
||||
mapping::ComputeHDivMassTensorVariation(
|
||||
context, variation, tensor_variation
|
||||
);
|
||||
mapping::ComputeHDivMassTensorVariation(context, variation, tensor_variation);
|
||||
|
||||
CHECK_THAT(variation.mapping_determinant_variation, WithinAbs(0.0, 0.0));
|
||||
check_zero_matrix(tensor_variation, 1.0e-14);
|
||||
@@ -323,23 +281,17 @@ TEST_CASE(
|
||||
"Identity",
|
||||
tags::unit &tags::transformations
|
||||
) {
|
||||
const mfem::DenseMatrix identity = make_identity_matrix();
|
||||
const mfem::DenseMatrix identity = make_identity_matrix();
|
||||
|
||||
const mfem::DenseMatrix rotation_variation =
|
||||
make_matrix({0.0, -0.30, 0.20, 0.30, 0.0, -0.15, -0.20, 0.15, 0.0});
|
||||
const mfem::DenseMatrix rotation_variation = make_matrix({0.0, -0.30, 0.20, 0.30, 0.0, -0.15, -0.20, 0.15, 0.0});
|
||||
|
||||
const mapping::MappingPointContext context = make_context(identity);
|
||||
const mapping::MappingPointVariation variation =
|
||||
make_variation(identity, rotation_variation);
|
||||
const mapping::MappingPointVariation variation = make_variation(identity, rotation_variation);
|
||||
|
||||
mfem::DenseMatrix tensor_variation;
|
||||
mapping::ComputeHDivMassTensorVariation(
|
||||
context, variation, tensor_variation
|
||||
);
|
||||
mapping::ComputeHDivMassTensorVariation(context, variation, tensor_variation);
|
||||
|
||||
CHECK_THAT(
|
||||
variation.mapping_determinant_variation, WithinAbs(0.0, 1.0e-15)
|
||||
);
|
||||
CHECK_THAT(variation.mapping_determinant_variation, WithinAbs(0.0, 1.0e-15));
|
||||
check_zero_matrix(tensor_variation, 1.0e-14);
|
||||
}
|
||||
|
||||
@@ -355,26 +307,18 @@ TEST_CASE(
|
||||
for (int i = 0; i < dimension; ++i)
|
||||
jacobian_variation(i, i) = scaling_variation;
|
||||
|
||||
const mapping::MappingPointContext context = make_context(identity);
|
||||
const mapping::MappingPointVariation variation =
|
||||
make_variation(identity, jacobian_variation);
|
||||
const mapping::MappingPointContext context = make_context(identity);
|
||||
const mapping::MappingPointVariation variation = make_variation(identity, jacobian_variation);
|
||||
|
||||
mfem::DenseMatrix tensor_variation;
|
||||
mapping::ComputeHDivMassTensorVariation(
|
||||
context, variation, tensor_variation
|
||||
);
|
||||
mapping::ComputeHDivMassTensorVariation(context, variation, tensor_variation);
|
||||
|
||||
CHECK_THAT(
|
||||
variation.mapping_determinant_variation,
|
||||
WithinAbs(3.0 * scaling_variation, 1.0e-14)
|
||||
);
|
||||
CHECK_THAT(variation.mapping_determinant_variation, WithinAbs(3.0 * scaling_variation, 1.0e-14));
|
||||
|
||||
for (int row = 0; row < dimension; ++row) {
|
||||
for (int column = 0; column < dimension; ++column) {
|
||||
const double expected = row == column ? -scaling_variation : 0.0;
|
||||
CHECK_THAT(
|
||||
tensor_variation(row, column), WithinAbs(expected, 1.0e-14)
|
||||
);
|
||||
CHECK_THAT(tensor_variation(row, column), WithinAbs(expected, 1.0e-14));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -387,21 +331,16 @@ TEST_CASE(
|
||||
|
||||
const mfem::DenseMatrix identity = make_identity_matrix();
|
||||
mfem::DenseMatrix jacobian_variation(dimension);
|
||||
jacobian_variation = 0.0;
|
||||
jacobian_variation(0, 1) = shear_variation;
|
||||
jacobian_variation = 0.0;
|
||||
jacobian_variation(0, 1) = shear_variation;
|
||||
|
||||
const mapping::MappingPointContext context = make_context(identity);
|
||||
const mapping::MappingPointVariation variation =
|
||||
make_variation(identity, jacobian_variation);
|
||||
const mapping::MappingPointContext context = make_context(identity);
|
||||
const mapping::MappingPointVariation variation = make_variation(identity, jacobian_variation);
|
||||
|
||||
mfem::DenseMatrix tensor_variation;
|
||||
mapping::ComputeHDivMassTensorVariation(
|
||||
context, variation, tensor_variation
|
||||
);
|
||||
mapping::ComputeHDivMassTensorVariation(context, variation, tensor_variation);
|
||||
|
||||
CHECK_THAT(
|
||||
variation.mapping_determinant_variation, WithinAbs(0.0, 1.0e-15)
|
||||
);
|
||||
CHECK_THAT(variation.mapping_determinant_variation, WithinAbs(0.0, 1.0e-15));
|
||||
CHECK_THAT(tensor_variation(0, 1), WithinAbs(shear_variation, 1.0e-14));
|
||||
CHECK_THAT(tensor_variation(1, 0), WithinAbs(shear_variation, 1.0e-14));
|
||||
|
||||
@@ -418,16 +357,14 @@ TEST_CASE(
|
||||
"Mapping Determinant Variation Matches Jacobi Formula",
|
||||
tags::unit &tags::transformations
|
||||
) {
|
||||
const mfem::DenseMatrix jacobian =
|
||||
make_matrix({1.24, 0.19, -0.07, -0.06, 0.88, 0.16, 0.04, -0.12, 1.19});
|
||||
const mfem::DenseMatrix jacobian = make_matrix({1.24, 0.19, -0.07, -0.06, 0.88, 0.16, 0.04, -0.12, 1.19});
|
||||
|
||||
const mfem::DenseMatrix jacobian_variation =
|
||||
make_matrix({0.08, -0.03, 0.05, 0.02, 0.06, -0.04, -0.01, 0.07, -0.02});
|
||||
|
||||
const mapping::MappingPointContext context = make_context(jacobian);
|
||||
const mapping::MappingPointVariation variation =
|
||||
make_variation(jacobian, jacobian_variation);
|
||||
constexpr double difference_step = 1.0e-3;
|
||||
const mapping::MappingPointContext context = make_context(jacobian);
|
||||
const mapping::MappingPointVariation variation = make_variation(jacobian, jacobian_variation);
|
||||
constexpr double difference_step = 1.0e-3;
|
||||
|
||||
mfem::DenseMatrix plus_one(context.mapping_jacobian);
|
||||
mfem::DenseMatrix plus_two(context.mapping_jacobian);
|
||||
@@ -439,12 +376,10 @@ TEST_CASE(
|
||||
minus_one.Add(-difference_step, variation.mapping_jacobian_variation);
|
||||
minus_two.Add(-2.0 * difference_step, variation.mapping_jacobian_variation);
|
||||
|
||||
const double finite_difference = (minus_two.Det() - 8.0 * minus_one.Det() +
|
||||
8.0 * plus_one.Det() - plus_two.Det()) /
|
||||
(12.0 * difference_step);
|
||||
const double analytic = variation.mapping_determinant_variation;
|
||||
const double relative_error = std::abs(finite_difference - analytic) /
|
||||
std::max(std::abs(analytic), 1.0e-14);
|
||||
const double finite_difference =
|
||||
(minus_two.Det() - 8.0 * minus_one.Det() + 8.0 * plus_one.Det() - plus_two.Det()) / (12.0 * difference_step);
|
||||
const double analytic = variation.mapping_determinant_variation;
|
||||
const double relative_error = std::abs(finite_difference - analytic) / std::max(std::abs(analytic), 1.0e-14);
|
||||
|
||||
INFO("Analytic determinant variation = " << analytic);
|
||||
INFO("Finite-difference determinant variation = " << finite_difference);
|
||||
|
||||
259
tests/models/stellar_model.cpp
Normal file
259
tests/models/stellar_model.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace {
|
||||
struct StellarModelExtensionTracker final {
|
||||
int structureValidationCount{0};
|
||||
int surfaceValidationCount{0};
|
||||
int surfaceResolutionCount{0};
|
||||
|
||||
const mean_field::eos::EquationOfState *structureEquationOfState{nullptr};
|
||||
|
||||
const mean_field::eos::EquationOfState *surfaceValidationEquationOfState{nullptr};
|
||||
|
||||
const mean_field::eos::EquationOfState *surfaceResolutionEquationOfState{nullptr};
|
||||
};
|
||||
|
||||
class StellarModelTestStructure final : public mean_field::models::structure::StructureBase {
|
||||
public:
|
||||
explicit StellarModelTestStructure(std::shared_ptr<StellarModelExtensionTracker> tracker)
|
||||
: m_tracker(std::move(tracker)),
|
||||
m_equationOfState(
|
||||
3.0,
|
||||
0.25
|
||||
) {
|
||||
}
|
||||
|
||||
[[nodiscard]] const mean_field::eos::EquationOfState &equationOfState() const noexcept override {
|
||||
m_tracker->structureEquationOfState = &m_equationOfState;
|
||||
return m_equationOfState;
|
||||
}
|
||||
|
||||
[[nodiscard]] double targetMass() const noexcept override {
|
||||
return 2.5;
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::models::structure::StructureSeed
|
||||
makeInitialSeed(const mean_field::models::structure::StructureSeedRequest &request) const override {
|
||||
mean_field::models::structure::StructureSeed seed;
|
||||
|
||||
seed.radius.SetSize(2);
|
||||
seed.density.SetSize(2);
|
||||
seed.enthalpy.SetSize(2);
|
||||
|
||||
seed.radius(0) = 0.0;
|
||||
seed.radius(1) = 1.0;
|
||||
|
||||
seed.density(0) = request.centralDensity;
|
||||
seed.density(1) = 0.0;
|
||||
|
||||
seed.enthalpy(0) = 1.0;
|
||||
seed.enthalpy(1) = 0.0;
|
||||
|
||||
seed.stellarRadius = 1.0;
|
||||
seed.centralDensity = request.centralDensity;
|
||||
seed.centralEnthalpy = 1.0;
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
void validate() const override {
|
||||
++m_tracker->structureValidationCount;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<StellarModelExtensionTracker> m_tracker;
|
||||
mean_field::eos::Polytrope m_equationOfState;
|
||||
};
|
||||
|
||||
class StellarModelTestSurface final : public mean_field::surface::SurfaceBase {
|
||||
public:
|
||||
explicit StellarModelTestSurface(std::shared_ptr<StellarModelExtensionTracker> tracker)
|
||||
: m_tracker(std::move(tracker)) {
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
mean_field::surface::ResolvedSurfaceCondition
|
||||
resolve(const mean_field::eos::EquationOfState &equationOfState) const override {
|
||||
++m_tracker->surfaceResolutionCount;
|
||||
|
||||
m_tracker->surfaceResolutionEquationOfState = &equationOfState;
|
||||
|
||||
return mean_field::surface::ResolvedSurfaceCondition{0.375};
|
||||
}
|
||||
|
||||
void validate(const mean_field::eos::EquationOfState &equationOfState) const override {
|
||||
++m_tracker->surfaceValidationCount;
|
||||
|
||||
m_tracker->surfaceValidationEquationOfState = &equationOfState;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<StellarModelExtensionTracker> m_tracker;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_CASE(
|
||||
"Stellar Model Owns Structure And Surface Prescriptions",
|
||||
tags::barotrope &tags::unit &tags::model
|
||||
) {
|
||||
STATIC_REQUIRE_FALSE(std::is_copy_constructible_v<mean_field::models::StellarModel>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(std::is_copy_assignable_v<mean_field::models::StellarModel>);
|
||||
|
||||
STATIC_REQUIRE(std::is_nothrow_move_constructible_v<mean_field::models::StellarModel>);
|
||||
|
||||
STATIC_REQUIRE(std::is_nothrow_move_assignable_v<mean_field::models::StellarModel>);
|
||||
|
||||
mean_field::models::StellarModel model{
|
||||
mean_field::models::structure::PolytropicStructure{mean_field::eos::Polytrope{3.0, 0.25}, 1.0},
|
||||
mean_field::surface::Isobaric{0.0}
|
||||
};
|
||||
|
||||
CHECK(model.targetMass() == 1.0);
|
||||
CHECK(model.resolvedSurfaceCondition().targetEnthalpy == 0.0);
|
||||
|
||||
CHECK(
|
||||
dynamic_cast<const mean_field::models::structure::PolytropicStructure *>(&model.structurePrescription()) !=
|
||||
nullptr
|
||||
);
|
||||
|
||||
CHECK(dynamic_cast<const mean_field::surface::Isobaric *>(&model.surfacePrescription()) != nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Stellar Model Delegates Seed Construction To Its Structure",
|
||||
tags::barotrope &tags::unit &tags::model
|
||||
) {
|
||||
mean_field::models::StellarModel model{
|
||||
mean_field::models::structure::PolytropicStructure{mean_field::eos::Polytrope{3.0, 0.25}, 1.0},
|
||||
mean_field::surface::Isobaric{}
|
||||
};
|
||||
|
||||
const mean_field::models::structure::StructureSeed seed =
|
||||
model.makeInitialSeed({.centralDensity = 2.0, .radialSampleCount = 64});
|
||||
|
||||
CHECK(seed.radius.Size() == 64);
|
||||
CHECK(seed.density.Size() == 64);
|
||||
CHECK(seed.enthalpy.Size() == 64);
|
||||
CHECK(seed.centralDensity == 2.0);
|
||||
CHECK(seed.stellarRadius > 0.0);
|
||||
CHECK(seed.density(0) == 2.0);
|
||||
CHECK(seed.density(63) == 0.0);
|
||||
CHECK(seed.enthalpy(63) == 0.0);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Moving A Stellar Model Preserves Stable Prescription Addresses",
|
||||
tags::barotrope &tags::unit &tags::model
|
||||
) {
|
||||
mean_field::models::StellarModel originalModel{
|
||||
mean_field::models::structure::PolytropicStructure{mean_field::eos::Polytrope{3.0, 0.25}, 1.0},
|
||||
mean_field::surface::Isobaric{}
|
||||
};
|
||||
|
||||
const mean_field::models::structure::StructureBase *structureAddress = &originalModel.structurePrescription();
|
||||
|
||||
const mean_field::surface::SurfaceBase *surfaceAddress = &originalModel.surfacePrescription();
|
||||
|
||||
const mean_field::eos::EquationOfState *equationOfStateAddress = &originalModel.equationOfState();
|
||||
|
||||
mean_field::models::StellarModel movedModel{std::move(originalModel)};
|
||||
|
||||
CHECK(&movedModel.structurePrescription() == structureAddress);
|
||||
CHECK(&movedModel.surfacePrescription() == surfaceAddress);
|
||||
CHECK(&movedModel.equationOfState() == equationOfStateAddress);
|
||||
CHECK(movedModel.targetMass() == 1.0);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Stellar Model Resolves A Positive Isobaric Surface",
|
||||
tags::barotrope &tags::unit &tags::model
|
||||
) {
|
||||
constexpr double targetPressure = 0.03125;
|
||||
|
||||
mean_field::models::StellarModel model{
|
||||
mean_field::models::structure::PolytropicStructure{mean_field::eos::Polytrope{3.0, 0.25}, 1.0},
|
||||
mean_field::surface::Isobaric{targetPressure}
|
||||
};
|
||||
|
||||
const double targetEnthalpy = model.resolvedSurfaceCondition().targetEnthalpy;
|
||||
|
||||
CHECK(targetEnthalpy > 0.0);
|
||||
CHECK(
|
||||
std::abs(model.equationOfState().pressure_from_enthalpy(targetEnthalpy) - targetPressure) <
|
||||
64.0 * std::numeric_limits<double>::epsilon()
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Stellar Model Supports Custom Structure And Surface Prescriptions",
|
||||
tags::barotrope &tags::unit &tags::model
|
||||
) {
|
||||
const auto tracker = std::make_shared<StellarModelExtensionTracker>();
|
||||
|
||||
mean_field::models::StellarModel model{StellarModelTestStructure{tracker}, StellarModelTestSurface{tracker}};
|
||||
|
||||
REQUIRE(tracker->structureValidationCount == 1);
|
||||
REQUIRE(tracker->surfaceValidationCount == 1);
|
||||
REQUIRE(tracker->surfaceResolutionCount == 1);
|
||||
|
||||
CHECK(dynamic_cast<const StellarModelTestStructure *>(&model.structurePrescription()) != nullptr);
|
||||
|
||||
CHECK(dynamic_cast<const StellarModelTestSurface *>(&model.surfacePrescription()) != nullptr);
|
||||
|
||||
const mean_field::eos::EquationOfState *ownedEquationOfState = &model.equationOfState();
|
||||
|
||||
CHECK(tracker->structureEquationOfState == ownedEquationOfState);
|
||||
|
||||
CHECK(tracker->surfaceValidationEquationOfState == ownedEquationOfState);
|
||||
|
||||
CHECK(tracker->surfaceResolutionEquationOfState == ownedEquationOfState);
|
||||
|
||||
CHECK(model.targetMass() == 2.5);
|
||||
|
||||
CHECK(model.resolvedSurfaceCondition().targetEnthalpy == 0.375);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Move Assignment Preserves Stellar Model Prescription Addresses",
|
||||
tags::barotrope &tags::unit &tags::model
|
||||
) {
|
||||
mean_field::models::StellarModel sourceModel{
|
||||
mean_field::models::structure::PolytropicStructure{mean_field::eos::Polytrope{3.0, 0.25}, 1.25},
|
||||
mean_field::surface::Isobaric{0.0}
|
||||
};
|
||||
|
||||
mean_field::models::StellarModel destinationModel{
|
||||
mean_field::models::structure::PolytropicStructure{mean_field::eos::Polytrope{2.0, 0.5}, 4.0},
|
||||
mean_field::surface::Isobaric{0.02}
|
||||
};
|
||||
|
||||
const mean_field::models::structure::StructureBase *sourceStructureAddress = &sourceModel.structurePrescription();
|
||||
|
||||
const mean_field::surface::SurfaceBase *sourceSurfaceAddress = &sourceModel.surfacePrescription();
|
||||
|
||||
const mean_field::eos::EquationOfState *sourceEquationOfStateAddress = &sourceModel.equationOfState();
|
||||
|
||||
const double sourceTargetEnthalpy = sourceModel.resolvedSurfaceCondition().targetEnthalpy;
|
||||
|
||||
destinationModel = std::move(sourceModel);
|
||||
|
||||
CHECK(&destinationModel.structurePrescription() == sourceStructureAddress);
|
||||
|
||||
CHECK(&destinationModel.surfacePrescription() == sourceSurfaceAddress);
|
||||
|
||||
CHECK(&destinationModel.equationOfState() == sourceEquationOfStateAddress);
|
||||
|
||||
CHECK(destinationModel.targetMass() == 1.25);
|
||||
|
||||
CHECK(destinationModel.resolvedSurfaceCondition().targetEnthalpy == sourceTargetEnthalpy);
|
||||
}
|
||||
@@ -1,297 +1,416 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <mfem.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace barotropic_closure_context_test_utils {
|
||||
mfem::Vector project_field(
|
||||
mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||
mfem::Coefficient &coefficient
|
||||
namespace field = mean_field::field;
|
||||
namespace domain = mean_field::utils::domain;
|
||||
namespace context = mean_field::operators::context::barotropic;
|
||||
|
||||
using Schema = domain::CoreEnvelopeVacuumDomainSchema;
|
||||
|
||||
struct Maps final {
|
||||
field::FieldDofMap density;
|
||||
field::FieldDofMap enthalpy;
|
||||
field::FieldDofMap displacement;
|
||||
|
||||
explicit Maps(const mean_field::fem::FEM &f)
|
||||
: density(
|
||||
field::make_field_dof_map<
|
||||
field::Density,
|
||||
Schema>(*f.densityFes)
|
||||
),
|
||||
enthalpy(
|
||||
field::make_field_dof_map<
|
||||
field::Enthalpy,
|
||||
Schema>(*f.enthalpyFes)
|
||||
),
|
||||
displacement(
|
||||
field::make_field_dof_map<
|
||||
field::Displacement,
|
||||
Schema>(*f.displacementFes)
|
||||
) {
|
||||
}
|
||||
};
|
||||
|
||||
[[nodiscard]] context::BarotropicClosureDependencies make_dependencies() {
|
||||
return {
|
||||
.discretization = {.identity = 101, .revision = 2},
|
||||
.density = {.identity = 103, .revision = 3},
|
||||
.enthalpy = {.identity = 107, .revision = 5},
|
||||
.displacement = {.identity = 109, .revision = 7}
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] context::BarotropicClosureStateView make_state(
|
||||
const mfem::Vector &density,
|
||||
const mfem::Vector &enthalpy,
|
||||
const mfem::Vector &displacement
|
||||
) {
|
||||
mfem::ParGridFunction field(&finiteElementSpace);
|
||||
|
||||
field.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector trueVector;
|
||||
field.GetTrueDofs(trueVector);
|
||||
|
||||
return trueVector;
|
||||
return {.density = density, .enthalpy = enthalpy, .displacement = displacement};
|
||||
}
|
||||
|
||||
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);
|
||||
[[nodiscard]] mfem::Vector reduce(
|
||||
const field::FieldDofMap &map,
|
||||
const mfem::Vector &full
|
||||
) {
|
||||
return map.gather(full);
|
||||
}
|
||||
|
||||
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);
|
||||
[[nodiscard]] mfem::Vector make_density(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase
|
||||
) {
|
||||
mfem::ParGridFunction value(f.densityFes.get());
|
||||
mfem::FunctionCoefficient coefficient([phase](const mfem::Vector &position) {
|
||||
return 0.71 + 0.05 * std::sin(0.63 * position(0) + phase) + 0.02 * position(1);
|
||||
});
|
||||
value.ProjectCoefficient(coefficient);
|
||||
mfem::Vector result;
|
||||
value.GetTrueDofs(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return project_field(*f.enthalpyFes, coefficient);
|
||||
[[nodiscard]] mfem::Vector make_enthalpy(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase
|
||||
) {
|
||||
mfem::ParGridFunction value(f.enthalpyFes.get());
|
||||
mfem::FunctionCoefficient coefficient([phase](const mfem::Vector &position) {
|
||||
return 0.93 + 0.04 * std::cos(0.57 * position(1) - phase) + 0.015 * position(2);
|
||||
});
|
||||
value.ProjectCoefficient(coefficient);
|
||||
mfem::Vector result;
|
||||
value.GetTrueDofs(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] double relative_error(
|
||||
const mfem::Vector &left,
|
||||
const mfem::Vector &right,
|
||||
const MPI_Comm communicator
|
||||
) {
|
||||
return gravity_prepared_test_utils::relative_error(left, right, communicator);
|
||||
}
|
||||
} // namespace barotropic_closure_context_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
"Barotropic Closure Context Tracks Independent Revisions",
|
||||
tags::barotrope &tags::closure &tags::hydro &tags::prepared &tags::unit
|
||||
"Prepared Barotropic Closure Owns Its Linearization Context",
|
||||
tags::barotrope &tags::closure &tags::contexts &tags::prepared &tags::field &tags::unit
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
using Operator = mean_field::operators::PreparedBarotropicClosureOperator;
|
||||
using Context = mean_field::operators::context::barotropic::BarotropicClosureLinearizationContext;
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
STATIC_REQUIRE_FALSE(std::is_copy_constructible_v<Context>);
|
||||
STATIC_REQUIRE_FALSE(std::is_copy_assignable_v<Context>);
|
||||
STATIC_REQUIRE_FALSE(std::is_move_constructible_v<Context>);
|
||||
STATIC_REQUIRE_FALSE(std::is_move_assignable_v<Context>);
|
||||
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
|
||||
auto args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
mean_field::operators::context::barotropic::
|
||||
BarotropicClosureLinearizationContext context(
|
||||
f, *f.domainMapperStateless, barotrope
|
||||
);
|
||||
const mean_field::eos::Polytrope equationOfState(3.0, 1.5);
|
||||
Operator preparedOperator(f, *f.domainMapperStateless, equationOfState);
|
||||
|
||||
mfem::Vector density =
|
||||
barotropic_closure_context_test_utils::make_density(f);
|
||||
CHECK_FALSE(preparedOperator.IsPrepared());
|
||||
CHECK_FALSE(preparedOperator.GetContext().IsPrepared());
|
||||
CHECK(&preparedOperator.GetContext() == &preparedOperator.GetContext());
|
||||
|
||||
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);
|
||||
const auto statistics = preparedOperator.GetContextPreparationStatistics();
|
||||
CHECK(statistics.staticPreparations == 0);
|
||||
CHECK(statistics.geometryPreparations == 0);
|
||||
CHECK(statistics.baseStatePreparations == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Barotropic Closure Context Reprepares A Consistent Frozen State",
|
||||
tags::barotrope &tags::closure &tags::hydro &tags::prepared &tags::unit
|
||||
"Barotropic Closure Context Applies Selective Invalidation In Reduced Field Coordinates",
|
||||
tags::barotrope &tags::closure &tags::contexts &tags::prepared &tags::field &tags::unit
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
using namespace barotropic_closure_context_test_utils;
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
auto args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
mean_field::operators::context::barotropic::
|
||||
BarotropicClosureLinearizationContext context(
|
||||
f, *f.domainMapperStateless, barotrope
|
||||
);
|
||||
const Maps maps(f);
|
||||
|
||||
const mfem::Vector density =
|
||||
barotropic_closure_context_test_utils::make_density(f);
|
||||
const mean_field::eos::Polytrope equationOfState(3.0, 1.5);
|
||||
|
||||
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
|
||||
mean_field::operators::PreparedBarotropicClosureOperator preparedOperator(
|
||||
f, *f.domainMapperStateless, equationOfState
|
||||
);
|
||||
|
||||
mfem::Vector changedDensity(density);
|
||||
changedDensity.Add(0.025, densityVariation);
|
||||
mfem::Vector density = reduce(maps.density, make_density(f, 0.17));
|
||||
|
||||
mfem::Vector changedEnthalpy(enthalpy);
|
||||
changedEnthalpy.Add(0.015, enthalpyVariation);
|
||||
mfem::Vector enthalpy = reduce(maps.enthalpy, make_enthalpy(f, 0.29));
|
||||
|
||||
const mfem::Vector deformedDisplacement =
|
||||
gravity_prepared_test_utils::make_displacement(f, 1.0);
|
||||
const mfem::Vector initialDisplacement =
|
||||
reduce(maps.displacement, gravity_prepared_test_utils::make_displacement(f, 0.35));
|
||||
|
||||
context.Prepare(
|
||||
changedDensity, changedEnthalpy, deformedDisplacement, revisions
|
||||
);
|
||||
/*
|
||||
* A second smooth, orientation-preserving geometry.
|
||||
*
|
||||
* Do not manufacture a new geometry by perturbing an arbitrary H1
|
||||
* coefficient. A modest change in one high-order displacement DOF can
|
||||
* correspond to a very large local displacement gradient and can invert
|
||||
* an element.
|
||||
*/
|
||||
const mfem::Vector changedDisplacement =
|
||||
reduce(maps.displacement, gravity_prepared_test_utils::make_displacement(f, 0.85));
|
||||
|
||||
mfem::Vector unchangedResidual;
|
||||
mfem::Vector unchangedAction;
|
||||
mfem::Vector displacement(initialDisplacement);
|
||||
|
||||
context.BuildResidual(unchangedResidual);
|
||||
auto dependencies = make_dependencies();
|
||||
|
||||
context.GetOperator().Mult(
|
||||
densityVariation, enthalpyVariation, displacementVariation,
|
||||
unchangedAction
|
||||
);
|
||||
const auto initialReport = preparedOperator.Prepare(make_state(density, enthalpy, displacement), dependencies);
|
||||
|
||||
const auto &context = preparedOperator.GetContext();
|
||||
|
||||
REQUIRE(preparedOperator.IsPrepared());
|
||||
|
||||
REQUIRE(context.IsPrepared());
|
||||
|
||||
CHECK(context.MatchesDependencies(dependencies));
|
||||
|
||||
CHECK(context.GetDependencies() == dependencies);
|
||||
|
||||
CHECK(initialReport.contextReport.preparedStaticDependencies);
|
||||
|
||||
CHECK(initialReport.contextReport.preparedGeometryState);
|
||||
|
||||
CHECK(initialReport.contextReport.preparedBaseState);
|
||||
|
||||
CHECK(initialReport.contextReport.updatedDensity);
|
||||
|
||||
CHECK(initialReport.contextReport.updatedEnthalpy);
|
||||
|
||||
CHECK(initialReport.contextReport.updatedDisplacement);
|
||||
|
||||
CHECK(initialReport.preparedElementData);
|
||||
|
||||
CHECK(initialReport.DidAnyWork());
|
||||
|
||||
CHECK(preparedOperator.GetPreparationCount() == 1);
|
||||
|
||||
const mfem::Vector frozenDensity = context.GetBaseDensity();
|
||||
|
||||
const mfem::Vector frozenEnthalpy = context.GetBaseEnthalpy();
|
||||
|
||||
const mfem::Vector frozenDisplacement = context.GetDisplacement();
|
||||
|
||||
/*
|
||||
* Modify all three candidate states without updating their dependency
|
||||
* stamps.
|
||||
*
|
||||
* The context must continue exposing the previously frozen state.
|
||||
*/
|
||||
density(0) += 0.25;
|
||||
enthalpy(0) -= 0.18;
|
||||
displacement = changedDisplacement;
|
||||
|
||||
const auto repeatedReport = preparedOperator.Prepare(make_state(density, enthalpy, displacement), dependencies);
|
||||
|
||||
CHECK_FALSE(repeatedReport.DidAnyWork());
|
||||
|
||||
CHECK_FALSE(repeatedReport.contextReport.updatedDensity);
|
||||
|
||||
CHECK_FALSE(repeatedReport.contextReport.updatedEnthalpy);
|
||||
|
||||
CHECK_FALSE(repeatedReport.contextReport.updatedDisplacement);
|
||||
|
||||
CHECK_FALSE(repeatedReport.preparedElementData);
|
||||
|
||||
CHECK(preparedOperator.GetPreparationCount() == 1);
|
||||
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
|
||||
CHECK(context.GetPreparationCount() == 1);
|
||||
CHECK(relative_error(context.GetBaseDensity(), frozenDensity, communicator) == 0.0);
|
||||
|
||||
CHECK(
|
||||
gravity_prepared_test_utils::relative_error(
|
||||
unchangedResidual, initialResidual, communicator
|
||||
) < 5.0e-15
|
||||
CHECK(relative_error(context.GetBaseEnthalpy(), frozenEnthalpy, communicator) == 0.0);
|
||||
|
||||
CHECK(relative_error(context.GetDisplacement(), frozenDisplacement, communicator) == 0.0);
|
||||
|
||||
/*
|
||||
* Density invalidation.
|
||||
*
|
||||
* Geometry remains frozen because the displacement dependency did not
|
||||
* change.
|
||||
*/
|
||||
++dependencies.density.revision;
|
||||
|
||||
const auto densityReport = preparedOperator.Prepare(make_state(density, enthalpy, displacement), dependencies);
|
||||
|
||||
CHECK_FALSE(densityReport.contextReport.preparedStaticDependencies);
|
||||
|
||||
CHECK_FALSE(densityReport.contextReport.preparedGeometryState);
|
||||
|
||||
CHECK(densityReport.contextReport.preparedBaseState);
|
||||
|
||||
CHECK(densityReport.contextReport.updatedDensity);
|
||||
|
||||
CHECK_FALSE(densityReport.contextReport.updatedEnthalpy);
|
||||
|
||||
CHECK_FALSE(densityReport.contextReport.updatedDisplacement);
|
||||
|
||||
CHECK(densityReport.preparedElementData);
|
||||
|
||||
CHECK(context.GetBaseDensity()(0) == density(0));
|
||||
|
||||
/*
|
||||
* The candidate displacement has changed, but because its revision has
|
||||
* not changed the context must still retain the original geometry.
|
||||
*/
|
||||
CHECK(relative_error(context.GetDisplacement(), frozenDisplacement, communicator) == 0.0);
|
||||
|
||||
/*
|
||||
* Enthalpy invalidation.
|
||||
*/
|
||||
++dependencies.enthalpy.revision;
|
||||
|
||||
const auto enthalpyReport = preparedOperator.Prepare(make_state(density, enthalpy, displacement), dependencies);
|
||||
|
||||
CHECK_FALSE(enthalpyReport.contextReport.preparedStaticDependencies);
|
||||
|
||||
CHECK_FALSE(enthalpyReport.contextReport.preparedGeometryState);
|
||||
|
||||
CHECK(enthalpyReport.contextReport.preparedBaseState);
|
||||
|
||||
CHECK_FALSE(enthalpyReport.contextReport.updatedDensity);
|
||||
|
||||
CHECK(enthalpyReport.contextReport.updatedEnthalpy);
|
||||
|
||||
CHECK_FALSE(enthalpyReport.contextReport.updatedDisplacement);
|
||||
|
||||
CHECK(enthalpyReport.preparedElementData);
|
||||
|
||||
CHECK(context.GetBaseEnthalpy()(0) == enthalpy(0));
|
||||
|
||||
CHECK(relative_error(context.GetDisplacement(), frozenDisplacement, communicator) == 0.0);
|
||||
|
||||
/*
|
||||
* Displacement invalidation.
|
||||
*
|
||||
* The changed geometry is now intentionally accepted. Because it came
|
||||
* from the smooth test displacement projection rather than an arbitrary
|
||||
* single H1 coefficient mutation, it remains a valid mapping.
|
||||
*/
|
||||
++dependencies.displacement.revision;
|
||||
|
||||
const auto displacementReport = preparedOperator.Prepare(make_state(density, enthalpy, displacement), dependencies);
|
||||
|
||||
CHECK_FALSE(displacementReport.contextReport.preparedStaticDependencies);
|
||||
|
||||
CHECK(displacementReport.contextReport.preparedGeometryState);
|
||||
|
||||
CHECK(displacementReport.contextReport.preparedBaseState);
|
||||
|
||||
CHECK_FALSE(displacementReport.contextReport.updatedDensity);
|
||||
|
||||
CHECK_FALSE(displacementReport.contextReport.updatedEnthalpy);
|
||||
|
||||
CHECK(displacementReport.contextReport.updatedDisplacement);
|
||||
|
||||
CHECK(displacementReport.preparedElementData);
|
||||
|
||||
CHECK(relative_error(context.GetDisplacement(), changedDisplacement, communicator) == 0.0);
|
||||
|
||||
/*
|
||||
* Discretization invalidates everything.
|
||||
*/
|
||||
++dependencies.discretization.revision;
|
||||
|
||||
const auto discretizationReport =
|
||||
preparedOperator.Prepare(make_state(density, enthalpy, displacement), dependencies);
|
||||
|
||||
CHECK(discretizationReport.contextReport.preparedStaticDependencies);
|
||||
|
||||
CHECK(discretizationReport.contextReport.preparedGeometryState);
|
||||
|
||||
CHECK(discretizationReport.contextReport.preparedBaseState);
|
||||
|
||||
CHECK(discretizationReport.contextReport.updatedDensity);
|
||||
|
||||
CHECK(discretizationReport.contextReport.updatedEnthalpy);
|
||||
|
||||
CHECK(discretizationReport.contextReport.updatedDisplacement);
|
||||
|
||||
CHECK(discretizationReport.preparedElementData);
|
||||
|
||||
const auto finalStatistics = preparedOperator.GetContextPreparationStatistics();
|
||||
|
||||
CHECK(finalStatistics.staticPreparations == 2);
|
||||
|
||||
CHECK(finalStatistics.geometryPreparations == 3);
|
||||
|
||||
CHECK(finalStatistics.baseStatePreparations == 5);
|
||||
|
||||
CHECK(preparedOperator.GetPreparationCount() == 5);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Barotropic Closure Context Uses Identity And Revision For Every Dependency",
|
||||
tags::barotrope &tags::closure &tags::contexts &tags::prepared &tags::field &tags::unit
|
||||
) {
|
||||
using namespace barotropic_closure_context_test_utils;
|
||||
|
||||
auto args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const Maps maps(f);
|
||||
const mean_field::eos::Polytrope equationOfState(3.0, 1.5);
|
||||
mean_field::operators::PreparedBarotropicClosureOperator preparedOperator(
|
||||
f, *f.domainMapperStateless, equationOfState
|
||||
);
|
||||
|
||||
CHECK(
|
||||
gravity_prepared_test_utils::relative_error(
|
||||
unchangedAction, initialAction, communicator
|
||||
) < 5.0e-15
|
||||
);
|
||||
mfem::Vector density = reduce(maps.density, make_density(f, 0.41));
|
||||
mfem::Vector enthalpy = reduce(maps.enthalpy, make_enthalpy(f, 0.53));
|
||||
mfem::Vector displacement = reduce(maps.displacement, gravity_prepared_test_utils::make_displacement(f, 0.60));
|
||||
|
||||
++revisions.density;
|
||||
++revisions.enthalpy;
|
||||
++revisions.displacement;
|
||||
auto dependencies = make_dependencies();
|
||||
preparedOperator.Prepare(make_state(density, enthalpy, displacement), dependencies);
|
||||
|
||||
context.Prepare(
|
||||
changedDensity, changedEnthalpy, deformedDisplacement, revisions
|
||||
);
|
||||
const mfem::Vector frozenDensity = preparedOperator.GetContext().GetBaseDensity();
|
||||
density(0) += 0.19;
|
||||
|
||||
mfem::Vector preparedResidual;
|
||||
mfem::Vector preparedAction;
|
||||
const auto sameStampReport = preparedOperator.Prepare(make_state(density, enthalpy, displacement), dependencies);
|
||||
CHECK_FALSE(sameStampReport.DidAnyWork());
|
||||
CHECK(preparedOperator.GetContext().GetBaseDensity()(0) == frozenDensity(0));
|
||||
|
||||
context.BuildResidual(preparedResidual);
|
||||
++dependencies.density.identity;
|
||||
++dependencies.density.revision;
|
||||
|
||||
context.GetOperator().Mult(
|
||||
densityVariation, enthalpyVariation, displacementVariation,
|
||||
preparedAction
|
||||
);
|
||||
const auto newIdentityReport = preparedOperator.Prepare(make_state(density, enthalpy, displacement), dependencies);
|
||||
CHECK_FALSE(newIdentityReport.contextReport.preparedStaticDependencies);
|
||||
CHECK_FALSE(newIdentityReport.contextReport.preparedGeometryState);
|
||||
CHECK(newIdentityReport.contextReport.preparedBaseState);
|
||||
CHECK(newIdentityReport.contextReport.updatedDensity);
|
||||
CHECK(preparedOperator.GetContext().GetBaseDensity()(0) == density(0));
|
||||
CHECK(preparedOperator.GetContext().MatchesDependencies(dependencies));
|
||||
|
||||
mfem::Vector referenceResidual;
|
||||
mfem::Vector referenceDensityAction;
|
||||
mfem::Vector referenceEnthalpyAction;
|
||||
mfem::Vector referenceDisplacementAction;
|
||||
++dependencies.displacement.identity;
|
||||
++dependencies.displacement.revision;
|
||||
|
||||
mean_field::operators::kernels::apply_barotropic_closure(
|
||||
f, *f.domainMapperStateless, barotrope, changedDensity, changedEnthalpy,
|
||||
deformedDisplacement, referenceResidual
|
||||
);
|
||||
const auto displacementIdentityReport =
|
||||
preparedOperator.Prepare(make_state(density, enthalpy, displacement), dependencies);
|
||||
CHECK_FALSE(displacementIdentityReport.contextReport.preparedStaticDependencies);
|
||||
CHECK(displacementIdentityReport.contextReport.preparedGeometryState);
|
||||
CHECK(displacementIdentityReport.contextReport.preparedBaseState);
|
||||
CHECK(displacementIdentityReport.contextReport.updatedDisplacement);
|
||||
|
||||
mean_field::operators::kernels::apply_barotropic_closure_density_action(
|
||||
f, *f.domainMapperStateless, barotrope, densityVariation,
|
||||
deformedDisplacement, referenceDensityAction
|
||||
);
|
||||
++dependencies.discretization.identity;
|
||||
++dependencies.discretization.revision;
|
||||
|
||||
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);
|
||||
}
|
||||
const auto discretizationIdentityReport =
|
||||
preparedOperator.Prepare(make_state(density, enthalpy, displacement), dependencies);
|
||||
CHECK(discretizationIdentityReport.contextReport.preparedStaticDependencies);
|
||||
CHECK(discretizationIdentityReport.contextReport.preparedGeometryState);
|
||||
CHECK(discretizationIdentityReport.contextReport.preparedBaseState);
|
||||
}
|
||||
|
||||
@@ -15,20 +15,13 @@ TEST_CASE(
|
||||
auto args = test_utils::setup_args();
|
||||
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
gravity_context::GravityFieldLinearizationContext context(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
gravity_context::GravityFieldLinearizationContext context(f, *f.domainMapperStateless);
|
||||
|
||||
mfem::Vector density = prepared_test::make_deterministic_vector(
|
||||
f.densityFes->GetTrueVSize(), 0.11
|
||||
);
|
||||
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
|
||||
);
|
||||
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;
|
||||
|
||||
@@ -43,8 +36,7 @@ TEST_CASE(
|
||||
|
||||
REQUIRE_FALSE(context.IsPrepared());
|
||||
|
||||
const gravity_context::GravityFieldPreparationReport initial_report =
|
||||
context.Prepare(make_state(), revisions);
|
||||
const gravity_context::GravityFieldPreparationReport initial_report = context.Prepare(make_state(), revisions);
|
||||
|
||||
REQUIRE(context.IsPrepared());
|
||||
CHECK(initial_report.geometry.reconstructed_operators);
|
||||
@@ -55,41 +47,27 @@ TEST_CASE(
|
||||
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 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);
|
||||
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
|
||||
);
|
||||
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);
|
||||
const gravity_context::GravityFieldPreparationReport potential_report = context.Prepare(make_state(), revisions);
|
||||
|
||||
CHECK_FALSE(potential_report.DidAnyWork());
|
||||
CHECK(
|
||||
context.GetRevisions().gravity_potential == revisions.gravity_potential
|
||||
);
|
||||
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);
|
||||
const gravity_context::GravityFieldPreparationReport density_report = context.Prepare(make_state(), revisions);
|
||||
|
||||
CHECK(density_report.updated_density);
|
||||
CHECK_FALSE(density_report.updated_gravity_gradient);
|
||||
@@ -99,8 +77,7 @@ TEST_CASE(
|
||||
gravity_gradient(0) -= 0.4;
|
||||
++revisions.gravity_gradient.value;
|
||||
|
||||
const gravity_context::GravityFieldPreparationReport gradient_report =
|
||||
context.Prepare(make_state(), revisions);
|
||||
const gravity_context::GravityFieldPreparationReport gradient_report = context.Prepare(make_state(), revisions);
|
||||
|
||||
CHECK_FALSE(gradient_report.updated_density);
|
||||
CHECK(gradient_report.updated_gravity_gradient);
|
||||
@@ -110,8 +87,7 @@ TEST_CASE(
|
||||
displacement = prepared_test::make_displacement(f, 1.0);
|
||||
++revisions.displacement.value;
|
||||
|
||||
const gravity_context::GravityFieldPreparationReport displacement_report =
|
||||
context.Prepare(make_state(), revisions);
|
||||
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);
|
||||
@@ -119,15 +95,8 @@ TEST_CASE(
|
||||
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
|
||||
);
|
||||
CHECK(context.GetGeometryContext().GetMassOperator().GetPreparationCount() == initial_mass_preparations + 1);
|
||||
CHECK(context.GetGeometryContext().GetSourceOperator().GetPreparationCount() == initial_source_preparations + 1);
|
||||
|
||||
++revisions.discretization.value;
|
||||
|
||||
@@ -139,15 +108,8 @@ TEST_CASE(
|
||||
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
|
||||
);
|
||||
CHECK(context.GetGeometryContext().GetMassOperator().GetPreparationCount() == 1);
|
||||
CHECK(context.GetGeometryContext().GetSourceOperator().GetPreparationCount() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
@@ -157,20 +119,13 @@ TEST_CASE(
|
||||
auto args = test_utils::setup_args();
|
||||
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
gravity_context::GravityFieldLinearizationContext context(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
gravity_context::GravityFieldLinearizationContext context(f, *f.domainMapperStateless);
|
||||
|
||||
mfem::Vector density = prepared_test::make_deterministic_vector(
|
||||
f.densityFes->GetTrueVSize(), 0.13
|
||||
);
|
||||
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
|
||||
);
|
||||
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;
|
||||
|
||||
@@ -182,9 +137,8 @@ TEST_CASE(
|
||||
revisions
|
||||
);
|
||||
|
||||
const mfem::Vector frozen_density = context.GetDensity();
|
||||
const mfem::Vector frozen_displacement =
|
||||
context.GetGeometryContext().GetDisplacement();
|
||||
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;
|
||||
@@ -192,49 +146,36 @@ TEST_CASE(
|
||||
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.GetDensity(), frozen_density, f.mesh->GetComm()
|
||||
context.GetGeometryContext().GetDisplacement(), frozen_displacement, f.displacementFes->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()
|
||||
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
|
||||
);
|
||||
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.GetDensity(), frozen_density, f.mesh->GetComm()
|
||||
context.GetGeometryContext().GetDisplacement(), frozen_displacement, f.displacementFes->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()
|
||||
context.GetGravityGradient(), frozen_gravity_gradient, f.gravityFluxFes->GetComm()
|
||||
) == 0.0
|
||||
);
|
||||
}
|
||||
@@ -246,21 +187,13 @@ TEST_CASE(
|
||||
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
|
||||
);
|
||||
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 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
|
||||
);
|
||||
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});
|
||||
@@ -270,36 +203,21 @@ TEST_CASE(
|
||||
mfem::Vector second_action_after;
|
||||
|
||||
first_context.GetMassOperator().Mult(gravity_gradient, first_action);
|
||||
second_context.GetMassOperator().Mult(
|
||||
gravity_gradient, second_action_before
|
||||
);
|
||||
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}
|
||||
);
|
||||
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
|
||||
);
|
||||
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
|
||||
);
|
||||
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
|
||||
);
|
||||
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);
|
||||
|
||||
@@ -5,9 +5,7 @@ import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace hydrostatic_context_test_utils {
|
||||
mean_field::operators::context::hydrostatic::
|
||||
HydrostaticEquilibriumDependencies
|
||||
make_dependencies() {
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumDependencies make_dependencies() {
|
||||
return {
|
||||
.discretization = {.identity = 101, .revision = 2},
|
||||
.enthalpy = {.identity = 103, .revision = 3},
|
||||
@@ -18,8 +16,7 @@ namespace hydrostatic_context_test_utils {
|
||||
};
|
||||
}
|
||||
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView
|
||||
make_state(
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView make_state(
|
||||
const mfem::Vector &enthalpy,
|
||||
const mfem::Vector &gravityPotential,
|
||||
const mfem::Vector &displacement,
|
||||
@@ -33,10 +30,7 @@ namespace hydrostatic_context_test_utils {
|
||||
};
|
||||
}
|
||||
|
||||
void check_base_only(
|
||||
const mean_field::operators::context::hydrostatic::
|
||||
HydrostaticPreparationReport &report
|
||||
) {
|
||||
void check_base_only(const mean_field::operators::context::hydrostatic::HydrostaticPreparationReport &report) {
|
||||
CHECK_FALSE(report.preparedStaticDependencies);
|
||||
CHECK_FALSE(report.preparedGeometryState);
|
||||
CHECK_FALSE(report.preparedRotationDependencies);
|
||||
@@ -48,32 +42,23 @@ TEST_CASE(
|
||||
"Hydrostatic Context Applies Selective Invalidation",
|
||||
tags::barotrope &tags::contexts &tags::hydro &tags::prepared &tags::unit
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
auto args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumContext
|
||||
context(f, *f.domainMapperStateless);
|
||||
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 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
|
||||
);
|
||||
gravity_prepared_test_utils::make_deterministic_vector(f.gravityPotentialFes->GetTrueVSize(), 0.31);
|
||||
|
||||
mfem::Vector displacement =
|
||||
gravity_prepared_test_utils::make_displacement(f, 0.35);
|
||||
mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.35);
|
||||
|
||||
double bernoulliConstant = 0.73;
|
||||
double bernoulliConstant = 0.73;
|
||||
|
||||
mean_field::operators::context::hydrostatic::
|
||||
HydrostaticEquilibriumDependencies dependencies =
|
||||
hydrostatic_context_test_utils::make_dependencies();
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumDependencies dependencies =
|
||||
hydrostatic_context_test_utils::make_dependencies();
|
||||
|
||||
CHECK_FALSE(context.IsPrepared());
|
||||
CHECK_FALSE(context.MatchesDependencies(dependencies));
|
||||
@@ -86,9 +71,7 @@ TEST_CASE(
|
||||
CHECK(initialStatistics.baseStatePreparations == 0);
|
||||
|
||||
const auto initialReport = context.Prepare(
|
||||
hydrostatic_context_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement, bernoulliConstant
|
||||
),
|
||||
hydrostatic_context_test_utils::make_state(enthalpy, gravityPotential, displacement, bernoulliConstant),
|
||||
dependencies
|
||||
);
|
||||
|
||||
@@ -106,14 +89,13 @@ TEST_CASE(
|
||||
CHECK(initialReport.updatedBernoulliConstant);
|
||||
CHECK(initialReport.DidAnyWork());
|
||||
|
||||
const mfem::Vector frozenEnthalpy = context.GetBaseEnthalpyTrue();
|
||||
const mfem::Vector frozenEnthalpy = context.GetBaseEnthalpyTrue();
|
||||
|
||||
const mfem::Vector frozenGravityPotential =
|
||||
context.GetBaseGravityPotentialTrue();
|
||||
const mfem::Vector frozenGravityPotential = context.GetBaseGravityPotentialTrue();
|
||||
|
||||
const mfem::Vector frozenDisplacement = context.GetDisplacementTrue();
|
||||
const mfem::Vector frozenDisplacement = context.GetDisplacementTrue();
|
||||
|
||||
const double frozenBernoulliConstant = context.GetBernoulliConstant();
|
||||
const double frozenBernoulliConstant = context.GetBernoulliConstant();
|
||||
|
||||
enthalpy(0) += 0.125;
|
||||
gravityPotential(0) -= 0.075;
|
||||
@@ -121,9 +103,7 @@ TEST_CASE(
|
||||
bernoulliConstant += 0.20;
|
||||
|
||||
const auto repeatedReport = context.Prepare(
|
||||
hydrostatic_context_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement, bernoulliConstant
|
||||
),
|
||||
hydrostatic_context_test_utils::make_state(enthalpy, gravityPotential, displacement, bernoulliConstant),
|
||||
dependencies
|
||||
);
|
||||
|
||||
@@ -136,22 +116,18 @@ TEST_CASE(
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
|
||||
CHECK(
|
||||
gravity_prepared_test_utils::relative_error(
|
||||
context.GetBaseEnthalpyTrue(), frozenEnthalpy, communicator
|
||||
) == 0.0
|
||||
gravity_prepared_test_utils::relative_error(context.GetBaseEnthalpyTrue(), frozenEnthalpy, communicator) == 0.0
|
||||
);
|
||||
|
||||
CHECK(
|
||||
gravity_prepared_test_utils::relative_error(
|
||||
context.GetBaseGravityPotentialTrue(), frozenGravityPotential,
|
||||
communicator
|
||||
context.GetBaseGravityPotentialTrue(), frozenGravityPotential, communicator
|
||||
) == 0.0
|
||||
);
|
||||
|
||||
CHECK(
|
||||
gravity_prepared_test_utils::relative_error(
|
||||
context.GetDisplacementTrue(), frozenDisplacement, communicator
|
||||
) == 0.0
|
||||
gravity_prepared_test_utils::relative_error(context.GetDisplacementTrue(), frozenDisplacement, communicator) ==
|
||||
0.0
|
||||
);
|
||||
|
||||
CHECK(context.GetBernoulliConstant() == frozenBernoulliConstant);
|
||||
@@ -159,9 +135,7 @@ TEST_CASE(
|
||||
++dependencies.enthalpy.revision;
|
||||
|
||||
const auto enthalpyReport = context.Prepare(
|
||||
hydrostatic_context_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement, bernoulliConstant
|
||||
),
|
||||
hydrostatic_context_test_utils::make_state(enthalpy, gravityPotential, displacement, bernoulliConstant),
|
||||
dependencies
|
||||
);
|
||||
|
||||
@@ -176,9 +150,7 @@ TEST_CASE(
|
||||
++dependencies.gravityPotential.revision;
|
||||
|
||||
const auto gravityPotentialReport = context.Prepare(
|
||||
hydrostatic_context_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement, bernoulliConstant
|
||||
),
|
||||
hydrostatic_context_test_utils::make_state(enthalpy, gravityPotential, displacement, bernoulliConstant),
|
||||
dependencies
|
||||
);
|
||||
|
||||
@@ -194,9 +166,7 @@ TEST_CASE(
|
||||
++dependencies.bernoulliConstant.revision;
|
||||
|
||||
const auto bernoulliReport = context.Prepare(
|
||||
hydrostatic_context_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement, bernoulliConstant
|
||||
),
|
||||
hydrostatic_context_test_utils::make_state(enthalpy, gravityPotential, displacement, bernoulliConstant),
|
||||
dependencies
|
||||
);
|
||||
|
||||
@@ -212,9 +182,7 @@ TEST_CASE(
|
||||
++dependencies.rotation.revision;
|
||||
|
||||
const auto rotationReport = context.Prepare(
|
||||
hydrostatic_context_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement, bernoulliConstant
|
||||
),
|
||||
hydrostatic_context_test_utils::make_state(enthalpy, gravityPotential, displacement, bernoulliConstant),
|
||||
dependencies
|
||||
);
|
||||
|
||||
@@ -230,9 +198,7 @@ TEST_CASE(
|
||||
++dependencies.displacement.revision;
|
||||
|
||||
const auto displacementReport = context.Prepare(
|
||||
hydrostatic_context_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement, bernoulliConstant
|
||||
),
|
||||
hydrostatic_context_test_utils::make_state(enthalpy, gravityPotential, displacement, bernoulliConstant),
|
||||
dependencies
|
||||
);
|
||||
|
||||
@@ -251,9 +217,7 @@ TEST_CASE(
|
||||
++dependencies.discretization.revision;
|
||||
|
||||
const auto discretizationReport = context.Prepare(
|
||||
hydrostatic_context_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement, bernoulliConstant
|
||||
),
|
||||
hydrostatic_context_test_utils::make_state(enthalpy, gravityPotential, displacement, bernoulliConstant),
|
||||
dependencies
|
||||
);
|
||||
|
||||
@@ -278,32 +242,23 @@ 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();
|
||||
auto args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumContext
|
||||
context(f, *f.domainMapperStateless);
|
||||
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
|
||||
);
|
||||
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
|
||||
);
|
||||
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);
|
||||
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();
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumDependencies dependencies =
|
||||
hydrostatic_context_test_utils::make_dependencies();
|
||||
|
||||
const auto preparedEnthalpyDependency = dependencies.enthalpy;
|
||||
|
||||
@@ -319,9 +274,7 @@ TEST_CASE(
|
||||
CHECK(resetNewIdentity.CanFollow(preparedEnthalpyDependency));
|
||||
|
||||
context.Prepare(
|
||||
hydrostatic_context_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement, bernoulliConstant
|
||||
),
|
||||
hydrostatic_context_test_utils::make_state(enthalpy, gravityPotential, displacement, bernoulliConstant),
|
||||
dependencies
|
||||
);
|
||||
|
||||
@@ -330,9 +283,7 @@ TEST_CASE(
|
||||
enthalpy(0) += 0.33;
|
||||
|
||||
const auto sameStampReport = context.Prepare(
|
||||
hydrostatic_context_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement, bernoulliConstant
|
||||
),
|
||||
hydrostatic_context_test_utils::make_state(enthalpy, gravityPotential, displacement, bernoulliConstant),
|
||||
dependencies
|
||||
);
|
||||
|
||||
@@ -343,9 +294,7 @@ TEST_CASE(
|
||||
dependencies.enthalpy.revision = 0;
|
||||
|
||||
const auto newIdentityReport = context.Prepare(
|
||||
hydrostatic_context_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement, bernoulliConstant
|
||||
),
|
||||
hydrostatic_context_test_utils::make_state(enthalpy, gravityPotential, displacement, bernoulliConstant),
|
||||
dependencies
|
||||
);
|
||||
|
||||
@@ -359,9 +308,7 @@ TEST_CASE(
|
||||
dependencies.rotation.revision = 0;
|
||||
|
||||
const auto newRotationIdentityReport = context.Prepare(
|
||||
hydrostatic_context_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement, bernoulliConstant
|
||||
),
|
||||
hydrostatic_context_test_utils::make_state(enthalpy, gravityPotential, displacement, bernoulliConstant),
|
||||
dependencies
|
||||
);
|
||||
|
||||
|
||||
325
tests/operators/contexts/pressure_force_context.cpp
Normal file
325
tests/operators/contexts/pressure_force_context.cpp
Normal file
@@ -0,0 +1,325 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <mfem.hpp>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace pressure_force_context_test_utils {
|
||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
|
||||
struct Maps final {
|
||||
mean_field::field::FieldDofMap enthalpy;
|
||||
mean_field::field::FieldDofMap displacement;
|
||||
|
||||
explicit Maps(const mean_field::fem::FEM &f)
|
||||
: enthalpy(
|
||||
mean_field::field::make_field_dof_map<
|
||||
mean_field::field::Enthalpy,
|
||||
DomainSchema>(*f.enthalpyFes)
|
||||
),
|
||||
displacement(
|
||||
mean_field::field::make_field_dof_map<
|
||||
mean_field::field::Displacement,
|
||||
DomainSchema>(*f.displacementFes)
|
||||
) {
|
||||
}
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
mfem::Vector make_enthalpy_true(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase
|
||||
) {
|
||||
mfem::Vector enthalpy(f.enthalpyFes->GetTrueVSize());
|
||||
|
||||
for (int index = 0; index < enthalpy.Size(); ++index) {
|
||||
const double position = static_cast<double>(index + 1);
|
||||
|
||||
enthalpy(index) =
|
||||
0.95 + 0.08 * std::sin(0.17 * position + phase) + 0.03 * std::cos(0.11 * position - 0.5 * phase);
|
||||
}
|
||||
|
||||
return enthalpy;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
double relative_difference(
|
||||
const mfem::Vector &left,
|
||||
const mfem::Vector &right,
|
||||
const MPI_Comm communicator
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
left.Size() == right.Size(), "Cannot compare pressure-force context vectors with "
|
||||
"different sizes."
|
||||
);
|
||||
|
||||
mfem::Vector difference(left);
|
||||
|
||||
difference -= right;
|
||||
|
||||
const double scale = std::max(
|
||||
{gravity_prepared_test_utils::global_norm(left, communicator),
|
||||
gravity_prepared_test_utils::global_norm(right, communicator),
|
||||
100.0 * std::numeric_limits<double>::epsilon()}
|
||||
);
|
||||
|
||||
return gravity_prepared_test_utils::global_norm(difference, communicator) / scale;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
mean_field::operators::context::pressure_force::PressureForceDependencies make_dependencies() {
|
||||
return {
|
||||
.discretization = {.identity = 101, .revision = 7},
|
||||
.enthalpy = {.identity = 103, .revision = 11},
|
||||
.displacement = {.identity = 107, .revision = 13}
|
||||
};
|
||||
}
|
||||
} // namespace pressure_force_context_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
"Pressure Force Context Applies Selective Invalidation In FieldDof Coordinates",
|
||||
tags::barotrope &tags::pressure &tags::prepared &tags::field &tags::unit
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const pressure_force_context_test_utils::Maps maps(f);
|
||||
|
||||
mean_field::operators::context::pressure_force::PressureForceLinearizationContext context(
|
||||
f, *f.domainMapperStateless, maps.enthalpy, maps.displacement
|
||||
);
|
||||
|
||||
mfem::Vector enthalpy = maps.enthalpy.gather(pressure_force_context_test_utils::make_enthalpy_true(f, 0.23));
|
||||
|
||||
const mfem::Vector initialDisplacement =
|
||||
maps.displacement.gather(gravity_prepared_test_utils::make_displacement(f, 0.41));
|
||||
|
||||
const mfem::Vector changedDisplacement =
|
||||
maps.displacement.gather(gravity_prepared_test_utils::make_displacement(f, 0.79));
|
||||
|
||||
mfem::Vector displacement(initialDisplacement);
|
||||
|
||||
auto dependencies = pressure_force_context_test_utils::make_dependencies();
|
||||
|
||||
const mean_field::operators::context::pressure_force::PressureForceStateView state{
|
||||
.enthalpy = enthalpy, .displacement = displacement
|
||||
};
|
||||
|
||||
CHECK_FALSE(context.IsPrepared());
|
||||
|
||||
const auto initialReport = context.Prepare(state, dependencies);
|
||||
|
||||
REQUIRE(context.IsPrepared());
|
||||
|
||||
CHECK(context.MatchesDependencies(dependencies));
|
||||
|
||||
CHECK(initialReport.DidAnyWork());
|
||||
|
||||
CHECK(initialReport.preparedStaticDependencies);
|
||||
|
||||
CHECK(initialReport.preparedGeometryState);
|
||||
|
||||
CHECK(initialReport.preparedMaterialState);
|
||||
|
||||
CHECK(initialReport.updatedEnthalpy);
|
||||
|
||||
CHECK(initialReport.updatedDisplacement);
|
||||
|
||||
REQUIRE(maps.enthalpy.reduced_size() < maps.enthalpy.full_size());
|
||||
|
||||
CHECK(maps.displacement.is_identity());
|
||||
|
||||
CHECK(context.GetBaseEnthalpy().Size() == maps.enthalpy.reduced_size());
|
||||
|
||||
CHECK(context.GetDisplacement().Size() == maps.displacement.reduced_size());
|
||||
|
||||
const auto unchangedReport = context.Prepare(state, dependencies);
|
||||
|
||||
CHECK_FALSE(unchangedReport.DidAnyWork());
|
||||
|
||||
const mfem::Vector frozenEnthalpy(context.GetBaseEnthalpy());
|
||||
|
||||
const mfem::Vector frozenDisplacement(context.GetDisplacement());
|
||||
|
||||
enthalpy(0) += 0.125;
|
||||
|
||||
displacement = changedDisplacement;
|
||||
|
||||
const auto unstampedReport = context.Prepare(state, dependencies);
|
||||
|
||||
CHECK_FALSE(unstampedReport.DidAnyWork());
|
||||
|
||||
CHECK(
|
||||
pressure_force_context_test_utils::relative_difference(
|
||||
context.GetBaseEnthalpy(), frozenEnthalpy, f.mesh->GetComm()
|
||||
) == 0.0
|
||||
);
|
||||
|
||||
CHECK(
|
||||
pressure_force_context_test_utils::relative_difference(
|
||||
context.GetDisplacement(), frozenDisplacement, f.mesh->GetComm()
|
||||
) == 0.0
|
||||
);
|
||||
|
||||
++dependencies.enthalpy.revision;
|
||||
|
||||
const auto enthalpyReport = context.Prepare(state, dependencies);
|
||||
|
||||
CHECK(enthalpyReport.DidAnyWork());
|
||||
|
||||
CHECK_FALSE(enthalpyReport.preparedStaticDependencies);
|
||||
|
||||
CHECK_FALSE(enthalpyReport.preparedGeometryState);
|
||||
|
||||
CHECK(enthalpyReport.preparedMaterialState);
|
||||
|
||||
CHECK(enthalpyReport.updatedEnthalpy);
|
||||
|
||||
CHECK_FALSE(enthalpyReport.updatedDisplacement);
|
||||
|
||||
CHECK(context.GetBaseEnthalpy()(0) == enthalpy(0));
|
||||
|
||||
CHECK(
|
||||
pressure_force_context_test_utils::relative_difference(
|
||||
context.GetDisplacement(), frozenDisplacement, f.mesh->GetComm()
|
||||
) == 0.0
|
||||
);
|
||||
|
||||
++dependencies.displacement.revision;
|
||||
|
||||
const auto displacementReport = context.Prepare(state, dependencies);
|
||||
|
||||
CHECK(displacementReport.DidAnyWork());
|
||||
|
||||
CHECK_FALSE(displacementReport.preparedStaticDependencies);
|
||||
|
||||
CHECK(displacementReport.preparedGeometryState);
|
||||
|
||||
CHECK(displacementReport.preparedMaterialState);
|
||||
|
||||
CHECK_FALSE(displacementReport.updatedEnthalpy);
|
||||
|
||||
CHECK(displacementReport.updatedDisplacement);
|
||||
|
||||
CHECK(
|
||||
pressure_force_context_test_utils::relative_difference(
|
||||
context.GetDisplacement(), changedDisplacement, f.mesh->GetComm()
|
||||
) == 0.0
|
||||
);
|
||||
|
||||
++dependencies.discretization.revision;
|
||||
|
||||
const auto discretizationReport = context.Prepare(state, dependencies);
|
||||
|
||||
CHECK(discretizationReport.preparedStaticDependencies);
|
||||
|
||||
CHECK(discretizationReport.preparedGeometryState);
|
||||
|
||||
CHECK(discretizationReport.preparedMaterialState);
|
||||
|
||||
CHECK(discretizationReport.updatedEnthalpy);
|
||||
|
||||
CHECK(discretizationReport.updatedDisplacement);
|
||||
|
||||
const auto &statistics = context.GetPreparationStatistics();
|
||||
|
||||
CHECK(statistics.staticPreparations == 2);
|
||||
|
||||
CHECK(statistics.geometryPreparations == 3);
|
||||
|
||||
CHECK(statistics.materialPreparations == 4);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Pressure Force Context Uses Identity And Revision In Every Dependency",
|
||||
tags::barotrope &tags::pressure &tags::prepared &tags::field &tags::unit
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const pressure_force_context_test_utils::Maps maps(f);
|
||||
|
||||
mean_field::operators::context::pressure_force::PressureForceLinearizationContext context(
|
||||
f, *f.domainMapperStateless, maps.enthalpy, maps.displacement
|
||||
);
|
||||
|
||||
const mfem::Vector enthalpy = maps.enthalpy.gather(pressure_force_context_test_utils::make_enthalpy_true(f, 0.61));
|
||||
|
||||
const mfem::Vector displacement = maps.displacement.gather(gravity_prepared_test_utils::make_displacement(f, 0.73));
|
||||
|
||||
const mean_field::operators::context::pressure_force::PressureForceStateView state{
|
||||
.enthalpy = enthalpy, .displacement = displacement
|
||||
};
|
||||
|
||||
auto dependencies = pressure_force_context_test_utils::make_dependencies();
|
||||
|
||||
context.Prepare(state, dependencies);
|
||||
|
||||
++dependencies.enthalpy.identity;
|
||||
|
||||
dependencies.enthalpy.revision = 0;
|
||||
|
||||
const auto enthalpyIdentityReport = context.Prepare(state, dependencies);
|
||||
|
||||
CHECK_FALSE(enthalpyIdentityReport.preparedStaticDependencies);
|
||||
|
||||
CHECK_FALSE(enthalpyIdentityReport.preparedGeometryState);
|
||||
|
||||
CHECK(enthalpyIdentityReport.preparedMaterialState);
|
||||
|
||||
CHECK(enthalpyIdentityReport.updatedEnthalpy);
|
||||
|
||||
CHECK_FALSE(enthalpyIdentityReport.updatedDisplacement);
|
||||
|
||||
++dependencies.displacement.identity;
|
||||
|
||||
dependencies.displacement.revision = 0;
|
||||
|
||||
const auto displacementIdentityReport = context.Prepare(state, dependencies);
|
||||
|
||||
CHECK_FALSE(displacementIdentityReport.preparedStaticDependencies);
|
||||
|
||||
CHECK(displacementIdentityReport.preparedGeometryState);
|
||||
|
||||
CHECK(displacementIdentityReport.preparedMaterialState);
|
||||
|
||||
CHECK_FALSE(displacementIdentityReport.updatedEnthalpy);
|
||||
|
||||
CHECK(displacementIdentityReport.updatedDisplacement);
|
||||
|
||||
++dependencies.discretization.identity;
|
||||
|
||||
dependencies.discretization.revision = 0;
|
||||
|
||||
const auto discretizationIdentityReport = context.Prepare(state, dependencies);
|
||||
|
||||
CHECK(discretizationIdentityReport.preparedStaticDependencies);
|
||||
|
||||
CHECK(discretizationIdentityReport.preparedGeometryState);
|
||||
|
||||
CHECK(discretizationIdentityReport.preparedMaterialState);
|
||||
|
||||
CHECK(discretizationIdentityReport.updatedEnthalpy);
|
||||
|
||||
CHECK(discretizationIdentityReport.updatedDisplacement);
|
||||
|
||||
CHECK(context.MatchesDependencies(dependencies));
|
||||
|
||||
const auto &statistics = context.GetPreparationStatistics();
|
||||
|
||||
CHECK(statistics.staticPreparations == 2);
|
||||
|
||||
CHECK(statistics.geometryPreparations == 3);
|
||||
|
||||
CHECK(statistics.materialPreparations == 4);
|
||||
}
|
||||
181
tests/operators/contexts/rotation_displacement_force_context.cpp
Normal file
181
tests/operators/contexts/rotation_displacement_force_context.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace rotational_displacement_force_context_test_utils {
|
||||
using Context =
|
||||
mean_field::operators::context::rotational_displacement_force::RotationalDisplacementForceLinearizationContext;
|
||||
|
||||
using Dependencies =
|
||||
mean_field::operators::context::rotational_displacement_force::RotationalDisplacementForceDependencies;
|
||||
|
||||
[[nodiscard]] Dependencies make_dependencies() {
|
||||
return {
|
||||
.discretization = {.identity = 101, .revision = 3},
|
||||
.density = {.identity = 103, .revision = 5},
|
||||
.displacement = {.identity = 107, .revision = 7},
|
||||
.rotation = {.identity = 109, .revision = 11}
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_density(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double offset
|
||||
) {
|
||||
mfem::ParGridFunction density(f.densityFes.get());
|
||||
|
||||
mfem::FunctionCoefficient coefficient([offset](const mfem::Vector &position) {
|
||||
return offset + 0.04 * position(0) - 0.03 * position(1) + 0.02 * position(2);
|
||||
});
|
||||
|
||||
density.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector densityTrue;
|
||||
density.GetTrueDofs(densityTrue);
|
||||
return densityTrue;
|
||||
}
|
||||
|
||||
[[nodiscard]] double relative_difference(
|
||||
const mfem::Vector &left,
|
||||
const mfem::Vector &right
|
||||
) {
|
||||
mfem::Vector difference(left);
|
||||
difference -= right;
|
||||
|
||||
return difference.Norml2() / std::max(right.Norml2(), 1.0e-30);
|
||||
}
|
||||
} // namespace rotational_displacement_force_context_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
"Rotational Displacement Force Context Applies Selective Invalidation",
|
||||
tags::centrifugal &tags::contexts &tags::unit
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
mfem::Vector density = rotational_displacement_force_context_test_utils::make_density(f, 0.83);
|
||||
|
||||
mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.47);
|
||||
|
||||
auto dependencies = rotational_displacement_force_context_test_utils::make_dependencies();
|
||||
|
||||
rotational_displacement_force_context_test_utils::Context context(f, *f.domainMapperStateless);
|
||||
|
||||
const auto initialReport = context.Prepare({.density = density, .displacement = displacement}, dependencies);
|
||||
|
||||
REQUIRE(context.IsPrepared());
|
||||
CHECK(context.MatchesDependencies(dependencies));
|
||||
CHECK(initialReport.preparedStaticDependencies);
|
||||
CHECK(initialReport.preparedGeometryState);
|
||||
CHECK(initialReport.preparedRotationDependencies);
|
||||
CHECK(initialReport.preparedBaseState);
|
||||
CHECK(initialReport.updatedDensity);
|
||||
CHECK(initialReport.updatedDisplacement);
|
||||
|
||||
CHECK(
|
||||
rotational_displacement_force_context_test_utils::relative_difference(context.GetBaseDensityTrue(), density) <
|
||||
1.0e-14
|
||||
);
|
||||
|
||||
CHECK(
|
||||
rotational_displacement_force_context_test_utils::relative_difference(
|
||||
context.GetDisplacementTrue(), displacement
|
||||
) < 1.0e-14
|
||||
);
|
||||
|
||||
const auto unchangedReport = context.Prepare({.density = density, .displacement = displacement}, dependencies);
|
||||
|
||||
CHECK_FALSE(unchangedReport.DidAnyWork());
|
||||
|
||||
density = rotational_displacement_force_context_test_utils::make_density(f, 1.17);
|
||||
|
||||
++dependencies.density.revision;
|
||||
|
||||
const auto densityReport = context.Prepare({.density = density, .displacement = displacement}, dependencies);
|
||||
|
||||
CHECK_FALSE(densityReport.preparedStaticDependencies);
|
||||
CHECK_FALSE(densityReport.preparedGeometryState);
|
||||
CHECK_FALSE(densityReport.preparedRotationDependencies);
|
||||
CHECK(densityReport.preparedBaseState);
|
||||
CHECK(densityReport.updatedDensity);
|
||||
CHECK_FALSE(densityReport.updatedDisplacement);
|
||||
|
||||
const mfem::Vector densityAfterDensityRevision(context.GetBaseDensityTrue());
|
||||
|
||||
displacement = gravity_prepared_test_utils::make_displacement(f, 0.81);
|
||||
|
||||
++dependencies.displacement.revision;
|
||||
|
||||
const auto displacementReport = context.Prepare({.density = density, .displacement = displacement}, dependencies);
|
||||
|
||||
CHECK_FALSE(displacementReport.preparedStaticDependencies);
|
||||
CHECK(displacementReport.preparedGeometryState);
|
||||
CHECK_FALSE(displacementReport.preparedRotationDependencies);
|
||||
CHECK(displacementReport.preparedBaseState);
|
||||
CHECK_FALSE(displacementReport.updatedDensity);
|
||||
CHECK(displacementReport.updatedDisplacement);
|
||||
|
||||
CHECK(
|
||||
rotational_displacement_force_context_test_utils::relative_difference(
|
||||
context.GetBaseDensityTrue(), densityAfterDensityRevision
|
||||
) < 1.0e-14
|
||||
);
|
||||
|
||||
++dependencies.rotation.revision;
|
||||
|
||||
const auto rotationReport = context.Prepare({.density = density, .displacement = displacement}, dependencies);
|
||||
|
||||
CHECK_FALSE(rotationReport.preparedStaticDependencies);
|
||||
CHECK_FALSE(rotationReport.preparedGeometryState);
|
||||
CHECK(rotationReport.preparedRotationDependencies);
|
||||
CHECK(rotationReport.preparedBaseState);
|
||||
CHECK_FALSE(rotationReport.updatedDensity);
|
||||
CHECK_FALSE(rotationReport.updatedDisplacement);
|
||||
|
||||
const auto statistics = context.GetPreparationStatistics();
|
||||
|
||||
CHECK(statistics.staticPreparations == 1);
|
||||
CHECK(statistics.geometryPreparations == 2);
|
||||
CHECK(statistics.rotationPreparations == 2);
|
||||
CHECK(statistics.baseStatePreparations == 4);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Rotational Displacement Force Context Treats New Identities As New "
|
||||
"Dependency Streams",
|
||||
tags::centrifugal &tags::contexts &tags::unit
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const mfem::Vector density = rotational_displacement_force_context_test_utils::make_density(f, 0.91);
|
||||
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.39);
|
||||
|
||||
auto dependencies = rotational_displacement_force_context_test_utils::make_dependencies();
|
||||
|
||||
rotational_displacement_force_context_test_utils::Context context(f, *f.domainMapperStateless);
|
||||
|
||||
context.Prepare({.density = density, .displacement = displacement}, dependencies);
|
||||
|
||||
dependencies.density.identity += 1000;
|
||||
dependencies.density.revision = 0;
|
||||
|
||||
const auto report = context.Prepare({.density = density, .displacement = displacement}, dependencies);
|
||||
|
||||
CHECK(report.preparedBaseState);
|
||||
CHECK(report.updatedDensity);
|
||||
CHECK_FALSE(report.preparedGeometryState);
|
||||
CHECK_FALSE(report.preparedRotationDependencies);
|
||||
}
|
||||
710
tests/operators/gravity_displacement_force.cpp
Normal file
710
tests/operators/gravity_displacement_force.cpp
Normal file
@@ -0,0 +1,710 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace gravity_displacement_force_test_utils {
|
||||
using CoupledForm = mean_field::utils::blocks::barotropic_equilibrium_form;
|
||||
|
||||
constexpr auto densityValue =
|
||||
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term);
|
||||
|
||||
constexpr auto displacementValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||
mean_field::utils::blocks::displacement_field.geometry_term
|
||||
);
|
||||
|
||||
constexpr auto gravityGradientValue =
|
||||
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::gravity_field.gradient_term);
|
||||
|
||||
constexpr auto gravityPotentialValue =
|
||||
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::gravity_field.poisson_term);
|
||||
|
||||
constexpr auto enthalpyValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||
mean_field::utils::blocks::enthalpy_field.specific_term
|
||||
);
|
||||
|
||||
constexpr auto barotropicConstantValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
|
||||
);
|
||||
|
||||
constexpr auto gravityGradientResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
|
||||
constexpr auto gravityPotentialResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
|
||||
constexpr auto densityResidual =
|
||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term);
|
||||
|
||||
constexpr auto displacementResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::displacement_field.geometry_term
|
||||
);
|
||||
|
||||
constexpr auto enthalpyResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::enthalpy_field.specific_term
|
||||
);
|
||||
|
||||
constexpr auto massResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
|
||||
);
|
||||
|
||||
[[nodiscard]] mean_field::operators::GravityDisplacementForceLayout make_layout(const mean_field::fem::FEM &f) {
|
||||
const std::array<int, CoupledForm::value_block_count> valueSizes{
|
||||
f.densityFes->GetTrueVSize(), f.displacementFes->GetTrueVSize(), f.gravityFluxFes->GetTrueVSize(),
|
||||
f.gravityPotentialFes->GetTrueVSize(), f.enthalpyFes->GetTrueVSize(), 1
|
||||
};
|
||||
|
||||
const std::array<int, CoupledForm::residual_block_count> residualSizes{
|
||||
f.gravityFluxFes->GetTrueVSize(), f.gravityPotentialFes->GetTrueVSize(), f.densityFes->GetTrueVSize(),
|
||||
f.displacementFes->GetTrueVSize(), f.enthalpyFes->GetTrueVSize(), 1
|
||||
};
|
||||
|
||||
return {valueSizes, residualSizes};
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_density(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase
|
||||
) {
|
||||
mfem::ParGridFunction densityField(f.densityFes.get());
|
||||
|
||||
mfem::FunctionCoefficient densityCoefficient([phase](const mfem::Vector &position) {
|
||||
return 0.82 + 0.07 * std::sin(0.8 * position(0) + phase) +
|
||||
0.05 * std::cos(0.6 * position(1) - 0.3 * phase) + 0.03 * position(2) * position(2);
|
||||
});
|
||||
|
||||
densityField.ProjectCoefficient(densityCoefficient);
|
||||
|
||||
mfem::Vector densityTrue;
|
||||
densityField.GetTrueDofs(densityTrue);
|
||||
return densityTrue;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_density_direction(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase
|
||||
) {
|
||||
mfem::ParGridFunction densityField(f.densityFes.get());
|
||||
|
||||
mfem::FunctionCoefficient densityCoefficient([phase](const mfem::Vector &position) {
|
||||
return 0.19 * std::sin(0.9 * position(0) + phase) - 0.13 * std::cos(0.7 * position(1) - phase) +
|
||||
0.08 * position(2);
|
||||
});
|
||||
|
||||
densityField.ProjectCoefficient(densityCoefficient);
|
||||
|
||||
mfem::Vector densityTrue;
|
||||
densityField.GetTrueDofs(densityTrue);
|
||||
return densityTrue;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_gravity_gradient(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase
|
||||
) {
|
||||
mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
|
||||
|
||||
auto gravityFunction = [phase](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
|
||||
value(0) = 0.31 + 0.08 * position(0) + 0.03 * phase * position(1);
|
||||
|
||||
value(1) = -0.17 + 0.06 * position(1) - 0.02 * phase * position(2);
|
||||
|
||||
value(2) = 0.23 - 0.05 * position(2) + 0.025 * phase * position(0);
|
||||
};
|
||||
|
||||
mfem::VectorFunctionCoefficient gravityCoefficient(3, gravityFunction);
|
||||
|
||||
gravityField.ProjectCoefficient(gravityCoefficient);
|
||||
|
||||
mfem::Vector gravityTrue;
|
||||
gravityField.GetTrueDofs(gravityTrue);
|
||||
return gravityTrue;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_gravity_gradient_direction(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase
|
||||
) {
|
||||
mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
|
||||
|
||||
auto gravityFunction = [phase](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
|
||||
value(0) = 0.14 * std::sin(position(0) + phase) + 0.03 * position(1);
|
||||
|
||||
value(1) = -0.11 * std::cos(position(1) - phase) + 0.04 * position(2);
|
||||
|
||||
value(2) = 0.09 * std::sin(position(2) + 0.5 * phase) - 0.02 * position(0);
|
||||
};
|
||||
|
||||
mfem::VectorFunctionCoefficient gravityCoefficient(3, gravityFunction);
|
||||
|
||||
gravityField.ProjectCoefficient(gravityCoefficient);
|
||||
|
||||
mfem::Vector gravityTrue;
|
||||
gravityField.GetTrueDofs(gravityTrue);
|
||||
return gravityTrue;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_displacement_direction(const mean_field::fem::FEM &f) {
|
||||
mfem::Vector direction = gravity_prepared_test_utils::make_displacement(f, 0.83);
|
||||
|
||||
const mfem::Vector second = gravity_prepared_test_utils::make_displacement(f, 0.29);
|
||||
|
||||
direction -= second;
|
||||
return direction;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_vacuum_only_density(const mean_field::fem::FEM &f) {
|
||||
mfem::ParGridFunction densityField(f.densityFes.get());
|
||||
densityField = 0.0;
|
||||
|
||||
const int vacuumAttribute = f.domainMapperStateless->GetVacuumElementAttribute();
|
||||
|
||||
mfem::Array<int> densityDofs;
|
||||
int localVacuumElements = 0;
|
||||
|
||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
|
||||
|
||||
REQUIRE(transformation != nullptr);
|
||||
|
||||
if (transformation->Attribute != vacuumAttribute) {
|
||||
continue;
|
||||
}
|
||||
|
||||
f.densityFes->GetElementDofs(elementId, densityDofs);
|
||||
|
||||
mfem::Vector elementDensity(densityDofs.Size());
|
||||
elementDensity = 1.0;
|
||||
densityField.SetSubVector(densityDofs, elementDensity);
|
||||
++localVacuumElements;
|
||||
}
|
||||
|
||||
int globalVacuumElements = 0;
|
||||
MPI_Allreduce(&localVacuumElements, &globalVacuumElements, 1, MPI_INT, MPI_SUM, f.mesh->GetComm());
|
||||
|
||||
REQUIRE(globalVacuumElements > 0);
|
||||
|
||||
mfem::Vector densityTrue;
|
||||
densityField.GetTrueDofs(densityTrue);
|
||||
return densityTrue;
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::context::gravity_field::GravityFieldRevisions make_revisions() {
|
||||
return {
|
||||
.discretization = {.value = 3},
|
||||
.displacement = {.value = 5},
|
||||
.density = {.value = 7},
|
||||
.gravity_gradient = {.value = 11},
|
||||
.gravity_potential = {.value = 13}
|
||||
};
|
||||
}
|
||||
|
||||
void prepare_gravity_context(
|
||||
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext &context,
|
||||
const mfem::Vector &density,
|
||||
const mfem::Vector &displacement,
|
||||
const mfem::Vector &gravityGradient,
|
||||
const mfem::Vector &gravityPotential,
|
||||
const mean_field::operators::context::gravity_field::GravityFieldRevisions &revisions
|
||||
) {
|
||||
context.Prepare(
|
||||
{.density = density,
|
||||
.displacement = displacement,
|
||||
.gravity_gradient = gravityGradient,
|
||||
.gravity_potential = gravityPotential},
|
||||
revisions
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] double relative_difference(
|
||||
const mfem::Vector &left,
|
||||
const mfem::Vector &right,
|
||||
const MPI_Comm communicator
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
left.Size() == right.Size(), "Cannot compare gravity-displacement-force vectors with "
|
||||
"different sizes."
|
||||
);
|
||||
|
||||
mfem::Vector difference(left);
|
||||
difference -= right;
|
||||
|
||||
const double scale = std::max(
|
||||
{gravity_prepared_test_utils::global_norm(left, communicator),
|
||||
gravity_prepared_test_utils::global_norm(right, communicator),
|
||||
100.0 * std::numeric_limits<double>::epsilon()}
|
||||
);
|
||||
|
||||
return gravity_prepared_test_utils::global_norm(difference, communicator) / scale;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector centered_difference(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mfem::Vector &baseDensity,
|
||||
const mfem::Vector &densityDirection,
|
||||
const mfem::Vector &baseGravityGradient,
|
||||
const mfem::Vector &gravityGradientDirection,
|
||||
const mfem::Vector &baseDisplacement,
|
||||
const mfem::Vector &displacementDirection,
|
||||
const double step
|
||||
) {
|
||||
mfem::Vector plusDensity(baseDensity);
|
||||
plusDensity.Add(step, densityDirection);
|
||||
|
||||
mfem::Vector minusDensity(baseDensity);
|
||||
minusDensity.Add(-step, densityDirection);
|
||||
|
||||
mfem::Vector plusGravity(baseGravityGradient);
|
||||
plusGravity.Add(step, gravityGradientDirection);
|
||||
|
||||
mfem::Vector minusGravity(baseGravityGradient);
|
||||
minusGravity.Add(-step, gravityGradientDirection);
|
||||
|
||||
mfem::Vector plusDisplacement(baseDisplacement);
|
||||
plusDisplacement.Add(step, displacementDirection);
|
||||
|
||||
mfem::Vector minusDisplacement(baseDisplacement);
|
||||
minusDisplacement.Add(-step, displacementDirection);
|
||||
|
||||
mfem::Vector plusResidual;
|
||||
mfem::Vector minusResidual;
|
||||
|
||||
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, plusDensity, plusGravity, plusDisplacement, plusResidual
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, minusDensity, minusGravity, minusDisplacement, minusResidual
|
||||
);
|
||||
|
||||
plusResidual -= minusResidual;
|
||||
plusResidual /= 2.0 * step;
|
||||
return plusResidual;
|
||||
}
|
||||
|
||||
template <int index>
|
||||
[[nodiscard]] mfem::Vector copy_residual_block(
|
||||
const mfem::Vector &action,
|
||||
const mean_field::operators::GravityDisplacementForceLayout &layout,
|
||||
const mean_field::utils::blocks::residual_block<index> block
|
||||
) {
|
||||
mfem::Vector result(layout.size(block));
|
||||
const int offset = layout.offset(block);
|
||||
|
||||
for (int entry = 0; entry < result.Size(); ++entry) {
|
||||
result(entry) = action(offset + entry);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace gravity_displacement_force_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
"Gravity Displacement Force Query Includes Every Registered Operand",
|
||||
tags::gravity &tags::quadrature &tags::unit
|
||||
) {
|
||||
using DisplacementField = mean_field::field::Field<mean_field::field::Displacement>;
|
||||
|
||||
constexpr int geometryWeightOrder = 4;
|
||||
|
||||
constexpr mean_field::quadrature::Query query =
|
||||
DisplacementField::make_query<mean_field::field::Displacement::Form::GravityForce>(
|
||||
mean_field::quadrature::QuadratureRole::discretization, geometryWeightOrder, {},
|
||||
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
/*
|
||||
* rho: 2
|
||||
* RT value: family order 2 + 1 = 3
|
||||
* geometry displacement gradient: 3 - 1 = 2
|
||||
* displacement test value: 3
|
||||
* reference-element geometry weight: 4
|
||||
*/
|
||||
constexpr int expectedBaseOrder = 2 + 3 + 2 + 3 + 4;
|
||||
|
||||
STATIC_REQUIRE(query.term == mean_field::quadrature::Term::gravity_force);
|
||||
|
||||
STATIC_REQUIRE(query.role == mean_field::quadrature::QuadratureRole::discretization);
|
||||
|
||||
STATIC_REQUIRE(query.domain == mean_field::utils::DOMAINS::STELLAR);
|
||||
|
||||
STATIC_REQUIRE(query.mapping == mean_field::quadrature::MappingKind::general);
|
||||
|
||||
STATIC_REQUIRE(query.base_order.has_value());
|
||||
STATIC_REQUIRE(*query.base_order == expectedBaseOrder);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Gravity Displacement Force Uses Positive Grad-Phi Sign And Excludes "
|
||||
"Vacuum",
|
||||
tags::gravity &tags::integration &tags::accuracy
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
mfem::ParGridFunction densityField(f.densityFes.get());
|
||||
mfem::ConstantCoefficient densityCoefficient(1.0);
|
||||
densityField.ProjectCoefficient(densityCoefficient);
|
||||
|
||||
mfem::Vector density;
|
||||
densityField.GetTrueDofs(density);
|
||||
|
||||
mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
|
||||
|
||||
auto constantGravityFunction = [](const mfem::Vector &, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value = 0.0;
|
||||
value(0) = 1.0;
|
||||
};
|
||||
|
||||
mfem::VectorFunctionCoefficient gravityCoefficient(3, constantGravityFunction);
|
||||
|
||||
gravityField.ProjectCoefficient(gravityCoefficient);
|
||||
|
||||
mfem::Vector gravityGradient;
|
||||
gravityField.GetTrueDofs(gravityGradient);
|
||||
|
||||
mfem::Vector displacement(f.displacementFes->GetTrueVSize());
|
||||
displacement = 0.0;
|
||||
|
||||
mfem::Vector residual;
|
||||
|
||||
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, density, gravityGradient, displacement, residual
|
||||
);
|
||||
|
||||
mfem::ParGridFunction testField(f.displacementFes.get());
|
||||
testField.ProjectCoefficient(gravityCoefficient);
|
||||
|
||||
mfem::Vector testDirection;
|
||||
testField.GetTrueDofs(testDirection);
|
||||
|
||||
const double signedWork = gravity_prepared_test_utils::global_dot(residual, testDirection, f.mesh->GetComm());
|
||||
|
||||
INFO("Constant +x gravity-force work = " << signedWork);
|
||||
CHECK(signedWork > 0.0);
|
||||
|
||||
const mfem::Vector vacuumDensity = gravity_displacement_force_test_utils::make_vacuum_only_density(f);
|
||||
|
||||
mfem::Vector vacuumResidual;
|
||||
|
||||
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, vacuumDensity, gravityGradient, displacement, vacuumResidual
|
||||
);
|
||||
|
||||
CHECK(gravity_prepared_test_utils::global_norm(vacuumResidual, f.mesh->GetComm()) == 0.0);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Gravity Displacement Force Reuses Shared Gravity Revisions",
|
||||
tags::gravity &tags::prepared &tags::integration
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
mfem::Vector density = gravity_displacement_force_test_utils::make_density(f, 0.31);
|
||||
|
||||
const mfem::Vector gravityGradient = gravity_displacement_force_test_utils::make_gravity_gradient(f, 0.47);
|
||||
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.61);
|
||||
|
||||
mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize());
|
||||
gravityPotential = 0.0;
|
||||
|
||||
auto revisions = gravity_displacement_force_test_utils::make_revisions();
|
||||
|
||||
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
|
||||
gravity_displacement_force_test_utils::prepare_gravity_context(
|
||||
gravityContext, density, displacement, gravityGradient, gravityPotential, revisions
|
||||
);
|
||||
|
||||
mean_field::operators::PreparedGravityDisplacementForceOperator preparedOperator(
|
||||
f, *f.domainMapperStateless, gravityContext
|
||||
);
|
||||
|
||||
const auto initialReport = preparedOperator.Prepare();
|
||||
REQUIRE(initialReport.DidAnyWork());
|
||||
REQUIRE(preparedOperator.IsPrepared());
|
||||
|
||||
mfem::Vector preparedResidual;
|
||||
mfem::Vector kernelResidual;
|
||||
|
||||
preparedOperator.BuildResidual(preparedResidual);
|
||||
|
||||
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, density, gravityGradient, displacement, kernelResidual
|
||||
);
|
||||
|
||||
CHECK(
|
||||
gravity_displacement_force_test_utils::relative_difference(
|
||||
preparedResidual, kernelResidual, f.mesh->GetComm()
|
||||
) < 2.0e-12
|
||||
);
|
||||
|
||||
CHECK_FALSE(preparedOperator.Prepare().DidAnyWork());
|
||||
|
||||
++revisions.gravity_potential.value;
|
||||
|
||||
gravity_displacement_force_test_utils::prepare_gravity_context(
|
||||
gravityContext, density, displacement, gravityGradient, gravityPotential, revisions
|
||||
);
|
||||
|
||||
CHECK(preparedOperator.IsPrepared());
|
||||
CHECK_FALSE(preparedOperator.Prepare().DidAnyWork());
|
||||
|
||||
density = gravity_displacement_force_test_utils::make_density(f, 0.79);
|
||||
++revisions.density.value;
|
||||
|
||||
gravity_displacement_force_test_utils::prepare_gravity_context(
|
||||
gravityContext, density, displacement, gravityGradient, gravityPotential, revisions
|
||||
);
|
||||
|
||||
CHECK_FALSE(preparedOperator.IsPrepared());
|
||||
|
||||
const auto densityReport = preparedOperator.Prepare();
|
||||
CHECK(densityReport.DidAnyWork());
|
||||
CHECK(preparedOperator.IsPrepared());
|
||||
CHECK(preparedOperator.GetResidualPreparationCount() == 2);
|
||||
CHECK(preparedOperator.GetResidualApplicationCount() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Gravity Displacement Force Jacobian Matches All Columns And Centered "
|
||||
"Differences",
|
||||
tags::gravity &tags::prepared &tags::jacobian &tags::accuracy
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const mfem::Vector density = gravity_displacement_force_test_utils::make_density(f, 0.37);
|
||||
|
||||
const mfem::Vector densityDirection = gravity_displacement_force_test_utils::make_density_direction(f, 0.53);
|
||||
|
||||
const mfem::Vector gravityGradient = gravity_displacement_force_test_utils::make_gravity_gradient(f, 0.67);
|
||||
|
||||
const mfem::Vector gravityGradientDirection =
|
||||
gravity_displacement_force_test_utils::make_gravity_gradient_direction(f, 0.71);
|
||||
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.59);
|
||||
|
||||
const mfem::Vector displacementDirection = gravity_displacement_force_test_utils::make_displacement_direction(f);
|
||||
|
||||
mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize());
|
||||
gravityPotential = 0.0;
|
||||
|
||||
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
|
||||
gravity_displacement_force_test_utils::prepare_gravity_context(
|
||||
gravityContext, density, displacement, gravityGradient, gravityPotential,
|
||||
gravity_displacement_force_test_utils::make_revisions()
|
||||
);
|
||||
|
||||
mean_field::operators::PreparedGravityDisplacementForceOperator preparedOperator(
|
||||
f, *f.domainMapperStateless, gravityContext
|
||||
);
|
||||
|
||||
preparedOperator.Prepare();
|
||||
|
||||
mfem::Vector densityAction;
|
||||
mfem::Vector gravityAction;
|
||||
mfem::Vector displacementAction;
|
||||
mfem::Vector completeAction;
|
||||
|
||||
preparedOperator.ApplyDensityJacobianAction(densityDirection, densityAction);
|
||||
|
||||
preparedOperator.ApplyGravityGradientJacobianAction(gravityGradientDirection, gravityAction);
|
||||
|
||||
preparedOperator.ApplyDisplacementJacobianAction(displacementDirection, displacementAction);
|
||||
|
||||
preparedOperator.ApplyCompleteJacobianAction(
|
||||
densityDirection, displacementDirection, gravityGradientDirection, completeAction
|
||||
);
|
||||
|
||||
mfem::Vector summedColumns(densityAction);
|
||||
summedColumns += gravityAction;
|
||||
summedColumns += displacementAction;
|
||||
|
||||
CHECK(
|
||||
gravity_displacement_force_test_utils::relative_difference(completeAction, summedColumns, f.mesh->GetComm()) <
|
||||
2.0e-12
|
||||
);
|
||||
|
||||
mfem::Vector zeroDensity(densityDirection.Size());
|
||||
mfem::Vector zeroGravity(gravityGradientDirection.Size());
|
||||
mfem::Vector zeroDisplacement(displacementDirection.Size());
|
||||
zeroDensity = 0.0;
|
||||
zeroGravity = 0.0;
|
||||
zeroDisplacement = 0.0;
|
||||
|
||||
constexpr double step = 1.0e-5;
|
||||
|
||||
const mfem::Vector densityDifference = gravity_displacement_force_test_utils::centered_difference(
|
||||
f, density, densityDirection, gravityGradient, zeroGravity, displacement, zeroDisplacement, step
|
||||
);
|
||||
|
||||
const mfem::Vector gravityDifference = gravity_displacement_force_test_utils::centered_difference(
|
||||
f, density, zeroDensity, gravityGradient, gravityGradientDirection, displacement, zeroDisplacement, step
|
||||
);
|
||||
|
||||
const mfem::Vector displacementDifference = gravity_displacement_force_test_utils::centered_difference(
|
||||
f, density, zeroDensity, gravityGradient, zeroGravity, displacement, displacementDirection, step
|
||||
);
|
||||
|
||||
const mfem::Vector completeDifference = gravity_displacement_force_test_utils::centered_difference(
|
||||
f, density, densityDirection, gravityGradient, gravityGradientDirection, displacement, displacementDirection,
|
||||
step
|
||||
);
|
||||
|
||||
const double densityError =
|
||||
gravity_displacement_force_test_utils::relative_difference(densityAction, densityDifference, f.mesh->GetComm());
|
||||
|
||||
const double gravityError =
|
||||
gravity_displacement_force_test_utils::relative_difference(gravityAction, gravityDifference, f.mesh->GetComm());
|
||||
|
||||
const double displacementError = gravity_displacement_force_test_utils::relative_difference(
|
||||
displacementAction, displacementDifference, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
const double completeError = gravity_displacement_force_test_utils::relative_difference(
|
||||
completeAction, completeDifference, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
INFO("Density-column centered-difference error = " << densityError);
|
||||
INFO("Gravity-column centered-difference error = " << gravityError);
|
||||
INFO("Displacement-column centered-difference error = " << displacementError);
|
||||
INFO("Complete centered-difference error = " << completeError);
|
||||
|
||||
CHECK(densityError < 2.0e-9);
|
||||
CHECK(gravityError < 2.0e-9);
|
||||
CHECK(displacementError < 2.0e-8);
|
||||
CHECK(completeError < 3.0e-8);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Gravity Displacement Force MFEM Adapter Routes Only R-d",
|
||||
tags::gravity &tags::prepared &tags::mfem_operators &tags::unit
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const mfem::Vector density = gravity_displacement_force_test_utils::make_density(f, 0.41);
|
||||
|
||||
const mfem::Vector densityDirection = gravity_displacement_force_test_utils::make_density_direction(f, 0.57);
|
||||
|
||||
const mfem::Vector gravityGradient = gravity_displacement_force_test_utils::make_gravity_gradient(f, 0.63);
|
||||
|
||||
const mfem::Vector gravityGradientDirection =
|
||||
gravity_displacement_force_test_utils::make_gravity_gradient_direction(f, 0.77);
|
||||
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.51);
|
||||
|
||||
const mfem::Vector displacementDirection = gravity_displacement_force_test_utils::make_displacement_direction(f);
|
||||
|
||||
mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize());
|
||||
gravityPotential = 0.0;
|
||||
|
||||
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
|
||||
gravity_displacement_force_test_utils::prepare_gravity_context(
|
||||
gravityContext, density, displacement, gravityGradient, gravityPotential,
|
||||
gravity_displacement_force_test_utils::make_revisions()
|
||||
);
|
||||
|
||||
mean_field::operators::PreparedGravityDisplacementForceOperator preparedOperator(
|
||||
f, *f.domainMapperStateless, gravityContext
|
||||
);
|
||||
|
||||
preparedOperator.Prepare();
|
||||
|
||||
const auto layout = gravity_displacement_force_test_utils::make_layout(f);
|
||||
|
||||
mean_field::operators::PreparedGravityDisplacementForceJacobianOperator adapter(layout, preparedOperator);
|
||||
|
||||
mfem::BlockVector direction(layout.value_offsets());
|
||||
direction = 0.0;
|
||||
|
||||
direction.GetBlock(gravity_displacement_force_test_utils::densityValue) = densityDirection;
|
||||
|
||||
direction.GetBlock(gravity_displacement_force_test_utils::displacementValue) = displacementDirection;
|
||||
|
||||
direction.GetBlock(gravity_displacement_force_test_utils::gravityGradientValue) = gravityGradientDirection;
|
||||
|
||||
direction.GetBlock(gravity_displacement_force_test_utils::gravityPotentialValue) = 0.29;
|
||||
|
||||
direction.GetBlock(gravity_displacement_force_test_utils::enthalpyValue) = -0.37;
|
||||
|
||||
direction.GetBlock(gravity_displacement_force_test_utils::barotropicConstantValue) = 0.43;
|
||||
|
||||
mfem::Vector action;
|
||||
adapter.Mult(direction, action);
|
||||
|
||||
mfem::Vector expectedDisplacementAction;
|
||||
|
||||
preparedOperator.ApplyCompleteJacobianAction(
|
||||
densityDirection, displacementDirection, gravityGradientDirection, expectedDisplacementAction
|
||||
);
|
||||
|
||||
const mfem::Vector actualDisplacementAction = gravity_displacement_force_test_utils::copy_residual_block(
|
||||
action, layout, gravity_displacement_force_test_utils::displacementResidual
|
||||
);
|
||||
|
||||
CHECK(
|
||||
gravity_displacement_force_test_utils::relative_difference(
|
||||
actualDisplacementAction, expectedDisplacementAction, f.mesh->GetComm()
|
||||
) < 2.0e-12
|
||||
);
|
||||
|
||||
const std::array<mfem::Vector, 5> zeroRows{
|
||||
gravity_displacement_force_test_utils::copy_residual_block(
|
||||
action, layout, gravity_displacement_force_test_utils::gravityGradientResidual
|
||||
),
|
||||
gravity_displacement_force_test_utils::copy_residual_block(
|
||||
action, layout, gravity_displacement_force_test_utils::gravityPotentialResidual
|
||||
),
|
||||
gravity_displacement_force_test_utils::copy_residual_block(
|
||||
action, layout, gravity_displacement_force_test_utils::densityResidual
|
||||
),
|
||||
gravity_displacement_force_test_utils::copy_residual_block(
|
||||
action, layout, gravity_displacement_force_test_utils::enthalpyResidual
|
||||
),
|
||||
gravity_displacement_force_test_utils::copy_residual_block(
|
||||
action, layout, gravity_displacement_force_test_utils::massResidual
|
||||
)
|
||||
};
|
||||
|
||||
for (const mfem::Vector &row : zeroRows) {
|
||||
CHECK(gravity_prepared_test_utils::global_norm(row, f.mesh->GetComm()) == 0.0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,381 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <numbers>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace gravity_displacement_force_analytic_test_utils {
|
||||
struct AffineCase {
|
||||
const char *name;
|
||||
std::array<double, 3> scales;
|
||||
};
|
||||
|
||||
[[nodiscard]] double analytic_sphere_volume(const double radius) {
|
||||
return (4.0 / 3.0) * std::numbers::pi * radius * radius * radius;
|
||||
}
|
||||
|
||||
[[nodiscard]] double determinant(
|
||||
const std::array<
|
||||
double,
|
||||
3> &scales
|
||||
) {
|
||||
return scales[0] * scales[1] * scales[2];
|
||||
}
|
||||
|
||||
[[nodiscard]] double relative_scalar_error(
|
||||
const double computed,
|
||||
const double expected
|
||||
) {
|
||||
return std::abs(computed - expected) / std::max(std::abs(expected), 1.0e-30);
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_constant_density(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double densityValue
|
||||
) {
|
||||
mfem::ParGridFunction densityField(f.densityFes.get());
|
||||
mfem::ConstantCoefficient densityCoefficient(densityValue);
|
||||
densityField.ProjectCoefficient(densityCoefficient);
|
||||
|
||||
mfem::Vector densityTrue;
|
||||
densityField.GetTrueDofs(densityTrue);
|
||||
return densityTrue;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_reference_gravity(
|
||||
const mean_field::fem::FEM &f,
|
||||
const std::array<
|
||||
double,
|
||||
3> &referenceGravity
|
||||
) {
|
||||
mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
|
||||
|
||||
mfem::VectorFunctionCoefficient gravityCoefficient(
|
||||
f.mesh->Dimension(), [referenceGravity](const mfem::Vector &, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
value(component) = referenceGravity[static_cast<std::size_t>(component)];
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
gravityField.ProjectCoefficient(gravityCoefficient);
|
||||
|
||||
mfem::Vector gravityTrue;
|
||||
gravityField.GetTrueDofs(gravityTrue);
|
||||
return gravityTrue;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_radial_gravity(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double radialCoefficient
|
||||
) {
|
||||
mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
|
||||
|
||||
mfem::VectorFunctionCoefficient gravityCoefficient(
|
||||
f.mesh->Dimension(), [radialCoefficient](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(position.Size());
|
||||
|
||||
for (int component = 0; component < position.Size(); ++component) {
|
||||
value(component) = radialCoefficient * position(component);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
gravityField.ProjectCoefficient(gravityCoefficient);
|
||||
|
||||
mfem::Vector gravityTrue;
|
||||
gravityField.GetTrueDofs(gravityTrue);
|
||||
return gravityTrue;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_affine_displacement(
|
||||
const mean_field::fem::FEM &f,
|
||||
const std::array<
|
||||
double,
|
||||
3> &scales
|
||||
) {
|
||||
mfem::ParGridFunction displacementField(f.displacementFes.get());
|
||||
|
||||
mfem::VectorFunctionCoefficient displacementCoefficient(
|
||||
f.mesh->Dimension(), [scales](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(position.Size());
|
||||
|
||||
for (int component = 0; component < position.Size(); ++component) {
|
||||
value(component) = (scales[static_cast<std::size_t>(component)] - 1.0) * position(component);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
displacementField.ProjectCoefficient(displacementCoefficient);
|
||||
|
||||
mfem::Vector displacementTrue;
|
||||
displacementField.GetTrueDofs(displacementTrue);
|
||||
return displacementTrue;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_constant_test_direction(
|
||||
const mean_field::fem::FEM &f,
|
||||
const int selectedComponent
|
||||
) {
|
||||
mfem::ParGridFunction testField(f.displacementFes.get());
|
||||
|
||||
mfem::VectorFunctionCoefficient testCoefficient(
|
||||
f.mesh->Dimension(), [selectedComponent](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(position.Size());
|
||||
value = 0.0;
|
||||
value(selectedComponent) = 1.0;
|
||||
}
|
||||
);
|
||||
|
||||
testField.ProjectCoefficient(testCoefficient);
|
||||
|
||||
mfem::Vector testTrue;
|
||||
testField.GetTrueDofs(testTrue);
|
||||
return testTrue;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_dilation_test_direction(const mean_field::fem::FEM &f) {
|
||||
mfem::ParGridFunction testField(f.displacementFes.get());
|
||||
|
||||
mfem::VectorFunctionCoefficient testCoefficient(
|
||||
f.mesh->Dimension(), [](const mfem::Vector &position, mfem::Vector &value) { value = position; }
|
||||
);
|
||||
|
||||
testField.ProjectCoefficient(testCoefficient);
|
||||
|
||||
mfem::Vector testTrue;
|
||||
testField.GetTrueDofs(testTrue);
|
||||
return testTrue;
|
||||
}
|
||||
|
||||
void set_mass_normalized_density(
|
||||
mean_field::fem::FEM &f,
|
||||
const double targetMass,
|
||||
mfem::ParGridFunction &densityField
|
||||
) {
|
||||
const mfem::Vector stellarDensityTrue = gravity_prepared_test_utils::make_domain_supported_density(f, true);
|
||||
|
||||
densityField.SetFromTrueDofs(stellarDensityTrue);
|
||||
|
||||
const double unnormalizedMass =
|
||||
mean_field::analysis::domain_integrate_grid_function(f, densityField, mean_field::utils::DOMAINS::STELLAR);
|
||||
|
||||
MFEM_VERIFY(unnormalizedMass > 0.0, "The analytic gravity-force test obtained non-positive mass.");
|
||||
|
||||
densityField *= targetMass / unnormalizedMass;
|
||||
}
|
||||
} // namespace gravity_displacement_force_analytic_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
"Gravity Displacement Force Matches Analytic Affine Resultants",
|
||||
tags::gravity &tags::accuracy &tags::analytic_comparison &tags::integration
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
REQUIRE(f.domainMapperStateless != nullptr);
|
||||
REQUIRE(f.mapping != nullptr);
|
||||
|
||||
constexpr double densityValue = 1.37;
|
||||
|
||||
constexpr std::array<double, 3> physicalGravity{0.31, -0.47, 0.22};
|
||||
|
||||
constexpr std::array<gravity_displacement_force_analytic_test_utils::AffineCase, 3> affineCases{
|
||||
{{.name = "identity geometry", .scales = {1.0, 1.0, 1.0}},
|
||||
{.name = "volume-preserving affine geometry", .scales = {1.14, 0.93, 1.0 / (1.14 * 0.93)}},
|
||||
{.name = "volume-changing affine geometry", .scales = {1.11, 0.96, 1.07}}}
|
||||
};
|
||||
|
||||
const mfem::Vector density = gravity_displacement_force_analytic_test_utils::make_constant_density(f, densityValue);
|
||||
|
||||
const double referenceVolume =
|
||||
gravity_displacement_force_analytic_test_utils::analytic_sphere_volume(mean_field::utils::RADIUS);
|
||||
|
||||
constexpr double relativeTolerance = 5.0e-6;
|
||||
|
||||
for (const gravity_displacement_force_analytic_test_utils::AffineCase &affineCase : affineCases) {
|
||||
DYNAMIC_SECTION(affineCase.name) {
|
||||
const double mapDeterminant =
|
||||
gravity_displacement_force_analytic_test_utils::determinant(affineCase.scales);
|
||||
|
||||
REQUIRE(mapDeterminant > 0.0);
|
||||
|
||||
std::array<double, 3> referenceGravity{};
|
||||
|
||||
/*
|
||||
* For x = A X, the H(div) Piola relation is
|
||||
*
|
||||
* g_phys = A g_ref / det(A).
|
||||
*
|
||||
* Prescribe the RT pullback that represents the requested
|
||||
* constant physical gravity field exactly.
|
||||
*/
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
referenceGravity[static_cast<std::size_t>(component)] =
|
||||
mapDeterminant * physicalGravity[static_cast<std::size_t>(component)] /
|
||||
affineCase.scales[static_cast<std::size_t>(component)];
|
||||
}
|
||||
|
||||
const mfem::Vector gravityGradient =
|
||||
gravity_displacement_force_analytic_test_utils::make_reference_gravity(f, referenceGravity);
|
||||
|
||||
const mfem::Vector displacement =
|
||||
gravity_displacement_force_analytic_test_utils::make_affine_displacement(f, affineCase.scales);
|
||||
|
||||
mfem::Vector residual;
|
||||
|
||||
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, density, gravityGradient, displacement, residual
|
||||
);
|
||||
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
const mfem::Vector testDirection =
|
||||
gravity_displacement_force_analytic_test_utils::make_constant_test_direction(f, component);
|
||||
|
||||
const double computedResultant =
|
||||
gravity_prepared_test_utils::global_dot(residual, testDirection, f.mesh->GetComm());
|
||||
|
||||
const double expectedResultant = densityValue * physicalGravity[static_cast<std::size_t>(component)] *
|
||||
mapDeterminant * referenceVolume;
|
||||
|
||||
const double relativeError = gravity_displacement_force_analytic_test_utils::relative_scalar_error(
|
||||
computedResultant, expectedResultant
|
||||
);
|
||||
|
||||
CAPTURE(component);
|
||||
INFO("Map determinant = " << mapDeterminant);
|
||||
INFO("Computed resultant = " << computedResultant);
|
||||
INFO("Analytic resultant = " << expectedResultant);
|
||||
INFO("Relative resultant error = " << relativeError);
|
||||
|
||||
CHECK(relativeError < relativeTolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Gravity Displacement Force Reproduces Analytic Homogeneous Sphere Work",
|
||||
tags::gravity &tags::accuracy &tags::analytic_comparison &tags::integration
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
REQUIRE(f.domainMapperStateless != nullptr);
|
||||
|
||||
const double radius = mean_field::utils::RADIUS;
|
||||
const double mass = mean_field::utils::MASS;
|
||||
const double volume = gravity_displacement_force_analytic_test_utils::analytic_sphere_volume(radius);
|
||||
|
||||
const double densityValue = mass / volume;
|
||||
const double radialGravityCoefficient = mean_field::utils::G * mass / (radius * radius * radius);
|
||||
|
||||
const mfem::Vector density = gravity_displacement_force_analytic_test_utils::make_constant_density(f, densityValue);
|
||||
|
||||
const mfem::Vector gravityGradient =
|
||||
gravity_displacement_force_analytic_test_utils::make_radial_gravity(f, radialGravityCoefficient);
|
||||
|
||||
mfem::Vector displacement(f.displacementFes->GetTrueVSize());
|
||||
displacement = 0.0;
|
||||
|
||||
mfem::Vector residual;
|
||||
|
||||
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, density, gravityGradient, displacement, residual
|
||||
);
|
||||
|
||||
const mfem::Vector dilationDirection =
|
||||
gravity_displacement_force_analytic_test_utils::make_dilation_test_direction(f);
|
||||
|
||||
const double computedWork = gravity_prepared_test_utils::global_dot(residual, dilationDirection, f.mesh->GetComm());
|
||||
|
||||
const double analyticWork = (3.0 / 5.0) * mean_field::utils::G * mass * mass / radius;
|
||||
|
||||
const double relativeError =
|
||||
gravity_displacement_force_analytic_test_utils::relative_scalar_error(computedWork, analyticWork);
|
||||
|
||||
INFO("Computed positive gravity work = " << computedWork);
|
||||
INFO("Analytic positive gravity work = " << analyticWork);
|
||||
INFO("Computed gravitational virial = " << -computedWork);
|
||||
INFO("Analytic binding energy = " << -analyticWork);
|
||||
INFO("Relative analytic work error = " << relativeError);
|
||||
|
||||
REQUIRE(computedWork > 0.0);
|
||||
CHECK(relativeError < 1.0e-5);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Solved Homogeneous Sphere Gravity Force Matches Analytic Virial",
|
||||
tags::gravity &tags::accuracy &tags::analytic_comparison &tags::integration &tags::initialization
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
args.p.rtol = 1.0e-13;
|
||||
args.p.max_iters = std::max(args.p.max_iters, 1000);
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
REQUIRE(f.domainMapperStateless != nullptr);
|
||||
|
||||
mfem::ParGridFunction displacementField(f.displacementFes.get());
|
||||
displacementField = 0.0;
|
||||
|
||||
REQUIRE(f.mapping != nullptr);
|
||||
f.mapping->ResetDisplacement();
|
||||
mean_field::physics::update_stiffness_matrix(f);
|
||||
|
||||
const double radius = mean_field::utils::RADIUS;
|
||||
const double mass = mean_field::utils::MASS;
|
||||
|
||||
mfem::ParGridFunction densityField(f.densityFes.get());
|
||||
|
||||
gravity_displacement_force_analytic_test_utils::set_mass_normalized_density(f, mass, densityField);
|
||||
|
||||
const mean_field::physics::GravitySolution gravitySolution =
|
||||
mean_field::physics::grav_potential_new(f, args, densityField, displacementField);
|
||||
|
||||
mfem::Vector densityTrue;
|
||||
mfem::Vector gravityGradientTrue;
|
||||
mfem::Vector displacementTrue;
|
||||
|
||||
densityField.GetTrueDofs(densityTrue);
|
||||
gravitySolution.gradPhi.GetTrueDofs(gravityGradientTrue);
|
||||
displacementField.GetTrueDofs(displacementTrue);
|
||||
|
||||
mfem::Vector residual;
|
||||
|
||||
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, densityTrue, gravityGradientTrue, displacementTrue, residual
|
||||
);
|
||||
|
||||
const mfem::Vector dilationDirection =
|
||||
gravity_displacement_force_analytic_test_utils::make_dilation_test_direction(f);
|
||||
|
||||
const double computedWork = gravity_prepared_test_utils::global_dot(residual, dilationDirection, f.mesh->GetComm());
|
||||
|
||||
const double analyticWork = (3.0 / 5.0) * mean_field::utils::G * mass * mass / radius;
|
||||
|
||||
const double relativeError =
|
||||
gravity_displacement_force_analytic_test_utils::relative_scalar_error(computedWork, analyticWork);
|
||||
|
||||
INFO("Solved-field positive gravity work = " << computedWork);
|
||||
INFO("Analytic positive gravity work = " << analyticWork);
|
||||
INFO("Solved-field gravitational virial = " << -computedWork);
|
||||
INFO("Analytic homogeneous-sphere binding energy = " << -analyticWork);
|
||||
INFO("Relative solved-field virial error = " << relativeError);
|
||||
|
||||
REQUIRE(computedWork > 0.0);
|
||||
CHECK(relativeError < 1.0e-5);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -44,23 +44,17 @@ namespace {
|
||||
}
|
||||
|
||||
mfem::Vector make_base_density(const mean_field::fem::FEM &f) {
|
||||
mfem::FunctionCoefficient coefficient(
|
||||
[](const mfem::Vector &position) {
|
||||
return 0.55 + 0.025 * position(0) - 0.010 * position(1) +
|
||||
0.006 * position(2);
|
||||
}
|
||||
);
|
||||
mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) {
|
||||
return 0.55 + 0.025 * position(0) - 0.010 * position(1) + 0.006 * position(2);
|
||||
});
|
||||
|
||||
return project_scalar_field(*f.densityFes, coefficient);
|
||||
}
|
||||
|
||||
mfem::Vector make_base_enthalpy(const mean_field::fem::FEM &f) {
|
||||
mfem::FunctionCoefficient coefficient(
|
||||
[](const mfem::Vector &position) {
|
||||
return 0.90 + 0.020 * position(0) - 0.010 * position(1) +
|
||||
0.005 * position(2);
|
||||
}
|
||||
);
|
||||
mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) {
|
||||
return 0.90 + 0.020 * position(0) - 0.010 * position(1) + 0.005 * position(2);
|
||||
});
|
||||
|
||||
return project_scalar_field(*f.enthalpyFes, coefficient);
|
||||
}
|
||||
@@ -69,23 +63,20 @@ namespace {
|
||||
|
||||
TEST_CASE(
|
||||
"Barotropic Closure Vanishes For A Representable Constant State",
|
||||
tags::hydro &tags::residuals &tags::unit &tags::closure &tags::kernels
|
||||
&tags::barotrope
|
||||
tags::hydro &tags::residuals &tags::unit &tags::closure &tags::kernels &tags::barotrope
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
auto args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
|
||||
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
|
||||
|
||||
constexpr double enthalpyValue = 0.8;
|
||||
constexpr double enthalpyValue = 0.8;
|
||||
|
||||
const double densityValue = barotrope.density_from_enthalpy(enthalpyValue);
|
||||
const double densityValue = barotrope.density_from_enthalpy(enthalpyValue);
|
||||
|
||||
const mfem::Vector enthalpy =
|
||||
project_constant(*f.enthalpyFes, enthalpyValue);
|
||||
const mfem::Vector enthalpy = project_constant(*f.enthalpyFes, enthalpyValue);
|
||||
|
||||
const mfem::Vector density = project_constant(*f.densityFes, densityValue);
|
||||
const mfem::Vector density = project_constant(*f.densityFes, densityValue);
|
||||
|
||||
const mfem::Vector displacement = make_zero_displacement(f);
|
||||
|
||||
@@ -93,19 +84,17 @@ TEST_CASE(
|
||||
mfem::Vector scale;
|
||||
|
||||
mean_field::operators::kernels::apply_barotropic_closure(
|
||||
f, *f.domainMapperStateless, barotrope, density, enthalpy, displacement,
|
||||
residual
|
||||
f, *f.domainMapperStateless, barotrope, density, enthalpy, displacement, residual
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::apply_barotropic_closure_density_action(
|
||||
f, *f.domainMapperStateless, barotrope, density, displacement, scale
|
||||
);
|
||||
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
|
||||
const double relativeResidual =
|
||||
gravity_prepared_test_utils::global_norm(residual, communicator) /
|
||||
gravity_prepared_test_utils::global_norm(scale, communicator);
|
||||
const double relativeResidual = gravity_prepared_test_utils::global_norm(residual, communicator) /
|
||||
gravity_prepared_test_utils::global_norm(scale, communicator);
|
||||
|
||||
INFO("Relative constant-state closure residual = " << relativeResidual);
|
||||
|
||||
@@ -114,40 +103,34 @@ TEST_CASE(
|
||||
|
||||
TEST_CASE(
|
||||
"Barotropic Closure Density Action Matches The Stellar Mass Matrix",
|
||||
tags::hydro &tags::jacobian &tags::unit &tags::closure &tags::kernels
|
||||
&tags::barotrope
|
||||
tags::hydro &tags::jacobian &tags::unit &tags::closure &tags::kernels &tags::barotrope
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
auto args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
|
||||
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
|
||||
|
||||
const mfem::Vector displacement = make_zero_displacement(f);
|
||||
|
||||
const mfem::Vector densityVariation =
|
||||
gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.densityFes->GetTrueVSize(), 0.37
|
||||
);
|
||||
gravity_prepared_test_utils::make_deterministic_vector(f.densityFes->GetTrueVSize(), 0.37);
|
||||
|
||||
mfem::Vector kernelAction;
|
||||
|
||||
mean_field::operators::kernels::apply_barotropic_closure_density_action(
|
||||
f, *f.domainMapperStateless, barotrope, densityVariation, displacement,
|
||||
kernelAction
|
||||
f, *f.domainMapperStateless, barotrope, densityVariation, displacement, kernelAction
|
||||
);
|
||||
|
||||
using Schema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
using Stellar = mean_field::utils::domain::Stellar;
|
||||
|
||||
mfem::Array<int> stellarMarker(f.mesh->attributes.Max());
|
||||
stellarMarker = 0;
|
||||
|
||||
const int vacuumAttribute =
|
||||
f.domainMapperStateless->GetVacuumElementAttribute();
|
||||
|
||||
for (int attributeIndex = 0; attributeIndex < f.mesh->attributes.Size();
|
||||
++attributeIndex) {
|
||||
for (int attributeIndex = 0; attributeIndex < f.mesh->attributes.Size(); ++attributeIndex) {
|
||||
const int attribute = f.mesh->attributes[attributeIndex];
|
||||
|
||||
if (attribute != vacuumAttribute) {
|
||||
if (Schema::template attribute_belongs_to<Stellar>(attribute)) {
|
||||
stellarMarker[attribute - 1] = 1;
|
||||
}
|
||||
}
|
||||
@@ -159,9 +142,7 @@ TEST_CASE(
|
||||
massForm.Assemble();
|
||||
massForm.Finalize();
|
||||
|
||||
std::unique_ptr<mfem::HypreParMatrix> massMatrix(
|
||||
massForm.ParallelAssemble()
|
||||
);
|
||||
std::unique_ptr<mfem::HypreParMatrix> massMatrix(massForm.ParallelAssemble());
|
||||
|
||||
REQUIRE(massMatrix != nullptr);
|
||||
REQUIRE(massMatrix->Width() == densityVariation.Size());
|
||||
@@ -170,9 +151,8 @@ TEST_CASE(
|
||||
referenceAction = 0.0;
|
||||
|
||||
massMatrix->Mult(densityVariation, referenceAction);
|
||||
const double relativeError = gravity_prepared_test_utils::relative_error(
|
||||
kernelAction, referenceAction, f.mesh->GetComm()
|
||||
);
|
||||
const double relativeError =
|
||||
gravity_prepared_test_utils::relative_error(kernelAction, referenceAction, f.mesh->GetComm());
|
||||
|
||||
INFO("Density-action mass-matrix error = " << relativeError);
|
||||
|
||||
@@ -181,32 +161,24 @@ TEST_CASE(
|
||||
|
||||
TEST_CASE(
|
||||
"Barotropic Closure Jacobian Matches A Combined Centered Difference",
|
||||
tags::hydro &tags::jacobian &tags::unit &tags::closure &tags::kernels
|
||||
&tags::barotrope
|
||||
tags::hydro &tags::jacobian &tags::unit &tags::closure &tags::kernels &tags::barotrope
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
auto args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
|
||||
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
|
||||
|
||||
mfem::FunctionCoefficient densityCoefficient(
|
||||
[](const mfem::Vector &position) {
|
||||
return 0.4 + 0.03 * position(0) - 0.01 * position(1);
|
||||
}
|
||||
);
|
||||
mfem::FunctionCoefficient densityCoefficient([](const mfem::Vector &position) {
|
||||
return 0.4 + 0.03 * position(0) - 0.01 * position(1);
|
||||
});
|
||||
|
||||
mfem::FunctionCoefficient enthalpyCoefficient(
|
||||
[](const mfem::Vector &position) {
|
||||
return 0.9 + 0.02 * position(0) - 0.01 * position(1);
|
||||
}
|
||||
);
|
||||
mfem::FunctionCoefficient enthalpyCoefficient([](const mfem::Vector &position) {
|
||||
return 0.9 + 0.02 * position(0) - 0.01 * position(1);
|
||||
});
|
||||
|
||||
mfem::FunctionCoefficient enthalpyVariationCoefficient(
|
||||
[](const mfem::Vector &position) {
|
||||
return 0.07 + 0.015 * position(0) + 0.008 * position(2);
|
||||
}
|
||||
);
|
||||
mfem::FunctionCoefficient enthalpyVariationCoefficient([](const mfem::Vector &position) {
|
||||
return 0.07 + 0.015 * position(0) + 0.008 * position(2);
|
||||
});
|
||||
|
||||
mfem::ParGridFunction densityField(f.densityFes.get());
|
||||
mfem::ParGridFunction enthalpyField(f.enthalpyFes.get());
|
||||
@@ -225,46 +197,33 @@ TEST_CASE(
|
||||
enthalpyVariationField.GetTrueDofs(enthalpyVariation);
|
||||
|
||||
const mfem::Vector densityVariation =
|
||||
gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.densityFes->GetTrueVSize(), 0.63
|
||||
);
|
||||
gravity_prepared_test_utils::make_deterministic_vector(f.densityFes->GetTrueVSize(), 0.63);
|
||||
|
||||
const mfem::Vector displacement =
|
||||
gravity_prepared_test_utils::make_displacement(f, 1.0);
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 1.0);
|
||||
|
||||
constexpr double differenceStep = 1.0e-6;
|
||||
|
||||
const mfem::Vector plusDensity =
|
||||
gravity_prepared_test_utils::linear_combination(
|
||||
density, 1.0, densityVariation, differenceStep
|
||||
);
|
||||
gravity_prepared_test_utils::linear_combination(density, 1.0, densityVariation, differenceStep);
|
||||
|
||||
const mfem::Vector minusDensity =
|
||||
gravity_prepared_test_utils::linear_combination(
|
||||
density, 1.0, densityVariation, -differenceStep
|
||||
);
|
||||
gravity_prepared_test_utils::linear_combination(density, 1.0, densityVariation, -differenceStep);
|
||||
|
||||
const mfem::Vector plusEnthalpy =
|
||||
gravity_prepared_test_utils::linear_combination(
|
||||
enthalpy, 1.0, enthalpyVariation, differenceStep
|
||||
);
|
||||
gravity_prepared_test_utils::linear_combination(enthalpy, 1.0, enthalpyVariation, differenceStep);
|
||||
|
||||
const mfem::Vector minusEnthalpy =
|
||||
gravity_prepared_test_utils::linear_combination(
|
||||
enthalpy, 1.0, enthalpyVariation, -differenceStep
|
||||
);
|
||||
gravity_prepared_test_utils::linear_combination(enthalpy, 1.0, enthalpyVariation, -differenceStep);
|
||||
|
||||
mfem::Vector plusResidual;
|
||||
mfem::Vector minusResidual;
|
||||
|
||||
mean_field::operators::kernels::apply_barotropic_closure(
|
||||
f, *f.domainMapperStateless, barotrope, plusDensity, plusEnthalpy,
|
||||
displacement, plusResidual
|
||||
f, *f.domainMapperStateless, barotrope, plusDensity, plusEnthalpy, displacement, plusResidual
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::apply_barotropic_closure(
|
||||
f, *f.domainMapperStateless, barotrope, minusDensity, minusEnthalpy,
|
||||
displacement, minusResidual
|
||||
f, *f.domainMapperStateless, barotrope, minusDensity, minusEnthalpy, displacement, minusResidual
|
||||
);
|
||||
|
||||
mfem::Vector finiteDifference(plusResidual);
|
||||
@@ -275,21 +234,18 @@ TEST_CASE(
|
||||
mfem::Vector enthalpyAction;
|
||||
|
||||
mean_field::operators::kernels::apply_barotropic_closure_density_action(
|
||||
f, *f.domainMapperStateless, barotrope, densityVariation, displacement,
|
||||
densityAction
|
||||
f, *f.domainMapperStateless, barotrope, densityVariation, displacement, densityAction
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::apply_barotropic_closure_enthalpy_action(
|
||||
f, *f.domainMapperStateless, barotrope, enthalpy, enthalpyVariation,
|
||||
displacement, enthalpyAction
|
||||
f, *f.domainMapperStateless, barotrope, enthalpy, enthalpyVariation, displacement, enthalpyAction
|
||||
);
|
||||
|
||||
mfem::Vector analyticAction(densityAction);
|
||||
analyticAction += enthalpyAction;
|
||||
|
||||
const double relativeError = gravity_prepared_test_utils::relative_error(
|
||||
analyticAction, finiteDifference, f.mesh->GetComm()
|
||||
);
|
||||
const double relativeError =
|
||||
gravity_prepared_test_utils::relative_error(analyticAction, finiteDifference, f.mesh->GetComm());
|
||||
|
||||
INFO("Combined EOS Jacobian error = " << relativeError);
|
||||
|
||||
@@ -298,57 +254,45 @@ TEST_CASE(
|
||||
|
||||
TEST_CASE(
|
||||
"Barotropic Closure Density Action Excludes Vacuum And Uses Mapped Volume",
|
||||
tags::hydro &tags::mapping &tags::unit &tags::closure &tags::barotrope
|
||||
&tags::kernels
|
||||
tags::hydro &tags::mapping &tags::unit &tags::closure &tags::barotrope &tags::kernels
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
auto args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
|
||||
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
|
||||
|
||||
const mfem::Vector stellarDensity =
|
||||
gravity_prepared_test_utils::make_domain_supported_density(f, true);
|
||||
const mfem::Vector stellarDensity = gravity_prepared_test_utils::make_domain_supported_density(f, true);
|
||||
|
||||
const mfem::Vector vacuumDensity =
|
||||
gravity_prepared_test_utils::make_domain_supported_density(f, false);
|
||||
const mfem::Vector vacuumDensity = gravity_prepared_test_utils::make_domain_supported_density(f, false);
|
||||
|
||||
const mfem::Vector identityDisplacement =
|
||||
gravity_prepared_test_utils::make_displacement(f, 0.0);
|
||||
const mfem::Vector identityDisplacement = gravity_prepared_test_utils::make_displacement(f, 0.0);
|
||||
|
||||
const mfem::Vector deformedDisplacement =
|
||||
gravity_prepared_test_utils::make_displacement(f, 1.0);
|
||||
const mfem::Vector deformedDisplacement = gravity_prepared_test_utils::make_displacement(f, 1.0);
|
||||
|
||||
mfem::Vector stellarAction;
|
||||
mfem::Vector vacuumAction;
|
||||
mfem::Vector deformedAction;
|
||||
|
||||
mean_field::operators::kernels::apply_barotropic_closure_density_action(
|
||||
f, *f.domainMapperStateless, barotrope, stellarDensity,
|
||||
identityDisplacement, stellarAction
|
||||
f, *f.domainMapperStateless, barotrope, stellarDensity, identityDisplacement, stellarAction
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::apply_barotropic_closure_density_action(
|
||||
f, *f.domainMapperStateless, barotrope, vacuumDensity,
|
||||
identityDisplacement, vacuumAction
|
||||
f, *f.domainMapperStateless, barotrope, vacuumDensity, identityDisplacement, vacuumAction
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::apply_barotropic_closure_density_action(
|
||||
f, *f.domainMapperStateless, barotrope, stellarDensity,
|
||||
deformedDisplacement, deformedAction
|
||||
f, *f.domainMapperStateless, barotrope, stellarDensity, deformedDisplacement, deformedAction
|
||||
);
|
||||
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
|
||||
const double stellarNorm =
|
||||
gravity_prepared_test_utils::global_norm(stellarAction, communicator);
|
||||
const double stellarNorm = gravity_prepared_test_utils::global_norm(stellarAction, communicator);
|
||||
|
||||
const double vacuumNorm =
|
||||
gravity_prepared_test_utils::global_norm(vacuumAction, communicator);
|
||||
const double vacuumNorm = gravity_prepared_test_utils::global_norm(vacuumAction, communicator);
|
||||
|
||||
const double geometryChange = gravity_prepared_test_utils::relative_error(
|
||||
deformedAction, stellarAction, communicator
|
||||
);
|
||||
const double geometryChange =
|
||||
gravity_prepared_test_utils::relative_error(deformedAction, stellarAction, communicator);
|
||||
|
||||
INFO("Stellar action norm = " << stellarNorm);
|
||||
INFO("Vacuum action norm = " << vacuumNorm);
|
||||
@@ -361,37 +305,30 @@ TEST_CASE(
|
||||
|
||||
TEST_CASE(
|
||||
"Barotropic Closure Displacement Action Matches Centered Differences",
|
||||
tags::barotrope &tags::closure &tags::hydro &tags::integration
|
||||
&tags::jacobian &tags::mapping &tags::physics
|
||||
tags::barotrope &tags::closure &tags::hydro &tags::integration &tags::jacobian &tags::mapping &tags::physics
|
||||
&tags::kernels
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
auto args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.domainMapperStateless != nullptr);
|
||||
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
|
||||
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
|
||||
|
||||
const mfem::Vector baseDensity =
|
||||
barotropic_closure_geometry_test_utils::make_base_density(f);
|
||||
const mfem::Vector baseDensity = barotropic_closure_geometry_test_utils::make_base_density(f);
|
||||
|
||||
const mfem::Vector baseEnthalpy =
|
||||
barotropic_closure_geometry_test_utils::make_base_enthalpy(f);
|
||||
const mfem::Vector baseEnthalpy = barotropic_closure_geometry_test_utils::make_base_enthalpy(f);
|
||||
|
||||
const mfem::Vector displacementVariation =
|
||||
gravity_prepared_test_utils::make_displacement(f, 0.65);
|
||||
const mfem::Vector displacementVariation = gravity_prepared_test_utils::make_displacement(f, 0.65);
|
||||
|
||||
constexpr double differenceStep = 1.0e-5;
|
||||
constexpr double differenceStep = 1.0e-5;
|
||||
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
|
||||
for (const double deformationScale : {0.0, 1.0}) {
|
||||
DYNAMIC_SECTION("Base deformation scale = " << deformationScale) {
|
||||
const mfem::Vector baseDisplacement =
|
||||
gravity_prepared_test_utils::make_displacement(
|
||||
f, deformationScale
|
||||
);
|
||||
const mfem::Vector baseDisplacement = gravity_prepared_test_utils::make_displacement(f, deformationScale);
|
||||
|
||||
mfem::Vector plusDisplacement(baseDisplacement);
|
||||
|
||||
@@ -406,50 +343,36 @@ TEST_CASE(
|
||||
mfem::Vector analyticAction;
|
||||
|
||||
mean_field::operators::kernels::apply_barotropic_closure(
|
||||
f, *f.domainMapperStateless, barotrope, baseDensity,
|
||||
baseEnthalpy, plusDisplacement, plusResidual
|
||||
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy, plusDisplacement, plusResidual
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::apply_barotropic_closure(
|
||||
f, *f.domainMapperStateless, barotrope, baseDensity,
|
||||
baseEnthalpy, minusDisplacement, minusResidual
|
||||
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy, minusDisplacement, minusResidual
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::
|
||||
apply_barotropic_closure_displacement_action(
|
||||
f, *f.domainMapperStateless, barotrope, baseDensity,
|
||||
baseEnthalpy, baseDisplacement, displacementVariation,
|
||||
analyticAction
|
||||
);
|
||||
mean_field::operators::kernels::apply_barotropic_closure_displacement_action(
|
||||
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy, baseDisplacement,
|
||||
displacementVariation, analyticAction
|
||||
);
|
||||
|
||||
mfem::Vector finiteDifference(plusResidual);
|
||||
|
||||
finiteDifference -= minusResidual;
|
||||
finiteDifference *= 1.0 / (2.0 * differenceStep);
|
||||
|
||||
const double analyticNorm =
|
||||
gravity_prepared_test_utils::global_norm(
|
||||
analyticAction, communicator
|
||||
);
|
||||
const double analyticNorm = gravity_prepared_test_utils::global_norm(analyticAction, communicator);
|
||||
|
||||
const double finiteDifferenceNorm =
|
||||
gravity_prepared_test_utils::global_norm(
|
||||
finiteDifference, communicator
|
||||
);
|
||||
gravity_prepared_test_utils::global_norm(finiteDifference, communicator);
|
||||
|
||||
const double relativeError =
|
||||
gravity_prepared_test_utils::relative_error(
|
||||
analyticAction, finiteDifference, communicator
|
||||
);
|
||||
gravity_prepared_test_utils::relative_error(analyticAction, finiteDifference, communicator);
|
||||
|
||||
INFO("Base deformation scale = " << deformationScale);
|
||||
|
||||
INFO("Analytic geometry-action norm = " << analyticNorm);
|
||||
|
||||
INFO(
|
||||
"Finite-difference geometry-action norm = "
|
||||
<< finiteDifferenceNorm
|
||||
);
|
||||
INFO("Finite-difference geometry-action norm = " << finiteDifferenceNorm);
|
||||
|
||||
INFO("Geometry-action relative error = " << relativeError);
|
||||
|
||||
@@ -463,34 +386,26 @@ TEST_CASE(
|
||||
|
||||
TEST_CASE(
|
||||
"Barotropic Closure Displacement Action Is Linear In Its Direction",
|
||||
tags::barotrope &tags::closure &tags::hydro &tags::jacobian &tags::mapping
|
||||
&tags::physics &tags::unit
|
||||
tags::barotrope &tags::closure &tags::hydro &tags::jacobian &tags::mapping &tags::physics &tags::unit &tags::kernels
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
auto args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.domainMapperStateless != nullptr);
|
||||
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
|
||||
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
|
||||
|
||||
const mfem::Vector baseDensity =
|
||||
barotropic_closure_geometry_test_utils::make_base_density(f);
|
||||
const mfem::Vector baseDensity = barotropic_closure_geometry_test_utils::make_base_density(f);
|
||||
|
||||
const mfem::Vector baseEnthalpy =
|
||||
barotropic_closure_geometry_test_utils::make_base_enthalpy(f);
|
||||
const mfem::Vector baseEnthalpy = barotropic_closure_geometry_test_utils::make_base_enthalpy(f);
|
||||
|
||||
const mfem::Vector baseDisplacement =
|
||||
gravity_prepared_test_utils::make_displacement(f, 0.8);
|
||||
const mfem::Vector baseDisplacement = gravity_prepared_test_utils::make_displacement(f, 0.8);
|
||||
|
||||
const mfem::Vector firstDirection =
|
||||
gravity_prepared_test_utils::make_displacement(f, 0.4);
|
||||
const mfem::Vector firstDirection = gravity_prepared_test_utils::make_displacement(f, 0.4);
|
||||
|
||||
mfem::Vector secondDirection =
|
||||
gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.displacementFes->GetTrueVSize(), 0.91
|
||||
);
|
||||
gravity_prepared_test_utils::make_deterministic_vector(f.displacementFes->GetTrueVSize(), 0.91);
|
||||
|
||||
secondDirection *= 0.01;
|
||||
|
||||
@@ -511,29 +426,23 @@ TEST_CASE(
|
||||
mfem::Vector combinedAction;
|
||||
mfem::Vector zeroAction;
|
||||
|
||||
mean_field::operators::kernels::
|
||||
apply_barotropic_closure_displacement_action(
|
||||
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy,
|
||||
baseDisplacement, firstDirection, firstAction
|
||||
);
|
||||
mean_field::operators::kernels::apply_barotropic_closure_displacement_action(
|
||||
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy, baseDisplacement, firstDirection, firstAction
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::
|
||||
apply_barotropic_closure_displacement_action(
|
||||
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy,
|
||||
baseDisplacement, secondDirection, secondAction
|
||||
);
|
||||
mean_field::operators::kernels::apply_barotropic_closure_displacement_action(
|
||||
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy, baseDisplacement, secondDirection,
|
||||
secondAction
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::
|
||||
apply_barotropic_closure_displacement_action(
|
||||
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy,
|
||||
baseDisplacement, combinedDirection, combinedAction
|
||||
);
|
||||
mean_field::operators::kernels::apply_barotropic_closure_displacement_action(
|
||||
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy, baseDisplacement, combinedDirection,
|
||||
combinedAction
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::
|
||||
apply_barotropic_closure_displacement_action(
|
||||
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy,
|
||||
baseDisplacement, zeroDirection, zeroAction
|
||||
);
|
||||
mean_field::operators::kernels::apply_barotropic_closure_displacement_action(
|
||||
f, *f.domainMapperStateless, barotrope, baseDensity, baseEnthalpy, baseDisplacement, zeroDirection, zeroAction
|
||||
);
|
||||
|
||||
mfem::Vector expectedAction(firstAction);
|
||||
|
||||
@@ -543,15 +452,12 @@ TEST_CASE(
|
||||
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
|
||||
const double expectedNorm =
|
||||
gravity_prepared_test_utils::global_norm(expectedAction, communicator);
|
||||
const double expectedNorm = gravity_prepared_test_utils::global_norm(expectedAction, communicator);
|
||||
|
||||
const double linearityError = gravity_prepared_test_utils::relative_error(
|
||||
combinedAction, expectedAction, communicator
|
||||
);
|
||||
const double linearityError =
|
||||
gravity_prepared_test_utils::relative_error(combinedAction, expectedAction, communicator);
|
||||
|
||||
const double zeroActionNorm =
|
||||
gravity_prepared_test_utils::global_norm(zeroAction, communicator);
|
||||
const double zeroActionNorm = gravity_prepared_test_utils::global_norm(zeroAction, communicator);
|
||||
|
||||
INFO("Expected combined-action norm = " << expectedNorm);
|
||||
|
||||
@@ -568,55 +474,45 @@ TEST_CASE(
|
||||
|
||||
TEST_CASE(
|
||||
"Barotropic Closure Displacement Action Excludes Vacuum",
|
||||
tags::barotrope &tags::closure &tags::hydro &tags::mapping &tags::physics
|
||||
&tags::unit
|
||||
tags::barotrope &tags::closure &tags::hydro &tags::mapping &tags::physics &tags::unit &tags::kernels
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
auto args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.domainMapperStateless != nullptr);
|
||||
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
|
||||
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
|
||||
|
||||
const mfem::Vector stellarDensity =
|
||||
gravity_prepared_test_utils::make_domain_supported_density(f, true);
|
||||
const mfem::Vector stellarDensity = gravity_prepared_test_utils::make_domain_supported_density(f, true);
|
||||
|
||||
const mfem::Vector vacuumDensity =
|
||||
gravity_prepared_test_utils::make_domain_supported_density(f, false);
|
||||
const mfem::Vector vacuumDensity = gravity_prepared_test_utils::make_domain_supported_density(f, false);
|
||||
|
||||
mfem::Vector zeroEnthalpy(f.enthalpyFes->GetTrueVSize());
|
||||
zeroEnthalpy = 0.0;
|
||||
zeroEnthalpy = 0.0;
|
||||
|
||||
const mfem::Vector baseDisplacement =
|
||||
gravity_prepared_test_utils::make_displacement(f, 0.7);
|
||||
const mfem::Vector baseDisplacement = gravity_prepared_test_utils::make_displacement(f, 0.7);
|
||||
|
||||
const mfem::Vector displacementVariation =
|
||||
gravity_prepared_test_utils::make_displacement(f, 0.5);
|
||||
const mfem::Vector displacementVariation = gravity_prepared_test_utils::make_displacement(f, 0.5);
|
||||
|
||||
mfem::Vector stellarAction;
|
||||
mfem::Vector vacuumAction;
|
||||
|
||||
mean_field::operators::kernels::
|
||||
apply_barotropic_closure_displacement_action(
|
||||
f, *f.domainMapperStateless, barotrope, stellarDensity,
|
||||
zeroEnthalpy, baseDisplacement, displacementVariation, stellarAction
|
||||
);
|
||||
mean_field::operators::kernels::apply_barotropic_closure_displacement_action(
|
||||
f, *f.domainMapperStateless, barotrope, stellarDensity, zeroEnthalpy, baseDisplacement, displacementVariation,
|
||||
stellarAction
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::
|
||||
apply_barotropic_closure_displacement_action(
|
||||
f, *f.domainMapperStateless, barotrope, vacuumDensity, zeroEnthalpy,
|
||||
baseDisplacement, displacementVariation, vacuumAction
|
||||
);
|
||||
mean_field::operators::kernels::apply_barotropic_closure_displacement_action(
|
||||
f, *f.domainMapperStateless, barotrope, vacuumDensity, zeroEnthalpy, baseDisplacement, displacementVariation,
|
||||
vacuumAction
|
||||
);
|
||||
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
|
||||
const double stellarNorm =
|
||||
gravity_prepared_test_utils::global_norm(stellarAction, communicator);
|
||||
const double stellarNorm = gravity_prepared_test_utils::global_norm(stellarAction, communicator);
|
||||
|
||||
const double vacuumNorm =
|
||||
gravity_prepared_test_utils::global_norm(vacuumAction, communicator);
|
||||
const double vacuumNorm = gravity_prepared_test_utils::global_norm(vacuumAction, communicator);
|
||||
|
||||
INFO("Stellar geometry-action norm = " << stellarNorm);
|
||||
|
||||
|
||||
@@ -34,8 +34,7 @@ namespace hydrostatic_kernel_test_utils {
|
||||
|
||||
mfem::Vector make_enthalpy(const mean_field::fem::FEM &f) {
|
||||
mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) {
|
||||
return 1.10 + 0.035 * position(0) - 0.021 * position(1) +
|
||||
0.014 * position(2);
|
||||
return 1.10 + 0.035 * position(0) - 0.021 * position(1) + 0.014 * position(2);
|
||||
});
|
||||
|
||||
return project_scalar(*f.enthalpyFes, coefficient);
|
||||
@@ -43,8 +42,7 @@ namespace hydrostatic_kernel_test_utils {
|
||||
|
||||
mfem::Vector make_potential(const mean_field::fem::FEM &f) {
|
||||
mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) {
|
||||
return -0.72 + 0.018 * position(0) + 0.011 * position(1) -
|
||||
0.025 * position(2);
|
||||
return -0.72 + 0.018 * position(0) + 0.011 * position(1) - 0.025 * position(2);
|
||||
});
|
||||
|
||||
return project_scalar(*f.gravityPotentialFes, coefficient);
|
||||
@@ -98,23 +96,18 @@ namespace hydrostatic_kernel_test_utils {
|
||||
mfem::Vector difference(computed);
|
||||
difference -= reference;
|
||||
|
||||
return gravity_prepared_test_utils::global_norm(
|
||||
difference, communicator
|
||||
) /
|
||||
return gravity_prepared_test_utils::global_norm(difference, communicator) /
|
||||
std::max(normalization, std::numeric_limits<double>::epsilon());
|
||||
}
|
||||
|
||||
mfem::Vector
|
||||
make_vacuum_supported_potential(const mean_field::fem::FEM &f) {
|
||||
mfem::Vector make_vacuum_supported_potential(const mean_field::fem::FEM &f) {
|
||||
mfem::Vector attributeValues(f.mesh->attributes.Max());
|
||||
|
||||
attributeValues = 0.0;
|
||||
attributeValues = 0.0;
|
||||
|
||||
const int vacuumAttribute =
|
||||
f.domainMapperStateless->GetVacuumElementAttribute();
|
||||
const int vacuumAttribute = f.domainMapperStateless->GetVacuumElementAttribute();
|
||||
|
||||
for (int attributeIndex = 0; attributeIndex < f.mesh->attributes.Size();
|
||||
++attributeIndex) {
|
||||
for (int attributeIndex = 0; attributeIndex < f.mesh->attributes.Size(); ++attributeIndex) {
|
||||
const int attribute = f.mesh->attributes[attributeIndex];
|
||||
|
||||
if (attribute == vacuumAttribute) {
|
||||
@@ -144,10 +137,9 @@ namespace hydrostatic_kernel_test_utils {
|
||||
const mfem::Vector &input,
|
||||
mfem::Vector &output
|
||||
) const override {
|
||||
mean_field::operators::kernels::
|
||||
apply_hydrostatic_equilibrium_enthalpy_action(
|
||||
f_, domainMapper_, input, displacementTrue_, output
|
||||
);
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium_enthalpy_action(
|
||||
f_, domainMapper_, input, displacementTrue_, output
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -161,10 +153,9 @@ namespace hydrostatic_kernel_test_utils {
|
||||
|
||||
TEST_CASE(
|
||||
"Rigid Rotation Potential Derivative Matches Centered Differences",
|
||||
tags::barotrope &tags::hydro &tags::jacobian &tags::physics &tags::unit
|
||||
tags::barotrope &tags::hydro &tags::jacobian &tags::physics &tags::unit &tags::kernels
|
||||
) {
|
||||
const mean_field::physics::RigidRotation rotation =
|
||||
hydrostatic_kernel_test_utils::make_rotation();
|
||||
const mean_field::physics::RigidRotation rotation = hydrostatic_kernel_test_utils::make_rotation();
|
||||
|
||||
mfem::Vector position(3);
|
||||
mfem::Vector direction(3);
|
||||
@@ -186,17 +177,12 @@ TEST_CASE(
|
||||
minusPosition.Add(-epsilon, direction);
|
||||
|
||||
const double centeredDerivative =
|
||||
(rotation.potential(plusPosition) - rotation.potential(minusPosition)) /
|
||||
(2.0 * epsilon);
|
||||
(rotation.potential(plusPosition) - rotation.potential(minusPosition)) / (2.0 * epsilon);
|
||||
|
||||
const double analyticDerivative =
|
||||
rotation.potential_directional_derivative(position, direction);
|
||||
const double analyticDerivative = rotation.potential_directional_derivative(position, direction);
|
||||
|
||||
const double relativeError =
|
||||
std::abs(centeredDerivative - analyticDerivative) /
|
||||
std::max(
|
||||
std::abs(analyticDerivative), std::numeric_limits<double>::epsilon()
|
||||
);
|
||||
const double relativeError = std::abs(centeredDerivative - analyticDerivative) /
|
||||
std::max(std::abs(analyticDerivative), std::numeric_limits<double>::epsilon());
|
||||
|
||||
INFO("Rigid-rotation derivative error = " << relativeError);
|
||||
|
||||
@@ -205,42 +191,31 @@ TEST_CASE(
|
||||
|
||||
TEST_CASE(
|
||||
"Hydrostatic Residual Vanishes For A Manufactured Rotating State",
|
||||
tags::barotrope &tags::hydro &tags::integration &tags::kernels
|
||||
&tags::physics &tags::residuals
|
||||
tags::barotrope &tags::hydro &tags::integration &tags::kernels &tags::physics &tags::residuals
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
auto args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
const mean_field::physics::RigidRotation rotation =
|
||||
hydrostatic_kernel_test_utils::make_rotation();
|
||||
const mean_field::physics::RigidRotation rotation = hydrostatic_kernel_test_utils::make_rotation();
|
||||
|
||||
constexpr double bernoulliConstant = 0.73;
|
||||
constexpr double potentialValue = -0.21;
|
||||
constexpr double constantOffset = 0.40;
|
||||
constexpr double bernoulliConstant = 0.73;
|
||||
constexpr double potentialValue = -0.21;
|
||||
constexpr double constantOffset = 0.40;
|
||||
|
||||
mfem::FunctionCoefficient enthalpyCoefficient(
|
||||
[&rotation](const mfem::Vector &position) {
|
||||
return bernoulliConstant - potentialValue +
|
||||
rotation.potential(position);
|
||||
}
|
||||
);
|
||||
mfem::FunctionCoefficient enthalpyCoefficient([&rotation](const mfem::Vector &position) {
|
||||
return bernoulliConstant - potentialValue + rotation.potential(position);
|
||||
});
|
||||
|
||||
const mfem::Vector interpolatedEnthalpy =
|
||||
hydrostatic_kernel_test_utils::project_scalar(
|
||||
*f.enthalpyFes, enthalpyCoefficient
|
||||
);
|
||||
hydrostatic_kernel_test_utils::project_scalar(*f.enthalpyFes, enthalpyCoefficient);
|
||||
|
||||
const mfem::Vector potential =
|
||||
hydrostatic_kernel_test_utils::make_constant_field(
|
||||
*f.gravityPotentialFes, potentialValue
|
||||
);
|
||||
hydrostatic_kernel_test_utils::make_constant_field(*f.gravityPotentialFes, potentialValue);
|
||||
|
||||
const mfem::Vector displacement =
|
||||
gravity_prepared_test_utils::make_displacement(f, 0.0);
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.0);
|
||||
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
|
||||
/*
|
||||
* First measure the residual of the nodally interpolated
|
||||
@@ -253,35 +228,26 @@ TEST_CASE(
|
||||
mfem::Vector interpolatedReferenceResidual;
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, rotation, interpolatedEnthalpy, potential,
|
||||
displacement, bernoulliConstant, interpolatedResidual
|
||||
f, *f.domainMapperStateless, rotation, interpolatedEnthalpy, potential, displacement, bernoulliConstant,
|
||||
interpolatedResidual
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, rotation, interpolatedEnthalpy, potential,
|
||||
displacement, bernoulliConstant + constantOffset,
|
||||
interpolatedReferenceResidual
|
||||
f, *f.domainMapperStateless, rotation, interpolatedEnthalpy, potential, displacement,
|
||||
bernoulliConstant + constantOffset, interpolatedReferenceResidual
|
||||
);
|
||||
|
||||
const double interpolatedResidualNorm =
|
||||
gravity_prepared_test_utils::global_norm(
|
||||
interpolatedResidual, communicator
|
||||
);
|
||||
gravity_prepared_test_utils::global_norm(interpolatedResidual, communicator);
|
||||
|
||||
const double interpolatedReferenceNorm =
|
||||
gravity_prepared_test_utils::global_norm(
|
||||
interpolatedReferenceResidual, communicator
|
||||
);
|
||||
gravity_prepared_test_utils::global_norm(interpolatedReferenceResidual, communicator);
|
||||
|
||||
REQUIRE(interpolatedReferenceNorm > 1.0e-12);
|
||||
|
||||
const double representationFloor =
|
||||
interpolatedResidualNorm / interpolatedReferenceNorm;
|
||||
const double representationFloor = interpolatedResidualNorm / interpolatedReferenceNorm;
|
||||
|
||||
INFO(
|
||||
"Interpolated rotating-state residual norm = "
|
||||
<< interpolatedResidualNorm
|
||||
);
|
||||
INFO("Interpolated rotating-state residual norm = " << interpolatedResidualNorm);
|
||||
|
||||
INFO(
|
||||
"Interpolated rotating-state relative "
|
||||
@@ -310,8 +276,9 @@ TEST_CASE(
|
||||
* side is in the range of M_h. Starting CG from zero keeps the
|
||||
* iteration in the active stellar subspace.
|
||||
*/
|
||||
hydrostatic_kernel_test_utils::HydrostaticEnthalpyMassOperator
|
||||
enthalpyMassOperator(f, *f.domainMapperStateless, displacement);
|
||||
hydrostatic_kernel_test_utils::HydrostaticEnthalpyMassOperator enthalpyMassOperator(
|
||||
f, *f.domainMapperStateless, displacement
|
||||
);
|
||||
|
||||
mfem::Vector correctionRightHandSide(interpolatedResidual);
|
||||
|
||||
@@ -332,20 +299,11 @@ TEST_CASE(
|
||||
|
||||
projectionSolver.Mult(correctionRightHandSide, enthalpyCorrection);
|
||||
|
||||
INFO(
|
||||
"Discrete-equilibrium projection converged = "
|
||||
<< projectionSolver.GetConverged()
|
||||
);
|
||||
INFO("Discrete-equilibrium projection converged = " << projectionSolver.GetConverged());
|
||||
|
||||
INFO(
|
||||
"Discrete-equilibrium projection iterations = "
|
||||
<< projectionSolver.GetNumIterations()
|
||||
);
|
||||
INFO("Discrete-equilibrium projection iterations = " << projectionSolver.GetNumIterations());
|
||||
|
||||
INFO(
|
||||
"Discrete-equilibrium projection final norm = "
|
||||
<< projectionSolver.GetFinalNorm()
|
||||
);
|
||||
INFO("Discrete-equilibrium projection final norm = " << projectionSolver.GetFinalNorm());
|
||||
|
||||
REQUIRE(projectionSolver.GetConverged());
|
||||
|
||||
@@ -356,9 +314,7 @@ TEST_CASE(
|
||||
correctionEquationResidual -= correctionRightHandSide;
|
||||
|
||||
const double correctionEquationNorm =
|
||||
gravity_prepared_test_utils::global_norm(
|
||||
correctionEquationResidual, communicator
|
||||
);
|
||||
gravity_prepared_test_utils::global_norm(correctionEquationResidual, communicator);
|
||||
|
||||
INFO(
|
||||
"Discrete-equilibrium correction-equation "
|
||||
@@ -366,10 +322,7 @@ TEST_CASE(
|
||||
<< correctionEquationNorm
|
||||
);
|
||||
|
||||
CHECK(
|
||||
correctionEquationNorm <=
|
||||
std::max(5.0e-12 * interpolatedResidualNorm, 5.0e-15)
|
||||
);
|
||||
CHECK(correctionEquationNorm <= std::max(5.0e-12 * interpolatedResidualNorm, 5.0e-15));
|
||||
|
||||
mfem::Vector discreteEnthalpy(interpolatedEnthalpy);
|
||||
|
||||
@@ -379,25 +332,20 @@ TEST_CASE(
|
||||
mfem::Vector referenceResidual;
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, rotation, discreteEnthalpy, potential,
|
||||
displacement, bernoulliConstant, exactResidual
|
||||
f, *f.domainMapperStateless, rotation, discreteEnthalpy, potential, displacement, bernoulliConstant,
|
||||
exactResidual
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, rotation, discreteEnthalpy, potential,
|
||||
displacement, bernoulliConstant + constantOffset, referenceResidual
|
||||
f, *f.domainMapperStateless, rotation, discreteEnthalpy, potential, displacement,
|
||||
bernoulliConstant + constantOffset, referenceResidual
|
||||
);
|
||||
|
||||
const double exactNorm =
|
||||
gravity_prepared_test_utils::global_norm(exactResidual, communicator);
|
||||
const double exactNorm = gravity_prepared_test_utils::global_norm(exactResidual, communicator);
|
||||
|
||||
const double referenceNorm = gravity_prepared_test_utils::global_norm(
|
||||
referenceResidual, communicator
|
||||
);
|
||||
const double referenceNorm = gravity_prepared_test_utils::global_norm(referenceResidual, communicator);
|
||||
|
||||
const double correctionNorm = gravity_prepared_test_utils::global_norm(
|
||||
enthalpyCorrection, communicator
|
||||
);
|
||||
const double correctionNorm = gravity_prepared_test_utils::global_norm(enthalpyCorrection, communicator);
|
||||
|
||||
INFO("Enthalpy representation correction norm = " << correctionNorm);
|
||||
|
||||
@@ -412,43 +360,31 @@ TEST_CASE(
|
||||
|
||||
TEST_CASE(
|
||||
"Exact Constant Hydrostatic Equilibrium Remains Zero Under Deformation",
|
||||
tags::barotrope &tags::hydro &tags::integration &tags::jacobian
|
||||
&tags::kernels &tags::mapping &tags::physics
|
||||
tags::barotrope &tags::hydro &tags::integration &tags::jacobian &tags::kernels &tags::mapping &tags::physics
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
auto args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
const mean_field::physics::RigidRotation rotation =
|
||||
hydrostatic_kernel_test_utils::make_zero_rotation();
|
||||
const mean_field::physics::RigidRotation rotation = hydrostatic_kernel_test_utils::make_zero_rotation();
|
||||
|
||||
constexpr double enthalpyValue = 1.20;
|
||||
constexpr double potentialValue = -0.35;
|
||||
constexpr double enthalpyValue = 1.20;
|
||||
constexpr double potentialValue = -0.35;
|
||||
|
||||
constexpr double bernoulliConstant = enthalpyValue + potentialValue;
|
||||
constexpr double bernoulliConstant = enthalpyValue + potentialValue;
|
||||
|
||||
const mfem::Vector enthalpy =
|
||||
hydrostatic_kernel_test_utils::make_constant_field(
|
||||
*f.enthalpyFes, enthalpyValue
|
||||
);
|
||||
const mfem::Vector enthalpy = hydrostatic_kernel_test_utils::make_constant_field(*f.enthalpyFes, enthalpyValue);
|
||||
|
||||
const mfem::Vector potential =
|
||||
hydrostatic_kernel_test_utils::make_constant_field(
|
||||
*f.gravityPotentialFes, potentialValue
|
||||
);
|
||||
hydrostatic_kernel_test_utils::make_constant_field(*f.gravityPotentialFes, potentialValue);
|
||||
|
||||
const mfem::Vector displacementVariation =
|
||||
gravity_prepared_test_utils::make_displacement(f, 0.67);
|
||||
const mfem::Vector displacementVariation = gravity_prepared_test_utils::make_displacement(f, 0.67);
|
||||
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
|
||||
for (const double deformationScale : {0.0, 0.5, 1.0}) {
|
||||
DYNAMIC_SECTION("Deformation scale = " << deformationScale) {
|
||||
const mfem::Vector displacement =
|
||||
gravity_prepared_test_utils::make_displacement(
|
||||
f, deformationScale
|
||||
);
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, deformationScale);
|
||||
|
||||
mfem::Vector exactResidual;
|
||||
mfem::Vector referenceResidual;
|
||||
@@ -456,48 +392,35 @@ TEST_CASE(
|
||||
mfem::Vector referenceGeometryAction;
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, potential,
|
||||
displacement, bernoulliConstant, exactResidual
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant,
|
||||
exactResidual
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, potential,
|
||||
displacement, bernoulliConstant + 0.50, referenceResidual
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant + 0.50,
|
||||
referenceResidual
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::
|
||||
apply_hydrostatic_equilibrium_displacement_action(
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, potential,
|
||||
displacement, bernoulliConstant, displacementVariation,
|
||||
exactGeometryAction
|
||||
);
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action(
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant,
|
||||
displacementVariation, exactGeometryAction
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::
|
||||
apply_hydrostatic_equilibrium_displacement_action(
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, potential,
|
||||
displacement, bernoulliConstant + 0.50,
|
||||
displacementVariation, referenceGeometryAction
|
||||
);
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action(
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant + 0.50,
|
||||
displacementVariation, referenceGeometryAction
|
||||
);
|
||||
|
||||
const double exactResidualNorm =
|
||||
gravity_prepared_test_utils::global_norm(
|
||||
exactResidual, communicator
|
||||
);
|
||||
const double exactResidualNorm = gravity_prepared_test_utils::global_norm(exactResidual, communicator);
|
||||
|
||||
const double referenceResidualNorm =
|
||||
gravity_prepared_test_utils::global_norm(
|
||||
referenceResidual, communicator
|
||||
);
|
||||
gravity_prepared_test_utils::global_norm(referenceResidual, communicator);
|
||||
|
||||
const double exactGeometryNorm =
|
||||
gravity_prepared_test_utils::global_norm(
|
||||
exactGeometryAction, communicator
|
||||
);
|
||||
gravity_prepared_test_utils::global_norm(exactGeometryAction, communicator);
|
||||
|
||||
const double referenceGeometryNorm =
|
||||
gravity_prepared_test_utils::global_norm(
|
||||
referenceGeometryAction, communicator
|
||||
);
|
||||
gravity_prepared_test_utils::global_norm(referenceGeometryAction, communicator);
|
||||
|
||||
REQUIRE(referenceResidualNorm > 1.0e-12);
|
||||
|
||||
@@ -512,64 +435,49 @@ TEST_CASE(
|
||||
|
||||
TEST_CASE(
|
||||
"Hydrostatic Equilibrium Excludes Vacuum Elements",
|
||||
tags::barotrope &tags::hydro &tags::kernels &tags::mapping &tags::physics
|
||||
&tags::unit
|
||||
tags::barotrope &tags::hydro &tags::kernels &tags::mapping &tags::physics &tags::unit
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
auto args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
const mean_field::physics::RigidRotation rotation =
|
||||
hydrostatic_kernel_test_utils::make_zero_rotation();
|
||||
const mean_field::physics::RigidRotation rotation = hydrostatic_kernel_test_utils::make_zero_rotation();
|
||||
|
||||
const mfem::Vector zeroEnthalpy(f.enthalpyFes->GetTrueVSize());
|
||||
|
||||
mfem::Vector enthalpy(zeroEnthalpy);
|
||||
enthalpy = 0.0;
|
||||
enthalpy = 0.0;
|
||||
|
||||
const mfem::Vector vacuumPotential =
|
||||
hydrostatic_kernel_test_utils::make_vacuum_supported_potential(f);
|
||||
const mfem::Vector vacuumPotential = hydrostatic_kernel_test_utils::make_vacuum_supported_potential(f);
|
||||
|
||||
const mfem::Vector stellarPotential =
|
||||
hydrostatic_kernel_test_utils::make_constant_field(
|
||||
*f.gravityPotentialFes, 1.0
|
||||
);
|
||||
hydrostatic_kernel_test_utils::make_constant_field(*f.gravityPotentialFes, 1.0);
|
||||
|
||||
const mfem::Vector displacement =
|
||||
gravity_prepared_test_utils::make_displacement(f, 1.0);
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 1.0);
|
||||
|
||||
mfem::Vector residual;
|
||||
mfem::Vector vacuumAction;
|
||||
mfem::Vector stellarAction;
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, vacuumPotential,
|
||||
displacement, 0.0, residual
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, vacuumPotential, displacement, 0.0, residual
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::
|
||||
apply_hydrostatic_equilibrium_potential_action(
|
||||
f, *f.domainMapperStateless, vacuumPotential, displacement,
|
||||
vacuumAction
|
||||
);
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium_potential_action(
|
||||
f, *f.domainMapperStateless, vacuumPotential, displacement, vacuumAction
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::
|
||||
apply_hydrostatic_equilibrium_potential_action(
|
||||
f, *f.domainMapperStateless, stellarPotential, displacement,
|
||||
stellarAction
|
||||
);
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium_potential_action(
|
||||
f, *f.domainMapperStateless, stellarPotential, displacement, stellarAction
|
||||
);
|
||||
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
|
||||
const double residualNorm =
|
||||
gravity_prepared_test_utils::global_norm(residual, communicator);
|
||||
const double residualNorm = gravity_prepared_test_utils::global_norm(residual, communicator);
|
||||
|
||||
const double vacuumActionNorm =
|
||||
gravity_prepared_test_utils::global_norm(vacuumAction, communicator);
|
||||
const double vacuumActionNorm = gravity_prepared_test_utils::global_norm(vacuumAction, communicator);
|
||||
|
||||
const double stellarActionNorm =
|
||||
gravity_prepared_test_utils::global_norm(stellarAction, communicator);
|
||||
const double stellarActionNorm = gravity_prepared_test_utils::global_norm(stellarAction, communicator);
|
||||
|
||||
REQUIRE(stellarActionNorm > 1.0e-12);
|
||||
|
||||
@@ -580,40 +488,28 @@ TEST_CASE(
|
||||
|
||||
TEST_CASE(
|
||||
"Hydrostatic Jacobian Matches Blocks And Centered Differences",
|
||||
tags::barotrope &tags::hydro &tags::integration &tags::jacobian
|
||||
&tags::kernels &tags::mapping &tags::physics
|
||||
tags::barotrope &tags::hydro &tags::integration &tags::jacobian &tags::kernels &tags::mapping &tags::physics
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
auto args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
const mean_field::physics::RigidRotation rotation =
|
||||
hydrostatic_kernel_test_utils::make_rotation();
|
||||
const mean_field::physics::RigidRotation rotation = hydrostatic_kernel_test_utils::make_rotation();
|
||||
|
||||
const mfem::Vector enthalpy =
|
||||
hydrostatic_kernel_test_utils::make_enthalpy(f);
|
||||
const mfem::Vector enthalpy = hydrostatic_kernel_test_utils::make_enthalpy(f);
|
||||
|
||||
const mfem::Vector potential =
|
||||
hydrostatic_kernel_test_utils::make_potential(f);
|
||||
const mfem::Vector potential = hydrostatic_kernel_test_utils::make_potential(f);
|
||||
|
||||
const mfem::Vector displacement =
|
||||
gravity_prepared_test_utils::make_displacement(f, 1.0);
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 1.0);
|
||||
|
||||
const mfem::Vector enthalpyVariation =
|
||||
gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.enthalpyFes->GetTrueVSize(), 0.23
|
||||
);
|
||||
gravity_prepared_test_utils::make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), 0.23);
|
||||
|
||||
const mfem::Vector potentialVariation =
|
||||
gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.gravityPotentialFes->GetTrueVSize(), 0.47
|
||||
);
|
||||
gravity_prepared_test_utils::make_deterministic_vector(f.gravityPotentialFes->GetTrueVSize(), 0.47);
|
||||
|
||||
const mfem::Vector displacementVariation =
|
||||
gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.displacementFes->GetTrueVSize(), 0.71
|
||||
);
|
||||
gravity_prepared_test_utils::make_deterministic_vector(f.displacementFes->GetTrueVSize(), 0.71);
|
||||
|
||||
constexpr double bernoulliConstant = 0.41;
|
||||
constexpr double constantVariation = -0.37;
|
||||
@@ -625,35 +521,26 @@ TEST_CASE(
|
||||
mfem::Vector displacementAction;
|
||||
mfem::Vector completeAction;
|
||||
|
||||
mean_field::operators::kernels::
|
||||
apply_hydrostatic_equilibrium_enthalpy_action(
|
||||
f, *f.domainMapperStateless, enthalpyVariation, displacement,
|
||||
enthalpyAction
|
||||
);
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium_enthalpy_action(
|
||||
f, *f.domainMapperStateless, enthalpyVariation, displacement, enthalpyAction
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::
|
||||
apply_hydrostatic_equilibrium_potential_action(
|
||||
f, *f.domainMapperStateless, potentialVariation, displacement,
|
||||
potentialAction
|
||||
);
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium_potential_action(
|
||||
f, *f.domainMapperStateless, potentialVariation, displacement, potentialAction
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::
|
||||
apply_hydrostatic_equilibrium_constant_action(
|
||||
f, *f.domainMapperStateless, constantVariation, displacement,
|
||||
constantAction
|
||||
);
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium_constant_action(
|
||||
f, *f.domainMapperStateless, constantVariation, displacement, constantAction
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::
|
||||
apply_hydrostatic_equilibrium_displacement_action(
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, potential,
|
||||
displacement, bernoulliConstant, displacementVariation,
|
||||
displacementAction
|
||||
);
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action(
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant,
|
||||
displacementVariation, displacementAction
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium_action(
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, potential,
|
||||
displacement, bernoulliConstant, enthalpyVariation, potentialVariation,
|
||||
constantVariation, displacementVariation, completeAction
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant, enthalpyVariation,
|
||||
potentialVariation, constantVariation, displacementVariation, completeAction
|
||||
);
|
||||
|
||||
mfem::Vector blockAction(enthalpyAction);
|
||||
@@ -663,25 +550,21 @@ TEST_CASE(
|
||||
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
|
||||
const double blockError = gravity_prepared_test_utils::relative_error(
|
||||
completeAction, blockAction, communicator
|
||||
);
|
||||
const double blockError = gravity_prepared_test_utils::relative_error(completeAction, blockAction, communicator);
|
||||
|
||||
INFO("Hydrostatic block reconstruction error = " << blockError);
|
||||
|
||||
CHECK(blockError < 5.0e-13);
|
||||
|
||||
auto evaluate_residual = [&f, &rotation](
|
||||
const mfem::Vector &trialEnthalpy,
|
||||
const mfem::Vector &trialPotential,
|
||||
const mfem::Vector &trialDisplacement,
|
||||
const double trialConstant
|
||||
const mfem::Vector &trialEnthalpy, const mfem::Vector &trialPotential,
|
||||
const mfem::Vector &trialDisplacement, const double trialConstant
|
||||
) {
|
||||
mfem::Vector residual;
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, rotation, trialEnthalpy,
|
||||
trialPotential, trialDisplacement, trialConstant, residual
|
||||
f, *f.domainMapperStateless, rotation, trialEnthalpy, trialPotential, trialDisplacement, trialConstant,
|
||||
residual
|
||||
);
|
||||
|
||||
return residual;
|
||||
@@ -694,16 +577,10 @@ TEST_CASE(
|
||||
|
||||
minusEnthalpy.Add(-epsilon, enthalpyVariation);
|
||||
|
||||
const mfem::Vector enthalpyDifference =
|
||||
hydrostatic_kernel_test_utils::centered_difference(
|
||||
evaluate_residual(
|
||||
plusEnthalpy, potential, displacement, bernoulliConstant
|
||||
),
|
||||
evaluate_residual(
|
||||
minusEnthalpy, potential, displacement, bernoulliConstant
|
||||
),
|
||||
epsilon
|
||||
);
|
||||
const mfem::Vector enthalpyDifference = hydrostatic_kernel_test_utils::centered_difference(
|
||||
evaluate_residual(plusEnthalpy, potential, displacement, bernoulliConstant),
|
||||
evaluate_residual(minusEnthalpy, potential, displacement, bernoulliConstant), epsilon
|
||||
);
|
||||
|
||||
mfem::Vector plusPotential(potential);
|
||||
mfem::Vector minusPotential(potential);
|
||||
@@ -712,29 +589,15 @@ TEST_CASE(
|
||||
|
||||
minusPotential.Add(-epsilon, potentialVariation);
|
||||
|
||||
const mfem::Vector potentialDifference =
|
||||
hydrostatic_kernel_test_utils::centered_difference(
|
||||
evaluate_residual(
|
||||
enthalpy, plusPotential, displacement, bernoulliConstant
|
||||
),
|
||||
evaluate_residual(
|
||||
enthalpy, minusPotential, displacement, bernoulliConstant
|
||||
),
|
||||
epsilon
|
||||
);
|
||||
const mfem::Vector potentialDifference = hydrostatic_kernel_test_utils::centered_difference(
|
||||
evaluate_residual(enthalpy, plusPotential, displacement, bernoulliConstant),
|
||||
evaluate_residual(enthalpy, minusPotential, displacement, bernoulliConstant), epsilon
|
||||
);
|
||||
|
||||
const mfem::Vector constantDifference =
|
||||
hydrostatic_kernel_test_utils::centered_difference(
|
||||
evaluate_residual(
|
||||
enthalpy, potential, displacement,
|
||||
bernoulliConstant + epsilon * constantVariation
|
||||
),
|
||||
evaluate_residual(
|
||||
enthalpy, potential, displacement,
|
||||
bernoulliConstant - epsilon * constantVariation
|
||||
),
|
||||
epsilon
|
||||
);
|
||||
const mfem::Vector constantDifference = hydrostatic_kernel_test_utils::centered_difference(
|
||||
evaluate_residual(enthalpy, potential, displacement, bernoulliConstant + epsilon * constantVariation),
|
||||
evaluate_residual(enthalpy, potential, displacement, bernoulliConstant - epsilon * constantVariation), epsilon
|
||||
);
|
||||
|
||||
mfem::Vector plusDisplacement(displacement);
|
||||
mfem::Vector minusDisplacement(displacement);
|
||||
@@ -743,33 +606,22 @@ TEST_CASE(
|
||||
|
||||
minusDisplacement.Add(-epsilon, displacementVariation);
|
||||
|
||||
const mfem::Vector displacementDifference =
|
||||
hydrostatic_kernel_test_utils::centered_difference(
|
||||
evaluate_residual(
|
||||
enthalpy, potential, plusDisplacement, bernoulliConstant
|
||||
),
|
||||
evaluate_residual(
|
||||
enthalpy, potential, minusDisplacement, bernoulliConstant
|
||||
),
|
||||
epsilon
|
||||
);
|
||||
|
||||
const double enthalpyError = gravity_prepared_test_utils::relative_error(
|
||||
enthalpyAction, enthalpyDifference, communicator
|
||||
const mfem::Vector displacementDifference = hydrostatic_kernel_test_utils::centered_difference(
|
||||
evaluate_residual(enthalpy, potential, plusDisplacement, bernoulliConstant),
|
||||
evaluate_residual(enthalpy, potential, minusDisplacement, bernoulliConstant), epsilon
|
||||
);
|
||||
|
||||
const double potentialError = gravity_prepared_test_utils::relative_error(
|
||||
potentialAction, potentialDifference, communicator
|
||||
);
|
||||
const double enthalpyError =
|
||||
gravity_prepared_test_utils::relative_error(enthalpyAction, enthalpyDifference, communicator);
|
||||
|
||||
const double constantError = gravity_prepared_test_utils::relative_error(
|
||||
constantAction, constantDifference, communicator
|
||||
);
|
||||
const double potentialError =
|
||||
gravity_prepared_test_utils::relative_error(potentialAction, potentialDifference, communicator);
|
||||
|
||||
const double constantError =
|
||||
gravity_prepared_test_utils::relative_error(constantAction, constantDifference, communicator);
|
||||
|
||||
const double displacementError =
|
||||
gravity_prepared_test_utils::relative_error(
|
||||
displacementAction, displacementDifference, communicator
|
||||
);
|
||||
gravity_prepared_test_utils::relative_error(displacementAction, displacementDifference, communicator);
|
||||
|
||||
INFO("Hydrostatic enthalpy-block error = " << enthalpyError);
|
||||
|
||||
@@ -803,35 +655,26 @@ TEST_CASE(
|
||||
|
||||
combinedMinusDisplacement.Add(-epsilon, displacementVariation);
|
||||
|
||||
const mfem::Vector combinedDifference =
|
||||
hydrostatic_kernel_test_utils::centered_difference(
|
||||
evaluate_residual(
|
||||
combinedPlusEnthalpy, combinedPlusPotential,
|
||||
combinedPlusDisplacement,
|
||||
bernoulliConstant + epsilon * constantVariation
|
||||
),
|
||||
evaluate_residual(
|
||||
combinedMinusEnthalpy, combinedMinusPotential,
|
||||
combinedMinusDisplacement,
|
||||
bernoulliConstant - epsilon * constantVariation
|
||||
),
|
||||
epsilon
|
||||
);
|
||||
const mfem::Vector combinedDifference = hydrostatic_kernel_test_utils::centered_difference(
|
||||
evaluate_residual(
|
||||
combinedPlusEnthalpy, combinedPlusPotential, combinedPlusDisplacement,
|
||||
bernoulliConstant + epsilon * constantVariation
|
||||
),
|
||||
evaluate_residual(
|
||||
combinedMinusEnthalpy, combinedMinusPotential, combinedMinusDisplacement,
|
||||
bernoulliConstant - epsilon * constantVariation
|
||||
),
|
||||
epsilon
|
||||
);
|
||||
|
||||
const double blockNormSum =
|
||||
gravity_prepared_test_utils::global_norm(enthalpyAction, communicator) +
|
||||
gravity_prepared_test_utils::global_norm(
|
||||
potentialAction, communicator
|
||||
) +
|
||||
gravity_prepared_test_utils::global_norm(constantAction, communicator) +
|
||||
gravity_prepared_test_utils::global_norm(
|
||||
displacementAction, communicator
|
||||
);
|
||||
const double blockNormSum = gravity_prepared_test_utils::global_norm(enthalpyAction, communicator) +
|
||||
gravity_prepared_test_utils::global_norm(potentialAction, communicator) +
|
||||
gravity_prepared_test_utils::global_norm(constantAction, communicator) +
|
||||
gravity_prepared_test_utils::global_norm(displacementAction, communicator);
|
||||
|
||||
const double simultaneousError =
|
||||
hydrostatic_kernel_test_utils::sum_normalized_error(
|
||||
completeAction, combinedDifference, blockNormSum, communicator
|
||||
);
|
||||
const double simultaneousError = hydrostatic_kernel_test_utils::sum_normalized_error(
|
||||
completeAction, combinedDifference, blockNormSum, communicator
|
||||
);
|
||||
|
||||
INFO("Hydrostatic simultaneous Jacobian error = " << simultaneousError);
|
||||
|
||||
@@ -840,75 +683,58 @@ TEST_CASE(
|
||||
|
||||
TEST_CASE(
|
||||
"Hydrostatic Displacement Action Is Linear In Its Direction",
|
||||
tags::barotrope &tags::hydro &tags::integration &tags::jacobian
|
||||
&tags::mapping &tags::physics &tags::unit
|
||||
tags::barotrope &tags::hydro &tags::integration &tags::jacobian &tags::mapping &tags::physics &tags::unit
|
||||
&tags::kernels
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
auto args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
const mean_field::physics::RigidRotation rotation =
|
||||
hydrostatic_kernel_test_utils::make_rotation();
|
||||
const mean_field::physics::RigidRotation rotation = hydrostatic_kernel_test_utils::make_rotation();
|
||||
|
||||
const mfem::Vector enthalpy =
|
||||
hydrostatic_kernel_test_utils::make_enthalpy(f);
|
||||
const mfem::Vector enthalpy = hydrostatic_kernel_test_utils::make_enthalpy(f);
|
||||
|
||||
const mfem::Vector potential =
|
||||
hydrostatic_kernel_test_utils::make_potential(f);
|
||||
const mfem::Vector potential = hydrostatic_kernel_test_utils::make_potential(f);
|
||||
|
||||
const mfem::Vector displacement =
|
||||
gravity_prepared_test_utils::make_displacement(f, 1.0);
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 1.0);
|
||||
|
||||
const mfem::Vector firstDirection =
|
||||
gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.displacementFes->GetTrueVSize(), 0.31
|
||||
);
|
||||
gravity_prepared_test_utils::make_deterministic_vector(f.displacementFes->GetTrueVSize(), 0.31);
|
||||
|
||||
const mfem::Vector secondDirection =
|
||||
gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.displacementFes->GetTrueVSize(), 0.83
|
||||
);
|
||||
gravity_prepared_test_utils::make_deterministic_vector(f.displacementFes->GetTrueVSize(), 0.83);
|
||||
|
||||
constexpr double firstScale = 0.43;
|
||||
constexpr double secondScale = -0.29;
|
||||
constexpr double bernoulliConstant = 0.41;
|
||||
|
||||
const mfem::Vector combinedDirection =
|
||||
gravity_prepared_test_utils::linear_combination(
|
||||
firstDirection, firstScale, secondDirection, secondScale
|
||||
);
|
||||
gravity_prepared_test_utils::linear_combination(firstDirection, firstScale, secondDirection, secondScale);
|
||||
|
||||
mfem::Vector firstAction;
|
||||
mfem::Vector secondAction;
|
||||
mfem::Vector combinedAction;
|
||||
|
||||
mean_field::operators::kernels::
|
||||
apply_hydrostatic_equilibrium_displacement_action(
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, potential,
|
||||
displacement, bernoulliConstant, firstDirection, firstAction
|
||||
);
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action(
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant, firstDirection,
|
||||
firstAction
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::
|
||||
apply_hydrostatic_equilibrium_displacement_action(
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, potential,
|
||||
displacement, bernoulliConstant, secondDirection, secondAction
|
||||
);
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action(
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant, secondDirection,
|
||||
secondAction
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::
|
||||
apply_hydrostatic_equilibrium_displacement_action(
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, potential,
|
||||
displacement, bernoulliConstant, combinedDirection, combinedAction
|
||||
);
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action(
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant, combinedDirection,
|
||||
combinedAction
|
||||
);
|
||||
|
||||
const mfem::Vector expectedAction =
|
||||
gravity_prepared_test_utils::linear_combination(
|
||||
firstAction, firstScale, secondAction, secondScale
|
||||
);
|
||||
gravity_prepared_test_utils::linear_combination(firstAction, firstScale, secondAction, secondScale);
|
||||
|
||||
const double linearityError = gravity_prepared_test_utils::relative_error(
|
||||
combinedAction, expectedAction, f.mesh->GetComm()
|
||||
);
|
||||
const double linearityError =
|
||||
gravity_prepared_test_utils::relative_error(combinedAction, expectedAction, f.mesh->GetComm());
|
||||
|
||||
INFO("Hydrostatic displacement-linearity error = " << linearityError);
|
||||
|
||||
@@ -917,13 +743,11 @@ TEST_CASE(
|
||||
|
||||
TEST_CASE(
|
||||
"Hydrostatic Residual Is Translationally Invariant On Deformed Geometry",
|
||||
tags::barotrope &tags::hydro &tags::integration &tags::kernels
|
||||
&tags::mapping &tags::physics &tags::residuals
|
||||
tags::barotrope &tags::hydro &tags::integration &tags::kernels &tags::mapping &tags::physics &tags::residuals
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
auto args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
mfem::Vector angularVelocity(3);
|
||||
|
||||
@@ -946,33 +770,25 @@ TEST_CASE(
|
||||
mfem::Vector translatedCenter(center);
|
||||
translatedCenter += translation;
|
||||
|
||||
const mean_field::physics::RigidRotation baseRotation(
|
||||
angularVelocity, center
|
||||
);
|
||||
const mean_field::physics::RigidRotation baseRotation(angularVelocity, center);
|
||||
|
||||
const mean_field::physics::RigidRotation translatedRotation(
|
||||
angularVelocity, translatedCenter
|
||||
);
|
||||
const mean_field::physics::RigidRotation translatedRotation(angularVelocity, translatedCenter);
|
||||
|
||||
const mfem::Vector enthalpy =
|
||||
hydrostatic_kernel_test_utils::make_enthalpy(f);
|
||||
const mfem::Vector enthalpy = hydrostatic_kernel_test_utils::make_enthalpy(f);
|
||||
|
||||
const mfem::Vector potential =
|
||||
hydrostatic_kernel_test_utils::make_potential(f);
|
||||
const mfem::Vector potential = hydrostatic_kernel_test_utils::make_potential(f);
|
||||
|
||||
/*
|
||||
* Use a nontrivially deformed base state so this checks rotation
|
||||
* and mapped geometry simultaneously. The comparison state adds
|
||||
* an exactly representable rigid translation to that deformation.
|
||||
*/
|
||||
const mfem::Vector baseDisplacement =
|
||||
gravity_prepared_test_utils::make_displacement(f, 0.73);
|
||||
const mfem::Vector baseDisplacement = gravity_prepared_test_utils::make_displacement(f, 0.73);
|
||||
|
||||
mfem::ParGridFunction translationField(f.displacementFes.get());
|
||||
|
||||
mfem::VectorFunctionCoefficient translationCoefficient(
|
||||
f.mesh->Dimension(),
|
||||
[&translation](const mfem::Vector &, mfem::Vector &value) {
|
||||
f.mesh->Dimension(), [&translation](const mfem::Vector &, mfem::Vector &value) {
|
||||
value.SetSize(translation.Size());
|
||||
value = translation;
|
||||
}
|
||||
@@ -994,13 +810,13 @@ TEST_CASE(
|
||||
mfem::Vector untranslatedCenterResidual;
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, baseRotation, enthalpy, potential,
|
||||
baseDisplacement, bernoulliConstant, baseResidual
|
||||
f, *f.domainMapperStateless, baseRotation, enthalpy, potential, baseDisplacement, bernoulliConstant,
|
||||
baseResidual
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, translatedRotation, enthalpy, potential,
|
||||
translatedDisplacement, bernoulliConstant, translatedResidual
|
||||
f, *f.domainMapperStateless, translatedRotation, enthalpy, potential, translatedDisplacement, bernoulliConstant,
|
||||
translatedResidual
|
||||
);
|
||||
|
||||
/*
|
||||
@@ -1008,43 +824,29 @@ TEST_CASE(
|
||||
* center fixed. This must not agree with the covariant result.
|
||||
*/
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, baseRotation, enthalpy, potential,
|
||||
translatedDisplacement, bernoulliConstant, untranslatedCenterResidual
|
||||
f, *f.domainMapperStateless, baseRotation, enthalpy, potential, translatedDisplacement, bernoulliConstant,
|
||||
untranslatedCenterResidual
|
||||
);
|
||||
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
|
||||
const double baseResidualNorm =
|
||||
gravity_prepared_test_utils::global_norm(baseResidual, communicator);
|
||||
const double baseResidualNorm = gravity_prepared_test_utils::global_norm(baseResidual, communicator);
|
||||
|
||||
const double translatedResidualNorm =
|
||||
gravity_prepared_test_utils::global_norm(
|
||||
translatedResidual, communicator
|
||||
);
|
||||
const double translatedResidualNorm = gravity_prepared_test_utils::global_norm(translatedResidual, communicator);
|
||||
|
||||
const double translationInvarianceError =
|
||||
gravity_prepared_test_utils::relative_error(
|
||||
translatedResidual, baseResidual, communicator
|
||||
);
|
||||
gravity_prepared_test_utils::relative_error(translatedResidual, baseResidual, communicator);
|
||||
|
||||
const double fixedCenterDifference =
|
||||
gravity_prepared_test_utils::relative_error(
|
||||
untranslatedCenterResidual, translatedResidual, communicator
|
||||
);
|
||||
gravity_prepared_test_utils::relative_error(untranslatedCenterResidual, translatedResidual, communicator);
|
||||
|
||||
INFO("Base deformed hydrostatic residual norm = " << baseResidualNorm);
|
||||
|
||||
INFO("Translated hydrostatic residual norm = " << translatedResidualNorm);
|
||||
|
||||
INFO(
|
||||
"Mapped-rotation translation invariance error = "
|
||||
<< translationInvarianceError
|
||||
);
|
||||
INFO("Mapped-rotation translation invariance error = " << translationInvarianceError);
|
||||
|
||||
INFO(
|
||||
"Relative change with untranslated rotation center = "
|
||||
<< fixedCenterDifference
|
||||
);
|
||||
INFO("Relative change with untranslated rotation center = " << fixedCenterDifference);
|
||||
|
||||
REQUIRE(baseResidualNorm > 1.0e-12);
|
||||
REQUIRE(translatedResidualNorm > 1.0e-12);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <array>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
@@ -19,44 +19,34 @@ namespace pressure_force_kernel_test_utils {
|
||||
for (int index = 0; index < size; ++index) {
|
||||
const double position = static_cast<double>(index + 1);
|
||||
|
||||
vector(index) = 0.71 + 0.19 * std::sin(0.31 * position + phase) +
|
||||
0.08 * std::cos(0.17 * position - 0.5 * phase);
|
||||
vector(index) =
|
||||
0.71 + 0.19 * std::sin(0.31 * position + phase) + 0.08 * std::cos(0.17 * position - 0.5 * phase);
|
||||
}
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector
|
||||
make_zero_displacement(const mean_field::fem::FEM &f) {
|
||||
[[nodiscard]] mfem::Vector make_zero_displacement(const mean_field::fem::FEM &f) {
|
||||
mfem::Vector displacementTrue(f.displacementFes->GetTrueVSize());
|
||||
displacementTrue = 0.0;
|
||||
return displacementTrue;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector
|
||||
make_vacuum_only_enthalpy(const mean_field::fem::FEM &f) {
|
||||
mfem::Vector enthalpyTrue =
|
||||
make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), 0.43);
|
||||
[[nodiscard]] mfem::Vector make_vacuum_only_enthalpy(const mean_field::fem::FEM &f) {
|
||||
mfem::Vector enthalpyTrue = make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), 0.43);
|
||||
|
||||
mfem::Array<int> stellarElementMask;
|
||||
mean_field::utils::populate_element_mask(
|
||||
f.mesh.get(), mean_field::utils::DOMAINS::STELLAR,
|
||||
stellarElementMask
|
||||
);
|
||||
mean_field::utils::populate_element_mask(f.mesh.get(), mean_field::utils::DOMAINS::STELLAR, stellarElementMask);
|
||||
|
||||
mfem::Array<int> stellarEnthalpyTrueDofs;
|
||||
mean_field::utils::populate_domain_tdofs(
|
||||
f.enthalpyFes.get(), stellarElementMask, stellarEnthalpyTrueDofs
|
||||
);
|
||||
mean_field::utils::populate_domain_tdofs(f.enthalpyFes.get(), stellarElementMask, stellarEnthalpyTrueDofs);
|
||||
|
||||
for (int listIndex = 0; listIndex < stellarEnthalpyTrueDofs.Size();
|
||||
++listIndex) {
|
||||
for (int listIndex = 0; listIndex < stellarEnthalpyTrueDofs.Size(); ++listIndex) {
|
||||
const int trueDof = stellarEnthalpyTrueDofs[listIndex];
|
||||
|
||||
MFEM_VERIFY(
|
||||
trueDof >= 0 && trueDof < enthalpyTrue.Size(),
|
||||
"The stellar enthalpy true-DOF mask contains an "
|
||||
"invalid index."
|
||||
trueDof >= 0 && trueDof < enthalpyTrue.Size(), "The stellar enthalpy true-DOF mask contains an "
|
||||
"invalid index."
|
||||
);
|
||||
|
||||
enthalpyTrue(trueDof) = 0.0;
|
||||
@@ -65,11 +55,9 @@ namespace pressure_force_kernel_test_utils {
|
||||
return enthalpyTrue;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector
|
||||
make_positive_asymmetric_enthalpy(const mean_field::fem::FEM &f) {
|
||||
[[nodiscard]] mfem::Vector make_positive_asymmetric_enthalpy(const mean_field::fem::FEM &f) {
|
||||
mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) {
|
||||
return 1.10 + 0.07 * position(0) - 0.04 * position(1) +
|
||||
0.03 * position(2);
|
||||
return 1.10 + 0.07 * position(0) - 0.04 * position(1) + 0.03 * position(2);
|
||||
});
|
||||
|
||||
mfem::ParGridFunction enthalpyField(f.enthalpyFes.get());
|
||||
@@ -89,15 +77,9 @@ namespace pressure_force_kernel_test_utils {
|
||||
) {
|
||||
const int dimension = f.mesh->Dimension();
|
||||
|
||||
MFEM_VERIFY(
|
||||
component >= 0 && component < dimension,
|
||||
"The requested vector component is invalid."
|
||||
);
|
||||
MFEM_VERIFY(component >= 0 && component < dimension, "The requested vector component is invalid.");
|
||||
|
||||
MFEM_VERIFY(
|
||||
coordinate >= -1 && coordinate < dimension,
|
||||
"The requested coordinate is invalid."
|
||||
);
|
||||
MFEM_VERIFY(coordinate >= -1 && coordinate < dimension, "The requested coordinate is invalid.");
|
||||
|
||||
/*
|
||||
* coordinate == -1 gives the rigid translation e_component.
|
||||
@@ -107,9 +89,7 @@ namespace pressure_force_kernel_test_utils {
|
||||
* w = x_coordinate e_component.
|
||||
*/
|
||||
mfem::VectorFunctionCoefficient coefficient(
|
||||
dimension,
|
||||
[component, coordinate,
|
||||
dimension](const mfem::Vector &position, mfem::Vector &value) {
|
||||
dimension, [component, coordinate, dimension](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(dimension);
|
||||
value = 0.0;
|
||||
|
||||
@@ -132,20 +112,192 @@ namespace pressure_force_kernel_test_utils {
|
||||
const mfem::Vector &right,
|
||||
MPI_Comm communicator
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
left.Size() == right.Size(),
|
||||
"The global dot-product vectors have different sizes."
|
||||
);
|
||||
MFEM_VERIFY(left.Size() == right.Size(), "The global dot-product vectors have different sizes.");
|
||||
|
||||
const double localDot = left * right;
|
||||
double globalDot = 0.0;
|
||||
|
||||
MPI_Allreduce(
|
||||
&localDot, &globalDot, 1, MPI_DOUBLE, MPI_SUM, communicator
|
||||
);
|
||||
MPI_Allreduce(&localDot, &globalDot, 1, MPI_DOUBLE, MPI_SUM, communicator);
|
||||
|
||||
return globalDot;
|
||||
}
|
||||
|
||||
[[nodiscard]] double integrate_pressure(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::mapping::DomainMapperStateless &domainMapper,
|
||||
const mean_field::eos::Polytrope &barotrope,
|
||||
const mfem::Vector &enthalpyTrue,
|
||||
const mfem::Vector &displacementTrue
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
enthalpyTrue.Size() == f.enthalpyFes->GetTrueVSize(),
|
||||
"The pressure-integral enthalpy vector has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
|
||||
"The pressure-integral displacement vector has the wrong size."
|
||||
);
|
||||
|
||||
mfem::Vector enthalpyLocal(f.enthalpyFes->GetVSize());
|
||||
|
||||
const mfem::Operator *enthalpyProlongation = f.enthalpyFes->GetProlongationMatrix();
|
||||
|
||||
if (enthalpyProlongation != nullptr) {
|
||||
enthalpyProlongation->Mult(enthalpyTrue, enthalpyLocal);
|
||||
} else {
|
||||
enthalpyLocal = enthalpyTrue;
|
||||
}
|
||||
|
||||
mfem::Vector displacementLocal(f.displacementFes->GetVSize());
|
||||
|
||||
const mfem::Operator *displacementProlongation = f.displacementFes->GetProlongationMatrix();
|
||||
|
||||
if (displacementProlongation != nullptr) {
|
||||
displacementProlongation->Mult(displacementTrue, displacementLocal);
|
||||
} else {
|
||||
displacementLocal = displacementTrue;
|
||||
}
|
||||
|
||||
const double pressureExtraOrderValue =
|
||||
barotrope.polytropic_index() * static_cast<double>(mean_field::field::Enthalpy::Scalar::familyOrder);
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(pressureExtraOrderValue) && pressureExtraOrderValue >= 0.0 &&
|
||||
pressureExtraOrderValue <= static_cast<double>(std::numeric_limits<int>::max()),
|
||||
"The pressure-integral EOS order is invalid."
|
||||
);
|
||||
|
||||
const int pressureExtraOrder = static_cast<int>(std::ceil(pressureExtraOrderValue));
|
||||
|
||||
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||
|
||||
mean_field::mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension());
|
||||
|
||||
mean_field::mapping::VolumeMappingContext mappingContext;
|
||||
|
||||
mfem::Array<int> enthalpyDofs;
|
||||
mfem::Array<int> displacementDofs;
|
||||
mfem::Array<int> compactificationDofs;
|
||||
|
||||
mfem::Vector elementEnthalpy;
|
||||
mfem::Vector elementDisplacement;
|
||||
mfem::Vector elementCompactification;
|
||||
mfem::Vector enthalpyShape;
|
||||
|
||||
double localPressureIntegral = 0.0;
|
||||
|
||||
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
|
||||
|
||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
|
||||
|
||||
MFEM_VERIFY(
|
||||
transformation != nullptr, "The pressure-integral reference received a null "
|
||||
"element transformation."
|
||||
);
|
||||
|
||||
if (transformation->Attribute == vacuumAttribute) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &enthalpyElement = *f.enthalpyFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &displacementElement = *f.displacementFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &compactificationElement = *f.compactificationFes->GetFE(elementId);
|
||||
|
||||
mfem::DofTransformation *enthalpyDofTransformation = f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
|
||||
|
||||
mfem::DofTransformation *displacementDofTransformation =
|
||||
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
|
||||
|
||||
mfem::DofTransformation *compactificationDofTransformation =
|
||||
f.compactificationFes->GetElementDofs(elementId, compactificationDofs);
|
||||
|
||||
enthalpyLocal.GetSubVector(enthalpyDofs, elementEnthalpy);
|
||||
|
||||
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
|
||||
|
||||
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
|
||||
|
||||
if (enthalpyDofTransformation != nullptr) {
|
||||
enthalpyDofTransformation->InvTransformPrimal(elementEnthalpy);
|
||||
}
|
||||
|
||||
if (displacementDofTransformation != nullptr) {
|
||||
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
|
||||
}
|
||||
|
||||
if (compactificationDofTransformation != nullptr) {
|
||||
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
|
||||
}
|
||||
|
||||
const mean_field::mapping::ElementDisplacementData displacementData =
|
||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
|
||||
|
||||
const mean_field::mapping::ElementCompactificationData compactificationData(
|
||||
compactificationElement, elementCompactification
|
||||
);
|
||||
|
||||
const mean_field::mapping::ElementMappingData mappingData{
|
||||
.displacement = displacementData, .compactification = compactificationData
|
||||
};
|
||||
|
||||
const mean_field::quadrature::Query query =
|
||||
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureIntegral>(
|
||||
mean_field::quadrature::QuadratureRole::diagnostic, transformation->OrderW(),
|
||||
std::array<int, 1>{pressureExtraOrder}, mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
const mean_field::quadrature::MfemRule rule =
|
||||
f.quadratureFactory->get(query, transformation->GetGeometryType());
|
||||
|
||||
MFEM_VERIFY(rule.integration_rule != nullptr, "The pressure-integral quadrature rule is null.");
|
||||
|
||||
enthalpyShape.SetSize(enthalpyElement.GetDof());
|
||||
|
||||
for (int quadratureIndex = 0; quadratureIndex < rule.integration_rule->GetNPoints(); ++quadratureIndex) {
|
||||
const mfem::IntegrationPoint &integrationPoint = rule.integration_rule->IntPoint(quadratureIndex);
|
||||
|
||||
transformation->SetIntPoint(&integrationPoint);
|
||||
|
||||
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, integrationPoint, workspace, mappingContext
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
mappingStatus == mean_field::mapping::MappingStatus::valid,
|
||||
"Stateless mapping failed in the "
|
||||
"independent pressure integral. Element: "
|
||||
<< elementId << ", attribute: " << transformation->Attribute
|
||||
<< ", quadrature point: " << quadratureIndex << ", status: " << static_cast<int>(mappingStatus)
|
||||
);
|
||||
|
||||
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
|
||||
|
||||
const double enthalpyValue = elementEnthalpy * enthalpyShape;
|
||||
|
||||
const double pressureValue = barotrope.pressure_from_enthalpy(enthalpyValue);
|
||||
|
||||
const double contribution = pressureValue * mappingContext.quadrature.weight;
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(pressureValue) && std::isfinite(contribution), "The independent pressure integral "
|
||||
"encountered a non-finite value."
|
||||
);
|
||||
|
||||
localPressureIntegral += contribution;
|
||||
}
|
||||
}
|
||||
|
||||
double globalPressureIntegral = 0.0;
|
||||
|
||||
MPI_Allreduce(&localPressureIntegral, &globalPressureIntegral, 1, MPI_DOUBLE, MPI_SUM, f.mesh->GetComm());
|
||||
|
||||
return globalPressureIntegral;
|
||||
}
|
||||
} // namespace pressure_force_kernel_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
@@ -154,31 +306,26 @@ TEST_CASE(
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 0.25);
|
||||
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
|
||||
|
||||
mfem::Vector enthalpyTrue(f.enthalpyFes->GetTrueVSize());
|
||||
enthalpyTrue = 0.0;
|
||||
enthalpyTrue = 0.0;
|
||||
|
||||
const mfem::Vector displacementTrue =
|
||||
pressure_force_kernel_test_utils::make_zero_displacement(f);
|
||||
const mfem::Vector displacementTrue = pressure_force_kernel_test_utils::make_zero_displacement(f);
|
||||
|
||||
mfem::Vector residualTrue;
|
||||
|
||||
mean_field::operators::kernels::apply_pressure_force_residual(
|
||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue,
|
||||
residualTrue
|
||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue, residualTrue
|
||||
);
|
||||
|
||||
REQUIRE(residualTrue.Size() == f.displacementFes->GetTrueVSize());
|
||||
|
||||
const double residualNorm = gravity_prepared_test_utils::global_norm(
|
||||
residualTrue, f.mesh->GetComm()
|
||||
);
|
||||
const double residualNorm = gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm());
|
||||
|
||||
CHECK(residualNorm == 0.0);
|
||||
}
|
||||
@@ -189,19 +336,15 @@ TEST_CASE(
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 0.25);
|
||||
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
|
||||
|
||||
const mfem::Vector enthalpyTrue =
|
||||
pressure_force_kernel_test_utils::make_vacuum_only_enthalpy(f);
|
||||
const mfem::Vector enthalpyTrue = pressure_force_kernel_test_utils::make_vacuum_only_enthalpy(f);
|
||||
|
||||
const double enthalpyNorm = gravity_prepared_test_utils::global_norm(
|
||||
enthalpyTrue, f.mesh->GetComm()
|
||||
);
|
||||
const double enthalpyNorm = gravity_prepared_test_utils::global_norm(enthalpyTrue, f.mesh->GetComm());
|
||||
|
||||
/*
|
||||
* Ensure this is a real exclusion test rather than another
|
||||
@@ -209,21 +352,17 @@ TEST_CASE(
|
||||
*/
|
||||
REQUIRE(enthalpyNorm > 0.0);
|
||||
|
||||
const mfem::Vector displacementTrue =
|
||||
pressure_force_kernel_test_utils::make_zero_displacement(f);
|
||||
const mfem::Vector displacementTrue = pressure_force_kernel_test_utils::make_zero_displacement(f);
|
||||
|
||||
mfem::Vector residualTrue;
|
||||
|
||||
mean_field::operators::kernels::apply_pressure_force_residual(
|
||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue,
|
||||
residualTrue
|
||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue, residualTrue
|
||||
);
|
||||
|
||||
REQUIRE(residualTrue.Size() == f.displacementFes->GetTrueVSize());
|
||||
|
||||
const double residualNorm = gravity_prepared_test_utils::global_norm(
|
||||
residualTrue, f.mesh->GetComm()
|
||||
);
|
||||
const double residualNorm = gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm());
|
||||
|
||||
CHECK(residualNorm == 0.0);
|
||||
}
|
||||
@@ -234,12 +373,11 @@ TEST_CASE(
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 0.25);
|
||||
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
|
||||
|
||||
/*
|
||||
* With n = 3 and K = 1/4:
|
||||
@@ -247,21 +385,17 @@ TEST_CASE(
|
||||
* P(1) = 1/4.
|
||||
*/
|
||||
mfem::Vector enthalpyTrue(f.enthalpyFes->GetTrueVSize());
|
||||
enthalpyTrue = 1.0;
|
||||
enthalpyTrue = 1.0;
|
||||
|
||||
const mfem::Vector displacementTrue =
|
||||
pressure_force_kernel_test_utils::make_zero_displacement(f);
|
||||
const mfem::Vector displacementTrue = pressure_force_kernel_test_utils::make_zero_displacement(f);
|
||||
|
||||
mfem::Vector residualTrue;
|
||||
|
||||
mean_field::operators::kernels::apply_pressure_force_residual(
|
||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue,
|
||||
residualTrue
|
||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue, residualTrue
|
||||
);
|
||||
|
||||
const double residualNorm = gravity_prepared_test_utils::global_norm(
|
||||
residualTrue, f.mesh->GetComm()
|
||||
);
|
||||
const double residualNorm = gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm());
|
||||
|
||||
INFO("Positive-pressure residual norm = " << residualNorm);
|
||||
|
||||
@@ -272,36 +406,29 @@ TEST_CASE(
|
||||
|
||||
TEST_CASE(
|
||||
"Pressure Force Residual Does No Work Against Rigid Translations",
|
||||
tags::barotrope &tags::pressure &tags::kernels &tags::integration
|
||||
&tags::accuracy
|
||||
tags::barotrope &tags::pressure &tags::kernels &tags::integration &tags::accuracy
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
REQUIRE(f.displacementFes->GetOrdering() == mfem::Ordering::byNODES);
|
||||
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 0.25);
|
||||
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
|
||||
|
||||
const mfem::Vector enthalpyTrue =
|
||||
pressure_force_kernel_test_utils::make_positive_asymmetric_enthalpy(f);
|
||||
const mfem::Vector enthalpyTrue = pressure_force_kernel_test_utils::make_positive_asymmetric_enthalpy(f);
|
||||
|
||||
const mfem::Vector displacementTrue =
|
||||
pressure_force_kernel_test_utils::make_zero_displacement(f);
|
||||
const mfem::Vector displacementTrue = pressure_force_kernel_test_utils::make_zero_displacement(f);
|
||||
|
||||
mfem::Vector residualTrue;
|
||||
|
||||
mean_field::operators::kernels::apply_pressure_force_residual(
|
||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue,
|
||||
residualTrue
|
||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue, residualTrue
|
||||
);
|
||||
|
||||
const double residualNorm = gravity_prepared_test_utils::global_norm(
|
||||
residualTrue, f.mesh->GetComm()
|
||||
);
|
||||
const double residualNorm = gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm());
|
||||
|
||||
REQUIRE(residualNorm > 0.0);
|
||||
|
||||
@@ -309,21 +436,14 @@ TEST_CASE(
|
||||
|
||||
for (int component = 0; component < dimension; ++component) {
|
||||
const mfem::Vector translationTrue =
|
||||
pressure_force_kernel_test_utils::make_component_test_field(
|
||||
f, component, -1
|
||||
);
|
||||
pressure_force_kernel_test_utils::make_component_test_field(f, component, -1);
|
||||
|
||||
const double translationNorm = gravity_prepared_test_utils::global_norm(
|
||||
translationTrue, f.mesh->GetComm()
|
||||
);
|
||||
const double translationNorm = gravity_prepared_test_utils::global_norm(translationTrue, f.mesh->GetComm());
|
||||
|
||||
const double translationWork =
|
||||
pressure_force_kernel_test_utils::global_dot(
|
||||
translationTrue, residualTrue, f.mesh->GetComm()
|
||||
);
|
||||
pressure_force_kernel_test_utils::global_dot(translationTrue, residualTrue, f.mesh->GetComm());
|
||||
|
||||
const double dotProductScale =
|
||||
std::fmax(residualNorm * translationNorm, 1.0);
|
||||
const double dotProductScale = std::fmax(residualNorm * translationNorm, 1.0);
|
||||
|
||||
CAPTURE(component, translationWork, dotProductScale);
|
||||
|
||||
@@ -332,32 +452,27 @@ TEST_CASE(
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Pressure Force Residual Respects byNODES Component Layout",
|
||||
tags::barotrope &tags::pressure &tags::kernels &tags::integration
|
||||
&tags::accuracy
|
||||
"Pressure Force Residual Matches Independent Pressure Integral",
|
||||
tags::barotrope &tags::pressure &tags::kernels &tags::integration &tags::accuracy
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
REQUIRE(f.displacementFes->GetOrdering() == mfem::Ordering::byNODES);
|
||||
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 0.25);
|
||||
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
|
||||
|
||||
const mfem::Vector enthalpyTrue =
|
||||
pressure_force_kernel_test_utils::make_positive_asymmetric_enthalpy(f);
|
||||
const mfem::Vector enthalpyTrue = pressure_force_kernel_test_utils::make_positive_asymmetric_enthalpy(f);
|
||||
|
||||
const mfem::Vector displacementTrue =
|
||||
pressure_force_kernel_test_utils::make_zero_displacement(f);
|
||||
const mfem::Vector displacementTrue = pressure_force_kernel_test_utils::make_zero_displacement(f);
|
||||
|
||||
mfem::Vector residualTrue;
|
||||
|
||||
mean_field::operators::kernels::apply_pressure_force_residual(
|
||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue,
|
||||
residualTrue
|
||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue, residualTrue
|
||||
);
|
||||
|
||||
const int dimension = f.mesh->Dimension();
|
||||
@@ -369,17 +484,21 @@ TEST_CASE(
|
||||
for (int component = 0; component < dimension; ++component) {
|
||||
for (int coordinate = 0; coordinate < dimension; ++coordinate) {
|
||||
const mfem::Vector affineTestTrue =
|
||||
pressure_force_kernel_test_utils::make_component_test_field(
|
||||
f, component, coordinate
|
||||
);
|
||||
pressure_force_kernel_test_utils::make_component_test_field(f, component, coordinate);
|
||||
|
||||
virtualWork(component, coordinate) =
|
||||
pressure_force_kernel_test_utils::global_dot(
|
||||
affineTestTrue, residualTrue, f.mesh->GetComm()
|
||||
);
|
||||
pressure_force_kernel_test_utils::global_dot(affineTestTrue, residualTrue, f.mesh->GetComm());
|
||||
}
|
||||
}
|
||||
|
||||
const double pressureIntegral = pressure_force_kernel_test_utils::integrate_pressure(
|
||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue
|
||||
);
|
||||
|
||||
REQUIRE(std::isfinite(pressureIntegral));
|
||||
|
||||
REQUIRE(pressureIntegral > 100.0 * std::numeric_limits<double>::epsilon());
|
||||
|
||||
double meanDiagonalWork = 0.0;
|
||||
|
||||
for (int component = 0; component < dimension; ++component) {
|
||||
@@ -388,37 +507,173 @@ TEST_CASE(
|
||||
|
||||
meanDiagonalWork /= static_cast<double>(dimension);
|
||||
|
||||
// INFO(
|
||||
// "Affine pressure virtual-work tensor:\n"
|
||||
// << virtualWork
|
||||
// );
|
||||
const double comparisonTolerance = 1.0e-6 * std::abs(pressureIntegral);
|
||||
|
||||
INFO("Independent pressure integral = " << pressureIntegral);
|
||||
|
||||
INFO("Expected diagonal virtual work = " << -pressureIntegral);
|
||||
|
||||
INFO("Mean diagonal virtual work = " << meanDiagonalWork);
|
||||
|
||||
REQUIRE(
|
||||
std::abs(meanDiagonalWork) >
|
||||
100.0 * std::numeric_limits<double>::epsilon()
|
||||
);
|
||||
INFO("Comparison tolerance = " << comparisonTolerance);
|
||||
|
||||
const double comparisonTolerance = 1.0e-8 * std::abs(meanDiagonalWork);
|
||||
/*
|
||||
* This separate mean check gives a compact diagnostic if all three
|
||||
* diagonal components drift together.
|
||||
*/
|
||||
CHECK(std::abs(meanDiagonalWork + pressureIntegral) <= comparisonTolerance);
|
||||
|
||||
for (int component = 0; component < dimension; ++component) {
|
||||
for (int coordinate = 0; coordinate < dimension; ++coordinate) {
|
||||
const double computedWork = virtualWork(component, coordinate);
|
||||
|
||||
CAPTURE(
|
||||
component, coordinate, computedWork, meanDiagonalWork,
|
||||
comparisonTolerance
|
||||
);
|
||||
const double expectedWork = component == coordinate ? -pressureIntegral : 0.0;
|
||||
|
||||
if (component == coordinate) {
|
||||
CHECK(
|
||||
std::abs(computedWork - meanDiagonalWork) <=
|
||||
comparisonTolerance
|
||||
);
|
||||
} else {
|
||||
CHECK(std::abs(computedWork) <= comparisonTolerance);
|
||||
}
|
||||
CAPTURE(component, coordinate, computedWork, expectedWork, pressureIntegral, comparisonTolerance);
|
||||
|
||||
CHECK(std::abs(computedWork - expectedWork) <= comparisonTolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const double relativeMeanError = std::abs(meanDiagonalWork + pressureIntegral) / std::abs(pressureIntegral);
|
||||
|
||||
INFO("Relative mean diagonal error = " << relativeMeanError);
|
||||
|
||||
CHECK(relativeMeanError <= 1.0e-6);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Pressure Force Residual Matches Deformed Pressure Volume Variation",
|
||||
tags::barotrope &tags::pressure &tags::kernels &tags::integration &tags::accuracy
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
|
||||
|
||||
/*
|
||||
* This field is positive but spatially nonuniform, so the test
|
||||
* exercises a genuinely nonuniform pressure distribution.
|
||||
*/
|
||||
const mfem::Vector enthalpyTrue =
|
||||
pressure_force_kernel_test_utils::make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), 0.37);
|
||||
|
||||
/*
|
||||
* make_displacement() contains anisotropic diagonal terms and
|
||||
* quadratic cross terms. A scale of 0.67 therefore provides a
|
||||
* nonzero, nonspherical, valid base geometry.
|
||||
*/
|
||||
const mfem::Vector baseDisplacementTrue = gravity_prepared_test_utils::make_displacement(f, 0.67);
|
||||
|
||||
/*
|
||||
* Differentiate along the same smooth deformation family. Thus
|
||||
*
|
||||
* d(epsilon) = (0.67 + epsilon) d_shape.
|
||||
*
|
||||
* This gives a controlled geometry path while still evaluating
|
||||
* the derivative at a genuinely deformed base state.
|
||||
*/
|
||||
const mfem::Vector displacementVariationTrue = gravity_prepared_test_utils::make_displacement(f, 1.0);
|
||||
|
||||
const double baseDisplacementNorm =
|
||||
gravity_prepared_test_utils::global_norm(baseDisplacementTrue, f.mesh->GetComm());
|
||||
|
||||
const double variationNorm = gravity_prepared_test_utils::global_norm(displacementVariationTrue, f.mesh->GetComm());
|
||||
|
||||
REQUIRE(baseDisplacementNorm > 100.0 * std::numeric_limits<double>::epsilon());
|
||||
|
||||
REQUIRE(variationNorm > 100.0 * std::numeric_limits<double>::epsilon());
|
||||
|
||||
mfem::Vector residualTrue;
|
||||
|
||||
mean_field::operators::kernels::apply_pressure_force_residual(
|
||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue, baseDisplacementTrue, residualTrue
|
||||
);
|
||||
|
||||
REQUIRE(residualTrue.Size() == f.displacementFes->GetTrueVSize());
|
||||
|
||||
const double residualWork =
|
||||
pressure_force_kernel_test_utils::global_dot(displacementVariationTrue, residualTrue, f.mesh->GetComm());
|
||||
|
||||
REQUIRE(std::isfinite(residualWork));
|
||||
|
||||
REQUIRE(std::abs(residualWork) > 100.0 * std::numeric_limits<double>::epsilon());
|
||||
|
||||
/*
|
||||
* The relatively broad initial sweep lets us see the expected
|
||||
* centered-difference convergence before reaching the quadrature
|
||||
* and representation plateau.
|
||||
*/
|
||||
constexpr std::array<double, 4> differenceSteps{1.0e-2, 5.0e-3, 2.5e-3, 1.25e-3};
|
||||
|
||||
double bestRelativeDiscrepancy = std::numeric_limits<double>::infinity();
|
||||
|
||||
for (const double differenceStep : differenceSteps) {
|
||||
mfem::Vector displacementPlus(baseDisplacementTrue);
|
||||
|
||||
mfem::Vector displacementMinus(baseDisplacementTrue);
|
||||
|
||||
displacementPlus.Add(differenceStep, displacementVariationTrue);
|
||||
|
||||
displacementMinus.Add(-differenceStep, displacementVariationTrue);
|
||||
|
||||
const double pressureIntegralPlus = pressure_force_kernel_test_utils::integrate_pressure(
|
||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementPlus
|
||||
);
|
||||
|
||||
const double pressureIntegralMinus = pressure_force_kernel_test_utils::integrate_pressure(
|
||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementMinus
|
||||
);
|
||||
|
||||
REQUIRE(std::isfinite(pressureIntegralPlus));
|
||||
REQUIRE(std::isfinite(pressureIntegralMinus));
|
||||
|
||||
const double pressureVolumeDerivative = (pressureIntegralPlus - pressureIntegralMinus) / (2.0 * differenceStep);
|
||||
|
||||
REQUIRE(std::isfinite(pressureVolumeDerivative));
|
||||
|
||||
double comparisonScale = std::abs(residualWork);
|
||||
|
||||
if (std::abs(pressureVolumeDerivative) > comparisonScale) {
|
||||
comparisonScale = std::abs(pressureVolumeDerivative);
|
||||
}
|
||||
|
||||
REQUIRE(comparisonScale > 100.0 * std::numeric_limits<double>::epsilon());
|
||||
|
||||
const double absoluteDiscrepancy = std::abs(residualWork + pressureVolumeDerivative);
|
||||
|
||||
const double relativeDiscrepancy = absoluteDiscrepancy / comparisonScale;
|
||||
|
||||
if (relativeDiscrepancy < bestRelativeDiscrepancy) {
|
||||
bestRelativeDiscrepancy = relativeDiscrepancy;
|
||||
}
|
||||
|
||||
INFO("Difference step = " << differenceStep);
|
||||
|
||||
INFO("Pressure residual work = " << residualWork);
|
||||
|
||||
INFO("Pressure-volume derivative = " << pressureVolumeDerivative);
|
||||
|
||||
INFO("Residual work plus derivative = " << residualWork + pressureVolumeDerivative);
|
||||
|
||||
INFO("Relative discrepancy = " << relativeDiscrepancy);
|
||||
|
||||
/*
|
||||
* The signs must be opposite because the implemented pressure
|
||||
* force is the negative variation of the pressure-volume
|
||||
* functional.
|
||||
*/
|
||||
CHECK(residualWork * pressureVolumeDerivative < 0.0);
|
||||
}
|
||||
|
||||
INFO("Best pressure-volume relative discrepancy = " << bestRelativeDiscrepancy);
|
||||
|
||||
/*
|
||||
* This is intentionally a provisional but meaningful threshold.
|
||||
* We will tighten it after measuring the convergence plateau.
|
||||
*/
|
||||
CHECK(bestRelativeDiscrepancy < 1.0e-8);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
954
tests/operators/prepared_displacement_operator.cpp
Normal file
954
tests/operators/prepared_displacement_operator.cpp
Normal file
@@ -0,0 +1,954 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <mfem.hpp>
|
||||
#include <mpi.h>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace prepared_displacement_residual_test_utils {
|
||||
using CoupledForm = mean_field::utils::blocks::barotropic_equilibrium_form;
|
||||
|
||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
|
||||
constexpr auto densityValue =
|
||||
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term);
|
||||
|
||||
constexpr auto displacementValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||
mean_field::utils::blocks::displacement_field.geometry_term
|
||||
);
|
||||
|
||||
constexpr auto gravityGradientValue =
|
||||
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::gravity_field.gradient_term);
|
||||
|
||||
constexpr auto gravityPotentialValue =
|
||||
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::gravity_field.poisson_term);
|
||||
|
||||
constexpr auto enthalpyValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||
mean_field::utils::blocks::enthalpy_field.specific_term
|
||||
);
|
||||
|
||||
constexpr auto barotropicConstantValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
|
||||
);
|
||||
|
||||
constexpr auto gravityGradientResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
|
||||
constexpr auto gravityPotentialResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
|
||||
constexpr auto densityResidual =
|
||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term);
|
||||
|
||||
constexpr auto displacementResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::displacement_field.geometry_term
|
||||
);
|
||||
|
||||
constexpr auto enthalpyResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::enthalpy_field.specific_term
|
||||
);
|
||||
|
||||
constexpr auto massResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
|
||||
);
|
||||
|
||||
[[nodiscard]] mean_field::field::FieldDofMap make_enthalpy_map(const mean_field::fem::FEM &f) {
|
||||
return mean_field::field::make_field_dof_map<mean_field::field::Enthalpy, DomainSchema>(*f.enthalpyFes);
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::DisplacementResidualLayout make_layout(const mean_field::fem::FEM &f) {
|
||||
const auto enthalpyMap = make_enthalpy_map(f);
|
||||
|
||||
/*
|
||||
* Transitional displacement-composer layout.
|
||||
*
|
||||
* Pressure has now migrated its h column to supported FieldDof
|
||||
* coordinates, while the density-consuming mechanical children are
|
||||
* intentionally still full-space until the next migration slice.
|
||||
*
|
||||
* The adapter only writes R_d. The unrelated residual-row sizes remain
|
||||
* at their current full-space values in this standalone adapter test.
|
||||
*/
|
||||
const std::array<int, CoupledForm::value_block_count> valueSizes{
|
||||
f.densityFes->GetTrueVSize(), f.displacementFes->GetTrueVSize(), f.gravityFluxFes->GetTrueVSize(),
|
||||
f.gravityPotentialFes->GetTrueVSize(), enthalpyMap.reduced_size(), 1
|
||||
};
|
||||
|
||||
const std::array<int, CoupledForm::residual_block_count> residualSizes{
|
||||
f.gravityFluxFes->GetTrueVSize(), f.gravityPotentialFes->GetTrueVSize(), f.densityFes->GetTrueVSize(),
|
||||
f.displacementFes->GetTrueVSize(), f.enthalpyFes->GetTrueVSize(), 1
|
||||
};
|
||||
|
||||
return {valueSizes, residualSizes};
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_density(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase
|
||||
) {
|
||||
mfem::ParGridFunction field(f.densityFes.get());
|
||||
|
||||
mfem::FunctionCoefficient coefficient([phase](const mfem::Vector &position) {
|
||||
return 0.84 + 0.06 * std::sin(0.73 * position(0) + phase) + 0.04 * std::cos(0.61 * position(1) - phase) +
|
||||
0.025 * position(2) * position(2);
|
||||
});
|
||||
|
||||
field.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector trueDofs;
|
||||
field.GetTrueDofs(trueDofs);
|
||||
return trueDofs;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_density_direction(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase
|
||||
) {
|
||||
mfem::ParGridFunction field(f.densityFes.get());
|
||||
|
||||
mfem::FunctionCoefficient coefficient([phase](const mfem::Vector &position) {
|
||||
return 0.17 * std::sin(0.91 * position(0) + phase) - 0.11 * std::cos(0.79 * position(1) - phase) +
|
||||
0.07 * position(2);
|
||||
});
|
||||
|
||||
field.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector trueDofs;
|
||||
field.GetTrueDofs(trueDofs);
|
||||
return trueDofs;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_gravity_gradient(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase
|
||||
) {
|
||||
mfem::ParGridFunction field(f.gravityFluxFes.get());
|
||||
|
||||
auto function = [phase](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
|
||||
value(0) = 0.31 + 0.08 * position(0) + 0.03 * phase * position(1);
|
||||
|
||||
value(1) = -0.17 + 0.06 * position(1) - 0.02 * phase * position(2);
|
||||
|
||||
value(2) = 0.23 - 0.05 * position(2) + 0.025 * phase * position(0);
|
||||
};
|
||||
|
||||
mfem::VectorFunctionCoefficient coefficient(3, function);
|
||||
field.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector trueDofs;
|
||||
field.GetTrueDofs(trueDofs);
|
||||
return trueDofs;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_gravity_gradient_direction(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase
|
||||
) {
|
||||
mfem::ParGridFunction field(f.gravityFluxFes.get());
|
||||
|
||||
auto function = [phase](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
|
||||
value(0) = 0.14 * std::sin(position(0) + phase) + 0.03 * position(1);
|
||||
|
||||
value(1) = -0.11 * std::cos(position(1) - phase) + 0.04 * position(2);
|
||||
|
||||
value(2) = 0.09 * std::sin(position(2) + 0.5 * phase) - 0.02 * position(0);
|
||||
};
|
||||
|
||||
mfem::VectorFunctionCoefficient coefficient(3, function);
|
||||
field.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector trueDofs;
|
||||
field.GetTrueDofs(trueDofs);
|
||||
return trueDofs;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_positive_enthalpy(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase
|
||||
) {
|
||||
/*
|
||||
* Build a full H1 test state first, then return exactly the
|
||||
* solver-facing supported FieldDof coordinates consumed by the
|
||||
* migrated pressure-force operator.
|
||||
*
|
||||
* Keeping the public helper name unchanged means every existing
|
||||
* displacement-composer test automatically migrates to the new
|
||||
* pressure contract without inventing a parallel "*_full" helper.
|
||||
*/
|
||||
mfem::Vector fullEnthalpy(f.enthalpyFes->GetTrueVSize());
|
||||
|
||||
for (int index = 0; index < fullEnthalpy.Size(); ++index) {
|
||||
const double coordinate = static_cast<double>(index + 1);
|
||||
|
||||
fullEnthalpy(index) =
|
||||
0.93 + 0.09 * std::sin(0.23 * coordinate + phase) + 0.04 * std::cos(0.17 * coordinate - 0.5 * phase);
|
||||
}
|
||||
|
||||
return make_enthalpy_map(f).gather(fullEnthalpy);
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_enthalpy_direction(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase
|
||||
) {
|
||||
mfem::Vector fullDirection(f.enthalpyFes->GetTrueVSize());
|
||||
|
||||
for (int index = 0; index < fullDirection.Size(); ++index) {
|
||||
const double coordinate = static_cast<double>(index + 1);
|
||||
|
||||
fullDirection(index) =
|
||||
0.27 * std::sin(0.19 * coordinate + phase) + 0.14 * std::cos(0.13 * coordinate - 0.5 * phase);
|
||||
}
|
||||
|
||||
return make_enthalpy_map(f).gather(fullDirection);
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_displacement_direction(const mean_field::fem::FEM &f) {
|
||||
mfem::Vector direction = gravity_prepared_test_utils::make_displacement(f, 0.91);
|
||||
|
||||
const mfem::Vector second = gravity_prepared_test_utils::make_displacement(f, 0.27);
|
||||
|
||||
direction -= second;
|
||||
return direction;
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::physics::RigidRotation make_rotation(const double scale = 1.0) {
|
||||
mfem::Vector angularVelocity(3);
|
||||
angularVelocity(0) = scale * 0.17;
|
||||
angularVelocity(1) = scale * -0.09;
|
||||
angularVelocity(2) = scale * 0.62;
|
||||
|
||||
mfem::Vector center(3);
|
||||
center(0) = 0.04;
|
||||
center(1) = -0.03;
|
||||
center(2) = 0.02;
|
||||
|
||||
return mean_field::physics::RigidRotation(angularVelocity, center);
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::DisplacementResidualDependencies make_dependencies() {
|
||||
return {
|
||||
.discretization = {.identity = 401, .revision = 3},
|
||||
.density = {.identity = 409, .revision = 5},
|
||||
.displacement = {.identity = 419, .revision = 7},
|
||||
.gravityGradient = {.identity = 421, .revision = 11},
|
||||
.enthalpy = {.identity = 431, .revision = 13},
|
||||
.rotation = {.identity = 433, .revision = 17}
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::context::gravity_field::GravityFieldRevisions make_gravity_revisions(
|
||||
const mean_field::operators::DisplacementResidualDependencies &dependencies,
|
||||
const std::uint64_t potentialRevision
|
||||
) {
|
||||
return {
|
||||
.discretization = {.value = dependencies.discretization.revision},
|
||||
.displacement = {.value = dependencies.displacement.revision},
|
||||
.density = {.value = dependencies.density.revision},
|
||||
.gravity_gradient = {.value = dependencies.gravityGradient.revision},
|
||||
.gravity_potential = {.value = potentialRevision}
|
||||
};
|
||||
}
|
||||
|
||||
void prepare_gravity_context(
|
||||
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext &context,
|
||||
const mfem::Vector &density,
|
||||
const mfem::Vector &displacement,
|
||||
const mfem::Vector &gravityGradient,
|
||||
const mfem::Vector &gravityPotential,
|
||||
const mean_field::operators::DisplacementResidualDependencies &dependencies,
|
||||
const std::uint64_t potentialRevision
|
||||
) {
|
||||
context.Prepare(
|
||||
{.density = density,
|
||||
.displacement = displacement,
|
||||
.gravity_gradient = gravityGradient,
|
||||
.gravity_potential = gravityPotential},
|
||||
make_gravity_revisions(dependencies, potentialRevision)
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] double relative_difference(
|
||||
const mfem::Vector &left,
|
||||
const mfem::Vector &right,
|
||||
const MPI_Comm communicator
|
||||
) {
|
||||
REQUIRE(left.Size() == right.Size());
|
||||
|
||||
mfem::Vector difference(left);
|
||||
difference -= right;
|
||||
|
||||
const double scale = std::max(
|
||||
{gravity_prepared_test_utils::global_norm(left, communicator),
|
||||
gravity_prepared_test_utils::global_norm(right, communicator),
|
||||
100.0 * std::numeric_limits<double>::epsilon()}
|
||||
);
|
||||
|
||||
return gravity_prepared_test_utils::global_norm(difference, communicator) / scale;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector
|
||||
explicit_residual_sum(const mean_field::operators::PreparedDisplacementResidualOperator &preparedOperator) {
|
||||
mfem::Vector pressure;
|
||||
mfem::Vector gravity;
|
||||
mfem::Vector rotation;
|
||||
|
||||
preparedOperator.GetPressureOperator().BuildResidual(pressure);
|
||||
preparedOperator.GetGravityOperator().BuildResidual(gravity);
|
||||
preparedOperator.GetRotationalOperator().BuildResidual(rotation);
|
||||
|
||||
pressure += gravity;
|
||||
pressure += rotation;
|
||||
return pressure;
|
||||
}
|
||||
|
||||
template <int index>
|
||||
[[nodiscard]] mfem::Vector copy_residual_block(
|
||||
const mfem::Vector &action,
|
||||
const mean_field::operators::DisplacementResidualLayout &layout,
|
||||
const mean_field::utils::blocks::residual_block<index> block
|
||||
) {
|
||||
mfem::Vector result(layout.size(block));
|
||||
const int offset = layout.offset(block);
|
||||
|
||||
for (int entry = 0; entry < result.Size(); ++entry) {
|
||||
result(entry) = action(offset + entry);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace prepared_displacement_residual_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Displacement Residual Equals The Three Prepared Contributors",
|
||||
tags::barotrope &tags::prepared &tags::integration &tags::residuals
|
||||
) {
|
||||
using Operator = mean_field::operators::PreparedDisplacementResidualOperator;
|
||||
|
||||
STATIC_REQUIRE_FALSE(std::is_copy_constructible_v<Operator>);
|
||||
STATIC_REQUIRE_FALSE(std::is_copy_assignable_v<Operator>);
|
||||
STATIC_REQUIRE_FALSE(std::is_move_constructible_v<Operator>);
|
||||
STATIC_REQUIRE_FALSE(std::is_move_assignable_v<Operator>);
|
||||
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const auto enthalpyMap = prepared_displacement_residual_test_utils::make_enthalpy_map(f);
|
||||
|
||||
const mfem::Vector density = prepared_displacement_residual_test_utils::make_density(f, 0.31);
|
||||
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.67);
|
||||
|
||||
const mfem::Vector gravityGradient = prepared_displacement_residual_test_utils::make_gravity_gradient(f, 0.47);
|
||||
|
||||
mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize());
|
||||
gravityPotential = 0.0;
|
||||
|
||||
const mfem::Vector enthalpy = prepared_displacement_residual_test_utils::make_positive_enthalpy(f, 0.53);
|
||||
|
||||
REQUIRE(enthalpyMap.reduced_size() < enthalpyMap.full_size());
|
||||
REQUIRE(enthalpy.Size() == enthalpyMap.reduced_size());
|
||||
|
||||
const auto dependencies = prepared_displacement_residual_test_utils::make_dependencies();
|
||||
|
||||
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
|
||||
prepared_displacement_residual_test_utils::prepare_gravity_context(
|
||||
gravityContext, density, displacement, gravityGradient, gravityPotential, dependencies, 19
|
||||
);
|
||||
|
||||
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
||||
const mean_field::physics::RigidRotation rotation = prepared_displacement_residual_test_utils::make_rotation(0.83);
|
||||
|
||||
Operator preparedOperator(f, *f.domainMapperStateless, equationOfState, gravityContext);
|
||||
|
||||
REQUIRE(preparedOperator.GetPressureOperator().GetEnthalpySize() == enthalpy.Size());
|
||||
|
||||
const auto initialReport = preparedOperator.Prepare({.enthalpy = enthalpy}, dependencies, rotation);
|
||||
|
||||
REQUIRE(initialReport.pressure.DidAnyWork());
|
||||
REQUIRE(initialReport.gravity.DidAnyWork());
|
||||
REQUIRE(initialReport.rotation.DidAnyWork());
|
||||
REQUIRE(initialReport.assembledResidual);
|
||||
REQUIRE(preparedOperator.IsPrepared());
|
||||
|
||||
CHECK(&preparedOperator.GetFEM() == &f);
|
||||
CHECK(&preparedOperator.GetGravityContext() == &gravityContext);
|
||||
CHECK(&preparedOperator.GetGravityOperator().GetGravityContext() == &gravityContext);
|
||||
|
||||
mfem::Vector compositeResidual;
|
||||
preparedOperator.BuildResidual(compositeResidual);
|
||||
|
||||
const mfem::Vector explicitResidual =
|
||||
prepared_displacement_residual_test_utils::explicit_residual_sum(preparedOperator);
|
||||
|
||||
const double compositionError = prepared_displacement_residual_test_utils::relative_difference(
|
||||
compositeResidual, explicitResidual, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
INFO("Prepared residual composition error = " << compositionError);
|
||||
CHECK(compositionError < 2.0e-15);
|
||||
|
||||
const std::uint64_t preparationCount = preparedOperator.GetResidualPreparationCount();
|
||||
|
||||
const auto repeatedReport = preparedOperator.Prepare({.enthalpy = enthalpy}, dependencies, rotation);
|
||||
|
||||
CHECK_FALSE(repeatedReport.DidAnyWork());
|
||||
CHECK_FALSE(repeatedReport.assembledResidual);
|
||||
CHECK(preparedOperator.GetResidualPreparationCount() == preparationCount);
|
||||
CHECK(preparedOperator.IsPrepared());
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Displacement Residual Selectively Orchestrates Its Children",
|
||||
tags::barotrope &tags::prepared &tags::contexts &tags::integration
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
mfem::Vector density = prepared_displacement_residual_test_utils::make_density(f, 0.29);
|
||||
|
||||
mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.61);
|
||||
|
||||
mfem::Vector gravityGradient = prepared_displacement_residual_test_utils::make_gravity_gradient(f, 0.43);
|
||||
|
||||
mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize());
|
||||
gravityPotential = 0.0;
|
||||
|
||||
mfem::Vector enthalpy = prepared_displacement_residual_test_utils::make_positive_enthalpy(f, 0.51);
|
||||
|
||||
auto dependencies = prepared_displacement_residual_test_utils::make_dependencies();
|
||||
|
||||
std::uint64_t potentialRevision = 19;
|
||||
|
||||
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
|
||||
prepared_displacement_residual_test_utils::prepare_gravity_context(
|
||||
gravityContext, density, displacement, gravityGradient, gravityPotential, dependencies, potentialRevision
|
||||
);
|
||||
|
||||
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
||||
|
||||
mean_field::physics::RigidRotation rotation = prepared_displacement_residual_test_utils::make_rotation(0.79);
|
||||
|
||||
mean_field::operators::PreparedDisplacementResidualOperator preparedOperator(
|
||||
f, *f.domainMapperStateless, equationOfState, gravityContext
|
||||
);
|
||||
|
||||
preparedOperator.Prepare({.enthalpy = enthalpy}, dependencies, rotation);
|
||||
|
||||
gravityPotential = 0.17;
|
||||
++potentialRevision;
|
||||
|
||||
prepared_displacement_residual_test_utils::prepare_gravity_context(
|
||||
gravityContext, density, displacement, gravityGradient, gravityPotential, dependencies, potentialRevision
|
||||
);
|
||||
|
||||
const auto potentialReport = preparedOperator.Prepare({.enthalpy = enthalpy}, dependencies, rotation);
|
||||
|
||||
CHECK_FALSE(potentialReport.DidAnyWork());
|
||||
CHECK_FALSE(potentialReport.assembledResidual);
|
||||
|
||||
enthalpy = prepared_displacement_residual_test_utils::make_positive_enthalpy(f, 0.83);
|
||||
++dependencies.enthalpy.revision;
|
||||
|
||||
const auto enthalpyReport = preparedOperator.Prepare({.enthalpy = enthalpy}, dependencies, rotation);
|
||||
|
||||
CHECK(enthalpyReport.pressure.DidAnyWork());
|
||||
CHECK_FALSE(enthalpyReport.gravity.DidAnyWork());
|
||||
CHECK_FALSE(enthalpyReport.rotation.DidAnyWork());
|
||||
CHECK(enthalpyReport.assembledResidual);
|
||||
|
||||
gravityGradient = prepared_displacement_residual_test_utils::make_gravity_gradient(f, 0.91);
|
||||
++dependencies.gravityGradient.revision;
|
||||
|
||||
prepared_displacement_residual_test_utils::prepare_gravity_context(
|
||||
gravityContext, density, displacement, gravityGradient, gravityPotential, dependencies, potentialRevision
|
||||
);
|
||||
|
||||
const auto gravityReport = preparedOperator.Prepare({.enthalpy = enthalpy}, dependencies, rotation);
|
||||
|
||||
CHECK_FALSE(gravityReport.pressure.DidAnyWork());
|
||||
CHECK(gravityReport.gravity.DidAnyWork());
|
||||
CHECK_FALSE(gravityReport.rotation.DidAnyWork());
|
||||
CHECK(gravityReport.assembledResidual);
|
||||
|
||||
density = prepared_displacement_residual_test_utils::make_density(f, 1.07);
|
||||
++dependencies.density.revision;
|
||||
|
||||
prepared_displacement_residual_test_utils::prepare_gravity_context(
|
||||
gravityContext, density, displacement, gravityGradient, gravityPotential, dependencies, potentialRevision
|
||||
);
|
||||
|
||||
const auto densityReport = preparedOperator.Prepare({.enthalpy = enthalpy}, dependencies, rotation);
|
||||
|
||||
CHECK_FALSE(densityReport.pressure.DidAnyWork());
|
||||
CHECK(densityReport.gravity.DidAnyWork());
|
||||
CHECK(densityReport.rotation.DidAnyWork());
|
||||
CHECK(densityReport.assembledResidual);
|
||||
|
||||
rotation = prepared_displacement_residual_test_utils::make_rotation(1.13);
|
||||
++dependencies.rotation.revision;
|
||||
|
||||
const auto rotationReport = preparedOperator.Prepare({.enthalpy = enthalpy}, dependencies, rotation);
|
||||
|
||||
CHECK_FALSE(rotationReport.pressure.DidAnyWork());
|
||||
CHECK_FALSE(rotationReport.gravity.DidAnyWork());
|
||||
CHECK(rotationReport.rotation.DidAnyWork());
|
||||
CHECK(rotationReport.assembledResidual);
|
||||
|
||||
displacement = gravity_prepared_test_utils::make_displacement(f, 0.89);
|
||||
++dependencies.displacement.revision;
|
||||
|
||||
prepared_displacement_residual_test_utils::prepare_gravity_context(
|
||||
gravityContext, density, displacement, gravityGradient, gravityPotential, dependencies, potentialRevision
|
||||
);
|
||||
|
||||
const auto displacementReport = preparedOperator.Prepare({.enthalpy = enthalpy}, dependencies, rotation);
|
||||
|
||||
CHECK(displacementReport.pressure.DidAnyWork());
|
||||
CHECK(displacementReport.gravity.DidAnyWork());
|
||||
CHECK(displacementReport.rotation.DidAnyWork());
|
||||
CHECK(displacementReport.assembledResidual);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Displacement Residual Jacobian Equals The Contributor Sums",
|
||||
tags::barotrope &tags::prepared &tags::jacobian &tags::accuracy
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const mfem::Vector density = prepared_displacement_residual_test_utils::make_density(f, 0.37);
|
||||
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.73);
|
||||
|
||||
const mfem::Vector gravityGradient = prepared_displacement_residual_test_utils::make_gravity_gradient(f, 0.59);
|
||||
|
||||
mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize());
|
||||
gravityPotential = 0.0;
|
||||
|
||||
const mfem::Vector enthalpy = prepared_displacement_residual_test_utils::make_positive_enthalpy(f, 0.61);
|
||||
|
||||
const mfem::Vector densityDirection = prepared_displacement_residual_test_utils::make_density_direction(f, 0.71);
|
||||
|
||||
const mfem::Vector displacementDirection =
|
||||
prepared_displacement_residual_test_utils::make_displacement_direction(f);
|
||||
|
||||
const mfem::Vector gravityDirection =
|
||||
prepared_displacement_residual_test_utils::make_gravity_gradient_direction(f, 0.83);
|
||||
|
||||
const mfem::Vector enthalpyDirection = prepared_displacement_residual_test_utils::make_enthalpy_direction(f, 0.97);
|
||||
|
||||
const auto dependencies = prepared_displacement_residual_test_utils::make_dependencies();
|
||||
|
||||
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
|
||||
prepared_displacement_residual_test_utils::prepare_gravity_context(
|
||||
gravityContext, density, displacement, gravityGradient, gravityPotential, dependencies, 19
|
||||
);
|
||||
|
||||
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
||||
const mean_field::physics::RigidRotation rotation = prepared_displacement_residual_test_utils::make_rotation(0.91);
|
||||
|
||||
mean_field::operators::PreparedDisplacementResidualOperator preparedOperator(
|
||||
f, *f.domainMapperStateless, equationOfState, gravityContext
|
||||
);
|
||||
|
||||
preparedOperator.Prepare({.enthalpy = enthalpy}, dependencies, rotation);
|
||||
|
||||
mfem::Vector densityAction;
|
||||
mfem::Vector displacementAction;
|
||||
mfem::Vector gravityAction;
|
||||
mfem::Vector enthalpyAction;
|
||||
mfem::Vector completeAction;
|
||||
|
||||
preparedOperator.ApplyDensityJacobianAction(densityDirection, densityAction);
|
||||
|
||||
preparedOperator.ApplyDisplacementJacobianAction(displacementDirection, displacementAction);
|
||||
|
||||
preparedOperator.ApplyGravityGradientJacobianAction(gravityDirection, gravityAction);
|
||||
|
||||
preparedOperator.ApplyEnthalpyJacobianAction(enthalpyDirection, enthalpyAction);
|
||||
|
||||
preparedOperator.ApplyCompleteJacobianAction(
|
||||
densityDirection, displacementDirection, gravityDirection, enthalpyDirection, completeAction
|
||||
);
|
||||
|
||||
mfem::Vector expectedDensity;
|
||||
mfem::Vector expectedRotationDensity;
|
||||
|
||||
preparedOperator.GetGravityOperator().ApplyDensityJacobianAction(densityDirection, expectedDensity);
|
||||
|
||||
preparedOperator.GetRotationalOperator().ApplyDensityJacobianAction(densityDirection, expectedRotationDensity);
|
||||
|
||||
expectedDensity += expectedRotationDensity;
|
||||
|
||||
mfem::Vector expectedDisplacement;
|
||||
mfem::Vector expectedGravityDisplacement;
|
||||
mfem::Vector expectedRotationDisplacement;
|
||||
|
||||
preparedOperator.GetPressureOperator().ApplyDisplacementJacobianAction(displacementDirection, expectedDisplacement);
|
||||
|
||||
preparedOperator.GetGravityOperator().ApplyDisplacementJacobianAction(
|
||||
displacementDirection, expectedGravityDisplacement
|
||||
);
|
||||
|
||||
preparedOperator.GetRotationalOperator().ApplyDisplacementJacobianAction(
|
||||
displacementDirection, expectedRotationDisplacement
|
||||
);
|
||||
|
||||
expectedDisplacement += expectedGravityDisplacement;
|
||||
expectedDisplacement += expectedRotationDisplacement;
|
||||
|
||||
mfem::Vector expectedGravity;
|
||||
preparedOperator.GetGravityOperator().ApplyGravityGradientJacobianAction(gravityDirection, expectedGravity);
|
||||
|
||||
mfem::Vector expectedEnthalpy;
|
||||
preparedOperator.GetPressureOperator().ApplyEnthalpyJacobianAction(enthalpyDirection, expectedEnthalpy);
|
||||
|
||||
mfem::Vector summedColumns(densityAction);
|
||||
summedColumns += displacementAction;
|
||||
summedColumns += gravityAction;
|
||||
summedColumns += enthalpyAction;
|
||||
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
|
||||
CHECK(
|
||||
prepared_displacement_residual_test_utils::relative_difference(densityAction, expectedDensity, communicator) <
|
||||
2.0e-15
|
||||
);
|
||||
|
||||
CHECK(
|
||||
prepared_displacement_residual_test_utils::relative_difference(
|
||||
displacementAction, expectedDisplacement, communicator
|
||||
) < 2.0e-15
|
||||
);
|
||||
|
||||
CHECK(
|
||||
prepared_displacement_residual_test_utils::relative_difference(gravityAction, expectedGravity, communicator) <
|
||||
2.0e-15
|
||||
);
|
||||
|
||||
CHECK(
|
||||
prepared_displacement_residual_test_utils::relative_difference(enthalpyAction, expectedEnthalpy, communicator) <
|
||||
2.0e-15
|
||||
);
|
||||
|
||||
CHECK(
|
||||
prepared_displacement_residual_test_utils::relative_difference(completeAction, summedColumns, communicator) <
|
||||
2.0e-15
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Displacement Residual Jacobian Matches A Simultaneous "
|
||||
"Centered Difference On Deformed Geometry",
|
||||
tags::barotrope &tags::prepared &tags::jacobian &tags::accuracy &tags::geometry
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const mfem::Vector baseDensity = prepared_displacement_residual_test_utils::make_density(f, 0.41);
|
||||
|
||||
const mfem::Vector baseDisplacement = gravity_prepared_test_utils::make_displacement(f, 0.79);
|
||||
|
||||
const mfem::Vector baseGravity = prepared_displacement_residual_test_utils::make_gravity_gradient(f, 0.63);
|
||||
|
||||
const mfem::Vector baseEnthalpy = prepared_displacement_residual_test_utils::make_positive_enthalpy(f, 0.67);
|
||||
|
||||
const mfem::Vector densityDirection = prepared_displacement_residual_test_utils::make_density_direction(f, 0.73);
|
||||
|
||||
const mfem::Vector displacementDirection =
|
||||
prepared_displacement_residual_test_utils::make_displacement_direction(f);
|
||||
|
||||
const mfem::Vector gravityDirection =
|
||||
prepared_displacement_residual_test_utils::make_gravity_gradient_direction(f, 0.89);
|
||||
|
||||
const mfem::Vector enthalpyDirection = prepared_displacement_residual_test_utils::make_enthalpy_direction(f, 1.01);
|
||||
|
||||
mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize());
|
||||
gravityPotential = 0.0;
|
||||
|
||||
auto dependencies = prepared_displacement_residual_test_utils::make_dependencies();
|
||||
|
||||
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
|
||||
prepared_displacement_residual_test_utils::prepare_gravity_context(
|
||||
gravityContext, baseDensity, baseDisplacement, baseGravity, gravityPotential, dependencies, 19
|
||||
);
|
||||
|
||||
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
||||
const mean_field::physics::RigidRotation rotation = prepared_displacement_residual_test_utils::make_rotation(0.87);
|
||||
|
||||
mean_field::operators::PreparedDisplacementResidualOperator preparedOperator(
|
||||
f, *f.domainMapperStateless, equationOfState, gravityContext
|
||||
);
|
||||
|
||||
preparedOperator.Prepare({.enthalpy = baseEnthalpy}, dependencies, rotation);
|
||||
|
||||
mfem::Vector jacobianAction;
|
||||
|
||||
preparedOperator.ApplyCompleteJacobianAction(
|
||||
densityDirection, displacementDirection, gravityDirection, enthalpyDirection, jacobianAction
|
||||
);
|
||||
|
||||
constexpr double step = 1.0e-5;
|
||||
|
||||
mfem::Vector plusDensity(baseDensity);
|
||||
plusDensity.Add(step, densityDirection);
|
||||
|
||||
mfem::Vector minusDensity(baseDensity);
|
||||
minusDensity.Add(-step, densityDirection);
|
||||
|
||||
mfem::Vector plusDisplacement(baseDisplacement);
|
||||
plusDisplacement.Add(step, displacementDirection);
|
||||
|
||||
mfem::Vector minusDisplacement(baseDisplacement);
|
||||
minusDisplacement.Add(-step, displacementDirection);
|
||||
|
||||
mfem::Vector plusGravity(baseGravity);
|
||||
plusGravity.Add(step, gravityDirection);
|
||||
|
||||
mfem::Vector minusGravity(baseGravity);
|
||||
minusGravity.Add(-step, gravityDirection);
|
||||
|
||||
mfem::Vector plusEnthalpy(baseEnthalpy);
|
||||
plusEnthalpy.Add(step, enthalpyDirection);
|
||||
|
||||
mfem::Vector minusEnthalpy(baseEnthalpy);
|
||||
minusEnthalpy.Add(-step, enthalpyDirection);
|
||||
|
||||
++dependencies.density.revision;
|
||||
++dependencies.displacement.revision;
|
||||
++dependencies.gravityGradient.revision;
|
||||
++dependencies.enthalpy.revision;
|
||||
|
||||
prepared_displacement_residual_test_utils::prepare_gravity_context(
|
||||
gravityContext, plusDensity, plusDisplacement, plusGravity, gravityPotential, dependencies, 19
|
||||
);
|
||||
|
||||
preparedOperator.Prepare({.enthalpy = plusEnthalpy}, dependencies, rotation);
|
||||
|
||||
mfem::Vector plusResidual;
|
||||
preparedOperator.BuildResidual(plusResidual);
|
||||
|
||||
++dependencies.density.revision;
|
||||
++dependencies.displacement.revision;
|
||||
++dependencies.gravityGradient.revision;
|
||||
++dependencies.enthalpy.revision;
|
||||
|
||||
prepared_displacement_residual_test_utils::prepare_gravity_context(
|
||||
gravityContext, minusDensity, minusDisplacement, minusGravity, gravityPotential, dependencies, 19
|
||||
);
|
||||
|
||||
preparedOperator.Prepare({.enthalpy = minusEnthalpy}, dependencies, rotation);
|
||||
|
||||
mfem::Vector minusResidual;
|
||||
preparedOperator.BuildResidual(minusResidual);
|
||||
|
||||
plusResidual -= minusResidual;
|
||||
plusResidual /= 2.0 * step;
|
||||
|
||||
const double centeredDifferenceError =
|
||||
prepared_displacement_residual_test_utils::relative_difference(jacobianAction, plusResidual, f.mesh->GetComm());
|
||||
|
||||
INFO("Composite simultaneous centered-difference error = " << centeredDifferenceError);
|
||||
|
||||
CHECK(centeredDifferenceError < 8.0e-8);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Displacement Residual MFEM Adapter Routes Only R-d",
|
||||
tags::barotrope &tags::prepared &tags::jacobian &tags::mfem_operators &tags::unit
|
||||
) {
|
||||
using JacobianForm = mean_field::utils::blocks::barotropic_equilibrium_jacobian_form;
|
||||
|
||||
using DisplacementResidualType = mean_field::utils::blocks::displacement::geometry::residual;
|
||||
|
||||
STATIC_REQUIRE(
|
||||
mean_field::utils::blocks::has_jacobian_coupling_v<
|
||||
DisplacementResidualType, mean_field::utils::blocks::density::mass::value, JacobianForm>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
mean_field::utils::blocks::has_jacobian_coupling_v<
|
||||
DisplacementResidualType, mean_field::utils::blocks::displacement::geometry::value, JacobianForm>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
mean_field::utils::blocks::has_jacobian_coupling_v<
|
||||
DisplacementResidualType, mean_field::utils::blocks::gravity::gradient::value, JacobianForm>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
mean_field::utils::blocks::has_jacobian_coupling_v<
|
||||
DisplacementResidualType, mean_field::utils::blocks::enthalpy::specific::value, JacobianForm>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE_FALSE(
|
||||
mean_field::utils::blocks::has_jacobian_coupling_v<
|
||||
DisplacementResidualType, mean_field::utils::blocks::gravity::poisson::value, JacobianForm>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE_FALSE(
|
||||
mean_field::utils::blocks::has_jacobian_coupling_v<
|
||||
DisplacementResidualType, mean_field::utils::blocks::barotropic_constant::mass_normalization::value,
|
||||
JacobianForm>
|
||||
);
|
||||
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const mfem::Vector density = prepared_displacement_residual_test_utils::make_density(f, 0.43);
|
||||
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.71);
|
||||
|
||||
const mfem::Vector gravityGradient = prepared_displacement_residual_test_utils::make_gravity_gradient(f, 0.57);
|
||||
|
||||
mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize());
|
||||
gravityPotential = 0.0;
|
||||
|
||||
const mfem::Vector enthalpy = prepared_displacement_residual_test_utils::make_positive_enthalpy(f, 0.69);
|
||||
|
||||
const mfem::Vector densityDirection = prepared_displacement_residual_test_utils::make_density_direction(f, 0.77);
|
||||
|
||||
const mfem::Vector displacementDirection =
|
||||
prepared_displacement_residual_test_utils::make_displacement_direction(f);
|
||||
|
||||
const mfem::Vector gravityDirection =
|
||||
prepared_displacement_residual_test_utils::make_gravity_gradient_direction(f, 0.93);
|
||||
|
||||
const mfem::Vector enthalpyDirection = prepared_displacement_residual_test_utils::make_enthalpy_direction(f, 1.03);
|
||||
|
||||
const auto dependencies = prepared_displacement_residual_test_utils::make_dependencies();
|
||||
|
||||
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
|
||||
prepared_displacement_residual_test_utils::prepare_gravity_context(
|
||||
gravityContext, density, displacement, gravityGradient, gravityPotential, dependencies, 19
|
||||
);
|
||||
|
||||
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
||||
const mean_field::physics::RigidRotation rotation = prepared_displacement_residual_test_utils::make_rotation(0.95);
|
||||
|
||||
mean_field::operators::PreparedDisplacementResidualOperator preparedOperator(
|
||||
f, *f.domainMapperStateless, equationOfState, gravityContext
|
||||
);
|
||||
|
||||
preparedOperator.Prepare({.enthalpy = enthalpy}, dependencies, rotation);
|
||||
|
||||
const mean_field::operators::DisplacementResidualLayout layout =
|
||||
prepared_displacement_residual_test_utils::make_layout(f);
|
||||
|
||||
mean_field::operators::PreparedDisplacementResidualJacobianOperator adapter(layout, preparedOperator);
|
||||
|
||||
CHECK(adapter.Width() == layout.value_offsets().Last());
|
||||
CHECK(adapter.Height() == layout.residual_offsets().Last());
|
||||
CHECK(
|
||||
layout.size(prepared_displacement_residual_test_utils::enthalpyValue) ==
|
||||
preparedOperator.GetPressureOperator().GetEnthalpySize()
|
||||
);
|
||||
|
||||
mfem::BlockVector direction(layout.value_offsets());
|
||||
direction = 0.0;
|
||||
|
||||
direction.GetBlock(prepared_displacement_residual_test_utils::densityValue) = densityDirection;
|
||||
|
||||
direction.GetBlock(prepared_displacement_residual_test_utils::displacementValue) = displacementDirection;
|
||||
|
||||
direction.GetBlock(prepared_displacement_residual_test_utils::gravityGradientValue) = gravityDirection;
|
||||
|
||||
direction.GetBlock(prepared_displacement_residual_test_utils::enthalpyValue) = enthalpyDirection;
|
||||
|
||||
direction.GetBlock(prepared_displacement_residual_test_utils::gravityPotentialValue) = 0.59;
|
||||
|
||||
direction.GetBlock(prepared_displacement_residual_test_utils::barotropicConstantValue) = -0.73;
|
||||
|
||||
mfem::Vector expectedDisplacementAction;
|
||||
|
||||
preparedOperator.ApplyCompleteJacobianAction(
|
||||
densityDirection, displacementDirection, gravityDirection, enthalpyDirection, expectedDisplacementAction
|
||||
);
|
||||
|
||||
mfem::Vector action;
|
||||
adapter.Mult(direction, action);
|
||||
|
||||
const mfem::Vector routedDisplacement = prepared_displacement_residual_test_utils::copy_residual_block(
|
||||
action, layout, prepared_displacement_residual_test_utils::displacementResidual
|
||||
);
|
||||
|
||||
CHECK(
|
||||
prepared_displacement_residual_test_utils::relative_difference(
|
||||
routedDisplacement, expectedDisplacementAction, f.mesh->GetComm()
|
||||
) < 2.0e-15
|
||||
);
|
||||
|
||||
const mfem::Vector gravityGradientBlock = prepared_displacement_residual_test_utils::copy_residual_block(
|
||||
action, layout, prepared_displacement_residual_test_utils::gravityGradientResidual
|
||||
);
|
||||
|
||||
const mfem::Vector gravityPotentialBlock = prepared_displacement_residual_test_utils::copy_residual_block(
|
||||
action, layout, prepared_displacement_residual_test_utils::gravityPotentialResidual
|
||||
);
|
||||
|
||||
const mfem::Vector densityBlock = prepared_displacement_residual_test_utils::copy_residual_block(
|
||||
action, layout, prepared_displacement_residual_test_utils::densityResidual
|
||||
);
|
||||
|
||||
const mfem::Vector enthalpyBlock = prepared_displacement_residual_test_utils::copy_residual_block(
|
||||
action, layout, prepared_displacement_residual_test_utils::enthalpyResidual
|
||||
);
|
||||
|
||||
const mfem::Vector massBlock = prepared_displacement_residual_test_utils::copy_residual_block(
|
||||
action, layout, prepared_displacement_residual_test_utils::massResidual
|
||||
);
|
||||
|
||||
CHECK(gravity_prepared_test_utils::global_norm(gravityGradientBlock, f.mesh->GetComm()) == 0.0);
|
||||
|
||||
CHECK(gravity_prepared_test_utils::global_norm(gravityPotentialBlock, f.mesh->GetComm()) == 0.0);
|
||||
|
||||
CHECK(gravity_prepared_test_utils::global_norm(densityBlock, f.mesh->GetComm()) == 0.0);
|
||||
|
||||
CHECK(gravity_prepared_test_utils::global_norm(enthalpyBlock, f.mesh->GetComm()) == 0.0);
|
||||
|
||||
CHECK(gravity_prepared_test_utils::global_norm(massBlock, f.mesh->GetComm()) == 0.0);
|
||||
}
|
||||
@@ -16,25 +16,18 @@ TEST_CASE(
|
||||
auto args = test_utils::setup_args();
|
||||
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
operators::PreparedMappedGravitySourceOperator prepared_operator(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
operators::PreparedMappedGravitySourceOperator prepared_operator(f, *f.domainMapperStateless);
|
||||
REQUIRE(prepared_operator.Width() == f.densityFes->GetTrueVSize());
|
||||
REQUIRE(
|
||||
prepared_operator.Height() == f.gravityPotentialFes->GetTrueVSize()
|
||||
);
|
||||
REQUIRE(prepared_operator.Height() == f.gravityPotentialFes->GetTrueVSize());
|
||||
|
||||
const mfem::Vector density = prepared_test::make_deterministic_vector(
|
||||
f.densityFes->GetTrueVSize(), 0.41
|
||||
);
|
||||
const mfem::Vector density = prepared_test::make_deterministic_vector(f.densityFes->GetTrueVSize(), 0.41);
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
|
||||
mfem::Vector identity_action;
|
||||
mfem::Vector deformed_action;
|
||||
|
||||
for (const double deformation_scale : {0.0, 1.0}) {
|
||||
const mfem::Vector displacement =
|
||||
prepared_test::make_displacement(f, deformation_scale);
|
||||
const mfem::Vector displacement = prepared_test::make_displacement(f, deformation_scale);
|
||||
|
||||
prepared_operator.Prepare(displacement);
|
||||
|
||||
@@ -42,23 +35,13 @@ TEST_CASE(
|
||||
mfem::Vector reference_action;
|
||||
|
||||
prepared_operator.Mult(density, prepared_action);
|
||||
operators::kernels::apply_mapped_source(
|
||||
f, *f.domainMapperStateless, density, displacement, reference_action
|
||||
);
|
||||
operators::kernels::apply_mapped_source(f, *f.domainMapperStateless, density, displacement, reference_action);
|
||||
|
||||
const double relative_error = prepared_test::relative_error(
|
||||
prepared_action, reference_action, communicator
|
||||
);
|
||||
const double relative_error = prepared_test::relative_error(prepared_action, reference_action, communicator);
|
||||
|
||||
INFO("Deformation scale = " << deformation_scale);
|
||||
INFO(
|
||||
"Prepared source norm = "
|
||||
<< prepared_test::global_norm(prepared_action, communicator)
|
||||
);
|
||||
INFO(
|
||||
"Reference source norm = "
|
||||
<< prepared_test::global_norm(reference_action, communicator)
|
||||
);
|
||||
INFO("Prepared source norm = " << prepared_test::global_norm(prepared_action, communicator));
|
||||
INFO("Reference source norm = " << prepared_test::global_norm(reference_action, communicator));
|
||||
INFO("Relative prepared-source error = " << relative_error);
|
||||
|
||||
REQUIRE(prepared_operator.IsPrepared());
|
||||
@@ -71,9 +54,7 @@ TEST_CASE(
|
||||
}
|
||||
}
|
||||
|
||||
const double geometry_change = prepared_test::relative_error(
|
||||
deformed_action, identity_action, communicator
|
||||
);
|
||||
const double geometry_change = prepared_test::relative_error(deformed_action, identity_action, communicator);
|
||||
|
||||
INFO("Relative source change under deformation = " << geometry_change);
|
||||
|
||||
@@ -88,28 +69,17 @@ TEST_CASE(
|
||||
auto args = test_utils::setup_args();
|
||||
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
operators::PreparedMappedGravitySourceOperator prepared_operator(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
operators::PreparedMappedGravitySourceOperator prepared_operator(f, *f.domainMapperStateless);
|
||||
REQUIRE(prepared_operator.Width() == f.densityFes->GetTrueVSize());
|
||||
REQUIRE(
|
||||
prepared_operator.Height() == f.gravityPotentialFes->GetTrueVSize()
|
||||
);
|
||||
REQUIRE(prepared_operator.Height() == f.gravityPotentialFes->GetTrueVSize());
|
||||
const mfem::Vector displacement = prepared_test::make_displacement(f, 1.0);
|
||||
prepared_operator.Prepare(displacement);
|
||||
|
||||
const mfem::Vector first = prepared_test::make_deterministic_vector(
|
||||
f.densityFes->GetTrueVSize(), 0.27
|
||||
);
|
||||
const mfem::Vector second = prepared_test::make_deterministic_vector(
|
||||
f.densityFes->GetTrueVSize(), 0.79
|
||||
);
|
||||
const mfem::Vector combination =
|
||||
prepared_test::linear_combination(first, 1.3, second, -0.6);
|
||||
const mfem::Vector stellar_density =
|
||||
prepared_test::make_domain_supported_density(f, true);
|
||||
const mfem::Vector vacuum_density =
|
||||
prepared_test::make_domain_supported_density(f, false);
|
||||
const mfem::Vector first = prepared_test::make_deterministic_vector(f.densityFes->GetTrueVSize(), 0.27);
|
||||
const mfem::Vector second = prepared_test::make_deterministic_vector(f.densityFes->GetTrueVSize(), 0.79);
|
||||
const mfem::Vector combination = prepared_test::linear_combination(first, 1.3, second, -0.6);
|
||||
const mfem::Vector stellar_density = prepared_test::make_domain_supported_density(f, true);
|
||||
const mfem::Vector vacuum_density = prepared_test::make_domain_supported_density(f, false);
|
||||
|
||||
mfem::Vector first_action;
|
||||
mfem::Vector second_action;
|
||||
@@ -123,20 +93,14 @@ TEST_CASE(
|
||||
prepared_operator.Mult(stellar_density, stellar_action);
|
||||
prepared_operator.Mult(vacuum_density, vacuum_action);
|
||||
|
||||
const mfem::Vector expected_combination = prepared_test::linear_combination(
|
||||
first_action, 1.3, second_action, -0.6
|
||||
);
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
const mfem::Vector expected_combination = prepared_test::linear_combination(first_action, 1.3, second_action, -0.6);
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
|
||||
const double linearity_error = prepared_test::relative_error(
|
||||
combination_action, expected_combination, communicator
|
||||
);
|
||||
const double stellar_norm =
|
||||
prepared_test::global_norm(stellar_action, communicator);
|
||||
const double vacuum_norm =
|
||||
prepared_test::global_norm(vacuum_action, communicator);
|
||||
const std::uint64_t preparation_count =
|
||||
prepared_operator.GetPreparationCount();
|
||||
const double linearity_error =
|
||||
prepared_test::relative_error(combination_action, expected_combination, communicator);
|
||||
const double stellar_norm = prepared_test::global_norm(stellar_action, communicator);
|
||||
const double vacuum_norm = prepared_test::global_norm(vacuum_action, communicator);
|
||||
const std::uint64_t preparation_count = prepared_operator.GetPreparationCount();
|
||||
|
||||
mfem::Vector repeated_action;
|
||||
prepared_operator.Mult(first, repeated_action);
|
||||
@@ -148,10 +112,6 @@ TEST_CASE(
|
||||
CHECK_THAT(linearity_error, WithinAbs(0.0, 2.0e-12));
|
||||
CHECK(stellar_norm > 0.0);
|
||||
CHECK(vacuum_norm <= 1.0e-13 * stellar_norm);
|
||||
CHECK(
|
||||
prepared_test::relative_error(
|
||||
repeated_action, first_action, communicator
|
||||
) < 2.0e-14
|
||||
);
|
||||
CHECK(prepared_test::relative_error(repeated_action, first_action, communicator) < 2.0e-14);
|
||||
CHECK(prepared_operator.GetPreparationCount() == preparation_count);
|
||||
}
|
||||
|
||||
@@ -16,22 +16,17 @@ TEST_CASE(
|
||||
auto args = test_utils::setup_args();
|
||||
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
operators::PreparedMappedHDivMassOperator prepared_operator(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
operators::PreparedMappedHDivMassOperator prepared_operator(f, *f.domainMapperStateless);
|
||||
|
||||
const mfem::Vector gravity_gradient =
|
||||
prepared_test::make_deterministic_vector(
|
||||
f.gravityFluxFes->GetTrueVSize(), 0.21
|
||||
);
|
||||
prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(), 0.21);
|
||||
const MPI_Comm communicator = f.gravityFluxFes->GetComm();
|
||||
|
||||
mfem::Vector identity_action;
|
||||
mfem::Vector deformed_action;
|
||||
|
||||
for (const double deformation_scale : {0.0, 1.0}) {
|
||||
const mfem::Vector displacement =
|
||||
prepared_test::make_displacement(f, deformation_scale);
|
||||
const mfem::Vector displacement = prepared_test::make_displacement(f, deformation_scale);
|
||||
|
||||
prepared_operator.Prepare(displacement);
|
||||
|
||||
@@ -40,23 +35,14 @@ TEST_CASE(
|
||||
|
||||
prepared_operator.Mult(gravity_gradient, prepared_action);
|
||||
operators::kernels::apply_mapped_hdiv_mass(
|
||||
f, *f.domainMapperStateless, gravity_gradient, displacement,
|
||||
reference_action
|
||||
f, *f.domainMapperStateless, gravity_gradient, displacement, reference_action
|
||||
);
|
||||
|
||||
const double relative_error = prepared_test::relative_error(
|
||||
prepared_action, reference_action, communicator
|
||||
);
|
||||
const double relative_error = prepared_test::relative_error(prepared_action, reference_action, communicator);
|
||||
|
||||
INFO("Deformation scale = " << deformation_scale);
|
||||
INFO(
|
||||
"Prepared action norm = "
|
||||
<< prepared_test::global_norm(prepared_action, communicator)
|
||||
);
|
||||
INFO(
|
||||
"Reference action norm = "
|
||||
<< prepared_test::global_norm(reference_action, communicator)
|
||||
);
|
||||
INFO("Prepared action norm = " << prepared_test::global_norm(prepared_action, communicator));
|
||||
INFO("Reference action norm = " << prepared_test::global_norm(reference_action, communicator));
|
||||
INFO("Relative prepared-operator error = " << relative_error);
|
||||
|
||||
REQUIRE(prepared_operator.IsPrepared());
|
||||
@@ -69,9 +55,7 @@ TEST_CASE(
|
||||
}
|
||||
}
|
||||
|
||||
const double geometry_change = prepared_test::relative_error(
|
||||
deformed_action, identity_action, communicator
|
||||
);
|
||||
const double geometry_change = prepared_test::relative_error(deformed_action, identity_action, communicator);
|
||||
|
||||
INFO("Relative action change under deformation = " << geometry_change);
|
||||
|
||||
@@ -86,20 +70,13 @@ TEST_CASE(
|
||||
auto args = test_utils::setup_args();
|
||||
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
operators::PreparedMappedHDivMassOperator prepared_operator(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
operators::PreparedMappedHDivMassOperator prepared_operator(f, *f.domainMapperStateless);
|
||||
const mfem::Vector displacement = prepared_test::make_displacement(f, 1.0);
|
||||
prepared_operator.Prepare(displacement);
|
||||
|
||||
const mfem::Vector first = prepared_test::make_deterministic_vector(
|
||||
f.gravityFluxFes->GetTrueVSize(), 0.17
|
||||
);
|
||||
const mfem::Vector second = prepared_test::make_deterministic_vector(
|
||||
f.gravityFluxFes->GetTrueVSize(), 0.83
|
||||
);
|
||||
const mfem::Vector combination =
|
||||
prepared_test::linear_combination(first, 1.7, second, -0.4);
|
||||
const mfem::Vector first = prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(), 0.17);
|
||||
const mfem::Vector second = prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(), 0.83);
|
||||
const mfem::Vector combination = prepared_test::linear_combination(first, 1.7, second, -0.4);
|
||||
|
||||
mfem::Vector first_action;
|
||||
mfem::Vector second_action;
|
||||
@@ -110,32 +87,22 @@ TEST_CASE(
|
||||
prepared_operator.Mult(second, second_action);
|
||||
prepared_operator.Mult(combination, combination_action);
|
||||
|
||||
mfem::Vector expected_combination = prepared_test::linear_combination(
|
||||
first_action, 1.7, second_action, -0.4
|
||||
);
|
||||
mfem::Vector expected_combination = prepared_test::linear_combination(first_action, 1.7, second_action, -0.4);
|
||||
|
||||
mfem::Vector zero(first.Size());
|
||||
zero = 0.0;
|
||||
prepared_operator.Mult(zero, zero_action);
|
||||
|
||||
const MPI_Comm communicator = f.gravityFluxFes->GetComm();
|
||||
const MPI_Comm communicator = f.gravityFluxFes->GetComm();
|
||||
|
||||
const double first_second_product =
|
||||
prepared_test::global_dot(first, second_action, communicator);
|
||||
const double second_first_product =
|
||||
prepared_test::global_dot(second, first_action, communicator);
|
||||
const double symmetry_error = prepared_test::relative_scalar_error(
|
||||
first_second_product, second_first_product
|
||||
);
|
||||
const double linearity_error = prepared_test::relative_error(
|
||||
combination_action, expected_combination, communicator
|
||||
);
|
||||
const double first_energy =
|
||||
prepared_test::global_dot(first, first_action, communicator);
|
||||
const double second_energy =
|
||||
prepared_test::global_dot(second, second_action, communicator);
|
||||
const std::uint64_t preparation_count =
|
||||
prepared_operator.GetPreparationCount();
|
||||
const double first_second_product = prepared_test::global_dot(first, second_action, communicator);
|
||||
const double second_first_product = prepared_test::global_dot(second, first_action, communicator);
|
||||
const double symmetry_error = prepared_test::relative_scalar_error(first_second_product, second_first_product);
|
||||
const double linearity_error =
|
||||
prepared_test::relative_error(combination_action, expected_combination, communicator);
|
||||
const double first_energy = prepared_test::global_dot(first, first_action, communicator);
|
||||
const double second_energy = prepared_test::global_dot(second, second_action, communicator);
|
||||
const std::uint64_t preparation_count = prepared_operator.GetPreparationCount();
|
||||
|
||||
mfem::Vector repeated_action;
|
||||
prepared_operator.Mult(first, repeated_action);
|
||||
@@ -149,16 +116,9 @@ TEST_CASE(
|
||||
|
||||
CHECK_THAT(symmetry_error, WithinAbs(0.0, 2.0e-12));
|
||||
CHECK_THAT(linearity_error, WithinAbs(0.0, 2.0e-12));
|
||||
CHECK_THAT(
|
||||
prepared_test::global_norm(zero_action, communicator),
|
||||
WithinAbs(0.0, 1.0e-14)
|
||||
);
|
||||
CHECK_THAT(prepared_test::global_norm(zero_action, communicator), WithinAbs(0.0, 1.0e-14));
|
||||
CHECK(first_energy > 0.0);
|
||||
CHECK(second_energy > 0.0);
|
||||
CHECK(
|
||||
prepared_test::relative_error(
|
||||
repeated_action, first_action, communicator
|
||||
) < 2.0e-14
|
||||
);
|
||||
CHECK(prepared_test::relative_error(repeated_action, first_action, communicator) < 2.0e-14);
|
||||
CHECK(prepared_operator.GetPreparationCount() == preparation_count);
|
||||
}
|
||||
@@ -5,9 +5,7 @@ import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace prepared_hydrostatic_test_utils {
|
||||
static mean_field::operators::context::hydrostatic::
|
||||
HydrostaticEquilibriumDependencies
|
||||
make_dependencies() {
|
||||
static mean_field::operators::context::hydrostatic::HydrostaticEquilibriumDependencies make_dependencies() {
|
||||
return {
|
||||
.discretization = {.identity = 211, .revision = 2},
|
||||
.enthalpy = {.identity = 223, .revision = 3},
|
||||
@@ -18,8 +16,7 @@ namespace prepared_hydrostatic_test_utils {
|
||||
};
|
||||
}
|
||||
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView
|
||||
make_state(
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView make_state(
|
||||
const mfem::Vector &enthalpy,
|
||||
const mfem::Vector &gravityPotential,
|
||||
const mfem::Vector &displacement,
|
||||
@@ -37,18 +34,14 @@ namespace prepared_hydrostatic_test_utils {
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase = 0.19
|
||||
) {
|
||||
return gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.enthalpyFes->GetTrueVSize(), phase
|
||||
);
|
||||
return gravity_prepared_test_utils::make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), phase);
|
||||
}
|
||||
|
||||
mfem::Vector make_gravity_potential(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase = 0.37
|
||||
) {
|
||||
return gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.gravityPotentialFes->GetTrueVSize(), phase
|
||||
);
|
||||
return gravity_prepared_test_utils::make_deterministic_vector(f.gravityPotentialFes->GetTrueVSize(), phase);
|
||||
}
|
||||
|
||||
mean_field::physics::RigidRotation make_rotation(const double scale = 1.0) {
|
||||
@@ -72,39 +65,30 @@ TEST_CASE(
|
||||
"Prepared Hydrostatic Residual Matches Stateless Kernel",
|
||||
tags::barotrope &tags::hydro &tags::prepared &tags::residuals &tags::unit
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
auto args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator
|
||||
preparedOperator(f, *f.domainMapperStateless);
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator preparedOperator(f, *f.domainMapperStateless);
|
||||
|
||||
const mfem::Vector enthalpy =
|
||||
prepared_hydrostatic_test_utils::make_enthalpy(f);
|
||||
const mfem::Vector enthalpy = prepared_hydrostatic_test_utils::make_enthalpy(f);
|
||||
|
||||
const mfem::Vector gravityPotential =
|
||||
prepared_hydrostatic_test_utils::make_gravity_potential(f);
|
||||
const mfem::Vector gravityPotential = prepared_hydrostatic_test_utils::make_gravity_potential(f);
|
||||
|
||||
const mfem::Vector displacement =
|
||||
gravity_prepared_test_utils::make_displacement(f, 0.73);
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.73);
|
||||
|
||||
constexpr double bernoulliConstant = 0.41;
|
||||
constexpr double bernoulliConstant = 0.41;
|
||||
|
||||
const mean_field::physics::RigidRotation rotation =
|
||||
prepared_hydrostatic_test_utils::make_rotation();
|
||||
const mean_field::physics::RigidRotation rotation = prepared_hydrostatic_test_utils::make_rotation();
|
||||
|
||||
const auto dependencies =
|
||||
prepared_hydrostatic_test_utils::make_dependencies();
|
||||
const auto dependencies = prepared_hydrostatic_test_utils::make_dependencies();
|
||||
|
||||
CHECK_FALSE(preparedOperator.IsPrepared());
|
||||
CHECK(preparedOperator.GetResidualPreparationCount() == 0);
|
||||
CHECK(preparedOperator.GetResidualApplicationCount() == 0);
|
||||
|
||||
const auto report = preparedOperator.Prepare(
|
||||
prepared_hydrostatic_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement, bernoulliConstant
|
||||
),
|
||||
prepared_hydrostatic_test_utils::make_state(enthalpy, gravityPotential, displacement, bernoulliConstant),
|
||||
dependencies, rotation
|
||||
);
|
||||
|
||||
@@ -114,13 +98,12 @@ TEST_CASE(
|
||||
preparedOperator.BuildResidual(preparedResidual);
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, gravityPotential,
|
||||
displacement, bernoulliConstant, referenceResidual
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, gravityPotential, displacement, bernoulliConstant,
|
||||
referenceResidual
|
||||
);
|
||||
|
||||
const double relativeError = gravity_prepared_test_utils::relative_error(
|
||||
preparedResidual, referenceResidual, f.mesh->GetComm()
|
||||
);
|
||||
const double relativeError =
|
||||
gravity_prepared_test_utils::relative_error(preparedResidual, referenceResidual, f.mesh->GetComm());
|
||||
|
||||
INFO("Prepared hydrostatic residual relative error = " << relativeError);
|
||||
|
||||
@@ -150,41 +133,33 @@ TEST_CASE(
|
||||
"Prepared Hydrostatic Residual Reuses And Selectively Rebuilds Data",
|
||||
tags::barotrope &tags::hydro &tags::prepared &tags::residuals &tags::unit
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
auto args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator
|
||||
preparedOperator(f, *f.domainMapperStateless);
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator preparedOperator(f, *f.domainMapperStateless);
|
||||
|
||||
mfem::Vector enthalpy = prepared_hydrostatic_test_utils::make_enthalpy(f);
|
||||
mfem::Vector enthalpy = prepared_hydrostatic_test_utils::make_enthalpy(f);
|
||||
|
||||
mfem::Vector gravityPotential =
|
||||
prepared_hydrostatic_test_utils::make_gravity_potential(f);
|
||||
mfem::Vector gravityPotential = prepared_hydrostatic_test_utils::make_gravity_potential(f);
|
||||
|
||||
mfem::Vector displacement =
|
||||
gravity_prepared_test_utils::make_displacement(f, 0.42);
|
||||
mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.42);
|
||||
|
||||
double bernoulliConstant = 0.36;
|
||||
double bernoulliConstant = 0.36;
|
||||
|
||||
const mfem::Vector initialEnthalpy(enthalpy);
|
||||
const mfem::Vector initialGravityPotential(gravityPotential);
|
||||
const mfem::Vector initialDisplacement(displacement);
|
||||
const double initialBernoulliConstant = bernoulliConstant;
|
||||
const double initialBernoulliConstant = bernoulliConstant;
|
||||
|
||||
const mean_field::physics::RigidRotation initialRotation =
|
||||
prepared_hydrostatic_test_utils::make_rotation(0.80);
|
||||
const mean_field::physics::RigidRotation initialRotation = prepared_hydrostatic_test_utils::make_rotation(0.80);
|
||||
|
||||
const mean_field::physics::RigidRotation changedRotation =
|
||||
prepared_hydrostatic_test_utils::make_rotation(1.25);
|
||||
const mean_field::physics::RigidRotation changedRotation = prepared_hydrostatic_test_utils::make_rotation(1.25);
|
||||
|
||||
auto dependencies = prepared_hydrostatic_test_utils::make_dependencies();
|
||||
auto dependencies = prepared_hydrostatic_test_utils::make_dependencies();
|
||||
|
||||
preparedOperator.Prepare(
|
||||
prepared_hydrostatic_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement, bernoulliConstant
|
||||
),
|
||||
prepared_hydrostatic_test_utils::make_state(enthalpy, gravityPotential, displacement, bernoulliConstant),
|
||||
dependencies, initialRotation
|
||||
);
|
||||
|
||||
@@ -197,9 +172,7 @@ TEST_CASE(
|
||||
bernoulliConstant += 0.23;
|
||||
|
||||
const auto unchangedReport = preparedOperator.Prepare(
|
||||
prepared_hydrostatic_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement, bernoulliConstant
|
||||
),
|
||||
prepared_hydrostatic_test_utils::make_state(enthalpy, gravityPotential, displacement, bernoulliConstant),
|
||||
dependencies, changedRotation
|
||||
);
|
||||
|
||||
@@ -209,11 +182,7 @@ TEST_CASE(
|
||||
CHECK_FALSE(unchangedReport.DidAnyWork());
|
||||
CHECK_FALSE(unchangedReport.updatedRotation);
|
||||
CHECK_FALSE(unchangedReport.preparedResidual);
|
||||
CHECK(
|
||||
gravity_prepared_test_utils::relative_error(
|
||||
unchangedResidual, initialResidual, f.mesh->GetComm()
|
||||
) == 0.0
|
||||
);
|
||||
CHECK(gravity_prepared_test_utils::relative_error(unchangedResidual, initialResidual, f.mesh->GetComm()) == 0.0);
|
||||
CHECK(preparedOperator.GetResidualPreparationCount() == 1);
|
||||
|
||||
// Only the enthalpy stamp changes. The altered potential,
|
||||
@@ -221,9 +190,7 @@ TEST_CASE(
|
||||
++dependencies.enthalpy.revision;
|
||||
|
||||
const auto enthalpyReport = preparedOperator.Prepare(
|
||||
prepared_hydrostatic_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement, bernoulliConstant
|
||||
),
|
||||
prepared_hydrostatic_test_utils::make_state(enthalpy, gravityPotential, displacement, bernoulliConstant),
|
||||
dependencies, changedRotation
|
||||
);
|
||||
|
||||
@@ -233,9 +200,8 @@ TEST_CASE(
|
||||
preparedOperator.BuildResidual(enthalpyResidual);
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, initialRotation, enthalpy,
|
||||
initialGravityPotential, initialDisplacement, initialBernoulliConstant,
|
||||
enthalpyReference
|
||||
f, *f.domainMapperStateless, initialRotation, enthalpy, initialGravityPotential, initialDisplacement,
|
||||
initialBernoulliConstant, enthalpyReference
|
||||
);
|
||||
|
||||
CHECK_FALSE(enthalpyReport.contextReport.preparedStaticDependencies);
|
||||
@@ -245,17 +211,13 @@ TEST_CASE(
|
||||
CHECK_FALSE(enthalpyReport.updatedRotation);
|
||||
CHECK(enthalpyReport.preparedResidual);
|
||||
CHECK(
|
||||
gravity_prepared_test_utils::relative_error(
|
||||
enthalpyResidual, enthalpyReference, f.mesh->GetComm()
|
||||
) < 2.0e-12
|
||||
gravity_prepared_test_utils::relative_error(enthalpyResidual, enthalpyReference, f.mesh->GetComm()) < 2.0e-12
|
||||
);
|
||||
|
||||
++dependencies.rotation.revision;
|
||||
|
||||
const auto rotationReport = preparedOperator.Prepare(
|
||||
prepared_hydrostatic_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement, bernoulliConstant
|
||||
),
|
||||
prepared_hydrostatic_test_utils::make_state(enthalpy, gravityPotential, displacement, bernoulliConstant),
|
||||
dependencies, changedRotation
|
||||
);
|
||||
|
||||
@@ -265,9 +227,8 @@ TEST_CASE(
|
||||
preparedOperator.BuildResidual(rotationResidual);
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, changedRotation, enthalpy,
|
||||
initialGravityPotential, initialDisplacement, initialBernoulliConstant,
|
||||
rotationReference
|
||||
f, *f.domainMapperStateless, changedRotation, enthalpy, initialGravityPotential, initialDisplacement,
|
||||
initialBernoulliConstant, rotationReference
|
||||
);
|
||||
|
||||
CHECK_FALSE(rotationReport.contextReport.preparedStaticDependencies);
|
||||
@@ -277,17 +238,13 @@ TEST_CASE(
|
||||
CHECK(rotationReport.updatedRotation);
|
||||
CHECK(rotationReport.preparedResidual);
|
||||
CHECK(
|
||||
gravity_prepared_test_utils::relative_error(
|
||||
rotationResidual, rotationReference, f.mesh->GetComm()
|
||||
) < 2.0e-12
|
||||
gravity_prepared_test_utils::relative_error(rotationResidual, rotationReference, f.mesh->GetComm()) < 2.0e-12
|
||||
);
|
||||
|
||||
++dependencies.displacement.revision;
|
||||
|
||||
const auto displacementReport = preparedOperator.Prepare(
|
||||
prepared_hydrostatic_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement, bernoulliConstant
|
||||
),
|
||||
prepared_hydrostatic_test_utils::make_state(enthalpy, gravityPotential, displacement, bernoulliConstant),
|
||||
dependencies, changedRotation
|
||||
);
|
||||
|
||||
@@ -297,9 +254,8 @@ TEST_CASE(
|
||||
preparedOperator.BuildResidual(displacementResidual);
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, changedRotation, enthalpy,
|
||||
initialGravityPotential, displacement, initialBernoulliConstant,
|
||||
displacementReference
|
||||
f, *f.domainMapperStateless, changedRotation, enthalpy, initialGravityPotential, displacement,
|
||||
initialBernoulliConstant, displacementReference
|
||||
);
|
||||
|
||||
CHECK_FALSE(displacementReport.contextReport.preparedStaticDependencies);
|
||||
@@ -309,9 +265,8 @@ TEST_CASE(
|
||||
CHECK_FALSE(displacementReport.updatedRotation);
|
||||
CHECK(displacementReport.preparedResidual);
|
||||
CHECK(
|
||||
gravity_prepared_test_utils::relative_error(
|
||||
displacementResidual, displacementReference, f.mesh->GetComm()
|
||||
) < 2.0e-12
|
||||
gravity_prepared_test_utils::relative_error(displacementResidual, displacementReference, f.mesh->GetComm()) <
|
||||
2.0e-12
|
||||
);
|
||||
|
||||
const auto &statistics = preparedOperator.GetContextPreparationStatistics();
|
||||
@@ -324,9 +279,7 @@ TEST_CASE(
|
||||
CHECK(preparedOperator.GetResidualApplicationCount() == 5);
|
||||
|
||||
const double displacementEffect =
|
||||
gravity_prepared_test_utils::relative_error(
|
||||
displacementResidual, rotationResidual, f.mesh->GetComm()
|
||||
);
|
||||
gravity_prepared_test_utils::relative_error(displacementResidual, rotationResidual, f.mesh->GetComm());
|
||||
|
||||
INFO("Residual change after displacement update = " << displacementEffect);
|
||||
|
||||
|
||||
@@ -24,8 +24,7 @@ namespace prepared_hydrostatic_analytic_solve_test_utils {
|
||||
public:
|
||||
EnthalpyJacobianOperator(
|
||||
const int enthalpySize,
|
||||
const mean_field::operators::PreparedHydrostaticEquilibriumOperator
|
||||
&preparedOperator
|
||||
const mean_field::operators::PreparedHydrostaticEquilibriumOperator &preparedOperator
|
||||
)
|
||||
: mfem::Operator(enthalpySize),
|
||||
m_preparedOperator(preparedOperator) {
|
||||
@@ -39,13 +38,10 @@ namespace prepared_hydrostatic_analytic_solve_test_utils {
|
||||
}
|
||||
|
||||
private:
|
||||
const mean_field::operators::PreparedHydrostaticEquilibriumOperator
|
||||
&m_preparedOperator;
|
||||
const mean_field::operators::PreparedHydrostaticEquilibriumOperator &m_preparedOperator;
|
||||
};
|
||||
|
||||
mean_field::operators::context::hydrostatic::
|
||||
HydrostaticEquilibriumDependencies
|
||||
make_dependencies() {
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumDependencies make_dependencies() {
|
||||
return {
|
||||
.discretization = {.identity = 701, .revision = 2},
|
||||
.enthalpy = {.identity = 709, .revision = 3},
|
||||
@@ -56,8 +52,7 @@ namespace prepared_hydrostatic_analytic_solve_test_utils {
|
||||
};
|
||||
}
|
||||
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView
|
||||
make_state(
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView make_state(
|
||||
const mfem::Vector &enthalpy,
|
||||
const mfem::Vector &gravityPotential,
|
||||
const mfem::Vector &displacement
|
||||
@@ -84,11 +79,9 @@ namespace prepared_hydrostatic_analytic_solve_test_utils {
|
||||
return vector;
|
||||
}
|
||||
|
||||
mean_field::physics::RigidRotation
|
||||
make_rotation(const AnalyticCase &analyticCase) {
|
||||
mean_field::physics::RigidRotation make_rotation(const AnalyticCase &analyticCase) {
|
||||
return mean_field::physics::RigidRotation(
|
||||
make_vector(analyticCase.angularVelocity),
|
||||
make_vector(analyticCase.rotationCenter)
|
||||
make_vector(analyticCase.angularVelocity), make_vector(analyticCase.rotationCenter)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -101,9 +94,7 @@ namespace prepared_hydrostatic_analytic_solve_test_utils {
|
||||
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
physicalPosition(component) =
|
||||
analyticCase
|
||||
.deformationScale[static_cast<std::size_t>(component)] *
|
||||
referencePosition(component);
|
||||
analyticCase.deformationScale[static_cast<std::size_t>(component)] * referencePosition(component);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,11 +102,9 @@ namespace prepared_hydrostatic_analytic_solve_test_utils {
|
||||
double normalizedRadiusSquared = 0.0;
|
||||
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
const double normalizedCoordinate =
|
||||
referencePosition(component) / mean_field::utils::RADIUS;
|
||||
const double normalizedCoordinate = referencePosition(component) / mean_field::utils::RADIUS;
|
||||
|
||||
normalizedRadiusSquared +=
|
||||
normalizedCoordinate * normalizedCoordinate;
|
||||
normalizedRadiusSquared += normalizedCoordinate * normalizedCoordinate;
|
||||
}
|
||||
|
||||
return enthalpyAmplitude * std::max(0.0, 1.0 - normalizedRadiusSquared);
|
||||
@@ -137,20 +126,16 @@ namespace prepared_hydrostatic_analytic_solve_test_utils {
|
||||
*
|
||||
* analytically.
|
||||
*/
|
||||
return bernoulliConstant + rotation.potential(physicalPosition) -
|
||||
exact_enthalpy_value(referencePosition);
|
||||
return bernoulliConstant + rotation.potential(physicalPosition) - exact_enthalpy_value(referencePosition);
|
||||
}
|
||||
|
||||
mfem::Array<int>
|
||||
make_stellar_element_marker(const mean_field::fem::FEM &f) {
|
||||
mfem::Array<int> make_stellar_element_marker(const mean_field::fem::FEM &f) {
|
||||
mfem::Array<int> stellarElementMarker(f.mesh->GetNE());
|
||||
|
||||
const int vacuumAttribute =
|
||||
f.domainMapperStateless->GetVacuumElementAttribute();
|
||||
const int vacuumAttribute = f.domainMapperStateless->GetVacuumElementAttribute();
|
||||
|
||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||
stellarElementMarker[elementId] =
|
||||
f.mesh->GetAttribute(elementId) != vacuumAttribute;
|
||||
stellarElementMarker[elementId] = f.mesh->GetAttribute(elementId) != vacuumAttribute;
|
||||
}
|
||||
|
||||
return stellarElementMarker;
|
||||
@@ -159,9 +144,8 @@ namespace prepared_hydrostatic_analytic_solve_test_utils {
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria",
|
||||
tags::barotrope &tags::hydro &tags::prepared &tags::integration
|
||||
&tags::solver &tags::convergence &tags::accuracy
|
||||
&tags::analytic_comparison
|
||||
tags::barotrope &tags::hydro &tags::prepared &tags::integration &tags::solver &tags::convergence &tags::accuracy
|
||||
&tags::analytic_comparison
|
||||
) {
|
||||
using prepared_hydrostatic_analytic_solve_test_utils::AnalyticCase;
|
||||
|
||||
@@ -191,70 +175,53 @@ TEST_CASE(
|
||||
.rotationCenter = {0.031, -0.024, 0.018}}}
|
||||
};
|
||||
|
||||
auto args = test_utils::setup_args();
|
||||
auto args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
|
||||
const mfem::Array<int> stellarElementMarker =
|
||||
prepared_hydrostatic_analytic_solve_test_utils::
|
||||
make_stellar_element_marker(f);
|
||||
prepared_hydrostatic_analytic_solve_test_utils::make_stellar_element_marker(f);
|
||||
|
||||
for (const AnalyticCase &analyticCase : analyticCases) {
|
||||
DYNAMIC_SECTION(analyticCase.name) {
|
||||
const double deformationDeterminant =
|
||||
analyticCase.deformationScale[0] *
|
||||
analyticCase.deformationScale[1] *
|
||||
analyticCase.deformationScale[2];
|
||||
analyticCase.deformationScale[0] * analyticCase.deformationScale[1] * analyticCase.deformationScale[2];
|
||||
|
||||
REQUIRE(std::abs(deformationDeterminant - 1.0) < 2.0e-14);
|
||||
|
||||
const mean_field::physics::RigidRotation rotation =
|
||||
prepared_hydrostatic_analytic_solve_test_utils::make_rotation(
|
||||
analyticCase
|
||||
);
|
||||
prepared_hydrostatic_analytic_solve_test_utils::make_rotation(analyticCase);
|
||||
|
||||
auto displacementFunction = [&analyticCase](
|
||||
const mfem::Vector
|
||||
&referencePosition,
|
||||
mfem::Vector &displacementValue
|
||||
) {
|
||||
mfem::Vector physicalPosition;
|
||||
auto displacementFunction =
|
||||
[&analyticCase](const mfem::Vector &referencePosition, mfem::Vector &displacementValue) {
|
||||
mfem::Vector physicalPosition;
|
||||
|
||||
prepared_hydrostatic_analytic_solve_test_utils::map_to_physical(
|
||||
referencePosition, analyticCase, physicalPosition
|
||||
);
|
||||
|
||||
displacementValue.SetSize(3);
|
||||
displacementValue = physicalPosition;
|
||||
displacementValue -= referencePosition;
|
||||
};
|
||||
|
||||
auto potentialFunction = [&analyticCase, &rotation](
|
||||
const mfem::Vector &referencePosition
|
||||
) {
|
||||
return prepared_hydrostatic_analytic_solve_test_utils::
|
||||
exact_potential_value(
|
||||
referencePosition, analyticCase, rotation
|
||||
prepared_hydrostatic_analytic_solve_test_utils::map_to_physical(
|
||||
referencePosition, analyticCase, physicalPosition
|
||||
);
|
||||
|
||||
displacementValue.SetSize(3);
|
||||
displacementValue = physicalPosition;
|
||||
displacementValue -= referencePosition;
|
||||
};
|
||||
|
||||
auto potentialFunction = [&analyticCase, &rotation](const mfem::Vector &referencePosition) {
|
||||
return prepared_hydrostatic_analytic_solve_test_utils::exact_potential_value(
|
||||
referencePosition, analyticCase, rotation
|
||||
);
|
||||
};
|
||||
|
||||
auto enthalpyFunction = [](const mfem::Vector &referencePosition) {
|
||||
return prepared_hydrostatic_analytic_solve_test_utils::
|
||||
exact_enthalpy_value(referencePosition);
|
||||
return prepared_hydrostatic_analytic_solve_test_utils::exact_enthalpy_value(referencePosition);
|
||||
};
|
||||
|
||||
mfem::VectorFunctionCoefficient displacementCoefficient(
|
||||
f.mesh->Dimension(), displacementFunction
|
||||
);
|
||||
mfem::VectorFunctionCoefficient displacementCoefficient(f.mesh->Dimension(), displacementFunction);
|
||||
|
||||
mfem::FunctionCoefficient potentialCoefficient(potentialFunction);
|
||||
|
||||
mfem::FunctionCoefficient exactEnthalpyCoefficient(
|
||||
enthalpyFunction
|
||||
);
|
||||
mfem::FunctionCoefficient exactEnthalpyCoefficient(enthalpyFunction);
|
||||
|
||||
/*
|
||||
* Project the prescribed geometry and potential.
|
||||
@@ -284,21 +251,17 @@ TEST_CASE(
|
||||
|
||||
mfem::ParGridFunction zeroEnthalpyField(f.enthalpyFes.get());
|
||||
|
||||
zeroEnthalpyField = 0.0;
|
||||
zeroEnthalpyField = 0.0;
|
||||
|
||||
const double exactEnthalpyNorm = zeroEnthalpyField.ComputeL2Error(
|
||||
exactEnthalpyCoefficient, nullptr, &stellarElementMarker
|
||||
);
|
||||
const double exactEnthalpyNorm =
|
||||
zeroEnthalpyField.ComputeL2Error(exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
|
||||
|
||||
const double projectionError =
|
||||
projectedEnthalpyField.ComputeL2Error(
|
||||
exactEnthalpyCoefficient, nullptr, &stellarElementMarker
|
||||
);
|
||||
projectedEnthalpyField.ComputeL2Error(exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
|
||||
|
||||
REQUIRE(exactEnthalpyNorm > 0.0);
|
||||
|
||||
const double relativeProjectionError =
|
||||
projectionError / exactEnthalpyNorm;
|
||||
const double relativeProjectionError = projectionError / exactEnthalpyNorm;
|
||||
|
||||
/*
|
||||
* Begin deliberately far from equilibrium.
|
||||
@@ -307,16 +270,12 @@ TEST_CASE(
|
||||
|
||||
enthalpy = 0.0;
|
||||
|
||||
auto dependencies = prepared_hydrostatic_analytic_solve_test_utils::
|
||||
make_dependencies();
|
||||
auto dependencies = prepared_hydrostatic_analytic_solve_test_utils::make_dependencies();
|
||||
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator
|
||||
preparedOperator(f, *f.domainMapperStateless);
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator preparedOperator(f, *f.domainMapperStateless);
|
||||
|
||||
const auto initialReport = preparedOperator.Prepare(
|
||||
prepared_hydrostatic_analytic_solve_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement
|
||||
),
|
||||
prepared_hydrostatic_analytic_solve_test_utils::make_state(enthalpy, gravityPotential, displacement),
|
||||
dependencies, rotation
|
||||
);
|
||||
|
||||
@@ -327,10 +286,7 @@ TEST_CASE(
|
||||
|
||||
preparedOperator.BuildResidual(initialResidual);
|
||||
|
||||
const double initialResidualNorm =
|
||||
gravity_prepared_test_utils::global_norm(
|
||||
initialResidual, communicator
|
||||
);
|
||||
const double initialResidualNorm = gravity_prepared_test_utils::global_norm(initialResidual, communicator);
|
||||
|
||||
REQUIRE(initialResidualNorm > 1.0e-12);
|
||||
|
||||
@@ -344,10 +300,9 @@ TEST_CASE(
|
||||
* rotation, and displacement makes this a well-defined
|
||||
* enthalpy solve.
|
||||
*/
|
||||
prepared_hydrostatic_analytic_solve_test_utils::
|
||||
EnthalpyJacobianOperator enthalpyJacobian(
|
||||
f.enthalpyFes->GetTrueVSize(), preparedOperator
|
||||
);
|
||||
prepared_hydrostatic_analytic_solve_test_utils::EnthalpyJacobianOperator enthalpyJacobian(
|
||||
f.enthalpyFes->GetTrueVSize(), preparedOperator
|
||||
);
|
||||
|
||||
mfem::Vector rightHandSide(initialResidual);
|
||||
rightHandSide *= -1.0;
|
||||
@@ -375,9 +330,7 @@ TEST_CASE(
|
||||
|
||||
INFO("Linear solver converged = " << linearSolver.GetConverged());
|
||||
|
||||
INFO(
|
||||
"Linear solver iterations = " << linearSolver.GetNumIterations()
|
||||
);
|
||||
INFO("Linear solver iterations = " << linearSolver.GetNumIterations());
|
||||
|
||||
INFO("Linear solver final norm = " << linearSolver.GetFinalNorm());
|
||||
|
||||
@@ -392,9 +345,7 @@ TEST_CASE(
|
||||
++dependencies.enthalpy.revision;
|
||||
|
||||
const auto solvedReport = preparedOperator.Prepare(
|
||||
prepared_hydrostatic_analytic_solve_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement
|
||||
),
|
||||
prepared_hydrostatic_analytic_solve_test_utils::make_state(enthalpy, gravityPotential, displacement),
|
||||
dependencies, rotation
|
||||
);
|
||||
|
||||
@@ -410,13 +361,9 @@ TEST_CASE(
|
||||
|
||||
preparedOperator.BuildResidual(solvedResidual);
|
||||
|
||||
const double solvedResidualNorm =
|
||||
gravity_prepared_test_utils::global_norm(
|
||||
solvedResidual, communicator
|
||||
);
|
||||
const double solvedResidualNorm = gravity_prepared_test_utils::global_norm(solvedResidual, communicator);
|
||||
|
||||
const double residualReduction =
|
||||
solvedResidualNorm / initialResidualNorm;
|
||||
const double residualReduction = solvedResidualNorm / initialResidualNorm;
|
||||
|
||||
/*
|
||||
* Compare the solved field with the continuum analytic
|
||||
@@ -431,12 +378,9 @@ TEST_CASE(
|
||||
solvedEnthalpyField.SetFromTrueDofs(enthalpy);
|
||||
|
||||
const double solvedAnalyticError =
|
||||
solvedEnthalpyField.ComputeL2Error(
|
||||
exactEnthalpyCoefficient, nullptr, &stellarElementMarker
|
||||
);
|
||||
solvedEnthalpyField.ComputeL2Error(exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
|
||||
|
||||
const double relativeSolvedAnalyticError =
|
||||
solvedAnalyticError / exactEnthalpyNorm;
|
||||
const double relativeSolvedAnalyticError = solvedAnalyticError / exactEnthalpyNorm;
|
||||
|
||||
INFO("Deformation determinant = " << deformationDeterminant);
|
||||
|
||||
@@ -446,15 +390,9 @@ TEST_CASE(
|
||||
|
||||
INFO("Weak residual reduction = " << residualReduction);
|
||||
|
||||
INFO(
|
||||
"Relative analytic projection floor = "
|
||||
<< relativeProjectionError
|
||||
);
|
||||
INFO("Relative analytic projection floor = " << relativeProjectionError);
|
||||
|
||||
INFO(
|
||||
"Relative solved analytic L2 error = "
|
||||
<< relativeSolvedAnalyticError
|
||||
);
|
||||
INFO("Relative solved analytic L2 error = " << relativeSolvedAnalyticError);
|
||||
|
||||
/*
|
||||
* The discrete Bernoulli equation must be solved essentially
|
||||
@@ -469,10 +407,7 @@ TEST_CASE(
|
||||
* also contains potential-projection and mapped-space
|
||||
* compatibility errors.
|
||||
*/
|
||||
CHECK(
|
||||
relativeSolvedAnalyticError <
|
||||
std::max(5.0 * relativeProjectionError, 1.25e-4)
|
||||
);
|
||||
CHECK(relativeSolvedAnalyticError < std::max(5.0 * relativeProjectionError, 1.25e-4));
|
||||
|
||||
/*
|
||||
* Record that the analytic error remains within one order of
|
||||
|
||||
@@ -5,9 +5,7 @@ import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace prepared_hydrostatic_complete_test_utils {
|
||||
mean_field::operators::context::hydrostatic::
|
||||
HydrostaticEquilibriumDependencies
|
||||
make_dependencies() {
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumDependencies make_dependencies() {
|
||||
return {
|
||||
.discretization = {.identity = 503, .revision = 2},
|
||||
.enthalpy = {.identity = 509, .revision = 3},
|
||||
@@ -18,8 +16,7 @@ namespace prepared_hydrostatic_complete_test_utils {
|
||||
};
|
||||
}
|
||||
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView
|
||||
make_state(
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView make_state(
|
||||
const mfem::Vector &enthalpy,
|
||||
const mfem::Vector &gravityPotential,
|
||||
const mfem::Vector &displacement,
|
||||
@@ -54,11 +51,9 @@ namespace prepared_hydrostatic_complete_test_utils {
|
||||
const double firstPhase,
|
||||
const double secondPhase
|
||||
) {
|
||||
mfem::Vector direction =
|
||||
gravity_prepared_test_utils::make_displacement(f, firstPhase);
|
||||
mfem::Vector direction = gravity_prepared_test_utils::make_displacement(f, firstPhase);
|
||||
|
||||
const mfem::Vector secondField =
|
||||
gravity_prepared_test_utils::make_displacement(f, secondPhase);
|
||||
const mfem::Vector secondField = gravity_prepared_test_utils::make_displacement(f, secondPhase);
|
||||
|
||||
direction -= secondField;
|
||||
return direction;
|
||||
@@ -95,25 +90,21 @@ namespace prepared_hydrostatic_complete_test_utils {
|
||||
displacementPlus.Add(step, displacementVariation);
|
||||
displacementMinus.Add(-step, displacementVariation);
|
||||
|
||||
const double bernoulliConstantPlus =
|
||||
baseBernoulliConstant + step * bernoulliConstantVariation;
|
||||
const double bernoulliConstantPlus = baseBernoulliConstant + step * bernoulliConstantVariation;
|
||||
|
||||
const double bernoulliConstantMinus =
|
||||
baseBernoulliConstant - step * bernoulliConstantVariation;
|
||||
const double bernoulliConstantMinus = baseBernoulliConstant - step * bernoulliConstantVariation;
|
||||
|
||||
mfem::Vector residualPlus;
|
||||
mfem::Vector residualMinus;
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, rotation, enthalpyPlus,
|
||||
gravityPotentialPlus, displacementPlus, bernoulliConstantPlus,
|
||||
residualPlus
|
||||
f, *f.domainMapperStateless, rotation, enthalpyPlus, gravityPotentialPlus, displacementPlus,
|
||||
bernoulliConstantPlus, residualPlus
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, rotation, enthalpyMinus,
|
||||
gravityPotentialMinus, displacementMinus, bernoulliConstantMinus,
|
||||
residualMinus
|
||||
f, *f.domainMapperStateless, rotation, enthalpyMinus, gravityPotentialMinus, displacementMinus,
|
||||
bernoulliConstantMinus, residualMinus
|
||||
);
|
||||
|
||||
difference = residualPlus;
|
||||
@@ -140,34 +131,25 @@ namespace prepared_hydrostatic_complete_test_utils {
|
||||
TEST_CASE(
|
||||
"Prepared Hydrostatic Complete Jacobian Matches Sum And Centered "
|
||||
"Differences",
|
||||
tags::barotrope &tags::hydro &tags::integration &tags::jacobian
|
||||
&tags::prepared &tags::self_consistency
|
||||
tags::barotrope &tags::hydro &tags::integration &tags::jacobian &tags::prepared &tags::self_consistency
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
auto args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator
|
||||
preparedOperator(f, *f.domainMapperStateless);
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator preparedOperator(f, *f.domainMapperStateless);
|
||||
|
||||
const mfem::Vector enthalpy =
|
||||
gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.enthalpyFes->GetTrueVSize(), 0.34
|
||||
);
|
||||
gravity_prepared_test_utils::make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), 0.34);
|
||||
|
||||
const mfem::Vector gravityPotential =
|
||||
gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.gravityPotentialFes->GetTrueVSize(), 0.57
|
||||
);
|
||||
gravity_prepared_test_utils::make_deterministic_vector(f.gravityPotentialFes->GetTrueVSize(), 0.57);
|
||||
|
||||
const mfem::Vector displacement =
|
||||
gravity_prepared_test_utils::make_displacement(f, 0.68);
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.68);
|
||||
|
||||
constexpr double bernoulliConstant = 0.43;
|
||||
constexpr double bernoulliConstant = 0.43;
|
||||
|
||||
const mean_field::physics::RigidRotation rotation =
|
||||
prepared_hydrostatic_complete_test_utils::make_rotation();
|
||||
const mean_field::physics::RigidRotation rotation = prepared_hydrostatic_complete_test_utils::make_rotation();
|
||||
|
||||
preparedOperator.Prepare(
|
||||
prepared_hydrostatic_complete_test_utils::make_state(
|
||||
@@ -177,19 +159,13 @@ TEST_CASE(
|
||||
);
|
||||
|
||||
const mfem::Vector enthalpyVariation =
|
||||
gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.enthalpyFes->GetTrueVSize(), 1.07
|
||||
);
|
||||
gravity_prepared_test_utils::make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), 1.07);
|
||||
|
||||
const mfem::Vector gravityPotentialVariation =
|
||||
gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.gravityPotentialFes->GetTrueVSize(), 1.31
|
||||
);
|
||||
gravity_prepared_test_utils::make_deterministic_vector(f.gravityPotentialFes->GetTrueVSize(), 1.31);
|
||||
|
||||
const mfem::Vector displacementVariation =
|
||||
prepared_hydrostatic_complete_test_utils::make_displacement_direction(
|
||||
f, 1.19, 0.38
|
||||
);
|
||||
prepared_hydrostatic_complete_test_utils::make_displacement_direction(f, 1.19, 0.38);
|
||||
|
||||
constexpr double bernoulliConstantVariation = -0.37;
|
||||
|
||||
@@ -199,25 +175,16 @@ TEST_CASE(
|
||||
mfem::Vector displacementAction;
|
||||
mfem::Vector completeAction;
|
||||
|
||||
preparedOperator.ApplyEnthalpyJacobianAction(
|
||||
enthalpyVariation, enthalpyAction
|
||||
);
|
||||
preparedOperator.ApplyEnthalpyJacobianAction(enthalpyVariation, enthalpyAction);
|
||||
|
||||
preparedOperator.ApplyGravityPotentialJacobianAction(
|
||||
gravityPotentialVariation, gravityPotentialAction
|
||||
);
|
||||
preparedOperator.ApplyGravityPotentialJacobianAction(gravityPotentialVariation, gravityPotentialAction);
|
||||
|
||||
preparedOperator.ApplyBernoulliConstantJacobianAction(
|
||||
bernoulliConstantVariation, bernoulliConstantAction
|
||||
);
|
||||
preparedOperator.ApplyBernoulliConstantJacobianAction(bernoulliConstantVariation, bernoulliConstantAction);
|
||||
|
||||
preparedOperator.ApplyDisplacementJacobianAction(
|
||||
displacementVariation, displacementAction
|
||||
);
|
||||
preparedOperator.ApplyDisplacementJacobianAction(displacementVariation, displacementAction);
|
||||
|
||||
preparedOperator.ApplyCompleteJacobianAction(
|
||||
enthalpyVariation, gravityPotentialVariation,
|
||||
bernoulliConstantVariation, displacementVariation, completeAction
|
||||
enthalpyVariation, gravityPotentialVariation, bernoulliConstantVariation, displacementVariation, completeAction
|
||||
);
|
||||
|
||||
mfem::Vector summedAction(enthalpyAction);
|
||||
@@ -230,27 +197,20 @@ TEST_CASE(
|
||||
mfem::Vector centeredDifference;
|
||||
|
||||
prepared_hydrostatic_complete_test_utils::centered_complete_difference(
|
||||
f, rotation, enthalpy, gravityPotential, displacement,
|
||||
bernoulliConstant, enthalpyVariation, gravityPotentialVariation,
|
||||
displacementVariation, bernoulliConstantVariation, finiteDifferenceStep,
|
||||
f, rotation, enthalpy, gravityPotential, displacement, bernoulliConstant, enthalpyVariation,
|
||||
gravityPotentialVariation, displacementVariation, bernoulliConstantVariation, finiteDifferenceStep,
|
||||
centeredDifference
|
||||
);
|
||||
|
||||
const double summationError = gravity_prepared_test_utils::relative_error(
|
||||
completeAction, summedAction, f.mesh->GetComm()
|
||||
);
|
||||
const double summationError =
|
||||
gravity_prepared_test_utils::relative_error(completeAction, summedAction, f.mesh->GetComm());
|
||||
|
||||
const double centeredDifferenceError =
|
||||
gravity_prepared_test_utils::relative_error(
|
||||
completeAction, centeredDifference, f.mesh->GetComm()
|
||||
);
|
||||
gravity_prepared_test_utils::relative_error(completeAction, centeredDifference, f.mesh->GetComm());
|
||||
|
||||
INFO("Complete-action summation error = " << summationError);
|
||||
|
||||
INFO(
|
||||
"Complete-action centered-difference error = "
|
||||
<< centeredDifferenceError
|
||||
);
|
||||
INFO("Complete-action centered-difference error = " << centeredDifferenceError);
|
||||
|
||||
CHECK(preparedOperator.GetCompleteJacobianStatistics().applications == 1);
|
||||
|
||||
@@ -261,29 +221,21 @@ TEST_CASE(
|
||||
TEST_CASE(
|
||||
"Prepared Hydrostatic MFEM Adapter Uses Four Block Layout And Reuses "
|
||||
"Preparation",
|
||||
tags::barotrope &tags::hydro &tags::integration &tags::jacobian
|
||||
&tags::mfem_operators &tags::prepared &tags::unit
|
||||
tags::barotrope &tags::hydro &tags::integration &tags::jacobian &tags::mfem_operators &tags::prepared &tags::unit
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
auto args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator
|
||||
preparedOperator(f, *f.domainMapperStateless);
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator preparedOperator(f, *f.domainMapperStateless);
|
||||
|
||||
const mfem::Vector enthalpy =
|
||||
gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.enthalpyFes->GetTrueVSize(), 0.41
|
||||
);
|
||||
gravity_prepared_test_utils::make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), 0.41);
|
||||
|
||||
const mfem::Vector gravityPotential =
|
||||
gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.gravityPotentialFes->GetTrueVSize(), 0.63
|
||||
);
|
||||
gravity_prepared_test_utils::make_deterministic_vector(f.gravityPotentialFes->GetTrueVSize(), 0.63);
|
||||
|
||||
const mfem::Vector displacement =
|
||||
gravity_prepared_test_utils::make_displacement(f, 0.74);
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.74);
|
||||
|
||||
constexpr double bernoulliConstant = 0.38;
|
||||
|
||||
@@ -295,49 +247,31 @@ TEST_CASE(
|
||||
prepared_hydrostatic_complete_test_utils::make_rotation()
|
||||
);
|
||||
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumJacobianOperator
|
||||
adapter(f, preparedOperator);
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumJacobianOperator adapter(f, preparedOperator);
|
||||
|
||||
const auto &layout = adapter.GetLayout();
|
||||
|
||||
CHECK(
|
||||
layout.Offset(
|
||||
mean_field::operators::HydrostaticJacobianInputBlock::enthalpy
|
||||
) == 0
|
||||
);
|
||||
CHECK(layout.Offset(mean_field::operators::HydrostaticJacobianInputBlock::enthalpy) == 0);
|
||||
|
||||
CHECK(
|
||||
layout.Offset(
|
||||
mean_field::operators::HydrostaticJacobianInputBlock::
|
||||
gravityPotential
|
||||
) == f.enthalpyFes->GetTrueVSize()
|
||||
layout.Offset(mean_field::operators::HydrostaticJacobianInputBlock::gravityPotential) ==
|
||||
f.enthalpyFes->GetTrueVSize()
|
||||
);
|
||||
|
||||
CHECK(
|
||||
layout.Size(
|
||||
mean_field::operators::HydrostaticJacobianInputBlock::
|
||||
bernoulliConstant
|
||||
) == 1
|
||||
);
|
||||
CHECK(layout.Size(mean_field::operators::HydrostaticJacobianInputBlock::bernoulliConstant) == 1);
|
||||
|
||||
CHECK(adapter.Width() == layout.GetTotalSize());
|
||||
CHECK(adapter.Height() == layout.GetResidualSize());
|
||||
CHECK(adapter.Height() == f.enthalpyFes->GetTrueVSize());
|
||||
|
||||
const mfem::Vector enthalpyVariation =
|
||||
gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.enthalpyFes->GetTrueVSize(), 1.12
|
||||
);
|
||||
gravity_prepared_test_utils::make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), 1.12);
|
||||
|
||||
const mfem::Vector gravityPotentialVariation =
|
||||
gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.gravityPotentialFes->GetTrueVSize(), 1.39
|
||||
);
|
||||
gravity_prepared_test_utils::make_deterministic_vector(f.gravityPotentialFes->GetTrueVSize(), 1.39);
|
||||
|
||||
const mfem::Vector displacementVariation =
|
||||
prepared_hydrostatic_complete_test_utils::make_displacement_direction(
|
||||
f, 1.28, 0.49
|
||||
);
|
||||
prepared_hydrostatic_complete_test_utils::make_displacement_direction(f, 1.28, 0.49);
|
||||
|
||||
constexpr double bernoulliConstantVariation = 0.29;
|
||||
|
||||
@@ -345,49 +279,39 @@ TEST_CASE(
|
||||
packedDirection = 0.0;
|
||||
|
||||
prepared_hydrostatic_complete_test_utils::set_block(
|
||||
packedDirection, layout,
|
||||
mean_field::operators::HydrostaticJacobianInputBlock::enthalpy,
|
||||
enthalpyVariation
|
||||
packedDirection, layout, mean_field::operators::HydrostaticJacobianInputBlock::enthalpy, enthalpyVariation
|
||||
);
|
||||
|
||||
prepared_hydrostatic_complete_test_utils::set_block(
|
||||
packedDirection, layout,
|
||||
mean_field::operators::HydrostaticJacobianInputBlock::gravityPotential,
|
||||
packedDirection, layout, mean_field::operators::HydrostaticJacobianInputBlock::gravityPotential,
|
||||
gravityPotentialVariation
|
||||
);
|
||||
|
||||
prepared_hydrostatic_complete_test_utils::set_block(
|
||||
packedDirection, layout,
|
||||
mean_field::operators::HydrostaticJacobianInputBlock::displacement,
|
||||
packedDirection, layout, mean_field::operators::HydrostaticJacobianInputBlock::displacement,
|
||||
displacementVariation
|
||||
);
|
||||
|
||||
packedDirection(layout.Offset(
|
||||
mean_field::operators::HydrostaticJacobianInputBlock::bernoulliConstant
|
||||
)) = bernoulliConstantVariation;
|
||||
packedDirection(layout.Offset(mean_field::operators::HydrostaticJacobianInputBlock::bernoulliConstant)) =
|
||||
bernoulliConstantVariation;
|
||||
|
||||
mfem::Vector directAction;
|
||||
mfem::Vector adapterAction;
|
||||
|
||||
preparedOperator.ApplyCompleteJacobianAction(
|
||||
enthalpyVariation, gravityPotentialVariation,
|
||||
bernoulliConstantVariation, displacementVariation, directAction
|
||||
enthalpyVariation, gravityPotentialVariation, bernoulliConstantVariation, displacementVariation, directAction
|
||||
);
|
||||
|
||||
adapter.Mult(packedDirection, adapterAction);
|
||||
|
||||
const double adapterError = gravity_prepared_test_utils::relative_error(
|
||||
adapterAction, directAction, f.mesh->GetComm()
|
||||
);
|
||||
const double adapterError =
|
||||
gravity_prepared_test_utils::relative_error(adapterAction, directAction, f.mesh->GetComm());
|
||||
|
||||
const auto contextStatisticsBefore =
|
||||
preparedOperator.GetContextPreparationStatistics();
|
||||
const auto contextStatisticsBefore = preparedOperator.GetContextPreparationStatistics();
|
||||
|
||||
const auto algebraicStatisticsBefore =
|
||||
preparedOperator.GetAlgebraicJacobianStatistics();
|
||||
const auto algebraicStatisticsBefore = preparedOperator.GetAlgebraicJacobianStatistics();
|
||||
|
||||
const auto displacementStatisticsBefore =
|
||||
preparedOperator.GetDisplacementJacobianStatistics();
|
||||
const auto displacementStatisticsBefore = preparedOperator.GetDisplacementJacobianStatistics();
|
||||
|
||||
mfem::Vector secondPackedDirection(packedDirection);
|
||||
secondPackedDirection *= -0.61;
|
||||
@@ -399,18 +323,13 @@ TEST_CASE(
|
||||
expectedSecondAction *= -0.61;
|
||||
|
||||
const double adapterLinearityError =
|
||||
gravity_prepared_test_utils::relative_error(
|
||||
secondAdapterAction, expectedSecondAction, f.mesh->GetComm()
|
||||
);
|
||||
gravity_prepared_test_utils::relative_error(secondAdapterAction, expectedSecondAction, f.mesh->GetComm());
|
||||
|
||||
const auto &contextStatisticsAfter =
|
||||
preparedOperator.GetContextPreparationStatistics();
|
||||
const auto &contextStatisticsAfter = preparedOperator.GetContextPreparationStatistics();
|
||||
|
||||
const auto &algebraicStatisticsAfter =
|
||||
preparedOperator.GetAlgebraicJacobianStatistics();
|
||||
const auto &algebraicStatisticsAfter = preparedOperator.GetAlgebraicJacobianStatistics();
|
||||
|
||||
const auto &displacementStatisticsAfter =
|
||||
preparedOperator.GetDisplacementJacobianStatistics();
|
||||
const auto &displacementStatisticsAfter = preparedOperator.GetDisplacementJacobianStatistics();
|
||||
|
||||
INFO("MFEM adapter/direct-action error = " << adapterError);
|
||||
|
||||
@@ -422,15 +341,9 @@ TEST_CASE(
|
||||
|
||||
CHECK(contextStatisticsAfter == contextStatisticsBefore);
|
||||
|
||||
CHECK(
|
||||
algebraicStatisticsAfter.preparations ==
|
||||
algebraicStatisticsBefore.preparations
|
||||
);
|
||||
CHECK(algebraicStatisticsAfter.preparations == algebraicStatisticsBefore.preparations);
|
||||
|
||||
CHECK(
|
||||
displacementStatisticsAfter.preparations ==
|
||||
displacementStatisticsBefore.preparations
|
||||
);
|
||||
CHECK(displacementStatisticsAfter.preparations == displacementStatisticsBefore.preparations);
|
||||
|
||||
CHECK(preparedOperator.GetCompleteJacobianStatistics().applications == 3);
|
||||
}
|
||||
|
||||
@@ -5,9 +5,7 @@ import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace prepared_hydrostatic_displacement_test_utils {
|
||||
mean_field::operators::context::hydrostatic::
|
||||
HydrostaticEquilibriumDependencies
|
||||
make_dependencies() {
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumDependencies make_dependencies() {
|
||||
return {
|
||||
.discretization = {.identity = 401, .revision = 2},
|
||||
.enthalpy = {.identity = 409, .revision = 3},
|
||||
@@ -18,8 +16,7 @@ namespace prepared_hydrostatic_displacement_test_utils {
|
||||
};
|
||||
}
|
||||
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView
|
||||
make_state(
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView make_state(
|
||||
const mfem::Vector &enthalpy,
|
||||
const mfem::Vector &gravityPotential,
|
||||
const mfem::Vector &displacement,
|
||||
@@ -37,18 +34,14 @@ namespace prepared_hydrostatic_displacement_test_utils {
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase = 0.29
|
||||
) {
|
||||
return gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.enthalpyFes->GetTrueVSize(), phase
|
||||
);
|
||||
return gravity_prepared_test_utils::make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), phase);
|
||||
}
|
||||
|
||||
mfem::Vector make_gravity_potential(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase = 0.47
|
||||
) {
|
||||
return gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.gravityPotentialFes->GetTrueVSize(), phase
|
||||
);
|
||||
return gravity_prepared_test_utils::make_deterministic_vector(f.gravityPotentialFes->GetTrueVSize(), phase);
|
||||
}
|
||||
|
||||
mfem::Vector make_displacement_direction(
|
||||
@@ -56,11 +49,9 @@ namespace prepared_hydrostatic_displacement_test_utils {
|
||||
const double firstPhase,
|
||||
const double secondPhase
|
||||
) {
|
||||
mfem::Vector direction =
|
||||
gravity_prepared_test_utils::make_displacement(f, firstPhase);
|
||||
mfem::Vector direction = gravity_prepared_test_utils::make_displacement(f, firstPhase);
|
||||
|
||||
const mfem::Vector secondField =
|
||||
gravity_prepared_test_utils::make_displacement(f, secondPhase);
|
||||
const mfem::Vector secondField = gravity_prepared_test_utils::make_displacement(f, secondPhase);
|
||||
|
||||
direction -= secondField;
|
||||
return direction;
|
||||
@@ -103,13 +94,13 @@ namespace prepared_hydrostatic_displacement_test_utils {
|
||||
mfem::Vector residualMinus;
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, gravityPotential,
|
||||
displacementPlus, bernoulliConstant, residualPlus
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, gravityPotential, displacementPlus, bernoulliConstant,
|
||||
residualPlus
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, gravityPotential,
|
||||
displacementMinus, bernoulliConstant, residualMinus
|
||||
f, *f.domainMapperStateless, rotation, enthalpy, gravityPotential, displacementMinus, bernoulliConstant,
|
||||
residualMinus
|
||||
);
|
||||
|
||||
difference = residualPlus;
|
||||
@@ -122,9 +113,7 @@ namespace prepared_hydrostatic_displacement_test_utils {
|
||||
const mfem::Vector &expected,
|
||||
const MPI_Comm communicator
|
||||
) {
|
||||
return gravity_prepared_test_utils::relative_error(
|
||||
actual, expected, communicator
|
||||
);
|
||||
return gravity_prepared_test_utils::relative_error(actual, expected, communicator);
|
||||
}
|
||||
} // namespace prepared_hydrostatic_displacement_test_utils
|
||||
|
||||
@@ -132,32 +121,26 @@ TEST_CASE(
|
||||
"Prepared Hydrostatic Displacement Jacobian Matches Centered Differences",
|
||||
tags::barotrope &tags::hydro &tags::jacobian &tags::prepared &tags::unit
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
auto args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator
|
||||
preparedOperator(f, *f.domainMapperStateless);
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator preparedOperator(f, *f.domainMapperStateless);
|
||||
|
||||
const mfem::Vector enthalpy =
|
||||
prepared_hydrostatic_displacement_test_utils::make_enthalpy(f);
|
||||
const mfem::Vector enthalpy = prepared_hydrostatic_displacement_test_utils::make_enthalpy(f);
|
||||
|
||||
const mfem::Vector gravityPotential =
|
||||
prepared_hydrostatic_displacement_test_utils::make_gravity_potential(f);
|
||||
const mfem::Vector gravityPotential = prepared_hydrostatic_displacement_test_utils::make_gravity_potential(f);
|
||||
|
||||
const mfem::Vector displacement =
|
||||
gravity_prepared_test_utils::make_displacement(f, 0.73);
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.73);
|
||||
|
||||
constexpr double bernoulliConstant = 0.39;
|
||||
constexpr double bernoulliConstant = 0.39;
|
||||
|
||||
const mean_field::physics::RigidRotation rotation =
|
||||
prepared_hydrostatic_displacement_test_utils::make_rotation(0.9);
|
||||
|
||||
const auto dependencies =
|
||||
prepared_hydrostatic_displacement_test_utils::make_dependencies();
|
||||
const auto dependencies = prepared_hydrostatic_displacement_test_utils::make_dependencies();
|
||||
|
||||
const auto report = preparedOperator.Prepare(
|
||||
const auto report = preparedOperator.Prepare(
|
||||
prepared_hydrostatic_displacement_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement, bernoulliConstant
|
||||
),
|
||||
@@ -165,12 +148,10 @@ TEST_CASE(
|
||||
);
|
||||
|
||||
const mfem::Vector firstVariation =
|
||||
prepared_hydrostatic_displacement_test_utils::
|
||||
make_displacement_direction(f, 1.17, 0.31);
|
||||
prepared_hydrostatic_displacement_test_utils::make_displacement_direction(f, 1.17, 0.31);
|
||||
|
||||
const mfem::Vector secondVariation =
|
||||
prepared_hydrostatic_displacement_test_utils::
|
||||
make_displacement_direction(f, 1.43, 0.58);
|
||||
prepared_hydrostatic_displacement_test_utils::make_displacement_direction(f, 1.43, 0.58);
|
||||
|
||||
mfem::Vector combinedVariation(firstVariation);
|
||||
combinedVariation += secondVariation;
|
||||
@@ -179,51 +160,36 @@ TEST_CASE(
|
||||
mfem::Vector secondAction;
|
||||
mfem::Vector combinedAction;
|
||||
|
||||
preparedOperator.ApplyDisplacementJacobianAction(
|
||||
firstVariation, firstAction
|
||||
);
|
||||
preparedOperator.ApplyDisplacementJacobianAction(firstVariation, firstAction);
|
||||
|
||||
preparedOperator.ApplyDisplacementJacobianAction(
|
||||
secondVariation, secondAction
|
||||
);
|
||||
preparedOperator.ApplyDisplacementJacobianAction(secondVariation, secondAction);
|
||||
|
||||
preparedOperator.ApplyDisplacementJacobianAction(
|
||||
combinedVariation, combinedAction
|
||||
);
|
||||
preparedOperator.ApplyDisplacementJacobianAction(combinedVariation, combinedAction);
|
||||
|
||||
constexpr double finiteDifferenceStep = 1.0e-5;
|
||||
|
||||
mfem::Vector centeredDifference;
|
||||
|
||||
prepared_hydrostatic_displacement_test_utils::
|
||||
centered_displacement_difference(
|
||||
f, rotation, enthalpy, gravityPotential, displacement,
|
||||
firstVariation, bernoulliConstant, finiteDifferenceStep,
|
||||
centeredDifference
|
||||
);
|
||||
prepared_hydrostatic_displacement_test_utils::centered_displacement_difference(
|
||||
f, rotation, enthalpy, gravityPotential, displacement, firstVariation, bernoulliConstant, finiteDifferenceStep,
|
||||
centeredDifference
|
||||
);
|
||||
|
||||
mfem::Vector sumOfActions(firstAction);
|
||||
sumOfActions += secondAction;
|
||||
|
||||
const double centeredDifferenceError =
|
||||
prepared_hydrostatic_displacement_test_utils::relative_error(
|
||||
firstAction, centeredDifference, f.mesh->GetComm()
|
||||
);
|
||||
const double centeredDifferenceError = prepared_hydrostatic_displacement_test_utils::relative_error(
|
||||
firstAction, centeredDifference, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
const double linearityError =
|
||||
prepared_hydrostatic_displacement_test_utils::relative_error(
|
||||
combinedAction, sumOfActions, f.mesh->GetComm()
|
||||
);
|
||||
prepared_hydrostatic_displacement_test_utils::relative_error(combinedAction, sumOfActions, f.mesh->GetComm());
|
||||
|
||||
INFO(
|
||||
"Prepared displacement centered-difference error = "
|
||||
<< centeredDifferenceError
|
||||
);
|
||||
INFO("Prepared displacement centered-difference error = " << centeredDifferenceError);
|
||||
|
||||
INFO("Prepared displacement linearity error = " << linearityError);
|
||||
|
||||
const auto &statistics =
|
||||
preparedOperator.GetDisplacementJacobianStatistics();
|
||||
const auto &statistics = preparedOperator.GetDisplacementJacobianStatistics();
|
||||
|
||||
CHECK(report.preparedDisplacementJacobianData);
|
||||
CHECK(statistics.preparations == 1);
|
||||
@@ -237,32 +203,23 @@ TEST_CASE(
|
||||
"Data",
|
||||
tags::barotrope &tags::hydro &tags::jacobian &tags::prepared &tags::unit
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
auto args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator
|
||||
preparedOperator(f, *f.domainMapperStateless);
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator preparedOperator(f, *f.domainMapperStateless);
|
||||
|
||||
const mfem::Vector enthalpy =
|
||||
prepared_hydrostatic_displacement_test_utils::make_enthalpy(f, 0.37);
|
||||
const mfem::Vector enthalpy = prepared_hydrostatic_displacement_test_utils::make_enthalpy(f, 0.37);
|
||||
|
||||
const mfem::Vector gravityPotential =
|
||||
prepared_hydrostatic_displacement_test_utils::make_gravity_potential(
|
||||
f, 0.53
|
||||
);
|
||||
const mfem::Vector gravityPotential = prepared_hydrostatic_displacement_test_utils::make_gravity_potential(f, 0.53);
|
||||
|
||||
mfem::Vector displacement =
|
||||
gravity_prepared_test_utils::make_displacement(f, 0.42);
|
||||
mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.42);
|
||||
|
||||
constexpr double bernoulliConstant = 0.36;
|
||||
constexpr double bernoulliConstant = 0.36;
|
||||
|
||||
mean_field::physics::RigidRotation rotation =
|
||||
prepared_hydrostatic_displacement_test_utils::make_rotation(0.75);
|
||||
mean_field::physics::RigidRotation rotation = prepared_hydrostatic_displacement_test_utils::make_rotation(0.75);
|
||||
|
||||
auto dependencies =
|
||||
prepared_hydrostatic_displacement_test_utils::make_dependencies();
|
||||
auto dependencies = prepared_hydrostatic_displacement_test_utils::make_dependencies();
|
||||
|
||||
preparedOperator.Prepare(
|
||||
prepared_hydrostatic_displacement_test_utils::make_state(
|
||||
@@ -272,30 +229,21 @@ TEST_CASE(
|
||||
);
|
||||
|
||||
const mfem::Vector displacementVariation =
|
||||
prepared_hydrostatic_displacement_test_utils::
|
||||
make_displacement_direction(f, 1.09, 0.27);
|
||||
prepared_hydrostatic_displacement_test_utils::make_displacement_direction(f, 1.09, 0.27);
|
||||
|
||||
const mfem::Vector secondVariation =
|
||||
prepared_hydrostatic_displacement_test_utils::
|
||||
make_displacement_direction(f, 1.36, 0.64);
|
||||
prepared_hydrostatic_displacement_test_utils::make_displacement_direction(f, 1.36, 0.64);
|
||||
|
||||
mfem::Vector initialAction;
|
||||
mfem::Vector secondDirectionAction;
|
||||
|
||||
preparedOperator.ApplyDisplacementJacobianAction(
|
||||
displacementVariation, initialAction
|
||||
);
|
||||
preparedOperator.ApplyDisplacementJacobianAction(displacementVariation, initialAction);
|
||||
|
||||
preparedOperator.ApplyDisplacementJacobianAction(
|
||||
secondVariation, secondDirectionAction
|
||||
);
|
||||
preparedOperator.ApplyDisplacementJacobianAction(secondVariation, secondDirectionAction);
|
||||
|
||||
CHECK(
|
||||
preparedOperator.GetDisplacementJacobianStatistics().preparations == 1
|
||||
);
|
||||
CHECK(preparedOperator.GetDisplacementJacobianStatistics().preparations == 1);
|
||||
|
||||
rotation =
|
||||
prepared_hydrostatic_displacement_test_utils::make_rotation(1.45);
|
||||
rotation = prepared_hydrostatic_displacement_test_utils::make_rotation(1.45);
|
||||
|
||||
++dependencies.rotation.revision;
|
||||
|
||||
@@ -308,30 +256,24 @@ TEST_CASE(
|
||||
|
||||
mfem::Vector rotationUpdatedAction;
|
||||
|
||||
preparedOperator.ApplyDisplacementJacobianAction(
|
||||
displacementVariation, rotationUpdatedAction
|
||||
);
|
||||
preparedOperator.ApplyDisplacementJacobianAction(displacementVariation, rotationUpdatedAction);
|
||||
|
||||
constexpr double finiteDifferenceStep = 1.0e-5;
|
||||
|
||||
mfem::Vector rotationReference;
|
||||
|
||||
prepared_hydrostatic_displacement_test_utils::
|
||||
centered_displacement_difference(
|
||||
f, rotation, enthalpy, gravityPotential, displacement,
|
||||
displacementVariation, bernoulliConstant, finiteDifferenceStep,
|
||||
rotationReference
|
||||
);
|
||||
prepared_hydrostatic_displacement_test_utils::centered_displacement_difference(
|
||||
f, rotation, enthalpy, gravityPotential, displacement, displacementVariation, bernoulliConstant,
|
||||
finiteDifferenceStep, rotationReference
|
||||
);
|
||||
|
||||
const double rotationReferenceError =
|
||||
prepared_hydrostatic_displacement_test_utils::relative_error(
|
||||
rotationUpdatedAction, rotationReference, f.mesh->GetComm()
|
||||
);
|
||||
const double rotationReferenceError = prepared_hydrostatic_displacement_test_utils::relative_error(
|
||||
rotationUpdatedAction, rotationReference, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
const double rotationEffect =
|
||||
prepared_hydrostatic_displacement_test_utils::relative_error(
|
||||
rotationUpdatedAction, initialAction, f.mesh->GetComm()
|
||||
);
|
||||
const double rotationEffect = prepared_hydrostatic_displacement_test_utils::relative_error(
|
||||
rotationUpdatedAction, initialAction, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
CHECK_FALSE(rotationReport.contextReport.preparedGeometryState);
|
||||
CHECK(rotationReport.contextReport.preparedRotationDependencies);
|
||||
@@ -355,54 +297,34 @@ TEST_CASE(
|
||||
|
||||
mfem::Vector geometryUpdatedAction;
|
||||
|
||||
preparedOperator.ApplyDisplacementJacobianAction(
|
||||
displacementVariation, geometryUpdatedAction
|
||||
);
|
||||
preparedOperator.ApplyDisplacementJacobianAction(displacementVariation, geometryUpdatedAction);
|
||||
|
||||
mfem::Vector geometryReference;
|
||||
|
||||
prepared_hydrostatic_displacement_test_utils::
|
||||
centered_displacement_difference(
|
||||
f, rotation, enthalpy, gravityPotential, displacement,
|
||||
displacementVariation, bernoulliConstant, finiteDifferenceStep,
|
||||
geometryReference
|
||||
);
|
||||
|
||||
const double geometryReferenceError =
|
||||
prepared_hydrostatic_displacement_test_utils::relative_error(
|
||||
geometryUpdatedAction, geometryReference, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
const double geometryEffect =
|
||||
prepared_hydrostatic_displacement_test_utils::relative_error(
|
||||
geometryUpdatedAction, rotationUpdatedAction, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
INFO(
|
||||
"Rotation-updated displacement Jacobian error = "
|
||||
<< rotationReferenceError
|
||||
prepared_hydrostatic_displacement_test_utils::centered_displacement_difference(
|
||||
f, rotation, enthalpy, gravityPotential, displacement, displacementVariation, bernoulliConstant,
|
||||
finiteDifferenceStep, geometryReference
|
||||
);
|
||||
|
||||
INFO(
|
||||
"Displacement Jacobian change after rotation update = "
|
||||
<< rotationEffect
|
||||
const double geometryReferenceError = prepared_hydrostatic_displacement_test_utils::relative_error(
|
||||
geometryUpdatedAction, geometryReference, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
INFO(
|
||||
"Geometry-updated displacement Jacobian error = "
|
||||
<< geometryReferenceError
|
||||
const double geometryEffect = prepared_hydrostatic_displacement_test_utils::relative_error(
|
||||
geometryUpdatedAction, rotationUpdatedAction, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
INFO(
|
||||
"Displacement Jacobian change after geometry update = "
|
||||
<< geometryEffect
|
||||
);
|
||||
INFO("Rotation-updated displacement Jacobian error = " << rotationReferenceError);
|
||||
|
||||
const auto &contextStatistics =
|
||||
preparedOperator.GetContextPreparationStatistics();
|
||||
INFO("Displacement Jacobian change after rotation update = " << rotationEffect);
|
||||
|
||||
const auto &displacementStatistics =
|
||||
preparedOperator.GetDisplacementJacobianStatistics();
|
||||
INFO("Geometry-updated displacement Jacobian error = " << geometryReferenceError);
|
||||
|
||||
INFO("Displacement Jacobian change after geometry update = " << geometryEffect);
|
||||
|
||||
const auto &contextStatistics = preparedOperator.GetContextPreparationStatistics();
|
||||
|
||||
const auto &displacementStatistics = preparedOperator.GetDisplacementJacobianStatistics();
|
||||
|
||||
CHECK(geometryReport.contextReport.preparedGeometryState);
|
||||
CHECK(geometryReport.contextReport.preparedRotationDependencies);
|
||||
|
||||
@@ -5,9 +5,7 @@ import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace prepared_hydrostatic_jacobian_test_utils {
|
||||
mean_field::operators::context::hydrostatic::
|
||||
HydrostaticEquilibriumDependencies
|
||||
make_dependencies() {
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumDependencies make_dependencies() {
|
||||
return {
|
||||
.discretization = {.identity = 307, .revision = 2},
|
||||
.enthalpy = {.identity = 311, .revision = 3},
|
||||
@@ -18,8 +16,7 @@ namespace prepared_hydrostatic_jacobian_test_utils {
|
||||
};
|
||||
}
|
||||
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView
|
||||
make_state(
|
||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView make_state(
|
||||
const mfem::Vector &enthalpy,
|
||||
const mfem::Vector &gravityPotential,
|
||||
const mfem::Vector &displacement,
|
||||
@@ -37,18 +34,14 @@ namespace prepared_hydrostatic_jacobian_test_utils {
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase = 0.23
|
||||
) {
|
||||
return gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.enthalpyFes->GetTrueVSize(), phase
|
||||
);
|
||||
return gravity_prepared_test_utils::make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), phase);
|
||||
}
|
||||
|
||||
mfem::Vector make_gravity_potential(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase = 0.41
|
||||
) {
|
||||
return gravity_prepared_test_utils::make_deterministic_vector(
|
||||
f.gravityPotentialFes->GetTrueVSize(), phase
|
||||
);
|
||||
return gravity_prepared_test_utils::make_deterministic_vector(f.gravityPotentialFes->GetTrueVSize(), phase);
|
||||
}
|
||||
|
||||
mean_field::physics::RigidRotation make_rotation(const double scale = 1.0) {
|
||||
@@ -83,15 +76,13 @@ namespace prepared_hydrostatic_jacobian_test_utils {
|
||||
mfem::Vector residualMinus;
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, rotation, enthalpyPlus,
|
||||
gravityPotentialPlus, displacement, bernoulliConstantPlus,
|
||||
residualPlus
|
||||
f, *f.domainMapperStateless, rotation, enthalpyPlus, gravityPotentialPlus, displacement,
|
||||
bernoulliConstantPlus, residualPlus
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||
f, *f.domainMapperStateless, rotation, enthalpyMinus,
|
||||
gravityPotentialMinus, displacement, bernoulliConstantMinus,
|
||||
residualMinus
|
||||
f, *f.domainMapperStateless, rotation, enthalpyMinus, gravityPotentialMinus, displacement,
|
||||
bernoulliConstantMinus, residualMinus
|
||||
);
|
||||
|
||||
// The two states are separated by one complete variation:
|
||||
@@ -105,9 +96,7 @@ namespace prepared_hydrostatic_jacobian_test_utils {
|
||||
const mfem::Vector &expected,
|
||||
MPI_Comm communicator
|
||||
) {
|
||||
return gravity_prepared_test_utils::relative_error(
|
||||
actual, expected, communicator
|
||||
);
|
||||
return gravity_prepared_test_utils::relative_error(actual, expected, communicator);
|
||||
}
|
||||
} // namespace prepared_hydrostatic_jacobian_test_utils
|
||||
|
||||
@@ -116,42 +105,33 @@ TEST_CASE(
|
||||
"Differences",
|
||||
tags::barotrope &tags::hydro &tags::jacobian &tags::prepared &tags::unit
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
auto args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator
|
||||
preparedOperator(f, *f.domainMapperStateless);
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator preparedOperator(f, *f.domainMapperStateless);
|
||||
|
||||
const mfem::Vector enthalpy =
|
||||
prepared_hydrostatic_jacobian_test_utils::make_enthalpy(f);
|
||||
const mfem::Vector enthalpy = prepared_hydrostatic_jacobian_test_utils::make_enthalpy(f);
|
||||
|
||||
const mfem::Vector gravityPotential =
|
||||
prepared_hydrostatic_jacobian_test_utils::make_gravity_potential(f);
|
||||
const mfem::Vector gravityPotential = prepared_hydrostatic_jacobian_test_utils::make_gravity_potential(f);
|
||||
|
||||
const mfem::Vector displacement =
|
||||
gravity_prepared_test_utils::make_displacement(f, 0.73);
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.73);
|
||||
|
||||
constexpr double bernoulliConstant = 0.39;
|
||||
constexpr double bernoulliConstant = 0.39;
|
||||
|
||||
const mean_field::physics::RigidRotation rotation =
|
||||
prepared_hydrostatic_jacobian_test_utils::make_rotation();
|
||||
const mean_field::physics::RigidRotation rotation = prepared_hydrostatic_jacobian_test_utils::make_rotation();
|
||||
|
||||
const auto report = preparedOperator.Prepare(
|
||||
const auto report = preparedOperator.Prepare(
|
||||
prepared_hydrostatic_jacobian_test_utils::make_state(
|
||||
enthalpy, gravityPotential, displacement, bernoulliConstant
|
||||
),
|
||||
prepared_hydrostatic_jacobian_test_utils::make_dependencies(), rotation
|
||||
);
|
||||
|
||||
const mfem::Vector enthalpyVariation =
|
||||
prepared_hydrostatic_jacobian_test_utils::make_enthalpy(f, 0.71);
|
||||
const mfem::Vector enthalpyVariation = prepared_hydrostatic_jacobian_test_utils::make_enthalpy(f, 0.71);
|
||||
|
||||
const mfem::Vector gravityPotentialVariation =
|
||||
prepared_hydrostatic_jacobian_test_utils::make_gravity_potential(
|
||||
f, 0.83
|
||||
);
|
||||
prepared_hydrostatic_jacobian_test_utils::make_gravity_potential(f, 0.83);
|
||||
|
||||
constexpr double bernoulliConstantVariation = -0.31;
|
||||
|
||||
@@ -160,21 +140,14 @@ TEST_CASE(
|
||||
mfem::Vector bernoulliConstantAction;
|
||||
mfem::Vector combinedAction;
|
||||
|
||||
preparedOperator.ApplyEnthalpyJacobianAction(
|
||||
enthalpyVariation, enthalpyAction
|
||||
);
|
||||
preparedOperator.ApplyEnthalpyJacobianAction(enthalpyVariation, enthalpyAction);
|
||||
|
||||
preparedOperator.ApplyGravityPotentialJacobianAction(
|
||||
gravityPotentialVariation, gravityPotentialAction
|
||||
);
|
||||
preparedOperator.ApplyGravityPotentialJacobianAction(gravityPotentialVariation, gravityPotentialAction);
|
||||
|
||||
preparedOperator.ApplyBernoulliConstantJacobianAction(
|
||||
bernoulliConstantVariation, bernoulliConstantAction
|
||||
);
|
||||
preparedOperator.ApplyBernoulliConstantJacobianAction(bernoulliConstantVariation, bernoulliConstantAction);
|
||||
|
||||
preparedOperator.ApplyAlgebraicJacobianAction(
|
||||
enthalpyVariation, gravityPotentialVariation,
|
||||
bernoulliConstantVariation, combinedAction
|
||||
enthalpyVariation, gravityPotentialVariation, bernoulliConstantVariation, combinedAction
|
||||
);
|
||||
|
||||
mfem::Vector enthalpyPlus(enthalpy);
|
||||
@@ -188,9 +161,8 @@ TEST_CASE(
|
||||
mfem::Vector enthalpyReference;
|
||||
|
||||
prepared_hydrostatic_jacobian_test_utils::centered_residual_difference(
|
||||
f, rotation, enthalpyPlus, enthalpyMinus, gravityPotential,
|
||||
gravityPotential, displacement, bernoulliConstant, bernoulliConstant,
|
||||
enthalpyReference
|
||||
f, rotation, enthalpyPlus, enthalpyMinus, gravityPotential, gravityPotential, displacement, bernoulliConstant,
|
||||
bernoulliConstant, enthalpyReference
|
||||
);
|
||||
|
||||
enthalpyPlus = enthalpy;
|
||||
@@ -203,8 +175,7 @@ TEST_CASE(
|
||||
mfem::Vector gravityPotentialReference;
|
||||
|
||||
prepared_hydrostatic_jacobian_test_utils::centered_residual_difference(
|
||||
f, rotation, enthalpy, enthalpy, gravityPotentialPlus,
|
||||
gravityPotentialMinus, displacement, bernoulliConstant,
|
||||
f, rotation, enthalpy, enthalpy, gravityPotentialPlus, gravityPotentialMinus, displacement, bernoulliConstant,
|
||||
bernoulliConstant, gravityPotentialReference
|
||||
);
|
||||
|
||||
@@ -214,9 +185,8 @@ TEST_CASE(
|
||||
mfem::Vector bernoulliConstantReference;
|
||||
|
||||
prepared_hydrostatic_jacobian_test_utils::centered_residual_difference(
|
||||
f, rotation, enthalpy, enthalpy, gravityPotential, gravityPotential,
|
||||
displacement, bernoulliConstant + 0.5 * bernoulliConstantVariation,
|
||||
bernoulliConstant - 0.5 * bernoulliConstantVariation,
|
||||
f, rotation, enthalpy, enthalpy, gravityPotential, gravityPotential, displacement,
|
||||
bernoulliConstant + 0.5 * bernoulliConstantVariation, bernoulliConstant - 0.5 * bernoulliConstantVariation,
|
||||
bernoulliConstantReference
|
||||
);
|
||||
|
||||
@@ -230,10 +200,9 @@ TEST_CASE(
|
||||
mfem::Vector combinedReference;
|
||||
|
||||
prepared_hydrostatic_jacobian_test_utils::centered_residual_difference(
|
||||
f, rotation, enthalpyPlus, enthalpyMinus, gravityPotentialPlus,
|
||||
gravityPotentialMinus, displacement,
|
||||
bernoulliConstant + 0.5 * bernoulliConstantVariation,
|
||||
bernoulliConstant - 0.5 * bernoulliConstantVariation, combinedReference
|
||||
f, rotation, enthalpyPlus, enthalpyMinus, gravityPotentialPlus, gravityPotentialMinus, displacement,
|
||||
bernoulliConstant + 0.5 * bernoulliConstantVariation, bernoulliConstant - 0.5 * bernoulliConstantVariation,
|
||||
combinedReference
|
||||
);
|
||||
|
||||
mfem::Vector sumOfBlocks(enthalpyAction);
|
||||
@@ -241,30 +210,21 @@ TEST_CASE(
|
||||
sumOfBlocks += bernoulliConstantAction;
|
||||
|
||||
const double enthalpyError =
|
||||
prepared_hydrostatic_jacobian_test_utils::relative_error(
|
||||
enthalpyAction, enthalpyReference, f.mesh->GetComm()
|
||||
);
|
||||
prepared_hydrostatic_jacobian_test_utils::relative_error(enthalpyAction, enthalpyReference, f.mesh->GetComm());
|
||||
|
||||
const double gravityPotentialError =
|
||||
prepared_hydrostatic_jacobian_test_utils::relative_error(
|
||||
gravityPotentialAction, gravityPotentialReference, f.mesh->GetComm()
|
||||
);
|
||||
const double gravityPotentialError = prepared_hydrostatic_jacobian_test_utils::relative_error(
|
||||
gravityPotentialAction, gravityPotentialReference, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
const double bernoulliConstantError =
|
||||
prepared_hydrostatic_jacobian_test_utils::relative_error(
|
||||
bernoulliConstantAction, bernoulliConstantReference,
|
||||
f.mesh->GetComm()
|
||||
);
|
||||
const double bernoulliConstantError = prepared_hydrostatic_jacobian_test_utils::relative_error(
|
||||
bernoulliConstantAction, bernoulliConstantReference, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
const double combinedError =
|
||||
prepared_hydrostatic_jacobian_test_utils::relative_error(
|
||||
combinedAction, combinedReference, f.mesh->GetComm()
|
||||
);
|
||||
prepared_hydrostatic_jacobian_test_utils::relative_error(combinedAction, combinedReference, f.mesh->GetComm());
|
||||
|
||||
const double blockSumError =
|
||||
prepared_hydrostatic_jacobian_test_utils::relative_error(
|
||||
combinedAction, sumOfBlocks, f.mesh->GetComm()
|
||||
);
|
||||
prepared_hydrostatic_jacobian_test_utils::relative_error(combinedAction, sumOfBlocks, f.mesh->GetComm());
|
||||
|
||||
INFO("Enthalpy block error = " << enthalpyError);
|
||||
INFO("Gravity-potential block error = " << gravityPotentialError);
|
||||
@@ -293,30 +253,23 @@ TEST_CASE(
|
||||
"Geometry",
|
||||
tags::barotrope &tags::hydro &tags::jacobian &tags::prepared &tags::unit
|
||||
) {
|
||||
auto args = test_utils::setup_args();
|
||||
auto args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f =
|
||||
mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator
|
||||
preparedOperator(f, *f.domainMapperStateless);
|
||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator preparedOperator(f, *f.domainMapperStateless);
|
||||
|
||||
mfem::Vector enthalpy =
|
||||
prepared_hydrostatic_jacobian_test_utils::make_enthalpy(f);
|
||||
mfem::Vector enthalpy = prepared_hydrostatic_jacobian_test_utils::make_enthalpy(f);
|
||||
|
||||
mfem::Vector gravityPotential =
|
||||
prepared_hydrostatic_jacobian_test_utils::make_gravity_potential(f);
|
||||
mfem::Vector gravityPotential = prepared_hydrostatic_jacobian_test_utils::make_gravity_potential(f);
|
||||
|
||||
mfem::Vector displacement =
|
||||
gravity_prepared_test_utils::make_displacement(f, 0.42);
|
||||
mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.42);
|
||||
|
||||
double bernoulliConstant = 0.37;
|
||||
double bernoulliConstant = 0.37;
|
||||
|
||||
mean_field::physics::RigidRotation rotation =
|
||||
prepared_hydrostatic_jacobian_test_utils::make_rotation(0.8);
|
||||
mean_field::physics::RigidRotation rotation = prepared_hydrostatic_jacobian_test_utils::make_rotation(0.8);
|
||||
|
||||
auto dependencies =
|
||||
prepared_hydrostatic_jacobian_test_utils::make_dependencies();
|
||||
auto dependencies = prepared_hydrostatic_jacobian_test_utils::make_dependencies();
|
||||
|
||||
preparedOperator.Prepare(
|
||||
prepared_hydrostatic_jacobian_test_utils::make_state(
|
||||
@@ -325,32 +278,25 @@ TEST_CASE(
|
||||
dependencies, rotation
|
||||
);
|
||||
|
||||
const mfem::Vector enthalpyVariation =
|
||||
prepared_hydrostatic_jacobian_test_utils::make_enthalpy(f, 0.67);
|
||||
const mfem::Vector enthalpyVariation = prepared_hydrostatic_jacobian_test_utils::make_enthalpy(f, 0.67);
|
||||
|
||||
const mfem::Vector gravityPotentialVariation =
|
||||
prepared_hydrostatic_jacobian_test_utils::make_gravity_potential(
|
||||
f, 0.79
|
||||
);
|
||||
prepared_hydrostatic_jacobian_test_utils::make_gravity_potential(f, 0.79);
|
||||
|
||||
constexpr double bernoulliConstantVariation = 0.28;
|
||||
|
||||
mfem::Vector initialAction;
|
||||
|
||||
preparedOperator.ApplyAlgebraicJacobianAction(
|
||||
enthalpyVariation, gravityPotentialVariation,
|
||||
bernoulliConstantVariation, initialAction
|
||||
enthalpyVariation, gravityPotentialVariation, bernoulliConstantVariation, initialAction
|
||||
);
|
||||
|
||||
enthalpy = prepared_hydrostatic_jacobian_test_utils::make_enthalpy(f, 1.13);
|
||||
enthalpy = prepared_hydrostatic_jacobian_test_utils::make_enthalpy(f, 1.13);
|
||||
|
||||
gravityPotential =
|
||||
prepared_hydrostatic_jacobian_test_utils::make_gravity_potential(
|
||||
f, 1.31
|
||||
);
|
||||
gravityPotential = prepared_hydrostatic_jacobian_test_utils::make_gravity_potential(f, 1.31);
|
||||
|
||||
bernoulliConstant = 0.62;
|
||||
rotation = prepared_hydrostatic_jacobian_test_utils::make_rotation(1.4);
|
||||
rotation = prepared_hydrostatic_jacobian_test_utils::make_rotation(1.4);
|
||||
|
||||
++dependencies.enthalpy.revision;
|
||||
++dependencies.gravityPotential.revision;
|
||||
@@ -367,8 +313,7 @@ TEST_CASE(
|
||||
mfem::Vector baseStateChangedAction;
|
||||
|
||||
preparedOperator.ApplyAlgebraicJacobianAction(
|
||||
enthalpyVariation, gravityPotentialVariation,
|
||||
bernoulliConstantVariation, baseStateChangedAction
|
||||
enthalpyVariation, gravityPotentialVariation, bernoulliConstantVariation, baseStateChangedAction
|
||||
);
|
||||
|
||||
CHECK_FALSE(baseStateReport.contextReport.preparedGeometryState);
|
||||
@@ -382,19 +327,15 @@ TEST_CASE(
|
||||
) == 0.0
|
||||
);
|
||||
|
||||
const mfem::Vector secondEnthalpyVariation =
|
||||
prepared_hydrostatic_jacobian_test_utils::make_enthalpy(f, 1.57);
|
||||
const mfem::Vector secondEnthalpyVariation = prepared_hydrostatic_jacobian_test_utils::make_enthalpy(f, 1.57);
|
||||
|
||||
const mfem::Vector secondGravityPotentialVariation =
|
||||
prepared_hydrostatic_jacobian_test_utils::make_gravity_potential(
|
||||
f, 1.73
|
||||
);
|
||||
prepared_hydrostatic_jacobian_test_utils::make_gravity_potential(f, 1.73);
|
||||
|
||||
mfem::Vector secondDirectionAction;
|
||||
|
||||
preparedOperator.ApplyAlgebraicJacobianAction(
|
||||
secondEnthalpyVariation, secondGravityPotentialVariation, -0.19,
|
||||
secondDirectionAction
|
||||
secondEnthalpyVariation, secondGravityPotentialVariation, -0.19, secondDirectionAction
|
||||
);
|
||||
|
||||
CHECK(preparedOperator.GetAlgebraicJacobianStatistics().preparations == 1);
|
||||
@@ -413,8 +354,7 @@ TEST_CASE(
|
||||
mfem::Vector geometryChangedAction;
|
||||
|
||||
preparedOperator.ApplyAlgebraicJacobianAction(
|
||||
enthalpyVariation, gravityPotentialVariation,
|
||||
bernoulliConstantVariation, geometryChangedAction
|
||||
enthalpyVariation, gravityPotentialVariation, bernoulliConstantVariation, geometryChangedAction
|
||||
);
|
||||
|
||||
mfem::Vector enthalpyPlus(enthalpy);
|
||||
@@ -432,31 +372,23 @@ TEST_CASE(
|
||||
mfem::Vector geometryReference;
|
||||
|
||||
prepared_hydrostatic_jacobian_test_utils::centered_residual_difference(
|
||||
f, rotation, enthalpyPlus, enthalpyMinus, gravityPotentialPlus,
|
||||
gravityPotentialMinus, displacement,
|
||||
bernoulliConstant + 0.5 * bernoulliConstantVariation,
|
||||
bernoulliConstant - 0.5 * bernoulliConstantVariation, geometryReference
|
||||
f, rotation, enthalpyPlus, enthalpyMinus, gravityPotentialPlus, gravityPotentialMinus, displacement,
|
||||
bernoulliConstant + 0.5 * bernoulliConstantVariation, bernoulliConstant - 0.5 * bernoulliConstantVariation,
|
||||
geometryReference
|
||||
);
|
||||
|
||||
const double geometryReferenceError =
|
||||
prepared_hydrostatic_jacobian_test_utils::relative_error(
|
||||
geometryChangedAction, geometryReference, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
const double geometryEffect =
|
||||
prepared_hydrostatic_jacobian_test_utils::relative_error(
|
||||
geometryChangedAction, initialAction, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
INFO(
|
||||
"Geometry-updated algebraic Jacobian error = " << geometryReferenceError
|
||||
const double geometryReferenceError = prepared_hydrostatic_jacobian_test_utils::relative_error(
|
||||
geometryChangedAction, geometryReference, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
INFO(
|
||||
"Algebraic Jacobian change after deformation update = "
|
||||
<< geometryEffect
|
||||
const double geometryEffect = prepared_hydrostatic_jacobian_test_utils::relative_error(
|
||||
geometryChangedAction, initialAction, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
INFO("Geometry-updated algebraic Jacobian error = " << geometryReferenceError);
|
||||
|
||||
INFO("Algebraic Jacobian change after deformation update = " << geometryEffect);
|
||||
|
||||
const auto &statistics = preparedOperator.GetAlgebraicJacobianStatistics();
|
||||
|
||||
CHECK(geometryReport.contextReport.preparedGeometryState);
|
||||
|
||||
508
tests/operators/prepared_mass_normalization.cpp
Normal file
508
tests/operators/prepared_mass_normalization.cpp
Normal file
@@ -0,0 +1,508 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <mfem.hpp>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace mass_normalization_test_utils {
|
||||
using CoupledForm = mean_field::utils::blocks::barotropic_equilibrium_form;
|
||||
|
||||
constexpr auto densityValue =
|
||||
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term);
|
||||
|
||||
constexpr auto displacementValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||
mean_field::utils::blocks::displacement_field.geometry_term
|
||||
);
|
||||
|
||||
constexpr auto massResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
|
||||
);
|
||||
|
||||
[[nodiscard]] mean_field::operators::MassNormalizationLayout make_layout(const mean_field::fem::FEM &f) {
|
||||
const std::array<int, CoupledForm::value_block_count> valueSizes{
|
||||
f.densityFes->GetTrueVSize(), f.displacementFes->GetTrueVSize(), f.gravityFluxFes->GetTrueVSize(),
|
||||
f.gravityPotentialFes->GetTrueVSize(), f.enthalpyFes->GetTrueVSize(), 1
|
||||
};
|
||||
|
||||
const std::array<int, CoupledForm::residual_block_count> residualSizes{
|
||||
f.gravityFluxFes->GetTrueVSize(), f.gravityPotentialFes->GetTrueVSize(), f.densityFes->GetTrueVSize(),
|
||||
f.displacementFes->GetTrueVSize(), f.enthalpyFes->GetTrueVSize(), 1
|
||||
};
|
||||
|
||||
return {valueSizes, residualSizes};
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_density(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase
|
||||
) {
|
||||
mfem::ParGridFunction field(f.densityFes.get());
|
||||
mfem::FunctionCoefficient coefficient([phase](const mfem::Vector &position) {
|
||||
return 0.91 + 0.07 * std::sin(0.83 * position(0) + phase) + 0.05 * std::cos(0.61 * position(1) - phase) +
|
||||
0.03 * position(2) * position(2);
|
||||
});
|
||||
field.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector result;
|
||||
field.GetTrueDofs(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_constant_density(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double value
|
||||
) {
|
||||
mfem::ParGridFunction field(f.densityFes.get());
|
||||
mfem::ConstantCoefficient coefficient(value);
|
||||
field.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector result;
|
||||
field.GetTrueDofs(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_density_direction(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase
|
||||
) {
|
||||
mfem::ParGridFunction field(f.densityFes.get());
|
||||
mfem::FunctionCoefficient coefficient([phase](const mfem::Vector &position) {
|
||||
return 0.19 * std::sin(0.71 * position(0) + phase) - 0.13 * std::cos(0.89 * position(1) - phase) +
|
||||
0.08 * position(2);
|
||||
});
|
||||
field.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector result;
|
||||
field.GetTrueDofs(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_affine_displacement(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double scale
|
||||
) {
|
||||
mfem::ParGridFunction field(f.displacementFes.get());
|
||||
mfem::VectorFunctionCoefficient coefficient(
|
||||
f.mesh->Dimension(), [scale](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(position.Size());
|
||||
for (int dimension = 0; dimension < position.Size(); ++dimension) {
|
||||
value(dimension) = scale * position(dimension);
|
||||
}
|
||||
}
|
||||
);
|
||||
field.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector result;
|
||||
field.GetTrueDofs(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_displacement_direction(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double scale
|
||||
) {
|
||||
mfem::ParGridFunction field(f.displacementFes.get());
|
||||
mfem::VectorFunctionCoefficient coefficient(
|
||||
f.mesh->Dimension(), [scale](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value(0) = scale * (0.07 * position(0) + 0.018 * position(1) * position(2));
|
||||
value(1) = scale * (-0.05 * position(1) + 0.013 * position(0) * position(2));
|
||||
value(2) = scale * (0.04 * position(2) - 0.011 * position(0) * position(1));
|
||||
}
|
||||
);
|
||||
field.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector result;
|
||||
field.GetTrueDofs(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::MassNormalizationDependencies make_dependencies() {
|
||||
return {
|
||||
.discretization = {.identity = 701, .revision = 3},
|
||||
.density = {.identity = 709, .revision = 5},
|
||||
.displacement = {.identity = 719, .revision = 7},
|
||||
.targetMass = {.identity = 727, .revision = 11}
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::context::gravity_field::GravityFieldRevisions make_gravity_revisions(
|
||||
const mean_field::operators::MassNormalizationDependencies &dependencies,
|
||||
const std::uint64_t gravityGradientRevision = 13,
|
||||
const std::uint64_t gravityPotentialRevision = 17
|
||||
) {
|
||||
return {
|
||||
.discretization = {.value = dependencies.discretization.revision},
|
||||
.displacement = {.value = dependencies.displacement.revision},
|
||||
.density = {.value = dependencies.density.revision},
|
||||
.gravity_gradient = {.value = gravityGradientRevision},
|
||||
.gravity_potential = {.value = gravityPotentialRevision}
|
||||
};
|
||||
}
|
||||
|
||||
void prepare_gravity_context(
|
||||
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext &context,
|
||||
const mean_field::fem::FEM &f,
|
||||
const mfem::Vector &density,
|
||||
const mfem::Vector &displacement,
|
||||
const mean_field::operators::MassNormalizationDependencies &dependencies,
|
||||
const std::uint64_t gravityGradientRevision = 13,
|
||||
const std::uint64_t gravityPotentialRevision = 17
|
||||
) {
|
||||
mfem::Vector gravityGradient(f.gravityFluxFes->GetTrueVSize());
|
||||
gravityGradient = 0.0;
|
||||
|
||||
mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize());
|
||||
gravityPotential = 0.0;
|
||||
|
||||
context.Prepare(
|
||||
{.density = density,
|
||||
.displacement = displacement,
|
||||
.gravity_gradient = gravityGradient,
|
||||
.gravity_potential = gravityPotential},
|
||||
make_gravity_revisions(dependencies, gravityGradientRevision, gravityPotentialRevision)
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] double residual_value(const mean_field::operators::PreparedMassNormalizationOperator &massOperator) {
|
||||
mfem::Vector residual;
|
||||
massOperator.BuildResidual(residual);
|
||||
REQUIRE(residual.Size() == 1);
|
||||
return residual(0);
|
||||
}
|
||||
|
||||
[[nodiscard]] double relative_error(
|
||||
const double computed,
|
||||
const double reference
|
||||
) {
|
||||
return std::abs(computed - reference) /
|
||||
std::max(std::abs(reference), 100.0 * std::numeric_limits<double>::epsilon());
|
||||
}
|
||||
} // namespace mass_normalization_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Mass Normalization Has The Analytic Affine Volume Scaling",
|
||||
tags::barotrope &tags::prepared &tags::analytic_comparison
|
||||
) {
|
||||
using Operator = mean_field::operators::PreparedMassNormalizationOperator;
|
||||
|
||||
STATIC_REQUIRE_FALSE(std::is_copy_constructible_v<Operator>);
|
||||
STATIC_REQUIRE_FALSE(std::is_copy_assignable_v<Operator>);
|
||||
STATIC_REQUIRE_FALSE(std::is_move_constructible_v<Operator>);
|
||||
STATIC_REQUIRE_FALSE(std::is_move_assignable_v<Operator>);
|
||||
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const double densityValue = 1.37;
|
||||
const double targetMass = 0.73;
|
||||
const double affineScale = 0.086;
|
||||
|
||||
const mfem::Vector density = mass_normalization_test_utils::make_constant_density(f, densityValue);
|
||||
mfem::Vector displacement(f.displacementFes->GetTrueVSize());
|
||||
displacement = 0.0;
|
||||
|
||||
auto dependencies = mass_normalization_test_utils::make_dependencies();
|
||||
|
||||
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
|
||||
mass_normalization_test_utils::prepare_gravity_context(gravityContext, f, density, displacement, dependencies);
|
||||
|
||||
Operator massOperator(f, *f.domainMapperStateless, gravityContext);
|
||||
|
||||
const auto initialReport = massOperator.Prepare({.targetMass = targetMass}, dependencies);
|
||||
|
||||
CHECK(initialReport.rebuiltStaticPlan);
|
||||
CHECK(initialReport.refreshedGeometry);
|
||||
CHECK(initialReport.refreshedDensity);
|
||||
CHECK(initialReport.updatedTargetMass);
|
||||
CHECK(initialReport.assembledResidual);
|
||||
|
||||
const double undeformedMass = massOperator.GetCurrentMass();
|
||||
const mean_field::mapping::COORDINATE_SPACE volumeCoordinates =
|
||||
f.has_mapping() ? mean_field::mapping::COORDINATE_SPACE::PHYSICAL
|
||||
: mean_field::mapping::COORDINATE_SPACE::REFERENCE;
|
||||
|
||||
const double independentlyIntegratedMass =
|
||||
densityValue * mean_field::analysis::get_mesh_volume(f, volumeCoordinates, mean_field::utils::DOMAINS::STELLAR);
|
||||
|
||||
CHECK(mass_normalization_test_utils::relative_error(undeformedMass, independentlyIntegratedMass) < 1.0e-12);
|
||||
|
||||
CHECK(
|
||||
mass_normalization_test_utils::relative_error(
|
||||
mass_normalization_test_utils::residual_value(massOperator), undeformedMass - targetMass
|
||||
) < 2.0e-15
|
||||
);
|
||||
|
||||
displacement = mass_normalization_test_utils::make_affine_displacement(f, affineScale);
|
||||
++dependencies.displacement.revision;
|
||||
|
||||
mass_normalization_test_utils::prepare_gravity_context(gravityContext, f, density, displacement, dependencies);
|
||||
|
||||
const auto deformedReport = massOperator.Prepare({.targetMass = targetMass}, dependencies);
|
||||
|
||||
CHECK_FALSE(deformedReport.rebuiltStaticPlan);
|
||||
CHECK(deformedReport.refreshedGeometry);
|
||||
CHECK_FALSE(deformedReport.refreshedDensity);
|
||||
|
||||
const double expectedScale = std::pow(1.0 + affineScale, 3);
|
||||
const double measuredScale = massOperator.GetCurrentMass() / undeformedMass;
|
||||
|
||||
INFO("Expected affine mass scale = " << expectedScale);
|
||||
INFO("Measured affine mass scale = " << measuredScale);
|
||||
CHECK(mass_normalization_test_utils::relative_error(measuredScale, expectedScale) < 5e-7);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Mass Normalization Density Jacobian Matches Centered Difference",
|
||||
tags::barotrope &tags::prepared &tags::jacobian &tags::accuracy
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
mfem::Vector density = mass_normalization_test_utils::make_density(f, 0.31);
|
||||
const mfem::Vector densityDirection = mass_normalization_test_utils::make_density_direction(f, 0.67);
|
||||
const mfem::Vector displacement = mass_normalization_test_utils::make_displacement_direction(f, 0.43);
|
||||
|
||||
auto dependencies = mass_normalization_test_utils::make_dependencies();
|
||||
|
||||
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
mass_normalization_test_utils::prepare_gravity_context(gravityContext, f, density, displacement, dependencies);
|
||||
|
||||
mean_field::operators::PreparedMassNormalizationOperator massOperator(f, *f.domainMapperStateless, gravityContext);
|
||||
massOperator.Prepare({.targetMass = 1.23}, dependencies);
|
||||
|
||||
mfem::Vector analyticAction;
|
||||
massOperator.ApplyDensityJacobianAction(densityDirection, analyticAction);
|
||||
|
||||
constexpr double epsilon = 1.0e-3;
|
||||
mfem::Vector densityPlus(density);
|
||||
densityPlus.Add(epsilon, densityDirection);
|
||||
++dependencies.density.revision;
|
||||
mass_normalization_test_utils::prepare_gravity_context(gravityContext, f, densityPlus, displacement, dependencies);
|
||||
massOperator.Prepare({.targetMass = 1.23}, dependencies);
|
||||
const double residualPlus = mass_normalization_test_utils::residual_value(massOperator);
|
||||
|
||||
mfem::Vector densityMinus(density);
|
||||
densityMinus.Add(-epsilon, densityDirection);
|
||||
++dependencies.density.revision;
|
||||
mass_normalization_test_utils::prepare_gravity_context(gravityContext, f, densityMinus, displacement, dependencies);
|
||||
massOperator.Prepare({.targetMass = 1.23}, dependencies);
|
||||
const double residualMinus = mass_normalization_test_utils::residual_value(massOperator);
|
||||
|
||||
const double finiteDifference = (residualPlus - residualMinus) / (2.0 * epsilon);
|
||||
|
||||
INFO("Density action = " << analyticAction(0));
|
||||
INFO("Density centered difference = " << finiteDifference);
|
||||
CHECK(mass_normalization_test_utils::relative_error(analyticAction(0), finiteDifference) < 3.0e-8);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Mass Normalization Geometry Jacobian Matches Centered Difference",
|
||||
tags::barotrope &tags::prepared &tags::jacobian &tags::geometry
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const mfem::Vector density = mass_normalization_test_utils::make_density(f, 0.37);
|
||||
mfem::Vector displacement = mass_normalization_test_utils::make_displacement_direction(f, 0.51);
|
||||
const mfem::Vector displacementDirection = mass_normalization_test_utils::make_displacement_direction(f, -0.79);
|
||||
|
||||
auto dependencies = mass_normalization_test_utils::make_dependencies();
|
||||
|
||||
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
mass_normalization_test_utils::prepare_gravity_context(gravityContext, f, density, displacement, dependencies);
|
||||
|
||||
mean_field::operators::PreparedMassNormalizationOperator massOperator(f, *f.domainMapperStateless, gravityContext);
|
||||
massOperator.Prepare({.targetMass = 1.11}, dependencies);
|
||||
|
||||
mfem::Vector analyticAction;
|
||||
massOperator.ApplyDisplacementJacobianAction(displacementDirection, analyticAction);
|
||||
|
||||
constexpr double epsilon = 1.0e-6;
|
||||
mfem::Vector displacementPlus(displacement);
|
||||
displacementPlus.Add(epsilon, displacementDirection);
|
||||
++dependencies.displacement.revision;
|
||||
mass_normalization_test_utils::prepare_gravity_context(gravityContext, f, density, displacementPlus, dependencies);
|
||||
massOperator.Prepare({.targetMass = 1.11}, dependencies);
|
||||
const double residualPlus = mass_normalization_test_utils::residual_value(massOperator);
|
||||
|
||||
mfem::Vector displacementMinus(displacement);
|
||||
displacementMinus.Add(-epsilon, displacementDirection);
|
||||
++dependencies.displacement.revision;
|
||||
mass_normalization_test_utils::prepare_gravity_context(gravityContext, f, density, displacementMinus, dependencies);
|
||||
massOperator.Prepare({.targetMass = 1.11}, dependencies);
|
||||
const double residualMinus = mass_normalization_test_utils::residual_value(massOperator);
|
||||
|
||||
const double finiteDifference = (residualPlus - residualMinus) / (2.0 * epsilon);
|
||||
|
||||
INFO("Geometry action = " << analyticAction(0));
|
||||
INFO("Geometry centered difference = " << finiteDifference);
|
||||
CHECK(mass_normalization_test_utils::relative_error(analyticAction(0), finiteDifference) < 2.0e-7);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Mass Normalization Selectively Refreshes Its Cached State",
|
||||
tags::barotrope &tags::prepared &tags::contexts &tags::integration
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
mfem::Vector density = mass_normalization_test_utils::make_density(f, 0.29);
|
||||
mfem::Vector displacement = mass_normalization_test_utils::make_displacement_direction(f, 0.41);
|
||||
auto dependencies = mass_normalization_test_utils::make_dependencies();
|
||||
|
||||
std::uint64_t gravityPotentialRevision = 17;
|
||||
|
||||
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
mass_normalization_test_utils::prepare_gravity_context(
|
||||
gravityContext, f, density, displacement, dependencies, 13, gravityPotentialRevision
|
||||
);
|
||||
|
||||
mean_field::operators::PreparedMassNormalizationOperator massOperator(f, *f.domainMapperStateless, gravityContext);
|
||||
|
||||
massOperator.Prepare({.targetMass = 1.0}, dependencies);
|
||||
const std::uint64_t preparationCount = massOperator.GetPreparationCount();
|
||||
|
||||
const auto repeated = massOperator.Prepare({.targetMass = 1.0}, dependencies);
|
||||
CHECK_FALSE(repeated.DidAnyWork());
|
||||
CHECK(massOperator.GetPreparationCount() == preparationCount);
|
||||
|
||||
++gravityPotentialRevision;
|
||||
mass_normalization_test_utils::prepare_gravity_context(
|
||||
gravityContext, f, density, displacement, dependencies, 13, gravityPotentialRevision
|
||||
);
|
||||
|
||||
const auto potentialOnly = massOperator.Prepare({.targetMass = 1.0}, dependencies);
|
||||
CHECK_FALSE(potentialOnly.DidAnyWork());
|
||||
|
||||
const double residualBeforeTargetChange = mass_normalization_test_utils::residual_value(massOperator);
|
||||
const double massBeforeTargetChange = massOperator.GetCurrentMass();
|
||||
|
||||
++dependencies.targetMass.revision;
|
||||
const auto targetOnly = massOperator.Prepare({.targetMass = 1.4}, dependencies);
|
||||
CHECK(targetOnly.updatedTargetMass);
|
||||
CHECK(targetOnly.assembledResidual);
|
||||
CHECK_FALSE(targetOnly.rebuiltStaticPlan);
|
||||
CHECK_FALSE(targetOnly.refreshedGeometry);
|
||||
CHECK_FALSE(targetOnly.refreshedDensity);
|
||||
CHECK(massOperator.GetCurrentMass() == massBeforeTargetChange);
|
||||
CHECK(
|
||||
mass_normalization_test_utils::relative_error(
|
||||
mass_normalization_test_utils::residual_value(massOperator) - residualBeforeTargetChange, -0.4
|
||||
) < 2.0e-15
|
||||
);
|
||||
|
||||
const double massBeforeDensityChange = massOperator.GetCurrentMass();
|
||||
density = mass_normalization_test_utils::make_density(f, 0.83);
|
||||
++dependencies.density.revision;
|
||||
mass_normalization_test_utils::prepare_gravity_context(
|
||||
gravityContext, f, density, displacement, dependencies, 13, gravityPotentialRevision
|
||||
);
|
||||
|
||||
const auto densityOnly = massOperator.Prepare({.targetMass = 1.4}, dependencies);
|
||||
CHECK(densityOnly.refreshedDensity);
|
||||
CHECK(densityOnly.assembledResidual);
|
||||
CHECK_FALSE(densityOnly.refreshedGeometry);
|
||||
CHECK(massOperator.GetCurrentMass() != massBeforeDensityChange);
|
||||
|
||||
displacement = mass_normalization_test_utils::make_displacement_direction(f, 0.87);
|
||||
++dependencies.displacement.revision;
|
||||
mass_normalization_test_utils::prepare_gravity_context(
|
||||
gravityContext, f, density, displacement, dependencies, 13, gravityPotentialRevision
|
||||
);
|
||||
|
||||
const auto geometryOnly = massOperator.Prepare({.targetMass = 1.4}, dependencies);
|
||||
CHECK(geometryOnly.refreshedGeometry);
|
||||
CHECK(geometryOnly.assembledResidual);
|
||||
CHECK_FALSE(geometryOnly.refreshedDensity);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Mass Normalization Complete Action And Coupled Routing Are Exact",
|
||||
tags::barotrope &tags::prepared &tags::jacobian &tags::mfem_operators
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const mfem::Vector density = mass_normalization_test_utils::make_density(f, 0.47);
|
||||
const mfem::Vector displacement = mass_normalization_test_utils::make_displacement_direction(f, 0.57);
|
||||
const mfem::Vector densityDirection = mass_normalization_test_utils::make_density_direction(f, 0.71);
|
||||
const mfem::Vector displacementDirection = mass_normalization_test_utils::make_displacement_direction(f, -0.63);
|
||||
|
||||
const auto dependencies = mass_normalization_test_utils::make_dependencies();
|
||||
|
||||
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
mass_normalization_test_utils::prepare_gravity_context(gravityContext, f, density, displacement, dependencies);
|
||||
|
||||
mean_field::operators::PreparedMassNormalizationOperator massOperator(f, *f.domainMapperStateless, gravityContext);
|
||||
massOperator.Prepare({.targetMass = 1.19}, dependencies);
|
||||
|
||||
mfem::Vector densityAction;
|
||||
mfem::Vector displacementAction;
|
||||
mfem::Vector completeAction;
|
||||
|
||||
massOperator.ApplyDensityJacobianAction(densityDirection, densityAction);
|
||||
massOperator.ApplyDisplacementJacobianAction(displacementDirection, displacementAction);
|
||||
massOperator.ApplyCompleteJacobianAction(densityDirection, displacementDirection, completeAction);
|
||||
|
||||
CHECK(
|
||||
mass_normalization_test_utils::relative_error(completeAction(0), densityAction(0) + displacementAction(0)) <
|
||||
2.0e-15
|
||||
);
|
||||
|
||||
const auto layout = mass_normalization_test_utils::make_layout(f);
|
||||
mean_field::operators::PreparedMassNormalizationJacobianOperator adapter(layout, massOperator);
|
||||
|
||||
mfem::Vector direction(layout.value_offsets().Last());
|
||||
direction = 0.0;
|
||||
|
||||
for (int entry = 0; entry < densityDirection.Size(); ++entry) {
|
||||
direction(layout.offset(mass_normalization_test_utils::densityValue) + entry) = densityDirection(entry);
|
||||
}
|
||||
|
||||
for (int entry = 0; entry < displacementDirection.Size(); ++entry) {
|
||||
direction(layout.offset(mass_normalization_test_utils::displacementValue) + entry) =
|
||||
displacementDirection(entry);
|
||||
}
|
||||
|
||||
mfem::Vector coupledAction;
|
||||
adapter.Mult(direction, coupledAction);
|
||||
|
||||
const int massOffset = layout.offset(mass_normalization_test_utils::massResidual);
|
||||
|
||||
REQUIRE(coupledAction.Size() == layout.residual_offsets().Last());
|
||||
CHECK(coupledAction(massOffset) == completeAction(0));
|
||||
|
||||
for (int entry = 0; entry < coupledAction.Size(); ++entry) {
|
||||
if (entry != massOffset) {
|
||||
CHECK(coupledAction(entry) == 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
CHECK(&massOperator.GetFEM() == &f);
|
||||
CHECK(&massOperator.GetGravityContext() == &gravityContext);
|
||||
CHECK(adapter.GetLayout().residual_offsets().Last() == layout.residual_offsets().Last());
|
||||
}
|
||||
706
tests/operators/prepared_pressure_force.cpp
Normal file
706
tests/operators/prepared_pressure_force.cpp
Normal file
@@ -0,0 +1,706 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <mfem.hpp>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace prepared_pressure_force_test_utils {
|
||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
|
||||
using CoupledForm = mean_field::utils::blocks::barotropic_equilibrium_form;
|
||||
|
||||
struct Maps final {
|
||||
mean_field::field::FieldDofMap density;
|
||||
mean_field::field::FieldDofMap displacement;
|
||||
mean_field::field::FieldDofMap gravityFlux;
|
||||
mean_field::field::FieldDofMap gravityPotential;
|
||||
mean_field::field::FieldDofMap enthalpy;
|
||||
|
||||
explicit Maps(const mean_field::fem::FEM &f)
|
||||
: density(
|
||||
mean_field::field::make_field_dof_map<
|
||||
mean_field::field::Density,
|
||||
DomainSchema>(*f.densityFes)
|
||||
),
|
||||
displacement(
|
||||
mean_field::field::make_field_dof_map<
|
||||
mean_field::field::Displacement,
|
||||
DomainSchema>(*f.displacementFes)
|
||||
),
|
||||
gravityFlux(
|
||||
mean_field::field::make_field_dof_map<
|
||||
mean_field::field::Gravity,
|
||||
DomainSchema>(*f.gravityFluxFes)
|
||||
),
|
||||
gravityPotential(
|
||||
mean_field::field::make_field_dof_map<
|
||||
mean_field::field::Gravity,
|
||||
DomainSchema>(*f.gravityPotentialFes)
|
||||
),
|
||||
enthalpy(
|
||||
mean_field::field::make_field_dof_map<
|
||||
mean_field::field::Enthalpy,
|
||||
DomainSchema>(*f.enthalpyFes)
|
||||
) {
|
||||
}
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
mfem::Vector make_positive_enthalpy_true(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase
|
||||
) {
|
||||
mfem::Vector enthalpy(f.enthalpyFes->GetTrueVSize());
|
||||
|
||||
for (int index = 0; index < enthalpy.Size(); ++index) {
|
||||
const double position = static_cast<double>(index + 1);
|
||||
|
||||
enthalpy(index) =
|
||||
0.93 + 0.09 * std::sin(0.23 * position + phase) + 0.04 * std::cos(0.17 * position - 0.5 * phase);
|
||||
}
|
||||
|
||||
return enthalpy;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
mfem::Vector make_enthalpy_direction_true(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase
|
||||
) {
|
||||
mfem::Vector direction(f.enthalpyFes->GetTrueVSize());
|
||||
|
||||
for (int index = 0; index < direction.Size(); ++index) {
|
||||
const double position = static_cast<double>(index + 1);
|
||||
|
||||
direction(index) =
|
||||
0.27 * std::sin(0.19 * position + phase) + 0.14 * std::cos(0.13 * position - 0.5 * phase);
|
||||
}
|
||||
|
||||
return direction;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
mfem::Vector make_displacement_direction_true(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
f.mesh->Dimension() == 3, "The prepared pressure-force test requires a "
|
||||
"three-dimensional mesh."
|
||||
);
|
||||
|
||||
mfem::ParGridFunction directionField(f.displacementFes.get());
|
||||
|
||||
mfem::VectorFunctionCoefficient directionCoefficient(
|
||||
3, [phase](const mfem::Vector &position, mfem::Vector &value) {
|
||||
const double x = position(0);
|
||||
|
||||
const double y = position(1);
|
||||
|
||||
const double z = position(2);
|
||||
|
||||
value.SetSize(3);
|
||||
|
||||
value(0) = 0.019 * x + 0.011 * y * z - 0.006 * z * z + 0.004 * phase * y;
|
||||
|
||||
value(1) = -0.016 * y + 0.008 * x * z + 0.005 * x * x - 0.003 * phase * z;
|
||||
|
||||
value(2) = 0.013 * z - 0.010 * x * y + 0.006 * y * y + 0.004 * phase * x;
|
||||
}
|
||||
);
|
||||
|
||||
directionField.ProjectCoefficient(directionCoefficient);
|
||||
|
||||
mfem::Vector directionTrue;
|
||||
|
||||
directionField.GetTrueDofs(directionTrue);
|
||||
|
||||
return directionTrue;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
double relative_difference(
|
||||
const mfem::Vector &left,
|
||||
const mfem::Vector &right,
|
||||
const MPI_Comm communicator
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
left.Size() == right.Size(), "Cannot compare prepared pressure-force vectors with "
|
||||
"different sizes."
|
||||
);
|
||||
|
||||
mfem::Vector difference(left);
|
||||
|
||||
difference -= right;
|
||||
|
||||
const double scale = std::max(
|
||||
{gravity_prepared_test_utils::global_norm(left, communicator),
|
||||
gravity_prepared_test_utils::global_norm(right, communicator),
|
||||
100.0 * std::numeric_limits<double>::epsilon()}
|
||||
);
|
||||
|
||||
return gravity_prepared_test_utils::global_norm(difference, communicator) / scale;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
mean_field::operators::context::pressure_force::PressureForceDependencies make_dependencies() {
|
||||
return {
|
||||
.discretization = {.identity = 1201, .revision = 3},
|
||||
.enthalpy = {.identity = 1213, .revision = 5},
|
||||
.displacement = {.identity = 1217, .revision = 7}
|
||||
};
|
||||
}
|
||||
|
||||
constexpr auto densityValue =
|
||||
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term);
|
||||
|
||||
constexpr auto displacementValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||
mean_field::utils::blocks::displacement_field.geometry_term
|
||||
);
|
||||
|
||||
constexpr auto gravityGradientValue =
|
||||
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::gravity_field.gradient_term);
|
||||
|
||||
constexpr auto gravityPotentialValue =
|
||||
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::gravity_field.poisson_term);
|
||||
|
||||
constexpr auto enthalpyValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||
mean_field::utils::blocks::enthalpy_field.specific_term
|
||||
);
|
||||
|
||||
constexpr auto barotropicConstantValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
|
||||
);
|
||||
|
||||
constexpr auto gravityGradientResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
|
||||
constexpr auto gravityPotentialResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
|
||||
constexpr auto densityResidual =
|
||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term);
|
||||
|
||||
constexpr auto displacementResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::displacement_field.geometry_term
|
||||
);
|
||||
|
||||
constexpr auto enthalpyResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::enthalpy_field.specific_term
|
||||
);
|
||||
|
||||
constexpr auto massResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
|
||||
);
|
||||
|
||||
[[nodiscard]]
|
||||
mean_field::operators::BarotropicEquilibriumLayout make_coupled_layout(const Maps &maps) {
|
||||
const std::array<int, CoupledForm::value_block_count> valueSizes{
|
||||
maps.density.reduced_size(), maps.displacement.reduced_size(), maps.gravityFlux.reduced_size(),
|
||||
maps.gravityPotential.reduced_size(), maps.enthalpy.reduced_size(), 1
|
||||
};
|
||||
|
||||
const std::array<int, CoupledForm::residual_block_count> residualSizes{
|
||||
maps.gravityFlux.reduced_size(), maps.gravityPotential.reduced_size(), maps.density.reduced_size(),
|
||||
maps.displacement.reduced_size(), maps.enthalpy.reduced_size(), 1
|
||||
};
|
||||
|
||||
return {valueSizes, residualSizes};
|
||||
}
|
||||
|
||||
template <int index>
|
||||
[[nodiscard]]
|
||||
mfem::Vector copy_residual_block(
|
||||
const mfem::Vector &action,
|
||||
const mean_field::operators::BarotropicEquilibriumLayout &layout,
|
||||
const mean_field::utils::blocks::residual_block<index> block
|
||||
) {
|
||||
mfem::Vector result(layout.size(block));
|
||||
|
||||
const int offset = layout.offset(block);
|
||||
|
||||
for (int entry = 0; entry < result.Size(); ++entry) {
|
||||
result(entry) = action(offset + entry);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace prepared_pressure_force_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Pressure Force Uses FieldDof Supported Dimensions And Owns Its Context",
|
||||
tags::barotrope &tags::pressure &tags::prepared &tags::field &tags::unit
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const prepared_pressure_force_test_utils::Maps maps(f);
|
||||
|
||||
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
||||
|
||||
mean_field::operators::PreparedPressureForceOperator preparedOperator(f, *f.domainMapperStateless, equationOfState);
|
||||
|
||||
REQUIRE(maps.enthalpy.reduced_size() < maps.enthalpy.full_size());
|
||||
|
||||
CHECK(maps.displacement.is_identity());
|
||||
|
||||
CHECK(preparedOperator.GetEnthalpySize() == maps.enthalpy.reduced_size());
|
||||
|
||||
CHECK(preparedOperator.GetDisplacementSize() == maps.displacement.reduced_size());
|
||||
|
||||
CHECK(
|
||||
&preparedOperator.GetContext().GetPreparationStatistics() == &preparedOperator.GetContextPreparationStatistics()
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Pressure Force Jacobian Matches Full Stateless Columns Through FieldDof Restriction",
|
||||
tags::barotrope &tags::pressure &tags::prepared &tags::field &tags::integration &tags::accuracy
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const prepared_pressure_force_test_utils::Maps maps(f);
|
||||
|
||||
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
||||
|
||||
const mfem::Vector enthalpy =
|
||||
maps.enthalpy.gather(prepared_pressure_force_test_utils::make_positive_enthalpy_true(f, 0.47));
|
||||
|
||||
const mfem::Vector displacement = maps.displacement.gather(gravity_prepared_test_utils::make_displacement(f, 0.69));
|
||||
|
||||
const mfem::Vector enthalpyDirection =
|
||||
maps.enthalpy.gather(prepared_pressure_force_test_utils::make_enthalpy_direction_true(f, 0.73));
|
||||
|
||||
const mfem::Vector displacementDirection =
|
||||
maps.displacement.gather(prepared_pressure_force_test_utils::make_displacement_direction_true(f, 0.83));
|
||||
|
||||
mean_field::operators::PreparedPressureForceOperator preparedOperator(f, *f.domainMapperStateless, equationOfState);
|
||||
|
||||
preparedOperator.Prepare(
|
||||
{.enthalpy = enthalpy, .displacement = displacement}, prepared_pressure_force_test_utils::make_dependencies()
|
||||
);
|
||||
|
||||
const mfem::Vector enthalpyTrue = maps.enthalpy.scatter(enthalpy);
|
||||
|
||||
const mfem::Vector displacementTrue = maps.displacement.scatter(displacement);
|
||||
|
||||
const mfem::Vector enthalpyDirectionTrue = maps.enthalpy.scatter(enthalpyDirection);
|
||||
|
||||
const mfem::Vector displacementDirectionTrue = maps.displacement.scatter(displacementDirection);
|
||||
|
||||
mfem::Vector preparedEnthalpyAction;
|
||||
mfem::Vector kernelEnthalpyActionTrue;
|
||||
|
||||
preparedOperator.ApplyEnthalpyJacobianAction(enthalpyDirection, preparedEnthalpyAction);
|
||||
|
||||
mean_field::operators::kernels::apply_pressure_force_enthalpy_action(
|
||||
f, *f.domainMapperStateless, equationOfState, enthalpyTrue, enthalpyDirectionTrue, displacementTrue,
|
||||
kernelEnthalpyActionTrue
|
||||
);
|
||||
|
||||
const mfem::Vector kernelEnthalpyAction = maps.displacement.gather(kernelEnthalpyActionTrue);
|
||||
|
||||
CHECK(
|
||||
prepared_pressure_force_test_utils::relative_difference(
|
||||
preparedEnthalpyAction, kernelEnthalpyAction, f.mesh->GetComm()
|
||||
) < 2.0e-12
|
||||
);
|
||||
|
||||
mfem::Vector preparedDisplacementAction;
|
||||
mfem::Vector kernelDisplacementActionTrue;
|
||||
|
||||
preparedOperator.ApplyDisplacementJacobianAction(displacementDirection, preparedDisplacementAction);
|
||||
|
||||
mean_field::operators::kernels::apply_pressure_force_displacement_action(
|
||||
f, *f.domainMapperStateless, equationOfState, enthalpyTrue, displacementDirectionTrue, displacementTrue,
|
||||
kernelDisplacementActionTrue
|
||||
);
|
||||
|
||||
const mfem::Vector kernelDisplacementAction = maps.displacement.gather(kernelDisplacementActionTrue);
|
||||
|
||||
CHECK(
|
||||
prepared_pressure_force_test_utils::relative_difference(
|
||||
preparedDisplacementAction, kernelDisplacementAction, f.mesh->GetComm()
|
||||
) < 2.0e-12
|
||||
);
|
||||
|
||||
mfem::Vector fusedAction;
|
||||
|
||||
preparedOperator.ApplyCompleteJacobianAction(enthalpyDirection, displacementDirection, fusedAction);
|
||||
|
||||
mfem::Vector expectedFusedAction(kernelEnthalpyAction);
|
||||
|
||||
expectedFusedAction += kernelDisplacementAction;
|
||||
|
||||
CHECK(
|
||||
prepared_pressure_force_test_utils::relative_difference(fusedAction, expectedFusedAction, f.mesh->GetComm()) <
|
||||
2.0e-12
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Pressure Force MFEM Adapter Routes Reduced Coupled FieldDof Blocks",
|
||||
tags::barotrope &tags::pressure &tags::prepared &tags::field &tags::integration &tags::jacobian
|
||||
&tags::mfem_operators &tags::unit
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const prepared_pressure_force_test_utils::Maps maps(f);
|
||||
|
||||
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
||||
|
||||
const mfem::Vector enthalpy =
|
||||
maps.enthalpy.gather(prepared_pressure_force_test_utils::make_positive_enthalpy_true(f, 0.53));
|
||||
|
||||
const mfem::Vector displacement = maps.displacement.gather(gravity_prepared_test_utils::make_displacement(f, 0.71));
|
||||
|
||||
const mfem::Vector enthalpyDirection =
|
||||
maps.enthalpy.gather(prepared_pressure_force_test_utils::make_enthalpy_direction_true(f, 0.89));
|
||||
|
||||
const mfem::Vector displacementDirection =
|
||||
maps.displacement.gather(prepared_pressure_force_test_utils::make_displacement_direction_true(f, 0.97));
|
||||
|
||||
mean_field::operators::PreparedPressureForceOperator preparedOperator(f, *f.domainMapperStateless, equationOfState);
|
||||
|
||||
preparedOperator.Prepare(
|
||||
{.enthalpy = enthalpy, .displacement = displacement}, prepared_pressure_force_test_utils::make_dependencies()
|
||||
);
|
||||
|
||||
const mean_field::operators::BarotropicEquilibriumLayout layout =
|
||||
prepared_pressure_force_test_utils::make_coupled_layout(maps);
|
||||
|
||||
mean_field::operators::PreparedPressureForceJacobianOperator adapter(layout, preparedOperator);
|
||||
|
||||
CHECK(layout.size(prepared_pressure_force_test_utils::enthalpyValue) == maps.enthalpy.reduced_size());
|
||||
|
||||
CHECK(layout.size(prepared_pressure_force_test_utils::densityValue) == maps.density.reduced_size());
|
||||
|
||||
mfem::BlockVector direction(layout.value_offsets());
|
||||
|
||||
direction = 0.0;
|
||||
|
||||
/*
|
||||
* Populate unrelated columns deliberately.
|
||||
*/
|
||||
direction.GetBlock(prepared_pressure_force_test_utils::densityValue) = 0.37;
|
||||
|
||||
direction.GetBlock(prepared_pressure_force_test_utils::gravityGradientValue) = -0.41;
|
||||
|
||||
direction.GetBlock(prepared_pressure_force_test_utils::gravityPotentialValue) = 0.59;
|
||||
|
||||
direction.GetBlock(prepared_pressure_force_test_utils::barotropicConstantValue) = -0.73;
|
||||
|
||||
direction.GetBlock(prepared_pressure_force_test_utils::displacementValue) = displacementDirection;
|
||||
|
||||
direction.GetBlock(prepared_pressure_force_test_utils::enthalpyValue) = enthalpyDirection;
|
||||
|
||||
mfem::Vector expectedDisplacementAction;
|
||||
|
||||
preparedOperator.ApplyCompleteJacobianAction(enthalpyDirection, displacementDirection, expectedDisplacementAction);
|
||||
|
||||
mfem::Vector action;
|
||||
|
||||
adapter.Mult(direction, action);
|
||||
|
||||
const mfem::Vector displacementResidualAction = prepared_pressure_force_test_utils::copy_residual_block(
|
||||
action, layout, prepared_pressure_force_test_utils::displacementResidual
|
||||
);
|
||||
|
||||
CHECK(
|
||||
prepared_pressure_force_test_utils::relative_difference(
|
||||
displacementResidualAction, expectedDisplacementAction, f.mesh->GetComm()
|
||||
) < 2.0e-14
|
||||
);
|
||||
|
||||
CHECK(
|
||||
prepared_pressure_force_test_utils::copy_residual_block(
|
||||
action, layout, prepared_pressure_force_test_utils::gravityGradientResidual
|
||||
)
|
||||
.Norml2() == 0.0
|
||||
);
|
||||
|
||||
CHECK(
|
||||
prepared_pressure_force_test_utils::copy_residual_block(
|
||||
action, layout, prepared_pressure_force_test_utils::gravityPotentialResidual
|
||||
)
|
||||
.Norml2() == 0.0
|
||||
);
|
||||
|
||||
CHECK(
|
||||
prepared_pressure_force_test_utils::copy_residual_block(
|
||||
action, layout, prepared_pressure_force_test_utils::densityResidual
|
||||
)
|
||||
.Norml2() == 0.0
|
||||
);
|
||||
|
||||
CHECK(
|
||||
prepared_pressure_force_test_utils::copy_residual_block(
|
||||
action, layout, prepared_pressure_force_test_utils::enthalpyResidual
|
||||
)
|
||||
.Norml2() == 0.0
|
||||
);
|
||||
|
||||
CHECK(
|
||||
prepared_pressure_force_test_utils::copy_residual_block(
|
||||
action, layout, prepared_pressure_force_test_utils::massResidual
|
||||
)
|
||||
.Norml2() == 0.0
|
||||
);
|
||||
}
|
||||
TEST_CASE(
|
||||
"Pressure Force Residual Converges To A Manufactured Analytic Force",
|
||||
tags::barotrope &tags::pressure &tags::kernels &tags::integration &tags::convergence &tags::h_refinement
|
||||
&tags::analytic_comparison &tags::accuracy
|
||||
) {
|
||||
constexpr int dimension = 3;
|
||||
|
||||
constexpr std::array<int, 2> refinementLevels{0, 1};
|
||||
|
||||
constexpr double minimumObservedRate = 3.0;
|
||||
constexpr double finestRelativeTolerance = 2.0e-3;
|
||||
|
||||
constexpr double amplitude = 1.0;
|
||||
constexpr double bumpSharpness = 0.25;
|
||||
constexpr double supportRadiusFraction = 0.90;
|
||||
|
||||
std::array<double, refinementLevels.size()> relativeErrors{};
|
||||
|
||||
for (std::size_t levelIndex = 0; levelIndex < refinementLevels.size(); ++levelIndex) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, refinementLevels[levelIndex]);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
REQUIRE(f.mesh->Dimension() == dimension);
|
||||
REQUIRE(f.mesh->GetNE() > 0);
|
||||
|
||||
const MPI_Comm communicator = f.mesh->GetComm();
|
||||
|
||||
constexpr double supportRadius = supportRadiusFraction * mean_field::utils::RADIUS;
|
||||
|
||||
constexpr double supportRadiusSquared = supportRadius * supportRadius;
|
||||
|
||||
auto analyticEnthalpyFunction = [supportRadiusSquared](const mfem::Vector &position) {
|
||||
const double normalizedRadiusSquared = (position * position) / supportRadiusSquared;
|
||||
|
||||
if (normalizedRadiusSquared >= 1.0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
const double distanceToSupportBoundary = 1.0 - normalizedRadiusSquared;
|
||||
|
||||
return amplitude * std::exp(-bumpSharpness * normalizedRadiusSquared / distanceToSupportBoundary);
|
||||
};
|
||||
|
||||
auto analyticPressureForceFunction = [supportRadiusSquared](const mfem::Vector &position, mfem::Vector &force) {
|
||||
force.SetSize(dimension);
|
||||
force = 0.0;
|
||||
|
||||
const double normalizedRadiusSquared = (position * position) / supportRadiusSquared;
|
||||
|
||||
if (normalizedRadiusSquared >= 1.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const double distanceToSupportBoundary = 1.0 - normalizedRadiusSquared;
|
||||
|
||||
const double enthalpy =
|
||||
amplitude * std::exp(-bumpSharpness * normalizedRadiusSquared / distanceToSupportBoundary);
|
||||
|
||||
const double pressureGradientScale =
|
||||
-2.0 * bumpSharpness * std::pow(enthalpy, 4.0) /
|
||||
(supportRadiusSquared * distanceToSupportBoundary * distanceToSupportBoundary);
|
||||
|
||||
for (int component = 0; component < dimension; ++component) {
|
||||
force(component) = pressureGradientScale * position(component);
|
||||
}
|
||||
};
|
||||
|
||||
mfem::FunctionCoefficient analyticEnthalpyCoefficient(analyticEnthalpyFunction);
|
||||
|
||||
mfem::VectorFunctionCoefficient analyticPressureForceCoefficient(dimension, analyticPressureForceFunction);
|
||||
|
||||
mfem::ParGridFunction discreteEnthalpyField(f.enthalpyFes.get());
|
||||
|
||||
discreteEnthalpyField.ProjectCoefficient(analyticEnthalpyCoefficient);
|
||||
|
||||
mfem::Vector discreteEnthalpyTrue;
|
||||
discreteEnthalpyField.GetTrueDofs(discreteEnthalpyTrue);
|
||||
|
||||
mfem::Vector zeroDisplacement(f.displacementFes->GetTrueVSize());
|
||||
|
||||
zeroDisplacement = 0.0;
|
||||
|
||||
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
|
||||
|
||||
mfem::Vector discreteResidual;
|
||||
|
||||
mean_field::operators::kernels::apply_pressure_force_residual(
|
||||
f, *f.domainMapperStateless, barotrope, discreteEnthalpyTrue, zeroDisplacement, discreteResidual
|
||||
);
|
||||
|
||||
REQUIRE(discreteResidual.Size() == f.displacementFes->GetTrueVSize());
|
||||
|
||||
mfem::Array<int> stellarMarker(f.mesh->attributes.Max());
|
||||
|
||||
stellarMarker = 0;
|
||||
|
||||
const int vacuumAttribute = f.domainMapperStateless->GetVacuumElementAttribute();
|
||||
|
||||
for (int attributeIndex = 0; attributeIndex < f.mesh->attributes.Size(); ++attributeIndex) {
|
||||
const int attribute = f.mesh->attributes[attributeIndex];
|
||||
|
||||
if (attribute != vacuumAttribute) {
|
||||
stellarMarker[attribute - 1] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
const mfem::Geometry::Type elementGeometry = f.displacementFes->GetFE(0)->GetGeomType();
|
||||
|
||||
for (int element = 1; element < f.mesh->GetNE(); ++element) {
|
||||
REQUIRE(f.displacementFes->GetFE(element)->GetGeomType() == elementGeometry);
|
||||
}
|
||||
|
||||
const int referenceQuadratureOrder = 2 * f.displacementFes->GetMaxElementOrder() + 16;
|
||||
|
||||
const mfem::IntegrationRule &referenceQuadrature =
|
||||
mfem::IntRules.Get(elementGeometry, referenceQuadratureOrder);
|
||||
|
||||
auto *analyticForceIntegrator = new mfem::VectorDomainLFIntegrator(analyticPressureForceCoefficient);
|
||||
|
||||
analyticForceIntegrator->SetIntRule(&referenceQuadrature);
|
||||
|
||||
mfem::ParLinearForm analyticForceLoad(f.displacementFes.get());
|
||||
|
||||
analyticForceLoad.AddDomainIntegrator(analyticForceIntegrator, stellarMarker);
|
||||
|
||||
analyticForceLoad.Assemble();
|
||||
|
||||
std::unique_ptr<mfem::HypreParVector> analyticForceHypreVector(analyticForceLoad.ParallelAssemble());
|
||||
|
||||
REQUIRE(analyticForceHypreVector != nullptr);
|
||||
|
||||
mfem::Vector analyticForceTrue(*analyticForceHypreVector);
|
||||
|
||||
REQUIRE(analyticForceTrue.Size() == discreteResidual.Size());
|
||||
|
||||
const double analyticForceNorm = gravity_prepared_test_utils::global_norm(analyticForceTrue, communicator);
|
||||
|
||||
REQUIRE(std::isfinite(analyticForceNorm));
|
||||
REQUIRE(analyticForceNorm > 0.0);
|
||||
|
||||
mfem::Vector residualError(discreteResidual);
|
||||
residualError -= analyticForceTrue;
|
||||
|
||||
mfem::ParBilinearForm rieszForm(f.displacementFes.get());
|
||||
|
||||
rieszForm.AddDomainIntegrator(new mfem::VectorMassIntegrator());
|
||||
|
||||
rieszForm.AddDomainIntegrator(new mfem::VectorDiffusionIntegrator());
|
||||
|
||||
rieszForm.Assemble();
|
||||
rieszForm.Finalize();
|
||||
|
||||
std::unique_ptr<mfem::HypreParMatrix> rieszMatrix(rieszForm.ParallelAssemble());
|
||||
|
||||
REQUIRE(rieszMatrix != nullptr);
|
||||
REQUIRE(rieszMatrix->Height() == discreteResidual.Size());
|
||||
REQUIRE(rieszMatrix->Width() == discreteResidual.Size());
|
||||
|
||||
mfem::HypreBoomerAMG rieszPreconditioner(*rieszMatrix);
|
||||
|
||||
rieszPreconditioner.SetPrintLevel(0);
|
||||
|
||||
mfem::CGSolver rieszSolver(communicator);
|
||||
|
||||
rieszSolver.SetOperator(*rieszMatrix);
|
||||
rieszSolver.SetPreconditioner(rieszPreconditioner);
|
||||
rieszSolver.SetRelTol(1.0e-13);
|
||||
rieszSolver.SetAbsTol(1.0e-15);
|
||||
rieszSolver.SetMaxIter(5000);
|
||||
rieszSolver.SetPrintLevel(1);
|
||||
|
||||
auto calculateDualNorm = [&rieszSolver, communicator](const mfem::Vector &functional) {
|
||||
mfem::Vector rieszRepresentative(functional.Size());
|
||||
|
||||
rieszRepresentative = 0.0;
|
||||
|
||||
rieszSolver.Mult(functional, rieszRepresentative);
|
||||
|
||||
MFEM_VERIFY(
|
||||
rieszSolver.GetConverged(), "The pressure-force convergence-test Riesz solve "
|
||||
"did not converge."
|
||||
);
|
||||
|
||||
const double dualNormSquared =
|
||||
gravity_prepared_test_utils::global_dot(functional, rieszRepresentative, communicator);
|
||||
|
||||
MFEM_VERIFY(std::isfinite(dualNormSquared), "The pressure-force dual norm is not finite.");
|
||||
|
||||
MFEM_VERIFY(
|
||||
dualNormSquared >= -100.0 * std::numeric_limits<double>::epsilon(),
|
||||
"The pressure-force Riesz operator produced a "
|
||||
"negative dual norm."
|
||||
);
|
||||
|
||||
return std::sqrt(std::max(dualNormSquared, 0.0));
|
||||
};
|
||||
|
||||
const double errorDualNorm = calculateDualNorm(residualError);
|
||||
|
||||
const double analyticDualNorm = calculateDualNorm(analyticForceTrue);
|
||||
|
||||
REQUIRE(std::isfinite(errorDualNorm));
|
||||
REQUIRE(std::isfinite(analyticDualNorm));
|
||||
REQUIRE(errorDualNorm > 0.0);
|
||||
REQUIRE(analyticDualNorm > 0.0);
|
||||
|
||||
relativeErrors[levelIndex] = errorDualNorm / analyticDualNorm;
|
||||
|
||||
INFO("Pressure-force refinement level = " << refinementLevels[levelIndex]);
|
||||
|
||||
INFO("Pressure-force true DOFs = " << f.displacementFes->GlobalTrueVSize());
|
||||
|
||||
INFO("Pressure-force relative dual error = " << relativeErrors[levelIndex]);
|
||||
}
|
||||
|
||||
for (const double relativeError : relativeErrors) {
|
||||
REQUIRE(std::isfinite(relativeError));
|
||||
REQUIRE(relativeError > 0.0);
|
||||
}
|
||||
|
||||
static_assert(refinementLevels.size() == 2, "This reduced convergence test expects exactly two refinement levels.");
|
||||
|
||||
const double observedRate = std::log(relativeErrors[0] / relativeErrors[1]) / std::log(2.0);
|
||||
|
||||
INFO("Level 0 pressure-force relative dual error = " << relativeErrors[0]);
|
||||
|
||||
INFO("Level 1 pressure-force relative dual error = " << relativeErrors[1]);
|
||||
|
||||
INFO("Level 0 to 1 pressure-force convergence rate = " << observedRate);
|
||||
|
||||
CHECK(relativeErrors[1] < relativeErrors[0]);
|
||||
|
||||
CHECK(observedRate > minimumObservedRate);
|
||||
|
||||
CHECK(relativeErrors[1] < finestRelativeTolerance);
|
||||
}
|
||||
609
tests/operators/prepared_rotation_displacement_force.cpp
Normal file
609
tests/operators/prepared_rotation_displacement_force.cpp
Normal file
@@ -0,0 +1,609 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace rotational_displacement_force_test_utils {
|
||||
using CoupledForm = mean_field::utils::blocks::barotropic_equilibrium_form;
|
||||
|
||||
constexpr auto densityValue =
|
||||
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term);
|
||||
|
||||
constexpr auto displacementValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||
mean_field::utils::blocks::displacement_field.geometry_term
|
||||
);
|
||||
|
||||
constexpr auto gravityGradientValue =
|
||||
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::gravity_field.gradient_term);
|
||||
|
||||
constexpr auto gravityPotentialValue =
|
||||
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::gravity_field.poisson_term);
|
||||
|
||||
constexpr auto enthalpyValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||
mean_field::utils::blocks::enthalpy_field.specific_term
|
||||
);
|
||||
|
||||
constexpr auto barotropicConstantValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
|
||||
);
|
||||
|
||||
constexpr auto gravityGradientResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
|
||||
constexpr auto gravityPotentialResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
|
||||
constexpr auto densityResidual =
|
||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term);
|
||||
|
||||
constexpr auto displacementResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::displacement_field.geometry_term
|
||||
);
|
||||
|
||||
constexpr auto enthalpyResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::enthalpy_field.specific_term
|
||||
);
|
||||
|
||||
constexpr auto massResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
|
||||
);
|
||||
|
||||
[[nodiscard]] mean_field::operators::RotationalDisplacementForceLayout make_layout(const mean_field::fem::FEM &f) {
|
||||
const std::array<int, CoupledForm::value_block_count> valueSizes{
|
||||
f.densityFes->GetTrueVSize(), f.displacementFes->GetTrueVSize(), f.gravityFluxFes->GetTrueVSize(),
|
||||
f.gravityPotentialFes->GetTrueVSize(), f.enthalpyFes->GetTrueVSize(), 1
|
||||
};
|
||||
|
||||
const std::array<int, CoupledForm::residual_block_count> residualSizes{
|
||||
f.gravityFluxFes->GetTrueVSize(), f.gravityPotentialFes->GetTrueVSize(), f.densityFes->GetTrueVSize(),
|
||||
f.displacementFes->GetTrueVSize(), f.enthalpyFes->GetTrueVSize(), 1
|
||||
};
|
||||
|
||||
return {valueSizes, residualSizes};
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_density(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase
|
||||
) {
|
||||
mfem::ParGridFunction densityField(f.densityFes.get());
|
||||
|
||||
mfem::FunctionCoefficient densityCoefficient([phase](const mfem::Vector &position) {
|
||||
return 0.88 + 0.06 * std::sin(0.7 * position(0) + phase) + 0.04 * std::cos(0.6 * position(1) - phase) +
|
||||
0.025 * position(2) * position(2);
|
||||
});
|
||||
|
||||
densityField.ProjectCoefficient(densityCoefficient);
|
||||
|
||||
mfem::Vector densityTrue;
|
||||
densityField.GetTrueDofs(densityTrue);
|
||||
return densityTrue;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_density_direction(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double phase
|
||||
) {
|
||||
mfem::ParGridFunction densityField(f.densityFes.get());
|
||||
|
||||
mfem::FunctionCoefficient densityCoefficient([phase](const mfem::Vector &position) {
|
||||
return 0.17 * std::sin(0.9 * position(0) + phase) - 0.12 * std::cos(0.8 * position(1) - phase) +
|
||||
0.07 * position(2);
|
||||
});
|
||||
|
||||
densityField.ProjectCoefficient(densityCoefficient);
|
||||
|
||||
mfem::Vector densityTrue;
|
||||
densityField.GetTrueDofs(densityTrue);
|
||||
return densityTrue;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_displacement_direction(const mean_field::fem::FEM &f) {
|
||||
mfem::Vector direction = gravity_prepared_test_utils::make_displacement(f, 0.91);
|
||||
|
||||
const mfem::Vector second = gravity_prepared_test_utils::make_displacement(f, 0.27);
|
||||
|
||||
direction -= second;
|
||||
return direction;
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::physics::RigidRotation make_rotation(const double scale = 1.0) {
|
||||
mfem::Vector angularVelocity(3);
|
||||
angularVelocity(0) = scale * 0.17;
|
||||
angularVelocity(1) = scale * -0.09;
|
||||
angularVelocity(2) = scale * 0.62;
|
||||
|
||||
mfem::Vector center(3);
|
||||
center(0) = 0.04;
|
||||
center(1) = -0.03;
|
||||
center(2) = 0.02;
|
||||
|
||||
return mean_field::physics::RigidRotation(angularVelocity, center);
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::context::rotational_displacement_force::RotationalDisplacementForceDependencies
|
||||
make_dependencies() {
|
||||
return {
|
||||
.discretization = {.identity = 211, .revision = 3},
|
||||
.density = {.identity = 223, .revision = 5},
|
||||
.displacement = {.identity = 227, .revision = 7},
|
||||
.rotation = {.identity = 229, .revision = 11}
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_vacuum_only_density(const mean_field::fem::FEM &f) {
|
||||
mfem::ParGridFunction densityField(f.densityFes.get());
|
||||
densityField = 0.0;
|
||||
|
||||
const int vacuumAttribute = f.domainMapperStateless->GetVacuumElementAttribute();
|
||||
|
||||
mfem::Array<int> densityDofs;
|
||||
int localVacuumElements = 0;
|
||||
|
||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
|
||||
|
||||
REQUIRE(transformation != nullptr);
|
||||
|
||||
if (transformation->Attribute != vacuumAttribute) {
|
||||
continue;
|
||||
}
|
||||
|
||||
f.densityFes->GetElementDofs(elementId, densityDofs);
|
||||
|
||||
mfem::Vector elementDensity(densityDofs.Size());
|
||||
elementDensity = 1.0;
|
||||
densityField.SetSubVector(densityDofs, elementDensity);
|
||||
++localVacuumElements;
|
||||
}
|
||||
|
||||
int globalVacuumElements = 0;
|
||||
|
||||
MPI_Allreduce(&localVacuumElements, &globalVacuumElements, 1, MPI_INT, MPI_SUM, f.mesh->GetComm());
|
||||
|
||||
REQUIRE(globalVacuumElements > 0);
|
||||
|
||||
mfem::Vector densityTrue;
|
||||
densityField.GetTrueDofs(densityTrue);
|
||||
return densityTrue;
|
||||
}
|
||||
|
||||
[[nodiscard]] double global_norm(
|
||||
const mfem::Vector &vector,
|
||||
MPI_Comm communicator
|
||||
) {
|
||||
const double localSquaredNorm = vector * vector;
|
||||
double globalSquaredNorm = 0.0;
|
||||
|
||||
MPI_Allreduce(&localSquaredNorm, &globalSquaredNorm, 1, MPI_DOUBLE, MPI_SUM, communicator);
|
||||
|
||||
return std::sqrt(globalSquaredNorm);
|
||||
}
|
||||
|
||||
[[nodiscard]] double global_dot(
|
||||
const mfem::Vector &left,
|
||||
const mfem::Vector &right,
|
||||
MPI_Comm communicator
|
||||
) {
|
||||
REQUIRE(left.Size() == right.Size());
|
||||
|
||||
const double localDot = left * right;
|
||||
double globalDot = 0.0;
|
||||
|
||||
MPI_Allreduce(&localDot, &globalDot, 1, MPI_DOUBLE, MPI_SUM, communicator);
|
||||
|
||||
return globalDot;
|
||||
}
|
||||
|
||||
[[nodiscard]] double relative_difference(
|
||||
const mfem::Vector &computed,
|
||||
const mfem::Vector &reference,
|
||||
MPI_Comm communicator
|
||||
) {
|
||||
REQUIRE(computed.Size() == reference.Size());
|
||||
|
||||
mfem::Vector difference(computed);
|
||||
difference -= reference;
|
||||
|
||||
return global_norm(difference, communicator) /
|
||||
std::max(global_norm(reference, communicator), std::numeric_limits<double>::epsilon());
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector centered_difference(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::physics::RigidRotation &rotation,
|
||||
const mfem::Vector &baseDensity,
|
||||
const mfem::Vector &densityDirection,
|
||||
const mfem::Vector &baseDisplacement,
|
||||
const mfem::Vector &displacementDirection,
|
||||
const double step
|
||||
) {
|
||||
mfem::Vector plusDensity(baseDensity);
|
||||
plusDensity.Add(step, densityDirection);
|
||||
|
||||
mfem::Vector minusDensity(baseDensity);
|
||||
minusDensity.Add(-step, densityDirection);
|
||||
|
||||
mfem::Vector plusDisplacement(baseDisplacement);
|
||||
plusDisplacement.Add(step, displacementDirection);
|
||||
|
||||
mfem::Vector minusDisplacement(baseDisplacement);
|
||||
minusDisplacement.Add(-step, displacementDirection);
|
||||
|
||||
mfem::Vector plusResidual;
|
||||
mfem::Vector minusResidual;
|
||||
|
||||
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, rotation, plusDensity, plusDisplacement, plusResidual
|
||||
);
|
||||
|
||||
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, rotation, minusDensity, minusDisplacement, minusResidual
|
||||
);
|
||||
|
||||
plusResidual -= minusResidual;
|
||||
plusResidual /= 2.0 * step;
|
||||
return plusResidual;
|
||||
}
|
||||
|
||||
template <int index>
|
||||
[[nodiscard]] mfem::Vector copy_residual_block(
|
||||
const mfem::Vector &action,
|
||||
const mean_field::operators::RotationalDisplacementForceLayout &layout,
|
||||
const mean_field::utils::blocks::residual_block<index> block
|
||||
) {
|
||||
mfem::Vector result(layout.size(block));
|
||||
const int offset = layout.offset(block);
|
||||
|
||||
for (int entry = 0; entry < result.Size(); ++entry) {
|
||||
result(entry) = action(offset + entry);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace rotational_displacement_force_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
"Rotational Displacement Force Query Includes Density Test And Linear "
|
||||
"Position",
|
||||
tags::centrifugal &tags::quadrature &tags::unit
|
||||
) {
|
||||
using DisplacementField = mean_field::field::Field<mean_field::field::Displacement>;
|
||||
|
||||
constexpr int geometryWeightOrder = 4;
|
||||
|
||||
constexpr mean_field::quadrature::Query query =
|
||||
DisplacementField::make_query<mean_field::field::Displacement::Form::CentrifugalForce>(
|
||||
mean_field::quadrature::QuadratureRole::discretization, geometryWeightOrder, std::array<int, 1>{1},
|
||||
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
/* density: 2, displacement test: 3, position: 1, geometry: 4 */
|
||||
constexpr int expectedBaseOrder = 2 + 3 + 1 + 4;
|
||||
|
||||
STATIC_REQUIRE(query.term == mean_field::quadrature::Term::centrifugal);
|
||||
STATIC_REQUIRE(query.domain == mean_field::utils::DOMAINS::STELLAR);
|
||||
STATIC_REQUIRE(query.mapping == mean_field::quadrature::MappingKind::general);
|
||||
STATIC_REQUIRE(query.base_order.has_value());
|
||||
STATIC_REQUIRE(*query.base_order == expectedBaseOrder);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Rotational Displacement Force Uses Negative Rotation-Potential "
|
||||
"Gradient And Excludes Vacuum",
|
||||
tags::centrifugal &tags::kernels &tags::integration &tags::accuracy
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const mfem::Vector density = rotational_displacement_force_test_utils::make_density(f, 0.31);
|
||||
|
||||
mfem::Vector displacement(f.displacementFes->GetTrueVSize());
|
||||
displacement = 0.0;
|
||||
|
||||
const mean_field::physics::RigidRotation rotation = rotational_displacement_force_test_utils::make_rotation();
|
||||
|
||||
mfem::Vector residual;
|
||||
|
||||
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, rotation, density, displacement, residual
|
||||
);
|
||||
|
||||
mfem::ParGridFunction gradientTestField(f.displacementFes.get());
|
||||
|
||||
auto gradientFunction = [&rotation](const mfem::Vector &position, mfem::Vector &value) {
|
||||
rotation.potential_gradient(position, value);
|
||||
};
|
||||
|
||||
mfem::VectorFunctionCoefficient gradientCoefficient(3, gradientFunction);
|
||||
|
||||
gradientTestField.ProjectCoefficient(gradientCoefficient);
|
||||
|
||||
mfem::Vector gradientTestDirection;
|
||||
gradientTestField.GetTrueDofs(gradientTestDirection);
|
||||
|
||||
const double signedWork =
|
||||
rotational_displacement_force_test_utils::global_dot(residual, gradientTestDirection, f.mesh->GetComm());
|
||||
|
||||
INFO("Rotation-force work against grad(Psi) = " << signedWork);
|
||||
CHECK(signedWork < 0.0);
|
||||
|
||||
const mfem::Vector vacuumDensity = rotational_displacement_force_test_utils::make_vacuum_only_density(f);
|
||||
|
||||
mfem::Vector vacuumResidual;
|
||||
|
||||
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, rotation, vacuumDensity, displacement, vacuumResidual
|
||||
);
|
||||
|
||||
CHECK(rotational_displacement_force_test_utils::global_norm(vacuumResidual, f.mesh->GetComm()) == 0.0);
|
||||
|
||||
mfem::Vector zeroAngularVelocity(3);
|
||||
mfem::Vector zeroCenter(3);
|
||||
zeroAngularVelocity = 0.0;
|
||||
zeroCenter = 0.0;
|
||||
|
||||
const mean_field::physics::RigidRotation zeroRotation(zeroAngularVelocity, zeroCenter);
|
||||
|
||||
mfem::Vector zeroRotationResidual;
|
||||
|
||||
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, zeroRotation, density, displacement, zeroRotationResidual
|
||||
);
|
||||
|
||||
CHECK(rotational_displacement_force_test_utils::global_norm(zeroRotationResidual, f.mesh->GetComm()) == 0.0);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Rotational Displacement Force Reprepares Selectively",
|
||||
tags::centrifugal &tags::prepared &tags::integration
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
mfem::Vector density = rotational_displacement_force_test_utils::make_density(f, 0.37);
|
||||
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.53);
|
||||
|
||||
mean_field::physics::RigidRotation rotation = rotational_displacement_force_test_utils::make_rotation(0.81);
|
||||
|
||||
auto dependencies = rotational_displacement_force_test_utils::make_dependencies();
|
||||
|
||||
mean_field::operators::PreparedRotationalDisplacementForceOperator preparedOperator(f, *f.domainMapperStateless);
|
||||
|
||||
const auto initialReport =
|
||||
preparedOperator.Prepare({.density = density, .displacement = displacement}, dependencies, rotation);
|
||||
|
||||
REQUIRE(initialReport.DidAnyWork());
|
||||
REQUIRE(initialReport.updatedRotation);
|
||||
REQUIRE(initialReport.preparedResidual);
|
||||
REQUIRE(preparedOperator.IsPrepared());
|
||||
|
||||
mfem::Vector preparedResidual;
|
||||
mfem::Vector kernelResidual;
|
||||
|
||||
preparedOperator.BuildResidual(preparedResidual);
|
||||
|
||||
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, rotation, density, displacement, kernelResidual
|
||||
);
|
||||
|
||||
CHECK(
|
||||
rotational_displacement_force_test_utils::relative_difference(
|
||||
preparedResidual, kernelResidual, f.mesh->GetComm()
|
||||
) < 2.0e-12
|
||||
);
|
||||
|
||||
CHECK_FALSE(preparedOperator.Prepare({.density = density, .displacement = displacement}, dependencies, rotation)
|
||||
.DidAnyWork());
|
||||
|
||||
density = rotational_displacement_force_test_utils::make_density(f, 0.79);
|
||||
|
||||
++dependencies.density.revision;
|
||||
|
||||
const auto densityReport =
|
||||
preparedOperator.Prepare({.density = density, .displacement = displacement}, dependencies, rotation);
|
||||
|
||||
CHECK(densityReport.preparedResidual);
|
||||
CHECK_FALSE(densityReport.updatedRotation);
|
||||
|
||||
rotation = rotational_displacement_force_test_utils::make_rotation(1.23);
|
||||
|
||||
++dependencies.rotation.revision;
|
||||
|
||||
const auto rotationReport =
|
||||
preparedOperator.Prepare({.density = density, .displacement = displacement}, dependencies, rotation);
|
||||
|
||||
CHECK(rotationReport.updatedRotation);
|
||||
CHECK(rotationReport.preparedResidual);
|
||||
CHECK(preparedOperator.GetResidualPreparationCount() == 3);
|
||||
CHECK(preparedOperator.GetResidualApplicationCount() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Rotational Displacement Force Jacobian Matches Both Columns And "
|
||||
"Centered Differences",
|
||||
tags::centrifugal &tags::prepared &tags::jacobian &tags::accuracy
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const mfem::Vector density = rotational_displacement_force_test_utils::make_density(f, 0.43);
|
||||
|
||||
const mfem::Vector densityDirection = rotational_displacement_force_test_utils::make_density_direction(f, 0.59);
|
||||
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.61);
|
||||
|
||||
const mfem::Vector displacementDirection = rotational_displacement_force_test_utils::make_displacement_direction(f);
|
||||
|
||||
const mean_field::physics::RigidRotation rotation = rotational_displacement_force_test_utils::make_rotation(0.93);
|
||||
|
||||
mean_field::operators::PreparedRotationalDisplacementForceOperator preparedOperator(f, *f.domainMapperStateless);
|
||||
|
||||
preparedOperator.Prepare(
|
||||
{.density = density, .displacement = displacement},
|
||||
rotational_displacement_force_test_utils::make_dependencies(), rotation
|
||||
);
|
||||
|
||||
mfem::Vector densityAction;
|
||||
mfem::Vector displacementAction;
|
||||
mfem::Vector completeAction;
|
||||
|
||||
preparedOperator.ApplyDensityJacobianAction(densityDirection, densityAction);
|
||||
|
||||
preparedOperator.ApplyDisplacementJacobianAction(displacementDirection, displacementAction);
|
||||
|
||||
preparedOperator.ApplyCompleteJacobianAction(densityDirection, displacementDirection, completeAction);
|
||||
|
||||
mfem::Vector summedColumns(densityAction);
|
||||
summedColumns += displacementAction;
|
||||
|
||||
CHECK(
|
||||
rotational_displacement_force_test_utils::relative_difference(
|
||||
completeAction, summedColumns, f.mesh->GetComm()
|
||||
) < 2.0e-12
|
||||
);
|
||||
|
||||
mfem::Vector zeroDensity(densityDirection.Size());
|
||||
mfem::Vector zeroDisplacement(displacementDirection.Size());
|
||||
zeroDensity = 0.0;
|
||||
zeroDisplacement = 0.0;
|
||||
|
||||
constexpr double step = 1.0e-5;
|
||||
|
||||
const mfem::Vector densityDifference = rotational_displacement_force_test_utils::centered_difference(
|
||||
f, rotation, density, densityDirection, displacement, zeroDisplacement, step
|
||||
);
|
||||
|
||||
const mfem::Vector displacementDifference = rotational_displacement_force_test_utils::centered_difference(
|
||||
f, rotation, density, zeroDensity, displacement, displacementDirection, step
|
||||
);
|
||||
|
||||
const mfem::Vector completeDifference = rotational_displacement_force_test_utils::centered_difference(
|
||||
f, rotation, density, densityDirection, displacement, displacementDirection, step
|
||||
);
|
||||
|
||||
const double densityError = rotational_displacement_force_test_utils::relative_difference(
|
||||
densityAction, densityDifference, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
const double displacementError = rotational_displacement_force_test_utils::relative_difference(
|
||||
displacementAction, displacementDifference, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
const double completeError = rotational_displacement_force_test_utils::relative_difference(
|
||||
completeAction, completeDifference, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
INFO("Density-column centered-difference error = " << densityError);
|
||||
INFO("Displacement-column centered-difference error = " << displacementError);
|
||||
INFO("Complete centered-difference error = " << completeError);
|
||||
|
||||
CHECK(densityError < 2.0e-9);
|
||||
CHECK(displacementError < 3.0e-8);
|
||||
CHECK(completeError < 4.0e-8);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Rotational Displacement Force MFEM Adapter Routes Only R-d",
|
||||
tags::centrifugal &tags::prepared &tags::mfem_operators &tags::unit
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const mfem::Vector density = rotational_displacement_force_test_utils::make_density(f, 0.47);
|
||||
|
||||
const mfem::Vector densityDirection = rotational_displacement_force_test_utils::make_density_direction(f, 0.63);
|
||||
|
||||
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.57);
|
||||
|
||||
const mfem::Vector displacementDirection = rotational_displacement_force_test_utils::make_displacement_direction(f);
|
||||
|
||||
const mean_field::physics::RigidRotation rotation = rotational_displacement_force_test_utils::make_rotation(0.87);
|
||||
|
||||
mean_field::operators::PreparedRotationalDisplacementForceOperator preparedOperator(f, *f.domainMapperStateless);
|
||||
|
||||
preparedOperator.Prepare(
|
||||
{.density = density, .displacement = displacement},
|
||||
rotational_displacement_force_test_utils::make_dependencies(), rotation
|
||||
);
|
||||
|
||||
const auto layout = rotational_displacement_force_test_utils::make_layout(f);
|
||||
|
||||
mean_field::operators::PreparedRotationalDisplacementForceJacobianOperator adapter(layout, preparedOperator);
|
||||
|
||||
mfem::BlockVector direction(layout.value_offsets());
|
||||
direction = 0.0;
|
||||
|
||||
direction.GetBlock(rotational_displacement_force_test_utils::densityValue) = densityDirection;
|
||||
|
||||
direction.GetBlock(rotational_displacement_force_test_utils::displacementValue) = displacementDirection;
|
||||
|
||||
direction.GetBlock(rotational_displacement_force_test_utils::gravityGradientValue) = 0.23;
|
||||
|
||||
direction.GetBlock(rotational_displacement_force_test_utils::gravityPotentialValue) = -0.31;
|
||||
|
||||
direction.GetBlock(rotational_displacement_force_test_utils::enthalpyValue) = 0.37;
|
||||
|
||||
direction.GetBlock(rotational_displacement_force_test_utils::barotropicConstantValue) = -0.41;
|
||||
|
||||
mfem::Vector action;
|
||||
adapter.Mult(direction, action);
|
||||
|
||||
mfem::Vector expectedDisplacementAction;
|
||||
|
||||
preparedOperator.ApplyCompleteJacobianAction(densityDirection, displacementDirection, expectedDisplacementAction);
|
||||
|
||||
const mfem::Vector actualDisplacementAction = rotational_displacement_force_test_utils::copy_residual_block(
|
||||
action, layout, rotational_displacement_force_test_utils::displacementResidual
|
||||
);
|
||||
|
||||
CHECK(
|
||||
rotational_displacement_force_test_utils::relative_difference(
|
||||
actualDisplacementAction, expectedDisplacementAction, f.mesh->GetComm()
|
||||
) < 2.0e-12
|
||||
);
|
||||
|
||||
const std::array<mfem::Vector, 5> zeroRows{
|
||||
rotational_displacement_force_test_utils::copy_residual_block(
|
||||
action, layout, rotational_displacement_force_test_utils::gravityGradientResidual
|
||||
),
|
||||
rotational_displacement_force_test_utils::copy_residual_block(
|
||||
action, layout, rotational_displacement_force_test_utils::gravityPotentialResidual
|
||||
),
|
||||
rotational_displacement_force_test_utils::copy_residual_block(
|
||||
action, layout, rotational_displacement_force_test_utils::densityResidual
|
||||
),
|
||||
rotational_displacement_force_test_utils::copy_residual_block(
|
||||
action, layout, rotational_displacement_force_test_utils::enthalpyResidual
|
||||
),
|
||||
rotational_displacement_force_test_utils::copy_residual_block(
|
||||
action, layout, rotational_displacement_force_test_utils::massResidual
|
||||
)
|
||||
};
|
||||
|
||||
for (const mfem::Vector &row : zeroRows) {
|
||||
CHECK(rotational_displacement_force_test_utils::global_norm(row, f.mesh->GetComm()) == 0.0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <numbers>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace rotational_displacement_force_affine_deformation_test_utils {
|
||||
[[nodiscard]] double global_dot(
|
||||
const mfem::Vector &left,
|
||||
const mfem::Vector &right,
|
||||
MPI_Comm communicator
|
||||
) {
|
||||
REQUIRE(left.Size() == right.Size());
|
||||
|
||||
const double localDot = left * right;
|
||||
double globalDot = 0.0;
|
||||
|
||||
MPI_Allreduce(&localDot, &globalDot, 1, MPI_DOUBLE, MPI_SUM, communicator);
|
||||
|
||||
return globalDot;
|
||||
}
|
||||
|
||||
[[nodiscard]] double relative_error(
|
||||
const double computed,
|
||||
const double expected
|
||||
) {
|
||||
return std::abs(computed - expected) / std::max(std::abs(expected), std::numeric_limits<double>::epsilon());
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector project_constant_density(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double densityValue
|
||||
) {
|
||||
mfem::ParGridFunction density(f.densityFes.get());
|
||||
mfem::ConstantCoefficient coefficient(densityValue);
|
||||
density.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector densityTrue;
|
||||
density.GetTrueDofs(densityTrue);
|
||||
return densityTrue;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector project_affine_vector(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mfem::DenseMatrix &linearMap,
|
||||
const mfem::Vector &offset
|
||||
) {
|
||||
REQUIRE(linearMap.Height() == 3);
|
||||
REQUIRE(linearMap.Width() == 3);
|
||||
REQUIRE(offset.Size() == 3);
|
||||
|
||||
auto affineFunction = [&linearMap, &offset](const mfem::Vector &referencePosition, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
linearMap.Mult(referencePosition, value);
|
||||
value += offset;
|
||||
};
|
||||
|
||||
mfem::ParGridFunction field(f.displacementFes.get());
|
||||
mfem::VectorFunctionCoefficient coefficient(3, affineFunction);
|
||||
field.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector trueDofs;
|
||||
field.GetTrueDofs(trueDofs);
|
||||
return trueDofs;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::DenseMatrix make_deformation() {
|
||||
mfem::DenseMatrix deformation(3);
|
||||
deformation = 0.0;
|
||||
|
||||
deformation(0, 0) = 1.08;
|
||||
deformation(0, 1) = 0.06;
|
||||
deformation(0, 2) = -0.03;
|
||||
deformation(1, 1) = 0.96;
|
||||
deformation(1, 2) = 0.04;
|
||||
deformation(2, 2) = 1.0 / (1.08 * 0.96);
|
||||
|
||||
return deformation;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::DenseMatrix make_displacement_gradient(const mfem::DenseMatrix &deformation) {
|
||||
mfem::DenseMatrix displacementGradient(deformation);
|
||||
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
displacementGradient(component, component) -= 1.0;
|
||||
}
|
||||
|
||||
return displacementGradient;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::DenseMatrix make_rotation_potential_hessian(const mfem::Vector &angularVelocity) {
|
||||
REQUIRE(angularVelocity.Size() == 3);
|
||||
|
||||
const double angularSpeedSquared = angularVelocity * angularVelocity;
|
||||
mfem::DenseMatrix hessian(3);
|
||||
|
||||
for (int row = 0; row < 3; ++row) {
|
||||
for (int column = 0; column < 3; ++column) {
|
||||
hessian(row, column) =
|
||||
(row == column ? angularSpeedSquared : 0.0) - angularVelocity(row) * angularVelocity(column);
|
||||
}
|
||||
}
|
||||
|
||||
return hessian;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::DenseMatrix multiply(
|
||||
const mfem::DenseMatrix &left,
|
||||
const mfem::DenseMatrix &right
|
||||
) {
|
||||
REQUIRE(left.Width() == right.Height());
|
||||
|
||||
mfem::DenseMatrix product(left.Height(), right.Width());
|
||||
product = 0.0;
|
||||
|
||||
for (int row = 0; row < product.Height(); ++row) {
|
||||
for (int column = 0; column < product.Width(); ++column) {
|
||||
for (int inner = 0; inner < left.Width(); ++inner) {
|
||||
product(row, column) += left(row, inner) * right(inner, column);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return product;
|
||||
}
|
||||
|
||||
[[nodiscard]] double frobenius_product(
|
||||
const mfem::DenseMatrix &left,
|
||||
const mfem::DenseMatrix &right
|
||||
) {
|
||||
REQUIRE(left.Height() == right.Height());
|
||||
REQUIRE(left.Width() == right.Width());
|
||||
|
||||
double product = 0.0;
|
||||
|
||||
for (int row = 0; row < left.Height(); ++row) {
|
||||
for (int column = 0; column < left.Width(); ++column) {
|
||||
product += left(row, column) * right(row, column);
|
||||
}
|
||||
}
|
||||
|
||||
return product;
|
||||
}
|
||||
} // namespace rotational_displacement_force_affine_deformation_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
"Rotational Displacement Force Matches A Nontrivially Deformed "
|
||||
"Homogeneous Ellipsoid",
|
||||
tags::centrifugal &tags::analytic_comparison &tags::accuracy &tags::geometry
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const double radius = mean_field::utils::RADIUS;
|
||||
const double mass = mean_field::utils::MASS;
|
||||
const double referenceVolume = 4.0 * std::numbers::pi * radius * radius * radius / 3.0;
|
||||
|
||||
const mfem::DenseMatrix deformation =
|
||||
rotational_displacement_force_affine_deformation_test_utils::make_deformation();
|
||||
|
||||
const double deformationDeterminant = deformation.Det();
|
||||
|
||||
REQUIRE(deformationDeterminant > 0.0);
|
||||
REQUIRE(std::abs(deformationDeterminant - 1.0) < 2.0e-14);
|
||||
|
||||
mfem::Vector deformationOffset(3);
|
||||
deformationOffset(0) = 0.031;
|
||||
deformationOffset(1) = -0.024;
|
||||
deformationOffset(2) = 0.018;
|
||||
|
||||
const mfem::DenseMatrix displacementGradient =
|
||||
rotational_displacement_force_affine_deformation_test_utils::make_displacement_gradient(deformation);
|
||||
|
||||
const mfem::Vector displacement =
|
||||
rotational_displacement_force_affine_deformation_test_utils::project_affine_vector(
|
||||
f, displacementGradient, deformationOffset
|
||||
);
|
||||
|
||||
const double densityValue = mass / (deformationDeterminant * referenceVolume);
|
||||
|
||||
const mfem::Vector density =
|
||||
rotational_displacement_force_affine_deformation_test_utils::project_constant_density(f, densityValue);
|
||||
|
||||
mfem::Vector angularVelocity(3);
|
||||
angularVelocity(0) = 0.17;
|
||||
angularVelocity(1) = -0.12;
|
||||
angularVelocity(2) = 0.43;
|
||||
|
||||
/*
|
||||
* Put the rotation center at the mapped ellipsoid's center. The affine
|
||||
* translation is therefore present in the geometry, but cancels from
|
||||
* x - x_c in the exact centrifugal acceleration.
|
||||
*/
|
||||
const mfem::Vector rotationCenter(deformationOffset);
|
||||
|
||||
const mean_field::physics::RigidRotation rotation(angularVelocity, rotationCenter);
|
||||
|
||||
mfem::Vector residual;
|
||||
|
||||
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, rotation, density, displacement, residual
|
||||
);
|
||||
|
||||
const mfem::DenseMatrix rotationPotentialHessian =
|
||||
rotational_displacement_force_affine_deformation_test_utils::make_rotation_potential_hessian(angularVelocity);
|
||||
|
||||
const mfem::DenseMatrix forceMomentTensor =
|
||||
rotational_displacement_force_affine_deformation_test_utils::multiply(rotationPotentialHessian, deformation);
|
||||
|
||||
const double angularSpeedSquared = angularVelocity * angularVelocity;
|
||||
const double momentScale = mass * radius * radius * angularSpeedSquared / 5.0;
|
||||
|
||||
REQUIRE(momentScale > 0.0);
|
||||
|
||||
mfem::Vector zeroOffset(3);
|
||||
zeroOffset = 0.0;
|
||||
|
||||
/*
|
||||
* For X in a homogeneous reference sphere and the affine map x = A X+b,
|
||||
*
|
||||
* integral X_i X_j dM = (M R^2 / 5) delta_ij.
|
||||
*
|
||||
* With S = |Omega|^2 I - Omega Omega^T and the affine probe
|
||||
* w = E_(row,column) X, the exact residual action is
|
||||
*
|
||||
* R_rot(w) = -(M R^2 / 5) (S A)_(row,column).
|
||||
*
|
||||
* Checking all nine probes compares the complete analytic second-moment
|
||||
* response tensor, including the shear and oblique-axis couplings.
|
||||
*/
|
||||
for (int row = 0; row < 3; ++row) {
|
||||
for (int column = 0; column < 3; ++column) {
|
||||
mfem::DenseMatrix probe(3);
|
||||
probe = 0.0;
|
||||
probe(row, column) = 1.0;
|
||||
|
||||
const mfem::Vector probeDirection =
|
||||
rotational_displacement_force_affine_deformation_test_utils::project_affine_vector(
|
||||
f, probe, zeroOffset
|
||||
);
|
||||
|
||||
const double computedAction = rotational_displacement_force_affine_deformation_test_utils::global_dot(
|
||||
residual, probeDirection, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
const double expectedAction = -mass * radius * radius / 5.0 * forceMomentTensor(row, column);
|
||||
|
||||
const double normalizedAbsoluteError = std::abs(computedAction - expectedAction) / momentScale;
|
||||
|
||||
INFO("Affine probe row = " << row);
|
||||
INFO("Affine probe column = " << column);
|
||||
INFO("Computed affine-probe action = " << computedAction);
|
||||
INFO("Analytic affine-probe action = " << expectedAction);
|
||||
INFO("Normalized affine-probe absolute error = " << normalizedAbsoluteError);
|
||||
|
||||
CHECK(normalizedAbsoluteError < 2.5e-5);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The physical dilation about the rotation center is w = x-x_c = A X.
|
||||
* Its exact work is the deformed rotational virial,
|
||||
*
|
||||
* R_rot(x-x_c) = -(M R^2 / 5) tr(A^T S A) = -2T.
|
||||
*/
|
||||
const mfem::Vector dilationDirection =
|
||||
rotational_displacement_force_affine_deformation_test_utils::project_affine_vector(f, deformation, zeroOffset);
|
||||
|
||||
const double computedVirial = rotational_displacement_force_affine_deformation_test_utils::global_dot(
|
||||
residual, dilationDirection, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
const double expectedVirial =
|
||||
-mass * radius * radius / 5.0 *
|
||||
rotational_displacement_force_affine_deformation_test_utils::frobenius_product(deformation, forceMomentTensor);
|
||||
|
||||
const double virialRelativeError =
|
||||
rotational_displacement_force_affine_deformation_test_utils::relative_error(computedVirial, expectedVirial);
|
||||
|
||||
const double sphericalVirial = -(2.0 / 5.0) * mass * angularSpeedSquared * radius * radius;
|
||||
|
||||
const double deformationSignal = std::abs(expectedVirial - sphericalVirial) / std::abs(sphericalVirial);
|
||||
|
||||
INFO("Deformation determinant = " << deformationDeterminant);
|
||||
INFO("Computed deformed rotational virial = " << computedVirial);
|
||||
INFO("Analytic deformed rotational virial = " << expectedVirial);
|
||||
INFO("Spherical rotational virial = " << sphericalVirial);
|
||||
INFO("Relative deformation signal = " << deformationSignal);
|
||||
INFO("Deformed virial relative error = " << virialRelativeError);
|
||||
|
||||
CHECK(computedVirial < 0.0);
|
||||
CHECK(deformationSignal > 5.0e-2);
|
||||
CHECK(virialRelativeError < 2.5e-5);
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <numbers>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace rotational_displacement_force_analytic_test_utils {
|
||||
[[nodiscard]] double global_dot(
|
||||
const mfem::Vector &left,
|
||||
const mfem::Vector &right,
|
||||
MPI_Comm communicator
|
||||
) {
|
||||
REQUIRE(left.Size() == right.Size());
|
||||
|
||||
const double localDot = left * right;
|
||||
double globalDot = 0.0;
|
||||
|
||||
MPI_Allreduce(&localDot, &globalDot, 1, MPI_DOUBLE, MPI_SUM, communicator);
|
||||
|
||||
return globalDot;
|
||||
}
|
||||
|
||||
[[nodiscard]] double relative_error(
|
||||
const double computed,
|
||||
const double expected
|
||||
) {
|
||||
return std::abs(computed - expected) / std::max(std::abs(expected), std::numeric_limits<double>::epsilon());
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector project_constant_density(
|
||||
const mean_field::fem::FEM &f,
|
||||
const double densityValue
|
||||
) {
|
||||
mfem::ParGridFunction density(f.densityFes.get());
|
||||
mfem::ConstantCoefficient densityCoefficient(densityValue);
|
||||
density.ProjectCoefficient(densityCoefficient);
|
||||
|
||||
mfem::Vector densityTrue;
|
||||
density.GetTrueDofs(densityTrue);
|
||||
return densityTrue;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector project_vector_function(
|
||||
const mean_field::fem::FEM &f,
|
||||
const std::function<void(
|
||||
const mfem::Vector &,
|
||||
mfem::Vector &
|
||||
)> &function
|
||||
) {
|
||||
mfem::ParGridFunction field(f.displacementFes.get());
|
||||
mfem::VectorFunctionCoefficient coefficient(3, function);
|
||||
field.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector trueDofs;
|
||||
field.GetTrueDofs(trueDofs);
|
||||
return trueDofs;
|
||||
}
|
||||
} // namespace rotational_displacement_force_analytic_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
"Rigid Rotation Gradient And Hessian Action Match The Analytic "
|
||||
"Potential",
|
||||
tags::centrifugal &tags::unit &tags::accuracy
|
||||
) {
|
||||
mfem::Vector angularVelocity(3);
|
||||
angularVelocity(0) = 0.23;
|
||||
angularVelocity(1) = -0.31;
|
||||
angularVelocity(2) = 0.67;
|
||||
|
||||
mfem::Vector center(3);
|
||||
center(0) = 0.11;
|
||||
center(1) = -0.07;
|
||||
center(2) = 0.05;
|
||||
|
||||
const mean_field::physics::RigidRotation rotation(angularVelocity, center);
|
||||
|
||||
mfem::Vector position(3);
|
||||
position(0) = 0.41;
|
||||
position(1) = -0.29;
|
||||
position(2) = 0.37;
|
||||
|
||||
mfem::Vector direction(3);
|
||||
direction(0) = -0.17;
|
||||
direction(1) = 0.23;
|
||||
direction(2) = 0.13;
|
||||
|
||||
mfem::Vector gradient;
|
||||
mfem::Vector hessianAction;
|
||||
|
||||
rotation.potential_gradient(position, gradient);
|
||||
|
||||
rotation.potential_gradient_directional_derivative(direction, hessianAction);
|
||||
|
||||
const double directionalDerivative = rotation.potential_directional_derivative(position, direction);
|
||||
|
||||
CHECK(
|
||||
rotational_displacement_force_analytic_test_utils::relative_error(gradient * direction, directionalDerivative) <
|
||||
2.0e-15
|
||||
);
|
||||
|
||||
constexpr double step = 1.0e-6;
|
||||
|
||||
mfem::Vector plusPosition(position);
|
||||
plusPosition.Add(step, direction);
|
||||
|
||||
mfem::Vector minusPosition(position);
|
||||
minusPosition.Add(-step, direction);
|
||||
|
||||
mfem::Vector plusGradient;
|
||||
mfem::Vector minusGradient;
|
||||
|
||||
rotation.potential_gradient(plusPosition, plusGradient);
|
||||
rotation.potential_gradient(minusPosition, minusGradient);
|
||||
|
||||
plusGradient -= minusGradient;
|
||||
plusGradient /= 2.0 * step;
|
||||
|
||||
plusGradient -= hessianAction;
|
||||
|
||||
CHECK(plusGradient.Norml2() < 2.0e-10);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Rotational Displacement Force Reproduces The Homogeneous Sphere "
|
||||
"Rotational Virial",
|
||||
tags::centrifugal &tags::analytic_comparison &tags::accuracy
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const double radius = mean_field::utils::RADIUS;
|
||||
const double mass = mean_field::utils::MASS;
|
||||
const double volume = 4.0 * std::numbers::pi * radius * radius * radius / 3.0;
|
||||
const double densityValue = mass / volume;
|
||||
const double angularSpeed = 0.73;
|
||||
|
||||
const mfem::Vector density =
|
||||
rotational_displacement_force_analytic_test_utils::project_constant_density(f, densityValue);
|
||||
|
||||
mfem::Vector displacement(f.displacementFes->GetTrueVSize());
|
||||
displacement = 0.0;
|
||||
|
||||
mfem::Vector angularVelocity(3);
|
||||
mfem::Vector center(3);
|
||||
angularVelocity = 0.0;
|
||||
center = 0.0;
|
||||
angularVelocity(2) = angularSpeed;
|
||||
|
||||
const mean_field::physics::RigidRotation rotation(angularVelocity, center);
|
||||
|
||||
mfem::Vector residual;
|
||||
|
||||
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, rotation, density, displacement, residual
|
||||
);
|
||||
|
||||
const mfem::Vector dilationDirection = rotational_displacement_force_analytic_test_utils::project_vector_function(
|
||||
f, [](const mfem::Vector &position, mfem::Vector &value) { value = position; }
|
||||
);
|
||||
|
||||
const double computedWork =
|
||||
rotational_displacement_force_analytic_test_utils::global_dot(residual, dilationDirection, f.mesh->GetComm());
|
||||
|
||||
const double expectedWork = -(2.0 / 5.0) * mass * angularSpeed * angularSpeed * radius * radius;
|
||||
|
||||
const double relativeError =
|
||||
rotational_displacement_force_analytic_test_utils::relative_error(computedWork, expectedWork);
|
||||
|
||||
INFO("Computed rotational virial work = " << computedWork);
|
||||
INFO("Analytic rotational virial work = " << expectedWork);
|
||||
INFO("Rotational virial relative error = " << relativeError);
|
||||
|
||||
CHECK(computedWork < 0.0);
|
||||
CHECK(relativeError < 1.0e-5);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Rotational Displacement Force Matches The Analytic Off-Axis "
|
||||
"Resultant",
|
||||
tags::centrifugal &tags::analytic_comparison &tags::accuracy
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const double radius = mean_field::utils::RADIUS;
|
||||
const double mass = mean_field::utils::MASS;
|
||||
const double volume = 4.0 * std::numbers::pi * radius * radius * radius / 3.0;
|
||||
const double densityValue = mass / volume;
|
||||
const double angularSpeed = 0.61;
|
||||
|
||||
const mfem::Vector density =
|
||||
rotational_displacement_force_analytic_test_utils::project_constant_density(f, densityValue);
|
||||
|
||||
mfem::Vector displacement(f.displacementFes->GetTrueVSize());
|
||||
displacement = 0.0;
|
||||
|
||||
mfem::Vector angularVelocity(3);
|
||||
mfem::Vector center(3);
|
||||
angularVelocity = 0.0;
|
||||
center = 0.0;
|
||||
angularVelocity(2) = angularSpeed;
|
||||
center(0) = 0.13;
|
||||
center(1) = -0.08;
|
||||
|
||||
const mean_field::physics::RigidRotation rotation(angularVelocity, center);
|
||||
|
||||
mfem::Vector residual;
|
||||
|
||||
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
||||
f, *f.domainMapperStateless, rotation, density, displacement, residual
|
||||
);
|
||||
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
const mfem::Vector translationDirection =
|
||||
rotational_displacement_force_analytic_test_utils::project_vector_function(
|
||||
f, [component](const mfem::Vector &, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value = 0.0;
|
||||
value(component) = 1.0;
|
||||
}
|
||||
);
|
||||
|
||||
const double computedResultant = rotational_displacement_force_analytic_test_utils::global_dot(
|
||||
residual, translationDirection, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
const double expectedResultant = component < 2 ? mass * angularSpeed * angularSpeed * center(component) : 0.0;
|
||||
|
||||
INFO("Resultant component = " << component);
|
||||
INFO("Computed resultant = " << computedResultant);
|
||||
INFO("Expected resultant = " << expectedResultant);
|
||||
|
||||
if (component < 2) {
|
||||
CHECK(
|
||||
rotational_displacement_force_analytic_test_utils::relative_error(
|
||||
computedResultant, expectedResultant
|
||||
) < 1.0e-5
|
||||
);
|
||||
} else {
|
||||
CHECK(std::abs(computedResultant) < 1.0e-10);
|
||||
}
|
||||
}
|
||||
}
|
||||
2365
tests/operators/prepared_stellar_equilibrium.cpp
Normal file
2365
tests/operators/prepared_stellar_equilibrium.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -15,42 +15,26 @@ TEST_CASE(
|
||||
constexpr double polytropic_index = 3.0;
|
||||
constexpr double polytropic_constant = 1.5;
|
||||
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(
|
||||
polytropic_index, polytropic_constant
|
||||
);
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(polytropic_index, polytropic_constant);
|
||||
|
||||
const std::array<double, 5> densities{1.0e-6, 1.0e-3, 0.1, 0.7, 2.0};
|
||||
|
||||
for (const double density : densities) {
|
||||
const double pressure = barotrope.pressure_from_density(density);
|
||||
const double pressure = barotrope.pressure_from_density(density);
|
||||
|
||||
const double enthalpy = barotrope.enthalpy_from_density(density);
|
||||
const double enthalpy = barotrope.enthalpy_from_density(density);
|
||||
|
||||
const double reconstructed_density =
|
||||
barotrope.density_from_enthalpy(enthalpy);
|
||||
const double reconstructed_density = barotrope.density_from_enthalpy(enthalpy);
|
||||
|
||||
const double reconstructed_pressure =
|
||||
barotrope.pressure_from_enthalpy(enthalpy);
|
||||
const double reconstructed_pressure = barotrope.pressure_from_enthalpy(enthalpy);
|
||||
|
||||
CHECK_THAT(
|
||||
reconstructed_density, Catch::Matchers::WithinRel(density, 2.0e-14)
|
||||
);
|
||||
CHECK_THAT(reconstructed_density, Catch::Matchers::WithinRel(density, 2.0e-14));
|
||||
|
||||
CHECK_THAT(
|
||||
reconstructed_pressure,
|
||||
Catch::Matchers::WithinRel(pressure, 2.0e-14)
|
||||
);
|
||||
CHECK_THAT(reconstructed_pressure, Catch::Matchers::WithinRel(pressure, 2.0e-14));
|
||||
|
||||
CHECK_THAT(
|
||||
pressure, Catch::Matchers::WithinRel(
|
||||
density * enthalpy / (polytropic_index + 1.0), 2.0e-14
|
||||
)
|
||||
);
|
||||
CHECK_THAT(pressure, Catch::Matchers::WithinRel(density * enthalpy / (polytropic_index + 1.0), 2.0e-14));
|
||||
|
||||
CHECK_THAT(
|
||||
barotrope.pressure_derivative_from_enthalpy(enthalpy),
|
||||
Catch::Matchers::WithinRel(density, 2.0e-14)
|
||||
);
|
||||
CHECK_THAT(barotrope.pressure_derivative_from_enthalpy(enthalpy), Catch::Matchers::WithinRel(density, 2.0e-14));
|
||||
|
||||
CHECK_THAT(
|
||||
barotrope.pressure_derivative_from_density(density),
|
||||
@@ -71,27 +55,21 @@ TEST_CASE(
|
||||
const double step = 1.0e-6 * std::max(1.0, enthalpy);
|
||||
|
||||
const double density_difference =
|
||||
(barotrope.density_from_enthalpy(enthalpy + step) -
|
||||
barotrope.density_from_enthalpy(enthalpy - step)) /
|
||||
(barotrope.density_from_enthalpy(enthalpy + step) - barotrope.density_from_enthalpy(enthalpy - step)) /
|
||||
(2.0 * step);
|
||||
|
||||
const double pressure_difference =
|
||||
(barotrope.pressure_from_enthalpy(enthalpy + step) -
|
||||
barotrope.pressure_from_enthalpy(enthalpy - step)) /
|
||||
(barotrope.pressure_from_enthalpy(enthalpy + step) - barotrope.pressure_from_enthalpy(enthalpy - step)) /
|
||||
(2.0 * step);
|
||||
|
||||
CHECK_THAT(
|
||||
density_difference,
|
||||
Catch::Matchers::WithinRel(
|
||||
barotrope.density_derivative_from_enthalpy(enthalpy), 5.0e-10
|
||||
)
|
||||
Catch::Matchers::WithinRel(barotrope.density_derivative_from_enthalpy(enthalpy), 5.0e-10)
|
||||
);
|
||||
|
||||
CHECK_THAT(
|
||||
pressure_difference,
|
||||
Catch::Matchers::WithinRel(
|
||||
barotrope.pressure_derivative_from_enthalpy(enthalpy), 5.0e-10
|
||||
)
|
||||
Catch::Matchers::WithinRel(barotrope.pressure_derivative_from_enthalpy(enthalpy), 5.0e-10)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -119,21 +97,12 @@ TEST_CASE(
|
||||
"Polytropic Barotrope Rejects Invalid Material Parameters",
|
||||
tags::hydro &tags::unit
|
||||
) {
|
||||
CHECK_THROWS_AS(
|
||||
mean_field::physics::PolytropicBarotrope(0.5, 1.0),
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(mean_field::physics::PolytropicBarotrope(0.5, 1.0), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS(mean_field::physics::PolytropicBarotrope(3.0, 0.0), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
mean_field::physics::PolytropicBarotrope(3.0, 0.0),
|
||||
std::invalid_argument
|
||||
);
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
mean_field::physics::PolytropicBarotrope(
|
||||
std::numeric_limits<double>::infinity(), 1.0
|
||||
),
|
||||
std::invalid_argument
|
||||
mean_field::physics::PolytropicBarotrope(std::numeric_limits<double>::infinity(), 1.0), std::invalid_argument
|
||||
);
|
||||
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.0);
|
||||
|
||||
@@ -20,8 +20,7 @@ namespace polytropic_barotrope_test_utils {
|
||||
const double position,
|
||||
const double step
|
||||
) {
|
||||
return (function(position + step) - function(position - step)) /
|
||||
(2.0 * step);
|
||||
return (function(position + step) - function(position - step)) / (2.0 * step);
|
||||
}
|
||||
|
||||
template <typename Integrand>
|
||||
@@ -31,10 +30,8 @@ namespace polytropic_barotrope_test_utils {
|
||||
) {
|
||||
double integral = 0.0;
|
||||
|
||||
for (int pointIndex = 0; pointIndex < integrationRule.GetNPoints();
|
||||
++pointIndex) {
|
||||
const mfem::IntegrationPoint &integrationPoint =
|
||||
integrationRule.IntPoint(pointIndex);
|
||||
for (int pointIndex = 0; pointIndex < integrationRule.GetNPoints(); ++pointIndex) {
|
||||
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(pointIndex);
|
||||
|
||||
integral += integrationPoint.weight * integrand(integrationPoint);
|
||||
}
|
||||
@@ -55,12 +52,9 @@ TEST_CASE(
|
||||
|
||||
for (const double polytropicIndex : polytropicIndices) {
|
||||
DYNAMIC_SECTION("polytropic index n = " << polytropicIndex) {
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(
|
||||
polytropicIndex, polytropicConstant
|
||||
);
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(polytropicIndex, polytropicConstant);
|
||||
|
||||
const double expectedEnthalpyScale =
|
||||
(polytropicIndex + 1.0) * polytropicConstant;
|
||||
const double expectedEnthalpyScale = (polytropicIndex + 1.0) * polytropicConstant;
|
||||
|
||||
CHECK(barotrope.polytropic_index() == polytropicIndex);
|
||||
|
||||
@@ -71,45 +65,25 @@ TEST_CASE(
|
||||
for (const double density : densities) {
|
||||
CAPTURE(polytropicIndex, polytropicConstant, density);
|
||||
|
||||
const double expectedPressure =
|
||||
polytropicConstant *
|
||||
std::pow(density, 1.0 + 1.0 / polytropicIndex);
|
||||
const double expectedPressure = polytropicConstant * std::pow(density, 1.0 + 1.0 / polytropicIndex);
|
||||
|
||||
const double expectedEnthalpy =
|
||||
expectedEnthalpyScale *
|
||||
std::pow(density, 1.0 / polytropicIndex);
|
||||
const double expectedEnthalpy = expectedEnthalpyScale * std::pow(density, 1.0 / polytropicIndex);
|
||||
|
||||
const double pressureFromDensity =
|
||||
barotrope.pressure_from_density(density);
|
||||
const double pressureFromDensity = barotrope.pressure_from_density(density);
|
||||
|
||||
const double enthalpyFromDensity =
|
||||
barotrope.enthalpy_from_density(density);
|
||||
const double enthalpyFromDensity = barotrope.enthalpy_from_density(density);
|
||||
|
||||
const double recoveredDensity =
|
||||
barotrope.density_from_enthalpy(enthalpyFromDensity);
|
||||
const double recoveredDensity = barotrope.density_from_enthalpy(enthalpyFromDensity);
|
||||
|
||||
const double pressureFromEnthalpy =
|
||||
barotrope.pressure_from_enthalpy(enthalpyFromDensity);
|
||||
const double pressureFromEnthalpy = barotrope.pressure_from_enthalpy(enthalpyFromDensity);
|
||||
|
||||
CHECK_THAT(
|
||||
pressureFromDensity,
|
||||
Catch::Matchers::WithinRel(expectedPressure, 2.0e-13)
|
||||
);
|
||||
CHECK_THAT(pressureFromDensity, Catch::Matchers::WithinRel(expectedPressure, 2.0e-13));
|
||||
|
||||
CHECK_THAT(
|
||||
enthalpyFromDensity,
|
||||
Catch::Matchers::WithinRel(expectedEnthalpy, 2.0e-13)
|
||||
);
|
||||
CHECK_THAT(enthalpyFromDensity, Catch::Matchers::WithinRel(expectedEnthalpy, 2.0e-13));
|
||||
|
||||
CHECK_THAT(
|
||||
recoveredDensity,
|
||||
Catch::Matchers::WithinRel(density, 5.0e-13)
|
||||
);
|
||||
CHECK_THAT(recoveredDensity, Catch::Matchers::WithinRel(density, 5.0e-13));
|
||||
|
||||
CHECK_THAT(
|
||||
pressureFromEnthalpy,
|
||||
Catch::Matchers::WithinRel(expectedPressure, 5.0e-13)
|
||||
);
|
||||
CHECK_THAT(pressureFromEnthalpy, Catch::Matchers::WithinRel(expectedPressure, 5.0e-13));
|
||||
|
||||
/*
|
||||
* Polytropic identity:
|
||||
@@ -118,10 +92,7 @@ TEST_CASE(
|
||||
*/
|
||||
CHECK_THAT(
|
||||
pressureFromEnthalpy,
|
||||
Catch::Matchers::WithinRel(
|
||||
density * enthalpyFromDensity / (polytropicIndex + 1.0),
|
||||
5.0e-13
|
||||
)
|
||||
Catch::Matchers::WithinRel(density * enthalpyFromDensity / (polytropicIndex + 1.0), 5.0e-13)
|
||||
);
|
||||
|
||||
/*
|
||||
@@ -133,9 +104,8 @@ TEST_CASE(
|
||||
* value as density_from_enthalpy().
|
||||
*/
|
||||
CHECK(
|
||||
barotrope.pressure_derivative_from_enthalpy(
|
||||
enthalpyFromDensity
|
||||
) == barotrope.density_from_enthalpy(enthalpyFromDensity)
|
||||
barotrope.pressure_derivative_from_enthalpy(enthalpyFromDensity) ==
|
||||
barotrope.density_from_enthalpy(enthalpyFromDensity)
|
||||
);
|
||||
|
||||
/*
|
||||
@@ -149,9 +119,7 @@ TEST_CASE(
|
||||
*/
|
||||
CHECK_THAT(
|
||||
barotrope.pressure_derivative_from_density(density),
|
||||
Catch::Matchers::WithinRel(
|
||||
enthalpyFromDensity / polytropicIndex, 5.0e-13
|
||||
)
|
||||
Catch::Matchers::WithinRel(enthalpyFromDensity / polytropicIndex, 5.0e-13)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -169,63 +137,41 @@ TEST_CASE(
|
||||
constexpr double polytropicConstant = 0.61;
|
||||
|
||||
for (const double polytropicIndex : polytropicIndices) {
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(
|
||||
polytropicIndex, polytropicConstant
|
||||
);
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(polytropicIndex, polytropicConstant);
|
||||
|
||||
DYNAMIC_SECTION("polytropic index n = " << polytropicIndex) {
|
||||
for (const double enthalpy : positiveValues) {
|
||||
const double step = 2.0e-6 * std::max(1.0, std::abs(enthalpy));
|
||||
const double step = 2.0e-6 * std::max(1.0, std::abs(enthalpy));
|
||||
|
||||
const double numericalDerivative =
|
||||
polytropic_barotrope_test_utils::centered_derivative(
|
||||
[&barotrope](const double perturbedEnthalpy) {
|
||||
return barotrope.pressure_from_enthalpy(
|
||||
perturbedEnthalpy
|
||||
);
|
||||
},
|
||||
enthalpy, step
|
||||
);
|
||||
|
||||
const double analyticDerivative =
|
||||
barotrope.pressure_derivative_from_enthalpy(enthalpy);
|
||||
|
||||
CAPTURE(
|
||||
polytropicIndex, enthalpy, step, numericalDerivative,
|
||||
analyticDerivative
|
||||
const double numericalDerivative = polytropic_barotrope_test_utils::centered_derivative(
|
||||
[&barotrope](const double perturbedEnthalpy) {
|
||||
return barotrope.pressure_from_enthalpy(perturbedEnthalpy);
|
||||
},
|
||||
enthalpy, step
|
||||
);
|
||||
|
||||
CHECK_THAT(
|
||||
numericalDerivative,
|
||||
Catch::Matchers::WithinRel(analyticDerivative, 5.0e-8)
|
||||
);
|
||||
const double analyticDerivative = barotrope.pressure_derivative_from_enthalpy(enthalpy);
|
||||
|
||||
CAPTURE(polytropicIndex, enthalpy, step, numericalDerivative, analyticDerivative);
|
||||
|
||||
CHECK_THAT(numericalDerivative, Catch::Matchers::WithinRel(analyticDerivative, 5.0e-8));
|
||||
}
|
||||
|
||||
for (const double density : positiveValues) {
|
||||
const double step = 2.0e-6 * std::max(1.0, std::abs(density));
|
||||
const double step = 2.0e-6 * std::max(1.0, std::abs(density));
|
||||
|
||||
const double numericalDerivative =
|
||||
polytropic_barotrope_test_utils::centered_derivative(
|
||||
[&barotrope](const double perturbedDensity) {
|
||||
return barotrope.pressure_from_density(
|
||||
perturbedDensity
|
||||
);
|
||||
},
|
||||
density, step
|
||||
);
|
||||
|
||||
const double analyticDerivative =
|
||||
barotrope.pressure_derivative_from_density(density);
|
||||
|
||||
CAPTURE(
|
||||
polytropicIndex, density, step, numericalDerivative,
|
||||
analyticDerivative
|
||||
const double numericalDerivative = polytropic_barotrope_test_utils::centered_derivative(
|
||||
[&barotrope](const double perturbedDensity) {
|
||||
return barotrope.pressure_from_density(perturbedDensity);
|
||||
},
|
||||
density, step
|
||||
);
|
||||
|
||||
CHECK_THAT(
|
||||
numericalDerivative,
|
||||
Catch::Matchers::WithinRel(analyticDerivative, 5.0e-8)
|
||||
);
|
||||
const double analyticDerivative = barotrope.pressure_derivative_from_density(density);
|
||||
|
||||
CAPTURE(polytropicIndex, density, step, numericalDerivative, analyticDerivative);
|
||||
|
||||
CHECK_THAT(numericalDerivative, Catch::Matchers::WithinRel(analyticDerivative, 5.0e-8));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,36 +188,24 @@ TEST_CASE(
|
||||
constexpr double polytropicConstant = 0.61;
|
||||
|
||||
for (const double polytropicIndex : polytropicIndices) {
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(
|
||||
polytropicIndex, polytropicConstant
|
||||
);
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(polytropicIndex, polytropicConstant);
|
||||
|
||||
DYNAMIC_SECTION("polytropic index n = " << polytropicIndex) {
|
||||
for (const double enthalpy : enthalpies) {
|
||||
const double step = 2.0e-6 * std::max(1.0, std::abs(enthalpy));
|
||||
const double step = 2.0e-6 * std::max(1.0, std::abs(enthalpy));
|
||||
|
||||
const double numericalDerivative =
|
||||
polytropic_barotrope_test_utils::centered_derivative(
|
||||
[&barotrope](const double perturbedEnthalpy) {
|
||||
return barotrope.density_from_enthalpy(
|
||||
perturbedEnthalpy
|
||||
);
|
||||
},
|
||||
enthalpy, step
|
||||
);
|
||||
|
||||
const double analyticDerivative =
|
||||
barotrope.density_derivative_from_enthalpy(enthalpy);
|
||||
|
||||
CAPTURE(
|
||||
polytropicIndex, enthalpy, step, numericalDerivative,
|
||||
analyticDerivative
|
||||
const double numericalDerivative = polytropic_barotrope_test_utils::centered_derivative(
|
||||
[&barotrope](const double perturbedEnthalpy) {
|
||||
return barotrope.density_from_enthalpy(perturbedEnthalpy);
|
||||
},
|
||||
enthalpy, step
|
||||
);
|
||||
|
||||
CHECK_THAT(
|
||||
numericalDerivative,
|
||||
Catch::Matchers::WithinRel(analyticDerivative, 5.0e-8)
|
||||
);
|
||||
const double analyticDerivative = barotrope.density_derivative_from_enthalpy(enthalpy);
|
||||
|
||||
CAPTURE(polytropicIndex, enthalpy, step, numericalDerivative, analyticDerivative);
|
||||
|
||||
CHECK_THAT(numericalDerivative, Catch::Matchers::WithinRel(analyticDerivative, 5.0e-8));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -287,9 +221,7 @@ TEST_CASE(
|
||||
constexpr double exteriorEnthalpy = -0.3;
|
||||
|
||||
for (const double polytropicIndex : polytropicIndices) {
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(
|
||||
polytropicIndex, polytropicConstant
|
||||
);
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(polytropicIndex, polytropicConstant);
|
||||
|
||||
DYNAMIC_SECTION("polytropic index n = " << polytropicIndex) {
|
||||
/*
|
||||
@@ -314,15 +246,9 @@ TEST_CASE(
|
||||
|
||||
CHECK(barotrope.pressure_from_enthalpy(exteriorEnthalpy) == 0.0);
|
||||
|
||||
CHECK(
|
||||
barotrope.density_derivative_from_enthalpy(exteriorEnthalpy) ==
|
||||
0.0
|
||||
);
|
||||
CHECK(barotrope.density_derivative_from_enthalpy(exteriorEnthalpy) == 0.0);
|
||||
|
||||
CHECK(
|
||||
barotrope.pressure_derivative_from_enthalpy(exteriorEnthalpy) ==
|
||||
0.0
|
||||
);
|
||||
CHECK(barotrope.pressure_derivative_from_enthalpy(exteriorEnthalpy) == 0.0);
|
||||
|
||||
/*
|
||||
* At h = 0, rho(h) has a nonzero right
|
||||
@@ -331,10 +257,7 @@ TEST_CASE(
|
||||
const double expectedSurfaceDensityDerivative =
|
||||
polytropicIndex == 1.0 ? 1.0 / barotrope.enthalpy_scale() : 0.0;
|
||||
|
||||
CHECK(
|
||||
barotrope.density_derivative_from_enthalpy(0.0) ==
|
||||
expectedSurfaceDensityDerivative
|
||||
);
|
||||
CHECK(barotrope.density_derivative_from_enthalpy(0.0) == expectedSurfaceDensityDerivative);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -343,27 +266,15 @@ TEST_CASE(
|
||||
"Polytropic Barotrope Rejects Invalid Physical Inputs",
|
||||
tags::barotrope &tags::physics &tags::unit &tags::pressure
|
||||
) {
|
||||
CHECK_THROWS_AS(
|
||||
mean_field::physics::PolytropicBarotrope(0.999, 1.0),
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(mean_field::physics::PolytropicBarotrope(0.999, 1.0), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
mean_field::physics::PolytropicBarotrope(
|
||||
std::numeric_limits<double>::infinity(), 1.0
|
||||
),
|
||||
std::invalid_argument
|
||||
mean_field::physics::PolytropicBarotrope(std::numeric_limits<double>::infinity(), 1.0), std::invalid_argument
|
||||
);
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
mean_field::physics::PolytropicBarotrope(3.0, 0.0),
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(mean_field::physics::PolytropicBarotrope(3.0, 0.0), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
mean_field::physics::PolytropicBarotrope(3.0, -1.0),
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(mean_field::physics::PolytropicBarotrope(3.0, -1.0), std::invalid_argument);
|
||||
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 0.75);
|
||||
|
||||
@@ -371,45 +282,31 @@ TEST_CASE(
|
||||
|
||||
CHECK_THROWS_AS(barotrope.enthalpy_from_density(-0.1), std::domain_error);
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
barotrope.pressure_derivative_from_density(-0.1), std::domain_error
|
||||
);
|
||||
CHECK_THROWS_AS(barotrope.pressure_derivative_from_density(-0.1), std::domain_error);
|
||||
|
||||
constexpr std::array<double, 3> nonfiniteValues{
|
||||
std::numeric_limits<double>::infinity(),
|
||||
-std::numeric_limits<double>::infinity(),
|
||||
std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity(),
|
||||
std::numeric_limits<double>::quiet_NaN()
|
||||
};
|
||||
|
||||
for (const double nonfiniteValue : nonfiniteValues) {
|
||||
CAPTURE(nonfiniteValue);
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
barotrope.density_from_enthalpy(nonfiniteValue), std::domain_error
|
||||
);
|
||||
CHECK_THROWS_AS(barotrope.density_from_enthalpy(nonfiniteValue), std::domain_error);
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
barotrope.pressure_from_enthalpy(nonfiniteValue), std::domain_error
|
||||
);
|
||||
CHECK_THROWS_AS(barotrope.pressure_from_enthalpy(nonfiniteValue), std::domain_error);
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
barotrope.density_derivative_from_enthalpy(nonfiniteValue),
|
||||
std::domain_error
|
||||
);
|
||||
CHECK_THROWS_AS(barotrope.density_derivative_from_enthalpy(nonfiniteValue), std::domain_error);
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
barotrope.pressure_derivative_from_enthalpy(nonfiniteValue),
|
||||
std::domain_error
|
||||
);
|
||||
CHECK_THROWS_AS(barotrope.pressure_derivative_from_enthalpy(nonfiniteValue), std::domain_error);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Pressure Force And Pressure Integral Have Distinct Registered Forms",
|
||||
tags::barotrope &tags::pressure &tags::pressure_gradient &tags::quadrature
|
||||
&tags::unit
|
||||
tags::barotrope &tags::pressure &tags::pressure_gradient &tags::quadrature &tags::unit
|
||||
) {
|
||||
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||
|
||||
/*
|
||||
* For the registered H1 order p = 3 and n = 3:
|
||||
@@ -423,39 +320,29 @@ TEST_CASE(
|
||||
*
|
||||
* beyond the registered enthalpy operand.
|
||||
*/
|
||||
constexpr int enthalpyOrder =
|
||||
mean_field::field::Enthalpy::Scalar::familyOrder;
|
||||
constexpr int enthalpyOrder = mean_field::field::Enthalpy::Scalar::familyOrder;
|
||||
|
||||
constexpr int pressureExtraOrder = 3 * enthalpyOrder;
|
||||
|
||||
constexpr int geometryWeightOrder = 2;
|
||||
|
||||
constexpr mean_field::quadrature::Query pressureIntegralQuery =
|
||||
EnthalpyField::make_query<
|
||||
mean_field::field::Enthalpy::Form::PressureIntegral>(
|
||||
mean_field::quadrature::QuadratureRole::diagnostic,
|
||||
geometryWeightOrder, std::array<int, 1>{pressureExtraOrder},
|
||||
mean_field::utils::DOMAINS::STELLAR,
|
||||
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureIntegral>(
|
||||
mean_field::quadrature::QuadratureRole::diagnostic, geometryWeightOrder,
|
||||
std::array<int, 1>{pressureExtraOrder}, mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
constexpr mean_field::quadrature::Query pressureForceQuery =
|
||||
EnthalpyField::make_query<
|
||||
mean_field::field::Enthalpy::Form::PressureForce>(
|
||||
mean_field::quadrature::QuadratureRole::discretization,
|
||||
geometryWeightOrder, std::array<int, 1>{pressureExtraOrder},
|
||||
mean_field::utils::DOMAINS::STELLAR,
|
||||
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureForce>(
|
||||
mean_field::quadrature::QuadratureRole::discretization, geometryWeightOrder,
|
||||
std::array<int, 1>{pressureExtraOrder}, mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
STATIC_CHECK(
|
||||
mean_field::field::Enthalpy::Form::PressureIntegral::
|
||||
dynamicOrderCount == 1
|
||||
);
|
||||
STATIC_CHECK(mean_field::field::Enthalpy::Form::PressureIntegral::dynamicOrderCount == 1);
|
||||
|
||||
STATIC_CHECK(
|
||||
mean_field::field::Enthalpy::Form::PressureForce::dynamicOrderCount == 1
|
||||
);
|
||||
STATIC_CHECK(mean_field::field::Enthalpy::Form::PressureForce::dynamicOrderCount == 1);
|
||||
|
||||
STATIC_CHECK(
|
||||
mean_field::field::Enthalpy::Form::PressureIntegral::policyKey !=
|
||||
@@ -487,24 +374,13 @@ TEST_CASE(
|
||||
*/
|
||||
CHECK(*pressureForceQuery.base_order == 16);
|
||||
|
||||
CHECK(
|
||||
pressureIntegralQuery.term ==
|
||||
mean_field::quadrature::Term::pressure_integral
|
||||
);
|
||||
CHECK(pressureIntegralQuery.term == mean_field::quadrature::Term::pressure_integral);
|
||||
|
||||
CHECK(
|
||||
pressureForceQuery.term == mean_field::quadrature::Term::pressure_force
|
||||
);
|
||||
CHECK(pressureForceQuery.term == mean_field::quadrature::Term::pressure_force);
|
||||
|
||||
CHECK(
|
||||
pressureIntegralQuery.role ==
|
||||
mean_field::quadrature::QuadratureRole::diagnostic
|
||||
);
|
||||
CHECK(pressureIntegralQuery.role == mean_field::quadrature::QuadratureRole::diagnostic);
|
||||
|
||||
CHECK(
|
||||
pressureForceQuery.role ==
|
||||
mean_field::quadrature::QuadratureRole::discretization
|
||||
);
|
||||
CHECK(pressureForceQuery.role == mean_field::quadrature::QuadratureRole::discretization);
|
||||
|
||||
CHECK(pressureIntegralQuery.domain == mean_field::utils::DOMAINS::STELLAR);
|
||||
|
||||
@@ -515,20 +391,16 @@ TEST_CASE(
|
||||
* controls.
|
||||
*/
|
||||
mean_field::quadrature::RuleSet ruleSet =
|
||||
mean_field::quadrature::make_rule_set(
|
||||
mean_field::quadrature::Mode::production
|
||||
);
|
||||
mean_field::quadrature::make_rule_set(mean_field::quadrature::Mode::production);
|
||||
|
||||
ruleSet.pressure_integral.boost = 3;
|
||||
ruleSet.pressure_force.boost = 5;
|
||||
|
||||
const mean_field::quadrature::Policy policy(std::move(ruleSet));
|
||||
|
||||
const mean_field::quadrature::Resolution pressureIntegralResolution =
|
||||
policy.resolve(pressureIntegralQuery);
|
||||
const mean_field::quadrature::Resolution pressureIntegralResolution = policy.resolve(pressureIntegralQuery);
|
||||
|
||||
const mean_field::quadrature::Resolution pressureForceResolution =
|
||||
policy.resolve(pressureForceQuery);
|
||||
const mean_field::quadrature::Resolution pressureForceResolution = policy.resolve(pressureForceQuery);
|
||||
|
||||
CHECK(pressureIntegralResolution.base_order == 14);
|
||||
|
||||
@@ -544,14 +416,12 @@ TEST_CASE(
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Stage Four Pressure Quadrature Exactly Integrates An N Three Polynomial",
|
||||
tags::barotrope &tags::pressure &tags::pressure_gradient &tags::quadrature
|
||||
&tags::accuracy
|
||||
"Pressure Quadrature Exactly Integrates An N Three Polynomial",
|
||||
tags::barotrope &tags::pressure &tags::pressure_gradient &tags::quadrature &tags::accuracy
|
||||
) {
|
||||
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||
|
||||
constexpr int enthalpyOrder =
|
||||
mean_field::field::Enthalpy::Scalar::familyOrder;
|
||||
constexpr int enthalpyOrder = mean_field::field::Enthalpy::Scalar::familyOrder;
|
||||
|
||||
constexpr int pressureExtraOrder = 3 * enthalpyOrder;
|
||||
|
||||
@@ -565,29 +435,19 @@ TEST_CASE(
|
||||
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 0.25);
|
||||
|
||||
constexpr mean_field::quadrature::Query pressureIntegralQuery =
|
||||
EnthalpyField::make_query<
|
||||
mean_field::field::Enthalpy::Form::PressureIntegral>(
|
||||
mean_field::quadrature::QuadratureRole::diagnostic, 0,
|
||||
std::array<int, 1>{pressureExtraOrder},
|
||||
mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::affine
|
||||
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureIntegral>(
|
||||
mean_field::quadrature::QuadratureRole::diagnostic, 0, std::array<int, 1>{pressureExtraOrder},
|
||||
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::affine
|
||||
);
|
||||
|
||||
constexpr mean_field::quadrature::Query pressureForceQuery =
|
||||
EnthalpyField::make_query<
|
||||
mean_field::field::Enthalpy::Form::PressureForce>(
|
||||
mean_field::quadrature::QuadratureRole::discretization, 0,
|
||||
std::array<int, 1>{pressureExtraOrder},
|
||||
mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::affine
|
||||
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureForce>(
|
||||
mean_field::quadrature::QuadratureRole::discretization, 0, std::array<int, 1>{pressureExtraOrder},
|
||||
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::affine
|
||||
);
|
||||
|
||||
const mean_field::quadrature::RuleFactory ruleFactory{
|
||||
mean_field::quadrature::Policy(
|
||||
mean_field::quadrature::make_rule_set(
|
||||
mean_field::quadrature::Mode::production
|
||||
)
|
||||
)
|
||||
mean_field::quadrature::Policy(mean_field::quadrature::make_rule_set(mean_field::quadrature::Mode::production))
|
||||
};
|
||||
|
||||
const mean_field::quadrature::MfemRule pressureIntegralRule =
|
||||
@@ -606,21 +466,17 @@ TEST_CASE(
|
||||
*
|
||||
* P = x^12 y^12 z^12 / 4.
|
||||
*/
|
||||
const double numericalPressureIntegral =
|
||||
polytropic_barotrope_test_utils::integrate_cube(
|
||||
*pressureIntegralRule.integration_rule,
|
||||
[&barotrope](const mfem::IntegrationPoint &integrationPoint) {
|
||||
const double coordinateProduct = integrationPoint.x *
|
||||
integrationPoint.y *
|
||||
integrationPoint.z;
|
||||
const double numericalPressureIntegral = polytropic_barotrope_test_utils::integrate_cube(
|
||||
*pressureIntegralRule.integration_rule, [&barotrope](const mfem::IntegrationPoint &integrationPoint) {
|
||||
const double coordinateProduct = integrationPoint.x * integrationPoint.y * integrationPoint.z;
|
||||
|
||||
const double enthalpy = std::pow(coordinateProduct, 3.0);
|
||||
const double enthalpy = std::pow(coordinateProduct, 3.0);
|
||||
|
||||
return barotrope.pressure_from_enthalpy(enthalpy);
|
||||
}
|
||||
);
|
||||
return barotrope.pressure_from_enthalpy(enthalpy);
|
||||
}
|
||||
);
|
||||
|
||||
const double analyticPressureIntegral = 0.25 / std::pow(13.0, 3.0);
|
||||
const double analyticPressureIntegral = 0.25 / std::pow(13.0, 3.0);
|
||||
|
||||
/*
|
||||
* Choose a representable vector test function whose
|
||||
@@ -633,51 +489,34 @@ TEST_CASE(
|
||||
* -P div(w)
|
||||
* = -x^14 y^14 z^14 / 4.
|
||||
*/
|
||||
const double numericalPressureForceIntegral =
|
||||
polytropic_barotrope_test_utils::integrate_cube(
|
||||
*pressureForceRule.integration_rule,
|
||||
[&barotrope](const mfem::IntegrationPoint &integrationPoint) {
|
||||
const double coordinateProduct = integrationPoint.x *
|
||||
integrationPoint.y *
|
||||
integrationPoint.z;
|
||||
const double numericalPressureForceIntegral = polytropic_barotrope_test_utils::integrate_cube(
|
||||
*pressureForceRule.integration_rule, [&barotrope](const mfem::IntegrationPoint &integrationPoint) {
|
||||
const double coordinateProduct = integrationPoint.x * integrationPoint.y * integrationPoint.z;
|
||||
|
||||
const double enthalpy = std::pow(coordinateProduct, 3.0);
|
||||
const double enthalpy = std::pow(coordinateProduct, 3.0);
|
||||
|
||||
const double pressure =
|
||||
barotrope.pressure_from_enthalpy(enthalpy);
|
||||
const double pressure = barotrope.pressure_from_enthalpy(enthalpy);
|
||||
|
||||
const double testDivergence =
|
||||
integrationPoint.x * integrationPoint.x *
|
||||
integrationPoint.y * integrationPoint.y *
|
||||
integrationPoint.z * integrationPoint.z;
|
||||
const double testDivergence = integrationPoint.x * integrationPoint.x * integrationPoint.y *
|
||||
integrationPoint.y * integrationPoint.z * integrationPoint.z;
|
||||
|
||||
return -pressure * testDivergence;
|
||||
}
|
||||
);
|
||||
return -pressure * testDivergence;
|
||||
}
|
||||
);
|
||||
|
||||
const double analyticPressureForceIntegral = -0.25 / std::pow(15.0, 3.0);
|
||||
|
||||
INFO(
|
||||
"Pressure-integral quadrature order = "
|
||||
<< pressureIntegralRule.resolution.order
|
||||
);
|
||||
INFO("Pressure-integral quadrature order = " << pressureIntegralRule.resolution.order);
|
||||
|
||||
INFO(
|
||||
"Pressure-force quadrature order = "
|
||||
<< pressureForceRule.resolution.order
|
||||
);
|
||||
INFO("Pressure-force quadrature order = " << pressureForceRule.resolution.order);
|
||||
|
||||
INFO("Numerical pressure integral = " << numericalPressureIntegral);
|
||||
|
||||
INFO("Analytic pressure integral = " << analyticPressureIntegral);
|
||||
|
||||
INFO(
|
||||
"Numerical pressure-force integral = " << numericalPressureForceIntegral
|
||||
);
|
||||
INFO("Numerical pressure-force integral = " << numericalPressureForceIntegral);
|
||||
|
||||
INFO(
|
||||
"Analytic pressure-force integral = " << analyticPressureForceIntegral
|
||||
);
|
||||
INFO("Analytic pressure-force integral = " << analyticPressureForceIntegral);
|
||||
|
||||
CHECK(pressureIntegralRule.resolution.base_order == 12);
|
||||
|
||||
@@ -687,13 +526,7 @@ TEST_CASE(
|
||||
|
||||
CHECK(pressureForceRule.resolution.order == 14);
|
||||
|
||||
CHECK_THAT(
|
||||
numericalPressureIntegral,
|
||||
Catch::Matchers::WithinAbs(analyticPressureIntegral, 5.0e-14)
|
||||
);
|
||||
CHECK_THAT(numericalPressureIntegral, Catch::Matchers::WithinAbs(analyticPressureIntegral, 5.0e-14));
|
||||
|
||||
CHECK_THAT(
|
||||
numericalPressureForceIntegral,
|
||||
Catch::Matchers::WithinAbs(analyticPressureForceIntegral, 5.0e-14)
|
||||
);
|
||||
CHECK_THAT(numericalPressureForceIntegral, Catch::Matchers::WithinAbs(analyticPressureForceIntegral, 5.0e-14));
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -40,6 +40,8 @@ namespace {
|
||||
return "gravity_divergence";
|
||||
case quadrature::Term::gravity_source:
|
||||
return "gravity_source";
|
||||
case quadrature::Term::gravity_force:
|
||||
return "gravity_force";
|
||||
case quadrature::Term::gravity_boundary:
|
||||
return "gravity_boundary";
|
||||
case quadrature::Term::centrifugal:
|
||||
@@ -92,9 +94,7 @@ TEST_CASE(
|
||||
"Quadrature Policy Computes Base Orders",
|
||||
tags::unit &tags::quadrature
|
||||
) {
|
||||
const quadrature::Policy policy(
|
||||
quadrature::make_rule_set(quadrature::Mode::production)
|
||||
);
|
||||
const quadrature::Policy policy(quadrature::make_rule_set(quadrature::Mode::production));
|
||||
|
||||
quadrature::Query generic_query = {
|
||||
.term = quadrature::Term::gravitational_energy,
|
||||
@@ -104,8 +104,7 @@ TEST_CASE(
|
||||
.geometry_weight_order = 5
|
||||
};
|
||||
|
||||
const quadrature::Resolution generic_resolution =
|
||||
policy.resolve(generic_query);
|
||||
const quadrature::Resolution generic_resolution = policy.resolve(generic_query);
|
||||
CHECK(generic_resolution.base_order == 14);
|
||||
CHECK(generic_resolution.boost == 0);
|
||||
CHECK(generic_resolution.order == 14);
|
||||
@@ -141,26 +140,22 @@ TEST_CASE(
|
||||
"Quadrature Policy Composes Global and Term Boosts",
|
||||
tags::unit &tags::quadrature
|
||||
) {
|
||||
quadrature::RuleSet rule_set =
|
||||
quadrature::make_rule_set(quadrature::Mode::production, 3);
|
||||
quadrature::RuleSet rule_set = quadrature::make_rule_set(quadrature::Mode::production, 3);
|
||||
rule_set.gravity_hdiv_mass.boost = 5;
|
||||
rule_set.error_norm.boost = 2;
|
||||
const quadrature::Policy policy(rule_set);
|
||||
|
||||
const quadrature::Resolution mass_resolution =
|
||||
policy.resolve(make_query(quadrature::Term::gravity_hdiv_mass, 7));
|
||||
const quadrature::Resolution mass_resolution = policy.resolve(make_query(quadrature::Term::gravity_hdiv_mass, 7));
|
||||
CHECK(mass_resolution.base_order == 7);
|
||||
CHECK(mass_resolution.boost == 8);
|
||||
CHECK(mass_resolution.order == 15);
|
||||
CHECK_FALSE(mass_resolution.used_fixed_order);
|
||||
|
||||
const quadrature::Resolution error_resolution =
|
||||
policy.resolve(make_query(quadrature::Term::error_norm, 7));
|
||||
const quadrature::Resolution error_resolution = policy.resolve(make_query(quadrature::Term::error_norm, 7));
|
||||
CHECK(error_resolution.boost == 5);
|
||||
CHECK(error_resolution.order == 12);
|
||||
|
||||
const quadrature::Resolution source_resolution =
|
||||
policy.resolve(make_query(quadrature::Term::gravity_source, 7));
|
||||
const quadrature::Resolution source_resolution = policy.resolve(make_query(quadrature::Term::gravity_source, 7));
|
||||
CHECK(source_resolution.boost == 3);
|
||||
CHECK(source_resolution.order == 10);
|
||||
}
|
||||
@@ -169,23 +164,20 @@ TEST_CASE(
|
||||
"Quadrature Fixed Orders Have Defined Precedence",
|
||||
tags::unit &tags::quadrature
|
||||
) {
|
||||
quadrature::RuleSet rule_set =
|
||||
quadrature::make_rule_set(quadrature::Mode::production, 4);
|
||||
quadrature::RuleSet rule_set = quadrature::make_rule_set(quadrature::Mode::production, 4);
|
||||
rule_set.fallback.fixed_order = 17;
|
||||
rule_set.gravity_hdiv_mass.fixed_order = 23;
|
||||
rule_set.gravity_hdiv_mass.boost = 100;
|
||||
rule_set.gravity_source.boost = 100;
|
||||
const quadrature::Policy policy(rule_set);
|
||||
|
||||
const quadrature::Resolution term_resolution =
|
||||
policy.resolve(make_query(quadrature::Term::gravity_hdiv_mass, 8));
|
||||
const quadrature::Resolution term_resolution = policy.resolve(make_query(quadrature::Term::gravity_hdiv_mass, 8));
|
||||
CHECK(term_resolution.base_order == 8);
|
||||
CHECK(term_resolution.boost == 0);
|
||||
CHECK(term_resolution.order == 23);
|
||||
CHECK(term_resolution.used_fixed_order);
|
||||
|
||||
const quadrature::Resolution fallback_resolution =
|
||||
policy.resolve(make_query(quadrature::Term::gravity_source, 8));
|
||||
const quadrature::Resolution fallback_resolution = policy.resolve(make_query(quadrature::Term::gravity_source, 8));
|
||||
CHECK(fallback_resolution.base_order == 8);
|
||||
CHECK(fallback_resolution.boost == 0);
|
||||
CHECK(fallback_resolution.order == 17);
|
||||
@@ -200,25 +192,16 @@ TEST_CASE(
|
||||
constexpr int global_boost = 3;
|
||||
|
||||
for (const quadrature::Mode mode :
|
||||
{quadrature::Mode::fast, quadrature::Mode::production,
|
||||
quadrature::Mode::convergence}) {
|
||||
const quadrature::Policy policy(
|
||||
quadrature::make_rule_set(mode, global_boost)
|
||||
);
|
||||
const quadrature::Resolution resolution = policy.resolve(
|
||||
make_query(quadrature::Term::error_norm, base_order)
|
||||
);
|
||||
{quadrature::Mode::fast, quadrature::Mode::production, quadrature::Mode::convergence}) {
|
||||
const quadrature::Policy policy(quadrature::make_rule_set(mode, global_boost));
|
||||
const quadrature::Resolution resolution = policy.resolve(make_query(quadrature::Term::error_norm, base_order));
|
||||
CHECK(resolution.boost == global_boost);
|
||||
CHECK(resolution.order == base_order + global_boost);
|
||||
}
|
||||
|
||||
const quadrature::Policy reference_policy(
|
||||
quadrature::make_rule_set(quadrature::Mode::reference, global_boost)
|
||||
);
|
||||
const quadrature::Policy reference_policy(quadrature::make_rule_set(quadrature::Mode::reference, global_boost));
|
||||
const quadrature::Resolution reference_resolution =
|
||||
reference_policy.resolve(
|
||||
make_query(quadrature::Term::error_norm, base_order)
|
||||
);
|
||||
reference_policy.resolve(make_query(quadrature::Term::error_norm, base_order));
|
||||
CHECK(reference_resolution.boost == global_boost + 8);
|
||||
CHECK(reference_resolution.order == base_order + global_boost + 8);
|
||||
}
|
||||
@@ -231,6 +214,7 @@ TEST_CASE(
|
||||
rule_set.gravity_hdiv_mass.boost = 1;
|
||||
rule_set.gravity_divergence.boost = 2;
|
||||
rule_set.gravity_source.boost = 3;
|
||||
rule_set.gravity_force.boost = 19;
|
||||
rule_set.gravity_boundary.boost = 4;
|
||||
rule_set.centrifugal.boost = 18;
|
||||
rule_set.density_projection.boost = 5;
|
||||
@@ -248,10 +232,11 @@ TEST_CASE(
|
||||
rule_set.error_norm.boost = 17;
|
||||
const quadrature::Policy policy(rule_set);
|
||||
|
||||
const std::array<std::pair<quadrature::Term, int>, 18> cases = {
|
||||
const std::array<std::pair<quadrature::Term, int>, 19> cases = {
|
||||
{{quadrature::Term::gravity_hdiv_mass, 1},
|
||||
{quadrature::Term::gravity_divergence, 2},
|
||||
{quadrature::Term::gravity_source, 3},
|
||||
{quadrature::Term::gravity_force, 19},
|
||||
{quadrature::Term::gravity_boundary, 4},
|
||||
{quadrature::Term::centrifugal, 18},
|
||||
{quadrature::Term::density_projection, 5},
|
||||
@@ -271,8 +256,7 @@ TEST_CASE(
|
||||
|
||||
for (const auto &[term, expected_boost] : cases) {
|
||||
DYNAMIC_SECTION(get_term_name(term)) {
|
||||
const quadrature::Resolution resolution =
|
||||
policy.resolve(make_query(term, 20));
|
||||
const quadrature::Resolution resolution = policy.resolve(make_query(term, 20));
|
||||
CHECK(resolution.boost == expected_boost);
|
||||
CHECK(resolution.order == 20 + expected_boost);
|
||||
}
|
||||
@@ -283,44 +267,26 @@ TEST_CASE(
|
||||
"Quadrature Policy Rejects Invalid Orders",
|
||||
tags::unit &tags::quadrature
|
||||
) {
|
||||
const quadrature::Policy policy(
|
||||
quadrature::make_rule_set(quadrature::Mode::production)
|
||||
);
|
||||
const quadrature::Policy policy(quadrature::make_rule_set(quadrature::Mode::production));
|
||||
|
||||
quadrature::Query negative_component_query = {
|
||||
.term = quadrature::Term::error_norm, .trial_order = -1
|
||||
};
|
||||
REQUIRE_THROWS_AS(
|
||||
policy.resolve(negative_component_query), std::invalid_argument
|
||||
);
|
||||
quadrature::Query negative_component_query = {.term = quadrature::Term::error_norm, .trial_order = -1};
|
||||
REQUIRE_THROWS_AS(policy.resolve(negative_component_query), std::invalid_argument);
|
||||
|
||||
quadrature::Query negative_base_query = {
|
||||
.term = quadrature::Term::error_norm, .base_order = -1
|
||||
};
|
||||
REQUIRE_THROWS_AS(
|
||||
policy.resolve(negative_base_query), std::invalid_argument
|
||||
);
|
||||
quadrature::Query negative_base_query = {.term = quadrature::Term::error_norm, .base_order = -1};
|
||||
REQUIRE_THROWS_AS(policy.resolve(negative_base_query), std::invalid_argument);
|
||||
|
||||
quadrature::RuleSet negative_fixed_rule_set;
|
||||
negative_fixed_rule_set.error_norm.fixed_order = -1;
|
||||
const quadrature::Policy negative_fixed_policy(negative_fixed_rule_set);
|
||||
REQUIRE_THROWS_AS(
|
||||
negative_fixed_policy.resolve(
|
||||
make_query(quadrature::Term::error_norm, 3)
|
||||
),
|
||||
std::invalid_argument
|
||||
negative_fixed_policy.resolve(make_query(quadrature::Term::error_norm, 3)), std::invalid_argument
|
||||
);
|
||||
|
||||
quadrature::RuleSet negative_resolved_rule_set;
|
||||
negative_resolved_rule_set.fallback.boost = -4;
|
||||
const quadrature::Policy negative_resolved_policy(
|
||||
negative_resolved_rule_set
|
||||
);
|
||||
const quadrature::Policy negative_resolved_policy(negative_resolved_rule_set);
|
||||
REQUIRE_THROWS_AS(
|
||||
negative_resolved_policy.resolve(
|
||||
make_query(quadrature::Term::error_norm, 3)
|
||||
),
|
||||
std::invalid_argument
|
||||
negative_resolved_policy.resolve(make_query(quadrature::Term::error_norm, 3)), std::invalid_argument
|
||||
);
|
||||
}
|
||||
|
||||
@@ -328,15 +294,12 @@ TEST_CASE(
|
||||
"MFEM Rule Factory Returns the Resolved Rule",
|
||||
tags::unit &tags::quadrature
|
||||
) {
|
||||
quadrature::RuleSet rule_set =
|
||||
quadrature::make_rule_set(quadrature::Mode::production, 2);
|
||||
rule_set.error_norm.boost = 3;
|
||||
quadrature::RuleSet rule_set = quadrature::make_rule_set(quadrature::Mode::production, 2);
|
||||
rule_set.error_norm.boost = 3;
|
||||
const quadrature::RuleFactory factory{quadrature::Policy(rule_set)};
|
||||
const quadrature::MfemRule selected_rule = factory.get(
|
||||
make_query(quadrature::Term::error_norm, 4), mfem::Geometry::CUBE
|
||||
);
|
||||
const mfem::IntegrationRule &expected_rule =
|
||||
mfem::IntRules.Get(mfem::Geometry::CUBE, 9);
|
||||
const quadrature::MfemRule selected_rule =
|
||||
factory.get(make_query(quadrature::Term::error_norm, 4), mfem::Geometry::CUBE);
|
||||
const mfem::IntegrationRule &expected_rule = mfem::IntRules.Get(mfem::Geometry::CUBE, 9);
|
||||
|
||||
REQUIRE(selected_rule.integration_rule != nullptr);
|
||||
CHECK(selected_rule.resolution.base_order == 4);
|
||||
@@ -351,74 +314,49 @@ TEST_CASE(
|
||||
tags::unit &tags::quadrature
|
||||
) {
|
||||
constexpr int polynomial_degree = 7;
|
||||
const quadrature::RuleFactory factory{quadrature::Policy(
|
||||
quadrature::make_rule_set(quadrature::Mode::production)
|
||||
)};
|
||||
const quadrature::MfemRule selected_rule = factory.get(
|
||||
make_query(quadrature::Term::error_norm, polynomial_degree),
|
||||
mfem::Geometry::CUBE
|
||||
);
|
||||
const quadrature::RuleFactory factory{quadrature::Policy(quadrature::make_rule_set(quadrature::Mode::production))};
|
||||
const quadrature::MfemRule selected_rule =
|
||||
factory.get(make_query(quadrature::Term::error_norm, polynomial_degree), mfem::Geometry::CUBE);
|
||||
double numerical_integral = 0.0;
|
||||
|
||||
for (int i = 0; i < selected_rule.integration_rule->GetNPoints(); ++i) {
|
||||
const mfem::IntegrationPoint &integration_point =
|
||||
selected_rule.integration_rule->IntPoint(i);
|
||||
numerical_integral += integration_point.weight *
|
||||
std::pow(integration_point.x, polynomial_degree) *
|
||||
const mfem::IntegrationPoint &integration_point = selected_rule.integration_rule->IntPoint(i);
|
||||
numerical_integral += integration_point.weight * std::pow(integration_point.x, polynomial_degree) *
|
||||
std::pow(integration_point.y, polynomial_degree) *
|
||||
std::pow(integration_point.z, polynomial_degree);
|
||||
}
|
||||
|
||||
const double one_dimensional_integral =
|
||||
1.0 / static_cast<double>(polynomial_degree + 1);
|
||||
const double analytic_integral = one_dimensional_integral *
|
||||
one_dimensional_integral *
|
||||
one_dimensional_integral;
|
||||
CHECK_THAT(
|
||||
numerical_integral,
|
||||
Catch::Matchers::WithinAbs(analytic_integral, 5.0e-14)
|
||||
);
|
||||
const double one_dimensional_integral = 1.0 / static_cast<double>(polynomial_degree + 1);
|
||||
const double analytic_integral = one_dimensional_integral * one_dimensional_integral * one_dimensional_integral;
|
||||
CHECK_THAT(numerical_integral, Catch::Matchers::WithinAbs(analytic_integral, 5.0e-14));
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Policy Controlled Hdiv Mass Assembly Matches Overintegrated Reference",
|
||||
tags::quadrature &tags::solver &tags::integration
|
||||
) {
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian3D(
|
||||
1, 1, 1, mfem::Element::HEXAHEDRON, 1.0, 1.0, 1.0
|
||||
);
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian3D(1, 1, 1, mfem::Element::HEXAHEDRON, 1.0, 1.0, 1.0);
|
||||
mfem::RT_FECollection rt_collection(2, 3);
|
||||
mfem::FiniteElementSpace rt_space(&mesh, &rt_collection);
|
||||
const mfem::FiniteElement *rt_element = rt_space.GetTypicalFE();
|
||||
mfem::ElementTransformation *transformation =
|
||||
mesh.GetElementTransformation(0);
|
||||
const int base_order =
|
||||
2 * rt_element->GetOrder() + transformation->OrderW();
|
||||
const mfem::FiniteElement *rt_element = rt_space.GetTypicalFE();
|
||||
mfem::ElementTransformation *transformation = mesh.GetElementTransformation(0);
|
||||
const int base_order = 2 * rt_element->GetOrder() + transformation->OrderW();
|
||||
|
||||
const quadrature::RuleFactory production_factory{quadrature::Policy(
|
||||
quadrature::make_rule_set(quadrature::Mode::production)
|
||||
)};
|
||||
const quadrature::MfemRule production_rule = production_factory.get(
|
||||
make_query(quadrature::Term::gravity_hdiv_mass, base_order),
|
||||
rt_element->GetGeomType()
|
||||
);
|
||||
|
||||
quadrature::RuleSet reference_rule_set =
|
||||
quadrature::make_rule_set(quadrature::Mode::production);
|
||||
reference_rule_set.gravity_hdiv_mass.boost = 8;
|
||||
const quadrature::RuleFactory reference_factory{
|
||||
quadrature::Policy(reference_rule_set)
|
||||
const quadrature::RuleFactory production_factory{
|
||||
quadrature::Policy(quadrature::make_rule_set(quadrature::Mode::production))
|
||||
};
|
||||
const quadrature::MfemRule reference_rule = reference_factory.get(
|
||||
make_query(quadrature::Term::gravity_hdiv_mass, base_order),
|
||||
rt_element->GetGeomType()
|
||||
);
|
||||
const quadrature::MfemRule production_rule =
|
||||
production_factory.get(make_query(quadrature::Term::gravity_hdiv_mass, base_order), rt_element->GetGeomType());
|
||||
|
||||
quadrature::RuleSet reference_rule_set = quadrature::make_rule_set(quadrature::Mode::production);
|
||||
reference_rule_set.gravity_hdiv_mass.boost = 8;
|
||||
const quadrature::RuleFactory reference_factory{quadrature::Policy(reference_rule_set)};
|
||||
const quadrature::MfemRule reference_rule =
|
||||
reference_factory.get(make_query(quadrature::Term::gravity_hdiv_mass, base_order), rt_element->GetGeomType());
|
||||
|
||||
mfem::BilinearForm production_mass(&rt_space);
|
||||
auto *production_integrator = new mfem::VectorFEMassIntegrator();
|
||||
production_integrator->SetIntegrationRule(
|
||||
*production_rule.integration_rule
|
||||
);
|
||||
production_integrator->SetIntegrationRule(*production_rule.integration_rule);
|
||||
production_mass.AddDomainIntegrator(production_integrator);
|
||||
production_mass.Assemble();
|
||||
production_mass.Finalize();
|
||||
@@ -442,8 +380,7 @@ TEST_CASE(
|
||||
|
||||
mfem::Vector difference(production_output);
|
||||
difference -= reference_output;
|
||||
const double relative_difference =
|
||||
difference.Norml2() / reference_output.Norml2();
|
||||
const double relative_difference = difference.Norml2() / reference_output.Norml2();
|
||||
INFO("Production quadrature order = " << production_rule.resolution.order);
|
||||
INFO("Reference quadrature order = " << reference_rule.resolution.order);
|
||||
INFO("Relative operator difference = " << relative_difference);
|
||||
@@ -454,28 +391,22 @@ TEST_CASE(
|
||||
"HDiv Mass Helper Resolves the MFEM Baseline",
|
||||
tags::unit &tags::quadrature &tags::solver
|
||||
) {
|
||||
mfem::Mesh mesh =
|
||||
mfem::Mesh::MakeCartesian3D(1, 1, 1, mfem::Element::HEXAHEDRON);
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian3D(1, 1, 1, mfem::Element::HEXAHEDRON);
|
||||
mfem::RT_FECollection rt_collection(2, 3);
|
||||
mfem::FiniteElementSpace rt_space(&mesh, &rt_collection);
|
||||
|
||||
quadrature::RuleSet rule_set =
|
||||
quadrature::make_rule_set(quadrature::Mode::production);
|
||||
quadrature::RuleSet rule_set = quadrature::make_rule_set(quadrature::Mode::production);
|
||||
rule_set.gravity_hdiv_mass.boost = 3;
|
||||
quadrature::RuleFactory factory{quadrature::Policy(std::move(rule_set))};
|
||||
|
||||
const mfem::FiniteElement &element = *rt_space.GetTypicalFE();
|
||||
const mfem::ElementTransformation &transformation =
|
||||
*mesh.GetElementTransformation(0);
|
||||
const mfem::FiniteElement &element = *rt_space.GetTypicalFE();
|
||||
const mfem::ElementTransformation &transformation = *mesh.GetElementTransformation(0);
|
||||
mfem::VectorFEMassIntegrator integrator;
|
||||
|
||||
const quadrature::Resolution resolution =
|
||||
factory.configure_gravity_hdiv_mass(
|
||||
integrator, quadrature::QuadratureRole::discretization, element,
|
||||
transformation
|
||||
);
|
||||
const int expected_base_order =
|
||||
2 * element.GetOrder() + transformation.OrderW();
|
||||
const quadrature::Resolution resolution = factory.configure_gravity_hdiv_mass(
|
||||
integrator, quadrature::QuadratureRole::discretization, element, transformation
|
||||
);
|
||||
const int expected_base_order = 2 * element.GetOrder() + transformation.OrderW();
|
||||
|
||||
CHECK(resolution.base_order == expected_base_order);
|
||||
CHECK(resolution.boost == 3);
|
||||
@@ -486,33 +417,27 @@ TEST_CASE(
|
||||
"Gravity Divergence Helper Resolves Preconditioner Rule",
|
||||
tags::unit &tags::quadrature &tags::solver
|
||||
) {
|
||||
mfem::Mesh mesh =
|
||||
mfem::Mesh::MakeCartesian3D(1, 1, 1, mfem::Element::HEXAHEDRON);
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian3D(1, 1, 1, mfem::Element::HEXAHEDRON);
|
||||
mfem::RT_FECollection rt_collection(2, 3);
|
||||
mfem::L2_FECollection l2_collection(2, 3);
|
||||
mfem::FiniteElementSpace rt_space(&mesh, &rt_collection);
|
||||
mfem::FiniteElementSpace l2_space(&mesh, &l2_collection);
|
||||
|
||||
quadrature::RuleSet rule_set =
|
||||
quadrature::make_rule_set(quadrature::Mode::production);
|
||||
quadrature::RuleSet rule_set = quadrature::make_rule_set(quadrature::Mode::production);
|
||||
rule_set.gravity_divergence.boost = 2;
|
||||
rule_set.roles.preconditioner.boost = 3;
|
||||
quadrature::RuleFactory factory{quadrature::Policy(std::move(rule_set))};
|
||||
|
||||
const mfem::FiniteElement &trial_element = *rt_space.GetTypicalFE();
|
||||
const mfem::FiniteElement &test_element = *l2_space.GetTypicalFE();
|
||||
const mfem::ElementTransformation &transformation =
|
||||
*mesh.GetElementTransformation(0);
|
||||
const mfem::FiniteElement &trial_element = *rt_space.GetTypicalFE();
|
||||
const mfem::FiniteElement &test_element = *l2_space.GetTypicalFE();
|
||||
const mfem::ElementTransformation &transformation = *mesh.GetElementTransformation(0);
|
||||
mfem::VectorFEDivergenceIntegrator integrator;
|
||||
|
||||
const quadrature::Resolution resolution =
|
||||
factory.configure_gravity_divergence(
|
||||
integrator, quadrature::QuadratureRole::preconditioner,
|
||||
trial_element, test_element, transformation
|
||||
);
|
||||
const int expected_base_order = std::max(0, trial_element.GetOrder() - 1) +
|
||||
test_element.GetOrder() +
|
||||
transformation.OrderW();
|
||||
const quadrature::Resolution resolution = factory.configure_gravity_divergence(
|
||||
integrator, quadrature::QuadratureRole::preconditioner, trial_element, test_element, transformation
|
||||
);
|
||||
const int expected_base_order =
|
||||
std::max(0, trial_element.GetOrder() - 1) + test_element.GetOrder() + transformation.OrderW();
|
||||
|
||||
CHECK(resolution.base_order == expected_base_order);
|
||||
CHECK(resolution.boost == 5);
|
||||
@@ -529,147 +454,93 @@ TEST_CASE(
|
||||
CHECK(mean_field::field::Displacement::Vector::familyOrder == 3);
|
||||
CHECK(mean_field::field::Enthalpy::Scalar::familyOrder == 3);
|
||||
|
||||
CHECK((std::same_as<
|
||||
mean_field::field::Density::Scalar::Space, mean_field::field::L2>));
|
||||
CHECK((
|
||||
std::same_as<
|
||||
mean_field::field::Gravity::Potential::Space, mean_field::field::L2>
|
||||
));
|
||||
CHECK((std::same_as<
|
||||
mean_field::field::Gravity::Flux::Space, mean_field::field::RT>));
|
||||
CHECK((std::same_as<
|
||||
mean_field::field::Displacement::Vector::Space,
|
||||
mean_field::field::H1>));
|
||||
CHECK((std::same_as<
|
||||
mean_field::field::Enthalpy::Scalar::Space, mean_field::field::H1>));
|
||||
CHECK((std::same_as<mean_field::field::Density::Scalar::Space, mean_field::field::L2>));
|
||||
CHECK((std::same_as<mean_field::field::Gravity::Potential::Space, mean_field::field::L2>));
|
||||
CHECK((std::same_as<mean_field::field::Gravity::Flux::Space, mean_field::field::RT>));
|
||||
CHECK((std::same_as<mean_field::field::Displacement::Vector::Space, mean_field::field::H1>));
|
||||
CHECK((std::same_as<mean_field::field::Enthalpy::Scalar::Space, mean_field::field::H1>));
|
||||
|
||||
CHECK(mean_field::field::Density::Scalar::rankValue == 0);
|
||||
CHECK(mean_field::field::Gravity::Potential::rankValue == 0);
|
||||
CHECK(mean_field::field::Gravity::Flux::rankValue == 1);
|
||||
CHECK(mean_field::field::Displacement::Vector::rankValue == 1);
|
||||
CHECK(mean_field::field::Enthalpy::Scalar::rankValue == 0);
|
||||
CHECK(
|
||||
mean_field::field::Gravity::Flux::familyOrder ==
|
||||
mean_field::field::Gravity::Potential::familyOrder
|
||||
);
|
||||
CHECK(mean_field::field::Gravity::Flux::familyOrder == mean_field::field::Gravity::Potential::familyOrder);
|
||||
|
||||
CHECK(
|
||||
std::is_empty_v<mean_field::field::Field<mean_field::field::Gravity>>
|
||||
);
|
||||
CHECK(
|
||||
std::is_empty_v<
|
||||
mean_field::field::Field<mean_field::field::Displacement>>
|
||||
);
|
||||
CHECK(
|
||||
std::is_empty_v<mean_field::field::Field<mean_field::field::Density>>
|
||||
);
|
||||
CHECK(
|
||||
std::is_empty_v<mean_field::field::Field<mean_field::field::Enthalpy>>
|
||||
);
|
||||
CHECK(std::is_empty_v<mean_field::field::Field<mean_field::field::Gravity>>);
|
||||
CHECK(std::is_empty_v<mean_field::field::Field<mean_field::field::Displacement>>);
|
||||
CHECK(std::is_empty_v<mean_field::field::Field<mean_field::field::Density>>);
|
||||
CHECK(std::is_empty_v<mean_field::field::Field<mean_field::field::Enthalpy>>);
|
||||
|
||||
STATIC_CHECK(field::RegisteredQuantity<field::BarotropicConstant::Scalar>);
|
||||
STATIC_CHECK(
|
||||
field::GlobalScalarQuantity<field::BarotropicConstant::Scalar>
|
||||
);
|
||||
STATIC_CHECK(field::GlobalScalarQuantity<field::BarotropicConstant::Scalar>);
|
||||
STATIC_CHECK_FALSE(field::FieldQuantity<field::BarotropicConstant::Scalar>);
|
||||
|
||||
STATIC_CHECK(
|
||||
field::BarotropicConstant::Scalar::storageKind ==
|
||||
field::StorageKind::global_scalar
|
||||
);
|
||||
STATIC_CHECK(field::BarotropicConstant::Scalar::storageKind == field::StorageKind::global_scalar);
|
||||
STATIC_CHECK(field::BarotropicConstant::Scalar::staticBlockSize == 1);
|
||||
|
||||
STATIC_CHECK(
|
||||
field::Enthalpy::Scalar::storageKind ==
|
||||
field::StorageKind::finite_element
|
||||
);
|
||||
STATIC_CHECK(
|
||||
field::Enthalpy::Scalar::staticBlockSize == field::dynamicBlockSize
|
||||
);
|
||||
STATIC_CHECK(field::Enthalpy::Scalar::storageKind == field::StorageKind::finite_element);
|
||||
STATIC_CHECK(field::Enthalpy::Scalar::staticBlockSize == field::dynamicBlockSize);
|
||||
|
||||
STATIC_CHECK_FALSE(
|
||||
CanMakeFec<
|
||||
field::Field<field::BarotropicConstant>,
|
||||
field::BarotropicConstant::Scalar>
|
||||
);
|
||||
STATIC_CHECK_FALSE(CanMakeFec<field::Field<field::BarotropicConstant>, field::BarotropicConstant::Scalar>);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Forms Produce Typed Quadrature Queries",
|
||||
tags::unit &tags::quadrature
|
||||
) {
|
||||
using GravityField = field::Field<field::Gravity>;
|
||||
using DensityField = field::Field<field::Density>;
|
||||
using EnthalpyField = field::Field<field::Enthalpy>;
|
||||
using GravityField = field::Field<field::Gravity>;
|
||||
using DensityField = field::Field<field::Density>;
|
||||
using EnthalpyField = field::Field<field::Enthalpy>;
|
||||
|
||||
constexpr quadrature::Query hdiv_query =
|
||||
GravityField::make_query<field::Gravity::Form::HDivMass>(
|
||||
quadrature::QuadratureRole::discretization, 2, {},
|
||||
utils::DOMAINS::ALL, quadrature::MappingKind::general
|
||||
);
|
||||
constexpr quadrature::Query divergence_query =
|
||||
GravityField::make_query<field::Gravity::Form::DivergenceCoupling>(
|
||||
quadrature::QuadratureRole::preconditioner, 2
|
||||
);
|
||||
constexpr quadrature::Query source_query =
|
||||
GravityField::make_query<field::Gravity::Form::SourceProjection>(
|
||||
quadrature::QuadratureRole::projection, 2, {},
|
||||
utils::DOMAINS::STELLAR
|
||||
);
|
||||
constexpr quadrature::Query center_of_mass_query =
|
||||
DensityField::make_query<field::Density::Form::CenterOfMass>(
|
||||
quadrature::QuadratureRole::diagnostic, 2, std::array<int, 1>{1},
|
||||
utils::DOMAINS::STELLAR
|
||||
);
|
||||
constexpr quadrature::Query eos_closure_query =
|
||||
EnthalpyField::make_query<field::Enthalpy::Form::EosClosureSource>(
|
||||
quadrature::QuadratureRole::discretization, 2,
|
||||
std::array<int, 1>{6}, utils::DOMAINS::STELLAR,
|
||||
quadrature::MappingKind::general
|
||||
);
|
||||
constexpr quadrature::Query hdiv_query = GravityField::make_query<field::Gravity::Form::HDivMass>(
|
||||
quadrature::QuadratureRole::discretization, 2, {}, utils::DOMAINS::ALL, quadrature::MappingKind::general
|
||||
);
|
||||
constexpr quadrature::Query divergence_query = GravityField::make_query<field::Gravity::Form::DivergenceCoupling>(
|
||||
quadrature::QuadratureRole::preconditioner, 2
|
||||
);
|
||||
constexpr quadrature::Query source_query = GravityField::make_query<field::Gravity::Form::SourceProjection>(
|
||||
quadrature::QuadratureRole::projection, 2, {}, utils::DOMAINS::STELLAR
|
||||
);
|
||||
constexpr quadrature::Query center_of_mass_query = DensityField::make_query<field::Density::Form::CenterOfMass>(
|
||||
quadrature::QuadratureRole::diagnostic, 2, std::array<int, 1>{1}, utils::DOMAINS::STELLAR
|
||||
);
|
||||
constexpr quadrature::Query eos_closure_query = EnthalpyField::make_query<field::Enthalpy::Form::EosClosureSource>(
|
||||
quadrature::QuadratureRole::discretization, 2, std::array<int, 1>{6}, utils::DOMAINS::STELLAR,
|
||||
quadrature::MappingKind::general
|
||||
);
|
||||
constexpr quadrature::Query equilibrium_gravity_query =
|
||||
EnthalpyField::make_query<field::Enthalpy::Form::EquilibriumGravity>(
|
||||
quadrature::QuadratureRole::discretization, 2, {},
|
||||
utils::DOMAINS::STELLAR, quadrature::MappingKind::general
|
||||
quadrature::QuadratureRole::discretization, 2, {}, utils::DOMAINS::STELLAR, quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
constexpr quadrature::Query equilibrium_constant_query =
|
||||
EnthalpyField::make_query<field::Enthalpy::Form::EquilibriumConstant>(
|
||||
quadrature::QuadratureRole::discretization, 2, {},
|
||||
utils::DOMAINS::STELLAR, quadrature::MappingKind::general
|
||||
);
|
||||
constexpr quadrature::Query rotation_query =
|
||||
EnthalpyField::make_query<field::Enthalpy::Form::EquilibriumRotation>(
|
||||
quadrature::QuadratureRole::discretization, 2,
|
||||
std::array<int, 1>{2}, utils::DOMAINS::STELLAR,
|
||||
quadrature::MappingKind::general
|
||||
quadrature::QuadratureRole::discretization, 2, {}, utils::DOMAINS::STELLAR, quadrature::MappingKind::general
|
||||
);
|
||||
constexpr quadrature::Query rotation_query = EnthalpyField::make_query<field::Enthalpy::Form::EquilibriumRotation>(
|
||||
quadrature::QuadratureRole::discretization, 2, std::array<int, 1>{2}, utils::DOMAINS::STELLAR,
|
||||
quadrature::MappingKind::general
|
||||
);
|
||||
constexpr quadrature::Query isobaric_surface_query =
|
||||
EnthalpyField::make_query<field::Enthalpy::Form::IsobaricSurface>(
|
||||
quadrature::QuadratureRole::discretization, 2, {},
|
||||
utils::DOMAINS::STELLAR, quadrature::MappingKind::general
|
||||
quadrature::QuadratureRole::discretization, 2, {}, utils::DOMAINS::STELLAR, quadrature::MappingKind::general
|
||||
);
|
||||
constexpr quadrature::Query mesh_extension_query =
|
||||
field::Field<field::Displacement>::make_query<
|
||||
field::Displacement::Form::MeshExtension>(
|
||||
quadrature::QuadratureRole::discretization, 2, {},
|
||||
utils::DOMAINS::ALL, quadrature::MappingKind::general
|
||||
field::Field<field::Displacement>::make_query<field::Displacement::Form::MeshExtension>(
|
||||
quadrature::QuadratureRole::discretization, 2, {}, utils::DOMAINS::ALL, quadrature::MappingKind::general
|
||||
);
|
||||
constexpr quadrature::Query mass_normalization_query =
|
||||
DensityField::make_query<field::Density::Form::MassNormalization>(
|
||||
quadrature::QuadratureRole::discretization, 2, {},
|
||||
utils::DOMAINS::STELLAR, quadrature::MappingKind::general
|
||||
quadrature::QuadratureRole::discretization, 2, {}, utils::DOMAINS::STELLAR, quadrature::MappingKind::general
|
||||
);
|
||||
constexpr quadrature::Query pressure_integral_query =
|
||||
EnthalpyField::make_query<field::Enthalpy::Form::PressureIntegral>(
|
||||
quadrature::QuadratureRole::diagnostic, 2, std::array<int, 1>{9},
|
||||
utils::DOMAINS::STELLAR, quadrature::MappingKind::general
|
||||
quadrature::QuadratureRole::diagnostic, 2, std::array<int, 1>{9}, utils::DOMAINS::STELLAR,
|
||||
quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
STATIC_CHECK(
|
||||
mean_field::field::Gravity::Form::SourceProjection::dynamicOrderCount ==
|
||||
0
|
||||
);
|
||||
STATIC_CHECK(mean_field::field::Gravity::Form::SourceProjection::dynamicOrderCount == 0);
|
||||
STATIC_CHECK(hdiv_query.base_order.has_value());
|
||||
STATIC_CHECK(*hdiv_query.base_order == 8);
|
||||
STATIC_CHECK(*divergence_query.base_order == 6);
|
||||
@@ -684,47 +555,20 @@ TEST_CASE(
|
||||
STATIC_CHECK(*pressure_integral_query.base_order == 14);
|
||||
STATIC_CHECK(*equilibrium_constant_query.base_order == 5);
|
||||
|
||||
CHECK(
|
||||
equilibrium_constant_query.term ==
|
||||
quadrature::Term::hydrostatic_equilibrium
|
||||
);
|
||||
CHECK(equilibrium_constant_query.term == quadrature::Term::hydrostatic_equilibrium);
|
||||
CHECK(hdiv_query.term == mean_field::quadrature::Term::gravity_hdiv_mass);
|
||||
CHECK(
|
||||
hdiv_query.role ==
|
||||
mean_field::quadrature::QuadratureRole::discretization
|
||||
);
|
||||
CHECK(hdiv_query.role == mean_field::quadrature::QuadratureRole::discretization);
|
||||
CHECK(hdiv_query.domain == mean_field::utils::DOMAINS::ALL);
|
||||
CHECK(hdiv_query.mapping == mean_field::quadrature::MappingKind::general);
|
||||
CHECK(source_query.term == mean_field::quadrature::Term::gravity_source);
|
||||
CHECK(
|
||||
center_of_mass_query.term ==
|
||||
mean_field::quadrature::Term::center_of_mass
|
||||
);
|
||||
CHECK(center_of_mass_query.term == mean_field::quadrature::Term::center_of_mass);
|
||||
CHECK(eos_closure_query.term == mean_field::quadrature::Term::eos_closure);
|
||||
CHECK(
|
||||
equilibrium_gravity_query.term ==
|
||||
mean_field::quadrature::Term::hydrostatic_equilibrium
|
||||
);
|
||||
CHECK(
|
||||
rotation_query.term ==
|
||||
mean_field::quadrature::Term::hydrostatic_equilibrium
|
||||
);
|
||||
CHECK(
|
||||
isobaric_surface_query.term ==
|
||||
mean_field::quadrature::Term::isobaric_surface
|
||||
);
|
||||
CHECK(
|
||||
mesh_extension_query.term ==
|
||||
mean_field::quadrature::Term::mesh_extension
|
||||
);
|
||||
CHECK(
|
||||
mass_normalization_query.term ==
|
||||
mean_field::quadrature::Term::mass_normalization
|
||||
);
|
||||
CHECK(
|
||||
pressure_integral_query.term ==
|
||||
mean_field::quadrature::Term::pressure_integral
|
||||
);
|
||||
CHECK(equilibrium_gravity_query.term == mean_field::quadrature::Term::hydrostatic_equilibrium);
|
||||
CHECK(rotation_query.term == mean_field::quadrature::Term::hydrostatic_equilibrium);
|
||||
CHECK(isobaric_surface_query.term == mean_field::quadrature::Term::isobaric_surface);
|
||||
CHECK(mesh_extension_query.term == mean_field::quadrature::Term::mesh_extension);
|
||||
CHECK(mass_normalization_query.term == mean_field::quadrature::Term::mass_normalization);
|
||||
CHECK(pressure_integral_query.term == mean_field::quadrature::Term::pressure_integral);
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
(GravityField::make_query<mean_field::field::Gravity::Form::HDivMass>(
|
||||
@@ -733,10 +577,8 @@ TEST_CASE(
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
(DensityField::make_query<
|
||||
mean_field::field::Density::Form::CenterOfMass>(
|
||||
mean_field::quadrature::QuadratureRole::diagnostic, 2,
|
||||
std::array<int, 1>{-1}
|
||||
(DensityField::make_query<mean_field::field::Density::Form::CenterOfMass>(
|
||||
mean_field::quadrature::QuadratureRole::diagnostic, 2, std::array<int, 1>{-1}
|
||||
)),
|
||||
std::invalid_argument
|
||||
);
|
||||
@@ -755,37 +597,19 @@ TEST_CASE(
|
||||
DensityField::make_fec<field::Density::Scalar>(3);
|
||||
std::unique_ptr<mfem::FiniteElementCollection> potential_collection =
|
||||
GravityField::make_fec<field::Gravity::Potential>(3);
|
||||
std::unique_ptr<mfem::FiniteElementCollection> flux_collection =
|
||||
GravityField::make_fec<field::Gravity::Flux>(3);
|
||||
std::unique_ptr<mfem::FiniteElementCollection> flux_collection = GravityField::make_fec<field::Gravity::Flux>(3);
|
||||
std::unique_ptr<mfem::FiniteElementCollection> displacement_collection =
|
||||
DisplacementField::make_fec<field::Displacement::Vector>(3);
|
||||
std::unique_ptr<mfem::FiniteElementCollection> enthalpy_collection =
|
||||
EnthalpyField::make_fec<field::Enthalpy::Scalar>(3);
|
||||
|
||||
CHECK(
|
||||
dynamic_cast<mfem::L2_FECollection *>(density_collection.get()) !=
|
||||
nullptr
|
||||
);
|
||||
CHECK(
|
||||
dynamic_cast<mfem::L2_FECollection *>(potential_collection.get()) !=
|
||||
nullptr
|
||||
);
|
||||
CHECK(
|
||||
dynamic_cast<mfem::RT_FECollection *>(flux_collection.get()) != nullptr
|
||||
);
|
||||
CHECK(
|
||||
dynamic_cast<mfem::H1_FECollection *>(displacement_collection.get()) !=
|
||||
nullptr
|
||||
);
|
||||
CHECK(
|
||||
dynamic_cast<mfem::H1_FECollection *>(enthalpy_collection.get()) !=
|
||||
nullptr
|
||||
);
|
||||
CHECK(dynamic_cast<mfem::L2_FECollection *>(density_collection.get()) != nullptr);
|
||||
CHECK(dynamic_cast<mfem::L2_FECollection *>(potential_collection.get()) != nullptr);
|
||||
CHECK(dynamic_cast<mfem::RT_FECollection *>(flux_collection.get()) != nullptr);
|
||||
CHECK(dynamic_cast<mfem::H1_FECollection *>(displacement_collection.get()) != nullptr);
|
||||
CHECK(dynamic_cast<mfem::H1_FECollection *>(enthalpy_collection.get()) != nullptr);
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
(DensityField::make_fec<mean_field::field::Density::Scalar>(0)),
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS((DensityField::make_fec<mean_field::field::Density::Scalar>(0)), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
@@ -806,26 +630,11 @@ TEST_CASE(
|
||||
CHECK(fem.densityFes.get() != fem.gravityPotentialFes.get());
|
||||
CHECK(fem.densityFec.get() != fem.gravityPotentialFec.get());
|
||||
|
||||
CHECK(
|
||||
fem.densityFes->GetMaxElementOrder() ==
|
||||
mean_field::field::Density::Scalar::familyOrder
|
||||
);
|
||||
CHECK(
|
||||
fem.gravityPotentialFes->GetMaxElementOrder() ==
|
||||
mean_field::field::Gravity::Potential::familyOrder
|
||||
);
|
||||
CHECK(
|
||||
fem.gravityFluxFes->GetMaxElementOrder() ==
|
||||
mean_field::field::Gravity::Flux::familyOrder + 1
|
||||
);
|
||||
CHECK(
|
||||
fem.displacementFes->GetMaxElementOrder() ==
|
||||
mean_field::field::Displacement::Vector::familyOrder
|
||||
);
|
||||
CHECK(
|
||||
fem.enthalpyFes->GetMaxElementOrder() ==
|
||||
mean_field::field::Enthalpy::Scalar::familyOrder
|
||||
);
|
||||
CHECK(fem.densityFes->GetMaxElementOrder() == mean_field::field::Density::Scalar::familyOrder);
|
||||
CHECK(fem.gravityPotentialFes->GetMaxElementOrder() == mean_field::field::Gravity::Potential::familyOrder);
|
||||
CHECK(fem.gravityFluxFes->GetMaxElementOrder() == mean_field::field::Gravity::Flux::familyOrder + 1);
|
||||
CHECK(fem.displacementFes->GetMaxElementOrder() == mean_field::field::Displacement::Vector::familyOrder);
|
||||
CHECK(fem.enthalpyFes->GetMaxElementOrder() == mean_field::field::Enthalpy::Scalar::familyOrder);
|
||||
|
||||
CHECK(fem.densityFes->GetVDim() == 1);
|
||||
CHECK(fem.gravityPotentialFes->GetVDim() == 1);
|
||||
@@ -837,22 +646,14 @@ TEST_CASE(
|
||||
REQUIRE(fem.blockTrueOffsets.Size() == 3);
|
||||
CHECK(fem.blockTrueOffsets[0] == 0);
|
||||
CHECK(fem.blockTrueOffsets[1] == fem.displacementFes->GetTrueVSize());
|
||||
CHECK(
|
||||
fem.blockTrueOffsets[2] ==
|
||||
fem.displacementFes->GetTrueVSize() + fem.densityFes->GetTrueVSize()
|
||||
);
|
||||
CHECK(fem.blockTrueOffsets[2] == fem.displacementFes->GetTrueVSize() + fem.densityFes->GetTrueVSize());
|
||||
|
||||
REQUIRE(fem.gravityBlockTrueOffsets.Size() == 3);
|
||||
CHECK(fem.gravityBlockTrueOffsets[0] == 0);
|
||||
CHECK(fem.gravityBlockTrueOffsets[1] == fem.gravityFluxFes->GetTrueVSize());
|
||||
CHECK(
|
||||
fem.gravityBlockTrueOffsets[2] ==
|
||||
fem.gravityFluxFes->GetTrueVSize() +
|
||||
fem.gravityPotentialFes->GetTrueVSize()
|
||||
fem.gravityBlockTrueOffsets[2] == fem.gravityFluxFes->GetTrueVSize() + fem.gravityPotentialFes->GetTrueVSize()
|
||||
);
|
||||
|
||||
CHECK(
|
||||
fem.gravityContext.source_form->Height() ==
|
||||
fem.gravityPotentialFes->GetTrueVSize()
|
||||
);
|
||||
CHECK(fem.gravityContext.source_form->Height() == fem.gravityPotentialFes->GetTrueVSize());
|
||||
}
|
||||
76
tests/surface/isobaric.cpp
Normal file
76
tests/surface/isobaric.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
TEST_CASE(
|
||||
"Isobaric Surface Resolves Zero Pressure To Zero Enthalpy",
|
||||
tags::barotrope &tags::unit &tags::surface
|
||||
) {
|
||||
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
||||
|
||||
const mean_field::surface::Isobaric surface;
|
||||
|
||||
const mean_field::surface::ResolvedSurfaceCondition resolved = surface.resolve(equationOfState);
|
||||
|
||||
CHECK(surface.targetPressure() == 0.0);
|
||||
CHECK(resolved.targetEnthalpy == 0.0);
|
||||
CHECK(resolved.residual(0.0) == 0.0);
|
||||
CHECK(resolved.residual(0.37) == 0.37);
|
||||
CHECK(resolved.jacobianAction(-0.19) == -0.19);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Isobaric Surface Resolves Positive Pressure Through The EOS",
|
||||
tags::barotrope &tags::unit &tags::surface
|
||||
) {
|
||||
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
||||
|
||||
constexpr double targetPressure = 0.03125;
|
||||
|
||||
const mean_field::surface::Isobaric surface(targetPressure);
|
||||
|
||||
const mean_field::surface::ResolvedSurfaceCondition resolved = surface.resolve(equationOfState);
|
||||
|
||||
const double recoveredPressure = equationOfState.pressure_from_enthalpy(resolved.targetEnthalpy);
|
||||
|
||||
INFO("Resolved surface enthalpy = " << resolved.targetEnthalpy);
|
||||
INFO("Recovered surface pressure = " << recoveredPressure);
|
||||
|
||||
CHECK(resolved.targetEnthalpy > 0.0);
|
||||
CHECK(std::abs(recoveredPressure - targetPressure) < 64.0 * std::numeric_limits<double>::epsilon());
|
||||
CHECK(resolved.residual(resolved.targetEnthalpy) == 0.0);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Isobaric Surface Rejects Invalid Pressure Targets",
|
||||
tags::barotrope &tags::unit &tags::surface
|
||||
) {
|
||||
CHECK_THROWS_AS(mean_field::surface::Isobaric(-1.0), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS(mean_field::surface::Isobaric(std::numeric_limits<double>::infinity()), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS(mean_field::surface::Isobaric(std::numeric_limits<double>::quiet_NaN()), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Surface Base Dispatch Preserves The Isobaric Prescription",
|
||||
tags::barotrope &tags::unit &tags::surface
|
||||
) {
|
||||
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
||||
|
||||
const mean_field::surface::Isobaric isobaric(0.02);
|
||||
|
||||
const mean_field::surface::SurfaceBase &surface = isobaric;
|
||||
|
||||
surface.validate(equationOfState);
|
||||
|
||||
const mean_field::surface::ResolvedSurfaceCondition resolved = surface.resolve(equationOfState);
|
||||
|
||||
CHECK(resolved.targetEnthalpy > 0.0);
|
||||
CHECK(resolved.residual(resolved.targetEnthalpy) == 0.0);
|
||||
}
|
||||
@@ -32,8 +32,7 @@ template <std::size_t N> struct Tag {
|
||||
return Catch::StringRef(chars.data(), N - 1);
|
||||
}
|
||||
|
||||
template <std::size_t M>
|
||||
consteval Tag<N + M - 1> operator&(const Tag<M> &other) const {
|
||||
template <std::size_t M> consteval Tag<N + M - 1> operator&(const Tag<M> &other) const {
|
||||
std::array<char, N + M - 1> res{};
|
||||
std::ranges::copy(chars.begin(), chars.end() - 1, res.begin());
|
||||
std::ranges::copy(other.chars, res.begin() + (N - 1));
|
||||
@@ -95,8 +94,7 @@ export namespace gravity_prepared_test_utils {
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
const double index = static_cast<double>(i + 1);
|
||||
vector(i) = std::sin(0.37 * index + phase) +
|
||||
0.31 * std::cos(0.19 * index - 0.5 * phase);
|
||||
vector(i) = std::sin(0.37 * index + phase) + 0.31 * std::cos(0.19 * index - 0.5 * phase);
|
||||
}
|
||||
|
||||
return vector;
|
||||
@@ -108,20 +106,14 @@ export namespace gravity_prepared_test_utils {
|
||||
) {
|
||||
mfem::ParGridFunction displacement(f.displacementFes.get());
|
||||
|
||||
auto displacement_function =
|
||||
[scale](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value(0) = scale * (0.04 * position(0) +
|
||||
0.01 * position(1) * position(2));
|
||||
value(1) = scale * (-0.03 * position(1) +
|
||||
0.008 * position(0) * position(2));
|
||||
value(2) = scale * (0.02 * position(2) -
|
||||
0.006 * position(0) * position(1));
|
||||
};
|
||||
auto displacement_function = [scale](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value(0) = scale * (0.04 * position(0) + 0.01 * position(1) * position(2));
|
||||
value(1) = scale * (-0.03 * position(1) + 0.008 * position(0) * position(2));
|
||||
value(2) = scale * (0.02 * position(2) - 0.006 * position(0) * position(1));
|
||||
};
|
||||
|
||||
mfem::VectorFunctionCoefficient coefficient(
|
||||
f.mesh->Dimension(), displacement_function
|
||||
);
|
||||
mfem::VectorFunctionCoefficient coefficient(f.mesh->Dimension(), displacement_function);
|
||||
displacement.ProjectCoefficient(coefficient);
|
||||
|
||||
mfem::Vector displacement_true;
|
||||
@@ -134,10 +126,9 @@ export namespace gravity_prepared_test_utils {
|
||||
const bool stellar
|
||||
) {
|
||||
mfem::Vector attribute_values(f.mesh->attributes.Max());
|
||||
attribute_values = 0.0;
|
||||
attribute_values = 0.0;
|
||||
|
||||
const int vacuum_attribute =
|
||||
f.domainMapperStateless->GetVacuumElementAttribute();
|
||||
const int vacuum_attribute = f.domainMapperStateless->GetVacuumElementAttribute();
|
||||
|
||||
for (int i = 0; i < f.mesh->attributes.Size(); ++i) {
|
||||
const int attribute = f.mesh->attributes[i];
|
||||
@@ -163,10 +154,7 @@ export namespace gravity_prepared_test_utils {
|
||||
const mfem::Vector &second,
|
||||
const double second_scale
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
first.Size() == second.Size(),
|
||||
"Cannot combine vectors with different sizes."
|
||||
);
|
||||
MFEM_VERIFY(first.Size() == second.Size(), "Cannot combine vectors with different sizes.");
|
||||
|
||||
mfem::Vector combination(first);
|
||||
combination *= first_scale;
|
||||
@@ -180,10 +168,7 @@ export namespace gravity_prepared_test_utils {
|
||||
) {
|
||||
const double local_norm_squared = vector * vector;
|
||||
double global_norm_squared = 0.0;
|
||||
MPI_Allreduce(
|
||||
&local_norm_squared, &global_norm_squared, 1, MPI_DOUBLE, MPI_SUM,
|
||||
communicator
|
||||
);
|
||||
MPI_Allreduce(&local_norm_squared, &global_norm_squared, 1, MPI_DOUBLE, MPI_SUM, communicator);
|
||||
return std::sqrt(global_norm_squared);
|
||||
}
|
||||
|
||||
@@ -192,16 +177,11 @@ export namespace gravity_prepared_test_utils {
|
||||
const mfem::Vector &second,
|
||||
MPI_Comm communicator
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
first.Size() == second.Size(),
|
||||
"Cannot take the dot product of vectors with different sizes."
|
||||
);
|
||||
MFEM_VERIFY(first.Size() == second.Size(), "Cannot take the dot product of vectors with different sizes.");
|
||||
|
||||
const double local_dot = first * second;
|
||||
double global_dot = 0.0;
|
||||
MPI_Allreduce(
|
||||
&local_dot, &global_dot, 1, MPI_DOUBLE, MPI_SUM, communicator
|
||||
);
|
||||
MPI_Allreduce(&local_dot, &global_dot, 1, MPI_DOUBLE, MPI_SUM, communicator);
|
||||
return global_dot;
|
||||
}
|
||||
|
||||
@@ -210,90 +190,76 @@ export namespace gravity_prepared_test_utils {
|
||||
const mfem::Vector &reference,
|
||||
MPI_Comm communicator
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
computed.Size() == reference.Size(),
|
||||
"Cannot compare vectors with different sizes."
|
||||
);
|
||||
MFEM_VERIFY(computed.Size() == reference.Size(), "Cannot compare vectors with different sizes.");
|
||||
|
||||
mfem::Vector difference(computed);
|
||||
difference -= reference;
|
||||
|
||||
return global_norm(difference, communicator) /
|
||||
std::max(
|
||||
global_norm(reference, communicator),
|
||||
std::numeric_limits<double>::epsilon()
|
||||
);
|
||||
std::max(global_norm(reference, communicator), std::numeric_limits<double>::epsilon());
|
||||
}
|
||||
|
||||
inline double relative_scalar_error(
|
||||
const double computed,
|
||||
const double reference
|
||||
) {
|
||||
return std::abs(computed - reference) /
|
||||
std::max(
|
||||
std::abs(reference), std::numeric_limits<double>::epsilon()
|
||||
);
|
||||
return std::abs(computed - reference) / std::max(std::abs(reference), std::numeric_limits<double>::epsilon());
|
||||
}
|
||||
} // namespace gravity_prepared_test_utils
|
||||
|
||||
export namespace tags {
|
||||
inline constexpr auto geometry = make_tag("geometry");
|
||||
inline constexpr auto physics = make_tag("physics");
|
||||
inline constexpr auto unit = make_tag("unit");
|
||||
inline constexpr auto mesh = make_tag("mesh");
|
||||
inline constexpr auto integration = make_tag("integration");
|
||||
inline constexpr auto solver = make_tag("solver");
|
||||
inline constexpr auto integrator = make_tag("integrator");
|
||||
inline constexpr auto mapping = make_tag("mapping");
|
||||
inline constexpr auto utils = make_tag("utils");
|
||||
inline constexpr auto mfem_operators = make_tag("operators");
|
||||
inline constexpr auto initialization = make_tag("initialization");
|
||||
inline constexpr auto accuracy = make_tag("accuracy");
|
||||
inline constexpr auto closure = make_tag("closure");
|
||||
inline constexpr auto kernels = make_tag("kernels");
|
||||
inline constexpr auto geometry = make_tag("geometry");
|
||||
inline constexpr auto physics = make_tag("physics");
|
||||
inline constexpr auto unit = make_tag("unit");
|
||||
inline constexpr auto mesh = make_tag("mesh");
|
||||
inline constexpr auto integration = make_tag("integration");
|
||||
inline constexpr auto solver = make_tag("solver");
|
||||
inline constexpr auto integrator = make_tag("integrator");
|
||||
inline constexpr auto mapping = make_tag("mapping");
|
||||
inline constexpr auto utils = make_tag("utils");
|
||||
inline constexpr auto mfem_operators = make_tag("operators");
|
||||
inline constexpr auto initialization = make_tag("initialization");
|
||||
inline constexpr auto accuracy = make_tag("accuracy");
|
||||
inline constexpr auto closure = make_tag("closure");
|
||||
inline constexpr auto kernels = make_tag("kernels");
|
||||
inline constexpr auto surface = make_tag("surface");
|
||||
inline constexpr auto model = make_tag("model");
|
||||
|
||||
inline constexpr auto legacy_comparison = make_tag("legacy_comparison");
|
||||
inline constexpr auto pressure = sub_tag(physics, "pressure");
|
||||
inline constexpr auto field = sub_tag(mesh & physics, "field");
|
||||
|
||||
inline constexpr auto hydro = sub_tag(physics, "hydro");
|
||||
inline constexpr auto jacobian = sub_tag(integration & physics, "jacobian");
|
||||
inline constexpr auto residuals =
|
||||
sub_tag(integration & physics, "residuals");
|
||||
inline constexpr auto volume = sub_tag(mesh & geometry, "volume");
|
||||
inline constexpr auto quadrature =
|
||||
sub_tag(mesh & geometry & solver, "quadrature");
|
||||
inline constexpr auto convergence = sub_tag(solver, "convergence");
|
||||
inline constexpr auto transformations =
|
||||
sub_tag(mesh & geometry, "transformations");
|
||||
inline constexpr auto legacy_comparison = make_tag("legacy_comparison");
|
||||
inline constexpr auto pressure = sub_tag(physics, "pressure");
|
||||
|
||||
inline constexpr auto h_refinement =
|
||||
sub_tag(mesh & convergence, "h_refinement");
|
||||
inline constexpr auto p_refinement =
|
||||
sub_tag(mesh & convergence, "p_refinement");
|
||||
inline constexpr auto hydro = sub_tag(physics, "hydro");
|
||||
inline constexpr auto jacobian = sub_tag(integration & physics, "jacobian");
|
||||
inline constexpr auto residuals = sub_tag(integration & physics, "residuals");
|
||||
inline constexpr auto volume = sub_tag(mesh & geometry, "volume");
|
||||
inline constexpr auto quadrature = sub_tag(mesh & geometry & solver, "quadrature");
|
||||
inline constexpr auto convergence = sub_tag(solver, "convergence");
|
||||
inline constexpr auto transformations = sub_tag(mesh & geometry, "transformations");
|
||||
|
||||
inline constexpr auto analytic_comparison =
|
||||
sub_tag(solver & physics & residuals, "analytic_comparison");
|
||||
inline constexpr auto self_consistency =
|
||||
sub_tag(solver & physics, "self_consistency");
|
||||
inline constexpr auto h_refinement = sub_tag(mesh & convergence, "h_refinement");
|
||||
inline constexpr auto p_refinement = sub_tag(mesh & convergence, "p_refinement");
|
||||
|
||||
inline constexpr auto centrifugal =
|
||||
sub_tag(solver & physics, "centrifugal");
|
||||
inline constexpr auto advection = sub_tag(solver & physics, "advection");
|
||||
inline constexpr auto coriolis = sub_tag(solver & physics, "coriolis");
|
||||
inline constexpr auto gravity = sub_tag(solver & physics, "gravity");
|
||||
inline constexpr auto enthalpy = sub_tag(solver & physics, "enthalpy");
|
||||
inline constexpr auto barotrope = sub_tag(physics, "barotrope");
|
||||
inline constexpr auto mass_continuity =
|
||||
sub_tag(solver & physics, "mass_continuity");
|
||||
inline constexpr auto pressure_gradient =
|
||||
sub_tag(solver & physics, "pressure_gradient");
|
||||
inline constexpr auto viscosity = sub_tag(solver & physics, "viscosity");
|
||||
inline constexpr auto analytic_comparison = sub_tag(solver & physics & residuals, "analytic_comparison");
|
||||
inline constexpr auto self_consistency = sub_tag(solver & physics, "self_consistency");
|
||||
|
||||
inline constexpr auto compactification =
|
||||
sub_tag(mesh & mapping, "compactification");
|
||||
inline constexpr auto kelvin = sub_tag(compactification, "kelvin");
|
||||
inline constexpr auto centrifugal = sub_tag(solver & physics, "centrifugal");
|
||||
inline constexpr auto advection = sub_tag(solver & physics, "advection");
|
||||
inline constexpr auto coriolis = sub_tag(solver & physics, "coriolis");
|
||||
inline constexpr auto gravity = sub_tag(solver & physics, "gravity");
|
||||
inline constexpr auto enthalpy = sub_tag(solver & physics, "enthalpy");
|
||||
inline constexpr auto barotrope = sub_tag(physics, "barotrope");
|
||||
inline constexpr auto mass_continuity = sub_tag(solver & physics, "mass_continuity");
|
||||
inline constexpr auto pressure_gradient = sub_tag(solver & physics, "pressure_gradient");
|
||||
inline constexpr auto viscosity = sub_tag(solver & physics, "viscosity");
|
||||
|
||||
inline constexpr auto prepared = sub_tag(solver & physics, "prepared");
|
||||
inline constexpr auto contexts = sub_tag(solver, "contexts");
|
||||
inline constexpr auto compactification = sub_tag(mesh & mapping, "compactification");
|
||||
inline constexpr auto kelvin = sub_tag(compactification, "kelvin");
|
||||
|
||||
inline constexpr auto prepared = sub_tag(solver & physics, "prepared");
|
||||
inline constexpr auto contexts = sub_tag(solver, "contexts");
|
||||
|
||||
inline constexpr auto domain = sub_tag(mesh, "domain");
|
||||
|
||||
} // namespace tags
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
@@ -65,15 +67,13 @@ std::string ansiToHtml(const std::string &text) {
|
||||
|
||||
while (i < len) {
|
||||
// Look for ANSI CSI sequence '\033[' or '\x1b['
|
||||
if ((htmlEscaped[i] == '\033' || htmlEscaped[i] == '\x1b') &&
|
||||
i + 1 < len && htmlEscaped[i + 1] == '[') {
|
||||
if ((htmlEscaped[i] == '\033' || htmlEscaped[i] == '\x1b') && i + 1 < len && htmlEscaped[i + 1] == '[') {
|
||||
size_t seqStart = i + 2;
|
||||
size_t seqEnd = htmlEscaped.find('m', seqStart);
|
||||
|
||||
if (seqEnd != std::string::npos) {
|
||||
std::string codeStr =
|
||||
htmlEscaped.substr(seqStart, seqEnd - seqStart);
|
||||
i = seqEnd + 1;
|
||||
std::string codeStr = htmlEscaped.substr(seqStart, seqEnd - seqStart);
|
||||
i = seqEnd + 1;
|
||||
|
||||
std::istringstream codeStream(codeStr);
|
||||
std::string codeVal;
|
||||
@@ -229,10 +229,28 @@ class CheckReporter : public Catch::StreamingReporterBase {
|
||||
|
||||
std::vector<std::string> m_currentFailures;
|
||||
std::vector<std::string> m_currentInfos;
|
||||
std::unordered_set<unsigned int> m_currentInfoSequences;
|
||||
std::vector<TestCaseData> m_testRunData;
|
||||
|
||||
void captureInfoMessages(Catch::AssertionStats const &assertionStats) {
|
||||
for (auto const &message : assertionStats.infoMessages) {
|
||||
if (m_currentInfoSequences.insert(message.sequence).second) {
|
||||
m_currentInfos.push_back(message.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
using StreamingReporterBase::StreamingReporterBase;
|
||||
explicit CheckReporter(Catch::ReporterConfig &&config) : Catch::StreamingReporterBase(std::move(config)) {
|
||||
// INFO messages are delivered through assertionEnded. Request passing
|
||||
// assertions as well so HTML logging does not depend on Catch2's -s
|
||||
// flag.
|
||||
m_preferences.shouldReportAllAssertions = true;
|
||||
|
||||
// This reporter does not use assertionStarting events. Disabling them
|
||||
// preserves Catch2's successful-assertion fast path where possible.
|
||||
m_preferences.shouldReportAllAssertionStarts = false;
|
||||
}
|
||||
|
||||
static std::string getDescription() {
|
||||
return "Console reporter with wrapping, tags, and collapsible HTML "
|
||||
@@ -245,28 +263,26 @@ public:
|
||||
|
||||
std::cout << '\n';
|
||||
std::cout << std::left << std::setw(85) << "Test Case Name"
|
||||
<< "Status " << std::right << std::setw(8) << "Passed"
|
||||
<< std::setw(8) << "Failed" << '\n';
|
||||
<< "Status " << std::right << std::setw(8) << "Passed" << std::setw(8) << "Failed" << '\n';
|
||||
std::cout << std::string(121, '-') << '\n';
|
||||
}
|
||||
|
||||
void assertionEnded(Catch::AssertionStats const &assertionStats) override {
|
||||
StreamingReporterBase::assertionEnded(assertionStats);
|
||||
|
||||
// Capture INFO messages regardless of pass/fail status
|
||||
for (auto const &msg : assertionStats.infoMessages) {
|
||||
m_currentInfos.push_back(msg.message);
|
||||
}
|
||||
// Capture every INFO message encountered by either a passing or failing
|
||||
// assertion. Message sequence IDs prevent a scoped INFO from being
|
||||
// repeated once for every assertion that occurs while it remains
|
||||
// active.
|
||||
captureInfoMessages(assertionStats);
|
||||
|
||||
if (!assertionStats.assertionResult.isOk()) {
|
||||
auto const &result = assertionStats.assertionResult;
|
||||
std::ostringstream oss;
|
||||
|
||||
oss << " \033[31m-> FAILED:\033[0m "
|
||||
<< result.getSourceInfo().file << ":"
|
||||
<< result.getSourceInfo().line << '\n';
|
||||
oss << " " << result.getTestMacroName() << "( "
|
||||
<< result.getExpression() << " )\n";
|
||||
oss << " \033[31m-> FAILED:\033[0m " << result.getSourceInfo().file << ":" << result.getSourceInfo().line
|
||||
<< '\n';
|
||||
oss << " " << result.getTestMacroName() << "( " << result.getExpression() << " )\n";
|
||||
|
||||
if (result.hasExpandedExpression()) {
|
||||
oss << " with expansion:\n"
|
||||
@@ -290,10 +306,8 @@ public:
|
||||
std::string name = stats.testInfo->name;
|
||||
auto wrappedName = wrapText(name, 83);
|
||||
|
||||
std::cout << std::left << std::setw(85) << wrappedName[0] << mark
|
||||
<< " " << std::right << std::setw(8)
|
||||
<< stats.totals.assertions.passed << std::setw(8)
|
||||
<< stats.totals.assertions.failed << '\n';
|
||||
std::cout << std::left << std::setw(85) << wrappedName[0] << mark << " " << std::right << std::setw(8)
|
||||
<< stats.totals.assertions.passed << std::setw(8) << stats.totals.assertions.failed << '\n';
|
||||
|
||||
for (size_t i = 1; i < wrappedName.size(); ++i) {
|
||||
std::cout << " \033[90m↳ \033[0m" // Dim indent arrow
|
||||
@@ -317,12 +331,13 @@ public:
|
||||
}
|
||||
|
||||
m_testRunData.push_back(
|
||||
{name, tagsStr, passed, stats.totals.assertions.passed,
|
||||
stats.totals.assertions.failed, m_currentFailures, m_currentInfos}
|
||||
{name, tagsStr, passed, stats.totals.assertions.passed, stats.totals.assertions.failed, m_currentFailures,
|
||||
m_currentInfos}
|
||||
);
|
||||
|
||||
m_currentFailures.clear();
|
||||
m_currentInfos.clear();
|
||||
m_currentInfoSequences.clear();
|
||||
}
|
||||
|
||||
void testRunEnded(Catch::TestRunStats const &_testRunStats) override {
|
||||
@@ -334,27 +349,17 @@ public:
|
||||
auto const &as = _testRunStats.totals.assertions;
|
||||
|
||||
std::string tc_passed_str =
|
||||
tc.passed > 0
|
||||
? "\033[32m" + std::to_string(tc.passed) + " passed\033[0m"
|
||||
: "0 passed";
|
||||
tc.passed > 0 ? "\033[32m" + std::to_string(tc.passed) + " passed\033[0m" : "0 passed";
|
||||
std::string tc_failed_str =
|
||||
tc.failed > 0
|
||||
? "\033[31m" + std::to_string(tc.failed) + " failed\033[0m"
|
||||
: "0 failed";
|
||||
tc.failed > 0 ? "\033[31m" + std::to_string(tc.failed) + " failed\033[0m" : "0 failed";
|
||||
|
||||
std::string as_passed_str =
|
||||
as.passed > 0
|
||||
? "\033[32m" + std::to_string(as.passed) + " passed\033[0m"
|
||||
: "0 passed";
|
||||
as.passed > 0 ? "\033[32m" + std::to_string(as.passed) + " passed\033[0m" : "0 passed";
|
||||
std::string as_failed_str =
|
||||
as.failed > 0
|
||||
? "\033[31m" + std::to_string(as.failed) + " failed\033[0m"
|
||||
: "0 failed";
|
||||
as.failed > 0 ? "\033[31m" + std::to_string(as.failed) + " failed\033[0m" : "0 failed";
|
||||
|
||||
std::cout << "Test Cases: " << tc_passed_str << ", " << tc_failed_str
|
||||
<< ", " << tc.total() << " total\n";
|
||||
std::cout << "Assertions: " << as_passed_str << ", " << as_failed_str
|
||||
<< ", " << as.total() << " total\n\n";
|
||||
std::cout << "Test Cases: " << tc_passed_str << ", " << tc_failed_str << ", " << tc.total() << " total\n";
|
||||
std::cout << "Assertions: " << as_passed_str << ", " << as_failed_str << ", " << as.total() << " total\n\n";
|
||||
|
||||
generateHtmlReport(_testRunStats);
|
||||
}
|
||||
@@ -365,68 +370,66 @@ private:
|
||||
if (!html)
|
||||
return;
|
||||
|
||||
html
|
||||
<< "<!DOCTYPE html>\n<html lang='en'>\n<head>\n"
|
||||
<< "<meta charset='UTF-8'>\n"
|
||||
<< "<meta name='viewport' content='width=device-width, "
|
||||
"initial-scale=1.0'>\n"
|
||||
<< "<title>Test Run Summary</title>\n"
|
||||
<< "<style>\n"
|
||||
<< "body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe "
|
||||
"UI', "
|
||||
"Roboto, Helvetica, Arial, sans-serif; "
|
||||
"background: #f4f6f8; color: #333; margin: 0; padding: 2rem; }\n"
|
||||
<< "h1 { color: #2c3e50; border-bottom: 2px solid #e0e0e0; "
|
||||
"padding-bottom: 0.5rem; }\n"
|
||||
<< ".summary-cards { display: flex; gap: 1rem; margin-bottom: "
|
||||
"2rem; }\n"
|
||||
<< ".card { background: white; padding: 1rem 1.5rem; "
|
||||
"border-radius: "
|
||||
"8px; box-shadow: 0 2px 4px "
|
||||
"rgba(0,0,0,0.05); flex: 1; }\n"
|
||||
<< ".card h3 { margin-top: 0; font-size: 0.9rem; color: #7f8c8d; "
|
||||
"text-transform: uppercase; }\n"
|
||||
<< ".card p { font-size: 1.5rem; font-weight: bold; margin: 0; }\n"
|
||||
<< ".text-green { color: #27ae60; }\n"
|
||||
<< ".text-red { color: #e74c3c; }\n"
|
||||
<< ".test-item { background: white; border-radius: 8px; padding: "
|
||||
"1rem; "
|
||||
"margin-bottom: 1rem; box-shadow: 0 2px "
|
||||
"4px rgba(0,0,0,0.05); border-left: 5px solid #bdc3c7; }\n"
|
||||
<< ".test-item.passed { border-left-color: #27ae60; }\n"
|
||||
<< ".test-item.failed { border-left-color: #e74c3c; }\n"
|
||||
<< ".test-header { display: flex; justify-content: space-between; "
|
||||
"align-items: flex-start; }\n"
|
||||
<< ".test-name { font-size: 1.1rem; font-weight: 600; margin: 0 0 "
|
||||
"0.5rem 0; word-break: break-word; }\n"
|
||||
<< ".tags { font-size: 0.8rem; color: #2980b9; background: "
|
||||
"#ebf5fb; "
|
||||
"padding: 2px 6px; border-radius: 4px; "
|
||||
"display: inline-block; margin-top: 4px; }\n"
|
||||
<< ".stats { font-size: 0.9rem; color: #7f8c8d; }\n"
|
||||
<< "details { margin-top: 0.8rem; background: #f8f9fa; border: 1px "
|
||||
"solid #e9ecef; border-radius: 6px; "
|
||||
"padding: 0.5rem 0.8rem; }\n"
|
||||
<< "summary { cursor: pointer; font-weight: 600; color: #34495e; "
|
||||
"user-select: none; font-size: 0.9rem; }\n"
|
||||
<< "summary:hover { color: #2980b9; }\n"
|
||||
<< "pre { background: #1e293b; color: #f8fafc; padding: 1rem; "
|
||||
"border-radius: 4px; overflow-x: auto; "
|
||||
"font-size: 0.85rem; line-height: 1.4; margin-top: 0.5rem; }\n"
|
||||
<< "pre.info-block { background: #0f172a; border-left: 4px solid "
|
||||
"#0284c7; }\n"
|
||||
<< "</style>\n</head>\n<body>\n";
|
||||
html << "<!DOCTYPE html>\n<html lang='en'>\n<head>\n"
|
||||
<< "<meta charset='UTF-8'>\n"
|
||||
<< "<meta name='viewport' content='width=device-width, "
|
||||
"initial-scale=1.0'>\n"
|
||||
<< "<title>Test Run Summary</title>\n"
|
||||
<< "<style>\n"
|
||||
<< "body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe "
|
||||
"UI', "
|
||||
"Roboto, Helvetica, Arial, sans-serif; "
|
||||
"background: #f4f6f8; color: #333; margin: 0; padding: 2rem; }\n"
|
||||
<< "h1 { color: #2c3e50; border-bottom: 2px solid #e0e0e0; "
|
||||
"padding-bottom: 0.5rem; }\n"
|
||||
<< ".summary-cards { display: flex; gap: 1rem; margin-bottom: "
|
||||
"2rem; }\n"
|
||||
<< ".card { background: white; padding: 1rem 1.5rem; "
|
||||
"border-radius: "
|
||||
"8px; box-shadow: 0 2px 4px "
|
||||
"rgba(0,0,0,0.05); flex: 1; }\n"
|
||||
<< ".card h3 { margin-top: 0; font-size: 0.9rem; color: #7f8c8d; "
|
||||
"text-transform: uppercase; }\n"
|
||||
<< ".card p { font-size: 1.5rem; font-weight: bold; margin: 0; }\n"
|
||||
<< ".text-green { color: #27ae60; }\n"
|
||||
<< ".text-red { color: #e74c3c; }\n"
|
||||
<< ".test-item { background: white; border-radius: 8px; padding: "
|
||||
"1rem; "
|
||||
"margin-bottom: 1rem; box-shadow: 0 2px "
|
||||
"4px rgba(0,0,0,0.05); border-left: 5px solid #bdc3c7; }\n"
|
||||
<< ".test-item.passed { border-left-color: #27ae60; }\n"
|
||||
<< ".test-item.failed { border-left-color: #e74c3c; }\n"
|
||||
<< ".test-header { display: flex; justify-content: space-between; "
|
||||
"align-items: flex-start; }\n"
|
||||
<< ".test-name { font-size: 1.1rem; font-weight: 600; margin: 0 0 "
|
||||
"0.5rem 0; word-break: break-word; }\n"
|
||||
<< ".tags { font-size: 0.8rem; color: #2980b9; background: "
|
||||
"#ebf5fb; "
|
||||
"padding: 2px 6px; border-radius: 4px; "
|
||||
"display: inline-block; margin-top: 4px; }\n"
|
||||
<< ".stats { font-size: 0.9rem; color: #7f8c8d; }\n"
|
||||
<< "details { margin-top: 0.8rem; background: #f8f9fa; border: 1px "
|
||||
"solid #e9ecef; border-radius: 6px; "
|
||||
"padding: 0.5rem 0.8rem; }\n"
|
||||
<< "summary { cursor: pointer; font-weight: 600; color: #34495e; "
|
||||
"user-select: none; font-size: 0.9rem; }\n"
|
||||
<< "summary:hover { color: #2980b9; }\n"
|
||||
<< "pre { background: #1e293b; color: #f8fafc; padding: 1rem; "
|
||||
"border-radius: 4px; overflow-x: auto; "
|
||||
"font-size: 0.85rem; line-height: 1.4; margin-top: 0.5rem; }\n"
|
||||
<< "pre.info-block { background: #0f172a; border-left: 4px solid "
|
||||
"#0284c7; }\n"
|
||||
<< "</style>\n</head>\n<body>\n";
|
||||
|
||||
html << "<h1>Test Run Summary</h1>\n";
|
||||
|
||||
// Summary Cards
|
||||
html << "<div class='summary-cards'>\n";
|
||||
html << "<div class='card'><h3>Total Cases</h3><p>"
|
||||
<< stats.totals.testCases.total() << "</p></div>\n";
|
||||
html << "<div class='card'><h3>Cases Passed</h3><p class='text-green'>"
|
||||
<< stats.totals.testCases.passed << "</p></div>\n";
|
||||
html << "<div class='card'><h3>Cases Failed</h3><p class='text-red'>"
|
||||
<< stats.totals.testCases.failed << "</p></div>\n";
|
||||
html << "<div class='card'><h3>Total Cases</h3><p>" << stats.totals.testCases.total() << "</p></div>\n";
|
||||
html << "<div class='card'><h3>Cases Passed</h3><p class='text-green'>" << stats.totals.testCases.passed
|
||||
<< "</p></div>\n";
|
||||
html << "<div class='card'><h3>Cases Failed</h3><p class='text-red'>" << stats.totals.testCases.failed
|
||||
<< "</p></div>\n";
|
||||
html << "</div>\n";
|
||||
|
||||
for (const auto &test : m_testRunData) {
|
||||
@@ -434,26 +437,21 @@ private:
|
||||
html << "<div class='test-item " << statusClass << "'>\n";
|
||||
html << " <div class='test-header'>\n";
|
||||
html << " <div>\n";
|
||||
html << " <h3 class='test-name'>" << escapeHtml(test.name)
|
||||
<< "</h3>\n";
|
||||
html << " <h3 class='test-name'>" << escapeHtml(test.name) << "</h3>\n";
|
||||
if (!test.tags.empty()) {
|
||||
html << " <div class='tags'>" << escapeHtml(test.tags)
|
||||
<< "</div>\n";
|
||||
html << " <div class='tags'>" << escapeHtml(test.tags) << "</div>\n";
|
||||
}
|
||||
html << " </div>\n";
|
||||
html << " <div class='stats'>\n";
|
||||
html << " <span class='text-green'>✓ "
|
||||
<< test.assertionsPassed << "</span> | ";
|
||||
html << " <span class='text-red'>✗ "
|
||||
<< test.assertionsFailed << "</span>\n";
|
||||
html << " <span class='text-green'>✓ " << test.assertionsPassed << "</span> | ";
|
||||
html << " <span class='text-red'>✗ " << test.assertionsFailed << "</span>\n";
|
||||
html << " </div>\n";
|
||||
html << " </div>\n";
|
||||
|
||||
// Collapsible INFO Messages section with ANSI color rendering
|
||||
if (!test.infoMessages.empty()) {
|
||||
html << " <details>\n";
|
||||
html << " <summary>Info Logs (" << test.infoMessages.size()
|
||||
<< ")</summary>\n";
|
||||
html << " <summary>Info Logs (" << test.infoMessages.size() << ")</summary>\n";
|
||||
html << " <pre class='info-block'>";
|
||||
for (const auto &info : test.infoMessages) {
|
||||
html << "[INFO] " << ansiToHtml(info) << "\n";
|
||||
@@ -465,8 +463,8 @@ private:
|
||||
// Collapsible Failures section with ANSI color rendering
|
||||
if (!test.failureMessages.empty()) {
|
||||
html << " <details open>\n";
|
||||
html << " <summary class='text-red'>Failure Details ("
|
||||
<< test.failureMessages.size() << ")</summary>\n";
|
||||
html << " <summary class='text-red'>Failure Details (" << test.failureMessages.size()
|
||||
<< ")</summary>\n";
|
||||
html << " <pre>";
|
||||
for (const auto &msg : test.failureMessages) {
|
||||
html << ansiToHtml(msg) << "\n";
|
||||
@@ -542,14 +540,11 @@ int main(
|
||||
}
|
||||
|
||||
const auto is_reporter_option = [](const std::string &argument) {
|
||||
return argument == "-r" || argument == "--reporter" ||
|
||||
argument.starts_with("-r=") ||
|
||||
return argument == "-r" || argument == "--reporter" || argument.starts_with("-r=") ||
|
||||
argument.starts_with("--reporter=");
|
||||
};
|
||||
|
||||
if (const bool has_reporter =
|
||||
std::ranges::any_of(catch_arguments, is_reporter_option);
|
||||
!has_reporter) {
|
||||
if (const bool has_reporter = std::ranges::any_of(catch_arguments, is_reporter_option); !has_reporter) {
|
||||
catch_arguments.emplace_back("--reporter");
|
||||
catch_arguments.emplace_back("check");
|
||||
}
|
||||
@@ -563,9 +558,7 @@ int main(
|
||||
|
||||
Catch::Session session;
|
||||
|
||||
if (const int catch_parse_result = session.applyCommandLine(
|
||||
static_cast<int>(catch_argv.size()), catch_argv.data()
|
||||
);
|
||||
if (const int catch_parse_result = session.applyCommandLine(static_cast<int>(catch_argv.size()), catch_argv.data());
|
||||
catch_parse_result != 0) {
|
||||
return catch_parse_result;
|
||||
}
|
||||
@@ -577,8 +570,7 @@ int main(
|
||||
|
||||
const int hdiv_max_q1d = mfem::DeviceDofQuadLimits::Get().HDIV_MAX_Q1D;
|
||||
std::cout << "H(div) maximum Q1D = " << hdiv_max_q1d << '\n';
|
||||
std::cout << "Approximate maximum safe integration order = "
|
||||
<< 2 * hdiv_max_q1d - 1 << '\n';
|
||||
std::cout << "Approximate maximum safe integration order = " << 2 * hdiv_max_q1d - 1 << '\n';
|
||||
|
||||
mean_field::utils::Args test_args = cfg.main();
|
||||
|
||||
@@ -597,4 +589,4 @@ int main(
|
||||
test_utils::set_args(std::move(test_args));
|
||||
|
||||
return session.run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,7 @@ namespace {
|
||||
|
||||
template <typename Query, typename List> struct contains_type;
|
||||
|
||||
template <typename Query>
|
||||
struct contains_type<Query, blocks::type_list<>> : std::false_type { };
|
||||
template <typename Query> struct contains_type<Query, blocks::type_list<>> : std::false_type { };
|
||||
|
||||
template <typename Query, typename Head, typename... Tail>
|
||||
struct contains_type<Query, blocks::type_list<Head, Tail...>>
|
||||
@@ -23,30 +22,24 @@ namespace {
|
||||
std::true_type,
|
||||
contains_type<Query, blocks::type_list<Tail...>>> { };
|
||||
|
||||
template <typename Query, typename List>
|
||||
inline constexpr bool contains_type_v = contains_type<Query, List>::value;
|
||||
template <typename Query, typename List> inline constexpr bool contains_type_v = contains_type<Query, List>::value;
|
||||
|
||||
template <int index, typename List> struct type_at;
|
||||
|
||||
template <typename Head, typename... Tail>
|
||||
struct type_at<0, blocks::type_list<Head, Tail...>> {
|
||||
template <typename Head, typename... Tail> struct type_at<0, blocks::type_list<Head, Tail...>> {
|
||||
using type = Head;
|
||||
};
|
||||
|
||||
template <int index, typename Head, typename... Tail>
|
||||
struct type_at<index, blocks::type_list<Head, Tail...>> {
|
||||
template <int index, typename Head, typename... Tail> struct type_at<index, blocks::type_list<Head, Tail...>> {
|
||||
static_assert(index > 0);
|
||||
using type =
|
||||
typename type_at<index - 1, blocks::type_list<Tail...>>::type;
|
||||
using type = typename type_at<index - 1, blocks::type_list<Tail...>>::type;
|
||||
};
|
||||
|
||||
template <int index, typename List>
|
||||
using type_at_t = typename type_at<index, List>::type;
|
||||
template <int index, typename List> using type_at_t = typename type_at<index, List>::type;
|
||||
|
||||
template <typename Row> struct block_row_traits;
|
||||
|
||||
template <typename Residual, typename... Values>
|
||||
struct block_row_traits<blocks::block_row<Residual, Values...>> {
|
||||
template <typename Residual, typename... Values> struct block_row_traits<blocks::block_row<Residual, Values...>> {
|
||||
using residual = Residual;
|
||||
using values = blocks::type_list<Values...>;
|
||||
|
||||
@@ -56,10 +49,7 @@ namespace {
|
||||
template <typename Row, typename... ExpectedValues>
|
||||
inline constexpr bool row_has_exact_values_v =
|
||||
block_row_traits<Row>::value_count == sizeof...(ExpectedValues) &&
|
||||
(contains_type_v<
|
||||
ExpectedValues,
|
||||
typename block_row_traits<Row>::values> &&
|
||||
...);
|
||||
(contains_type_v<ExpectedValues, typename block_row_traits<Row>::values> && ...);
|
||||
|
||||
struct foreign_value final : blocks::value_block_base { };
|
||||
struct foreign_residual final : blocks::residual_block_base { };
|
||||
@@ -69,17 +59,10 @@ TEST_CASE(
|
||||
"Block Types Preserve Their Semantic Hierarchy",
|
||||
tags::unit &tags::solver &tags::utils
|
||||
) {
|
||||
STATIC_REQUIRE(
|
||||
std::is_base_of_v<blocks::block, blocks::residual_block_base>
|
||||
);
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::block, blocks::residual_block_base>);
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::block, blocks::value_block_base>);
|
||||
STATIC_REQUIRE(
|
||||
std::is_base_of_v<
|
||||
blocks::residual_block_base, blocks::residual_block<0>>
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
std::is_base_of_v<blocks::value_block_base, blocks::value_block<0>>
|
||||
);
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::residual_block_base, blocks::residual_block<0>>);
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::value_block_base, blocks::value_block<0>>);
|
||||
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::field, blocks::density>);
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::field, blocks::displacement>);
|
||||
@@ -87,45 +70,19 @@ TEST_CASE(
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::field, blocks::enthalpy>);
|
||||
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::term, blocks::density::mass>);
|
||||
STATIC_REQUIRE(
|
||||
std::is_base_of_v<blocks::term, blocks::displacement::geometry>
|
||||
);
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::term, blocks::displacement::geometry>);
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::term, blocks::gravity::gradient>);
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::term, blocks::gravity::poisson>);
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::term, blocks::enthalpy::specific>);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::is_base_of_v<
|
||||
blocks::value_block_base, blocks::density::mass::value>
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
std::is_base_of_v<
|
||||
blocks::residual_block_base, blocks::density::mass::residual>
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
std::is_base_of_v<
|
||||
blocks::value_block_base, blocks::gravity::gradient::value>
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
std::is_base_of_v<
|
||||
blocks::residual_block_base, blocks::gravity::gradient::residual>
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
std::is_base_of_v<
|
||||
blocks::value_block_base, blocks::gravity::poisson::value>
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
std::is_base_of_v<
|
||||
blocks::residual_block_base, blocks::gravity::poisson::residual>
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
std::is_base_of_v<
|
||||
blocks::value_block_base, blocks::enthalpy::specific::value>
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
std::is_base_of_v<
|
||||
blocks::residual_block_base, blocks::enthalpy::specific::residual>
|
||||
);
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::value_block_base, blocks::density::mass::value>);
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::residual_block_base, blocks::density::mass::residual>);
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::value_block_base, blocks::gravity::gradient::value>);
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::residual_block_base, blocks::gravity::gradient::residual>);
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::value_block_base, blocks::gravity::poisson::value>);
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::residual_block_base, blocks::gravity::poisson::residual>);
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::value_block_base, blocks::enthalpy::specific::value>);
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::residual_block_base, blocks::enthalpy::specific::residual>);
|
||||
|
||||
STATIC_REQUIRE(std::is_empty_v<blocks::density>);
|
||||
STATIC_REQUIRE(std::is_empty_v<blocks::displacement>);
|
||||
@@ -136,41 +93,22 @@ TEST_CASE(
|
||||
STATIC_REQUIRE(std::is_empty_v<blocks::gravity::poisson>);
|
||||
STATIC_REQUIRE(std::is_empty_v<blocks::enthalpy::specific>);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::is_base_of_v<blocks::field, blocks::barotropic_constant>
|
||||
);
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::field, blocks::barotropic_constant>);
|
||||
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::term, blocks::barotropic_constant::mass_normalization>);
|
||||
|
||||
STATIC_REQUIRE(std::is_base_of_v<blocks::value_block_base, blocks::barotropic_constant::mass_normalization::value>);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::is_base_of_v<
|
||||
blocks::term, blocks::barotropic_constant::mass_normalization>
|
||||
std::is_base_of_v<blocks::residual_block_base, blocks::barotropic_constant::mass_normalization::residual>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::is_base_of_v<
|
||||
blocks::value_block_base,
|
||||
blocks::barotropic_constant::mass_normalization::value>
|
||||
);
|
||||
STATIC_REQUIRE(blocks::barotropic_constant::mass_normalization::value::static_block_size == 1);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::is_base_of_v<
|
||||
blocks::residual_block_base,
|
||||
blocks::barotropic_constant::mass_normalization::residual>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
blocks::barotropic_constant::mass_normalization::value::
|
||||
static_block_size == 1
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
blocks::barotropic_constant::mass_normalization::residual::
|
||||
static_block_size == 1
|
||||
);
|
||||
STATIC_REQUIRE(blocks::barotropic_constant::mass_normalization::residual::static_block_size == 1);
|
||||
|
||||
STATIC_REQUIRE(std::is_empty_v<blocks::barotropic_constant>);
|
||||
STATIC_REQUIRE(
|
||||
std::is_empty_v<blocks::barotropic_constant::mass_normalization>
|
||||
);
|
||||
STATIC_REQUIRE(std::is_empty_v<blocks::barotropic_constant::mass_normalization>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
@@ -180,36 +118,19 @@ TEST_CASE(
|
||||
tags::unit &tags::solver &tags::utils
|
||||
) {
|
||||
using list = blocks::type_list<
|
||||
blocks::density::mass::value, blocks::displacement::geometry::value,
|
||||
blocks::gravity::gradient::value, blocks::gravity::poisson::value>;
|
||||
blocks::density::mass::value, blocks::displacement::geometry::value, blocks::gravity::gradient::value,
|
||||
blocks::gravity::poisson::value>;
|
||||
|
||||
STATIC_REQUIRE(list::size == 4);
|
||||
STATIC_REQUIRE(
|
||||
blocks::type_index_v<blocks::density::mass::value, list> == 0
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
blocks::type_index_v<blocks::displacement::geometry::value, list> == 1
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
blocks::type_index_v<blocks::gravity::gradient::value, list> == 2
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
blocks::type_index_v<blocks::gravity::poisson::value, list> == 3
|
||||
);
|
||||
STATIC_REQUIRE(blocks::type_index_v<blocks::density::mass::value, list> == 0);
|
||||
STATIC_REQUIRE(blocks::type_index_v<blocks::displacement::geometry::value, list> == 1);
|
||||
STATIC_REQUIRE(blocks::type_index_v<blocks::gravity::gradient::value, list> == 2);
|
||||
STATIC_REQUIRE(blocks::type_index_v<blocks::gravity::poisson::value, list> == 3);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::is_same_v<type_at_t<0, list>, blocks::density::mass::value>
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
std::is_same_v<
|
||||
type_at_t<1, list>, blocks::displacement::geometry::value>
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
std::is_same_v<type_at_t<2, list>, blocks::gravity::gradient::value>
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
std::is_same_v<type_at_t<3, list>, blocks::gravity::poisson::value>
|
||||
);
|
||||
STATIC_REQUIRE(std::is_same_v<type_at_t<0, list>, blocks::density::mass::value>);
|
||||
STATIC_REQUIRE(std::is_same_v<type_at_t<1, list>, blocks::displacement::geometry::value>);
|
||||
STATIC_REQUIRE(std::is_same_v<type_at_t<2, list>, blocks::gravity::gradient::value>);
|
||||
STATIC_REQUIRE(std::is_same_v<type_at_t<3, list>, blocks::gravity::poisson::value>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
@@ -218,53 +139,24 @@ TEST_CASE(
|
||||
"Gravity Field Form Resolves Value And Residual Blocks",
|
||||
tags::unit &tags::solver &tags::utils
|
||||
) {
|
||||
using form = blocks::gravity_field_form;
|
||||
using form = blocks::gravity_field_form;
|
||||
|
||||
constexpr auto density_value =
|
||||
blocks::get_value_block<form>(blocks::density_field.mass_term);
|
||||
constexpr auto displacement_value =
|
||||
blocks::get_value_block<form>(blocks::displacement_field.geometry_term);
|
||||
constexpr auto gravity_gradient_value =
|
||||
blocks::get_value_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_potential_value =
|
||||
blocks::get_value_block<form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto gravity_gradient_residual =
|
||||
blocks::get_residual_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_poisson_residual =
|
||||
blocks::get_residual_block<form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto density_value = blocks::get_value_block<form>(blocks::density_field.mass_term);
|
||||
constexpr auto displacement_value = blocks::get_value_block<form>(blocks::displacement_field.geometry_term);
|
||||
constexpr auto gravity_gradient_value = blocks::get_value_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_potential_value = blocks::get_value_block<form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto gravity_gradient_residual = blocks::get_residual_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_poisson_residual = blocks::get_residual_block<form>(blocks::gravity_field.poisson_term);
|
||||
|
||||
STATIC_REQUIRE(form::value_block_count == 4);
|
||||
STATIC_REQUIRE(form::residual_block_count == 2);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::is_same_v<
|
||||
std::remove_cv_t<decltype(density_value)>, blocks::value_block<0>>
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
std::is_same_v<
|
||||
std::remove_cv_t<decltype(displacement_value)>,
|
||||
blocks::value_block<1>>
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
std::is_same_v<
|
||||
std::remove_cv_t<decltype(gravity_gradient_value)>,
|
||||
blocks::value_block<2>>
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
std::is_same_v<
|
||||
std::remove_cv_t<decltype(gravity_potential_value)>,
|
||||
blocks::value_block<3>>
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
std::is_same_v<
|
||||
std::remove_cv_t<decltype(gravity_gradient_residual)>,
|
||||
blocks::residual_block<0>>
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
std::is_same_v<
|
||||
std::remove_cv_t<decltype(gravity_poisson_residual)>,
|
||||
blocks::residual_block<1>>
|
||||
);
|
||||
STATIC_REQUIRE(std::is_same_v<std::remove_cv_t<decltype(density_value)>, blocks::value_block<0>>);
|
||||
STATIC_REQUIRE(std::is_same_v<std::remove_cv_t<decltype(displacement_value)>, blocks::value_block<1>>);
|
||||
STATIC_REQUIRE(std::is_same_v<std::remove_cv_t<decltype(gravity_gradient_value)>, blocks::value_block<2>>);
|
||||
STATIC_REQUIRE(std::is_same_v<std::remove_cv_t<decltype(gravity_potential_value)>, blocks::value_block<3>>);
|
||||
STATIC_REQUIRE(std::is_same_v<std::remove_cv_t<decltype(gravity_gradient_residual)>, blocks::residual_block<0>>);
|
||||
STATIC_REQUIRE(std::is_same_v<std::remove_cv_t<decltype(gravity_poisson_residual)>, blocks::residual_block<1>>);
|
||||
|
||||
CHECK(static_cast<int>(density_value) == 0);
|
||||
CHECK(static_cast<int>(displacement_value) == 1);
|
||||
@@ -278,20 +170,14 @@ TEST_CASE(
|
||||
"Resolved Blocks Implicitly Convert For MFEM Interfaces",
|
||||
tags::unit &tags::solver &tags::utils
|
||||
) {
|
||||
using form = blocks::gravity_field_form;
|
||||
using form = blocks::gravity_field_form;
|
||||
|
||||
constexpr auto density_value =
|
||||
blocks::get_value_block<form>(blocks::density_field.mass_term);
|
||||
constexpr auto gravity_potential_value =
|
||||
blocks::get_value_block<form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto gravity_gradient_residual =
|
||||
blocks::get_residual_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_poisson_residual =
|
||||
blocks::get_residual_block<form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto density_value = blocks::get_value_block<form>(blocks::density_field.mass_term);
|
||||
constexpr auto gravity_potential_value = blocks::get_value_block<form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto gravity_gradient_residual = blocks::get_residual_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_poisson_residual = blocks::get_residual_block<form>(blocks::gravity_field.poisson_term);
|
||||
|
||||
auto consume_block_index = [](const int block_index) {
|
||||
return block_index;
|
||||
};
|
||||
auto consume_block_index = [](const int block_index) { return block_index; };
|
||||
|
||||
CHECK(consume_block_index(density_value) == 0);
|
||||
CHECK(consume_block_index(gravity_potential_value) == 3);
|
||||
@@ -305,35 +191,21 @@ TEST_CASE(
|
||||
) {
|
||||
using reordered_form = blocks::block_form<
|
||||
blocks::type_list<
|
||||
blocks::gravity::poisson::value, blocks::gravity::gradient::value,
|
||||
blocks::density::mass::value,
|
||||
blocks::gravity::poisson::value, blocks::gravity::gradient::value, blocks::density::mass::value,
|
||||
blocks::displacement::geometry::value>,
|
||||
blocks::type_list<
|
||||
blocks::gravity::poisson::residual,
|
||||
blocks::gravity::gradient::residual>>;
|
||||
blocks::type_list<blocks::gravity::poisson::residual, blocks::gravity::gradient::residual>>;
|
||||
|
||||
constexpr auto gravity_potential_value =
|
||||
blocks::get_value_block<reordered_form>(
|
||||
blocks::gravity_field.poisson_term
|
||||
);
|
||||
blocks::get_value_block<reordered_form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto gravity_gradient_value =
|
||||
blocks::get_value_block<reordered_form>(
|
||||
blocks::gravity_field.gradient_term
|
||||
);
|
||||
constexpr auto density_value = blocks::get_value_block<reordered_form>(
|
||||
blocks::density_field.mass_term
|
||||
);
|
||||
constexpr auto displacement_value = blocks::get_value_block<reordered_form>(
|
||||
blocks::displacement_field.geometry_term
|
||||
);
|
||||
blocks::get_value_block<reordered_form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto density_value = blocks::get_value_block<reordered_form>(blocks::density_field.mass_term);
|
||||
constexpr auto displacement_value =
|
||||
blocks::get_value_block<reordered_form>(blocks::displacement_field.geometry_term);
|
||||
constexpr auto gravity_poisson_residual =
|
||||
blocks::get_residual_block<reordered_form>(
|
||||
blocks::gravity_field.poisson_term
|
||||
);
|
||||
blocks::get_residual_block<reordered_form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto gravity_gradient_residual =
|
||||
blocks::get_residual_block<reordered_form>(
|
||||
blocks::gravity_field.gradient_term
|
||||
);
|
||||
blocks::get_residual_block<reordered_form>(blocks::gravity_field.gradient_term);
|
||||
|
||||
STATIC_REQUIRE(static_cast<int>(gravity_potential_value) == 0);
|
||||
STATIC_REQUIRE(static_cast<int>(gravity_gradient_value) == 1);
|
||||
@@ -355,18 +227,12 @@ TEST_CASE(
|
||||
const std::array<int, form::residual_block_count> residual_sizes{23, 29};
|
||||
const blocks::form_layout<form> layout(value_sizes, residual_sizes);
|
||||
|
||||
constexpr auto density_value =
|
||||
blocks::get_value_block<form>(blocks::density_field.mass_term);
|
||||
constexpr auto displacement_value =
|
||||
blocks::get_value_block<form>(blocks::displacement_field.geometry_term);
|
||||
constexpr auto gravity_gradient_value =
|
||||
blocks::get_value_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_potential_value =
|
||||
blocks::get_value_block<form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto gravity_gradient_residual =
|
||||
blocks::get_residual_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_poisson_residual =
|
||||
blocks::get_residual_block<form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto density_value = blocks::get_value_block<form>(blocks::density_field.mass_term);
|
||||
constexpr auto displacement_value = blocks::get_value_block<form>(blocks::displacement_field.geometry_term);
|
||||
constexpr auto gravity_gradient_value = blocks::get_value_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_potential_value = blocks::get_value_block<form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto gravity_gradient_residual = blocks::get_residual_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_poisson_residual = blocks::get_residual_block<form>(blocks::gravity_field.poisson_term);
|
||||
|
||||
CHECK(layout.size(density_value) == 11);
|
||||
CHECK(layout.size(displacement_value) == 13);
|
||||
@@ -405,18 +271,12 @@ TEST_CASE(
|
||||
const std::array<int, form::residual_block_count> residual_sizes{0, 7};
|
||||
const blocks::form_layout<form> layout(value_sizes, residual_sizes);
|
||||
|
||||
constexpr auto density_value =
|
||||
blocks::get_value_block<form>(blocks::density_field.mass_term);
|
||||
constexpr auto displacement_value =
|
||||
blocks::get_value_block<form>(blocks::displacement_field.geometry_term);
|
||||
constexpr auto gravity_gradient_value =
|
||||
blocks::get_value_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_potential_value =
|
||||
blocks::get_value_block<form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto gravity_gradient_residual =
|
||||
blocks::get_residual_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_poisson_residual =
|
||||
blocks::get_residual_block<form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto density_value = blocks::get_value_block<form>(blocks::density_field.mass_term);
|
||||
constexpr auto displacement_value = blocks::get_value_block<form>(blocks::displacement_field.geometry_term);
|
||||
constexpr auto gravity_gradient_value = blocks::get_value_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_potential_value = blocks::get_value_block<form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto gravity_gradient_residual = blocks::get_residual_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_poisson_residual = blocks::get_residual_block<form>(blocks::gravity_field.poisson_term);
|
||||
|
||||
CHECK(layout.size(density_value) == 3);
|
||||
CHECK(layout.size(displacement_value) == 0);
|
||||
@@ -446,18 +306,12 @@ TEST_CASE(
|
||||
const std::array<int, form::residual_block_count> residual_sizes{13, 17};
|
||||
const blocks::form_layout<form> layout(value_sizes, residual_sizes);
|
||||
|
||||
constexpr auto density_value =
|
||||
blocks::get_value_block<form>(blocks::density_field.mass_term);
|
||||
constexpr auto displacement_value =
|
||||
blocks::get_value_block<form>(blocks::displacement_field.geometry_term);
|
||||
constexpr auto gravity_gradient_value =
|
||||
blocks::get_value_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_potential_value =
|
||||
blocks::get_value_block<form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto gravity_gradient_residual =
|
||||
blocks::get_residual_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_poisson_residual =
|
||||
blocks::get_residual_block<form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto density_value = blocks::get_value_block<form>(blocks::density_field.mass_term);
|
||||
constexpr auto displacement_value = blocks::get_value_block<form>(blocks::displacement_field.geometry_term);
|
||||
constexpr auto gravity_gradient_value = blocks::get_value_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_potential_value = blocks::get_value_block<form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto gravity_gradient_residual = blocks::get_residual_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_poisson_residual = blocks::get_residual_block<form>(blocks::gravity_field.poisson_term);
|
||||
|
||||
mfem::BlockVector values(layout.value_offsets());
|
||||
mfem::BlockVector residuals(layout.residual_offsets());
|
||||
@@ -504,24 +358,15 @@ TEST_CASE(
|
||||
const std::array<int, form::residual_block_count> residual_sizes{13, 17};
|
||||
const blocks::form_layout<form> layout(value_sizes, residual_sizes);
|
||||
|
||||
constexpr auto density_value =
|
||||
blocks::get_value_block<form>(blocks::density_field.mass_term);
|
||||
constexpr auto gravity_gradient_residual =
|
||||
blocks::get_residual_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_poisson_residual =
|
||||
blocks::get_residual_block<form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto density_value = blocks::get_value_block<form>(blocks::density_field.mass_term);
|
||||
constexpr auto gravity_gradient_residual = blocks::get_residual_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_poisson_residual = blocks::get_residual_block<form>(blocks::gravity_field.poisson_term);
|
||||
|
||||
mfem::DenseMatrix source_block(
|
||||
layout.size(gravity_poisson_residual), layout.size(density_value)
|
||||
);
|
||||
mfem::DenseMatrix source_block(layout.size(gravity_poisson_residual), layout.size(density_value));
|
||||
source_block = 1.0;
|
||||
|
||||
mfem::BlockOperator block_operator(
|
||||
layout.residual_offsets(), layout.value_offsets()
|
||||
);
|
||||
block_operator.SetBlock(
|
||||
gravity_poisson_residual, density_value, &source_block
|
||||
);
|
||||
mfem::BlockOperator block_operator(layout.residual_offsets(), layout.value_offsets());
|
||||
block_operator.SetBlock(gravity_poisson_residual, density_value, &source_block);
|
||||
|
||||
mfem::BlockVector values(layout.value_offsets());
|
||||
mfem::BlockVector residuals(layout.residual_offsets());
|
||||
@@ -534,8 +379,7 @@ TEST_CASE(
|
||||
|
||||
CHECK(residuals.GetBlock(gravity_gradient_residual).Norml2() == 0.0);
|
||||
|
||||
const mfem::Vector &poisson_residual =
|
||||
residuals.GetBlock(gravity_poisson_residual);
|
||||
const mfem::Vector &poisson_residual = residuals.GetBlock(gravity_poisson_residual);
|
||||
REQUIRE(poisson_residual.Size() == residual_sizes[1]);
|
||||
|
||||
for (int i = 0; i < poisson_residual.Size(); ++i) {
|
||||
@@ -555,53 +399,19 @@ TEST_CASE(
|
||||
|
||||
STATIC_REQUIRE(blocks::gravity_jacobian_form::size == 2);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::is_same_v<
|
||||
typename gradient_traits::residual,
|
||||
blocks::gravity::gradient::residual>
|
||||
);
|
||||
STATIC_REQUIRE(std::is_same_v<typename gradient_traits::residual, blocks::gravity::gradient::residual>);
|
||||
STATIC_REQUIRE(gradient_traits::value_count == 3);
|
||||
STATIC_REQUIRE(
|
||||
contains_type_v<
|
||||
blocks::gravity::gradient::value, typename gradient_traits::values>
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
contains_type_v<
|
||||
blocks::gravity::poisson::value, typename gradient_traits::values>
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
contains_type_v<
|
||||
blocks::displacement::geometry::value,
|
||||
typename gradient_traits::values>
|
||||
);
|
||||
STATIC_REQUIRE_FALSE(
|
||||
contains_type_v<
|
||||
blocks::density::mass::value, typename gradient_traits::values>
|
||||
);
|
||||
STATIC_REQUIRE(contains_type_v<blocks::gravity::gradient::value, typename gradient_traits::values>);
|
||||
STATIC_REQUIRE(contains_type_v<blocks::gravity::poisson::value, typename gradient_traits::values>);
|
||||
STATIC_REQUIRE(contains_type_v<blocks::displacement::geometry::value, typename gradient_traits::values>);
|
||||
STATIC_REQUIRE_FALSE(contains_type_v<blocks::density::mass::value, typename gradient_traits::values>);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::is_same_v<
|
||||
typename poisson_traits::residual,
|
||||
blocks::gravity::poisson::residual>
|
||||
);
|
||||
STATIC_REQUIRE(std::is_same_v<typename poisson_traits::residual, blocks::gravity::poisson::residual>);
|
||||
STATIC_REQUIRE(poisson_traits::value_count == 3);
|
||||
STATIC_REQUIRE(
|
||||
contains_type_v<
|
||||
blocks::gravity::gradient::value, typename poisson_traits::values>
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
contains_type_v<
|
||||
blocks::density::mass::value, typename poisson_traits::values>
|
||||
);
|
||||
STATIC_REQUIRE(
|
||||
contains_type_v<
|
||||
blocks::displacement::geometry::value,
|
||||
typename poisson_traits::values>
|
||||
);
|
||||
STATIC_REQUIRE_FALSE(
|
||||
contains_type_v<
|
||||
blocks::gravity::poisson::value, typename poisson_traits::values>
|
||||
);
|
||||
STATIC_REQUIRE(contains_type_v<blocks::gravity::gradient::value, typename poisson_traits::values>);
|
||||
STATIC_REQUIRE(contains_type_v<blocks::density::mass::value, typename poisson_traits::values>);
|
||||
STATIC_REQUIRE(contains_type_v<blocks::displacement::geometry::value, typename poisson_traits::values>);
|
||||
STATIC_REQUIRE_FALSE(contains_type_v<blocks::gravity::poisson::value, typename poisson_traits::values>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
@@ -610,36 +420,23 @@ TEST_CASE(
|
||||
"Barotropic Equilibrium Form Encodes The Agreed Row And Column Layouts",
|
||||
tags::unit &tags::solver &tags::utils
|
||||
) {
|
||||
using form = blocks::barotropic_equilibrium_form;
|
||||
using form = blocks::barotropic_equilibrium_form;
|
||||
|
||||
constexpr auto density_value =
|
||||
blocks::get_value_block<form>(blocks::density_field.mass_term);
|
||||
constexpr auto displacement_value =
|
||||
blocks::get_value_block<form>(blocks::displacement_field.geometry_term);
|
||||
constexpr auto gravity_gradient_value =
|
||||
blocks::get_value_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_potential_value =
|
||||
blocks::get_value_block<form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto enthalpy_value =
|
||||
blocks::get_value_block<form>(blocks::enthalpy_field.specific_term);
|
||||
constexpr auto barotropic_constant_value = blocks::get_value_block<form>(
|
||||
blocks::barotropic_constant_field.mass_normalization_term
|
||||
);
|
||||
constexpr auto density_value = blocks::get_value_block<form>(blocks::density_field.mass_term);
|
||||
constexpr auto displacement_value = blocks::get_value_block<form>(blocks::displacement_field.geometry_term);
|
||||
constexpr auto gravity_gradient_value = blocks::get_value_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_potential_value = blocks::get_value_block<form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto enthalpy_value = blocks::get_value_block<form>(blocks::enthalpy_field.specific_term);
|
||||
constexpr auto barotropic_constant_value =
|
||||
blocks::get_value_block<form>(blocks::barotropic_constant_field.mass_normalization_term);
|
||||
|
||||
constexpr auto gravity_gradient_residual =
|
||||
blocks::get_residual_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_poisson_residual =
|
||||
blocks::get_residual_block<form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto density_residual =
|
||||
blocks::get_residual_block<form>(blocks::density_field.mass_term);
|
||||
constexpr auto displacement_residual = blocks::get_residual_block<form>(
|
||||
blocks::displacement_field.geometry_term
|
||||
);
|
||||
constexpr auto enthalpy_residual =
|
||||
blocks::get_residual_block<form>(blocks::enthalpy_field.specific_term);
|
||||
constexpr auto mass_residual = blocks::get_residual_block<form>(
|
||||
blocks::barotropic_constant_field.mass_normalization_term
|
||||
);
|
||||
constexpr auto gravity_gradient_residual = blocks::get_residual_block<form>(blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_poisson_residual = blocks::get_residual_block<form>(blocks::gravity_field.poisson_term);
|
||||
constexpr auto density_residual = blocks::get_residual_block<form>(blocks::density_field.mass_term);
|
||||
constexpr auto displacement_residual = blocks::get_residual_block<form>(blocks::displacement_field.geometry_term);
|
||||
constexpr auto enthalpy_residual = blocks::get_residual_block<form>(blocks::enthalpy_field.specific_term);
|
||||
constexpr auto mass_residual =
|
||||
blocks::get_residual_block<form>(blocks::barotropic_constant_field.mass_normalization_term);
|
||||
|
||||
STATIC_REQUIRE(form::value_block_count == 6);
|
||||
STATIC_REQUIRE(form::residual_block_count == 6);
|
||||
@@ -678,48 +475,41 @@ TEST_CASE(
|
||||
|
||||
STATIC_REQUIRE(
|
||||
row_has_exact_values_v<
|
||||
gradient_row, blocks::gravity::gradient::value,
|
||||
blocks::gravity::poisson::value,
|
||||
gradient_row, blocks::gravity::gradient::value, blocks::gravity::poisson::value,
|
||||
blocks::displacement::geometry::value>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
row_has_exact_values_v<
|
||||
poisson_row, blocks::gravity::gradient::value,
|
||||
blocks::density::mass::value, blocks::displacement::geometry::value>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
row_has_exact_values_v<
|
||||
density_row, blocks::density::mass::value,
|
||||
blocks::enthalpy::specific::value,
|
||||
poisson_row, blocks::gravity::gradient::value, blocks::density::mass::value,
|
||||
blocks::displacement::geometry::value>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
row_has_exact_values_v<
|
||||
displacement_row, blocks::displacement::geometry::value,
|
||||
blocks::enthalpy::specific::value>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
row_has_exact_values_v<
|
||||
enthalpy_row, blocks::enthalpy::specific::value,
|
||||
blocks::gravity::poisson::value,
|
||||
blocks::displacement::geometry::value,
|
||||
blocks::barotropic_constant::mass_normalization::value>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
row_has_exact_values_v<
|
||||
mass_row, blocks::density::mass::value,
|
||||
density_row, blocks::density::mass::value, blocks::enthalpy::specific::value,
|
||||
blocks::displacement::geometry::value>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
row_has_exact_values_v<
|
||||
displacement_row, blocks::density::mass::value, blocks::displacement::geometry::value,
|
||||
blocks::gravity::gradient::value, blocks::enthalpy::specific::value>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
row_has_exact_values_v<
|
||||
enthalpy_row, blocks::enthalpy::specific::value, blocks::gravity::poisson::value,
|
||||
blocks::displacement::geometry::value, blocks::barotropic_constant::mass_normalization::value>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
row_has_exact_values_v<mass_row, blocks::density::mass::value, blocks::displacement::geometry::value>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE_FALSE(
|
||||
blocks::has_jacobian_coupling_v<
|
||||
blocks::displacement::geometry::residual,
|
||||
blocks::gravity::poisson::value, jacobian>
|
||||
blocks::displacement::geometry::residual, blocks::gravity::poisson::value, jacobian>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE_FALSE(
|
||||
@@ -730,8 +520,7 @@ TEST_CASE(
|
||||
|
||||
STATIC_REQUIRE_FALSE(
|
||||
blocks::has_jacobian_coupling_v<
|
||||
blocks::barotropic_constant::mass_normalization::residual,
|
||||
blocks::enthalpy::specific::value, jacobian>
|
||||
blocks::barotropic_constant::mass_normalization::residual, blocks::enthalpy::specific::value, jacobian>
|
||||
);
|
||||
|
||||
CHECK(true);
|
||||
@@ -751,46 +540,36 @@ TEST_CASE(
|
||||
using enthalpy_row = type_at_t<4, jacobian>;
|
||||
using mass_row = type_at_t<5, jacobian>;
|
||||
|
||||
using missing_row = blocks::type_list<
|
||||
gradient_row, poisson_row, density_row, displacement_row, enthalpy_row>;
|
||||
using missing_row = blocks::type_list<gradient_row, poisson_row, density_row, displacement_row, enthalpy_row>;
|
||||
|
||||
using duplicate_row = blocks::type_list<
|
||||
gradient_row, poisson_row, density_row, displacement_row, enthalpy_row,
|
||||
enthalpy_row>;
|
||||
using duplicate_row =
|
||||
blocks::type_list<gradient_row, poisson_row, density_row, displacement_row, enthalpy_row, enthalpy_row>;
|
||||
|
||||
using reordered_rows = blocks::type_list<
|
||||
poisson_row, gradient_row, density_row, displacement_row, enthalpy_row,
|
||||
mass_row>;
|
||||
using reordered_rows =
|
||||
blocks::type_list<poisson_row, gradient_row, density_row, displacement_row, enthalpy_row, mass_row>;
|
||||
|
||||
using foreign_value_row = blocks::block_row<
|
||||
blocks::enthalpy::specific::residual, blocks::enthalpy::specific::value,
|
||||
blocks::gravity::poisson::value, blocks::displacement::geometry::value,
|
||||
foreign_value>;
|
||||
blocks::enthalpy::specific::residual, blocks::enthalpy::specific::value, blocks::gravity::poisson::value,
|
||||
blocks::displacement::geometry::value, foreign_value>;
|
||||
|
||||
using foreign_value_jacobian = blocks::type_list<
|
||||
gradient_row, poisson_row, density_row, displacement_row,
|
||||
foreign_value_row, mass_row>;
|
||||
using foreign_value_jacobian =
|
||||
blocks::type_list<gradient_row, poisson_row, density_row, displacement_row, foreign_value_row, mass_row>;
|
||||
|
||||
using duplicate_value_row = blocks::block_row<
|
||||
blocks::enthalpy::specific::residual, blocks::enthalpy::specific::value,
|
||||
blocks::gravity::poisson::value, blocks::displacement::geometry::value,
|
||||
blocks::barotropic_constant::mass_normalization::value,
|
||||
blocks::enthalpy::specific::residual, blocks::enthalpy::specific::value, blocks::gravity::poisson::value,
|
||||
blocks::displacement::geometry::value, blocks::barotropic_constant::mass_normalization::value,
|
||||
blocks::barotropic_constant::mass_normalization::value>;
|
||||
|
||||
using duplicate_value_jacobian = blocks::type_list<
|
||||
gradient_row, poisson_row, density_row, displacement_row,
|
||||
duplicate_value_row, mass_row>;
|
||||
using duplicate_value_jacobian =
|
||||
blocks::type_list<gradient_row, poisson_row, density_row, displacement_row, duplicate_value_row, mass_row>;
|
||||
|
||||
using unknown_residual_row =
|
||||
blocks::block_row<foreign_residual, blocks::density::mass::value>;
|
||||
using unknown_residual_row = blocks::block_row<foreign_residual, blocks::density::mass::value>;
|
||||
|
||||
using unknown_residual_jacobian = blocks::type_list<
|
||||
gradient_row, poisson_row, density_row, displacement_row, enthalpy_row,
|
||||
unknown_residual_row>;
|
||||
using unknown_residual_jacobian =
|
||||
blocks::type_list<gradient_row, poisson_row, density_row, displacement_row, enthalpy_row, unknown_residual_row>;
|
||||
|
||||
using duplicate_value_form = blocks::block_form<
|
||||
blocks::type_list<
|
||||
blocks::density::mass::value, blocks::density::mass::value>,
|
||||
blocks::type_list<blocks::density::mass::value, blocks::density::mass::value>,
|
||||
blocks::type_list<blocks::density::mass::residual>>;
|
||||
|
||||
STATIC_REQUIRE(blocks::block_form_is_valid_v<form>);
|
||||
@@ -804,17 +583,11 @@ TEST_CASE(
|
||||
|
||||
STATIC_REQUIRE_FALSE((blocks::valid_jacobian_form<form, reordered_rows>));
|
||||
|
||||
STATIC_REQUIRE_FALSE((
|
||||
blocks::valid_jacobian_form<form, foreign_value_jacobian>
|
||||
));
|
||||
STATIC_REQUIRE_FALSE((blocks::valid_jacobian_form<form, foreign_value_jacobian>));
|
||||
|
||||
STATIC_REQUIRE_FALSE((
|
||||
blocks::valid_jacobian_form<form, duplicate_value_jacobian>
|
||||
));
|
||||
STATIC_REQUIRE_FALSE((blocks::valid_jacobian_form<form, duplicate_value_jacobian>));
|
||||
|
||||
STATIC_REQUIRE_FALSE((
|
||||
blocks::valid_jacobian_form<form, unknown_residual_jacobian>
|
||||
));
|
||||
STATIC_REQUIRE_FALSE((blocks::valid_jacobian_form<form, unknown_residual_jacobian>));
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
@@ -826,21 +599,17 @@ TEST_CASE(
|
||||
using form = blocks::barotropic_equilibrium_form;
|
||||
|
||||
// Columns: [rho, d, g, Phi, h, C]
|
||||
const std::array<int, form::value_block_count> value_sizes{11, 13, 17,
|
||||
19, 23, 1};
|
||||
const std::array<int, form::value_block_count> value_sizes{11, 13, 17, 19, 23, 1};
|
||||
|
||||
// Rows: [R_g, R_Phi, R_rho, R_d, R_h, R_M]
|
||||
const std::array<int, form::residual_block_count> residual_sizes{17, 19, 11,
|
||||
13, 23, 1};
|
||||
const std::array<int, form::residual_block_count> residual_sizes{17, 19, 11, 13, 23, 1};
|
||||
|
||||
const blocks::form_layout<form> layout(value_sizes, residual_sizes);
|
||||
|
||||
constexpr auto constant_value = blocks::get_value_block<form>(
|
||||
blocks::barotropic_constant_field.mass_normalization_term
|
||||
);
|
||||
constexpr auto mass_residual = blocks::get_residual_block<form>(
|
||||
blocks::barotropic_constant_field.mass_normalization_term
|
||||
);
|
||||
constexpr auto constant_value =
|
||||
blocks::get_value_block<form>(blocks::barotropic_constant_field.mass_normalization_term);
|
||||
constexpr auto mass_residual =
|
||||
blocks::get_residual_block<form>(blocks::barotropic_constant_field.mass_normalization_term);
|
||||
|
||||
CHECK(layout.size(constant_value) == 1);
|
||||
CHECK(layout.size(mass_residual) == 1);
|
||||
@@ -851,11 +620,7 @@ TEST_CASE(
|
||||
CHECK(layout.value_offsets().Last() == 84);
|
||||
CHECK(layout.residual_offsets().Last() == 84);
|
||||
|
||||
const std::array<int, form::value_block_count> invalid_value_sizes{11, 13,
|
||||
17, 19,
|
||||
23, 2};
|
||||
const std::array<int, form::value_block_count> invalid_value_sizes{11, 13, 17, 19, 23, 2};
|
||||
|
||||
CHECK_THROWS(
|
||||
blocks::form_layout<form>(invalid_value_sizes, residual_sizes)
|
||||
);
|
||||
CHECK_THROWS(blocks::form_layout<form>(invalid_value_sizes, residual_sizes));
|
||||
}
|
||||
1135
tests/utils/domain.cpp
Normal file
1135
tests/utils/domain.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user