/* SPDX-FileCopyrightText: Copyright (c) 2016 - 2026, the adamantine authors. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #define BOOST_TEST_MODULE Geometry #include #include #include #include #include #include #include #include #include #include "main.cc" namespace utf = boost::unit_test; template void check_material_id( dealii::parallel::distributed::Triangulation const &tria, dealii::types::boundary_id top_boundary) { for (auto cell : dealii::filter_iterators(tria.active_cell_iterators(), dealii::IteratorFilters::LocallyOwnedCell())) { if (cell->at_boundary()) { bool powder = false; for (unsigned int i = 0; i < dealii::GeometryInfo::faces_per_cell; ++i) { if ((cell->face(i)->at_boundary()) && (cell->face(i)->boundary_id() == top_boundary)) { BOOST_TEST(cell->user_index() == static_cast( adamantine::SolidLiquidPowder::State::powder)); powder = true; break; } } if (powder == false) BOOST_TEST(cell->user_index() == static_cast( adamantine::SolidLiquidPowder::State::solid)); } else BOOST_TEST(cell->user_index() == static_cast( adamantine::SolidLiquidPowder::State::solid)); } } BOOST_AUTO_TEST_CASE(geometry_2D) { MPI_Comm communicator = MPI_COMM_WORLD; boost::property_tree::ptree database; database.put("import_mesh", false); database.put("length", 12); database.put("length_divisions", 4); database.put("height", 6); database.put("height_divisions", 5); database.put("material_height", 6.); database.put("use_powder", true); database.put("powder_layer", 1.2); boost::optional units_optional_database; adamantine::Geometry<2> geometry(communicator, database, units_optional_database); dealii::parallel::distributed::Triangulation<2> const &tria = geometry.get_triangulation(); BOOST_TEST(tria.n_active_cells() == 20); dealii::types::boundary_id const top_boundary = 3; check_material_id(tria, top_boundary); } BOOST_AUTO_TEST_CASE(geometry_3D) { MPI_Comm communicator = MPI_COMM_WORLD; boost::property_tree::ptree database; database.put("import_mesh", false); database.put("length", 12); database.put("length_divisions", 4); database.put("height", 4); database.put("height_divisions", 2); database.put("width", 6); database.put("width_divisions", 5); database.put("material_height", 4); database.put("use_powder", true); database.put("powder_layer", 2); boost::optional units_optional_database; adamantine::Geometry<3> geometry(communicator, database, units_optional_database); dealii::parallel::distributed::Triangulation<3> const &tria = geometry.get_triangulation(); BOOST_TEST(tria.n_active_cells() == 40); dealii::types::boundary_id const top_boundary = 5; check_material_id(tria, top_boundary); } BOOST_AUTO_TEST_CASE(geometry_shifted_origin, *utf::tolerance(1e-12)) { MPI_Comm communicator = MPI_COMM_WORLD; boost::property_tree::ptree database; database.put("import_mesh", false); database.put("length", 12); database.put("length_divisions", 4); database.put("height", 4); database.put("height_divisions", 2); database.put("width", 6); database.put("width_divisions", 5); database.put("material_height", 4); database.put("use_powder", true); database.put("powder_layer", 2); database.put("length_origin", 2.); database.put("height_origin", -1.); database.put("width_origin", 3.); boost::optional units_optional_database; adamantine::Geometry<3> geometry(communicator, database, units_optional_database); dealii::parallel::distributed::Triangulation<3> const &tria = geometry.get_triangulation(); auto bounding_box = dealii::GridTools::compute_bounding_box(tria); BOOST_TEST(bounding_box.get_boundary_points().first(0) == 2.); BOOST_TEST(bounding_box.get_boundary_points().first(1) == 3.); BOOST_TEST(bounding_box.get_boundary_points().first(2) == -1.); BOOST_TEST(bounding_box.get_boundary_points().second(0) == 14.); BOOST_TEST(bounding_box.get_boundary_points().second(1) == 9.); BOOST_TEST(bounding_box.get_boundary_points().second(2) == 3.); } BOOST_AUTO_TEST_CASE(gmsh) { MPI_Comm communicator = MPI_COMM_WORLD; boost::property_tree::ptree database; database.put("import_mesh", true); database.put("mesh_file", "extruded_cube.msh"); database.put("mesh_format", "gmsh"); database.put("material_height", 1.); database.put("use_powder", true); database.put("powder_layer", 0.05); boost::optional units_optional_database; adamantine::Geometry<3> geometry(communicator, database, units_optional_database); dealii::parallel::distributed::Triangulation<3> const &tria = geometry.get_triangulation(); BOOST_TEST(tria.n_active_cells() == 320); dealii::types::boundary_id const top_boundary = 1; check_material_id(tria, top_boundary); } BOOST_AUTO_TEST_CASE(read_stl) { MPI_Comm communicator = MPI_COMM_WORLD; boost::property_tree::ptree database; database.put("import_mesh", false); database.put("length", 12); database.put("length_divisions", 4); database.put("height", 4); database.put("height_divisions", 2); database.put("width", 6); database.put("width_divisions", 5); database.put("material_height", 4); database.put("use_powder", true); database.put("powder_layer", 2); database.put("stl_filename", "Simple_3D_ring.stl"); boost::optional units_optional_database; adamantine::Geometry<3> geometry(communicator, database, units_optional_database); // The STL file contains 512 triangles. The bounding box of the ring is // 21x21x4. auto stl_triangles = geometry.get_stl_triangles(); BOOST_TEST(stl_triangles.extent(0) == 512); double x_min = 1000.0; double x_max = -1000.0; double y_min = 1000.0; double y_max = -1000.0; double z_min = 1000.0; double z_max = -1000.0; for (unsigned int i = 0; i < stl_triangles.extent(0); ++i) { auto t = stl_triangles(i); x_min = std::min(t.a[0], x_min); x_max = std::max(t.a[0], x_max); y_min = std::min(t.a[1], y_min); y_max = std::max(t.a[1], y_max); z_min = std::min(t.a[2], z_min); z_max = std::max(t.a[2], z_max); x_min = std::min(t.b[0], x_min); x_max = std::max(t.b[0], x_max); y_min = std::min(t.b[1], y_min); y_max = std::max(t.b[1], y_max); z_min = std::min(t.b[2], z_min); z_max = std::max(t.b[2], z_max); x_min = std::min(t.c[0], x_min); x_max = std::max(t.c[0], x_max); y_min = std::min(t.c[1], y_min); y_max = std::max(t.c[1], y_max); z_min = std::min(t.c[2], z_min); z_max = std::max(t.c[2], z_max); } BOOST_TEST(x_max - x_min == 21.0); BOOST_TEST(y_max - y_min == 21.0); BOOST_TEST(z_max - z_min == 4.0); } BOOST_AUTO_TEST_CASE(geometry_stl_mesh, *utf::tolerance(1e-12)) { MPI_Comm communicator = MPI_COMM_WORLD; boost::property_tree::ptree database; database.put("import_mesh", false); database.put("length_divisions", 4); database.put("height_divisions", 2); database.put("width_divisions", 5); database.put("stl_filename", "Simple_3D_ring.stl"); boost::optional units_optional_database; adamantine::Geometry<3> geometry(communicator, database, units_optional_database); dealii::parallel::distributed::Triangulation<3> const &tria = geometry.get_triangulation(); auto bounding_box = dealii::GridTools::compute_bounding_box(tria); BOOST_TEST(bounding_box.get_boundary_points().second(0) - bounding_box.get_boundary_points().first(0) == 21.0); BOOST_TEST(bounding_box.get_boundary_points().second(1) - bounding_box.get_boundary_points().first(1) == 21.0); BOOST_TEST(bounding_box.get_boundary_points().second(2) - bounding_box.get_boundary_points().first(2) == 4.0); } #if ARBORX_VERSION_MAJOR >= 2 BOOST_AUTO_TEST_CASE(within_stl) { MPI_Comm communicator = MPI_COMM_WORLD; boost::property_tree::ptree database; database.put("import_mesh", false); database.put("length_divisions", 42); database.put("width_divisions", 42); database.put("height_divisions", 4); database.put("stl_filename", "Simple_3D_ring.stl"); boost::optional units_optional_database; adamantine::Geometry<3> geometry(communicator, database, units_optional_database); dealii::FE_Q<3> fe(1); dealii::DoFHandler<3> dof_handler(geometry.get_triangulation()); dof_handler.distribute_dofs(fe); std::vector cells_within_vec; for (auto const &cell : dof_handler.active_cell_iterators()) { bool within = geometry.is_within_stl(cell); cells_within_vec.push_back(within); } // Uncommenting the code below allows to visualize the cells that are within // the STL and to write the gold solution. /* dealii::Vector cells_within(cells_within_vec.size()); for (unsigned int i = 0; i < cells_within_vec.size(); ++i) cells_within[i] = cells_within_vec[i]; dealii::DataOut<3> data_out; data_out.attach_dof_handler(dof_handler); data_out.add_data_vector(cells_within, "within"); data_out.build_patches(); std::ofstream output("cells_within.vtu"); data_out.write_vtu(output); std::ofstream gold_file_writer("within_stl_gold.txt"); for (auto const value : cells_within_vec) gold_file_writer << value << " "; gold_file_writer.close(); */ std::ifstream gold_file("within_stl_gold.txt"); for (unsigned int i = 0; i < cells_within_vec.size(); ++i) { double gold_value = -1.; gold_file >> gold_value; BOOST_TEST(cells_within_vec[i] == gold_value); } } #endif