|
DNDSR 0.1.0.dev1+gcd065ad
Distributed Numeric Data Structure for CFV
|
This guide walks through the DNDSR array infrastructure from the perspective of someone writing a new module or solver on top of it. For the design rationale see Array Infrastructure.
| You need... | Use | Why |
|---|---|---|
| Local scratch storage (no MPI) | Array<T, rs> | Lightest weight; no MPI overhead |
| Per-entity data on a distributed mesh | ArrayPair<ParArray<T, rs>> | Father+son+transformer in one object |
| Mesh connectivity (variable row width) | ArrayAdjacencyPair<NonUniformSize> | CSR storage + AdjacencyRow iteration |
| Per-cell Eigen vectors (coords, fluxes) | ArrayEigenVectorPair<N> | operator[] returns Eigen::Map<Vector> |
| Solver DOFs with norm/dot/AXPY | ArrayDof<M, N> (or tUDof/tURec/tUGrad) | Adds MPI-collective vector ops |
The simplest case is a local array with a fixed row width. (Source reference: DNDS/Array.hpp:60 for the class, DNDS/Array.hpp:346 for Resize.)
When the row width is not known until runtime (e.g. reconstruction order is a user setting), use DynamicSize:
Mesh connectivity is the classic CSR case: a triangle has 3 nodes, a quad has 4, a hex has 8. Use NonUniformSize for both _row_size and _row_max:
Why compress/decompress? The decompressed form uses a vector<vector<T>> which allows arbitrary per-row resizing but is scattered in memory. The compressed form is a single contiguous allocation, which is required for MPI communication (the MPI datatype describes offsets into one buffer) and for CUDA device transfer.
ParArray adds MPI awareness to Array. Every rank allocates its own portion; the global index mapping tells each rank where its rows sit in the global index space. (Source: DNDS/ArrayTransformer.hpp:35.)
You rarely use ParArray alone – the ghost layer (ArrayTransformer) is almost always needed.
Finite volume schemes need data from cells on neighboring ranks. ArrayTransformer manages a father (owned) and son (ghost) array pair and the MPI machinery to exchange data between them. (Source: DNDS/ArrayTransformer.hpp:342.)
The setup has four steps. Each step is explained with why it exists:
After setup, pulling ghost data is two lines:
In practice, most code uses ArrayPair which bundles father + son + transformer. The most common pattern is:
cell2cell) is set up with the full four-step process above.coords, u, uRec) borrow the primary's ghost mapping and only rebuild the MPI types (because the element size differs).(Source: DNDS/ArrayPair.hpp:269 for BorrowAndPull.)
ArrayDof is what you use for your PDE unknowns. It inherits everything from ArrayEigenMatrixPair (father + son + transformer + per-row Eigen::Map<Matrix> access) and adds MPI-collective vector-space operations. (Source: DNDS/ArrayDOF.hpp:134.)
If you are writing a new solver, the typical startup is:
FiniteVolume and VariationalReconstruction.BuildUDof, BuildURec, BuildUGrad to get your state arrays.See the Euler solver at Euler/EulerSolver.hxx for the full pattern.
The Python bindings (built with pybind11) mirror the C++ API. The naming convention encodes the element type, row size, row max, and alignment: <Base>_<type>_<rs>_<rm>_<align> where d=double, q=int64, D=DynamicSize, I=NonUniformSize, N=NoAlign.
| Python class | C++ equivalent |
|---|---|
DNDS.ParArray_d_5_5_N | ParArray<real, 5> |
DNDS.ParArray_q_I_I_N | ParArray<index, NonUniformSize, NonUniformSize> |
DNDS.ParArrayPair_d_5_5_N | ArrayPair<ParArray<real, 5>> |
For DOF arrays, the CFV module provides tUDof_D (dynamic-size) and fixed-size variants. See test/CFV/test_fv_correctness.py for a complete working example.