From ce3be63744694d628cae16a9ecadc498e5d91eda Mon Sep 17 00:00:00 2001 From: Maximilian Ludwig Date: Mon, 31 Aug 2026 09:31:31 +0200 Subject: [PATCH] Unify reduced lung test mesh construction The junction and boundary condition tests built their meshes from RedAirway elements, which they only used as generic line2 elements. They now share the mesh helpers of the helpers test, which build through the production discretization builder instead. Co-Authored-By: Claude Opus 5 --- ..._reduced_lung_boundary_conditions_test.cpp | 28 +-------- .../tests/4C_reduced_lung_helpers_test.cpp | 49 +++------------- .../tests/4C_reduced_lung_junctions_test.cpp | 45 ++++----------- .../tests/4C_reduced_lung_test_utils_test.hpp | 57 +++++++++++++++++++ 4 files changed, 79 insertions(+), 100 deletions(-) diff --git a/src/reduced_lung/tests/4C_reduced_lung_boundary_conditions_test.cpp b/src/reduced_lung/tests/4C_reduced_lung_boundary_conditions_test.cpp index f4a079d004c..3f8bdc2ece0 100644 --- a/src/reduced_lung/tests/4C_reduced_lung_boundary_conditions_test.cpp +++ b/src/reduced_lung/tests/4C_reduced_lung_boundary_conditions_test.cpp @@ -13,7 +13,7 @@ #include "4C_linalg_map.hpp" #include "4C_linalg_sparsematrix.hpp" #include "4C_linalg_vector.hpp" -#include "4C_red_airways_elementbase.hpp" +#include "4C_reduced_lung_test_utils_test.hpp" #include "4C_unittest_utils_assertions_test.hpp" #include "4C_utils_exceptions.hpp" #include "4C_utils_function_manager.hpp" @@ -36,29 +36,6 @@ namespace using namespace FourC::ReducedLung; using namespace FourC::ReducedLung::BoundaryConditions; - std::unique_ptr make_airway_discretization( - const std::vector& node_ids, const std::vector>& element_nodes) - { - auto dis = - std::make_unique("boundary_conditions_test", MPI_COMM_WORLD, 3); - - for (int node_id : node_ids) - { - std::array coords{static_cast(node_id), 0.0, 0.0}; - dis->add_node(coords, node_id, nullptr); - } - - for (size_t i = 0; i < element_nodes.size(); ++i) - { - auto ele = std::make_shared(static_cast(i), 0); - ele->set_node_ids(2, element_nodes[i].data()); - dis->add_element(ele); - } - - dis->fill_complete(Core::FE::OptionsFillComplete::none()); - return dis; - } - using InputBc = ReducedLungParameters::BoundaryConditions; //! Boundary condition input together with the constrained nodes of the mesh it refers to. @@ -199,7 +176,8 @@ namespace BoundaryConditionFixture make_fixture() { BoundaryConditionFixture fixture; - fixture.discretization = make_airway_discretization({0, 1, 2}, {{0, 1}, {1, 2}}); + fixture.discretization = + ReducedLung::TestUtils::make_chain_discretization("boundary_conditions_test", 2); fixture.set_bc_input(make_constant_parameters()); fixture.ele_ids_per_node = {{0, {0}}, {1, {0, 1}}, {2, {1}}}; fixture.global_dof_per_ele = {{0, 3}, {1, 3}}; diff --git a/src/reduced_lung/tests/4C_reduced_lung_helpers_test.cpp b/src/reduced_lung/tests/4C_reduced_lung_helpers_test.cpp index 35fc86fcfc1..65e5bf6465e 100644 --- a/src/reduced_lung/tests/4C_reduced_lung_helpers_test.cpp +++ b/src/reduced_lung/tests/4C_reduced_lung_helpers_test.cpp @@ -13,6 +13,7 @@ #include "4C_io_input_field.hpp" #include "4C_linalg_map.hpp" #include "4C_rebalance.hpp" +#include "4C_reduced_lung_test_utils_test.hpp" #include @@ -127,45 +128,6 @@ namespace return params; } - std::unique_ptr make_discretization( - const std::vector>& node_coordinates, - const std::vector>& element_nodes, const char* name) - { - auto discretization = std::make_unique(name, MPI_COMM_WORLD, 3); - Core::Rebalance::RebalanceParameters rebalance_parameters; - build_discretization_from_nodes_and_elements( - *discretization, node_coordinates, element_nodes, rebalance_parameters); - discretization->fill_complete(Core::FE::OptionsFillComplete{ - .assign_degrees_of_freedom = true, - .init_elements = true, - .do_boundary_conditions = false, - }); - return discretization; - } - - //! A straight chain of @p num_elements line2 elements with unit length along the x-axis. - std::unique_ptr make_chain_discretization( - const int num_elements, const char* name) - { - std::vector> node_coordinates; - std::vector> element_nodes; - for (int node_id = 0; node_id <= num_elements; ++node_id) - { - node_coordinates.push_back({static_cast(node_id), 0.0, 0.0}); - if (node_id > 0) element_nodes.push_back({node_id - 1, node_id}); - } - return make_discretization(node_coordinates, element_nodes, name); - } - - //! A single element splitting into two elements at its outlet node. - std::unique_ptr make_bifurcation_discretization(const char* name) - { - const std::vector> node_coordinates{ - {0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {2.0, 1.0, 0.0}, {2.0, -1.0, 0.0}}; - const std::vector> element_nodes{{0, 1}, {1, 2}, {1, 3}}; - return make_discretization(node_coordinates, element_nodes, name); - } - TEST(ReducedLungHelpersTests, CreateGlobalDofMapsBuildsOffsetsInElementOrder) { const std::map local_dof_per_ele{{7, 3}, {1, 4}, {5, 2}}; @@ -308,7 +270,8 @@ namespace TEST(ReducedLungHelpersTests, CreateLocalElementModelsBuildsModelContainers) { const auto params = make_setup_model_parameters(); - auto discretization = make_chain_discretization(3, "local_element_models_test"); + auto discretization = + ReducedLung::TestUtils::make_chain_discretization("local_element_models_test", 3); Airways::AirwayContainer airways; TerminalUnits::TerminalUnitContainer terminal_units; @@ -355,7 +318,8 @@ namespace TEST(ReducedLungHelpersTests, CreateLocalElementModelsUsesSelectedModelStateCount) { const auto params = make_revisited_airway_model_parameters(); - auto discretization = make_chain_discretization(4, "revisited_airway_model_test"); + auto discretization = + ReducedLung::TestUtils::make_chain_discretization("revisited_airway_model_test", 4); Airways::AirwayContainer airways; TerminalUnits::TerminalUnitContainer terminal_units; @@ -377,7 +341,8 @@ namespace TEST(ReducedLungHelpersTests, CreateGlobalEleIdsPerNodeCollectsAdjacency) { - auto discretization = make_bifurcation_discretization("global_ele_ids_test"); + auto discretization = + ReducedLung::TestUtils::make_bifurcation_discretization("global_ele_ids_test"); auto global_ele_ids_per_node = create_global_ele_ids_per_node(*discretization, MPI_COMM_WORLD); ASSERT_EQ(global_ele_ids_per_node.size(), 4u); diff --git a/src/reduced_lung/tests/4C_reduced_lung_junctions_test.cpp b/src/reduced_lung/tests/4C_reduced_lung_junctions_test.cpp index d0df966011f..4ce0802254f 100644 --- a/src/reduced_lung/tests/4C_reduced_lung_junctions_test.cpp +++ b/src/reduced_lung/tests/4C_reduced_lung_junctions_test.cpp @@ -13,7 +13,7 @@ #include "4C_linalg_map.hpp" #include "4C_linalg_sparsematrix.hpp" #include "4C_linalg_vector.hpp" -#include "4C_red_airways_elementbase.hpp" +#include "4C_reduced_lung_test_utils_test.hpp" #include "4C_unittest_utils_assertions_test.hpp" #include "4C_utils_exceptions.hpp" @@ -55,28 +55,6 @@ namespace } } - std::unique_ptr make_airway_discretization( - const std::vector& node_ids, const std::vector>& element_nodes) - { - auto dis = std::make_unique("junctions_test", MPI_COMM_WORLD, 3); - - for (int node_id : node_ids) - { - std::array coords{static_cast(node_id), 0.0, 0.0}; - dis->add_node(coords, node_id, nullptr); - } - - for (size_t i = 0; i < element_nodes.size(); ++i) - { - auto ele = std::make_shared(static_cast(i), 0); - ele->set_node_ids(2, element_nodes[i].data()); - dis->add_element(ele); - } - - dis->fill_complete(Core::FE::OptionsFillComplete::none()); - return dis; - } - TEST(JunctionsTests, ConnectionResidualAssembly) { ConnectionData connections; @@ -226,9 +204,9 @@ namespace GTEST_SKIP() << "Junction creation tests require a serial communicator."; } - auto dis = make_airway_discretization({1, 2, 3}, {{1, 2}, {2, 3}}); + auto dis = ReducedLung::TestUtils::make_chain_discretization("junctions_test", 2); - std::map> ele_ids_per_node{{1, {0}}, {2, {0, 1}}, {3, {1}}}; + std::map> ele_ids_per_node{{0, {0}}, {1, {0, 1}}, {2, {1}}}; std::map global_dof_per_ele{{0, 3}, {1, 3}}; std::map first_global_dof_of_ele{{0, 0}, {1, 3}}; @@ -256,9 +234,9 @@ namespace GTEST_SKIP() << "Junction creation tests require a serial communicator."; } - auto dis = make_airway_discretization({1, 2, 3, 4}, {{1, 2}, {2, 3}, {2, 4}}); + auto dis = ReducedLung::TestUtils::make_bifurcation_discretization("junctions_test"); - std::map> ele_ids_per_node{{1, {0}}, {2, {0, 1, 2}}, {3, {1}}, {4, {2}}}; + std::map> ele_ids_per_node{{0, {0}}, {1, {0, 1, 2}}, {2, {1}}, {3, {2}}}; std::map global_dof_per_ele{{0, 3}, {1, 3}, {2, 3}}; std::map first_global_dof_of_ele{{0, 0}, {1, 3}, {2, 6}}; @@ -287,9 +265,9 @@ namespace GTEST_SKIP() << "Junction creation tests require a serial communicator."; } - auto dis = make_airway_discretization({1, 2, 3}, {{1, 2}, {2, 3}}); + auto dis = ReducedLung::TestUtils::make_chain_discretization("junctions_test", 2); - std::map> ele_ids_per_node{{1, {0}}, {3, {1}}}; + std::map> ele_ids_per_node{{0, {0}}, {2, {1}}}; std::map global_dof_per_ele{{0, 3}, {1, 3}}; std::map first_global_dof_of_ele{{0, 0}, {1, 3}}; @@ -310,9 +288,10 @@ namespace GTEST_SKIP() << "Junction creation tests require a serial communicator."; } - auto dis = make_airway_discretization({1, 2, 3}, {{1, 2}, {3, 2}}); + auto dis = ReducedLung::TestUtils::make_line2_discretization( + "junctions_test", {{0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {2.0, 0.0, 0.0}}, {{0, 1}, {2, 1}}); - std::map> ele_ids_per_node{{1, {0}}, {2, {0, 1}}, {3, {1}}}; + std::map> ele_ids_per_node{{0, {0}}, {1, {0, 1}}, {2, {1}}}; std::map global_dof_per_ele{{0, 3}, {1, 3}}; std::map first_global_dof_of_ele{{0, 0}, {1, 3}}; @@ -333,9 +312,9 @@ namespace GTEST_SKIP() << "Junction creation tests require a serial communicator."; } - auto dis = make_airway_discretization({1, 2, 3}, {{1, 2}, {2, 3}}); + auto dis = ReducedLung::TestUtils::make_chain_discretization("junctions_test", 2); - std::map> ele_ids_per_node{{1, {0}}, {2, {0, 1, 2, 3}}, {3, {1}}}; + std::map> ele_ids_per_node{{0, {0}}, {1, {0, 1, 2, 3}}, {2, {1}}}; std::map global_dof_per_ele{{0, 3}, {1, 3}}; std::map first_global_dof_of_ele{{0, 0}, {1, 3}}; diff --git a/src/reduced_lung/tests/4C_reduced_lung_test_utils_test.hpp b/src/reduced_lung/tests/4C_reduced_lung_test_utils_test.hpp index f5262b181a3..8904a07b36b 100644 --- a/src/reduced_lung/tests/4C_reduced_lung_test_utils_test.hpp +++ b/src/reduced_lung/tests/4C_reduced_lung_test_utils_test.hpp @@ -13,12 +13,19 @@ #include "4C_config.hpp" +#include "4C_fem_discretization.hpp" #include "4C_linalg_map.hpp" #include "4C_linalg_sparsematrix.hpp" #include "4C_linalg_vector.hpp" +#include "4C_rebalance.hpp" #include "4C_reduced_lung_airways.hpp" +#include "4C_reduced_lung_helpers.hpp" #include "4C_reduced_lung_terminal_unit.hpp" +#include + +#include +#include #include #include @@ -26,6 +33,56 @@ FOUR_C_NAMESPACE_OPEN namespace ReducedLung::TestUtils { + /** + * Build a line2 discretization from nodal coordinates and connectivity, using the same routine + * as the production code. Nodes and elements get 0-based ids in the order they are passed; + * @p element_nodes is the connectivity [node_in, node_out] referring to those node ids. + */ + inline std::unique_ptr make_line2_discretization( + const std::string& name, const std::vector>& node_coordinates, + const std::vector>& element_nodes) + { + auto discretization = std::make_unique(name, MPI_COMM_WORLD, 3); + const Core::Rebalance::RebalanceParameters rebalance_parameters{}; + build_discretization_from_nodes_and_elements( + *discretization, node_coordinates, element_nodes, rebalance_parameters); + discretization->fill_complete(Core::FE::OptionsFillComplete{ + .assign_degrees_of_freedom = true, + .init_elements = true, + .do_boundary_conditions = false, + }); + + return discretization; + } + + /** + * Build a straight line discretization along the x-axis with 0-based node ids. + * The discretization is bifurcation-free consisting of @p num_elements line2 elements of unit + * length. + */ + inline std::unique_ptr make_chain_discretization( + const std::string& name, const int num_elements) + { + std::vector> node_coordinates; + std::vector> element_nodes; + for (int node_id = 0; node_id <= num_elements; ++node_id) + { + node_coordinates.push_back({static_cast(node_id), 0.0, 0.0}); + if (node_id > 0) element_nodes.push_back({node_id - 1, node_id}); + } + return make_line2_discretization(name, node_coordinates, element_nodes); + } + + //! A single element splitting into two elements at its outlet node. + inline std::unique_ptr make_bifurcation_discretization( + const std::string& name) + { + const std::vector> node_coordinates{ + {0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {2.0, 1.0, 0.0}, {2.0, -1.0, 0.0}}; + const std::vector> element_nodes{{0, 1}, {1, 2}, {1, 3}}; + return make_line2_discretization(name, node_coordinates, element_nodes); + } + // Generalized helper to check a Jacobian column against a central finite-difference // approximation of the residual. Works for both TerminalUnitModel and AirwayModel as long // as the model exposes `data`, `residual_evaluator` and the data structure