Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
DLR-AMR
GitHub Repository: DLR-AMR/t8code
Path: blob/main/mesh_handle/mesh.hxx
902 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 mesh.hxx
 * Definition of the mesh class of the handle.
 */

#pragma once

#include <t8.h>
#include <t8_forest/t8_forest_general.h>
#include "element.hxx"
#include "competence_pack.hxx"
#include <t8_forest/t8_forest_ghost.h>
#include <vector>
#include <type_traits>

namespace t8_mesh_handle
{

/** Concept to ensure that a type is MPI safe.
 */
template <typename TType>
concept T8MPISafeType
  = std::is_void_v<TType> || (std::is_trivially_copyable_v<TType> && std::is_standard_layout_v<TType>);

/**
 * Wrapper for a forest that enables it to be handled as a simple mesh object.
 * \tparam TCompetencePack The competences you want to add to the default functionality of the mesh.
 *         \see element for more details on the choice of the template parameter.   
 *         \note Please pack your competences using the \ref competence_pack class.
 * \tparam TUserDataType The user data type you want to associate with the mesh. Use void (this is also the default) if you do not want to set user data.
 * \tparam TElementDataType The element data type you want to use for each element of the mesh. 
 *         The data type has to be MPI safe as the data for ghost elements will be exchanged via MPI.
 *         Use void (this is also the default) if you do not want to set element data.
 */
template <typename TCompetencePack = competence_pack<>, typename TUserDataType = void,
          T8MPISafeType TElementDataType = void>
class mesh {
 public:
  using SelfType = mesh<TCompetencePack, TUserDataType,
                        TElementDataType>;  /**< Type of the current class with all template parameters specified. */
  using UserDataType = TUserDataType;       /**< Make Type of the user data accessible. */
  using ElementDataType = TElementDataType; /**< Make Type of the element data accessible. */
  using element_class =
    typename TCompetencePack::template apply<SelfType,
                                             element>; /**< The element class of the mesh with given competences. */
  friend element_class; /**< Element class as friend such that private members (e.g. the forest) can be accessed. */
  using mesh_const_iterator =
    typename std::vector<element_class>::const_iterator; /**< Constant iterator type for the mesh elements. */
  using mesh_iterator =
    typename std::vector<element_class>::iterator; /**< Non-const iterator type for the mesh elements. */

  /** 
   * Constructor for a mesh of the handle. 
   * \param [in] forest The forest from which the mesh should be created. 
   */
  mesh (t8_forest_t forest): m_forest (forest)
  {
    T8_ASSERT ((std::is_same<typename TCompetencePack::is_competence_pack, void>::value));
    T8_ASSERT (t8_forest_is_committed (m_forest));
    update_elements ();
  }

  /** 
   * Destructor for a mesh of the handle. 
   * The forest in use will be unreferenced. 
   * Call \ref t8_forest_ref before if you want to keep it alive.
   */
  ~mesh ()
  {
    t8_forest_unref (&m_forest);
  }

  /**
   * Getter for the number of local elements in the mesh.
   * \return Number of local elements in the mesh.
   */
  t8_locidx_t
  get_num_local_elements () const
  {
    return t8_forest_get_local_num_leaf_elements (m_forest);
  }

  /**
   * Getter for the number of ghost elements.
   * \return Number of ghost elements in the mesh.
   */
  t8_locidx_t
  get_num_ghosts () const
  {
    return t8_forest_get_num_ghosts (m_forest);
  }

  /** 
   * Getter for the dimension of the mesh.
   * \return The dimension.
   */
  int
  get_dimension () const
  {
    return t8_forest_get_dimension (m_forest);
  }

  /**
   * Returns a constant iterator to the first (local) mesh element.
   * \return Constant iterator to the first (local) mesh element.
   */
  mesh_const_iterator
  cbegin () const
  {
    return m_elements.cbegin ();
  }

  /**
   * Returns a constant iterator to a mesh element following the last (local) element of the mesh.
   * \return Constant iterator to the mesh element following the last (local) element of the mesh.
   */
  mesh_const_iterator
  cend () const
  {
    return m_elements.cend ();
  }

  /**
   * Returns an iterator to the first (local) mesh element.
   * \return Iterator to the first (local) mesh element.
   */
  mesh_iterator
  begin ()
  {
    return m_elements.begin ();
  }

  /**
   * Returns an iterator to a mesh element following the last (local) element of the mesh.
   * \return Iterator to the mesh element following the last (local) element of the mesh.
   */
  mesh_iterator
  end ()
  {
    return m_elements.end ();
  }

  /**
   * Getter for an element given its local index. This could be a (local) mesh element or 
   *  a ghost element. 
   * The indices 0, 1, ... num_local_el - 1 refer to local mesh elements and 
   *    num_local_el , ... , num_local_el + num_ghosts - 1 refer to ghost elements.
   * \param [in] local_index The local index of the element to access.
   * \return Constant reference to the element.
   */
  const element_class&
  operator[] (t8_locidx_t local_index) const
  {
    T8_ASSERT (0 <= local_index && local_index < get_num_local_elements () + get_num_ghosts ());
    if (local_index < get_num_local_elements ()) {
      return m_elements[local_index];
    }
    else {
      return m_ghosts[local_index - get_num_local_elements ()];
    }
  }

  /**
   * Non const version of operator above.
   * \param [in] local_index The local index of the element to access.
   * \return Reference to the element.
   */
  element_class&
  operator[] (t8_locidx_t local_index)
  {
    return const_cast<element_class&> (static_cast<const mesh*> (this)->operator[] (local_index));
  }

  /**
   * Getter for the forest the mesh is defined for.
   * \return The forest the mesh is defined for.
   */
  t8_forest_t
  get_forest () const
  {
    return m_forest;
  }

  /** 
   * Set the user data of the mesh. This can i.e. be used to pass user defined arguments to the adapt routine.
   * \param [in] data The user data of class TUserDataType. Data will never be touched by mesh handling routines.
   */
  template <typename UserDataType = TUserDataType, typename = std::enable_if_t<!std::is_void<UserDataType>::value>>
  void
  set_user_data (const UserDataType& data)
  {
    t8_forest_set_user_data (m_forest, data);
  }

  /** 
   * Get the user data of the mesh. 
   * \return The user data previously set using \ref set_user_data.   
   */
  template <typename UserDataType = TUserDataType, typename = std::enable_if_t<!std::is_void<UserDataType>::value>>
  const UserDataType&
  get_user_data () const
  {
    return *static_cast<const UserDataType*> (t8_forest_get_user_data (m_forest));
  }

  /** 
   * Set the element data vector. The vector should have the length of num_local_elements.
   * \param [in] element_data The element data vector to set with one entry of class TElementDataType 
   *            for each local mesh element (excluding ghosts).
   */
  template <typename ElementDataType = TElementDataType,
            typename = std::enable_if_t<!std::is_void<ElementDataType>::value>>
  void
  set_element_data (std::vector<ElementDataType> element_data)
  {
    T8_ASSERT (element_data.size () == static_cast<size_t> (get_num_local_elements ()));
    m_element_data = std::move (element_data);
    m_element_data.reserve (get_num_local_elements () + get_num_ghosts ());
    m_element_data.resize (get_num_local_elements ());
  }

  /** 
   * Get the element data vector.
   * The element data of the local mesh elements can be set using \ref set_element_data.
   * If ghost entries should be filled, one should call \ref exchange_ghost_data on each process first.
   * \return Element data vector with data of Type TElementDataType.
   */
  template <typename ElementDataType = TElementDataType,
            typename = std::enable_if_t<!std::is_void<ElementDataType>::value>>
  const std::vector<ElementDataType>&
  get_element_data () const
  {
    return m_element_data;
  }

  /** 
  * Exchange the element data for ghost elements between processes.
  * This routine has to be called on each process after setting the element data for all local elements.
  */
  template <typename ElementDataType = TElementDataType,
            typename = std::enable_if_t<!std::is_void<ElementDataType>::value>>
  void
  exchange_ghost_data ()
  {
    // t8_forest_ghost_exchange_data expects an sc_array, so we need to wrap our data array to one.
    sc_array* sc_array_wrapper;
    m_element_data.resize (get_num_local_elements () + get_num_ghosts ());
    sc_array_wrapper = sc_array_new_data (m_element_data.data (), sizeof (ElementDataType),
                                          get_num_local_elements () + get_num_ghosts ());

    // Data exchange: entries with indices > num_local_elements will get overwritten.
    t8_forest_ghost_exchange_data (m_forest, sc_array_wrapper);

    sc_array_destroy (sc_array_wrapper);
  }

 private:
  /** 
   * Setter for the forest. 
   * \param [in] input_forest The forest from which the mesh should be a wrapper. 
   */
  void
  set_forest (t8_forest_t input_forest)
  {
    T8_ASSERT (t8_forest_is_committed (input_forest));
    m_forest = input_forest;
    update_elements ();
    if constexpr (!std::is_void<TElementDataType>::value) {
      t8_global_infof ("The forest of the mesh handle has been updated. Please note that the element data in the mesh "
                       "has to be updated accordingly. Use the function set_element_data() to provide element data "
                       "fitting to the new forest.\n");
    }
  }

  /** 
   * Update the storage of the mesh elements according to the current forest. 
   */
  void
  update_elements ()
  {
    m_elements.clear ();
    m_elements.reserve (get_num_local_elements ());
    // Iterate through forest elements and fill the element vector with newly created mesh elements.
    for (t8_locidx_t itree = 0; itree < t8_forest_get_num_local_trees (m_forest); ++itree) {
      const t8_locidx_t num_elems = t8_forest_get_tree_num_leaf_elements (m_forest, itree);
      for (t8_locidx_t ielem = 0; ielem < num_elems; ++ielem) {
        m_elements.push_back (element_class (this, itree, ielem));
      }
    }
    update_ghost_elements ();
  }

  /** 
   * Update the storage of the ghost elements according to the current forest. 
   */
  void
  update_ghost_elements ()
  {
    m_ghosts.clear ();
    m_ghosts.reserve (get_num_ghosts ());
    t8_locidx_t num_loc_trees = t8_forest_get_num_local_trees (m_forest);

    for (t8_locidx_t itree = 0; itree < t8_forest_get_num_ghost_trees (m_forest); ++itree) {
      const t8_locidx_t num_elems = t8_forest_ghost_tree_num_leaf_elements (m_forest, itree);
      for (t8_locidx_t ielem = 0; ielem < num_elems; ++ielem) {
        m_ghosts.push_back (element_class (this, num_loc_trees + itree, ielem, true));
      }
    }
  }

  t8_forest_t m_forest;                  /**< The forest the mesh should be defined for. */
  std::vector<element_class> m_elements; /**< Vector storing the (local) mesh elements. */
  std::vector<element_class> m_ghosts;   /**< Vector storing the (local) ghost elements. */
  std::conditional_t<!std::is_void_v<TElementDataType>, std::vector<TElementDataType>, std::nullptr_t>
    m_element_data; /**< Vector storing the (local) element data. */
};

}  // namespace t8_mesh_handle