#include #include #include #include import mean_field; import test_helpers; using namespace mean_field; namespace { namespace blocks = utils::blocks; template struct contains_type; template struct contains_type> : std::false_type { }; template struct contains_type> : std::conditional_t< std::is_same_v, std::true_type, contains_type>> { }; template inline constexpr bool contains_type_v = contains_type::value; template struct type_at; template struct type_at<0, blocks::type_list> { using type = Head; }; template struct type_at> { static_assert(index > 0); using type = typename type_at>::type; }; template using type_at_t = typename type_at::type; template struct block_row_traits; template struct block_row_traits> { using residual = Residual; using values = blocks::type_list; static constexpr int value_count = sizeof...(Values); }; template inline constexpr bool row_has_exact_values_v = block_row_traits::value_count == sizeof...(ExpectedValues) && (contains_type_v< ExpectedValues, typename block_row_traits::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 ); STATIC_REQUIRE(std::is_base_of_v); STATIC_REQUIRE( std::is_base_of_v< blocks::residual_block_base, blocks::residual_block<0>> ); STATIC_REQUIRE( std::is_base_of_v> ); STATIC_REQUIRE(std::is_base_of_v); STATIC_REQUIRE(std::is_base_of_v); STATIC_REQUIRE(std::is_base_of_v); STATIC_REQUIRE(std::is_base_of_v); STATIC_REQUIRE(std::is_base_of_v); STATIC_REQUIRE( std::is_base_of_v ); STATIC_REQUIRE(std::is_base_of_v); STATIC_REQUIRE(std::is_base_of_v); STATIC_REQUIRE(std::is_base_of_v); 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); STATIC_REQUIRE(std::is_empty_v); STATIC_REQUIRE(std::is_empty_v); STATIC_REQUIRE(std::is_empty_v); STATIC_REQUIRE(std::is_empty_v); STATIC_REQUIRE(std::is_empty_v); STATIC_REQUIRE(std::is_empty_v); STATIC_REQUIRE(std::is_empty_v); STATIC_REQUIRE( std::is_base_of_v ); 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); STATIC_REQUIRE( std::is_empty_v ); 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 == 0 ); STATIC_REQUIRE( blocks::type_index_v == 1 ); STATIC_REQUIRE( blocks::type_index_v == 2 ); STATIC_REQUIRE( blocks::type_index_v == 3 ); STATIC_REQUIRE( std::is_same_v, 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, blocks::gravity::gradient::value> ); STATIC_REQUIRE( std::is_same_v, 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
(blocks::density_field.mass_term); constexpr auto displacement_value = blocks::get_value_block(blocks::displacement_field.geometry_term); constexpr auto gravity_gradient_value = blocks::get_value_block(blocks::gravity_field.gradient_term); constexpr auto gravity_potential_value = blocks::get_value_block(blocks::gravity_field.poisson_term); constexpr auto gravity_gradient_residual = blocks::get_residual_block(blocks::gravity_field.gradient_term); constexpr auto gravity_poisson_residual = blocks::get_residual_block(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, blocks::value_block<0>> ); STATIC_REQUIRE( std::is_same_v< std::remove_cv_t, blocks::value_block<1>> ); STATIC_REQUIRE( std::is_same_v< std::remove_cv_t, blocks::value_block<2>> ); STATIC_REQUIRE( std::is_same_v< std::remove_cv_t, blocks::value_block<3>> ); STATIC_REQUIRE( std::is_same_v< std::remove_cv_t, blocks::residual_block<0>> ); STATIC_REQUIRE( std::is_same_v< std::remove_cv_t, blocks::residual_block<1>> ); CHECK(static_cast(density_value) == 0); CHECK(static_cast(displacement_value) == 1); CHECK(static_cast(gravity_gradient_value) == 2); CHECK(static_cast(gravity_potential_value) == 3); CHECK(static_cast(gravity_gradient_residual) == 0); CHECK(static_cast(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(blocks::density_field.mass_term); constexpr auto gravity_potential_value = blocks::get_value_block(blocks::gravity_field.poisson_term); constexpr auto gravity_gradient_residual = blocks::get_residual_block(blocks::gravity_field.gradient_term); constexpr auto gravity_poisson_residual = blocks::get_residual_block(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( blocks::gravity_field.poisson_term ); constexpr auto gravity_gradient_value = blocks::get_value_block( blocks::gravity_field.gradient_term ); constexpr auto density_value = blocks::get_value_block( blocks::density_field.mass_term ); constexpr auto displacement_value = blocks::get_value_block( blocks::displacement_field.geometry_term ); constexpr auto gravity_poisson_residual = blocks::get_residual_block( blocks::gravity_field.poisson_term ); constexpr auto gravity_gradient_residual = blocks::get_residual_block( blocks::gravity_field.gradient_term ); STATIC_REQUIRE(static_cast(gravity_potential_value) == 0); STATIC_REQUIRE(static_cast(gravity_gradient_value) == 1); STATIC_REQUIRE(static_cast(density_value) == 2); STATIC_REQUIRE(static_cast(displacement_value) == 3); STATIC_REQUIRE(static_cast(gravity_poisson_residual) == 0); STATIC_REQUIRE(static_cast(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 value_sizes{11, 13, 17, 19}; const std::array residual_sizes{23, 29}; const blocks::form_layout layout(value_sizes, residual_sizes); constexpr auto density_value = blocks::get_value_block(blocks::density_field.mass_term); constexpr auto displacement_value = blocks::get_value_block(blocks::displacement_field.geometry_term); constexpr auto gravity_gradient_value = blocks::get_value_block(blocks::gravity_field.gradient_term); constexpr auto gravity_potential_value = blocks::get_value_block(blocks::gravity_field.poisson_term); constexpr auto gravity_gradient_residual = blocks::get_residual_block(blocks::gravity_field.gradient_term); constexpr auto gravity_poisson_residual = blocks::get_residual_block(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 value_sizes{3, 0, 5, 0}; const std::array residual_sizes{0, 7}; const blocks::form_layout layout(value_sizes, residual_sizes); constexpr auto density_value = blocks::get_value_block(blocks::density_field.mass_term); constexpr auto displacement_value = blocks::get_value_block(blocks::displacement_field.geometry_term); constexpr auto gravity_gradient_value = blocks::get_value_block(blocks::gravity_field.gradient_term); constexpr auto gravity_potential_value = blocks::get_value_block(blocks::gravity_field.poisson_term); constexpr auto gravity_gradient_residual = blocks::get_residual_block(blocks::gravity_field.gradient_term); constexpr auto gravity_poisson_residual = blocks::get_residual_block(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 value_sizes{3, 5, 7, 11}; const std::array residual_sizes{13, 17}; const blocks::form_layout layout(value_sizes, residual_sizes); constexpr auto density_value = blocks::get_value_block(blocks::density_field.mass_term); constexpr auto displacement_value = blocks::get_value_block(blocks::displacement_field.geometry_term); constexpr auto gravity_gradient_value = blocks::get_value_block(blocks::gravity_field.gradient_term); constexpr auto gravity_potential_value = blocks::get_value_block(blocks::gravity_field.poisson_term); constexpr auto gravity_gradient_residual = blocks::get_residual_block(blocks::gravity_field.gradient_term); constexpr auto gravity_poisson_residual = blocks::get_residual_block(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 value_sizes{3, 5, 7, 11}; const std::array residual_sizes{13, 17}; const blocks::form_layout layout(value_sizes, residual_sizes); constexpr auto density_value = blocks::get_value_block(blocks::density_field.mass_term); constexpr auto gravity_gradient_residual = blocks::get_residual_block(blocks::gravity_field.gradient_term); constexpr auto gravity_poisson_residual = blocks::get_residual_block(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; using poisson_traits = block_row_traits; 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(blocks::density_field.mass_term); constexpr auto displacement_value = blocks::get_value_block(blocks::displacement_field.geometry_term); constexpr auto gravity_gradient_value = blocks::get_value_block(blocks::gravity_field.gradient_term); constexpr auto gravity_potential_value = blocks::get_value_block(blocks::gravity_field.poisson_term); constexpr auto enthalpy_value = blocks::get_value_block(blocks::enthalpy_field.specific_term); constexpr auto barotropic_constant_value = blocks::get_value_block( blocks::barotropic_constant_field.mass_normalization_term ); constexpr auto gravity_gradient_residual = blocks::get_residual_block(blocks::gravity_field.gradient_term); constexpr auto gravity_poisson_residual = blocks::get_residual_block(blocks::gravity_field.poisson_term); constexpr auto density_residual = blocks::get_residual_block(blocks::density_field.mass_term); constexpr auto displacement_residual = blocks::get_residual_block( blocks::displacement_field.geometry_term ); constexpr auto enthalpy_residual = blocks::get_residual_block(blocks::enthalpy_field.specific_term); constexpr auto mass_residual = blocks::get_residual_block( 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(density_value) == 0); STATIC_REQUIRE(static_cast(displacement_value) == 1); STATIC_REQUIRE(static_cast(gravity_gradient_value) == 2); STATIC_REQUIRE(static_cast(gravity_potential_value) == 3); STATIC_REQUIRE(static_cast(enthalpy_value) == 4); STATIC_REQUIRE(static_cast(barotropic_constant_value) == 5); STATIC_REQUIRE(static_cast(gravity_gradient_residual) == 0); STATIC_REQUIRE(static_cast(gravity_poisson_residual) == 1); STATIC_REQUIRE(static_cast(density_residual) == 2); STATIC_REQUIRE(static_cast(displacement_residual) == 3); STATIC_REQUIRE(static_cast(enthalpy_residual) == 4); STATIC_REQUIRE(static_cast(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; 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>; STATIC_REQUIRE(blocks::block_form_is_valid_v); STATIC_REQUIRE((blocks::valid_jacobian_form)); STATIC_REQUIRE_FALSE(blocks::block_form_is_valid_v); STATIC_REQUIRE_FALSE((blocks::valid_jacobian_form)); STATIC_REQUIRE_FALSE((blocks::valid_jacobian_form)); STATIC_REQUIRE_FALSE((blocks::valid_jacobian_form)); STATIC_REQUIRE_FALSE(( blocks::valid_jacobian_form )); STATIC_REQUIRE_FALSE(( blocks::valid_jacobian_form )); STATIC_REQUIRE_FALSE(( blocks::valid_jacobian_form )); 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 value_sizes{11, 13, 17, 19, 23, 1}; // Rows: [R_g, R_Phi, R_rho, R_d, R_h, R_M] const std::array residual_sizes{17, 19, 11, 13, 23, 1}; const blocks::form_layout layout(value_sizes, residual_sizes); constexpr auto constant_value = blocks::get_value_block( blocks::barotropic_constant_field.mass_normalization_term ); constexpr auto mass_residual = blocks::get_residual_block( 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 invalid_value_sizes{11, 13, 17, 19, 23, 2}; CHECK_THROWS( blocks::form_layout(invalid_value_sizes, residual_sizes) ); }