DNDSR 0.1.0.dev1+gcd065ad
Distributed Numeric Data Structure for CFV
Loading...
Searching...
No Matches
Array.hpp
Go to the documentation of this file.
1#pragma once
2/// @file Array.hpp
3/// @brief Core 2D variable-length array container with five data layouts.
4/// @par Unit Test Coverage (test_Array.cpp)
5/// - All 5 layouts: TABLE_StaticFixed, TABLE_Fixed, TABLE_StaticMax, TABLE_Max, CSR
6/// - Resize, ResizeRow, Size, RowSize, RowSizeMax, GetDataLayout
7/// - Element access: operator(), operator[]
8/// - DataSize, DataSizeBytes
9/// - Compress / Decompress round-trip (CSR)
10/// - clone, CopyData, copy constructor, SwapData
11/// - WriteSerializer / ReadSerializer (JSON, 3 layouts)
12/// - GetArraySignature, ParseArraySignatureTuple, ArraySignatureIsCompatible
13/// - hash
14/// @par Not Yet Tested
15/// - ReserveRow, RawDataVector, view, FullSizeBytes, operator<<
16/// - WriteSerializer / ReadSerializer with TABLE_StaticMax, TABLE_Max
17/// - to_host / to_device / clear_device / deviceView (CUDA)
18/// - Move semantics
19#include <cassert>
20#include <memory>
21#ifndef DNDS_ARRAY_HPP
22# define DNDS_ARRAY_HPP
23
24# include <vector>
25# include <iostream>
26# include <typeinfo>
27# include <utility>
28
29# include <fmt/core.h>
30
31# include "Defines.hpp"
32# include "ArrayBasic.hpp"
33# include "DeviceStorage.hpp"
34# include "DeviceView.hpp"
35# include "SerializerBase.hpp"
36# include "SerializerJSON.hpp"
37# include "Vector.hpp"
38
39namespace DNDS
40{
41
42 /**
43 * @brief Core 2D variable-length array container, the storage foundation of DNDSR.
44 *
45 * @details
46 * @ref DNDS::Array "Array" is a single template that unifies five distinct storage layouts
47 * used throughout the CFD code base (cell volumes, conservative variables,
48 * mesh connectivity, reconstruction coefficients, etc.). The layout is
49 * chosen at compile time by combining `_row_size` and `_row_max`:
50 *
51 * ## Array layouts
52 * | | _row_size>=0 | _row_size==DynamicSize | _row_size==NonUniformSize |
53 * | --- | --- | --- | --- |
54 * |_row_max>=0 | TABLE_StaticFixed | TABLE_Fixed | TABLE_StaticMax |
55 * |_row_max==DynamicSize | TABLE_StaticFixed _row_max ignored | TABLE_Fixed _row_max ignored | TABLE_Max |
56 * |_row_max==NonUniformSize | TABLE_StaticFixed _row_max ignored | TABLE_Fixed _row_max ignored | CSR |
57 *
58 * Concrete semantics per layout:
59 * - **TABLE_StaticFixed**: every row has `_row_size` elements (compile-time
60 * constant). Used for Euler state vectors (5 reals), cell volumes (1),
61 * coordinates (3), etc.
62 * - **TABLE_Fixed**: every row has the same runtime-determined width
63 * (`_row_size_dynamic`). Used when the width depends on solver settings
64 * (e.g., reconstruction polynomial order).
65 * - **TABLE_StaticMax / TABLE_Max**: rows are padded to a maximum width
66 * (`_row_max` compile-time or runtime), with an auxiliary `_pRowSizes`
67 * vector giving the actual used width per row. Offers O(1) random access
68 * at the cost of wasted space for short rows.
69 * - **CSR**: flat buffer plus `_pRowStart[n+1]`. No wasted space but needs
70 * pointer indirection. Two internal sub-states: *compressed* (flat) and
71 * *decompressed* (`vector<vector<T>>`, used during incremental row growth).
72 * `Compress()` must be called before MPI communication or serialization.
73 *
74 * The class inherits from @ref DNDS::ObjectNaming "ObjectNaming" so each instance may carry a
75 * human-readable name (e.g. `"coords"`, `"cell2node"`) shown in assertions.
76 *
77 * @tparam T Element type. Typically #real (double) or #index (int64_t).
78 * Must be trivially copyable for CUDA / MPI paths.
79 * @tparam _row_size Row width:
80 * - `>=0` fixed width at compile time;
81 * - @ref DynamicSize uniform width set at Resize();
82 * - @ref NonUniformSize variable per row (combined with `_row_max`).
83 * @tparam _row_max Only relevant when `_row_size == NonUniformSize`:
84 * - `>=0` `TABLE_StaticMax` layout;
85 * - @ref DynamicSize `TABLE_Max` layout;
86 * - @ref NonUniformSize `CSR` layout.
87 * @tparam _align Alignment hint (currently only @ref NoAlign is used).
88 *
89 * @sa ArrayBasic.hpp for the layout enum and signature parsing.
90 * @sa ArrayTransformer for adding MPI ghost communication.
91 * @sa ArrayPair for the typical father/son bundle.
92 * @sa docs/architecture/array_infrastructure.md, docs/guides/array_usage.md.
93 * @todo Implement the `_align` feature (currently ignored).
94 */
95 template <class T, rowsize _row_size = 1, rowsize _row_max = _row_size, rowsize _align = NoAlign>
96 class Array : public ArrayLayout<T, _row_size, _row_max, _align>, public ObjectNaming
97 {
98 public:
100
102 using t_Layout::al,
116
118
119 static constexpr bool IsCSR() { return isCSR; }
120
121 public:
122 //* compressed data
124 //* uncompressed data (only for CSR)
125 using t_DataUncompressed = std::vector<std::vector<value_type>>;
126
127 //* non uniform data: CSR
130
131 //* non uniform-with max data: TABLE
134
135 protected:
136 t_pRowStart _pRowStart; // CSR in number of T
137 t_pRowSizes _pRowSizes; // TABLE in number of T
140
142
143 index _size = 0; // in number of T
144 rowsize _row_size_dynamic = 0; // in number of T
145 public:
146 /// @brief Shared pointer to the row-start index (CSR layout only).
147 /// @details `_pRowStart->at(i)` gives the flat-buffer offset of row `i`.
148 /// Size is `Size()+1`; the sentinel at the end equals `DataSize()`.
150 /// @brief Shared pointer to the per-row size vector (TABLE_Max / TABLE_StaticMax).
151 /// @details For padded layouts, records the number of "used" columns in each row.
153
154 public:
155 /// @brief Default-constructed array: empty, no storage.
156 Array() = default;
157
158 /// @brief Named constructor: sets the object name for tracing/debugging.
159 /// Delegates to the default constructor, then sets the name.
161 {
162 this->setObjectName(std::move(objName.name));
163 }
164
165 // TODO: constructors
166 // TODO: A indexer-copying build method:
167 // TODO: for CSR: c->c, u->u
168 // TODO: for intertype: CSR->Max, Max->CSR ...
169
170 /// @brief Number of rows currently stored. O(1).
171 [[nodiscard]] index Size() const { return _size; }
172
173 /// @brief Uniform row width for fixed layouts (no row index needed).
174 /// @details Valid only for `TABLE_Fixed` and `TABLE_StaticFixed`; asserts otherwise.
175 /// @return Width in number of `T` elements.
176 [[nodiscard]] rowsize RowSize() const // iRow is actually dummy here
177 {
178 if constexpr (_dataLayout == TABLE_Fixed)
179 return _row_size_dynamic;
180 else if constexpr (_dataLayout == TABLE_StaticFixed)
181 return rs;
182 else
183 {
184 DNDS_assert_info(false, "invalid call");
185 return -1;
186 }
187 }
188
189 /// @brief Number of T elements per row in flat storage (data stride).
190 /// For TABLE_StaticMax/TABLE_Max, this is _row_max (padded), not RowSize (used).
191 /// Not valid for CSR (variable stride per row).
193 {
194 if constexpr (_dataLayout == TABLE_StaticFixed)
195 return rs;
196 else if constexpr (_dataLayout == TABLE_StaticMax)
197 return rm;
198 else if constexpr (_dataLayout == TABLE_Fixed || _dataLayout == TABLE_Max)
199 return _row_size_dynamic;
200 else
201 {
202 DNDS_assert(false);
203 return 0;
204 }
205 }
206
207 /// @brief Width used by row `iRow` in number of `T` elements.
208 /// @details Works for every layout:
209 /// - `TABLE_*Fixed`: returns the uniform row width (iRow ignored for value);
210 /// - `TABLE_*Max`: returns `_pRowSizes->at(iRow)`;
211 /// - `CSR`: returns `pRowStart[iRow+1] - pRowStart[iRow]` when
212 /// compressed, else the nested vector size.
213 /// @param iRow Row index in `[0, Size())`. Asserted in non-fixed layouts.
215 {
216 if constexpr (_dataLayout == TABLE_Fixed)
217 return _row_size_dynamic;
218 else if constexpr (_dataLayout == TABLE_StaticFixed)
219 return rs;
220 DNDS_assert_info(iRow < _size && iRow >= 0, "query position out of range");
221 if constexpr (_dataLayout == TABLE_Max || _dataLayout == TABLE_StaticMax) // TABLE with Max
222 {
223 DNDS_assert_info(_pRowSizes, "_pRowSizes invalid"); // TABLE with Max must have a RowSizes
224 return _pRowSizes->at(iRow);
225 }
226 else if constexpr (_dataLayout == CSR)
227 {
228 if (IfCompressed())
229 {
230 index pDiff = _pRowStart->at(iRow + 1) - _pRowStart->at(iRow);
231 DNDS_assert(pDiff < INT32_MAX); // overflow
232 return static_cast<rowsize>(pDiff);
233 }
234 else
235 {
236 auto rs_cur = _dataUncompressed.at(iRow).size();
237 return static_cast<rowsize>(rs_cur);
238 }
239 }
240 }
241
242 /// @brief Maximum allowed row width for `TABLE_Max` / `TABLE_StaticMax`.
243 /// @details Returns the compile-time `rm` for `TABLE_StaticMax` or the
244 /// dynamic max (`_row_size_dynamic`) for `TABLE_Max`. Asserts for other layouts.
246 {
247 if constexpr (_dataLayout == TABLE_Max || _dataLayout == TABLE_StaticMax)
249 else
250 DNDS_assert_info(false, "invalid call");
251 }
252
253 /// @brief "Logical" row-field width used by derived (Eigen) arrays: max for
254 /// padded layouts, uniform width for fixed layouts. Not valid for CSR.
256 {
257 if constexpr (_dataLayout == TABLE_Max || _dataLayout == TABLE_StaticMax)
258 return this->RowSizeMax();
259 else if constexpr (_dataLayout == TABLE_Fixed || _dataLayout == TABLE_StaticFixed)
260 return this->RowSize();
261 else
262 DNDS_assert_info(false, "invalid call");
263 }
264
265 /// @brief Per-row "field" size for CSR (= actual row width). Invalid elsewhere.
267 {
268 if constexpr (_dataLayout == CSR)
269 return this->RowSize(iRow);
270 else
271 DNDS_assert_info(false, "invalid call");
272 }
273
274 protected:
275 [[nodiscard]] bool IfCompressed_() const
276 {
277 if constexpr (_dataLayout == CSR)
278 {
279 if (_size > 0)
280 {
281 return bool(_pRowStart);
282 }
283 return bool(_pRowStart); // size-0 array is not always considered compressed
284 }
285 return true; // non-CSR ones are always compressed
286 }
287
288 public:
289 /// @brief (CSR only) Whether the array is in packed / flat form.
290 /// @details `true` means data sits in a single `_data` buffer plus `_pRowStart`;
291 /// `false` means rows live in a `vector<vector<T>>` (`_dataUncompressed`), which
292 /// allows per-row `ResizeRow()`. MPI / serialization / CUDA require the
293 /// compressed form -- call @ref Compress() first.
294 [[nodiscard]] bool IfCompressed() const
295 {
296 static_assert(_dataLayout == CSR, "invalid call");
297 return IfCompressed_();
298 }
299
300 /// @brief (CSR only) Switch to the uncompressed (nested vector) representation.
301 /// @details Copies each row out of the flat buffer into an entry of
302 /// `_dataUncompressed`, then clears the flat buffer. No-op if already uncompressed.
303 /// After this, @ref ResizeRow and @ref ReserveRow may be used to reshape individual rows.
305 {
306 if (!IfCompressed())
307 return;
308 _dataUncompressed.resize(_size);
309 for (index i = 0; i < _size; i++)
310 {
311 // _dataUncompressed[i].resize( - _pRowStart->at(i));
312 auto iterStart = _data.begin() + _pRowStart->at(i);
313 auto iterEnd = _data.begin() + _pRowStart->at(i + 1);
315 }
316 _data.clear();
317 _pRowStart.reset();
318 }
319
320 /// @brief (CSR only) Pack the nested-vector representation into a flat
321 /// buffer plus `_pRowStart`. No-op if already compressed.
322 /// @details Row widths are frozen; further per-row resizing requires another
323 /// @ref Decompress. Mandatory before MPI ghost exchange, CUDA transfer, or serialization.
325 {
326 if (IfCompressed())
327 return;
328 _pRowStart = std::make_shared<
329 typename decltype(_pRowStart)::element_type>(_size + 1, 0);
330 _pRowStart->at(0) = 0;
331 for (index i = 0; i < _size; i++)
332 {
333 index rsI = _pRowStart->at(i);
334 index rsIP = rsI + static_cast<index>(_dataUncompressed.at(i).size());
336 _pRowStart->at(i + 1) = rsIP;
337 }
338 _data.resize(_pRowStart->at(_size));
339 for (index i = 0; i < _size; i++)
340 {
341 // // _dataUncompressed[i].resize( - _pRowStart->at(i));
342 // auto iterStart = _data.begin() + _pRowStart->at(i);
343 // auto iterEnd = _data.begin() + _pRowStart->at(i + 1);
344 // _dataUncompressed[i].assign(iterStart, iterEnd);
345 // _data.push_back(data)
347 sizeof(T) * _dataUncompressed[i].size());
348 DNDS_check_throw(_pRowStart->at(i) + _dataUncompressed[i].size() <= _data.size());
349 //! any better way?
350 }
351 _dataUncompressed.clear();
352 }
353
354 /// @brief Layout-polymorphic compress: no-op for non-CSR, calls @ref CSRCompress for CSR.
355 void Compress()
356 {
357 if constexpr (_dataLayout == CSR)
358 CSRCompress();
359 }
360 /// @brief Layout-polymorphic decompress: no-op for non-CSR, calls @ref CSRDecompress for CSR.
362 {
363 if constexpr (_dataLayout == CSR)
365 }
366
367 /// @brief Access to the underlying flat buffer (`host_device_vector<T>`).
368 /// @details For CSR, asserts that the array is compressed. Mutating the buffer
369 /// bypasses all row-size bookkeeping -- use with care.
371 {
372 if constexpr (_dataLayout == CSR)
374 return _data;
375 }
376
377 /**
378 * @brief Resize the array, setting a uniform or maximum row width.
379 *
380 * @details
381 * Invalidates all existing data and resets row-size metadata.
382 *
383 * Layout-specific semantics:
384 * - `TABLE_StaticFixed`: `nRow_size_dynamic` must equal `rs`.
385 * - `TABLE_StaticMax`: `nRow_size_dynamic` must equal `rm`.
386 * - `TABLE_Fixed`: sets the runtime uniform row width.
387 * - `TABLE_Max`: sets the runtime maximum row width (per-row sizes start at 0).
388 * - `CSR`: requires the array to be **decompressed**; allocates
389 * `nSize` empty rows (nested vectors sized to `nRow_size_dynamic`).
390 *
391 * @param nSize New number of rows.
392 * @param nRow_size_dynamic Row width (uniform layouts) or max width
393 * (padded layouts) or initial row length (CSR).
394 */
396 {
397 if constexpr (_dataLayout == CSR) // to un compressed
398 {
399 DNDS_check_throw_info(!IfCompressed(), "Need to decompress before auto resizing");
400 _size = nSize;
401 // _dataUncompressed.resize(nSize, typename decltype(_dataUncompressed)::value_type(nRow_size_dynamic));
402 // _dataUncompressed.resize(nSize);
404 }
405 else
406 {
407 _size = nSize;
408 if constexpr (_dataLayout == TABLE_Fixed || _dataLayout == TABLE_Max)
410 else if constexpr (_dataLayout == TABLE_StaticFixed)
412 else if constexpr (_dataLayout == TABLE_StaticMax)
414
415 if constexpr (_dataLayout == TABLE_Max || _dataLayout == TABLE_StaticMax)
416 {
417 if (_pRowSizes.use_count() == 1)
418 _pRowSizes->resize(nSize, 0);
419 else
420 _pRowSizes = std::make_shared<
421 typename decltype(_pRowSizes)::element_type>(nSize, 0);
422 }
423 }
424 }
425
426 /// @brief Resize using only the row count (layouts with an implicit row width).
427 /// @details Valid for:
428 /// - `TABLE_StaticFixed` / `TABLE_StaticMax` (width comes from template params);
429 /// - `CSR` decompressed (rows start empty, grow via @ref ResizeRow).
430 /// Asserts for other layouts -- use the two-argument overload instead.
431 /// @param nSize New number of rows.
433 {
434 if constexpr (_dataLayout == CSR)
435 {
436 DNDS_check_throw_info(!IfCompressed(), "Need to decompress before auto resizing");
437 _size = nSize;
438 _dataUncompressed.resize(nSize);
439 }
440 else if constexpr (_dataLayout == TABLE_StaticFixed)
441 {
442 _size = nSize;
443 _data.resize(nSize * rs);
444 }
445 else if constexpr (_dataLayout == TABLE_StaticMax)
446 {
447 _size = nSize;
448 _data.resize(nSize * rm);
449 if (_pRowSizes.use_count() == 1)
450 _pRowSizes->resize(nSize, 0);
451 else
452 _pRowSizes = std::make_shared<
453 typename decltype(_pRowSizes)::element_type>(nSize, 0);
454 }
455 else
456 {
457 static_assert(_dataLayout == CSR ||
460 "Resize(index nSize) is invalid call");
461 DNDS_check_throw_info(false, "invalid call");
462 }
463 }
464
465 /**
466 * @brief Resize a CSR array directly to the compressed form via a width functor.
467 *
468 * @details CSR-only. Allocates `nSize+1` `_pRowStart` entries populated from
469 * the prefix sum of `FRowSize(i)`, then sizes the flat buffer to match.
470 * No nested-vector intermediate step; the array is compressed on return.
471 *
472 * @tparam TFRowSize Callable with signature `rowsize(index)`.
473 * @param nSize New number of rows.
474 * @param FRowSize Width-of-row-i functor.
475 */
476 template <class TFRowSize>
478 {
479 if constexpr (_dataLayout == CSR)
480 {
481 _size = nSize;
482 _pRowSizes.reset(), _dataUncompressed.clear(); //*directly to compressed
483 _pRowStart = std::make_shared<typename decltype(_pRowStart)::element_type>(nSize + 1);
484 _pRowStart->operator[](0) = 0;
485 for (index i = 0; i < nSize; i++)
486 (*_pRowStart)[i + 1] = (*_pRowStart)[i] + FRowSize(i);
487 _data.resize(_pRowStart->at(nSize));
488 }
489 static_assert(_dataLayout == CSR, "Only Non Uniform, CSR for now");
490 static_assert(std::is_invocable_r_v<rowsize, TFRowSize, index>, "Call invalid");
491 }
492
493 /**
494 * @brief Change the width of a single row.
495 *
496 * @details Valid for `CSR` (decompressed only) and `TABLE_*Max`.
497 * For `TABLE_*Max`, the new size must not exceed the configured maximum and
498 * the per-row-size vector is copied-on-write if shared with another array.
499 * For CSR, the array must be uncompressed; call @ref Decompress first.
500 *
501 * @param iRow Row index in `[0, Size())`.
502 * @param nRowSize New width in `T` elements.
503 */
505 {
506 if constexpr (_dataLayout == CSR)
507 {
508 DNDS_check_throw_info(!IfCompressed(), "Need to decompress before auto resizing row");
509 DNDS_check_throw_info(iRow < _size && iRow >= 0, "query position out of range");
510 _dataUncompressed.at(iRow).resize(nRowSize);
511 }
512 else if constexpr (_dataLayout == TABLE_Max ||
514 {
515 DNDS_check_throw(nRowSize <= (_dataLayout == TABLE_Max ? _row_size_dynamic : rm)); //_row_size_dynamic is max now
516 DNDS_check_throw_info(iRow < _size && iRow >= 0, "query position out of range");
517 DNDS_check_throw_info(_pRowSizes, "_pRowSizes invalid");
518 if (_pRowSizes.use_count() > 1) // shared
519 _pRowSizes = std::make_shared<
520 typename decltype(_pRowSizes)::element_type>(*_pRowSizes); // copy the row sizes
521 _pRowSizes->at(iRow) = nRowSize; // change
522 }
523 else
524 {
525 static_assert(_dataLayout == CSR ||
528 "invalid call");
529 DNDS_check_throw_info(false, "invalid call");
530 }
531 }
532
533 /// @brief Reserve capacity for a CSR decompressed row without changing its size.
534 /// @details Analogous to `std::vector::reserve` for the nested row buffer.
535 /// CSR-only, decompressed-only.
537 {
538 if constexpr (_dataLayout == CSR)
539 {
540 DNDS_check_throw_info(!IfCompressed(), "Need to decompress before auto resizing row");
541 DNDS_check_throw_info(iRow < _size && iRow >= 0, "query position out of range");
542 _dataUncompressed.at(iRow).reserve(nRowSize);
543 }
544 else
545 {
546 DNDS_check_throw_info(false, "invalid call");
547 static_assert(_dataLayout == CSR, "invalid call");
548 }
549 }
550
551 // TODO: Data reference query method and pointer query method
552 // TODO: ? same-size compress for non-uniforms
553
554 /// @brief Produce a lightweight, device-agnostic view onto the array.
555 /// @details The returned @ref DNDS::ArrayView "ArrayView" captures pointers and sizes but does
556 /// not own any storage. It is the type that implements actual `operator[]`
557 /// indexing for all layouts; it is also host/device-callable and is the
558 /// building block for @ref DNDS::ArrayDeviceView "ArrayDeviceView" on CUDA.
560 {
561 return t_View(_size, _data.data(), _data.size(),
562 _pRowStart ? _pRowStart->data() : nullptr, _pRowStart ? _pRowStart->size() : 0,
563 _pRowSizes ? _pRowSizes->data() : nullptr, _pRowSizes ? _pRowSizes->size() : 0,
566 }
567
568 /// @brief Bounds-checked element access.
569 /// @details Asserts that `iRow` and `iCol` are in range (taking the used
570 /// row size into account, not just the stride). Works for every layout.
571 /// @param iRow Row index in `[0, Size())`.
572 /// @param iCol Column index in `[0, RowSize(iRow))`.
573 const T &at(index iRow, rowsize iCol) const
574 {
576 fmt::format(
577 "query position i[{}] out of range [0, {}), array: {}",
578 iRow, _size, this->getObjectIdentity(GetArrayName())));
579 DNDS_assert_info(iCol < RowSize(iRow) && iCol >= 0,
580 fmt::format(
581 "query position j[{}] out of range [0, {}), array: {}",
582 iCol, RowSize(iRow), this->getObjectIdentity(GetArrayName())));
583 if constexpr (_dataLayout == TABLE_StaticFixed)
584 return _data.at(iRow * rs + iCol);
585 else if constexpr (_dataLayout == TABLE_StaticMax)
586 return _data.at(iRow * rm + iCol);
587 else if constexpr (_dataLayout == TABLE_Fixed)
588 return _data.at(iRow * _row_size_dynamic + iCol);
589 else if constexpr (_dataLayout == TABLE_Max)
590 return _data.at(iRow * _row_size_dynamic + iCol);
591 else if constexpr (_dataLayout == CSR)
592 {
593 if (IfCompressed())
594 return _data.at(_pRowStart->at(iRow) + iCol);
595 else
596 return _dataUncompressed.at(iRow).at(iCol);
597 }
598 else
599 {
600 DNDS_assert_info(false, "invalid call");
601 }
602 }
603
604 /// @brief Bounds-checked 2D element access (writable).
605 /// @details Convenience wrapper around #at. `iCol` defaults to 0 so `arr(i)`
606 /// accesses the first column, useful for single-column layouts.
608 {
609 return const_cast<T &>(at(iRow, iCol));
610 }
611
612 /// @brief Bounds-checked 2D element access (read-only).
613 const T &operator()(index iRow, rowsize iCol = 0) const
614 {
615 return at(iRow, iCol);
616 }
617
618 /**
619 * @brief Return a raw pointer to the start of row `iRow`.
620 *
621 * @details Fast, untyped access used by stencil loops. Derived classes
622 * (e.g. @ref DNDS::ArrayEigenVector "ArrayEigenVector", @ref DNDS::ArrayEigenMatrix "ArrayEigenMatrix") override this to return typed
623 * Eigen maps instead of `T*`.
624 *
625 * @param iRow Row index. For `CSR` compressed, `iRow == Size()` is allowed
626 * and returns the past-the-end pointer, useful for computing
627 * the flat buffer end in sweeps.
628 * @return Pointer to the first element of row `iRow`.
629 */
631 {
632 return this->view().operator[](iRow);
633 // DNDS_assert_info(iRow <= _size && iRow >= 0, "query position i out of range");
634 // if constexpr (_dataLayout == TABLE_StaticFixed)
635 // return _data.data() + iRow * rs;
636 // else if constexpr (_dataLayout == TABLE_StaticMax)
637 // return _data.data() + iRow * rm;
638 // else if constexpr (_dataLayout == TABLE_Fixed)
639 // return _data.data() + iRow * _row_size_dynamic;
640 // else if constexpr (_dataLayout == TABLE_Max)
641 // return _data.data() + iRow * _row_size_dynamic;
642 // else if constexpr (_dataLayout == CSR)
643 // {
644 // if (IfCompressed())
645 // return _data.data() + _pRowStart->at(iRow);
646 // else if (this->Size() == 0)
647 // {
648 // static_assert(((T *)(NULL) - (T *)(NULL)) == 0);
649 // return (T *)(NULL); // used for past-the-end inquiry of size 0 array
650 // }
651 // else
652 // {
653 // DNDS_assert_info(iRow < _size, "past-the-end query forbidden for CSR uncompressed");
654 // return _dataUncompressed.at(iRow).data();
655 // }
656 // }
657 // else
658 // {
659 // DNDS_assert_info(false, "invalid call");
660 // }
661 }
662
663 /// @brief Const row pointer, see the non-const overload.
664 const T *operator[](index iRow) const
665 {
666 return static_cast<const T *>(const_cast<self_type *>(this)->operator[](iRow));
667 }
668
669 /// @brief Raw pointer to the flat data buffer.
670 /// @param B Target device. `DeviceBackend::Unknown` (default) returns the host
671 /// pointer; otherwise returns the device pointer (must match the
672 /// array's current device).
674 {
675 if constexpr (_dataLayout == CSR)
676 DNDS_assert_info(this->IfCompressed(), "CSR must be compressed to get data pointer");
677 if (B == DeviceBackend::Unknown)
678 return _data.data();
679 else
680 {
681 DNDS_assert(_data.device() == B);
682 return _data.dataDevice();
683 }
684 }
685
686 /// @brief Total number of `T` elements currently stored in the flat buffer.
687 /// @details For CSR, requires the array to be compressed.
688 size_t DataSize() const
689 {
690 if (this->Size() == 0)
691 return 0;
692 if constexpr (_dataLayout == CSR)
693 DNDS_assert_info(this->IfCompressed(), "CSR must be compressed to get DataSize()");
694 return _data.size();
695 }
696
697 /// @brief Flat buffer size in bytes (= `DataSize() * sizeof(T)`).
698 size_t DataSizeBytes() const
699 {
700 return this->DataSize() * sizeof_T;
701 }
702
703 /// @brief Copy raw row data from another Array of the same type.
704 /// Works for all layouts (StaticFixed, Fixed, CSR, etc.) and is not hidden
705 /// by derived types that make operator()/operator[] private.
707 {
708 auto rs = src.RowSize(srcRow);
709 DNDS_assert(rs == this->RowSize(dstRow));
710 const T *srcPtr = const_cast<self_type &>(src)[srcRow];
711 T *dstPtr = (*this)[dstRow];
712 if constexpr (std::is_trivially_copyable_v<T>)
713 std::memcpy(dstPtr, srcPtr, rs * sizeof(T));
714 else
715 for (rowsize j = 0; j < rs; j++)
716 dstPtr[j] = srcPtr[j];
717 }
718
719 /// @brief Set per-row sizes from a source, applying an index mapping, then compress.
720 /// For CSR layout only. rowSizeFunc(i) returns the desired row size for row i.
721 /// Not hidden by derived types that make ResizeRow private.
722 template <class TRowSizeFunc>
724 {
725 static_assert(_dataLayout == CSR, "ResizeRowsAndCompress only for CSR");
726 for (index i = 0; i < this->Size(); i++)
727 this->ResizeRow(i, rowSizeFunc(i));
728 this->Compress();
729 }
730
731 /// @brief Total footprint in bytes including structural arrays.
732 /// @details Sums the flat data buffer, `_pRowStart` (if any), and
733 /// `_pRowSizes` (if any). Approximate because shared-ownership of row
734 /// structures is not deduplicated.
735 size_t FullSizeBytes() const
736 {
737 size_t b = this->DataSize() * sizeof_T;
738 if (_pRowStart)
739 b += _pRowStart->size() * sizeof(index);
740 if (_pRowSizes)
741 b += _pRowSizes->size() * sizeof(rowsize);
742 return b;
743 }
744
745 /// @brief Combined hash of size, structural arrays, and data.
746 /// @details Byte-hashes non-hashable elements. Intended for testing /
747 /// equality diagnostics; not guaranteed cryptographically strong.
748 std::size_t hash()
749 {
750 std::size_t hashData;
751 if constexpr (_dataLayout == CSR)
752 {
753 if (IfCompressed())
754 hashData = vector_hash<T>()(_data.begin(), _data.end());
755 else
757 }
758 else
759 hashData = vector_hash<T>()(_data.begin(), _data.end());
760 std::size_t hashSize = 0;
761 if (_pRowSizes)
763 if (_pRowStart)
764 hashSize = vector_hash<index>()(_pRowSizes->begin(), _pRowSizes->end());
765 return array_hash<std::size_t, 3>()(std::array<std::size_t, 3>{std::size_t(_size), hashSize, hashData});
766 }
767
768 /// @brief Pretty-print rows, one per line, tab-separated.
769 friend std::ostream &operator<<(std::ostream &o, const Array<T, _row_size, _row_max, _align> &A)
770 {
771 for (index i = 0; i < A._size; i++)
772 {
773 for (index j = 0; j < A.RowSize(i); j++)
774 o << A(i, j) << "\t";
775 o << std::endl;
776 }
777 return o;
778 }
779
780 /// @brief Compile-time layout tag (one of `TABLE_StaticFixed`, `TABLE_Fixed`,
781 /// `TABLE_StaticMax`, `TABLE_Max`, `CSR`).
782 static constexpr DataLayout GetDataLayoutStatic() { return _dataLayout; }
783 /// @brief Runtime accessor for the layout tag (constexpr-folded).
784 constexpr DataLayout GetDataLayout() { return _dataLayout; }
785
786 /// @brief Shallow clone: copies all metadata and shares structural/data storage.
787 /// @details Copies `_size`, `_row_size_dynamic`, device backend, and the
788 /// nested-vector storage. The `_data`, `_pRowStart`, `_pRowSizes` members
789 /// are `host_device_vector` / `shared_ptr`, so they share ownership with
790 /// the source; subsequent modifications to one may affect the other.
791 void clone(const self_type &R)
792 {
793 this->_size = R._size;
794 this->_data = R._data;
795 this->_pRowSizes = R._pRowSizes;
796 this->_pRowStart = R._pRowStart;
797 this->_dataUncompressed = R._dataUncompressed;
798 this->_row_size_dynamic = R._row_size_dynamic;
799
800 this->deviceBackend = R.deviceBackend;
801 }
802
803 /// @brief Deep copy alias. Currently delegates to #clone; kept for API
804 /// compatibility and to allow a future true deep-copy implementation.
805 void CopyData(const self_type &R)
806 {
807 this->clone(R);
808 // non-trivial copy call: unique_ptr
809 }
810
811 /// @brief Copy-assignment; implemented via #clone with self-assign guard.
813 {
814 if (this == &R)
815 return *this;
816 this->clone(R);
817 return *this;
818 }
819
820 /// @brief Copy constructor (same semantics as #clone).
822 {
823 this->clone(R);
824 }
825
826 /// @brief Swap the storage of two arrays in-place.
827 /// @details Both arrays must already have identical logical size and
828 /// flat-buffer size. Swaps only what the current layout uses (flat buffer
829 /// plus structural pointers, or the nested vectors for CSR decompressed).
830 // TODO: SwapData on device?
832 {
834 DNDS_check_throw(R._data.size() == _data.size());
835 if constexpr (_dataLayout == CSR)
836 {
837 if (IfCompressed())
838 {
839 _data.swap(R._data);
840 _pRowStart.swap(R._pRowStart);
841 }
842 else
843 {
844 _dataUncompressed.swap(R._dataUncompressed);
845 _pRowSizes.swap(R._pRowSizes);
846 }
847 }
848 else
849 {
850 _data.swap(R._data);
851 }
852 {
853 std::swap(deviceBackend, R.deviceBackend);
854 }
855 }
856
858 {
859 auto treatAsBytes = [&]()
860 { serializerP->WriteUint8Array("data", (uint8_t *)_data.data(), _data.size() * sizeof_T, offset * sizeof_T); };
861 if constexpr (std::is_same_v<T, index>)
862 {
863 // TODO: OPTIMIZE serializer pass a range
864 serializerP->WriteIndexVector("data", std::vector<index>(_data), offset);
865 }
866 else if constexpr (std::is_same_v<T, real>)
867 {
868 if (!std::dynamic_pointer_cast<Serializer::SerializerJSON>(serializerP))
869 { // TODO: OPTIMIZE serializer pass a range
870 serializerP->WriteRealVector("data", std::vector<real>(_data), offset);
871 }
872 else
873 treatAsBytes();
874 }
875 else
876 treatAsBytes();
877 }
878
880 {
881 auto treatAsBytes = [&]()
882 {
884 if (localOffset.isDist())
885 {
887 }
888 index bufferSize{0};
889 serializerP->ReadUint8Array("data", nullptr, bufferSize, localOffset);
891 _data.resize(bufferSize / sizeof_T);
892 uint8_t dummy{};
893 serializerP->ReadUint8Array("data", bufferSize == 0 ? &dummy : (uint8_t *)_data.data(), bufferSize, localOffset);
894 localOffset.CheckMultipleOf(sizeof_T);
895 offset = localOffset / sizeof_T;
896 };
897
898 if constexpr (std::is_same_v<T, index>)
899 { // TODO: OPTIMIZE host_device_vector accept rvalue std::vector
900 std::vector<index> data_tmp;
901 serializerP->ReadIndexVector("data", data_tmp, offset);
902 _data = data_tmp;
903 }
904 else if constexpr (std::is_same_v<T, real>)
905 {
906 if (!std::dynamic_pointer_cast<Serializer::SerializerJSON>(serializerP))
907 { // TODO: OPTIMIZE host_device_vector accept rvalue std::vector
908 std::vector<real> data_tmp;
909 serializerP->ReadRealVector("data", data_tmp, offset);
910 _data = data_tmp;
911 }
912 else
913 treatAsBytes();
914 }
915 else
916 treatAsBytes();
917 }
918
919 /// @brief Serialize (write) array data to a serializer.
920 ///
921 /// Writes metadata (array_sig, array_type, size, row_size_dynamic), structural
922 /// data (pRowStart for CSR, pRowSizes for TABLE_Max/StaticMax), and the flat
923 /// data buffer into a sub-path `name` under the serializer's current path.
924 ///
925 /// This method is called by ParArray::WriteSerializer. Users should call the
926 /// ParArray version, which handles MPI coordination and CSR global offsets.
927 ///
928 /// @param serializerP Serializer instance (JSON per-rank or H5 collective).
929 /// @param name Sub-path name under which the array is stored.
930 /// @param offset [in] Row-level partitioning offset. Typically Parts
931 /// (serializer computes per-rank offsets automatically).
932 /// @param dataOffset [in] Element-level data range for this rank.
933 /// - Per-rank or non-CSR: Unknown (default). Array writes
934 /// pRowStart in local coordinates.
935 /// - Collective CSR: must be isDist() = {localDataCount,
936 /// globalDataStart}, computed by ParArray via MPI_Scan.
937 /// Array skips pRowStart (ParArray writes it separately
938 /// in global coordinates). Asserted for collective CSR.
941 Serializer::ArrayGlobalOffset dataOffset = Serializer::ArrayGlobalOffset_Unknown)
942 {
943 auto cwd = serializerP->GetCurrentPath();
944 serializerP->CreatePath(name);
945 serializerP->GoToPath(name);
946
947 serializerP->WriteString("array_sig", GetArraySignature());
948 serializerP->WriteString("array_type", typeid(self_type).name());
949 if (serializerP->IsPerRank())
950 serializerP->WriteIndex("size", _size);
951 else
952 {
953 std::vector<index> _size_vv;
954 _size_vv.push_back(_size);
955 serializerP->WriteIndexVectorPerRank("size", _size_vv);
956 }
957 serializerP->WriteInt("row_size_dynamic", _row_size_dynamic);
958 // if (_size == 0) //! cannot do this, collective calls!
959 // return;
960 if constexpr (_dataLayout == CSR)
961 {
962 if (!this->IfCompressed())
963 this->Compress();
964 // For collective serializers, dataOffset must be isDist() (set by ParArray).
965 // ParArray writes pRowStart in global coordinates; Array only writes for per-rank.
966 DNDS_assert_info(serializerP->IsPerRank() || dataOffset.isDist(),
967 "CSR collective write requires isDist dataOffset from ParArray");
968 if (dataOffset.isDist())
969 {
970 // ParArray handles pRowStart write in global coords
971 }
972 else
973 {
974 serializerP->WriteSharedIndexVector("pRowStart", _pRowStart, offset);
975 }
976 }
977 else if constexpr (_dataLayout == TABLE_Max || _dataLayout == TABLE_StaticMax)
978 {
979 serializerP->WriteSharedRowsizeVector("pRowSizes", _pRowSizes, offset);
980 }
981 else // fixed
982 {
983 }
984 // doing data
985 this->__WriteSerializerData(serializerP, offset);
986
987 serializerP->GoToPath(cwd);
988 }
989
990 /// @brief Convenience overload that discards the dataOffset output.
991 /// @see ReadSerializer(SerializerBaseSSP, const std::string&, ArrayGlobalOffset&, ArrayGlobalOffset&)
994 {
995 Serializer::ArrayGlobalOffset dataOffset = Serializer::ArrayGlobalOffset_Unknown;
996 ReadSerializer(serializerP, name, offset, dataOffset);
997 }
998
999 /// @brief Deserialize (read) array data from a serializer.
1000 ///
1001 /// Reads metadata (array_sig, size, row_size_dynamic), structural data
1002 /// (pRowStart for CSR, pRowSizes for TABLE_Max/StaticMax), and the flat data
1003 /// buffer from a sub-path `name` under the serializer's current path.
1004 ///
1005 /// This method is called by ParArray::ReadSerializer. Users should call the
1006 /// ParArray version, which handles EvenSplit resolution and CSR offset computation.
1007 ///
1008 /// Input `offset` must NOT be EvenSplit; ParArray resolves that before calling.
1009 ///
1010 /// @param serializerP Serializer instance (JSON per-rank or H5 collective).
1011 /// @param name Sub-path name for this array.
1012 /// @param offset [in/out] Row-level offset.
1013 /// - In: Unknown (same-np, serializer uses ::rank_offsets)
1014 /// or isDist({localRows, globalRowStart}).
1015 /// - Out: updated to the resolved row-level position after
1016 /// reading (derived from dataOffset / DataStride for non-CSR).
1017 /// @param dataOffset [out] Element-level data offset resolved during the read.
1018 /// - Per-rank or Unknown offset: stays Unknown.
1019 /// - Collective isDist non-CSR: {localRows * DataStride(),
1020 /// globalRowStart * DataStride()}.
1021 /// - Collective CSR: {localDataCount, globalDataStart},
1022 /// derived from the stored global pRowStart.
1026 {
1027 DNDS_assert_info(!(offset == Serializer::ArrayGlobalOffset_EvenSplit),
1028 "Array::ReadSerializer must not receive EvenSplit; ParArray should resolve it first");
1029
1030 auto cwd = serializerP->GetCurrentPath();
1031 serializerP->GoToPath(name);
1032
1033 // --- Phase 1: Read metadata ---
1034 std::string array_sigRead;
1035 serializerP->ReadString("array_sig", array_sigRead);
1037 array_sigRead + ", i am : " + this->GetArraySignature());
1039 if (_row_size != rsR)
1040 {
1041 if (_row_size == NonUniformSize || rsR == NonUniformSize)
1042 DNDS_check_throw_info(false, "can't handle here");
1043 }
1044 if (serializerP->IsPerRank())
1045 serializerP->ReadIndex("size", _size);
1046 else if (offset.isDist())
1047 _size = offset.size();
1048 else
1049 {
1050 std::vector<index> _size_vv;
1051 Serializer::ArrayGlobalOffset offsetV = Serializer::ArrayGlobalOffset_Unknown;
1052 serializerP->ReadIndexVector("size", _size_vv, offsetV);
1053 DNDS_check_throw(_size_vv.size() == 1);
1054 _size = _size_vv.front();
1055 }
1056 serializerP->ReadInt("row_size_dynamic", _row_size_dynamic);
1057 if (_row_size >= 0) // TODO: fix this! need full conversion check (maybe just a casting)
1058 {
1059 if (rsR == DynamicSize)
1061 else if (rsR >= 0)
1062 DNDS_check_throw(rsR == _row_size), _row_size_dynamic = 0;
1063 }
1064 if (_row_size == DynamicSize && rsR >= 0)
1066 if (_row_max == DynamicSize && rmR >= 0)
1067 _row_size_dynamic = rmR; // TODO: fix this! need a _row_max_dynamic ?
1068
1069 // --- Phase 2: Read structural data and resolve dataOffset ---
1071
1072 // --- Phase 3: Read flat data and propagate offsets ---
1074 // TODO: check data validity
1075
1076 serializerP->GoToPath(cwd);
1077 }
1078
1079 /// @brief Result type for ReadSerializerMeta.
1080 ///
1081 /// Derived array types can extend this struct to carry additional
1082 /// metadata fields (e.g., mat_nRow_dynamic from ArrayEigenMatrix).
1089
1090 /// @brief Reads only metadata from a serialized array without reading data.
1091 ///
1092 /// Navigates to sub-path `name` and reads array_sig, row_size_dynamic, and
1093 /// size. Does NOT read structural data (pRowStart, pRowSizes) or the flat
1094 /// data buffer. The array's internal state is not modified.
1095 ///
1096 /// Derived types (ArrayEigenMatrix, ArrayEigenUniMatrixBatch) override this
1097 /// method to also read their own extra metadata and to call the base version
1098 /// with the "array" sub-path they wrap around the base Array serialization.
1099 ///
1100 /// @param serializerP Serializer instance.
1101 /// @param name Sub-path name for this array.
1102 /// @return Metadata: array_sig, row_size_dynamic, size (local size for
1103 /// per-rank; 0 for collective without ParArray).
1105 {
1106 auto cwd = serializerP->GetCurrentPath();
1107 serializerP->GoToPath(name);
1108
1110 serializerP->ReadString("array_sig", result.array_sig);
1111 serializerP->ReadInt("row_size_dynamic", result.row_size_dynamic);
1112 if (serializerP->IsPerRank())
1113 serializerP->ReadIndex("size", result.size);
1114
1115 serializerP->GoToPath(cwd);
1116 return result;
1117 }
1118
1119 /// @brief Reads structural data (pRowStart / pRowSizes) and resolves dataOffset.
1120 ///
1121 /// After metadata has been read and _size / _row_size_dynamic set, this method
1122 /// reads layout-specific structural data from the serializer and computes the
1123 /// element-level dataOffset from the row-level offset.
1124 ///
1125 /// @param serializerP Serializer instance (already at the array's sub-path).
1126 /// @param offset [in/out] Row-level offset; updated for non-CSR if
1127 /// dataOffset could be derived.
1128 /// @param dataOffset [out] Element-level data offset resolved from structural
1129 /// data (CSR: from global pRowStart; non-CSR: offset * DataStride).
1134 {
1135 if constexpr (_dataLayout == CSR)
1136 {
1137 DNDS_assert_info(serializerP->IsPerRank() || offset.isDist(),
1138 "CSR collective read requires isDist offset from ParArray");
1139 if (offset.isDist())
1140 {
1142 serializerP->ReadSharedIndexVector("pRowStart", _pRowStart, prsOffset);
1144 for (index i = _size; i >= 0; i--)
1147 }
1148 else
1149 {
1150 serializerP->ReadSharedIndexVector("pRowStart", _pRowStart, offset);
1151 }
1152 }
1153 else if constexpr (_dataLayout == TABLE_Max || _dataLayout == TABLE_StaticMax)
1154 {
1155 serializerP->ReadSharedRowsizeVector("pRowSizes", _pRowSizes, offset);
1156 }
1157 else // TABLE_StaticFixed, TABLE_Fixed
1158 {
1159 }
1160
1161 // CSR dataOffset is resolved above from pRowStart.
1162 // For non-CSR, derive from row offset * DataStride.
1163 if constexpr (_dataLayout != CSR)
1164 {
1165 if (offset.isDist())
1166 dataOffset = offset * this->DataStride();
1167 }
1168 }
1169
1170 /// @brief Reads flat data and propagates resolved offsets back to the caller.
1171 ///
1172 /// @param serializerP Serializer instance (already at the array's sub-path).
1173 /// @param offset [in/out] Row-level offset; updated from dataOffset for non-CSR.
1174 /// @param dataOffset [in/out] Element-level data offset; may be updated from
1175 /// Unknown to Parts-resolved by __ReadSerializerData.
1180 {
1181 Serializer::ArrayGlobalOffset dataReadOffset = Serializer::ArrayGlobalOffset_Unknown;
1182 if (dataOffset.isDist())
1184 this->__ReadSerializerData(serializerP, dataReadOffset);
1186 if constexpr (_dataLayout != CSR)
1187 {
1188 if (dataOffset.isDist())
1189 {
1190 dataOffset.CheckMultipleOf(this->DataStride());
1191 offset = dataOffset / this->DataStride();
1192 }
1193 }
1194 }
1195
1196 public:
1197 /// @brief Mirror the flat/structural buffers back to host memory.
1198 /// @details CSR arrays must be compressed before calling. After this the
1199 /// array still has a device mirror unless #clear_device is also called.
1200 void to_host()
1201 {
1202 if constexpr (_dataLayout == CSR)
1204 fmt::format("CSR need compressing before to_host: {}",
1206 _data.to_host();
1208 }
1209
1210 /// @brief Mirror the flat/structural buffers to a target device (e.g. CUDA).
1211 /// @details CSR arrays must be compressed. `backend` must match a supported
1212 /// backend from @ref DNDS::DeviceBackend "DeviceBackend"; see `DeviceStorage.hpp`.
1214 {
1215 if constexpr (_dataLayout == CSR)
1217 fmt::format("CSR need compressing before to_device: {}",
1219 _data.to_device(backend);
1220 if (_pRowStart)
1221 _pRowStart->to_device(backend);
1222 if (_pRowSizes)
1223 _pRowSizes->to_device(backend);
1224 deviceBackend = _data.device();
1225 }
1226
1227 /// @brief Release any device-side mirror of this array's buffers.
1229 {
1230 _data.clear_device();
1231 if (_pRowStart)
1232 _pRowStart->clear_device();
1233 if (_pRowSizes)
1234 _pRowSizes->clear_device();
1235
1237 }
1238
1239 template <DeviceBackend B>
1241
1242 template <DeviceBackend B>
1244
1245 /// @brief Mutable device-callable view (`Eigen::Map`-style row access on GPU).
1246 /// @tparam B Device backend; must either match the array's current device
1247 /// or be `DeviceBackend::Host` (which yields a host-backed view).
1248 template <DeviceBackend B>
1250 {
1251 DNDS_check_throw_info((this->deviceBackend == B &&
1252 B != DeviceBackend::Unknown) ||
1253 (B == DeviceBackend::Host),
1254 fmt::format("{}: not on device {}, currently on {}",
1257 device_backend_name(this->deviceBackend)));
1258
1260 _size, _data.data(), _data.size(),
1261 _pRowStart ? _pRowStart->data() : nullptr, _pRowStart ? _pRowStart->size() : 0,
1262 _pRowSizes ? _pRowSizes->data() : nullptr, _pRowSizes ? _pRowSizes->size() : 0,
1264 _data.dataDevice(),
1265 _pRowStart ? _pRowStart->dataDevice() : nullptr,
1266 _pRowSizes ? _pRowSizes->dataDevice() : nullptr);
1267 }
1268
1269 /// @brief Const device-callable view. See non-const overload.
1270 template <DeviceBackend B>
1272 {
1273 DNDS_check_throw_info((this->deviceBackend == B &&
1274 B != DeviceBackend::Unknown) ||
1275 (B == DeviceBackend::Host),
1276 fmt::format("{}: not on device {}, currently on {}",
1279 device_backend_name(this->deviceBackend)));
1280
1282 _size, _data.data(), _data.size(),
1283 _pRowStart ? _pRowStart->data() : nullptr, _pRowStart ? _pRowStart->size() : 0,
1284 _pRowSizes ? _pRowSizes->data() : nullptr, _pRowSizes ? _pRowSizes->size() : 0,
1286 _data.dataDevice(),
1287 _pRowStart ? _pRowStart->dataDevice() : nullptr,
1288 _pRowSizes ? _pRowSizes->dataDevice() : nullptr);
1289 }
1290
1291 /// @brief Current device backend the data is mirrored on, or @ref Unknown if host-only.
1293 {
1294 return this->deviceBackend;
1295 }
1296
1297 /// @brief Random-access iterator over rows for a given device backend.
1298 /// @details `operator*` yields a @ref DNDS::RowView "RowView" `{pointer, rowSize}`. Used by
1299 /// `std::transform` / CUDA kernels that sweep over rows.
1300 template <DeviceBackend B>
1301 class iterator : public ArrayIteratorBase<iterator<B>>
1302 {
1303 public:
1308 using iterator_category = std::random_access_iterator_tag;
1309
1310 protected:
1312
1313 public:
1314 auto getView() const { return view; }
1320
1321 DNDS_DEVICE_CALLABLE reference operator*() const { return {view.operator[](this->iRow), view.RowSize(this->iRow)}; }
1322 DNDS_DEVICE_CALLABLE reference operator*() { return {view.operator[](this->iRow), view.RowSize(this->iRow)}; }
1323 };
1324
1325 /// @brief Iterator to the first row, viewed on device backend `B`.
1326 template <DeviceBackend B>
1328 {
1329 return {deviceView<B>(), 0};
1330 }
1331
1332 /// @brief Iterator one past the last row, viewed on device backend `B`.
1333 template <DeviceBackend B>
1335 {
1336 return {deviceView<B>(), this->Size()};
1337 }
1338 };
1339
1340}
1341
1342#endif
Array layout descriptors, non-owning views, row views, and iterator base.
Core type aliases, constants, and metaprogramming utilities for the DNDS framework.
#define DNDS_DEVICE_CALLABLE
Definition Defines.hpp:76
Device memory abstraction layer with backend-specific storage and factory creation.
Non-owning device-side views of Array objects for host and CUDA backends.
#define DNDS_assert_info(expr, info)
Debug-only assertion with an extra std::string info message.
Definition Errors.hpp:113
#define DNDS_assert(expr)
Debug-only assertion (compiled out when DNDS_NDEBUG is defined). Prints the expression + file/line + ...
Definition Errors.hpp:108
#define DNDS_check_throw_info(expr, info)
Same as DNDS_check_throw but attaches a user-supplied info message to the thrown std::runtime_error.
Definition Errors.hpp:96
#define DNDS_check_throw(expr)
Runtime check active in both debug and release builds. Throws std::runtime_error if expr evaluates to...
Definition Errors.hpp:89
Base types and abstract interface for array serialization.
Per-rank JSON serializer implementing the SerializerBase interface.
Host-device vector types with optional GPU storage and device-side views.
Non-owning device-callable view of an Array, specialised per DeviceBackend.
CRTP base for row-granularity iterators over an Array / ArrayView.
Compile-time layout descriptor deducing the concrete DataLayout from element type and row-size templa...
static bool ArraySignatureIsCompatible(const std::string &v)
Whether a stored signature can be read into this array type.
static std::tuple< int, int, int, int > ParseArraySignatureTuple(const std::string &v)
Parse a signature string into (sizeof_T, row_size, row_max, align).
static std::string GetArraySignatureRelaxed()
Signature with _row_size / _row_max replaced by DynamicSize.
static constexpr DataLayout _GetDataLayout()
Deduce the DataLayout tag from the template parameters.
static std::string GetArrayName()
Human-readable type identifier including element typeid, sizes, and alignment.
static std::string GetArraySignature()
Compiler-independent identifier used by serializers to tag an array.
Non-owning view of a single row: {pointer, size}.
const T & at(index iRow, rowsize iCol) const
Bounds-checked element read (not device-callable because CSR decompressed uses std::vector::at which ...
DNDS_DEVICE_CALLABLE index Size() const
Number of rows in the viewed array.
DNDS_DEVICE_CALLABLE T * data()
Raw pointer to the start of the flat data buffer.
DNDS_DEVICE_CALLABLE rowsize RowSize() const
Uniform row width for fixed layouts (asserts otherwise).
std::conditional_t< _dataLayout==TABLE_Max||_dataLayout==TABLE_Fixed, rowsize, EmptyNoDefault > _row_size_dynamic
Random-access iterator over rows for a given device backend.
Definition Array.hpp:1302
auto getView() const
Definition Array.hpp:1314
DNDS_DEVICE_CALLABLE reference operator*()
Definition Array.hpp:1322
DNDS_DEVICE_CALLABLE reference operator*() const
Definition Array.hpp:1321
std::random_access_iterator_tag iterator_category
Definition Array.hpp:1308
DNDS_DEVICE_CALLABLE iterator(const iterator &)=default
DNDS_DEVICE_CALLABLE ~iterator()=default
DNDS_DEVICE_CALLABLE iterator(const view_type &n_view, index n_iRow)
Definition Array.hpp:1317
Core 2D variable-length array container, the storage foundation of DNDSR.
Definition Array.hpp:97
t_pRowStart getRowStart()
Shared pointer to the row-start index (CSR layout only).
Definition Array.hpp:149
rowsize RowSizeField(index iRow) const
Per-row "field" size for CSR (= actual row width). Invalid elsewhere.
Definition Array.hpp:266
const T & operator()(index iRow, rowsize iCol=0) const
Bounds-checked 2D element access (read-only).
Definition Array.hpp:613
t_pRowSizes _pRowSizes
Definition Array.hpp:137
t_DataUncompressed _dataUncompressed
Definition Array.hpp:141
friend std::ostream & operator<<(std::ostream &o, const Array< T, _row_size, _row_max, _align > &A)
Pretty-print rows, one per line, tab-separated.
Definition Array.hpp:769
void to_device(DeviceBackend backend=DeviceBackend::Host)
Mirror the flat/structural buffers to a target device (e.g. CUDA).
Definition Array.hpp:1213
std::size_t hash()
Combined hash of size, structural arrays, and data.
Definition Array.hpp:748
DeviceBackend deviceBackend
Definition Array.hpp:139
void Resize(index nSize, rowsize nRow_size_dynamic)
Resize the array, setting a uniform or maximum row width.
Definition Array.hpp:395
void CopyData(const self_type &R)
Deep copy alias. Currently delegates to clone; kept for API compatibility and to allow a future true ...
Definition Array.hpp:805
void ReadSerializer(Serializer::SerializerBaseSSP serializerP, const std::string &name, Serializer::ArrayGlobalOffset &offset, Serializer::ArrayGlobalOffset &dataOffset)
Deserialize (read) array data from a serializer.
Definition Array.hpp:1023
index _size
Definition Array.hpp:143
const T * operator[](index iRow) const
Const row pointer, see the non-const overload.
Definition Array.hpp:664
size_t FullSizeBytes() const
Total footprint in bytes including structural arrays.
Definition Array.hpp:735
T * operator[](index iRow)
Return a raw pointer to the start of row iRow.
Definition Array.hpp:630
rowsize DataStride() const
Number of T elements per row in flat storage (data stride). For TABLE_StaticMax/TABLE_Max,...
Definition Array.hpp:192
void Decompress()
Layout-polymorphic decompress: no-op for non-CSR, calls CSRDecompress for CSR.
Definition Array.hpp:361
void ReserveRow(index iRow, rowsize nRowSize)
Reserve capacity for a CSR decompressed row without changing its size.
Definition Array.hpp:536
rowsize RowSize() const
Uniform row width for fixed layouts (no row index needed).
Definition Array.hpp:176
t_deviceView< B > deviceView()
Mutable device-callable view (Eigen::Map-style row access on GPU).
Definition Array.hpp:1249
rowsize _row_size_dynamic
Definition Array.hpp:144
rowsize RowSize(index iRow) const
Width used by row iRow in number of T elements.
Definition Array.hpp:214
void __WriteSerializerData(const Serializer::SerializerBaseSSP &serializerP, Serializer::ArrayGlobalOffset offset)
Definition Array.hpp:857
t_pRowStart _pRowStart
Definition Array.hpp:136
bool IfCompressed_() const
Definition Array.hpp:275
void WriteSerializer(Serializer::SerializerBaseSSP serializerP, const std::string &name, Serializer::ArrayGlobalOffset offset, Serializer::ArrayGlobalOffset dataOffset=Serializer::ArrayGlobalOffset_Unknown)
Serialize (write) array data to a serializer.
Definition Array.hpp:939
static constexpr bool IsCSR()
Definition Array.hpp:119
t_Data & RawDataVector()
Access to the underlying flat buffer (host_device_vector<T>).
Definition Array.hpp:370
const T & at(index iRow, rowsize iCol) const
Bounds-checked element access.
Definition Array.hpp:573
DeviceBackend device() const
Current device backend the data is mirrored on, or Unknown if host-only.
Definition Array.hpp:1292
bool IfCompressed() const
(CSR only) Whether the array is in packed / flat form.
Definition Array.hpp:294
static constexpr DataLayout GetDataLayoutStatic()
Compile-time layout tag (one of TABLE_StaticFixed, TABLE_Fixed, TABLE_StaticMax, TABLE_Max,...
Definition Array.hpp:782
void to_host()
Mirror the flat/structural buffers back to host memory.
Definition Array.hpp:1200
t_View view()
Produce a lightweight, device-agnostic view onto the array.
Definition Array.hpp:559
ArrayDeviceView< B, T, _row_size, _row_max, _align > t_deviceView
Definition Array.hpp:1240
ArrayView< T, _row_size, _row_max, _align > t_View
Definition Array.hpp:117
iterator< B > begin()
Iterator to the first row, viewed on device backend B.
Definition Array.hpp:1327
void SwapData(self_type &R)
Swap the storage of two arrays in-place.
Definition Array.hpp:831
void ResizeRowsAndCompress(TRowSizeFunc &&rowSizeFunc)
Set per-row sizes from a source, applying an index mapping, then compress. For CSR layout only....
Definition Array.hpp:723
t_pRowSizes getRowSizes()
Shared pointer to the per-row size vector (TABLE_Max / TABLE_StaticMax).
Definition Array.hpp:152
self_type & operator=(const self_type &R)
Copy-assignment; implemented via clone with self-assign guard.
Definition Array.hpp:812
void Resize(index nSize)
Resize using only the row count (layouts with an implicit row width).
Definition Array.hpp:432
void CopyRowFrom(index dstRow, const self_type &src, index srcRow)
Copy raw row data from another Array of the same type. Works for all layouts (StaticFixed,...
Definition Array.hpp:706
ReadSerializerMetaResult ReadSerializerMeta(Serializer::SerializerBaseSSP serializerP, const std::string &name)
Reads only metadata from a serialized array without reading data.
Definition Array.hpp:1104
void __ReadSerializerStructuralAndResolveDataOffset(const Serializer::SerializerBaseSSP &serializerP, Serializer::ArrayGlobalOffset &offset, Serializer::ArrayGlobalOffset &dataOffset)
Reads structural data (pRowStart / pRowSizes) and resolves dataOffset.
Definition Array.hpp:1130
size_t DataSizeBytes() const
Flat buffer size in bytes (= DataSize() * sizeof(T)).
Definition Array.hpp:698
Array(ObjName objName)
Named constructor: sets the object name for tracing/debugging. Delegates to the default constructor,...
Definition Array.hpp:160
std::vector< std::vector< value_type > > t_DataUncompressed
Definition Array.hpp:125
void ResizeRow(index iRow, rowsize nRowSize)
Change the width of a single row.
Definition Array.hpp:504
size_t DataSize() const
Total number of T elements currently stored in the flat buffer.
Definition Array.hpp:688
void CSRCompress()
(CSR only) Pack the nested-vector representation into a flat buffer plus _pRowStart....
Definition Array.hpp:324
void clear_device()
Release any device-side mirror of this array's buffers.
Definition Array.hpp:1228
void Resize(index nSize, TFRowSize &&FRowSize)
Resize a CSR array directly to the compressed form via a width functor.
Definition Array.hpp:477
Array()=default
Default-constructed array: empty, no storage.
Array(const self_type &R)
Copy constructor (same semantics as clone).
Definition Array.hpp:821
t_Data _data
Definition Array.hpp:138
T * data(DeviceBackend B=DeviceBackend::Unknown)
Raw pointer to the flat data buffer.
Definition Array.hpp:673
T & operator()(index iRow, rowsize iCol=0)
Bounds-checked 2D element access (writable).
Definition Array.hpp:607
void Compress()
Layout-polymorphic compress: no-op for non-CSR, calls CSRCompress for CSR.
Definition Array.hpp:355
rowsize RowSizeMax() const
Maximum allowed row width for TABLE_Max / TABLE_StaticMax.
Definition Array.hpp:245
t_deviceViewConst< B > deviceView() const
Const device-callable view. See non-const overload.
Definition Array.hpp:1271
void __ReadSerializerData(const Serializer::SerializerBaseSSP &serializerP, Serializer::ArrayGlobalOffset &offset)
Definition Array.hpp:879
void CSRDecompress()
(CSR only) Switch to the uncompressed (nested vector) representation.
Definition Array.hpp:304
index Size() const
Number of rows currently stored. O(1).
Definition Array.hpp:171
void clone(const self_type &R)
Shallow clone: copies all metadata and shares structural/data storage.
Definition Array.hpp:791
rowsize RowSizeField() const
"Logical" row-field width used by derived (Eigen) arrays: max for padded layouts, uniform width for f...
Definition Array.hpp:255
iterator< B > end()
Iterator one past the last row, viewed on device backend B.
Definition Array.hpp:1334
void ReadSerializer(Serializer::SerializerBaseSSP serializerP, const std::string &name, Serializer::ArrayGlobalOffset &offset)
Convenience overload that discards the dataOffset output.
Definition Array.hpp:992
constexpr DataLayout GetDataLayout()
Runtime accessor for the layout tag (constexpr-folded).
Definition Array.hpp:784
void __ReadSerializerDataAndPropagateOffset(const Serializer::SerializerBaseSSP &serializerP, Serializer::ArrayGlobalOffset &offset, Serializer::ArrayGlobalOffset &dataOffset)
Reads flat data and propagates resolved offsets back to the caller.
Definition Array.hpp:1176
Mixin base class providing a runtime instance name for tracing/debugging.
Definition Defines.hpp:221
std::string getObjectIdentity(const std::string &sig) const
Definition Defines.hpp:237
void setObjectName(const std::string &name)
Definition Defines.hpp:232
Describes one rank's window into a globally-distributed dataset.
index offset() const
Global offset of this rank's data (or a sentinel value, see Offset_Parts etc.).
index size() const
Local size this rank owns (in element units of the caller's choosing).
bool isDist() const
Whether this descriptor carries a real distributed offset (rather than a sentinel like Offset_Parts).
ssp< SerializerBase > SerializerBaseSSP
the host side operators are provided as implemented
DeviceBackend
Enumerates the backends a DeviceStorage / Array can live on.
@ Unknown
Unset / sentinel.
@ Host
Plain CPU memory.
const char * device_backend_name(DeviceBackend B)
Canonical string name for a DeviceBackend (used in log messages).
int32_t rowsize
Row-width / per-row element-count type (signed 32-bit).
Definition Defines.hpp:109
DNDS_CONSTANT const rowsize DynamicSize
Template parameter flag: "row width is set at runtime but uniform".
Definition Defines.hpp:277
DataLayout
Enumeration of the five concrete data layouts supported by Array.
@ TABLE_StaticFixed
Fixed row width, known at compile time.
@ TABLE_Max
Padded variable rows; max width set at runtime.
@ CSR
Compressed Sparse Row (flat buffer + row-start index).
@ TABLE_StaticMax
Padded variable rows; max width fixed at compile time.
@ TABLE_Fixed
Fixed row width, set at runtime (uniform across rows).
DNDS_CONSTANT const rowsize NonUniformSize
Template parameter flag: "each row has an independent width".
Definition Defines.hpp:279
int64_t index
Global row / DOF index type (signed 64-bit; handles multi-billion-cell meshes).
Definition Defines.hpp:107
Result type for ReadSerializerMeta.
Definition Array.hpp:1084
Tag type for naming objects created via make_ssp.
Definition Defines.hpp:249
tVec b(NCells)
auto result