Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
DLR-AMR
GitHub Repository: DLR-AMR/t8code
Path: blob/main/test/t8_cmesh/t8_gtest_cmesh_face_is_boundary.cxx
933 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_cmesh/t8_cmesh.h>
#include <t8_cmesh/t8_cmesh_examples.h>
#include <test/t8_gtest_macros.hxx>
#include <t8_geometry/t8_geometry_implementations/t8_geometry_zero.h>

/* Test for one tree */
struct cmesh_face_boundary_one_tree: public testing::TestWithParam<t8_eclass>
{
 protected:
  void
  SetUp () override
  {
    eclass = GetParam ();

    /* For each eclass create a cmesh consisting only of one tree. */
    cmesh = t8_cmesh_new_from_class (eclass, sc_MPI_COMM_WORLD);

    /* We now check each face */
    num_faces = t8_eclass_num_faces[(int) eclass];
  }
  void
  TearDown () override
  {
    t8_cmesh_destroy (&cmesh);
  }

  t8_cmesh_t cmesh;
  t8_eclass eclass;
  int num_faces;
};

TEST_P (cmesh_face_boundary_one_tree, check_face_is_boundary_one_tree)
{

  /* We check whether all faces of the tree are a boundary face. */
  ASSERT_TRUE (t8_cmesh_is_committed (cmesh)) << "Cmesh commit failed";

  for (int iface = 0; iface < num_faces; ++iface) {
    ASSERT_TRUE (t8_cmesh_tree_face_is_boundary (cmesh, 0, iface)) << "Face is not detected as a boundary";
    ASSERT_LT (t8_cmesh_get_face_neighbor (cmesh, 0, iface, NULL, NULL), 0)
      << "Face neighbor on boundary face detected";
    const t8_eclass_t should_be_invalid = t8_cmesh_get_tree_face_neighbor_eclass (cmesh, 0, iface);
    EXPECT_EQ (should_be_invalid, T8_ECLASS_INVALID)
      << "eclass of non-existing face neighbor should be invalid but is not.";
  }
}

INSTANTIATE_TEST_SUITE_P (t8_gtest_cmesh_face_boundary, cmesh_face_boundary_one_tree, AllEclasses, print_eclass);

/* For a two tree cmesh, compute a parallel distribution of the trees.
 * If we have more than 1 process, the first half of process (rank < size/2)
 * gets tree 0, the other half gets tree 1. */
static void
t8_test_compute_parallel_bounds (sc_MPI_Comm comm, t8_gloidx_t *first_tree, t8_gloidx_t *last_tree)
{
  int mpirank;
  int mpisize;
  int mpiret;
  int first_tree_shared;

  mpiret = sc_MPI_Comm_rank (comm, &mpirank);
  SC_CHECK_MPI (mpiret);
  mpiret = sc_MPI_Comm_size (comm, &mpisize);
  SC_CHECK_MPI (mpiret);

  /* If only one process, it gets all trees */
  if (mpisize == 1) {
    *first_tree = 0;
    *last_tree = 1;
    return;
  }
  /* First half of processes gets tree 0, other half gets tree 1 */
  if (mpirank < mpisize / 2) {
    /* The first tree is shared if this rank is in the lower half, but not rank 0 */
    first_tree_shared = mpirank > 0;
    /* The first tree is 0 */
    *first_tree = first_tree_shared ? -1 : 0;
    *last_tree = 0;
  }
  else {
    /* The first tree is shared if this process is not mpisize/2 */
    first_tree_shared = mpirank > mpisize / 2;
    /* The first tree is 1 */
    *first_tree = first_tree_shared ? -2 : 1;
    *last_tree = 1;
  }
}

/* Test for two tree cmesh
 * Creates coarse meshes with two trees for each eclass,
 * one for each face of the first tree as the connecting face.
 * This, only the remaining trees should register as boundary trees. */
struct cmesh_face_boundary_two_trees: public testing::TestWithParam<std::tuple<t8_eclass, int>>
{
 protected:
  void
  SetUp () override
  {
    eclass = std::get<0> (GetParam ());
    do_partition = std::get<1> (GetParam ());
    num_faces = t8_eclass_num_faces[(int) eclass];
  }

  t8_eclass eclass;
  int do_partition;
  int num_faces;
};

TEST_P (cmesh_face_boundary_two_trees, check_face_is_boundary_two_trees)
{

  t8_cmesh_t cmesh;
  t8_gloidx_t first_tree;
  t8_gloidx_t last_tree;

  t8_test_compute_parallel_bounds (sc_MPI_COMM_WORLD, &first_tree, &last_tree);

  for (int iface = 0; iface < num_faces; ++iface) {
    /* For each face of the eclass we construct one cmesh having this face as a connecting face.
     * Once partitioned and once replicated */
    t8_cmesh_init (&cmesh);
    t8_cmesh_set_tree_class (cmesh, 0, eclass);
    t8_cmesh_set_tree_class (cmesh, 1, eclass);
    /* Connect face iface of tree 0 with face iface of tree 1 with orientation 0 */
    t8_cmesh_set_join (cmesh, 0, 1, iface, iface, 0);
    t8_debugf ("Connecting tree 0 to tree 1 via face %i\n", iface);

    if (do_partition) {
      /* Set the cmesh to be partitioned.
       * We do it in such a way that each process has one local and one ghost tree. */
      t8_cmesh_set_partition_range (cmesh, 3, first_tree, last_tree);
    }
    t8_cmesh_commit (cmesh, sc_MPI_COMM_WORLD);
    ASSERT_TRUE (t8_cmesh_is_committed (cmesh)) << "Cmesh commit failed";
    for (int checkface = 0; checkface < num_faces; ++checkface) {
      if (iface != checkface) {
        /* The face checkface is a boundary face for tree 0 and tree 1 */
        /* Check that tree 0 face is a boundary */
        ASSERT_TRUE (t8_cmesh_tree_face_is_boundary (cmesh, 0, checkface)) << "Face is not detected as a boundary";
        /* Check that tree 1 face is a boundary */
        ASSERT_TRUE (t8_cmesh_tree_face_is_boundary (cmesh, 1, checkface)) << "Face is not detected as a boundary";
        /* Check that we do not detect a face neighbor for tree 0 or tree 1 at this face */
        ASSERT_LT (t8_cmesh_get_face_neighbor (cmesh, 0, checkface, NULL, NULL), 0)
          << "Face neighbor on boundary face detected. Tree 0 face " << checkface << ".";
        ASSERT_LT (t8_cmesh_get_face_neighbor (cmesh, 1, checkface, NULL, NULL), 0)
          << "Face neighbor on boundary face detected. Tree 1 face " << checkface << ".";
        const t8_eclass_t should_be_invalid_tree0 = t8_cmesh_get_tree_face_neighbor_eclass (cmesh, 0, checkface);
        EXPECT_EQ (should_be_invalid_tree0, T8_ECLASS_INVALID)
          << "eclass of non-existing face neighbor should be invalid but is not.";
        const t8_eclass_t should_be_invalid_tree1 = t8_cmesh_get_tree_face_neighbor_eclass (cmesh, 1, checkface);
        EXPECT_EQ (should_be_invalid_tree1, T8_ECLASS_INVALID)
          << "eclass of non-existing face neighbor should be invalid but is not.";
      }
      else {
        /* checkface == iface 
         * Thus, tree 0 is connected to tree 1 across this face */
        t8_locidx_t face_neighbor;
        int dual_face = -1, orientation = -1;
        /* Check that tree 0 face is not a boundary */
        ASSERT_FALSE (t8_cmesh_tree_face_is_boundary (cmesh, 0, checkface))
          << "Face is wrongly detected as a boundary.";
        /* Compute the face neighbor info */
        t8_debugf ("Checking face neighbor of local tree 0 across face %i.\n", checkface);
        face_neighbor = t8_cmesh_get_face_neighbor (cmesh, 0, iface, &dual_face, &orientation);
        /* Check the face_neighbor info */
        ASSERT_TRUE (face_neighbor) << "Wrong face neighbor computed. Expected 1 got" << face_neighbor << ".";
        ASSERT_EQ (dual_face, checkface) << "Wrong dual face. Expected " << checkface << " got " << dual_face << ".";
        ASSERT_EQ (orientation, 0) << "Wrong orientation. Expected 0 got " << orientation << ".";
        // Check t8_cmesh_get_tree_face_neighbor_eclass
        const t8_eclass_t neighbor_class_tree0 = t8_cmesh_get_tree_face_neighbor_eclass (cmesh, 0, iface);
        EXPECT_EQ (neighbor_class_tree0, eclass) << "Wrong eclass of face neighbor computed.";
        /* Check that tree 1 face is not a boundary */
        ASSERT_FALSE (t8_cmesh_tree_face_is_boundary (cmesh, 1, checkface))
          << "Face is wrongly detected as a boundary.";
        /* Reset the dual face and orientation to catch false positives (when the get_face_neighbor
         * function does not touch dual_face and orientation) */
        dual_face = orientation = -1;
        /* Compute the face neighbor info */
        t8_debugf ("Checking face neighbor of local tree 1 across face %i.\n", checkface);
        face_neighbor = t8_cmesh_get_face_neighbor (cmesh, 1, checkface, &dual_face, &orientation);
        /* Check the face_neighbor info */
        ASSERT_EQ (face_neighbor, 0) << "Wrong face neighbor computed. Expected 0 got " << face_neighbor << ".";
        ASSERT_EQ (dual_face, checkface) << "Wrong dual face. Expected " << checkface << " got " << dual_face << ".";
        ASSERT_EQ (orientation, 0) << "Wrong orientation. Expected 0 got " << orientation << ".";
        // Check t8_cmesh_get_tree_face_neighbor_eclass
        const t8_eclass_t neighbor_class_tree1 = t8_cmesh_get_tree_face_neighbor_eclass (cmesh, 1, checkface);
        EXPECT_EQ (neighbor_class_tree1, eclass) << "Wrong eclass of face neighbor computed.";
      }
    }
    t8_cmesh_destroy (&cmesh);
  } /* End iface loop */
}

INSTANTIATE_TEST_SUITE_P (t8_gtest_cmesh_face_boundary, cmesh_face_boundary_two_trees,
                          testing::Combine (AllEclasses, testing::Values (0, 1)));