Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
DLR-AMR
GitHub Repository: DLR-AMR/t8code
Path: blob/main/test/mesh_handle/t8_gtest_handle_data.cxx
903 views
/*
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) 2025 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_gtest_handle_data.cxx
 * Tests to check that \ref t8_mesh_handle::mesh user data and element data functionality works as intended.
 */

#include <gtest/gtest.h>
#include <t8.h>

#include <mesh_handle/mesh.hxx>
#include <mesh_handle/competence_pack.hxx>
#include <mesh_handle/constructor_wrappers.hxx>
#include <t8_types/t8_vec.hxx>
#include <vector>

// --- Test for user data. ---
// Dummy user data taken from a tutorial for test purposes.
struct dummy_user_data
{
  t8_3D_point midpoint;             /* The midpoint of our sphere. */
  double refine_if_inside_radius;   /* if an element's center is smaller than this value, we refine the element. */
  double coarsen_if_outside_radius; /* if an element's center is larger this value, we coarsen its family. */
};

/** Check that user data can be set and accesses for the handle.
 */
TEST (t8_gtest_handle_data, set_and_get_user_data)
{
  // Define mesh handle.
  const int level = 2;
  using mesh_class = t8_mesh_handle::mesh<t8_mesh_handle::competence_pack<>, dummy_user_data>;
  auto mesh = t8_mesh_handle::handle_hypercube_hybrid_uniform_default<mesh_class> (level, sc_MPI_COMM_WORLD);

  struct dummy_user_data user_data = {
    t8_3D_point ({ 41.0, 42., 43.0 }), /* Midpoints of the sphere. */
    100.0,                             /* Refine if inside this radius. */
    3.0                                /* Coarsen if outside this radius. */
  };

  // Set user data for the mesh handle and check that the getter returns the same data.
  mesh->set_user_data (&user_data);
  auto mesh_user_data = mesh->get_user_data ();
  EXPECT_EQ (mesh_user_data.midpoint, user_data.midpoint);
  EXPECT_EQ (mesh_user_data.refine_if_inside_radius, user_data.refine_if_inside_radius);
  EXPECT_EQ (mesh_user_data.coarsen_if_outside_radius, user_data.coarsen_if_outside_radius);
}

// --- Test for element data. ---
/** Dummy element data taken from a tutorial for test purposes. */
struct data_per_element
{
  int level;
  double volume;
};

/** Check that element data can be set for the handle and that exchanging data for the ghosts works. */
TEST (t8_gtest_handle_data, set_and_get_element_data)
{
  // Define mesh handle.
  const int level = 2;
  using mesh_class = t8_mesh_handle::mesh<t8_mesh_handle::all_cache_competences, void, data_per_element>;
  auto mesh
    = t8_mesh_handle::handle_hypercube_hybrid_uniform_default<mesh_class> (level, sc_MPI_COMM_WORLD, true, true, false);
  if ((mesh->get_dimension () > 1) && (mesh->get_num_local_elements () > 1)) {
    // Ensure that we actually test with ghost elements.
    EXPECT_GT (mesh->get_num_ghosts (), 0);
  }

  // Create element data for all local mesh elements.
  std::vector<data_per_element> element_data;
  for (const auto &elem : *mesh) {
    element_data.push_back ({ elem.get_level (), elem.get_volume () });
  }
  mesh->set_element_data (std::move (element_data));
  // Get element data and check that the data for all elements (including ghosts) is correct.
  mesh->exchange_ghost_data ();
  auto mesh_element_data = mesh->get_element_data ();
  for (t8_locidx_t ielem = 0; ielem < mesh->get_num_local_elements () + mesh->get_num_ghosts (); ielem++) {
    EXPECT_EQ (mesh_element_data[ielem].level, level) << "ielem = " << ielem;
    EXPECT_EQ (mesh_element_data[ielem].volume, (*mesh)[ielem].get_volume ()) << "ielem = " << ielem;
  }

  // Modify element data for elements that are in the first half of the global trees.
  auto forest = mesh->get_forest ();
  t8_gloidx_t barrier = t8_forest_get_num_global_trees (forest) / 2.0;
  const int newlevel = 42;
  const double newvolume = 42.42;
  for (auto it = mesh->begin (); it != mesh->end (); ++it) {
    if (t8_forest_global_tree_id (forest, it->get_local_tree_id ()) < barrier) {
      it->set_element_data ({ newlevel, newvolume });
    }
  }
  mesh->exchange_ghost_data ();
  // Check for mesh elements with updated data.
  for (const auto &elem : *mesh) {
    if (t8_forest_global_tree_id (forest, elem.get_local_tree_id ()) < barrier) {
      EXPECT_EQ (elem.get_element_data ().level, newlevel);
      EXPECT_EQ (elem.get_element_data ().volume, newvolume);
    }
    else {
      EXPECT_EQ (elem.get_element_data ().level, level);
      EXPECT_EQ (elem.get_element_data ().volume, elem.get_volume ());
    }
  }
  // Check for ghost elements with updated data.
  for (t8_locidx_t ighost = mesh->get_num_local_elements ();
       ighost < mesh->get_num_local_elements () + mesh->get_num_ghosts (); ighost++) {
    if (t8_forest_ghost_get_global_treeid (
          forest, (*mesh)[ighost].get_local_tree_id () - t8_forest_get_num_local_trees (forest))
        < barrier) {
      EXPECT_EQ ((*mesh)[ighost].get_element_data ().level, newlevel);
      EXPECT_EQ ((*mesh)[ighost].get_element_data ().volume, newvolume);
    }
    else {
      EXPECT_EQ ((*mesh)[ighost].get_element_data ().level, level);
      EXPECT_EQ ((*mesh)[ighost].get_element_data ().volume, (*mesh)[ighost].get_volume ());
    }
  }
}