LCOV - code coverage report
Current view: top level - coupling/indexing - CellIndex.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 60 67 89.6 %
Date: 2025-06-25 11:26:37 Functions: 88 91 96.7 %

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <iostream>
       4             : #include <iterator>
       5             : #include <string_view>
       6             : #include <type_traits>
       7             : 
       8             : #include "tarch/la/Vector.h"
       9             : 
      10             : namespace coupling {
      11             : namespace indexing {
      12             : 
      13             : /**
      14             :  * Expresses type parametrisation of CellIndex specialisations.
      15             :  *
      16             :  * vector: implies representation as vector, false implies scalar index. \n
      17             :  * local: implies indexing restricted to local MD domain. \n
      18             :  * md2macro: implies indexing restricted to cells that are sent from MD to macro
      19             :  * solver. \n noGhost: implies ghost layer cells to not be included in indexing.
      20             :  * \n
      21             :  *
      22             :  * @author Felix Maurer
      23             :  */
      24             : enum class IndexTrait { vector, local, md2macro, noGhost };
      25             : namespace TraitOperations {
      26             : 
      27             : /**
      28             :  * Returns true iff the template and runtime argument match. Curried operator==
      29             :  * for above enum class.
      30             :  */
      31             : template <IndexTrait t1> constexpr bool is_same(const IndexTrait& t2) { return t1 == t2; }
      32             : 
      33             : /**
      34             :  * Checks if a given parameter pack of at least two IndexTrait entries is in the
      35             :  * correct order. The correct order is the order in which the Traits occur in
      36             :  * IndexTrait's declaration, i.e.:\n
      37             :  *
      38             :  *              *vector < local < md2macro < noGhost*
      39             :  *
      40             :  * This order is naturally induced by operator> on the enum class IndexTrait.
      41             :  *
      42             :  * @tparam Parameter pack of at least two IndexTraits
      43             :  * @return true iff the parameter pack is ordered correctly.
      44             :  */
      45             : template <IndexTrait t1, IndexTrait t2, IndexTrait... rest> constexpr bool is_ordered() {
      46             :   if constexpr (sizeof...(rest) == 0) {
      47             :     return t1 < t2;
      48             :   } else {
      49             :     return t1 < t2 and is_ordered<t2, rest...>();
      50             :   }
      51             : }
      52             : 
      53             : } // namespace TraitOperations
      54             : 
      55             : /**
      56             :  * Index used to describe spatial location of a CouplingCell.
      57             :  * Since various different ways of expressing this location are useful for
      58             :  * different applications, IndexTraits are used to describe the context of this
      59             :  * index. \n
      60             :  *
      61             :  * All commonly used (arithmetic) operations on CouplingCell indices are
      62             :  * provided as well as seamless conversion between any two ways of expressing
      63             :  * these indices. (cf. user-defined conversion function below)\n
      64             :  *
      65             :  * @tparam dim number of dimensions of the coupled simulation
      66             :  * @tparam traits... index type parametrisation (expressed via IndexTraits) used
      67             :  * by this specific index
      68             :  *
      69             :  * @author Felix Maurer
      70             :  */
      71             : template <unsigned int dim, IndexTrait... traits> class CellIndex;
      72             : 
      73             : /**
      74             :  * Base CellIndex specialisation (mainly) used for conversions.
      75             :  */
      76             : template <unsigned int dim> using BaseIndex = CellIndex<dim, IndexTrait::vector>;
      77             : 
      78             : // TODO: refactor as member functions
      79             : template <unsigned int dim, IndexTrait... traits> unsigned int convertToScalar(const CellIndex<dim, traits...>&);
      80             : 
      81             : template <unsigned int dim, IndexTrait... traits> tarch::la::Vector<dim, int> convertToVector(const CellIndex<dim, traits...>&);
      82             : } // namespace indexing
      83             : } // namespace coupling
      84             : 
      85       20520 : template <unsigned int dim, coupling::indexing::IndexTrait... traits> class coupling::indexing::CellIndex {
      86             : public:
      87             :   /**
      88             :    * Check at compile time wether traits... is in proper order, i.e.
      89             :    *
      90             :    * \forall i<j: traits[i] < traits[j]
      91             :    *
      92             :    * where '<' is the standard operator< on enum classes.
      93             :    * This means: vector < local < md2macro < noGhost
      94             :    *
      95             :    * Note that this causes duplicate IndexTraits in 'traits' to be not accepted.
      96             :    */
      97             :   static constexpr bool checkIndexTraitOrder() {
      98             :     if constexpr (sizeof...(traits) > 1) {
      99             :       return coupling::indexing::TraitOperations::is_ordered<traits...>();
     100             :     } else {
     101             :       return true;
     102             :     }
     103             :   }
     104             :   static_assert(checkIndexTraitOrder(), "Invalid order in IndexTrait parameter pack! Correct oder: "
     105             :                                         "IndexTrait::vector < IndexTrait::local < IndexTrait::md2macro "
     106             :                                         "< IndexTrait::noGhost");
     107             : 
     108             :   /**
     109             :    * The type of this CellIndex's underlying index representation.
     110             :    */
     111             :   using value_T = std::conditional_t<(coupling::indexing::TraitOperations::is_same<coupling::indexing::IndexTrait::vector>(traits) or ...),
     112             :                                      tarch::la::Vector<dim, int>, unsigned int>;
     113             : 
     114             :   /**
     115             :    * Constructors
     116             :    */
     117         720 :   CellIndex() = default;
     118     2558004 :   CellIndex(const CellIndex& ci) = default;
     119    15784992 :   explicit CellIndex(const value_T& i) : _index(i) {}
     120             : 
     121             :   /**
     122             :    * Conversion function: Convert to CellIndex of same dim but different
     123             :    * IndexType.
     124             :    *
     125             :    * @tparam convert_to_T IndexType parameter of the CellIndex specialisation to
     126             :    * convert to
     127             :    * @return CellIndex of different template parametrisation.
     128             :    */
     129             :   template <coupling::indexing::IndexTrait... converted_traits> operator CellIndex<dim, converted_traits...>() const;
     130             : 
     131             :   /**
     132             :    * Access to primive value_T of this index.
     133             :    * Should be used as sparingly as possible since it can lead to bugs
     134             :    * preventable by using CellIndex instances instead of primitives.
     135             :    *
     136             :    * @return unsigned integer/vector representation of this index.
     137             :    */
     138    47271912 :   value_T get() const { return _index; } // TODO: why (value_T) cast?
     139             : 
     140             :   /**
     141             :    * Increments the index by one.
     142             :    * Note that this does NOT increment indices in vector representation in all
     143             :    * directions.
     144             :    */
     145     1246344 :   CellIndex& operator++() {
     146             :     if constexpr (std::is_same_v<value_T, tarch::la::Vector<dim, int>>) {
     147      659432 :       ++_index[0];
     148      659432 :       if (_index[0] == (int)numberCellsInDomain[0]) {
     149       77248 :         _index[0] = 0;
     150       77248 :         ++_index[1];
     151       77248 :         if (_index[1] == (int)numberCellsInDomain[1]) {
     152       16056 :           _index[1] = 0;
     153             :           if constexpr (dim == 3)
     154       16056 :             ++_index[2];
     155             :         }
     156             :       }
     157             :     } else
     158      259272 :       ++_index;
     159             : 
     160      659432 :     return *this;
     161             :   }
     162             :   /**
     163             :    * Decrements the index by one.
     164             :    * Note that this does NOT decrement indices in vector representation in all
     165             :    * directions.
     166             :    */
     167      433792 :   CellIndex& operator--() {
     168             :     if constexpr (std::is_same_v<value_T, tarch::la::Vector<dim, int>>) {
     169      216896 :       --_index[0];
     170      216896 :       if (_index[0] < 0) {
     171       21696 :         _index[0] = numberCellsInDomain[0] - 1;
     172       21696 :         --_index[1];
     173       21696 :         if (_index[1] < 0) {
     174        2432 :           _index[1] = numberCellsInDomain[1] - 1;
     175             :           if constexpr (dim == 3)
     176        2432 :             --_index[2];
     177             :         }
     178             :       }
     179             :     } else
     180      216896 :       --_index;
     181             : 
     182      216896 :     return *this;
     183             :   }
     184             : 
     185             :   /*
     186             :    * Any two indices fulfill some relation iff the unsigned integers underlying
     187             :    * their CellIndex<dim, {}> equivalents fulfill that relation. Note that this
     188             :    * is different than what the static member 'contains(...)' does.
     189             :    *
     190             :    * @param CellIndex index to compare this index to
     191             :    */
     192     2356372 :   bool operator==(const CellIndex& i) const { return _index == i.get(); }
     193      767112 :   bool operator!=(const CellIndex& i) const { return not(i == *this); }
     194             :   bool operator<(const CellIndex& i) const { return convertToScalar<dim, traits...>(*this) < convertToScalar<dim, traits...>(i); };
     195             :   bool operator<=(const CellIndex& i) const { return convertToScalar<dim, traits...>(*this) <= convertToScalar<dim, traits...>(i); };
     196             :   bool operator>(const CellIndex& i) const { return convertToScalar<dim, traits...>(*this) > convertToScalar<dim, traits...>(i); };
     197             :   bool operator>=(const CellIndex& i) const { return convertToScalar<dim, traits...>(*this) >= convertToScalar<dim, traits...>(i); };
     198             : 
     199             :   /**
     200             :    * Initialises all static members dependant only on upperBoundary and
     201             :    * lowerBoundary
     202             :    */
     203       12224 :   static void setDomainParameters() {
     204      110016 :     auto cellnum = upperBoundary.get() - lowerBoundary.get() + tarch::la::Vector<dim, int>{1};
     205       48896 :     for (unsigned int d = 0; d < dim; d++)
     206       72972 :       cellnum[d] = std::max(0, cellnum[d]);
     207       48896 :     numberCellsInDomain = tarch::la::Vector<dim, unsigned int>{cellnum};
     208             : 
     209       12224 :     linearNumberCellsInDomain = 1;
     210       48896 :     for (unsigned int d = 0; d < dim; d++)
     211       36672 :       linearNumberCellsInDomain *= numberCellsInDomain[d];
     212             : 
     213       12224 :     tarch::la::Vector<dim, unsigned int> divFactor{1};
     214       36672 :     for (unsigned int d = 1; d < dim; d++)
     215       24448 :       divFactor[d] = divFactor[d - 1] * (numberCellsInDomain[d - 1]);
     216       12224 :     divisionFactor = divFactor;
     217       12224 :   }
     218             : 
     219             :   /**
     220             :    * Checks if a given index is within the domain between this the lower and
     221             :    * upper bound of this kind of indexing.
     222             :    *
     223             :    * @param CellIndex index index to be checked
     224             :    * @return true iff i is within the domain of this CellIndex template
     225             :    * specialisation.
     226             :    */
     227     3910352 :   static bool contains(const coupling::indexing::BaseIndex<dim>& index) {
     228    10562976 :     for (unsigned int d = 0; d < dim; d++) {
     229    17323968 :       if (index.get()[d] < CellIndex::lowerBoundary.get()[d])
     230             :         return false;
     231    15314864 :       if (index.get()[d] > CellIndex::upperBoundary.get()[d])
     232             :         return false;
     233             :     }
     234             : 
     235             :     return true;
     236             :   }
     237             : 
     238           0 :   tarch::la::Vector<dim, double> getCellMidPoint() const {
     239           0 :     BaseIndex<dim> globalIndex{*this};
     240           0 :     tarch::la::Vector<dim, double> cellMidPoint(IndexingService<dim>::getInstance().getGlobalMDDomainOffset() -
     241           0 :                                                 0.5 * IndexingService<dim>::getInstance().getCouplingCellSize());
     242           0 :     for (unsigned int d = 0; d < dim; d++)
     243           0 :       cellMidPoint[d] += ((double)(globalIndex.get()[d])) * IndexingService<dim>::getInstance().getCouplingCellSize()[d];
     244           0 :     return cellMidPoint;
     245             :   }
     246             : 
     247             :   /**
     248             :    * Defines where this type of cell index starts counting.
     249             :    * Read inclusively, e.g.: lowerBoundary = {1,2,3} means {1,2,3} is the first
     250             :    * index contained in this type of cell index' domain.
     251             :    */
     252             :   static BaseIndex<dim> lowerBoundary;
     253             :   /**
     254             :    * Defines where this type of cell index stops counting.
     255             :    * Read inclusively, e.g.: upperBoundary = {4,5,6} means {4,5,6} is the last
     256             :    * index contained in this type of cell index' domain.
     257             :    */
     258             :   static BaseIndex<dim> upperBoundary;
     259             : 
     260             :   /**
     261             :    * Number of cells in this indexing's domain. The above declared boundaries
     262             :    * are inclusive. Initialised in setDomainParameters().
     263             :    */
     264             :   static tarch::la::Vector<dim, unsigned int> numberCellsInDomain;
     265             : 
     266             :   /**
     267             :    * Number of cells in this indexing's domain. Same as numberCellsInDomain, but
     268             :    * expressed as a scalar.
     269             :    */
     270             :   static unsigned int linearNumberCellsInDomain;
     271             : 
     272             :   /**
     273             :    * Used in scalar -> vector indexing conversion functions
     274             :    * Initialised in setDomainParameters().
     275             :    */
     276             :   static tarch::la::Vector<dim, unsigned int> divisionFactor;
     277             : 
     278             :   class IndexIterator {
     279             :   public:
     280       18744 :     IndexIterator(CellIndex x) : _idx(x) {}
     281       65172 :     IndexIterator(const IndexIterator& a) : _idx(a._idx) {}
     282             : 
     283      262456 :     const CellIndex& operator*() const { return _idx; }
     284             :     const CellIndex* operator->() const { return &_idx; }
     285             : 
     286             :     // Prefix increment
     287      812552 :     IndexIterator& operator++() {
     288      464388 :       ++_idx;
     289      766140 :       return *this;
     290             :     }
     291             : 
     292             :     // Postfix increment
     293             :     IndexIterator operator++(int) {
     294             :       IndexIterator tmp = *this;
     295             :       ++(*this);
     296             :       return tmp;
     297             :     }
     298             : 
     299             :     friend bool operator==(const IndexIterator& a, const IndexIterator& b) { return a._idx == b._idx; };
     300      767112 :     friend bool operator!=(const IndexIterator& a, const IndexIterator& b) { return a._idx != b._idx; };
     301             : 
     302             :   private:
     303             :     CellIndex _idx;
     304             :   };
     305        1036 :   static IndexIterator begin() {
     306             :     // if constexpr is neccessary here (otherwise benchmark performance breaks, due to runtime if)
     307             :     if constexpr (std::is_same_v<unsigned int, value_T>) {
     308             :       // If this CellIndex is scalar and if the domain is empty, then lowerBoundary can not be converted to CellIndex
     309             :       // Thus we have to return something else as an iterator for the empty domain
     310         468 :       if (linearNumberCellsInDomain == 0)
     311          16 :         return IndexIterator(CellIndex{});
     312             :       else
     313         452 :         return IndexIterator(lowerBoundary);
     314             :     } else
     315        1096 :       return IndexIterator(lowerBoundary);
     316             :   }
     317        8988 :   static IndexIterator end() {
     318             :     // Todo: This if-else branching is quite slow? (~factor 2)
     319        8988 :     if (linearNumberCellsInDomain == 0)
     320          16 :       return begin();
     321             :     else
     322       26012 :       return ++IndexIterator(upperBoundary);
     323             :   }
     324             : 
     325             :   static const char TNAME[];
     326             : 
     327             : private:
     328             :   value_T _index;
     329             : 
     330             :   template <coupling::indexing::IndexTrait T1, coupling::indexing::IndexTrait... other_traits> CellIndex<dim, other_traits...> getScalarCellIndex(int value) {
     331             :     static_assert(T1 == coupling::indexing::IndexTrait::vector);
     332             :     return CellIndex<dim, other_traits...>(value);
     333             :   }
     334             : };
     335             : 
     336             : // Include implementation
     337             : #include "CellIndex.cpph"

Generated by: LCOV version 1.14