861 lines
32 KiB
C++
861 lines
32 KiB
C++
#include <array>
|
|
#include <catch2/catch_test_macros.hpp>
|
|
#include <mfem.hpp>
|
|
#include <type_traits>
|
|
|
|
import mean_field;
|
|
import test_helpers;
|
|
|
|
using namespace mean_field;
|
|
|
|
namespace {
|
|
namespace blocks = utils::blocks;
|
|
|
|
template <typename Query, typename List> struct contains_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...>>
|
|
: std::conditional_t<
|
|
std::is_same_v<Query, Head>,
|
|
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 <int index, typename List> struct type_at;
|
|
|
|
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...>> {
|
|
static_assert(index > 0);
|
|
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 <typename Row> struct block_row_traits;
|
|
|
|
template <typename Residual, typename... Values>
|
|
struct block_row_traits<blocks::block_row<Residual, Values...>> {
|
|
using residual = Residual;
|
|
using values = blocks::type_list<Values...>;
|
|
|
|
static constexpr int value_count = sizeof...(Values);
|
|
};
|
|
|
|
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> &&
|
|
...);
|
|
|
|
struct foreign_value final : blocks::value_block_base { };
|
|
struct foreign_residual final : blocks::residual_block_base { };
|
|
} // namespace
|
|
|
|
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::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::field, blocks::density>);
|
|
STATIC_REQUIRE(std::is_base_of_v<blocks::field, blocks::displacement>);
|
|
STATIC_REQUIRE(std::is_base_of_v<blocks::field, blocks::gravity>);
|
|
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::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_empty_v<blocks::density>);
|
|
STATIC_REQUIRE(std::is_empty_v<blocks::displacement>);
|
|
STATIC_REQUIRE(std::is_empty_v<blocks::gravity>);
|
|
STATIC_REQUIRE(std::is_empty_v<blocks::enthalpy>);
|
|
STATIC_REQUIRE(std::is_empty_v<blocks::density::mass>);
|
|
STATIC_REQUIRE(std::is_empty_v<blocks::gravity::gradient>);
|
|
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::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::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(std::is_empty_v<blocks::barotropic_constant>);
|
|
STATIC_REQUIRE(
|
|
std::is_empty_v<blocks::barotropic_constant::mass_normalization>
|
|
);
|
|
|
|
CHECK(true);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Type Lists Resolve Their Types At Compile Time",
|
|
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>;
|
|
|
|
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(
|
|
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);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Gravity Field Form Resolves Value And Residual Blocks",
|
|
tags::unit &tags::solver &tags::utils
|
|
) {
|
|
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);
|
|
|
|
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>>
|
|
);
|
|
|
|
CHECK(static_cast<int>(density_value) == 0);
|
|
CHECK(static_cast<int>(displacement_value) == 1);
|
|
CHECK(static_cast<int>(gravity_gradient_value) == 2);
|
|
CHECK(static_cast<int>(gravity_potential_value) == 3);
|
|
CHECK(static_cast<int>(gravity_gradient_residual) == 0);
|
|
CHECK(static_cast<int>(gravity_poisson_residual) == 1);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Resolved Blocks Implicitly Convert For MFEM Interfaces",
|
|
tags::unit &tags::solver &tags::utils
|
|
) {
|
|
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);
|
|
|
|
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);
|
|
CHECK(consume_block_index(gravity_gradient_residual) == 0);
|
|
CHECK(consume_block_index(gravity_poisson_residual) == 1);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Block Indices Belong To Their Form",
|
|
tags::unit &tags::solver &tags::utils
|
|
) {
|
|
using reordered_form = blocks::block_form<
|
|
blocks::type_list<
|
|
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>>;
|
|
|
|
constexpr auto gravity_potential_value =
|
|
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
|
|
);
|
|
constexpr auto gravity_poisson_residual =
|
|
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
|
|
);
|
|
|
|
STATIC_REQUIRE(static_cast<int>(gravity_potential_value) == 0);
|
|
STATIC_REQUIRE(static_cast<int>(gravity_gradient_value) == 1);
|
|
STATIC_REQUIRE(static_cast<int>(density_value) == 2);
|
|
STATIC_REQUIRE(static_cast<int>(displacement_value) == 3);
|
|
STATIC_REQUIRE(static_cast<int>(gravity_poisson_residual) == 0);
|
|
STATIC_REQUIRE(static_cast<int>(gravity_gradient_residual) == 1);
|
|
|
|
CHECK(true);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Form Layout Constructs Runtime Offsets From Block Sizes",
|
|
tags::unit &tags::solver &tags::utils
|
|
) {
|
|
using form = blocks::gravity_field_form;
|
|
|
|
const std::array<int, form::value_block_count> value_sizes{11, 13, 17, 19};
|
|
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);
|
|
|
|
CHECK(layout.size(density_value) == 11);
|
|
CHECK(layout.size(displacement_value) == 13);
|
|
CHECK(layout.size(gravity_gradient_value) == 17);
|
|
CHECK(layout.size(gravity_potential_value) == 19);
|
|
CHECK(layout.size(gravity_gradient_residual) == 23);
|
|
CHECK(layout.size(gravity_poisson_residual) == 29);
|
|
|
|
CHECK(layout.offset(density_value) == 0);
|
|
CHECK(layout.offset(displacement_value) == 11);
|
|
CHECK(layout.offset(gravity_gradient_value) == 24);
|
|
CHECK(layout.offset(gravity_potential_value) == 41);
|
|
CHECK(layout.offset(gravity_gradient_residual) == 0);
|
|
CHECK(layout.offset(gravity_poisson_residual) == 23);
|
|
|
|
REQUIRE(layout.value_offsets().Size() == 5);
|
|
CHECK(layout.value_offsets()[0] == 0);
|
|
CHECK(layout.value_offsets()[1] == 11);
|
|
CHECK(layout.value_offsets()[2] == 24);
|
|
CHECK(layout.value_offsets()[3] == 41);
|
|
CHECK(layout.value_offsets()[4] == 60);
|
|
|
|
REQUIRE(layout.residual_offsets().Size() == 3);
|
|
CHECK(layout.residual_offsets()[0] == 0);
|
|
CHECK(layout.residual_offsets()[1] == 23);
|
|
CHECK(layout.residual_offsets()[2] == 52);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Form Layout Supports Empty Runtime Blocks",
|
|
tags::unit &tags::solver &tags::utils
|
|
) {
|
|
using form = blocks::gravity_field_form;
|
|
|
|
const std::array<int, form::value_block_count> value_sizes{3, 0, 5, 0};
|
|
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);
|
|
|
|
CHECK(layout.size(density_value) == 3);
|
|
CHECK(layout.size(displacement_value) == 0);
|
|
CHECK(layout.size(gravity_gradient_value) == 5);
|
|
CHECK(layout.size(gravity_potential_value) == 0);
|
|
CHECK(layout.size(gravity_gradient_residual) == 0);
|
|
CHECK(layout.size(gravity_poisson_residual) == 7);
|
|
|
|
CHECK(layout.offset(density_value) == 0);
|
|
CHECK(layout.offset(displacement_value) == 3);
|
|
CHECK(layout.offset(gravity_gradient_value) == 3);
|
|
CHECK(layout.offset(gravity_potential_value) == 8);
|
|
CHECK(layout.offset(gravity_gradient_residual) == 0);
|
|
CHECK(layout.offset(gravity_poisson_residual) == 0);
|
|
|
|
CHECK(layout.value_offsets().Last() == 8);
|
|
CHECK(layout.residual_offsets().Last() == 7);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Form Layout Integrates With MFEM Block Vectors",
|
|
tags::unit &tags::solver &tags::utils
|
|
) {
|
|
using form = blocks::gravity_field_form;
|
|
|
|
const std::array<int, form::value_block_count> value_sizes{3, 5, 7, 11};
|
|
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);
|
|
|
|
mfem::BlockVector values(layout.value_offsets());
|
|
mfem::BlockVector residuals(layout.residual_offsets());
|
|
|
|
values = 0.0;
|
|
residuals = 0.0;
|
|
|
|
values.GetBlock(density_value) = 1.0;
|
|
values.GetBlock(displacement_value) = 2.0;
|
|
values.GetBlock(gravity_gradient_value) = 3.0;
|
|
values.GetBlock(gravity_potential_value) = 4.0;
|
|
|
|
residuals.GetBlock(gravity_gradient_residual) = 5.0;
|
|
residuals.GetBlock(gravity_poisson_residual) = 6.0;
|
|
|
|
CHECK(values.GetBlock(density_value).Size() == 3);
|
|
CHECK(values.GetBlock(displacement_value).Size() == 5);
|
|
CHECK(values.GetBlock(gravity_gradient_value).Size() == 7);
|
|
CHECK(values.GetBlock(gravity_potential_value).Size() == 11);
|
|
CHECK(residuals.GetBlock(gravity_gradient_residual).Size() == 13);
|
|
CHECK(residuals.GetBlock(gravity_poisson_residual).Size() == 17);
|
|
|
|
CHECK(values.GetBlock(density_value).Min() == 1.0);
|
|
CHECK(values.GetBlock(density_value).Max() == 1.0);
|
|
CHECK(values.GetBlock(displacement_value).Min() == 2.0);
|
|
CHECK(values.GetBlock(displacement_value).Max() == 2.0);
|
|
CHECK(values.GetBlock(gravity_gradient_value).Min() == 3.0);
|
|
CHECK(values.GetBlock(gravity_gradient_value).Max() == 3.0);
|
|
CHECK(values.GetBlock(gravity_potential_value).Min() == 4.0);
|
|
CHECK(values.GetBlock(gravity_potential_value).Max() == 4.0);
|
|
CHECK(residuals.GetBlock(gravity_gradient_residual).Min() == 5.0);
|
|
CHECK(residuals.GetBlock(gravity_gradient_residual).Max() == 5.0);
|
|
CHECK(residuals.GetBlock(gravity_poisson_residual).Min() == 6.0);
|
|
CHECK(residuals.GetBlock(gravity_poisson_residual).Max() == 6.0);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Compile Time Blocks Configure An MFEM Block Operator",
|
|
tags::unit &tags::solver &tags::utils
|
|
) {
|
|
using form = blocks::gravity_field_form;
|
|
|
|
const std::array<int, form::value_block_count> value_sizes{3, 5, 7, 11};
|
|
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);
|
|
|
|
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::BlockVector values(layout.value_offsets());
|
|
mfem::BlockVector residuals(layout.residual_offsets());
|
|
|
|
values = 0.0;
|
|
residuals = 0.0;
|
|
values.GetBlock(density_value) = 2.0;
|
|
|
|
block_operator.Mult(values, residuals);
|
|
|
|
CHECK(residuals.GetBlock(gravity_gradient_residual).Norml2() == 0.0);
|
|
|
|
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) {
|
|
CHECK(poisson_residual(i) == 2.0 * value_sizes[0]);
|
|
}
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Gravity Jacobian Form Describes The Expected Couplings",
|
|
tags::unit &tags::solver &tags::utils
|
|
) {
|
|
using gradient_row = type_at_t<0, blocks::gravity_jacobian_form>;
|
|
using poisson_row = type_at_t<1, blocks::gravity_jacobian_form>;
|
|
|
|
using gradient_traits = block_row_traits<gradient_row>;
|
|
using poisson_traits = block_row_traits<poisson_row>;
|
|
|
|
STATIC_REQUIRE(blocks::gravity_jacobian_form::size == 2);
|
|
|
|
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(
|
|
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>
|
|
);
|
|
|
|
CHECK(true);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Barotropic Equilibrium Form Encodes The Agreed Row And Column Layouts",
|
|
tags::unit &tags::solver &tags::utils
|
|
) {
|
|
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 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);
|
|
|
|
STATIC_REQUIRE(static_cast<int>(density_value) == 0);
|
|
STATIC_REQUIRE(static_cast<int>(displacement_value) == 1);
|
|
STATIC_REQUIRE(static_cast<int>(gravity_gradient_value) == 2);
|
|
STATIC_REQUIRE(static_cast<int>(gravity_potential_value) == 3);
|
|
STATIC_REQUIRE(static_cast<int>(enthalpy_value) == 4);
|
|
STATIC_REQUIRE(static_cast<int>(barotropic_constant_value) == 5);
|
|
|
|
STATIC_REQUIRE(static_cast<int>(gravity_gradient_residual) == 0);
|
|
STATIC_REQUIRE(static_cast<int>(gravity_poisson_residual) == 1);
|
|
STATIC_REQUIRE(static_cast<int>(density_residual) == 2);
|
|
STATIC_REQUIRE(static_cast<int>(displacement_residual) == 3);
|
|
STATIC_REQUIRE(static_cast<int>(enthalpy_residual) == 4);
|
|
STATIC_REQUIRE(static_cast<int>(mass_residual) == 5);
|
|
|
|
CHECK(true);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Barotropic Jacobian Form Encodes The Exact Direct Coupling Graph",
|
|
tags::unit &tags::solver &tags::utils
|
|
) {
|
|
using jacobian = blocks::barotropic_equilibrium_jacobian_form;
|
|
|
|
using gradient_row = type_at_t<0, jacobian>;
|
|
using poisson_row = type_at_t<1, jacobian>;
|
|
using density_row = type_at_t<2, jacobian>;
|
|
using displacement_row = type_at_t<3, jacobian>;
|
|
using enthalpy_row = type_at_t<4, jacobian>;
|
|
using mass_row = type_at_t<5, jacobian>;
|
|
|
|
STATIC_REQUIRE(jacobian::size == 6);
|
|
|
|
STATIC_REQUIRE(
|
|
row_has_exact_values_v<
|
|
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,
|
|
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,
|
|
blocks::displacement::geometry::value>
|
|
);
|
|
|
|
STATIC_REQUIRE_FALSE(
|
|
blocks::has_jacobian_coupling_v<
|
|
blocks::displacement::geometry::residual,
|
|
blocks::gravity::poisson::value, jacobian>
|
|
);
|
|
|
|
STATIC_REQUIRE_FALSE(
|
|
blocks::has_jacobian_coupling_v<
|
|
blocks::barotropic_constant::mass_normalization::residual,
|
|
blocks::barotropic_constant::mass_normalization::value, jacobian>
|
|
);
|
|
|
|
STATIC_REQUIRE_FALSE(
|
|
blocks::has_jacobian_coupling_v<
|
|
blocks::barotropic_constant::mass_normalization::residual,
|
|
blocks::enthalpy::specific::value, jacobian>
|
|
);
|
|
|
|
CHECK(true);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Block Validators Reject Structurally Malformed Forms",
|
|
tags::unit &tags::solver &tags::utils
|
|
) {
|
|
using form = blocks::barotropic_equilibrium_form;
|
|
using jacobian = blocks::barotropic_equilibrium_jacobian_form;
|
|
|
|
using gradient_row = type_at_t<0, jacobian>;
|
|
using poisson_row = type_at_t<1, jacobian>;
|
|
using density_row = type_at_t<2, jacobian>;
|
|
using displacement_row = type_at_t<3, jacobian>;
|
|
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 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 foreign_value_row = blocks::block_row<
|
|
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 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::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 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 duplicate_value_form = blocks::block_form<
|
|
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>);
|
|
STATIC_REQUIRE((blocks::valid_jacobian_form<form, jacobian>));
|
|
|
|
STATIC_REQUIRE_FALSE(blocks::block_form_is_valid_v<duplicate_value_form>);
|
|
|
|
STATIC_REQUIRE_FALSE((blocks::valid_jacobian_form<form, missing_row>));
|
|
|
|
STATIC_REQUIRE_FALSE((blocks::valid_jacobian_form<form, duplicate_row>));
|
|
|
|
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, duplicate_value_jacobian>
|
|
));
|
|
|
|
STATIC_REQUIRE_FALSE((
|
|
blocks::valid_jacobian_form<form, unknown_residual_jacobian>
|
|
));
|
|
|
|
CHECK(true);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Barotropic Layout Preserves Distinct Row And Column Offsets",
|
|
tags::unit &tags::solver &tags::utils
|
|
) {
|
|
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};
|
|
|
|
// 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 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
|
|
);
|
|
|
|
CHECK(layout.size(constant_value) == 1);
|
|
CHECK(layout.size(mass_residual) == 1);
|
|
|
|
CHECK(layout.offset(constant_value) == 83);
|
|
CHECK(layout.offset(mass_residual) == 83);
|
|
|
|
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};
|
|
|
|
CHECK_THROWS(
|
|
blocks::form_layout<form>(invalid_value_sizes, residual_sizes)
|
|
);
|
|
} |