SpECTRE Documentation Coverage Report
Current view: top level - ParallelAlgorithms/LinearSolver/Schwarz - ElementCenteredSubdomainData.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 5.9 % 68 4
Test Date: 2026-10-22 17:29:42
Legend: Lines:     hit not hit

            Line data    Source code
       1            0 : // Distributed under the MIT License.
       2              : // See LICENSE.txt for details.
       3              : 
       4              : #pragma once
       5              : 
       6              : #include <cstddef>
       7              : #include <ostream>
       8              : #include <pup.h>
       9              : #include <string>
      10              : #include <tuple>
      11              : 
      12              : #include "DataStructures/Variables.hpp"
      13              : #include "NumericalAlgorithms/LinearSolver/InnerProduct.hpp"
      14              : #include "ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp"
      15              : #include "Utilities/Algorithm.hpp"
      16              : #include "Utilities/MakeWithValue.hpp"
      17              : #include "Utilities/TMPL.hpp"
      18              : 
      19              : namespace LinearSolver::Schwarz {
      20              : 
      21              : namespace detail {
      22              : // Defines a consistent ordering of overlap IDs
      23              : template <size_t Dim, typename ValueType>
      24              : void ordered_overlap_ids(
      25              :     const gsl::not_null<std::vector<OverlapId<Dim>>*> overlap_ids,
      26              :     const OverlapMap<Dim, ValueType>& overlap_map) {
      27              :   // Return early if the overlap IDs haven't changed
      28              :   if (overlap_ids->size() == overlap_map.size() and
      29              :       alg::all_of(*overlap_ids,
      30              :                   [&overlap_map](const OverlapId<Dim>& overlap_id) {
      31              :                     return overlap_map.contains(overlap_id);
      32              :                   })) {
      33              :     return;
      34              :   }
      35              :   overlap_ids->resize(overlap_map.size());
      36              :   std::transform(overlap_map.begin(), overlap_map.end(), overlap_ids->begin(),
      37              :                  [](const auto& overlap_id_and_value) {
      38              :                    return overlap_id_and_value.first;
      39              :                  });
      40              :   std::sort(overlap_ids->begin(), overlap_ids->end());
      41              : }
      42              : }  // namespace detail
      43              : 
      44              : /// \cond
      45              : template <bool Const, size_t Dim, typename TagsList>
      46              : struct ElementCenteredSubdomainDataIterator;
      47              : /// \endcond
      48              : 
      49              : /*!
      50              :  * \brief Data on an element-centered subdomain
      51              :  *
      52              :  * An element-centered subdomain consists of a central element and overlap
      53              :  * regions with all neighboring elements. This class holds data on such a
      54              :  * subdomain. It supports vector space operations (addition and scalar
      55              :  * multiplication) and an inner product, which allows the use of this data type
      56              :  * with linear solvers (see e.g. `LinearSolver::Serial::Gmres`).
      57              :  */
      58              : template <size_t Dim, typename TagsList>
      59            1 : struct ElementCenteredSubdomainData {
      60            0 :   static constexpr size_t volume_dim = Dim;
      61            0 :   using ElementData = Variables<TagsList>;
      62            0 :   using OverlapData = ElementData;
      63            0 :   using iterator = ElementCenteredSubdomainDataIterator<false, Dim, TagsList>;
      64            0 :   using const_iterator =
      65              :       ElementCenteredSubdomainDataIterator<true, Dim, TagsList>;
      66            0 :   using value_type = typename ElementData::value_type;
      67              : 
      68            0 :   ElementCenteredSubdomainData() = default;
      69            0 :   ElementCenteredSubdomainData(const ElementCenteredSubdomainData&) = default;
      70            0 :   ElementCenteredSubdomainData& operator=(const ElementCenteredSubdomainData&) =
      71              :       default;
      72            0 :   ElementCenteredSubdomainData(ElementCenteredSubdomainData&&) = default;
      73            0 :   ElementCenteredSubdomainData& operator=(ElementCenteredSubdomainData&&) =
      74              :       default;
      75            0 :   ~ElementCenteredSubdomainData() = default;
      76              : 
      77            0 :   explicit ElementCenteredSubdomainData(const size_t element_num_points)
      78              :       : element_data{element_num_points} {}
      79              : 
      80              :   template <typename UsedForSizeTagsList>
      81            0 :   void destructive_resize(
      82              :       const ElementCenteredSubdomainData<Dim, UsedForSizeTagsList>&
      83              :           used_for_size) {
      84              :     if (UNLIKELY(element_data.number_of_grid_points() !=
      85              :                  used_for_size.element_data.number_of_grid_points())) {
      86              :       element_data.initialize(
      87              :           used_for_size.element_data.number_of_grid_points());
      88              :     }
      89              :     // Erase overlaps that don't exist in `used_for_size`
      90              :     for (auto it = overlap_data.cbegin(); it != overlap_data.cend();) {
      91              :       if (used_for_size.overlap_data.find(it->first) ==
      92              :           used_for_size.overlap_data.end()) {
      93              :         it = overlap_data.erase(it);
      94              :       } else {
      95              :         ++it;
      96              :       }
      97              :     }
      98              :     // Insert or resize overlaps to match `used_for_size`
      99              :     for (const auto& [overlap_id, used_for_overlap_size] :
     100              :          used_for_size.overlap_data) {
     101              :       if (UNLIKELY(overlap_data[overlap_id].number_of_grid_points() !=
     102              :                    used_for_overlap_size.number_of_grid_points())) {
     103              :         overlap_data[overlap_id].initialize(
     104              :             used_for_overlap_size.number_of_grid_points());
     105              :       }
     106              :     }
     107              :   }
     108              : 
     109            0 :   ElementCenteredSubdomainData(
     110              :       Variables<TagsList> local_element_data,
     111              :       OverlapMap<Dim, Variables<TagsList>> local_overlap_data)
     112              :       : element_data{std::move(local_element_data)},
     113              :         overlap_data{std::move(local_overlap_data)} {}
     114              : 
     115            0 :   size_t size() const {
     116              :     return std::accumulate(
     117              :         overlap_data.begin(), overlap_data.end(), element_data.size(),
     118              :         [](const size_t size, const auto& overlap_id_and_data) {
     119              :           return size + overlap_id_and_data.second.size();
     120              :         });
     121              :   }
     122            0 :   iterator begin() {
     123              :     detail::ordered_overlap_ids(make_not_null(&ordered_overlap_ids_),
     124              :                                 overlap_data);
     125              :     return {this};
     126              :   }
     127            0 :   iterator end() { return {}; }
     128            0 :   const_iterator begin() const {
     129              :     detail::ordered_overlap_ids(make_not_null(&ordered_overlap_ids_),
     130              :                                 overlap_data);
     131              :     return {this};
     132              :   }
     133            0 :   const_iterator end() const { return {}; }
     134            0 :   const_iterator cbegin() const { return begin(); }
     135            0 :   const_iterator cend() const { return end(); }
     136              : 
     137              :   // NOLINTNEXTLINE(google-runtime-references)
     138            0 :   void pup(PUP::er& p) {
     139              :     p | element_data;
     140              :     p | overlap_data;
     141              :   }
     142              : 
     143              :   template <typename RhsTagsList>
     144            0 :   ElementCenteredSubdomainData& operator+=(
     145              :       const ElementCenteredSubdomainData<Dim, RhsTagsList>& rhs) {
     146              :     element_data += rhs.element_data;
     147              :     for (auto& [overlap_id, data] : overlap_data) {
     148              :       data += rhs.overlap_data.at(overlap_id);
     149              :     }
     150              :     return *this;
     151              :   }
     152              : 
     153              :   template <typename RhsTagsList>
     154            0 :   ElementCenteredSubdomainData& operator-=(
     155              :       const ElementCenteredSubdomainData<Dim, RhsTagsList>& rhs) {
     156              :     element_data -= rhs.element_data;
     157              :     for (auto& [overlap_id, data] : overlap_data) {
     158              :       data -= rhs.overlap_data.at(overlap_id);
     159              :     }
     160              :     return *this;
     161              :   }
     162              : 
     163            0 :   ElementCenteredSubdomainData& operator*=(const value_type scalar) {
     164              :     element_data *= scalar;
     165              :     for (auto& [overlap_id, data] : overlap_data) {
     166              :       data *= scalar;
     167              :       // Silence unused-variable warning on GCC 7
     168              :       (void)overlap_id;
     169              :     }
     170              :     return *this;
     171              :   }
     172              : 
     173            0 :   ElementCenteredSubdomainData& operator/=(const value_type scalar) {
     174              :     element_data /= scalar;
     175              :     for (auto& [overlap_id, data] : overlap_data) {
     176              :       data /= scalar;
     177              :       // Silence unused-variable warning on GCC 7
     178              :       (void)overlap_id;
     179              :     }
     180              :     return *this;
     181              :   }
     182              : 
     183            0 :   ElementData element_data{};
     184            0 :   OverlapMap<Dim, OverlapData> overlap_data{};
     185              : 
     186              :   private:
     187              :    // Cache for iterators, so they don't have to allocate, fill and sort this
     188              :    // vector every time
     189              :    // NOLINTNEXTLINE(spectre-mutable)
     190            0 :    mutable std::vector<OverlapId<Dim>> ordered_overlap_ids_{};
     191              : 
     192            0 :    friend ElementCenteredSubdomainDataIterator<false, Dim, TagsList>;
     193            0 :    friend ElementCenteredSubdomainDataIterator<true, Dim, TagsList>;
     194              : };
     195              : 
     196              : template <size_t Dim, typename LhsTagsList, typename RhsTagsList>
     197            0 : decltype(auto) operator+(
     198              :     ElementCenteredSubdomainData<Dim, LhsTagsList> lhs,
     199              :     const ElementCenteredSubdomainData<Dim, RhsTagsList>& rhs) {
     200              :   lhs += rhs;
     201              :   return lhs;
     202              : }
     203              : 
     204              : template <size_t Dim, typename LhsTagsList, typename RhsTagsList>
     205            0 : decltype(auto) operator-(
     206              :     ElementCenteredSubdomainData<Dim, LhsTagsList> lhs,
     207              :     const ElementCenteredSubdomainData<Dim, RhsTagsList>& rhs) {
     208              :   lhs -= rhs;
     209              :   return lhs;
     210              : }
     211              : 
     212              : template <size_t Dim, typename TagsList,
     213              :           typename ValueType =
     214              :               typename ElementCenteredSubdomainData<Dim, TagsList>::value_type>
     215            0 : decltype(auto) operator*(const ValueType scalar,
     216              :                          ElementCenteredSubdomainData<Dim, TagsList> data) {
     217              :   data *= scalar;
     218              :   return data;
     219              : }
     220              : 
     221              : template <size_t Dim, typename TagsList,
     222              :           typename ValueType =
     223              :               typename ElementCenteredSubdomainData<Dim, TagsList>::value_type>
     224            0 : decltype(auto) operator*(ElementCenteredSubdomainData<Dim, TagsList> data,
     225              :                          const ValueType scalar) {
     226              :   data *= scalar;
     227              :   return data;
     228              : }
     229              : 
     230              : template <size_t Dim, typename TagsList,
     231              :           typename ValueType =
     232              :               typename ElementCenteredSubdomainData<Dim, TagsList>::value_type>
     233            0 : decltype(auto) operator/(ElementCenteredSubdomainData<Dim, TagsList> data,
     234              :                          const ValueType scalar) {
     235              :   data /= scalar;
     236              :   return data;
     237              : }
     238              : 
     239              : template <size_t Dim, typename TagsList>
     240            0 : std::ostream& operator<<(
     241              :     std::ostream& os,
     242              :     const ElementCenteredSubdomainData<Dim, TagsList>& subdomain_data) {
     243              :   os << "Element data:\n"
     244              :      << subdomain_data.element_data << "\nOverlap data:\n"
     245              :      << subdomain_data.overlap_data;
     246              :   return os;
     247              : }
     248              : 
     249              : template <size_t Dim, typename TagsList>
     250            0 : bool operator==(const ElementCenteredSubdomainData<Dim, TagsList>& lhs,
     251              :                 const ElementCenteredSubdomainData<Dim, TagsList>& rhs) {
     252              :   return lhs.element_data == rhs.element_data and
     253              :          lhs.overlap_data == rhs.overlap_data;
     254              : }
     255              : 
     256              : template <size_t Dim, typename TagsList>
     257            0 : bool operator!=(const ElementCenteredSubdomainData<Dim, TagsList>& lhs,
     258              :                 const ElementCenteredSubdomainData<Dim, TagsList>& rhs) {
     259              :   return not(lhs == rhs);
     260              : }
     261              : 
     262              : /*!
     263              :  * \brief Iterate over `LinearSolver::Schwarz::ElementCenteredSubdomainData`
     264              :  *
     265              :  * This iterator guarantees that it steps through the data in the same order as
     266              :  * long as these conditions are satisfied:
     267              :  *
     268              :  * - The set of overlap IDs in the `overlap_data` doesn't change
     269              :  * - The extents of the `element_data` and the `overlap_data` doesn't change
     270              :  *
     271              :  * Iterating requires sorting the overlap IDs. If you find this impacts
     272              :  * performance, be advised to implement the internal data storage in
     273              :  * `ElementCenteredSubdomainData` so it stores its data contiguously, e.g. by
     274              :  * implementing non-owning variables.
     275              :  */
     276              : template <bool Const, size_t Dim, typename TagsList>
     277            1 : struct ElementCenteredSubdomainDataIterator {
     278              :  private:
     279            0 :   using PtrType =
     280              :       tmpl::conditional_t<Const,
     281              :                           const ElementCenteredSubdomainData<Dim, TagsList>*,
     282              :                           ElementCenteredSubdomainData<Dim, TagsList>*>;
     283            0 :   using VarsPtrType = tmpl::conditional_t<Const, const Variables<TagsList>*,
     284              :                                           Variables<TagsList>*>;
     285              : 
     286              :  public:
     287            0 :   using difference_type = ptrdiff_t;
     288            0 :   using value_type = typename Variables<TagsList>::value_type;
     289            0 :   using pointer = value_type*;
     290            0 :   using reference = value_type&;
     291            0 :   using iterator_category = std::forward_iterator_tag;
     292              : 
     293              :   /// Construct begin state
     294            1 :   ElementCenteredSubdomainDataIterator(PtrType data) : data_(data) {
     295              :     reset();
     296              :   }
     297              : 
     298            0 :   void reset() {
     299              :     overlap_index_ = (data_->element_data.size() == 0 and
     300              :                       data_->ordered_overlap_ids_.empty())
     301              :                          ? std::numeric_limits<size_t>::max()
     302              :                          : 0;
     303              :     update_vars_ref();
     304              :     data_index_ = 0;
     305              :   }
     306              : 
     307              :   /// Construct end state
     308            1 :   ElementCenteredSubdomainDataIterator() {
     309              :     overlap_index_ = std::numeric_limits<size_t>::max();
     310              :     data_index_ = 0;
     311              :   }
     312              : 
     313            0 :   ElementCenteredSubdomainDataIterator& operator++() {
     314              :     ++data_index_;
     315              :     if (data_index_ == vars_ref_->size()) {
     316              :       ++overlap_index_;
     317              :       update_vars_ref();
     318              :       data_index_ = 0;
     319              :     }
     320              :     if (overlap_index_ == data_->ordered_overlap_ids_.size() + 1) {
     321              :       overlap_index_ = std::numeric_limits<size_t>::max();
     322              :     }
     323              :     return *this;
     324              :   }
     325              : 
     326            0 :   tmpl::conditional_t<Const, value_type, value_type&> operator*() const {
     327              :     return vars_ref_->data()[data_index_];
     328              :   }
     329              : 
     330              :  private:
     331            0 :   void update_vars_ref() {
     332              :     // The random-access operation is relatively slow because it computes a
     333              :     // hash, so it's important for performance to avoid repeating it at every
     334              :     // data index. Instead, we update this reference only when the overlap index
     335              :     // changes.
     336              :     if (overlap_index_ > data_->ordered_overlap_ids_.size()) {
     337              :       return;
     338              :     }
     339              :     vars_ref_ = overlap_index_ == 0
     340              :                     ? &data_->element_data
     341              :                     : &data_->overlap_data.at(
     342              :                           data_->ordered_overlap_ids_[overlap_index_ - 1]);
     343              :   }
     344              : 
     345            0 :   friend bool operator==(const ElementCenteredSubdomainDataIterator& lhs,
     346              :                          const ElementCenteredSubdomainDataIterator& rhs) {
     347              :     return lhs.overlap_index_ == rhs.overlap_index_ and
     348              :            lhs.data_index_ == rhs.data_index_;
     349              :   }
     350              : 
     351            0 :   friend bool operator!=(const ElementCenteredSubdomainDataIterator& lhs,
     352              :                          const ElementCenteredSubdomainDataIterator& rhs) {
     353              :     return not(lhs == rhs);
     354              :   }
     355              : 
     356            0 :   PtrType data_;
     357            0 :   size_t overlap_index_;
     358            0 :   size_t data_index_;
     359            0 :   VarsPtrType vars_ref_ = nullptr;
     360              : };
     361              : 
     362              : }  // namespace LinearSolver::Schwarz
     363              : 
     364              : namespace LinearSolver::InnerProductImpls {
     365              : 
     366              : template <size_t Dim, typename LhsTagsList, typename RhsTagsList>
     367            0 : struct InnerProductImpl<
     368              :     Schwarz::ElementCenteredSubdomainData<Dim, LhsTagsList>,
     369              :     Schwarz::ElementCenteredSubdomainData<Dim, RhsTagsList>> {
     370            0 :   static auto apply(
     371              :       const Schwarz::ElementCenteredSubdomainData<Dim, LhsTagsList>& lhs,
     372              :       const Schwarz::ElementCenteredSubdomainData<Dim, RhsTagsList>& rhs) {
     373              :     auto result = inner_product(lhs.element_data, rhs.element_data);
     374              :     for (const auto& [overlap_id, lhs_data] : lhs.overlap_data) {
     375              :       result += inner_product(lhs_data, rhs.overlap_data.at(overlap_id));
     376              :     }
     377              :     return result;
     378              :   }
     379              : };
     380              : 
     381              : }  // namespace LinearSolver::InnerProductImpls
     382              : 
     383              : namespace MakeWithValueImpls {
     384              : 
     385              : template <size_t Dim, typename TagsListOut, typename TagsListIn>
     386            0 : struct MakeWithValueImpl<
     387              :     LinearSolver::Schwarz::ElementCenteredSubdomainData<Dim, TagsListOut>,
     388              :     LinearSolver::Schwarz::ElementCenteredSubdomainData<Dim, TagsListIn>> {
     389            0 :   using SubdomainDataIn =
     390              :       LinearSolver::Schwarz::ElementCenteredSubdomainData<Dim, TagsListIn>;
     391            0 :   using SubdomainDataOut =
     392              :       LinearSolver::Schwarz::ElementCenteredSubdomainData<Dim, TagsListOut>;
     393              :   static SPECTRE_ALWAYS_INLINE SubdomainDataOut
     394            0 :   apply(const SubdomainDataIn& input, const double value) {
     395              :     SubdomainDataOut output{};
     396              :     output.element_data =
     397              :         make_with_value<typename SubdomainDataOut::ElementData>(
     398              :             input.element_data, value);
     399              :     for (const auto& [overlap_id, input_data] : input.overlap_data) {
     400              :       output.overlap_data.emplace(
     401              :           overlap_id, make_with_value<typename SubdomainDataOut::OverlapData>(
     402              :                           input_data, value));
     403              :     }
     404              :     return output;
     405              :   }
     406              : };
     407              : 
     408              : }  // namespace MakeWithValueImpls
        

Generated by: LCOV version 2.0-1