Vlasiator ebf0dd394 on dev (v5.4.0 + 1054 commits)
Loading...
Searching...
No Matches
cpu_trans_pencils.cpp
Go to the documentation of this file.
1#include "../grid.h"
2using namespace std;
3using namespace spatial_cell;
4
6#include "../logger.h"
7
8#ifdef USE_GPU
9// just for uploading pencil information to GPU
10#include "../arch/gpu_base.hpp"
11#endif
12
13extern Logger logFile;
14
15// Cell lists for ghost translation
16std::unordered_set<CellID> ghostTranslate_sources_x;
17std::unordered_set<CellID> ghostTranslate_sources_y;
18std::unordered_set<CellID> ghostTranslate_sources_z;
19std::unordered_set<CellID> ghostTranslate_active_x;
20std::unordered_set<CellID> ghostTranslate_active_y;
21std::unordered_set<CellID> ghostTranslate_active_z;
22
23std::array<setOfPencils,3> DimensionPencils;
24
25//Is cell translated? It is not translated if DO_NO_COMPUTE or if it is sysboundary cell and not in first sysboundarylayer
26bool do_translate_cell(const SpatialCell* const SC){
29 return false;
30 } else {
31 return true;
32 }
33}
34
35bool check_is_active(const dccrg::Dccrg<SpatialCell,dccrg::Cartesian_Geometry>& mpiGrid, CellID cid, int dimension) {
37 switch (dimension) {
38 case 0:
39 if (ghostTranslate_active_x.count(cid)) {
40 return true;
41 }
42 break;
43 case 1:
44 if (ghostTranslate_active_y.count(cid)) {
45 return true;
46 }
47 break;
48 case 2:
49 if (ghostTranslate_active_z.count(cid)) {
50 return true;
51 }
52 break;
53 default:
54 cerr << __FILE__ << ":"<< __LINE__ << " Wrong dimension, abort"<<endl;
55 abort();
56 }
57 return false;
58 } else {
59 if (mpiGrid.is_local(cid)) {
60 return true;
61 }
62 return false;
63 }
64}
65
66bool check_is_written_to(const dccrg::Dccrg<SpatialCell,dccrg::Cartesian_Geometry>& mpiGrid,const CellID cid,const int dimension) {
67 // Only called if doing ghost translation
68 const SpatialCell *SC = mpiGrid[cid];
69 if (!SC) {
70 return false;
71 }
73 return false;
74 }
75 // Order is z -> x -> y
76 switch (dimension) {
77 // checks for (cell) && (cell->sysBoundaryFlag == sysboundarytype::NOT_SYSBOUNDARY) are before call
78 case 0: // Second direction (x): Write into all cells which are used in y-translation
79 if (ghostTranslate_sources_y.count(cid)) {
80 return true;
81 }
82 break;
83 case 1: // Last direction (y): Write only into local cells
84 if (mpiGrid.is_local(cid)) {
85 return true;
86 }
87 break;
88 case 2: // First direction (z): Write into all cells which are used in x-translation
89 if (ghostTranslate_sources_x.count(cid)) {
90 return true;
91 }
92 break;
93 default:
94 cerr << __FILE__ << ":"<< __LINE__ << " Wrong dimension, abort"<<endl;
95 abort();
96 }
97 return false;
98}
99
100/* Get the one-dimensional neighborhood index for a given direction and neighborhood size.
101 *
102 * @param dimension spatial dimension of neighborhood
103 * @param stencil neighborhood size in cells
104 * @return neighborhood index that can be passed to DCCRG functions
105 */
106int getNeighborhood(const uint dimension, const uint stencil) {
107
108 if (stencil == 1) {
109 switch (dimension) {
110 case 0:
112 case 1:
114 case 2:
116 default:
117 cerr << __FILE__ << ":"<< __LINE__ << " Wrong dimension, abort"<<endl;
118 abort();
119 }
120 }
121 if (stencil == VLASOV_STENCIL_WIDTH) {
122 switch (dimension) {
123 case 0:
125 case 1:
127 case 2:
129 default:
130 cerr << __FILE__ << ":"<< __LINE__ << " Wrong dimension, abort"<<endl;
131 abort();
132 }
133 }
134 if (stencil == VLASOV_STENCIL_WIDTH+1) {
135 switch (dimension) {
136 case 0:
138 case 1:
140 case 2:
142 default:
143 cerr << __FILE__ << ":"<< __LINE__ << " Wrong dimension, abort"<<endl;
144 abort();
145 }
146 }
147 cerr << __FILE__ << ":"<< __LINE__ << " Wrong stencil, abort"<<endl;
148 abort();
149}
150
154void findNeighborhoodCells(const dccrg::Dccrg<SpatialCell,dccrg::Cartesian_Geometry>& mpiGrid,
155 const CellID startingCellID,
156 const uint dimension,
157 const uint searchLength,
158 std::vector<CellID>& foundCells) {
159
160 const int neighborhood = getNeighborhood(dimension,searchLength);
161 foundCells.clear();
162
163 const SpatialCell *ccell = mpiGrid[startingCellID];
164 if (!ccell) {
165 return;
166 }
167
168 const auto* NbrPairs = mpiGrid.get_neighbors_of(startingCellID, neighborhood);
169 // Verified 9th July 2024: current get_neighbors_of() returns unique cells, only once per cell, in correct order.
170 for (const auto& nbrPair : *NbrPairs) {
171 SpatialCell *ncell = mpiGrid[nbrPair.first];
172 if (!ncell) {
173 continue;
174 }
175 // Is the cell translated?
176 if (!do_translate_cell(ncell)) {
177 continue;
178 }
179 foundCells.push_back(nbrPair.first);
180 }
181}
182
183void prepareGhostTranslationCellLists(const dccrg::Dccrg<SpatialCell,dccrg::Cartesian_Geometry>& mpiGrid,
184 const vector<CellID>& localPropagatedCells) {
185 // Clear existing lists
192
193 // return if there's no cells to start with
194 if (localPropagatedCells.size() == 0) {
195 return;
196 }
197
198 std::vector<CellID> foundCells;
199
200 // Cell lists include:
201 // 1) Any local (translated) non-sysboundary cells
202 // 2) per-dimension, in translation order, one layer of face neighbours , remote or local (including translated sysboundary cells)
203 // as active cells
204 // 3) per-dimension, in translation order, one or VLASOV_STENCIL_WIDTH layers of face neighbours, remote or local (including translated sysboundary cells)
205 // as source cells
206 // Done only at LB so not threaded for now
207
208 // Ghost translation stencil size set by parameter, defaults to VLASOV_STENCIL_WIDTH+1;
209 int searchLength = P::vlasovSolverGhostTranslateExtent;
210
215
216 phiprof::Timer ghostYTimer {"prepare ghost translation Y lists"};
217 int dimension = 1;
218 for (const CellID c : localPropagatedCells) {
219 const SpatialCell *ccell = mpiGrid[c];
220 if (!ccell) {
221 continue;
222 }
223 // Is the cell translated?
224 if (!do_translate_cell(ccell)) {
225 continue;
226 }
228 // Update as sources only non-sysb cells
229 // (note, source cells not part of these lists are still updated through MPI)
230 if (mpiGrid[c]->sysBoundaryFlag == sysboundarytype::NOT_SYSBOUNDARY) {
232 }
233
234 // Sources to be updated
235 findNeighborhoodCells(mpiGrid, c, dimension, searchLength, foundCells);
236 for (const CellID cid: foundCells) {
237 // Update as sources only non-sysb cells
238 if (mpiGrid[cid]->sysBoundaryFlag == sysboundarytype::NOT_SYSBOUNDARY) {
239 ghostTranslate_sources_y.insert(cid);
240 }
241 }
242 // Cells to be translated so local end result is good (find neighborhood contains do_translate check)
243 findNeighborhoodCells(mpiGrid, c, dimension, 1, foundCells);
244 for (const CellID cid: foundCells) {
245 ghostTranslate_active_y.insert(cid);
246 }
247 } // end loop over local propagated cells
248 ghostYTimer.stop();
249
253
254 phiprof::Timer ghostXTimer {"prepare ghost translation X lists"};
255 dimension = 0;
256 for (const CellID c : ghostTranslate_sources_y) {
257 const SpatialCell *ccell = mpiGrid[c];
258 if (!ccell) {
259 continue;
260 }
261 // Is the cell translated?
262 if (!do_translate_cell(ccell)) {
263 continue;
264 }
266 // Update as sources only non-sysb cells
267 if (mpiGrid[c]->sysBoundaryFlag == sysboundarytype::NOT_SYSBOUNDARY) {
269 }
270 // Sources to be updated
271 findNeighborhoodCells(mpiGrid, c, dimension, searchLength, foundCells);
272 for (CellID cid: foundCells) {
273 // Update as sources only non-sysb cells
274 if (mpiGrid[cid]->sysBoundaryFlag == sysboundarytype::NOT_SYSBOUNDARY) {
275 ghostTranslate_sources_x.insert(cid);
276 }
277 }
278 // Cells to be translated so local end result is good
279 findNeighborhoodCells(mpiGrid, c, dimension, 1, foundCells);
280 for (const CellID cid: foundCells) {
281 ghostTranslate_active_x.insert(cid);
282 }
283 } // end loop over y-translation sources
284 ghostXTimer.stop();
285
289
290 phiprof::Timer ghostZTimer {"prepare ghost translation Z lists"};
291 dimension = 2;
292 for (const CellID c : ghostTranslate_sources_x) {
293 const SpatialCell *ccell = mpiGrid[c];
294 if (!ccell) {
295 continue;
296 }
297 // Is the cell translated?
298 if (!do_translate_cell(ccell)) {
299 continue;
300 }
302 // Update as sources only non-sysb cells
303 if (mpiGrid[c]->sysBoundaryFlag == sysboundarytype::NOT_SYSBOUNDARY) {
305 }
306 // Sources to be updated
307 findNeighborhoodCells(mpiGrid, c, dimension, searchLength, foundCells);
308 for (const CellID cid: foundCells) {
309 // Update as sources only non-sysb cells
310 if (mpiGrid[cid]->sysBoundaryFlag == sysboundarytype::NOT_SYSBOUNDARY) {
311 ghostTranslate_sources_z.insert(cid);
312 }
313 }
314 // Cells to be translated so local end result is good
315 findNeighborhoodCells(mpiGrid, c, dimension, 1, foundCells);
316 for (const CellID cid: foundCells) {
317 ghostTranslate_active_z.insert(cid);
318 }
319 } // end loop over y-translation sources
320 ghostZTimer.stop();
321
322
323 // Gather and report statistics
324 std::vector<int64_t> localCounts;
325 localCounts.push_back(ghostTranslate_sources_x.size());
326 localCounts.push_back(ghostTranslate_sources_y.size());
327 localCounts.push_back(ghostTranslate_sources_z.size());
328 localCounts.push_back(ghostTranslate_active_x.size());
329 localCounts.push_back(ghostTranslate_active_y.size());
330 localCounts.push_back(ghostTranslate_active_z.size());
331 localCounts.push_back(localPropagatedCells.size());
332 int nc = localCounts.size();
333 std::vector<int64_t> globalCounts(nc*4);
334 int world_size;
335 MPI_Comm_size(MPI_COMM_WORLD, &world_size);
336 MPI_Reduce(localCounts.data(), globalCounts.data(), nc, MPI_INT64_T, MPI_SUM, MASTER_RANK, MPI_COMM_WORLD);
337 MPI_Reduce(localCounts.data(), &(globalCounts.at(nc)), nc, MPI_INT64_T, MPI_MIN, MASTER_RANK, MPI_COMM_WORLD);
338 MPI_Reduce(localCounts.data(), &(globalCounts.at(2*nc)), nc, MPI_INT64_T, MPI_MAX, MASTER_RANK, MPI_COMM_WORLD);
339 for(int i = 0; i <nc; i++) { // calc avgs
340 globalCounts.at(3*nc+i) = globalCounts.at(i) / world_size;
341 }
342 logFile << "(CELLS) tstep = " << P::tstep << " time = " << P::t << " \n source cells (tot / avg / min / max) (x y z) [ ";
343 for(int dim = 0; dim <3; dim++) { // tot
344 logFile << globalCounts.at(dim) << " ";
345 }
346 logFile << " / ";
347 for(int dim = 0; dim <3; dim++) { // avg
348 logFile << globalCounts.at(3*nc+dim) << " ";
349 }
350 logFile << " / ";
351 for(int dim = 0; dim <3; dim++) { // min
352 logFile << globalCounts.at(nc+dim) << " ";
353 }
354 logFile << " / ";
355 for(int dim = 0; dim <3; dim++) { // max
356 logFile << globalCounts.at(2*nc+dim) << " ";
357 }
358 logFile << "] \n active cells (tot / avg / min / max) (x y z) [ ";
359 for(int dim = 3; dim <6; dim++) { // tot
360 logFile << globalCounts.at(dim) << " ";
361 }
362 logFile << " / ";
363 for(int dim = 3; dim <6; dim++) { // avg
364 logFile << globalCounts.at(3*nc+dim) << " ";
365 }
366 logFile << " / ";
367 for(int dim = 3; dim <6; dim++) { // min
368 logFile << globalCounts.at(nc+dim) << " ";
369 }
370 logFile << " / ";
371 for(int dim = 3; dim <6; dim++) { // max
372 logFile << globalCounts.at(2*nc+dim) << " ";
373 }
374 logFile << "] \n local cells (tot / avg / min / max) [ ";
375 logFile << globalCounts.at(nc-1) << " / ";
376 logFile << globalCounts.at(4*nc-1) << " / ";
377 logFile << globalCounts.at(2*nc-1) << " / ";
378 logFile << globalCounts.at(3*nc-1);
379
380 std::vector<float> localCountsF;
381 localCountsF.push_back((float)localCounts.at(0) / (float)localCounts.at(6));
382 localCountsF.push_back((float)localCounts.at(1) / (float)localCounts.at(6));
383 localCountsF.push_back((float)localCounts.at(2) / (float)localCounts.at(6));
384 localCountsF.push_back((float)localCounts.at(3) / (float)localCounts.at(6));
385 localCountsF.push_back((float)localCounts.at(4) / (float)localCounts.at(6));
386 localCountsF.push_back((float)localCounts.at(5) / (float)localCounts.at(6));
387 int fc = localCountsF.size();
388 std::vector<float> globalCountsF(4*fc);
389 MPI_Reduce(localCountsF.data(), globalCountsF.data(), fc, MPI_FLOAT, MPI_SUM, MASTER_RANK, MPI_COMM_WORLD);
390 MPI_Reduce(localCountsF.data(), &(globalCountsF.at(fc)), fc, MPI_FLOAT, MPI_MIN, MASTER_RANK, MPI_COMM_WORLD);
391 MPI_Reduce(localCountsF.data(), &(globalCountsF.at(2*fc)), fc, MPI_FLOAT, MPI_MAX, MASTER_RANK, MPI_COMM_WORLD);
392 for(int i = 0; i <fc; i++) { // calc avgs
393 globalCountsF.at(3*fc+i) = globalCountsF.at(i) / world_size;
394 }
395 logFile << "] \n source ratios (avg / min / max) (x y z) [ ";
396 for(int dim = 0; dim <3; dim++) { // avg
397 logFile << globalCountsF.at(3*fc+dim) << " ";
398 }
399 logFile << " / ";
400 for(int dim = 0; dim <3; dim++) { // min
401 logFile << globalCountsF.at(fc+dim) << " ";
402 }
403 logFile << " / ";
404 for(int dim = 0; dim <3; dim++) { // max
405 logFile << globalCountsF.at(2*fc+dim) << " ";
406 }
407 logFile << "] \n active ratios (avg / min / max) (x y z) [ ";
408 for(int dim = 3; dim <6; dim++) { // avg
409 logFile << globalCountsF.at(3*fc+dim) << " ";
410 }
411 logFile << " / ";
412 for(int dim = 3; dim <6; dim++) { // min
413 logFile << globalCountsF.at(fc+dim) << " ";
414 }
415 logFile << " / ";
416 for(int dim = 3; dim <6; dim++) { // max
417 logFile << globalCountsF.at(2*fc+dim) << " ";
418 }
419 logFile << "]" << endl << flush;
420 return;
421}
422
423/* Get cellIDs for spatial cells that are considered target / source cells for a pencil.
424 *
425 * Source cells are cells that the pencil reads data from to compute polynomial
426 * fits that are used for propagation in the vlasov solver. All cells included
427 * in the pencil proper + VLASOV_STENCIL_WIDTH cells on both ends are source cells.
428 * Invalid cells are replaced by closest good cells.
429 * Boundary cells are included.
430 *
431 * Target cells are cells that the pencil writes data into after translation by
432 * the vlasov solver. All cells included in the pencil proper + 1 cells on both ends
433 * are target cells.
434 *
435 * There is only one list of cellIDs for the pencil, where each source cell has also
436 * their width stored, and for target cells, the relative contribution is stored. If
437 * the cell in question is not a target cell, the contribution is set to zero.
438 *
439 * @param [in] mpiGrid DCCRG grid object
440 * @param [inout] ids pointer to subsection of vector of ids for actual pencil
441 * @param [in] L length of pencil (including stencil cells)
442 * @param [in] dimension spatial dimension
443 * @param [in] path index of the desired face neighbor when going to a higher refinement level
444 * @param [out] source pointer to subsection of vector storing cell widths
445 * @param [out] targetRatios pointer to subsection of vector storing relative contribution of pencil to target cells
446 */
447void computeSpatialSourceCellsForPencil(const dccrg::Dccrg<SpatialCell,dccrg::Cartesian_Geometry>& mpiGrid,
448 CellID *ids,
449 const uint L,
450 const uint dimension,
451 std::vector<uint> path,
452 Realf* sourceDZ,
453 Realf* targetRatios
454 ){
455
456 // These neighborhoods now include the AMR addition beyond the regular vlasov stencil
457 int neighborhood = getNeighborhood(dimension,VLASOV_STENCIL_WIDTH);
458 stringstream ss;
459 for (uint j = 0; j < L; ++j) {
460 ss<< ids[j] << " ";
461 }
462
463 // Insert pointers for neighbors of ids.front() and ids.back()
464 const auto* frontNbrPairs = mpiGrid.get_neighbors_of(ids[VLASOV_STENCIL_WIDTH], neighborhood);
465 const auto* backNbrPairs = mpiGrid.get_neighbors_of(ids[L-VLASOV_STENCIL_WIDTH-1], neighborhood);
466 // TODO cleanup
467 // Create list of unique distances in the negative direction from the first cell in pencil
468 std::set< int > distances;
469 for (const auto& nbrPair : *frontNbrPairs) {
470 if (nbrPair.second[dimension] < 0) {
471 // gather absolute distance values
472 distances.insert(-nbrPair.second[dimension]);
473 }
474 }
475 int iSrc = VLASOV_STENCIL_WIDTH - 1;
476 // Iterate through distances for VLASOV_STENCIL_WIDTH elements starting from the smallest distance.
477 for (auto it = distances.begin(); it != distances.end(); ++it) {
478 if (iSrc < 0) {
479 break; // found enough elements
480 }
481
482 // Collect all neighbors at distance *it to a vector
483 std::vector< CellID > neighbors;
484 for (const auto& nbrPair : *frontNbrPairs) {
485 int distanceInRefinedCells = -nbrPair.second[dimension];
486 if (distanceInRefinedCells == *it) {
487 neighbors.push_back(nbrPair.first);
488 }
489 }
490 // Get rid of duplicate neighbor cells at single distance
491 std::sort(neighbors.begin(), neighbors.end());
492 neighbors.erase(unique(neighbors.begin(), neighbors.end()), neighbors.end());
493
494 // Find source cells (VLASOV_STENCIL_WIDTH at each end)
495 size_t refLvl = mpiGrid.get_refinement_level(ids[VLASOV_STENCIL_WIDTH]);
496 size_t pathPos = 0;
497 if (path.size() > refLvl) {
498 pathPos = path[refLvl];
499 }
500 if (neighbors.size() == 1) {
501 if (ids[iSrc+1] == neighbors.at(0)) {
502 continue; // already found this cell for different distance
503 }
504 ids[iSrc--] = neighbors.at(0);
505 } else if ( pathPos < neighbors.size() ) {
506 if (ids[iSrc+1] == neighbors.at(pathPos)) {
507 continue; // already found this cell for different distance (should not happen)
508 }
509 ids[iSrc--] = neighbors.at(pathPos);
510 } else {
511 ss<<"error too few front neighbors for path! cellid "<<ids[VLASOV_STENCIL_WIDTH]<<
512 " Nsize "<<neighbors.size()<<" L "<<L<<" refLvl "<<refLvl<<" iSrc "<<iSrc<<
513 " pathsize "<<path.size()<<" path "<<path[refLvl]<<std::endl;
514 std::cerr<<ss.str();
515 }
516 }
517
518 distances.clear();
519 // Create list of unique distances in the positive direction from the last cell in pencil
520 for (const auto& nbrPair : *backNbrPairs) {
521 if (nbrPair.second[dimension] > 0) {
522 distances.insert(nbrPair.second[dimension]);
523 }
524 }
525
526 // Iterate through distances for VLASOV_STENCIL_WIDTH elements starting from the smallest distance.
527 // Distances are positive here so smallest distance has smallest value.
528 iSrc = L - VLASOV_STENCIL_WIDTH;
529 for (auto it = distances.begin(); it != distances.end(); ++it) {
530 if (iSrc >= (int)L) {
531 break; // Found enough cells
532 }
533
534 // Collect all neighbors at distance *it to a vector
535 std::vector< CellID > neighbors;
536 for (const auto& nbrPair : *backNbrPairs) {
537 int distanceInRefinedCells = nbrPair.second[dimension];
538 if (distanceInRefinedCells == *it) {
539 neighbors.push_back(nbrPair.first);
540 }
541 }
542 // Get rid of duplicate neighbor cells at single distance
543 std::sort(neighbors.begin(), neighbors.end());
544 neighbors.erase(unique(neighbors.begin(), neighbors.end()), neighbors.end());
545
546 size_t refLvl = mpiGrid.get_refinement_level(ids[L-VLASOV_STENCIL_WIDTH-1]);
547 size_t pathPos = 0;
548 if (path.size() > refLvl) {
549 pathPos = path[refLvl];
550 }
551 if (neighbors.size() == 1) {
552 if (ids[iSrc-1] == neighbors.at(0)) {
553 continue; // already found this cell for different distance
554 }
555 ids[iSrc++] = neighbors.at(0);
556 } else if ( pathPos < neighbors.size() ) {
557 if (ids[iSrc-1] == neighbors.at(pathPos)) {
558 continue; // already found this cell for different distance (should not happen)
559 }
560 ids[iSrc++] = neighbors.at(pathPos);
561 } else {
562 ss<<"error too few back neighbors for path! cellid "<<ids[L-VLASOV_STENCIL_WIDTH-1]<<
563 " Nsize "<<neighbors.size()<<" L "<<L<<" refLvl "<<refLvl<<" iSrc "<<iSrc<<
564 " pathsize "<<path.size()<<" path "<<path[refLvl]<<std::endl;
565 std::cerr<<ss.str();
566 }
567 }
568
569 /*loop to negative side and replace all invalid cells with the closest good cell*/
570 CellID lastGoodCell = ids[VLASOV_STENCIL_WIDTH];
571 for(int i = VLASOV_STENCIL_WIDTH - 1; i >= 0 ;--i){
572 bool isGood = false;
573 if (ids[i]!=0) {
574 if (mpiGrid[ids[i]] != NULL) {
575 if (mpiGrid[ids[i]]->sysBoundaryFlag == sysboundarytype::NOT_SYSBOUNDARY ||
576 (mpiGrid[ids[i]]->sysBoundaryFlag != sysboundarytype::NOT_SYSBOUNDARY && mpiGrid[ids[i]]->sysBoundaryFlag != sysboundarytype::DO_NOT_COMPUTE && mpiGrid[ids[i]]->sysBoundaryLayer == 1)
577 ) {
578 isGood = true;
579 }
580 }
581 }
582 if (!isGood) {
583 ids[i] = lastGoodCell;
584 } else {
585 lastGoodCell = ids[i];
586 }
587 }
588
589 /*loop to positive side and replace all invalid cells with the closest good cell*/
590 lastGoodCell = ids[L - VLASOV_STENCIL_WIDTH - 1];
591 for(int i = (int)L - VLASOV_STENCIL_WIDTH; i < (int)L; ++i){
592 bool isGood = false;
593 if (ids[i]!=0) {
594 if (mpiGrid[ids[i]] != NULL) {
595 if (mpiGrid[ids[i]]->sysBoundaryFlag == sysboundarytype::NOT_SYSBOUNDARY ||
596 (mpiGrid[ids[i]]->sysBoundaryFlag != sysboundarytype::NOT_SYSBOUNDARY && mpiGrid[ids[i]]->sysBoundaryFlag != sysboundarytype::DO_NOT_COMPUTE && mpiGrid[ids[i]]->sysBoundaryLayer == 1)
597 ) {
598 isGood = true;
599 }
600 }
601 }
602 if (!isGood) {
603 ids[i] = lastGoodCell;
604 } else {
605 lastGoodCell = ids[i];
606 }
607 }
608
609 // Loop over all cells and store widths in translation direction
610 for (int i = 0; i < (int)L; ++i) {
611 sourceDZ[i] = mpiGrid[ids[i]]->parameters[CellParams::DX+dimension];
612 }
613
614 // Loop over all cells and store pencil-to-cell cross-sectional area for target cells
615 for (int i = 0; i < (int)L; ++i) {
616 if ((i < VLASOV_STENCIL_WIDTH-1) || (i > (int)L-VLASOV_STENCIL_WIDTH)) {
617 // Source cell, not a target cell
618 targetRatios[i]=0.0;
619 continue;
620 }
622 if (!check_is_written_to(mpiGrid, ids[i], dimension)) {
623 targetRatios[i]=0.0;
624 continue;
625 }
626 }
627
628 if (ids[i]) {
629 const SpatialCell* tc = mpiGrid[ids[i]];
631 // areaRatio is the ratio of the cross-section of the spatial cell to the cross-section of the pencil.
632 const int diff = tc->SpatialCell::parameters[CellParams::REFINEMENT_LEVEL] - path.size();
633 if (diff>0) {
634 // Undefined behaviour! Cell is smaller than pencil (higher reflevel than path size)
635 std::cerr<<"Error in path size to cell size: " << __FILE__ << ":" << __LINE__ << std::endl;
636 targetRatios[i] = 0.0;
637 } else {
638 const int ratio = 1 << -diff;
639 const Realf Rratio = (Realf)ratio;
640 targetRatios[i] = 1.0 / (Rratio*Rratio);
641 }
642 } else { // Don't write to this cell
643 targetRatios[i] = 0.0;
644 }
645 } else { // Don't write to this cell
646 std::cerr<<"Found zero id in pencils!"<<std::endl;
647 targetRatios[i] = 0.0;
648 }
649 }
650}
651
652/* Select one nearest neighbor of a cell on the + side in a given dimension. If the neighbor
653 * has a higher level of refinement, a path variable is needed to make the selection.
654 * Returns INVALID_CELLID if the nearest neighbor is not local to this process.
655 * (or, if activated, not included in the translation list for ghost translation)
656 *
657 * @param grid DCCRG grid object
658 * @param id DCCRG cell id
659 * @param dimension spatial dimension
660 * @param path index of the desired face neighbor
661 * @return neighbor DCCRG cell id of the neighbor
662 */
663CellID selectPositiveNeighbor(const dccrg::Dccrg<SpatialCell,dccrg::Cartesian_Geometry> &grid,
664 const CellID id, const int dimension = 0, const uint path = 0) {
665 // If face neighbours are at a higher refinement level, only returns the one which
666 // which has a neighbor index matching the input path
667
668 //int neighborhood = getNeighborhood(dimension,1);
669 //const auto* nbrPairs = grid.get_neighbors_of(id, neighborhood);
670
671 vector < CellID > myNeighbors;
672 CellID neighbor = INVALID_CELLID;
673
674 // Iterate through neighbor ids in the positive direction of the chosen dimension,
675 // select the neighbor indicated by path, if it is local to this process.
676 for (const auto& [neighbor, dir] : grid.get_face_neighbors_of(id)) {
677 if (dir == ((int)dimension + 1)) {
678 myNeighbors.push_back(neighbor);
679 }
680 }
681 // TODO Verify: are neighbours always in the same order? Let's sort based
682 // on CellID to be sure.
683 std::sort(myNeighbors.begin(), myNeighbors.end());
684
685 if ( myNeighbors.size() == 0 ) {
686 return neighbor; // == INVALID_CELLID
687 }
688
689 int neighborIndex = 0;
690 if (myNeighbors.size() > 1) {
691 neighborIndex = path;
692 }
693
694 if (check_is_active(grid, myNeighbors[neighborIndex], dimension)) {
695 neighbor = myNeighbors[neighborIndex];
696 }
697
698 return neighbor;
699}
700
701/* Recursive function for building one-dimensional pencils to cover local DCCRG cells.
702 * Starts from a given seedID and proceeds finding the nearest neighbor in the given dimension
703 * and adding it to the pencil until no neighbors are found or an endId is met. When a higher
704 * refinement level (ie. multiple nearest neighbors) is met, the pencil splits into four
705 * copies to remain at a width of 1 cell. This is done by the function calling itself recursively
706 * and passing as inputs the cells added so far. The cell selected by each copy of the function
707 * at a split is stored in the path variable, the same path has to be followed if a refinement
708 * level is encoutered multiple times.
709 *
710 * @param [in] grid DCCRG grid object
711 * @param [out] pencils Pencil data struct
712 * @param [in] seedId DCCRG cell id where we start building the pencil.
713 * The pencil will continue in the + direction in the given dimension until an end condition is met
714 * @param [in] dimension Spatial dimension
715 * @param [in] path Integer value that determines which neighbor is added to the pencil when a higher refinement level is met
716 * @param [in] endIds Prescribed end conditions for the pencil. If any of these cell ids is about to be added to the pencil,
717 * the builder terminates.
718 */
719void buildPencilsWithNeighbors( const dccrg::Dccrg<SpatialCell,dccrg::Cartesian_Geometry> &grid,
720 setOfPencils &pencils, const CellID seedId,
721 vector<CellID> ids, const uint dimension,
722 vector<uint> path, const vector<CellID> &endIds) {
723
724 const bool debug = false;
725 CellID nextNeighbor;
726 CellID id = seedId;
727 int startingRefLvl = grid.get_refinement_level(id);
728 bool periodic = false;
729 // If this is a new pencil (instead of being a result of a pencil being split
730 if ( ids.size() == 0 ) {
731 ids.push_back(seedId);
732 }
733 // If the cell where we start is refined, we need to figure out which path
734 // to follow in future refined cells. This is a bit hacky but we have to
735 // use the order or the children of the parent cell to figure out which
736 // corner we are in.
737
738 std::array<double, 3> coordinates = grid.get_center(seedId);
739 int startingPathSize = path.size();
740
741 // Find the "pre-existing" path for new pencils starting at higher reflevels
742 if ( startingRefLvl > startingPathSize ) {
743 CellID myId = seedId;
744 for ( int i = path.size(); i < startingRefLvl; ++i) {
745 //CellID parentId = grid.mapping.get_parent(myId);
746 CellID parentId = grid.get_parent(myId);
747
748 auto myCoords = grid.get_center(myId);
749 auto parentCoords = grid.get_center(parentId);
750 int ix=0, iy=0;
751 switch(dimension) {
752 case 0:
753 ix = 1;
754 iy = 2;
755 break;
756 case 1:
757 ix = 0;
758 iy = 2;
759 break;
760 case 2:
761 ix = 0;
762 iy = 1;
763 break;
764 default:
765 cerr << __FILE__ << ":"<< __LINE__ << " Wrong dimension, abort"<<endl;
766 abort();
767 }
768 //int ix = (dimension + 1) % 3; // incorrect for DCCRG
769 //int iy = (dimension + 2) % 3;
770
771 int step = -1;
772
773 if (myCoords[ix] < parentCoords[ix] && myCoords[iy] < parentCoords[iy]) {
774 step = 0;
775 } else if (myCoords[ix] > parentCoords[ix] && myCoords[iy] < parentCoords[iy]) {
776 step = 1;
777 } else if (myCoords[ix] < parentCoords[ix] && myCoords[iy] > parentCoords[iy]) {
778 step = 2;
779 } else if (myCoords[ix] > parentCoords[ix] && myCoords[iy] > parentCoords[iy]) {
780 step = 3;
781 }
782
783 // path needs to end up in reflevel order, whereas this loop goes in reverse order
784 path.insert(path.begin(), step);
785 myId = parentId;
786 }
787 }
788
789 // Now start loop for gathering ids into pencil. Break when next found cell is rejected.
790 while (id != INVALID_CELLID) {
791
792 periodic = false;
793 bool neighborExists = false;
794 int refLvl = 0;
795
796 // Find the refinement level in the neighboring (local) cell. Check all possible neighbors
797 // in case some of them are remote.
798 for (int tmpPath = 0; tmpPath < 4; ++tmpPath) {
799 nextNeighbor = selectPositiveNeighbor(grid,id,dimension,tmpPath);
800 if (nextNeighbor != INVALID_CELLID) {
801 refLvl = max(refLvl,grid.get_refinement_level(nextNeighbor));
802 neighborExists = true;
803 }
804 }
805
806 // If there are no local acceptable neighbors, we can stop. This is not an error.
807 if (!neighborExists) {
808 break;
809 }
810
811 // Do we need to consider refinement?
812 if (refLvl > 0) {
813 // If we have encountered this refinement level before and stored
814 // the path this builder follows, we will just take the same path
815 // again.
816 if ( static_cast<int>(path.size()) >= refLvl ) {
817
818 if (debug) {
819 std::cout << "I am cell " << id << ". ";
820 std::cout << "I have seen refinement level " << refLvl << " before. Path is ";
821 for (auto k = path.begin(); k != path.end(); ++k)
822 std::cout << *k << " ";
823 std::cout << std::endl;
824 }
825
826 nextNeighbor = selectPositiveNeighbor(grid,id,dimension,path[refLvl - 1]);
827 if (nextNeighbor != INVALID_CELLID) {
828 coordinates = grid.get_center(nextNeighbor);
829 }
830 } else {
831 // We encounter a new refinement level.
832 if (debug) {
833 std::cout << "I am cell " << id << ". ";
834 std::cout << "I have NOT seen refinement level " << refLvl << " before. Path is ";
835 for (auto k = path.begin(); k != path.end(); ++k)
836 std::cout << *k << ' ';
837 std::cout << std::endl;
838 }
839
840 // Create a path through each neighbor cell
841 for ( uint newPath : {0,1,2,3} ) {
842 vector < uint > myPath = path;
843 // Extend the path to cover the new reflevel
844 myPath.push_back(newPath);
845 nextNeighbor = selectPositiveNeighbor(grid,id,dimension,newPath);
846
847 if ( newPath == 3 ) {
848 // This builder continues with neighbor 3 with an extended path
849 path = myPath;
850 if (nextNeighbor != INVALID_CELLID) {
851 coordinates = grid.get_center(nextNeighbor);
852 }
853 } else {
854 // Spawn recursive new builders for neighbors 0,1,2
855 buildPencilsWithNeighbors(grid,pencils,id,ids,dimension,myPath,endIds);
856 }
857 }
858 }
859 } // Closes if (refLvl == 0)
860
861 // If we found a neighbor, let's verify if it should be translated
862 if (nextNeighbor != INVALID_CELLID) {
863 if (debug) {
864 std::cout << " Next neighbor is " << nextNeighbor << "." << std::endl;
865 }
866 // Non-local, non-translated, and ids belonging to other pencils are not included
867 if ( std::any_of(endIds.begin(), endIds.end(), [nextNeighbor](uint i){return i == nextNeighbor;}) ||
868 !do_translate_cell(grid[nextNeighbor])) {
869 nextNeighbor = INVALID_CELLID;
870 } else {
871 // Yep, this goes in this pencil.
872 ids.push_back(nextNeighbor);
873 }
874 }
875
876 id = nextNeighbor;
877 } // Closes while loop - end of pencil reached.
878
879 // Get the x,y - coordinates of the pencil (in the direction perpendicular to the pencil)
880 double x,y;
881 int ix=0, iy=0;
882
883 switch(dimension) {
884 case 0:
885 ix = 1;
886 iy = 2;
887 break;
888 case 1:
889 ix = 0;
890 iy = 2;
891 break;
892 case 2:
893 ix = 0;
894 iy = 1;
895 break;
896 default:
897 cerr << __FILE__ << ":"<< __LINE__ << " Wrong dimension, abort"<<endl;
898 abort();
899 }
900 //ix = (dimension + 1) % 3; // incorrect for DCCRG
901 //iy = (dimension + 2) % 3;
902
903 x = coordinates[ix];
904 y = coordinates[iy];
905
906 pencils.addPencil(ids,x,y,periodic,path);
907 return;
908}
909
910/* Determine which cells in the local DCCRG mesh should be starting points for pencils.
911 * If a neighbor cell is non-local, across a periodic boundary, or in non-periodic boundary layer 1
912 * then we use this cell as a seed for pencils
913 *
914 * @param [in] mpiGrid DCCRG grid object
915 * @param [in] propagatedCells List of local cells that get propagated
916 * ie. not L2-boundary or DO_NOT_COMPUTE
917 * @param [in] dimension Spatial dimension
918 * @param [out] seedIds list of cell ids that will be starting points for pencils
919 */
920void getSeedIds(const dccrg::Dccrg<SpatialCell,dccrg::Cartesian_Geometry>& mpiGrid,
921 const vector<CellID> &propagatedCells,
922 const uint dimension,
923 vector<CellID> &seedIds) {
924
925 const bool debug = false;
926 int myRank;
927 if (debug) {
928 MPI_Comm_rank(MPI_COMM_WORLD,&myRank);
929 }
930
931 // These neighborhoods no longer include the AMR addition beyond the regular vlasov stencil
932 const int neighborhood = getNeighborhood(dimension,VLASOV_STENCIL_WIDTH);
933
934 #pragma omp parallel for
935 for (uint i=0; i<propagatedCells.size(); i++) {
936 const CellID celli = propagatedCells[i];
937
938 bool addToSeedIds = P::amrTransShortPencils;
939 if (addToSeedIds) {
940 #pragma omp critical
941 seedIds.push_back(celli);
942 continue;
943 }
944 auto myIndices = mpiGrid.mapping.get_indices(celli);
945 int myRefLevel;
946
947 /* -----------------------------------------
948 | A | | B | |_|_|_|_| | | C | |
949 | | | | | | | | | | | | |
950 -----------------------------------------
951 For optimal pencil generation, we need seedids at A, B, and C.
952 A Is triggered in the first if-clause. Pencils starting from B
953 will be split (but won't cause A to split), and pencils from
954 C will be able to remain again un-split. These checks need to be done
955 only if we aren't already at the maximum refinement level.
956
957 */
958
959 // First check negative face neighbors (A)
960 // Returns all neighbors as (id, direction-dimension) pair pointers.
961 for (const auto& [neighbor, dir] : mpiGrid.get_face_neighbors_of(celli) ) {
962 if ( dir == -((int)dimension + 1) ) {
963 // Check that the neighbor is not across a periodic boundary by calculating
964 // the distance in indices between this cell and its neighbor.
965 auto nbrIndices = mpiGrid.mapping.get_indices(neighbor);
966
967 // If a neighbor is non-local (or not ghost-translated), across a periodic boundary,
968 // or in non-periodic boundary layer >1 (non-translated cell)
969 // then we use the current cell as a seed for pencils
970 if ( (myIndices[dimension] < nbrIndices[dimension]) ||
971 !check_is_active(mpiGrid, neighbor, dimension) ||
972 !do_translate_cell(mpiGrid[neighbor]) )
973 {
974 addToSeedIds = true;
975 break;
976 }
977 }
978 } // finish check A
979 if ( addToSeedIds ) {
980 #pragma omp critical
981 seedIds.push_back(celli);
982 continue;
983 }
984 myRefLevel = mpiGrid.get_refinement_level(celli);
985 if (mpiGrid.get_maximum_refinement_level() == myRefLevel) {
986 continue;
987 }
988
989 /* Proceed with B, checking if the next positive neighbour has the same refinement level as ccell, but the
990 second neighbour a higher one. Iterate through positive distances for VLASOV_STENCIL_WIDTH elements
991 starting from the smallest distance. */
992
993 // Gather neighbours in neighbourhood stencil
994 const auto* nbrPairs = mpiGrid.get_neighbors_of(celli, neighborhood);
995 // Create list of unique neighbour distances in both directions (using ordered sets)
996 std::set< int > distancesplus;
997 std::set< int > distancesminus;
998 for (const auto& nbrPair : *nbrPairs) {
999 if (nbrPair.second[dimension] > 0) {
1000 distancesplus.insert(nbrPair.second[dimension]);
1001 }
1002 if (nbrPair.second[dimension] < 0) {
1003 // gather absolute distance values for correct order
1004 distancesminus.insert(-nbrPair.second[dimension]);
1005 }
1006 }
1007 int iSrc = VLASOV_STENCIL_WIDTH-1;
1008 for (auto it = distancesplus.begin(); it != distancesplus.end(); ++it) {
1009 if (iSrc < 0) {
1010 break; // found enough elements
1011 }
1012 for (const auto& nbrPair : *nbrPairs) {
1013 int distanceInRefinedCells = nbrPair.second[dimension];
1014 if (distanceInRefinedCells == *it) {
1015 // Break search if we are not at the final entry, and have different refinement level
1016 if (iSrc!=0 && mpiGrid.get_refinement_level(nbrPair.first)!=myRefLevel) {
1017 iSrc = -1;
1018 break;
1019 }
1020 // Flag as seed id if VLASOV_STENCIL_WIDTH positive neighbour is at higher refinement level
1021 if (iSrc==0 && mpiGrid.get_refinement_level(nbrPair.first)>myRefLevel) {
1022 addToSeedIds = true;
1023 break;
1024 }
1025 }
1026 }
1027 iSrc--;
1028 } // Finish B check
1029
1030 if (addToSeedIds) {
1031 #pragma omp critical
1032 seedIds.push_back(celli);
1033 continue;
1034 }
1035 /* Proceed with C, checking if the next two negative neighbours have the same refinement level as ccell, but the
1036 third neighbour a higher one. Iterate through negative distances for VLASOV_STENCIL_WIDTH+1 elements
1037 starting from the smallest distance. */
1038 iSrc = VLASOV_STENCIL_WIDTH;
1039 for (auto it = distancesminus.begin(); it != distancesminus.end(); ++it) {
1040 if (iSrc < 0) {
1041 break; // found enough elements
1042 }
1043 for (const auto& nbrPair : *nbrPairs) {
1044 int distanceInRefinedCells = -nbrPair.second[dimension];
1045 if (distanceInRefinedCells == *it) {
1046 // Break search if we are not at the final entry, and have different refinement level
1047 if (iSrc!=0 && mpiGrid.get_refinement_level(nbrPair.first)!=myRefLevel) {
1048 iSrc = -1;
1049 break;
1050 }
1051 // Flag as seed id if VLASOV_STENCIL_WIDTH+1 positive neighbour is at higher refinement level
1052 if (iSrc==0 && mpiGrid.get_refinement_level(nbrPair.first)>myRefLevel) {
1053 addToSeedIds = true;
1054 break;
1055 }
1056 }
1057 }
1058 iSrc--;
1059 } // Finish C check
1060
1061 if (addToSeedIds) {
1062 #pragma omp critical
1063 seedIds.push_back(celli);
1064 }
1065 }
1066
1067 if (debug) {
1068 cout << "Rank " << myRank << ", Seed ids are: ";
1069 for (const auto seedId : seedIds) {
1070 cout << seedId << " ";
1071 }
1072 cout << endl;
1073 }
1074}
1075
1076/* Check whether the ghost cells around the pencil contain higher refinement than the pencil does.
1077 * If they do, the pencil must be split to match the finest refined ghost cell.
1078 *
1079 * @param mpiGrid DCCRG grid object
1080 * @param pencils Pencil data struct
1081 * @param dimension Spatial dimension
1082 */
1083void check_ghost_cells(const dccrg::Dccrg<SpatialCell,dccrg::Cartesian_Geometry>& mpiGrid,
1084 setOfPencils& pencils,
1085 const uint dimension) {
1086
1087 const bool debug = false;
1088 int neighborhood = getNeighborhood(dimension,VLASOV_STENCIL_WIDTH);
1089
1090 int myRank;
1091 if (debug) {
1092 MPI_Comm_rank(MPI_COMM_WORLD,&myRank);
1093 }
1094
1095 std::vector<CellID> pencilIdsToSplit;
1096
1097 #pragma omp parallel for
1098 for (uint pencili = 0; pencili < pencils.N; ++pencili) {
1099
1100 // This check isn't in use at the moment, because no pencils are ever flagged periodic..
1101 if (pencils.periodic[pencili]) {
1102 continue;
1103 }
1104
1105 // This returns a list of only the central cells, excluding the stencil
1106 auto ids = pencils.getIds(pencili);
1107
1108 // It is possible that the pencil has already been refined by the pencil building algorithm
1109 // and is on a higher refinement level than the refinement level of any of the cells it contains
1110 // due to e.g. process boundaries. Example: x is the pencil, N is non-local cells.
1111 /* -----------------------------------------
1112 | | | | |_|_|_|_| N | N | N | N |
1113 |xxx|xxx|xxx|xxx|N|N|N|N| | | | |
1114 -----------------------------------------
1115 */
1116 int maxPencilRefLvl = pencils.path[pencili].size();
1117 int maxNbrRefLvl = 0;
1118
1119 const auto* frontNeighbors = mpiGrid.get_neighbors_of(ids.front(),neighborhood);
1120 const auto* backNeighbors = mpiGrid.get_neighbors_of(ids.back(),neighborhood);
1121
1122 // Create list of unique distances in the negative direction from the first cell in pencil
1123 std::set< int > distances; // is sorted
1124 for (const auto& nbrPair : *frontNeighbors) {
1125 if (nbrPair.second[dimension] < 0) {
1126 // gather absolute distance values
1127 distances.insert(-nbrPair.second[dimension]);
1128 }
1129 }
1130 int foundcells = 0;
1131 CellID lastcell = INVALID_CELLID;
1132 // Iterate through distances for VLASOV_STENCIL_WIDTH elements starting from the smallest distance.
1133 for (auto it = distances.begin(); it != distances.end(); ++it) {
1134 for (const auto& nbrPair : *frontNeighbors) {
1135 if (nbrPair.first==lastcell) {
1136 continue;
1137 }
1138 int distanceInRefinedCells = -nbrPair.second[dimension];
1139 if (distanceInRefinedCells == *it) {
1140 maxNbrRefLvl = max(maxNbrRefLvl,mpiGrid.get_refinement_level(nbrPair.first));
1141 lastcell = nbrPair.first;
1142 foundcells++;
1143 continue;
1144 }
1145 }
1146 if (foundcells >= VLASOV_STENCIL_WIDTH) {
1147 break; // checked enough distances
1148 }
1149 }
1150
1151 // Create list of unique distances in the positive direction from the last cell in pencil
1152 distances.clear();
1153 for (const auto& nbrPair : *backNeighbors) {
1154 if (nbrPair.second[dimension] > 0) {
1155 distances.insert(nbrPair.second[dimension]);
1156 }
1157 }
1158 foundcells = 0;
1159 lastcell = INVALID_CELLID;
1160 for (auto it = distances.begin(); it != distances.end(); ++it) {
1161 for (const auto& nbrPair : *backNeighbors) {
1162 if (nbrPair.first==lastcell) {
1163 continue;
1164 }
1165 int distanceInRefinedCells = nbrPair.second[dimension];
1166 if (distanceInRefinedCells == *it) {
1167 maxNbrRefLvl = max(maxNbrRefLvl,mpiGrid.get_refinement_level(nbrPair.first));
1168 lastcell = nbrPair.first;
1169 foundcells++;
1170 continue;
1171 }
1172 }
1173 if (foundcells >= VLASOV_STENCIL_WIDTH) {
1174 break; // checked enough distances
1175 }
1176 }
1177
1178 if (maxNbrRefLvl > maxPencilRefLvl) {
1179 if (debug) {
1180 std::cout << "I am rank " << myRank << ". ";
1181 std::cout << "Found refinement level " << maxNbrRefLvl << " in one of the ghost cells of pencil " << pencili << ". ";
1182 std::cout << "Highest refinement level in this pencil is " << maxPencilRefLvl;
1183 std::cout << ". Splitting pencil " << pencili << endl;
1184 }
1185 // Let's avoid modifying pencils while we are looping over it. Write down the indices of pencils
1186 // that need to be split and split them later.
1187 #pragma omp critical
1188 {
1189 pencilIdsToSplit.push_back(pencili);
1190 }
1191 }
1192 }
1193
1194 // No threading here! Splitting requires knowledge of all
1195 // already existing pencils.
1196 for (const auto pencili: pencilIdsToSplit) {
1197
1198 Real dx = 0.0;
1199 Real dy = 0.0;
1200 auto ids = pencils.getIds(pencili);
1201 switch(dimension) {
1202 case 0:
1203 dx = mpiGrid[ids[0]]->SpatialCell::parameters[CellParams::DY];
1204 dy = mpiGrid[ids[0]]->SpatialCell::parameters[CellParams::DZ];
1205 break;
1206 case 1:
1207 dx = mpiGrid[ids[0]]->SpatialCell::parameters[CellParams::DX];
1208 dy = mpiGrid[ids[0]]->SpatialCell::parameters[CellParams::DZ];
1209 break;
1210 case 2:
1211 dx = mpiGrid[ids[0]]->SpatialCell::parameters[CellParams::DX];
1212 dy = mpiGrid[ids[0]]->SpatialCell::parameters[CellParams::DY];
1213 break;
1214 }
1215
1216 // WARNING threading inside this function
1217 pencils.split(pencili,dx,dy);
1218
1219 }
1220}
1221
1222/* Debugging function, prints the list of cells in each pencil
1223 *
1224 * @param pencils Pencil data struct
1225 * @param dimension Spatial dimension
1226 * @param myRank MPI rank
1227 */
1228void printPencilsFunc(const setOfPencils& pencils, const uint dimension, const int myRank,const dccrg::Dccrg<SpatialCell,dccrg::Cartesian_Geometry>& mpiGrid) {
1229
1230// Print out ids of pencils (if needed for debugging)
1231 uint ibeg = 0;
1232 uint iend = 0;
1233 stringstream ss;
1234 ss << "I am rank " << myRank << ", I have " << pencils.N << " pencils along dimension " << dimension << ":\n";
1235 MPI_Barrier(MPI_COMM_WORLD);
1236 if (myRank == MASTER_RANK) {
1237 ss << "(D=DO_NOT_COMPUTE, S=Sysboundary L2, L=Sysboundary L1, N=Non-sysboundary L2, G=Ghost cell)" << std::endl;
1238 ss << "t, N, mpirank, dimension, length (x, y): indices {path} DZs AreaRatios" << std::endl;
1239 }
1240 MPI_Barrier(MPI_COMM_WORLD);
1241 ss << "----------------------------------------------------------------------" << std::endl;
1242 for (uint i = 0; i < pencils.N; i++) {
1243 const uint L = pencils.lengthOfPencils[i];
1244 iend = ibeg + L;
1245 ss << P::t << ", ";
1246 ss << i << ", ";
1247 ss << myRank << ", ";
1248 ss << dimension << ", ";
1249 ss << L << ", ";
1250 ss << "(" << pencils.x[i] << ", " << pencils.y[i] << "): ";
1251 for (auto j = pencils.ids.begin() + ibeg; j != pencils.ids.begin() + iend; ++j) {
1252 ss << *j;
1253 if (*j && mpiGrid[*j]) {
1254 SpatialCell* c = mpiGrid[*j];
1255 if (c->sysBoundaryFlag == sysboundarytype::DO_NOT_COMPUTE) ss<<"D";
1256 if (c->sysBoundaryLayer != 1 && c->sysBoundaryFlag != sysboundarytype::NOT_SYSBOUNDARY) ss<<"S";
1257 if (c->sysBoundaryLayer == 1 && c->sysBoundaryFlag != sysboundarytype::NOT_SYSBOUNDARY) ss<<"L";
1258 if (c->sysBoundaryLayer == 2 && c->sysBoundaryFlag == sysboundarytype::NOT_SYSBOUNDARY) ss<<"N";
1259 if (!mpiGrid.is_local(*j)) ss<<"G";
1260 }
1261 ss<< " ";
1262 }
1263
1264 ss << "{";
1265 for (auto step : pencils.path[i]) {
1266 ss << step << ", ";
1267 }
1268 ss << "}";
1269
1270 ss << "source DZs: ";
1271 for (auto j = pencils.sourceDZ.begin() + ibeg; j != pencils.sourceDZ.begin() + iend; ++j) {
1272 ss << *j << " ";
1273 }
1274
1275 ss << "target Ratios: ";
1276 for (auto j = pencils.targetRatios.begin() + ibeg; j != pencils.targetRatios.begin() + iend; ++j) {
1277 ss << *j << " ";
1278 }
1279
1280 ibeg = iend;
1281 ss << "\n";
1282 }
1283
1284 for (const auto& [bin, pencilsInBin] : pencils.pencilsInBin) {
1285 const auto& cells = pencils.targetCellsInBin.at(bin);
1286 std::set<uint64_t> collisions;
1287
1288 ss << "Bin " << bin << " pencils: ";
1289 if (pencilsInBin.empty()) {
1290 ss << "EMPTY ";
1291 }
1292
1293 for (auto pencil : pencilsInBin) {
1294 ss << pencil << " ";
1295 }
1296
1297 ss << "\n";
1298
1299 ss << "Bin " << bin << " cells: ";
1300 if (cells.empty()) {
1301 ss << "EMPTY ";
1302 }
1303
1304 for (auto id : cells) {
1305 ss << id << " ";
1306 for (auto [bin2, cells2] : pencils.targetCellsInBin) {
1307 if (bin != bin2 && cells2.contains(id)) {
1308 collisions.insert(bin2);
1309 }
1310 }
1311 }
1312
1313 ss << "\n";
1314
1315 if (collisions.empty()) {
1316 ss << "No collisions";
1317 } else {
1318 ss << "COLLISIONS WITH: ";
1319 for (auto j : collisions) {
1320 ss << j << " ";
1321 }
1322 }
1323 ss << std::endl;
1324 }
1325
1326 std::cout<<std::flush;
1327 MPI_Barrier(MPI_COMM_WORLD);
1328 std::cout<<ss.str();
1329 MPI_Barrier(MPI_COMM_WORLD);
1330 if (myRank == MASTER_RANK) {
1331 std::cout << "-----------------------------------------------------------------" << std::flush << std::endl;
1332 }
1333}
1334
1335/* Wrapper function for calling seed ID selection and pencil generation, for all dimensions.
1336 * Includes threading and gathering of pencils into thread-containers.
1337 *
1338 * @param [in] mpiGrid DCCRG grid object
1339 */
1340void prepareSeedIdsAndPencils(const dccrg::Dccrg<SpatialCell,dccrg::Cartesian_Geometry>& mpiGrid) {
1341 phiprof::Timer timer {"GetSeedIdsAndBuildPencils"};
1342 // Remove all old pencils now
1343 for (int dimension=0; dimension<3; dimension++) {
1344 DimensionPencils[dimension].removeAllPencils();
1345 }
1346 for (int dimension=0; dimension<3; dimension++) {
1347 prepareSeedIdsAndPencils(mpiGrid, dimension);
1348 }
1349}
1350
1351/* Wrapper function for calling seed ID selection and pencil generation, per dimension.
1352 * Includes threading and gathering of pencils into thread-containers.
1353 *
1354 * @param [in] mpiGrid DCCRG grid object
1355 * @param [in] dimension Spatial dimension
1356 */
1357void prepareSeedIdsAndPencils(const dccrg::Dccrg<SpatialCell,dccrg::Cartesian_Geometry>& mpiGrid,
1358 const uint dimension) {
1359
1360 // Optional heavy printouts for debugging
1361 const bool printPencils = false;
1362 const bool printSeeds = false;
1363 int myRank, mpi_size;
1364 if (printPencils || printSeeds) {
1365 MPI_Comm_rank(MPI_COMM_WORLD,&myRank);
1366 MPI_Comm_size(MPI_COMM_WORLD, &mpi_size);
1367 }
1368
1369 switch (dimension) {
1370 case 0:
1371 if (P::xcells_ini == 1) {
1372 return;
1373 }
1374 break;
1375 case 1:
1376 if (P::ycells_ini == 1) {
1377 return;
1378 }
1379 break;
1380 case 2:
1381 if (P::zcells_ini == 1) {
1382 return;
1383 }
1384 break;
1385 default:
1386 std::cerr<<"Error in dimension: __FILE__:__LINE__"<<std::endl;
1387 abort();
1388 }
1389 const vector<CellID>& localCells = getLocalCells();
1390 vector<CellID> propagatedCells;
1391 // Figure out which spatial cells are translated,
1392 // result independent of particle species.
1394 // Sets already include check for do_translate_cell
1395 switch (dimension) {
1396 case 0:
1397 propagatedCells.assign(ghostTranslate_active_x.begin(),ghostTranslate_active_x.end());
1398 break;
1399 case 1:
1400 propagatedCells.assign(ghostTranslate_active_y.begin(),ghostTranslate_active_y.end());
1401 break;
1402 case 2:
1403 propagatedCells.assign(ghostTranslate_active_z.begin(),ghostTranslate_active_z.end());
1404 break;
1405 default:
1406 std::cerr<<"Error in dimension: __FILE__:__LINE__"<<std::endl;
1407 abort();
1408 }
1409 } else {
1410 for (size_t c=0; c<localCells.size(); ++c) {
1411 if (do_translate_cell(mpiGrid[localCells[c]])) {
1412 propagatedCells.push_back(localCells[c]);
1413 }
1414 }
1415 }
1416
1417 phiprof::Timer getSeedIdsTimer {"getSeedIds"};
1418 vector<CellID> seedIds;
1419 getSeedIds(mpiGrid, propagatedCells, dimension, seedIds);
1420 getSeedIdsTimer.stop();
1421 if (printSeeds) {
1422 for (int rank=0; rank<mpi_size; ++rank) {
1423 MPI_Barrier(MPI_COMM_WORLD);
1424 if (rank!=myRank) {
1425 continue;
1426 }
1427 stringstream ss;
1428 ss<<"Task "<<myRank<<" Dimension "<<dimension<<" Seed Ids (D=DO_NOT_COMPUTE, S=Sysboundary L2, L=Sysboundary L1, N=Non-sysboundary L2, G=Ghost cell)"<<std::endl<<std::endl;
1429 for (uint i = 0; i < seedIds.size(); i++) {
1430 ss << seedIds.at(i);
1431 if (seedIds.at(i) && mpiGrid[seedIds.at(i)]) {
1432 SpatialCell* c = mpiGrid[seedIds.at(i)];
1433 if (c->sysBoundaryFlag == sysboundarytype::DO_NOT_COMPUTE) ss<<"D";
1434 if (c->sysBoundaryLayer != 1 && c->sysBoundaryFlag != sysboundarytype::NOT_SYSBOUNDARY) ss<<"S";
1435 if (c->sysBoundaryLayer == 1 && c->sysBoundaryFlag != sysboundarytype::NOT_SYSBOUNDARY) ss<<"L";
1436 if (c->sysBoundaryLayer == 2 && c->sysBoundaryFlag == sysboundarytype::NOT_SYSBOUNDARY) ss<<"N";
1437 if (!mpiGrid.is_local(seedIds.at(i))) ss<<"G";
1438 }
1439 ss<<" ";
1440 }
1441 ss<<std::endl<<std::endl;
1442 std::cerr<<ss.str();
1443 }
1444 }
1445
1446 phiprof::Timer buildPencilsTimer {"buildPencils"};
1447
1448 #pragma omp parallel
1449 {
1450 // Empty vectors for internal use of buildPencilsWithNeighbors. Could be default values but
1451 // default vectors are complicated. Should overload buildPencilsWithNeighbors like suggested here
1452 // https://stackoverflow.com/questions/3147274/c-default-argument-for-vectorint
1453 std::vector<CellID> ids;
1454 vector<uint> path;
1455 // thread-internal pencil set to be accumulated at the end
1456 setOfPencils thread_pencils;
1457 // iterators used in the accumulation
1458 std::vector<CellID>::iterator ibeg, iend;
1459
1460 #pragma omp for schedule(guided,8)
1461 for (uint i=0; i<seedIds.size(); i++) {
1462 cuint seedId = seedIds[i];
1463 // Construct pencils from the seedIds into a set of pencils.
1464 buildPencilsWithNeighbors(mpiGrid, thread_pencils, seedId, ids, dimension, path, seedIds);
1465 }
1466
1467 // accumulate thread results in global set of pencils
1468 #pragma omp critical
1469 {
1470 for (uint i=0; i<thread_pencils.N; i++) {
1471 // Use vector range constructor
1472 ibeg = thread_pencils.ids.begin() + thread_pencils.idsStart[i];
1473 iend = ibeg + thread_pencils.lengthOfPencils[i];
1474 std::vector<CellID> pencilIds(ibeg, iend);
1475 DimensionPencils[dimension].addPencil(pencilIds,thread_pencils.x[i],thread_pencils.y[i],thread_pencils.periodic[i],thread_pencils.path[i]);
1476 }
1477 }
1478 }
1479
1480 phiprof::Timer checkGhostCellsTimer {"check_ghost_cells"};
1481 // Check refinement of two ghost cells on each end of each pencil
1482 // in case pencil needs to be split.
1483 // This function contains threading.
1484 check_ghost_cells(mpiGrid,DimensionPencils[dimension],dimension);
1485 checkGhostCellsTimer.stop();
1486
1487 phiprof::Timer findSourceRatiosTimer {"Find_source_cells_ratios_dz"};
1488 // Compute also the stencil around the pencil (source cells), and
1489 // Store source cell widths and target cell contribution ratios.
1490 #pragma omp parallel for schedule(guided)
1491 for (uint i=0; i<DimensionPencils[dimension].N; ++i) {
1492 const uint L = DimensionPencils[dimension].lengthOfPencils[i];
1493 CellID *pencilIds = DimensionPencils[dimension].ids.data() + DimensionPencils[dimension].idsStart[i];
1494 Realf* pencilDZ = DimensionPencils[dimension].sourceDZ.data() + DimensionPencils[dimension].idsStart[i];
1495 Realf* pencilAreaRatio = DimensionPencils[dimension].targetRatios.data() + DimensionPencils[dimension].idsStart[i];
1496 computeSpatialSourceCellsForPencil(mpiGrid,pencilIds,L,dimension,DimensionPencils[dimension].path[i],pencilDZ,pencilAreaRatio);
1497 }
1498 findSourceRatiosTimer.stop();
1499
1500 // ****************************************************************************
1501
1502 phiprof::Timer binPencilsTimer {"bin_pencils"};
1503 DimensionPencils[dimension].binPencils();
1504 binPencilsTimer.stop();
1505
1506 if (printPencils) {
1507 for (int rank=0; rank<mpi_size; ++rank) {
1508 MPI_Barrier(MPI_COMM_WORLD);
1509 if (rank!=myRank) {
1510 continue;
1511 }
1512 printPencilsFunc(DimensionPencils[dimension],dimension,myRank,mpiGrid);
1513 }
1514 }
1515 buildPencilsTimer.stop();
1516
1517 //GPUTODO: move gpu buffers and their upload to separate gpu_trans_pencils .hpp and .cpp files
1518 #ifdef USE_GPU
1519 // Update GPU allocations
1520 const uint thisN = DimensionPencils[dimension].N;
1521 const uint thisSum = DimensionPencils[dimension].sumOfLengths;
1522
1523 gpuMemoryManager.createPointer(DimensionPencils[dimension].gpu_lengthOfPencils);
1524 gpuMemoryManager.createPointer(DimensionPencils[dimension].gpu_idsStart);
1525 gpuMemoryManager.createPointer(DimensionPencils[dimension].gpu_sourceDZ);
1526 gpuMemoryManager.createPointer(DimensionPencils[dimension].gpu_targetRatios);
1527
1528 gpuMemoryManager.allocate(DimensionPencils[dimension].gpu_lengthOfPencils, thisN*sizeof(uint));
1529 gpuMemoryManager.allocate(DimensionPencils[dimension].gpu_idsStart, thisN*sizeof(uint));
1530 gpuMemoryManager.allocate(DimensionPencils[dimension].gpu_sourceDZ, thisSum*sizeof(Realf));
1531 gpuMemoryManager.allocate(DimensionPencils[dimension].gpu_targetRatios, thisSum*sizeof(Realf));
1532
1533 // Copy data over
1534 CHK_ERR( gpuMemcpy(gpuMemoryManager.getPointer<uint>(DimensionPencils[dimension].gpu_lengthOfPencils),
1535 DimensionPencils[dimension].lengthOfPencils.data(), thisN*sizeof(uint), gpuMemcpyHostToDevice) );
1536 CHK_ERR( gpuMemcpy(gpuMemoryManager.getPointer<uint>(DimensionPencils[dimension].gpu_idsStart),
1537 DimensionPencils[dimension].idsStart.data(), thisN*sizeof(uint), gpuMemcpyHostToDevice) );
1538 CHK_ERR( gpuMemcpy(gpuMemoryManager.getPointer<Realf>(DimensionPencils[dimension].gpu_sourceDZ),
1539 DimensionPencils[dimension].sourceDZ.data(), thisSum*sizeof(Realf), gpuMemcpyHostToDevice) );
1540 CHK_ERR( gpuMemcpy(gpuMemoryManager.getPointer<Realf>(DimensionPencils[dimension].gpu_targetRatios),
1541 DimensionPencils[dimension].targetRatios.data(), thisSum*sizeof(Realf), gpuMemcpyHostToDevice) );
1542 #endif
1543
1544}
for i
Definition Dispersion.m:24
dx
Definition Dispersion.m:38
Constants c
Definition Dispersion.m:45
#define gpuMemcpyHostToDevice
#define CHK_ERR(err)
#define gpuMemcpy
size_t size(const uint popID) const
const std::vector< CellID > & getLocalCells()
Definition main.cpp:39
#define MASTER_RANK
Definition common.h:67
void buildPencilsWithNeighbors(const dccrg::Dccrg< SpatialCell, dccrg::Cartesian_Geometry > &grid, setOfPencils &pencils, const CellID seedId, vector< CellID > ids, const uint dimension, vector< uint > path, const vector< CellID > &endIds)
std::unordered_set< CellID > ghostTranslate_sources_x
void prepareGhostTranslationCellLists(const dccrg::Dccrg< SpatialCell, dccrg::Cartesian_Geometry > &mpiGrid, const vector< CellID > &localPropagatedCells)
void prepareSeedIdsAndPencils(const dccrg::Dccrg< SpatialCell, dccrg::Cartesian_Geometry > &mpiGrid)
void getSeedIds(const dccrg::Dccrg< SpatialCell, dccrg::Cartesian_Geometry > &mpiGrid, const vector< CellID > &propagatedCells, const uint dimension, vector< CellID > &seedIds)
std::unordered_set< CellID > ghostTranslate_sources_z
int getNeighborhood(const uint dimension, const uint stencil)
std::unordered_set< CellID > ghostTranslate_active_z
void printPencilsFunc(const setOfPencils &pencils, const uint dimension, const int myRank, const dccrg::Dccrg< SpatialCell, dccrg::Cartesian_Geometry > &mpiGrid)
std::array< setOfPencils, 3 > DimensionPencils
bool check_is_written_to(const dccrg::Dccrg< SpatialCell, dccrg::Cartesian_Geometry > &mpiGrid, const CellID cid, const int dimension)
std::unordered_set< CellID > ghostTranslate_active_x
CellID selectPositiveNeighbor(const dccrg::Dccrg< SpatialCell, dccrg::Cartesian_Geometry > &grid, const CellID id, const int dimension=0, const uint path=0)
bool check_is_active(const dccrg::Dccrg< SpatialCell, dccrg::Cartesian_Geometry > &mpiGrid, CellID cid, int dimension)
std::unordered_set< CellID > ghostTranslate_sources_y
void computeSpatialSourceCellsForPencil(const dccrg::Dccrg< SpatialCell, dccrg::Cartesian_Geometry > &mpiGrid, CellID *ids, const uint L, const uint dimension, std::vector< uint > path, Realf *sourceDZ, Realf *targetRatios)
void findNeighborhoodCells(const dccrg::Dccrg< SpatialCell, dccrg::Cartesian_Geometry > &mpiGrid, const CellID startingCellID, const uint dimension, const uint searchLength, std::vector< CellID > &foundCells)
std::unordered_set< CellID > ghostTranslate_active_y
void check_ghost_cells(const dccrg::Dccrg< SpatialCell, dccrg::Cartesian_Geometry > &mpiGrid, setOfPencils &pencils, const uint dimension)
bool do_translate_cell(const SpatialCell *const SC)
const uint32_t cuint
Definition definitions.h:50
float Real
Definition definitions.h:41
uint64_t CellID
Definition definitions.h:54
float Realf
Definition definitions.h:33
GPUMemoryManager gpuMemoryManager
Definition gpu_base.cpp:64
int myRank
Definition gpu_base.cpp:48
Logger logFile
Definition main.cpp:25
const int j
const int k
__global__ void const Realf const uint *__restrict__ const uint *__restrict__ const vmesh::GlobalID *__restrict__ const uint const uint const uint const Realf const vmesh::VelocityMesh *__restrict__ const vmesh::VelocityBlockContainer Realf Realf const Realf *__restrict__ pencilDZ
@ REFINEMENT_LEVEL
Definition common.h:203
@ VLASOV_SOLVER_Z
Definition common.h:80
@ VLASOV_SOLVER_TARGET_X
Definition common.h:81
@ VLASOV_SOLVER_X
Definition common.h:78
@ VLASOV_SOLVER_TARGET_Z
Definition common.h:83
@ VLASOV_SOLVER_Y_GHOST
Definition common.h:96
@ VLASOV_SOLVER_TARGET_Y
Definition common.h:82
@ VLASOV_SOLVER_Y
Definition common.h:79
@ VLASOV_SOLVER_X_GHOST
Definition common.h:95
@ VLASOV_SOLVER_Z_GHOST
Definition common.h:97
const uint64_t INVALID_CELLID
Definition parameters.h:35
static bool amrTransShortPencils
Definition parameters.h:234
static uint zcells_ini
Definition parameters.h:50
static bool vlasovSolverGhostTranslate
Definition parameters.h:63
static uint ycells_ini
Definition parameters.h:49
static uint xcells_ini
Definition parameters.h:48
static Real t
Definition parameters.h:52
static uint tstep
Definition parameters.h:73
static uint vlasovSolverGhostTranslateExtent
Definition parameters.h:64
std::vector< bool > periodic
std::vector< Real > x
Definition grid_test.cpp:32
std::vector< Real > y
Definition grid_test.cpp:32
std::vector< CellID > ids
Definition grid_test.cpp:31
std::vector< uint > idsStart
std::vector< uint > lengthOfPencils
Definition grid_test.cpp:30
std::vector< std::vector< uint > > path
static ARCH_HOSTDEV VecSimple< T > max(VecSimple< T > const &l, VecSimple< T > const &r)