restricted the unknown state vector to surface deformation and implemented one prescription, NodalRadialSurface, while the full volumetric displacment field is reconstructed analytically from that. This reduced the number of degrees of freedom in the system by a factor of 80 while also removing many null vectors from the system.
662 lines
31 KiB
C++
662 lines
31 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::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>
|
|
);
|
|
|
|
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(
|
|
"Reduced Stellar Equilibrium Form Encodes Surface Shape Coordinates And Couplings",
|
|
tags::reduced_stellar_geometry &tags::unit &tags::utils
|
|
) {
|
|
using form = blocks::surface_deformed_stellar_equilibrium_form;
|
|
using jacobian = blocks::surface_deformed_stellar_equilibrium_jacobian_form;
|
|
|
|
constexpr auto surface_parameters =
|
|
blocks::get_value_block<form>(blocks::surface_deformation_field.parameters_term);
|
|
constexpr auto surface_shape =
|
|
blocks::get_residual_block<form>(blocks::surface_deformation_field.shape_equilibrium_term);
|
|
|
|
STATIC_REQUIRE(form::value_block_count == 6);
|
|
STATIC_REQUIRE(form::residual_block_count == 6);
|
|
STATIC_REQUIRE(static_cast<int>(surface_parameters) == 1);
|
|
STATIC_REQUIRE(static_cast<int>(surface_shape) == 3);
|
|
STATIC_REQUIRE((blocks::valid_jacobian_form<form, jacobian>));
|
|
|
|
STATIC_REQUIRE(
|
|
blocks::has_jacobian_coupling_v<
|
|
blocks::gravity::gradient::residual, blocks::surface_deformation::parameters::value, jacobian>
|
|
);
|
|
STATIC_REQUIRE(
|
|
blocks::has_jacobian_coupling_v<
|
|
blocks::surface_deformation::shape_equilibrium::residual, blocks::surface_deformation::parameters::value,
|
|
jacobian>
|
|
);
|
|
STATIC_REQUIRE_FALSE(blocks::contains_type_v<blocks::displacement::geometry::value, typename form::value_blocks>);
|
|
STATIC_REQUIRE_FALSE(
|
|
blocks::contains_type_v<blocks::displacement::geometry::residual, typename form::residual_blocks>
|
|
);
|
|
|
|
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));
|
|
}
|