• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Open-Sn / opensn / 22654533986

04 Mar 2026 12:48AM UTC coverage: 74.268% (+0.08%) from 74.192%
22654533986

push

github

web-flow
Merge pull request #960 from andrsd/fv-removal

Removing finite-volume spatial discretization

0 of 2 new or added lines in 1 file covered. (0.0%)

183 existing lines in 9 files now uncovered.

19993 of 26920 relevant lines covered (74.27%)

67230185.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.86
/framework/data_types/ndarray.h
1
// SPDX-FileCopyrightText: 2024 The OpenSn Authors <https://open-sn.github.io/opensn/>
2
// SPDX-License-Identifier: MIT
3

4
#pragma once
5

6
#include <cstddef>
7
#include <type_traits>
8
#include <cassert>
9
#include <array>
10
#include <memory>
11
#include <initializer_list>
12
#include <stdexcept>
13
#include <algorithm>
14
#include <numeric>
15
#include <vector>
16
#include <string>
17

18
namespace opensn
19
{
20

21
template <typename T, int D>
22
class NDArray
2,147,483,647✔
23
{
24
private:
25
  template <bool...>
26
  struct bool_pack
27
  {
28
  };
29

30
  template <class... U>
31
  using conjunction = std::is_same<bool_pack<true, U::value...>, bool_pack<U::value..., true>>;
32

33
  template <typename... U>
34
  using AllIntegral = typename conjunction<std::is_integral<U>...>::type;
35

36
public:
37
  /**
38
   * Creates an empty array.
39
   * \throw std::bad_alloc if memory allocation fails.
40
   *
41
   * This constructor creates an empty array and initializes the reference
42
   * count to one.
43
   */
44
  NDArray() noexcept : size_(0), storage_(nullptr), dimensions_{}, strides_{} {}
10,999,908✔
45

46
  /**
47
   * Creates an array with the specified number of elements in each dimension,
48
   * from an array.
49
   *
50
   * \param dims `std::array` list of the number of elements in each
51
   *             dimension.
52
   * \throw std::bad_alloc if memory allocation fails.
53
   *
54
   * This constructor creates an array with the specified size.
55
   */
56
  explicit NDArray(const std::array<int, D>& dims) : size_(0)
57
  {
58
    SetDimensions(dims);
59
    Initialize();
60
  }
61

62
  /**
63
   * Creates an array with the specified number of elements in each dimension,
64
   * from an array and initializes the array.
65
   *
66
   * \param dims `std::array` list of the number of elements in each
67
   *             dimension.
68
   * \param value Initial element value.
69
   * \throw std::bad_alloc if memory allocation fails.
70
   *
71
   * This constructor creates an array with the specified size.
72
   */
73
  explicit NDArray(const std::array<int, D>& dims, T value) : size_(0)
74
  {
75
    SetDimensions(dims);
76
    ValueInitialize(value);
77
  }
78

79
  /**
80
   * Creates an array with the specified number of elements in each dimension,
81
   * from an initializer-list.
82
   *
83
   * \param dims `std::vector` list of the number of elements in each
84
   *             dimension.
85
   * \throw std::bad_alloc if memory allocation fails.
86
   *
87
   * This constructor creates an array with the specified size.
88
   */
89
  template <typename U>
90
  NDArray(const std::initializer_list<U>& dims) : size_(0)
95,011,532✔
91
  {
92
    SetDimensions(dims);
95,011,532✔
93
    Initialize();
95,011,532✔
94
  }
95,011,532✔
95

96
  /**
97
   * Creates an array with the specified number of elements in each dimension,
98
   * from an array. Each entry in the array is assigned the designated value.
99
   *
100
   * \param dims `std::array` list of the number of elements in each
101
   *             dimension.
102
   * \param value The value to assing to each element.
103
   * \throw std::bad_alloc if memory allocation fails.
104
   *
105
   * This constructor creates an array with the specified size.
106
   */
107
  template <typename U>
108
  explicit NDArray(const std::array<U, D>& dims, T value) : size_(0)
109
  {
110
    SetDimensions(dims);
111
    ValueInitialize(value);
112
  }
113

114
  /**
115
   * Creates an array with the specified number of elements in each dimension,
116
   * from an array. Each entry in the array is assigned the designated value.
117
   *
118
   * \param dims `std::array` list of the number of elements in each
119
   *             dimension.
120
   * \param value The value to assing to each element.
121
   * \throw std::bad_alloc if memory allocation fails.
122
   *
123
   * This constructor creates an array with the specified size.
124
   */
125
  template <typename U>
126
  explicit NDArray(const std::array<U, D>& dims) : size_(0)
127
  {
128
    SetDimensions(dims);
129
    Initialize();
130
  }
131

132
  /**
133
   * Creates an array with the specified number of elements in each dimension,
134
   * from an initializer-list. Each entry in the array is assigned the
135
   * designated value.
136
   *
137
   * \param dims `std::initializer` list of the number of elements in each
138
   *             dimension.
139
   * \param value The value to assing to each element.
140
   */
141
  template <typename U>
142
  NDArray(const std::initializer_list<U>& dims, T value) : size_(0)
2,147,483,647✔
143
  {
144
    SetDimensions(dims);
2,147,483,647✔
145
    ValueInitialize(value);
2,147,483,647✔
146
  }
2,147,483,647✔
147

148
  /// Copy constructor
149
  NDArray(const NDArray<T, D>& other)
532,731,790✔
150
    : size_(other.size_),
532,731,790✔
151
      storage_(std::make_unique<T[]>(other.size_)),
532,731,790✔
152
      dimensions_(other.dimensions_),
532,731,790✔
153
      strides_(other.strides_)
532,731,790✔
154
  {
155
    std::copy(other.storage_.get(), other.storage_.get() + size_, storage_.get());
532,731,790✔
156
  }
532,731,790✔
157

158
  /// Move constructor
159
  NDArray(NDArray<T, D>&& other) noexcept
160
    : size_(other.size_),
161
      storage_(std::move(other.storage_)),
162
      dimensions_(std::move(other.dimensions_)),
163
      strides_(std::move(other.strides_))
164
  {
165
  }
166

167
  /**
168
   * Assign from another array.
169
   *
170
   * \param other The array to copy.
171
   */
172
  NDArray<T, D>& operator=(NDArray<T, D> const& other)
173
  {
174
    if (this == &other)
175
      return *this;
176
    NDArray<T, D>(other).swap(*this);
177
    return *this;
178
  }
179

180
  /// Move assignment operator
181
  NDArray<T, D>& operator=(NDArray<T, D>&& other) noexcept
2,147,483,647✔
182
  {
183
    if (this != &other)
2,147,483,647✔
184
    {
185
      size_ = other.size_;
2,147,483,647✔
186
      dimensions_ = std::move(other.dimensions_);
2,147,483,647✔
187
      strides_ = std::move(other.strides_);
2,147,483,647✔
188
      storage_ = std::move(other.storage_);
2,147,483,647✔
189
      other.size_ = 0;
2,147,483,647✔
190
    }
191
    return *this;
2,147,483,647✔
192
  }
193

194
  /**
195
   * Resizes the array with a vector.
196
   *
197
   * \param dims std::vector of the number of elements in each
198
   *             dimension.
199
   * \throw std::bad_alloc if memory allocation fails.
200
   *
201
   * This method resizes the array to the specified number of elements. If the
202
   * current size is equal to the new size, no memory allocation occurs.
203
   */
204
  template <typename U>
205
  void resize(const std::array<U, D>& dims)
206
  {
207
    SetDimensions(dims);
208
    if (size_ != std::accumulate(dims.begin(), dims.end(), size_t{1}, std::multiplies<>()))
209
      Initialize();
210
  }
211

212
  /**
213
   * Resizes the array with an array.
214
   *
215
   * \param dims std::array of the number of elements in each
216
   *             dimension.
217
   * \throw std::bad_alloc if memory allocation fails.
218
   *
219
   * This method resizes the array to the specified number of elements. If the
220
   * current size is equal to the new size, no memory allocation occurs.
221
   */
222
  template <typename U, size_t N>
223
  void resize(const std::array<U, N>& dims)
224
  {
225
    SetDimensions(dims);
226
    if (size_ != std::accumulate(dims.begin(), dims.end(), size_t{1}, std::multiplies<>()))
227
      Initialize();
228
  }
229

230
  /**
231
   * Resizes the array with an initializer_list.
232
   *
233
   * \param dims std::initializer_list of the number of elements in each
234
   *             dimension.
235
   * \throw std::bad_alloc if memory allocation fails.
236
   *
237
   * This method resizes the array to the specified number of elements. If the
238
   * current size is equal to the new size, no memory allocation occurs.
239
   */
240
  template <typename U>
241
  void resize(const std::initializer_list<U>& dims)
242
  {
243
    SetDimensions(dims);
244
    if (size_ != std::accumulate(dims.begin(), dims.end(), size_t{1}, std::multiplies<>()))
245
      Initialize();
246
  }
247

248
  /**
249
   * Accesses the specified element for an array with N dimensions.
250
   *
251
   * \param args Indices for each dimension.
252
   * \return Read/write reference to the element.
253
   */
254
  template <typename... Args>
255
  __attribute__((always_inline)) constexpr T& operator()(Args... args) noexcept
2,147,483,647✔
256
  {
257
    return storage_[ComputeIndex(args...)];
2,147,483,647✔
258
  }
259

260
  __attribute__((always_inline)) constexpr T& operator()(size_t idx) noexcept
261
  {
262
    static_assert(D == 1, "Can be only used on 1-dimensional arrays");
263
    return storage_[idx];
264
  }
265

266
  __attribute__((always_inline)) constexpr T& operator()(size_t i, size_t j) noexcept
2,147,483,647✔
267
  {
268
    static_assert(D == 2, "Can be only used on 2-dimensional arrays");
269
    return storage_[i * strides_[0] + j];
2,147,483,647✔
270
  }
271

272
  /**
273
   * Accesses the specified element for an array with N dimensions with bounds checking.
274
   *
275
   * \param args Indices for each dimension.
276
   * \return Read/write reference to the element.
277
   */
278
  template <typename... Args>
279
  constexpr T& at(Args... args)
280
  {
281
    size_t indices[]{static_cast<size_t>(args)...};
282
    for (int i = 0; i < D; ++i)
283
    {
284
      if (indices[i] >= dimensions_[i])
285
        throw std::out_of_range("Index out of bounds.");
286
    }
287
    return storage_[ComputeIndex(args...)];
288
  }
289

290
  /**
291
   * Accesses the specified element for an array with N dimensions.
292
   *
293
   * \param args Indices for each dimension.
294
   * \return Read reference to the element.
295
   */
296
  template <typename... Args>
297
  __attribute__((always_inline)) constexpr const T& operator()(Args... args) const noexcept
2,147,483,647✔
298
  {
299
    return storage_[ComputeIndex(args...)];
2,147,483,647✔
300
  }
301

302
  __attribute__((always_inline)) constexpr T const& operator()(size_t idx) const noexcept
11,192,712✔
303
  {
304
    static_assert(D == 1, "Can be only used on 1-dimensional arrays");
305
    return storage_[idx];
11,192,712✔
306
  }
307

308
  __attribute__((always_inline)) constexpr T const& operator()(size_t i, size_t j) const noexcept
2,147,483,647✔
309
  {
310
    static_assert(D == 2, "Can be only used on 2-dimensional arrays");
311
    return storage_[i * strides_[0] + j];
2,147,483,647✔
312
  }
313

314
  /**
315
   * Accesses the specified element for an array with N dimensions with bounds checking.
316
   *
317
   * \param args Indices for each dimension.
318
   * \return Read reference to the element.
319
   */
320
  template <typename... Args>
321
  constexpr const T& at(Args... args) const
322
  {
323
    size_t indices[]{static_cast<size_t>(args)...};
324
    for (int i = 0; i < D; ++i)
325
    {
326
      if (indices[i] >= dimensions_[i])
327
        throw std::out_of_range("Index out of bounds.");
328
    }
329
    return storage_[ComputeIndex(args...)];
330
  }
331

332
  /// Returns an iterator pointing to the beginning of the array.
333
  constexpr T* begin() const noexcept { return storage_.get(); }
334

335
  /// Returns a constant iterator pointing to the beginning of the array.
336
  constexpr const T* cbegin() const noexcept { return storage_.get(); }
337

338
  /// Returns an iterator pointing to the end of the array.
339
  constexpr T* end() const noexcept { return storage_.get() + size_; }
340

341
  /// Returns a constant iterator pointing to the end of the array.
342
  constexpr const T* cend() const noexcept { return storage_.get() + size_; }
343

344
  /// Returns the number of elements in the array.
345
  constexpr size_t size() const noexcept { return size_; }
346

347
  /// Returns true if the array has no elements
348
  constexpr bool empty() const noexcept { return size_ == 0; }
15,298,615✔
349

350
  /// Returns a pointer to the underlying array data.
351
  constexpr T* data() const noexcept { return storage_.get(); }
352

353
  /// Returns the rank of the array.
354
  constexpr size_t rank() const noexcept { return D; }
355

356
  /// Returns the dimension of the array.
357
  std::vector<size_t> dimension() const
13,740,841✔
358
  {
359
    return std::vector<size_t>(dimensions_.begin(), dimensions_.begin() + D);
13,740,841✔
360
  }
361

362
  /// Sets each element of the array to the specified value.
363
  void set(T value) noexcept { std::fill(storage_.get(), storage_.get() + size_, value); }
255,000,233✔
364

365
  /**
366
   * Returns a linear index to the specified element with safety checks.
367
   *
368
   * \param args The indices of the desired element.
369
   * \throw std::invalid_argument if the number of arguments are incorrect and
370
   * std::out_of_range if one of the dimension-indices are out of range.
371
   * \return Linear index to the specified element.
372
   */
373
  template <typename... Args>
374
  size_t MapNDtoLin(Args... args) const
375
  {
376
    if (sizeof...(args) != D)
377
    {
378
      throw std::invalid_argument("Number of arguments " + std::to_string(sizeof...(args)) +
379
                                  " not equal to rank " + std::to_string(D));
380
    }
381

382
    size_t indices[]{static_cast<size_t>(args)...};
383
    for (int i = 0; i < D; ++i)
384
    {
385
      if (indices[i] >= dimensions_[i])
386
      {
387
        throw std::out_of_range("Index " + std::to_string(i) + " out of range " +
388
                                std::to_string(indices[i]) + " must be <" +
389
                                std::to_string(dimensions_[i]));
390
      }
391
    }
392

393
    return ComputeIndex(args...);
394
  }
395

396
  /**
397
   * Swap the contents of this array with another array.
398
   *
399
   * \param other The array to swap with.
400
   */
401
  void swap(NDArray<T, D>& other) noexcept
402
  {
403
    std::swap(size_, other.size_);
404
    std::swap(storage_, other.storage_);
405
    std::swap(dimensions_, other.dimensions_);
406
    std::swap(strides_, other.strides_);
407
  }
408

409
private:
410
  template <typename U>
411
  void SetDimensions(const std::vector<U>& dims)
412
  {
413
    if (dims.size() > D)
414
      throw std::invalid_argument("Number of dimension parameters exceeds the rank.");
415
    std::copy(dims.begin(), dims.end(), dimensions_.begin());
416
  }
417

418
  template <typename U>
419
  void SetDimensions(const std::array<U, D>& dims)
420
  {
421
    std::copy(dims.begin(), dims.end(), dimensions_.begin());
422
  }
423

424
  template <typename U>
425
  void SetDimensions(const std::initializer_list<U>& dims)
2,147,483,647✔
426
  {
427
    if (dims.size() > D)
2,147,483,647✔
428
      throw std::invalid_argument("Number of dimension parameters exceeds the rank.");
×
429
    std::copy(dims.begin(), dims.end(), dimensions_.begin());
2,147,483,647✔
430
  }
2,147,483,647✔
431

432
  void Initialize()
2,147,483,647✔
433
  {
434
    size_ = 1;
2,147,483,647✔
435
    strides_[D - 1] = 1;
2,147,483,647✔
436
    for (size_t i = D; i-- > 0;)
2,147,483,647✔
437
    {
438
      size_ *= dimensions_[i];
2,147,483,647✔
439
      if (i > 0)
2,147,483,647✔
440
        strides_[i - 1] = strides_[i] * dimensions_[i];
72,159,708✔
441
    }
442

443
    storage_ = std::make_unique<T[]>(size_);
2,147,483,647✔
444
  }
2,147,483,647✔
445

446
  void ValueInitialize(T value)
2,147,483,647✔
447
  {
448
    Initialize();
2,147,483,647✔
449
    std::fill_n(storage_.get(), size_, value);
2,147,483,647✔
450
  }
2,147,483,647✔
451

452
  template <typename... Args>
453
  __attribute__((always_inline)) constexpr size_t ComputeIndex(Args... args) const noexcept
2,147,483,647✔
454
  {
455
    size_t indices[]{static_cast<size_t>(args)...};
2,147,483,647✔
456

457
    if constexpr (sizeof...(args) == 1)
458
      return indices[0];
459
    else if constexpr (sizeof...(args) == 2)
460
      return indices[0] * strides_[0] + indices[1] * strides_[1];
2,147,483,647✔
461
    else if constexpr (sizeof...(args) == 3)
2,147,483,647✔
462
      return indices[0] * strides_[0] + indices[1] * strides_[1] + indices[2] * strides_[2];
2,147,483,647✔
463
    else if constexpr (sizeof...(args) == 4)
2,820,822✔
464
    {
2,820,822✔
465
      return indices[0] * strides_[0] + indices[1] * strides_[1] + indices[2] * strides_[2] +
5,641,644✔
466
             indices[3] * strides_[3];
1,679,720✔
467
    }
2,820,822✔
468
    else if constexpr (sizeof...(args) == 5)
1,440,625✔
469
    {
1,440,625✔
470
      return indices[0] * strides_[0] + indices[1] * strides_[1] + indices[2] * strides_[2] +
5,762,500✔
471
             indices[3] * strides_[3] + indices[4] * strides_[4];
4,321,875✔
472
    }
×
473

×
474
    size_t index = 0;
×
475
    for (int i = 0; i < D; ++i)
1,440,625✔
476
      index += indices[i] * strides_[i];
1,441,250✔
477
    return index;
39,014,804✔
478
  }
64✔
479

64✔
480
private:
64✔
481
  size_t size_;
64✔
482
  std::unique_ptr<T[]> storage_;
64✔
483
  std::array<size_t, D> dimensions_;
189✔
484
  std::array<size_t, D> strides_;
189✔
485
};
276,209,471✔
486

276,209,471✔
487
} // namespace opensn
276,209,471✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc