opm-grid
SparseTable.hpp
1 //===========================================================================
2 //
3 // File: SparseTable.hpp
4 //
5 // Created: Fri Apr 24 09:50:27 2009
6 //
7 // Author(s): Atgeirr F Rasmussen <atgeirr@sintef.no>
8 //
9 // $Date$
10 //
11 // $Revision$
12 //
13 //===========================================================================
14 
15 /*
16  Copyright 2009, 2010 SINTEF ICT, Applied Mathematics.
17  Copyright 2009, 2010 Statoil ASA.
18 
19  This file is part of the Open Porous Media project (OPM).
20 
21  OPM is free software: you can redistribute it and/or modify
22  it under the terms of the GNU General Public License as published by
23  the Free Software Foundation, either version 3 of the License, or
24  (at your option) any later version.
25 
26  OPM is distributed in the hope that it will be useful,
27  but WITHOUT ANY WARRANTY; without even the implied warranty of
28  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29  GNU General Public License for more details.
30 
31  You should have received a copy of the GNU General Public License
32  along with OPM. If not, see <http://www.gnu.org/licenses/>.
33 */
34 
35 #ifndef OPM_SPARSETABLE_HEADER
36 #define OPM_SPARSETABLE_HEADER
37 
38 #include <opm/grid/utility/ErrorMacros.hpp>
39 #include <opm/grid/utility/IteratorRange.hpp>
40 
41 #if HAVE_OPM_COMMON
42 #include <opm/common/utility/gpuistl_if_available.hpp>
43 #endif
44 
45 #include <algorithm>
46 #include <cassert>
47 #include <numeric>
48 #include <ostream>
49 #include <type_traits>
50 #include <vector>
51 
52 namespace Opm
53 {
54 
55 
56  template<class>
57 inline constexpr bool always_false_v = false;
58 
59 // Poison iterator is a helper class that will allow for compilation only when it is not used.
60 // Its intention is to be used so that we can have a SparseTable of GPU data, which requires the
61 // GPUBuffer intermediate storage type, which does not support iterators.
62 template<class T>
64  // iterator traits so it type-checks where an iterator is required
65  using iterator_category = std::input_iterator_tag;
66  using value_type = T;
67  using difference_type = std::ptrdiff_t;
68  using pointer = T*;
69  using reference = T&;
70 
71  PoisonIterator() = default;
72 
73  // Dereference
74  reference operator*() const {
75  static_assert(always_false_v<T>, "PoisonIterator: operator*() is not allowed.");
76  return *ptr_;
77  }
78 
79  pointer operator->() const {
80  static_assert(always_false_v<T>, "PoisonIterator: operator->() is not allowed.");
81  return ptr_;
82  }
83 
84  // Pre-increment
85  PoisonIterator& operator++() {
86  static_assert(always_false_v<T>, "PoisonIterator: operator++() is not allowed.");
87  return *this;
88  }
89 
90  // Post-increment
91  PoisonIterator operator++(int) {
92  static_assert(always_false_v<T>, "PoisonIterator: operator++(int) is not allowed.");
93  return *this;
94  }
95 
96  // Equality/inequality
97  friend bool operator==(const PoisonIterator&, const PoisonIterator&) {
98  static_assert(always_false_v<T>, "PoisonIterator: operator== is not allowed.");
99  return true;
100  }
101 
102  friend bool operator!=(const PoisonIterator&, const PoisonIterator&) {
103  static_assert(always_false_v<T>, "PoisonIterator: operator!= is not allowed.");
104  return false;
105  }
106 
107 private:
108  T* ptr_ = nullptr; // placeholder to keep types consistent
109 };
110 
115  template <typename T, template <typename, typename...> class Storage = std::vector>
117  {
118  public:
121  : row_start_(1, 0)
122  {
123  }
124 
130  template <typename DataIter, typename IntegerIter>
131  SparseTable(DataIter data_beg, DataIter data_end,
132  IntegerIter rowsize_beg, IntegerIter rowsize_end)
133  : data_(data_beg, data_end)
134  {
135  setRowStartsFromSizes(rowsize_beg, rowsize_end);
136  }
137 
138  SparseTable (Storage<T>&& data, Storage<int>&& row_starts)
139  : data_(std::move(data))
140  , row_start_(std::move(row_starts))
141  {
142  // removed for non-default template instantiations
143  // because we cannot access the zero'th element if Storage is a GpuBuffer
144  if constexpr (std::is_same_v<Storage<T>, std::vector<T>>) {
145  OPM_ERROR_IF(row_start_.size() == 0 || row_start_[0] != 0,
146  "Invalid row_start array");
147  }
148  }
149 
150 
157  template <typename DataIter, typename IntegerIter>
158  void assign(DataIter data_beg, DataIter data_end,
159  IntegerIter rowsize_beg, IntegerIter rowsize_end)
160  {
161  data_.assign(data_beg, data_end);
162  setRowStartsFromSizes(rowsize_beg, rowsize_end);
163  }
164 
165 
169  template <typename IntegerIter>
170  void allocate(IntegerIter rowsize_beg, IntegerIter rowsize_end)
171  {
172  typedef typename Storage<T>::size_type sz_t;
173 
174  sz_t ndata = std::accumulate(rowsize_beg, rowsize_end, sz_t(0));
175  data_.resize(ndata);
176  setRowStartsFromSizes(rowsize_beg, rowsize_end);
177  }
178 
179 
181  template <typename DataIter>
182  void appendRow(DataIter row_beg, DataIter row_end)
183  {
184  data_.insert(data_.end(), row_beg, row_end);
185  row_start_.push_back(data_.size());
186  }
187 
189  OPM_HOST_DEVICE bool empty() const
190  {
191  return row_start_.size()==1;
192  }
193 
195  OPM_HOST_DEVICE int size() const
196  {
197  return row_start_.size() - 1;
198  }
199 
201  void reserve(int exptd_nrows, int exptd_ndata)
202  {
203  row_start_.reserve(exptd_nrows + 1);
204  data_.reserve(exptd_ndata);
205  }
206 
208  void swap(SparseTable<T>& other)
209  {
210  row_start_.swap(other.row_start_);
211  data_.swap(other.data_);
212  }
213 
215  OPM_HOST_DEVICE int dataSize() const
216  {
217  return data_.size();
218  }
219 
221  OPM_HOST_DEVICE int rowSize(int row) const
222  {
223 #ifndef NDEBUG
224  OPM_ERROR_IF(row < 0 || row >= size(),
225  "Row index " + std::to_string(row) + " is out of range");
226 #endif
227  return row_start_[row + 1] - row_start_[row];
228  }
229 
231  void clear()
232  {
233  data_.clear();
234  row_start_.resize(1);
235  }
236 
237  // Helper templates to select iterator range types only if (const_)iterator exists.
238  // Default: PoisonIterator (for non-traversable types)
239  template<class U, class = void>
243  };
244 
245  // If Storage has const_iterator, use it (e.g. std::vector)
246  template<class U>
247  struct row_type_helper<U, std::void_t<typename U::const_iterator>> {
250  };
251 
252 #if HAVE_CUDA
253  // Specialization for GpuView: use its iterator
254  template<typename TT>
255  struct row_type_helper<gpuistl::GpuView<TT>> {
258  };
259 
260  // Specialization for GpuBuffer: always PoisonIterator
261  template<typename TT>
262  struct row_type_helper<gpuistl::GpuBuffer<TT>> {
263  using const_type = iterator_range<PoisonIterator<TT>>;
264  using mutable_type = mutable_iterator_range<PoisonIterator<TT>>;
265  };
266 #endif // HAVE_CUDA
267 
268  using row_type = typename row_type_helper<Storage<T>>::const_type;
269  using mutable_row_type = typename row_type_helper<Storage<T>>::mutable_type;
270 
272  OPM_HOST_DEVICE row_type operator[](int row) const
273  {
274  assert(row >= 0 && row < size());
275  return row_type{data_.begin()+ row_start_[row],
276  data_.begin() + row_start_[row + 1]};
277  }
278 
280  OPM_HOST_DEVICE mutable_row_type operator[](int row)
281  {
282  assert(row >= 0 && row < size());
283  return mutable_row_type{data_.begin() + row_start_[row],
284  data_.begin() + row_start_[row + 1]};
285  }
286 
289  class Iterator
290  {
291  public:
292  OPM_HOST_DEVICE Iterator(const SparseTable& table, const int begin_row_index)
293  : table_(table)
294  , row_index_(begin_row_index)
295  {
296  }
297  OPM_HOST_DEVICE Iterator& operator++()
298  {
299  ++row_index_;
300  return *this;
301  }
302  OPM_HOST_DEVICE row_type operator*() const
303  {
304  return table_[row_index_];
305  }
306  OPM_HOST_DEVICE bool operator==(const Iterator& other)
307  {
308  assert(&table_ == &other.table_);
309  return row_index_ == other.row_index_;
310  }
311  OPM_HOST_DEVICE bool operator!=(const Iterator& other)
312  {
313  return !(*this == other);
314  }
315  private:
316  const SparseTable& table_;
317  int row_index_;
318  };
319 
321  OPM_HOST_DEVICE Iterator begin() const
322  {
323  return Iterator(*this, 0);
324  }
325  OPM_HOST_DEVICE Iterator end() const
326  {
327  return Iterator(*this, size());
328  }
329 
331  OPM_HOST_DEVICE bool operator==(const SparseTable& other) const
332  {
333  return data_ == other.data_ && row_start_ == other.row_start_;
334  }
335 
336  template<class charT, class traits>
337  void print(std::basic_ostream<charT, traits>& os) const
338  {
339  os << "Number of rows: " << size() << '\n';
340 
341  os << "Row starts = [";
342  std::ranges::copy(row_start_, std::ostream_iterator<int>(os, " "));
343  os << "\b]\n";
344 
345  os << "Data values = [";
346  std::ranges::copy(data_, std::ostream_iterator<T>(os, " "));
347  os << "\b]\n";
348  }
349  const T data(int i)const {
350  return data_[i];
351  }
352 
353  // Get pointer to start of databuffer
354  // This is useful for getting access to the buffer itself so we can copy to GPU easily
355  const T* dataPtr() const
356  {
357  return data_.data();
358  }
359 
360  // Access the data being stored directly (for instance used for copying to GPU)
361  const Storage<T>& dataStorage() const
362  {
363  return data_;
364  }
365 
366  // Access indices of where all rows start
367  const Storage<int>& rowStarts() const
368  {
369  return row_start_;
370  }
371  private:
372  Storage<T> data_;
373  // Like in the compressed row sparse matrix format,
374  // row_start_.size() is equal to the number of rows + 1.
375  Storage<int> row_start_;
376 
377  template <class IntegerIter>
378  void setRowStartsFromSizes(IntegerIter rowsize_beg, IntegerIter rowsize_end)
379  {
380 #ifndef NDEBUG
381  // Check that all row sizes given are nonnegative.
382  for (auto it = rowsize_beg; it != rowsize_end; ++it) {
383  if (*it < 0) {
384  OPM_THROW(std::runtime_error, "Negative row size given.");
385  }
386  }
387 #endif
388  // Since we do not store the row sizes, but cumulative row sizes,
389  // we have to create the cumulative ones.
390  int num_rows = rowsize_end - rowsize_beg;
391  row_start_.resize(num_rows + 1);
392  row_start_[0] = 0;
393  std::partial_sum(rowsize_beg, rowsize_end, row_start_.begin() + 1);
394  // Check that data_ and row_start_ match.
395  if (int(data_.size()) != row_start_.back()) {
396  OPM_THROW(std::runtime_error, "End of row start indices different from data size.");
397  }
398 
399  }
400  };
401 
402 } // namespace Opm
403 
404 #if HAVE_CUDA
405 namespace Opm::gpuistl {
406 
407 template <class T>
408 auto copy_to_gpu(const SparseTable<T>& cpu_table)
409 {
410  return SparseTable<T, GpuBuffer>(
411  GpuBuffer<T>(cpu_table.dataStorage()),
412  GpuBuffer<int>(cpu_table.rowStarts())
413  );
414 }
415 
416 template <class T>
417 auto make_view(SparseTable<T, GpuBuffer>& buffer_table)
418 {
419  return SparseTable<T, GpuView>(
420  GpuView<T>(const_cast<T*>(buffer_table.dataStorage().data()),
421  buffer_table.dataStorage().size()),
422  GpuView<int>(const_cast<int*>(buffer_table.rowStarts().data()),
423  buffer_table.rowStarts().size())
424  );
425 }
426 
427 } // namespace Opm::gpuistl
428 #endif // HAVE_CUDA
429 
430 #endif // OPM_SPARSETABLE_HEADER
OPM_HOST_DEVICE bool empty() const
True if the table contains no rows.
Definition: SparseTable.hpp:189
Definition: IteratorRange.hpp:76
A SparseTable stores a table with rows of varying size as efficiently as possible.
Definition: SparseTable.hpp:116
OPM_HOST_DEVICE int size() const
Returns the number of rows in the table.
Definition: SparseTable.hpp:195
void swap(SparseTable< T > &other)
Swap contents for other SparseTable<T>
Definition: SparseTable.hpp:208
OPM_HOST_DEVICE int dataSize() const
Returns the number of data elements.
Definition: SparseTable.hpp:215
Definition: Intersection.hpp:329
void reserve(int exptd_nrows, int exptd_ndata)
Allocate storage for table of expected size.
Definition: SparseTable.hpp:201
OPM_HOST_DEVICE bool operator==(const SparseTable &other) const
Equality.
Definition: SparseTable.hpp:331
Holds the implementation of the CpGrid as a pimple.
Definition: CellQuadrature.cpp:71
OPM_HOST_DEVICE Iterator begin() const
Iterator access.
Definition: SparseTable.hpp:321
void clear()
Makes the table empty().
Definition: SparseTable.hpp:231
OPM_HOST_DEVICE row_type operator[](int row) const
Returns a row of the table.
Definition: SparseTable.hpp:272
SparseTable(DataIter data_beg, DataIter data_end, IntegerIter rowsize_beg, IntegerIter rowsize_end)
A constructor taking all the data for the table and row sizes.
Definition: SparseTable.hpp:131
OPM_HOST_DEVICE int rowSize(int row) const
Returns the size of a table row.
Definition: SparseTable.hpp:221
void allocate(IntegerIter rowsize_beg, IntegerIter rowsize_end)
Request storage for table of given size.
Definition: SparseTable.hpp:170
Iterator for iterating over the container as a whole, i.e.
Definition: SparseTable.hpp:289
OPM_HOST_DEVICE mutable_row_type operator[](int row)
Returns a mutable row of the table.
Definition: SparseTable.hpp:280
Definition: IteratorRange.hpp:56
SparseTable()
Default constructor. Yields an empty SparseTable.
Definition: SparseTable.hpp:120
Definition: SparseTable.hpp:240
void appendRow(DataIter row_beg, DataIter row_end)
Appends a row to the table.
Definition: SparseTable.hpp:182
Definition: SparseTable.hpp:63
void assign(DataIter data_beg, DataIter data_end, IntegerIter rowsize_beg, IntegerIter rowsize_end)
Sets the table to contain the given data, organized into rows as indicated by the given row sizes...
Definition: SparseTable.hpp:158