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 <initializer_list>
48 #include <numeric>
49 #include <ostream>
50 #include <type_traits>
51 #include <vector>
52 
53 namespace Opm
54 {
55 
56 
57 template<class>
58 inline constexpr bool always_false_v = false;
59 
60 // Poison iterator is a helper class that will allow for compilation only when it is not used.
61 // Its intention is to be used so that we can have a SparseTable of GPU data, which requires the
62 // GPUBuffer intermediate storage type, which does not support iterators.
63 template<class T>
65  // iterator traits so it type-checks where an iterator is required
66  using iterator_category = std::input_iterator_tag;
67  using value_type = T;
68  using difference_type = std::ptrdiff_t;
69  using pointer = T*;
70  using reference = T&;
71 
72  PoisonIterator() = default;
73 
74  // Dereference
75  reference operator*() const {
76  static_assert(always_false_v<T>, "PoisonIterator: operator*() is not allowed.");
77  return *ptr_;
78  }
79 
80  pointer operator->() const {
81  static_assert(always_false_v<T>, "PoisonIterator: operator->() is not allowed.");
82  return ptr_;
83  }
84 
85  // Pre-increment
86  PoisonIterator& operator++() {
87  static_assert(always_false_v<T>, "PoisonIterator: operator++() is not allowed.");
88  return *this;
89  }
90 
91  // Post-increment
92  PoisonIterator operator++(int) {
93  static_assert(always_false_v<T>, "PoisonIterator: operator++(int) is not allowed.");
94  return *this;
95  }
96 
97  // Equality/inequality
98  friend bool operator==(const PoisonIterator&, const PoisonIterator&) {
99  static_assert(always_false_v<T>, "PoisonIterator: operator== is not allowed.");
100  return true;
101  }
102 
103  friend bool operator!=(const PoisonIterator&, const PoisonIterator&) {
104  static_assert(always_false_v<T>, "PoisonIterator: operator!= is not allowed.");
105  return false;
106  }
107 
108 private:
109  T* ptr_ = nullptr; // placeholder to keep types consistent
110 };
111 
116  template <typename T, template <typename, typename...> class Storage = std::vector>
118  {
119  public:
122  : row_start_(1, 0)
123  {
124  }
125 
131  template <typename DataIter, typename IntegerIter>
132  SparseTable(DataIter data_beg, DataIter data_end,
133  IntegerIter rowsize_beg, IntegerIter rowsize_end)
134  : data_(data_beg, data_end)
135  {
136  setRowStartsFromSizes(rowsize_beg, rowsize_end);
137  }
138 
139  SparseTable(Storage<T>&& data, Storage<int>&& row_starts)
140  : data_(std::move(data))
141  , row_start_(std::move(row_starts))
142  {
143  // removed for non-default template instantiations
144  // because we cannot access the zero'th element if Storage is a GpuBuffer
145  if constexpr (std::is_same_v<Storage<T>, std::vector<T>>) {
146  OPM_ERROR_IF(row_start_.size() == 0 || row_start_[0] != 0,
147  "Invalid row_start array");
148  }
149  }
150 
151 
153  SparseTable(std::initializer_list<std::initializer_list<T>> initlist) requires (std::is_same_v<Storage<T>, std::vector<T>>)
154  {
155  row_start_.push_back(0);
156  for (const auto& row : initlist) {
157  data_.insert(data_.end(), row);
158  row_start_.push_back(data_.size());
159  }
160  }
161 
168  template <typename DataIter, typename IntegerIter>
169  void assign(DataIter data_beg, DataIter data_end,
170  IntegerIter rowsize_beg, IntegerIter rowsize_end)
171  {
172  data_.assign(data_beg, data_end);
173  setRowStartsFromSizes(rowsize_beg, rowsize_end);
174  }
175 
176 
180  template <typename IntegerIter>
181  void allocate(IntegerIter rowsize_beg, IntegerIter rowsize_end)
182  {
183  typedef typename Storage<T>::size_type sz_t;
184 
185  sz_t ndata = std::accumulate(rowsize_beg, rowsize_end, sz_t(0));
186  data_.resize(ndata);
187  setRowStartsFromSizes(rowsize_beg, rowsize_end);
188  }
189 
190 
192  template <typename DataIter>
193  void appendRow(DataIter row_beg, DataIter row_end)
194  {
195  data_.insert(data_.end(), row_beg, row_end);
196  row_start_.push_back(data_.size());
197  }
198 
200  OPM_HOST_DEVICE bool empty() const
201  {
202  return row_start_.size()==1;
203  }
204 
206  OPM_HOST_DEVICE int size() const
207  {
208  return row_start_.size() - 1;
209  }
210 
212  void reserve(int exptd_nrows, int exptd_ndata)
213  {
214  row_start_.reserve(exptd_nrows + 1);
215  data_.reserve(exptd_ndata);
216  }
217 
219  void swap(SparseTable<T>& other)
220  {
221  row_start_.swap(other.row_start_);
222  data_.swap(other.data_);
223  }
224 
226  OPM_HOST_DEVICE int dataSize() const
227  {
228  return data_.size();
229  }
230 
232  OPM_HOST_DEVICE int rowSize(int row) const
233  {
234 #ifndef NDEBUG
235  OPM_ERROR_IF(row < 0 || row >= size(),
236  "Row index " + std::to_string(row) + " is out of range");
237 #endif
238  return row_start_[row + 1] - row_start_[row];
239  }
240 
242  void clear()
243  {
244  data_.clear();
245  row_start_.resize(1);
246  }
247 
248  // Helper templates to select iterator range types only if (const_)iterator exists.
249  // Default: PoisonIterator (for non-traversable types)
250  template<class U, class = void>
254  };
255 
256  // If Storage has const_iterator, use it (e.g. std::vector)
257  template<class U>
258  struct row_type_helper<U, std::void_t<typename U::const_iterator>> {
261  };
262 
263 #if HAVE_CUDA
264  // Specialization for GpuView: use its iterator
265  template<typename TT>
266  struct row_type_helper<gpuistl::GpuView<TT>> {
269  };
270 
271  // Specialization for GpuBuffer: always PoisonIterator
272  template<typename TT>
273  struct row_type_helper<gpuistl::GpuBuffer<TT>> {
274  using const_type = iterator_range<PoisonIterator<TT>>;
275  using mutable_type = mutable_iterator_range<PoisonIterator<TT>>;
276  };
277 #endif // HAVE_CUDA
278 
279  using row_type = typename row_type_helper<Storage<T>>::const_type;
280  using mutable_row_type = typename row_type_helper<Storage<T>>::mutable_type;
281 
283  OPM_HOST_DEVICE row_type operator[](int row) const
284  {
285  assert(row >= 0 && row < size());
286  return row_type{data_.begin()+ row_start_[row],
287  data_.begin() + row_start_[row + 1]};
288  }
289 
291  OPM_HOST_DEVICE mutable_row_type operator[](int row)
292  {
293  assert(row >= 0 && row < size());
294  return mutable_row_type{data_.begin() + row_start_[row],
295  data_.begin() + row_start_[row + 1]};
296  }
297 
300  class Iterator
301  {
302  public:
303  OPM_HOST_DEVICE Iterator(const SparseTable& table, const int begin_row_index)
304  : table_(table)
305  , row_index_(begin_row_index)
306  {
307  }
308  OPM_HOST_DEVICE Iterator& operator++()
309  {
310  ++row_index_;
311  return *this;
312  }
313  OPM_HOST_DEVICE row_type operator*() const
314  {
315  return table_[row_index_];
316  }
317  OPM_HOST_DEVICE bool operator==(const Iterator& other)
318  {
319  assert(&table_ == &other.table_);
320  return row_index_ == other.row_index_;
321  }
322  OPM_HOST_DEVICE bool operator!=(const Iterator& other)
323  {
324  return !(*this == other);
325  }
326  private:
327  const SparseTable& table_;
328  int row_index_;
329  };
330 
332  OPM_HOST_DEVICE Iterator begin() const
333  {
334  return Iterator(*this, 0);
335  }
336  OPM_HOST_DEVICE Iterator end() const
337  {
338  return Iterator(*this, size());
339  }
340 
342  OPM_HOST_DEVICE bool operator==(const SparseTable& other) const
343  {
344  return data_ == other.data_ && row_start_ == other.row_start_;
345  }
346 
347  template<class charT, class traits>
348  void print(std::basic_ostream<charT, traits>& os) const
349  {
350  os << "Number of rows: " << size() << '\n';
351 
352  os << "Row starts = [";
353  std::ranges::copy(row_start_, std::ostream_iterator<int>(os, " "));
354  os << "\b]\n";
355 
356  os << "Data values = [";
357  std::ranges::copy(data_, std::ostream_iterator<T>(os, " "));
358  os << "\b]\n";
359  }
360  const T data(int i)const {
361  return data_[i];
362  }
363 
364  // Get pointer to start of databuffer
365  // This is useful for getting access to the buffer itself so we can copy to GPU easily
366  const T* dataPtr() const
367  {
368  return data_.data();
369  }
370 
371  // Access the data being stored directly (for instance used for copying to GPU)
372  const Storage<T>& dataStorage() const
373  {
374  return data_;
375  }
376 
377  // Access indices of where all rows start
378  const Storage<int>& rowStarts() const
379  {
380  return row_start_;
381  }
382  private:
383  Storage<T> data_;
384  // Like in the compressed row sparse matrix format,
385  // row_start_.size() is equal to the number of rows + 1.
386  Storage<int> row_start_;
387 
388  template <class IntegerIter>
389  void setRowStartsFromSizes(IntegerIter rowsize_beg, IntegerIter rowsize_end)
390  {
391 #ifndef NDEBUG
392  // Check that all row sizes given are nonnegative.
393  for (auto it = rowsize_beg; it != rowsize_end; ++it) {
394  if (*it < 0) {
395  OPM_THROW(std::runtime_error, "Negative row size given.");
396  }
397  }
398 #endif
399  // Since we do not store the row sizes, but cumulative row sizes,
400  // we have to create the cumulative ones.
401  int num_rows = rowsize_end - rowsize_beg;
402  row_start_.resize(num_rows + 1);
403  row_start_[0] = 0;
404  std::partial_sum(rowsize_beg, rowsize_end, row_start_.begin() + 1);
405  // Check that data_ and row_start_ match.
406  if (int(data_.size()) != row_start_.back()) {
407  OPM_THROW(std::runtime_error, "End of row start indices different from data size.");
408  }
409 
410  }
411  };
412 
413 } // namespace Opm
414 
415 #if HAVE_CUDA
416 namespace Opm::gpuistl {
417 
418 template <class T>
419 auto copy_to_gpu(const SparseTable<T>& cpu_table)
420 {
421  return SparseTable<T, GpuBuffer>(
422  GpuBuffer<T>(cpu_table.dataStorage()),
423  GpuBuffer<int>(cpu_table.rowStarts())
424  );
425 }
426 
427 template <class T>
428 auto make_view(SparseTable<T, GpuBuffer>& buffer_table)
429 {
430  return SparseTable<T, GpuView>(
431  GpuView<T>(const_cast<T*>(buffer_table.dataStorage().data()),
432  buffer_table.dataStorage().size()),
433  GpuView<int>(const_cast<int*>(buffer_table.rowStarts().data()),
434  buffer_table.rowStarts().size())
435  );
436 }
437 
438 } // namespace Opm::gpuistl
439 #endif // HAVE_CUDA
440 
441 #endif // OPM_SPARSETABLE_HEADER
OPM_HOST_DEVICE bool empty() const
True if the table contains no rows.
Definition: SparseTable.hpp:200
Definition: IteratorRange.hpp:76
A SparseTable stores a table with rows of varying size as efficiently as possible.
Definition: SparseTable.hpp:117
OPM_HOST_DEVICE int size() const
Returns the number of rows in the table.
Definition: SparseTable.hpp:206
void swap(SparseTable< T > &other)
Swap contents for other SparseTable<T>
Definition: SparseTable.hpp:219
OPM_HOST_DEVICE int dataSize() const
Returns the number of data elements.
Definition: SparseTable.hpp:226
Definition: Intersection.hpp:329
void reserve(int exptd_nrows, int exptd_ndata)
Allocate storage for table of expected size.
Definition: SparseTable.hpp:212
OPM_HOST_DEVICE bool operator==(const SparseTable &other) const
Equality.
Definition: SparseTable.hpp:342
Holds the implementation of the CpGrid as a pimple.
Definition: CellQuadrature.cpp:71
OPM_HOST_DEVICE Iterator begin() const
Iterator access.
Definition: SparseTable.hpp:332
void clear()
Makes the table empty().
Definition: SparseTable.hpp:242
OPM_HOST_DEVICE row_type operator[](int row) const
Returns a row of the table.
Definition: SparseTable.hpp:283
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:132
OPM_HOST_DEVICE int rowSize(int row) const
Returns the size of a table row.
Definition: SparseTable.hpp:232
void allocate(IntegerIter rowsize_beg, IntegerIter rowsize_end)
Request storage for table of given size.
Definition: SparseTable.hpp:181
Iterator for iterating over the container as a whole, i.e.
Definition: SparseTable.hpp:300
OPM_HOST_DEVICE mutable_row_type operator[](int row)
Returns a mutable row of the table.
Definition: SparseTable.hpp:291
Definition: IteratorRange.hpp:56
SparseTable()
Default constructor. Yields an empty SparseTable.
Definition: SparseTable.hpp:121
Definition: SparseTable.hpp:251
void appendRow(DataIter row_beg, DataIter row_end)
Appends a row to the table.
Definition: SparseTable.hpp:193
SparseTable(std::initializer_list< std::initializer_list< T >> initlist) requires(std
Initializer list constructor for easy construction of small SparseTables.
Definition: SparseTable.hpp:153
Definition: SparseTable.hpp:64
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:169