diff --git a/src/t8_cmesh.h b/src/t8_cmesh.h index 206bb7ff31..e5616dc734 100644 --- a/src/t8_cmesh.h +++ b/src/t8_cmesh.h @@ -277,10 +277,37 @@ t8_cmesh_set_attribute (t8_cmesh_t cmesh, t8_gloidx_t gtree_id, int package_id, * \param [in] string The string to store as attribute. * \note You can also use \ref t8_cmesh_set_attribute, but we recommend using this * specialized function for strings. + * \note If an attribute with the given package_id and key already exists, then it will get overwritten. */ void t8_cmesh_set_attribute_string (t8_cmesh_t cmesh, t8_gloidx_t gtree_id, int package_id, int key, const char *string); +/** Store an array of t8_gloidx_t as an attribute at a tree in a cmesh. + * \param [in, out] cmesh The cmesh to be updated. + * \param [in] gtree_id The global id of the tree. + * \param [in] package_id Unique identifier of a valid software package. \see sc_package_register + * \param [in] key An integer key used to identify this attribute under all + * attributes with the same package_id. + * \a key must be a unique value for this tree and package_id. + * \param [in] data The array to store as attribute. + * \param [in] data_count The number of entries in \a data. + * \param [in] data_persists This flag can be used to optimize memory. If true + * then t8code assumes that the attribute data is present at the + * memory that \a data points to when \ref t8_cmesh_commit is called + * (This is more memory efficient). + * If the flag is false an internal copy of the data is created + * immediately and this copy is used at commit. + * In both cases a copy of the data is used by t8_code after t8_cmesh_commit. + * \note You can also use \ref t8_cmesh_set_attribute, but we recommend using this + * specialized function for arrays. + * \note If an attribute with the given package_id and key already exists, then it will get overwritten. + * \note We do not store the number of data entries \a data_count of the attribute array. + * You can keep track of the data count yourself by using another attribute. + */ +void +t8_cmesh_set_attribute_gloidx_array (t8_cmesh_t cmesh, t8_gloidx_t gtree_id, int package_id, int key, + const t8_gloidx_t *data, const size_t data_count, int data_persists); + /** Insert a face-connection between two trees in a cmesh. * \param [in,out] cmesh The cmesh to be updated. * \param [in] tree1 The tree id of the first of the two trees. @@ -632,14 +659,33 @@ t8_cmesh_get_tree_vertices (t8_cmesh_t cmesh, t8_locidx_t ltreeid); * \param [in] key A key used to identify the attribute under all * attributes of this tree with the same \a package_id. * \param [in] tree_id The local number of the tree. - * \param [out] data_size The size of the attribute in bytes. * \return The attribute pointer of the tree \a ltree_id or NULL if the attribute is not found. - * \a cmesh must be committed before calling this function. + * \note \a cmesh must be committed before calling this function. * \see t8_cmesh_set_attribute */ void * t8_cmesh_get_attribute (t8_cmesh_t cmesh, int package_id, int key, t8_locidx_t ltree_id); +/** Return the attribute pointer of a tree for a gloidx_t array. + * \param [in] cmesh The cmesh. + * \param [in] package_id The identifier of a valid software package. \see sc_package_register + * \param [in] key A key used to identify the attribute under all + * attributes of this tree with the same \a package_id. + * \param [in] tree_id The local number of the tree. + * \param [in] data_count The number of entries in the array that are requested. + * This must be smaller or equal to the \a data_count parameter + * of the corresponding call to \ref t8_cmesh_set_attribute_gloidx_array + * \return The attribute pointer of the tree \a ltree_id or NULL if the attribute is not found. + * \note \a cmesh must be committed before calling this function. + * \note No check is performed whether the attribute actually stored \a data_count many entries since + * we do not store the number of data entries of the attribute array. + * You can keep track of the data count yourself by using another attribute. + * \see t8_cmesh_set_attribute_gloidx_array + */ +t8_gloidx_t * +t8_cmesh_get_attribute_gloidx_array (t8_cmesh_t cmesh, int package_id, int key, t8_locidx_t ltree_id, + const size_t data_count); + /** Return the shared memory array storing the partition table of * a partitioned cmesh. * \param [in] cmesh The cmesh. diff --git a/src/t8_cmesh/t8_cmesh.c b/src/t8_cmesh/t8_cmesh.c index 6062f09c2d..1ce61db1cb 100644 --- a/src/t8_cmesh/t8_cmesh.c +++ b/src/t8_cmesh/t8_cmesh.c @@ -354,8 +354,7 @@ void t8_cmesh_set_attribute (t8_cmesh_t cmesh, t8_gloidx_t gtree_id, int package_id, int key, void *data, size_t data_size, int data_persists) { - T8_ASSERT (cmesh != NULL); - T8_ASSERT (!cmesh->committed); + T8_ASSERT (t8_cmesh_is_initialized (cmesh)); t8_stash_add_attribute (cmesh->stash, gtree_id, package_id, key, data_size, data, !data_persists); } @@ -363,8 +362,7 @@ t8_cmesh_set_attribute (t8_cmesh_t cmesh, t8_gloidx_t gtree_id, int package_id, void t8_cmesh_set_attribute_string (t8_cmesh_t cmesh, t8_gloidx_t gtree_id, int package_id, int key, const char *string) { - T8_ASSERT (cmesh != NULL); - T8_ASSERT (!cmesh->committed); + T8_ASSERT (t8_cmesh_is_initialized (cmesh)); /* The size is the string's length + the terminating '\0' */ size_t size = strlen (string) + 1; @@ -372,6 +370,16 @@ t8_cmesh_set_attribute_string (t8_cmesh_t cmesh, t8_gloidx_t gtree_id, int packa t8_cmesh_set_attribute (cmesh, gtree_id, package_id, key, (void *) string, size, 0); } +void +t8_cmesh_set_attribute_gloidx_array (t8_cmesh_t cmesh, t8_gloidx_t gtree_id, int package_id, int key, + const t8_gloidx_t *data, size_t data_count, int data_persists) +{ + T8_ASSERT (t8_cmesh_is_initialized (cmesh)); + + const size_t data_size = data_count * sizeof (*data); + t8_stash_add_attribute (cmesh->stash, gtree_id, package_id, key, data_size, (void *) data, !data_persists); +} + double * t8_cmesh_get_tree_vertices (t8_cmesh_t cmesh, t8_locidx_t ltreeid) { @@ -384,11 +392,9 @@ t8_cmesh_get_tree_vertices (t8_cmesh_t cmesh, t8_locidx_t ltreeid) void * t8_cmesh_get_attribute (t8_cmesh_t cmesh, int package_id, int key, t8_locidx_t ltree_id) { - int is_ghost; - - T8_ASSERT (cmesh->committed); + T8_ASSERT (t8_cmesh_is_committed (cmesh)); T8_ASSERT (t8_cmesh_treeid_is_local_tree (cmesh, ltree_id) || t8_cmesh_treeid_is_ghost (cmesh, ltree_id)); - is_ghost = t8_cmesh_treeid_is_ghost (cmesh, ltree_id); + const int is_ghost = t8_cmesh_treeid_is_ghost (cmesh, ltree_id); if (is_ghost) { ltree_id = t8_cmesh_ltreeid_to_ghostid (cmesh, ltree_id); @@ -396,6 +402,30 @@ t8_cmesh_get_attribute (t8_cmesh_t cmesh, int package_id, int key, t8_locidx_t l return t8_cmesh_trees_get_attribute (cmesh->trees, ltree_id, package_id, key, NULL, is_ghost); } +/* Return the attribute pointer of a tree for a gloidx_t array. + * \param [in] cmesh The cmesh. + * \param [in] package_id The identifier of a valid software package. \see sc_package_register + * \param [in] key A key used to identify the attribute under all + * attributes of this tree with the same \a package_id. + * \param [in] tree_id The local number of the tree. + * \param [out] data_count The number of entries in the array that are requested. + * This must be smaller or equal to the \a data_count parameter + * of the corresponding call to \ref t8_cmesh_set_attribute_gloidx_array + * \return The attribute pointer of the tree \a ltree_id or NULL if the attribute is not found. + * \note \a cmesh must be committed before calling this function. + * \note No check is performed whether the attribute actually stored \a data_count many entries since + * we do not store the number of data entries of the attribute array. + * You can keep track of the data count yourself by using another attribute. + * \see t8_cmesh_set_attribute_gloidx_array + */ +t8_gloidx_t * +t8_cmesh_get_attribute_gloidx_array (t8_cmesh_t cmesh, int package_id, int key, t8_locidx_t ltree_id, + const size_t data_count) +{ + T8_ASSERT (0 <= data_count); + return (t8_gloidx_t *) t8_cmesh_get_attribute (cmesh, package_id, key, ltree_id); +} + t8_shmem_array_t t8_cmesh_get_partition_table (t8_cmesh_t cmesh) { diff --git a/test/Makefile.am b/test/Makefile.am index 798fbd1a46..b066ad0033 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -41,6 +41,7 @@ t8code_googletest_programs = \ test/t8_cmesh/t8_gtest_cmesh_set_join_by_vertices \ test/t8_forest/t8_gtest_element_volume \ test/t8_cmesh/t8_gtest_multiple_attributes \ + test/t8_cmesh/t8_gtest_attribute_gloidx_array \ test/t8_schemes/t8_gtest_successor \ test/t8_schemes/t8_gtest_boundary_extrude \ test/t8_forest/t8_gtest_search \ @@ -182,6 +183,10 @@ test_t8_cmesh_t8_gtest_multiple_attributes_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_cmesh/t8_gtest_multiple_attributes.cxx +test_t8_cmesh_t8_gtest_attribute_gloidx_array_SOURCES = \ + test/t8_gtest_main.cxx \ + test/t8_cmesh/t8_gtest_attribute_gloidx_array.cxx + test_t8_schemes_t8_gtest_successor_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_schemes/t8_gtest_successor.cxx @@ -394,6 +399,10 @@ test_t8_cmesh_t8_gtest_multiple_attributes_LDADD = $(t8_gtest_target_ld_add) test_t8_cmesh_t8_gtest_multiple_attributes_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_cmesh_t8_gtest_multiple_attributes_CPPFLAGS = $(t8_gtest_target_cpp_flags) +test_t8_cmesh_t8_gtest_attribute_gloidx_array_LDADD = $(t8_gtest_target_ld_add) +test_t8_cmesh_t8_gtest_attribute_gloidx_array_LDFLAGS = $(t8_gtest_target_ld_flags) +test_t8_cmesh_t8_gtest_attribute_gloidx_array_CPPFLAGS = $(t8_gtest_target_cpp_flags) + test_t8_schemes_t8_gtest_successor_LDADD = $(t8_gtest_target_ld_add) test_t8_schemes_t8_gtest_successor_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_schemes_t8_gtest_successor_CPPFLAGS = $(t8_gtest_target_cpp_flags) @@ -521,6 +530,7 @@ test_t8_cmesh_t8_gtest_cmesh_partition_CPPFLAGS += $(t8_gtest_target_mpi_cpp_fla test_t8_cmesh_t8_gtest_cmesh_set_partition_offsets_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_forest_t8_gtest_element_volume_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_cmesh_t8_gtest_multiple_attributes_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) +test_t8_cmesh_t8_gtest_attribute_gloidx_array_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_schemes_t8_gtest_successor_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_schemes_t8_gtest_boundary_extrude_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_forest_t8_gtest_search_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) diff --git a/test/t8_cmesh/t8_gtest_attribute_gloidx_array.cxx b/test/t8_cmesh/t8_gtest_attribute_gloidx_array.cxx new file mode 100644 index 0000000000..e4c2bfbfa9 --- /dev/null +++ b/test/t8_cmesh/t8_gtest_attribute_gloidx_array.cxx @@ -0,0 +1,121 @@ +/* + 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 + +/* Test the t8_cmesh_set_attribute_gloidx_array and t8_cmesh_get_attribute_gloidx_array functions. + * We create a cmesh with two trees and add an array with N entries to each tree. + * The first tree has data_persists = 0, the second data_persists = 1. + * Then we get the array and check whether it's entries are correct. + * We parametrize the test over the number of entries N and the treeid (0 or 1). + * Thus for each element count N, we do one test for the first tree and one for the second tree. + */ + +#define T8_ATTRIBUTE_TEST_MAX_NUM_ENTRIES 1000 + +class cmesh_attribute_gloidx_array: public testing::TestWithParam> { + protected: + /* in Setup we build a two tree cmesh, fill an array with entries + * and set the array as attribute for both trees with different data_persists settings. */ + void + SetUp () override + { + num_entries = std::get<0> (GetParam ()); + check_tree_id = std::get<1> (GetParam ()); + + /* Build a cmesh with one QUAD tree and one TRIANGLE tree. */ + t8_cmesh_init (&cmesh); + t8_cmesh_set_dimension (cmesh, 2); + t8_cmesh_set_tree_class (cmesh, 0, T8_ECLASS_QUAD); + t8_cmesh_set_tree_class (cmesh, 1, T8_ECLASS_TRIANGLE); + + /* Allocate space for entries. */ + entries = T8_ALLOC (t8_gloidx_t, num_entries); + + /* Fill with 0, 1, 2, 3, 4 ... */ + for (t8_locidx_t ientry = 0; ientry < num_entries; ++ientry) { + entries[ientry] = ientry; + } + + t8_debugf ("Calling set_attribute with count %i\n", num_entries); + /* Set the array as attribute twice. Once with data_persist and once without. */ + /* Attribute at tree 0, data_persist = 0 */ + t8_gloidx_t tree_with_attribute = 0; + int data_persists = 0; + t8_cmesh_set_attribute_gloidx_array (cmesh, tree_with_attribute, t8_get_package_id (), T8_CMESH_NEXT_POSSIBLE_KEY, + entries, num_entries, data_persists); + + /* Attribute at tree 1, data_persist = 1 */ + tree_with_attribute = 1; + data_persists = 1; + t8_cmesh_set_attribute_gloidx_array (cmesh, tree_with_attribute, t8_get_package_id (), T8_CMESH_NEXT_POSSIBLE_KEY, + entries, num_entries, data_persists); + + /* Commit the cmesh */ + t8_cmesh_commit (cmesh, sc_MPI_COMM_WORLD); + /* It is save to free the entries after commit, since the value got copied. */ + T8_FREE (entries); + } + + void + TearDown () override + { + t8_cmesh_destroy (&cmesh); + } + + t8_cmesh_t cmesh; + t8_locidx_t num_entries; + t8_locidx_t check_tree_id; + t8_gloidx_t *entries; + t8_gloidx_t *get_entries; +}; + +/** Check attribute values of the trees against reference values. */ +TEST_P (cmesh_attribute_gloidx_array, check_values_data) +{ + get_entries = t8_cmesh_get_attribute_gloidx_array (cmesh, t8_get_package_id (), T8_CMESH_NEXT_POSSIBLE_KEY, + check_tree_id, num_entries); + + /* If we did not store any values, we except to get the NULL pointer back. */ + if (entries == NULL) { + EXPECT_EQ (get_entries, nullptr); + /* Number of entries must be < 0 in that case and we cannot continue the test otherwise. */ + ASSERT_EQ (num_entries, 0); + } + else { + /* Otherwise it must not be NULL and we abort the test if it is. */ + ASSERT_NE (get_entries, nullptr); + } + + /* Check for equality of the values. */ + for (t8_locidx_t ientry = 0; ientry < num_entries; ++ientry) { + EXPECT_EQ (get_entries[ientry], ientry); + } +} + +/* Test for different number of entries and trees 0 and 1. + * 0, 100, 200, ... T8_ATTRIBUTE_TEST_MAX_NUM_ENTRIES */ +INSTANTIATE_TEST_SUITE_P (t8_gtest_attribute_gloidx_array, cmesh_attribute_gloidx_array, + testing::Combine (testing::Range (0, T8_ATTRIBUTE_TEST_MAX_NUM_ENTRIES + 1, 100), + testing::Range (0, 2))); \ No newline at end of file