diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 899c014f74..ff69fc337d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -195,6 +195,9 @@ install( FILES t8_vec.h t8_version.h t8_vtk.h + src/t8_data/t8_shmem.h + src/t8_data/t8_containers.h + src/t8_data/t8_stdvector_conversion.hxx t8_windows.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include ) diff --git a/src/Makefile.am b/src/Makefile.am index f3b3b82999..3e0ad6f318 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -44,7 +44,8 @@ libt8_installed_headers_cmesh = \ src/t8_cmesh/t8_cmesh_types.h \ src/t8_cmesh/t8_cmesh_stash.h libt8_installed_headers_data = \ - src/t8_data/t8_shmem.h src/t8_data/t8_containers.h + src/t8_data/t8_shmem.h src/t8_data/t8_containers.h \ + src/t8_data/t8_stdvector_conversion.hxx libt8_installed_headers_forest = \ src/t8_forest/t8_forest.h \ src/t8_forest/t8_forest_general.h \ diff --git a/src/t8_data/t8_stdvector_conversion.hxx b/src/t8_data/t8_stdvector_conversion.hxx new file mode 100644 index 0000000000..d47c0082dd --- /dev/null +++ b/src/t8_data/t8_stdvector_conversion.hxx @@ -0,0 +1,101 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2024 the developers + + t8code is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + t8code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \file t8_stdvector_conversion.hxx + * Basic conversion routines for std::vector to t8code data types. + */ + +#ifndef T8_STDVECTOR_CONVERSION_H +#define T8_STDVECTOR_CONVERSION_H +#include +#include +#include +#include + +/* Template to create sc_array view from vector*/ +template +sc_array_t * +t8_create_sc_array_view_from_vector (const std::vector &vector) +{ + void *vector_data = (void *) vector.data (); + sc_array_t *new_view = sc_array_new_data (vector_data, sizeof (T), vector.size ()); + return new_view; +} + +/* Wrapper function for partition data */ +template +void +t8_forest_partition_data_stdvector (t8_forest_t forest_from, t8_forest_t forest_to, const std::vector &data_in_vec, + std::vector &data_out_vec) +{ + /* Create temporary sc array. */ + sc_array_t *data_in_view, *data_out_view; + + T8_ASSERT (data_in_vec.size () == t8_forest_get_local_num_elements (forest_from)); + T8_ASSERT (data_out_vec.size () == t8_forest_get_local_num_elements (forest_to)); + + data_in_view = t8_create_sc_array_view_from_vector (data_in_vec); + data_out_view = t8_create_sc_array_view_from_vector (data_out_vec); + /* calling the original function with the sc_array_t view */ + t8_forest_partition_data (forest_from, forest_to, data_in_view, data_out_view); + + /* Clean-up memory */ + sc_array_destroy (data_in_view); + sc_array_destroy (data_out_view); +} + +/* Wrapper function for ghost exchange function */ +template +void +t8_forest_ghost_exchange_data_with_vector (t8_forest_t forest, const std::vector &element_vector) +{ + T8_ASSERT (t8_forest_is_committed (forest)); + + /*Create sc_array_t view from the vector*/ + sc_array_t *element_data = t8_create_sc_array_view_from_vector (element_vector); + + /* calling the original function with the sc_array_t view */ + t8_forest_ghost_exchange_data (forest, element_data); + + /*Clean up the sc_array_t view*/ + sc_array_destroy (element_data); +} + +/*Wrapper function to handle std::vector directly for t8_forest_search*/ +template +void +t8_forest_search_with_vector (t8_forest_t forest, t8_forest_search_query_fn search_fn, + t8_forest_search_query_fn query_fn, const std::vector &query_vector) +{ + t8_debugf ("Entering t8_forest_search_with_vector\n"); + T8_ASSERT (t8_forest_is_committed (forest)); + + /*Create sc_array_t view from the vector*/ + sc_array_t *queries = t8_create_sc_array_view_from_vector (query_vector); + + /*calling the original t8_forest_search function with the sc_array_t view */ + t8_forest_search (forest, search_fn, query_fn, queries); + + /* Clean up the sc_array_t view */ + sc_array_destroy (queries); +} +#endif // T8_STDVECTOR_CONVERSION_H diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ef9f9ba71a..14304771c3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -63,6 +63,85 @@ function( copy_test_file TEST_FILE_NAME ) configure_file(${CMAKE_CURRENT_LIST_DIR}/testfiles/${TEST_FILE_NAME} ${CMAKE_CURRENT_BINARY_DIR}/test/testfiles/${TEST_FILE_NAME} COPYONLY) endfunction() + +add_t8_test( NAME t8_gtest_cmesh_bcast SOURCES t8_gtest_main.cxx t8_cmesh/t8_gtest_bcast.cxx ) +add_t8_test( NAME t8_gtest_eclass SOURCES t8_gtest_main.cxx t8_gtest_eclass.cxx ) +add_t8_test( NAME t8_gtest_vec SOURCES t8_gtest_main.cxx t8_gtest_vec.cxx ) +add_t8_test( NAME t8_gtest_mat SOURCES t8_gtest_main.cxx t8_gtest_mat.cxx ) +add_t8_test( NAME t8_gtest_refcount SOURCES t8_gtest_main.cxx t8_gtest_refcount.cxx ) +add_t8_test( NAME t8_gtest_occ_linkage SOURCES t8_gtest_main.cxx t8_gtest_occ_linkage.cxx ) +add_t8_test( NAME t8_gtest_version SOURCES t8_gtest_main.cxx t8_gtest_version.cxx ) +add_t8_test( NAME t8_gtest_basics SOURCES t8_gtest_main.cxx t8_gtest_basics.cxx ) +add_t8_test( NAME t8_gtest_netcdf_linkage SOURCES t8_gtest_main.cxx t8_gtest_netcdf_linkage.cxx ) +add_t8_test( NAME t8_gtest_vtk_linkage SOURCES t8_gtest_main.cxx t8_gtest_vtk_linkage.cxx ) + +add_t8_test( NAME t8_gtest_hypercube SOURCES t8_gtest_main.cxx t8_cmesh/t8_gtest_hypercube.cxx ) +add_t8_test( NAME t8_gtest_cmesh_readmshfile SOURCES t8_gtest_main.cxx t8_cmesh/t8_gtest_cmesh_readmshfile.cxx ) +add_t8_test( NAME t8_gtest_cmesh_copy SOURCES t8_gtest_main.cxx t8_cmesh/t8_gtest_cmesh_copy.cxx ) +add_t8_test( NAME t8_gtest_cmesh_face_is_boundary SOURCES t8_gtest_main.cxx t8_cmesh/t8_gtest_cmesh_face_is_boundary.cxx ) +add_t8_test( NAME t8_gtest_cmesh_partition SOURCES t8_gtest_main.cxx t8_cmesh/t8_gtest_cmesh_partition.cxx ) +add_t8_test( NAME t8_gtest_cmesh_set_partition_offsets SOURCES t8_gtest_main.cxx t8_cmesh/t8_gtest_cmesh_set_partition_offsets.cxx ) +add_t8_test( NAME t8_gtest_cmesh_set_join_by_vertices SOURCES t8_gtest_main.cxx t8_cmesh/t8_gtest_cmesh_set_join_by_vertices.cxx ) +add_t8_test( NAME t8_gtest_cmesh_add_attributes_when_derive SOURCES t8_gtest_main.cxx t8_cmesh/t8_gtest_cmesh_add_attributes_when_derive.cxx ) +add_t8_test( NAME t8_gtest_cmesh_tree_vertices_negative_volume SOURCES t8_gtest_main.cxx t8_cmesh/t8_gtest_cmesh_tree_vertices_negative_volume.cxx ) + +add_t8_test( NAME t8_gtest_multiple_attributes SOURCES t8_gtest_main.cxx t8_cmesh/t8_gtest_multiple_attributes.cxx ) +add_t8_test( NAME t8_gtest_attribute_gloidx_array SOURCES t8_gtest_main.cxx t8_cmesh/t8_gtest_attribute_gloidx_array.cxx ) + +add_t8_test( NAME t8_gtest_shmem SOURCES t8_gtest_main.cxx t8_data/t8_gtest_shmem.cxx ) + +add_t8_test( NAME t8_gtest_element_volume SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_element_volume.cxx ) +add_t8_test( NAME t8_gtest_search SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_search.cxx ) +add_t8_test( NAME t8_gtest_half_neighbors SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_half_neighbors.cxx ) +add_t8_test( NAME t8_gtest_find_owner SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_find_owner.cxx ) +add_t8_test( NAME t8_gtest_user_data SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_user_data.cxx ) +add_t8_test( NAME t8_gtest_transform SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_transform.cxx ) +add_t8_test( NAME t8_gtest_ghost_exchange SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_ghost_exchange.cxx ) +add_t8_test( NAME t8_gtest_ghost_delete SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_ghost_delete.cxx ) +add_t8_test( NAME t8_gtest_ghost_and_owner SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_ghost_and_owner.cxx ) +add_t8_test( NAME t8_gtest_balance SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_balance.cxx ) +add_t8_test( NAME t8_gtest_forest_commit SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_forest_commit.cxx ) +add_t8_test( NAME t8_gtest_forest_face_normal SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_forest_face_normal.cxx ) +add_t8_test( NAME t8_gtest_element_is_leaf SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_element_is_leaf.cxx ) +add_t8_test( NAME t8_gtest_partition_data SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_partition_data.cxx ) +add_t8_test( NAME stdvectortest SOURCES t8_gtest_main.cxx t8_forest/stdvectortest.cxx ) +add_t8_test( NAME t8_gtest_ghost_exchange_stdvector SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_ghost_exchange_stdvector.cxx ) + + +add_t8_test( NAME t8_gtest_permute_hole SOURCES t8_gtest_main.cxx t8_forest_incomplete/t8_gtest_permute_hole.cxx ) +add_t8_test( NAME t8_gtest_recursive SOURCES t8_gtest_main.cxx t8_forest_incomplete/t8_gtest_recursive.cxx ) +add_t8_test( NAME t8_gtest_iterate_replace SOURCES t8_gtest_main.cxx t8_forest_incomplete/t8_gtest_iterate_replace.cxx ) +add_t8_test( NAME t8_gtest_empty_local_tree SOURCES t8_gtest_main.cxx t8_forest_incomplete/t8_gtest_empty_local_tree.cxx ) +add_t8_test( NAME t8_gtest_empty_global_tree SOURCES t8_gtest_main.cxx t8_forest_incomplete/t8_gtest_empty_global_tree.cxx ) + +add_t8_test( NAME t8_gtest_geometry_cad SOURCES t8_gtest_main.cxx t8_geometry/t8_geometry_implementations/t8_gtest_geometry_cad.cxx ) +add_t8_test( NAME t8_gtest_geometry_linear SOURCES t8_gtest_main.cxx t8_geometry/t8_geometry_implementations/t8_gtest_geometry_linear.cxx ) +add_t8_test( NAME t8_gtest_geometry_lagrange SOURCES t8_gtest_main.cxx t8_geometry/t8_geometry_implementations/t8_gtest_geometry_lagrange.cxx ) +add_t8_test( NAME t8_gtest_geometry_handling SOURCES t8_gtest_main.cxx t8_geometry/t8_gtest_geometry_handling.cxx ) +add_t8_test( NAME t8_gtest_point_inside SOURCES t8_gtest_main.cxx t8_geometry/t8_gtest_point_inside.cxx ) + +add_t8_test( NAME t8_gtest_vtk_reader SOURCES t8_gtest_main.cxx t8_IO/t8_gtest_vtk_reader.cxx ) +add_t8_test( NAME t8_gtest_vtk_writer SOURCES t8_gtest_main.cxx t8_IO/t8_gtest_vtk_writer.cxx ) + +add_t8_test( NAME t8_gtest_nca SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_nca.cxx ) +add_t8_test( NAME t8_gtest_pyra_connectivity SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_pyra_connectivity.cxx ) +add_t8_test( NAME t8_gtest_face_neigh SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_face_neigh.cxx ) +add_t8_test( NAME t8_gtest_init_linear_id SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_init_linear_id.cxx ) +add_t8_test( NAME t8_gtest_ancestor SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_ancestor.cxx ) +add_t8_test( NAME t8_gtest_element_count_leaves SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_element_count_leaves.cxx ) +add_t8_test( NAME t8_gtest_element_ref_coords SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_element_ref_coords.cxx ) +add_t8_test( NAME t8_gtest_descendant SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_descendant.cxx ) +add_t8_test( NAME t8_gtest_find_parent SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_find_parent.cxx ) +add_t8_test( NAME t8_gtest_equal SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_equal.cxx ) +add_t8_test( NAME t8_gtest_successor SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_successor.cxx ) +add_t8_test( NAME t8_gtest_boundary_extrude SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_boundary_extrude.cxx ) +add_t8_test( NAME t8_gtest_face_descendant SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_face_descendant.cxx ) +add_t8_test( NAME t8_gtest_default SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_default.cxx ) +add_t8_test( NAME t8_gtest_child_parent_face SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_child_parent_face.cxx ) +add_t8_test( NAME t8_gtest_pack_unpack SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_pack_unpack.cxx ) +add_t8_test( NAME t8_gtest_root SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_root.cxx ) +add_t8_test( NAME t8_gtest_scheme_consistency SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_scheme_consistency.cxx ) +======= add_t8_test( NAME t8_gtest_cmesh_bcast_parallel SOURCES t8_gtest_main.cxx t8_cmesh/t8_gtest_bcast.cxx ) add_t8_test( NAME t8_gtest_eclass_serial SOURCES t8_gtest_main.cxx t8_gtest_eclass.cxx ) add_t8_test( NAME t8_gtest_vec_serial SOURCES t8_gtest_main.cxx t8_gtest_vec.cxx ) diff --git a/test/Makefile.am b/test/Makefile.am index 6ee6f4d17a..34cf92b1a5 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -74,6 +74,8 @@ t8code_googletest_programs = \ test/t8_forest/t8_gtest_user_data \ test/t8_forest/t8_gtest_transform \ test/t8_forest/t8_gtest_ghost_exchange \ + test/t8_forest/t8_gtest_ghost_exchange_stdvector \ + test/t8_forest/stdvectortest \ test/t8_forest/t8_gtest_ghost_delete \ test/t8_forest/t8_gtest_ghost_and_owner \ test/t8_forest/t8_gtest_forest_commit \ @@ -286,10 +288,20 @@ test_t8_forest_t8_gtest_ghost_exchange_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_forest/t8_gtest_ghost_exchange.cxx + +test_t8_forest_t8_gtest_ghost_exchange_stdvector_SOURCES = \ + test/t8_gtest_main.cxx \ + test/t8_forest/t8_gtest_ghost_exchange_stdvector.cxx + +test_t8_forest_stdvectortest_SOURCES = \ + test/t8_gtest_main.cxx \ + test/t8_forest/stdvectortest.cxx + test_t8_forest_t8_gtest_ghost_delete_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_forest/t8_gtest_ghost_delete.cxx + test_t8_forest_t8_gtest_ghost_and_owner_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_forest/t8_gtest_ghost_and_owner.cxx @@ -554,6 +566,14 @@ test_t8_forest_t8_gtest_ghost_exchange_LDADD = $(t8_gtest_target_ld_add) test_t8_forest_t8_gtest_ghost_exchange_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_forest_t8_gtest_ghost_exchange_CPPFLAGS = $(t8_gtest_target_cpp_flags) +test_t8_forest_t8_gtest_ghost_exchange_stdvector_LDADD = $(t8_gtest_target_ld_add) +test_t8_forest_t8_gtest_ghost_exchange_stdvector_LDFLAGS = $(t8_gtest_target_ld_flags) +test_t8_forest_t8_gtest_ghost_exchange_stdvector_CPPFLAGS = $(t8_gtest_target_cpp_flags) + +test_t8_forest_stdvectortest_LDADD = $(t8_gtest_target_ld_add) +test_t8_forest_stdvectortest_LDFLAGS = $(t8_gtest_target_ld_flags) +test_t8_forest_stdvectortest_CPPFLAGS = $(t8_gtest_target_cpp_flags) + test_t8_forest_t8_gtest_ghost_delete_LDADD = $(t8_gtest_target_ld_add) test_t8_forest_t8_gtest_ghost_delete_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_forest_t8_gtest_ghost_delete_CPPFLAGS = $(t8_gtest_target_cpp_flags) @@ -678,7 +698,11 @@ test_t8_geometry_t8_gtest_point_inside_CPPFLAGS += $(t8_gtest_target_mpi_cpp_fla test_t8_forest_t8_gtest_user_data_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_forest_t8_gtest_transform_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_forest_t8_gtest_ghost_exchange_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) + +test_t8_forest_t8_gtest_ghost_exchange_stdvector_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) +test_t8_forest_stdvectortest_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_forest_t8_gtest_ghost_delete_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) + test_t8_forest_t8_gtest_ghost_and_owner_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_forest_t8_gtest_forest_commit_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_forest_t8_gtest_balance_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) diff --git a/test/t8_forest/stdvectortest.cxx b/test/t8_forest/stdvectortest.cxx new file mode 100644 index 0000000000..767ba19f62 --- /dev/null +++ b/test/t8_forest/stdvectortest.cxx @@ -0,0 +1,86 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2024 the developers + + t8code is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + t8code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include +#include +#include +#include // header for sc_array conversion +#include "sc_containers.h" // header for sc_array_t and related functions + +// Define sc_array_count function +size_t +sc_array_count (const sc_array_t* sc_arr) +{ + return sc_arr->elem_count; // Access the count member +} + +template +class VectorTest: public ::testing::Test { + protected: + void + SetUp () override + { + // Initialize the vector with some test values + vec = { T (1), T (2), T (3), T (4), T (5) }; + arr = vec.data (); + // Convert std::vector to sc_array + sc_arr = t8_create_sc_array_view_from_vector (vec); + } + + void + TearDown () override + { + // Clean up the sc_array to prevent memory leaks + sc_array_destroy (sc_arr); + } + + std::vector vec; // Standard vector + const T* arr; // Raw pointer to the vector data + sc_array_t* sc_arr; // Converted sc_array +}; + +TYPED_TEST_SUITE_P (VectorTest); + +TYPED_TEST_P (VectorTest, LengthTest) +{ + // Test that the vector length is as expected + ASSERT_EQ (this->vec.size (), 5u) << "Vector length should be 5 for testing purposes"; + // Test that the sc_array length matches the vector length + ASSERT_EQ (sc_array_count (this->sc_arr), this->vec.size ()) << "sc_array length should match vector length"; +} + +TYPED_TEST_P (VectorTest, ElementComparisonTest) +{ + for (size_t i = 0; i < this->vec.size (); ++i) { + // Compare elements in the vector + EXPECT_EQ (this->vec[i], this->arr[i]) << "Element mismatch at index " << i; + // Compare elements between the vector and sc_array + EXPECT_EQ (this->vec[i], *(reinterpret_cast (sc_array_index (this->sc_arr, i)))) + << "Element mismatch between vector and sc_array at index " << i; + } +} + +REGISTER_TYPED_TEST_SUITE_P (VectorTest, LengthTest, ElementComparisonTest); + +using MyTypes = ::testing::Types; + +INSTANTIATE_TYPED_TEST_SUITE_P (MyVectorTests, VectorTest, MyTypes, ); diff --git a/test/t8_forest/t8_gtest_ghost_exchange_stdvector.cxx b/test/t8_forest/t8_gtest_ghost_exchange_stdvector.cxx new file mode 100644 index 0000000000..d98c3e12bd --- /dev/null +++ b/test/t8_forest/t8_gtest_ghost_exchange_stdvector.cxx @@ -0,0 +1,189 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2024 the developers + + t8code is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + t8code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "test/t8_cmesh_generator/t8_cmesh_example_sets.hxx" +#include +#include +/* TODO: when this test works for all cmeshes remove if statement in test_cmesh_ghost_exchange_all () */ + +/* This test program tests the forest ghost exchange routine. + * Given a forest for which the ghost layer was created and an array + * storing data for the local elements and the ghost elements, ghost_exchange + * communicates the data of the local elements to the ghost entries of the + * processes for which these elements are ghost. + * We test the ghost exchange routine for several forests on different + * coarse meshes. + * One test is an integer entry '42' for each element, + * in a second test, we store the element's linear id in the data array. + */ + +class forest_ghost_exchange: public testing::TestWithParam { + protected: + void + SetUp () override + { + scheme = t8_scheme_new_default_cxx (); + /* Construct a cmesh */ + cmesh = GetParam ()->cmesh_create (); + if (t8_cmesh_is_empty (cmesh)) { + /* empty cmeshes are currently not supported */ + GTEST_SKIP (); + } + } + void + TearDown () override + { + t8_cmesh_destroy (&cmesh); + t8_scheme_cxx_unref (&scheme); + } + t8_scheme_cxx_t *scheme; + t8_cmesh_t cmesh; +}; +static int +t8_test_exchange_adapt (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, t8_locidx_t lelement_id, + t8_eclass_scheme_c *ts, const int is_family, const int num_elements, t8_element_t *elements[]) +{ + /* refine every second element up to the maximum level */ + int level = ts->t8_element_level (elements[0]); + t8_linearidx_t eid = ts->t8_element_get_linear_id (elements[0], level); + int maxlevel = *(int *) t8_forest_get_user_data (forest); + + if (eid % 2 && level < maxlevel) { + return 1; + } + return 0; +} + +/* Construct a data array of uin64_t for all elements and all ghosts, + * fill the element's entries with their linear id, perform the ghost exchange and + * check whether the ghost's entries are their linear id. + */ +static void +t8_test_ghost_exchange_data_id (t8_forest_t forest) +{ + t8_eclass_scheme_c *ts; + size_t array_pos = 0; + + t8_locidx_t num_elements = t8_forest_get_local_num_elements (forest); + t8_locidx_t num_ghosts = t8_forest_get_num_ghosts (forest); + /* Initialize a vector of the required size */ + std::vector element_data (num_elements + num_ghosts); + + /* Fill the local element entries with their linear id */ + for (t8_locidx_t itree = 0; itree < t8_forest_get_num_local_trees (forest); itree++) { + /* Get the eclass scheme for this tree */ + ts = t8_forest_get_eclass_scheme (forest, t8_forest_get_tree_class (forest, itree)); + for (t8_locidx_t ielem = 0; ielem < t8_forest_get_tree_num_elements (forest, itree); ielem++) { + /* Get a pointer to this element */ + const t8_element_t *elem = t8_forest_get_element_in_tree (forest, itree, ielem); + /* Compute the linear id of this element */ + t8_linearidx_t elem_id = ts->t8_element_get_linear_id (elem, ts->t8_element_level (elem)); + /* Store this id at the element's index in the array */ + element_data[array_pos] = elem_id; + array_pos++; + } + } + + /* Perform the data exchange */ + t8_forest_ghost_exchange_data_with_vector (forest, element_data); + + /* We now iterate over all ghost elements and check whether the correct + * id was received */ + for (t8_locidx_t itree = 0; itree < t8_forest_get_num_ghost_trees (forest); itree++) { + /* Get the eclass scheme of this ghost tree */ + ts = t8_forest_get_eclass_scheme (forest, t8_forest_ghost_get_tree_class (forest, itree)); + for (t8_locidx_t ielem = 0; ielem < t8_forest_ghost_tree_num_elements (forest, itree); ielem++) { + /* Get a pointer to this ghost */ + const t8_element_t *elem = t8_forest_ghost_get_element (forest, itree, ielem); + /* Compute its ghost_id */ + t8_linearidx_t ghost_id = ts->t8_element_get_linear_id (elem, ts->t8_element_level (elem)); + /* Compare this id with the entry in the element_data array */ + t8_linearidx_t ghost_entry = element_data[array_pos]; + ASSERT_EQ (ghost_id, ghost_entry) << "Error when exchanging ghost data. Received wrong element id.\n"; + /* Since array pos ended with the last element in the loop above, we can + * continue counting for the ghost elements */ + array_pos++; + } + } +} + +/* Construct a data array of ints for all elements and all ghosts, + * fill the element's entries with '42', perform the ghost exchange and + * check whether the ghost's entries are '42'. + */ +static void +t8_test_ghost_exchange_data_int (t8_forest_t forest) +{ + + t8_locidx_t num_elements = t8_forest_get_local_num_elements (forest); + t8_locidx_t num_ghosts = t8_forest_get_num_ghosts (forest); + std::vector element_data (num_elements + num_ghosts); + /* Fill the local element entries with the integer 42 */ + std::fill (element_data.begin (), element_data.begin () + num_elements, 42); + + /* Perform the ghost data exchange */ + t8_forest_ghost_exchange_data_with_vector (forest, element_data); + + /* Check for the ghosts that we received the correct data */ + for (t8_locidx_t ielem = 0; ielem < num_ghosts; ielem++) { + /* Get the integer for this ghost */ + int ghost_int = element_data[num_elements + ielem]; + ASSERT_EQ (ghost_int, 42) << "Error when exchanging ghost data. Received wrong data.\n"; + } +} + +TEST_P (forest_ghost_exchange, test_ghost_exchange) +{ + + /* Compute the minimum level, such that the forest is nonempty */ + int min_level = t8_forest_min_nonempty_level (cmesh, scheme); + /* we start with an empty level */ + min_level = SC_MAX (min_level - 1, 0); + + for (int level = min_level; level < min_level + 3; level++) { + /* ref the scheme since we reuse it */ + t8_scheme_cxx_ref (scheme); + /* ref the cmesh since we reuse it */ + t8_cmesh_ref (cmesh); + /* Create a uniformly refined forest */ + t8_forest_t forest = t8_forest_new_uniform (cmesh, scheme, level, 1, sc_MPI_COMM_WORLD); + /* exchange ghost data */ + t8_test_ghost_exchange_data_int (forest); + t8_test_ghost_exchange_data_id (forest); + /* Adapt the forest and exchange data again */ + int maxlevel = level + 2; + t8_forest_t forest_adapt = t8_forest_new_adapt (forest, t8_test_exchange_adapt, 1, 1, &maxlevel); + t8_test_ghost_exchange_data_int (forest_adapt); + t8_test_ghost_exchange_data_id (forest_adapt); + t8_forest_unref (&forest_adapt); + } +} + +INSTANTIATE_TEST_SUITE_P (t8_gtest_ghost_exchange, forest_ghost_exchange, AllCmeshsParam, pretty_print_base_example); diff --git a/tutorials/CMakeLists.txt b/tutorials/CMakeLists.txt index 8e749f3854..cd6d2d0510 100644 --- a/tutorials/CMakeLists.txt +++ b/tutorials/CMakeLists.txt @@ -44,6 +44,7 @@ add_t8_tutorial( NAME t8_step7_interpolation SOURCES general/t8_step7 add_t8_tutorial( NAME t8_tutorial_build_cmesh SOURCES general/t8_tutorial_build_cmesh.cxx general/t8_tutorial_build_cmesh_main.cxx) add_t8_tutorial( NAME t8_tutorial_search SOURCES general/t8_tutorial_search.cxx general/t8_step3_adapt_forest.cxx ) add_t8_tutorial( NAME t8_features_curved_meshes SOURCES features/t8_features_curved_meshes.cxx) +add_t8_tutorial( NAME t8_step5_element_data_vector_input SOURCES general/t8_step5_element_data_vector_input.cxx) copy_tutorial_file (features/t8_features_curved_meshes_generate_cmesh_hex.geo) copy_tutorial_file (features/t8_features_curved_meshes_generate_cmesh_quad.geo) diff --git a/tutorials/Makefile.am b/tutorials/Makefile.am index 53abd1b8c7..de6d93e2c6 100644 --- a/tutorials/Makefile.am +++ b/tutorials/Makefile.am @@ -10,6 +10,7 @@ bin_PROGRAMS += \ tutorials/general/t8_step4_partition_balance_ghost \ tutorials/general/t8_step5_element_data \ tutorials/general/t8_step5_element_data_c_interface \ + tutorials/general/t8_step5_element_data_vector_input \ tutorials/general/t8_step6_stencil \ tutorials/general/t8_step7_interpolation \ tutorials/general/t8_tutorial_build_cmesh \ @@ -39,6 +40,10 @@ tutorials_general_t8_step5_element_data_SOURCES = \ tutorials/general/t8_step3_adapt_forest.cxx \ tutorials/general/t8_step5_element_data.cxx \ tutorials/general/t8_step5_main.cxx +tutorials_general_t8_step5_element_data_vector_input_SOURCES = \ + tutorials/general/t8_step3_adapt_forest.cxx \ + tutorials/general/t8_step5_element_data_vector_input.cxx \ + tutorials/general/t8_step5_main.cxx tutorials_general_t8_step5_element_data_c_interface_SOURCES = \ tutorials/general/t8_step3_adapt_forest.cxx \ tutorials/general/t8_step5_element_data_c_interface.c \ diff --git a/tutorials/general/t8_step5_element_data_vector_input.cxx b/tutorials/general/t8_step5_element_data_vector_input.cxx new file mode 100644 index 0000000000..0c099c8082 --- /dev/null +++ b/tutorials/general/t8_step5_element_data_vector_input.cxx @@ -0,0 +1,319 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element types in parallel. + + Copyright (C) 2015 the developers + + t8code is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + t8code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* See also: https://github.com/holke/t8code/wiki/Step-5---Store-element-data + * + * This is step5 of the t8code tutorials. + * In the following we will store data in the individual elements of our forest. + * To do this, we will again create a uniform forest, which will get adapted as in step4, + * with the difference that we partition, balance and create ghost elements all in the same step. + * After adapting the forest we will learn how to build a data array and gather data for + * the local elements. Furthermore, we exchange the data values of the ghost elements and + * output the volume data to vtu. + * + * How you can experiment here: + * - Look at the paraview output files of the adapted forest. + * You can apply a clip filter to look into the cube. Also you can apply (in addition) + * the threshold filter to display only elements with certain properties. + * But at first you may just want to enter the tooltip selection mode 'Hover Cells On' + * to display cell information when hover over them. + * - Change the adaptation criterion as you wish to adapt elements or families as desired. + * - Store even more data per element, for instance the coordinates of its midpoint. + * You can again apply the threshold filter to your new data. Don't forget to write the + * data into the output file. + * */ + +#include /* General t8code header, always include this. */ +#include /* cmesh definition and basic interface. */ +#include /* A collection of exemplary cmeshes */ +#include /* forest definition and basic interface. */ +#include /* save forest */ +#include /* geometrical information */ +#include /* default refinement scheme. */ +#include +#include +#include + +T8_EXTERN_C_BEGIN (); + +/* The data that we want to store for each element. + * In this example we want to store the element's level and volume. */ +struct t8_step5_data_per_element +{ + int level; + double volume; +}; + +static t8_forest_t +t8_step5_build_forest (sc_MPI_Comm comm, int level) +{ + t8_cmesh_t cmesh = t8_cmesh_new_hypercube_hybrid (comm, 0, 0); + t8_scheme_cxx_t *scheme = t8_scheme_new_default_cxx (); + struct t8_step3_adapt_data adapt_data = { + { 0.5, 0.5, 1 }, /* Midpoints of the sphere. */ + 0.2, /* Refine if inside this radius. */ + 0.4 /* Coarsen if outside this radius. */ + }; + /* Start with a uniform forest. */ + t8_forest_t forest = t8_forest_new_uniform (cmesh, scheme, level, 0, comm); + t8_forest_t forest_apbg; + + /* Adapt, partition, balance and create ghost elements all in the same step. */ + t8_forest_init (&forest_apbg); + t8_forest_set_user_data (forest_apbg, &adapt_data); + t8_forest_set_adapt (forest_apbg, forest, t8_step3_adapt_callback, 0); + t8_forest_set_partition (forest_apbg, NULL, 0); + t8_forest_set_balance (forest_apbg, NULL, 0); + t8_forest_set_ghost (forest_apbg, 1, T8_GHOST_FACES); + t8_forest_commit (forest_apbg); + + return forest_apbg; +} + +std::vector +t8_step5_create_element_data (t8_forest_t forest) +{ + t8_locidx_t num_local_elements; + t8_locidx_t num_ghost_elements; + + /* Check that forest is a committed, that is valid and usable, forest. */ + T8_ASSERT (t8_forest_is_committed (forest)); + + /* Get the number of local elements of forest. */ + num_local_elements = t8_forest_get_local_num_elements (forest); + /* Get the number of ghost elements of forest. */ + num_ghost_elements = t8_forest_get_num_ghosts (forest); + std::vector element_data (num_local_elements + num_ghost_elements); + /* Now we need to build an array of our data that is as long as the number + * of elements plus the number of ghosts. You can use any allocator such as + * new, malloc or the t8code provide allocation macro T8_ALLOC. + * Note that in the latter case you need + * to use T8_FREE in order to free the memory. + */ + /* Note: We will later need to associate this data with an sc_array in order to exchange the values for + * the ghost elements, which we can do with sc_array_new_data (see t8_step5_exchange_ghost_data). + * We could also have directly allocated the data here in an sc_array with + * sc_array_new_count (sizeof (struct data_per_element), num_local_elements + num_ghost_elements); + */ + + /* Let us now fill the data with something. + * For this, we iterate through all trees and for each tree through all its elements, calling + * t8_forest_get_element_in_tree to get a pointer to the current element. + * This is the recommended and most performant way. + * An alternative is to iterate over the number of local elements and use + * t8_forest_get_element. However, this function needs to perform a binary search + * for the element and the tree it is in, while t8_forest_get_element_in_tree has a + * constant look up time. You should only use t8_forest_get_element if you do not know + * in which tree an element is. + */ + { + t8_locidx_t itree, num_local_trees; + t8_locidx_t current_index; + t8_locidx_t ielement, num_elements_in_tree; + t8_eclass_t tree_class; + t8_eclass_scheme_c *eclass_scheme; + const t8_element_t *element; + + /* Get the number of trees that have elements of this process. */ + num_local_trees = t8_forest_get_num_local_trees (forest); + for (itree = 0, current_index = 0; itree < num_local_trees; ++itree) { + /* This loop iterates through all local trees in the forest. */ + /* Each tree may have a different element class (quad/tri/hex/tet etc.) and therefore + * also a different way to interpret its elements. In order to be able to handle elements + * of a tree, we need to get its eclass_scheme, and in order to so we first get its eclass. */ + tree_class = t8_forest_get_tree_class (forest, itree); + eclass_scheme = t8_forest_get_eclass_scheme (forest, tree_class); + /* Get the number of elements of this tree. */ + num_elements_in_tree = t8_forest_get_tree_num_elements (forest, itree); + for (ielement = 0; ielement < num_elements_in_tree; ++ielement, ++current_index) { + /* This loop iterates through all the local elements of the forest in the current tree. */ + + /* We can now write to the position current_index into our array in order to store + * data for this element. */ + /* Since in this example we want to compute the data based on the element in question, + * we need to get a pointer to this element. */ + element = t8_forest_get_element_in_tree (forest, itree, ielement); + /* We want to store the elements level and its volume as data. We compute these + * via the eclass_scheme and the forest_element interface. */ + element_data[current_index].level = eclass_scheme->t8_element_level (element); + element_data[current_index].volume = t8_forest_element_volume (forest, itree, element); + } + } + } + return element_data; +} + +/* Each process has computed the data entries for its local elements. + * In order to get the values for the ghost elements, we use t8_forest_ghost_exchange_data. + * Calling this function will fill all the ghost entries of our element data array with the + * value on the process that owns the corresponding element. */ +static void +t8_step5_exchange_ghost_data (t8_forest_t forest, std::vector &data) +{ + sc_array *sc_array_wrapper; + + /* t8_forest_ghost_exchange_data expects an sc_array (of length num_local_elements + num_ghosts). + * We wrap our data array to an sc_array. */ + sc_array_wrapper = t8_create_sc_array_view_from_vector (data); + + /* Carry out the data exchange. The entries with indices > num_local_elements will get overwritten. + */ + t8_forest_ghost_exchange_data (forest, sc_array_wrapper); + + /* Destroy the wrapper array. This will not free the data memory since we used sc_array_new_data. */ + sc_array_destroy (sc_array_wrapper); +} + +/* Write the forest as vtu and also write the element's volumes in the file. + * + * t8code supports writing element based data to vtu as long as its stored + * as doubles. Each of the data fields to write has to be provided in its own + * array of length num_local_elements. + * We support two types: T8_VTK_SCALAR - One double per element + * and T8_VTK_VECTOR - 3 doubles per element + */ +static void +t8_step5_output_data_to_vtu (t8_forest_t forest, std::vector data, const char *prefix) +{ + t8_locidx_t num_elements = t8_forest_get_local_num_elements (forest); + t8_locidx_t ielem; + /* We need to allocate a new array to store the volumes on their own. + * This array has one entry per local element. */ + double *element_volumes = T8_ALLOC (double, num_elements); + /* The number of user defined data fields to write. */ + int num_data = 1; + /* For each user defined data field we need one t8_vtk_data_field_t variable */ + t8_vtk_data_field_t vtk_data; + /* Set the type of this variable. Since we have one value per element, we pick T8_VTK_SCALAR */ + vtk_data.type = T8_VTK_SCALAR; + /* The name of the field as should be written to the file. */ + strcpy (vtk_data.description, "Element volume"); + vtk_data.data = element_volumes; + /* Copy the element's volumes from our data array to the output array. */ + for (ielem = 0; ielem < num_elements; ++ielem) { + element_volumes[ielem] = data[ielem].volume; + } + { + /* To write user defined data, we need to extended output function t8_forest_vtk_write_file + * from t8_forest_vtk.h. Despite writin user data, it also offers more control over which + * properties of the forest to write. */ + int write_treeid = 1; + int write_mpirank = 1; + int write_level = 1; + int write_element_id = 1; + int write_ghosts = 0; + t8_forest_write_vtk_ext (forest, prefix, write_treeid, write_mpirank, write_level, write_element_id, write_ghosts, + 0, 0, num_data, &vtk_data); + } + T8_FREE (element_volumes); +} + +int +t8_step5_main (int argc, char **argv) +{ + int mpiret; + sc_MPI_Comm comm; + t8_forest_t forest; + /* The prefix for our output files. */ + const char *prefix_forest = "t8_step5_forest"; + const char *prefix_forest_with_data = "t8_step5_forest_with_volume_data"; + /* The uniform refinement level of the forest. */ + const int level = 3; + /* The array that will hold our per element data. */ + + /* Initialize MPI. This has to happen before we initialize sc or t8code. */ + mpiret = sc_MPI_Init (&argc, &argv); + /* Error check the MPI return value. */ + SC_CHECK_MPI (mpiret); + + /* Initialize the sc library, has to happen before we initialize t8code. */ + sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_ESSENTIAL); + /* Initialize t8code with log level SC_LP_PRODUCTION. See sc.h for more info on the log levels. */ + t8_init (SC_LP_PRODUCTION); + + /* Print a message on the root process. */ + t8_global_productionf (" [step5] \n"); + t8_global_productionf (" [step5] Hello, this is the step5 example of t8code.\n"); + t8_global_productionf ( + " [step5] In this example we will store data on our elements and exchange the data of ghost elements.\n"); + t8_global_productionf (" [step5] \n"); + + /* We will use MPI_COMM_WORLD as a communicator. */ + comm = sc_MPI_COMM_WORLD; + + /* + * Setup. + * Build cmesh and uniform forest. + * Adapt forest similar to step 3 & 4. + */ + + t8_global_productionf (" [step5] \n"); + t8_global_productionf (" [step5] Creating an adapted forest as in step3.\n"); + t8_global_productionf (" [step5] \n"); + + forest = t8_step5_build_forest (comm, level); + t8_forest_write_vtk (forest, prefix_forest); + t8_global_productionf (" [step5] Wrote forest to vtu files: %s*\n", prefix_forest); + + /* + * Build data array and gather data for the local elements. + */ + auto data = t8_step5_create_element_data (forest); + + t8_global_productionf (" [step5] Computed level and volume data for local elements.\n"); + if (t8_forest_get_local_num_elements (forest) > 0) { + /* Output the stored data of the first local element (if it exists). */ + t8_global_productionf (" [step5] Element 0 has level %i and volume %e.\n", data[0].level, data[0].volume); + } + + /* + * Exchange the data values of the ghost elements + */ + t8_step5_exchange_ghost_data (forest, data); + t8_global_productionf (" [step5] Exchanged ghost data.\n"); + + if (t8_forest_get_num_ghosts (forest) > 0) { + /* output the data of the first ghost element (if it exists) */ + t8_locidx_t first_ghost_index = t8_forest_get_local_num_elements (forest); + t8_global_productionf (" [step5] Ghost 0 has level %i and volume %e.\n", data[first_ghost_index].level, + data[first_ghost_index].volume); + } + + /* + * Output the volume data to vtu. + */ + t8_step5_output_data_to_vtu (forest, data, prefix_forest_with_data); + t8_global_productionf (" [step5] Wrote forest and volume data to %s*.\n", prefix_forest_with_data); + + /* Destroy the forest. */ + t8_forest_unref (&forest); + t8_global_productionf (" [step5] Destroyed forest.\n"); + + sc_finalize (); + + mpiret = sc_MPI_Finalize (); + SC_CHECK_MPI (mpiret); + + return 0; +} + +T8_EXTERN_C_END ();