Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
DLR-AMR
GitHub Repository: DLR-AMR/t8code
Path: blob/main/test/t8_forest_incomplete/t8_gtest_iterate_replace.cxx
914 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) 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.
*/

#include <gtest/gtest.h>
#include <t8.h>
#include <t8_cmesh/t8_cmesh_examples.h>
#include "test/t8_cmesh_generator/t8_cmesh_example_sets.hxx"
#include <t8_forest/t8_forest.h>
#include <t8_forest/t8_forest_iterate.h>
#include <t8_schemes/t8_default/t8_default.hxx>
#include <test/t8_gtest_macros.hxx>

/* In this test, we first adapt a forest and store every callback return value.
 * In the next step, we call t8_forest_iterate_replace. Instead of interpolating
 * the stored data, we check in every callback call inside of
 * t8_forest_iterate_replace if it is passed the correct values.
 */

struct forest_iterate: public testing::TestWithParam<cmesh_example_base *>
{
 protected:
  void
  SetUp () override
  {
#if T8_TEST_LEVEL_INT >= 1
    constexpr int level = 2;
#else
    constexpr int level = 3;
#endif
    t8_cmesh_t cmesh = GetParam ()->cmesh_create ();
    if (t8_cmesh_is_empty (cmesh)) {
      /* empty cmeshes are currently not supported */
      t8_cmesh_unref (&cmesh);
      GTEST_SKIP ();
    }
    forest = t8_forest_new_uniform (cmesh, t8_scheme_new_default (), level, 0, sc_MPI_COMM_WORLD);
  }
  void
  TearDown () override
  {
    if (forest != NULL) {
      t8_forest_unref (&forest);
    }
  }
  t8_forest_t forest { NULL };
};

/** This structure contains an array with all return values of all
 * callback function calls in the adaptation process of a forest.
 */
struct t8_return_data
{
  int *callbacks;
};

/** Inside the callback of iterate_replace we compare \a refine
 * with the according return value of the callback of forest_adapt.
 * If true, we check the parameter \a num_outgoing, \a first_outgoing
 * \a num_incoming and \a first_incoming for correctness. */
void
t8_forest_replace (t8_forest_t forest_old, t8_forest_t forest_new, t8_locidx_t which_tree, const t8_eclass_t tree_class,
                   const t8_scheme *scheme, int refine, int num_outgoing, t8_locidx_t first_outgoing, int num_incoming,
                   t8_locidx_t first_incoming)
{
  /* Note, the new forest contains the callback returns of the old forest */
  struct t8_return_data *adapt_data = (struct t8_return_data *) t8_forest_get_user_data (forest_new);
  T8_ASSERT (adapt_data != NULL);

  /* Local element index of the old and new forest. */
  t8_locidx_t elidx_old = first_outgoing;
  for (t8_locidx_t tidx = 0; tidx < which_tree; tidx++) {
    elidx_old += t8_forest_get_tree_num_leaf_elements (forest_old, tidx);
  }

  ASSERT_EQ (adapt_data->callbacks[elidx_old], refine);

  /* Element remained untouched. */
  if (refine == 0) {
    ASSERT_EQ (num_outgoing, 1);
    ASSERT_EQ (num_incoming, 1);
  }
  /* Element/family got coarsened. */
  else if (refine == -1) {
    ASSERT_EQ (num_incoming, 1);

    /* Begin check family */
    const t8_element_t *parent = t8_forest_get_leaf_element_in_tree (forest_new, which_tree, first_incoming);
    t8_element_t *parent_compare;
    scheme->element_new (tree_class, 1, &parent_compare);
    int family_size = 1;
    t8_locidx_t tree_num_elements_old = t8_forest_get_tree_num_leaf_elements (forest_old, which_tree);
    for (t8_locidx_t elidx = 1; elidx < scheme->element_get_num_children (tree_class, parent)
                                && elidx + first_outgoing < tree_num_elements_old;
         elidx++) {
      const t8_element_t *child = t8_forest_get_leaf_element_in_tree (forest_old, which_tree, first_outgoing + elidx);
      scheme->element_get_parent (tree_class, child, parent_compare);
      if (scheme->element_is_equal (tree_class, parent, parent_compare)) {
        family_size++;
      }
    }
    scheme->element_destroy (tree_class, 1, &parent_compare);
    ASSERT_EQ (num_outgoing, family_size);
    /* End check family */

    /* If element got coarsened, only the first element
     * should be called in the callback of forest_adapt. */
    for (t8_locidx_t i = 1; i < num_outgoing; i++) {
      ASSERT_EQ (adapt_data->callbacks[elidx_old + i], -3);
    }
  }
  /* Element got removed. */
  else if (refine == -2) {
    ASSERT_EQ (num_outgoing, 1);
    ASSERT_EQ (num_incoming, 0);
    ASSERT_EQ (first_incoming, -1);
  }
  /* Element got refined. */
  else if (refine == 1) {
    ASSERT_EQ (num_outgoing, 1);
    const t8_element_t *element = t8_forest_get_leaf_element_in_tree (forest_old, which_tree, first_outgoing);
    const t8_locidx_t family_size = scheme->element_get_num_children (tree_class, element);
    ASSERT_EQ (num_incoming, family_size);
  }
}

/** For each local element: Remove, coarsen, leave untouched, or refine it depending on its index.
 *      if \a lelement_id mod 12 < 3  -> leave element untouched
 * else if \a lelement_id mod 12 < 6  -> coarse element
 * else if \a lelement_id mod 12 < 9  -> remove element
 * else if \a lelement_id mod 12 < 12 -> refine element
*/
int
t8_adapt_callback (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree,
                   [[maybe_unused]] const t8_eclass_t tree_class, t8_locidx_t lelement_id,
                   [[maybe_unused]] const t8_scheme *scheme, const int is_family,
                   [[maybe_unused]] const int num_elements, [[maybe_unused]] t8_element_t *elements[])
{
  struct t8_return_data *return_data = (struct t8_return_data *) t8_forest_get_user_data (forest);
  T8_ASSERT (return_data != NULL);

  const int id_mod_12 = lelement_id % 12;
  int return_val;
  switch (id_mod_12) {
  case 0:
  case 1:
  case 2:
    /* < 3 */
    return_val = 0;  // keep element
    break;
  case 3:
  case 4:
  case 5:
    /* < 6 */
    if (is_family) {
      return_val = -1;  // Coarsen family
    }
    else {
      return_val = 0;  // keep if not part of a family
    }
    break;
  case 6:
  case 7:
  case 8:
    return_val = -2;  // remove
    break;
  default:
    return_val = 1;  // refine
    break;
  }

  T8_ASSERT (-3 < return_val);
  T8_ASSERT (return_val < 2);

  /* Get the local index of current element in the local forest. */
  t8_locidx_t lelement_id_forest = lelement_id;
  for (t8_locidx_t tidx = 0; tidx < which_tree; tidx++) {
    lelement_id_forest += t8_forest_get_tree_num_leaf_elements (forest_from, tidx);
  }
  /* Store the return value. */
  return_data->callbacks[lelement_id_forest] = return_val;
  return return_val;
}

t8_forest_t
t8_adapt_forest (t8_forest_t forest_from, t8_forest_adapt_t adapt_fn, int do_adapt, int do_partition, void *user_data)
{
  t8_forest_t forest_new;

  t8_forest_init (&forest_new);
  if (do_adapt) {
    t8_forest_set_adapt (forest_new, forest_from, adapt_fn, 0);
    if (do_partition) {
      t8_forest_set_partition (forest_new, NULL, 0);
    }
  }
  else if (do_partition) {
    t8_forest_set_partition (forest_new, forest_from, 0);
  }
  if (user_data != NULL) {
    t8_forest_set_user_data (forest_new, user_data);
  }
  t8_forest_commit (forest_new);

  return forest_new;
}

TEST_P (forest_iterate, test_iterate_replace)
{
  const int runs = 2;

  for (int run = 0; run < runs; run++) {
    const t8_locidx_t num_elements = t8_forest_get_local_num_leaf_elements (forest);
    int *adapt_callbacks = T8_TESTSUITE_ALLOC (int, num_elements);

    for (t8_locidx_t elidx = 0; elidx < num_elements; elidx++) {
      adapt_callbacks[elidx] = -3;
    }
    struct t8_return_data data
    {
      adapt_callbacks
    };

    t8_forest_ref (forest);
    t8_forest_t forest_adapt;
    forest_adapt = t8_adapt_forest (forest, t8_adapt_callback, 1, 0, &data);

    t8_forest_iterate_replace (forest_adapt, forest, t8_forest_replace);
    t8_forest_unref (&forest);

    /* Partition the forest. This is useful as preparation for the second run with the adapted forest. */
    forest_adapt = t8_adapt_forest (forest_adapt, NULL, 0, 1, NULL);

    T8_TESTSUITE_FREE (adapt_callbacks);
    forest = forest_adapt;
  }
}

INSTANTIATE_TEST_SUITE_P (t8_gtest_iterate_replace, forest_iterate, AllCmeshsParam, pretty_print_base_example);