DNDSR 0.1.0.dev1+gcd065ad
Distributed Numeric Data Structure for CFV
Loading...
Searching...
No Matches
test_ArrayTransformer.cpp
Go to the documentation of this file.
1/**
2 * @file test_ArrayTransformer.cpp
3 * @brief Comprehensive doctest-based unit tests for DNDS::ArrayTransformer
4 * ghost/halo communication, run under mpirun with 1, 2, and 4 ranks.
5 *
6 * Tests pull-based and push-based ghost exchange for TABLE_StaticFixed,
7 * TABLE_Fixed, CSR, and compound element types. Also covers persistent
8 * communication and BorrowGGIndexing.
9 *
10 * @see @ref dnds_unit_tests for the full test-suite overview.
11 * @test ParArray basics, pull (StaticFixed, Fixed, CSR, std::array),
12 * persistent pull, BorrowGGIndexing, push.
13 */
14
15#define DOCTEST_CONFIG_IMPLEMENT
16#include "doctest.h"
17#include "DNDS/Array.hpp"
19#include <cstdint>
20#include <memory>
21#include <vector>
22#include <cstdlib>
23#include <numeric>
24
25using namespace DNDS;
26
27int main(int argc, char **argv)
28{
29 MPI_Init(&argc, &argv);
30 doctest::Context ctx;
31 ctx.applyCommandLine(argc, argv);
32 int res = ctx.run();
33 MPI_Finalize();
34 return res;
35}
36
37// ---------------------------------------------------------------------------
38// Helpers
39// ---------------------------------------------------------------------------
40
41static MPIInfo worldMPI()
42{
44 mpi.setWorld();
45 return mpi;
46}
47
48/// Build a vector of global indices owned by other ranks (first `nPer` from
49/// each non-local rank). Indices are relative to the GlobalOffsetsMapping.
50static std::vector<DNDS::index> pullFirstNFromOthers(
51 const MPIInfo &mpi,
52 const GlobalOffsetsMapping &gm,
53 DNDS::index nPerRank)
54{
55 std::vector<DNDS::index> idx;
56 for (MPI_int r = 0; r < mpi.size; r++)
57 {
58 if (r == mpi.rank)
59 continue;
60 DNDS::index base = gm(r, 0);
61 for (DNDS::index i = 0; i < nPerRank; i++)
62 idx.push_back(base + i);
63 }
64 return idx;
65}
66
67// ---------------------------------------------------------------------------
68TEST_CASE("ParArray basics")
69{
70 MPIInfo mpi = worldMPI();
71
73 a.setMPI(mpi);
74 a.Resize(10);
75
76 // Fill: a(i,j) = rank*1000 + i*10 + j
77 for (DNDS::index i = 0; i < a.Size(); i++)
78 for (DNDS::rowsize j = 0; j < 3; j++)
79 a(i, j) = mpi.rank * 1000.0 + i * 10.0 + j;
80
82
83 SUBCASE("globalSize is consistent")
84 {
85 DNDS::index gs = a.globalSize();
86 CHECK(gs == 10 * mpi.size);
87 }
88
89 SUBCASE("AssertConsistent does not throw")
90 {
91 CHECK_NOTHROW(a.AssertConsistent());
92 }
93
94 SUBCASE("local data unchanged after mapping")
95 {
96 for (DNDS::index i = 0; i < a.Size(); i++)
97 for (DNDS::rowsize j = 0; j < 3; j++)
98 CHECK(a(i, j) == doctest::Approx(mpi.rank * 1000.0 + i * 10.0 + j));
99 }
100}
101
102// ---------------------------------------------------------------------------
103TEST_CASE("ArrayTransformer pull - TABLE_StaticFixed")
104{
105 MPIInfo mpi = worldMPI();
106 constexpr DNDS::index nLocal = 100;
107 constexpr DNDS::rowsize cols = 3;
108
109 auto father = std::make_shared<ParArray<DNDS::real, cols>>(mpi);
110 father->Resize(nLocal);
111
112 // Fill with a value encoding the global index
113 father->createGlobalMapping();
114 DNDS::index gOff = (*father->pLGlobalMapping)(mpi.rank, 0);
115 for (DNDS::index i = 0; i < nLocal; i++)
116 for (DNDS::rowsize j = 0; j < cols; j++)
117 (*father)(i, j) = static_cast<DNDS::real>((gOff + i) * 10 + j);
118
119 // Ghost mapping: pull first 5 from every other rank
120 auto son = std::make_shared<ParArray<DNDS::real, cols>>(mpi);
123 trans.createFatherGlobalMapping();
124 auto pullIdx = pullFirstNFromOthers(mpi, *trans.pLGlobalMapping, 5);
125 trans.createGhostMapping(std::vector<DNDS::index>(pullIdx));
126 trans.createMPITypes();
127 trans.pullOnce();
128
129 CHECK(son->Size() == static_cast<DNDS::index>(pullIdx.size()));
130
131 // Verify each ghost value
132 for (DNDS::index g = 0; g < son->Size(); g++)
133 {
134 // Recover which global index this ghost corresponds to.
135 // Ghost array is sorted by rank then ascending index.
136 // We verify via the encoded value.
137 for (DNDS::rowsize j = 0; j < cols; j++)
138 {
139 DNDS::real val = (*son)(g, j);
140 // val == globalIdx*10 + j => globalIdx = (val - j) / 10
141 DNDS::index gIdx = static_cast<DNDS::index>((val - j) / 10.0 + 0.5);
142 CHECK(val == doctest::Approx(static_cast<DNDS::real>(gIdx * 10 + j)));
143 }
144 }
145}
146
147// ---------------------------------------------------------------------------
148TEST_CASE("ArrayTransformer pull - TABLE_Fixed (DynamicSize)")
149{
150 MPIInfo mpi = worldMPI();
151 constexpr DNDS::index nLocal = 50;
152 constexpr DNDS::rowsize dynCols = 5;
153
154 auto father = std::make_shared<ParArray<DNDS::real, DynamicSize>>(mpi);
156
157 father->createGlobalMapping();
158 DNDS::index gOff = (*father->pLGlobalMapping)(mpi.rank, 0);
159 for (DNDS::index i = 0; i < nLocal; i++)
160 for (DNDS::rowsize j = 0; j < dynCols; j++)
161 (*father)(i, j) = (gOff + i) * 0.1 + j * 0.001;
162
163 // Pull everything: complete replication
164 DNDS::index gSize = father->globalSize();
165 std::vector<DNDS::index> pullAll(gSize);
166 std::iota(pullAll.begin(), pullAll.end(), DNDS::index(0));
167
168 auto son = std::make_shared<ParArray<DNDS::real, DynamicSize>>(mpi);
171 trans.createFatherGlobalMapping();
172 trans.createGhostMapping(std::vector<DNDS::index>(pullAll));
173 trans.createMPITypes();
174 trans.pullOnce();
175
176 CHECK(son->Size() == gSize);
177
178 for (DNDS::index g = 0; g < son->Size(); g++)
179 for (DNDS::rowsize j = 0; j < dynCols; j++)
180 {
181 // ghost element g has global index determined by the mapping
182 // We can reconstruct the expected value from the ghost index mapping
183 DNDS::index ghostGlobal = trans.pLGhostMapping->ghostIndex[g];
184 DNDS::real expected = ghostGlobal * 0.1 + j * 0.001;
185 CHECK((*son)(g, j) == doctest::Approx(expected));
186 }
187}
188
189// ---------------------------------------------------------------------------
190TEST_CASE("ArrayTransformer pull - CSR layout")
191{
192 MPIInfo mpi = worldMPI();
193 constexpr DNDS::index nLocal = 30;
194
195 auto father = std::make_shared<ParArray<DNDS::real, NonUniformSize, NonUniformSize>>(mpi);
196
197 // Build with varying row sizes: row i has (i % 4 + 1) columns
198 father->Resize(nLocal, [](DNDS::index i) -> DNDS::rowsize
199 { return static_cast<DNDS::rowsize>(i % 4 + 1); });
200
201 father->createGlobalMapping();
202 DNDS::index gOff = (*father->pLGlobalMapping)(mpi.rank, 0);
203 for (DNDS::index i = 0; i < nLocal; i++)
204 for (DNDS::rowsize j = 0; j < father->RowSize(i); j++)
205 (*father)(i, j) = (gOff + i) * 100.0 + j;
206
207 // Pull a subset: first 3 elements from every other rank
208 auto son = std::make_shared<ParArray<DNDS::real, NonUniformSize, NonUniformSize>>(mpi);
211 trans.createFatherGlobalMapping();
212 auto pullIdx = pullFirstNFromOthers(mpi, *trans.pLGlobalMapping, 3);
213 trans.createGhostMapping(std::vector<DNDS::index>(pullIdx));
214 trans.createMPITypes();
215 trans.pullOnce();
216
217 CHECK(son->Size() == static_cast<DNDS::index>(pullIdx.size()));
218
219 // Verify ghost data and row sizes
220 for (DNDS::index g = 0; g < son->Size(); g++)
221 {
222 DNDS::index ghostGlobal = trans.pLGhostMapping->ghostIndex[g];
223 // Derive the original local rank and local index
224 MPI_int srcRank = -1;
225 DNDS::index srcLoc = -1;
226 bool found = trans.pLGlobalMapping->search(ghostGlobal, srcRank, srcLoc);
227 CHECK(found);
228
229 DNDS::rowsize expectedRowSize = static_cast<DNDS::rowsize>(srcLoc % 4 + 1);
230 CHECK(son->RowSize(g) == expectedRowSize);
231
232 for (DNDS::rowsize j = 0; j < son->RowSize(g); j++)
233 CHECK((*son)(g, j) == doctest::Approx(ghostGlobal * 100.0 + j));
234 }
235}
236
237// ---------------------------------------------------------------------------
238TEST_CASE("ArrayTransformer pull - std::array elements")
239{
240 MPIInfo mpi = worldMPI();
241 using Arr9 = std::array<DNDS::real, 9>;
242 constexpr DNDS::index nLocal = 20;
243
244 auto father = std::make_shared<ParArray<Arr9, DynamicSize>>(mpi);
245 father->Resize(nLocal, 1);
246
247 father->createGlobalMapping();
248 DNDS::index gOff = (*father->pLGlobalMapping)(mpi.rank, 0);
249 for (DNDS::index i = 0; i < nLocal; i++)
250 {
251 Arr9 val{};
252 for (int k = 0; k < 9; k++)
253 val[k] = static_cast<DNDS::real>(gOff + i) + k * 0.01;
254 (*father)(i, 0) = val;
255 }
256
257 auto son = std::make_shared<ParArray<Arr9, DynamicSize>>(mpi);
260 trans.createFatherGlobalMapping();
261
262 // Pull first 4 from each other rank
263 auto pullIdx = pullFirstNFromOthers(mpi, *trans.pLGlobalMapping, 4);
264 trans.createGhostMapping(std::vector<DNDS::index>(pullIdx));
265 trans.createMPITypes();
266 trans.pullOnce();
267
268 CHECK(son->Size() == static_cast<DNDS::index>(pullIdx.size()));
269
270 for (DNDS::index g = 0; g < son->Size(); g++)
271 {
272 DNDS::index ghostGlobal = trans.pLGhostMapping->ghostIndex[g];
273 Arr9 got = (*son)(g, 0);
274 for (int k = 0; k < 9; k++)
275 {
276 DNDS::real expected = static_cast<DNDS::real>(ghostGlobal) + k * 0.01;
277 CHECK(got[k] == doctest::Approx(expected).epsilon(1e-14));
278 }
279 }
280}
281
282// ---------------------------------------------------------------------------
283TEST_CASE("ArrayTransformer persistent pull")
284{
285 MPIInfo mpi = worldMPI();
286 constexpr DNDS::index nLocal = 40;
287 constexpr DNDS::rowsize dynCols = 4;
288
289 auto father = std::make_shared<ParArray<DNDS::real, DynamicSize>>(mpi);
290 father->Resize(nLocal, dynCols);
291
292 father->createGlobalMapping();
293 DNDS::index gOff = (*father->pLGlobalMapping)(mpi.rank, 0);
294
295 auto fillFather = [&](DNDS::real offset)
296 {
297 for (DNDS::index i = 0; i < nLocal; i++)
298 for (DNDS::rowsize j = 0; j < dynCols; j++)
299 (*father)(i, j) = (gOff + i) + j * 0.01 + offset;
300 };
301
302 fillFather(0.0);
303
304 auto son = std::make_shared<ParArray<DNDS::real, DynamicSize>>(mpi);
307 trans.createFatherGlobalMapping();
308
309 auto pullIdx = pullFirstNFromOthers(mpi, *trans.pLGlobalMapping, 5);
310 trans.createGhostMapping(std::vector<DNDS::index>(pullIdx));
311 trans.createMPITypes();
312
313 // First persistent pull round
314 trans.initPersistentPull();
315 trans.startPersistentPull();
316 trans.waitPersistentPull();
317
318 CHECK(son->Size() == static_cast<DNDS::index>(pullIdx.size()));
319
320 for (DNDS::index g = 0; g < son->Size(); g++)
321 {
322 DNDS::index ghostGlobal = trans.pLGhostMapping->ghostIndex[g];
323 for (DNDS::rowsize j = 0; j < dynCols; j++)
324 CHECK((*son)(g, j) == doctest::Approx(ghostGlobal + j * 0.01 + 0.0));
325 }
326
327 // Modify father data and pull again
328 fillFather(1000.0);
329 trans.startPersistentPull();
330 trans.waitPersistentPull();
331
332 for (DNDS::index g = 0; g < son->Size(); g++)
333 {
334 DNDS::index ghostGlobal = trans.pLGhostMapping->ghostIndex[g];
335 for (DNDS::rowsize j = 0; j < dynCols; j++)
336 CHECK((*son)(g, j) == doctest::Approx(ghostGlobal + j * 0.01 + 1000.0));
337 }
338
339 trans.clearPersistentPull();
340}
341
342// ---------------------------------------------------------------------------
343TEST_CASE("ArrayTransformer BorrowGGIndexing")
344{
345 MPIInfo mpi = worldMPI();
346 constexpr DNDS::index nLocal = 60;
347 constexpr DNDS::rowsize cols = 2;
348
349 // First array: full ghost setup
350 auto father1 = std::make_shared<ParArray<DNDS::real, cols>>(mpi);
351 father1->Resize(nLocal);
352
353 father1->createGlobalMapping();
354 DNDS::index gOff = (*father1->pLGlobalMapping)(mpi.rank, 0);
355 for (DNDS::index i = 0; i < nLocal; i++)
356 for (DNDS::rowsize j = 0; j < cols; j++)
357 (*father1)(i, j) = (gOff + i) * 10.0 + j;
358
359 auto son1 = std::make_shared<ParArray<DNDS::real, cols>>(mpi);
361 trans1.setFatherSon(father1, son1);
362 trans1.createFatherGlobalMapping();
363
364 auto pullIdx = pullFirstNFromOthers(mpi, *trans1.pLGlobalMapping, 8);
365 trans1.createGhostMapping(std::vector<DNDS::index>(pullIdx));
366 trans1.createMPITypes();
367 trans1.pullOnce();
368
369 // Second array: same distribution, borrow indexing
370 auto father2 = std::make_shared<ParArray<DNDS::real, cols>>(mpi);
371 father2->Resize(nLocal);
372 father2->createGlobalMapping();
373 for (DNDS::index i = 0; i < nLocal; i++)
374 for (DNDS::rowsize j = 0; j < cols; j++)
375 (*father2)(i, j) = (gOff + i) * -1.0 + j * 0.5;
376
377 auto son2 = std::make_shared<ParArray<DNDS::real, cols>>(mpi);
379 trans2.setFatherSon(father2, son2);
380 trans2.BorrowGGIndexing(trans1);
381 trans2.createMPITypes();
382 trans2.pullOnce();
383
384 CHECK(son2->Size() == son1->Size());
385
386 for (DNDS::index g = 0; g < son2->Size(); g++)
387 {
388 DNDS::index ghostGlobal = trans2.pLGhostMapping->ghostIndex[g];
389 for (DNDS::rowsize j = 0; j < cols; j++)
390 CHECK((*son2)(g, j) == doctest::Approx(ghostGlobal * -1.0 + j * 0.5));
391 }
392}
393
394// ---------------------------------------------------------------------------
395TEST_CASE("ArrayTransformer push")
396{
397 MPIInfo mpi = worldMPI();
398 constexpr DNDS::index nLocal = 50;
399 constexpr DNDS::rowsize cols = 3;
400
401 // Father: fill with zeros
402 auto father = std::make_shared<ParArray<DNDS::real, cols>>(mpi);
403 father->Resize(nLocal);
404 for (DNDS::index i = 0; i < nLocal; i++)
405 for (DNDS::rowsize j = 0; j < cols; j++)
406 (*father)(i, j) = 0.0;
407
408 father->createGlobalMapping();
409 DNDS::index gOff = (*father->pLGlobalMapping)(mpi.rank, 0);
410
411 auto son = std::make_shared<ParArray<DNDS::real, cols>>(mpi);
414 trans.createFatherGlobalMapping();
415
416 // Pull first 5 from every other rank
417 auto pullIdx = pullFirstNFromOthers(mpi, *trans.pLGlobalMapping, 5);
418 trans.createGhostMapping(std::vector<DNDS::index>(pullIdx));
419 trans.createMPITypes();
420 trans.pullOnce(); // son now has ghost data (all zeros)
421
422 // Write recognisable values into son (ghost data)
423 for (DNDS::index g = 0; g < son->Size(); g++)
424 for (DNDS::rowsize j = 0; j < cols; j++)
425 (*son)(g, j) = 9999.0 + g + j * 0.1;
426
427 // Push ghost values back to father
428 trans.pushOnce();
429
430 // Verify: the father's first 5 elements should have been overwritten
431 // by the pushes from other ranks (each other rank pushed into our first 5).
432 // With nproc > 1, elements 0..4 of father will get the last push value
433 // (MPI push order is implementation-defined for overlapping pushes, but
434 // with correct ghost mapping each ghost maps to a unique father element).
435 // Our pullIdx requests indices from *other* ranks, so pushOnce sends
436 // son data back to the rank that owns those father elements.
437 // Therefore OUR father[0..4] will receive pushes from every other rank.
438
439 if (mpi.size > 1)
440 {
441 // At least the first 5 elements of father should be non-zero now
442 // (overwritten by push from remote ranks).
443 bool anyNonZero = false;
444 for (DNDS::index i = 0; i < 5; i++)
445 for (DNDS::rowsize j = 0; j < cols; j++)
446 if ((*father)(i, j) != 0.0)
447 anyNonZero = true;
448 CHECK(anyNonZero);
449 }
450
451 // For a more precise check: with 2 ranks, rank 0 pulls first 5 from
452 // rank 1 and vice versa. After push, rank 0's elements 0..4 receive
453 // the values that rank 1 wrote into its son, and vice versa.
454 if (mpi.size == 2)
455 {
456 MPI_int other = 1 - mpi.rank;
457 // Our first 5 elements were pushed from the other rank.
458 // The other rank's son had ghost entries for our first 5.
459 // Each entry is: 9999.0 + g + j*0.1 where g is the ghost index on *that* rank.
460 // The ghost index ordering is deterministic (sorted), so g == 0..4
461 // for the 5 elements pulled from us.
462 for (DNDS::index i = 0; i < 5; i++)
463 for (DNDS::rowsize j = 0; j < cols; j++)
464 {
465 DNDS::real expected = 9999.0 + i + j * 0.1;
466 CHECK((*father)(i, j) == doctest::Approx(expected));
467 }
468 }
469}
470
471// ===================================================================
472// Parametric: ArrayTransformer pull across types, layouts, and row sizes
473// ===================================================================
474// Full cross-product:
475// Types: real, index, uint16_t, int32_t
476// Layouts: StaticFixed, Dynamic (each with RS = 1, 3, 7), CSR
477// = 4 types x (2 layouts x 3 RS + 1 CSR) = 28 cases
478
479struct LayoutStaticFixed {};
480struct LayoutDynamic {};
481struct LayoutCSR {};
482
483template <class T, class Layout, DNDS::rowsize RS>
485{
486 using type = T;
487 using layout = Layout;
488 static constexpr DNDS::rowsize rs = RS;
489};
490
491#define TRANS_TAG_STR(T, L, RS) TYPE_TO_STRING(TransTag<T, L, RS>)
492
493// real
501// index
509// uint16_t
517// int32_t
525
526#undef TRANS_TAG_STR
527
528#define TRANS_ALL_TAGS \
529 TransTag<DNDS::real, LayoutStaticFixed, 1>, \
530 TransTag<DNDS::real, LayoutStaticFixed, 3>, \
531 TransTag<DNDS::real, LayoutStaticFixed, 7>, \
532 TransTag<DNDS::real, LayoutDynamic, 1>, \
533 TransTag<DNDS::real, LayoutDynamic, 3>, \
534 TransTag<DNDS::real, LayoutDynamic, 7>, \
535 TransTag<DNDS::real, LayoutCSR, 0>, \
536 TransTag<DNDS::index, LayoutStaticFixed, 1>, \
537 TransTag<DNDS::index, LayoutStaticFixed, 3>, \
538 TransTag<DNDS::index, LayoutStaticFixed, 7>, \
539 TransTag<DNDS::index, LayoutDynamic, 1>, \
540 TransTag<DNDS::index, LayoutDynamic, 3>, \
541 TransTag<DNDS::index, LayoutDynamic, 7>, \
542 TransTag<DNDS::index, LayoutCSR, 0>, \
543 TransTag<uint16_t, LayoutStaticFixed, 1>, \
544 TransTag<uint16_t, LayoutStaticFixed, 3>, \
545 TransTag<uint16_t, LayoutStaticFixed, 7>, \
546 TransTag<uint16_t, LayoutDynamic, 1>, \
547 TransTag<uint16_t, LayoutDynamic, 3>, \
548 TransTag<uint16_t, LayoutDynamic, 7>, \
549 TransTag<uint16_t, LayoutCSR, 0>, \
550 TransTag<int32_t, LayoutStaticFixed, 1>, \
551 TransTag<int32_t, LayoutStaticFixed, 3>, \
552 TransTag<int32_t, LayoutStaticFixed, 7>, \
553 TransTag<int32_t, LayoutDynamic, 1>, \
554 TransTag<int32_t, LayoutDynamic, 3>, \
555 TransTag<int32_t, LayoutDynamic, 7>, \
556 TransTag<int32_t, LayoutCSR, 0>
557
558TEST_CASE_TEMPLATE("ArrayTransformer pull", Tag, TRANS_ALL_TAGS)
559{
560 using T = typename Tag::type;
561 using L = typename Tag::layout;
562 constexpr DNDS::rowsize RS = Tag::rs;
563
564 MPIInfo mpi = worldMPI();
565
566 for (DNDS::index nLocal : {10, 50, 200})
567 {
568 CAPTURE(nLocal);
569 constexpr DNDS::index nGhostPerRank = 5;
570
571 if constexpr (std::is_same_v<L, LayoutStaticFixed>)
572 {
573 auto father = std::make_shared<ParArray<T, RS>>(mpi);
574 father->Resize(nLocal);
575
576 father->createGlobalMapping();
577 DNDS::index gOff = (*father->pLGlobalMapping)(mpi.rank, 0);
578 for (DNDS::index i = 0; i < nLocal; i++)
579 for (DNDS::rowsize j = 0; j < RS; j++)
580 (*father)(i, j) = static_cast<T>((gOff + i) * 100 + j);
581
582 auto son = std::make_shared<ParArray<T, RS>>(mpi);
585 trans.createFatherGlobalMapping();
586 auto pullIdx = pullFirstNFromOthers(mpi, *trans.pLGlobalMapping, nGhostPerRank);
587 trans.createGhostMapping(std::vector<DNDS::index>(pullIdx));
588 trans.createMPITypes();
589 trans.pullOnce();
590
591 CHECK(son->Size() == static_cast<DNDS::index>(pullIdx.size()));
592
593 for (DNDS::index g = 0; g < son->Size(); g++)
594 {
595 DNDS::index ghostGlobal = trans.pLGhostMapping->ghostIndex[g];
596 for (DNDS::rowsize j = 0; j < RS; j++)
597 CHECK((*son)(g, j) == static_cast<T>(ghostGlobal * 100 + j));
598 }
599 }
600 else if constexpr (std::is_same_v<L, LayoutDynamic>)
601 {
602 auto father = std::make_shared<ParArray<T, DynamicSize>>(mpi);
603 father->Resize(nLocal, RS);
604
605 father->createGlobalMapping();
606 DNDS::index gOff = (*father->pLGlobalMapping)(mpi.rank, 0);
607 for (DNDS::index i = 0; i < nLocal; i++)
608 for (DNDS::rowsize j = 0; j < RS; j++)
609 (*father)(i, j) = static_cast<T>((gOff + i) * 100 + j);
610
611 auto son = std::make_shared<ParArray<T, DynamicSize>>(mpi);
614 trans.createFatherGlobalMapping();
615 auto pullIdx = pullFirstNFromOthers(mpi, *trans.pLGlobalMapping, nGhostPerRank);
616 trans.createGhostMapping(std::vector<DNDS::index>(pullIdx));
617 trans.createMPITypes();
618 trans.pullOnce();
619
620 CHECK(son->Size() == static_cast<DNDS::index>(pullIdx.size()));
621
622 for (DNDS::index g = 0; g < son->Size(); g++)
623 {
624 DNDS::index ghostGlobal = trans.pLGhostMapping->ghostIndex[g];
625 for (DNDS::rowsize j = 0; j < RS; j++)
626 CHECK((*son)(g, j) == static_cast<T>(ghostGlobal * 100 + j));
627 }
628 }
629 else // LayoutCSR
630 {
631 auto father = std::make_shared<ParArray<T, NonUniformSize, NonUniformSize>>(mpi);
632
633 father->Resize(nLocal, [](DNDS::index i) -> DNDS::rowsize
634 { return static_cast<DNDS::rowsize>(i % 4 + 1); });
635
636 father->createGlobalMapping();
637 DNDS::index gOff = (*father->pLGlobalMapping)(mpi.rank, 0);
638 for (DNDS::index i = 0; i < nLocal; i++)
639 for (DNDS::rowsize j = 0; j < father->RowSize(i); j++)
640 (*father)(i, j) = static_cast<T>((gOff + i) * 100 + j);
641
642 auto son = std::make_shared<ParArray<T, NonUniformSize, NonUniformSize>>(mpi);
645 trans.createFatherGlobalMapping();
646 auto pullIdx = pullFirstNFromOthers(mpi, *trans.pLGlobalMapping, nGhostPerRank);
647 trans.createGhostMapping(std::vector<DNDS::index>(pullIdx));
648 trans.createMPITypes();
649 trans.pullOnce();
650
651 CHECK(son->Size() == static_cast<DNDS::index>(pullIdx.size()));
652
653 for (DNDS::index g = 0; g < son->Size(); g++)
654 {
655 DNDS::index ghostGlobal = trans.pLGhostMapping->ghostIndex[g];
656 MPI_int srcRank = -1;
657 DNDS::index srcLoc = -1;
658 bool found = trans.pLGlobalMapping->search(ghostGlobal, srcRank, srcLoc);
659 CHECK(found);
660
661 DNDS::rowsize expectedRowSize = static_cast<DNDS::rowsize>(srcLoc % 4 + 1);
662 CHECK(son->RowSize(g) == expectedRowSize);
663
664 for (DNDS::rowsize j = 0; j < son->RowSize(g); j++)
665 CHECK((*son)(g, j) == static_cast<T>(ghostGlobal * 100 + j));
666 }
667 }
668 } // for nLocal
669}
ParArray (MPI-aware array) and ArrayTransformer (ghost/halo communication).
Core 2D variable-length array container with five data layouts.
Ghost-communication engine for a father / son ParArray pair.
void setFatherSon(const t_pArray &n_father, const t_pArray &n_son)
Attach father and son arrays. First setup step.
void Resize(index nSize, rowsize nRow_size_dynamic)
Resize the array, setting a uniform or maximum row width.
Definition Array.hpp:395
index Size() const
Number of rows currently stored. O(1).
Definition Array.hpp:171
Table mapping rank-local row indices to the global index space.
MPI-aware Array: adds a communicator, rank, and global index mapping.
void setMPI(const MPIInfo &n_mpi)
Install the MPI context after default construction.
index globalSize() const
Returns the total global size (sum of sizes across all ranks).
bool AssertConsistent()
Check array consistency across all ranks.
void createGlobalMapping()
Collective: build the global offsets table.
int main()
Definition dummy.cpp:3
the host side operators are provided as implemented
int32_t rowsize
Row-width / per-row element-count type (signed 32-bit).
Definition Defines.hpp:109
int64_t index
Global row / DOF index type (signed 64-bit; handles multi-billion-cell meshes).
Definition Defines.hpp:107
double real
Canonical floating-point scalar used throughout DNDSR (double precision).
Definition Defines.hpp:105
int MPI_int
MPI counterpart type for MPI_int (= C int). Used for counts and ranks in MPI calls.
Definition MPI.hpp:43
Lightweight bundle of an MPI communicator and the calling rank's coordinates.
Definition MPI.hpp:215
static constexpr DNDS::rowsize rs
ArrayTransformer< DNDS::real, DynamicSize > trans
constexpr DNDS::index nLocal
TEST_CASE_TEMPLATE("ArrayTransformer pull", Tag, TRANS_ALL_TAGS)
CHECK(son->Size()==gSize)
constexpr DNDS::rowsize dynCols
#define TRANS_TAG_STR(T, L, RS)
DNDS::real expected
DNDS::index gOff
std::vector< DNDS::index > pullAll(gSize)
#define TRANS_ALL_TAGS
tVec r(NCells)
auto res
Definition test_ODE.cpp:196
TEST_CASE("3D: VFV P2 HQM error < P1 on sinCos3D")