MAIA bb96820c
Multiphysics at AIA
Loading...
Searching...
No Matches
cartesiangrid.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The m-AIA AUTHORS
2//
3// This file is part of m-AIA (https://git.rwth-aachen.de/aia/m-AIA/m-AIA)
4//
5// SPDX-License-Identifier: LGPL-3.0-only
6
7#include "cartesiangrid.h"
8
9#include <algorithm>
10#include <cmath>
11#include <limits>
12#include <set>
13#include <stack>
14#include <sys/stat.h>
15#include <unordered_map>
16#include "COMM/mpiexchange.h"
17#include "COMM/mpioverride.h"
18#include "IO/parallelio.h"
19#include "UTIL/hilbert.h"
20#include "UTIL/maiamath.h"
21#include "cartesiangridio.h"
22#include "globals.h"
23#include "partition.h"
24#include "typetraits.h"
25
26using namespace std;
27using namespace maia;
28
29
30template <MInt nDim>
31CartesianGrid<nDim>::CartesianGrid(MInt maxCells, const MFloat* const bBox, const MPI_Comm comm,
32 const MString& fileName)
33 : m_maxNoCells(maxCells), m_mpiComm(comm), m_paraViewPlugin((MBool)!fileName.empty()), m_maxNoNghbrs(m_noDirs) {
34 TRACE();
35
36 if(paraViewPlugin()) {
37 cerr << "Initialising MAIA for the ParaView-Plugin" << endl;
38 }
39
40
41 // Initialize bounding box (only makes sense if multi-solver is disabled)
42 if(!g_multiSolverGrid || m_paraViewPlugin) {
43 copy_n(bBox, 2 * nDim, &m_boundingBox[0]);
44 // Remove the following line once transition to new grid format is complete
45 // (see also setupWindowHaloCellConnectivity() -> moved to gridio::loadGrid())
46 copy_n(bBox, 2 * nDim, &m_boundingBoxBackup[0]);
47 }
48
49 // Determine domainId and number of domains
50 MPI_Comm_rank(mpiComm(), &m_domainId);
51 MPI_Comm_size(mpiComm(), &m_noDomains);
52
53 const MLong oldAllocatedBytes = allocatedBytes();
54 NEW_TIMER_GROUP(gridTimer, "Cartesian Grid");
55 NEW_TIMER(timertotal, "Total", gridTimer);
56 NEW_SUB_TIMER(timerAllocProp, "allocProperties", timertotal);
57 NEW_SUB_TIMER(timerLoadGridPar, "loadGrid", timertotal);
58 NEW_SUB_TIMER(timerCreateMB, "window/halo generation", timertotal);
59
60 RECORD_TIMER_START(timertotal);
61 RECORD_TIMER_START(timerAllocProp);
62
63 if(!m_paraViewPlugin) {
64 m_partitionCellOffspringThreshold = 50000;
76 if(Context::propertyExists("minCellMaxSize")) {
77 mTerm(1, AT_, "Error: Property minCellMaxSize is deprecated. Please rename to partitionCellOffspringThreshold.");
78 } else if(Context::propertyExists("partitionCellMaxNoOffspring")) {
79 if(domainId() == 0)
80 cerr << "Warning: Property partitionCellMaxNoOffspring is deprecated. Please rename to "
81 "partitionCellOffspringThreshold."
82 << endl;
83 m_partitionCellOffspringThreshold =
84 Context::getBasicProperty<MInt>("partitionCellMaxNoOffspring", AT_, &m_partitionCellOffspringThreshold);
85 } else {
86 m_partitionCellOffspringThreshold =
87 Context::getBasicProperty<MInt>("partitionCellOffspringThreshold", AT_, &m_partitionCellOffspringThreshold);
88 }
89
90 m_partitionCellWorkloadThreshold = 50000.;
102 m_partitionCellWorkloadThreshold =
103 Context::getBasicProperty<MFloat>("partitionCellWorkloadThreshold", AT_, &m_partitionCellWorkloadThreshold);
104
105 m_maxUniformRefinementLevel = -1;
106 if(Context::propertyExists("maxGeometricalRfnLvl")) {
107 m_maxUniformRefinementLevel = Context::getBasicProperty<MInt>("maxGeometricalRfnLvl", AT_);
108 if(domainId() == 0)
109 cerr << "Warning: Property maxGeometricalRfnLvl is deprecated and should be provided as "
110 "maxUniformRefinementLevel by the grid file."
111 << endl;
112 }
113
114 m_maxRfnmntLvl = Context::getBasicProperty<MInt>("maxRfnmntLvl", AT_);
115 if(m_maxRfnmntLvl > 63) {
116 mTerm(1, AT_,
117 "Error: m_maxRfnmntLvl > 31 not supported. Long bitshift from FPOW2() only provides 63 correct entries.");
118 }
119
120 m_allowInterfaceRefinement = false;
121 m_allowInterfaceRefinement =
122 Context::getBasicProperty<MBool>("allowInterfaceRefinement", AT_, &m_allowInterfaceRefinement);
123
124 m_cutOff = false;
125 m_cutOff = (MBool)Context::getBasicProperty<MInt>("cutOff", AT_, &m_cutOff);
126
127 if(Context::propertyExists("cutOffDirections")) {
128 m_cutOff = true;
129 }
140 m_newMinLevel = -1;
141 m_newMinLevel = Context::getBasicProperty<MInt>("newMinLevel", AT_, &m_newMinLevel);
142
143 m_noPeriodicCartesianDirs = 0;
144 m_periodicCartesianDir.fill(0.0);
145
157 if(Context::propertyExists("periodicCartesianDir")) {
158 for(MInt dir = 0; dir < nDim; dir++) {
159 m_periodicCartesianDir[dir] =
160 Context::getBasicProperty<MInt>("periodicCartesianDir", AT_, &m_periodicCartesianDir[dir], dir);
161 }
162 } else if(Context::propertyExists("periodicDir")) {
163 for(MInt dir = 0; dir < nDim; dir++) {
164 m_periodicCartesianDir[dir] =
165 Context::getBasicProperty<MInt>("periodicDir", AT_, &m_periodicCartesianDir[dir], dir);
166 }
167 }
168 // Read properties requiered for azimuthal periodicy
169 m_azimuthalPer = false;
170 m_azimuthalPer = Context::getBasicProperty<MBool>("azimuthalPer", AT_, &m_azimuthalPer);
171 if(m_azimuthalPer) {
172 MInt cnt = 0;
173 for(MInt dir = 0; dir < nDim; dir++) {
174 m_azimuthalAngle = PI / 180.0 * Context::getBasicProperty<MFloat>("azimuthalAngle", AT_, &m_azimuthalAngle);
175 m_azimuthalPerCenter[dir] =
176 Context::getBasicProperty<MFloat>("azimuthalPerCenter", AT_, &m_azimuthalPerCenter[dir], dir);
177 if(m_periodicCartesianDir[dir] == 0) {
178 ASSERT(nDim == 3, "2D grid but periodicCartesianDir is 0!");
179 m_azimuthalAxialDir = dir;
180 } else {
181 ASSERT(cnt < 2, "Azimuthal periodicity is used, but more than 2 peridodic directions!");
182 m_azimuthalPeriodicDir[cnt] = dir;
183 cnt++;
184 }
185 }
186 ASSERT(cnt == 2, "Azimuthal periodicity is used, but less than 2 peridodic directions!");
187 }
188 for(MInt dir = 0; dir < nDim; dir++) {
189 if(m_periodicCartesianDir[dir]) m_noPeriodicCartesianDirs++;
190 }
191 m_log << "Number of periodic Cartesian directions is " << m_noPeriodicCartesianDirs << " ("
192 << m_periodicCartesianDir[0] << "," << m_periodicCartesianDir[1] << "," << m_periodicCartesianDir[2] << ")"
193 << endl;
194
195 // Get output and restart directories
196 MString testcaseDir = "./";
197 testcaseDir = Context::getBasicProperty<MString>("testcaseDir", AT_, &testcaseDir);
198
210 m_outputDir = Context::getBasicProperty<MString>("outputDir", AT_);
211
212 m_restart = false;
213 m_restart = Context::getBasicProperty<MBool>("restartFile", AT_, &m_restart);
214
215 MBool pp = false;
216 pp = Context::getBasicProperty<MBool>("postProcessing", AT_, &pp);
217
230 m_restartDir = Context::getBasicProperty<MString>("restartDir", AT_, &m_outputDir);
231 if(!m_restart && !pp) {
232 m_restartDir = testcaseDir + m_outputDir;
233 } else {
234 m_restartDir = testcaseDir + m_restartDir;
235 }
236 m_outputDir = testcaseDir + m_outputDir;
237
249 m_noHaloLayers = 2;
250 m_noHaloLayers = Context::getBasicProperty<MInt>("noHaloLayers", AT_, &m_noHaloLayers);
251
252
253 m_loadGridPartition = false;
266 m_loadGridPartition = Context::getBasicProperty<MBool>("loadGridPartition", AT_, &m_loadGridPartition);
267
275 m_loadPartition = false;
276 m_loadPartition = Context::getBasicProperty<MBool>("loadPartition", AT_, &m_loadPartition);
277
289 m_partitionParallelSplit = false;
290 m_partitionParallelSplit =
291 Context::getBasicProperty<MBool>("partitionParallelSplit", AT_, &m_partitionParallelSplit);
292
306 // To test as many routines as possible using the offset, it has to be switched between 0 and
307 // std::numeric_limits<MInt>::max(). The place, where the offset has to be set to 0 depends on whether it is a
308 // restartFile using a restartGrid, where all globalIds already start with an offset.
309 MBool offset = false;
310 offset = Context::getBasicProperty<MBool>("bitOffset", AT_, &offset);
311 if(offset) m_32BitOffset = std::numeric_limits<MInt>::max();
312
313
314 // Philipp Brokof, Moritz Waldmann:
315 // Initialize status flag for additional LB-grid checks
323 m_lbGridChecks = false;
324 if(Context::propertyExists("lbGridChecks")) {
325 m_lbGridChecks = Context::getBasicProperty<MBool>("lbGridChecks", AT_);
326 }
327
337 m_coarseRatio = 0.2;
338 m_coarseRatio = Context::getBasicProperty<MFloat>("coarseRatio", AT_, &m_coarseRatio);
339
347 m_allowCoarsening = true;
348 m_allowCoarsening = Context::getBasicProperty<MBool>("allowCoarsening", AT_, &m_allowCoarsening);
349
362 m_updatePartitionCellsOnRestart = true;
363 m_updatePartitionCellsOnRestart =
364 Context::getBasicProperty<MBool>("updatePartitionCellsOnRestart", AT_, &m_updatePartitionCellsOnRestart);
365
366
367 m_lowMemAdaptation = true;
368 m_lowMemAdaptation = Context::getBasicProperty<MBool>("lowMemAdaptation", AT_, &m_lowMemAdaptation);
369 if(m_lowMemAdaptation) {
370 m_log << "Adaptation mode: low memory" << endl;
371 } else {
372 m_log << "Adaptation mode: default" << endl;
373 }
374
375 } else {
376 // PARAVIEW PLUGIN
377 m_loadGridPartition = 0;
378 m_partitionCellOffspringThreshold = 50000;
379 m_partitionCellWorkloadThreshold = 50000.;
380 m_noPeriodicCartesianDirs = 0;
381 m_periodicCartesianDir.fill(0.0);
382 m_noHaloLayers = 1;
383 m_lbGridChecks = false;
384 m_coarseRatio = 0.2;
385 }
386
387 createPaths();
388
389 RECORD_TIMER_STOP(timerAllocProp);
390
391 m_log << "Initializing collector | "
392 << "#maxCells: " << maxCells << " | #dimensions: " << nDim << endl;
393
394 // Initialize tree
395 m_tree.reset(maxCells);
396 MInt noSolvers = 1;
397 // Set number of solvers
398 if(g_multiSolverGrid && !m_paraViewPlugin) {
399 noSolvers = Context::getBasicProperty<MInt>("noSolvers", AT_);
400 m_log << "Number of solver: " << noSolvers << std::endl;
401 } else if(m_paraViewPlugin) {
402 // in case of a paraview Plugin we need to reset the number of solvers
403 // since no property file is read from the plugin
404 m_gridInputFileName = fileName;
405 ParallelIo grid(m_gridInputFileName, maia::parallel_io::PIO_READ, mpiComm());
406 if(grid.hasAttribute("noSolvers")) {
407 grid.getAttribute(&noSolvers, "noSolvers");
408 }
409 }
410
411 m_maxLevel = 0;
412
413 m_addSolverToGrid = false;
414 if(!m_paraViewPlugin) {
415 m_addSolverToGrid = Context::getBasicProperty<MBool>("addSolverToGrid", AT_, &m_addSolverToGrid);
416 m_referenceSolver = Context::getBasicProperty<MInt>("referenceSolver", AT_, &m_referenceSolver);
417
418 if(domainId() == 0 && m_addSolverToGrid) {
419 cerr << "Adding additional solver to the grid file based on solver " << m_referenceSolver << endl;
420 }
421
422 if(!g_multiSolverGrid && m_addSolverToGrid) {
423 noSolvers++;
424 }
425 }
426
427 if(domainId() == 0) {
428 cerr << "Grid consisting of " << noSolvers << " solvers." << endl;
429 }
430
431 m_tree.setNoSolvers(noSolvers);
432
433 // assemble grid input file name
434 // note: requires noSolvers information in the grid-tree
435 if(!m_paraViewPlugin) {
436 m_gridInputFileName = "";
437 setGridInputFilename();
438 }
439
440 m_log << "Collector initialized" << endl;
441
442 // --- Properties related to solver specific halo generation --- //
457 m_haloMode = 0;
458 if(!m_paraViewPlugin) {
459 m_haloMode = Context::getBasicProperty<MInt>("haloMode", AT_, &m_haloMode);
460 TERMM_IF_NOT_COND(m_haloMode >= 0 && m_haloMode <= 2, "Check your input, dude!");
461 if(m_haloMode == 0 && domainId() == 0)
462 cerr << "\033[0;31m#### WARNING:\033[0m haloMode==0 is deprecated!!!" << endl;
463 } else {
464 m_haloMode = 0;
465 }
466
467 // read Solver specific number of halo layer
468 mAlloc(m_noSolverHaloLayers, treeb().noSolvers(), "m_noSolverHaloLayers", AT_);
469 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
470 // initialise with default
471 m_noSolverHaloLayers[solver] = m_noHaloLayers;
472 if(!m_paraViewPlugin && Context::propertyExists("noHaloLayers", solver)) {
473 m_noSolverHaloLayers[solver] = Context::getSolverProperty<MInt>("noHaloLayers", solver, AT_);
474 ASSERT(m_noSolverHaloLayers[solver] < WINDOWLAYER_MAX, "");
475 m_noHaloLayers = std::max(m_noHaloLayers, m_noSolverHaloLayers[solver]);
476 ASSERT(m_noSolverHaloLayers[solver] <= m_noHaloLayers, "");
477 m_log << " -- solverId " << solver << " -- no Solver Halo-Layers : " << m_noSolverHaloLayers[solver] << endl;
478 }
479 }
480
481 if(!m_paraViewPlugin && Context::propertyExists("checkRefinementHoles")) {
482 mAlloc(m_checkRefinementHoles, treeb().noSolvers(), "m_checkRefinementHoles", AT_);
491 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
492 m_checkRefinementHoles[solver] = Context::getSolverProperty<MBool>("checkRefinementHoles", solver, AT_);
493 }
494
495 mAlloc(m_diagSmoothing, treeb().noSolvers(), "m_diagSmoothing", AT_);
504 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
505 m_diagSmoothing[solver] = false;
506 m_diagSmoothing[solver] =
507 Context::getSolverProperty<MBool>("diagSmoothing", solver, AT_, &m_diagSmoothing[solver]);
508 }
509 }
510
511 if(!m_paraViewPlugin && Context::propertyExists("identicalSolvers")) {
512 m_noIdenticalSolvers = Context::propertyLength("identicalSolvers") / 2;
513 ASSERT(m_noIdenticalSolvers <= treeb().noSolvers(), "");
514 mAlloc(m_identicalSolvers, m_noIdenticalSolvers * 2, "m_identicalSolvers", AT_);
515 mAlloc(m_identicalSolverMaxLvl, m_noIdenticalSolvers, "m_identicalSolverMaxLvl", AT_);
516 mAlloc(m_identicalSolverLvlJumps, m_noIdenticalSolvers, "m_identicalSolverLvlJumps", AT_);
517 for(MInt s = 0; s < m_noIdenticalSolvers * 2; s++) {
518 m_identicalSolvers[s] = Context::getBasicProperty<MInt>("identicalSolvers", AT_, s);
519 }
520 for(MInt pair = 0; pair < m_noIdenticalSolvers; pair++) {
521 const MInt slaveId = m_identicalSolvers[pair * 2];
522 const MInt masterId = m_identicalSolvers[pair * 2 + 1];
523 MInt slaveMaxLvl = Context::getSolverProperty<MInt>("maxRfnmntLvl", slaveId, AT_);
524 if(Context::propertyExists("identicalSolverMaxLevel")) {
525 slaveMaxLvl = Context::getSolverProperty<MInt>("identicalSolverMaxLevel", slaveId, AT_);
526 }
527 const MInt masterMaxLvl = Context::getSolverProperty<MInt>("maxRfnmntLvl", masterId, AT_);
528 m_identicalSolverMaxLvl[pair] = slaveMaxLvl;
529 m_identicalSolverLvlJumps[pair] = masterMaxLvl - slaveMaxLvl;
530 cerr0 << "Slave solverId " << slaveId << " has maxRefinementLevel " << slaveMaxLvl << " and "
531 << m_identicalSolverLvlJumps[pair] << " refinement-Jumps towards the master!" << endl;
532 }
533 }
534
535 m_log << "/***** STARTING FLOW SOLVER ******/" << endl;
536 loadGridFile(timerLoadGridPar, timerCreateMB);
537
538 if(!m_paraViewPlugin) {
539 // After grid was loaded, initialize grid mapping if necessary
540 if(Context::propertyExists("donorGridFileName")) {
541 initGridMap();
542 }
543
544 if(m_addSolverToGrid) {
545 if(Context::propertyExists("solverBoundingBox")) {
546 if(domainId() == 0) {
547 cerr << "Applying solver bounding box to solver " << treeb().noSolvers() - 1 << endl;
548 }
549 std::array<MFloat, nDim * 2> boxCoordinates;
550 for(MInt i = 0; i < nDim * 2; i++) {
551 boxCoordinates[i] = Context::getBasicProperty<MFloat>("solverBoundingBox", AT_, i);
552 }
553 MInt solverMaxLvl = maxRefinementLevel();
554 solverMaxLvl = Context::getBasicProperty<MInt>("solverMaxLevel", AT_, &solverMaxLvl);
555 // loop top down and remove solver property if:
556 //- the cell is outside the box and
557 //- none of the cells children belong to the solver!
558 const MInt solverId = treeb().noSolvers() - 1;
559 for(MInt lvl = maxRefinementLevel(); lvl >= minLevel(); lvl--) {
560 for(MInt cellId = 0; cellId < noInternalCells(); cellId++) {
561 if(a_level(cellId) != lvl) continue;
562 if(!treeb().solver(cellId, treeb().noSolvers() - 1)) continue;
563 if(a_hasChildren(cellId, treeb().noSolvers() - 1)) continue;
564 for(MInt i = 0; i < nDim; i++) {
565 if(a_coordinate(cellId, i) < boxCoordinates[i] || a_coordinate(cellId, i) > boxCoordinates[nDim + i]) {
566 treeb().solver(cellId, treeb().noSolvers() - 1) = false;
567 }
568 }
569 if(a_level(cellId) > solverMaxLvl) {
570 treeb().solver(cellId, solverId) = false;
571 }
572 }
573
574 if(Context::propertyExists("addedSolverMaxLevel")) {
575 MInt addedSolverMaxLevel = Context::getBasicProperty<MInt>("addedSolverMaxLevel", AT_);
576 for(MInt cellId = 0; cellId < treeb().size(); cellId++) {
577 if(!treeb().solver(cellId, treeb().noSolvers() - 1)) continue;
578 if(a_level(cellId) > addedSolverMaxLevel) {
579 treeb().solver(cellId, treeb().noSolvers() - 1) = false;
580 }
581 }
582 MInt* recalcIds = nullptr;
583 mAlloc(recalcIds, maxNoCells(), "recalcIds", -1, AT_);
584
585 ASSERT(m_tree.noSolvers() > 1, "");
586 g_multiSolverGrid = true;
587
588 saveGrid((m_outputDir + m_gridInputFileName).c_str(), recalcIds);
589 }
590 }
591 // exchange solver bitset
592 // WH_old
593 if(m_haloMode > 0) {
594 exchangeSolverBitset(&m_tree.solverBits(0));
595 } else {
596 maia::mpi::exchangeBitset(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), &m_tree.solverBits(0),
597 m_tree.size());
598 }
599 }
600 MInt* recalcIds = nullptr;
601 mAlloc(recalcIds, maxNoCells(), "recalcIds", -1, AT_);
602 saveGrid((m_outputDir + m_gridInputFileName + "+addSolver").c_str(), recalcIds);
603 }
604
605 // Compute and store local bounding box
606 computeLocalBoundingBox(&m_localBoundingBox[0]);
607 }
608
609 if(noDomains() > 1) {
610 m_log << " -- minLevel for current domain: " << m_minLevel << endl;
611 m_log << " -- maxLevel for current domain: " << m_maxLevel << endl;
612 } else {
613 m_log << " -- minLevel = " << m_minLevel << endl;
614 m_log << " -- maxLevel = " << m_maxLevel << endl;
615 }
616
617 m_log << " [ Grid file has " << m_tree.size() << " cells ] " << endl;
618
619 RECORD_TIMER_STOP(timertotal);
620 DISPLAY_TIMER(timertotal);
621
622 // Store grid cell volume
623 // maxCellVolumeLevel is calculated up to level 32 because at some parts of the code
624 // the gridCellVolume up to level 32 is needed
625 MInt maxCellVolumeLevel = (m_maxLevel < 32) ? 32 : m_maxLevel;
626 mAlloc(m_gridCellVolume, maxCellVolumeLevel, "m_gridCellVolume", AT_);
627 for(MInt level = 0; level < maxCellVolumeLevel; level++) {
628 m_gridCellVolume[level] = F1;
629 for(MInt spaceId = 0; spaceId < nDim; spaceId++) {
630 m_gridCellVolume[level] *= cellLengthAtLevel(level);
631 }
632 }
633
634 // check that all minLevels are refined
635
636 if(!m_paraViewPlugin && m_newMinLevel > 0) {
637 // check if the grid is already sufficiently refiend!
638 MInt noUnrefinedMinCells = 0;
639 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
640 if(a_isHalo(cellId)) continue;
641 if(a_level(cellId) < m_newMinLevel) {
642 if(a_noChildren(cellId) < 1) noUnrefinedMinCells++;
643 }
644 }
645 MPI_Allreduce(MPI_IN_PLACE, &noUnrefinedMinCells, 1, MPI_INT, MPI_SUM, mpiComm(), AT_, "MPI_IN_PLACE",
646 "noUnrefinedMinCells");
647
648 if(noUnrefinedMinCells > 0) {
649 if(domainId() == 0) {
650 cerr << "Mincells not yet sufficiently refined for minLevel increase from " << m_minLevel << " to "
651 << m_newMinLevel << "!" << endl;
652 }
653 } else if(noDomains() == 1) {
654 // rename the file-name from which the restart was made
655 std::rename((m_restartDir + m_gridInputFileName).c_str(),
656 (m_restartDir + m_gridInputFileName + "backup").c_str());
657
658 m_targetGridMinLevel = m_newMinLevel;
659 m_updatedPartitionCells = false;
660 MInt* recalcIds = nullptr;
661 mAlloc(recalcIds, maxNoCells(), "recalcIds", -1, AT_);
662 if(m_maxUniformRefinementLevel < m_newMinLevel) {
663 m_maxUniformRefinementLevel = m_newMinLevel;
664 }
665
666 saveGrid((m_outputDir + m_gridInputFileName).c_str(), recalcIds);
667
668 cerr0 << "Successfully written new grid file! Exiting MAIA." << endl;
669 mTerm(0, AT_, "Finished writing new grid restartFile!");
670 }
671 }
672
673 if(!m_paraViewPlugin) {
674 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
675 MInt inactiveMinCells = 0;
676 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
677 if(a_level(cellId) != minLevel()) continue;
678 if(a_isHalo(cellId)) continue;
679 if(!a_solver(cellId, solver)) {
680 inactiveMinCells++;
681 }
682 }
683 MPI_Allreduce(MPI_IN_PLACE, &inactiveMinCells, 1, MPI_INT, MPI_SUM, mpiComm(), AT_, "MPI_IN_PLACE",
684 "inactiveMinCells");
685
686 if(inactiveMinCells > 0) {
687 cerr0 << "Solver " << solver << " has " << inactiveMinCells << " inactive mincells!" << endl;
688 }
689 }
690 }
691 printAllocatedMemory(oldAllocatedBytes, "CartesianGrid", mpiComm());
692}
693
694
698template <MInt nDim>
700 TRACE();
701
702 mDeallocate(m_paths1D);
703 mDeallocate(m_paths2D);
704 mDeallocate(m_paths3D);
705 mDeallocate(m_neighborList);
706}
707
708
709//-----------------------------------------------------------------------------
710
711
715template <MInt nDim>
717 TRACE();
718
719 stringstream s;
720 MString name;
721 MBool tmpFalse = false;
722
723 const MBool restart = forceRestart || m_restart;
724
725 const MBool useNonSpecifiedRestartFile =
726 restart ? Context::getBasicProperty<MBool>("useNonSpecifiedRestartFile", AT_, &tmpFalse) : true;
727
728 const MInt restartTimeStep = useNonSpecifiedRestartFile ? 0 : Context::getBasicProperty<MInt>("restartTimeStep", AT_);
729 MBool multilevel = false;
730 m_zonal = false;
731 multilevel = Context::getBasicProperty<MBool>("multilevel", AT_, &multilevel);
732 m_zonal = Context::getBasicProperty<MBool>("zonal", AT_, &m_zonal);
733
734 // NOTE: gridSolverId is the reference solverId used by the grid to read the gridFileName from the
735 // correcponding restartVariables file!
736 const MInt gridSolverId = 0;
737
738 MString solverType = Context::getSolverProperty<MString>("solvertype", gridSolverId, AT_);
739
740
741 if(solverType == "MAIA_LS_SOLVER" || solverType == "MAIA_MULTI_LS") {
742 // the ls solver needs special treatment right now since cartesian grid is expected but it can also have its own
743 // grid here the cartesian grid file name is required... ls solver right now has to have a cartesian grid file name
744 // 'grid.Netcdf' this will be adjusted in the near future
745 if(domainId() == 0) {
746 cerr << "cartesian grid file name has to be 'grid.Netcdf'" << endl;
747 }
748 m_gridInputFileName = "grid.Netcdf";
749 MBool m_adaptation = Context::getBasicProperty<MBool>("adaptation", AT_, &m_adaptation);
750 if(m_adaptation && m_restart) {
751 MInt restartTime = Context::getBasicProperty<MInt>("restartTimeStep", AT_);
752
753 stringstream reinitFile;
754 reinitFile << "restartGrid_00" << restartTime << ".Netcdf";
755
756 m_gridInputFileName = reinitFile.str();
757 cerr << "grid file name changed to : " << m_gridInputFileName << endl;
758 }
759
760 } else {
761 if(restart) {
762 stringstream varFileName;
763 if(solverType == "MAIA_LATTICE_BOLTZMANN") {
764 // Get restart filename for LB
765 varFileName << m_restartDir;
766 varFileName << "restart_" << restartTimeStep << ParallelIo::fileExt();
767 } else if(solverType == "MAIA_DISCONTINUOUS_GALERKIN") {
768 // Get restart filename for DG
769 if(useNonSpecifiedRestartFile) {
770 varFileName << m_restartDir << "restart" << ParallelIo::fileExt();
771 } else if(g_multiSolverGrid) {
772 varFileName << m_restartDir << "restart_b" << gridSolverId << "_" << setw(8) << setfill('0')
773 << restartTimeStep << ParallelIo::fileExt();
774 } else {
775 varFileName << m_restartDir << "restart_" << setw(8) << setfill('0') << restartTimeStep
776 << ParallelIo::fileExt();
777 }
778 } else if(solverType == "MAIA_DG_MULTISOLVER") {
779 // Get restart filename for DG
780 if(useNonSpecifiedRestartFile) {
781 varFileName << m_restartDir << "restart_b0" << ParallelIo::fileExt();
782 } else {
783 varFileName << m_restartDir << "restart_b0_" << setw(8) << setfill('0') << restartTimeStep
784 << ParallelIo::fileExt();
785 }
786 } else if(solverType == "MAIA_LEVELSET_SOLVER") {
787 varFileName << m_restartDir << "restartLSCG";
788 if(treeb().noSolvers() > 1) varFileName << "_" << gridSolverId;
789 if(!useNonSpecifiedRestartFile) varFileName << "_" << restartTimeStep;
790 varFileName << ParallelIo::fileExt();
791 } else {
792 // Get restart filename for FV
793 if(!multilevel) {
794 if(m_zonal) {
795 varFileName << m_restartDir << "restartVariables" << gridSolverId;
796 } else {
797 varFileName << m_restartDir << "restartVariables";
798 }
799 if(!useNonSpecifiedRestartFile) varFileName << "_" << restartTimeStep;
800 varFileName << ParallelIo::fileExt();
801 } else {
802 // For multilevel, use solver-specific file name for restart files
803 const MInt maxLength = 256;
804 array<MChar, maxLength> buffer{};
805 snprintf(buffer.data(), maxLength, "restart_b00_t%08d", restartTimeStep);
806 varFileName << m_restartDir << MString(buffer.data()) << ParallelIo::fileExt();
807 }
808 }
809 const MString fileName = varFileName.str();
810 m_log << "Trying to read grid input file name from restart file " << fileName << endl;
811 if(fileExists(fileName.c_str())) {
812 ParallelIo parallelIo(fileName.c_str(), maia::parallel_io::PIO_READ, mpiComm());
813 parallelIo.getAttribute(&name, "gridFile");
814 s << name;
815 } else {
816 MString tmp = Context::getBasicProperty<MString>("gridInputFileName", AT_);
817 s << tmp;
818 }
819 } else {
820 MString tmp = Context::getBasicProperty<MString>("gridInputFileName", AT_);
821 s << tmp;
822 }
823 m_gridInputFileName = s.str();
824 struct stat buffer {};
825 if(restart && stat((m_restartDir + m_gridInputFileName).c_str(), &buffer) != 0) {
826 mTerm(1, AT_, "Grid input file " + m_restartDir + m_gridInputFileName + " does not exist.");
827 }
828 m_log << "Cartesian Grid input file name is " << m_gridInputFileName
829 << " (full path: " << m_restartDir + m_gridInputFileName << ")." << endl;
830 }
831}
832//-----------------------------------------------------------------------------
833
834
839template <MInt nDim>
840void CartesianGrid<nDim>::loadGridFile(const MInt timerLoadGridPar, const MInt timerCreateMB) {
841 TRACE();
842
843 // 1. read grid from disk
844 RECORD_TIMER_START(timerLoadGridPar);
845 loadGrid(m_restartDir + m_gridInputFileName);
846 RECORD_TIMER_STOP(timerLoadGridPar);
847
848 // ---
849 // At this point, parentIds, childIds, and nghbrIds are local ids with respect to the collector
850
851 // 2. create window and halo cells
852 if(noDomains() > 1 || m_noPeriodicCartesianDirs > 0) {
853 RECORD_TIMER_START(timerCreateMB);
854 // also calls storeMinLevelCells and computeLeafLevel!
855 setupWindowHaloCellConnectivity();
856 RECORD_TIMER_STOP(timerCreateMB);
857 } else {
858 storeMinLevelCells();
859 computeLeafLevel();
860 }
861
862#ifdef MAIA_GRID_SANITY_CHECKS
863 gridSanityChecks();
864 checkWindowHaloConsistency(true);
865#endif
866}
867
868
869//-----------------------------------------------------------------------------
870
871
883template <MInt nDim>
884MInt CartesianGrid<nDim>::findNeighborDomainId(const MLong globalId, const MInt noDomains, const MLong* domainOffsets) {
885 if(globalId < domainOffsets[0] || globalId > domainOffsets[noDomains]) {
886 TERMM(1, "Invalid global id: " + std::to_string(globalId)
887 + "; domainOffset[0]=" + std::to_string(domainOffsets[noDomains])
888 + "; domainOffset[noDomains]=" + std::to_string(domainOffsets[noDomains]));
889 return -1;
890 }
891
892 // Search for global cell id in (the sorted) domain offsets array in logarithmic time
893 auto lowerBound = std::lower_bound(&domainOffsets[0], &domainOffsets[0] + noDomains, globalId);
894 const MInt dist = std::distance(&domainOffsets[0], lowerBound);
895 // Check if this cell is a domain offset (i.e. in the offsets list)
896 const MBool isDomainOffset = (*lowerBound == globalId);
897 // Determine neighbor domain id
898 const MInt domain = (isDomainOffset) ? dist : dist - 1;
899 return domain;
900}
901
902
903//-----------------------------------------------------------------------------
904
905
910template <MInt nDim>
911template <class ITERATOR, typename U>
912MInt CartesianGrid<nDim>::findIndex(ITERATOR first, ITERATOR last, const U& val) {
913 if(distance(first, last) <= 0) return -1;
914 auto idx = (MInt)distance(first, find(first, last, val));
915 if(idx == (MInt)distance(first, last)) idx = -1;
916 return idx;
917}
918
919
920//-----------------------------------------------------------------------------
921
922
927template <MInt nDim>
928template <typename TA, typename TB, typename TC>
929MInt CartesianGrid<nDim>::setNeighborDomainIndex(const MInt ndom, vector<TA>& vecA, vector<TB>& vecB,
930 vector<TC>& vecC) {
931 ASSERT(ndom > -1, "");
932 if(m_nghbrDomainIndex[ndom] < 0) {
933 vecA.emplace_back();
934 vecB.emplace_back();
935 vecC.emplace_back();
936 m_nghbrDomainIndex[ndom] = (signed)m_nghbrDomains.size();
937 m_nghbrDomains.push_back(ndom);
938 }
939 return m_nghbrDomainIndex[ndom];
940}
941
946template <MInt nDim>
947template <typename TA, typename TB>
948MInt CartesianGrid<nDim>::setNeighborDomainIndex(const MInt ndom, vector<TA>& vecA, vector<TB>& vecB) {
949 ASSERT(ndom > -1, "");
950 if(m_nghbrDomainIndex[ndom] < 0) {
951 vecA.emplace_back();
952 vecB.emplace_back();
953 m_nghbrDomainIndex[ndom] = (signed)m_nghbrDomains.size();
954 m_nghbrDomains.push_back(ndom);
955 }
956 return m_nghbrDomainIndex[ndom];
957}
958
963template <MInt nDim>
965 vector<vector<MInt>>& vecB) {
966 ASSERT(ndom > -1, "");
967 if(m_azimuthalNghbrDomainIndex[ndom] < 0) {
968 vecA.emplace_back();
969 vecB.emplace_back();
970 m_azimuthalHigherLevelConnectivity.emplace_back();
971 m_azimuthalNghbrDomainIndex[ndom] = (signed)m_azimuthalNghbrDomains.size();
972 m_azimuthalNghbrDomains.push_back(ndom);
973 }
974 return m_azimuthalNghbrDomainIndex[ndom];
975}
976
981template <MInt nDim>
983 vector<vector<MLong>>& vecB) {
984 ASSERT(ndom > -1, "");
985 if(m_azimuthalNghbrDomainIndex[ndom] < 0) {
986 vecA.emplace_back();
987 vecB.emplace_back();
988 m_azimuthalHigherLevelConnectivity.emplace_back();
989 m_azimuthalNghbrDomainIndex[ndom] = (signed)m_azimuthalNghbrDomains.size();
990 m_azimuthalNghbrDomains.push_back(ndom);
991 }
992 return m_azimuthalNghbrDomainIndex[ndom];
993}
994
995//-----------------------------------------------------------------------------
996
997
1002template <MInt nDim>
1004 TRACE();
1005
1006 auto logDuration = [this](const MFloat timeStart, const MString comment) {
1007 logDuration_(timeStart, "WINDOW/HALO", comment, mpiComm(), domainId(), noDomains());
1008 };
1009 const MFloat winHaloTimeStart = wallTime();
1010
1011 std::vector<MInt>().swap(m_nghbrDomains);
1012 std::vector<MInt>().swap(m_nghbrDomainIndex);
1013 std::vector<std::vector<MInt>>().swap(m_haloCells);
1014 std::vector<std::vector<MInt>>().swap(m_windowCells);
1015 std::vector<std::unordered_map<MInt, M32X4bit<true>>>().swap(m_windowLayer_);
1016
1017 cerr0 << "Establish window/halo connectivity..." << endl;
1018
1019 /* if( Context::propertyExists("targetGridFileName") ) { */
1020 /* TERMM(1, "deprecated"); */
1021 /* MString targetGridFileName = Context::getBasicProperty<MString>("targetGridFileName",
1022 * AT_, &targetGridFileName); */
1023
1024 std::vector<MInt>().swap(m_nghbrDomains);
1025 m_nghbrDomainIndex.resize(noDomains());
1026 std::fill_n(m_nghbrDomainIndex.begin(), noDomains(), -1);
1027
1028 if(m_azimuthalPer) {
1029 m_azimuthalNghbrDomains.clear();
1030 m_azimuthalNghbrDomainIndex.resize(noDomains());
1031 std::fill_n(m_azimuthalNghbrDomainIndex.begin(), noDomains(), -1);
1032
1033 m_azimuthalHaloCells.clear();
1034 m_azimuthalUnmappedHaloCells.clear();
1035 m_azimuthalWindowCells.clear();
1036 }
1037
1038 // 1. create window-halo-connectivity on m_minLevel
1039 const MFloat minExchangeTimeStart = wallTime();
1040 createMinLevelExchangeCells();
1041 logDuration(minExchangeTimeStart, "Create min level exchange cells");
1042
1043 // 2. iteratively create window-halo-connectivity on m_minLevel+1...m_maxLevel
1044 const MFloat higherExchangeTimeStart = wallTime();
1045 if(m_maxLevel > m_minLevel) {
1046 // WH_old
1047 if(m_haloMode > 0)
1048 createHigherLevelExchangeCells();
1049 else
1050 createHigherLevelExchangeCells_old();
1051 }
1052 logDuration(higherExchangeTimeStart, "Create higher level exchange cells");
1053
1054#ifdef MAIA_GRID_SANITY_CHECKS
1055 checkWindowHaloConsistency();
1056#endif
1057
1058 // exchange cell properties
1059 exchangeProperties();
1060
1061 // create communication data
1062 updateHaloCellCollectors();
1063
1064 // WH_old
1065 // Exchange solver info
1066 if(m_haloMode > 0)
1067 exchangeSolverBitset(&m_tree.solverBits(0));
1068 else
1069 maia::mpi::exchangeBitset(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), &m_tree.solverBits(0),
1070 m_tree.size());
1071
1072 if(m_azimuthalPer) {
1073 if(noAzimuthalNeighborDomains() > 0) {
1074 maia::mpi::exchangeBitset(m_azimuthalNghbrDomains, m_azimuthalHaloCells, m_azimuthalWindowCells, mpiComm(),
1075 &m_tree.solverBits(0), m_tree.size());
1076 }
1077 // Set unmapped halos as true for each solver
1078 // Solver bit is updated in proxy
1079 for(MInt i = 0; i < noAzimuthalUnmappedHaloCells(); i++) {
1080 MInt cellId = azimuthalUnmappedHaloCell(i);
1081 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
1082 m_tree.solver(cellId, solver) = true;
1083 }
1084 }
1085 // To ensure that azimuthal halo cells are fully refined (have all children)
1086 // or are not refined at all, solver Bits need to be checked!
1087 correctAzimuthalSolverBits();
1088 }
1089
1090 storeMinLevelCells();
1091
1092 computeLeafLevel();
1093
1094 for(MInt i = 0; i < noNeighborDomains(); i++) {
1095 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
1096 a_hasProperty(m_haloCells[i][j], Cell::IsHalo) = true;
1097 a_hasProperty(m_haloCells[i][j], Cell::IsWindow) = false;
1098 }
1099 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
1100 a_hasProperty(m_windowCells[i][j], Cell::IsHalo) = false;
1101 a_hasProperty(m_windowCells[i][j], Cell::IsWindow) = true;
1102 }
1103 }
1104 if(m_azimuthalPer) {
1105 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
1106 for(MInt j = 0; j < (signed)m_azimuthalHaloCells[i].size(); j++) {
1107 a_hasProperty(m_azimuthalHaloCells[i][j], Cell::IsHalo) = true;
1108 a_hasProperty(m_azimuthalHaloCells[i][j], Cell::IsWindow) = false;
1109 }
1110 for(MInt j = 0; j < (signed)m_azimuthalWindowCells[i].size(); j++) {
1111 a_hasProperty(m_azimuthalWindowCells[i][j], Cell::IsHalo) = false;
1112 a_hasProperty(m_azimuthalWindowCells[i][j], Cell::IsWindow) = true;
1113 }
1114 }
1115 for(MInt j = 0; j < noAzimuthalUnmappedHaloCells(); j++) {
1116 a_hasProperty(m_azimuthalUnmappedHaloCells[j], Cell::IsHalo) = true;
1117 }
1118 }
1119
1120 createGlobalToLocalIdMapping();
1121
1122 MFloat cellCnt[4] = {F0, F0, F0, F0};
1123 for(MInt i = 0; i < noNeighborDomains(); i++) {
1124 cellCnt[0] += (MFloat)m_haloCells[i].size();
1125 cellCnt[1] += (MFloat)m_windowCells[i].size();
1126 }
1127 cellCnt[2] = (MFloat)m_tree.size();
1128 cellCnt[3] = (MFloat)noNeighborDomains();
1129 m_log << "Grid local halo/window cell ratio: " << cellCnt[0] << " " << cellCnt[1] << endl;
1130 m_log << "Grid local halo/window cell ratio: " << cellCnt[0] << "; relative " << cellCnt[0] / cellCnt[2] << "; "
1131 << cellCnt[1] << "; relative " << cellCnt[1] / cellCnt[2] << endl;
1132#ifndef NDEBUG
1133 MFloat maxHaloCellRatio = cellCnt[0] / cellCnt[2];
1134 MPI_Allreduce(MPI_IN_PLACE, &maxHaloCellRatio, 1, MPI_DOUBLE, MPI_MAX, mpiComm(), AT_, "MPI_IN_PLACE",
1135 "maxHaloCellRatio");
1136 m_log << "Grid maximum halo cell ratio: " << maxHaloCellRatio << endl;
1137
1138 MFloat maxWindowCellRatio = cellCnt[1] / cellCnt[2];
1139 MPI_Allreduce(MPI_IN_PLACE, &maxWindowCellRatio, 1, MPI_DOUBLE, MPI_MAX, mpiComm(), AT_, "MPI_IN_PLACE",
1140 "maxWindowCellRatio");
1141 m_log << "Grid maximum window cell ratio: " << maxWindowCellRatio << endl;
1142
1143 MPI_Allreduce(MPI_IN_PLACE, cellCnt, 4, MPI_DOUBLE, MPI_SUM, mpiComm(), AT_, "MPI_IN_PLACE", "cellCnt");
1144 cellCnt[0] /= cellCnt[2];
1145 cellCnt[1] /= cellCnt[2];
1146 cellCnt[3] /= (MFloat)noDomains();
1147 m_log << "Grid global halo/window cell ratio: " << cellCnt[0] << " " << cellCnt[1] << endl;
1148 MInt maxNoNeighborDomains = noNeighborDomains();
1149 MPI_Allreduce(MPI_IN_PLACE, &maxNoNeighborDomains, 1, MPI_INT, MPI_MAX, mpiComm(), AT_, "MPI_IN_PLACE",
1150 "maxNoNeighborDomains");
1151 m_log << "Grid maximum number of neighbor domains: " << maxNoNeighborDomains << ", average: " << cellCnt[3] << endl;
1152#endif
1153
1154 cerr0 << "done." << endl;
1155 logDuration(winHaloTimeStart, "Window/halo total");
1156}
1157
1158
1159//-------------------------------------------------------------------------------------------
1160
1161
1166template <MInt nDim>
1168 TRACE();
1169
1170 if(domainId() == 0) cerr << " * create minLevel exchange cells...";
1171
1172 const MInt oldNoCells = m_tree.size();
1173 ScratchSpace<MInt> noHalos(noDomains(), AT_, "noHalos");
1174 ScratchSpace<MInt> noWindows(noDomains(), AT_, "noWindows");
1175 ScratchSpace<MLong> hilbertOffsets(noDomains() + 1, AT_, "hilbertOffsets");
1176 vector<vector<MInt>> halos;
1177 vector<vector<MLong>> hilbertIds;
1178 vector<vector<MLong>> recvHilbertIds;
1179 MInt noMinLevelCells = 0;
1180 MFloat bbox[2 * nDim];
1181 for(MInt i = 0; i < nDim; i++) {
1182 bbox[i] = numeric_limits<MFloat>::max();
1183 bbox[nDim + i] = numeric_limits<MFloat>::lowest();
1184 }
1185 for(MInt cellId = 0; cellId < m_noInternalCells; cellId++) {
1186 for(MInt i = 0; i < nDim; i++) {
1187 bbox[i] = mMin(bbox[i], a_coordinate(cellId, i) - F1B2 * cellLengthAtLevel(a_level(cellId)));
1188 bbox[nDim + i] = mMax(bbox[nDim + i], a_coordinate(cellId, i) + F1B2 * cellLengthAtLevel(a_level(cellId)));
1189 }
1190 }
1191 // Note: use epsilon based on lenght0 (was F1B2 * m_lengthLevel0 +- 1e-12 before), required for
1192 // multisolver grids with multisolver bounding box and shifted center of gravity
1193 const MFloat halfLength0 = 0.5 * ((F1 + F1 / FPOW2(30)) * m_lengthLevel0);
1194 for(MInt i = 0; i < nDim; i++) {
1195 ASSERT(bbox[i] > m_centerOfGravity[i] - halfLength0 && bbox[nDim + i] < m_centerOfGravity[i] + halfLength0,
1196 "bbox " + std::to_string(bbox[i]) + "," + std::to_string(bbox[nDim + i]) + ", center = "
1197 + std::to_string(m_centerOfGravity[i]) + ", halfLenght0 = " + std::to_string(halfLength0));
1198 }
1199 MPI_Allreduce(MPI_IN_PLACE, &bbox[0], nDim, MPI_DOUBLE, MPI_MIN, mpiComm(), AT_, "MPI_IN_PLACE", "bbox[0]");
1200 MPI_Allreduce(MPI_IN_PLACE, &bbox[nDim], nDim, MPI_DOUBLE, MPI_MAX, mpiComm(), AT_, "MPI_IN_PLACE", "bbox[nDim]");
1201
1202 if(((MLong)pow(2.0, m_targetGridMinLevel * nDim)) > std::numeric_limits<MLong>::max()) {
1203 mTerm(1, AT_, "Error: minLevel cell size is bigger than std::numeric_limits<MInt>::max()");
1204 }
1205
1206 // Count the min-level cells on this domain (including partition level ancestors)
1207 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
1208 a_isToDelete(cellId) = false;
1209 if(a_level(cellId) == m_minLevel) {
1210 noMinLevelCells++;
1211 }
1212 }
1213 ASSERT(noMinLevelCells > 0, "Error: no min-level cell (including partition level ancestors) found.");
1214
1215 unordered_multimap<MLong, MInt> hilbertToLocal;
1216 hilbertToLocal.reserve(noMinLevelCells + noMinLevelCells / 5);
1217 noHalos.fill(0);
1218 noWindows.fill(0);
1219
1220 MLong minHilbertIndex = std::numeric_limits<MLong>::max();
1221 MLong prevHilbertIndex = std::numeric_limits<MLong>::min();
1222 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
1223 if(a_level(cellId) != m_minLevel) continue;
1224 const MLong hilbertId = hilbertIndexGeneric(&a_coordinate(cellId, 0));
1225 hilbertToLocal.insert(make_pair(hilbertId, cellId));
1226 if(a_hasProperty(cellId, Cell::IsHalo)) {
1227 MInt ndom = findNeighborDomainId(a_globalId(cellId));
1228 ASSERT(a_hasProperty(cellId, Cell::IsPartLvlAncestor), "");
1229 ASSERT(ndom > -1, "");
1230 MInt idx = setNeighborDomainIndex(ndom, halos, hilbertIds);
1231 ASSERT(idx > -1, "");
1232 halos[idx].push_back(cellId);
1233 hilbertIds[idx].push_back(hilbertId);
1234 noHalos[ndom]++;
1235 }
1236 ASSERT((cellId < m_noInternalCells) != a_hasProperty(cellId, Cell::IsHalo), "");
1237 ASSERT(a_hasProperty(cellId, Cell::IsHalo)
1238 == (a_hasProperty(cellId, Cell::IsPartLvlAncestor) && (cellId >= m_noInternalCells)),
1239 "");
1240 if(a_hasProperty(cellId, Cell::IsHalo)) continue;
1241 minHilbertIndex = mMin(minHilbertIndex, hilbertId);
1242 ASSERT(hilbertId > prevHilbertIndex,
1243 "Min level cells not sorted by Hilbert id: " + to_string(hilbertId) + " > " + to_string(prevHilbertIndex));
1244 prevHilbertIndex = hilbertId;
1245 }
1246 MPI_Allgather(&minHilbertIndex, 1, MPI_LONG, &hilbertOffsets[0], 1, MPI_LONG, mpiComm(), AT_, "minHilbertIndex",
1247 "hilbertOffsets[0]");
1248 hilbertOffsets[0] = 0;
1249 hilbertOffsets[noDomains()] = std::numeric_limits<MLong>::max();
1250
1251 // some domains may not have any min level cells at all (partition level shift)
1252 for(MInt cpu = noDomains() - 1; cpu >= 0; cpu--) {
1253 if(hilbertOffsets[cpu] == std::numeric_limits<MLong>::max()) {
1254 hilbertOffsets[cpu] = hilbertOffsets[cpu + 1];
1255 }
1256 ASSERT(hilbertOffsets[cpu] <= hilbertOffsets[cpu + 1], "");
1257 }
1258
1259 // const MInt maxNoAdjacentCells = ICUBE[2*m_noHaloLayers+1];
1260 const MInt maxNoAdjacentCells = ipow(2 * m_noHaloLayers + 1, 3);
1261 ScratchSpace<MInt> cellList(maxNoAdjacentCells, AT_, "cellList");
1262 MInt ifcDirs[m_noDirs];
1263
1264 vector<pair<MInt, MInt>> interfaceCells;
1265 for(MInt cellId = 0; cellId < oldNoCells; cellId++) {
1266 if(a_level(cellId) > m_minLevel) continue;
1267 for(MInt dir = 0; dir < m_noDirs; dir++) {
1268 if(a_hasNeighbor(cellId, dir) > 0) continue;
1269 interfaceCells.emplace_back(cellId, dir);
1270 }
1271 }
1272
1273 // find direct and diagonal neighboring cells
1274 MUint cnt = 0;
1275 while(cnt < interfaceCells.size()) {
1276 fill(&ifcDirs[0], &ifcDirs[0] + m_noDirs, 0);
1277 const MInt cellId = interfaceCells[cnt].first;
1278 while(cnt < interfaceCells.size() && cellId == interfaceCells[cnt].first) {
1279 ifcDirs[interfaceCells[cnt].second] = 1;
1280 cnt++;
1281 }
1282 MInt cellCnt = 0;
1283 cellList[cellCnt++] = cellId;
1284 for(MInt dim = 0; dim < nDim; dim++) {
1285 const MInt cellCnt0 = cellCnt;
1286 for(MInt ori = 0; ori < 2; ori++) {
1287 const MInt dir = 2 * dim + ori;
1288 if(!ifcDirs[dir]) continue;
1289 for(MInt c = 0; c < cellCnt0; c++) {
1290 MInt nextId = cellList[c];
1291 for(MInt layer = 0; layer < m_noHaloLayers; layer++) {
1292 if(a_hasNeighbor(nextId, dir) > 0)
1293 nextId = a_neighborId(nextId, dir);
1294 else
1295 nextId = createAdjacentHaloCell(nextId, dir, &hilbertOffsets[0], hilbertToLocal, bbox, &noHalos[0], halos,
1296 hilbertIds);
1297 if(nextId < 0) break;
1298 cellList[cellCnt++] = nextId;
1299 }
1300 }
1301 }
1302 }
1303 }
1304
1305 MPI_Alltoall(&noHalos[0], 1, MPI_INT, &noWindows[0], 1, MPI_INT, mpiComm(), AT_, "noHalos[0]", "noWindows[0]");
1306
1307 for(MInt i = 0; i < noDomains(); i++) {
1308 if(noHalos[i] == 0 && noWindows[i] > 0) {
1309 ASSERT(m_nghbrDomainIndex[i] < 0, "");
1310 setNeighborDomainIndex(i, halos, hilbertIds);
1311 }
1312 }
1313
1314 ScratchSpace<MPI_Request> sendReq(noNeighborDomains(), AT_, "sendReq");
1315 sendReq.fill(MPI_REQUEST_NULL);
1316 recvHilbertIds.resize(noNeighborDomains());
1317 for(MInt i = 0; i < noNeighborDomains(); i++) {
1318 ASSERT(m_nghbrDomains[i] > -1 && m_nghbrDomains[i] < noDomains(), "");
1319 recvHilbertIds[i].resize(noWindows[m_nghbrDomains[i]]);
1320 }
1321 for(MInt i = 0; i < noNeighborDomains(); i++) {
1322 for(MInt k = 0; k < noHalos[m_nghbrDomains[i]]; k++) {
1323 ASSERT(hilbertIds[i][k] >= hilbertOffsets[m_nghbrDomains[i]]
1324 && hilbertIds[i][k] < hilbertOffsets[m_nghbrDomains[i] + 1],
1325 to_string(hilbertIds[i][k]) + " " + to_string(hilbertOffsets[m_nghbrDomains[i]]) + " "
1326 + to_string(hilbertOffsets[m_nghbrDomains[i] + 1]));
1327 }
1328 }
1329 for(MInt i = 0; i < noNeighborDomains(); i++) {
1330 MPI_Issend(&hilbertIds[i][0], noHalos[m_nghbrDomains[i]], MPI_LONG, m_nghbrDomains[i], 34, mpiComm(), &sendReq[i],
1331 AT_, "hilbertIds[i][0]");
1332 }
1333 for(MInt i = 0; i < noNeighborDomains(); i++) {
1334 MPI_Recv(&recvHilbertIds[i][0], noWindows[m_nghbrDomains[i]], MPI_LONG, m_nghbrDomains[i], 34, mpiComm(),
1335 MPI_STATUS_IGNORE, AT_, "recvHilbertIds[i][0]");
1336 }
1337 if(noNeighborDomains() > 0) MPI_Waitall(noNeighborDomains(), &sendReq[0], MPI_STATUSES_IGNORE, AT_);
1338 for(MInt i = 0; i < noNeighborDomains(); i++) {
1339 for(MInt k = 0; k < noWindows[m_nghbrDomains[i]]; k++) {
1340 ASSERT(recvHilbertIds[i][k] >= hilbertOffsets[domainId()]
1341 && recvHilbertIds[i][k] < hilbertOffsets[domainId() + 1],
1342 "");
1343 }
1344 }
1345
1346 vector<vector<MLong>> sendGlobalIds;
1347 vector<vector<MLong>> recvGlobalIds;
1348 sendGlobalIds.resize(noNeighborDomains());
1349 recvGlobalIds.resize(noNeighborDomains());
1350 for(MInt i = 0; i < noNeighborDomains(); i++) {
1351 ASSERT(m_nghbrDomains[i] > -1 && m_nghbrDomains[i] < noDomains(), "");
1352 sendGlobalIds[i].resize(noWindows[m_nghbrDomains[i]]);
1353 recvGlobalIds[i].resize(noHalos[m_nghbrDomains[i]]);
1354 }
1355
1356 for(MInt i = 0; i < noNeighborDomains(); i++) {
1357 m_windowCells.push_back(vector<MInt>());
1358 for(MInt k = 0; k < noWindows[m_nghbrDomains[i]]; k++) {
1359 MInt windowId = -1;
1360 auto range = hilbertToLocal.equal_range(recvHilbertIds[i][k]);
1361 for(auto it = range.first; it != range.second; ++it) {
1362 if(!a_hasProperty(it->second, Cell::IsPeriodic) && !a_hasProperty(it->second, Cell::IsHalo)) {
1363 windowId = it->second;
1364 }
1365 }
1366 ASSERT(windowId > -2 && windowId < m_noInternalCells, to_string(windowId) + " " + to_string(m_noInternalCells));
1367 if(windowId > -1) {
1368 ASSERT(a_level(windowId) == m_minLevel, "");
1369 m_windowCells[i].push_back(windowId);
1370 sendGlobalIds[i][k] = a_globalId(windowId);
1371 } else {
1372 sendGlobalIds[i][k] = -1;
1373 }
1374 }
1375 }
1376
1377 // send back globalId when the queried cell exists, -1 if not existing
1378 sendReq.fill(MPI_REQUEST_NULL);
1379 for(MInt i = 0; i < noNeighborDomains(); i++) {
1380 MPI_Issend(&sendGlobalIds[i][0], noWindows[m_nghbrDomains[i]], MPI_LONG, m_nghbrDomains[i], 35, mpiComm(),
1381 &sendReq[i], AT_, "sendGlobalIds[i][0]");
1382 }
1383 for(MInt i = 0; i < noNeighborDomains(); i++) {
1384 MPI_Recv(&recvGlobalIds[i][0], noHalos[m_nghbrDomains[i]], MPI_LONG, m_nghbrDomains[i], 35, mpiComm(),
1385 MPI_STATUS_IGNORE, AT_, "recvGlobalIds[i][0]");
1386 }
1387 if(noNeighborDomains() > 0) MPI_Waitall(noNeighborDomains(), &sendReq[0], MPI_STATUSES_IGNORE, AT_);
1388
1389 {
1390 ScratchSpace<MInt> posMapping(m_tree.size(), AT_, "posMapping");
1391 posMapping.fill(-1);
1392 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
1393 posMapping[cellId] = cellId;
1394 }
1395
1396 ScratchSpace<MInt> delFlag(m_tree.size(), AT_, "delFlag");
1397 delFlag.fill(0);
1398 for(MInt i = 0; i < noNeighborDomains(); i++) {
1399 for(MInt k = 0; k < noHalos[m_nghbrDomains[i]]; k++) {
1400 MInt cellId = halos[i][k];
1401 MLong globalId = recvGlobalIds[i][k];
1402 a_globalId(cellId) = globalId;
1403 if(globalId < 0) {
1404 ASSERT(cellId >= oldNoCells, "Attempting to delete parent gap halo cell.");
1405 delFlag[cellId] = 1;
1406 posMapping[cellId] = -1;
1407 }
1408 }
1409 }
1410
1411 for(MInt cellId = oldNoCells; cellId < m_tree.size(); cellId++) {
1412 if(delFlag[cellId]) {
1413 MInt otherCellId = deleteCell(cellId);
1414 if(otherCellId > -1) {
1415 ASSERT(delFlag[otherCellId] || posMapping[otherCellId] == otherCellId, "");
1416 if(!delFlag[otherCellId]) posMapping[otherCellId] = cellId;
1417 delFlag[cellId] = delFlag[otherCellId];
1418 delFlag[otherCellId] = 0;
1419 cellId--;
1420 }
1421 }
1422 }
1423
1424 for(MInt i = 0; i < noNeighborDomains(); i++) {
1425 m_haloCells.push_back(vector<MInt>());
1426 for(MInt k = 0; k < noHalos[m_nghbrDomains[i]]; k++) {
1427 MLong globalId = recvGlobalIds[i][k];
1428 if(globalId > -1) {
1429 ASSERT(posMapping[halos[i][k]] > -1, "");
1430 MInt cellId = posMapping[halos[i][k]];
1431 ASSERT(cellId > -1 && cellId < m_tree.size(), "");
1432 ASSERT(a_globalId(cellId) == globalId, "");
1433 m_haloCells[i].push_back(cellId);
1434 }
1435 }
1436 }
1437 }
1438
1439 for(MInt i = 0; i < nDim; i++) {
1440 if(m_periodicCartesianDir[i] > 0) {
1441 m_periodicCartesianLength[i] = bbox[nDim + i] - bbox[i];
1442 } else {
1443#ifdef MAIA_PGI_COMPILER
1444 m_periodicCartesianLength[i] = numeric_limits<MFloat>::quiet_NaN();
1445#else
1446 m_periodicCartesianLength[i] = numeric_limits<MFloat>::signaling_NaN();
1447#endif
1448 }
1449 }
1450
1451 IF_CONSTEXPR(nDim == 3) {
1452 if(m_azimuthalPer) {
1453 // First the azimuthal bounding box is determined
1454 // It is used determine where azimuthal halos cells are required
1455 for(MInt i = 0; i < nDim; i++) {
1456 MFloat dirLength = bbox[i + nDim] - bbox[i];
1457 MInt nBins = (MInt)(dirLength / cellLengthAtLevel(m_minLevel));
1458 m_azimuthalBbox.minCoord[i] = bbox[i];
1459 m_azimuthalBbox.boxLength[i] = dirLength;
1460 m_azimuthalBbox.nBins[i] = nBins;
1461 }
1462
1463 m_azimuthalBbox.init(m_azimuthalAngle, m_azimuthalPeriodicDir[0], m_azimuthalPeriodicDir[1], m_noHaloLayers);
1464 MInt perSize1 = m_azimuthalBbox.nBins[m_azimuthalBbox.perDir1];
1465 MInt perSize2 = m_azimuthalBbox.nBins[m_azimuthalBbox.perDir2];
1466 MFloatScratchSpace minPer1(perSize1, AT_, "minPer1");
1467 minPer1.fill(std::numeric_limits<MFloat>::max());
1468 MFloatScratchSpace maxPer1(perSize1, AT_, "maxPer1");
1469 maxPer1.fill(-std::numeric_limits<MFloat>::max());
1470 MFloatScratchSpace minPer2(perSize2, AT_, "minPer2");
1471 minPer2.fill(std::numeric_limits<MFloat>::max());
1472 MFloatScratchSpace maxPer2(perSize2, AT_, "maxPer2");
1473 maxPer2.fill(-std::numeric_limits<MFloat>::max());
1474
1475 MFloatScratchSpace minPer3(perSize1 * perSize2, AT_, "minPer3");
1476 minPer3.fill(std::numeric_limits<MFloat>::max());
1477 MFloatScratchSpace maxPer3(perSize1 * perSize2, AT_, "maxPer3");
1478 maxPer3.fill(-std::numeric_limits<MFloat>::max());
1479
1480 MFloat coordsCyl[3];
1481 for(MInt cellId = 0; cellId < m_noInternalCells; cellId++) {
1482 if(a_level(cellId) != m_minLevel) continue;
1483
1484 MInt ind1 = m_azimuthalBbox.index(&a_coordinate(cellId, 0), m_azimuthalBbox.perDir1);
1485 MInt ind2 = m_azimuthalBbox.index(&a_coordinate(cellId, 0), m_azimuthalBbox.perDir2);
1486 cartesianToCylindric(&a_coordinate(cellId, 0), coordsCyl);
1487 minPer1[ind1] = mMin(coordsCyl[1], minPer1[ind1]);
1488 maxPer1[ind1] = mMax(coordsCyl[1], maxPer1[ind1]);
1489 minPer2[ind2] = mMin(coordsCyl[1], minPer2[ind2]);
1490 maxPer2[ind2] = mMax(coordsCyl[1], maxPer2[ind2]);
1491
1492 minPer3[ind1 * perSize2 + ind2] = mMin(coordsCyl[1], minPer3[ind1 * perSize2 + ind2]);
1493 maxPer3[ind1 * perSize2 + ind2] = mMax(coordsCyl[1], maxPer3[ind1 * perSize2 + ind2]);
1494 }
1495 MPI_Allreduce(MPI_IN_PLACE, &minPer1[0], perSize1, MPI_DOUBLE, MPI_MIN, mpiComm(), AT_, "MPI_IN_PLACE",
1496 "minPer1[0]");
1497 MPI_Allreduce(MPI_IN_PLACE, &maxPer1[1], perSize1, MPI_DOUBLE, MPI_MAX, mpiComm(), AT_, "MPI_IN_PLACE",
1498 "maxPer1[0]");
1499 MPI_Allreduce(MPI_IN_PLACE, &minPer2[0], perSize2, MPI_DOUBLE, MPI_MIN, mpiComm(), AT_, "MPI_IN_PLACE",
1500 "minPer2[0]");
1501 MPI_Allreduce(MPI_IN_PLACE, &maxPer2[1], perSize2, MPI_DOUBLE, MPI_MAX, mpiComm(), AT_, "MPI_IN_PLACE",
1502 "maxPer2[0]");
1503
1504 MPI_Allreduce(MPI_IN_PLACE, &minPer3[0], perSize1 * perSize2, MPI_DOUBLE, MPI_MIN, mpiComm(), AT_, "MPI_IN_PLACE",
1505 "minPer3[0]");
1506 MPI_Allreduce(MPI_IN_PLACE, &maxPer3[1], perSize1 * perSize2, MPI_DOUBLE, MPI_MAX, mpiComm(), AT_, "MPI_IN_PLACE",
1507 "maxPer3[0]");
1508
1509 MFloat minPhi = minPer1[0];
1510 MFloat maxPhi = maxPer1[0];
1511 for(MInt p = 0; p < perSize1; p++) {
1512 minPhi = mMin(minPhi, minPer1[p]);
1513 maxPhi = mMax(maxPhi, maxPer1[p]);
1514 }
1515 for(MInt p = 0; p < perSize2; p++) {
1516 minPhi = mMin(minPhi, minPer2[p]);
1517 maxPhi = mMax(maxPhi, maxPer2[p]);
1518 }
1519 MFloat center = F1B2 * (minPhi + maxPhi);
1520
1521 std::copy_n(&minPer3[0], perSize1 * perSize2, &m_azimuthalBbox.minPer3[0]);
1522 std::copy_n(&maxPer3[0], perSize1 * perSize2, &m_azimuthalBbox.maxPer3[0]);
1523 m_azimuthalBbox.center = center;
1524
1525 // Now create the azimuthal periodic halo cells
1526 noHalos.fill(0);
1527 noWindows.fill(0);
1528 for(MInt i = 0; i < noNeighborDomains(); i++) {
1529 halos[i].clear();
1530 hilbertIds[i].clear();
1531 }
1532 hilbertToLocal.clear();
1533 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
1534 if(a_level(cellId) != m_minLevel) continue;
1535 const MLong hilbertId = hilbertIndexGeneric(&a_coordinate(cellId, 0));
1536 hilbertToLocal.insert(make_pair(hilbertId, cellId));
1537 }
1538
1539 // find direct and diagonal neighboring cells
1540 cnt = 0;
1541 ScratchSpace<MInt> cellLayer(maxNoAdjacentCells, AT_, "cellLayer");
1542 while(cnt < interfaceCells.size()) {
1543 fill(&ifcDirs[0], &ifcDirs[0] + m_noDirs, 0);
1544 const MInt cellId = interfaceCells[cnt].first;
1545 while(cnt < interfaceCells.size() && cellId == interfaceCells[cnt].first) {
1546 ifcDirs[interfaceCells[cnt].second] = 1;
1547 cnt++;
1548 }
1549 MInt cellCnt = 0;
1550 cellList[cellCnt] = cellId;
1551 cellLayer[cellCnt++] = 0;
1552 for(MInt dim = 0; dim < nDim; dim++) {
1553 const MInt cellCnt0 = cellCnt;
1554 for(MInt ori = 0; ori < 2; ori++) {
1555 const MInt dir = 2 * dim + ori;
1556 if(!ifcDirs[dir]) continue;
1557 for(MInt c = 0; c < cellCnt0; c++) {
1558 MInt nextId = cellList[c];
1559 MInt startLayer = cellLayer[c];
1560 for(MInt layer = startLayer; layer < m_noHaloLayers; layer++) {
1561 if(a_hasNeighbor(nextId, dir) > 0)
1562 nextId = a_neighborId(nextId, dir);
1563 else
1564 nextId = createAzimuthalHaloCell(nextId, dir, &hilbertOffsets[0], hilbertToLocal, bbox, &noHalos[0],
1565 halos, hilbertIds);
1566 if(nextId < 0) break;
1567 cellList[cellCnt] = nextId;
1568 cellLayer[cellCnt++] = layer;
1569 }
1570 }
1571 }
1572 }
1573 }
1574
1575 MPI_Alltoall(&noHalos[0], 1, MPI_INT, &noWindows[0], 1, MPI_INT, mpiComm(), AT_, "noHalos[0]", "noWindows[0]");
1576
1577 for(MInt i = 0; i < noDomains(); i++) {
1578 if(noHalos[i] == 0 && noWindows[i] > 0) {
1579 setAzimuthalNeighborDomainIndex(i, halos, hilbertIds);
1580 }
1581 }
1582
1583 ScratchSpace<MPI_Request> sendReqAzi(noAzimuthalNeighborDomains(), AT_, "sendReqAzi");
1584 sendReqAzi.fill(MPI_REQUEST_NULL);
1585 recvHilbertIds.resize(noAzimuthalNeighborDomains());
1586 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
1587 ASSERT(m_azimuthalNghbrDomains[i] > -1 && m_azimuthalNghbrDomains[i] < noDomains(), "");
1588 recvHilbertIds[i].resize(noWindows[m_azimuthalNghbrDomains[i]]);
1589 }
1590 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
1591 for(MInt k = 0; k < noHalos[m_azimuthalNghbrDomains[i]]; k++) {
1592 ASSERT(hilbertIds[i][k] >= hilbertOffsets[m_azimuthalNghbrDomains[i]]
1593 && hilbertIds[i][k] < hilbertOffsets[m_azimuthalNghbrDomains[i] + 1],
1594 to_string(hilbertIds[i][k]) + " " + to_string(hilbertOffsets[m_azimuthalNghbrDomains[i]]) + " "
1595 + to_string(hilbertOffsets[m_azimuthalNghbrDomains[i] + 1]));
1596 }
1597 }
1598 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
1599 MPI_Issend(&hilbertIds[i][0], noHalos[m_azimuthalNghbrDomains[i]], MPI_LONG, m_azimuthalNghbrDomains[i], 34,
1600 mpiComm(), &sendReqAzi[i], AT_, "hilbertIds[i][0]");
1601 }
1602 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
1603 MPI_Recv(&recvHilbertIds[i][0], noWindows[m_azimuthalNghbrDomains[i]], MPI_LONG, m_azimuthalNghbrDomains[i], 34,
1604 mpiComm(), MPI_STATUS_IGNORE, AT_, "recvHilbertIds[i][0]");
1605 }
1606 if(noAzimuthalNeighborDomains() > 0)
1607 MPI_Waitall(noAzimuthalNeighborDomains(), &sendReqAzi[0], MPI_STATUSES_IGNORE, AT_);
1608 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
1609 for(MInt k = 0; k < noWindows[m_azimuthalNghbrDomains[i]]; k++) {
1610 ASSERT(recvHilbertIds[i][k] >= hilbertOffsets[domainId()]
1611 && recvHilbertIds[i][k] < hilbertOffsets[domainId() + 1],
1612 "");
1613 }
1614 }
1615
1616 sendGlobalIds.resize(noAzimuthalNeighborDomains());
1617 recvGlobalIds.resize(noAzimuthalNeighborDomains());
1618 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
1619 ASSERT(m_azimuthalNghbrDomains[i] > -1 && m_azimuthalNghbrDomains[i] < noDomains(), "");
1620 sendGlobalIds[i].resize(noWindows[m_azimuthalNghbrDomains[i]]);
1621 recvGlobalIds[i].resize(noHalos[m_azimuthalNghbrDomains[i]]);
1622 }
1623
1624 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
1625 m_azimuthalWindowCells.push_back(vector<MInt>());
1626 for(MInt k = 0; k < noWindows[m_azimuthalNghbrDomains[i]]; k++) {
1627 MInt windowId = -1;
1628 auto range = hilbertToLocal.equal_range(recvHilbertIds[i][k]);
1629 for(auto it = range.first; it != range.second; ++it) {
1630 if(!a_hasProperty(it->second, Cell::IsPeriodic) && !a_hasProperty(it->second, Cell::IsHalo)) {
1631 windowId = it->second;
1632 }
1633 }
1634 ASSERT(windowId > -2 && windowId < m_noInternalCells,
1635 to_string(windowId) + " " + to_string(m_noInternalCells));
1636 if(windowId > -1) {
1637 ASSERT(a_level(windowId) == m_minLevel, "");
1638 m_azimuthalWindowCells[i].push_back(windowId);
1639 sendGlobalIds[i][k] = a_globalId(windowId);
1640 } else {
1641 sendGlobalIds[i][k] = -1;
1642 }
1643 }
1644 }
1645
1646 // send back globalId when the queried cell exists, -1 if not existing
1647 sendReqAzi.fill(MPI_REQUEST_NULL);
1648 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
1649 MPI_Issend(&sendGlobalIds[i][0], noWindows[m_azimuthalNghbrDomains[i]], MPI_LONG, m_azimuthalNghbrDomains[i],
1650 35, mpiComm(), &sendReqAzi[i], AT_, "sendGlobalIds[i][0]");
1651 }
1652 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
1653 MPI_Recv(&recvGlobalIds[i][0], noHalos[m_azimuthalNghbrDomains[i]], MPI_LONG, m_azimuthalNghbrDomains[i], 35,
1654 mpiComm(), MPI_STATUS_IGNORE, AT_, "recvGlobalIds[i][0]");
1655 }
1656 if(noAzimuthalNeighborDomains() > 0)
1657 MPI_Waitall(noAzimuthalNeighborDomains(), &sendReqAzi[0], MPI_STATUSES_IGNORE, AT_);
1658
1659 {
1660 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
1661 m_azimuthalHaloCells.push_back(vector<MInt>());
1662 for(MInt k = 0; k < noHalos[m_azimuthalNghbrDomains[i]]; k++) {
1663 MInt cellId = halos[i][k];
1664 MLong globalId = recvGlobalIds[i][k];
1665 if(globalId > -1) {
1666 ASSERT(cellId > -1 && cellId < m_tree.size(), "");
1667 a_globalId(cellId) = globalId;
1668 m_azimuthalHaloCells[i].push_back(cellId);
1669 } else { // Still keep it! Cell might be necessary. Solver flag will be adjusted in proxy
1670 ASSERT(cellId > -1 && cellId < m_tree.size(), "");
1671 a_globalId(cellId) = -1;
1672 m_azimuthalUnmappedHaloCells.push_back(cellId);
1673 m_azimuthalUnmappedHaloDomains.push_back(m_azimuthalNghbrDomains[i]);
1674 }
1675 }
1676 }
1677 }
1678
1679 // Determine the gridBndryCells. These cells are the outer cells of the grid
1680 // Tagging them is necessary for the refinement of the azimuthal periodic halo cells
1681 std::vector<MInt>().swap(m_gridBndryCells);
1682 MBool adaptation = false;
1683 adaptation = Context::getBasicProperty<MBool>("adaptation", AT_, &adaptation);
1684 if(adaptation) {
1685 if(domainId() == 0)
1686 cerr << endl << "Azimuthal periodic cells are not refined at grid bndry if adaption is on!" << endl;
1687 } else {
1688 if(domainId() == 0) cerr << "Set gridBndryCells on minLevel." << endl;
1689 MIntScratchSpace gridBndryCells(m_tree.size(), AT_, "gridBndryCells");
1690 gridBndryCells.fill(-1);
1691 MBool isBndry = false;
1692 for(MInt cellId = 0; cellId < oldNoCells; cellId++) {
1693 if(a_level(cellId) != m_minLevel) continue;
1694 isBndry = false;
1695 for(MInt dir = 0; dir < m_noDirs; dir++) {
1696 if(a_hasNeighbor(cellId, dir) > 0) continue;
1697 if(!m_periodicCartesianDir[dir / 2]) {
1698 MFloat coords[nDim];
1699 for(MInt d = 0; d < nDim; d++) {
1700 coords[d] = a_coordinate(cellId, d);
1701 }
1702 coords[dir / 2] += ((dir % 2) == 0 ? -F1 : F1) * cellLengthAtLevel(m_minLevel);
1703 if(coords[dir / 2] < bbox[dir / 2] || coords[dir / 2] > bbox[nDim + dir / 2]) continue;
1704 }
1705 isBndry = true;
1706 }
1707 if(isBndry) {
1708 if(!a_isHalo(cellId)) m_gridBndryCells.push_back(cellId);
1709 gridBndryCells[cellId] = 1;
1710 for(MInt dir = 0; dir < m_noDirs; dir++) {
1711 if(a_hasNeighbor(cellId, dir) > 0) {
1712 MInt nghbrId = a_neighborId(cellId, dir);
1713 if(!a_isHalo(nghbrId)) m_gridBndryCells.push_back(nghbrId);
1714 gridBndryCells[nghbrId] = 1;
1715 }
1716 }
1717 }
1718 }
1719 maia::mpi::exchangeData(m_nghbrDomains, m_windowCells, m_haloCells, mpiComm(), gridBndryCells.data());
1720 for(MInt i = 0; i < noNeighborDomains(); i++) {
1721 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
1722 if(gridBndryCells[m_windowCells[i][j]] == 1) {
1723 m_gridBndryCells.push_back(m_windowCells[i][j]);
1724 }
1725 }
1726 }
1727 }
1728 }
1729 }
1730
1731 // It's very polite to clear the data, but we don't need it
1732 /* halos.clear();
1733 hilbertIds.clear();
1734 recvHilbertIds.clear();
1735 hilbertToLocal.clear();*/
1736
1737 // Set window/halo flags
1738 for(MInt i = 0; i < noNeighborDomains(); i++) {
1739 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
1740 ASSERT(!a_hasProperty(m_haloCells[i][j], Cell::IsWindow), "halo cell is marked as window");
1741 a_hasProperty(m_haloCells[i][j], Cell::IsHalo) = true;
1742 }
1743 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
1744 ASSERT(!a_isHalo(m_windowCells[i][j]), "window cell is marked as halo");
1745 a_hasProperty(m_windowCells[i][j], Cell::IsWindow) = true;
1746 }
1747 }
1748 if(m_azimuthalPer) {
1749 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
1750 for(MInt j = 0; j < (signed)m_azimuthalHaloCells[i].size(); j++) {
1751 ASSERT(!a_hasProperty(m_azimuthalHaloCells[i][j], Cell::IsWindow), "azimuthal halo cell is marked as window");
1752 a_hasProperty(m_azimuthalHaloCells[i][j], Cell::IsHalo) = true;
1753 }
1754 for(MInt j = 0; j < (signed)m_azimuthalWindowCells[i].size(); j++) {
1755 ASSERT(!a_isHalo(m_azimuthalWindowCells[i][j]), "azimuthal window cell is marked as halo");
1756 a_hasProperty(m_azimuthalWindowCells[i][j], Cell::IsWindow) = true;
1757 }
1758 }
1759 for(MInt j = 0; j < noAzimuthalUnmappedHaloCells(); j++) {
1760 ASSERT(!a_hasProperty(m_azimuthalUnmappedHaloCells[j], Cell::IsWindow),
1761 "azimuthal halo cell is marked as window");
1762 a_hasProperty(m_azimuthalUnmappedHaloCells[j], Cell::IsHalo) = true;
1763 }
1764 }
1765
1766 if(m_haloMode > 0) {
1767 tagActiveWindowsAtMinLevel(m_minLevel);
1768#ifndef NDEBUG
1769 checkWindowLayer("tagActiveWindowsAtMinLevel completed: ");
1770#endif
1771 // Exchange solverBits also in case of 1 solver, because solverBits are used to disable halo cells, which
1772 // are created, but in tagActiveWindowsAtMinLevel seen to be superfluous
1773 // if(treeb().noSolvers() > 1) {
1774 exchangeSolverBitset(&m_tree.solverBits(0));
1775 //}
1776
1777 if(m_haloMode == 2) {
1778 for(MInt i = 0; i < noNeighborDomains(); i++) {
1779 std::vector<MInt> haloCells;
1780 haloCells.reserve(m_haloCells[i].size());
1781 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
1782 const MInt haloCellId = m_haloCells[i][j];
1783 if(m_tree.solverBits(haloCellId).any())
1784 haloCells.push_back(haloCellId);
1785 else {
1786 removeCell<false>(haloCellId);
1787 }
1788 }
1789 m_haloCells[i] = haloCells;
1790
1791 //
1792 std::vector<MInt> windowCells;
1793 windowCells.reserve(m_windowCells[i].size());
1794 std::set<MInt> erased;
1795 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
1796 const MInt windowCellId = m_windowCells[i][j];
1797 ASSERT(m_windowLayer_[i].find(windowCellId) != m_windowLayer_[i].end()
1798 || erased.find(windowCellId) != erased.end(),
1799 "");
1800 MBool validWindow = false;
1801 for(MInt solverId = 0; solverId < treeb().noSolvers(); ++solverId) {
1802 if(isSolverWindowCell(i, windowCellId, solverId)) {
1803 windowCells.push_back(windowCellId);
1804 validWindow = true;
1805 break;
1806 }
1807 }
1808 if(!validWindow) {
1809 const MBool ok = m_windowLayer_[i].erase(windowCellId);
1810 ASSERT(ok || erased.find(windowCellId) != erased.end(), "");
1811 erased.insert(windowCellId);
1812 MBool isWindow = false;
1813 for(const auto& w : m_windowLayer_) {
1814 if(w.find(windowCellId) != w.end()) {
1815 isWindow = true;
1816 break;
1817 }
1818 }
1819 if(!isWindow) a_hasProperty(windowCellId, Cell::IsWindow) = false;
1820 }
1821 }
1822 m_windowCells[i] = windowCells;
1823 }
1824
1825 compactCells();
1826 }
1827
1828#ifndef NDEBUG
1829 // for debugging, check consistency before new level
1830 checkWindowHaloConsistency(false, "createMinLevelExchangeCells completed: ");
1831#endif
1832 }
1833
1834
1835 cerr0 << " done." << endl;
1836}
1837
1838
1839//-------------------------------------------------------------------------------------------
1840
1846// clang-format off
1847template <MInt nDim>
1849 const MInt onlyLevel, const MBool duringMeshAdaptation,
1850 const std::vector<std::function<void(const MInt)>>& refineCellSolver, const std::vector<std::function<void(const MInt)>>& removeCellSolver, const MBool forceLeafLvlCorrection) {
1851 TRACE();
1852
1853 auto logDuration = [this] (const MFloat timeStart, const MString comment) {
1854 logDuration_(timeStart, "WINDOW/HALO HIGHER LEVEL", comment, mpiComm(), domainId(), noDomains());
1855 };
1856
1857 ScratchSpace<MInt> plaMap(m_maxNoCells, AT_, "plaMap");
1858 vector<MLong> refineChildIds;
1859 vector<MLong> recvChildIds;
1860 vector<vector<MInt>> haloMap;
1861 vector<vector<MInt>> windMap;
1862 vector<MInt> refineIds;
1863
1864 if(domainId() == 0 && onlyLevel < 0) cerr << " * create higher level exchange cells...";
1865
1866 plaMap.fill(-1);
1867 if(m_maxPartitionLevelShift > 0) {
1868 for(MUint i = 0; i < m_partitionLevelAncestorIds.size(); i++) {
1869 plaMap[m_partitionLevelAncestorIds[i]] = i;
1870 }
1871 }
1872
1873 if(m_azimuthalPer && onlyLevel <= m_minLevel) {
1874 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
1875 m_azimuthalHigherLevelConnectivity[i].clear();
1876 }
1877 }
1878
1879 // given window-halo cell connectivity on minLevel established in setupWindowHaloCellConnectivity(),
1880 // iteratively create connectivity on ascending levels
1881 for(MInt level = m_minLevel; level < m_maxLevel; level++) {
1882 if(onlyLevel > -1 && onlyLevel != level) continue;
1883
1884 const MFloat levelTimeStart = wallTime();
1885#ifndef NDEBUG
1886 // for debugging, check consistency before new level
1887 checkWindowHaloConsistency(false, "createHigherLevelExchangeCells level=" + to_string(level) + ": ");
1888#endif
1889
1890 refineIds.clear();
1891 MInt haloCnt = 0;
1892 MInt windowCnt = 0;
1893 haloMap.resize(noNeighborDomains());
1894 windMap.resize(noNeighborDomains());
1895
1896 // Sets to hold halo/window cells for each neighbor domain to allow fast searching if cells are
1897 // already present
1898 vector<std::set<MInt>> haloCellSet(noNeighborDomains());
1899 vector<std::set<MInt>> windCellSet(noNeighborDomains());
1900
1901 for(MInt i = 0; i < noNeighborDomains(); i++) {
1902 haloMap[i].resize(m_haloCells[i].size());
1903 windMap[i].resize(m_windowCells[i].size());
1904 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
1905 haloMap[i][j] = haloCnt++;
1906 }
1907 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
1908 windMap[i][j] = windowCnt++;
1909 }
1910
1911 haloCellSet[i].insert(m_haloCells[i].begin(), m_haloCells[i].end());
1912 windCellSet[i].insert(m_windowCells[i].begin(), m_windowCells[i].end());
1913 }
1914
1915 recvChildIds.resize(haloCnt * m_maxNoChilds);
1916 fill(recvChildIds.begin(), recvChildIds.end(), -1);
1917 refineChildIds.clear();
1918 refineChildIds.resize(windowCnt * m_maxNoChilds);
1919 fill(refineChildIds.begin(), refineChildIds.end(), -1);
1920
1921#ifndef NDEBUG
1922 for (MInt cellId = 0; cellId < m_tree.size(); ++cellId) {
1923 if (!a_isToDelete(cellId)) {
1924 if (a_hasProperty(cellId, Cell::IsPartLvlAncestor) && !a_hasProperty(cellId, Cell::IsPeriodic)) {
1925 const MInt minNghbrDomId = findNeighborDomainId(a_globalId(cellId));
1926 const MInt maxNghbrDomId =
1927 findNeighborDomainId(mMin(m_noCellsGlobal - 1, a_globalId(cellId) + (MLong)a_noOffsprings(cellId)-1));
1928 if (domainId()>=minNghbrDomId && domainId()<=maxNghbrDomId) {
1929 TERMM_IF_NOT_COND(a_hasChildren(cellId), "d=" + to_string(domainId()) + " nghbrs="
1930 + to_string(minNghbrDomId) + "|" + to_string(maxNghbrDomId) + " isHalo=" + to_string(a_isHalo(cellId)));
1931 }
1932 }
1933 }
1934 }
1935
1936 TERMM_IF_COND(bitOffset() && m_maxPartitionLevelShift>0, "Not a good idea to use bitOffset==true & partLvlShift!");
1937 if (!bitOffset()) {
1938 for(MInt i = 0; i < noNeighborDomains(); i++) {
1939 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
1940 const MInt cellId = m_windowCells[i][j];
1941
1942 if (!a_hasProperty(cellId, Cell::IsPartLvlAncestor)) {
1943 const MInt minNghbrDomId = findNeighborDomainId(a_globalId(cellId));
1944 // I don't know why but some cells in testcase LS/3D_minLevelIncrease have a_noOffsprings(cellId)==0
1945 const MInt maxNghbrDomId =
1946 findNeighborDomainId(mMin(m_noCellsGlobal - 1, a_globalId(cellId) + (MLong)std::max(1,a_noOffsprings(cellId))-1));
1947 TERMM_IF_NOT_COND(minNghbrDomId==maxNghbrDomId && minNghbrDomId==domainId(),
1948 "Window cell is not partLvlAncestor, but seems to have children on different domains!");
1949 }
1950 }
1951 }
1952 }
1953#endif
1954
1956 // In the following we provide all window cells, whose parent cell is partLvlAncestor and has children on more
1957 // then one domain, with the information they need and store it in the variable 'windowTemp'
1958
1959 // In periodic case we might need to add same window cell twice, therefore multimap. Save for all
1960 // window children of partLvlAncestor parents with children on more then one domain the connectivity to all halos.
1961 std::multimap<std::pair<MInt/*nghbrDomainIdx*/,MInt/*cellId*/>, M32X4bit<true>> windowsTemp;
1962 if (m_maxPartitionLevelShift>0) {
1963 // 1) Refine all partLvlAncestor cells with level+1 children on more than one domain
1964 std::set<MInt> partLvlAncSet;
1965 for(MUint i = 0; i < m_partitionLevelAncestorIds.size(); i++) {
1966 const MInt cellId = m_partitionLevelAncestorIds[i];
1967 if (a_level(cellId)!=level) continue;
1968
1969 ASSERT(a_hasProperty(cellId, Cell::IsPartLvlAncestor),
1970 "not a partition level ancestor: cellId" + std::to_string(cellId) + " halo"
1971 + std::to_string(a_hasProperty(cellId, Cell::IsHalo)) + " domain" + std::to_string(domainId()));
1972 ASSERT(a_hasProperty(cellId, Cell::IsHalo) || findNeighborDomainId(a_globalId(cellId)) == domainId(), "");
1973 ASSERT(a_noChildren(cellId)>0, "PartLvlAncestor cell must have children!");
1974 ASSERT((signed)i==plaMap[cellId], "");
1975
1976 // Guess: during meshAdaptation partLvlAncestor cells may already have halo children
1977 // Note: if cell is halo cell, we may have the situation that neither of the children on level+1 is on current domain
1978 std::set<MInt> nghbrDomIds;
1979 for(MInt child = 0; child < m_maxNoChilds; child++) {
1980 if(m_partitionLevelAncestorChildIds[i * m_maxNoChilds + child] > -1) {
1981 nghbrDomIds.insert(findNeighborDomainId(m_partitionLevelAncestorChildIds[i * m_maxNoChilds + child]));
1982 }
1983 }
1984 const MBool multikultiCell = nghbrDomIds.size()>1;
1985
1986 const MInt noChildren = a_noChildren(cellId);
1987 if(multikultiCell) {
1988 partLvlAncSet.insert(cellId);
1989
1990 MLong* childIds = &m_partitionLevelAncestorChildIds[i * m_maxNoChilds];
1991 if(accumulate(childIds, childIds + m_maxNoChilds, static_cast<MLong>(-1),
1992 [](const MLong& a, const MLong& b) { return std::max(a, b); })
1993 > -1) {
1994 refineCell(cellId, childIds, a_hasProperty(cellId, Cell::IsPartLvlAncestor));
1995 // Note: If function is called during meshAdaptation all children should already exist,
1996 // i.e.a_noChildren(cellId)==noChildren
1997 if (a_noChildren(cellId)>noChildren)
1998 refineIds.push_back(cellId); //TODO_SS labels:GRID,totest is it correct? is it necessary?
1999
2000 for(MInt child = 0; child < m_maxNoChilds; child++) {
2001 const MInt childId = a_childId(cellId, child);
2002 if(childId < 0) continue;
2003
2004 ASSERT(childId > -1 && childId < m_tree.size(), to_string(childId) + " " + to_string(m_tree.size()));
2005 ASSERT(a_globalId(childId) > -1, "");
2006 const MInt ndom = findNeighborDomainId(a_globalId(childId));
2007 ASSERT(ndom > -1, "");
2008
2009 if (a_isHalo(childId)) {
2010 // There can be partLvlHalo childs and non-partLvlHalo childs (the latter are newly craeted ones)
2011 ASSERT(ndom!=domainId(), "");
2012 } else {
2013 ASSERT(ndom==domainId(), "");
2014 }
2015 //TODO_SS labels:GRID,totest check the following
2016 a_hasProperty(childId, Cell::IsPeriodic) = a_hasProperty(cellId, Cell::IsPeriodic);
2017 }
2018 // The following check fails during meshAdaptation
2019 //TERMM_IF_NOT_COND(a_noChildren(cellId)-noChildren==noHaloChilds, "Expected non-partLvlAncestor halo child!");
2020 // Debug output
2021/* stringstream ss;
2022 ss << " PartLvlAncestor Refinement: d=" << domainId() << " cellId=" << cellId << " (halo=" << a_isHalo(cellId)
2023 << "): Created " << noHaloChilds << " halos on level=" << level+1;
2024 for (MInt child = 0; child < m_maxNoChilds; child++) {
2025 ss << "\n\t1) childId=" << childInfos[3*child];
2026 if (childInfos[3*child+1]==domainId())
2027 ss << " isWindow";
2028 else
2029 ss << " isHalo nghbrDom=" << childInfos[3*child+1] << " isPartLvlHalo=" << childInfos[3*child+2];
2031 }*/
2032
2033 }
2034 }
2035 }
2036
2037 // 2)
2038 // Determine all pairs of neighbor domains to connect:
2039 // For simplicity, if a window cell has children on level+1, which reside on different domains, then the
2040 // window/halo connectivity of the window cell is propagated to all the children, meaning that if domains
2041 // 2,5,11,23 have current cell as halo cell, they also have all children as halo cells.
2042 // Determine window cells on current domain with children on level+1, which reside on different domain (*).
2043 // Determine all neighbors, which have current window cell as halo cell and inform domain (*) about these neighbors.
2044 vector<MInt> sendDomIdx;
2045 vector<M32X4bit<>::type> sendData;
2046 MBoolScratchSpace cellFlag(m_tree.size(), AT_, "cellFlag");
2047 fill(cellFlag.begin(), cellFlag.end(), false);
2048 for(MInt i = 0; i < noNeighborDomains(); i++) {
2049 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
2050 const MInt cellId = m_windowCells[i][j];
2051 ASSERT(m_windowLayer_[i].find(cellId)!=m_windowLayer_[i].end(), "");
2052
2053 if (partLvlAncSet.find(cellId)!=partLvlAncSet.end()) {
2054 ASSERT(a_level(cellId) == level && a_noChildren(cellId) > 0, "");
2055 // Save all domains which own at least one child of current cell
2056 std::set<MInt> nghbrs;
2057 // Boolean to verify that current window cell has at least one window child
2058 MBool windowChild = false;
2059 for(MInt child = 0; child < m_maxNoChilds; child++) {
2060 const MInt cnt = windMap[i][j];
2061
2062 // The purpose of the following is to tag childs which don't reside on current domain because of pls
2063 const MInt pId = plaMap[cellId];
2064 ASSERT(pId>-1, "");
2065 if(m_partitionLevelAncestorChildIds[pId * m_maxNoChilds + child] > -1) {
2066 refineChildIds[m_maxNoChilds * cnt + child] = m_partitionLevelAncestorChildIds[pId * m_maxNoChilds + child];
2067 const MInt domainChild = findNeighborDomainId(m_partitionLevelAncestorChildIds[pId * m_maxNoChilds + child]);
2068 if (domainChild != domainId()) {
2069 nghbrs.insert(domainChild);
2070 } else {
2071 windowChild = true;
2072 const MInt childId = a_childId(cellId, child);
2073 ASSERT(childId>-1, " ");
2074 // In periodic case duplicate window cells possible
2075 TERMM_IF_COND(windowsTemp.find(std::make_pair(m_nghbrDomains[i],childId))!=windowsTemp.end()
2076 && m_noPeriodicCartesianDirs==0, " ");
2077 M32X4bit<true> windowLayer = m_windowLayer_[i].find(childId)!=m_windowLayer_[i].end()
2078 ? m_windowLayer_[i].at(childId) : m_windowLayer_[i].at(cellId);//M32X4bit<true>{};
2079 windowsTemp.insert({std::make_pair(m_nghbrDomains[i],childId), windowLayer});
2080 }
2081 }
2082 }
2083 ASSERT(windowChild, "At least one child must reside on current domain!");
2084 ASSERT(nghbrs.size()>0, "At least one child must reside on a different domain!");
2085
2086 for (const auto ndom : nghbrs) {
2087 const MInt idx = findIndex(m_nghbrDomains.begin(), m_nghbrDomains.end(), ndom);
2088 ASSERT(idx > -1, "");
2089 ASSERT(m_nghbrDomains[idx] == ndom, "");
2090 // In periodic case a neighbor domain can have the cell twice, meaning that current window cell is sending
2091 // the data to two halo cells of the same domain;
2092 // In periodic case we can also have the situation that a child belongs to a neighbor domain,
2093 // and that neighbor domain also has that cell as a periodic halo cell.
2094 if (ndom != m_nghbrDomains[i] || m_noPeriodicCartesianDirs>0) {
2095 sendDomIdx.push_back(idx);
2096 sendData.push_back(m_nghbrDomains[i]);
2097 sendData.push_back(a_globalId(cellId));
2098 M32X4bit<true> windowLayer = m_windowLayer_[i].at(cellId);
2099 sendData.push_back(windowLayer.data());
2100 }
2101 if (!cellFlag[cellId]) {
2102 sendDomIdx.push_back(idx);
2103 sendData.push_back(domainId());
2104 sendData.push_back(a_globalId(cellId));
2105 M32X4bit<true> windowLayer = m_windowLayer_[i].at(cellId);
2106 sendData.push_back(windowLayer.data());
2107 }
2108 }
2109 cellFlag[cellId] = true;
2110 }
2111 }
2112 }
2113
2114 // exchange redirected communication info
2115 vector<MInt> recvOffs;
2116 vector<M32X4bit<>::type> recvData;
2117 maia::mpi::exchangeScattered(m_nghbrDomains, sendDomIdx, sendData, mpiComm(), recvOffs, recvData, 3);
2118
2119 // create window cells if this domain is affected by redirected communication links
2120 const MInt noNgbrDomainsBak = noNeighborDomains();
2121 for(MInt i = 0; i < noNgbrDomainsBak; i++) {
2122 set<MInt> check4PeriodicHalos;
2123 for(MInt j = recvOffs[i]; j < recvOffs[i + 1]; j++) {
2124 auto ndom = static_cast<MInt>(recvData[3 * j]);
2125 const MLong globalCellId = recvData[3 * j + 1];
2126 if (ndom==domainId()) { //might occur in periodic case
2127 // If in periodic case a window cell appears multiple times, skip it one time
2128 ASSERT(m_noPeriodicCartesianDirs>0, "");
2129 if (check4PeriodicHalos.find(globalCellId)==check4PeriodicHalos.end()) {
2130 check4PeriodicHalos.insert(globalCellId);
2131 continue;
2132 }
2133 }
2134 auto windowLayer = recvData[3 * j + 2];
2135 ASSERT(ndom>-1, "");
2136 ASSERT(m_globalToLocalId.find(globalCellId)!=m_globalToLocalId.end(), "");
2137 const MInt cellId = m_globalToLocalId[globalCellId];
2138 ASSERT(cellId > -1, "");
2139 ASSERT(a_globalId(cellId) == globalCellId, "");
2140 ASSERT(findNeighborDomainId(globalCellId) != domainId(), "");
2141// const MInt idx = setNeighborDomainIndex(ndom, m_haloCells, m_windowCells, m_windowLayer_);
2142// TERMM_IF_NOT_COND(idx > -1, "");
2143
2144 // New neighbor domain, create new cell sets
2145// if(idx == static_cast<MInt>(windCellSet.size())) {
2146// windCellSet.emplace_back();
2147// haloCellSet.emplace_back();
2148// }
2149
2150 //const MInt pId = plaMap[cellId];
2151 //TERMM_IF_NOT_COND(pId>-1, "");
2152
2153 MBool foundWindow = false;
2154 for(MInt child = 0; child < m_maxNoChilds; child++) {
2155 const MInt childId = a_childId(cellId, child);
2156 if(childId < 0 || a_isHalo(childId)) continue;
2157 foundWindow = true;
2158
2159 // In periodic case duplicate window cells possible
2160 ASSERT(windowsTemp.find(std::make_pair(ndom,childId))==windowsTemp.end()
2161 || m_noPeriodicCartesianDirs>0, " ");
2162
2163// m_windowCells[idx].push_back(childId);
2164// windCellSet[idx].insert(childId);
2165 windowsTemp.insert({std::make_pair(ndom,childId), M32X4bit<true>(windowLayer)});
2166 }
2167 ASSERT(foundWindow, "");
2168 }
2169 }
2170 }
2172
2173
2174 // since setNeighborDomainIndex is called in tagActiveWindows2_ we might get new neighbors, so save old state
2175 std::vector<MInt> nghbrDomains(m_nghbrDomains);
2176
2177 tagActiveWindows2_(refineChildIds, level);
2178
2179 // We might have added a neighbor domain
2180 haloCellSet.resize(noNeighborDomains());
2181 windCellSet.resize(noNeighborDomains());
2182
2183// set globalIds of each windowCells's child
2184#ifdef _OPENMP
2185#pragma omp parallel for
2186#endif
2187 for(MInt i = 0; i < noNeighborDomains(); i++) {
2188 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
2189 const MInt cellId = m_windowCells[i][j];
2190
2191 if(a_level(cellId) == level && a_noChildren(cellId) > 0) {
2192 for(MInt child = 0; child < m_maxNoChilds; child++) {
2193 const MInt cnt = windMap[i][j];
2194
2195 if(refineChildIds[m_maxNoChilds * cnt + child] > 0) {
2196 const MInt childId = a_childId(cellId, child);
2197 ASSERT(childId > -1, "");
2198 ASSERT((a_globalId(static_cast<MInt>(childId)) >= m_domainOffsets[domainId()]
2199 && a_globalId(static_cast<MInt>(childId)) < m_domainOffsets[domainId() + 1])
2200 || a_hasProperty(cellId, Cell::IsPartLvlAncestor),
2201 to_string(a_globalId(static_cast<MInt>(childId))) + " "
2202 + to_string(m_domainOffsets[domainId()]) + " " + to_string(m_domainOffsets[domainId() + 1]));
2203 refineChildIds[m_maxNoChilds * cnt + child] = a_globalId(static_cast<MInt>(childId));
2204 }
2205 }
2206 }
2207 }
2208 }
2209
2210 // exchange childIds
2211 maia::mpi::exchangeBuffer(nghbrDomains,//m_nghbrDomains,
2212 m_haloCells,
2213 m_windowCells,
2214 mpiComm(),
2215 refineChildIds.data(),
2216 recvChildIds.data(),
2217 m_maxNoChilds);
2218
2219#ifndef NDEBUG
2220 if(m_maxPartitionLevelShift > 0) {
2221 for(MInt i = 0; i < noNeighborDomains(); i++) {
2222 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
2223 MInt cellId = m_haloCells[i][j];
2224 if(a_level(cellId) == level) {
2225 MInt pId = plaMap[cellId];
2226 if(m_maxPartitionLevelShift > 0 && pId > -1) {
2227 MInt cnt = haloMap[i][j];
2228 for(MInt child = 0; child < m_maxNoChilds; child++) {
2229 const MLong childId = m_partitionLevelAncestorChildIds[pId * m_maxNoChilds + child];
2230 ASSERT(childId == recvChildIds[m_maxNoChilds * cnt + child],
2231 std::to_string(childId) + " != " + std::to_string(recvChildIds[m_maxNoChilds * cnt + child])
2232 + "; c" + std::to_string(cellId) + "; g" + std::to_string(a_globalId(cellId)) + "; p"
2233 + std::to_string(pId));
2234 }
2235 }
2236 }
2237 }
2238 }
2239 }
2240#endif
2241
2242
2243 // This is the continuation of 'PartLvlShift (*)'
2244 if (m_maxPartitionLevelShift>0) {
2245 for (const auto& item : windowsTemp) {
2246 // ndom -> domain to which current window cell needs to send the data to
2247 const MInt ndom = item.first.first;
2248 // childId -> cellId of the current window cell
2249 const MInt childId = item.first.second;
2250 ASSERT(a_hasProperty(a_parentId(childId), Cell::IsPartLvlAncestor), "");
2251 const M32X4bit<true> windowLayer = item.second;
2252 const MInt idx = setNeighborDomainIndex(ndom, m_haloCells, m_windowCells, m_windowLayer_);
2253 ASSERT(idx > -1, "");
2254// TERMM_IF_NOT_COND(a_isHalo(a_parentId(childId)) && a_hasProperty(a_parentId(childId), Cell::IsPartLvlAncestor), "");
2255 ASSERT(a_hasProperty(childId, Cell::IsPartLvlAncestor) || a_hasProperty(childId, Cell::IsPartitionCell), "");
2256
2257 // New neighbor domain, create new cell sets
2258 if(idx == static_cast<MInt>(windCellSet.size())) {
2259 windCellSet.emplace_back();
2260 haloCellSet.emplace_back();
2261 }
2262
2263 if (m_noPeriodicCartesianDirs==0) {
2264 if(windCellSet[idx].find(childId) != windCellSet[idx].end())
2265 continue;
2266 } else {
2267 // Note: we can also have 4 halo cells mapped to same window cell (see DG/3D_cube_linearscalaradv_linear_periodic)
2268 //TERMM_IF_COND(windowsTemp.count(std::make_pair(ndom,childId))>2 || std::count(m_windowCells[idx].begin(), m_windowCells[idx].end(), childId)>2, "");
2269 if ((signed)windowsTemp.count(std::make_pair(ndom,childId))<=std::count(m_windowCells[idx].begin(), m_windowCells[idx].end(), childId)) //performance wise this is a shit
2270 continue;
2271 }
2272
2273 m_windowCells[idx].push_back(childId);
2274 windCellSet[idx].insert(childId);
2275
2276 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
2277 if (m_tree.solver(childId, solver) && !isSolverWindowCell(idx,childId,solver) && windowLayer.get(solver)<=m_noSolverHaloLayers[solver]) {
2278 const MInt w = std::max(windowLayer.get(solver), m_noSolverHaloLayers[solver]);
2279 m_windowLayer_[idx][childId].set(solver, w/*windowLayer.get(solver)*//*m_noSolverHaloLayers[solver]*/);
2280 }
2281 }
2282
2283 //TODO_SS labels:GRID,toenhance find a better way
2284 if (m_windowLayer_[idx].find(childId)==m_windowLayer_[idx].end()) {
2285 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
2286// if (m_tree.solver(childId, solver)) {
2287 //TERMM_IF_NOT_COND(m_tree.solver(a_parentId(childId), solver), "");
2288 // We can have cases that a partLvlAnc cell belongs to a solver, but one of its children
2289 // does not. So we can not just propagate the information from parent cell to children cell.
2290 // So we invalidate those window cells by setting layer to m_noSolverHaloLayers[solver]+1.
2291 const MInt w = m_tree.solver(childId, solver) ?
2292 std::max(windowLayer.get(solver), m_noSolverHaloLayers[solver]):
2293 m_noSolverHaloLayers[solver]+1;
2294 m_windowLayer_[idx][childId].set(solver, w/*windowLayer.get(solver)*//*m_noSolverHaloLayers[solver]*//*m_noHaloLayers+1*/);
2295// }
2296 }
2297 }
2298 }
2299 }
2300
2301
2302 // update regular window cells between this domain and m_nghbrDomains[i] to include the level+1 cells
2303 for(MInt i = 0; i < noNeighborDomains(); i++) {
2304 if (m_windowCells[i].size()==0) continue;
2305 vector<MInt> oldWindowVec(m_windowCells[i]);
2306 std::set<MInt> oldWindCellSet(windCellSet[i]);
2307
2308 std::vector<MInt>().swap(m_windowCells[i]);
2309 windCellSet[i].clear();
2310
2311 for(MInt j = 0; j < (signed)oldWindowVec.size(); j++) {
2312 MInt cellId = oldWindowVec[j];
2313
2314 ASSERT(m_noPeriodicCartesianDirs > 0 || windCellSet[i].find(cellId) == windCellSet[i].end(),
2315 "duplicate window cell: " + std::to_string(cellId) + " " + std::to_string(a_globalId(cellId)) + " "
2316 + std::to_string(j));
2317
2318 m_windowCells[i].push_back(cellId);
2319 windCellSet[i].insert(cellId);
2320
2321 ASSERT(cellId < m_tree.size(), to_string(level));
2322
2323 if(a_level(cellId) == level && a_noChildren(cellId) > 0) {
2324 for(MInt child = 0; child < m_maxNoChilds; child++) {
2325 MInt cnt = windMap[i][j];
2326
2327 const MInt childId = a_childId(cellId, child);
2328 if(childId < 0) continue;
2329
2330 // Skip child if it is already part of the old window cell vector and is handled by the
2331 // outer loop
2332 if(oldWindCellSet.find(childId) != oldWindCellSet.end()) {
2333 ASSERT(a_hasProperty(childId, Cell::IsPartLvlAncestor) || a_hasProperty(childId, Cell::IsPartitionCell),
2334 "not a partition level ancestor or partition cell");
2335 continue;
2336 }
2337
2338 const MLong globalChildId = refineChildIds[m_maxNoChilds * cnt + child];
2339 if(globalChildId > -1) {
2340 ASSERT(childId > -1 && childId < m_tree.size(), to_string(childId) + " " + to_string(m_tree.size()));
2341 // ASSERT( childId > -1 && childId < m_noInternalCells, to_string(childId) + " " +
2342 // to_string(m_noInternalCells) );
2343 MInt ndom = findNeighborDomainId(globalChildId);
2344 if(ndom == domainId()) {
2345 ASSERT(globalChildId == a_globalId(childId), "");
2346
2347 ASSERT(m_noPeriodicCartesianDirs > 0 || windCellSet[i].find(childId) == windCellSet[i].end(),
2348 "duplicate window cell: " + std::to_string(childId));
2349
2350 m_windowCells[i].push_back(childId);
2351// if (m_windowLayer_[i].find(childId)==m_windowLayer_[i].end()) {
2352 // I think the following fails because of testingFix_partitionLevelShift
2353 //TERMM_IF_NOT_COND(a_hasProperty(childId, Cell::IsPartLvlAncestor) || a_hasProperty(childId, Cell::IsPartitionCell), "");
2354 // Note: it is dangerous to set windowLayer=m_noHaloLayers+1, since in case
2355 // !isSolverWindowCell(i,childId,solver) for all solvers, we might get
2356 // a halo cell which is completly disabled
2357// for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
2358// if (m_tree.solver(childId, solver) && !isSolverWindowCell(i,childId,solver)) {
2359// m_windowLayer_[i][childId].set(solver, m_noHaloLayers+1/*1*/);
2360// }
2361// }
2362#ifndef NDEBUG
2363 MBool validWindow = false;
2364 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
2365 if (isSolverWindowCell(i,childId,solver)) {
2366 validWindow = true;
2367 break;
2368 }
2369 }
2370 TERMM_IF_NOT_COND(validWindow, "");
2371#endif
2372// }
2373 windCellSet[i].insert(childId);
2374 }
2375 }
2376 }
2377 }
2378 }
2379 }
2380
2381
2382 // create level+1 halo cells existing on other domains
2383 for(MInt i = 0; i < noNeighborDomains(); i++) {
2384 if (m_haloCells[i].size()==0) continue;
2385 vector<MInt> oldHaloVec(m_haloCells[i]);
2386 std::set<MInt> oldHaloCellSet(haloCellSet[i]);
2387
2388 std::vector<MInt>().swap(m_haloCells[i]);
2389 haloCellSet[i].clear();
2390
2391 for(MInt j = 0; j < (signed)oldHaloVec.size(); j++) {
2392 MInt cellId = oldHaloVec[j];
2393
2394 ASSERT(haloCellSet[i].find(cellId) == haloCellSet[i].end(),
2395 "duplicate halo cell: " + std::to_string(cellId) + " " + std::to_string(a_globalId(cellId)) + " "
2396 + std::to_string(j));
2397
2398 m_haloCells[i].push_back(cellId);
2399 haloCellSet[i].insert(cellId);
2400
2401 if(a_level(cellId) == level) {
2402 MInt cnt = haloMap[i][j];
2403
2404 if(accumulate(&recvChildIds[m_maxNoChilds * cnt], &recvChildIds[m_maxNoChilds * cnt] + m_maxNoChilds,
2405 static_cast<MLong>(-1), [](const MLong& a, const MLong& b) { return std::max(a, b); })
2406 > -1) {
2407 refineCell(cellId, &recvChildIds[m_maxNoChilds * cnt], a_hasProperty(cellId, Cell::IsPartLvlAncestor));
2408 refineIds.push_back(cellId);
2409
2410 for(MInt child = 0; child < m_maxNoChilds; child++) {
2411 const MInt childId = a_childId(cellId, child);
2412 if(childId < 0) continue;
2413
2414 // Skip halo child already present in case of partition level shifts
2415 if(oldHaloCellSet.find(childId) != oldHaloCellSet.end()) {
2416 ASSERT(a_hasProperty(childId, Cell::IsPartLvlAncestor) || a_hasProperty(childId, Cell::IsPartitionCell),
2417 "not a partition level ancestor or partition cell");
2418 continue;
2419 }
2420
2421 ASSERT(childId > -1 && childId < m_tree.size(), to_string(childId) + " " + to_string(m_tree.size()));
2422 ASSERT(a_globalId(childId) > -1, "");
2423 MInt ndom = findNeighborDomainId(a_globalId(childId));
2424 ASSERT(ndom > -1, "");
2425 if(recvChildIds[m_maxNoChilds * cnt + child] > -1)
2426 ASSERT(a_globalId(childId) == recvChildIds[m_maxNoChilds * cnt + child], "");
2427 if(recvChildIds[m_maxNoChilds * cnt + child] < 0) ASSERT(ndom == domainId(), "");
2428 if(ndom == m_nghbrDomains[i]) {
2429 if(a_hasProperty(childId, Cell::IsPartLvlAncestor) || a_hasProperty(childId, Cell::IsPartitionCell)) {
2430 // In case of a partition level shift skip if the child is already a halo cell
2431 if(haloCellSet[i].find(childId) != haloCellSet[i].end()) {
2432 continue;
2433 }
2434 }
2435
2436 m_haloCells[i].push_back(childId);
2437 haloCellSet[i].insert(childId);
2438 } else if(m_maxPartitionLevelShift > 0) {
2439 //if(a_hasProperty(cellId, Cell::IsPartLvlAncestor) && a_hasProperty(cellId, Cell::IsPeriodic))
2440 // mTerm(1, AT_, "check code here!!!");
2441#ifndef NDEBUG
2442 if (ndom==domainId() && a_hasProperty(cellId, Cell::IsPeriodic))
2443 TERMM_IF_NOT_COND(a_hasProperty(cellId, Cell::IsPartLvlAncestor), "");
2444#endif
2445 // Here also the case ndom==domainId(), where child halo lies on current domain because of periodicty
2446 // is dealt with
2447 if(ndom != domainId() || a_hasProperty(cellId, Cell::IsPeriodic)) {
2448 const MInt idx = setNeighborDomainIndex(ndom, m_haloCells, m_windowCells, m_windowLayer_);
2449 ASSERT(idx > -1, "");
2450
2451 // New neighbor domain, create new cell sets
2452 if(idx == static_cast<MInt>(windCellSet.size())) {
2453 windCellSet.emplace_back();
2454 haloCellSet.emplace_back();
2455 }
2456
2457 if(a_hasProperty(childId, Cell::IsPartLvlAncestor) || a_hasProperty(childId, Cell::IsPartitionCell)) {
2458 // In case of a partition level shift skip if the child is already a halo cell
2459 if(haloCellSet[idx].find(childId) != haloCellSet[idx].end()) {
2460 continue;
2461 }
2462 }
2463
2464 m_haloCells[idx].push_back(childId);
2465 haloCellSet[idx].insert(childId);
2466 }
2467 }
2468 a_hasProperty(childId, Cell::IsPeriodic) = a_hasProperty(cellId, Cell::IsPeriodic);
2469 }
2470 if(a_noChildren(cellId) == 0) mTerm(1, AT_, "all children gone");
2471 }
2472 }
2473 }
2474 }
2475
2476 // also create level+1 halo cells for local partitionLevelAncestors with children on different domains
2477 if(m_maxPartitionLevelShift > 0) {
2478 for(MUint i = 0; i < m_partitionLevelAncestorIds.size(); i++) {
2479 const MInt cellId = m_partitionLevelAncestorIds[i];
2480
2481 ASSERT(a_hasProperty(cellId, Cell::IsPartLvlAncestor),
2482 "not a partition level ancestor: cellId" + std::to_string(cellId) + " halo"
2483 + std::to_string(a_hasProperty(cellId, Cell::IsHalo)) + " domain" + std::to_string(domainId()));
2484
2485 if(a_hasProperty(cellId, Cell::IsHalo)) continue;
2486 ASSERT(findNeighborDomainId(a_globalId(cellId)) == domainId(), "");
2487 if(a_level(cellId) == level) {
2488 MLong* childIds = &m_partitionLevelAncestorChildIds[i * m_maxNoChilds];
2489 if(accumulate(childIds, childIds + m_maxNoChilds, static_cast<MLong>(-1),
2490 [](const MLong& a, const MLong& b) { return std::max(a, b); })
2491 > -1) {
2492 refineCell(cellId, childIds, a_hasProperty(cellId, Cell::IsPartLvlAncestor));
2493 refineIds.push_back(cellId);
2494
2495 for(MInt child = 0; child < m_maxNoChilds; child++) {
2496 const MInt childId = a_childId(cellId, child);
2497 if(childId < 0) continue;
2498 ASSERT(childId > -1 && childId < m_tree.size(), to_string(childId) + " " + to_string(m_tree.size()));
2499 a_globalId(childId) = childIds[child];
2500 MInt ndom = findNeighborDomainId(a_globalId(childId));
2501 ASSERT(ndom > -1, "");
2502 if(ndom != domainId()) {
2503 MInt idx = setNeighborDomainIndex(ndom, m_haloCells, m_windowCells, m_windowLayer_);
2504 ASSERT(idx > -1, "");
2505
2506 // New neighbor domain, create new cell sets
2507 if(idx == static_cast<MInt>(windCellSet.size())) {
2508 windCellSet.emplace_back();
2509 haloCellSet.emplace_back();
2510 }
2511
2512 if(a_hasProperty(childId, Cell::IsPartLvlAncestor) || a_hasProperty(childId, Cell::IsPartitionCell)) {
2513 // In case of a partition level shift skip if the child is already a halo cell
2514 if(haloCellSet[idx].find(childId) != haloCellSet[idx].end()) {
2515 continue;
2516 }
2517 }
2518
2519 m_haloCells[idx].push_back(childId);
2520 haloCellSet[idx].insert(childId);
2521 }
2522 a_hasProperty(childId, Cell::IsPeriodic) = a_hasProperty(cellId, Cell::IsPeriodic);
2523 }
2524 }
2525 if(a_noChildren(cellId) == 0) mTerm(1, AT_, "all children gone");
2526 }
2527 }
2528 }
2529
2530 // Refinement of the azimuthal periodic halo cells
2531 if(m_azimuthalPer) {
2532 refineChildIds.clear();
2533 recvChildIds.clear();
2534
2535 // In this function cells are tagged for refinement
2536 tagAzimuthalHigherLevelExchangeCells(refineChildIds, recvChildIds, level);
2537
2538 vector<vector<MLong>> shiftWindows;
2539 shiftWindows.resize(noDomains());
2540 MIntScratchSpace noShiftWindows(noNeighborDomains(), AT_, "noShiftWindows");
2541 noShiftWindows.fill(0);
2542
2543 MInt cnt = 0;
2544 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
2545 vector<MInt> oldWindowVec(m_azimuthalWindowCells[i]);
2546 vector<MInt> oldHigherLevelCon(m_azimuthalHigherLevelConnectivity[i]);
2547
2548 m_azimuthalWindowCells[i].clear();
2549 m_azimuthalHigherLevelConnectivity[i].clear();
2550
2551 for(MInt j = 0; j < (signed)oldWindowVec.size(); j++) {
2552 MInt cellId = oldWindowVec[j];
2553
2554 if(find(oldHigherLevelCon.begin(), oldHigherLevelCon.end(), j) != oldHigherLevelCon.end()) {
2555 m_azimuthalHigherLevelConnectivity[i].push_back(m_azimuthalWindowCells[i].size());
2556 }
2557 m_azimuthalWindowCells[i].push_back(cellId);
2558
2559 ASSERT(cellId < m_tree.size(), to_string(level));
2560
2561 if(a_level(cellId) == level) {
2562 for(MInt child = 0; child < m_maxNoChilds; child++) {
2563 const MLong globalChildId = refineChildIds[m_maxNoChilds * cnt + child];
2564 // If the correspondig halo cell will be refiened, add child to azimuthal window cell vector
2565 if(globalChildId > -1) {
2566 MInt ndom = findNeighborDomainId(globalChildId);
2567 // Since azimuthal halo cells and azimuthal window cells are not complete copies
2568 // in the sense of the cartesian grid, the corresponding internal cells for the children of
2569 // an azimuthal halo cell might not lie in the internal cell which is mapped to the parent
2570 // halo cell. Therefore, it might be a child of an azimuthal halo cell is connected to a
2571 // differnt mpi rank. This is handled by the shiftWindows.
2572 if(ndom == domainId()) {
2573 const MInt childId = globalIdToLocalId(globalChildId, true);
2574 if(level == m_minLevel && a_level(childId) <= m_minLevel) {
2575 m_azimuthalHigherLevelConnectivity[i].push_back(m_azimuthalWindowCells[i].size());
2576 }
2577 m_azimuthalWindowCells[i].push_back(childId);
2578 } else {
2579 ASSERT(m_nghbrDomainIndex[ndom] > -1, "");
2580 shiftWindows[m_nghbrDomainIndex[ndom]].push_back(globalChildId);
2581 shiftWindows[m_nghbrDomainIndex[ndom]].push_back(azimuthalNeighborDomain(i));
2582 noShiftWindows[m_nghbrDomainIndex[ndom]]++;
2583 }
2584 }
2585 }
2586 }
2587 cnt++;
2588 }
2589 }
2590
2591 vector<vector<MInt>> shiftHalos;
2592 shiftHalos.resize(noDomains());
2593 vector<vector<MInt>> shiftHaloDoms;
2594 shiftHaloDoms.resize(noDomains());
2595 MIntScratchSpace noShiftHalos(noDomains(), AT_, "noShiftHalos");
2596 noShiftHalos.fill(0);
2597 cnt = 0;
2598 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
2599 vector<MInt> oldHaloVec(m_azimuthalHaloCells[i]);
2600
2601 m_azimuthalHaloCells[i].clear();
2602
2603 for(MInt j = 0; j < (signed)oldHaloVec.size(); j++) {
2604 MInt cellId = oldHaloVec[j];
2605
2606 m_azimuthalHaloCells[i].push_back(cellId);
2607
2608 if(a_level(cellId) == level) {
2609 if(accumulate(&recvChildIds[m_maxNoChilds * cnt], &recvChildIds[m_maxNoChilds * cnt] + m_maxNoChilds,
2610 static_cast<MLong>(-2), [](const MLong& a, const MLong& b) { return std::max(a, b); })
2611 > -2) {
2612
2613 // Refine the azimuthal halo cell
2614 refineCell(cellId, nullptr, a_hasProperty(cellId, Cell::IsPartLvlAncestor));
2615 refineIds.push_back(cellId);
2616
2617 for(MInt child = 0; child < m_maxNoChilds; child++) {
2618 const MInt childId = a_childId(cellId, child);
2619 ASSERT(childId > -1 && childId < m_tree.size(), to_string(childId) + " " + to_string(m_tree.size()));
2620 a_hasProperty(childId, Cell::IsHalo) = a_hasProperty(cellId, Cell::IsHalo);
2621 a_hasProperty(childId, Cell::IsPeriodic) = a_hasProperty(cellId, Cell::IsPeriodic);
2622 a_globalId(childId) = recvChildIds[m_maxNoChilds * cnt + child];
2623 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
2624 treeb().solver(childId, solver) = treeb().solver(cellId, solver);
2625 }
2626
2627 // If the globalId of the child == -1, it has no corresponding interal window cell.
2628 // Since it still might be required is becomes an unmapped halo cell
2629 if(a_globalId(childId) == -1) {
2630 m_azimuthalUnmappedHaloCells.push_back(childId);
2631 m_azimuthalUnmappedHaloDomains.push_back(azimuthalNeighborDomain(i));
2632 continue;
2633 }
2634
2635 ASSERT(a_globalId(childId) > -1,"");
2636 MInt ndom = findNeighborDomainId(a_globalId(childId));
2637
2638 // Since azimuthal halo cells and azimuthal window cells are not complete copies
2639 // in the sense of the cartesian grid, the corresponding internal cells for the children of
2640 // an azimuthal halo cell might not lie in the internal cell which is mapped to the parent
2641 // halo cell. Therefore, it might be a child of an azimuthal halo cell is connected to a
2642 // differnt mpi rank. This is handled by the shiftHalos.
2643 if(ndom == azimuthalNeighborDomain(i)) {
2644 m_azimuthalHaloCells[i].push_back(childId);
2645 } else {
2646 shiftHalos[ndom].push_back(childId);
2647 shiftHaloDoms[ndom].push_back(azimuthalNeighborDomain(i));
2648 noShiftHalos[ndom]++;
2649 }
2650 }
2651 }
2652 }
2653 cnt++;
2654 }
2655 }
2656
2657 // Finally, the shiftWindows and shiftHalos are mapped.
2658 vector<vector<MLong>> newWindows;
2659 newWindows.resize(noNeighborDomains());
2660 MIntScratchSpace noNewWindows(noNeighborDomains(), AT_, "noNewWindows");
2661 noNewWindows.fill(0);
2662 vector<MInt> noValsToReceive;
2663 noValsToReceive.assign(noNeighborDomains(), 1);
2664 maia::mpi::exchangeBuffer(m_nghbrDomains, noValsToReceive, mpiComm(), noShiftWindows.data(), noNewWindows.getPointer());
2665
2666
2667 for(MInt i = 0; i < noNeighborDomains(); i++) {
2668 newWindows[i].resize(2*noNewWindows[i]);
2669 }
2670
2671 ScratchSpace<MPI_Request> sendReq(noNeighborDomains(), AT_, "sendReq");
2672 sendReq.fill(MPI_REQUEST_NULL);
2673
2674 const MInt magic_mpi_tag = 12;
2675 for(MInt i = 0; i < noNeighborDomains(); i++) {
2676 if(noShiftWindows[i] == 0) { continue;
2677}
2678 MPI_Issend(&shiftWindows[i][0], 2 * noShiftWindows[i], type_traits<MLong>::mpiType(), neighborDomain(i), magic_mpi_tag, mpiComm(), &sendReq[i], AT_, "shiftWindows[i][0]");
2679 }
2680
2681 for(MInt i = 0; i < noNeighborDomains(); i++) {
2682 if(noNewWindows[i] == 0) { continue;
2683}
2684 MPI_Recv(&newWindows[i][0], 2 * noNewWindows[i], type_traits<MLong>::mpiType(), neighborDomain(i), magic_mpi_tag, mpiComm(), MPI_STATUS_IGNORE, AT_, "newWindows[i][0]");
2685 }
2686
2687 for(MInt i = 0; i < noNeighborDomains(); i++) {
2688 if(noShiftWindows[i] == 0) { continue;
2689}
2690 MPI_Wait(&sendReq[i], MPI_STATUSES_IGNORE, AT_);
2691 }
2692
2693 // Sort windows - optimize as in updateHaloCellCollectors?
2694 for(MInt i = 0; i < noDomains(); i++) {
2695 shiftWindows[i].clear();
2696 for(MInt d = 0; d < noDomains(); d++) {
2697 MInt nghbrDomId = m_nghbrDomainIndex[d];
2698 if(nghbrDomId == -1) { continue;
2699}
2700 for(MInt j = 0; j < noNewWindows[nghbrDomId]; j++ ) {
2701 MInt haloDomain = newWindows[nghbrDomId][j*2 + 1];
2702 if(haloDomain == i) { shiftWindows[i].push_back(newWindows[nghbrDomId][j*2]);
2703}
2704 }
2705 }
2706 }
2707
2708 // New windows
2709 for(MInt i = 0; i < noDomains(); i++) {
2710 for(MUint j = 0; j < shiftWindows[i].size(); j++ ) {
2711 const MLong globalChildId = shiftWindows[i][j];
2712 ASSERT(findNeighborDomainId(globalChildId) == domainId(),"Wrong domain!");
2713
2714 const MInt childId = globalIdToLocalId(globalChildId, true);
2715 MInt idx = setAzimuthalNeighborDomainIndex(i, m_azimuthalHaloCells, m_azimuthalWindowCells);
2716
2717 if(level == m_minLevel && a_level(childId) <= m_minLevel) {
2718 m_azimuthalHigherLevelConnectivity[idx].push_back(m_azimuthalWindowCells[idx].size());
2719 }
2720 m_azimuthalWindowCells[idx].push_back(childId);
2721 }
2722 }
2723
2724 // Sort halos
2725 for(MInt i = 0; i < noDomains(); i++) {
2726 vector<MInt> shiftHalosBck(shiftHalos[i]);
2727 shiftHalos[i].clear();
2728 for(MInt d = 0; d < noDomains(); d++) {
2729 for(MInt j = 0; j < noShiftHalos[i]; j++ ) {
2730 MInt nghbrDomain = shiftHaloDoms[i][j];
2731
2732 if(nghbrDomain == d) { shiftHalos[i].push_back(shiftHalosBck[j]);
2733}
2734 }
2735 }
2736 }
2737
2738 // Create new halos
2739 for(MInt i = 0; i < noDomains(); i++) {
2740 for(MInt j = 0; j < noShiftHalos[i]; j++ ) {
2741 const MInt childId = shiftHalos[i][j];
2742 MInt idx = setAzimuthalNeighborDomainIndex(i, m_azimuthalHaloCells, m_azimuthalWindowCells);
2743
2744 m_azimuthalHaloCells[idx].push_back(childId);
2745 }
2746 }
2747
2748 // Refine unmapped halos
2749 if(!refineCellSolver.empty()) {
2750 if(domainId() == 0) { cerr << "Attention: Unmapped halo are not refined during adaptation!" << endl;
2751}
2752 } else {
2753 shiftWindows.clear();
2754 recvChildIds.clear();
2755 vector<MInt> recvChildDomainIds;
2756 tagAzimuthalUnmappedHaloCells(shiftWindows, recvChildIds, recvChildDomainIds, level);
2757
2758 // Newly mapped windows
2759 // If a child of an unmapped azimuthal halo cell has an adequate internal cell
2760 // This child becomes an regular azimuthal halo cell. Thus, also the internal window cell
2761 // is added to the azimuthal window cell vector
2762 for(MInt i = 0; i < noDomains(); i++) {
2763 for(MUint j = 0; j < shiftWindows[i].size(); j++ ) {
2764 const MLong globalChildId = shiftWindows[i][j];
2765 if(globalChildId < 0) { continue;
2766}
2767
2768 ASSERT(findNeighborDomainId(globalChildId) == domainId(),"Wrong domain! " + to_string(globalChildId) + " " + to_string(findNeighborDomainId(globalChildId)) + " " + to_string(domainId()));
2769
2770 MInt idx = setAzimuthalNeighborDomainIndex(i, m_azimuthalHaloCells, m_azimuthalWindowCells);
2771
2772 const MInt childId = globalIdToLocalId(globalChildId, true);
2773 m_azimuthalWindowCells[idx].push_back(childId);
2774 }
2775 }
2776
2777 // Newly mapped halos
2778 // If a child of an unmapped azimuthal halo cell has an adequate internal cell
2779 // This child becomes an regular azimuthal halo cell. Thus, it is added to the azimuthal halo cell vector
2780 // Otherwise the child becomes an unmapped azimuthal halo cell
2781 MInt noUnmappedCells = noAzimuthalUnmappedHaloCells();
2782 for(MInt i = 0; i < noUnmappedCells; i++ ) {
2783 MInt cellId = azimuthalUnmappedHaloCell(i);
2784 if(a_level(cellId) == level) {
2785 if(accumulate(&recvChildIds[m_maxNoChilds * i], &recvChildIds[m_maxNoChilds * i] + m_maxNoChilds,
2786 static_cast<MLong>(-2), [](const MLong& a, const MLong& b) { return std::max(a, b); })
2787 > -2) {
2788
2789 refineCell(cellId, nullptr, a_hasProperty(cellId, Cell::IsPartLvlAncestor));
2790 refineIds.push_back(cellId);
2791
2792 for(MInt child = 0; child < m_maxNoChilds; child++) {
2793 const MInt childId = a_childId(cellId, child);
2794
2795 ASSERT(childId > -1 && childId < m_tree.size(), to_string(childId) + " " + to_string(m_tree.size()));
2796 a_hasProperty(childId, Cell::IsHalo) = a_hasProperty(cellId, Cell::IsHalo);
2797 a_hasProperty(childId, Cell::IsPeriodic) = a_hasProperty(cellId, Cell::IsPeriodic);
2798 a_globalId(childId) = recvChildIds[m_maxNoChilds * i + child];
2799 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
2800 treeb().solver(childId, solver) = treeb().solver(cellId, solver);
2801 }
2802
2803 MInt dom = recvChildDomainIds[m_maxNoChilds * i + child];
2804
2805 if(a_globalId(childId) == -1) {
2806 m_azimuthalUnmappedHaloCells.push_back(childId);
2807 m_azimuthalUnmappedHaloDomains.push_back(dom);
2808 continue;
2809 }
2810
2811 ASSERT(findNeighborDomainId(a_globalId(childId)) == dom, "Wrong domain! " + to_string(a_globalId(childId)) + " " + to_string(findNeighborDomainId(a_globalId(childId))) + " " + to_string(dom) + " " + to_string(domainId()) + " " + to_string(cellId));
2812
2813 MInt idx = setAzimuthalNeighborDomainIndex(dom, m_azimuthalHaloCells, m_azimuthalWindowCells);
2814
2815 m_azimuthalHaloCells[idx].push_back(childId);
2816 }
2817 }
2818 }
2819 }
2820 }
2821
2822 // Update grid bndry cells
2823 MBool adaptation = false;
2824 adaptation = Context::getBasicProperty<MBool>("adaptation", AT_, &adaptation);
2825 if(!adaptation) {
2826 std::array<MFloat, 2*nDim> bbox;
2827 for(MInt i = 0; i < nDim; i++) {
2828 bbox[i] = numeric_limits<MFloat>::max();
2829 bbox[nDim + i] = numeric_limits<MFloat>::lowest();
2830 }
2831 for(MInt cellId = 0; cellId < m_noInternalCells; cellId++) {
2832 for(MInt i = 0; i < nDim; i++) {
2833 bbox[i] = mMin(bbox[i], a_coordinate(cellId, i) - F1B2 * cellLengthAtLevel(a_level(cellId)));
2834 bbox[nDim + i] = mMax(bbox[nDim + i], a_coordinate(cellId, i) + F1B2 * cellLengthAtLevel(a_level(cellId)));
2835 }
2836 }
2837 MPI_Allreduce(MPI_IN_PLACE, &bbox[0], nDim, MPI_DOUBLE, MPI_MIN, mpiComm(), AT_, "MPI_IN_PLACE", "bbox[0]");
2838 MPI_Allreduce(MPI_IN_PLACE, &bbox[nDim], nDim, MPI_DOUBLE, MPI_MAX, mpiComm(), AT_, "MPI_IN_PLACE", "bbox[nDim]");
2839 MBool isBndry = false;
2840 for(MInt i = 0; i < m_noInternalCells; i++) {
2841 MInt cellId =i;
2842 if(a_level(cellId) != level+1) { continue;
2843}
2844 isBndry = false;
2845 for(MInt dir = 0; dir < m_noDirs; dir++) {
2846 if(a_hasNeighbor(cellId, dir) > 0) { continue;
2847}
2848 if(a_hasParent(cellId) && a_hasNeighbor(a_parentId(cellId), dir) > 0) {
2849 MInt nghbrParentId = a_neighborId(a_parentId(cellId),dir);
2850 if(a_noChildren(nghbrParentId) == 0) {
2851 continue;
2852 }
2853 }
2854 if(!m_periodicCartesianDir[dir/2]) {
2855 std::array<MFloat, nDim> coords;
2856 for(MInt d = 0; d < nDim; d++) {
2857 coords[d] = a_coordinate(cellId,d);
2858 }
2859 coords[dir / 2] += ((dir % 2) == 0 ? -F1 : F1) * cellLengthAtLevel(m_minLevel);
2860 if(coords[dir / 2] < bbox[dir / 2] || coords[dir / 2] > bbox[nDim + dir / 2]) {
2861 continue;
2862}
2863 }
2864 isBndry = true;
2865 }
2866 if(isBndry) {
2867 if(!a_isHalo(cellId)) { m_gridBndryCells.push_back(cellId);
2868}
2869 for(MInt dir = 0; dir < m_noDirs; dir++) {
2870 if(a_hasNeighbor(cellId, dir) > 0) {
2871 MInt nghbrId = a_neighborId(cellId, dir);
2872 if(!a_isHalo(nghbrId)) {
2873 m_gridBndryCells.push_back(nghbrId);
2874 }
2875 }
2876 }
2877 }
2878 }
2879 }
2880 }
2881
2882 // sort halo and window cells by globalId to get matching connectivity
2883 // this may not even be required, check whether ordering is implicity matching for
2884 // a complex case with multiple partition level shifts
2885 if(m_maxPartitionLevelShift > 0) {
2886 for(MInt i = 0; i < noNeighborDomains(); i++) {
2887 sort(m_haloCells[i].begin(), m_haloCells[i].end(),
2888 [this](const MInt& a, const MInt& b) { return a_globalId(a) < a_globalId(b); });
2889 sort(m_windowCells[i].begin(), m_windowCells[i].end(),
2890 [this](const MInt& a, const MInt& b) { return a_globalId(a) < a_globalId(b); });
2891 }
2892 }
2893
2894 // Exchange solverBits also in case of 1 solver, because solverBits are used to disable halo cells, which
2895 // are created, but in tagActiveWindowsAtMinLevel seen to be superfluous; if we can ensure that in case of
2896 // noSolvers==0, all cells in m_windowCells have a windowLayer<=m_noSolverHaloLayers, we might recomment the
2897 // following if-statement
2898 //if(treeb().noSolvers() > 1) { // Exchange solver info
2899 exchangeSolverBitset(&m_tree.solverBits(0));
2900 //}
2901 if(m_azimuthalPer && noAzimuthalNeighborDomains() > 0) {
2902 maia::mpi::exchangeBitset(m_azimuthalNghbrDomains, m_azimuthalHaloCells, m_azimuthalWindowCells, mpiComm(), &m_tree.solverBits(0),
2903 m_tree.size());
2904
2905 // To ensure that azimuthal halo cells are fully refined (have all children)
2906 // or are not refined at all, solver Bits need to be checked!
2907 correctAzimuthalSolverBits();
2908 }
2909
2910 // trigger refineCell() for halo cells on the solvers
2911 if(!refineCellSolver.empty()) {
2912 // initially empty during solver startup, no solver refinement needed then.
2913 for(auto& cellId : refineIds) {
2914 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
2915 if(!m_tree.solver(cellId, solver)) continue;
2916 if(a_hasChildren(cellId, solver)) {
2917 refineCellSolver[solver](cellId); // call refineCell() function in each solver
2918 }
2919 }
2920 }
2921 refineIds.clear();
2922 }
2923
2924 // When we arrive at the highest level, we can optionally delete uneccesary cells
2925 if (m_haloMode==2 && (forceLeafLvlCorrection || (onlyLevel==-1 && level==m_maxLevel-1))) {
2926 tagActiveWindowsOnLeafLvl3(level+1, duringMeshAdaptation, removeCellSolver);
2927 }
2928
2929 // --- Exchange Cell::IsPartLvlAncestor & noOffsprings --- //
2930 // exchange some window cell data
2931 ScratchSpace<MInt> bprops(m_tree.size(), 2, AT_, "bprops");
2932 bprops.fill(-1);
2933 // Note: changed loop to iterate over whole tree, internal and halo cells not sorted!
2934 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
2935 if(a_isHalo(cellId) || a_isToDelete(cellId)) {
2936 continue;
2937 }
2938 bprops(cellId, 0) = (MInt)a_hasProperty(cellId, Cell::IsPartLvlAncestor);
2939 bprops(cellId, 1) = a_noOffsprings(cellId);
2940
2941 ASSERT(bprops(cellId, 0) > -1 && bprops(cellId, 1) > 0,
2942 std::to_string(cellId) + " " + std::to_string(bprops(cellId, 0)) + " "
2943 + std::to_string(bprops(cellId, 1)));
2944 }
2945
2946 maia::mpi::exchangeData(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), bprops.getPointer(), 2);
2947
2948 // Note: loop only over the current haloCells since only for these data is exchanged, this does
2949 // not work properly if in case of a partition level shift some halo cell is not part of
2950 // m_haloCells yet such that its property IsPartLvlAncestor is reset!
2951 for(MInt i = 0; i < noNeighborDomains(); i++) {
2952 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
2953 const MInt cellId = m_haloCells[i][j];
2954 ASSERT(!a_isToDelete(cellId), "");
2955 ASSERT(bprops(cellId, 0) > -1 && bprops(cellId, 1) > 0,
2956 std::to_string(cellId) + " " + std::to_string(bprops(cellId, 0)) + " "
2957 + std::to_string(bprops(cellId, 1)));
2958 a_hasProperty(cellId, Cell::IsPartLvlAncestor) = (MBool)bprops(cellId, 0);
2959 a_noOffsprings(cellId) = bprops(cellId, 1);
2960 }
2961 }
2962 // --- Exchange Cell::IsPartLvlAncestor & noOffsprings --- //
2963
2964 // Set window/halo flags
2965 for(MInt i = 0; i < noNeighborDomains(); i++) {
2966 // Halo flag are already set in refineCell
2967#ifndef NDEBUG
2968 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
2969 ASSERT(!a_hasProperty(m_haloCells[i][j], Cell::IsWindow), "halo cell is marked as window");
2970 ASSERT(a_hasProperty(m_haloCells[i][j], Cell::IsHalo), "halo cell flag not set!");
2971 // a_hasProperty(m_haloCells[i][j], Cell::IsHalo) = true;
2972 // a_hasProperty(m_haloCells[i][j], Cell::IsWindow) = false;
2973 }
2974#endif
2975 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
2976 ASSERT(!a_isHalo(m_windowCells[i][j]), "window cell is marked as halo");
2977 // a_hasProperty( m_windowCells[i][j], Cell::IsHalo ) = false;
2978 a_hasProperty(m_windowCells[i][j], Cell::IsWindow) = true;
2979 }
2980 }
2981 if(m_azimuthalPer) {
2982 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
2983 for(MInt j = 0; j < (signed)m_azimuthalHaloCells[i].size(); j++) {
2984 ASSERT(!a_hasProperty(m_azimuthalHaloCells[i][j], Cell::IsWindow), "azimuthal halo cell is marked as window");
2985 a_hasProperty(m_azimuthalHaloCells[i][j], Cell::IsHalo) = true;
2986 }
2987 for(MInt j = 0; j < (signed)m_azimuthalWindowCells[i].size(); j++) {
2988 ASSERT(!a_isHalo(m_azimuthalWindowCells[i][j]), "azimuthal window cell is marked as halo");
2989 a_hasProperty(m_azimuthalWindowCells[i][j], Cell::IsWindow) = true;
2990 }
2991 }
2992 for(MInt j = 0; j < noAzimuthalUnmappedHaloCells(); j++ ) {
2993 ASSERT(!a_hasProperty(m_azimuthalUnmappedHaloCells[j], Cell::IsWindow), "azimuthal halo cell is marked as window");
2994 a_hasProperty(m_azimuthalUnmappedHaloCells[j], Cell::IsHalo) = true;
2995 }
2996 }
2997
2998#ifndef NDEBUG
2999 // Sanity check, that parent of a halo cell is also a halo cell
3000 for (MInt i = 0; i < noNeighborDomains(); i++) {
3001 // We need to reinit set, because in tagActiveWindowsOnLeafLvl3 the ordering might change
3002 haloCellSet[i].clear();
3003 std::copy(m_haloCells[i].begin(), m_haloCells[i].end(), std::inserter(haloCellSet[i], haloCellSet[i].begin()));
3004 for (MInt j = 0; j < (signed)m_haloCells[i].size(); ++j) {
3005 MInt parentId = m_haloCells[i][j];
3006 TERMM_IF_NOT_COND(a_isHalo(parentId), "");
3007 char isSolverHalo = 0;
3008 for (MInt solver = 0; solver < treeb().noSolvers(); ++solver) {
3009 if (m_tree.solver(parentId,solver)) {
3010 isSolverHalo = isSolverHalo | (1 << solver);
3011 }
3012 }
3013 while(a_parentId(parentId)>-1) {
3014 parentId = a_parentId(parentId);
3015 TERMM_IF_NOT_COND(a_isHalo(parentId) || a_hasProperty(parentId, Cell::IsPartLvlAncestor), "");
3016 TERMM_IF_NOT_COND(haloCellSet[i].find(parentId)!=haloCellSet[i].end()
3017 || a_hasProperty(parentId, Cell::IsPartLvlAncestor), "");
3018 for (MInt solver = 0; solver < treeb().noSolvers(); ++solver) {
3019 if (isSolverHalo & (1<<solver)) {
3020 TERMM_IF_NOT_COND(m_tree.solver(parentId,solver), "");
3021 }
3022 if (m_tree.solver(parentId,solver)) {
3023 isSolverHalo = isSolverHalo | (1 << solver);
3024 }
3025 }
3026 }
3027 }
3028 }
3029 // for debugging, check consistency before new level
3030 checkWindowHaloConsistency(false, "createHigherLevelExchangeCells completed on level="+to_string(level)+": ");
3031#endif
3032
3033 logDuration(levelTimeStart, "level #"+std::to_string(level)+" total");
3034 }
3035
3036#ifndef NDEBUG
3037 if (onlyLevel==-1 || forceLeafLvlCorrection)
3038 checkWindowLayer("createHigherLevelExchangeCells completed: ");
3039#endif
3040
3041 if(domainId() == 0 && onlyLevel < 0) cerr << endl;
3042}
3043//clang-format on
3044
3045//WH_OLD
3052template <MInt nDim>
3054 const MInt onlyLevel, const std::vector<std::function<void(const MInt)>>& refineCellSolver) {
3055 TRACE();
3056
3057 ScratchSpace<MInt> plaMap(m_maxNoCells, AT_, "plaMap");
3058 vector<MLong> refineChildIds;
3059 vector<MLong> recvChildIds;
3060 vector<vector<MInt>> haloMap;
3061 vector<vector<MInt>> windMap;
3062 vector<MInt> refineIds;
3063
3064 if(domainId() == 0 && onlyLevel < 0) cerr << " * create higher level exchange cells...";
3065
3066 plaMap.fill(-1);
3067 if(m_maxPartitionLevelShift > 0) {
3068 for(MUint i = 0; i < m_partitionLevelAncestorIds.size(); i++) {
3069 plaMap[m_partitionLevelAncestorIds[i]] = i;
3070 }
3071 }
3072
3073 if(m_azimuthalPer && onlyLevel <= m_minLevel) {
3074 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
3075 m_azimuthalHigherLevelConnectivity[i].clear();
3076 }
3077 }
3078
3079 // given window-halo cell connectivity on minLevel established in setupWindowHaloCellConnectivity(),
3080 // iteratively create connectivity on ascending levels
3081 for(MInt level = m_minLevel; level < m_maxLevel; level++) {
3082 if(onlyLevel > -1 && onlyLevel != level) continue;
3083
3084 // for debugging, check consistency before new level
3085 // checkWindowHaloConsistency();
3086
3087 refineIds.clear();
3088 MInt haloCnt = 0;
3089 MInt windowCnt = 0;
3090 haloMap.resize(noNeighborDomains());
3091 windMap.resize(noNeighborDomains());
3092
3093 // Sets to hold halo/window cells for each neighbor domain to allow fast searching if cells are
3094 // already present
3095 vector<std::set<MInt>> haloCellSet(noNeighborDomains());
3096 vector<std::set<MInt>> windCellSet(noNeighborDomains());
3097
3098 for(MInt i = 0; i < noNeighborDomains(); i++) {
3099 haloMap[i].resize(m_haloCells[i].size());
3100 windMap[i].resize(m_windowCells[i].size());
3101 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
3102 haloMap[i][j] = haloCnt++;
3103 }
3104 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
3105 windMap[i][j] = windowCnt++;
3106 }
3107
3108 haloCellSet[i].insert(m_haloCells[i].begin(), m_haloCells[i].end());
3109 windCellSet[i].insert(m_windowCells[i].begin(), m_windowCells[i].end());
3110 }
3111
3112 recvChildIds.resize(haloCnt * m_maxNoChilds);
3113 fill(recvChildIds.begin(), recvChildIds.end(), -1);
3114 refineChildIds.clear();
3115 tagActiveWindows(refineChildIds, level);
3116
3117
3118// set globalIds of each windowCells's child
3119#ifdef _OPENMP
3120#pragma omp parallel for
3121#endif
3122 for(MInt i = 0; i < noNeighborDomains(); i++) {
3123 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
3124 MInt cellId = m_windowCells[i][j];
3125
3126 if(a_level(cellId) == level && a_noChildren(cellId) > 0) {
3127 for(MInt child = 0; child < m_maxNoChilds; child++) {
3128 MInt cnt = windMap[i][j];
3129
3130 if(refineChildIds[m_maxNoChilds * cnt + child] > 0) {
3131 ASSERT(a_childId(cellId, child) > -1, "");
3132 ASSERT((a_globalId(static_cast<MInt>(a_childId(cellId, child))) >= m_domainOffsets[domainId()]
3133 && a_globalId(static_cast<MInt>(a_childId(cellId, child))) < m_domainOffsets[domainId() + 1])
3134 || a_hasProperty(cellId, Cell::IsPartLvlAncestor),
3135 to_string(a_globalId(static_cast<MInt>(a_childId(cellId, child)))) + " "
3136 + to_string(m_domainOffsets[domainId()]) + " " + to_string(m_domainOffsets[domainId() + 1]));
3137 refineChildIds[m_maxNoChilds * cnt + child] = a_globalId(static_cast<MInt>(a_childId(cellId, child)));
3138 }
3139
3140 MInt pId = plaMap[cellId];
3141 if(m_maxPartitionLevelShift > 0 && pId > -1) {
3142 if(m_partitionLevelAncestorChildIds[pId * m_maxNoChilds + child] > -1) {
3143 ASSERT(refineChildIds[m_maxNoChilds * cnt + child] < 0
3144 || refineChildIds[m_maxNoChilds * cnt + child]
3145 == m_partitionLevelAncestorChildIds[pId * m_maxNoChilds + child],
3146 std::to_string(refineChildIds[m_maxNoChilds * cnt + child]) + " plac"
3147 + std::to_string(m_partitionLevelAncestorChildIds[pId * m_maxNoChilds + child]) + "; c"
3148 + std::to_string(cellId) + "; pId" + std::to_string(pId) + "; cnt" + std::to_string(cnt));
3149
3150 refineChildIds[m_maxNoChilds * cnt + child] =
3151 m_partitionLevelAncestorChildIds[pId * m_maxNoChilds + child];
3152 }
3153 }
3154 }
3155 }
3156 }
3157 }
3158
3159 // exchange childIds
3160 maia::mpi::exchangeBuffer(m_nghbrDomains,
3161 m_haloCells,
3162 m_windowCells,
3163 mpiComm(),
3164 refineChildIds.data(),
3165 recvChildIds.data(),
3166 m_maxNoChilds);
3167
3168#ifndef NDEBUG
3169 if(m_maxPartitionLevelShift > 0) {
3170 for(MInt i = 0; i < noNeighborDomains(); i++) {
3171 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
3172 MInt cellId = m_haloCells[i][j];
3173 if(a_level(cellId) == level) {
3174 MInt pId = plaMap[cellId];
3175 if(m_maxPartitionLevelShift > 0 && pId > -1) {
3176 MInt cnt = haloMap[i][j];
3177 for(MInt child = 0; child < m_maxNoChilds; child++) {
3178 const MLong childId = m_partitionLevelAncestorChildIds[pId * m_maxNoChilds + child];
3179 ASSERT(childId == recvChildIds[m_maxNoChilds * cnt + child],
3180 std::to_string(childId) + " != " + std::to_string(recvChildIds[m_maxNoChilds * cnt + child])
3181 + "; c" + std::to_string(cellId) + "; g" + std::to_string(a_globalId(cellId)) + "; p"
3182 + std::to_string(pId));
3183 }
3184 }
3185 }
3186 }
3187 }
3188 }
3189#endif
3190
3191
3192 // initiate communication between two other domains, if one of my childs lies on a different domain (due to
3193 // partitionLevelShift)
3194 if(m_maxPartitionLevelShift > 0) {
3195 // determine all pairs of neighbor domains to connect
3196 vector<MInt> sendDomIdx;
3197 vector<MLong> sendData;
3198 // Note: this assumed that internal and halo cells are separated which is not true if called from meshAdaptation!
3199 // MBoolScratchSpace cellFlag(m_noInternalCells, AT_, "cellFlag");
3200 MBoolScratchSpace cellFlag(m_tree.size(), AT_, "cellFlag");
3201 fill(cellFlag.begin(), cellFlag.end(), false);
3202
3203 for(MInt i = 0; i < noNeighborDomains(); i++) {
3204 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
3205 const MInt cellId = m_windowCells[i][j];
3206 ASSERT(!a_hasProperty(cellId, Cell::IsHalo), "window cell marked as halo");
3207
3208 if(a_level(cellId) == level && a_noChildren(cellId) > 0) {
3209 for(MInt child = 0; child < m_maxNoChilds; child++) {
3210 const MLong childId = refineChildIds[m_maxNoChilds * windMap[i][j] + child];
3211 if(childId < 0) continue;
3212 MInt ndom = findNeighborDomainId(childId);
3213 ASSERT(ndom > -1 && ndom < noDomains(), "");
3214 if(ndom != domainId()) {
3215 ASSERT(a_hasProperty(cellId, Cell::IsPartLvlAncestor), "");
3216 MInt idx = findIndex(m_nghbrDomains.begin(), m_nghbrDomains.end(), ndom);
3217 ASSERT(idx > -1, "");
3218 ASSERT(m_nghbrDomains[idx] == ndom, "");
3219 if(ndom != m_nghbrDomains[i]) {
3220 sendDomIdx.push_back(idx);
3221 sendData.push_back(m_nghbrDomains[i]);
3222 sendData.push_back(childId);
3223 }
3224 if(!cellFlag[cellId]) {
3225 sendDomIdx.push_back(idx);
3226 sendData.push_back(domainId());
3227 sendData.push_back(childId);
3228 }
3229 }
3230 }
3231 }
3232 cellFlag[cellId] = true;
3233 }
3234 }
3235
3236 // exchange redirected communication info
3237 vector<MInt> recvOffs;
3238 vector<MLong> recvData;
3239 maia::mpi::exchangeScattered(m_nghbrDomains, sendDomIdx, sendData, mpiComm(), recvOffs, recvData, 2);
3240
3241 // create window cells if this domain is affected by redirected communication links
3242 const MInt noNgbrDomainsBak = noNeighborDomains();
3243 for(MInt i = 0; i < noNgbrDomainsBak; i++) {
3244 for(MInt j = recvOffs[i]; j < recvOffs[i + 1]; j++) {
3245 auto ndom = static_cast<MInt>(recvData[2 * j]);
3246 MLong globalChildId = recvData[2 * j + 1];
3247 if(ndom > -1) {
3248 MInt childId = m_globalToLocalId[globalChildId];
3249 ASSERT(childId > -1, "");
3250 ASSERT(a_globalId(childId) == globalChildId, "");
3251 ASSERT(findNeighborDomainId(globalChildId) == domainId(), "");
3252 MInt idx = setNeighborDomainIndex(ndom, m_haloCells, m_windowCells);
3253 ASSERT(idx > -1, "");
3254
3255 // New neighbor domain, create new cell sets
3256 if(idx == static_cast<MInt>(windCellSet.size())) {
3257 windCellSet.emplace_back();
3258 haloCellSet.emplace_back();
3259 }
3260
3261 if(a_hasProperty(childId, Cell::IsPartLvlAncestor) || a_hasProperty(childId, Cell::IsPartitionCell)) {
3262 // In case of a partition level shift skip if the child is already a window cell
3263 if(windCellSet[idx].find(childId) != windCellSet[idx].end()) {
3264 continue;
3265 }
3266 }
3267
3268 ASSERT(windCellSet[idx].find(childId) == windCellSet[idx].end(),
3269 "duplicate window cell: " + std::to_string(childId));
3270
3271 m_windowCells[idx].push_back(childId);
3272 windCellSet[idx].insert(childId);
3273 }
3274 }
3275 }
3276
3277 } // ( m_maxPartitionLevelShift > 0 )
3278
3279
3280 // update regular window cells between this domain and m_nghbrDomains[i] to include the level+1 cells
3281 for(MInt i = 0; i < noNeighborDomains(); i++) {
3282 vector<MInt> oldWindowVec(m_windowCells[i]);
3283 std::set<MInt> oldWindCellSet(windCellSet[i]);
3284
3285 std::vector<MInt>().swap(m_windowCells[i]);
3286 windCellSet[i].clear();
3287
3288 for(MInt j = 0; j < (signed)oldWindowVec.size(); j++) {
3289 MInt cellId = oldWindowVec[j];
3290
3291 // TODO labels:GRID fix duplicate window cells in periodic cases!
3292 ASSERT(m_noPeriodicCartesianDirs > 0 || windCellSet[i].find(cellId) == windCellSet[i].end(),
3293 "duplicate window cell: " + std::to_string(cellId) + " " + std::to_string(a_globalId(cellId)) + " "
3294 + std::to_string(j));
3295
3296 m_windowCells[i].push_back(cellId);
3297 windCellSet[i].insert(cellId);
3298
3299 ASSERT(cellId < m_tree.size(), to_string(level));
3300
3301 if(a_level(cellId) == level && a_noChildren(cellId) > 0) {
3302 for(MInt child = 0; child < m_maxNoChilds; child++) {
3303 MInt cnt = windMap[i][j];
3304
3305 const MInt childId = a_childId(cellId, child);
3306 if(childId < 0) continue;
3307
3308 // Skip child if it is already part of the old window cell vector and is handled by the
3309 // outer loop
3310 if(oldWindCellSet.find(childId) != oldWindCellSet.end()) {
3311 ASSERT(a_hasProperty(childId, Cell::IsPartLvlAncestor) || a_hasProperty(childId, Cell::IsPartitionCell),
3312 "not a partition level ancestor or partition cell");
3313 continue;
3314 }
3315
3316 const MLong globalChildId = refineChildIds[m_maxNoChilds * cnt + child];
3317 if(globalChildId > -1) {
3318 ASSERT(childId > -1 && childId < m_tree.size(), to_string(childId) + " " + to_string(m_tree.size()));
3319 // ASSERT( childId > -1 && childId < m_noInternalCells, to_string(childId) + " " +
3320 // to_string(m_noInternalCells) );
3321 MInt ndom = findNeighborDomainId(globalChildId);
3322 if(ndom == domainId()) {
3323 ASSERT(globalChildId == a_globalId(childId), "");
3324
3325 // TODO labels:GRID fix duplicate window cells in periodic cases!
3326 ASSERT(m_noPeriodicCartesianDirs > 0 || windCellSet[i].find(childId) == windCellSet[i].end(),
3327 "duplicate window cell: " + std::to_string(childId));
3328
3329 m_windowCells[i].push_back(childId);
3330 windCellSet[i].insert(childId);
3331 }
3332 }
3333 }
3334 }
3335 }
3336 }
3337
3338
3339 // create level+1 halo cells existing on other domains
3340 for(MInt i = 0; i < noNeighborDomains(); i++) {
3341 vector<MInt> oldHaloVec(m_haloCells[i]);
3342 std::set<MInt> oldHaloCellSet(haloCellSet[i]);
3343
3344 std::vector<MInt>().swap(m_haloCells[i]);
3345 haloCellSet[i].clear();
3346
3347 for(MInt j = 0; j < (signed)oldHaloVec.size(); j++) {
3348 MInt cellId = oldHaloVec[j];
3349
3350 ASSERT(haloCellSet[i].find(cellId) == haloCellSet[i].end(),
3351 "duplicate halo cell: " + std::to_string(cellId) + " " + std::to_string(a_globalId(cellId)) + " "
3352 + std::to_string(j));
3353
3354 m_haloCells[i].push_back(cellId);
3355 haloCellSet[i].insert(cellId);
3356
3357 if(a_level(cellId) == level) {
3358 MInt cnt = haloMap[i][j];
3359
3360 if(accumulate(&recvChildIds[m_maxNoChilds * cnt], &recvChildIds[m_maxNoChilds * cnt] + m_maxNoChilds,
3361 static_cast<MLong>(-1), [](const MLong& a, const MLong& b) { return std::max(a, b); })
3362 > -1) {
3363 // NOTE: this leads to different noChilds on window/halo-Cells!
3364 // TODO labels:GRID,totest
3365 // check if halo cell, that is to be refined, is on the second periodic layer
3366 // if so, do not refine this cell
3367 // if(a_hasProperty(cellId,Cell::IsPeriodic)){
3368
3369 // }
3370
3371 refineCell(cellId, &recvChildIds[m_maxNoChilds * cnt], a_hasProperty(cellId, Cell::IsPartLvlAncestor));
3372 refineIds.push_back(cellId);
3373
3374 for(MInt child = 0; child < m_maxNoChilds; child++) {
3375 const MInt childId = a_childId(cellId, child);
3376 if(childId < 0) continue;
3377
3378 // Skip halo child already present in case of partition level shifts
3379 if(oldHaloCellSet.find(childId) != oldHaloCellSet.end()) {
3380 ASSERT(a_hasProperty(childId, Cell::IsPartLvlAncestor) || a_hasProperty(childId, Cell::IsPartitionCell),
3381 "not a partition level ancestor or partition cell");
3382 continue;
3383 }
3384
3385 ASSERT(childId > -1 && childId < m_tree.size(), to_string(childId) + " " + to_string(m_tree.size()));
3386 ASSERT(a_globalId(childId) > -1, "");
3387 MInt ndom = findNeighborDomainId(a_globalId(childId));
3388 ASSERT(ndom > -1, "");
3389 if(recvChildIds[m_maxNoChilds * cnt + child] > -1)
3390 ASSERT(a_globalId(childId) == recvChildIds[m_maxNoChilds * cnt + child], "");
3391 if(recvChildIds[m_maxNoChilds * cnt + child] < 0) ASSERT(ndom == domainId(), "");
3392 if(ndom == m_nghbrDomains[i]) {
3393 if(a_hasProperty(childId, Cell::IsPartLvlAncestor) || a_hasProperty(childId, Cell::IsPartitionCell)) {
3394 // In case of a partition level shift skip if the child is already a halo cell
3395 if(haloCellSet[i].find(childId) != haloCellSet[i].end()) {
3396 continue;
3397 }
3398 }
3399
3400 m_haloCells[i].push_back(childId);
3401 haloCellSet[i].insert(childId);
3402 } else if(m_maxPartitionLevelShift > 0) {
3403 if(a_hasProperty(cellId, Cell::IsPartLvlAncestor) && a_hasProperty(cellId, Cell::IsPeriodic))
3404 mTerm(1, AT_, "check code here!!!");
3405 if(ndom != domainId()) {
3406 MInt idx = setNeighborDomainIndex(ndom, m_haloCells, m_windowCells);
3407 ASSERT(idx > -1, "");
3408
3409 // New neighbor domain, create new cell sets
3410 if(idx == static_cast<MInt>(windCellSet.size())) {
3411 windCellSet.emplace_back();
3412 haloCellSet.emplace_back();
3413 }
3414
3415 if(a_hasProperty(childId, Cell::IsPartLvlAncestor) || a_hasProperty(childId, Cell::IsPartitionCell)) {
3416 // In case of a partition level shift skip if the child is already a halo cell
3417 if(haloCellSet[idx].find(childId) != haloCellSet[idx].end()) {
3418 continue;
3419 }
3420 }
3421
3422 m_haloCells[idx].push_back(childId);
3423 haloCellSet[idx].insert(childId);
3424 }
3425 }
3426 a_hasProperty(childId, Cell::IsPeriodic) = a_hasProperty(cellId, Cell::IsPeriodic);
3427 }
3428 if(a_noChildren(cellId) == 0) mTerm(1, AT_, "all children gone");
3429 }
3430 }
3431 }
3432 }
3433
3434 // also create level+1 halo cells for local partitionLevelAncestors with children on different domains
3435 if(m_maxPartitionLevelShift > 0) {
3436 for(MUint i = 0; i < m_partitionLevelAncestorIds.size(); i++) {
3437 const MInt cellId = m_partitionLevelAncestorIds[i];
3438
3439 ASSERT(a_hasProperty(cellId, Cell::IsPartLvlAncestor),
3440 "not a partition level ancestor: cellId" + std::to_string(cellId) + " halo"
3441 + std::to_string(a_hasProperty(cellId, Cell::IsHalo)) + " domain" + std::to_string(domainId()));
3442
3443 if(a_hasProperty(cellId, Cell::IsHalo)) continue;
3444 ASSERT(findNeighborDomainId(a_globalId(cellId)) == domainId(), "");
3445 if(a_level(cellId) == level) {
3446 MLong* childIds = &m_partitionLevelAncestorChildIds[i * m_maxNoChilds];
3447 if(accumulate(childIds, childIds + m_maxNoChilds, static_cast<MLong>(-1),
3448 [](const MLong& a, const MLong& b) { return std::max(a, b); })
3449 > -1) {
3450 refineCell(cellId, childIds, a_hasProperty(cellId, Cell::IsPartLvlAncestor));
3451 refineIds.push_back(cellId);
3452
3453 for(MInt child = 0; child < m_maxNoChilds; child++) {
3454 const MInt childId = a_childId(cellId, child);
3455 if(childId < 0) continue;
3456 ASSERT(childId > -1 && childId < m_tree.size(), to_string(childId) + " " + to_string(m_tree.size()));
3457 a_globalId(childId) = childIds[child];
3458 MInt ndom = findNeighborDomainId(a_globalId(childId));
3459 ASSERT(ndom > -1, "");
3460 if(ndom != domainId()) {
3461 MInt idx = setNeighborDomainIndex(ndom, m_haloCells, m_windowCells);
3462 ASSERT(idx > -1, "");
3463
3464 // New neighbor domain, create new cell sets
3465 if(idx == static_cast<MInt>(windCellSet.size())) {
3466 windCellSet.emplace_back();
3467 haloCellSet.emplace_back();
3468 }
3469
3470 if(a_hasProperty(childId, Cell::IsPartLvlAncestor) || a_hasProperty(childId, Cell::IsPartitionCell)) {
3471 // In case of a partition level shift skip if the child is already a halo cell
3472 if(haloCellSet[idx].find(childId) != haloCellSet[idx].end()) {
3473 continue;
3474 }
3475 }
3476
3477 m_haloCells[idx].push_back(childId);
3478 haloCellSet[idx].insert(childId);
3479 }
3480 a_hasProperty(childId, Cell::IsPeriodic) = a_hasProperty(cellId, Cell::IsPeriodic);
3481 }
3482 }
3483 if(a_noChildren(cellId) == 0) mTerm(1, AT_, "all children gone");
3484 }
3485 }
3486 }
3487
3488 // Refinement of the azimuthal periodic halo cells
3489 if(m_azimuthalPer) {
3490 refineChildIds.clear();
3491 recvChildIds.clear();
3492
3493 // In this function cells are tagged for refinement
3494 tagAzimuthalHigherLevelExchangeCells(refineChildIds, recvChildIds, level);
3495
3496 vector<vector<MLong>> shiftWindows;
3497 shiftWindows.resize(noDomains());
3498 MIntScratchSpace noShiftWindows(noNeighborDomains(), AT_, "noShiftWindows");
3499 noShiftWindows.fill(0);
3500
3501 MInt cnt = 0;
3502 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
3503 vector<MInt> oldWindowVec(m_azimuthalWindowCells[i]);
3504 vector<MInt> oldHigherLevelCon(m_azimuthalHigherLevelConnectivity[i]);
3505
3506 m_azimuthalWindowCells[i].clear();
3507 m_azimuthalHigherLevelConnectivity[i].clear();
3508
3509 for(MInt j = 0; j < (signed)oldWindowVec.size(); j++) {
3510 MInt cellId = oldWindowVec[j];
3511
3512 if(find(oldHigherLevelCon.begin(), oldHigherLevelCon.end(), j) != oldHigherLevelCon.end()) {
3513 m_azimuthalHigherLevelConnectivity[i].push_back(m_azimuthalWindowCells[i].size());
3514 }
3515 m_azimuthalWindowCells[i].push_back(cellId);
3516
3517 ASSERT(cellId < m_tree.size(), to_string(level));
3518
3519 if(a_level(cellId) == level) {
3520 for(MInt child = 0; child < m_maxNoChilds; child++) {
3521 const MLong globalChildId = refineChildIds[m_maxNoChilds * cnt + child];
3522 // If the correspondig halo cell will be refiened, add child to azimuthal window cell vector
3523 if(globalChildId > -1) {
3524 MInt ndom = findNeighborDomainId(globalChildId);
3525 // Since azimuthal halo cells and azimuthal window cells are not complete copies
3526 // in the sense of the cartesian grid, the corresponding internal cells for the children of
3527 // an azimuthal halo cell might not lie in the internal cell which is mapped to the parent
3528 // halo cell. Therefore, it might be a child of an azimuthal halo cell is connected to a
3529 // differnt mpi rank. This is handled by the shiftWindows.
3530 if(ndom == domainId()) {
3531 const MInt childId = globalIdToLocalId(globalChildId, true);
3532 if(level == m_minLevel && a_level(childId) <= m_minLevel) {
3533 m_azimuthalHigherLevelConnectivity[i].push_back(m_azimuthalWindowCells[i].size());
3534 }
3535 m_azimuthalWindowCells[i].push_back(childId);
3536 } else {
3537 ASSERT(m_nghbrDomainIndex[ndom] > -1, "");
3538 shiftWindows[m_nghbrDomainIndex[ndom]].push_back(globalChildId);
3539 shiftWindows[m_nghbrDomainIndex[ndom]].push_back(azimuthalNeighborDomain(i));
3540 noShiftWindows[m_nghbrDomainIndex[ndom]]++;
3541 }
3542 }
3543 }
3544 }
3545 cnt++;
3546 }
3547 }
3548
3549 vector<vector<MInt>> shiftHalos;
3550 shiftHalos.resize(noDomains());
3551 vector<vector<MInt>> shiftHaloDoms;
3552 shiftHaloDoms.resize(noDomains());
3553 MIntScratchSpace noShiftHalos(noDomains(), AT_, "noShiftHalos");
3554 noShiftHalos.fill(0);
3555 cnt = 0;
3556 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
3557 vector<MInt> oldHaloVec(m_azimuthalHaloCells[i]);
3558
3559 m_azimuthalHaloCells[i].clear();
3560
3561 for(MInt j = 0; j < (signed)oldHaloVec.size(); j++) {
3562 MInt cellId = oldHaloVec[j];
3563
3564 m_azimuthalHaloCells[i].push_back(cellId);
3565
3566 if(a_level(cellId) == level) {
3567 if(accumulate(&recvChildIds[m_maxNoChilds * cnt], &recvChildIds[m_maxNoChilds * cnt] + m_maxNoChilds,
3568 static_cast<MLong>(-2), [](const MLong& a, const MLong& b) { return std::max(a, b); })
3569 > -2) {
3570
3571 // Refine the azimuthal halo cell
3572 refineCell(cellId, nullptr, a_hasProperty(cellId, Cell::IsPartLvlAncestor));
3573 refineIds.push_back(cellId);
3574
3575 for(MInt child = 0; child < m_maxNoChilds; child++) {
3576 const MInt childId = a_childId(cellId, child);
3577 ASSERT(childId > -1 && childId < m_tree.size(), to_string(childId) + " " + to_string(m_tree.size()));
3578 a_hasProperty(childId, Cell::IsHalo) = a_hasProperty(cellId, Cell::IsHalo);
3579 a_hasProperty(childId, Cell::IsPeriodic) = a_hasProperty(cellId, Cell::IsPeriodic);
3580 a_globalId(childId) = recvChildIds[m_maxNoChilds * cnt + child];
3581 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
3582 treeb().solver(childId, solver) = treeb().solver(cellId, solver);
3583 }
3584
3585 // If the globalId of the child == -1, it has no corresponding interal window cell.
3586 // Since it still might be required is becomes an unmapped halo cell
3587 if(a_globalId(childId) == -1) {
3588 m_azimuthalUnmappedHaloCells.push_back(childId);
3589 m_azimuthalUnmappedHaloDomains.push_back(azimuthalNeighborDomain(i));
3590 continue;
3591 }
3592
3593 ASSERT(a_globalId(childId) > -1,"");
3594 MInt ndom = findNeighborDomainId(a_globalId(childId));
3595
3596 // Since azimuthal halo cells and azimuthal window cells are not complete copies
3597 // in the sense of the cartesian grid, the corresponding internal cells for the children of
3598 // an azimuthal halo cell might not lie in the internal cell which is mapped to the parent
3599 // halo cell. Therefore, it might be a child of an azimuthal halo cell is connected to a
3600 // differnt mpi rank. This is handled by the shiftHalos.
3601 if(ndom == azimuthalNeighborDomain(i)) {
3602 m_azimuthalHaloCells[i].push_back(childId);
3603 } else {
3604 shiftHalos[ndom].push_back(childId);
3605 shiftHaloDoms[ndom].push_back(azimuthalNeighborDomain(i));
3606 noShiftHalos[ndom]++;
3607 }
3608 }
3609 }
3610 }
3611 cnt++;
3612 }
3613 }
3614
3615 // Finally, the shiftWindows and shiftHalos are mapped.
3616 vector<vector<MLong>> newWindows;
3617 newWindows.resize(noNeighborDomains());
3618 MIntScratchSpace noNewWindows(noNeighborDomains(), AT_, "noNewWindows");
3619 noNewWindows.fill(0);
3620 vector<MInt> noValsToReceive;
3621 noValsToReceive.assign(noNeighborDomains(), 1);
3622 maia::mpi::exchangeBuffer(m_nghbrDomains, noValsToReceive, mpiComm(), noShiftWindows.data(), noNewWindows.getPointer());
3623
3624
3625 for(MInt i = 0; i < noNeighborDomains(); i++) {
3626 newWindows[i].resize(2*noNewWindows[i]);
3627 }
3628
3629 ScratchSpace<MPI_Request> sendReq(noNeighborDomains(), AT_, "sendReq");
3630 sendReq.fill(MPI_REQUEST_NULL);
3631
3632 for(MInt i = 0; i < noNeighborDomains(); i++) {
3633 if(noShiftWindows[i] == 0) continue;
3634 MPI_Issend(&shiftWindows[i][0], 2 * noShiftWindows[i], type_traits<MLong>::mpiType(), neighborDomain(i), 12, mpiComm(), &sendReq[i], AT_, "shiftWindows[i][0]");
3635 }
3636
3637 for(MInt i = 0; i < noNeighborDomains(); i++) {
3638 if(noNewWindows[i] == 0) continue;
3639 MPI_Recv(&newWindows[i][0], 2 * noNewWindows[i], type_traits<MLong>::mpiType(), neighborDomain(i), 12, mpiComm(), MPI_STATUS_IGNORE, AT_, "newWindows[i][0]");
3640 }
3641
3642 for(MInt i = 0; i < noNeighborDomains(); i++) {
3643 if(noShiftWindows[i] == 0) continue;
3644 MPI_Wait(&sendReq[i], MPI_STATUSES_IGNORE, AT_);
3645 }
3646
3647 // Sort windows - optimize as in updateHaloCellCollectors?
3648 for(MInt i = 0; i < noDomains(); i++) {
3649 shiftWindows[i].clear();
3650 for(MInt d = 0; d < noDomains(); d++) {
3651 MInt nghbrDomId = m_nghbrDomainIndex[d];
3652 if(nghbrDomId == -1) continue;
3653 for(MInt j = 0; j < noNewWindows[nghbrDomId]; j++ ) {
3654 MInt haloDomain = newWindows[nghbrDomId][j*2 + 1];
3655 if(haloDomain == i) shiftWindows[i].push_back(newWindows[nghbrDomId][j*2]);
3656 }
3657 }
3658 }
3659
3660 // New windows
3661 for(MInt i = 0; i < noDomains(); i++) {
3662 for(MUint j = 0; j < shiftWindows[i].size(); j++ ) {
3663 const MLong globalChildId = shiftWindows[i][j];
3664 ASSERT(findNeighborDomainId(globalChildId) == domainId(),"Wrong domain!");
3665
3666 const MInt childId = globalIdToLocalId(globalChildId, true);
3667 MInt idx = setAzimuthalNeighborDomainIndex(i, m_azimuthalHaloCells, m_azimuthalWindowCells);
3668
3669 if(level == m_minLevel && a_level(childId) <= m_minLevel) {
3670 m_azimuthalHigherLevelConnectivity[idx].push_back(m_azimuthalWindowCells[idx].size());
3671 }
3672 m_azimuthalWindowCells[idx].push_back(childId);
3673 }
3674 }
3675
3676 // Sort halos
3677 for(MInt i = 0; i < noDomains(); i++) {
3678 vector<MInt> shiftHalosBck(shiftHalos[i]);
3679 shiftHalos[i].clear();
3680 for(MInt d = 0; d < noDomains(); d++) {
3681 for(MInt j = 0; j < noShiftHalos[i]; j++ ) {
3682 MInt nghbrDomain = shiftHaloDoms[i][j];
3683
3684 if(nghbrDomain == d) shiftHalos[i].push_back(shiftHalosBck[j]);
3685 }
3686 }
3687 }
3688
3689 // Create new halos
3690 for(MInt i = 0; i < noDomains(); i++) {
3691 for(MInt j = 0; j < noShiftHalos[i]; j++ ) {
3692 const MInt childId = shiftHalos[i][j];
3693 MInt idx = setAzimuthalNeighborDomainIndex(i, m_azimuthalHaloCells, m_azimuthalWindowCells);
3694
3695 m_azimuthalHaloCells[idx].push_back(childId);
3696 }
3697 }
3698
3699 // Refine unmapped halos
3700 if(!refineCellSolver.empty()) {
3701 if(domainId() == 0) cerr << "Attention: Unmapped halo are not refined during adaptation!" << endl;
3702 } else {
3703 shiftWindows.clear();
3704 recvChildIds.clear();
3705 vector<MInt> recvChildDomainIds;
3706 tagAzimuthalUnmappedHaloCells(shiftWindows, recvChildIds, recvChildDomainIds, level);
3707
3708 // Newly mapped windows
3709 // If a child of an unmapped azimuthal halo cell has an adequate internal cell
3710 // This child becomes an regular azimuthal halo cell. Thus, also the internal window cell
3711 // is added to the azimuthal window cell vector
3712 for(MInt i = 0; i < noDomains(); i++) {
3713 for(MUint j = 0; j < shiftWindows[i].size(); j++ ) {
3714 const MLong globalChildId = shiftWindows[i][j];
3715 if(globalChildId < 0) continue;
3716
3717 ASSERT(findNeighborDomainId(globalChildId) == domainId(),"Wrong domain! " + to_string(globalChildId) + " " + to_string(findNeighborDomainId(globalChildId)) + " " + to_string(domainId()));
3718
3719 MInt idx = setAzimuthalNeighborDomainIndex(i, m_azimuthalHaloCells, m_azimuthalWindowCells);
3720
3721 const MInt childId = globalIdToLocalId(globalChildId, true);
3722 m_azimuthalWindowCells[idx].push_back(childId);
3723 }
3724 }
3725
3726 // Newly mapped halos
3727 // If a child of an unmapped azimuthal halo cell has an adequate internal cell
3728 // This child becomes an regular azimuthal halo cell. Thus, it is added to the azimuthal halo cell vector
3729 // Otherwise the child becomes an unmapped azimuthal halo cell
3730 MInt noUnmappedCells = noAzimuthalUnmappedHaloCells();
3731 for(MInt i = 0; i < noUnmappedCells; i++ ) {
3732 MInt cellId = azimuthalUnmappedHaloCell(i);
3733 if(a_level(cellId) == level) {
3734 if(accumulate(&recvChildIds[m_maxNoChilds * i], &recvChildIds[m_maxNoChilds * i] + m_maxNoChilds,
3735 static_cast<MLong>(-2), [](const MLong& a, const MLong& b) { return std::max(a, b); })
3736 > -2) {
3737
3738 refineCell(cellId, nullptr, a_hasProperty(cellId, Cell::IsPartLvlAncestor));
3739 refineIds.push_back(cellId);
3740
3741 for(MInt child = 0; child < m_maxNoChilds; child++) {
3742 const MInt childId = a_childId(cellId, child);
3743
3744 ASSERT(childId > -1 && childId < m_tree.size(), to_string(childId) + " " + to_string(m_tree.size()));
3745 a_hasProperty(childId, Cell::IsHalo) = a_hasProperty(cellId, Cell::IsHalo);
3746 a_hasProperty(childId, Cell::IsPeriodic) = a_hasProperty(cellId, Cell::IsPeriodic);
3747 a_globalId(childId) = recvChildIds[m_maxNoChilds * i + child];
3748 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
3749 treeb().solver(childId, solver) = treeb().solver(cellId, solver);
3750 }
3751
3752 MInt dom = recvChildDomainIds[m_maxNoChilds * i + child];
3753
3754 if(a_globalId(childId) == -1) {
3755 m_azimuthalUnmappedHaloCells.push_back(childId);
3756 m_azimuthalUnmappedHaloDomains.push_back(dom);
3757 continue;
3758 }
3759
3760 ASSERT(findNeighborDomainId(a_globalId(childId)) == dom, "Wrong domain! " + to_string(a_globalId(childId)) + " " + to_string(findNeighborDomainId(a_globalId(childId))) + " " + to_string(dom) + " " + to_string(domainId()) + " " + to_string(cellId));
3761
3762 MInt idx = setAzimuthalNeighborDomainIndex(dom, m_azimuthalHaloCells, m_azimuthalWindowCells);
3763
3764 m_azimuthalHaloCells[idx].push_back(childId);
3765 }
3766 }
3767 }
3768 }
3769 }
3770
3771 // Update grid bndry cells
3772 MBool adaptation = false;
3773 adaptation = Context::getBasicProperty<MBool>("adaptation", AT_, &adaptation);
3774 if(!adaptation) {
3775 std::array<MFloat, 2 * nDim> bbox;
3776 for(MInt i = 0; i < nDim; i++) {
3777 bbox[i] = numeric_limits<MFloat>::max();
3778 bbox[nDim + i] = numeric_limits<MFloat>::lowest();
3779 }
3780 for(MInt cellId = 0; cellId < m_noInternalCells; cellId++) {
3781 for(MInt i = 0; i < nDim; i++) {
3782 bbox[i] = mMin(bbox[i], a_coordinate(cellId, i) - F1B2 * cellLengthAtLevel(a_level(cellId)));
3783 bbox[nDim + i] = mMax(bbox[nDim + i], a_coordinate(cellId, i) + F1B2 * cellLengthAtLevel(a_level(cellId)));
3784 }
3785 }
3786 MPI_Allreduce(MPI_IN_PLACE, &bbox[0], nDim, MPI_DOUBLE, MPI_MIN, mpiComm(), AT_, "MPI_IN_PLACE", "bbox[0]");
3787 MPI_Allreduce(MPI_IN_PLACE, &bbox[nDim], nDim, MPI_DOUBLE, MPI_MAX, mpiComm(), AT_, "MPI_IN_PLACE", "bbox[nDim]");
3788 MBool isBndry = false;
3789 for(MInt i = 0; i < m_noInternalCells; i++) {
3790 MInt cellId =i;
3791 if(a_level(cellId) != level+1) { continue;
3792}
3793 isBndry = false;
3794 for(MInt dir = 0; dir < m_noDirs; dir++) {
3795 if(a_hasNeighbor(cellId, dir) > 0) { continue;
3796}
3797 if(a_hasParent(cellId) && a_hasNeighbor(a_parentId(cellId), dir) > 0) {
3798 MInt nghbrParentId = a_neighborId(a_parentId(cellId),dir);
3799 if(a_noChildren(nghbrParentId) == 0) {
3800 continue;
3801 }
3802 }
3803 if(!m_periodicCartesianDir[dir/2]) {
3804 std::array<MFloat, nDim> coords;
3805 for(MInt d = 0; d < nDim; d++) {
3806 coords[d] = a_coordinate(cellId,d);
3807 }
3808 coords[dir / 2] += ((dir % 2) == 0 ? -F1 : F1) * cellLengthAtLevel(m_minLevel);
3809 if(coords[dir / 2] < bbox[dir / 2] || coords[dir / 2] > bbox[nDim + dir / 2]) {
3810 continue;
3811}
3812 }
3813 isBndry = true;
3814 }
3815 if(isBndry) {
3816 if(!a_isHalo(cellId)) { m_gridBndryCells.push_back(cellId);
3817}
3818 for(MInt dir = 0; dir < m_noDirs; dir++) {
3819 if(a_hasNeighbor(cellId, dir) > 0) {
3820 MInt nghbrId = a_neighborId(cellId, dir);
3821 if(!a_isHalo(nghbrId)) {
3822 m_gridBndryCells.push_back(nghbrId);
3823 }
3824 }
3825 }
3826 }
3827 }
3828 }
3829 }
3830
3831 // sort halo and window cells by globalId to get matching connectivity
3832 // this may not even be required, check whether ordering is implicity matching for
3833 // a complex case with multiple partition level shifts
3834 if(m_maxPartitionLevelShift > 0) {
3835 for(MInt i = 0; i < noNeighborDomains(); i++) {
3836 sort(m_haloCells[i].begin(), m_haloCells[i].end(),
3837 [this](const MInt& a, const MInt& b) { return a_globalId(a) < a_globalId(b); });
3838 sort(m_windowCells[i].begin(), m_windowCells[i].end(),
3839 [this](const MInt& a, const MInt& b) { return a_globalId(a) < a_globalId(b); });
3840 }
3841 }
3842
3843
3844 if(treeb().noSolvers() > 1) { // Exchange solver info
3845 maia::mpi::exchangeBitset(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), &m_tree.solverBits(0),
3846 m_tree.size());
3847 if(m_azimuthalPer && noAzimuthalNeighborDomains() > 0) {
3848 maia::mpi::exchangeBitset(m_azimuthalNghbrDomains, m_azimuthalHaloCells, m_azimuthalWindowCells, mpiComm(), &m_tree.solverBits(0),
3849 m_tree.size());
3850
3851 // To ensure that azimuthal halo cells are fully refined (have all children)
3852 // or are not refined at all, solver Bits need to be checked!
3853 correctAzimuthalSolverBits();
3854 }
3855 }
3856
3857 // trigger refineCell() for halo cells on the solvers
3858 if(!refineCellSolver.empty()) {
3859 // initially empty during solver startup, no solver refinement needed then.
3860 for(auto& cellId : refineIds) {
3861 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
3862 if(!m_tree.solver(cellId, solver)) continue;
3863 if(a_hasChildren(cellId, solver)) {
3864 refineCellSolver[solver](cellId); // call refineCell() function in each solver
3865 }
3866 }
3867 }
3868 refineIds.clear();
3869 }
3870
3871 // Set window/halo flags
3872 for(MInt i = 0; i < noNeighborDomains(); i++) {
3873 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
3874 ASSERT(!a_hasProperty(m_haloCells[i][j], Cell::IsWindow), "halo cell is marked as window");
3875 a_hasProperty(m_haloCells[i][j], Cell::IsHalo) = true;
3876 // a_hasProperty(m_haloCells[i][j], Cell::IsWindow) = false;
3877 }
3878 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
3879 ASSERT(!a_isHalo(m_windowCells[i][j]), "window cell is marked as halo");
3880 // a_hasProperty( m_windowCells[i][j], Cell::IsHalo ) = false;
3881 a_hasProperty(m_windowCells[i][j], Cell::IsWindow) = true;
3882 }
3883 }
3884 if(m_azimuthalPer) {
3885 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
3886 for(MInt j = 0; j < (signed)m_azimuthalHaloCells[i].size(); j++) {
3887 ASSERT(!a_hasProperty(m_azimuthalHaloCells[i][j], Cell::IsWindow), "azimuthal halo cell is marked as window");
3888 a_hasProperty(m_azimuthalHaloCells[i][j], Cell::IsHalo) = true;
3889 }
3890 for(MInt j = 0; j < (signed)m_azimuthalWindowCells[i].size(); j++) {
3891 ASSERT(!a_isHalo(m_azimuthalWindowCells[i][j]), "azimuthal window cell is marked as halo");
3892 a_hasProperty(m_azimuthalWindowCells[i][j], Cell::IsWindow) = true;
3893 }
3894 }
3895 for(MInt j = 0; j < noAzimuthalUnmappedHaloCells(); j++ ) {
3896 ASSERT(!a_hasProperty(m_azimuthalUnmappedHaloCells[j], Cell::IsWindow), "azimuthal halo cell is marked as window");
3897 a_hasProperty(m_azimuthalUnmappedHaloCells[j], Cell::IsHalo) = true;
3898 }
3899 }
3900 }
3901
3902 if(domainId() == 0 && onlyLevel < 0) cerr << endl;
3903
3904}
3905
3906//-------------------------------------------------------------------------------------------
3907
3908/* \param[in] data data field, whre each field of the bitset corresponds to a solver
3909 * \param[in] defaultVal
3910 *
3911 * The concept of the following is to exchange 'data' from window cells to halo cells. 'data' is a bitset
3912 * with elements for each solver. If for the i-th solver a cell is not a window cell, the value 'defaultVal'
3913 * is exchanged instead of data[cellId][i]. One use case are the solverBits.
3914 */
3915template <MInt nDim>
3916template <std::size_t N>
3917void CartesianGrid<nDim>::exchangeSolverBitset(std::bitset<N>* const data, const MBool defaultVal) {
3918 TRACE();
3919
3920 //maia::mpi::exchangeBitset(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), &m_tree.solverBits(0),
3921 // m_tree.size());
3922 // return;
3923
3924 // There might be the case that a halo cell resides inside the domain of a solver, but since that solver
3925 // does not require that many layers we disable that cell for that solver. Sounds crazy, but trust me.
3926 static_assert(N <= 64, "conversion to ulong not appropriate, change to ullong!");
3927 ScratchSpace<MPI_Request> recvRequests(max(1, noNeighborDomains()), AT_, "recvRequests");
3928 fill(recvRequests.begin(), recvRequests.end(), MPI_REQUEST_NULL);
3929 MInt receiveCount = 0;
3930 for (const auto& vecHalo : m_haloCells) receiveCount+=vecHalo.size();
3931 ScratchSpace<MUlong> haloBuffer(max(1, receiveCount), AT_, "haloBuffer");
3932 for (MInt i = 0, offset = 0; i < noNeighborDomains(); i++) {
3933 const MInt noHaloCells = m_haloCells[i].size();
3934 if (noHaloCells<1) continue;
3935
3936 MPI_Irecv(&haloBuffer[offset], noHaloCells, type_traits<MUlong>::mpiType(), m_nghbrDomains[i],
3937 m_nghbrDomains[i], mpiComm(), &recvRequests[i], AT_, "haloBuffer[offset]");
3938
3939 offset += noHaloCells;
3940 }
3941
3942
3943 ScratchSpace<MPI_Request> sendRequests(max(1, noNeighborDomains()), AT_, "sendRequests");
3944 fill(sendRequests.begin(), sendRequests.end(), MPI_REQUEST_NULL);
3945 MInt sendCount = 0;
3946 for (const auto& vecWindow : m_windowCells) sendCount+=vecWindow.size();
3947 ScratchSpace<MUlong> tmp_data(sendCount, AT_, "tmp_data");
3948 for (MInt i = 0, idx = 0; i < noNeighborDomains(); i++) {
3949 const MInt offset = idx;
3950 const MInt noWindowCells = m_windowCells[i].size();
3951 if (noWindowCells<1) continue;
3952 for (const auto cellId : m_windowCells[i]) {
3953 const auto backup = data[cellId].to_ulong();
3954 ASSERT(m_windowLayer_[i].find(cellId)!=m_windowLayer_[i].end(), "You don't know what you are doing!");
3955 for (MInt solverId = 0; solverId < treeb().noSolvers(); ++solverId) {
3956 if (!isSolverWindowCell(i, cellId, solverId)) {
3957 data[cellId][solverId] = defaultVal;
3958 }
3959 }
3960 tmp_data[idx++] = data[cellId].to_ulong();
3961 data[cellId] = std::bitset<N>(backup);
3962 }
3963 ASSERT(idx-offset==noWindowCells, "");
3964
3965 MPI_Isend(&tmp_data[offset], noWindowCells, type_traits<MUlong>::mpiType(), m_nghbrDomains[i], domainId(),
3966 mpiComm(), &sendRequests[i], AT_, "tmp_data");
3967 }
3968
3969 // Finish MPI communication
3970 MPI_Waitall(noNeighborDomains(), &recvRequests[0], MPI_STATUSES_IGNORE, AT_);
3971 MPI_Waitall(noNeighborDomains(), &sendRequests[0], MPI_STATUSES_IGNORE, AT_);
3972
3973 for (MInt i = 0, idx = 0; i < noNeighborDomains(); i++) {
3974 for (const auto cellId : m_haloCells[i])
3975 data[cellId] = std::bitset<N>(haloBuffer[idx++]);
3976 }
3977}
3978
3979
3983template <MInt nDim>
3984MInt CartesianGrid<nDim>::createAdjacentHaloCell(const MInt cellId, const MInt dir, const MLong* hilbertOffsets,
3985 unordered_multimap<MLong, MInt>& hilbertToLocal,
3986 const MFloat* bbox, MInt* const noHalos,
3987 vector<vector<MInt>>& halos, vector<vector<MLong>>& hilbertIds) {
3988 static constexpr MInt revDir[6] = {1, 0, 3, 2, 5, 4};
3989 const MFloat cellLength = cellLengthAtCell(cellId);
3990
3991 MFloat coords[3];
3992 MFloat dcoords[3];
3993 MFloat dummyCoords[3];
3994 for(MInt i = 0; i < nDim; i++) {
3995 coords[i] = a_coordinate(cellId, i);
3996 }
3997 coords[dir / 2] += ((dir % 2) == 0 ? -F1 : F1) * cellLength;
3998
3999 for(MInt i = 0; i < nDim; i++) {
4000 if(!m_periodicCartesianDir[dir / 2] && (coords[dir / 2] < bbox[dir / 2] || coords[dir / 2] > bbox[nDim + dir / 2]))
4001 return -1;
4002 }
4003
4004 MBool insd = true;
4005 for(MInt i = 0; i < nDim; i++) {
4006 if(coords[i] < bbox[i] || coords[i] > bbox[nDim + i]) insd = false;
4007 }
4008 MBool isPeriodic = !insd;
4009
4010 if(m_azimuthalPer && isPeriodic) return -1;
4011
4012 MFloat dx[3];
4013 for(MInt i = 0; i < nDim; i++) {
4014 dx[i] = bbox[nDim + i] - bbox[i];
4015 }
4016 for(MInt i = 0; i < nDim; i++) {
4017 dummyCoords[i] = coords[i];
4018 }
4019 if(isPeriodic) {
4020 for(MInt i = 0; i < nDim; i++) {
4021 if(coords[i] < bbox[i])
4022 dummyCoords[i] += dx[i];
4023 else if(coords[i] > bbox[nDim + i])
4024 dummyCoords[i] -= dx[i];
4025 }
4026 }
4027
4028 for(MInt i = 0; i < nDim; i++) {
4029 if(dummyCoords[i] < bbox[i] || dummyCoords[i] > bbox[nDim + i]) {
4030 mTerm(1, AT_, "Halo cell coords outside domain");
4031 }
4032 }
4033
4034 const MLong hilbertIndex = hilbertIndexGeneric(&dummyCoords[0]);
4035 MInt ndom = -1;
4036 while(hilbertIndex >= hilbertOffsets[ndom + 1]) {
4037 ndom++;
4038 if(ndom == noDomains() - 1) break;
4039 }
4040 ASSERT(ndom > -1, "");
4041 ASSERT(hilbertIndex >= hilbertOffsets[ndom] && hilbertIndex < hilbertOffsets[ndom + 1],
4042 to_string(hilbertIndex) + " " + to_string(hilbertOffsets[ndom]) + " " + to_string(hilbertOffsets[ndom + 1]));
4043
4044 const MInt haloCellId = m_tree.size();
4045 m_tree.append();
4046
4047 for(MInt i = 0; i < nDim; i++) {
4048 a_level(haloCellId) = a_level(cellId);
4049 }
4050
4051 for(MInt i = 0; i < nDim; i++) {
4052 a_coordinate(haloCellId, i) = coords[i];
4053 }
4054
4055 hilbertToLocal.insert(make_pair(hilbertIndex, haloCellId));
4056
4057 for(MInt i = 0; i < m_noDirs; i++) {
4058 a_neighborId(haloCellId, i) = -1;
4059 }
4060 for(MInt i = 0; i < m_maxNoChilds; i++) {
4061 a_childId(haloCellId, i) = -1;
4062 }
4063 a_parentId(haloCellId) = -1;
4064
4065 a_neighborId(cellId, dir) = haloCellId;
4066 a_neighborId(haloCellId, revDir[dir]) = cellId;
4067 for(MInt otherDir = 0; otherDir < m_noDirs; otherDir++) {
4068 if(otherDir / 2 == dir / 2) continue;
4069 if(a_hasNeighbor(cellId, otherDir) == 0) continue;
4070 MInt nghbrId = a_neighborId(cellId, otherDir);
4071 if(a_hasNeighbor(nghbrId, dir) > 0) {
4072 nghbrId = a_neighborId(nghbrId, dir);
4073 a_neighborId(nghbrId, revDir[otherDir]) = haloCellId;
4074 a_neighborId(haloCellId, otherDir) = nghbrId;
4075 }
4076 }
4077 for(MInt ndir = 0; ndir < m_noDirs; ndir++) {
4078 if(a_hasNeighbor(haloCellId, ndir) > 0) continue;
4079 for(MInt i = 0; i < nDim; i++) {
4080 dummyCoords[i] = a_coordinate(haloCellId, i);
4081 }
4082 dummyCoords[ndir / 2] += ((ndir % 2) == 0 ? -F1 : F1) * cellLength;
4083 for(MInt i = 0; i < nDim; i++) {
4084 dcoords[i] = dummyCoords[i];
4085 }
4086 for(MInt i = 0; i < nDim; i++) {
4087 if(dummyCoords[i] < bbox[i])
4088 dummyCoords[i] += dx[i];
4089 else if(dummyCoords[i] > bbox[nDim + i])
4090 dummyCoords[i] -= dx[i];
4091 }
4092 const MLong nghbrHilbertId = hilbertIndexGeneric(&dummyCoords[0]);
4093 MInt nghbrId = -1;
4094 auto range = hilbertToLocal.equal_range(nghbrHilbertId);
4095 for(auto it = range.first; it != range.second; ++it) {
4096 MFloat dist = F0;
4097 for(MInt i = 0; i < nDim; i++) {
4098 dist += POW2(a_coordinate(it->second, i) - dcoords[i]);
4099 }
4100 dist = sqrt(dist);
4101 if(dist < 0.001 * cellLength) {
4102 if(nghbrId > -1) cerr << "duplicate " << hilbertToLocal.count(nghbrHilbertId) << endl;
4103 nghbrId = it->second;
4104 }
4105 }
4106 if(nghbrId < 0) continue;
4107
4108#ifndef NDEBUG
4109 for(MInt i = 0; i < nDim; i++) {
4110 ASSERT(fabs(a_coordinate(nghbrId, i) - a_coordinate(haloCellId, i)) < cellLength * 1.0001, "");
4111 }
4112#endif
4113 a_neighborId(nghbrId, revDir[ndir]) = haloCellId;
4114 a_neighborId(haloCellId, ndir) = nghbrId;
4115 }
4116
4117 a_resetProperties(haloCellId);
4118 a_hasProperty(haloCellId, Cell::IsHalo) = true;
4119 a_hasProperty(haloCellId, Cell::IsPeriodic) = isPeriodic;
4120
4121 MInt idx = setNeighborDomainIndex(ndom, halos, hilbertIds);
4122 ASSERT(idx > -1, "");
4123 halos[idx].push_back(haloCellId);
4124 hilbertIds[idx].push_back(hilbertIndex);
4125 noHalos[ndom]++;
4126
4127 return haloCellId;
4128}
4129
4130
4131// -------------------------------------------------------------------------------------------
4132
4133
4138template <MInt nDim>
4140 TRACE();
4141 const auto noNghbrDomains0 = (signed)m_nghbrDomains.size();
4142 const auto noAzimuthalNghbrDomains0 = (signed)m_azimuthalNghbrDomains.size();
4143 if(noNghbrDomains0 == 0 && noAzimuthalNghbrDomains0 == 0) {
4144 // serial computations can have neighbor domains, for example, when using
4145 // periodic boundaries, and that requires halo cells.
4146 //
4147 // That is, if there aren't any neighbor domains, halo cells are not required.
4148 return;
4149 }
4150
4151 ScratchSpace<MInt> domMap(noNghbrDomains0, AT_, "domMap");
4152
4153 vector<pair<MInt, MInt>> tmpDoms;
4154 tmpDoms.reserve(noNghbrDomains0);
4155
4156 // sort neighbor domain ids to avoid send/recv deadlocks
4157 for(MInt i = 0; i < noNghbrDomains0; i++) {
4158 if(m_haloCells[i].size() > 0 || m_windowCells[i].size() > 0) {
4159 tmpDoms.push_back(make_pair(m_nghbrDomains[i], i));
4160 }
4161 }
4162 sort(tmpDoms.begin(), tmpDoms.end()); // sort by ascending neighbor domain id
4163
4164 std::vector<MInt>().swap(m_nghbrDomains);
4165 ASSERT(m_nghbrDomainIndex.size() >= static_cast<size_t>(noDomains()),
4166 "m_nghbrDomainIndex size is " + std::to_string(m_nghbrDomainIndex.size())
4167 + " which is smaller than noDomains() = " + std::to_string(noDomains()));
4168 std::fill_n(m_nghbrDomainIndex.begin(), noDomains(), -1);
4169 for(MInt i = 0; i < (signed)tmpDoms.size(); i++) {
4170 domMap[i] = tmpDoms[i].second;
4171 m_nghbrDomainIndex[tmpDoms[i].first] = m_nghbrDomains.size();
4172 m_nghbrDomains.push_back(tmpDoms[i].first);
4173 if(m_nghbrDomains[i] == domainId() && m_noPeriodicCartesianDirs == 0) {
4174 TERMM(1, "Not supposed to happen: connection to self without periodicity.");
4175 }
4176 if(m_nghbrDomains[i] == domainId()) m_log << domainId() << ": periodic connection to self." << endl;
4177 }
4178
4179
4180 // sort window and halo cells by globalId
4181 const MBool sortHaloWindowCells = false;
4182 if(sortHaloWindowCells) {
4183 for(MInt i = 0; i < noNghbrDomains0; i++) {
4184 sort(m_haloCells[i].begin(), m_haloCells[i].end(),
4185 [this](const MInt& a, const MInt& b) { return a_globalId(a) < a_globalId(b); });
4186 sort(m_windowCells[i].begin(), m_windowCells[i].end(),
4187 [this](const MInt& a, const MInt& b) { return a_globalId(a) < a_globalId(b); });
4188 }
4189 }
4190
4191
4192 // write window/halo cells
4193 vector<std::vector<MInt>> haloCellBak(m_haloCells);
4194 vector<std::vector<MInt>> windowCellBak(m_windowCells);
4195 m_haloCells.resize(noNeighborDomains());
4196 m_windowCells.resize(noNeighborDomains());
4197 for(MInt i = 0; i < noNeighborDomains(); i++) {
4198 m_haloCells[i].resize(haloCellBak[domMap[i]].size());
4199 m_windowCells[i].resize(windowCellBak[domMap[i]].size());
4200 ASSERT(m_haloCells[i].size() >= haloCellBak[domMap[i]].size(), "");
4201 ASSERT(m_windowCells[i].size() >= windowCellBak[domMap[i]].size(), "");
4202 copy(haloCellBak[domMap[i]].begin(), haloCellBak[domMap[i]].end(), m_haloCells[i].begin());
4203 copy(windowCellBak[domMap[i]].begin(), windowCellBak[domMap[i]].end(), m_windowCells[i].begin());
4204 }
4205
4206 // TODO_SS labels:GRID,toenhance use move operation
4207 if (m_windowLayer_.size()>0 && m_haloMode>0) { //WH_old
4208 ASSERT(noNeighborDomains()<=(signed)m_windowLayer_.size(), "");
4209 std::vector<std::unordered_map<MInt, M32X4bit<true>>> windowLayerBak(m_windowLayer_);
4210 m_windowLayer_.resize(noNeighborDomains());
4211 for(MInt i = 0; i < noNeighborDomains(); i++) {
4212 m_windowLayer_[i] = windowLayerBak[domMap[i]];
4213 }
4214 }
4215
4216
4217 if(m_azimuthalPer) {
4218 ScratchSpace<MInt> domMapAzi(noAzimuthalNghbrDomains0, AT_, "domMapAzi");
4219 // sort neighbor domain ids to avoid send/recv deadlocks
4220 tmpDoms.clear();
4221 tmpDoms.reserve(noAzimuthalNghbrDomains0);
4222 for(MInt i = 0; i < noAzimuthalNghbrDomains0; i++) {
4223 if(m_azimuthalHaloCells[i].size() > 0 || m_azimuthalWindowCells[i].size() > 0) {
4224 tmpDoms.push_back(make_pair(m_azimuthalNghbrDomains[i], i));
4225 }
4226 }
4227 sort(tmpDoms.begin(), tmpDoms.end()); // sort by ascending neighbor domain id
4228
4229 m_azimuthalNghbrDomains.clear();
4230 ASSERT(m_azimuthalNghbrDomainIndex.size() >= static_cast<size_t>(noDomains()),
4231 "m_azimuthalNghbrDomainIndex size is " + std::to_string(m_azimuthalNghbrDomainIndex.size())
4232 + " which is smaller than noDomains() = " + std::to_string(noDomains()));
4233 std::fill_n(m_azimuthalNghbrDomainIndex.begin(), noDomains(), -1);
4234 for(MInt i = 0; i < (signed)tmpDoms.size(); i++) {
4235 domMapAzi[i] = tmpDoms[i].second;
4236 m_azimuthalNghbrDomainIndex[tmpDoms[i].first] = m_azimuthalNghbrDomains.size();
4237 m_azimuthalNghbrDomains.push_back(tmpDoms[i].first);
4238 if(m_azimuthalNghbrDomains[i] == domainId()) m_log << domainId() << ": periodic connection to self." << endl;
4239 }
4240
4241 // sort azimuthal window and halo cells by globalId
4242 if(sortHaloWindowCells) {
4243 vector<MInt> posMapAzi;
4244 for(MInt i = 0; i < noAzimuthalNghbrDomains0; i++) {
4245 sort(m_azimuthalHaloCells[i].begin(), m_azimuthalHaloCells[i].end(),
4246 [this](const MInt& a, const MInt& b) { return a_globalId(a) < a_globalId(b); });
4247
4248 vector<pair<MInt, MInt>> azimuthalWindowCells;
4249 for(MInt j = 0; j < (signed)m_azimuthalWindowCells[i].size(); j++) {
4250 azimuthalWindowCells.push_back(make_pair(m_azimuthalWindowCells[i][j], j));
4251 }
4252 m_azimuthalWindowCells[i].clear();
4253 sort(azimuthalWindowCells.begin(), azimuthalWindowCells.end(),
4254 [&](const auto& a, const auto& b) { return a_globalId(a.first) < a_globalId(b.first); });
4255 m_azimuthalWindowCells[i].resize(azimuthalWindowCells.size());
4256 posMapAzi.resize(azimuthalWindowCells.size());
4257 for(MInt j = 0; j < (signed)azimuthalWindowCells.size(); j++) {
4258 m_azimuthalWindowCells[i][j] = azimuthalWindowCells[j].first;
4259 posMapAzi[azimuthalWindowCells[j].second] = j;
4260 }
4261 vector<MInt> higherLevelConnectivityBak(m_azimuthalHigherLevelConnectivity[i]);
4262 m_azimuthalHigherLevelConnectivity[i].resize(higherLevelConnectivityBak.size());
4263 for(MInt c = 0; c < (signed)higherLevelConnectivityBak.size(); c++) {
4264 m_azimuthalHigherLevelConnectivity[i].push_back(posMapAzi[higherLevelConnectivityBak[c]]);
4265 }
4266 azimuthalWindowCells.clear();
4267 higherLevelConnectivityBak.clear();
4268 }
4269 }
4270
4271 // write window/halo cells
4272 haloCellBak.clear();
4273 windowCellBak.clear();
4274 haloCellBak = m_azimuthalHaloCells;
4275 windowCellBak = m_azimuthalWindowCells;
4276 vector<vector<MInt>> higherLevelConnectivityBak2(m_azimuthalHigherLevelConnectivity);
4277 m_azimuthalHaloCells.resize(noAzimuthalNeighborDomains());
4278 m_azimuthalWindowCells.resize(noAzimuthalNeighborDomains());
4279 m_azimuthalHigherLevelConnectivity.resize(noAzimuthalNeighborDomains());
4280 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
4281 m_azimuthalHaloCells[i].resize(haloCellBak[domMapAzi[i]].size());
4282 m_azimuthalWindowCells[i].resize(windowCellBak[domMapAzi[i]].size());
4283 m_azimuthalHigherLevelConnectivity[i].resize(higherLevelConnectivityBak2[domMapAzi[i]].size());
4284 ASSERT(m_azimuthalHaloCells[i].size() >= haloCellBak[domMapAzi[i]].size(), "");
4285 ASSERT(m_azimuthalWindowCells[i].size() >= windowCellBak[domMapAzi[i]].size(), "");
4286 copy(haloCellBak[domMapAzi[i]].begin(), haloCellBak[domMapAzi[i]].end(), m_azimuthalHaloCells[i].begin());
4287 copy(windowCellBak[domMapAzi[i]].begin(), windowCellBak[domMapAzi[i]].end(), m_azimuthalWindowCells[i].begin());
4288 copy(higherLevelConnectivityBak2[domMapAzi[i]].begin(), higherLevelConnectivityBak2[domMapAzi[i]].end(), m_azimuthalHigherLevelConnectivity[i].begin());
4289 }
4290 }
4291}
4292
4293
4294// -------------------------------------------------------------------------------------------
4295
4296
4301template <MInt nDim>
4302void CartesianGrid<nDim>::tagActiveWindows(vector<MLong>& refineChildIds, MInt level) {
4303 constexpr MInt maxNghbrDoms = 1024;
4304 MIntScratchSpace nghbrList(27 * m_maxNoChilds, AT_, "nghbrList");
4305 vector<std::bitset<maxNghbrDoms>> nghrDomFlag(m_tree.size());
4306 vector<std::bitset<maxNghbrDoms>> nghrDomFlagBak;
4307 vector<std::bitset<maxNghbrDoms>> nghrDomFlagBak2;
4308 set<MInt> exchangeCellList;
4309 if(noNeighborDomains() > maxNghbrDoms) {
4310 mTerm(1, AT_, "Too many neighbor domains(" + std::to_string(noNeighborDomains()) + "). Increase bitset size.");
4311 }
4312 // temporary solution for partitionLevelShifts, might produce a lot of halo cells, more clever way to flag active
4313 // windows should be found
4314 // TODO labels:GRID @ansgar_pls_adapt fix this!
4315 constexpr MBool testingFix_partitionLevelShift = true; // false;
4316
4317 // TODO labels:GRID @timw_multiSolverHalos remove old version!
4318 constexpr MBool multiSolverHaloLayer = true; // should be true
4319
4320 MInt windowCnt = 0;
4321 for(MInt i = 0; i < noNeighborDomains(); i++)
4322 windowCnt += m_windowCells[i].size();
4323 refineChildIds.resize(windowCnt * m_maxNoChilds);
4324 fill(refineChildIds.begin(), refineChildIds.end(), -1);
4325
4326
4327 // identify window cells on the next higher level, or coarser leaf window cells if not existing
4328 for(MInt i = 0; i < noNeighborDomains(); i++) {
4329 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
4330 const MInt cellId = m_windowCells[i][j];
4331
4332 // if ( a_level( cellId ) != level ) continue; //however, such cells should not exist, yet
4333 if(a_noChildren(cellId) > 0) {
4334 if(a_level(cellId) == level) {
4335 for(MInt child = 0; child < m_maxNoChilds; child++) {
4336 MInt childId = a_childId(cellId, child);
4337 if(childId < 0) continue;
4338
4339 // fix for unconsistent window tags with double window cells (domain interface and periodic)
4340 // const MBool doubleWindow = exchangeCellList.find(cellId) != exchangeCellList.end();
4341 // if(!doubleWindow){
4342 exchangeCellList.insert(childId);
4343 //}
4344 }
4345 }
4346 } else {
4347 exchangeCellList.insert(cellId);
4348 }
4349 }
4350 }
4351
4352 // add halo cells
4353 // NOTE: the loop below or similar assumptions that
4354 // internal/halo cells are sorted are wrong during adaptation
4355 // for (MInt cellId = m_noInternalCells; cellId < m_tree.size(); cellId++) {
4356 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
4357 if(a_isHalo(cellId)) {
4358 exchangeCellList.insert(cellId);
4359 }
4360 }
4361
4362 // exchangeCellList conaints all window and halo-Cells,
4363 // and additionally all children of all windowCells
4364
4365 if(m_maxPartitionLevelShift > 0) {
4366 // exchange some window cell data
4367 ScratchSpace<MInt> bprops(m_tree.size(), 2, AT_, "bprops");
4368 bprops.fill(-1);
4369 // Note: changed loop to iterate over whole tree, internal and halo cells not sorted!
4370 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
4371 if(a_isHalo(cellId) || a_isToDelete(cellId)) {
4372 continue;
4373 }
4374 bprops(cellId, 0) = (MInt)a_hasProperty(cellId, Cell::IsPartLvlAncestor);
4375 bprops(cellId, 1) = a_noOffsprings(cellId);
4376
4377 ASSERT(bprops(cellId, 0) > -1 && bprops(cellId, 1) > 0,
4378 std::to_string(cellId) + " " + std::to_string(bprops(cellId, 0)) + " "
4379 + std::to_string(bprops(cellId, 1)));
4380 }
4381 maia::mpi::exchangeData(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), bprops.getPointer(), 2);
4382
4383 // Note: loop only over the current haloCells since only for these data is exchanged, this does
4384 // not work properly if in case of a partition level shift some halo cell is not part of
4385 // m_haloCells yet such that its property IsPartLvlAncestor is reset!
4386 for(MInt i = 0; i < noNeighborDomains(); i++) {
4387 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
4388 const MInt cellId = m_haloCells[i][j];
4389 ASSERT(!a_isToDelete(cellId), "");
4390 ASSERT(bprops(cellId, 0) > -1 && bprops(cellId, 1) > 0,
4391 std::to_string(cellId) + " " + std::to_string(bprops(cellId, 0)) + " "
4392 + std::to_string(bprops(cellId, 1)));
4393 a_hasProperty(cellId, Cell::IsPartLvlAncestor) = (MBool)bprops(cellId, 0);
4394 a_noOffsprings(cellId) = bprops(cellId, 1);
4395 }
4396 }
4397 }
4398
4399 for(MInt i = m_tree.size(); i--;) {
4400 for(MInt j = 0; j < maxNghbrDoms; j++) {
4401 ASSERT(!nghrDomFlag[i][j], "");
4402 }
4403 }
4404
4405 // mark all halo cells as nghbrdomain
4406 for(MInt i = 0; i < noNeighborDomains(); i++) {
4407 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
4408 const MInt cellId = m_haloCells[i][j];
4409
4410 nghrDomFlag[cellId][i] = true;
4411
4412 if(m_maxPartitionLevelShift > 0) {
4413 if(a_hasProperty(cellId, Cell::IsPartLvlAncestor)) {
4414 MInt minNghbrDomId = findNeighborDomainId(a_globalId(cellId));
4415 MInt maxNghbrDomId =
4416 findNeighborDomainId(mMin(m_noCellsGlobal - 1, a_globalId(cellId) + (MLong)a_noOffsprings(cellId)));
4417 for(MInt k = 0; k < noNeighborDomains(); k++) {
4418 if(m_nghbrDomains[k] >= minNghbrDomId && m_nghbrDomains[k] <= maxNghbrDomId) {
4419 nghrDomFlag[cellId][k] = true;
4420 for(MInt child = 0; child < m_maxNoChilds; child++) {
4421 MInt childId = a_childId(cellId, child);
4422 if(childId > -1) {
4423 nghrDomFlag[childId][k] = true;
4424 }
4425 }
4426 }
4427 }
4428 }
4429 }
4430 }
4431 }
4432
4433 map<MInt, MInt> solverCellsAdded;
4434 solverCellsAdded.clear();
4435
4436 // extend halo cells by the defined number of halo layers
4437 // NOTE: m_noHaloLayers corresponds to the number of halo-Layers of the grid
4438 // for multiSolver applications some solvers may have a lower number of halo-Layers!
4439 for(MInt layer = 0; layer < m_noHaloLayers; layer++) {
4440 nghrDomFlagBak.assign(nghrDomFlag.begin(), nghrDomFlag.end());
4441
4442 for(MInt cellId : exchangeCellList) {
4443 if(a_level(cellId) > (level + 1)) continue;
4444 if(a_level(cellId) < (level + 1) && a_noChildren(cellId) > 0) continue;
4445 const MInt counter = getAdjacentGridCells(cellId, nghbrList, level);
4446 // check diffs when called instead!!!!
4447 for(MInt n = 0; n < counter; n++) {
4448 MInt nghbrId = nghbrList[n];
4449 if(nghbrId < 0) continue;
4450
4451 // if the neighbor doesn't belong to all solvers
4452 // an additional layer needs to be added to the layer is below the noHaloLayers
4453 // for the solver, which does not have the neighbor!
4454 // to ensure that this each solver has at least the number of halo layers required!
4455 if(g_multiSolverGrid && multiSolverHaloLayer && !m_paraViewPlugin) {
4456 for(MInt solverId = 0; solverId < treeb().noSolvers(); solverId++) {
4457 if(!treeb().solver(nghbrId, solverId) && layer < m_noSolverHaloLayers[solverId] /*&& layer > 0*/) {
4458 solverCellsAdded.insert(make_pair(nghbrId, solverId));
4459 }
4460 }
4461 }
4462
4463 for(MInt i = 0; i < noNeighborDomains(); i++) {
4464 if(nghrDomFlagBak[nghbrId][i]) {
4465 nghrDomFlag[cellId][i] = true;
4466 }
4467 }
4468
4469 // fix for unconsistent window tags with double window cells (domain interface and periodic)
4470 // MBool tagWindow = false;
4471 // for ( MInt i = 0; i < noNeighborDomains(); i++ ) {
4472
4473 // if(nghrDomFlagBak[ nghbrId ][ i ]){
4474 // //nghrDomFlag[ cellId ][ i ] = true;
4475 // tagWindow = true;
4476 // break;
4477 // }
4478 // }
4479 // if(tagWindow){
4480 // for ( MInt i = 0; i < noNeighborDomains(); i++ ) {
4481 // nghrDomFlag[ cellId ][ i ] = true;
4482 // }
4483 // }
4484
4485
4486 if(m_maxPartitionLevelShift > 0) {
4487 MInt rootId = a_parentId(cellId) > -1 ? a_parentId(cellId) : cellId;
4488 if(a_hasProperty(rootId, Cell::IsPartLvlAncestor)) {
4489 MInt minNghbrDomId = findNeighborDomainId(a_globalId(rootId));
4490 MInt maxNghbrDomId =
4491 findNeighborDomainId(mMin(m_noCellsGlobal - 1, a_globalId(rootId) + (MLong)a_noOffsprings(rootId)));
4492 for(MInt k = 0; k < noNeighborDomains(); k++) {
4493 if(m_nghbrDomains[k] >= minNghbrDomId && m_nghbrDomains[k] <= maxNghbrDomId) {
4494 nghrDomFlag[cellId][k] = true;
4495 }
4496 }
4497 }
4498 }
4499 }
4500 }
4501
4502 // now extend one additional layer around cells which might be missing the layer
4503 if(!solverCellsAdded.empty()) {
4504 nghrDomFlagBak2.assign(nghrDomFlag.begin(), nghrDomFlag.end());
4505
4506 for(map<MInt, MInt>::iterator it = solverCellsAdded.begin(); it != solverCellsAdded.end(); it++) {
4507 const MInt cellId = it->first;
4508 const MInt solverId = it->second;
4509
4510 // get additional cells
4511 // NOTE: diagonal neighbors are necessary as well, however checkNoHaloLayers
4512 // only checks for direct neighbors!
4513 const MInt counter = getAdjacentGridCells(cellId, nghbrList, level, true);
4514
4515 MBool anyNeighbor = false;
4516 const MInt size = noNeighborDomains();
4517 MBoolScratchSpace addedCell(size, AT_, "addedCell");
4518 fill(addedCell.begin(), addedCell.end(), false);
4519 for(MInt n = 0; n < counter; n++) {
4520 const MInt nghbrId = nghbrList[n];
4521 if(nghbrId < 0) continue;
4522 MInt parentId = a_parentId(cellId);
4523 if(parentId > -1) {
4524 if(!treeb().solver(parentId, solverId)) continue;
4525 }
4526 // labels:GRID testcase hack
4527 if(m_zonal) {
4528 MBool checkParentChilds = false;
4529 for(MInt child = 0; child < m_maxNoChilds; child++) {
4530 MInt childId = a_childId(cellId, child);
4531 if(childId > -1) {
4532 if(!treeb().solver(childId, solverId)) {
4533 checkParentChilds = true;
4534 break;
4535 }
4536 }
4537 }
4538 if(checkParentChilds) continue;
4539 }
4540 if(treeb().solver(nghbrId, solverId)) anyNeighbor = true;
4541 // if( !treeb().solver( nghbrId, solverId ) &&
4542 // a_level(nghbrId) == a_level(cellId)) continue;
4543 for(MInt i = 0; i < noNeighborDomains(); i++) {
4544 if(nghrDomFlagBak2[nghbrId][i]) {
4545 if(!nghrDomFlag[cellId][i]) {
4546 addedCell[i] = true;
4547 }
4548 nghrDomFlag[cellId][i] = true;
4549 }
4550 }
4551 }
4552 // remove the cell from the list again if:
4553 // no neighbors for the cell and intended solver could be found!
4554 // Meaning that the solver has a lower max-level or different boundingBox!
4555 if(!anyNeighbor) {
4556 // for ( MInt n = 0; n < counter; n++ ) {
4557 // const MInt nghbrId = nghbrList[ n ];
4558 for(MInt i = 0; i < noNeighborDomains(); i++) {
4559 if(addedCell[i]) {
4560 nghrDomFlag[cellId][i] = false;
4561 }
4562 }
4563 //}
4564 }
4565 }
4566 solverCellsAdded.clear();
4567 nghrDomFlagBak2.clear();
4568 }
4569 }
4570
4571 // not openmp compatible
4572 MInt cnt = 0;
4573 for(MInt i = 0; i < noNeighborDomains(); i++) {
4574 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
4575 MInt cellId = m_windowCells[i][j];
4576 if(a_level(cellId) == level) {
4577 for(MInt child = 0; child < m_maxNoChilds; child++) {
4578 MInt childId = a_childId(cellId, child);
4579 if(childId > -1) {
4580 if(nghrDomFlag[childId][i]) {
4581 refineChildIds[m_maxNoChilds * cnt + child] = 1;
4582 } else if(testingFix_partitionLevelShift && a_level(cellId) < m_minLevel + m_maxPartitionLevelShift) {
4583 refineChildIds[m_maxNoChilds * cnt + child] = 1;
4584 }
4585 }
4586 }
4587 }
4588 cnt++;
4589 }
4590 }
4591}
4592
4593
4599// clang-format off
4600template <MInt nDim>
4601void CartesianGrid<nDim>::tagActiveWindows2_(vector<MLong>& refineChildIds, const MInt level) {
4602
4603 MIntScratchSpace nghbrList(27 * m_maxNoChilds, AT_, "nghbrList");
4604
4605 // We rely on the fact, that exchangeCellList is sorted by the tuple's first element, then by its 2nd element etc.
4606 std::set<std::tuple<MInt/*layer*/,MInt/*cellId*/, MInt/*nghbrIdx*/,MInt/*solverId*/>> exchangeCellList;
4607 // partLvlShift
4608 auto newNghbrDomainsIndex = m_nghbrDomainIndex;
4609 auto newNghbrDomains = m_nghbrDomains;
4610
4611#ifndef NDEBUG
4612 std::set<MInt> allWindows;
4613#endif
4614
4615 // The algorithm for m_maxPartitionLevelShift>0 requires a_hasProperty(cellId, Cell::IsPartLvlAncestor) &
4616 // a_noOffsprings(cellId) of the halo cells to be set. This should be the case at this place.
4617
4618 // Get halo candidates
4619 for(MInt i = 0; i < noNeighborDomains(); i++) {
4620 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
4621 const MInt cellId = m_haloCells[i][j];
4622 // If this is called during meshAdaptation and grid has partLvlShift, then we might have halo cells for which
4623 // a_level(cellId)>level
4624 ASSERT(a_level(cellId)<=level || m_maxPartitionLevelShift>0, "");
4625 if (a_level(cellId)>level) continue;
4626
4627 // Note: currently we add all halos as candidates. A more sophisticated choice would be to add only those
4628 // who are adajcent to cells, which are not halos of same neighbor (the neighbor can still be a halo)
4629
4630 //TODO_SS labels:GRID what about the case a_noChildren(cellId)<IPOW2(nDim)
4631 if (a_level(cellId)==level || (a_noChildren(cellId)==0 && a_level(cellId)<level)) {
4632 for (MInt solverId = 0; solverId < treeb().noSolvers(); solverId++) {
4633 // For the following to work we need to exchange the solverBits before tagActiveWindowsAtMinLevel is
4634 // called (see comments in tagActiveWindowsAtMinLevel)
4635 // if (treeb().solver(cellId, solverId) || treeb().noSolvers()==1)
4636 if (treeb().solver(cellId, solverId)) {
4637 exchangeCellList.insert({std::make_tuple(0, cellId, i, solverId)});
4638 }
4639 }
4640 }
4641
4642 // Before we do the following we need to exchange the property IsPartLvlAncesotr and a_noOffsping (see above)
4643 if (a_hasProperty(cellId, Cell::IsPartLvlAncestor)) {
4644 if (a_level(cellId)==level || (a_noChildren(cellId)==0 && a_level(cellId)<level)) {
4645
4646 // TODO_SS labels:GRID what about the case a_noChildren(cellId)<IPOW2(nDim)
4647 const MInt minNghbrDomId = findNeighborDomainId(a_globalId(cellId));
4648 const MInt maxNghbrDomId =
4649 findNeighborDomainId(mMin(m_noCellsGlobal - 1, a_globalId(cellId) + (MLong)a_noOffsprings(cellId)-1));
4650
4651 for (MInt nghbrDom = minNghbrDomId; nghbrDom <= maxNghbrDomId; ++nghbrDom) {
4652 MInt k = newNghbrDomainsIndex[nghbrDom];
4653 if (nghbrDom==domainId()) continue; //TODO_SS labels:GRID not sure if this is correct
4654 //if (k<0 && nghbrDom==domainId()) continue; // periodic???
4655 if (k<0) {
4656 // We have a new neighbor
4657 k = newNghbrDomains.size();
4658 newNghbrDomainsIndex[nghbrDom] = k;
4659 newNghbrDomains.push_back(nghbrDom);
4660 }
4661 //if (i==k) continue;
4662// if(m_nghbrDomains[k] >= minNghbrDomId && m_nghbrDomains[k] <= maxNghbrDomId) { }
4663 for (MInt solverId = 0; solverId < treeb().noSolvers(); solverId++) {
4664 // For the following to work we need to exchange the solverBits before tagActiveWindowsAtMinLevel is
4665 // called (see comments in tagActiveWindowsAtMinLevel)
4666 // if (treeb().solver(cellId, solverId) || treeb().noSolvers()==1)
4667 if (treeb().solver(cellId, solverId)) {
4668 exchangeCellList.insert({std::make_tuple(0, cellId, k, solverId)});
4669
4670 for(MInt child = 0; child < m_maxNoChilds; child++) {
4671 const MInt childId = a_childId(cellId, child);
4672 if(childId > -1) {
4673
4674 //TODO_SS labels:GRID exchange noOffspring of halo cells (check if it is already done, especially those halo
4675 // cells which are not yet in m_haloCells, but are created because of partLvlShift)
4676 const MInt minNghbrDomId2 = findNeighborDomainId(a_globalId(childId));
4677 const MInt maxNghbrDomId2 =
4678 findNeighborDomainId(mMin(m_noCellsGlobal - 1, a_globalId(childId) + (MLong)a_noOffsprings(childId)-1));
4679 MInt lay = (newNghbrDomains[k] >= minNghbrDomId2 && newNghbrDomains[k] <= maxNghbrDomId2) ? 0 : 1;
4680 // TODO_SS labels:GRID since halo data are not exchanged yet, safety approach
4681 if (a_isHalo(childId)) lay = 0;
4682
4683 exchangeCellList.insert({std::make_tuple(lay, childId, k, solverId)});
4684 if (!a_isHalo(childId)) {
4685 if (treeb().solver(childId, solverId)) {
4686 //TODO_SS labels:GRID,totest following assert fails in periodic case when nghbrDom==domainId() --> check
4687 //TERMM_IF_COND(lay==0 && !a_hasProperty(childId, Cell::IsPartLvlAncestor), to_string(minNghbrDomId2) + " " + to_string(maxNghbrDomId2));
4688 //TODO_SS labels:GRID can we ensure that the newly found domain will also find current domain?
4689 m_windowLayer_[setNeighborDomainIndex(nghbrDom, m_haloCells, m_windowCells, m_windowLayer_)][childId].set(solverId, lay);
4690 }
4691 }
4692 }
4693 }
4694 }
4695 }
4696 }
4697 }
4698 }
4699 }
4700 }
4701
4702// vector<std::set<MInt>> windCellSet(noNeighborDomains());
4703// for(MInt i = 0; i < noNeighborDomains(); i++) {
4704// windCellSet[i].insert(m_windowCells[i].begin(), m_windowCells[i].end());
4705// }
4706
4707 // Get window candidates on lower level
4708 for(MInt i = 0; i < noNeighborDomains(); i++) {
4709 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
4710 const MInt cellId = m_windowCells[i][j];
4711#ifndef NDEBUG
4712 allWindows.insert(cellId);
4713 TERMM_IF_COND(m_windowLayer_[i].find(cellId)==m_windowLayer_[i].end(), std::to_string(m_maxPartitionLevelShift));
4714 TERMM_IF_COND(m_windowLayer_[i].at(cellId).all(), "");
4715#endif
4716
4717 if (a_level(cellId)==level) {
4718 for (MInt solverId = 0; solverId < treeb().noSolvers(); solverId++) {
4719 const MInt windowLayer = m_windowLayer_[i].at(cellId).get(solverId);
4720 // TODO_SS labels:GRID what about the case that a_noChildren<IPOW2(nDim)
4721 if (!a_hasChildren(cellId, solverId) && windowLayer<m_noSolverHaloLayers[solverId]) {
4722 // labels:GRID,DLB With DLB the following check fails: Bug?!
4723// if (windowLayer<1 && !a_hasProperty(cellId, Cell::IsPartLvlAncestor))
4724// TERMM(1, "");
4725 exchangeCellList.insert({std::make_tuple(windowLayer, cellId, i, solverId)});
4726 }
4727 }
4728 }
4729
4730 // 1) window cell (a_level(cellId)==level) with IPOW2(nDim) number of window children
4731 // 2) window cell (a_level(cellId)==level) with <IPOW2(nDim) number of window children
4732 if (a_hasProperty(cellId, Cell::IsPartLvlAncestor) && a_level(cellId)==level) {
4733 ASSERT(a_noChildren(cellId)>0, "");
4734 ASSERT(!a_isHalo(cellId), "");
4735
4736 MInt minNghbrDomId = findNeighborDomainId(a_globalId(cellId));
4737 MInt maxNghbrDomId =
4738 findNeighborDomainId(mMin(m_noCellsGlobal - 1, a_globalId(cellId) + (MLong)a_noOffsprings(cellId)-1));
4739#ifndef NDEBUG
4740 // All neighbor domains, who have children above this current window cell, should already be contained in
4741 // m_nghbrDomains
4742 for (MInt nD = minNghbrDomId; nD<=maxNghbrDomId; ++nD) {
4743 if (nD==domainId()) continue;
4744 MBool found = false;
4745 for (MInt ii = 0; ii < noNeighborDomains(); ii++) {
4746 if (m_nghbrDomains[ii]== nD) {
4747 found = true;
4748 break;
4749 }
4750 }
4751 TERMM_IF_NOT_COND(found, to_string(a_isHalo(cellId)) + " " + to_string(a_noOffsprings(cellId)));
4752 }
4753#endif
4754 if(m_nghbrDomains[i] >= minNghbrDomId && m_nghbrDomains[i] <= maxNghbrDomId) {
4755
4756 //TODO_SS labels:GRID do we need to put cellId into exchangeCellList in case a_noChildren(cellId)<IPOW2(nDim)
4757 for (MInt solverId = 0; solverId < treeb().noSolvers(); solverId++) {
4758 if (treeb().solver(cellId, solverId)) {
4759 exchangeCellList.insert({std::make_tuple(0, cellId, i, solverId)});
4760 }
4761 }
4762
4763// if (a_noChildren(cellId)==IPOW2(nDim)) {
4764 for(MInt child = 0; child < m_maxNoChilds; child++) {
4765 const MInt childId = a_childId(cellId, child);
4766 // Note: Due to cutOff we might have less then IPOW[nDim] children
4767 if (childId<0) continue;
4768 //TODO_SS labels:GRID,totest check the following assert: I guess during meshAdaptation is might fail
4769 //TERMM_IF_NOT_COND(!a_isHalo(childId), "");
4770
4771 minNghbrDomId = findNeighborDomainId(a_globalId(childId));
4772 maxNghbrDomId =
4773 findNeighborDomainId(mMin(m_noCellsGlobal - 1, a_globalId(childId) + (MLong)a_noOffsprings(childId)-1));
4774 // for(MInt k = 0; k < noNeighborDomains(); k++) { }
4775 //if (domainId()==m_nghbrDomains[k]) continue; // check if this makes sense
4776 //if(m_nghbrDomains[k] >= minNghbrDomId && m_nghbrDomains[k] <= maxNghbrDomId) { }
4777 MInt lay = (m_nghbrDomains[i] >= minNghbrDomId && m_nghbrDomains[i] <= maxNghbrDomId) ? 0 : 1;
4778 // TODO_SS labels:GRID since halo data are not exchanged yet, safety approach
4779 if (a_isHalo(childId)) lay = 0;
4780
4781 for (MInt solverId = 0; solverId < treeb().noSolvers(); solverId++) {
4782 //TODO_SS labels:GRID,totest maybe we could directly check solverBits of children, but first check
4783 // if solverBits of halo children are already set
4784 if (treeb().solver(cellId, solverId)) {
4785 exchangeCellList.insert({std::make_tuple(lay, childId, i, solverId)});
4786 if (!a_isHalo(childId)) {
4787 if (treeb().solver(childId, solverId)) {
4788// TERMM_IF_COND(lay==0 && !a_hasProperty(childId, Cell::IsPartLvlAncestor), to_string(domainId()) + " " + to_string(m_nghbrDomains[i]) + " " + to_string(minNghbrDomId) + " " + to_string(maxNghbrDomId));
4789 m_windowLayer_[i][childId].set(solverId, lay);
4790 }
4791 }
4792 }
4793 }
4794 }
4795 }
4796 }
4797 }
4798 }
4799
4800
4801 //
4802 std::vector<std::unordered_map<MInt, M32X4bit<true>>> haloLayer_(newNghbrDomains.size());
4803 for(MInt layer = 0; layer < m_noHaloLayers; layer++) {
4804
4805 const auto it_begin = exchangeCellList.lower_bound(std::make_tuple(layer,std::numeric_limits<MInt>::min(),
4806 std::numeric_limits<MInt>::min(),std::numeric_limits<MInt>::min()));
4807 const auto it_end = exchangeCellList.upper_bound(std::make_tuple(layer,std::numeric_limits<MInt>::max(),
4808 std::numeric_limits<MInt>::max(),std::numeric_limits<MInt>::max()));
4809 const MInt noCandidates = std::distance(it_begin, it_end);
4810
4811 MInt cnt = 0;
4812 for (auto it = it_begin; it!=it_end;) {
4813 const MInt cellId = get<1>(*it);
4814 TERMM_IF_NOT_COND(cellId > -1 && cellId < m_tree.size(), "");
4815 const MBool isHaloCellId = a_isHalo(cellId);
4816
4817 // 1) If cellId is on level 'level+1' it must be a window cell, and we are only looking for neighbors on same level
4818 // 2) If cellId is window cell, but not on 'level+1' we are only interested in neighbors on 'level+1', since we
4819 // should already have tagged windows on 'level' (and those windows on 'level' without children are put into
4820 // exchangeCellList above)
4821 // 3) If cellId is halo cell, it must be on level<='level': we allow both searching on lower and upper level
4822#ifndef NDEBUG
4823 TERMM_IF_COND(a_level(cellId)==level+1 && allWindows.find(a_parentId(cellId))==allWindows.end()
4824 && !a_hasProperty(a_parentId(cellId), Cell::IsPartLvlAncestor), "");
4825
4826 // The following check fails when this function is called during meshAdaptation
4827 //TERMM_IF_COND(a_isHalo(cellId) && a_level(cellId)>level && !a_hasProperty(cellId, Cell::IsPartLvlAncestor)/*&& m_maxPartitionLevelShift==0*/, "");
4828#endif
4829
4830 const MInt counter = a_level(cellId)==level+1 ? getAdjacentGridCells5<false,false>(cellId, nghbrList.data())
4831 : (isHaloCellId ? getAdjacentGridCells5<true,true>(cellId, nghbrList.data())
4832 : getAdjacentGridCells5<true,false>(cellId, nghbrList.data(), level+1));
4833
4834 // It can happen that we reached noCandidates, but by coincidence the next cellId is the same, but on layer+1
4835 //while (cellId==it->first.second && cnt<noCandidates) {
4836 while (cnt<noCandidates && cellId==get<1>(*it)) {
4837 ASSERT(get<0>(*it)==layer, "Send a bug report to the C++ vendor!");
4838 const MInt nghbrDomIdx = get<2>(*it);
4839 const MInt solverId = get<3>(*it);
4840
4841 for(MInt n = 0; n < counter; n++) {
4842 const MInt nghbrId = nghbrList[n];
4843 ASSERT(nghbrId > -1 && nghbrId < m_tree.size(), to_string(nghbrId) + "|" + to_string(m_tree.size()));
4844 ASSERT(a_level(nghbrId)<=level+1, to_string(a_level(nghbrId)) + "|" + to_string(level+1));
4845
4846 // At this place the solver tags of halo cells should already be set, i.e. solverBits are already exchanged.
4847 if (!treeb().solver(nghbrId, solverId)) continue;
4848
4849 // nghbrId is either
4850 // 1) a halo cell
4851 // 2) itself a window cell with no parent or its parent is a window cell or a window cell on level+1,
4852 // which not yet in allWindows
4853 const MBool isHalo = a_isHalo(nghbrId);
4854#ifndef NDEBUG
4855 if (m_maxPartitionLevelShift==0) {
4856 TERMM_IF_COND(isHalo && (allWindows.find(a_parentId(nghbrId))!=allWindows.end()
4857 || allWindows.find(nghbrId)!=allWindows.end()), "");
4858 }
4859#endif
4860
4861 //TODO_SS labels:GRID,totest check the last condition, if testcases fail and if it speeds up
4862 if (!isHalo && a_level(nghbrId)<level+1) continue;
4863
4864 // If parent is window, it must be in isSolverWindowCell(nghbrDomIdx, a_parentId(nghbrId), solverId)
4865 if (a_level(nghbrId)==level+1 && !a_isHalo(a_parentId(nghbrId)) && !isSolverWindowCell(nghbrDomIdx, a_parentId(nghbrId), solverId))
4866 continue;
4867
4868 // Sanity check that cell is in m_windowCells, in case it's not a halo
4869#ifndef NDEBUG
4870 TERMM_IF_COND(!isHalo
4871 && allWindows.find(a_parentId(nghbrId))==allWindows.end()
4872 && allWindows.find(nghbrId)!=allWindows.end()
4873 && !a_hasProperty(a_parentId(nghbrId), Cell::IsPartLvlAncestor), "");
4874
4875 // Check in case of no partition level shift
4876 if (m_maxPartitionLevelShift==0) {
4877 TERMM_IF_COND(a_level(nghbrId)==level+1 && allWindows.find(a_parentId(nghbrId))==allWindows.end(), "");
4878 } else {
4879 /* Actually I would expect for a_level(nghbrId)==level+1 the following possible situations:
4880 * 1) allWindows.find(a_parentId(nghbrId))!=allWindows.end() && !a_hasProperty(a_parentId(nghbrId), Cell::IsPartLvlAncestor)
4881 * 2) allWindows.find(a_parentId(nghbrId))!=allWindows.end() && a_hasProperty(a_parentId(nghbrId), Cell::IsPartLvlAncestor)
4882 * 3) allWindows.find(a_parentId(nghbrId))==allWindows.end() && a_hasProperty(a_parentId(nghbrId), Cell::IsPartLvlAncestor) && a_isHalo(a_parentId(nghbrId))
4883 * But since we are tagging currently way too many cells, we can have:
4884 * allWindows.find(a_parentId(nghbrId))==allWindows.end() && !a_hasProperty(a_parentId(nghbrId), Cell::IsPartLvlAncestor)
4885 */
4886 }
4887#endif
4888
4889 /* Currently nghbrId could be also a halo cell, e.g. if domain c has halo cells on domain a, we have to go
4890 * over the halos of domain b
4891 *
4892 * ------- -------
4893 * | |
4894 * a | b | c
4895 * | |
4896 */
4897 MInt nghbrDomIdx2 = nghbrDomIdx;
4898 if (m_maxPartitionLevelShift>0) {
4899 if (!isHalo) {
4900 const MInt ndom = newNghbrDomains[nghbrDomIdx];
4901 //TODO_SS labels:GRID can we ensure that the newly found domain will also find current domain?
4902 nghbrDomIdx2 = setNeighborDomainIndex(ndom, m_haloCells, m_windowCells, m_windowLayer_);
4903 }
4904 }
4905 const MInt windowLayer = isHalo ? haloLayer_[nghbrDomIdx][nghbrId].get(solverId)
4906 : m_windowLayer_[nghbrDomIdx2][nghbrId].get(solverId);
4907
4908 if (windowLayer>layer+1) {
4909
4910 isHalo ? haloLayer_[nghbrDomIdx][nghbrId].set(solverId, layer+1)
4911 : m_windowLayer_[nghbrDomIdx2].at(nghbrId).set(solverId, layer+1);
4912 ASSERT((isHalo && haloLayer_[nghbrDomIdx][nghbrId].get(solverId)==layer+1)
4913 || (!isHalo && m_windowLayer_[nghbrDomIdx2].at(nghbrId).get(solverId)==layer+1), "");
4914
4915 if (layer+1<m_noSolverHaloLayers[solverId])
4916 exchangeCellList.insert({std::make_tuple(layer+1, nghbrId, nghbrDomIdx, solverId)});
4917 }
4918 //}
4919 }
4920 ++cnt;
4921 ++it;
4922 }
4923 if (cnt==noCandidates) break;
4924 }
4925 } //loop over layers
4926
4927
4928 MInt cnt = 0;
4929 for(MInt i = 0; i < noNeighborDomains(); i++) {
4930 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
4931 MInt cellId = m_windowCells[i][j];
4932 if(a_level(cellId) == level) {
4933 for(MInt child = 0; child < m_maxNoChilds; child++) {
4934 const MInt childId = a_childId(cellId, child);
4935 if(childId > -1) {
4936 if (m_windowLayer_[i].find(childId)!=m_windowLayer_[i].end()) {
4937 ASSERT(!m_windowLayer_[i].at(childId).all(), "");
4938 refineChildIds[m_maxNoChilds * cnt + child] = 1;
4939 }
4940 }
4941 }
4942 }
4943 cnt++;
4944 }
4945 }
4946
4947 // DEBUG output
4948}
4949
4950
4951
4957template <MInt nDim>
4958void CartesianGrid<nDim>::tagActiveWindowsOnLeafLvl3(const MInt maxLevel, const MBool duringMeshAdaptation,
4959 const std::vector<std::function<void(const MInt)>>& removeCellSolver) {
4960
4961 MIntScratchSpace nghbrList(27 * m_maxNoChilds, AT_, "nghbrList");
4962
4963 // We don't need to exchange a_hasProperty(cellId, Cell::IsPartLvlAncestor) and a_noOffsprings(cellId)
4964 // here, because the halo cells on level=maxLevel are never partLvlAnc and have always
4965 // a_noOffsprings(cellId)==1.
4966
4967#ifndef NDEBUG
4968 set<MInt> halos;
4969 // Get halo candidates
4970 for(MInt i = 0; i < noNeighborDomains(); i++) {
4971 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
4972 const MInt cellId = m_haloCells[i][j];
4973 halos.insert(cellId);
4974 }
4975 }
4976#endif
4977
4978 std::unordered_map<MInt, M32X4bit<true>> haloLayers;
4979
4980 // Note: when this is called during meshAdaptation, then changes will only occur above m_maxUniformRefinementLevel
4981 for (MInt level = m_minLevel; level <= maxLevel; level++) {
4982 std::set<std::tuple<MInt/*halolayer*/,MInt/*cellId*/,MInt/*solverId*/>> exchangeCellList;
4983
4984 // Find first layer window cells
4985 for (MInt i = 0; i < noNeighborDomains(); i++) {
4986 for (auto& item : m_windowLayer_[i]) {
4987 const MInt cellId = item.first;
4988
4989 for (MInt solverId = 0; solverId < treeb().noSolvers(); solverId++) {
4990 // TODO_SS labels:GRID what about a_hasChildren(cellId, solverId)<IPOW2(nDim)
4991 if (a_level(cellId)==level || (a_level(cellId)==level-1 && !a_hasChildren(cellId, solverId))) {
4992 if (item.second.get(solverId)<=1) {
4993 ASSERT(m_tree.solver(cellId, solverId), "Cell was identifed to be a valid windowCell for"
4994 " solver=" + to_string(solverId) + ", but does not belong to that solver");
4995 exchangeCellList.insert({std::make_tuple(0, cellId, solverId)});
4996 }
4997 }
4998 }
4999 }
5000
5001 // Find halo cells on level-1 with no children
5002 for (const auto& item : haloLayers) {
5003 const MInt cellId = item.first;
5004 if (a_level(cellId)==level-1) {
5005 for (MInt solverId = 0; solverId < treeb().noSolvers(); solverId++) {
5006 const MInt haloLayer = item.second.get(solverId);
5007 if (!a_hasChildren(cellId, solverId) && haloLayer<m_noSolverHaloLayers[solverId]) {
5008 exchangeCellList.insert({std::make_tuple(haloLayer, cellId, solverId)});
5009 // TODO_SS labels:GRID what about a_hasChildren(cellId, solverId)<IPOW2(nDim)
5010 } /*else if (a_noChildren(cellId)<IPOW2(nDim) && haloLayer<=m_noSolverHaloLayers[solverId]) {
5011 for(MInt child = 0; child < m_maxNoChilds; child++) {
5012 MInt childId = a_childId(cellId, child);
5013 if(childId < 0) continue;
5014 if (treeb().solver(childId, solverId)) {
5015 haloLayers[childId].set(solverId, haloLayer);
5016 if (haloLayer<m_noSolverHaloLayers[solverId]) {
5017 exchangeCellList.insert({std::make_pair(haloLayer, childId), solverId});
5018 }
5019 }
5020 }
5021 }*/
5022 }
5023 }
5024 }
5025
5026 // In the following halo cells with children on current domain are also identified as 1st layer 'window' cells
5027 if (m_maxPartitionLevelShift>0) {
5028 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
5029 const MInt cellId = m_haloCells[i][j];
5030 if (a_hasProperty(cellId, Cell::IsPartLvlAncestor)) {
5031 const MInt minNghbrDomId = findNeighborDomainId(a_globalId(cellId));
5032 const MInt maxNghbrDomId =
5033 findNeighborDomainId(mMin(m_noCellsGlobal - 1, a_globalId(cellId) + (MLong)a_noOffsprings(cellId)-1));
5034 if (domainId()<minNghbrDomId || domainId()>maxNghbrDomId) continue;
5035 for (MInt solverId = 0; solverId < treeb().noSolvers(); solverId++) {
5036 if (m_tree.solver(cellId, solverId) && a_hasChildren(cellId, solverId)) {
5037 // Guess: The folowing assert fails e.g. if partitionCellMaxNoOffspring==1 and one solver is at least one
5038 // level coarser then the other one.
5039// TERMM_IF_NOT_COND(a_hasChildren(cellId, solverId), "");
5040 //TODO_SS labels:GRID what about noChildren<IPOW2(nDim)
5041 if (a_level(cellId)==level || (a_level(cellId)==level-1 && !a_hasChildren(cellId, solverId))) {
5042 exchangeCellList.insert({std::make_tuple(0, cellId, solverId)});
5043 // It may happen that one domain has no window cell at this level at all so we add this cell to
5044 // haloLayers with layer==0
5045 haloLayers[cellId].set(solverId,0);
5046 }
5047 }
5048 }
5049 }
5050 }
5051 }
5052 }
5053
5054
5055 // In the following no distinction is made regarding the neighbor. The current domain is extended by the
5056 // required number of halo layers, and halo cells required by current domain are inserted in haloLayers.
5057 for(MInt layer = 0; layer < m_noHaloLayers; layer++) {
5058
5059 const auto it_begin = exchangeCellList.lower_bound(std::make_tuple(layer,std::numeric_limits<MInt>::min(),
5060 std::numeric_limits<MInt>::min()));
5061 const auto it_end = exchangeCellList.upper_bound(std::make_tuple(layer,std::numeric_limits<MInt>::max(),
5062 std::numeric_limits<MInt>::min()));
5063 const MInt noCandidates = std::distance(it_begin, it_end);
5064
5065 MInt cnt = 0;
5066 for (auto it = it_begin; it!=it_end;) {
5067 const MInt cellId = get<1>(*it);
5068 ASSERT(cellId > -1 && cellId < m_tree.size(), "");
5069
5070 const MInt counter = a_level(cellId)==level ? getAdjacentGridCells5<false,false>(cellId, nghbrList.data())
5071 : getAdjacentGridCells5<true,false>(cellId, nghbrList.data(), level);
5072 // TEST1
5073// const MInt counter = a_level(cellId)==level ? getAdjacentGridCells5<false,true>(cellId, nghbrList.data())
5074// : getAdjacentGridCells5<true,false>(cellId, nghbrList.data());
5075
5076
5077 while (cnt<noCandidates && cellId==get<1>(*it)) {
5078 ASSERT(get<0>(*it)==layer, "Send a bug report to the C++ vendor!");
5079 const MInt solverId = get<2>(*it);
5080
5081 //TODO_SS labels:GRID the case hat one of the neighbors has a_noChildren<IPOW2(nDim)
5082 for(MInt n = 0; n < counter; n++) {
5083 const MInt nghbrId = nghbrList[n];
5084 ASSERT(nghbrId > -1 && nghbrId < m_tree.size(), "");
5085
5086 //TEST1
5087/* TERMM_IF_NOT_COND(a_level(nghbrId)==level || a_level(nghbrId)==level-1, "");
5088 if (!a_isHalo(nghbrId) || !treeb().solver(nghbrId, solverId))
5089 continue;
5090 if (a_level(nghbrId)==level-1 && !a_hasChildren(nghbrId,solverId)) continue;
5091 if (a_level(nghbrId)==level-1) {
5092 TERMM_IF_NOT_COND(a_noChildren(nghbrId)<IPOW2(nDim) || a_noChildren(nghbrId)==0, "");
5093 for(MInt child = 0; child < m_maxNoChilds; child++) {
5094 MInt childId = a_childId(nghbrId, child);
5095 if(childId < 0) continue;
5096
5097 TERMM_IF_NOT_COND(treeb().solver(childId,solverId) && a_isHalo(childId), "");
5098 const MInt haloLayer = haloLayers[childId].get(solverId);
5099
5100 if (haloLayer>layer+1) {
5101
5102 haloLayers[childId].set(solverId, layer+1);
5103
5104 if (layer+1<m_noSolverHaloLayers[solverId])
5105 exchangeCellList.insert({std::make_pair(layer+1, childId), solverId});
5106 }
5107 }
5108 continue;
5109 }*/
5110
5111#ifndef NDEBUG
5112 // Sanity checks
5113 TERMM_IF_NOT_COND(a_level(nghbrId)==level, "");
5114 // It might happen that a cell is disabled, but the isHalo flag is still set, that' why the a_isHalo test is not reliable
5115 TERMM_IF_NOT_COND(((halos.find(nghbrId)!=halos.end())==(findNeighborDomainId(a_globalId(nghbrId))!=domainId()))
5116 || a_hasProperty(nghbrId, Cell::IsPeriodic) || !treeb().solver(nghbrId, solverId), "");
5117#endif
5118
5119 // Skip if cell is not halo
5120 if (!a_isHalo(nghbrId) || !treeb().solver(nghbrId, solverId)/* || a_hasProperty(nghbrId, Cell::IsPartLvlAncestor)*/)
5121 continue;
5122
5123 const MInt haloLayer = haloLayers[nghbrId].get(solverId);
5124
5125 if (haloLayer>layer+1) {
5126
5127 haloLayers[nghbrId].set(solverId, layer+1);
5128 ASSERT(haloLayers[nghbrId].get(solverId)==layer+1, "");
5129
5130 if (layer+1<m_noSolverHaloLayers[solverId])
5131 exchangeCellList.insert({std::make_tuple(layer+1, nghbrId, solverId)});
5132 }
5133 }
5134 ++cnt;
5135 ++it;
5136 }
5137 if (cnt==noCandidates)
5138 break;
5139 }
5140 } //loop over layers
5141 }
5142
5143
5144 // Traverse through the tree to mark all required halos on all levels
5145 std::unordered_map<MInt,M8X1bit<false>> validHalos;
5146 for (auto it = haloLayers.cbegin(); it!=haloLayers.cend(); ++it) {
5147 const MInt cellId = it->first;
5148 ASSERT(validHalos.find(cellId)==validHalos.end(), "Duplicate halo cells!");
5149 auto& flag = validHalos[cellId];
5150 for (MInt solverId = 0; solverId < treeb().noSolvers(); solverId++) {
5151 if (it->second.get(solverId)<=m_noSolverHaloLayers[solverId]) {
5152 ASSERT(m_tree.solver(cellId,solverId), "");
5153 flag.set(solverId,1);
5154 }
5155 }
5156 }
5157
5158//#ifndef NDEBUG
5159 // Sanity check that all parents of valid halo cells are also marked as valid
5160 for (const auto& item : validHalos) {
5161 MInt parentId = item.first;
5162 while (a_parentId(parentId)>-1) {
5163 parentId = a_parentId(parentId);
5164 const auto it = validHalos.find(parentId);
5165
5166 // Case 1: noPartLvlShift, then parent must be halo, but it can reside on current domain in periodic case
5167 // Case 2: partLvlShift, then parent can be window
5168 TERMM_IF_NOT_COND(m_maxPartitionLevelShift>0 || (a_isHalo(parentId) && it!=validHalos.end()), "");
5169 TERMM_IF_NOT_COND(m_maxPartitionLevelShift>0 || a_hasProperty(parentId, Cell::IsPeriodic) || findNeighborDomainId(a_globalId(parentId))!=domainId(), "");
5170 TERMM_IF_NOT_COND(it!=validHalos.end() || (a_hasProperty(parentId, Cell::IsPartLvlAncestor) && !a_isHalo(parentId)), "");
5171
5172 if (it==validHalos.end()) break;
5173
5174 for (MInt solverId = 0; solverId < treeb().noSolvers(); solverId++) {
5175 TERMM_IF_COND(item.second.get(solverId) && !it->second.get(solverId), " solverId=" + to_string(solverId) +
5176 ": Parent of valid halo cell is no halo!");
5177 }
5178 }
5179 }
5180//#endif
5181
5182 // ---Disable all halos not required & communicate with respective windows --- //
5183
5184 // Receive information from halo cells, whether the respective window cell is required
5185 ScratchSpace<MPI_Request> recvRequests(max(1, noNeighborDomains()), AT_, "recvRequests");
5186 fill(recvRequests.begin(), recvRequests.end(), MPI_REQUEST_NULL);
5187 MInt receiveCount = 0;
5188 for (const auto& vecWindow : m_windowCells) receiveCount+=vecWindow.size();
5189 ScratchSpace<M8X1bit<>::type> windowBuffer(max(1, receiveCount), AT_, "windowBuffer");
5190 for (MInt i = 0, offset = 0; i < noNeighborDomains(); i++) {
5191 const MInt noWindowCells = m_windowCells[i].size();
5192 if (noWindowCells<1) continue;
5193
5194 MPI_Irecv(&windowBuffer[offset], noWindowCells, type_traits<M8X1bit<>::type>::mpiType(), m_nghbrDomains[i],
5195 m_nghbrDomains[i], mpiComm(), &recvRequests[i], AT_, "windowBuffer[offset]");
5196
5197 offset += noWindowCells;
5198 }
5199
5200 // The meshAdaptation forces us to keep children of pla's
5201// const MBool hasPartLvlShift = (m_maxPartitionLevelShift > 0);
5202 const MInt tmpScratchSize = m_tree.size();//(hasPartLvlShift) ? m_tree.size() : 1;
5203 ScratchSpace<MBool> isHaloPartLvlAncestor(tmpScratchSize, AT_, "isHaloPartLvlAncestor");
5204 isHaloPartLvlAncestor.fill(false);
5205
5206 // Determine relevant halo partition level ancestor window/halo cells that need to be preserved
5207 if(m_maxPartitionLevelShift > 0) {
5208 for(auto& i : m_partitionLevelAncestorIds) {
5209 ASSERT(a_hasProperty(i, Cell::IsPartLvlAncestor),
5210 "cell is not a partition level ancestor: " + std::to_string(i));
5211 // Mark halo partition level ancestors (which are part of the missing subtree of the grid)
5212 if(a_hasProperty(i, Cell::IsHalo)) {
5213 isHaloPartLvlAncestor[i] = true;
5214 }
5215 // Mark all halo childs of partition level ancestors (required for exchange of e.g. global
5216 // ids!)
5217 for(MInt child = 0; child < m_maxNoChilds; child++) {
5218 const MInt childId = a_childId(i, child);
5219 if(childId > -1 && a_isHalo(childId)) {
5220 isHaloPartLvlAncestor[childId] = true;
5221 }
5222 }
5223 }
5224 }
5225 // Should we keep all isHaloPartLvlAncestor cells, because they are kept in meshAdaptation?!
5226
5227 struct comp {
5228 MBool operator()(const std::pair<MInt, MInt>& m1, const std::pair<MInt, MInt>& m2) const {
5229 if(m1.first < m2.first) {
5230 return true;
5231 } else if (m1.first==m2.first) {
5232 if(m1.second < m2.second) {
5233 return true;
5234 } else {
5235 return false;
5236 }
5237 } else {
5238 return false;
5239 }
5240 }
5241 };
5242
5243 const MInt threesholdLvl = !duringMeshAdaptation ? 0/*m_minLevel*/ : m_maxUniformRefinementLevel;
5244 ScratchSpace<MPI_Request> sendRequests(max(1, noNeighborDomains()), AT_, "sendRequests");
5245 fill(sendRequests.begin(), sendRequests.end(), MPI_REQUEST_NULL);
5246 MInt sendCount = 0;
5247 for (const auto& vecHalo : m_haloCells) sendCount+=vecHalo.size();
5248 ScratchSpace<M8X1bit<>::type> tmp_data(sendCount, AT_, "tmp_data");
5249 std::map<std::pair<MInt/*level*/,MInt/*cellId*/>,M8X1bit<false>,comp> toDelete;
5250 for (MInt i = 0, idx = 0; i < noNeighborDomains(); i++) {
5251 if (m_maxPartitionLevelShift==0) toDelete.clear();
5252 const MInt offset = idx;
5253 const MInt noHaloCells = m_haloCells[i].size();
5254 if (noHaloCells<1) continue;
5255 std::vector<MInt> haloCells;
5256 haloCells.reserve(noHaloCells);
5257
5258 // Temporary fix: In case two halos are mapped to same window cell, keep them (occurs in periodic case)
5259 ScratchSpace<MBool> isDuplicateHalo(m_noPeriodicCartesianDirs>0 ? m_tree.size() : 1, AT_, "isDuplicateHalo");
5260 isDuplicateHalo.fill(false);
5261 if (m_noPeriodicCartesianDirs>0) {
5262 std::map<MLong,MInt> haloGlobalIds;
5263 for (const auto cellId : m_haloCells[i]) {
5264 const MLong globalId = a_globalId(cellId);
5265 if (haloGlobalIds.find(globalId)!=haloGlobalIds.end()) {
5266 isDuplicateHalo[haloGlobalIds[globalId]] = true;
5267 isDuplicateHalo[cellId] = true;
5268 } else
5269 haloGlobalIds.insert({std::make_pair(globalId, cellId)});
5270 }
5271 }
5272
5273 for (const auto cellId : m_haloCells[i]) {
5274 const MBool isValidHalo = validHalos.find(cellId)!=validHalos.end();
5275
5276#ifndef NDEBUG
5277 // Check that partLvlAnc halo cell, with children on current domain is identified as validHalo
5278 const MInt minNghbrDomId1 = findNeighborDomainId(a_globalId(cellId));
5279 const MInt maxNghbrDomId1 =
5280 findNeighborDomainId(mMin(m_noCellsGlobal - 1, a_globalId(cellId) + (MLong)a_noOffsprings(cellId)-1));
5281 if (domainId()>=minNghbrDomId1 && domainId()<=maxNghbrDomId1 && !a_hasProperty(cellId, Cell::IsPeriodic))
5282 TERMM_IF_NOT_COND(!a_hasProperty(cellId, Cell::IsPartLvlAncestor) || isValidHalo, "Cell is partLvlAncestor, but not found as valid halo!");
5283#endif
5284
5285 // TODO_SS labels:GRID,toenhance do it more efficiently
5286 // Don't delete anything on minLevel
5287 const M8X1bit<> solver = (a_level(cellId)<=threesholdLvl
5288 || isHaloPartLvlAncestor[cellId]
5289 || (m_noPeriodicCartesianDirs>0 && isDuplicateHalo[cellId]))
5290 ? M8X1bit<false>{M8X1bit<true>{}.data()} : (isValidHalo ? validHalos[cellId] : M8X1bit<false>{});
5291
5292 for (MInt solverId = 0; solverId < treeb().noSolvers(); solverId++) {
5293 if (m_tree.solver(cellId,solverId) && !solver.get(solverId)) {
5294 auto key = std::make_pair(-a_level(cellId),cellId);
5295 toDelete[key].set(solverId,true);
5296 }
5297 if (a_level(cellId)>threesholdLvl && !isHaloPartLvlAncestor[cellId] && m_tree.solver(cellId,solverId))
5298 m_tree.solver(cellId,solverId) = solver.get(solverId);
5299 }
5300 tmp_data[idx++] = solver.data();
5301 if (m_tree.solverBits(cellId).any() || isHaloPartLvlAncestor[cellId]) {
5302 haloCells.push_back(cellId);
5303 } else {
5304// a_hasProperty(cellId, Cell::IsHalo) = false;
5305// a_isToDelete(cellId)=true;
5306// m_freeIndices.insert(cellId);
5307// removeCell<false>(cellId);
5308// ASSERT(toDelete.find(std::make_pair(-a_level(cellId),cellId))!=toDelete.end(), "");
5309 //TODO_SS labels:GRID,toenhance Check if it might make sense, to set M8X1bit<false>(M8X1bit<true>.data()), to force
5310 // the solvers to delete the cells, if they exist in the solvers.
5311 // We need the following because, we might have partLvlAnc cell belonging to a solver,
5312 // but one of its children does not. So if we just propagate the informations from parent cell
5313 // we might have created halo cells, which are not used by any solver. We need to delete them.
5314 toDelete.insert({std::make_pair(-a_level(cellId),cellId), M8X1bit<false>()});
5315 }
5316 }
5317 m_haloCells[i] = haloCells;
5318 if (m_maxPartitionLevelShift==0) {
5319 // In case of partLvlShift we need to first loop over all neigbhors, because a cell marked for deletion
5320 // might still have children, which belong to a different neighbor and are therefore not deleted yet
5321 for (const auto& item : toDelete) {
5322 const MInt cellId = item.first.second;
5323 for (MInt solverId = 0; solverId < treeb().noSolvers(); solverId++) {
5324 if (!removeCellSolver.empty() && item.second.get(solverId)) {
5325 removeCellSolver[solverId](cellId);
5326 }
5327 }
5328 if (m_tree.solverBits(cellId).none()) {
5329 ASSERT(!isHaloPartLvlAncestor[cellId], "");
5330 removeCell<false>(cellId);
5331
5332 }
5333 }
5334 }
5335
5336 ASSERT(idx-offset==noHaloCells, "");
5337
5338 MPI_Isend(&tmp_data[offset], noHaloCells, type_traits<M8X1bit<>::type>::mpiType(), m_nghbrDomains[i], domainId(),
5339 mpiComm(), &sendRequests[i], AT_, "tmp_data");
5340 }
5341 if (m_maxPartitionLevelShift>0) {
5342 for (const auto& item : toDelete) {
5343 const MInt cellId = item.first.second;
5344 for (MInt solverId = 0; solverId < treeb().noSolvers(); solverId++) {
5345 if (!removeCellSolver.empty() && item.second.get(solverId)) {
5346 removeCellSolver[solverId](cellId);
5347 }
5348 }
5349 if (m_tree.solverBits(cellId).none()) {
5350 removeCell<false>(cellId);
5351 }
5352 }
5353 }
5354
5355 // Finish MPI communication
5356 MPI_Waitall(noNeighborDomains(), &recvRequests[0], MPI_STATUSES_IGNORE, AT_);
5357 MPI_Waitall(noNeighborDomains(), &sendRequests[0], MPI_STATUSES_IGNORE, AT_);
5358
5359 for (MInt i = 0, idx = 0; i < noNeighborDomains(); i++) {
5360 // In periodic cases some window cells may appear twice; we have ensured above that those cells are always kept
5361 std::vector<MInt> windowCells;
5362 windowCells.reserve(m_windowCells[i].size());
5363 for (const auto cellId : m_windowCells[i]) {
5364 ASSERT(m_windowLayer_[i].find(cellId)!=m_windowLayer_[i].end(), "");
5365 M8X1bit<> solver(windowBuffer[idx++]);
5366 for (MInt solverId = 0; solverId < treeb().noSolvers(); solverId++) {
5367 if (!solver.get(solverId) && isSolverWindowCell(i,cellId,solverId))
5368 m_windowLayer_[i].at(cellId).set(solverId, m_noHaloLayers+1);
5369 }
5370 MBool validWindow = false;
5371 for (MInt solverId = 0; solverId < treeb().noSolvers(); solverId++) {
5372 if (isSolverWindowCell(i,cellId,solverId)) {
5373 windowCells.push_back(cellId);
5374 validWindow=true;
5375 break;
5376 }
5377 }
5378 ASSERT(solver.any()==validWindow, "");
5379 // If a window cell was already checked and not deleted, we are not allowed to delete it now
5380 if (!validWindow) {
5381 //TODO_SS labels:GRID periodic cases
5382 const MBool ok = m_windowLayer_[i].erase(cellId);
5383 ASSERT(ok, "");
5384 MBool isWindow = false;
5385 for (const auto& w : m_windowLayer_) {
5386 if (w.find(cellId)!=w.end()) {
5387 isWindow = true;
5388 break;
5389 }
5390 }
5391 if (!isWindow)
5392 a_hasProperty(cellId, Cell::IsWindow) = false;
5393 }
5394 }
5395 m_windowCells[i] = windowCells;
5396 }
5397
5398 if (!duringMeshAdaptation)
5399 compactCells();
5400
5401 // DEBUG output
5402}
5403
5404
5410template <MInt nDim>
5412 TRACE();
5413
5414 MIntScratchSpace nghbrList(27 * m_maxNoChilds, AT_, "nghbrList");
5415
5416 // We rely on the fact, that exchangeCellList is sorted by the tuple's first element, then by its 2nd element etc.
5417 std::set<std::tuple<MInt/*layer*/,MInt/*cellId*/, MInt/*nghbrIdx*/,MInt/*solverId*/>> exchangeCellList;
5418 std::vector<std::unordered_map<MInt, M32X4bit<true>>> haloLayer_(noNeighborDomains());
5419
5420 ASSERT(m_windowLayer_.empty(), "");
5421 m_windowLayer_.resize(noNeighborDomains());
5422
5423 if(m_maxPartitionLevelShift > 0) {
5424 // exchange some window cell data
5425 ScratchSpace<MInt> bprops(m_tree.size(), 2, AT_, "bprops");
5426 bprops.fill(-1);
5427 // Note: changed loop to iterate over whole tree, internal and halo cells not sorted!
5428 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
5429 if(a_isHalo(cellId) || a_isToDelete(cellId)) {
5430 continue;
5431 }
5432 bprops(cellId, 0) = (MInt)a_hasProperty(cellId, Cell::IsPartLvlAncestor);
5433 bprops(cellId, 1) = a_noOffsprings(cellId);
5434
5435 ASSERT(bprops(cellId, 0) > -1 && bprops(cellId, 1) > 0,
5436 std::to_string(cellId) + " " + std::to_string(bprops(cellId, 0)) + " "
5437 + std::to_string(bprops(cellId, 1)));
5438 }
5439 maia::mpi::exchangeData(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), bprops.getPointer(), 2);
5440
5441 // Note: loop only over the current haloCells since only for these data is exchanged, this does
5442 // not work properly if in case of a partition level shift some halo cell is not part of
5443 // m_haloCells yet such that its property IsPartLvlAncestor is reset!
5444 for(MInt i = 0; i < noNeighborDomains(); i++) {
5445 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
5446 const MInt cellId = m_haloCells[i][j];
5447 ASSERT(!a_isToDelete(cellId), "");
5448 ASSERT(bprops(cellId, 0) > -1 && bprops(cellId, 1) > 0,
5449 std::to_string(cellId) + " " + std::to_string(bprops(cellId, 0)) + " "
5450 + std::to_string(bprops(cellId, 1)));
5451 a_hasProperty(cellId, Cell::IsPartLvlAncestor) = (MBool)bprops(cellId, 0);
5452 a_noOffsprings(cellId) = bprops(cellId, 1);
5453 }
5454 }
5455 }
5456
5457 // Get halo candidates
5458 for(MInt i = 0; i < noNeighborDomains(); i++) {
5459 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
5460 const MInt cellId = m_haloCells[i][j];
5461 ASSERT(a_level(cellId)==level, "We expect all halo cells at this point to be on minLevel");
5462
5463 // Note: currently we add all halos as candidates. A more sophisticated choice would be to add only those
5464 // who are adajcent to cells, which are not halos of same neighbor (the neighbor can still be a halo)
5465 for (MInt solverId = 0; solverId < treeb().noSolvers(); solverId++) {
5466 // For the following to work we need to first exchange the solverBits; Check if it speeds up the whole stuff.
5467 // if (treeb().solver(cellId, solverId) || treeb().noSolvers()==1)
5468 exchangeCellList.insert({std::make_tuple(0, cellId, i, solverId)});
5469 }
5470
5471 if(m_maxPartitionLevelShift > 0) {
5472 if(a_hasProperty(cellId, Cell::IsPartLvlAncestor)) {
5473 const MInt minNghbrDomId = findNeighborDomainId(a_globalId(cellId));
5474 const MInt maxNghbrDomId =
5475 findNeighborDomainId(mMin(m_noCellsGlobal - 1, a_globalId(cellId) + (MLong)a_noOffsprings(cellId)-1));
5476 for(MInt k = 0; k < noNeighborDomains(); k++) {
5477 if(m_nghbrDomains[k] >= minNghbrDomId && m_nghbrDomains[k] <= maxNghbrDomId) {
5478 // Note: currently we add all halos as candidates. A more sophisticated choice would be to add only those
5479 // who are adajcent to cells, which are not halos of same neighbor (the neighbor can still be a halo)
5480 for (MInt solverId = 0; solverId < treeb().noSolvers(); solverId++) {
5481 // For the following to work we need to first exchange the solverBits; Check if it speeds up the whole stuff.
5482 // if (treeb().solver(cellId, solverId) || treeb().noSolvers()==1)
5483 exchangeCellList.insert({std::make_tuple(0, cellId, k, solverId)});
5484 }
5485 }
5486 }
5487 }
5488 }
5489
5490 }
5491 }
5492
5493 vector<std::set<MInt>> windCellSet(noNeighborDomains());
5494 for(MInt i = 0; i < noNeighborDomains(); i++) {
5495 windCellSet[i].insert(m_windowCells[i].begin(), m_windowCells[i].end());
5496 }
5497 //
5498 for(MInt i = 0; i < noNeighborDomains(); i++) {
5499 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
5500 const MInt cellId = m_windowCells[i][j];
5501
5502 if (a_hasProperty(cellId, Cell::IsPartLvlAncestor)) {
5503 const MInt minNghbrDomId = findNeighborDomainId(a_globalId(cellId));
5504 const MInt maxNghbrDomId =
5505 findNeighborDomainId(mMin(m_noCellsGlobal - 1, a_globalId(cellId) + (MLong)a_noOffsprings(cellId)-1));
5506#ifndef NDEBUG
5507 for (MInt nD = minNghbrDomId; nD<=maxNghbrDomId; ++nD) {
5508 if (nD==domainId()) continue;
5509 MBool found = false;
5510 for (MInt ii = 0; ii < noNeighborDomains(); ii++) {
5511 if (m_nghbrDomains[ii]== nD) {
5512 found = true;
5513 break;
5514 }
5515 }
5516 TERMM_IF_NOT_COND(found, to_string(a_isHalo(cellId)) + " " + to_string(a_noOffsprings(cellId)));
5517 }
5518#endif
5519 for (MInt solverId = 0; solverId < treeb().noSolvers(); solverId++) {
5520 if (treeb().solver(cellId, solverId)) {
5521// for(MInt k = 0; k < noNeighborDomains(); k++) {
5522 //if (domainId()==m_nghbrDomains[k]) continue; // check if this makes sense
5523 if(m_nghbrDomains[i/*k*/] >= minNghbrDomId && m_nghbrDomains[i/*k*/] <= maxNghbrDomId) {
5524 TERMM_IF_NOT_COND(windCellSet[i].find(cellId)!=windCellSet[i].end(), "");
5525 m_windowLayer_[i/*k*/][cellId].set(solverId, 0);
5526 exchangeCellList.insert({std::make_tuple(0, cellId, i/*k*/, solverId)});
5527 }
5528// }
5529 }
5530 }
5531 }
5532 }
5533 }
5534
5535
5536
5537 //
5538 for(MInt layer = 0; layer < m_noHaloLayers; layer++) {
5539
5540 const auto it_begin = exchangeCellList.lower_bound(std::make_tuple(layer,std::numeric_limits<MInt>::min(),
5541 std::numeric_limits<MInt>::min(), std::numeric_limits<MInt>::min()));
5542 const auto it_end = exchangeCellList.upper_bound(std::make_tuple(layer+1,std::numeric_limits<MInt>::min(),
5543 std::numeric_limits<MInt>::min(), std::numeric_limits<MInt>::min()));
5544 const MInt noCandidates = std::distance(it_begin, it_end);
5545
5546 MInt cnt = 0;
5547 for (auto it = it_begin; it!=it_end;) {
5548 ASSERT(get<0>(*it)==layer, "Send a bug report to the C++ vendor!");
5549 const MInt cellId = get<1>(*it);
5550
5551 // Only look for nghbrs on same level
5552 const MInt counter = getAdjacentGridCells5<false,false>(cellId, nghbrList.data());
5553
5554 while (cnt<noCandidates && cellId==get<1>(*it)) {
5555 ASSERT(get<0>(*it)==layer, "Send a bug report to the C++ vendor!");
5556 const MInt nghbrDomIdx = get<2>(*it);
5557 ASSERT((signed)m_windowLayer_.size()>nghbrDomIdx, "");
5558 const MInt solverId = get<3>(*it);
5559
5560 for(MInt n = 0; n < counter; n++) {
5561 const MInt nghbrId = nghbrList[n];
5562 ASSERT(a_level(nghbrId)==level, "Nghbr expected to be on same level");
5563
5564 // if 'treeb().solver(nghbrId, solverId)' is run before the bitsets are exhchanged, it will always result
5565 // in false for halo cells.
5566 // nghbrId must be either haloCell or window cell (at least for m_maxPartitionLevelShift==0).
5567 // Since solverBits haven't yet been exchanged, we cannot check solver affiliation of halo cells
5568 const MBool isHalo = a_isHalo(nghbrId);
5569 if (!isHalo && !treeb().solver(nghbrId, solverId)) continue;
5570
5571 // Sanity check that cell is in m_windowCells (with partLvlShift this check doesn't work always)
5572 if (!isHalo && windCellSet[nghbrDomIdx].find(nghbrId)==windCellSet[nghbrDomIdx].end() && m_maxPartitionLevelShift>0) continue;
5573#ifndef NDEBUG
5574 TERMM_IF_COND(!isHalo && windCellSet[nghbrDomIdx].find(nghbrId)==windCellSet[nghbrDomIdx].end(), "This must be a window cell!");
5575#endif
5576
5577 MInt windowLayer = isHalo ? haloLayer_[nghbrDomIdx][nghbrId].get(solverId)
5578 : m_windowLayer_[nghbrDomIdx][nghbrId].get(solverId);
5579 if (windowLayer>layer+1) {
5580
5581 isHalo ? haloLayer_[nghbrDomIdx][nghbrId].set(solverId, layer+1)
5582 : m_windowLayer_[nghbrDomIdx].at(nghbrId).set(solverId, layer+1);
5583 ASSERT((isHalo && haloLayer_[nghbrDomIdx][nghbrId].get(solverId)==layer+1)
5584 || (!isHalo && m_windowLayer_[nghbrDomIdx].at(nghbrId).get(solverId)==layer+1), "");
5585
5586 if (layer+1<m_noSolverHaloLayers[solverId])
5587 exchangeCellList.insert({std::make_tuple(layer+1, nghbrId, nghbrDomIdx, solverId)});
5588 }
5589 }
5590 ++cnt;
5591 ++it;
5592 }
5593 if (cnt==noCandidates) break;
5594 }
5595 } //loop over layers
5596
5597 // All cells which are in m_windowCells but still not in m_windowLayer_ are appended to the latter (I don't know if
5598 // it's necessary or clever to do it)
5599 for (MInt i = 0; i < noNeighborDomains(); ++i) {
5600 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
5601 const MInt cellId = m_windowCells[i][j];
5602 ASSERT(!a_isHalo(cellId), "");
5603 if (m_windowLayer_[i].find(cellId)==m_windowLayer_[i].end()) {
5604 for (MInt solverId = 0; solverId < treeb().noSolvers(); ++solverId)
5605 if (m_tree.solver(cellId, solverId)) {
5606 // My guess: actually it should be sufficient to have m_noHaloLayers+1, but since the respective halos are
5607 // already created, we need to accept all cells in m_windowCells, which are not yet in m_windowLayer_,
5608 // to have matching buffer sizes in MPI communications
5609 m_windowLayer_[i][cellId].set(solverId, a_hasProperty(cellId, Cell::IsPartLvlAncestor) ? 1 : (m_haloMode==2) ? m_noHaloLayers+1 : m_noSolverHaloLayers[solverId]);
5610 }
5611 }
5612 }
5613 }
5614
5615 // DEBUG output
5616}
5617// clang-format on
5618//-------------------------------------------------------------------------------------------
5619
5620
5625template <MInt nDim>
5627 MBool diagonalNeighbors) {
5628 set<MInt> nghbrs;
5629 for(MInt dir0 = 0; dir0 < m_noDirs; dir0++) {
5630 MInt nghbrId0 = -1;
5631 if(a_hasNeighbor(cellId, dir0) > 0)
5632 nghbrId0 = a_neighborId(cellId, dir0);
5633 else if(a_parentId(cellId) > -1) {
5634 if(a_hasNeighbor(a_parentId(cellId), dir0) > 0) {
5635 nghbrId0 = a_neighborId(a_parentId(cellId), dir0);
5636 }
5637 }
5638
5639 if(nghbrId0 < 0) continue;
5640 if(a_noChildren(nghbrId0) > 0 && a_level(nghbrId0) <= level) {
5641 for(MInt child = 0; child < m_maxNoChilds; child++) {
5642 if(!childCode[dir0][child]) continue;
5643 if(a_childId(nghbrId0, child) > -1) nghbrs.insert(a_childId(nghbrId0, child));
5644 }
5645 } else {
5646 nghbrs.insert(nghbrId0);
5647 }
5648
5649 if(diagonalNeighbors) {
5650 for(MInt dir1 = 0; dir1 < m_noDirs; dir1++) {
5651 if((dir1 / 2) == (dir0 / 2)) continue;
5652 MInt nghbrId1 = -1;
5653 if(a_hasNeighbor(nghbrId0, dir1) > 0)
5654 nghbrId1 = a_neighborId(nghbrId0, dir1);
5655 else if(a_parentId(nghbrId0) > -1)
5656 if(a_hasNeighbor(a_parentId(nghbrId0), dir1) > 0) nghbrId1 = a_neighborId(a_parentId(nghbrId0), dir1);
5657 if(nghbrId1 < 0) continue;
5658 if(a_noChildren(nghbrId1) > 0 && a_level(nghbrId1) <= level) {
5659 for(MInt child = 0; child < m_maxNoChilds; child++) {
5660 if(!childCode[dir0][child] || !childCode[dir1][child]) continue;
5661 if(a_childId(nghbrId1, child) > -1) nghbrs.insert(a_childId(nghbrId1, child));
5662 }
5663 } else
5664 nghbrs.insert(nghbrId1);
5665 IF_CONSTEXPR(nDim == 3) {
5666 for(MInt dir2 = 0; dir2 < m_noDirs; dir2++) {
5667 if(((dir2 / 2) == (dir0 / 2)) || ((dir2 / 2) == (dir1 / 2))) continue;
5668 MInt nghbrId2 = -1;
5669 if(a_hasNeighbor(nghbrId1, dir2) > 0)
5670 nghbrId2 = a_neighborId(nghbrId1, dir2);
5671 else if(a_parentId(nghbrId1) > -1)
5672 if(a_hasNeighbor(a_parentId(nghbrId1), dir2) > 0) nghbrId2 = a_neighborId(a_parentId(nghbrId1), dir2);
5673 if(nghbrId2 < 0) continue;
5674 if(a_noChildren(nghbrId2) > 0 && a_level(nghbrId2) <= level) {
5675 for(MInt child = 0; child < m_maxNoChilds; child++) {
5676 if(!childCode[dir0][child] || !childCode[dir1][child] || !childCode[dir2][child]) continue;
5677 if(a_childId(nghbrId2, child) > -1) nghbrs.insert(a_childId(nghbrId2, child));
5678 }
5679 } else
5680 nghbrs.insert(nghbrId2);
5681 }
5682 }
5683 }
5684 }
5685 }
5686
5687
5688 MInt cnt = 0;
5689 for(MInt nghbr : nghbrs) {
5690 ASSERT(a_level(nghbr) <= level + 1, "");
5691 ASSERT(cnt < (signed)adjacentCells.size(), "");
5692 adjacentCells[cnt] = nghbr;
5693 cnt++;
5694 }
5695 return cnt;
5696}
5697
5698// Object oriented version of getAdjacent... which is fast as a bullet
5699// same level <false, false>
5700// one lower or/and one upper allowed
5701template <MInt nDim>
5702template <MBool finer, MBool coarser>
5703inline MInt CartesianGrid<nDim>::getAdjacentGridCells5(const MInt cellId, MInt* const adjacentCells, const MInt level,
5704 const MBool diagonalNeighbors) {
5705 static constexpr const MInt dimConverter[3][2] = {{1, 2}, {0, 2}, {0, 1}};
5706 set<MInt> nghbrs;
5707
5708 if(diagonalNeighbors) {
5709 for(MInt dim = 0; dim < nDim; ++dim) {
5710 if(nDim == 2)
5711 getAdjacentGridCells1d5<finer, coarser>(nghbrs, cellId, dim, dimConverter[dim][0]);
5712 else if(nDim == 3)
5713 getAdjacentGridCells1d5<finer, coarser>(nghbrs, cellId, dim, dimConverter[dim][0], dimConverter[dim][1]);
5714 }
5715 } else {
5716 for(MInt dim = 0; dim < nDim; ++dim) {
5717 getAdjacentGridCells1d5<finer, coarser>(nghbrs, cellId, dim);
5718 }
5719 }
5720
5721 ASSERT(nghbrs.size() <= 27 * m_maxNoChilds, "");
5722
5723 if(!finer && !coarser) {
5724 // same level
5725 const MInt noNghbrs = nghbrs.size();
5726 std::copy(nghbrs.begin(), nghbrs.end(), adjacentCells);
5727 return noNghbrs;
5728 } else if(level == -1) {
5729 // all levels
5730 const MInt noNghbrs = nghbrs.size();
5731 std::copy(nghbrs.begin(), nghbrs.end(), adjacentCells);
5732 return noNghbrs;
5733 } else {
5734 MInt cnt = 0;
5735 for(auto cellId_ : nghbrs) {
5736 if(a_level(cellId_) == level) {
5737 adjacentCells[cnt++] = cellId_;
5738 }
5739 }
5740 return cnt;
5741 }
5742}
5743
5748template <MInt nDim>
5749template <MBool finer, MBool coarser>
5750inline void CartesianGrid<nDim>::getAdjacentGridCells1d5(set<MInt>& nghbrs, const MInt cellId, const MInt dim,
5751 const MInt dimNext, const MInt dimNextNext,
5752 const uint_fast8_t childCodes) {
5753 TRACE();
5754
5755 for(MInt dir = 2 * dim; dir < 2 * dim + 2; ++dir) {
5756 auto childCodes_ = childCodes & childCodePro[dir];
5757 if(a_hasNeighbor(cellId, dir) > 0) {
5758 const MInt nghbrId0 = a_neighborId(cellId, dir);
5759 if(finer && a_noChildren(nghbrId0) > 0) {
5760 // The neighbor might have children, but which are not adjacent
5761 MBool childFound = false;
5762 for(MInt child = 0; child < m_maxNoChilds; child++) {
5763 if( !childCode[dir][child]) continue;
5764 if(a_childId(nghbrId0, child) > -1) {
5765 const MInt childId = a_childId(nghbrId0, child);
5766 // TODO_SS labels:GRID Can this cell have children and what to do in that case?!
5767 // if (nghbrs.find(childId)!=nghbrs.end()) continue;
5768 nghbrs.insert(childId);
5769 childFound = true;
5770 if(dimNext > -1)
5771 getAdjacentGridCells1d5<false, true>(nghbrs, childId, dimNext, dimNextNext, -1, childCodes_);
5772 if(dimNextNext > -1)
5773 getAdjacentGridCells1d5<false, true>(nghbrs, childId, dimNextNext, dimNext, -1, childCodes_);
5774 }
5775 }
5776 if(!childFound) { // TODO_SS labels:GRID,totest Check if this is required
5777 // if (nghbrs.find(nghbrId0)!=nghbrs.end()) continue;
5778 nghbrs.insert(nghbrId0);
5779 if(dimNext > -1)
5780 getAdjacentGridCells1d5<finer, coarser>(nghbrs, nghbrId0, dimNext, dimNextNext, -1, childCodes_);
5781 if(dimNextNext > -1)
5782 getAdjacentGridCells1d5<finer, coarser>(nghbrs, nghbrId0, dimNextNext, dimNext, -1, childCodes_);
5783 }
5784 } else {
5785 // if (nghbrs.find(nghbrId0)!=nghbrs.end()) continue;
5786 nghbrs.insert(nghbrId0);
5787 if(dimNext > -1)
5788 getAdjacentGridCells1d5<finer, coarser>(nghbrs, nghbrId0, dimNext, dimNextNext, -1, childCodes_);
5789 if(dimNextNext > -1)
5790 getAdjacentGridCells1d5<finer, coarser>(nghbrs, nghbrId0, dimNextNext, dimNext, -1, childCodes_);
5791 }
5792 } else if(coarser && a_parentId(cellId) > -1) {
5793 if(a_hasNeighbor(a_parentId(cellId), dir) > 0) {
5794 const MInt nghbrId0 = a_neighborId(a_parentId(cellId), dir);
5795 // TODO_SS labels:GRID Can this cell have children and what to do in that case?!
5796 // if (nghbrs.find(nghbrId0)!=nghbrs.end()) continue;
5797 nghbrs.insert(nghbrId0);
5798 // TODO_SS labels:GRID,totest check the following
5799 // TERMM_IF_COND(a_hasChildren(nghbrId0), "");
5800 if(dimNext > -1) getAdjacentGridCells1d5<true, false>(nghbrs, nghbrId0, dimNext, dimNextNext, -1, childCodes_);
5801 if(dimNextNext > -1)
5802 getAdjacentGridCells1d5<true, false>(nghbrs, nghbrId0, dimNextNext, dimNext, -1, childCodes_);
5803 }
5804 }
5805 }
5806}
5807
5808//-------------------------------------------------------------------------
5809
5810
5821template <MInt nDim>
5823 const MInt cellId, const MLong* const refineChildIds, const MBool mayHaveChildren,
5824 const std::vector<std::function<MInt(const MFloat*, const MInt, const MInt)>>& cellOutsideSolver,
5825 const bitset<maia::grid::tree::Tree<nDim>::maxNoSolvers()> refineFlag) {
5826 static constexpr MInt noInternalConnections = (nDim == 2) ? 4 : 12;
5827 static constexpr MInt connectionDirs[12] = {1, 1, 3, 3, 1, 1, 3, 3, 5, 5, 5, 5};
5828 static constexpr MInt childs0[12] = {0, 2, 0, 1, 4, 6, 4, 5, 0, 1, 2, 3};
5829 static constexpr MInt childs1[12] = {1, 3, 2, 3, 5, 7, 6, 7, 4, 5, 6, 7};
5830 static constexpr MInt dirStencil[3][8] = {
5831 {0, 1, 0, 1, 0, 1, 0, 1}, {2, 2, 3, 3, 2, 2, 3, 3}, {4, 4, 4, 4, 5, 5, 5, 5}};
5832 static constexpr MInt sideIds[3][8] = {{0, 1, 0, 1, 0, 1, 0, 1}, {0, 0, 1, 1, 0, 0, 1, 1}, {0, 0, 0, 0, 1, 1, 1, 1}};
5833 static constexpr MInt revDir[6] = {1, 0, 3, 2, 5, 4};
5834 static constexpr MInt otherSide[2] = {1, 0};
5835 static constexpr MFloat signStencil[8][3] = {{-F1, -F1, -F1}, {F1, -F1, -F1}, {-F1, F1, -F1}, {F1, F1, -F1},
5836 {-F1, -F1, F1}, {F1, -F1, F1}, {-F1, F1, F1}, {F1, F1, F1}};
5837
5838 ASSERT(m_maxRfnmntLvl >= m_maxLevel, "");
5839 if(a_level(cellId) >= m_maxRfnmntLvl) return;
5840
5841 const MInt childLevel = a_level(cellId) + 1;
5842 const MFloat childCellLength = cellLengthAtLevel(childLevel);
5843
5844 MInt noOutsideChilds = 0;
5845 for(MInt c = 0; c < m_maxNoChilds; c++) {
5847 std::fill_n(isOutside, maia::grid::tree::Tree<nDim>::maxNoSolvers(), -1);
5848
5849 if(!mayHaveChildren && a_childId(cellId, c) > -1) {
5850 // mTerm(1, AT_, "Not supposed to happen." );
5851 }
5852
5853 if(a_childId(cellId, c) > -1) continue;
5854
5855 if(refineChildIds != nullptr && refineChildIds[c] < 0) {
5856 a_childId(cellId, c) = -1;
5857 continue;
5858 }
5859
5860 MFloat coords[nDim];
5861 for(MInt i = 0; i < nDim; i++) {
5862 coords[i] = a_coordinate(cellId, i) + F1B2 * signStencil[c][i] * childCellLength;
5863 }
5864 MInt allOutside = 0;
5865 if(!cellOutsideSolver.empty()) {
5866 allOutside = 1;
5867 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
5868 if(refineFlag[solver]) {
5869 isOutside[solver] = cellOutsideSolver[solver](coords, childLevel, cellId);
5870 } else {
5871 isOutside[solver] = 1;
5872 }
5873 allOutside = mMin(allOutside, isOutside[solver]); // call cellOutsideSolver() function in each solver
5874 }
5875 }
5876 if(allOutside > 0) {
5877 noOutsideChilds++;
5878 a_childId(cellId, c) = -1;
5879 continue;
5880 }
5881
5882 MInt childId;
5883 if(m_freeIndices.size() > 0) {
5884 auto it = m_freeIndices.begin();
5885 childId = *(it);
5886 m_freeIndices.erase(it);
5887 ASSERT(childId > -1 && childId < m_tree.size(), "");
5888 } else {
5889 childId = m_tree.size();
5890 m_tree.append();
5891 }
5892
5893 for(MInt i = 0; i < nDim; i++) {
5894 a_coordinate(childId, i) = coords[i];
5895 }
5896
5897 if(refineChildIds != nullptr) {
5898 a_globalId(childId) = refineChildIds[c];
5899 } else {
5900 a_globalId(childId) = -1;
5901 }
5902
5903 treeb().resetSolver(childId);
5904 MBool any = false;
5905 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
5906 if(refineFlag[solver] && isOutside[solver] < 1) treeb().solver(childId, solver) = true;
5907 any = any || m_tree.solver(childId, solver);
5908 }
5909 ASSERT(any, "");
5910
5911 a_level(childId) = childLevel;
5912
5913 a_childId(cellId, c) = childId;
5914 a_parentId(childId) = cellId;
5915 a_isToDelete(childId) = false;
5916 a_resetProperties(childId);
5917 a_hasProperty(childId, Cell::IsHalo) = (refineChildIds != nullptr);
5918 // a_hasProperty( cellId, Cell::IsHalo);
5919 // a_hasProperty( childId, Cell::IsPeriodic) = a_hasProperty( cellId, Cell::IsPeriodic);
5920
5921 for(MInt k = 0; k < m_maxNoChilds; k++)
5922 a_childId(childId, k) = -1;
5923 for(MInt d = 0; d < 2 * nDim; d++) {
5924 a_neighborId(childId, d) = -1;
5925 }
5926
5927 if(treeb().noSolvers() == 1) {
5928 treeb().solver(childId, 0) = true; // remove if multisolver info set correctly!!!
5929 }
5930
5931 treeb().resetIsLeafCell(childId);
5932
5933 MInt nodeId[3];
5934 MInt revNodeId[3];
5935 for(MInt i = 0; i < nDim; i++) {
5936 nodeId[i] = sideIds[i][c] * IPOW2(i);
5937 revNodeId[i] = otherSide[sideIds[i][c]] * IPOW2(i);
5938 }
5939 for(MInt d = 0; d < nDim; d++) {
5940 const MInt dir = dirStencil[d][c];
5941 if(a_hasNeighbor(cellId, dir) == 0) {
5942 a_neighborId(childId, dir) = -1;
5943 } else if(a_noChildren(a_neighborId(cellId, dir)) == 0) {
5944 a_neighborId(childId, dir) = -1;
5945 } else {
5946 const MInt childNode = c - nodeId[d] + revNodeId[d];
5947 const MInt nghbrId = a_childId(a_neighborId(cellId, dir), childNode);
5948 if(nghbrId < 0) {
5949 a_neighborId(childId, dir) = -1;
5950 continue;
5951 }
5952 a_neighborId(childId, dir) = nghbrId;
5953 a_neighborId(nghbrId, revDir[dir]) = childId;
5954 }
5955 }
5956 a_hasProperty(childId, Cell::WasNewlyCreated) = true;
5957
5958 // Set a default weight and the number of offspring
5959 a_weight(childId) = 1.0;
5960 a_noOffsprings(childId) = 1;
5961 }
5962
5963 if(noOutsideChilds == m_maxNoChilds) {
5964 // all childs are outside!
5965 // NOTE: this occours when there is a difference in the geometry used in the grid-generation
5966 // geometryroot.cpp-isPointInsideNode and
5967 // geometry.cpp-pointIsInside of the geometry during the solver run!
5968 // FIX: call again but this time without the solver-Outside check,
5969 // meaning that all cells will be refined instead!
5970 // NOTE: How is that a fix?
5971 const std::vector<std::function<MInt(const MFloat*, const MInt, const MInt)>> cellOutsideEmpty;
5972 refineCell(cellId, nullptr, false, cellOutsideEmpty, refineFlag);
5973 return;
5974 }
5975
5976
5977 ASSERT(noOutsideChilds < m_maxNoChilds,
5978 to_string(noOutsideChilds) + "Check your geometry: all childs are outside or should not be refined! ");
5979 ASSERT(a_noChildren(cellId) > 0, "");
5980 treeb().resetIsLeafCell(cellId);
5981
5982 for(MInt c = 0; c < noInternalConnections; c++) {
5983 const MInt dir = connectionDirs[c];
5984 const MInt child0 = a_childId(cellId, childs0[c]);
5985 const MInt child1 = a_childId(cellId, childs1[c]);
5986 if(child0 > -1 && child1 > -1) {
5987 a_neighborId(child0, dir) = child1;
5988 a_neighborId(child1, revDir[dir]) = child0;
5989 }
5990 }
5991
5992 a_hasProperty(cellId, Cell::WasRefined) = true;
5993}
5994
5995
5996// --------------------------------------------------------------------------------------
5997
5998
6003template <MInt nDim>
6005 TRACE();
6006
6007 if(a_noChildren(cellId) <= 0) {
6008 mTerm(1, AT_, "Unexpected situation 1 in CartesianGrid::removeChilds(MInt childId)");
6009 }
6010 for(MInt i = 0; i < m_maxNoChilds; i++) {
6011 const MInt childId = a_childId(cellId, i);
6012
6013 if(childId > -1) {
6014 removeCell<true>(childId);
6015 a_childId(cellId, i) = -1;
6016 }
6017
6018 treeb().resetIsLeafCell(cellId);
6019 }
6020
6021 a_hasProperty(cellId, Cell::WasCoarsened) = true;
6022}
6023
6024
6029template <MInt nDim>
6030template <MBool removeChilds_>
6032 static constexpr MInt revDir[6] = {1, 0, 3, 2, 5, 4};
6033
6034 ASSERT(cellId > -1 && cellId < treeb().size(), "");
6035 ASSERT(a_noChildren(cellId) == 0, "No children expected for child. ");
6036 ASSERT(!a_hasProperty(cellId, Cell::IsPartitionCell) || a_hasProperty(cellId, Cell::IsHalo),
6037 "Error: cannot remove non-halo partition cell");
6038
6039 for(MInt dir = 0; dir < m_noDirs; dir++) {
6040 if(a_hasNeighbor(cellId, dir) > 0) {
6041 MInt nghbrId = a_neighborId(cellId, dir);
6042 if((nghbrId < 0) || (nghbrId >= treeb().size())) {
6043 mTerm(1, AT_,
6044 "Unexpected situation 2 in CartesianGrid::removeChildIds() >> " + to_string(cellId) + " " + to_string(dir)
6045 + " " + to_string(nghbrId) + " " + to_string(treeb().size()));
6046 }
6047 a_neighborId(nghbrId, revDir[dir]) = -1;
6048 a_neighborId(cellId, dir) = -1;
6049 } else {
6050 a_neighborId(cellId, dir) = -1;
6051 }
6052 }
6053 const MInt parentId = a_parentId(cellId);
6054 a_parentId(cellId) = -1;
6055 a_globalId(cellId) = -1;
6056 treeb().resetSolver(cellId);
6057
6058 a_resetProperties(cellId);
6059
6060 a_isToDelete(cellId) = true;
6061
6062 a_level(cellId) = -1;
6063 treeb().resetIsLeafCell(cellId);
6064
6065 if(cellId == (treeb().size() - 1)) {
6066 treeb().size(treeb().size() - 1);
6067 } else {
6068 m_freeIndices.insert(cellId);
6069 }
6070
6071 // return early if called from inside removeChilds
6072 if(removeChilds_ || parentId == -1) return;
6073
6074 ASSERT(parentId < treeb().size(), "");
6075
6076 //
6077 for(MInt i = 0; i < m_maxNoChilds; i++) {
6078 if(a_childId(parentId, i) == cellId) {
6079 a_childId(parentId, i) = -1;
6080 break;
6081 }
6082 }
6083
6084 //
6085 treeb().resetIsLeafCell(parentId);
6086 for(MInt solverId = 0; solverId < m_tree.noSolvers(); solverId++) {
6087 if(m_tree.solver(parentId, solverId)) {
6088 a_isLeafCell(parentId, solverId) =
6089 !a_hasChildren(parentId, solverId) && !a_hasProperty(parentId, Cell::IsPartLvlAncestor);
6090 }
6091 }
6092}
6093
6094//----------------------------------------------------------------------------------
6095
6096
6106template <MInt nDim>
6108 TRACE();
6109
6110 MInt offset[6] = {2, 2, 4, 4, 6, 6};
6111 MInt offset2[4] = {2, 2, 0, 0};
6112 MInt allNeighbors[(nDim == 2) ? 8 : 12];
6113 MInt secondLevelNeighbors[8];
6114
6115 IF_CONSTEXPR(nDim == 2) {
6116 MInt allNeighbors2D[8] = {0, 1, 2, 3, 0, 1, 2, 3};
6117 for(MInt i = 0; i < 8; i++) {
6118 allNeighbors[i] = allNeighbors2D[i];
6119 }
6120 // Holds the corresponding neighbor directions for the binary codes according to D2Q9.
6121 // 9 means in 2D that the corresponding code doesn't exist.
6122 MInt neighborCode2D[11] = {9, 0, 1, 9, 2, 6, 5, 9, 3, 7, 4};
6123 for(MInt i = 0; i < 11; i++) {
6124 m_neighborCode[i] = neighborCode2D[i];
6125 }
6126 }
6127 else {
6128 MInt allNeighbors3D[12] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5};
6129 for(MInt i = 0; i < 12; i++) {
6130 allNeighbors[i] = allNeighbors3D[i];
6131 }
6132 // Holds the corresponding neighbor directions for the binary codes according to D3Q27.
6133 // 27 means in 3D that the corresponding code doesn't exist.
6134 MInt neighborCode3D[43] = {27, 0, 1, 27, 2, 6, 8, 27, 3, 7, 9, 27, 27, 27, 27, 27, 4, 10, 12, 27, 14, 18,
6135 22, 27, 16, 20, 24, 27, 27, 27, 27, 27, 5, 11, 13, 27, 15, 19, 23, 27, 17, 21, 25};
6136 for(MInt i = 0; i < 43; i++) {
6137 m_neighborCode[i] = neighborCode3D[i];
6138 }
6139 }
6140 m_counter1D = 0;
6141 m_counter2D = 0;
6142 m_counter3D = 0;
6143
6144 // Determine counters
6145 // All 1D neighbors
6146 for(MInt i = 0; i < nDim * 2; i++) {
6147 m_counter1D++;
6148 // Remaining possible neighbors
6149 for(MInt j = 0; j < (nDim * 2 - 2); j++) {
6150 m_counter2D++;
6151 // Remaining possible neighbors
6152 for(MInt k = 0; k < nDim * 2 - 2 - 2; k++) {
6153 m_counter3D++;
6154 }
6155 }
6156 }
6157
6158 // Allocate memory
6159 mDeallocate(m_paths1D);
6160 mAlloc(m_paths1D, m_counter1D, "m_paths1D", AT_);
6161 mDeallocate(m_paths2D);
6162 mAlloc(m_paths2D, m_counter2D, 2, "m_paths2D", AT_);
6163 IF_CONSTEXPR(nDim == 3) {
6164 mDeallocate(m_paths3D);
6165 mAlloc(m_paths3D, m_counter3D, 3, "m_paths3D", AT_);
6166 }
6167
6168 m_counter1D = 0;
6169 m_counter2D = 0;
6170 m_counter3D = 0;
6171
6172 // save the paths...
6173 for(MInt i = 0; i < nDim * 2; i++) {
6174 // Save 1D paths
6175 m_paths1D[m_counter1D] = allNeighbors[i];
6176 m_counter1D++;
6177 // Remaining possible neighbors
6178 for(MInt j = 0; j < (nDim * 2 - 2); j++) {
6179 // Fill secondeLevelNeighbors twice!
6180 secondLevelNeighbors[j] = allNeighbors[offset[i] + j];
6181 secondLevelNeighbors[j + nDim * 2 - 2] = allNeighbors[offset[i] + j];
6182 }
6183 for(MInt j = 0; j < (nDim * 2 - 2); j++) {
6184 // Save 2D paths
6185 m_paths2D[m_counter2D][0] = allNeighbors[i];
6186 m_paths2D[m_counter2D][1] = secondLevelNeighbors[j];
6187 m_counter2D++;
6188 // Remaining possible neighbors
6189 IF_CONSTEXPR(nDim == 3) {
6190 MInt thirdLevelNeighbors[2];
6191 for(MInt k = 0; k < nDim * 2 - 2 - 2; k++) {
6192 // Fill thirdLevelNeighbors
6193 thirdLevelNeighbors[k] = secondLevelNeighbors[offset2[j] + k];
6194 // Save 3D paths
6195 m_paths3D[m_counter3D][0] = allNeighbors[i];
6196 m_paths3D[m_counter3D][1] = secondLevelNeighbors[j];
6197 m_paths3D[m_counter3D][2] = thirdLevelNeighbors[k];
6198
6199 m_counter3D++;
6200 }
6201 }
6202 }
6203 }
6204}
6205
6206//-----------------------------------------------------------------------------
6207
6215template <MInt nDim>
6217 vector<vector<MFloat>>& sensors, vector<MFloat>& sensorWeight, vector<bitset<64>>& sensorCellFlag,
6218 vector<MInt>& sensorSolverId, const std::vector<std::function<void(const MInt)>>& refineCellSolver,
6219 const std::vector<std::function<void(const MInt)>>& removeChildsSolver,
6220 const std::vector<std::function<void(const MInt)>>& removeCellSolver,
6221 const std::vector<std::function<void(const MInt, const MInt)>>& swapCellsSolver,
6222 const std::vector<std::function<MInt(const MFloat*, const MInt, const MInt)>>& cellOutsideSolver,
6223 const std::vector<std::function<void()>>& resizeGridMapSolver) {
6224 TRACE();
6225
6226 if(m_lowMemAdaptation) {
6227 this->meshAdaptationLowMem(sensors, sensorWeight, sensorCellFlag, sensorSolverId, refineCellSolver,
6228 removeChildsSolver, removeCellSolver, swapCellsSolver, cellOutsideSolver,
6229 resizeGridMapSolver);
6230 } else {
6231 this->meshAdaptationDefault(sensors, sensorWeight, sensorCellFlag, sensorSolverId, refineCellSolver,
6232 removeChildsSolver, removeCellSolver, swapCellsSolver, cellOutsideSolver,
6233 resizeGridMapSolver);
6234 }
6235}
6236
6237
6248template <MInt nDim>
6250 vector<vector<MFloat>>& sensors, vector<MFloat>& sensorWeight, vector<bitset<64>>& sensorCellFlag,
6251 vector<MInt>& sensorSolverId, const std::vector<std::function<void(const MInt)>>& refineCellSolver,
6252 const std::vector<std::function<void(const MInt)>>& removeChildsSolver,
6253 const std::vector<std::function<void(const MInt)>>& removeCellSolver,
6254 const std::vector<std::function<void(const MInt, const MInt)>>& swapCellsSolver,
6255 const std::vector<std::function<MInt(const MFloat*, const MInt, const MInt)>>& cellOutsideSolver,
6256 const std::vector<std::function<void()>>& resizeGridMapSolver) {
6257 TRACE();
6258
6259 //----------------------------
6260 // 0. init
6261 const MInt noCells = treeb().size();
6262 const MInt maxNoCells = m_maxNoCells;
6263 const MInt maxLevel = mMin(m_maxRfnmntLvl, m_maxLevel + 1);
6264
6265 const MInt noSensors = (signed)sensorWeight.size();
6266 ASSERT(sensorWeight.size() == sensors.size(), "");
6267 ASSERT(sensorWeight.size() == sensorSolverId.size(), "");
6268 ScratchSpace<MFloat> sensorCnt(noSensors, 2, AT_, "sensorCnt");
6269 ScratchSpace<MFloat> sensorThresholds(noSensors, 2, AT_, "sensorThresholds");
6270 ScratchSpace<bitset<maia::grid::tree::Tree<nDim>::maxNoSolvers()>> refineFlag(maxNoCells, AT_, "refineFlag");
6271 ScratchSpace<bitset<maia::grid::tree::Tree<nDim>::maxNoSolvers()>> coarseFlag(maxNoCells, AT_, "coarseFlag");
6272 ASSERT(m_freeIndices.empty(), "");
6273 m_freeIndices.clear();
6274 sensorCnt.fill(F0);
6275 for(MInt c = 0; c < maxNoCells; c++) {
6276 refineFlag[c].reset();
6277 coarseFlag[c].reset();
6278 }
6279 // these flags indicate which cells have changed for the subsequent reinitialization by the solvers
6280 for(MInt cellId = 0; cellId < noCells; cellId++) {
6281 a_hasProperty(cellId, Cell::IsToDelete) = false;
6282 a_hasProperty(cellId, Cell::WasNewlyCreated) = false;
6283 a_hasProperty(cellId, Cell::WasCoarsened) = false;
6284 a_hasProperty(cellId, Cell::WasRefined) = false;
6285
6286 // Reset the window cell flag,
6287 // is set for all preserved window cells when added to m_windowCells
6288 a_hasProperty(cellId, Cell::IsWindow) = false;
6289 }
6290
6291 //----------------------------
6292 // 1. exchange sensors and compute RMS values
6293 // TODO labels:GRID move these parts to the cartesian solver and do this sort of smoothing
6294 // on the solver grid in setSensors, after all sensors have been set there!!!!
6295 // the cartesiangrid should only get one refine/coarse flags for each cell from the solver!
6296 for(MInt cellId = 0; cellId < noCells; cellId++) {
6297 if(a_hasProperty(cellId, Cell::IsHalo)) continue;
6298 if(a_isToDelete(cellId)) continue;
6299
6300 // Note: sensors for partition level ancestors might not be set correctly!
6301 if(a_hasProperty(cellId, Cell::IsPartLvlAncestor)) {
6302 continue;
6303 }
6304
6305 for(MInt s = 0; s < noSensors; s++) {
6306 if(sensorCellFlag[cellId][s]) {
6307 ASSERT(sensorWeight[s] < F0 || std::fabs(sensors[s][cellId]) < MFloatMaxSqrt,
6308 "invalid sensor value: " + std::to_string(sensors[s][cellId]));
6309 sensorCnt(s, 0) += POW2(sensors[s][cellId]);
6310 sensorCnt(s, 1) += F1;
6311 }
6312 }
6313 }
6314
6315 if(noSensors > 0) {
6316 MPI_Allreduce(MPI_IN_PLACE, &sensorCnt(0), 2 * noSensors, MPI_DOUBLE, MPI_SUM, mpiComm(), AT_, "MPI_IN_PLACE",
6317 "sensorCnt(0)");
6318 }
6319
6320 // TODO labels:GRID,ADAPTATION introduce sensor type to replace this weight hack
6321 for(MInt s = 0; s < noSensors; s++) {
6322 if(sensorWeight[s] < F0) { // true/false sensor
6323 sensorThresholds(s, 0) = -1e-12;
6324 sensorThresholds(s, 1) = 1e-12;
6325 } else { // continuous sensor
6326 sensorThresholds(s, 1) = sensorWeight[s] * sqrt(mMax(1e-14, (sensorCnt(s, 0) / sensorCnt(s, 1))));
6327 sensorThresholds(s, 0) = m_coarseRatio * sensorThresholds(s, 1);
6328 }
6329 }
6330
6331 //---------------------------(1)
6332
6333 const MBool hasPartLvlShift = (m_maxPartitionLevelShift > 0);
6334 const MInt tmpScratchSize = (hasPartLvlShift) ? m_tree.size() : 1;
6335
6336 ScratchSpace<MBool> isHaloPartLvlAncestor(tmpScratchSize, AT_, "isHaloPartLvlAncestor");
6337
6338 isHaloPartLvlAncestor.fill(false);
6339
6340 // Determine relevant halo partition level ancestor window/halo cells that need to be preserved
6341 std::vector<std::vector<MInt>> oldWindowCells;
6342 std::vector<MInt> oldNoWindowCells;
6343 MInt totalNoWindowCells = 0;
6344
6345 for(MInt d = 0; d < noNeighborDomains(); d++) {
6346 oldWindowCells.push_back(m_windowCells[d]);
6347 oldNoWindowCells.push_back(noWindowCells(d));
6348 totalNoWindowCells += noWindowCells(d);
6349 }
6350
6351 MInt recvSize = (m_maxPartitionLevelShift > 0) ? totalNoWindowCells : 1;
6352 ScratchSpace<MBool> recvIsHaloPartLvlAncestor(recvSize, AT_, "recvIsHaloPartLvlAncestor");
6353
6354 if(m_maxPartitionLevelShift > 0) {
6355 for(auto& i : m_partitionLevelAncestorIds) {
6356 TERMM_IF_NOT_COND(a_hasProperty(i, Cell::IsPartLvlAncestor),
6357 "cell is not a partition level ancestor: " + std::to_string(i));
6358 // Mark halo partition level ancestors (which are part of the missing subtree of the grid)
6359 if(a_hasProperty(i, Cell::IsHalo)) {
6360 isHaloPartLvlAncestor[i] = true;
6361 }
6362 // Mark all halo childs of partition level ancestors (required for exchange of e.g. global
6363 // ids!)
6364 for(MInt child = 0; child < m_maxNoChilds; child++) {
6365 const MInt childId = a_childId(i, child);
6366 if(childId > -1 && a_isHalo(childId)) {
6367 isHaloPartLvlAncestor[childId] = true;
6368 }
6369 }
6370 }
6371
6372 // Reverse exchange markers from halo cells to corresponding window cells
6373 if(noNeighborDomains() > 0) {
6374#ifndef PVPLUGIN
6375 maia::mpi::reverseExchangeData(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), &isHaloPartLvlAncestor[0],
6376 &recvIsHaloPartLvlAncestor[0]);
6377#else
6378 TERMM(1, "not working for compilation of paraview plugin");
6379#endif
6380 }
6381 }
6382
6383 //----------------------------
6384 // 2. tag cells for coarsening / refinement
6385
6386 // init parent cells with coarse flag true and set false if any child intervenes
6387 for(MInt cellId = 0; cellId < m_noInternalCells; cellId++) {
6388 ASSERT(!a_hasProperty(cellId, Cell::IsHalo), "");
6389 ASSERT(!a_isToDelete(cellId), "");
6390 if(a_level(cellId) > m_maxUniformRefinementLevel) {
6391 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
6392 if(!m_tree.solver(cellId, solver)) continue;
6393 if(a_hasChildren(cellId, solver)) continue;
6394
6395 const MInt parentId = a_parentId(cellId, solver);
6396 ASSERT(parentId > -1, "");
6397 // Partition level shift, parent is a halo cell and cannot be coarsened
6398 if(a_isHalo(parentId) || a_hasProperty(parentId, Cell::IsPartLvlAncestor)) continue;
6399
6400 for(MInt s = 0; s < noSensors; s++) {
6401 if(sensorSolverId[s] != solver) continue;
6402 if(sensorCellFlag[cellId][s] || sensorCellFlag[parentId][s]) {
6403 // only coarsen cells if they are above the newMinLevel!
6404 if(a_level(cellId) > m_newMinLevel && m_allowCoarsening) {
6405 coarseFlag[a_parentId(cellId)][solver] = true;
6406 }
6407 }
6408 }
6409 }
6410 }
6411 }
6412
6413 for(MInt cellId = 0; cellId < m_noInternalCells; cellId++) {
6414 ASSERT(!a_hasProperty(cellId, Cell::IsHalo), "");
6415 ASSERT(!a_isToDelete(cellId), "");
6416 const MInt level = a_level(cellId);
6417 //---
6418 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
6419 if(!m_tree.solver(cellId, solver)) continue;
6420 if(a_hasChildren(cellId, solver)) continue;
6421 const MInt parentId = a_parentId(cellId, solver);
6422 // Partition level shift, if parent is a halo it cannot be coarsened and e.g.
6423 // sensors/coarseFlag[parentId] should not be accessed!
6424 const MBool partLvlAncestorParent = (parentId > -1) ? a_hasProperty(parentId, Cell::IsPartLvlAncestor) : false;
6425
6426 // cell should be marked for refinement
6427 MBool refine = false;
6428 // cell should be marked for coarsened
6429 MBool coarsen = true;
6430 // cell should be marked for refinement regardless
6431 // whether a different sensor marks this cell for coarsening
6432 MBool forceRefine = false;
6433 for(MInt s = 0; s < noSensors; s++) {
6434 if(sensorSolverId[s] != solver) continue;
6435 if(sensorCellFlag[cellId][s]) {
6436 coarsen = coarsen && (sensors[s][cellId] < sensorThresholds(s, 0));
6437 }
6438 if(level > m_maxUniformRefinementLevel && !partLvlAncestorParent && sensorCellFlag[parentId][s]) {
6439 coarsen = coarsen && (sensors[s][parentId] < sensorThresholds(s, 0));
6440 }
6441 // this sensor would refine the cell
6442 const MBool refineSensor = sensors[s][cellId] > sensorThresholds(s, 1) && sensorCellFlag[cellId][s];
6443 refine = refine || refineSensor;
6444
6445 // a true/false sensor marks the cell for refinement
6446 if(refineSensor && sensorWeight[s] < F0) {
6447 forceRefine = true;
6448 }
6449 }
6450 if(level < m_maxRfnmntLvl) {
6451 refineFlag[cellId][solver] = refine;
6452 }
6453
6454 if(level > m_maxUniformRefinementLevel && !partLvlAncestorParent) {
6455 coarseFlag[parentId][solver] = coarseFlag[parentId][solver] && coarsen;
6456 }
6457 // force a refinement of a cell:
6458 // for all minLevel cells which should be raised to the newMinLevel
6459 // for the cells forced by any true/false sensor!
6460 if((forceRefine && level < m_maxRfnmntLvl) || a_level(cellId) < m_newMinLevel) {
6461 refineFlag[cellId][solver] = true;
6462 coarseFlag[cellId][solver] = false;
6463 if(level > m_maxUniformRefinementLevel) {
6464 coarseFlag[parentId][solver] = false;
6465 }
6466 }
6467 }
6468 }
6469
6470 // 3. Exchange since consistency checks need neighbor values
6471 if(noNeighborDomains() > 0) {
6472 maia::mpi::exchangeBitset(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), coarseFlag.getPointer(),
6473 m_tree.size());
6474 maia::mpi::exchangeBitset(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), refineFlag.getPointer(),
6475 m_tree.size());
6476 }
6477
6478 //----------------------------
6479 // 4. consistency check for internal cells
6480 if(m_checkRefinementHoles != nullptr) {
6481 // necessary for solution adaptive refinement (i.e. at shocks)
6482 const MInt adaptationHoleLimit = nDim;
6483 MBool gridUpdated = true;
6484
6485 MInt loopCount = 0;
6486 while(gridUpdated && loopCount < 25) {
6487 MInt noChanges = 0;
6488 loopCount++;
6489 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
6490 if(!m_checkRefinementHoles[solver]) continue;
6491 for(MInt cellId = 0; cellId < noCells; cellId++) {
6492 if(a_isToDelete(cellId)) continue;
6493 if(a_hasProperty(cellId, Cell::IsHalo)) continue;
6494 if(!m_tree.solver(cellId, solver)) continue;
6495 if(a_hasChildren(cellId, solver)) continue;
6496
6497 // check, if current cell is a hole in the refinement => refine cell aswell
6498 if(!refineFlag[cellId][solver]
6499 || (a_parentId(cellId, solver) > -1 && coarseFlag[a_parentId(cellId, solver)][solver])) {
6500 MInt checkRefinedNeighbors = 0;
6501 MInt currentHoleLimit = adaptationHoleLimit;
6502 for(MInt dir = 0; dir < m_noDirs; dir++) {
6503 if(a_hasNeighbor(cellId, dir, solver) > 0) {
6504 const MInt nghbrId = a_neighborId(cellId, dir, solver);
6505 if(refineFlag[nghbrId][solver] || a_hasChildren(nghbrId, solver)) {
6506 checkRefinedNeighbors += 1;
6507 }
6508 } else {
6509 // Lower limit at boundaries
6510 currentHoleLimit--;
6511 }
6512 }
6513 if(checkRefinedNeighbors > adaptationHoleLimit) {
6514 refineFlag[cellId][solver] = true;
6515 coarseFlag[cellId][solver] = false;
6516 const MInt parentId = a_parentId(cellId, solver);
6517 if(parentId > -1) coarseFlag[parentId][solver] = false;
6518 noChanges += 1;
6519 }
6520 }
6521
6522 // check, if current cell coarsening would create a hole in the refinement => do not coarsen cell
6523 const MInt parentId = a_parentId(cellId, solver);
6524 if(parentId < 0) continue;
6525 if(coarseFlag[parentId][solver]) {
6526 ASSERT(!a_isHalo(parentId) && !a_hasProperty(parentId, Cell::IsPartLvlAncestor),
6527 "Partition level ancestor parent cell has coarseFlag set.");
6528 MInt checkRefinedNeighbors = 0;
6529 MInt currentHoleLimit = adaptationHoleLimit;
6530 for(MInt dir = 0; dir < m_noDirs; dir++) {
6531 if(a_hasNeighbor(parentId, dir, solver) > 0) {
6532 const MInt parNghbrId = a_neighborId(parentId, dir, solver);
6533 if(!coarseFlag[parNghbrId][solver]
6534 && (refineFlag[parNghbrId][solver] || a_hasChildren(parNghbrId, solver))) {
6535 checkRefinedNeighbors += 1;
6536 }
6537 } else {
6538 // Lower limit at boundaries
6539 currentHoleLimit--;
6540 }
6541 }
6542 if(checkRefinedNeighbors > adaptationHoleLimit) {
6543 coarseFlag[parentId][solver] = false;
6544 noChanges += 1;
6545 }
6546 }
6547
6548 // Check if the coarsening of neighboring cells would create a refined cell island => coarsen cell aswell
6549 if(!coarseFlag[parentId][solver]) {
6550 ASSERT(!a_isHalo(parentId) && !a_hasProperty(parentId, Cell::IsPartLvlAncestor),
6551 "Partition level ancestor parent cell has coarseFlag set.");
6552 MInt checkCoarseNeighbors = 0;
6553 MInt currentHoleLimit = adaptationHoleLimit;
6554 for(MInt dir = 0; dir < m_noDirs; dir++) {
6555 if(a_hasNeighbor(parentId, dir, solver) > 0) {
6556 const MInt parNghbrId = a_neighborId(parentId, dir, solver);
6557 if(coarseFlag[parNghbrId][solver]
6558 || (!refineFlag[parNghbrId][solver] && !a_hasChildren(parNghbrId, solver))) {
6559 checkCoarseNeighbors += 1;
6560 }
6561 } else {
6562 // Lower limit at boundaries
6563 currentHoleLimit--;
6564 }
6565 }
6566 if(checkCoarseNeighbors > currentHoleLimit) {
6567 coarseFlag[parentId][solver] = true;
6568 noChanges += 1;
6569 }
6570 }
6571
6572 // check if the current cell refinement creates a refined cell island => do not refine cell
6573 if(refineFlag[cellId][solver]) {
6574 MInt checkRefinedNeighbors = 0;
6575 for(MInt dir = 0; dir < m_noDirs; dir++) {
6576 if(a_hasNeighbor(cellId, dir, solver) > 0) {
6577 const MInt nghbrId = a_neighborId(cellId, dir, solver);
6578 if(refineFlag[nghbrId][solver] || a_hasChildren(nghbrId, solver)) {
6579 checkRefinedNeighbors += 1;
6580 }
6581 }
6582 }
6583 if(checkRefinedNeighbors == 0) {
6584 refineFlag[cellId][solver] = false;
6585 noChanges += 1;
6586 }
6587 }
6588 }
6589 }
6590 if(noChanges == 0) {
6591 gridUpdated = false;
6592 }
6593 }
6594 }
6595
6596 // smooth diagonal level jumps,
6597 // even if already existing in the base grid!
6598 if(m_diagSmoothing != nullptr) {
6599 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
6600 if(!m_diagSmoothing[solver]) continue;
6601 for(MInt lvl = maxLevel - 1; lvl >= minLevel(); lvl--) {
6602 for(MInt cellId = 0; cellId < noCells; cellId++) {
6603 if(a_level(cellId) != lvl) continue;
6604 if(!a_isLeafCell(cellId)) continue;
6605 if(!m_tree.solver(cellId, solver)) continue;
6606 if(refineFlag[cellId][solver]) continue;
6607 for(MInt i = 0; i < m_noDirs; i++) {
6608 const MInt nghbrId = a_neighborId(cellId, i, solver);
6609 if(nghbrId < 0) continue;
6610 if(a_isLeafCell(nghbrId)) continue;
6611 for(MInt child = 0; child < m_maxNoChilds; child++) {
6612 MInt childId = a_childId(nghbrId, child, solver);
6613 if(childId < 0) continue;
6614 if(!a_isLeafCell(childId)) {
6615 refineFlag[cellId][solver] = true;
6616 coarseFlag[cellId][solver] = false;
6617 MInt parentId = a_parentId(cellId, solver);
6618 if(parentId > -1) {
6619 coarseFlag[cellId][solver] = false;
6620 }
6621 break;
6622 }
6623 }
6624 for(MInt j = 0; j < m_noDirs; j++) {
6625 if(j / 2 == i / 2) continue;
6626 const MInt diagNghbrId = a_neighborId(nghbrId, j, solver);
6627 if(diagNghbrId < 0) continue;
6628 if(a_isLeafCell(diagNghbrId)) continue;
6629 for(MInt child = 0; child < m_maxNoChilds; child++) {
6630 MInt childId = a_childId(diagNghbrId, child, solver);
6631 if(childId < 0) continue;
6632 if(!a_isLeafCell(childId)) {
6633 refineFlag[cellId][solver] = true;
6634 coarseFlag[cellId][solver] = false;
6635 MInt parentId = a_parentId(cellId, solver);
6636 if(parentId > -1) {
6637 coarseFlag[cellId][solver] = false;
6638 }
6639 break;
6640 }
6641 }
6642 }
6643 }
6644 }
6645 }
6646 }
6647 }
6648
6649 // Additional consistency checks fixing contradictory flags
6650 for(MInt cellId = 0; cellId < noCells; cellId++) {
6651 if(a_hasProperty(cellId, Cell::IsHalo)) continue;
6652 if(a_isToDelete(cellId)) continue;
6653
6654 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
6655 if(!m_tree.solver(cellId, solver)) continue;
6656 if(a_hasChildren(cellId, solver)) continue;
6657 const MInt parentId = a_parentId(cellId, solver);
6658 if(refineFlag[cellId][solver] && coarseFlag[cellId][solver]) {
6659 refineFlag[cellId][solver] = false;
6660 coarseFlag[cellId][solver] = false;
6661 }
6662
6663 // TODO labels:GRID,ADAPTATION,totest check for multisolver!!!!!
6664 if(refineFlag[cellId][solver]) {
6665 if(parentId > -1) {
6666 if(coarseFlag[parentId][solver]) {
6667 ASSERT(!a_isHalo(parentId) && !a_hasProperty(parentId, Cell::IsPartLvlAncestor),
6668 "Partition level ancestor parent cell has coarseFlag set.");
6669 coarseFlag[parentId][solver] = false;
6670 for(MInt child = 0; child < m_maxNoChilds; child++) {
6671 MInt childId = a_childId(parentId, child, solver);
6672 if(childId > -1) {
6673 refineFlag[childId][solver] = false;
6674 }
6675 }
6676 }
6677 }
6678 }
6679 }
6680 }
6681 //---------------------------(4)
6682
6683 // ensure the same refinement between two different solvers in the combined/overlapping region!
6684 if(m_noIdenticalSolvers > 0) {
6685 for(MInt cellId = 0; cellId < m_noInternalCells; cellId++) {
6686 for(MInt pair = 0; pair < m_noIdenticalSolvers; pair++) {
6687 const MInt slaveId = m_identicalSolvers[pair * 2];
6688 const MInt masterId = m_identicalSolvers[pair * 2 + 1];
6689 if(treeb().solver(cellId, slaveId) && treeb().solver(cellId, masterId)) {
6690 if(!coarseFlag[cellId][masterId]) {
6691 coarseFlag[cellId][slaveId] = coarseFlag[cellId][masterId];
6692 }
6693 if(m_identicalSolverLvlJumps[pair] == 0) {
6694 refineFlag[cellId][slaveId] = refineFlag[cellId][masterId];
6695 } else if(m_identicalSolverLvlJumps[pair] == 1) {
6696 if(a_level(cellId) < m_identicalSolverMaxLvl[pair] && a_hasChildren(cellId, masterId)) {
6697 if(!a_hasChildren(cellId, slaveId)) {
6698 refineFlag[cellId][slaveId] = true;
6699 }
6700 coarseFlag[cellId][slaveId] = false;
6701 }
6702 } else {
6703 if(a_level(cellId) < m_identicalSolverMaxLvl[pair] && a_hasChildren(cellId, masterId)
6704 && a_childId(cellId, 0) > -1 && treeb().solver(a_childId(cellId, 0), masterId)
6705 && a_hasChildren(a_childId(cellId, 0), masterId)) {
6706 if(!a_hasChildren(cellId, slaveId)) {
6707 refineFlag[cellId][slaveId] = true;
6708 }
6709 coarseFlag[cellId][slaveId] = false;
6710 }
6711 }
6712 }
6713 }
6714 }
6715 }
6716
6717
6718 //----------------------------
6719 // 5. Coarsening step: loop from maxLevel to minLevel and exchange coarseFlags to retrieve
6720 // consistent refinement at domain interfaces; update window/halo collectors
6721 for(MInt level = m_maxRfnmntLvl - 1; level >= m_maxUniformRefinementLevel; level--) {
6722 for(MInt cellId = 0; cellId < noCells; cellId++) {
6723 if(a_level(cellId) != level) continue;
6724 if(a_isToDelete(cellId)) continue;
6725 if(a_hasProperty(cellId, Cell::IsHalo)) continue;
6726
6727 // TODO labels:GRID,ADAPTATION,totest check for multisolver setting.. neighborIds solver aware!!
6728 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
6729 if(!m_tree.solver(cellId, solver)) continue;
6730 if(!a_hasChildren(cellId, solver)) continue;
6731
6732 // Partition level ancestor cell cannot be coarsened (exept if its a partition cell)
6733 if(a_hasProperty(cellId, Cell::IsPartLvlAncestor) && !a_hasProperty(cellId, Cell::IsPartitionCell)) {
6734 coarseFlag[cellId][solver] = false;
6735 }
6736
6737 if(coarseFlag[cellId][solver]) {
6738 if(!coarsenCheck(cellId, solver)) {
6739 coarseFlag[cellId][solver] = false;
6740 }
6741 }
6742 }
6743 }
6744
6745 if(noNeighborDomains() > 0) {
6746 // exchangeSolverBitset(coarseFlag.data());
6747 maia::mpi::exchangeBitset(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), coarseFlag.data(),
6748 m_tree.size());
6749 }
6750
6751 for(MInt cellId = 0; cellId < noCells; cellId++) {
6752 if(a_level(cellId) != level) continue;
6753 if(a_isToDelete(cellId)) continue;
6754 if(a_hasProperty(cellId, Cell::IsHalo)) continue;
6755
6756 // TODO labels:GRID,ADAPTATION,totest check for multisolver setting.. neighborIds solver aware!!
6757 if(level < m_maxRfnmntLvl - 1) {
6758 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
6759 if(!m_tree.solver(cellId, solver)) continue;
6760 if(a_hasChildren(cellId, solver)) continue;
6761 if(!refineFlag[cellId][solver] && !coarseFlag[cellId][solver]) {
6762 for(MInt dir = 0; dir < m_noDirs; dir++) {
6763 if(a_hasNeighbor(cellId, dir, solver) > 0) {
6764 MInt nghbrId = a_neighborId(cellId, dir, solver);
6765 if(m_azimuthalPer && a_hasProperty(nghbrId, Cell::IsPeriodic)) continue;
6766 if(a_hasChildren(nghbrId, solver) > 0) {
6767 for(MInt child = 0; child < m_maxNoChilds; child++) {
6768 if(!childCode[dir][child]) continue;
6769 MInt childId = a_childId(nghbrId, child, solver);
6770 if(childId < 0) continue;
6771 if(refineFlag[childId][solver]) {
6772 refineFlag[cellId][solver] = true;
6773 }
6774 }
6775 }
6776 }
6777 }
6778 }
6779 }
6780 }
6781 }
6782
6783 if(noNeighborDomains() > 0) {
6784 // exchangeSolverBitset(refineFlag.data());
6785 maia::mpi::exchangeBitset(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), refineFlag.data(),
6786 m_tree.size());
6787 }
6788
6789 for(MInt cellId = 0; cellId < noCells; cellId++) {
6790 if(a_level(cellId) != level) continue;
6791 if(a_isToDelete(cellId)) continue;
6792 if(m_azimuthalPer && a_hasProperty(cellId, Cell::IsPeriodic)) continue;
6793
6794 // Partition level ancestor cell cannot be coarsened (exept if its a partition cell)
6795 if(a_hasProperty(cellId, Cell::IsPartLvlAncestor) && !a_hasProperty(cellId, Cell::IsPartitionCell)) {
6796 continue;
6797 }
6798
6799 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
6800 if(!m_tree.solver(cellId, solver)) continue;
6801 if(coarseFlag[cellId][solver]) {
6802 for(MInt child = 0; child < m_maxNoChilds; child++) {
6803 MInt childId = a_childId(cellId, child, solver);
6804 if(childId > -1) {
6805 coarseFlag[childId][solver] = false;
6806 refineFlag[childId][solver] = false;
6807 }
6808 }
6809 if(a_hasChildren(cellId, solver)) {
6810 removeChildsSolver[solver](cellId);
6811 // call removeChilds() function in each solver before child links are deleted
6812 refineFlag[cellId][solver] = false;
6813 for(MInt child = 0; child < m_maxNoChilds; child++) {
6814 MInt childId = a_childId(cellId, child);
6815 if(childId > -1) {
6816 treeb().solver(childId, solver) = false;
6817 }
6818 }
6819 }
6820 }
6821 }
6822 // check if the child can also be removed from the cartesian/combined grid
6823 // if the child is no-longer in any of the solvers, the grid child will be removed!
6824 if(a_noChildren(cellId) > 0) {
6825 MBool rmChilds = true;
6826 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
6827 if(a_hasChildren(cellId, solver)) {
6828 rmChilds = false;
6829 }
6830 }
6831 if(rmChilds) {
6832 removeChilds(cellId);
6833 }
6834 }
6835 }
6836
6837 for(MInt i = 0; i < noNeighborDomains(); i++) {
6838 std::set<MInt> deleted;
6839 // WH_old
6840 if(m_haloMode > 0) {
6841 for(auto it_ = m_windowLayer_[i].begin(); it_ != m_windowLayer_[i].end();) {
6842 if(a_isToDelete(it_->first)) {
6843 it_ = m_windowLayer_[i].erase(it_);
6844 } else {
6845 // We might have deleted a cell from a solver
6846 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
6847 if(it_->second.get(solver) < WINDOWLAYER_MAX /*<=m_noSolverHaloLayers[solver]*/
6848 && !m_tree.solver(it_->first, solver))
6849 it_->second.set(solver, WINDOWLAYER_MAX);
6850 if(it_->second.get(solver) > m_noSolverHaloLayers[solver]) it_->second.set(solver, WINDOWLAYER_MAX);
6851 }
6852 if(it_->second.all()) {
6853 deleted.insert(it_->first);
6854 it_ = m_windowLayer_[i].erase(it_);
6855 } else
6856 ++it_;
6857 }
6858 }
6859 }
6860
6861 // 1. coarsen window cells
6862#ifndef NDEBUG
6863 std::set<MInt> windCellSet;
6864 std::set<MInt> haloCellSet;
6865#endif
6866
6867 m_windowCells[i].clear();
6868
6869 auto it = oldWindowCells[i].begin();
6870
6871 for(it = oldWindowCells[i].begin(); it < oldWindowCells[i].end(); it++) {
6872 const MInt cellId = *it;
6873 if(!a_isToDelete(cellId) && (deleted.find(cellId) == deleted.end() || m_haloMode == 0)) { // WH_old
6874 ASSERT(cellId < treeb().size(), "");
6875
6876#ifndef NDEBUG
6877 // TODO labels:GRID,ADAPTATION fix duplicate window cells in periodic cases!
6878 ASSERT(m_noPeriodicCartesianDirs > 0 || windCellSet.find(cellId) == windCellSet.end(),
6879 "duplicate window cell: " + std::to_string(cellId));
6880 windCellSet.insert(cellId);
6881#endif
6882
6883 m_windowCells[i].push_back(cellId);
6884 }
6885 }
6886
6887 // 2. coarsen halo cells
6888 vector<MInt> oldHaloVec(m_haloCells[i]);
6889 m_haloCells[i].clear();
6890 for(it = oldHaloVec.begin(); it < oldHaloVec.end(); it++) {
6891 const MInt cellId = *it;
6892 if(!a_isToDelete(cellId)) {
6893 ASSERT(cellId < treeb().size(), "");
6894
6895#ifndef NDEBUG
6896 ASSERT(haloCellSet.find(cellId) == haloCellSet.end(), "duplicate halo cell: " + std::to_string(cellId));
6897 haloCellSet.insert(cellId);
6898#endif
6899
6900 if(m_tree.solverBits(cellId).any() || m_haloMode == 0) m_haloCells[i].push_back(cellId);
6901 }
6902 }
6903 oldHaloVec.clear();
6904 }
6905 }
6906 //---------------------------(5)
6907
6908
6909 //----------------------------
6910 // 6. Remove all halo cells, for levels > maxUniformRefinementLevel
6911 // since the number of halo layers is inconsistent at this point
6912 // Note: preserve halo partition level ancestors of the missing grid subtree in case of a
6913 // partition level shift
6914
6915 {
6916 for(MInt level = maxLevel - 1; level >= m_maxUniformRefinementLevel; level--) {
6917 for(MInt cellId = 0; cellId < noCells; cellId++) {
6918 if(a_isToDelete(cellId)) continue;
6919 if(!a_hasProperty(cellId, Cell::IsHalo)) continue;
6920 if(a_level(cellId) == level) {
6921 if(a_noChildren(cellId) > 0) {
6922 // Preserve partition level ancestor halos (and their childs) if they are part of the
6923 // missing subtree of the grid in case of a partition level shift
6924 if(a_hasProperty(cellId, Cell::IsPartLvlAncestor) && !a_hasProperty(cellId, Cell::IsPartitionCell)) {
6925 if(std::find(m_partitionLevelAncestorIds.begin(), m_partitionLevelAncestorIds.end(), cellId)
6926 != m_partitionLevelAncestorIds.end()) {
6927 continue;
6928 }
6929 }
6930
6931 for(MInt child = 0; child < m_maxNoChilds; child++) {
6932 MInt childId = a_childId(cellId, child);
6933 if(childId < 0) continue;
6934 refineFlag[childId].reset();
6935 coarseFlag[childId].reset();
6936 }
6937 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
6938 if(!m_tree.solver(cellId, solver)) continue;
6939 if(a_hasChildren(cellId, solver)) {
6940 removeChildsSolver[solver](cellId);
6941 }
6942 }
6943 removeChilds(cellId);
6944 coarseFlag[cellId].reset();
6945 refineFlag[cellId].reset();
6946 }
6947 }
6948 }
6949 }
6950
6951 ScratchSpace<MBool> isWindowPartLvlAncestor_(tmpScratchSize, AT_, "isWindowPartLvLAncestor");
6952 MInt cnt = 0;
6953 for(MInt i = 0; i < noNeighborDomains(); i++) {
6954 isWindowPartLvlAncestor_.fill(false);
6955 // now take data from recvIsHaloPartLvlAncestor and store it for this neighborDomain
6956 if(hasPartLvlShift) {
6957 for(MInt j = 0; j < oldNoWindowCells[i]; j++) {
6958 const MInt cellId = oldWindowCells[i][j];
6959 isWindowPartLvlAncestor_(cellId) = recvIsHaloPartLvlAncestor[cnt];
6960 cnt++;
6961 }
6962 }
6963
6964 m_windowCells[i].clear();
6965
6966 auto it = oldWindowCells[i].begin();
6967 for(it = oldWindowCells[i].begin(); it < oldWindowCells[i].end(); it++) {
6968 const MInt cellId = *it;
6969
6970 // Add partition level ancestor window cells if required
6971 const MBool addWindow = (hasPartLvlShift) ? isWindowPartLvlAncestor_(cellId) : false;
6972
6973 if((a_level(cellId) > -1 && a_level(cellId) <= m_maxUniformRefinementLevel) || addWindow) {
6974 m_windowCells[i].push_back(cellId);
6975 // Set window cell flag for preserved window cells
6976 a_hasProperty(cellId, Cell::IsWindow) = true;
6977 }
6978 }
6979
6980 // WH_old
6981 if(m_haloMode > 0) {
6982 for(auto it_ = m_windowLayer_[i].begin(); it_ != m_windowLayer_[i].end();) {
6983 const MInt cellId = it_->first;
6984
6985 // Add partition level ancestor window cells if required
6986 const MBool addWindow = (hasPartLvlShift) ? isWindowPartLvlAncestor_(cellId) : false;
6987
6988 if((a_level(cellId) > -1 && a_level(cellId) <= m_maxUniformRefinementLevel) || addWindow)
6989 ++it_;
6990 else
6991 it_ = m_windowLayer_[i].erase(it_);
6992 }
6993 }
6994
6995 vector<MInt> oldHaloVec(m_haloCells[i]);
6996 m_haloCells[i].clear();
6997 for(it = oldHaloVec.begin(); it < oldHaloVec.end(); it++) {
6998 const MInt cellId = *it;
6999
7000 // Add partition level ancestor halo cells if required
7001 const MBool addHalo = (hasPartLvlShift) ? isHaloPartLvlAncestor[cellId] : false;
7002
7003 if((a_level(cellId) > -1 && a_level(cellId) <= m_maxUniformRefinementLevel) || addHalo) {
7004 ASSERT(cellId > -1 && cellId < treeb().size(), to_string(cellId) + " " + to_string(treeb().size()));
7005 m_haloCells[i].push_back(cellId);
7006 }
7007 }
7008 oldHaloVec.clear();
7009 }
7010
7011 if(m_azimuthalPer) {
7012 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
7013 vector<MInt> oldAzimuthalWindowVec(m_azimuthalWindowCells[i]);
7014
7015 m_azimuthalWindowCells[i].clear();
7016 auto it = oldAzimuthalWindowVec.begin();
7017 for(MInt j = 0; j < (signed)oldAzimuthalWindowVec.size(); j++) {
7018 // Remove higher level azimuthal window cells from collector
7019 // If an azimuthal halo cell on a higher level is mapped to an
7020 // azimuthal window cell on a lower level. This azimuthal window cell
7021 // musst also be removed from the collector
7022 const MInt cellId = oldAzimuthalWindowVec[j];
7023 MBool keepWindow = true;
7024 if(find(m_azimuthalHigherLevelConnectivity[i].begin(), m_azimuthalHigherLevelConnectivity[i].end(), j)
7025 != m_azimuthalHigherLevelConnectivity[i].end()) {
7026 keepWindow = false;
7027 }
7028 if(a_level(cellId) > -1 && a_level(cellId) <= m_maxUniformRefinementLevel && keepWindow) {
7029 m_azimuthalWindowCells[i].push_back(cellId);
7030 a_hasProperty(cellId, Cell::IsWindow) = true;
7031 }
7032 }
7033 oldAzimuthalWindowVec.clear();
7034
7035 vector<MInt> oldAzimuthalHaloVec(m_azimuthalHaloCells[i]);
7036 m_azimuthalHaloCells[i].clear();
7037 for(it = oldAzimuthalHaloVec.begin(); it < oldAzimuthalHaloVec.end(); it++) {
7038 const MInt cellId = *it;
7039 if(a_level(cellId) > -1 && a_level(cellId) <= m_maxUniformRefinementLevel) {
7040 ASSERT(cellId > -1 && cellId < treeb().size(), "");
7041 m_azimuthalHaloCells[i].push_back(cellId);
7042 }
7043 }
7044 oldAzimuthalHaloVec.clear();
7045 }
7046
7047 // Remove higher level unmapped azimuthal halo cells
7048 vector<MInt> oldUnmappedHaloVec(m_azimuthalUnmappedHaloCells);
7049 vector<MInt> oldUnmappedHaloDomVec(m_azimuthalUnmappedHaloDomains);
7050 m_azimuthalUnmappedHaloCells.clear();
7051 m_azimuthalUnmappedHaloDomains.clear();
7052 for(MUint i = 0; i < oldUnmappedHaloVec.size(); i++) {
7053 MInt cellId = oldUnmappedHaloVec[i];
7054 if(a_level(cellId) > -1 && a_level(cellId) <= m_maxUniformRefinementLevel) {
7055 ASSERT(cellId > -1 && cellId < treeb().size(), "");
7056 m_azimuthalUnmappedHaloCells.push_back(cellId);
7057 m_azimuthalUnmappedHaloDomains.push_back(oldUnmappedHaloDomVec[i]);
7058 }
7059 }
7060 oldUnmappedHaloVec.clear();
7061 oldUnmappedHaloDomVec.clear();
7062
7063 // Update gridBndryCells
7064 vector<MInt> oldGridBndryVec(m_gridBndryCells);
7065 m_gridBndryCells.clear();
7066 for(MUint i = 0; i < oldGridBndryVec.size(); i++) {
7067 MInt cellId = oldGridBndryVec[i];
7068 if(a_level(cellId) > -1 && a_level(cellId) <= m_maxUniformRefinementLevel) {
7069 m_gridBndryCells.push_back(cellId);
7070 }
7071 }
7072 oldGridBndryVec.clear();
7073 }
7074 }
7075
7076 //---------------------------(6)
7077 //----------------------------
7078 // 7. Refinement step: loop from minLevel to maxLevel and exchange refineFlags to retrieve
7079 // a consistent refinement at domain interfaces; update window/halo connectivity at each level
7080 for(MInt level = m_maxUniformRefinementLevel; level < maxLevel; level++) {
7081 for(MInt cellId = 0; cellId < noCells; cellId++) {
7082 if(a_level(cellId) != level) continue;
7083 if(a_isToDelete(cellId)) continue;
7084 if(a_hasProperty(cellId, Cell::IsHalo)) continue;
7085
7086 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
7087 if(!m_tree.solver(cellId, solver)) continue;
7088 if(a_hasChildren(cellId, solver)) continue;
7089 if(refineFlag[cellId][solver]) {
7090 if(!refineCheck(cellId, solver, cellOutsideSolver)) {
7091 // comment out for stl interface refinement, causes weird remaining cells
7092 refineFlag[cellId][solver] = 0;
7093 }
7094 }
7095 }
7096
7097 if(refineFlag[cellId].any()) {
7098 refineCell(cellId, nullptr, false, cellOutsideSolver, refineFlag[cellId]);
7099
7100 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
7101 if(!a_hasChildren(cellId, solver) && false) { // sohel false
7102 // this is somehow destroying multisolver cases
7103 // if one solver already has a cell the other solver doesnt get it even if it needs it..
7104 // i think the solver flag is not set correctly in the refineCell function of cartesiangrid
7105 refineFlag[cellId][solver] = false;
7106 }
7107 if(refineFlag[cellId][solver]) {
7108 // Set solver flag for child cells
7109 setChildSolverFlag(cellId, solver, cellOutsideSolver[solver]);
7110 refineCellSolver[solver](cellId); // call refineCell() function in each solver
7111 }
7112 }
7113 for(MInt child = 0; child < m_maxNoChilds; child++) {
7114 MInt childId = a_childId(cellId, child);
7115 if(childId > -1 && a_hasProperty(childId, Cell::WasNewlyCreated)) {
7116 // Timw: only reset the child-flags if the cell was neawly created
7117 // otherwise this stops a refinement of the childs of a different solver!
7118 coarseFlag[childId].reset();
7119 refineFlag[childId].reset();
7120 }
7121 }
7122 }
7123 }
7124
7125 setLevel();
7126
7127 computeGlobalIds();
7128
7129 // WH_old
7130 if(m_haloMode > 0)
7131 // noOffspring required in createHigherLevelExchangeCells
7132 calculateNoOffspringsAndWorkload(static_cast<Collector<void>*>(nullptr), m_tree.size());
7133
7134 // Update child ids of partition level ancestors (stored as global ids)
7135 m_partitionLevelAncestorChildIds.clear();
7136 const MInt noPartLvlAncestorIds = m_partitionLevelAncestorIds.size();
7137 for(MInt i = 0; i < noPartLvlAncestorIds; i++) {
7138 const MInt cellId = m_partitionLevelAncestorIds[i];
7139
7140 for(MInt child = 0; child < m_maxNoChilds; child++) {
7141 const MInt childId = a_childId(cellId, child);
7142 const MLong childGlobalId = (childId > -1) ? a_globalId(childId) : -1;
7143 m_partitionLevelAncestorChildIds.push_back(childGlobalId);
7144 }
7145 }
7146
7147 if(noDomains() > 1 || m_noPeriodicCartesianDirs > 0) {
7148 updateHaloCellCollectors();
7149
7150 // WH_old
7151 if(m_haloMode > 0) {
7152 createHigherLevelExchangeCells(level, true, refineCellSolver, removeCellSolver, level == maxLevel - 1);
7153 // createHigherLevelExchangeCells(level, swapCellsSolver, refineCellSolver);
7154 } else
7155 createHigherLevelExchangeCells_old(level, refineCellSolver);
7156 }
7157
7158 for(MInt i = 0; i < noNeighborDomains(); i++) {
7159 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
7160 MInt cellId = m_haloCells[i][j];
7161 if(a_level(cellId) != level) continue;
7162 for(MInt child = 0; child < m_maxNoChilds; child++) {
7163 const MInt childId = a_childId(cellId, child);
7164 if(childId < 0) continue;
7165
7166 // Partition level shift: do not reset flags if the child of a halo cell is an internal
7167 // cell and already existed!
7168 if(!a_isHalo(childId)) {
7169 ASSERT(a_hasProperty(childId, Cell::IsPartLvlAncestor) || a_hasProperty(childId, Cell::IsPartitionCell),
7170 "child is not a partition level ancestor or a partition cell");
7171 a_hasProperty(cellId, Cell::WasRefined) = true;
7172 } else {
7173 coarseFlag[childId].reset();
7174 refineFlag[childId].reset();
7175 a_hasProperty(childId, Cell::WasNewlyCreated) = true;
7176 a_hasProperty(cellId, Cell::WasRefined) = true;
7177 }
7178 }
7179 }
7180 }
7181
7182 if(m_azimuthalPer) {
7183 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
7184 for(MInt j = 0; j < (signed)m_azimuthalHaloCells[i].size(); j++) {
7185 MInt cellId = m_azimuthalHaloCells[i][j];
7186 if(a_level(cellId) != level) continue;
7187 for(MInt child = 0; child < m_maxNoChilds; child++) {
7188 const MInt childId = a_childId(cellId, child);
7189 if(childId < 0) continue;
7190 if(!a_isHalo(childId)) {
7191 mTerm(1, AT_, "");
7192 } else {
7193 coarseFlag[childId].reset();
7194 refineFlag[childId].reset();
7195 a_hasProperty(childId, Cell::WasNewlyCreated) = true;
7196 a_hasProperty(cellId, Cell::WasRefined) = true;
7197 }
7198 }
7199 }
7200 }
7201 }
7202 }
7203
7204 //---------------------------(7)
7205
7206 //----------------------------
7207 // 8. finalize; synchonize window/halo cell order
7208 setLevel();
7209 computeGlobalIds();
7210 for(auto& rgm : resizeGridMapSolver) {
7211 rgm(); // make sure grid2solver is large enough
7212 }
7213 compactCells(swapCellsSolver);
7214 // Update after cells have been shifted (min-level cells, globalToLocalId, partitionCellIds, etc)
7215 computeGlobalIds();
7216 for(auto& rgm : resizeGridMapSolver) {
7217 rgm(); // update grid2solver after cell swapping
7218 }
7219 if(noDomains() > 1 || m_noPeriodicCartesianDirs > 0) {
7220 updateHaloCellCollectors();
7221 }
7222 computeLeafLevel();
7223 //---------------------------(8)
7224
7225
7226 //----------------------------
7227 // 9. finalize
7228 if(noNeighborDomains() > 0 || noAzimuthalNeighborDomains() > 0) {
7229 exchangeProperties();
7230 }
7231
7232 // Update local bounding box
7233 computeLocalBoundingBox(&m_localBoundingBox[0]);
7234 //---------------------------(9)
7235
7236 if(treeb().noSolvers() > 1 && noNeighborDomains() > 0) { // Exchange solver info
7237 // WH_old
7238 if(m_haloMode > 0) {
7239 exchangeSolverBitset(&m_tree.solverBits(0));
7240 } else
7241 maia::mpi::exchangeBitset(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), &m_tree.solverBits(0),
7242 m_tree.size());
7243 }
7244
7245 if(m_azimuthalPer && noAzimuthalNeighborDomains() > 0) {
7246 maia::mpi::exchangeBitset(m_azimuthalNghbrDomains, m_azimuthalHaloCells, m_azimuthalWindowCells, mpiComm(),
7247 &m_tree.solverBits(0), m_tree.size());
7248 // To ensure that azimuthal halo cells are fully refined (have all children)
7249 // or are not refined at all, solver Bits need to be checked!
7250 correctAzimuthalSolverBits();
7251 }
7252
7253
7254 // TODO labels:GRID,ADAPTATION update noOffspring? not updated during adaptation --> noOffspring required by
7255 // createHigherLevelExchangeCells, so added above
7256 // calculateNoOffspringsAndWorkload(static_cast<Collector<void>*>(nullptr), m_tree.size());
7257
7258 /*
7259 for ( MInt solver = 0; solver < treeb().noSolvers(); solver++ ){
7260 MInt debugHalo=0;
7261 MInt debugWindow=0;
7262 for ( MInt i = 0; i < noNeighborDomains(); i++ ) {
7263 for ( MInt j = 0; j < (signed)m_haloCells[i].size(); j++ ) {
7264 MInt id=m_haloCells[i][j];
7265 if(m_tree.solver( id, solver )){
7266 debugHalo++;
7267 }
7268 }
7269 for ( MInt j = 0; j < (signed)m_windowCells[i].size(); j++ ) {
7270 MInt id=m_windowCells[i][j];
7271 if(m_tree.solver( id, solver )){
7272 debugWindow++;
7273 }
7274 }
7275
7276 cerr << " neighbor: " << i << "--------------" << endl;
7277 cerr <<"count of halos for solver: " << solver << " : " << debugHalo << endl;
7278 cerr <<"count of windows for solver: " << solver << " : " << debugWindow << endl;
7279 cerr <<"total size halo: " << m_haloCells[i].size() << " window: " << m_windowCells[i].size() << endl;
7280 }
7281 }*/
7282
7283 // Update the partition cells when writing the next restart file (in case this is not previously
7284 // done via updatePartitionCells() during balancing)
7285 // TODO labels:GRID,DLB,ADAPTATION partition files written at final time step might not be usable since partition
7286 // cells are updated during new grid output
7287 m_updatedPartitionCells = false;
7288
7289#ifdef MAIA_GRID_SANITY_CHECKS
7290 gridSanityChecks();
7291 checkWindowHaloConsistency(true);
7292 if(m_haloMode > 0) // WH_old
7293 checkWindowLayer("meshAdaptation completed: ");
7294#endif
7295
7296 // Set adaptation status
7297 m_wasAdapted = true;
7298
7299 // cout << "dom: " << domainId() << " noNeighborDomains: " << noNeighborDomains() << " windowCell: " <<
7300 // windowCell_pre_size << " noWindowCells: " << noWindowCells_pre_size<< " isWindowPartLvlAncestor: " <<
7301 // isWindowPartLvlAncestor_size << endl;
7302}
7303
7304
7315template <MInt nDim>
7317 vector<vector<MFloat>>& sensors, vector<MFloat>& sensorWeight, vector<bitset<64>>& sensorCellFlag,
7318 vector<MInt>& sensorSolverId, const std::vector<std::function<void(const MInt)>>& refineCellSolver,
7319 const std::vector<std::function<void(const MInt)>>& removeChildsSolver,
7320 const std::vector<std::function<void(const MInt)>>& removeCellSolver,
7321 const std::vector<std::function<void(const MInt, const MInt)>>& swapCellsSolver,
7322 const std::vector<std::function<MInt(const MFloat*, const MInt, const MInt)>>& cellOutsideSolver,
7323 const std::vector<std::function<void()>>& resizeGridMapSolver) {
7324 TRACE();
7325
7326 //----------------------------
7327 // 0. init
7328 const MInt noCells = treeb().size();
7329 const MInt maxNoCells = m_maxNoCells;
7330 const MInt maxLevel = mMin(m_maxRfnmntLvl, m_maxLevel + 1);
7331
7332 const MInt noSensors = (signed)sensorWeight.size();
7333 ASSERT(sensorWeight.size() == sensors.size(), "");
7334 ASSERT(sensorWeight.size() == sensorSolverId.size(), "");
7335 ScratchSpace<MFloat> sensorCnt(noSensors, 2, AT_, "sensorCnt");
7336 ScratchSpace<MFloat> sensorThresholds(noSensors, 2, AT_, "sensorThresholds");
7337 ScratchSpace<bitset<maia::grid::tree::Tree<nDim>::maxNoSolvers()>> refineFlag(maxNoCells, AT_, "refineFlag");
7338 ScratchSpace<bitset<maia::grid::tree::Tree<nDim>::maxNoSolvers()>> coarseFlag(maxNoCells, AT_, "coarseFlag");
7339 ASSERT(m_freeIndices.empty(), "");
7340 std::set<MInt>().swap(m_freeIndices);
7341 sensorCnt.fill(F0);
7342 for(MInt c = 0; c < maxNoCells; c++) {
7343 refineFlag[c].reset();
7344 coarseFlag[c].reset();
7345 }
7346 // these flags indicate which cells have changed for the subsequent reinitialization by the solvers
7347 for(MInt cellId = 0; cellId < noCells; cellId++) {
7348 a_hasProperty(cellId, Cell::IsToDelete) = false;
7349 a_hasProperty(cellId, Cell::WasNewlyCreated) = false;
7350 a_hasProperty(cellId, Cell::WasCoarsened) = false;
7351 a_hasProperty(cellId, Cell::WasRefined) = false;
7352
7353 // Reset the window cell flag,
7354 // is set for all preserved window cells when added to m_windowCells
7355 a_hasProperty(cellId, Cell::IsWindow) = false;
7356 }
7357
7358 //----------------------------
7359 // 1. exchange sensors and compute RMS values
7360 // TODO labels:GRID move these parts to the cartesian solver and do this sort of smoothing
7361 // on the solver grid in setSensors, after all sensors have been set there!!!!
7362 // the cartesiangrid should only get one refine/coarse flags for each cell from the solver!
7363 for(MInt cellId = 0; cellId < noCells; cellId++) {
7364 if(a_hasProperty(cellId, Cell::IsHalo)) continue;
7365 if(a_isToDelete(cellId)) continue;
7366
7367 // Note: sensors for partition level ancestors might not be set correctly!
7368 if(a_hasProperty(cellId, Cell::IsPartLvlAncestor)) {
7369 continue;
7370 }
7371
7372 for(MInt s = 0; s < noSensors; s++) {
7373 if(sensorCellFlag[cellId][s]) {
7374 ASSERT(sensorWeight[s] < F0 || std::fabs(sensors[s][cellId]) < MFloatMaxSqrt,
7375 "invalid sensor value: " + std::to_string(sensors[s][cellId]));
7376 sensorCnt(s, 0) += POW2(sensors[s][cellId]);
7377 sensorCnt(s, 1) += F1;
7378 }
7379 }
7380 }
7381
7382 if(noSensors > 0) {
7383 MPI_Allreduce(MPI_IN_PLACE, &sensorCnt(0), 2 * noSensors, MPI_DOUBLE, MPI_SUM, mpiComm(), AT_, "MPI_IN_PLACE",
7384 "sensorCnt(0)");
7385 }
7386
7387 // TODO labels:GRID,ADAPTATION introduce sensor type to replace this weight hack
7388 for(MInt s = 0; s < noSensors; s++) {
7389 if(sensorWeight[s] < F0) { // true/false sensor
7390 sensorThresholds(s, 0) = -1e-12;
7391 sensorThresholds(s, 1) = 1e-12;
7392 } else { // continuous sensor
7393 sensorThresholds(s, 1) = sensorWeight[s] * sqrt(mMax(1e-14, (sensorCnt(s, 0) / sensorCnt(s, 1))));
7394 sensorThresholds(s, 0) = m_coarseRatio * sensorThresholds(s, 1);
7395 }
7396 }
7397
7398 //---------------------------(1)
7399
7400 const MBool hasPartLvlShift = (m_maxPartitionLevelShift > 0);
7401 const MInt tmpScratchSize = (hasPartLvlShift) ? m_tree.size() : 1;
7402
7403 ScratchSpace<MBool> isHaloPartLvlAncestor(tmpScratchSize, AT_, "isHaloPartLvlAncestor");
7404 // TODO labels:GRID,ADAPTATION make this work using less memory, e.g., use one bit for each neighbor?
7405 ScratchSpace<MBool> isWindowPartLvlAncestor(tmpScratchSize, noNeighborDomains(), AT_, "isWindowPartLvlAncestor");
7406
7407 isHaloPartLvlAncestor.fill(false);
7408 isWindowPartLvlAncestor.fill(false);
7409
7410 // Determine relevant halo partition level ancestor window/halo cells that need to be preserved
7411 if(m_maxPartitionLevelShift > 0) {
7412 MInt totalNoWindowCells = 0;
7413 for(MInt d = 0; d < noNeighborDomains(); d++) {
7414 totalNoWindowCells += noWindowCells(d);
7415 }
7416 ScratchSpace<MBool> recvIsHaloPartLvlAncestor(std::max(totalNoWindowCells, 1), AT_, "recvIsHaloPartLvlAncestor");
7417
7418 for(auto& i : m_partitionLevelAncestorIds) {
7419 TERMM_IF_NOT_COND(a_hasProperty(i, Cell::IsPartLvlAncestor),
7420 "cell is not a partition level ancestor: " + std::to_string(i));
7421 // Mark halo partition level ancestors (which are part of the missing subtree of the grid)
7422 if(a_hasProperty(i, Cell::IsHalo)) {
7423 isHaloPartLvlAncestor[i] = true;
7424 }
7425 // Mark all halo childs of partition level ancestors (required for exchange of e.g. global
7426 // ids!)
7427 for(MInt child = 0; child < m_maxNoChilds; child++) {
7428 const MInt childId = a_childId(i, child);
7429 if(childId > -1 && a_isHalo(childId)) {
7430 isHaloPartLvlAncestor[childId] = true;
7431 }
7432 }
7433 }
7434
7435 // Reverse exchange markers from halo cells to corresponding window cells
7436 if(noNeighborDomains() > 0) {
7437#ifndef PVPLUGIN
7438 maia::mpi::reverseExchangeData(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), &isHaloPartLvlAncestor[0],
7439 &recvIsHaloPartLvlAncestor[0]);
7440#else
7441 TERMM(1, "not working for compilation of paraview plugin");
7442#endif
7443 }
7444
7445 MInt cnt = 0;
7446 for(MInt d = 0; d < noNeighborDomains(); d++) {
7447 for(MInt j = 0; j < noWindowCells(d); j++) {
7448 const MInt cellId = windowCell(d, j);
7449 // Store marker for window cell separately for each neighbor domain!
7450 isWindowPartLvlAncestor(cellId, d) = recvIsHaloPartLvlAncestor[cnt];
7451 cnt++;
7452 }
7453 }
7454 }
7455
7456 //----------------------------
7457 // 2. tag cells for coarsening / refinement
7458
7459 // init parent cells with coarse flag true and set false if any child intervenes
7460 for(MInt cellId = 0; cellId < m_noInternalCells; cellId++) {
7461 ASSERT(!a_hasProperty(cellId, Cell::IsHalo), "");
7462 ASSERT(!a_isToDelete(cellId), "");
7463 if(a_level(cellId) > m_maxUniformRefinementLevel) {
7464 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
7465 if(!m_tree.solver(cellId, solver)) continue;
7466 if(a_hasChildren(cellId, solver)) continue;
7467
7468 const MInt parentId = a_parentId(cellId, solver);
7469 ASSERT(parentId > -1, "");
7470 // Partition level shift, parent is a halo cell and cannot be coarsened
7471 if(a_isHalo(parentId) || a_hasProperty(parentId, Cell::IsPartLvlAncestor)) continue;
7472
7473 for(MInt s = 0; s < noSensors; s++) {
7474 if(sensorSolverId[s] != solver) continue;
7475 if(sensorCellFlag[cellId][s] || sensorCellFlag[parentId][s]) {
7476 // only coarsen cells if they are above the newMinLevel!
7477 if(a_level(cellId) > m_newMinLevel && m_allowCoarsening) {
7478 coarseFlag[a_parentId(cellId)][solver] = true;
7479 }
7480 }
7481 }
7482 }
7483 }
7484 }
7485
7486 for(MInt cellId = 0; cellId < m_noInternalCells; cellId++) {
7487 ASSERT(!a_hasProperty(cellId, Cell::IsHalo), "");
7488 ASSERT(!a_isToDelete(cellId), "");
7489 const MInt level = a_level(cellId);
7490 //---
7491 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
7492 if(!m_tree.solver(cellId, solver)) continue;
7493 if(a_hasChildren(cellId, solver)) continue;
7494 const MInt parentId = a_parentId(cellId, solver);
7495 // Partition level shift, if parent is a halo it cannot be coarsened and e.g.
7496 // sensors/coarseFlag[parentId] should not be accessed!
7497 const MBool partLvlAncestorParent = (parentId > -1) ? a_hasProperty(parentId, Cell::IsPartLvlAncestor) : false;
7498
7499 // cell should be marked for refinement
7500 MBool refine = false;
7501 // cell should be marked for coarsened
7502 MBool coarsen = true;
7503 // cell should be marked for refinement regardless
7504 // whether a different sensor marks this cell for coarsening
7505 MBool forceRefine = false;
7506 for(MInt s = 0; s < noSensors; s++) {
7507 if(sensorSolverId[s] != solver) continue;
7508 if(sensorCellFlag[cellId][s]) {
7509 coarsen = coarsen && (sensors[s][cellId] < sensorThresholds(s, 0));
7510 }
7511 if(level > m_maxUniformRefinementLevel && !partLvlAncestorParent && sensorCellFlag[parentId][s]) {
7512 coarsen = coarsen && (sensors[s][parentId] < sensorThresholds(s, 0));
7513 }
7514 // this sensor would refine the cell
7515 const MBool refineSensor = sensors[s][cellId] > sensorThresholds(s, 1) && sensorCellFlag[cellId][s];
7516 refine = refine || refineSensor;
7517
7518 // a true/false sensor marks the cell for refinement
7519 if(refineSensor && sensorWeight[s] < F0) {
7520 forceRefine = true;
7521 }
7522 }
7523 if(level < m_maxRfnmntLvl) {
7524 refineFlag[cellId][solver] = refine;
7525 }
7526
7527 if(level > m_maxUniformRefinementLevel && !partLvlAncestorParent) {
7528 coarseFlag[parentId][solver] = coarseFlag[parentId][solver] && coarsen;
7529 }
7530 // force a refinement of a cell:
7531 // for all minLevel cells which should be raised to the newMinLevel
7532 // for the cells forced by any true/false sensor!
7533 if((forceRefine && level < m_maxRfnmntLvl) || a_level(cellId) < m_newMinLevel) {
7534 refineFlag[cellId][solver] = true;
7535 coarseFlag[cellId][solver] = false;
7536 if(level > m_maxUniformRefinementLevel) {
7537 coarseFlag[parentId][solver] = false;
7538 }
7539 }
7540 }
7541 }
7542
7543 // 3. Exchange since consistency checks need neighbor values
7544 if(noNeighborDomains() > 0) {
7545 maia::mpi::exchangeBitset(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), coarseFlag.getPointer(),
7546 m_tree.size());
7547 maia::mpi::exchangeBitset(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), refineFlag.getPointer(),
7548 m_tree.size());
7549 }
7550
7551 //----------------------------
7552 // 4. consistency check for internal cells
7553 if(m_checkRefinementHoles != nullptr) {
7554 // necessary for solution adaptive refinement (i.e. at shocks)
7555 const MInt adaptationHoleLimit = nDim;
7556 MBool gridUpdated = true;
7557
7558 MInt loopCount = 0;
7559 while(gridUpdated && loopCount < 25) {
7560 MInt noChanges = 0;
7561 loopCount++;
7562 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
7563 if(!m_checkRefinementHoles[solver]) continue;
7564 for(MInt cellId = 0; cellId < noCells; cellId++) {
7565 if(a_isToDelete(cellId)) continue;
7566 if(a_hasProperty(cellId, Cell::IsHalo)) continue;
7567 if(!m_tree.solver(cellId, solver)) continue;
7568 if(a_hasChildren(cellId, solver)) continue;
7569
7570 // check, if current cell is a hole in the refinement => refine cell aswell
7571 if(!refineFlag[cellId][solver]
7572 || (a_parentId(cellId, solver) > -1 && coarseFlag[a_parentId(cellId, solver)][solver])) {
7573 MInt checkRefinedNeighbors = 0;
7574 MInt currentHoleLimit = adaptationHoleLimit;
7575 for(MInt dir = 0; dir < m_noDirs; dir++) {
7576 if(a_hasNeighbor(cellId, dir, solver) > 0) {
7577 const MInt nghbrId = a_neighborId(cellId, dir, solver);
7578 if(refineFlag[nghbrId][solver] || a_hasChildren(nghbrId, solver)) {
7579 checkRefinedNeighbors += 1;
7580 }
7581 } else {
7582 // Lower limit at boundaries
7583 currentHoleLimit--;
7584 }
7585 }
7586 if(checkRefinedNeighbors > adaptationHoleLimit) {
7587 refineFlag[cellId][solver] = true;
7588 coarseFlag[cellId][solver] = false;
7589 const MInt parentId = a_parentId(cellId, solver);
7590 if(parentId > -1) coarseFlag[parentId][solver] = false;
7591 noChanges += 1;
7592 }
7593 }
7594
7595 // check, if current cell coarsening would create a hole in the refinement => do not coarsen cell
7596 const MInt parentId = a_parentId(cellId, solver);
7597 if(parentId < 0) continue;
7598 if(coarseFlag[parentId][solver]) {
7599 ASSERT(!a_isHalo(parentId) && !a_hasProperty(parentId, Cell::IsPartLvlAncestor),
7600 "Partition level ancestor parent cell has coarseFlag set.");
7601 MInt checkRefinedNeighbors = 0;
7602 MInt currentHoleLimit = adaptationHoleLimit;
7603 for(MInt dir = 0; dir < m_noDirs; dir++) {
7604 if(a_hasNeighbor(parentId, dir, solver) > 0) {
7605 const MInt parNghbrId = a_neighborId(parentId, dir, solver);
7606 if(!coarseFlag[parNghbrId][solver]
7607 && (refineFlag[parNghbrId][solver] || a_hasChildren(parNghbrId, solver))) {
7608 checkRefinedNeighbors += 1;
7609 }
7610 } else {
7611 // Lower limit at boundaries
7612 currentHoleLimit--;
7613 }
7614 }
7615 if(checkRefinedNeighbors > adaptationHoleLimit) {
7616 coarseFlag[parentId][solver] = false;
7617 noChanges += 1;
7618 }
7619 }
7620
7621 // Check if the coarsening of neighboring cells would create a refined cell island => coarsen cell aswell
7622 if(!coarseFlag[parentId][solver]) {
7623 ASSERT(!a_isHalo(parentId) && !a_hasProperty(parentId, Cell::IsPartLvlAncestor),
7624 "Partition level ancestor parent cell has coarseFlag set.");
7625 MInt checkCoarseNeighbors = 0;
7626 MInt currentHoleLimit = adaptationHoleLimit;
7627 for(MInt dir = 0; dir < m_noDirs; dir++) {
7628 if(a_hasNeighbor(parentId, dir, solver) > 0) {
7629 const MInt parNghbrId = a_neighborId(parentId, dir, solver);
7630 if(coarseFlag[parNghbrId][solver]
7631 || (!refineFlag[parNghbrId][solver] && !a_hasChildren(parNghbrId, solver))) {
7632 checkCoarseNeighbors += 1;
7633 }
7634 } else {
7635 // Lower limit at boundaries
7636 currentHoleLimit--;
7637 }
7638 }
7639 if(checkCoarseNeighbors > currentHoleLimit) {
7640 coarseFlag[parentId][solver] = true;
7641 noChanges += 1;
7642 }
7643 }
7644
7645 // check if the current cell refinement creates a refined cell island => do not refine cell
7646 if(refineFlag[cellId][solver]) {
7647 MInt checkRefinedNeighbors = 0;
7648 for(MInt dir = 0; dir < m_noDirs; dir++) {
7649 if(a_hasNeighbor(cellId, dir, solver) > 0) {
7650 const MInt nghbrId = a_neighborId(cellId, dir, solver);
7651 if(refineFlag[nghbrId][solver] || a_hasChildren(nghbrId, solver)) {
7652 checkRefinedNeighbors += 1;
7653 }
7654 }
7655 }
7656 if(checkRefinedNeighbors == 0) {
7657 refineFlag[cellId][solver] = false;
7658 noChanges += 1;
7659 }
7660 }
7661 }
7662 }
7663 if(noChanges == 0) {
7664 gridUpdated = false;
7665 }
7666 }
7667 }
7668
7669 // smooth diagonal level jumps,
7670 // even if already existing in the base grid!
7671 if(m_diagSmoothing != nullptr) {
7672 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
7673 if(!m_diagSmoothing[solver]) continue;
7674 for(MInt lvl = maxLevel - 1; lvl >= minLevel(); lvl--) {
7675 for(MInt cellId = 0; cellId < noCells; cellId++) {
7676 if(a_level(cellId) != lvl) continue;
7677 if(!a_isLeafCell(cellId)) continue;
7678 if(!m_tree.solver(cellId, solver)) continue;
7679 if(refineFlag[cellId][solver]) continue;
7680 for(MInt i = 0; i < m_noDirs; i++) {
7681 const MInt nghbrId = a_neighborId(cellId, i, solver);
7682 if(nghbrId < 0) continue;
7683 if(a_isLeafCell(nghbrId)) continue;
7684 for(MInt child = 0; child < m_maxNoChilds; child++) {
7685 MInt childId = a_childId(nghbrId, child, solver);
7686 if(childId < 0) continue;
7687 if(!a_isLeafCell(childId)) {
7688 refineFlag[cellId][solver] = true;
7689 coarseFlag[cellId][solver] = false;
7690 MInt parentId = a_parentId(cellId, solver);
7691 if(parentId > -1) {
7692 coarseFlag[cellId][solver] = false;
7693 }
7694 break;
7695 }
7696 }
7697 for(MInt j = 0; j < m_noDirs; j++) {
7698 if(j / 2 == i / 2) continue;
7699 const MInt diagNghbrId = a_neighborId(nghbrId, j, solver);
7700 if(diagNghbrId < 0) continue;
7701 if(a_isLeafCell(diagNghbrId)) continue;
7702 for(MInt child = 0; child < m_maxNoChilds; child++) {
7703 MInt childId = a_childId(diagNghbrId, child, solver);
7704 if(childId < 0) continue;
7705 if(!a_isLeafCell(childId)) {
7706 refineFlag[cellId][solver] = true;
7707 coarseFlag[cellId][solver] = false;
7708 MInt parentId = a_parentId(cellId, solver);
7709 if(parentId > -1) {
7710 coarseFlag[cellId][solver] = false;
7711 }
7712 break;
7713 }
7714 }
7715 }
7716 }
7717 }
7718 }
7719 }
7720 }
7721
7722 // Additional consistency checks fixing contradictory flags
7723 for(MInt cellId = 0; cellId < noCells; cellId++) {
7724 if(a_hasProperty(cellId, Cell::IsHalo)) continue;
7725 if(a_isToDelete(cellId)) continue;
7726
7727 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
7728 if(!m_tree.solver(cellId, solver)) continue;
7729 if(a_hasChildren(cellId, solver)) continue;
7730 const MInt parentId = a_parentId(cellId, solver);
7731 if(refineFlag[cellId][solver] && coarseFlag[cellId][solver]) {
7732 refineFlag[cellId][solver] = false;
7733 coarseFlag[cellId][solver] = false;
7734 }
7735
7736 // TODO labels:GRID,ADAPTATION,totest check for multisolver!!!!!
7737 if(refineFlag[cellId][solver]) {
7738 if(parentId > -1) {
7739 if(coarseFlag[parentId][solver]) {
7740 ASSERT(!a_isHalo(parentId) && !a_hasProperty(parentId, Cell::IsPartLvlAncestor),
7741 "Partition level ancestor parent cell has coarseFlag set.");
7742 coarseFlag[parentId][solver] = false;
7743 for(MInt child = 0; child < m_maxNoChilds; child++) {
7744 MInt childId = a_childId(parentId, child, solver);
7745 if(childId > -1) {
7746 refineFlag[childId][solver] = false;
7747 }
7748 }
7749 }
7750 }
7751 }
7752 }
7753 }
7754 //---------------------------(4)
7755
7756 // ensure the same refinement between two different solvers in the combined/overlapping region!
7757 if(m_noIdenticalSolvers > 0) {
7758 for(MInt cellId = 0; cellId < m_noInternalCells; cellId++) {
7759 for(MInt pair = 0; pair < m_noIdenticalSolvers; pair++) {
7760 const MInt slaveId = m_identicalSolvers[pair * 2];
7761 const MInt masterId = m_identicalSolvers[pair * 2 + 1];
7762 if(treeb().solver(cellId, slaveId) && treeb().solver(cellId, masterId)) {
7763 if(!coarseFlag[cellId][masterId]) {
7764 coarseFlag[cellId][slaveId] = coarseFlag[cellId][masterId];
7765 }
7766 if(m_identicalSolverLvlJumps[pair] == 0) {
7767 refineFlag[cellId][slaveId] = refineFlag[cellId][masterId];
7768 } else if(m_identicalSolverLvlJumps[pair] == 1) {
7769 if(a_level(cellId) < m_identicalSolverMaxLvl[pair] && a_hasChildren(cellId, masterId)) {
7770 if(!a_hasChildren(cellId, slaveId)) {
7771 refineFlag[cellId][slaveId] = true;
7772 }
7773 coarseFlag[cellId][slaveId] = false;
7774 }
7775 } else {
7776 if(a_level(cellId) < m_identicalSolverMaxLvl[pair] && a_hasChildren(cellId, masterId)
7777 && a_childId(cellId, 0) > -1 && treeb().solver(a_childId(cellId, 0), masterId)
7778 && a_hasChildren(a_childId(cellId, 0), masterId)) {
7779 if(!a_hasChildren(cellId, slaveId)) {
7780 refineFlag[cellId][slaveId] = true;
7781 }
7782 coarseFlag[cellId][slaveId] = false;
7783 }
7784 }
7785 }
7786 }
7787 }
7788 }
7789
7790
7791 //----------------------------
7792 // 5. Coarsening step: loop from maxLevel to minLevel and exchange coarseFlags to retrieve
7793 // consistent refinement at domain interfaces; update window/halo collectors
7794 for(MInt level = m_maxRfnmntLvl - 1; level >= m_maxUniformRefinementLevel; level--) {
7795 for(MInt cellId = 0; cellId < noCells; cellId++) {
7796 if(a_level(cellId) != level) continue;
7797 if(a_isToDelete(cellId)) continue;
7798 if(a_hasProperty(cellId, Cell::IsHalo)) continue;
7799
7800 // TODO labels:GRID,ADAPTATION,totest check for multisolver setting.. neighborIds solver aware!!
7801 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
7802 if(!m_tree.solver(cellId, solver)) continue;
7803 if(!a_hasChildren(cellId, solver)) continue;
7804
7805 // Partition level ancestor cell cannot be coarsened (exept if its a partition cell)
7806 if(a_hasProperty(cellId, Cell::IsPartLvlAncestor) && !a_hasProperty(cellId, Cell::IsPartitionCell)) {
7807 coarseFlag[cellId][solver] = false;
7808 }
7809
7810 if(coarseFlag[cellId][solver]) {
7811 if(!coarsenCheck(cellId, solver)) {
7812 coarseFlag[cellId][solver] = false;
7813 }
7814 }
7815 }
7816 }
7817
7818 if(noNeighborDomains() > 0) {
7819 // exchangeSolverBitset(coarseFlag.data());
7820 maia::mpi::exchangeBitset(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), coarseFlag.data(),
7821 m_tree.size());
7822 }
7823
7824 for(MInt cellId = 0; cellId < noCells; cellId++) {
7825 if(a_level(cellId) != level) continue;
7826 if(a_isToDelete(cellId)) continue;
7827 if(a_hasProperty(cellId, Cell::IsHalo)) continue;
7828
7829 // TODO labels:GRID,ADAPTATION,totest check for multisolver setting.. neighborIds solver aware!!
7830 if(level < m_maxRfnmntLvl - 1) {
7831 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
7832 if(!m_tree.solver(cellId, solver)) continue;
7833 if(a_hasChildren(cellId, solver)) continue;
7834 if(!refineFlag[cellId][solver] && !coarseFlag[cellId][solver]) {
7835 for(MInt dir = 0; dir < m_noDirs; dir++) {
7836 if(a_hasNeighbor(cellId, dir, solver) > 0) {
7837 MInt nghbrId = a_neighborId(cellId, dir, solver);
7838 if(m_azimuthalPer && a_hasProperty(nghbrId, Cell::IsPeriodic)) continue;
7839 if(a_hasChildren(nghbrId, solver) > 0) {
7840 for(MInt child = 0; child < m_maxNoChilds; child++) {
7841 if(!childCode[dir][child]) continue;
7842 MInt childId = a_childId(nghbrId, child, solver);
7843 if(childId < 0) continue;
7844 if(refineFlag[childId][solver]) {
7845 refineFlag[cellId][solver] = true;
7846 }
7847 }
7848 }
7849 }
7850 }
7851 }
7852 }
7853 }
7854 }
7855
7856 if(noNeighborDomains() > 0) {
7857 // exchangeSolverBitset(refineFlag.data());
7858 maia::mpi::exchangeBitset(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), refineFlag.data(),
7859 m_tree.size());
7860 }
7861
7862 for(MInt cellId = 0; cellId < noCells; cellId++) {
7863 if(a_level(cellId) != level) continue;
7864 if(a_isToDelete(cellId)) continue;
7865 if(m_azimuthalPer && a_hasProperty(cellId, Cell::IsPeriodic)) continue;
7866
7867 // Partition level ancestor cell cannot be coarsened (exept if its a partition cell)
7868 if(a_hasProperty(cellId, Cell::IsPartLvlAncestor) && !a_hasProperty(cellId, Cell::IsPartitionCell)) {
7869 continue;
7870 }
7871
7872 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
7873 if(!m_tree.solver(cellId, solver)) continue;
7874 if(coarseFlag[cellId][solver]) {
7875 for(MInt child = 0; child < m_maxNoChilds; child++) {
7876 MInt childId = a_childId(cellId, child, solver);
7877 if(childId > -1) {
7878 coarseFlag[childId][solver] = false;
7879 refineFlag[childId][solver] = false;
7880 }
7881 }
7882 if(a_hasChildren(cellId, solver)) {
7883 removeChildsSolver[solver](cellId);
7884 // call removeChilds() function in each solver before child links are deleted
7885 refineFlag[cellId][solver] = false;
7886 for(MInt child = 0; child < m_maxNoChilds; child++) {
7887 MInt childId = a_childId(cellId, child);
7888 if(childId > -1) {
7889 treeb().solver(childId, solver) = false;
7890 }
7891 }
7892 }
7893 }
7894 }
7895 // check if the child can also be removed from the cartesian/combined grid
7896 // if the child is no-longer in any of the solvers, the grid child will be removed!
7897 if(a_noChildren(cellId) > 0) {
7898 MBool rmChilds = true;
7899 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
7900 if(a_hasChildren(cellId, solver)) {
7901 rmChilds = false;
7902 }
7903 }
7904 if(rmChilds) {
7905 removeChilds(cellId);
7906 }
7907 }
7908 }
7909
7910 for(MInt i = 0; i < noNeighborDomains(); i++) {
7911 std::set<MInt> deleted;
7912 // WH_old
7913 if(m_haloMode > 0) {
7914 for(auto it_ = m_windowLayer_[i].begin(); it_ != m_windowLayer_[i].end();) {
7915 if(a_isToDelete(it_->first)) {
7916 it_ = m_windowLayer_[i].erase(it_);
7917 } else {
7918 // We might have deleted a cell from a solver
7919 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
7920 if(it_->second.get(solver) < WINDOWLAYER_MAX /*<=m_noSolverHaloLayers[solver]*/
7921 && !m_tree.solver(it_->first, solver))
7922 it_->second.set(solver, WINDOWLAYER_MAX);
7923 if(it_->second.get(solver) > m_noSolverHaloLayers[solver]) it_->second.set(solver, WINDOWLAYER_MAX);
7924 }
7925 if(it_->second.all()) {
7926 deleted.insert(it_->first);
7927 it_ = m_windowLayer_[i].erase(it_);
7928 } else
7929 ++it_;
7930 }
7931 }
7932 }
7933
7934 // 1. coarsen window cells
7935 vector<MInt> oldWindowVec(m_windowCells[i]);
7936#ifndef NDEBUG
7937 std::set<MInt> windCellSet;
7938 std::set<MInt> haloCellSet;
7939#endif
7940
7941 std::vector<MInt>().swap(m_windowCells[i]);
7942 auto it = oldWindowVec.begin();
7943 for(it = oldWindowVec.begin(); it < oldWindowVec.end(); it++) {
7944 const MInt cellId = *it;
7945 if(!a_isToDelete(cellId) && (deleted.find(cellId) == deleted.end() || m_haloMode == 0)) { // WH_old
7946 ASSERT(cellId < treeb().size(), "");
7947
7948#ifndef NDEBUG
7949 // TODO labels:GRID,ADAPTATION fix duplicate window cells in periodic cases!
7950 ASSERT(m_noPeriodicCartesianDirs > 0 || windCellSet.find(cellId) == windCellSet.end(),
7951 "duplicate window cell: " + std::to_string(cellId));
7952 windCellSet.insert(cellId);
7953#endif
7954
7955 m_windowCells[i].push_back(cellId);
7956 }
7957 }
7958 oldWindowVec.clear();
7959
7960 // 2. coarsen halo cells
7961 vector<MInt> oldHaloVec(m_haloCells[i]);
7962 std::vector<MInt>().swap(m_haloCells[i]);
7963 for(it = oldHaloVec.begin(); it < oldHaloVec.end(); it++) {
7964 const MInt cellId = *it;
7965 if(!a_isToDelete(cellId)) {
7966 ASSERT(cellId < treeb().size(), "");
7967
7968#ifndef NDEBUG
7969 ASSERT(haloCellSet.find(cellId) == haloCellSet.end(), "duplicate halo cell: " + std::to_string(cellId));
7970 haloCellSet.insert(cellId);
7971#endif
7972
7973 if(m_tree.solverBits(cellId).any() || m_haloMode == 0) m_haloCells[i].push_back(cellId);
7974 }
7975 }
7976 oldHaloVec.clear();
7977 }
7978 }
7979 //---------------------------(5)
7980
7981
7982 //----------------------------
7983 // 6. Remove all halo cells, for levels > maxUniformRefinementLevel
7984 // since the number of halo layers is inconsistent at this point
7985 // Note: preserve halo partition level ancestors of the missing grid subtree in case of a
7986 // partition level shift
7987
7988 {
7989 for(MInt level = maxLevel - 1; level >= m_maxUniformRefinementLevel; level--) {
7990 for(MInt cellId = 0; cellId < noCells; cellId++) {
7991 if(a_isToDelete(cellId)) continue;
7992 if(!a_hasProperty(cellId, Cell::IsHalo)) continue;
7993 if(a_level(cellId) == level) {
7994 if(a_noChildren(cellId) > 0) {
7995 // Preserve partition level ancestor halos (and their childs) if they are part of the
7996 // missing subtree of the grid in case of a partition level shift
7997 if(a_hasProperty(cellId, Cell::IsPartLvlAncestor) && !a_hasProperty(cellId, Cell::IsPartitionCell)) {
7998 if(std::find(m_partitionLevelAncestorIds.begin(), m_partitionLevelAncestorIds.end(), cellId)
7999 != m_partitionLevelAncestorIds.end()) {
8000 continue;
8001 }
8002 }
8003
8004 for(MInt child = 0; child < m_maxNoChilds; child++) {
8005 MInt childId = a_childId(cellId, child);
8006 if(childId < 0) continue;
8007 refineFlag[childId].reset();
8008 coarseFlag[childId].reset();
8009 }
8010 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
8011 if(!m_tree.solver(cellId, solver)) continue;
8012 if(a_hasChildren(cellId, solver)) {
8013 removeChildsSolver[solver](cellId);
8014 }
8015 }
8016 removeChilds(cellId);
8017 coarseFlag[cellId].reset();
8018 refineFlag[cellId].reset();
8019 }
8020 }
8021 }
8022 }
8023 for(MInt i = 0; i < noNeighborDomains(); i++) {
8024 vector<MInt> oldWindowVec(m_windowCells[i]);
8025 std::vector<MInt>().swap(m_windowCells[i]);
8026 auto it = oldWindowVec.begin();
8027 for(it = oldWindowVec.begin(); it < oldWindowVec.end(); it++) {
8028 const MInt cellId = *it;
8029
8030 // Add partition level ancestor window cells if required
8031 const MBool addWindow = (hasPartLvlShift) ? isWindowPartLvlAncestor(cellId, i) : false;
8032
8033 if((a_level(cellId) > -1 && a_level(cellId) <= m_maxUniformRefinementLevel) || addWindow) {
8034 m_windowCells[i].push_back(cellId);
8035 // Set window cell flag for preserved window cells
8036 a_hasProperty(cellId, Cell::IsWindow) = true;
8037 }
8038 }
8039 oldWindowVec.clear();
8040
8041 // WH_old
8042 if(m_haloMode > 0) {
8043 for(auto it_ = m_windowLayer_[i].begin(); it_ != m_windowLayer_[i].end();) {
8044 const MInt cellId = it_->first;
8045
8046 // Add partition level ancestor window cells if required
8047 const MBool addWindow = (hasPartLvlShift) ? isWindowPartLvlAncestor(cellId, i) : false;
8048
8049 if((a_level(cellId) > -1 && a_level(cellId) <= m_maxUniformRefinementLevel) || addWindow)
8050 ++it_;
8051 else
8052 it_ = m_windowLayer_[i].erase(it_);
8053 }
8054 }
8055
8056 vector<MInt> oldHaloVec(m_haloCells[i]);
8057 std::vector<MInt>().swap(m_haloCells[i]);
8058 for(it = oldHaloVec.begin(); it < oldHaloVec.end(); it++) {
8059 const MInt cellId = *it;
8060
8061 // Add partition level ancestor halo cells if required
8062 const MBool addHalo = (hasPartLvlShift) ? isHaloPartLvlAncestor[cellId] : false;
8063
8064 if((a_level(cellId) > -1 && a_level(cellId) <= m_maxUniformRefinementLevel) || addHalo) {
8065 ASSERT(cellId > -1 && cellId < treeb().size(), to_string(cellId) + " " + to_string(treeb().size()));
8066 m_haloCells[i].push_back(cellId);
8067 }
8068 }
8069 oldHaloVec.clear();
8070 }
8071
8072 if(m_azimuthalPer) {
8073 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
8074 vector<MInt> oldAzimuthalWindowVec(m_azimuthalWindowCells[i]);
8075
8076 m_azimuthalWindowCells[i].clear();
8077 auto it = oldAzimuthalWindowVec.begin();
8078 for(MInt j = 0; j < (signed)oldAzimuthalWindowVec.size(); j++) {
8079 // Remove higher level azimuthal window cells from collector
8080 // If an azimuthal halo cell on a higher level is mapped to an
8081 // azimuthal window cell on a lower level. This azimuthal window cell
8082 // musst also be removed from the collector
8083 const MInt cellId = oldAzimuthalWindowVec[j];
8084 MBool keepWindow = true;
8085 if(find(m_azimuthalHigherLevelConnectivity[i].begin(), m_azimuthalHigherLevelConnectivity[i].end(), j)
8086 != m_azimuthalHigherLevelConnectivity[i].end()) {
8087 keepWindow = false;
8088 }
8089 if(a_level(cellId) > -1 && a_level(cellId) <= m_maxUniformRefinementLevel && keepWindow) {
8090 m_azimuthalWindowCells[i].push_back(cellId);
8091 a_hasProperty(cellId, Cell::IsWindow) = true;
8092 }
8093 }
8094 oldAzimuthalWindowVec.clear();
8095
8096 vector<MInt> oldAzimuthalHaloVec(m_azimuthalHaloCells[i]);
8097 m_azimuthalHaloCells[i].clear();
8098 for(it = oldAzimuthalHaloVec.begin(); it < oldAzimuthalHaloVec.end(); it++) {
8099 const MInt cellId = *it;
8100 if(a_level(cellId) > -1 && a_level(cellId) <= m_maxUniformRefinementLevel) {
8101 ASSERT(cellId > -1 && cellId < treeb().size(), "");
8102 m_azimuthalHaloCells[i].push_back(cellId);
8103 }
8104 }
8105 oldAzimuthalHaloVec.clear();
8106 }
8107
8108 // Remove higher level unmapped azimuthal halo cells
8109 vector<MInt> oldUnmappedHaloVec(m_azimuthalUnmappedHaloCells);
8110 vector<MInt> oldUnmappedHaloDomVec(m_azimuthalUnmappedHaloDomains);
8111 m_azimuthalUnmappedHaloCells.clear();
8112 m_azimuthalUnmappedHaloDomains.clear();
8113 for(MUint i = 0; i < oldUnmappedHaloVec.size(); i++) {
8114 MInt cellId = oldUnmappedHaloVec[i];
8115 if(a_level(cellId) > -1 && a_level(cellId) <= m_maxUniformRefinementLevel) {
8116 ASSERT(cellId > -1 && cellId < treeb().size(), "");
8117 m_azimuthalUnmappedHaloCells.push_back(cellId);
8118 m_azimuthalUnmappedHaloDomains.push_back(oldUnmappedHaloDomVec[i]);
8119 }
8120 }
8121 oldUnmappedHaloVec.clear();
8122 oldUnmappedHaloDomVec.clear();
8123
8124 // Update gridBndryCells
8125 vector<MInt> oldGridBndryVec(m_gridBndryCells);
8126 m_gridBndryCells.clear();
8127 for(MUint i = 0; i < oldGridBndryVec.size(); i++) {
8128 MInt cellId = oldGridBndryVec[i];
8129 if(a_level(cellId) > -1 && a_level(cellId) <= m_maxUniformRefinementLevel) {
8130 m_gridBndryCells.push_back(cellId);
8131 }
8132 }
8133 oldGridBndryVec.clear();
8134 }
8135 }
8136
8137 //---------------------------(6)
8138 //----------------------------
8139 // 7. Refinement step: loop from minLevel to maxLevel and exchange refineFlags to retrieve
8140 // a consistent refinement at domain interfaces; update window/halo connectivity at each level
8141 for(MInt level = m_maxUniformRefinementLevel; level < maxLevel; level++) {
8142 for(MInt cellId = 0; cellId < noCells; cellId++) {
8143 if(a_level(cellId) != level) continue;
8144 if(a_isToDelete(cellId)) continue;
8145 if(a_hasProperty(cellId, Cell::IsHalo)) continue;
8146
8147 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
8148 if(!m_tree.solver(cellId, solver)) continue;
8149 if(a_hasChildren(cellId, solver)) continue;
8150 if(refineFlag[cellId][solver]) {
8151 if(!refineCheck(cellId, solver, cellOutsideSolver)) {
8152 refineFlag[cellId][solver] = 0;
8153 }
8154 }
8155 }
8156
8157 if(refineFlag[cellId].any()) {
8158 refineCell(cellId, nullptr, false, cellOutsideSolver, refineFlag[cellId]);
8159
8160 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
8161 if(!a_hasChildren(cellId, solver) && false) { // sohel false
8162 // this is somehow destroying multisolver cases
8163 // if one solver already has a cell the other solver doesnt get it even if it needs it..
8164 // i think the solver flag is not set correctly in the refineCell function of cartesiangrid
8165 refineFlag[cellId][solver] = false;
8166 }
8167 if(refineFlag[cellId][solver]) {
8168 // Set solver flag for child cells
8169 setChildSolverFlag(cellId, solver, cellOutsideSolver[solver]);
8170 refineCellSolver[solver](cellId); // call refineCell() function in each solver
8171 }
8172 }
8173 for(MInt child = 0; child < m_maxNoChilds; child++) {
8174 MInt childId = a_childId(cellId, child);
8175 if(childId > -1 && a_hasProperty(childId, Cell::WasNewlyCreated)) {
8176 // Timw: only reset the child-flags if the cell was neawly created
8177 // otherwise this stops a refinement of the childs of a different solver!
8178 coarseFlag[childId].reset();
8179 refineFlag[childId].reset();
8180 }
8181 }
8182 }
8183 }
8184
8185 setLevel();
8186
8187 computeGlobalIds();
8188
8189 // WH_old
8190 if(m_haloMode > 0)
8191 // noOffspring required in createHigherLevelExchangeCells
8192 calculateNoOffspringsAndWorkload(static_cast<Collector<void>*>(nullptr), m_tree.size());
8193
8194 // Update child ids of partition level ancestors (stored as global ids)
8195 m_partitionLevelAncestorChildIds.clear();
8196 const MInt noPartLvlAncestorIds = m_partitionLevelAncestorIds.size();
8197 for(MInt i = 0; i < noPartLvlAncestorIds; i++) {
8198 const MInt cellId = m_partitionLevelAncestorIds[i];
8199
8200 for(MInt child = 0; child < m_maxNoChilds; child++) {
8201 const MInt childId = a_childId(cellId, child);
8202 const MLong childGlobalId = (childId > -1) ? a_globalId(childId) : -1;
8203 m_partitionLevelAncestorChildIds.push_back(childGlobalId);
8204 }
8205 }
8206
8207 if(noDomains() > 1 || m_noPeriodicCartesianDirs > 0) {
8208 updateHaloCellCollectors();
8209
8210 // WH_old
8211 if(m_haloMode > 0) {
8212 createHigherLevelExchangeCells(level, true, refineCellSolver, removeCellSolver, level == maxLevel - 1);
8213 // createHigherLevelExchangeCells(level, swapCellsSolver, refineCellSolver);
8214 } else
8215 createHigherLevelExchangeCells_old(level, refineCellSolver);
8216 }
8217
8218 for(MInt i = 0; i < noNeighborDomains(); i++) {
8219 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
8220 MInt cellId = m_haloCells[i][j];
8221 if(a_level(cellId) != level) continue;
8222 for(MInt child = 0; child < m_maxNoChilds; child++) {
8223 const MInt childId = a_childId(cellId, child);
8224 if(childId < 0) continue;
8225
8226 // Partition level shift: do not reset flags if the child of a halo cell is an internal
8227 // cell and already existed!
8228 if(!a_isHalo(childId)) {
8229 ASSERT(a_hasProperty(childId, Cell::IsPartLvlAncestor) || a_hasProperty(childId, Cell::IsPartitionCell),
8230 "child is not a partition level ancestor or a partition cell");
8231 a_hasProperty(cellId, Cell::WasRefined) = true;
8232 } else {
8233 coarseFlag[childId].reset();
8234 refineFlag[childId].reset();
8235 a_hasProperty(childId, Cell::WasNewlyCreated) = true;
8236 a_hasProperty(cellId, Cell::WasRefined) = true;
8237 }
8238 }
8239 }
8240 }
8241
8242 if(m_azimuthalPer) {
8243 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
8244 for(MInt j = 0; j < (signed)m_azimuthalHaloCells[i].size(); j++) {
8245 MInt cellId = m_azimuthalHaloCells[i][j];
8246 if(a_level(cellId) != level) continue;
8247 for(MInt child = 0; child < m_maxNoChilds; child++) {
8248 const MInt childId = a_childId(cellId, child);
8249 if(childId < 0) continue;
8250 if(!a_isHalo(childId)) {
8251 mTerm(1, AT_, "");
8252 } else {
8253 coarseFlag[childId].reset();
8254 refineFlag[childId].reset();
8255 a_hasProperty(childId, Cell::WasNewlyCreated) = true;
8256 a_hasProperty(cellId, Cell::WasRefined) = true;
8257 }
8258 }
8259 }
8260 }
8261 }
8262 }
8263
8264 //---------------------------(7)
8265
8266 //----------------------------
8267 // 8. finalize; synchonize window/halo cell order
8268 setLevel();
8269 computeGlobalIds();
8270 for(auto& rgm : resizeGridMapSolver) {
8271 rgm(); // make sure grid2solver is large enough
8272 }
8273 compactCells(swapCellsSolver);
8274 // Update after cells have been shifted (min-level cells, globalToLocalId, partitionCellIds, etc)
8275 computeGlobalIds();
8276 for(auto& rgm : resizeGridMapSolver) {
8277 rgm(); // update grid2solver after cell swapping
8278 }
8279 if(noDomains() > 1 || m_noPeriodicCartesianDirs > 0) {
8280 updateHaloCellCollectors();
8281 }
8282 computeLeafLevel();
8283 //---------------------------(8)
8284
8285
8286 //----------------------------
8287 // 9. finalize
8288 if(noNeighborDomains() > 0 || noAzimuthalNeighborDomains() > 0) {
8289 exchangeProperties();
8290 }
8291
8292 // Update local bounding box
8293 computeLocalBoundingBox(&m_localBoundingBox[0]);
8294 //---------------------------(9)
8295
8296 if(treeb().noSolvers() > 1 && noNeighborDomains() > 0) { // Exchange solver info
8297 // WH_old
8298 if(m_haloMode > 0) {
8299 exchangeSolverBitset(&m_tree.solverBits(0));
8300 } else
8301 maia::mpi::exchangeBitset(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), &m_tree.solverBits(0),
8302 m_tree.size());
8303 }
8304
8305 if(m_azimuthalPer && noAzimuthalNeighborDomains() > 0) {
8306 maia::mpi::exchangeBitset(m_azimuthalNghbrDomains, m_azimuthalHaloCells, m_azimuthalWindowCells, mpiComm(),
8307 &m_tree.solverBits(0), m_tree.size());
8308 // To ensure that azimuthal halo cells are fully refined (have all children)
8309 // or are not refined at all, solver Bits need to be checked!
8310 correctAzimuthalSolverBits();
8311 }
8312
8313
8314 // TODO labels:GRID,ADAPTATION update noOffspring? not updated during adaptation --> noOffspring required by
8315 // createHigherLevelExchangeCells, so added above
8316 // calculateNoOffspringsAndWorkload(static_cast<Collector<void>*>(nullptr), m_tree.size());
8317
8318 /*
8319 for ( MInt solver = 0; solver < treeb().noSolvers(); solver++ ){
8320 MInt debugHalo=0;
8321 MInt debugWindow=0;
8322 for ( MInt i = 0; i < noNeighborDomains(); i++ ) {
8323 for ( MInt j = 0; j < (signed)m_haloCells[i].size(); j++ ) {
8324 MInt id=m_haloCells[i][j];
8325 if(m_tree.solver( id, solver )){
8326 debugHalo++;
8327 }
8328 }
8329 for ( MInt j = 0; j < (signed)m_windowCells[i].size(); j++ ) {
8330 MInt id=m_windowCells[i][j];
8331 if(m_tree.solver( id, solver )){
8332 debugWindow++;
8333 }
8334 }
8335
8336 cerr << " neighbor: " << i << "--------------" << endl;
8337 cerr <<"count of halos for solver: " << solver << " : " << debugHalo << endl;
8338 cerr <<"count of windows for solver: " << solver << " : " << debugWindow << endl;
8339 cerr <<"total size halo: " << m_haloCells[i].size() << " window: " << m_windowCells[i].size() << endl;
8340 }
8341 }*/
8342
8343 // Update the partition cells when writing the next restart file (in case this is not previously
8344 // done via updatePartitionCells() during balancing)
8345 // TODO labels:GRID,DLB,ADAPTATION partition files written at final time step might not be usable since partition
8346 // cells are updated during new grid output
8347 m_updatedPartitionCells = false;
8348
8349#ifdef MAIA_GRID_SANITY_CHECKS
8350 gridSanityChecks();
8351 checkWindowHaloConsistency(true);
8352 if(m_haloMode > 0) // WH_old
8353 checkWindowLayer("meshAdaptation completed: ");
8354#endif
8355
8356 // Set adaptation status
8357 m_wasAdapted = true;
8358}
8359
8360
8361// --------------------------------------------------------------------------------------
8362
8363
8368template <MInt nDim>
8370 ScratchSpace<MInt> isPeriodic(m_tree.size() - m_noInternalCells, AT_, "isPeriodic");
8371 for(MInt cellId = m_noInternalCells; cellId < m_tree.size(); cellId++) {
8372 ASSERT(a_hasProperty(cellId, Cell::IsHalo), "not a halo cell");
8373 isPeriodic[cellId - m_noInternalCells] = a_hasProperty(cellId, Cell::IsPeriodic);
8374 }
8375 maia::mpi::exchangeBitset(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), &m_tree.properties(0),
8376 m_tree.size());
8377 if(m_azimuthalPer && noAzimuthalNeighborDomains() > 0) {
8378 maia::mpi::exchangeBitset(m_azimuthalNghbrDomains, m_azimuthalHaloCells, m_azimuthalWindowCells, mpiComm(),
8379 &m_tree.properties(0), m_tree.size());
8380 }
8381 for(MInt cellId = m_noInternalCells; cellId < m_tree.size(); cellId++) {
8382 a_hasProperty(cellId, Cell::IsHalo) = true;
8383 a_hasProperty(cellId, Cell::IsWindow) = false;
8384 a_hasProperty(cellId, Cell::IsPeriodic) = isPeriodic[cellId - m_noInternalCells];
8385 }
8386}
8387
8388
8389// --------------------------------------------------------------------------------------
8390
8391
8396template <MInt nDim>
8398 const std::vector<std::function<void(const MInt, const MInt)>>& swapCellsSolver) {
8399 MIntScratchSpace oldCellId(m_maxNoCells, AT_, "oldCellId");
8400 MIntScratchSpace isToDelete(m_maxNoCells, AT_, "isToDelete");
8401 oldCellId.fill(-1);
8402 isToDelete.fill(0);
8403 for(auto& i : m_freeIndices) {
8404 isToDelete(i) = 1;
8405 }
8406 std::set<MInt>().swap(m_freeIndices);
8407
8408 // 1. determine number of cells and internal cells
8409 MInt noCells = 0;
8410 m_noInternalCells = 0;
8411 oldCellId.fill(-1);
8412 for(MInt cellId = 0; cellId < treeb().size(); cellId++) {
8413 if(isToDelete(cellId)) continue;
8414 oldCellId(cellId) = cellId;
8415 if(!a_hasProperty(cellId, Cell::IsHalo)) m_noInternalCells++;
8416 noCells++;
8417 }
8418
8419 // 2. remove holes created by previously deleted cells and move halo cells to the back
8420 MInt otherId = treeb().size() - 1;
8421 for(MInt cellId = 0; cellId < m_noInternalCells; cellId++) {
8422 if(isToDelete(cellId) || a_hasProperty(cellId, Cell::IsHalo)) {
8423 while(isToDelete(otherId) || a_hasProperty(otherId, Cell::IsHalo)) {
8424 otherId--;
8425 }
8426 ASSERT(cellId < otherId, "");
8427 swapCells(cellId, otherId);
8428 for(auto& swp : swapCellsSolver) {
8429 swp(cellId, otherId); // call swapCells() function in each solver
8430 }
8431 std::swap(oldCellId(cellId), oldCellId(otherId));
8432 std::swap(isToDelete(cellId), isToDelete(otherId));
8433 ASSERT(!a_hasProperty(cellId, Cell::IsHalo), "");
8434 ASSERT(isToDelete(otherId) || a_hasProperty(otherId, Cell::IsHalo), "");
8435 }
8436 ASSERT(!a_hasProperty(cellId, Cell::IsHalo) && !isToDelete(cellId), "");
8437 ASSERT(a_level(cellId) > -1, "");
8438 }
8439
8440
8441 // 3. remove holes in the range of halo cells
8442 otherId = treeb().size() - 1;
8443 for(MInt cellId = m_noInternalCells; cellId < noCells; cellId++) {
8444 if(isToDelete(cellId)) {
8445 while(isToDelete(otherId)) {
8446 otherId--;
8447 }
8448 ASSERT(cellId < otherId, "");
8449 ASSERT(otherId >= noCells, "");
8450 ASSERT(a_hasProperty(otherId, Cell::IsHalo) && !isToDelete(otherId), "");
8451 swapCells(cellId, otherId);
8452 for(auto& swp : swapCellsSolver) {
8453 swp(cellId, otherId); // call swapCells() function in each solver
8454 }
8455 std::swap(oldCellId(cellId), oldCellId(otherId));
8456 std::swap(isToDelete(cellId), isToDelete(otherId));
8457 }
8458 ASSERT(a_hasProperty(cellId, Cell::IsHalo) && !isToDelete(cellId), "");
8459 ASSERT(a_level(cellId) > -1, "");
8460 }
8461
8462 // 4. update window/halo cell ids
8463 treeb().size(noCells);
8464 MIntScratchSpace newCellId(m_maxNoCells, AT_, "newCellId");
8465 newCellId.fill(-1);
8466 for(MInt cellId = 0; cellId < noCells; cellId++) {
8467 if(oldCellId(cellId) < 0) continue;
8468 newCellId(oldCellId(cellId)) = cellId;
8469 }
8470 for(MInt i = 0; i < noNeighborDomains(); i++) {
8471 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
8472 MInt cellId = m_haloCells[i][j];
8473 if(newCellId(cellId) > -1) {
8474 m_haloCells[i][j] = newCellId(cellId);
8475 }
8476 }
8477 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
8478 MInt cellId = m_windowCells[i][j];
8479 if(newCellId(cellId) > -1) {
8480 m_windowCells[i][j] = newCellId(cellId);
8481 }
8482 }
8483
8484 // TODO_SS labels:GRID,toenhance maybe think of something more efficient
8485 if(m_haloMode > 0) { // WH_old
8486 std::unordered_map<MInt, M32X4bit<true>> windowLayerBak_(m_windowLayer_[i]);
8487 m_windowLayer_[i].clear();
8488 for(auto m : windowLayerBak_) {
8489 if(newCellId(m.first) > -1) {
8490 m_windowLayer_[i].insert({newCellId(m.first), m.second});
8491 }
8492 }
8493 }
8494 }
8495
8496 if(m_azimuthalPer) {
8497 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
8498 for(MInt j = 0; j < (signed)m_azimuthalHaloCells[i].size(); j++) {
8499 MInt cellId = m_azimuthalHaloCells[i][j];
8500 if(newCellId(cellId) > -1) {
8501 m_azimuthalHaloCells[i][j] = newCellId(cellId);
8502 }
8503 }
8504 for(MInt j = 0; j < (signed)m_azimuthalWindowCells[i].size(); j++) {
8505 MInt cellId = m_azimuthalWindowCells[i][j];
8506 if(newCellId(cellId) > -1) {
8507 m_azimuthalWindowCells[i][j] = newCellId(cellId);
8508 }
8509 }
8510 }
8511 for(MInt c = 0; c < noAzimuthalUnmappedHaloCells(); c++) {
8512 MInt cellId = azimuthalUnmappedHaloCell(c);
8513 if(newCellId(cellId) > -1) {
8514 m_azimuthalUnmappedHaloCells[c] = newCellId(cellId);
8515 }
8516 }
8517 }
8518
8519 // Update partition level ancestor data
8520 {
8521 std::vector<MInt> oldPartLevelAncestorIds(m_partitionLevelAncestorIds);
8522 const MInt noPartLvlAncestorIds = oldPartLevelAncestorIds.size();
8523
8524 // m_partitionLevelAncestorChildIds.clear();
8525 // Note: the variables below seem to be not required at the moment
8526 std::vector<MInt>().swap(m_partitionLevelAncestorIds);
8527 std::vector<MInt>().swap(m_partitionLevelAncestorNghbrDomains);
8528 std::vector<std::vector<MInt>>().swap(m_partitionLevelAncestorHaloCells);
8529 std::vector<std::vector<MInt>>().swap(m_partitionLevelAncestorWindowCells);
8530 m_noHaloPartitionLevelAncestors = 0;
8531
8532 // Update partition level ancestor ids and their child (global) ids
8533 for(MInt i = 0; i < noPartLvlAncestorIds; i++) {
8534 const MInt oldId = oldPartLevelAncestorIds[i];
8535 const MInt newId = newCellId(oldId);
8536 m_partitionLevelAncestorIds.push_back(newId);
8537
8538 // TODO labels:GRID Since we store the global ids of the childs, there is no need to update them.
8539 // If we would do so, we need to keep in mind that some children may reside on
8540 // neighbor domains, so we need to communicate.
8541 // for(MInt child = 0; child < m_maxNoChilds; child++) {
8542 // const MInt childId = a_childId(newId, child);
8543 // const MLong childGlobalId = (childId > -1) ? a_globalId(childId) : -1;
8544 // m_partitionLevelAncestorChildIds.push_back(childGlobalId);
8545 // }
8546 }
8547 }
8548}
8549
8550
8551// --------------------------------------------------------------------------------------
8552
8553
8558template <MInt nDim>
8559void CartesianGrid<nDim>::swapCells(const MInt cellId0, const MInt cellId1) {
8560 static constexpr MInt revDir[6] = {1, 0, 3, 2, 5, 4};
8561 if(cellId1 == cellId0) return;
8562 ASSERT(cellId1 > -1 && cellId0 > -1 && cellId1 < treeb().size() && cellId0 < treeb().size(),
8563 "Invalid cell range " << cellId1 << " " << cellId0 << " " << treeb().size());
8564
8565 // TODO labels:GRID use swap() of grid tree class instead?
8566 // swap parent-child links, the sequence is important in case cellId0 and cellId1 are in parent-child relation!
8567 MInt parentId0 = a_parentId(cellId0);
8568 MInt parentId1 = a_parentId(cellId1);
8569 MInt child0 = -1;
8570 MInt child1 = -1;
8571 if(parentId0 > -1) {
8572 for(MInt k = 0; k < m_maxNoChilds; k++) {
8573 if(a_childId(parentId0, k) == cellId0) {
8574 child0 = k;
8575 break;
8576 }
8577 }
8578 }
8579 if(parentId1 > -1) {
8580 for(MInt k = 0; k < m_maxNoChilds; k++) {
8581 if(a_childId(parentId1, k) == cellId1) {
8582 child1 = k;
8583 break;
8584 }
8585 }
8586 }
8587 for(MInt k = 0; k < m_maxNoChilds; k++) {
8588 if(a_childId(cellId0, k) > -1) {
8589 ASSERT(a_parentId(a_childId(cellId0, k)) == cellId0, "Wrong parent!");
8590 a_parentId(a_childId(cellId0, k)) = cellId1;
8591 }
8592 }
8593 for(MInt k = 0; k < m_maxNoChilds; k++) {
8594 if(a_childId(cellId1, k) > -1) {
8595 ASSERT(a_parentId(a_childId(cellId1, k)) == cellId1, "Wrong parent!");
8596 a_parentId(a_childId(cellId1, k)) = cellId0;
8597 }
8598 }
8599 if(child0 > -1) a_childId(parentId0, child0) = cellId1;
8600 if(child1 > -1) a_childId(parentId1, child1) = cellId0;
8601
8602 for(MInt k = 0; k < m_maxNoChilds; k++) {
8603 std::swap(a_childId(cellId1, k), a_childId(cellId0, k));
8604 }
8605 std::swap(a_parentId(cellId0), a_parentId(cellId1));
8606
8607 // swap forward/reverse neighbor links (in opposite directions, otherwise not working when cellId0 and cellId1 are
8608 // neighbors)
8609 for(MInt dir = 0; dir < m_noDirs; dir++) {
8610 MInt nghbrId0 = a_neighborId(cellId0, dir);
8611 MInt nghbrId1 = a_neighborId(cellId1, revDir[dir]);
8612 if(nghbrId0 > -1) {
8613 ASSERT(a_neighborId(nghbrId0, revDir[dir]) == cellId0, "Reverse link dead");
8614 a_neighborId(nghbrId0, revDir[dir]) = cellId1;
8615 }
8616 if(nghbrId1 > -1) {
8617 ASSERT(a_neighborId(nghbrId1, dir) == cellId1, "Reverse link dead");
8618 a_neighborId(nghbrId1, dir) = cellId0;
8619 }
8620 }
8621 for(MInt dir = 0; dir < m_noDirs; dir++) {
8622 std::swap(a_neighborId(cellId1, dir), a_neighborId(cellId0, dir));
8623 }
8624
8625
8626 // swap other data
8627 std::swap(a_level(cellId1), a_level(cellId0));
8628 std::swap(m_tree.properties(cellId0), m_tree.properties(cellId1));
8629 std::swap(m_tree.solverBits(cellId0), m_tree.solverBits(cellId1));
8630 std::swap(a_globalId(cellId1), a_globalId(cellId0));
8631 std::swap(m_tree.leafCellBits(cellId1), m_tree.leafCellBits(cellId0));
8632 std::swap(a_weight(cellId1), a_weight(cellId0));
8633 std::swap(a_workload(cellId1), a_workload(cellId0));
8634 std::swap(a_noOffsprings(cellId1), a_noOffsprings(cellId0));
8635 for(MInt i = 0; i < nDim; i++) {
8636 std::swap(a_coordinate(cellId1, i), a_coordinate(cellId0, i));
8637 }
8638}
8639
8640
8641// --------------------------------------------------------------------------------------
8642
8659template <MInt nDim>
8661 // Enables grid consistency checks(coarsening) for lattice boltzmann methods
8662 MBool extendCheckTo2ndNeighbor = m_lbGridChecks;
8663 MBool restrictDiagonalLevelJumps = m_lbGridChecks;
8664 MBool lbBndCheck = m_lbGridChecks;
8665
8666 ASSERT(m_tree.solver(cellId, solver), "");
8667
8668 // Check that coarse neighbor has all children
8669 // --> checks if coarse neighbor contains a boundary
8670 // --> checks that no interface parents with missing children are created
8671 // Use enhanced computations of directions in above check (See refine check)
8672 // for checking diagonal neighbors (corners) in 3D, too.
8673 if(lbBndCheck) {
8674 for(MInt dir = 0; dir < m_noDirs; dir++) {
8675 if(!a_hasNeighbor(cellId, dir, solver)) /*continue*/
8676 return false; // Avoid coarsening of boundary cells
8677 MInt cartesianNeighbor = a_neighborId(cellId, dir, solver);
8678 if(a_noChildren(cartesianNeighbor) != m_maxNoChilds && a_noChildren(cartesianNeighbor) != 0) return false;
8679 MInt d0 = dir / 2;
8680 MInt d1 = (d0 + 1) % nDim;
8681 for(MInt p = 0; p < 2; p++) {
8682 MInt dir1 = 2 * d1 + p;
8683 if(!a_hasNeighbor(cartesianNeighbor, dir1, solver)) continue;
8684 MInt diagonalNeighbor = a_neighborId(cartesianNeighbor, dir1, solver);
8685 if(a_noChildren(diagonalNeighbor) != m_maxNoChilds && a_noChildren(diagonalNeighbor) != 0) return false;
8686 IF_CONSTEXPR(nDim == 3) {
8687 MInt d2 = (d1 + 1) % nDim;
8688 for(MInt q = 0; q < 2; q++) {
8689 MInt dir2 = 2 * d2 + q;
8690 if(!a_hasNeighbor(diagonalNeighbor, dir2, solver)) continue;
8691 MInt cornerNeighbor = a_neighborId(diagonalNeighbor, dir2, solver);
8692 if(a_noChildren(cornerNeighbor) != m_maxNoChilds && a_noChildren(cornerNeighbor) != 0) return false;
8693 }
8694 }
8695 }
8696 }
8697 }
8698
8699
8700 // 1. run over all children
8701 for(MInt child = 0; child < m_maxNoChilds; child++) {
8702 MInt childId = a_childId(cellId, child, solver);
8703
8704 if(childId < 0) continue;
8705 if(a_hasChildren(childId, solver)) return false;
8706 for(MInt i = 0; i < m_noDirs; i++) {
8707 if(childCode[i][child]) continue;
8708 if(a_hasNeighbor(childId, i, solver) > 0) {
8709 // 1.1 check if any of the existing children has children (do not coarsen)
8710 if(a_hasChildren(a_neighborId(childId, i, solver), solver) > 0) {
8711 return false;
8712 }
8713
8714 // Extend to second Cartesian neighbor of child to prevent double interpolation in LB case
8715 if(extendCheckTo2ndNeighbor) {
8716 if(a_hasNeighbor(a_neighborId(childId, i, solver), i, solver)) {
8717 if(a_hasChildren(a_neighborId(a_neighborId(childId, i, solver), i, solver)) > 0) {
8718 return false;
8719 }
8720 }
8721 }
8722
8723 // Change compiler flag to if statement
8724 // #ifdef RESTRICT_DIAGONAL_LEVEL_JUMPS
8725 // 1.3. switch to restrict diagonal level jumps
8726 if(restrictDiagonalLevelJumps) {
8727 for(MInt j = 0; j < m_noDirs; j++) {
8728 if(j / 2 == i / 2) continue;
8729 if(a_hasNeighbor(a_neighborId(childId, i, solver), j, solver) > 0) {
8730 if(a_hasChildren(a_neighborId(a_neighborId(childId, i, solver), j, solver), solver)) {
8731 return false;
8732 }
8733
8734 // Extend to second diagonal neighbor of child to prevent double interpolation in LB case
8735 if(extendCheckTo2ndNeighbor) {
8736 MInt diagNeighbor = a_neighborId(a_neighborId(childId, i, solver), j, solver);
8737 if(a_hasNeighbor(diagNeighbor, i, solver) > 0) {
8738 // Test check for children along path ??
8739 if(a_hasChildren(a_neighborId(diagNeighbor, i, solver), solver)) return false;
8740
8741 if(a_hasNeighbor(a_neighborId(diagNeighbor, i, solver), j, solver) > 0) {
8742 if(a_hasChildren(a_neighborId(a_neighborId(diagNeighbor, i, solver), j, solver), solver)) {
8743 return false;
8744 }
8745 }
8746 }
8747 }
8748
8749 IF_CONSTEXPR(nDim == 3) {
8750 for(MInt k = 0; k < m_noDirs; k++) {
8751 // if ( k/2 == i/2 || k/2 == i/2) continue; // TODO labels:GRID old version, was this a typo
8752 // and meant something else?
8753 if(k / 2 == i / 2) continue;
8754 if(a_hasNeighbor(a_neighborId(a_neighborId(childId, i, solver), j, solver), k, solver) > 0) {
8755 if(a_hasChildren(a_neighborId(a_neighborId(a_neighborId(childId, i, solver), j, solver), k, solver),
8756 solver)
8757 > 0) {
8758 return false;
8759 }
8760
8761 // Extend to second corner neighbor of child to prevent double interplation in LB case
8762 if(extendCheckTo2ndNeighbor) {
8763 MInt cornerNeighbor =
8764 a_neighborId(a_neighborId(a_neighborId(childId, i, solver), j, solver), k, solver);
8765 if(a_hasNeighbor(cornerNeighbor, i, solver) > 0) {
8766 // Test check for children along path ??
8767 if(a_hasChildren(a_neighborId(cornerNeighbor, i, solver), solver)) return false;
8768
8769 if(a_hasNeighbor(a_neighborId(cornerNeighbor, i, solver), j, solver) > 0) {
8770 // Test check for children along path ??
8771 if(a_hasChildren(a_neighborId(a_neighborId(cornerNeighbor, i, solver), j, solver), solver))
8772 return false;
8773
8774 if(a_hasNeighbor(a_neighborId(a_neighborId(cornerNeighbor, i, solver), j, solver), k, solver)
8775 > 0) {
8776 if(a_hasChildren(a_neighborId(
8777 a_neighborId(a_neighborId(cornerNeighbor, i, solver), j, solver), k, solver)))
8778 return false;
8779 }
8780 }
8781 }
8782 }
8783 }
8784 }
8785 }
8786 }
8787 }
8788 }
8789 //#endif
8790 }
8791 }
8792 }
8793
8794 // Not needed for lb adaptation
8795 if(!m_lbGridChecks) {
8796 // 2. additionally checks if the current cell has a missing neighbor. In this case and if it has children, it has to
8797 // be a boundary cell and
8798 // it's children need to be marked as non-coarsenable cells (this is past this function horizon).
8799 if(!m_allowInterfaceRefinement)
8800 for(MInt i = 0; i < m_noDirs; i++)
8801 if(!a_hasNeighbor(cellId, i, solver)) return false;
8802 }
8803 return true;
8804}
8805
8806
8807// --------------------------------------------------------------------------------------
8808
8809
8814template <MInt nDim>
8816 const MInt cellId, const MInt solver,
8817 const std::vector<std::function<MInt(const MFloat*, const MInt, const MInt)>>& cellOutsideSolver) {
8818 // Enable grid checks for LB (refinement)
8819 MBool restrictDiagonalLevelJumps = m_lbGridChecks;
8820 MBool extendCheckTo2ndNeighbor = m_lbGridChecks;
8821
8822 ASSERT(m_tree.solver(cellId, solver), "");
8823 for(MInt dir = 0; dir < m_noDirs; dir++) {
8824 if(a_hasNeighbor(cellId, dir, solver)) {
8825 if(restrictDiagonalLevelJumps) {
8826 MInt d0 = dir / 2;
8827 MInt d1 = (d0 + 1) % nDim;
8828 for(MInt p = 0; p < 2; p++) {
8829 MInt dir1 = 2 * d1 + p;
8830 if(a_hasNeighbor(a_neighborId(cellId, dir, solver), dir1, solver) == 0) {
8831 return false;
8832 }
8833
8834 // Ensure two diagonal neigbors to avoid double interpolation in LB
8835 // For readability introduce names of cells
8836 MInt firstDiagNghbor;
8837 if(extendCheckTo2ndNeighbor) {
8838 firstDiagNghbor = a_neighborId(a_neighborId(cellId, dir, solver), dir1, solver);
8839 if(a_hasNeighbor(firstDiagNghbor, dir, solver)) {
8840 if(a_hasNeighbor(a_neighborId(firstDiagNghbor, dir, solver), dir1, solver) == 0) {
8841 return false;
8842 }
8843 } else {
8844 return false;
8845 }
8846 }
8847
8848 // From last merge: else if. But this crashes 3D case. Thus change to if
8849 IF_CONSTEXPR(nDim == 3) {
8850 MInt d2 = (d1 + 1) % nDim;
8851 for(MInt q = 0; q < 2; q++) {
8852 MInt dir2 = 2 * d2 + q;
8853 if(a_hasNeighbor(a_neighborId(a_neighborId(cellId, dir, solver), dir1, solver), dir2, solver) == 0) {
8854 return false;
8855 }
8856
8857 // Ensure two diagonal neigbors (corner) to avoid double interpolation in LB
8858 // The step-wise procedure is necessary to ensure "filling" between neighbors
8859 MInt firstCornerNeighbor =
8860 a_neighborId(a_neighborId(a_neighborId(cellId, dir, solver), dir1, solver), dir2, solver);
8861 if(extendCheckTo2ndNeighbor) {
8862 if(a_hasNeighbor(firstCornerNeighbor, dir, solver)) {
8863 if(a_hasNeighbor(a_neighborId(firstCornerNeighbor, dir, solver), dir1, solver)) {
8864 if(a_hasNeighbor(a_neighborId(a_neighborId(firstCornerNeighbor, dir, solver), dir1, solver), dir2,
8865 solver)
8866 == 0) {
8867 return false;
8868 }
8869 } else {
8870 return false;
8871 }
8872 } else {
8873 return false;
8874 }
8875 }
8876 }
8877 }
8878 }
8879 }
8880 } else if(m_allowInterfaceRefinement && hasCutOff()) {
8881 // continue;
8882 // check if the cell is a cutOff cell
8883 // meaning that the largest parent doesn't have a neighbor in that direction!
8884
8885 MInt parentId = a_parentId(cellId, solver);
8886 if(parentId > -1 && !a_hasNeighbor(parentId, dir, solver)) {
8887 continue;
8888 } else if(parentId > -1) {
8889 return false;
8890 }
8891
8892 } else if(m_allowInterfaceRefinement) {
8893 MFloat coords[nDim];
8894 for(MInt k = 0; k < nDim; k++) {
8895 coords[k] = a_coordinate(cellId, k);
8896 }
8897 coords[dir / 2] += ((dir % 2 == 0) ? -F1 : F1) * cellLengthAtCell(cellId);
8898 MInt isOutside = cellOutsideSolver[solver](coords, a_level(cellId), cellId);
8899 if(isOutside == 0) {
8900 return false;
8901 } else if(isOutside == -1) {
8902 MInt parentId = a_parentId(cellId, solver);
8903 while(parentId > -1) {
8904 if(a_hasNeighbor(parentId, dir, solver)) {
8905 return false;
8906 }
8907 parentId = a_parentId(parentId, solver);
8908 }
8909 }
8910 } else {
8911 return false;
8912 }
8913 }
8914 return true;
8915}
8916
8917//-------------------------------------------------------------------------------------
8918
8919
8926template <MInt nDim>
8928 TRACE();
8929 m_maxLevel = -1;
8930
8931 for(MInt i = 0; i < m_tree.size(); i++) { // m_noRegularCells
8932 if(a_isToDelete(i)) continue;
8933 if(a_hasProperty(i, Cell::IsHalo)) continue;
8934 m_maxLevel = (m_maxLevel < a_level(i)) ? a_level(i) : m_maxLevel;
8935 }
8936
8937 MPI_Allreduce(MPI_IN_PLACE, &m_maxLevel, 1, MPI_INT, MPI_MAX, mpiComm(), AT_, "MPI_IN_PLACE", "m_maxLevel");
8938
8939 if(m_minLevel < 0 || m_maxLevel < m_minLevel || m_maxLevel > 31 || m_minLevel > m_maxLevel) {
8940 mTerm(1, AT_, "Inconsistent min/max levels: " + to_string(m_minLevel) + "/" + to_string(m_maxLevel));
8941 }
8942}
8943
8944
8949template <MInt nDim>
8951 if(m_tree.size() == 0) {
8952 mTerm(1, AT_, " Error in CartesianGrid::deleteCell(), collector is empty.");
8953 }
8954 if(id >= m_tree.size()) {
8955 mTerm(1, AT_, " Error in CartesianGrid::deleteCell(), cell out of range.");
8956 }
8957
8958 a_resetProperties(id);
8959
8960 // If the cell to delete is the last cell, return -1, otherwise return the previously largest id
8961 const MInt lastId = m_tree.size() - 1;
8962 const MInt returnValue = (lastId <= id) ? -1 : lastId;
8963
8964 // Erase cell, move last cell to gap, shrink tree
8965 m_tree.removeAndFill(id);
8966
8967 return returnValue;
8968}
8969
8970
8971//-------------------------------------------------------------------------------------
8972
8982template <MInt nDim>
8984 const MFloat* const center,
8985 const MFloat length0,
8986 const MInt baseLevel) const {
8987 // TRACE();
8988 ASSERT(length0 > 0.0, "length needs to be > 0.");
8989 // Relate to unit cube
8990 MFloat x[nDim];
8991 for(MInt i = 0; i < nDim; i++) {
8992 x[i] = (coords[i] - center[i] + 1e-12 + F1B2 * length0) / length0;
8993 ASSERT(x[i] > F0 && x[i] < F1,
8994 "Normalized coordinate outside of range (0,1): " + std::to_string(x[i])
8995 + "; length0 = " + std::to_string(length0) + "; baseLevel = " + std::to_string(baseLevel)
8996 + "; coord = " + std::to_string(coords[i]) + "; center = " + std::to_string(center[i]));
8997 }
8998 const MLong hilbertId = maia::grid::hilbert::index<nDim>(&x[0], (MLong)baseLevel);
8999 ASSERT(hilbertId > -1, "Invalid hilbert id");
9000 return hilbertId;
9001}
9002
9003
9004template <MInt nDim>
9005void CartesianGrid<nDim>::saveGrid(const MChar* fileName, const std::vector<std::vector<MInt>>& haloCells,
9006 const std::vector<std::vector<MInt>>& windowCells,
9007 const std::vector<std::vector<MInt>>& azimuthalHaloCells,
9008 const std::vector<MInt>& azimuthalUnmappedHaloCells,
9009 const std::vector<std::vector<MInt>>& azimuthalWindowCells, MInt* recalcIdTree) {
9010 TRACE();
9011 maia::grid::IO<CartesianGrid<nDim>>::save(*this, fileName, static_cast<Collector<void>*>(nullptr), haloCells,
9012 windowCells, azimuthalHaloCells, azimuthalUnmappedHaloCells,
9013 azimuthalWindowCells, recalcIdTree);
9014}
9015
9016template <MInt nDim>
9017void CartesianGrid<nDim>::saveGrid(const MChar* fileName, MInt* recalcIdTree) {
9018 TRACE();
9019
9020 if(m_newMinLevel < 0) {
9021 maia::grid::IO<CartesianGrid<nDim>>::save(*this, fileName, static_cast<Collector<void>*>(nullptr), m_haloCells,
9022 m_windowCells, m_azimuthalHaloCells, m_azimuthalUnmappedHaloCells,
9023 m_azimuthalWindowCells, recalcIdTree);
9024
9025 } else { // if possible Increase MinLevel when writing the restart-File!
9026 // if the m_targetGridMinLevel has already been updated, the new grid restartFile
9027 // is forced without a backup and further check!
9028
9029 if(m_newMinLevel == m_targetGridMinLevel && noDomains() == 1) {
9030 maia::grid::IO<CartesianGrid<nDim>>::save(*this, fileName, static_cast<Collector<void>*>(nullptr), m_haloCells,
9031 m_windowCells, m_azimuthalHaloCells, m_azimuthalUnmappedHaloCells,
9032 m_azimuthalWindowCells, recalcIdTree);
9033 } else if(noDomains() > 1) {
9034 MInt backup = m_newMinLevel;
9035 m_newMinLevel = -1;
9036 maia::grid::IO<CartesianGrid<nDim>>::save(*this, fileName, static_cast<Collector<void>*>(nullptr), m_haloCells,
9037 m_windowCells, m_azimuthalHaloCells, m_azimuthalUnmappedHaloCells,
9038 m_azimuthalWindowCells, recalcIdTree);
9039 m_newMinLevel = backup;
9040
9041 } else {
9042 // check that all minLevels are refined
9043 MInt noUnrefinedMinCells = 0;
9044 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
9045 if(a_isHalo(cellId)) continue;
9046 if(a_level(cellId) < m_newMinLevel) {
9047 if(a_noChildren(cellId) < 1) noUnrefinedMinCells++;
9048 }
9049 }
9050
9051 MPI_Allreduce(MPI_IN_PLACE, &noUnrefinedMinCells, 1, MPI_INT, MPI_SUM, mpiComm(), AT_, "MPI_IN_PLACE",
9052 "noUnrefinedMinCells");
9053
9054 // if the minLevel cells are not sufficiently refiened a reguar output is wirtten
9055 if(noUnrefinedMinCells > 0) {
9056 MInt backup = m_newMinLevel;
9057 if(domainId() == 0) {
9058 cerr << "Mincells not yet sufficiently refined for minLevel increase from " << m_minLevel << " to "
9059 << m_newMinLevel << "!" << endl;
9060 }
9061 m_newMinLevel = -1;
9062 maia::grid::IO<CartesianGrid<nDim>>::save(*this, fileName, static_cast<Collector<void>*>(nullptr), m_haloCells,
9063 m_windowCells, m_azimuthalHaloCells, m_azimuthalUnmappedHaloCells,
9064 m_azimuthalWindowCells, recalcIdTree);
9065 m_newMinLevel = backup;
9066
9067 } else { // writing the restartGrid with increased minLevel!
9068
9069 // write the original grid as backup
9070 MInt backupMinLevel = m_newMinLevel;
9071 m_newMinLevel = -1;
9072 m_targetGridMinLevel = m_minLevel;
9073 std::stringstream s;
9074 s << "restartGrid_backup_" << globalTimeStep << ParallelIo::fileExt();
9075 MString newGridFileName = s.str();
9076
9078 *this, (m_outputDir + newGridFileName).c_str(), static_cast<Collector<void>*>(nullptr), m_haloCells,
9079 m_windowCells, m_azimuthalHaloCells, m_azimuthalUnmappedHaloCells, m_azimuthalWindowCells, recalcIdTree);
9080
9081 m_newMinLevel = backupMinLevel;
9082 m_targetGridMinLevel = m_newMinLevel;
9083
9084 if(domainId() == 0) {
9085 cerr << "Increasing minLevel from " << m_minLevel << " to " << m_newMinLevel << endl;
9086 }
9087
9088 MInt backupUniform = m_maxUniformRefinementLevel;
9089 if(m_maxUniformRefinementLevel < m_newMinLevel) {
9090 if(domainId() == 0) {
9091 cerr << "Increasing maxUniformRefinementLevel from " << m_maxUniformRefinementLevel << " to "
9092 << m_newMinLevel << endl;
9093 }
9094 m_maxUniformRefinementLevel = m_newMinLevel;
9095 }
9096 // writing the new grid
9097 maia::grid::IO<CartesianGrid<nDim>>::save(*this, fileName, static_cast<Collector<void>*>(nullptr), m_haloCells,
9098 m_windowCells, m_azimuthalHaloCells, m_azimuthalUnmappedHaloCells,
9099 m_azimuthalWindowCells, recalcIdTree);
9100
9101 m_maxUniformRefinementLevel = backupUniform;
9102 }
9103 }
9104 }
9105}
9106
9107//-----------------------------------------------------------------------
9108
9109
9115template <MInt nDim>
9117 TRACE();
9118 maia::grid::IO<CartesianGrid<nDim>>::load(*this, fileName);
9119}
9120
9121
9122//-----------------------------------------------------------------------
9123
9124
9128template <MInt nDim>
9129inline MInt CartesianGrid<nDim>::globalIdToLocalId(const MLong& globalId, const MBool termIfNotExisting) {
9130 ASSERT(m_globalToLocalId.size() > 0, "Error: m_globalToLocalId is empty.");
9131 auto it = m_globalToLocalId.find(globalId);
9132 if(it != m_globalToLocalId.end()) {
9133 return it->second;
9134 } else {
9135 TERMM_IF_COND(termIfNotExisting, "Mapping from global to local id not found.");
9136 return -1;
9137 }
9138}
9139
9140
9141//-----------------------------------------------------------------------
9142
9143
9147template <MInt nDim>
9148inline void CartesianGrid<nDim>::resetCell(const MInt& cellId) {
9149 a_resetProperties(cellId);
9150 m_tree.resetSolver(cellId);
9151 m_tree.resetIsLeafCell(cellId);
9152 a_parentId(cellId) = -1;
9153 a_globalId(cellId) = -1;
9154 a_level(cellId) = -1;
9155 a_noOffsprings(cellId) = 0;
9156 a_weight(cellId) = F1;
9157 a_workload(cellId) = F0;
9158 fill(&a_childId(cellId, 0), &a_childId(cellId, 0) + m_maxNoChilds, -1);
9159 fill(&a_neighborId(cellId, 0), &a_neighborId(cellId, 0) + m_maxNoNghbrs, -1);
9160}
9161
9162
9163// -----------------------------------------------------------------------------
9164
9165
9175template <MInt nDim>
9177 TRACE();
9178
9179 // Return fast if no donor grid is specified
9180 if(!Context::propertyExists("donorGridFileName")) {
9181 return;
9182 }
9183
9195 // Return if no grid map should be created
9196 if(!Context::getBasicProperty<MBool>("createGridMap", AT_)) {
9197 return;
9198 }
9199
9200 // Get name of donor grid file
9201 const MString donorGridFileName = Context::getBasicProperty<MString>("donorGridFileName", AT_);
9202
9203 // Get grid map file name if specified
9204 MString gridMapFileName = m_outputDir + "gridmap.Netcdf";
9205 gridMapFileName = Context::getBasicProperty<MString>("gridMapFileName", AT_, &gridMapFileName);
9206
9218 // If grid map should not be reused, re-generate it
9219 MBool regenerate = true;
9220 regenerate = Context::getBasicProperty<MBool>("forceGridMapGeneration", AT_, &regenerate);
9221 if(regenerate || !ParallelIo::fileExists(gridMapFileName, mpiComm())) {
9222 createGridMap(donorGridFileName, gridMapFileName);
9223 }
9224
9236 // Create donor grid partition file if desired
9237 MBool savePartition = false;
9238 savePartition = Context::getBasicProperty<MBool>("saveDonorGridPartition", AT_, &savePartition);
9239 if(savePartition) {
9240 // Get partition file name if specified
9241 MString gridPartitionFileName = Context::getBasicProperty<MString>("gridPartitionFileName", AT_);
9242
9243 // Load relevant information from grid map and store to grid partition file
9244 saveDonorGridPartition(gridMapFileName, gridPartitionFileName);
9245 }
9246}
9247
9248
9271template <MInt nDim>
9272void CartesianGrid<nDim>::createGridMap(const MString& donorGridFileName, const MString& gridMapFileName) {
9273 TRACE();
9274
9275 m_log << "Creating grid map from donor grid " << donorGridFileName << " to target grid " << m_gridInputFileName
9276 << " and writing the results to " << gridMapFileName << "..." << endl;
9277
9279 // Step 0: sanity checks
9281
9282 // Open donor grid for reading
9283 using namespace parallel_io;
9284 ParallelIo donorGrid(donorGridFileName, PIO_READ, mpiComm());
9285
9286 // Check extents
9287 array<MFloat, nDim> donorCenter;
9288 // donorGrid.setOffset(nDim, 0);
9289 // donorGrid.readArray(&donorCenter[0], "centerOfGravity");
9290 donorGrid.getAttribute(&donorCenter[0], "centerOfGravity", nDim);
9291 MFloat donorLengthLevel0 = NAN;
9292 // donorGrid.readScalar(&donorLengthLevel0, "lengthLevel0");
9293 donorGrid.getAttribute(&donorLengthLevel0, "lengthLevel0");
9294 const array<MString, 3> dirs = {{"x", "y", "z"}};
9295 for(MInt dir = 0; dir < nDim; dir++) {
9296 const array<MFloat, 2> donorExtent = {
9297 {donorCenter[dir] - 0.5 * donorLengthLevel0, donorCenter[dir] + 0.5 * donorLengthLevel0}};
9298 const array<MFloat, 2> targetExtent = {
9299 {m_centerOfGravity[dir] - 0.5 * lengthLevel0(), m_centerOfGravity[dir] + 0.5 * lengthLevel0()}};
9300 if(donorExtent[0] < targetExtent[0]) {
9301 TERMM(1, "Donor grid extents exceed target grid in negative " + dirs[dir] + "-direction");
9302 }
9303 if(donorExtent[1] > targetExtent[1]) {
9304 TERMM(1, "Donor grid extents exceed target grid in positive " + dirs[dir] + "-direction");
9305 }
9306 }
9307
9308 // Define epsilon for floating point comparisons (argh!)... the original
9309 // definition is taken from the constructor but it must be clear to anyone
9310 // that this is a less-than-optimal solution
9311 const MFloat eps = 1.0 / FPOW2(30) * m_lengthLevel0;
9312
9313 // Check cell length at min level
9314 MInt donorMinLevel = 0;
9315 // donorGrid.readScalar(&donorMinLevel, "minLevel");
9316 donorGrid.getAttribute(&donorMinLevel, "minLevel");
9317 const MFloat donorMinLevelLength = donorLengthLevel0 * FFPOW2(donorMinLevel);
9318 const MFloat targetMinLevelLength = cellLengthAtLevel(minLevel());
9319 if(fabs(donorMinLevelLength - targetMinLevelLength) > eps) {
9320 TERMM(1,
9321 "Length of min level cells do not match between donor and target "
9322 "grid: donor: "
9323 + to_string(donorMinLevelLength) + "; target: " + to_string(targetMinLevelLength));
9324 }
9325
9326 // Check if grid centers are displaced by an integer multiple of the min
9327 // level length
9328 for(MInt dir = 0; dir < nDim; dir++) {
9329 const MFloat displacement = fabs(donorCenter[dir] - m_centerOfGravity[dir]);
9330 const MFloat quotient = displacement / targetMinLevelLength;
9331 if(!isApproxInt(quotient, eps)) {
9332 TERMM(1, "The grid centers are displaced in the " + dirs[dir]
9333 + "-direction by a non-integer multiple of the length of a "
9334 "partition cell: "
9335 + to_string(quotient));
9336 }
9337 }
9338
9339
9341 // Step 1: partition donor grid
9343
9344 // Read partition cells and partition donor grid
9345 // const MInt noPartitionCells = donorGrid.getArraySize("partitionCellsId");
9346 MInt noPartitionCells = 0;
9347 donorGrid.getAttribute(&noPartitionCells, "noPartitionCells");
9348
9349 // Read data only on MPI root by setting the offsets accordingly
9350 donorGrid.setOffset(isMpiRoot() ? noPartitionCells : 0, 0);
9351
9352 // Ensure that there are no partition level shifts as this is not yet supported
9353 {
9354 // MIntScratchSpace partitionCellsLvlDiff(noPartitionCells, AT_, "partitionCellsLvlDiff");
9355 // donorGrid.readArray(&partitionCellsLvlDiff[0], "partitionCellsLvlDiff");
9357 // partitionCellsLvlDiff.cend(),
9358 // [](MInt diff) { return diff != 0; });
9359 MInt hasDiff = 0;
9360 donorGrid.getAttribute(&hasDiff, "maxPartitionLevelShift");
9361 if(hasDiff != 0) {
9362 TERMM(1, "partition level shifts not supported but level difference found");
9363 }
9364 }
9365
9366 // Determine offsets
9367 MIntScratchSpace partitionCellsId(noPartitionCells, AT_, "partitionCellsId");
9368 MIntScratchSpace globalIdOffsets(noDomains(), AT_, "globalIdOffsets");
9369 {
9370 // Determine partition cell offsets
9371 MIntScratchSpace offsets(noDomains() + 1, AT_, "offsets");
9372 MFloatScratchSpace partitionCellsWorkLoad(noPartitionCells, AT_, "partitionCellsWorkLoad");
9373 // donorGrid.readArray(&partitionCellsWorkLoad[0], "partitionCellsWorkLoad");
9374 donorGrid.readArray(&partitionCellsWorkLoad[0], "partitionCellsWorkload");
9375 if(isMpiRoot() && noDomains() > 1) {
9376 grid::optimalPartitioningSerial(&partitionCellsWorkLoad[0], noPartitionCells, noDomains(), &offsets[0]);
9377 }
9378
9379 // Determine global id offsets
9380 // MIntScratchSpace partitionCellsNoOffsprings(noPartitionCells, AT_,
9381 // "partitionCellsNoOffsprings");
9382 // donorGrid.readArray(&partitionCellsNoOffsprings[0], "partitionCellsNoOffsprings");
9383 // donorGrid.readArray(&partitionCellsId[0], "partitionCellsId");
9384 donorGrid.readArray(&partitionCellsId[0], "partitionCellsGlobalId");
9385 if(isMpiRoot() && noDomains() > 1) {
9386 grid::partitionCellToGlobalOffsets(&offsets[0], &partitionCellsId[0], noDomains(), &globalIdOffsets[0]);
9387 }
9388 }
9389
9390 // Distribute global id offsets
9391 MPI_Bcast(&globalIdOffsets[0], noDomains(), type_traits<MInt>::mpiType(), 0, mpiComm(), AT_, "globalIdOffsets[0]");
9392
9393 // Distribute partition cells ids
9394 MPI_Bcast(&partitionCellsId[0], noPartitionCells, type_traits<MInt>::mpiType(), 0, mpiComm(), AT_,
9395 "partitionCellsId[0]");
9396
9397
9399 // Step 2: calculate Hilbert indices for donor grid and current cells
9401
9402 // Determine Hilbert index for all partition cells for which coordinates were read
9403 MIntScratchSpace hilbertIds(noPartitionCells, AT_, "hilbertIds");
9404 const MInt noCells = m_noInternalCells;
9405 MIntScratchSpace localHilbertIds(noCells, AT_, "localHilbertIds");
9406 {
9407 // Determine offset and length for reading coordinate information
9408 // const MInt globalCount = donorGrid.getArraySize("parentId");
9409 MInt globalCount = 0;
9410 donorGrid.getAttribute(&globalCount, "noCells");
9411 const MInt globalIdOffset = globalIdOffsets[domainId()];
9412 const MInt localCount = (domainId() == noDomains() - 1)
9413 ? globalCount - globalIdOffsets[domainId()]
9414 : globalIdOffsets[domainId() + 1] - globalIdOffsets[domainId()];
9415
9416 // Read coordinates
9417 MFloatScratchSpace coordinates(nDim * localCount, AT_, "coordinates");
9418 /*donorGrid.setOffset(localCount, globalIdOffset);
9419 for (MInt i = 0; i < nDim; i++) {
9420 const MString name = "coordinates_" + to_string(i);
9421 donorGrid.readArray(&coordinates[i], name, nDim);
9422 }*/
9423 MLongScratchSpace minLevelCellsTreeId(noPartitionCells, AT_, "minLevelCellsTreeId");
9424 {
9425 donorGrid.setOffset(noPartitionCells, 0);
9426 donorGrid.readArray(minLevelCellsTreeId.data(), "minLevelCellsTreeId");
9427 }
9428
9429 // Determine Hilbert index
9430 fill(hilbertIds.begin(), hilbertIds.end(), -1);
9431 for(MInt i = 0; i < noPartitionCells; i++) {
9432 // Skip partition cells that are outside the range
9433 const MInt globalId = partitionCellsId[i];
9434 if(globalId < globalIdOffset || globalId >= globalIdOffset + localCount) {
9435 continue;
9436 }
9437
9438 // Determine coordinates relative to unit cube
9439 array<MFloat, nDim> x;
9440 const MInt localCellId = globalId - globalIdOffset;
9441 maia::grid::hilbert::treeIdToCoordinates<nDim>(&coordinates[localCellId * nDim], minLevelCellsTreeId[i],
9442 (MLong)m_minLevel, &donorCenter[0], donorLengthLevel0);
9443 for(MInt j = 0; j < nDim; j++) {
9444 x[j] = (coordinates[localCellId * nDim + j] - m_centerOfGravity[j] + 0.5 * lengthLevel0()) / lengthLevel0();
9445 }
9446
9447 // Calculate Hilbert index
9448 hilbertIds[i] = maia::grid::hilbert::index<nDim>(&x[0], minLevel());
9449 }
9450
9451 // Determine Hilbert index for all cells on current domain
9452 for(MInt cellId = 0; cellId < noCells; cellId++) {
9453 // Skip cells that are not partition cells
9454 if(a_level(cellId) != minLevel()) {
9455 continue;
9456 }
9457
9458 // Determine coordinates relative to unit cube
9459 array<MFloat, nDim> x;
9460 for(MInt j = 0; j < nDim; j++) {
9461 x[j] = (a_coordinate(cellId, j) - m_centerOfGravity[j] + 0.5 * lengthLevel0()) / lengthLevel0();
9462 }
9463
9464 // Calculate Hilbert index
9465 localHilbertIds[cellId] = maia::grid::hilbert::index<nDim>(&x[0], minLevel());
9466 }
9467 }
9468
9469
9471 // Step 3: exchange Hilbert ids of donor grid with all domains (parallel only)
9473
9474 // Determine how much data is to exchange and how to receive it
9475 {
9476 MIntScratchSpace dataCount(noDomains(), AT_, "dataCount");
9477 const MInt noCellsFound = count_if(hilbertIds.begin(), hilbertIds.end(), [](const MInt a) { return a != -1; });
9478 dataCount[domainId()] = noCellsFound;
9479 MPI_Allgather(MPI_IN_PLACE, 1, maia::type_traits<MInt>::mpiType(), &dataCount[0], 1,
9480 maia::type_traits<MInt>::mpiType(), mpiComm(), AT_, "MPI_IN_PLACE", "dataCount[0]");
9481 MIntScratchSpace displacements(noDomains(), AT_, "displacements");
9482 displacements[0] = 0;
9483 for(MInt i = 1; i < noDomains(); i++) {
9484 displacements[i] = displacements[i - 1] + dataCount[i - 1];
9485 }
9486 MIntScratchSpace sendBuffer(noCellsFound, AT_, "sendBuffer");
9487
9488 // Exchange partition cell ids
9489 for(MInt count = 0, i = 0; i < noPartitionCells; i++) {
9490 if(hilbertIds[i] == -1) {
9491 continue;
9492 }
9493 sendBuffer[count++] = partitionCellsId[i];
9494 }
9495 MPI_Allgatherv(&sendBuffer[0], noCellsFound, maia::type_traits<MInt>::mpiType(), &partitionCellsId[0],
9496 &dataCount[0], &displacements[0], maia::type_traits<MInt>::mpiType(), mpiComm(), AT_,
9497 "sendBuffer[0]", "partitionCellsId[0]");
9498
9499 // Exchange Hilbert ids
9500 for(MInt count = 0, i = 0; i < noPartitionCells; i++) {
9501 if(hilbertIds[i] == -1) {
9502 continue;
9503 }
9504 sendBuffer[count++] = hilbertIds[i];
9505 }
9506 MPI_Allgatherv(&sendBuffer[0], noCellsFound, maia::type_traits<MInt>::mpiType(), &hilbertIds[0], &dataCount[0],
9507 &displacements[0], maia::type_traits<MInt>::mpiType(), mpiComm(), AT_, "sendBuffer[0]",
9508 "hilbertIds[0]");
9509 }
9510
9511
9513 // Step 4: determine grid map and write to file
9515
9516 // Sort partition cell ids (and Hilbert ids) by Hilbert id
9517 {
9518 ScratchSpace<array<MInt, 2>> s(noPartitionCells, AT_, "s");
9519 for(MInt i = 0; i < noPartitionCells; i++) {
9520 s[i][0] = hilbertIds[i];
9521 s[i][1] = partitionCellsId[i];
9522 }
9523 sort(s.begin(), s.end(), [](const array<MInt, 2>& a, const array<MInt, 2>& b) { return a[0] < b[0]; });
9524 for(MInt i = 0; i < noPartitionCells; i++) {
9525 hilbertIds[i] = s[i][0];
9526 partitionCellsId[i] = s[i][1];
9527 }
9528 }
9529
9530 MIntScratchSpace gridMap(noCells, AT_, "gridMap");
9531 fill(gridMap.begin(), gridMap.end(), -1);
9532
9533 MInt firstMappedPartitionCellId = std::numeric_limits<MInt>::max();
9534 MInt noMappedPartitionCells = 0;
9535
9536 // Determine matching partition cells, the first mapped partition cell id and the number
9537 // of mapped partition cells on this domain
9538 for(MInt cellId = 0; cellId < noCells; cellId++) {
9539 // Skip cells that are not partition cells
9540 if(a_level(cellId) != minLevel()) {
9541 continue;
9542 }
9543
9544 // Find corresponding Hilbert id
9545 const MInt hilbertId = localHilbertIds[cellId];
9546 MInt* const lower = lower_bound(hilbertIds.data(), (hilbertIds.data() + hilbertIds.size()), hilbertId);
9547
9548 // If id was not found, no mapped cell exists; continue with next cell
9549 if(lower == (hilbertIds.data() + hilbertIds.size()) || *lower != hilbertId) {
9550 continue;
9551 }
9552
9553 // Store corresponding partition cell id to grid map
9554 const MInt partitionCellId = distance(hilbertIds.data(), lower);
9555 gridMap[cellId] = partitionCellsId[partitionCellId];
9556
9557 // Determine first partition cell id
9558 firstMappedPartitionCellId = min(firstMappedPartitionCellId, partitionCellId);
9559
9560 // Increase number of mapped min-cells
9561 noMappedPartitionCells++;
9562 }
9563
9564 // First mapped cell id on this domain
9565 const MInt firstMappedCellId = (noMappedPartitionCells > 0) ? partitionCellsId[firstMappedPartitionCellId] : 0;
9566
9567 // Determine the total number of donor cells on this domain
9568 MInt noDonorCells = 0;
9569 {
9570 // Read partitionCellsNoOffsprings from donor grid
9571 MIntScratchSpace partitionCellsNoOffsprings(max(noMappedPartitionCells, 1), AT_, "partitionCellsNoOffsprings");
9572 MIntScratchSpace partitionCellsId2(noPartitionCells, AT_, "partitionCellsId2");
9573 // const MInt partitionCellOffset
9574 // = (noMappedPartitionCells > 0) ? firstMappedPartitionCellId : 0;
9575 // donorGrid.setOffset(noMappedPartitionCells, partitionCellOffset);
9576 // donorGrid.readArray(&partitionCellsNoOffsprings[0], "partitionCellsNoOffsprings");
9577 donorGrid.setOffset(noPartitionCells, 0);
9578 donorGrid.readArray(&partitionCellsId2[0], "partitionCellsGlobalId");
9579 MInt globalCount;
9580 donorGrid.getAttribute(&globalCount, "noCells");
9581 for(MInt i = 0; i < noMappedPartitionCells; i++) {
9582 MInt id = firstMappedPartitionCellId + i;
9583 MInt nextId = (id == noPartitionCells - 1) ? globalCount : partitionCellsId2[id + 1];
9584 partitionCellsNoOffsprings[i] = nextId - partitionCellsId2[id];
9585 ASSERT(partitionCellsNoOffsprings[i] > 0, "");
9586 }
9587
9588 // Sum up the number of offsprings of all partition cells on this domain
9589 for(MInt i = 0; i < noMappedPartitionCells; i++) {
9590 noDonorCells += partitionCellsNoOffsprings[i];
9591 }
9592 }
9593
9594 // Calculate the donor grid domain offset
9595 ParallelIo::size_type offset, totalCount;
9596 ParallelIo::calcOffset(noDonorCells, &offset, &totalCount, mpiComm());
9597
9598 // Read the noChildIds array of the donor grid
9599 MIntScratchSpace noChildIds(max(noDonorCells, 1), AT_, "noChildIds");
9600 donorGrid.setOffset(noDonorCells, offset);
9601 // donorGrid.readArray(&noChildIds[0], "noChildIds");
9602 vector<MUchar> cellInfo(noDonorCells);
9603 donorGrid.readArray(cellInfo.data(), "cellInfo");
9604 for(MInt i = 0; i < noDonorCells; i++) {
9605 const MUint childCnt = static_cast<MUint>(cellInfo[i]) & 15u;
9606 noChildIds[i] = (MInt)childCnt;
9607 }
9608
9609 // Storage for the number of donor cell offspring for each target cell
9610 MIntScratchSpace gridMapNoOffspring(noCells, AT_, "gridMapNoOffspring");
9611 fill(gridMapNoOffspring.begin(), gridMapNoOffspring.end(), 0);
9612
9613 // Auxiliary method: Return the number of offspring, i.e. child cells,
9614 // child-child cells etc., for a given cell based on a list of number of
9615 // children for each cell, ordered depth-first. For a cell without children, 0
9616 // is returned.
9617 auto getNoOffspring = [](const MInt cellId, const MInt* const noChilds) {
9618 // Initialize with the number of child cells of considered cell
9619 MInt noOffspring = noChilds[cellId];
9620
9621 // Loop until all childs of childs of ... are taken into account
9622 // If there are only leaf cells left 'noOffspring' wont increase any more
9623 // since the number of children is zero from there on and the loop will end.
9624 MInt offspringId = 1;
9625 while(offspringId <= noOffspring) {
9626 noOffspring += noChilds[cellId + offspringId];
9627 offspringId++;
9628 }
9629 return noOffspring;
9630 };
9631
9632 // Determine all matching offspring cells of the mapped partition cells
9633 MInt noOneToMultipleMappings = 0;
9634 for(MInt cellId = 0; cellId < noCells;) {
9635 // Check if this is a partition cell with mapped, corresponding donor cell and
9636 // continue with the next cell if not. Non-partition cells will never be
9637 // considered as the else-condition below will take care of them.
9638 MInt donorGlobalCellId = gridMap[cellId];
9639 if(donorGlobalCellId == -1) {
9640 cellId++;
9641 continue;
9642 }
9643
9644 // Store child cells in grid map
9645 if(a_noChildren(cellId) == 0) {
9646 // No child cells on target grid, store the number of offspring on the
9647 // donor grid (0 if the donor cell has no children)
9648 const MInt donorLocalCellId = donorGlobalCellId - firstMappedCellId;
9649 gridMapNoOffspring[cellId] = getNoOffspring(donorLocalCellId, &noChildIds[0]);
9650
9651 // Continue with next cell
9652 cellId++;
9653 } else {
9654 // partition cell on target grid has children
9655 // Loop as long as the current cell is a descendant of the considered min
9656 // cell
9657 const MInt partitionCellLevel = a_level(cellId);
9658 // 'cellId' belongs to the partition cell, check the next cell in the first
9659 // iteration
9660 MInt currentCellId = cellId + 1;
9661
9662 while(currentCellId < noCells && a_level(currentCellId) > partitionCellLevel) {
9663 MInt donorLocalCellId = donorGlobalCellId - firstMappedCellId;
9664 // No child cells on the donor grid
9665 if(noChildIds[donorLocalCellId] == 0) {
9666 // Store mapping for matching cell
9667 gridMap[cellId] = donorGlobalCellId;
9668 const MInt parentLevel = a_level(cellId);
9669
9670 // Continue with next cell, this will either be a child of the last
9671 // cell, i.e. there is a one-to-many mapping, or it has the same or a
9672 // lower level, i.e. the last cell belongs to a one-to-one mapping.
9673 cellId++;
9674 // Loop over all offspring of the current target cell if there are any
9675 while(cellId < noCells && a_level(cellId) > parentLevel) {
9676 gridMap[cellId] = donorGlobalCellId; // The same for all offspring
9677 // Use -1 to indicate 'no additional donor cell' for this cell
9678 gridMapNoOffspring[cellId] = -1;
9679 noOneToMultipleMappings++;
9680
9681 // Continue with next cell
9682 cellId++;
9683 }
9684 // Move on to the next donor cell (one-to-one or one-to-many finished)
9685 donorGlobalCellId++;
9686 } else { // Donor cell has children
9687 if(a_noChildren(cellId) == 0) {
9688 // Target cell has no children, i.e. many-to-one mapping
9689 gridMap[cellId] = donorGlobalCellId;
9690
9691 // Store number of offspring on the donor grid
9692 donorLocalCellId = donorGlobalCellId - firstMappedCellId;
9693 const MInt noOffspring = getNoOffspring(donorLocalCellId, &noChildIds[0]);
9694 gridMapNoOffspring[cellId] = noOffspring;
9695
9696 // Increase the global donor cell id by the number of mapped cells
9697 donorGlobalCellId += noOffspring + 1;
9698 } else { // Both cells have children
9699 // Store mapping and move to next donor cell (and next target cell)
9700 gridMap[cellId] = donorGlobalCellId;
9701 donorGlobalCellId++;
9702 }
9703 // Continue with next cell
9704 cellId++;
9705 }
9706 // Set current cell id which to check in while() loop if it is still a
9707 // descendant of the considered partition cell
9708 currentCellId = cellId;
9709 }
9710 }
9711 }
9712
9713 // Open grid map file and write to it
9714 ParallelIo gridMapFile(gridMapFileName, PIO_REPLACE, mpiComm());
9715 gridMapFile.setAttribute(donorGridFileName, "donorGridFileName");
9716 gridMapFile.setAttribute(m_gridInputFileName, "gridFile");
9717 gridMapFile.defineScalar(PIO_INT, "noCells");
9718 gridMapFile.defineArray(PIO_INT, "cellIds", m_domainOffsets[noDomains()]);
9719 gridMapFile.defineArray(PIO_INT, "noOffspring", m_domainOffsets[noDomains()]);
9720 gridMapFile.setOffset(noCells, m_domainOffsets[domainId()]);
9721 gridMapFile.writeScalar(m_domainOffsets[noDomains()], "noCells");
9722 gridMapFile.writeArray(&gridMap[0], "cellIds");
9723 gridMapFile.writeArray(&gridMapNoOffspring[0], "noOffspring");
9724
9725 m_log << "Created & saved grid map file." << endl;
9726 if(noOneToMultipleMappings > 0) {
9727 m_log << "WARNING: There were " << noOneToMultipleMappings
9728 << " cells on the target grid that are finer than the "
9729 "corresponding donor cell."
9730 << endl;
9731 }
9732}
9733
9734
9746template <MInt nDim>
9747void CartesianGrid<nDim>::saveDonorGridPartition(const MString& gridMapFileName, const MString& gridPartitionFileName) {
9748 TRACE();
9749
9750 using namespace maia::parallel_io;
9751
9752 // Open grid map file and read mapping information
9753 ParallelIo gridMapFile(gridMapFileName, PIO_READ, mpiComm());
9754 MString donorGridFileName;
9755 gridMapFile.getAttribute(&donorGridFileName, "donorGridFileName");
9756 gridMapFile.setOffset(m_noInternalCells, m_domainOffsets[domainId()]);
9757 MIntScratchSpace gridMap(m_noInternalCells, AT_, "gridMap");
9758 gridMapFile.readArray(&gridMap[0], "cellIds");
9759
9760 m_log << "Saving grid partition for donor grid " << donorGridFileName << " and writing the results to "
9761 << gridPartitionFileName << "..." << endl;
9762
9763 // Determine global cell id of first mapped cell on this domain
9764 auto c = find_if(gridMap.begin(), gridMap.end(), [](const MInt a) { return a != -1; });
9765 const MInt cellId = (c == end(gridMap)) ? -1 : *c;
9766
9767 // Open grid partition file and write to it
9768 ParallelIo file(gridPartitionFileName, PIO_REPLACE, mpiComm());
9769 file.setAttribute(m_gridInputFileName, "targetGridFileName");
9770 file.setAttribute(donorGridFileName, "gridFile");
9771 file.defineArray(PIO_INT, "firstCellIds", noDomains());
9772 file.setOffset(1, domainId());
9773 file.writeArray(&cellId, "firstCellIds");
9774
9775 m_log << "Saved grid partition file." << endl;
9776}
9777
9778
9793template <MInt nDim>
9794void CartesianGrid<nDim>::loadDonorGridPartition(const MLong* const partitionCellsId, const MInt noPartitionCells) {
9795 TRACE();
9796
9797 MLongScratchSpace partitionCellOffsets(noDomains() + 1, AT_, "partitionCellOffsets");
9798
9799 if(domainId() == 0) {
9800 MString gridPartitionFileName = m_outputDir + "partition.Netcdf";
9801 gridPartitionFileName = Context::getBasicProperty<MString>("gridPartitionFileName", AT_, &gridPartitionFileName);
9802
9803 // Open grid partition file and read out first cell ids for each domain
9804 using namespace maia::parallel_io;
9805 ParallelIo file(gridPartitionFileName, PIO_READ, MPI_COMM_SELF);
9806 const MInt noTargetDomains = file.getArraySize("firstCellIds");
9807 file.setOffset(noTargetDomains, 0);
9808 MIntScratchSpace firstCellIds(noTargetDomains, AT_, "firstCellIds");
9809 file.readArray(&firstCellIds[0], "firstCellIds");
9810
9811 // Sanity check: make sure that first cell ids are monotonically increasing
9812 MInt previous = -1;
9813 for(auto&& first : firstCellIds) {
9814 // Skip if first id is -1 as it means there are no cells on that domain
9815 if(first == -1) {
9816 continue;
9817 }
9818
9819 // Abort if cell id is less than previous one
9820 if(first < previous) {
9821 TERMM(1, "Cell ids not monotonically increasing: " + to_string(first) + " < " + to_string(previous)
9822 + ". Did you forget to set 'targetGridFileName' when "
9823 "generating the donor grid file?");
9824 }
9825
9826 // Store id for next iteration
9827 previous = first;
9828 }
9829
9830 // Determine partition cell offsets. For each domain, find the position of the first
9831 // cell id in the list of mincell ids. This position is the desired offset.
9832 // First, store pointers for convenience
9833 const MLong* first = partitionCellsId;
9834 const MLong* const last = first + noPartitionCells;
9835 MInt donorDomainId = 0;
9836 for(MInt d = 0; d < noTargetDomains; d++) {
9837 // Skip target domain if it does not receive donor cells
9838 const MInt firstCellId = firstCellIds[d];
9839 if(firstCellId == -1) {
9840 continue;
9841 }
9842
9843 // Search list of partition cells for first cell id and store pointer
9844 const auto bound = lower_bound(first, last, firstCellId);
9845 if(distance(bound, last) == 0) {
9846 // If value was not found, somewhere (here or in the generation of the
9847 // grid partition file) an error must have occurred
9848 TERMM(1, "partition cell not found, this should not happen. Go fix your code!");
9849 } else {
9850 // If value was found, store the distance between the beginning of the
9851 // array and the found value as the offset that is searched for
9852 partitionCellOffsets[donorDomainId] = distance(partitionCellsId, bound);
9853
9854 // Reduce search space for increased efficiency
9855 first = bound;
9856
9857 // Increment domain id
9858 donorDomainId++;
9859 }
9860 }
9861
9862 partitionCellOffsets[noDomains()] = noPartitionCells;
9863 m_log << "Cartesian grid loaded grid partition from " << gridPartitionFileName << endl;
9864
9865 m_domainOffsets[0] = 0;
9866 for(MInt i = 1; i < noDomains(); i++) {
9867 m_domainOffsets[i] = partitionCellsId[partitionCellOffsets[i]];
9868 }
9869 m_domainOffsets[noDomains()] = m_noCellsGlobal;
9870
9871 // Sanity check: donorDomainId must be equal to number of domains after last
9872 // iteration
9873 if(donorDomainId != noDomains()) {
9874 TERMM(1, "mismatch between number of donor domains in partition file and "
9875 "actual number of "
9876 "donor domains");
9877 }
9878 }
9879 MPI_Bcast(partitionCellOffsets.data(), noDomains() + 1, MPI_LONG, 0, mpiComm(), AT_, "partitionCellOffsets.data()");
9880 MPI_Bcast(m_domainOffsets, noDomains() + 1, MPI_LONG, 0, mpiComm(), AT_, "m_domainOffsets");
9881 m_noPartitionCells = partitionCellOffsets[domainId() + 1] - partitionCellOffsets[domainId()];
9882 m_localPartitionCellOffsets[0] =
9883 partitionCellOffsets[domainId()]; // begin of the local partitionCells in the gobal partitionCell array
9884 m_localPartitionCellOffsets[1] = partitionCellOffsets[domainId() + 1]; // end index of the gobal partitionCell array
9885 m_localPartitionCellOffsets[2] =
9886 m_noPartitionCellsGlobal; // end of the local partitionCells in the gobal partitionCell array
9887 m_noInternalCells = m_domainOffsets[domainId() + 1] - m_domainOffsets[domainId()];
9888}
9889
9890
9892template <MInt nDim>
9894 TRACE();
9895
9896 std::map<MLong, MInt>().swap(m_globalToLocalId);
9897 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
9898 // multiple periodic cells with identical globalId may appear
9899 // if neccessary, m_globalToLocalId should be changed to a multimap
9900 if(a_hasProperty(cellId, Cell::IsPeriodic)) continue;
9901
9902 if(a_hasProperty(cellId, Cell::IsToDelete)) continue;
9903
9904 if(a_globalId(cellId) > -1) {
9905 ASSERT(m_globalToLocalId.count(a_globalId(cellId)) == 0,
9906 "Global id already in map: " + std::to_string(a_globalId(cellId)) + " " + std::to_string(cellId) + " "
9907 + std::to_string(m_globalToLocalId[a_globalId(cellId)]));
9908 m_globalToLocalId[a_globalId(cellId)] = cellId;
9909 } else if(!a_isToDelete(cellId)) {
9910 TERMM(1,
9911 "Error: invalid global id for cell #" + std::to_string(cellId) + ": " + std::to_string(a_globalId(cellId)));
9912 }
9913 }
9914}
9915
9916
9917// -----------------------------------------------------------------------------
9925template <MInt nDim>
9927 TRACE();
9928
9929 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
9930 // local childIds
9931 for(MInt childId = 0; childId < m_maxNoChilds; childId++) {
9932 if(a_childId(cellId, childId) == -1) {
9933 continue;
9934 }
9935
9936 if(m_globalToLocalId.count(a_childId(cellId, childId)) > 0) {
9937 a_childId(cellId, childId) = m_globalToLocalId[a_childId(cellId, childId)];
9938 } else {
9939 a_childId(cellId, childId) = -1;
9940 }
9941 }
9942 // this is commented out, because the noChildIds of the non leaf level halo cells on the second layer would be
9943 // corrected here to zero such that certain parts of the code will use different neighbor and surface informations
9944 // especially at the cut boundaries. This leads to serial-parallel inconsistencies because m_noChildIds is used as a
9945 // trigger for some algorithms
9946 /*
9947 a_noChildren( cellId ) = 0;
9948 for (MInt childId = 0; childId < m_maxNoChilds; childId++) {
9949 if (a_childId( cellId , childId ) > -1) {
9950 a_noChildren( cellId )++;
9951 }
9952 }
9953 */
9954 // local neighbor Ids
9955 for(MInt dirId = 0; dirId < m_noDirs; dirId++) {
9956 if(a_neighborId(cellId, dirId) > -1) {
9957 if(m_globalToLocalId.count(a_neighborId(cellId, dirId)) > 0) {
9958 a_neighborId(cellId, dirId) = m_globalToLocalId[a_neighborId(cellId, dirId)];
9959 } else {
9960 a_neighborId(cellId, dirId) = -1;
9961 }
9962 }
9963 }
9964
9965 // local parentId
9966 if(a_parentId(cellId) == -1) {
9967 a_parentId(cellId) = -1;
9968 } else {
9969 if(m_globalToLocalId.count(a_parentId(cellId)) > 0) {
9970 a_parentId(cellId) = m_globalToLocalId[a_parentId(cellId)];
9971 } else {
9972 a_parentId(cellId) = -1;
9973 }
9974 }
9975 }
9976}
9977
9978
9979// -----------------------------------------------------------------------------
9980
9981
9983template <MInt nDim>
9985 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
9986 if(a_parentId(cellId) > -1) {
9987 a_parentId(cellId) = a_globalId(static_cast<MInt>(a_parentId(cellId)));
9988 }
9989
9990 for(MInt i = 0; i < m_noDirs; i++) {
9991 if(a_hasNeighbor(cellId, i)) {
9992 a_neighborId(cellId, i) = a_globalId(static_cast<MInt>(a_neighborId(cellId, i)));
9993 }
9994 }
9995
9996 for(MInt i = 0; i < IPOW2(nDim); i++) {
9997 if(a_childId(cellId, i) > -1) {
9998 a_childId(cellId, i) = a_globalId(static_cast<MInt>(a_childId(cellId, i)));
9999 }
10000 }
10001 }
10002}
10003
10004
10011template <MInt nDim>
10013 // Update list of min-level cell ids (required later)
10014 storeMinLevelCells();
10015
10016 // Update number of internal cells
10017 m_noInternalCells = 0;
10018 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
10019 if(a_hasProperty(cellId, Cell::IsHalo)) continue;
10020 if(a_isToDelete(cellId)) continue;
10021 m_noInternalCells++;
10022 }
10023
10024 // Exchange number of internal cells per domain
10025 const MInt noCells = m_noInternalCells;
10026 MIntScratchSpace noCellsPerDomain(noDomains(), AT_, "noCellsPerDomain");
10027 MPI_Allgather(&noCells, 1, type_traits<MInt>::mpiType(), &noCellsPerDomain[0], 1, type_traits<MInt>::mpiType(),
10028 mpiComm(), AT_, "noCells", "noCellsPerDomain[0]");
10029 ASSERT(noCellsPerDomain[domainId()] == noCells, "Local number of cells does not match.");
10030
10031 if(m_domainOffsets == nullptr) {
10032 mAlloc(m_domainOffsets, noDomains() + 1, "m_domainOffsets", 0L, AT_);
10033 }
10034
10035 // Re-calculate domain offsets
10036 m_domainOffsets[0] = m_32BitOffset;
10037 for(MInt d = 0; d < noDomains(); d++) {
10038 m_domainOffsets[d + 1] = m_domainOffsets[d] + static_cast<MLong>(noCellsPerDomain[d]);
10039 }
10040
10041 m_noCellsGlobal = m_domainOffsets[noDomains()] - m_32BitOffset;
10042
10043 // Note: requirement is that min-level cells are stored, done above!
10044 const MInt firstMinLvlCell = m_minLevelCells[0];
10045 MInt localCnt = 0;
10046
10047 // Check the first min-level cell: if it is a partition-level ancestor and a halo it is the root
10048 // cell of the first local partition cell. The subtree starting from that min-level halo cell with
10049 // all locally relevant partition-level ancestor cells is contained in the local tree,
10050 // descendStoreGlobalId will skip any halo cell and will assign the first local cell the global id
10051 // corresponding to the domain offset and then continue with all offspring cells.
10052 if(a_hasProperty(firstMinLvlCell, Cell::IsPartLvlAncestor) && a_hasProperty(firstMinLvlCell, Cell::IsHalo)) {
10053 descendStoreGlobalId(firstMinLvlCell, localCnt);
10054 }
10055
10056 // Re-calculate global ids for all internal cells starting from the min-level cells
10057 // Iteration starts at 1 if first min-lvl cell is a halo partLvlAncestor (see above)
10058 for(MUint i = std::max(0, std::min(1, localCnt)); i < m_minLevelCells.size(); i++) {
10059 const MInt cellId = m_minLevelCells[i];
10060
10061 // skip halos, the only relevant halo min-level cell in case of a partition level shift is
10062 // handled above
10063 if(a_hasProperty(cellId, Cell::IsHalo)) continue;
10064 descendStoreGlobalId(cellId, localCnt);
10065 }
10066
10067 // Update global ids for all halo cells
10068 if(noNeighborDomains() > 0) {
10069 ScratchSpace<MLong> globalId(m_tree.size(), AT_, "globalId");
10070 for(MInt i = 0; i < noNeighborDomains(); i++) {
10071 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
10072 MInt cellId = m_windowCells[i][j];
10073 globalId(cellId) = a_globalId(cellId);
10074 ASSERT(globalId(cellId) >= m_domainOffsets[domainId()] && globalId(cellId) < m_domainOffsets[domainId() + 1],
10075 "");
10076 }
10077 }
10078 maia::mpi::exchangeData(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), &globalId[0], 1);
10079
10080 for(MInt i = 0; i < noNeighborDomains(); i++) {
10081 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
10082 MInt cellId = m_haloCells[i][j];
10083 a_globalId(cellId) = globalId(cellId);
10084 }
10085 }
10086 }
10087
10088 if(m_azimuthalPer && noAzimuthalNeighborDomains() > 0) {
10089 ScratchSpace<MLong> globalId(m_tree.size(), AT_, "globalId");
10090 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
10091 for(MInt j = 0; j < (signed)m_azimuthalWindowCells[i].size(); j++) {
10092 MInt cellId = m_azimuthalWindowCells[i][j];
10093 globalId(cellId) = a_globalId(cellId);
10094 ASSERT(globalId(cellId) >= m_domainOffsets[domainId()] && globalId(cellId) < m_domainOffsets[domainId() + 1],
10095 "");
10096 }
10097 }
10098 maia::mpi::exchangeData(m_azimuthalNghbrDomains, m_azimuthalHaloCells, m_azimuthalWindowCells, mpiComm(),
10099 &globalId[0], 1);
10100 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
10101 for(MInt j = 0; j < (signed)m_azimuthalHaloCells[i].size(); j++) {
10102 MInt cellId = m_azimuthalHaloCells[i][j];
10103 a_globalId(cellId) = globalId(cellId);
10104 }
10105 }
10106 }
10107
10108 // Update the global to local id mapping
10109 createGlobalToLocalIdMapping();
10110
10111 // Note: when updating the partition cells it can be possible that a rank currently does not have
10112 // a partition cell anymore
10113 TERMM_IF_NOT_COND(m_noPartitionCells > 0 || m_updatingPartitionCells,
10114 "Error: number of partition cells needs to be at least 1.");
10115
10116 updatePartitionCellInformation();
10117}
10118
10119
10121template <MInt nDim>
10123 // update partition cell global ids
10124 MInt noLocalPartitionCells = 0;
10125 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
10126 if(a_isToDelete(cellId)) {
10127 continue;
10128 }
10129 if(a_hasProperty(cellId, Cell::IsHalo)) {
10130 continue;
10131 }
10132
10133 if(a_hasProperty(cellId, Cell::IsPartitionCell)) {
10134 m_localPartitionCellGlobalIds[noLocalPartitionCells] = a_globalId(cellId);
10135 noLocalPartitionCells++;
10136 }
10137 }
10138
10139 if(noLocalPartitionCells == 0 && !m_updatingPartitionCells) {
10140 TERMM(1, "noLocalPartitionCells == 0");
10141 return;
10142 }
10143
10144 if(noLocalPartitionCells != m_noPartitionCells) {
10145 TERMM(-1, "Mismatch in partitionCells " + to_string(m_noPartitionCells) + "/" + to_string(noLocalPartitionCells));
10146 }
10147
10148 // sort by globalIds
10149 sort(m_localPartitionCellGlobalIds, m_localPartitionCellGlobalIds + noLocalPartitionCells);
10150
10151 // determine offset within GLOBAL!!! partition cells
10152 MIntScratchSpace localPartitionCellCounts(noDomains(), AT_, "localPartitionCellCounts");
10153 MPI_Allgather(&noLocalPartitionCells, 1, MPI_INT, &localPartitionCellCounts[0], 1, MPI_INT, mpiComm(), AT_,
10154 "noLocalPartitionCells", "localPartitionCellCounts[0]");
10155
10156 // sum up all previous domains partition cells
10157 MInt offset = 0;
10158 for(MInt dId = 0; dId < domainId(); dId++) {
10159 offset += localPartitionCellCounts[dId];
10160 }
10161
10162 // Set local partition cell offset, the next offset and the number of global partition cells
10163 m_localPartitionCellOffsets[0] = offset;
10164 m_localPartitionCellOffsets[1] = m_localPartitionCellOffsets[0] + noLocalPartitionCells;
10165 m_localPartitionCellOffsets[2] = m_noPartitionCellsGlobal;
10166
10167 // Create list of partition cell local ids (also ordered by global id)
10168 for(MInt i = 0; i < m_noPartitionCells; i++) {
10169 m_localPartitionCellLocalIds[i] = globalIdToLocalId(m_localPartitionCellGlobalIds[i]);
10170 }
10171}
10172
10173
10184template <MInt nDim>
10186 // Update global id (unless it is a halo cell)
10187 if(!a_hasProperty(cellId, Cell::IsHalo)) {
10188 ASSERT(localCnt < m_noInternalCells, "");
10189 a_globalId(cellId) = m_domainOffsets[domainId()] + localCnt++;
10190 ASSERT(a_globalId(cellId) >= m_domainOffsets[domainId()] && a_globalId(cellId) < m_domainOffsets[domainId() + 1],
10191 "Error: global id outside of global id range for this domain.");
10192 }
10193
10194 // Descend tree to all children
10195 for(MInt child = 0; child < ipow(2, nDim); child++) {
10196 if(a_childId(cellId, child) < 0) {
10197 continue;
10198 }
10199 descendStoreGlobalId(a_childId(cellId, child), localCnt);
10200 }
10201}
10202
10203
10207template <MInt nDim>
10208void CartesianGrid<nDim>::descendNoOffsprings(const MLong cellId, const MLong offset) {
10209 ASSERT(!a_hasProperty(cellId, Cell::IsPartLvlAncestor), "not supposed to be called for a partition level ancestor");
10210 a_noOffsprings(cellId) = 1;
10211 a_workload(cellId) = a_weight(cellId);
10212 if(a_noChildren(cellId) > 0) {
10213 for(MInt child = 0; child < ipow(2, nDim); child++) {
10214 if(a_childId(cellId, child) < 0) continue;
10215 MLong childId = a_childId(cellId, child) - offset;
10216 descendNoOffsprings(childId, offset);
10217 a_noOffsprings(cellId) += a_noOffsprings(childId);
10218 a_workload(cellId) += a_workload(childId);
10219 }
10220 }
10221}
10222
10223
10228template <MInt nDim>
10230 // Create map from hilbert id to all internal min cells
10231 m_minLevelCells.clear();
10232 map<MLong, MInt> minLevelCells;
10233 const MInt minLevel = updateMinlevel ? m_newMinLevel : m_minLevel;
10234 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
10235 if(a_hasProperty(cellId, Cell::IsHalo)) continue; // min-level halo partition level ancestor handled below
10236 if(a_isToDelete(cellId)) continue;
10237 if(a_level(cellId) == minLevel) {
10238 const MLong hilbertId =
10239 updateMinlevel ? generateHilbertIndex(cellId, m_newMinLevel) : generateHilbertIndex(cellId);
10240 TERMM_IF_COND(minLevelCells.count(hilbertId), "Error: duplicate hilbertId.");
10241 minLevelCells.insert(pair<MLong, MInt>(hilbertId, cellId));
10242 }
10243 }
10244
10245 MBool found = false;
10246 MInt minLvlHaloPartLvlAncestor = -1;
10247 for(auto& i : m_partitionLevelAncestorIds) {
10248 TERMM_IF_NOT_COND(a_hasProperty(i, Cell::IsPartLvlAncestor),
10249 "cell is not a partition level ancestor: " + std::to_string(i));
10250 TERMM_IF_COND(a_isToDelete(i), "Error: partition level ancestor marked for deletion.");
10251 // If there is a min-level halo partition level ancestor this is the min-level cell for the
10252 // first partition cell on this domain, it will be stored in m_minLevelCells as first entry
10253 // (lowest hilbert id)!
10254 if(a_level(i) == minLevel && a_hasProperty(i, Cell::IsHalo)) {
10255 TERMM_IF_COND(found, "there should only be a single min-level halo partition level ancestor "
10256 "in the list of partitionLevelAncestorIds! Other halo cells with "
10257 "these properties shouldnt be in that list!");
10258 const MInt hilbertId = updateMinlevel ? generateHilbertIndex(i, m_newMinLevel) : generateHilbertIndex(i);
10259 TERMM_IF_COND(minLevelCells.count(hilbertId), "duplicate hilbertId.");
10260 minLevelCells.insert(pair<MInt, MInt>(hilbertId, i));
10261 minLvlHaloPartLvlAncestor = i;
10262 found = true;
10263
10264 // DEBUG
10265 /* m_log << "found a min-level halo partition level ancestor: " << i << " " << hilbertId */
10266 /* << std::endl; */
10267 }
10268 }
10269
10270 for(auto& minLevelCell : minLevelCells) {
10271 m_minLevelCells.push_back(minLevelCell.second);
10272 }
10273
10274 // add halo min level cells to the back
10275 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
10276 // Skip the min-level halo partition level ancestor which was already added as a first entry to
10277 // m_minLevelCells above
10278 if(found && cellId == minLvlHaloPartLvlAncestor) continue;
10279
10280 if(a_level(cellId) == minLevel && a_hasProperty(cellId, Cell::IsHalo)) {
10281 TERMM_IF_COND(a_isToDelete(cellId), "Error: min-level halo marked for deletion.");
10282 m_minLevelCells.push_back(cellId);
10283 }
10284 }
10285}
10286
10287
10292template <MInt nDim>
10294 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
10295 m_tree.resetIsLeafCell(cellId);
10296 }
10297 for(MInt cellId = 0; cellId < m_noInternalCells; cellId++) {
10298 for(MInt solverId = 0; solverId < m_tree.noSolvers(); solverId++) {
10299 if(m_tree.solver(cellId, solverId)) {
10300 a_isLeafCell(cellId, solverId) =
10301 !a_hasChildren(cellId, solverId) && !a_hasProperty(cellId, Cell::IsPartLvlAncestor);
10302 }
10303 }
10304 }
10305
10306 if(noNeighborDomains() > 0) {
10307 maia::mpi::exchangeBitset(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), &m_tree.leafCellBits(0),
10308 m_tree.size());
10309 }
10310
10311 if(m_azimuthalPer) {
10312 if(noAzimuthalNeighborDomains() > 0) {
10313 maia::mpi::exchangeBitset(m_azimuthalNghbrDomains, m_azimuthalHaloCells, m_azimuthalWindowCells, mpiComm(),
10314 &m_tree.leafCellBits(0), m_tree.size());
10315 }
10316
10317 // Since connection between two different cell levels is possible in
10318 // azimuthal periodicity. Azimuthal halos need to be adjusted.
10319 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
10320 for(MInt j = 0; j < (signed)m_azimuthalHaloCells[i].size(); j++) {
10321 MInt cellId = azimuthalHaloCell(i, j);
10322 for(MInt solverId = 0; solverId < m_tree.noSolvers(); solverId++) {
10323 if(m_tree.solver(cellId, solverId)) {
10324 a_isLeafCell(cellId, solverId) =
10325 !a_hasChildren(cellId, solverId) && !a_hasProperty(cellId, Cell::IsPartLvlAncestor);
10326 }
10327 }
10328 }
10329 }
10330 for(MInt i = 0; i < noAzimuthalUnmappedHaloCells(); i++) {
10331 MInt cellId = azimuthalUnmappedHaloCell(i);
10332 for(MInt solverId = 0; solverId < m_tree.noSolvers(); solverId++) {
10333 if(m_tree.solver(cellId, solverId)) {
10334 a_isLeafCell(cellId, solverId) =
10335 !a_hasChildren(cellId, solverId) && !a_hasProperty(cellId, Cell::IsPartLvlAncestor);
10336 }
10337 }
10338 }
10339 }
10340}
10341
10342
10347template <MInt nDim>
10348void CartesianGrid<nDim>::balance(const MInt* const noCellsToReceiveByDomain,
10349 const MInt* const noCellsToSendByDomain,
10350 const MInt* const sortedCellId,
10351 const MLong* const partitionCellOffsets,
10352 const MLong* const globalIdOffsets) {
10353 TRACE();
10354
10355 const MInt noCells = m_tree.size();
10356 const MInt receiveSize = noCellsToReceiveByDomain[noDomains()];
10357
10358 // Note: cellInfo needs to be communicated since it cannot be fully reconstructed from the
10359 // communicated data but it is required to communicate the partition level ancestor data and
10360 // setup the missing subtrees of the grid
10361 ScratchSpace<MUchar> cellInfo(receiveSize, AT_, "cellInfo");
10362 {
10363 // Temporary buffers
10364 ScratchSpace<MUint> cellInfoSend(noCells, AT_, "cellInfoSend");
10365 ScratchSpace<MUint> cellInfoRecv(receiveSize, AT_, "cellInfoRecv");
10366
10367 // TODO labels:GRID,DLB since the cell info needs to be communicated maybe some of the other grid information is
10368 // not required anymore if the tree is rebuild with the cell info?
10369 for(MInt i = 0; i < noCells; i++) {
10370 // TODO labels:GRID,DLB make cellInfo computation a function that can be reused?
10371 const MUint noChilds = (MUint)a_noChildren(i);
10372 const MUint isMinLevel = (a_level(i) == m_minLevel);
10373 const MInt parent = m_globalToLocalId[a_parentId(i)];
10374 MUint position = 0;
10375 if(parent > -1) {
10376 for(MUint j = 0; j < (unsigned)m_maxNoChilds; j++) {
10377 if(a_childId(parent, j) == a_globalId(i)) {
10378 position = j;
10379 }
10380 }
10381 }
10382 const MUint tmpBit = noChilds | (position << 4) | (isMinLevel << 7);
10383 cellInfoSend[i] = tmpBit;
10384 }
10385
10386 // Redistribute cell info
10387 maia::mpi::communicateData(&cellInfoSend[0], noCells, sortedCellId, noDomains(), domainId(), mpiComm(),
10388 noCellsToSendByDomain, noCellsToReceiveByDomain, 1, &cellInfoRecv[0]);
10389
10390 for(MInt i = 0; i < receiveSize; i++) {
10391 // Convert cell info
10392 cellInfo[i] = static_cast<MUchar>(cellInfoRecv[i]);
10393 }
10394 }
10395
10396
10397 // ScratchSpaces to hold cell datas
10398 MLongScratchSpace parentId(receiveSize, FUN_, "parentId");
10399 MLongScratchSpace childIds(receiveSize, IPOW2(nDim), FUN_, "childIds");
10400 MLongScratchSpace nghbrIds(receiveSize, m_noDirs, FUN_, "nghbrIds");
10401 MLongScratchSpace globalId(receiveSize, FUN_, "globalId");
10402 MIntScratchSpace level(receiveSize, FUN_, "level");
10403
10404 MFloatScratchSpace coordinates(receiveSize, nDim, FUN_, "coordinates");
10405 MFloatScratchSpace weight(receiveSize, FUN_, "weight");
10406
10407 ScratchSpace<maia::grid::tree::SolverBitsetType> solver(receiveSize, FUN_, "solver");
10408 ScratchSpace<maia::grid::tree::PropertyBitsetType> properties(receiveSize, AT_, "properties");
10409
10410 // Reset ScratchSpaces
10411 parentId.fill(-1);
10412 childIds.fill(-1);
10413 nghbrIds.fill(-1);
10414 globalId.fill(-1);
10415 level.fill(-1);
10416
10417 coordinates.fill(-1.0);
10418 weight.fill(1.0);
10419
10422
10423 // Communicate Cell Data
10424 maia::mpi::communicateData(&a_parentId(0), noCells, sortedCellId, noDomains(), domainId(), mpiComm(),
10425 noCellsToSendByDomain, noCellsToReceiveByDomain, 1, &parentId[0]);
10426 maia::mpi::communicateData(&a_childId(0, 0), noCells, sortedCellId, noDomains(), domainId(), mpiComm(),
10427 noCellsToSendByDomain, noCellsToReceiveByDomain, IPOW2(nDim), &childIds[0]);
10428 maia::mpi::communicateData(&a_neighborId(0, 0), noCells, sortedCellId, noDomains(), domainId(), mpiComm(),
10429 noCellsToSendByDomain, noCellsToReceiveByDomain, m_noDirs, &nghbrIds[0]);
10430 maia::mpi::communicateData(&a_level(0), noCells, sortedCellId, noDomains(), domainId(), mpiComm(),
10431 noCellsToSendByDomain, noCellsToReceiveByDomain, 1, &level[0]);
10432 maia::mpi::communicateData(&a_globalId(0), noCells, sortedCellId, noDomains(), domainId(), mpiComm(),
10433 noCellsToSendByDomain, noCellsToReceiveByDomain, 1, &globalId[0]);
10434 maia::mpi::communicateData(&a_coordinate(0, 0), noCells, sortedCellId, noDomains(), domainId(), mpiComm(),
10435 noCellsToSendByDomain, noCellsToReceiveByDomain, nDim, &coordinates[0]);
10436 maia::mpi::communicateData(&a_weight(0), noCells, sortedCellId, noDomains(), domainId(), mpiComm(),
10437 noCellsToSendByDomain, noCellsToReceiveByDomain, 1, &weight[0]);
10438 maia::mpi::communicateBitsetData(&m_tree.solverBits(0), noCells, sortedCellId, noDomains(), domainId(), mpiComm(),
10439 noCellsToSendByDomain, noCellsToReceiveByDomain, 1, &solver[0]);
10440
10441 maia::mpi::communicateBitsetData(&m_tree.properties(0), noCells, sortedCellId, noDomains(), domainId(), mpiComm(),
10442 noCellsToSendByDomain, noCellsToReceiveByDomain, 1, &properties[0]);
10443
10444 std::map<MLong, MInt>().swap(m_globalToLocalId);
10445
10446 // Reset tree
10447 m_tree.clear();
10448
10449 // Storage for min-level cell information
10450 vector<MInt> localMinLevelCells(0);
10451 vector<MLong> minLevelCellsTreeId(0);
10452 ScratchSpace<MInt> localMinLevelId(receiveSize, AT_, "localMinLevelId");
10453 localMinLevelId.fill(-1);
10454
10455 vector<MLong> partitionCells(0);
10456
10457 // Iterate over all received cells and add each cell individually (cells are sorted by global id).
10458 for(MInt i = 0; i < receiveSize; i++) {
10459 // Determine cell id and append to collector
10460 const MInt cellId = m_tree.size();
10461 if(globalIdOffsets[domainId()] + (MLong)cellId != globalId(i)) {
10462 TERMM(1,
10463 "Global id mismatch." + to_string(m_domainOffsets[domainId()] + (MLong)cellId) + " "
10464 + to_string(globalId(i)));
10465 }
10466 m_tree.append();
10467
10468 // Set tree information
10469 a_parentId(cellId) = parentId(i);
10470 for(MInt j = 0; j < IPOW2(nDim); j++) {
10471 a_childId(cellId, j) = childIds(i, j);
10472 }
10473 for(MInt j = 0; j < m_noDirs; j++) {
10474 a_neighborId(cellId, j) = nghbrIds(i, j);
10475 }
10476 a_globalId(cellId) = globalId(i);
10477 a_level(cellId) = level(i);
10478 for(MInt j = 0; j < nDim; j++) {
10479 a_coordinate(cellId, j) = coordinates(i, j);
10480 }
10481 a_weight(cellId) = weight(i);
10482 m_tree.solverBits(cellId) = solver(i);
10483
10484 // Reset offsprings/workload and rebuild list of min cells
10485 a_noOffsprings(cellId) = 0;
10486 a_workload(cellId) = F0;
10487
10488 // Store min-level cell information
10489 if(a_level(cellId) == m_minLevel) {
10490 localMinLevelId[i] = (MInt)localMinLevelCells.size();
10491 localMinLevelCells.push_back(i);
10492
10493 MLong treeId = -1;
10494 maia::grid::hilbert::coordinatesToTreeId<nDim>(treeId, &a_coordinate(cellId, 0), (MLong)m_targetGridMinLevel,
10495 m_targetGridCenterOfGravity, m_targetGridLengthLevel0);
10496 minLevelCellsTreeId.push_back(treeId);
10497 }
10498
10499 // Set partition cell and partition level ancestor properties
10500 const maia::grid::tree::PropertyBitsetType cellProp = properties(i);
10501 const MBool isPartitionCell = cellProp[maia::grid::cell::p(Cell::IsPartitionCell)];
10502 const MBool isPartLvlAncestor = cellProp[maia::grid::cell::p(Cell::IsPartLvlAncestor)];
10503 m_tree.resetProperties(cellId);
10504 a_hasProperty(cellId, Cell::IsPartitionCell) = isPartitionCell;
10505 a_hasProperty(cellId, Cell::IsPartLvlAncestor) = isPartLvlAncestor;
10506
10507 // Store global ids of partition cells
10508 if(isPartitionCell) {
10509 partitionCells.push_back(globalId(i));
10510 }
10511 }
10512 m_noInternalCells = m_tree.size();
10513
10514 m_log << "Partition level shift: level diff of first cell is " << a_level(0) - m_minLevel << std::endl;
10515#ifndef NDEBUG
10516 std::cerr << domainId() << " Partition level shift: level diff of first cell is " << a_level(0) - m_minLevel
10517 << std::endl;
10518#endif
10519
10520 std::vector<MInt>().swap(m_minLevelCells);
10521
10522 // Set new global domain offsets
10523 std::copy_n(&globalIdOffsets[0], noDomains() + 1, &m_domainOffsets[0]);
10524
10525 m_localPartitionCellOffsets[0] = partitionCellOffsets[domainId()];
10526 m_localPartitionCellOffsets[1] = partitionCellOffsets[domainId() + 1];
10527 m_localPartitionCellOffsets[2] = m_noPartitionCellsGlobal;
10528 m_noPartitionCells = partitionCellOffsets[domainId() + 1] - partitionCellOffsets[domainId()];
10529
10530 const MLong noCellsCheck = globalIdOffsets[domainId() + 1] - globalIdOffsets[domainId()];
10531 if(noCellsCheck != m_noInternalCells) {
10532 TERMM(1, "Wrong number of internal cells: " + std::to_string(m_noInternalCells)
10533 + " != " + std::to_string(noCellsCheck));
10534 }
10535
10536 TERMM_IF_COND(m_noPartitionCells <= 0, "Cannot allocate array with " + to_string(m_noPartitionCells) + " elements.");
10537
10538 // Reallocate partition cell arrays with new size
10539 mDeallocate(m_localPartitionCellGlobalIds);
10540 mAlloc(m_localPartitionCellGlobalIds, m_noPartitionCells, "m_localPartitionCellGlobalIds", static_cast<MLong>(-1),
10541 AT_);
10542 mDeallocate(m_localPartitionCellLocalIds);
10543 mAlloc(m_localPartitionCellLocalIds, m_noPartitionCells, "m_localPartitionCellLocalIds", -1, AT_);
10544
10545 const MInt maxPartLvl = m_minLevel + m_maxPartitionLevelShift;
10546 // Store partition cell global ids and determine number of offsprings for all cells starting at
10547 // the partition level (required for communicatePartitionLevelAncestorData())
10548 for(MInt i = 0; i < m_noPartitionCells; i++) {
10549 auto cellId = (MInt)(partitionCells[i] - m_domainOffsets[domainId()]);
10550 const MInt partLevel = a_level(cellId);
10551 TERMM_IF_NOT_COND(a_hasProperty(cellId, Cell::IsPartitionCell), "Partition cell flag not set.");
10552 TERMM_IF_COND(partitionCells[i] != a_globalId(cellId), "Error: partition level cell global id mismatch.");
10553 TERMM_IF_COND(partLevel < m_minLevel || partLevel > maxPartLvl, "Invalid level for partition cell.");
10554
10555 m_localPartitionCellGlobalIds[i] = partitionCells[i];
10556 // TODO labels:DLB,GRID @ansgar does not work properly for PLS!
10557 descendNoOffsprings(cellId, m_domainOffsets[domainId()]);
10558 }
10559
10560 // Determine global min and max levels
10561 setLevel();
10562
10563 // Rebuild global-to-local id mapping and convert global ids back to local ids
10564 {
10565 createGlobalToLocalIdMapping();
10566
10567 const MInt noMinLevelCells = localMinLevelCells.size();
10568 if(noMinLevelCells == 0) {
10569 localMinLevelCells.push_back(-1);
10570 minLevelCellsTreeId.push_back(-1);
10571 }
10572
10573 // Communicate the partition level ancestor data and create the missing parts of the tree from
10574 // the min-level up to the partition level
10575 maia::grid::IO<CartesianGrid<nDim>>::communicatePartitionLevelAncestorData(
10576 *this, noMinLevelCells, &localMinLevelCells[0], &localMinLevelId[0], &minLevelCellsTreeId[0], &cellInfo[0]);
10577
10578 for(MInt i = m_noInternalCells; i < m_tree.size(); ++i) {
10579 ASSERT(a_hasProperty(i, Cell::IsPartLvlAncestor), "Cell is not a partition level ancestor.");
10580 a_hasProperty(i, Cell::IsHalo) = true;
10581 }
10582
10583 // Set neighbor ids
10584 maia::grid::IO<CartesianGrid<nDim>>::propagateNeighborsFromMinLevelToMaxLevel(*this);
10585
10586 // Update the global to local id mapping and change global ids in the tree into local ids
10587 createGlobalToLocalIdMapping();
10588 changeGlobalToLocalIds();
10589
10590 // Rebuild window/halo cell information
10591 setupWindowHaloCellConnectivity();
10592 }
10593
10594 setLevel();
10595
10596 // TODO labels:GRID,DLB @ansgar_pls is this still required here? after windowHalo?
10597 computeGlobalIds();
10598
10599 // Update local bounding box
10600 computeLocalBoundingBox(&m_localBoundingBox[0]);
10601
10602 // Set balance status
10603 m_wasBalancedAtLeastOnce = true;
10604 m_wasBalanced = true;
10605
10606#ifdef MAIA_GRID_SANITY_CHECKS
10607 gridSanityChecks();
10608#endif
10609}
10610
10611// --------------------------------------------------------------------------------------
10612
10617template <MInt nDim>
10619 TRACE();
10620
10621 m_neighborBackup.clear();
10622
10623 // For all halo cells that are periodic, reset globalId to -1
10624 // For all non-halo neighbors of periodic halo cells, reset neighbor id to -1
10625 for(MInt i = 0; i < noNeighborDomains(); i++) {
10626 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
10627 MInt cellId = m_haloCells[i][j];
10628 if(!a_hasProperty(cellId, Cell::IsPeriodic)) continue;
10629 if(!saveBackup) a_globalId(cellId) = -1;
10630 for(MInt dir = 0; dir < m_noDirs; dir++) {
10631 if(a_hasNeighbor(cellId, dir) == 0) continue;
10632 MInt nghbrId = a_neighborId(cellId, dir);
10633 if(!a_hasProperty(nghbrId, Cell::IsHalo)) {
10634 if(saveBackup) m_neighborBackup.push_back(make_tuple(cellId, nghbrId, dir));
10635 a_neighborId(cellId, dir) = -1;
10636 a_neighborId(nghbrId, m_revDir[dir]) = -1;
10637 }
10638 }
10639 }
10640 }
10641
10642 if(m_azimuthalPer) {
10643 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
10644 for(MInt j = 0; j < (signed)m_azimuthalHaloCells[i].size(); j++) {
10645 MInt cellId = m_azimuthalHaloCells[i][j];
10646 ASSERT(a_hasProperty(cellId, Cell::IsPeriodic), "");
10647 if(!saveBackup) a_globalId(cellId) = -1;
10648 for(MInt dir = 0; dir < m_noDirs; dir++) {
10649 if(a_hasNeighbor(cellId, dir) == 0) continue;
10650 MInt nghbrId = a_neighborId(cellId, dir);
10651 if(!a_hasProperty(nghbrId, Cell::IsHalo)) {
10652 if(saveBackup) m_neighborBackup.push_back(make_tuple(cellId, nghbrId, dir));
10653 a_neighborId(cellId, dir) = -1;
10654 a_neighborId(nghbrId, m_revDir[dir]) = -1;
10655 }
10656 }
10657 }
10658 }
10659 }
10660}
10661
10662// --------------------------------------------------------------------------------------
10663
10664
10669template <MInt nDim>
10671 TRACE();
10672
10673 for(MUint i = 0; i < m_neighborBackup.size(); i++) {
10674 MInt cellId = get<0>(m_neighborBackup[i]);
10675 MInt nghbrId = get<1>(m_neighborBackup[i]);
10676 MInt dir = get<2>(m_neighborBackup[i]);
10677 a_neighborId(cellId, dir) = nghbrId;
10678 a_neighborId(nghbrId, m_revDir[dir]) = cellId;
10679 }
10680 m_neighborBackup.clear();
10681}
10682
10683
10684// --------------------------------------------------------------------------------------
10685
10698template <MInt nDim>
10699template <typename CELLTYPE>
10701 TRACE();
10702 vector<vector<MInt>> partitionLevelAncestorHaloCells;
10703 vector<vector<MInt>> partitionLevelAncestorWindowCells;
10704
10705 if(m_maxPartitionLevelShift > 0) {
10706 determinePartLvlAncestorHaloWindowCells(partitionLevelAncestorHaloCells, partitionLevelAncestorWindowCells);
10707 } else {
10708 partitionLevelAncestorHaloCells.resize(noNeighborDomains());
10709 partitionLevelAncestorWindowCells.resize(noNeighborDomains());
10710 }
10711
10712 maia::grid::IO<CartesianGrid<nDim>>::calculateNoOffspringsAndWorkload(
10713 *this, input_cells, input_noCells, m_haloCells, m_windowCells, partitionLevelAncestorWindowCells,
10714 partitionLevelAncestorHaloCells);
10715}
10716
10717
10718template <MInt nDim>
10719void CartesianGrid<nDim>::partitionParallel(const MInt tmpCount, const MLong tmpOffset,
10720 const MFloat* const partitionCellsWorkload,
10721 const MLong* const partitionCellsGlobalId, const MFloat totalWorkload,
10722 MLong* partitionCellOffsets, MLong* globalIdOffsets,
10723 MBool computeOnlyPartition) {
10724 maia::grid::IO<CartesianGrid<nDim>>::partitionParallel(*this, tmpCount, tmpOffset, partitionCellsWorkload,
10725 partitionCellsGlobalId, totalWorkload, partitionCellOffsets,
10726 globalIdOffsets, computeOnlyPartition);
10727}
10728
10729
10732template <MInt nDim>
10733template <MBool t_correct>
10735 function<MFloat*(MInt, MFloat* const)> correctCellCoord) {
10736 ASSERT(solverId >= -1 && solverId < treeb().noSolvers(), "Invalid solver id " + to_string(solverId));
10737
10738 // array<MFloat, nDim> tmp;
10739 const MInt noLocalPartitionCells = m_localPartitionCellOffsets[1] - m_localPartitionCellOffsets[0];
10740
10741 // loop over partition cells
10742 for(MInt i = 0; i < noLocalPartitionCells; i++) {
10743 const MInt curId = m_localPartitionCellLocalIds[i];
10744
10745 MBool isInCell = pointWthCell<t_correct, false>(coord, curId, correctCellCoord);
10746
10747 if(isInCell) {
10748 if(solverId < 0) {
10749 return curId;
10750 } else {
10751 // If a solverId is given check if partition cell belongs to solver
10752 if(a_solver(curId, solverId)) {
10753 return curId;
10754 } else {
10755 return -1;
10756 }
10757 }
10758 }
10759 }
10760 return -1;
10761}
10762
10763/* J. Arvo
10764from "Graphics Gems", Academic Press, 1990
10765*/
10766template <MInt nDim>
10768 const MFloat* const sphereCenter, MFloat const radius) {
10769 MFloat dmin = 0;
10770 for(MInt n = 0; n < nDim; n++) {
10771 if(sphereCenter[n] < bMin[n]) {
10772 dmin += pow((sphereCenter[n] - bMin[n]), 2);
10773 } else if(sphereCenter[n] > bMax[n]) {
10774 dmin += pow((sphereCenter[n] - bMax[n]), 2);
10775 }
10776 }
10777 return (dmin <= pow(radius, 2));
10778}
10779
10780/* J. Arvo
10781from "Graphics Gems", Academic Press, 1990
10782*/
10783template <MInt nDim>
10785 const MFloat* const sphereCenter, MFloat const radius) {
10786 MFloat dmin = 0;
10787 MBool face = false;
10788 for(MInt i = 0; i < nDim; i++) {
10789 if(sphereCenter[i] < bMin[i]) {
10790 face = true;
10791 dmin += pow(sphereCenter[i] - bMin[i], 2);
10792 } else if(sphereCenter[i] > bMax[i]) {
10793 face = true;
10794 dmin += pow(sphereCenter[i] - bMax[i], 2);
10795 } else if(sphereCenter[i] - bMin[i] <= radius) {
10796 face = true;
10797 } else if(bMax[i] - sphereCenter[i] <= radius) {
10798 face = true;
10799 }
10800 }
10801 return (face && (dmin <= pow(radius, 2)));
10802}
10803
10804template <MInt nDim>
10806 const MInt noLocalPartitionCells = m_localPartitionCellOffsets[1] - m_localPartitionCellOffsets[0];
10807 MFloat bMin[nDim];
10808 MFloat bMax[nDim];
10809
10810 MBool intersectsACell = false;
10811 MInt intersectingCellId = -1;
10812 for(MInt i = 0; i < noLocalPartitionCells; i++) {
10813 const MInt curId = m_localPartitionCellLocalIds[i];
10814 const MFloat _halfCellLength = halfCellLength(curId);
10815 const MFloat* cCoord = m_tree.coordinate(curId);
10816
10817 for(MInt n = 0; n < nDim; n++) {
10818 bMin[n] = cCoord[n] - _halfCellLength;
10819 bMax[n] = cCoord[n] + _halfCellLength;
10820 }
10821
10822 intersectsACell = boxSphereIntersection(bMin, bMax, center, radius);
10823
10824 if(intersectsACell) {
10825 intersectingCellId = curId;
10826 }
10827 }
10828 return intersectingCellId;
10829}
10830
10831template <MInt nDim>
10833 MBool onlyPartition) {
10834 MBool intersectsACell = false;
10835 MInt intersectingCellId = -1;
10836 std::vector<MFloat> bMin(nDim);
10837 std::vector<MFloat> bMax(nDim);
10838
10839 for(MInt i = 0; i < noHaloCells(domainId); i++) {
10840 const MInt curId = haloCell(domainId, i);
10841 // skip all non-partition cells
10842 if(onlyPartition && !a_hasProperty(curId, Cell::IsPartitionCell)) {
10843 continue;
10844 }
10845
10846 const MFloat _halfCellLength = halfCellLength(curId);
10847 const MFloat* cCoord = m_tree.coordinate(curId);
10848
10849 for(MInt n = 0; n < nDim; n++) {
10850 // std::cout << " GRID POS: " << cCoord[n];
10851 bMin[n] = cCoord[n] - _halfCellLength;
10852 bMax[n] = cCoord[n] + _halfCellLength;
10853 }
10854
10855 intersectsACell = boxSphereIntersection(bMin.data(), bMax.data(), center, radius);
10856 if(intersectsACell) {
10857 intersectingCellId = curId;
10858 break;
10859 }
10860 }
10861 return intersectingCellId;
10862}
10863
10864template <MInt nDim>
10866 const MFloat* bMin = &m_localBoundingBox[0];
10867 const MFloat* bMax = &m_localBoundingBox[nDim];
10868
10869 MBool intersecting = boxSphereIntersection(bMin, bMax, center, radius);
10870 return intersecting;
10871}
10872
10873template <MInt nDim>
10875 TERMM(1, "function not used anywhere, check this code!");
10876
10877 if(a_hasProperty(cellId, Cell::IsPartitionCell)) {
10878 return cellId;
10879 }
10880
10881 MInt parentId = a_parentId(cellId);
10882 while(!a_hasProperty(parentId, Cell::IsPartitionCell)) {
10883 parentId = a_parentId(parentId);
10884 }
10885 return parentId;
10886}
10887
10888template <MInt nDim>
10890 // loop over all halo cells
10891 for(MInt n = 0; n < noNeighborDomains(); n++) {
10892 for(MInt i = 0; i < noHaloCells(n); i++) {
10893 const MInt curId = haloCell(n, i);
10894
10895 if(!a_hasProperty(curId, Cell::IsPartitionCell)) {
10896 continue;
10897 }
10898
10899 MBool isInCell = true;
10900
10901 const MFloat* cellCoords = m_tree.coordinate(curId);
10902 const MFloat _halfCellLength = halfCellLength(curId);
10903
10904 for(MInt dimId = 0; dimId < nDim; dimId++) {
10905 if(coord[dimId] < cellCoords[dimId] - _halfCellLength or coord[dimId] >= cellCoords[dimId] + _halfCellLength) {
10906 isInCell = false;
10907 break;
10908 }
10909 }
10910
10911 if(isInCell) {
10912 if(solverId < 0) {
10913 return curId;
10914 } else {
10915 // If a solverId is given check if partition cell belongs to solver
10916 if(a_solver(curId, solverId)) {
10917 return curId;
10918 } else {
10919 return -1;
10920 }
10921 }
10922 }
10923 }
10924 }
10925 return -1;
10926}
10927
10928template <MInt nDim>
10929template <MBool t_correct>
10930MInt CartesianGrid<nDim>::findContainingHaloCell(const MFloat* const coord, const MInt solverId, MInt domainId,
10931 MBool onlyPartitionCells,
10932 function<MFloat*(MInt, MFloat* const)> correctCellCoord) {
10933 ASSERT(solverId >= -1 && solverId < treeb().noSolvers(), "Invalid solver id " + to_string(solverId));
10934
10935 MInt n = domainId;
10936 for(MInt i = 0; i < noHaloCells(n); i++) {
10937 const MInt curId = haloCell(n, i);
10938
10939 if(onlyPartitionCells && !a_hasProperty(curId, Cell::IsPartitionCell)) {
10940 continue;
10941 }
10942
10943 MBool isInCell = pointWthCell<t_correct, false>(coord, curId, correctCellCoord);
10944
10945 if(isInCell) {
10946 if(solverId < 0) {
10947 return curId;
10948 } else {
10949 // If a solverId is given check if partition cell belongs to solver
10950 if(a_solver(curId, solverId)) {
10951 return curId;
10952 } else {
10953 return -1;
10954 }
10955 }
10956 }
10957 }
10958 return -1;
10959}
10960
10961
10978template <MInt nDim>
10979template <MBool t_correct>
10981 const MFloat* coord, function<MFloat*(MInt, MFloat* const)> correctCellCoord, const MInt solverId) {
10982 TRACE();
10983
10984 // Return early if point is not inside the local bounding box
10985 if(!pointInLocalBoundingBox(coord)) {
10986 return -1;
10987 }
10988
10989 ASSERT(solverId >= -1 && solverId < treeb().noSolvers(), "Invalid solver id " + to_string(solverId));
10990 // Check if the lookup should be global or on a solver basis using the given solverId
10991 const MBool global = (solverId < 0);
10992
10993 MInt cellId = findContainingPartitionCell<t_correct>(coord, solverId, correctCellCoord);
10994
10995 // return -1 if not in local partition cells
10996 if(cellId == -1) {
10997 return -1;
10998 }
10999
11000 // loop down to leaf cell (global or for solver) containing coord
11001 while((global) ? a_hasChildren(cellId) : a_hasChildren(cellId, solverId)) {
11002 MInt childId = 0;
11003 for(; childId < IPOW2(nDim); childId++) {
11004 const MInt childCellId = (global) ? a_childId(cellId, childId) : a_childId(cellId, childId, solverId);
11005 if(childCellId < 0) continue;
11006 MBool isInCell = pointWthCell<t_correct, false>(coord, childCellId, correctCellCoord);
11007 if(isInCell) {
11008 cellId = childCellId;
11009 break;
11010 }
11011 }
11012 if(childId == IPOW2(nDim)) {
11013 mTerm(1, AT_, "No matching child during loop down!");
11014 }
11015 }
11016
11017 if(global) {
11018 ASSERT(m_tree.isLeafCell(cellId), "No leaf cell... " + std::to_string(cellId));
11019 } else {
11020 ASSERT(!a_hasChildren(cellId, solverId), "No leaf cell... " + std::to_string(cellId));
11021 }
11022 return cellId;
11023}
11024
11025
11046template <MInt nDim>
11047template <MBool t_correct>
11049 function<MFloat*(MInt, MFloat* const)> correctCellCoord,
11050 const MInt solverId, const MBool allowNonLeafHalo) {
11051 TRACE();
11052
11053 // Check if the lookup should be global or on a solver basis using the given solverId
11054 const MBool global = (solverId < 0);
11055
11056 MInt cellId = startId;
11057 array<MFloat, nDim> tmp;
11058
11059 auto findExistingNghbr = [&](const MInt _cellId, const MInt _dir, const MInt _solverId) {
11060 MInt curId = _cellId;
11061 if(_solverId < 0) {
11062 while(curId > -1 && a_neighborId(_cellId, _dir) < 0) {
11063 curId = a_parentId(_cellId);
11064 }
11065 } else {
11066 while(curId > -1 && a_neighborId(curId, _dir, _solverId) < 0) {
11067 curId = a_parentId(curId, _solverId);
11068 }
11069 }
11070 if(curId > -1 && solverId > -1) {
11071 return a_neighborId(curId, _dir, _solverId);
11072 } else if(curId > -1 && solverId < 0) {
11073 return a_neighborId(_cellId, _dir);
11074 } else {
11075 return static_cast<MLong>(-1);
11076 }
11077 };
11078
11079 set<MInt> checkedCells;
11080 // iterate overall neighbors till found
11081 while(!pointWthCell<t_correct, true>(coord, cellId, correctCellCoord)) {
11082 // current cellCoords
11083 const MFloat* tmpCoord = m_tree.coordinate(cellId);
11084 const MFloat* cellCoords = t_correct ? (correctCellCoord)(cellId, &tmp[0]) : tmpCoord;
11085 const MFloat halfCellLength = cellLengthAtLevel(a_level(cellId) + 1);
11086
11087 checkedCells.insert(cellId);
11088
11089 MInt direction = -1;
11090
11091 // determine which cell side is crossed
11092 for(MInt i = 0; i < nDim; i++) {
11093 const MFloat difference = coord[i] - cellCoords[i];
11094 const MFloat absDifference = fabs(difference);
11095 if(absDifference > halfCellLength) {
11096 const MInt temp = 2 * i + ((difference > 0.0) ? 1 : 0);
11097 const MLong neighborId = findExistingNghbr(cellId, temp, solverId);
11098 if(neighborId > -1) {
11099 direction = temp;
11100 cellId = neighborId;
11101 break;
11102 } else if(a_parentId(cellId) > -1
11103 && pointWthCell<t_correct, true>(coord, a_parentId(cellId), correctCellCoord)) {
11104 // point is within the parent cell but not within any of the cells-neighbors
11105 // this means, that the matching child where the point should be is outside the STL geometry
11106 return -1;
11107 }
11108 }
11109 }
11110
11111 if(direction == -1) {
11112 // no valid cell in any direction of coords
11113 // meaning that the cell is to far (in all directions) from the startId
11114 // start a general search in the entire domain instead
11115 return findContainingLeafCell<t_correct>(coord, correctCellCoord, solverId);
11116 }
11117
11118 // If tmpCoord is located on the cell surface of two cells, this loop would jump between these cells until the end
11119 // of time.
11120 if(checkedCells.find(cellId) != checkedCells.end()) break;
11121 }
11122
11123 // loop down to leaf cell (global or for solver) containing coord
11124 while((global) ? a_hasChildren(cellId) : a_hasChildren(cellId, solverId)) {
11125 MInt childId = 0;
11126 for(; childId < IPOW2(nDim); childId++) {
11127 const MInt childCellId = (global) ? a_childId(cellId, childId) : a_childId(cellId, childId, solverId);
11128 if(childCellId < 0) {
11129 continue;
11130 }
11131 MInt cnt = 0;
11132 const MFloat* cellCoords = m_tree.coordinate(childCellId);
11133 // TODO FIXME labels:GRID
11134 // const MFloat* cellCoords = t_correct ? (*correctCellCoord)(curId, &tmp[0]): tmpCoord;
11135 const MFloat constHalfCellLength = halfCellLength(childCellId);
11136
11137 for(MInt dimId = 0; dimId < nDim; dimId++) {
11138 MFloat dist = coord[dimId] - cellCoords[dimId];
11139 if(coord[dimId] >= cellCoords[dimId] - constHalfCellLength
11140 && coord[dimId] < cellCoords[dimId] + constHalfCellLength) {
11141 cnt++;
11142 } else {
11143 const MFloat increaseOrderOfMagnitude = 10.0; // MFloatEps is to small.
11144 if(approx(fabs(dist), constHalfCellLength, increaseOrderOfMagnitude * MFloatEps)) {
11145 MInt dir = (dist > F0) ? 1 : 0;
11146 if(a_neighborId(childCellId, dimId + dir) > -1) {
11147 if(a_parentId(a_neighborId(childCellId, dimId + dir)) != a_parentId(childCellId)) {
11148 // Point on surface of parent cell
11149 cnt++;
11150 } else {
11151 if(dist > F0) {
11152 // Point in center of parent cell. Choose cell with smaller cellCoords[dimId].
11153 cnt++;
11154 }
11155 }
11156 } else {
11157 // Point musst be on surface of parent cell. Else neighbor would exist.
11158 cnt++;
11159 }
11160 }
11161 }
11162 }
11163
11164 if(cnt == nDim) {
11165 cellId = childCellId;
11166 break;
11167 }
11168 }
11169 if(childId == IPOW2(nDim)) {
11170 if(!a_isHalo(cellId)) {
11171 mTerm(1, AT_, "No matching child during loop down!");
11172 } else {
11173 if(!allowNonLeafHalo) {
11174 cout << "dom: " << domainId() << " no matching child during loop down" << endl;
11175 return -1;
11176 } else {
11177 return cellId;
11178 }
11179 }
11180 }
11181 }
11182
11183 if(global) {
11184 ASSERT(m_tree.isLeafCell(cellId), "No leaf cell... " + std::to_string(cellId));
11185 } else {
11186 ASSERT(!a_hasChildren(cellId, solverId), "No leaf cell... " + std::to_string(cellId));
11187 }
11188
11189 return cellId;
11190}
11191
11192
11209template <MInt nDim>
11210void CartesianGrid<nDim>::propagateDistance(std::vector<MInt>& list, MIntScratchSpace& distMem, MInt dist) {
11211 TRACE();
11212
11213 m_log << "running distance propagation for distance " << dist << endl;
11214
11215 for(const auto gridCellId : list)
11216 propagationStep(gridCellId, 0, distMem.data(), dist);
11217
11218 if(noNeighborDomains() > 0) {
11219 MIntScratchSpace recvMemOffset(noNeighborDomains() + 1, AT_, "recvMemOffset");
11220 recvMemOffset.fill(0);
11221 MInt allRecv = 0;
11222 for(MInt dom = 0; dom < noNeighborDomains(); dom++) {
11223 allRecv += (signed)m_haloCells[dom].size();
11224 recvMemOffset[dom + 1] = allRecv;
11225 }
11226 MIntScratchSpace recvMem(allRecv, AT_, "recvMem");
11227 MInt checkHalos = 1;
11228 MInt nmbrComm = 0;
11229 m_log << "checking halos" << endl;
11230 while(checkHalos) {
11231 checkHalos = 0;
11232 // exchange distance
11233 exchangeNotInPlace(distMem.data(), recvMem.data());
11234 // update halo cells and propagate
11235 for(MInt dom = 0; dom < noNeighborDomains(); dom++) {
11236 for(MInt cell = 0; cell < (signed)m_haloCells[dom].size(); cell++) {
11237 MInt windowDist = recvMem[recvMemOffset[dom] + cell];
11238 MInt haloDist = distMem[m_haloCells[dom][cell]];
11239 if(windowDist < haloDist) {
11240 propagationStep(m_haloCells[dom][cell], windowDist, distMem.data(), dist);
11241 checkHalos = 1;
11242 }
11243 }
11244 }
11245 MPI_Allreduce(MPI_IN_PLACE, &checkHalos, 1, MPI_INT, MPI_MAX, mpiComm(), AT_, "MPI_IN_PLACE", "checkHalos");
11246 if(checkHalos) {
11247 nmbrComm++;
11248 m_log << "checking halos again " << nmbrComm << endl;
11249 }
11250 }
11251 }
11252}
11253
11266template <MInt nDim>
11268 TRACE();
11269 if(dist < distMem[cellId]) {
11270 distMem[cellId] = dist;
11271 if(dist < endDist) {
11272 for(MInt i = 0; i < 2 * nDim; i++) {
11273 const MInt nghbrId = a_neighborId(cellId, i);
11274 if(nghbrId > -1) propagationStep(nghbrId, dist + 1, distMem, endDist);
11275 }
11276 }
11277 }
11278}
11279
11290template <MInt nDim>
11291template <typename DATATYPE>
11292void CartesianGrid<nDim>::exchangeNotInPlace(DATATYPE* exchangeVar, DATATYPE* recvMem) {
11293 TRACE();
11294 maia::mpi::exchangeData(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), exchangeVar, recvMem);
11295}
11296
11297template <MInt nDim>
11298template <typename DATATYPE>
11299void CartesianGrid<nDim>::generalExchange(MInt noVars, const MInt* vars, DATATYPE** exchangeVar, MInt noDomSend,
11300 MInt* domSend, const MInt* noCellsSendPerDom, const MInt* cellIdsSend,
11301 MInt noDomRecv, MInt* domRecv, const MInt* noCellsRecvPerDom,
11302 const MInt* cellIdsRecv) {
11303 TRACE();
11304
11305 // recv info
11306 MIntScratchSpace recvMemOffset(noDomRecv + 1, AT_, "recvMemOffset");
11307 recvMemOffset.fill(0);
11308 MInt allRecv = 0;
11309 for(MInt dom = 0; dom < noDomRecv; dom++) {
11310 allRecv += noCellsRecvPerDom[dom];
11311 recvMemOffset[dom + 1] = allRecv;
11312 }
11313 ScratchSpace<DATATYPE> recvMem(allRecv, AT_, "recvMem");
11314
11315 // send info
11316 MIntScratchSpace sendMemOffset(noDomSend + 1, AT_, "sendMemOffset");
11317 sendMemOffset.fill(0);
11318 MInt allSend = 0;
11319 for(MInt dom = 0; dom < noDomSend; dom++) {
11320 allSend += noCellsSendPerDom[dom];
11321 sendMemOffset[dom + 1] = allSend;
11322 }
11323 ScratchSpace<DATATYPE> sendMem(allSend, AT_, "sendMem");
11324
11325 DATATYPE* ptr = &(exchangeVar[0][0]);
11326
11327 for(MInt i = 0; i < noVars; i++) {
11328 MInt var = vars[i];
11329
11330 // fill the send mem
11331 for(MInt j = 0; j < allSend; j++)
11332 sendMem[j] = ptr[cellIdsSend[j] * noVars + var];
11333
11334 // communicate
11335 ScratchSpace<MPI_Request> mpi_request_(noDomSend, AT_, "mpi_request_");
11336
11337 for(MInt dom = 0; dom < noDomSend; dom++)
11338 MPI_Issend(&(sendMem[sendMemOffset[dom]]), noCellsSendPerDom[dom] * sizeof(DATATYPE), MPI_CHAR, domSend[dom], 0,
11339 mpiComm(), &mpi_request_[dom], AT_, "(sendMem[sendMemOffset[dom]])");
11340
11341 MPI_Status status_;
11342 for(MInt dom = 0; dom < noDomRecv; dom++)
11343 MPI_Recv(&(recvMem[recvMemOffset[dom]]), noCellsRecvPerDom[dom] * sizeof(DATATYPE), MPI_CHAR, domRecv[dom], 0,
11344 mpiComm(), &status_, AT_, "(recvMem[recvMemOffset[dom]])");
11345
11346 for(MInt dom = 0; dom < noDomSend; dom++)
11347 MPI_Wait(&mpi_request_[dom], &status_, AT_);
11348
11349 // distribute the recv mem
11350 for(MInt j = 0; j < allRecv; j++)
11351 ptr[cellIdsRecv[j] * noVars + var] = recvMem[j];
11352 }
11353}
11354
11355
11362template <MInt nDim>
11363void CartesianGrid<nDim>::createGridSlice(const MString& axis, const MFloat intercept, const MString& fileName) {
11364 TRACE();
11365
11366 createGridSlice(axis, intercept, fileName, -1, nullptr, nullptr, nullptr, nullptr);
11367}
11368
11369
11405template <MInt nDim>
11407 const MFloat intercept,
11408 const MString& fileName,
11409 const MInt solverId,
11410 MInt* const noSliceCellIds,
11411 MInt* const sliceCellIds,
11412 MInt* const noSliceHilbertIds,
11413 MInt* const sliceHilbertInfo,
11414 MInt* const noSliceContHilbertIds,
11415 MInt* const sliceContiguousHilbertInfo) {
11416 TRACE();
11417
11418 // Check if maximum and minimum cell levels are equal
11419 if(m_minLevel == m_maxLevel) {
11420 if(domainId() == 0) {
11421 // TODO FIXME labels:GRID what is the reason for this?
11422 std::cerr << "Warning: CartesianGrid::createGridSlice minLevel and maxLevel grid are equal! "
11423 << "(hang-up might occur)" << std::endl;
11424 }
11425 }
11426
11427 // Dimension check
11428 IF_CONSTEXPR(nDim != 3) { TERMM(1, "Can not create a 2D slice from a 2D grid."); }
11429
11430 m_log << "Creating 2D slice from grid... ";
11431
11432 // New number of dimension
11433 const MInt nDimSlice = 2;
11434
11435 // Check for slice orientation
11436 MInt axisNum;
11437 // Reference to relevant coordinates
11438 array<MInt, nDimSlice> coordArray{};
11439 if(axis == "x") {
11440 axisNum = 0;
11441 coordArray = {{1, 2}};
11442 } else if(axis == "y") {
11443 axisNum = 1;
11444 coordArray = {{0, 2}};
11445 } else if(axis == "z") {
11446 axisNum = 2;
11447 coordArray = {{0, 1}};
11448 } else {
11449 TERMM(1, "Unknown axis! Check your property file.");
11450 }
11451
11452 MBool isAtCenterOfGravity = false;
11453 // Check whether intercept is located at the centroid of the specified axis
11454 if(approx(intercept, m_targetGridCenterOfGravity[axisNum], MFloatEps)) {
11455 isAtCenterOfGravity = true;
11456 }
11457
11459 // Step 1: identify cells in slice
11461
11462 // Get center of gravity (needed for hilbertId in slice)
11463 std::array<MFloat, nDimSlice> centerOfGravity{};
11464 centerOfGravity[0] = m_centerOfGravity[coordArray[0]];
11465 centerOfGravity[1] = m_centerOfGravity[coordArray[1]];
11466
11467 std::array<MFloat, nDimSlice> targetGridCenterOfGravity{};
11468 targetGridCenterOfGravity[0] = m_targetGridCenterOfGravity[coordArray[0]];
11469 targetGridCenterOfGravity[1] = m_targetGridCenterOfGravity[coordArray[1]];
11470
11471 // Contains all cell ids relevant to slice
11472 MInt noSlicePartitionCells = 0;
11473 MInt noSliceMinLevelCells = 0;
11474 MInt noSliceCells = 0;
11475 MLong noLeafCells = 0;
11476 // SliceCellCollector for all local sliceCells with localCellId, hilbertId, isPartitionCell and
11477 // isMinCell information. This collector is useful for sorting this data in groups
11478 // After sorting the data is separated into single arrays
11479 ScratchSpace<array<MInt, 4>> sliceCellCollector(m_noInternalCells, AT_, "sliceCellCollector");
11480 for(MInt i = 0; i < m_noInternalCells; i++) {
11481 sliceCellCollector[i].fill(0);
11482 }
11483 // Determine partition cells of slice
11484 const MInt noLocalPartitionCells = m_localPartitionCellOffsets[1] - m_localPartitionCellOffsets[0];
11485
11486 // Scratch to access partitionCells in following for loop
11487 MIntScratchSpace localPartitionCellGlobalIds(noLocalPartitionCells + 1, AT_, "localPartitionCellGlobalIds");
11488 copy_n(&m_localPartitionCellGlobalIds[0], noLocalPartitionCells, &localPartitionCellGlobalIds[0]);
11489 // This partitionCell would be located on the next domain, slice cell search on this
11490 // domains is perfmored until this global cellId
11491 localPartitionCellGlobalIds[noLocalPartitionCells] = m_noInternalCells + m_domainOffsets[domainId()];
11492
11493 // Count how many cells belong to a hilbertId (for this solver) on this domain. Needed for mapping and file writing
11494 std::map<MInt, MInt> hilbertIdCount;
11495 std::map<MInt, MInt> hilbertIdCountSolver;
11496
11497 for(MInt i = 0, cellId = 0; i < noLocalPartitionCells; i++) {
11498 // get local partition cellId
11499 const MInt partitionCellId = localPartitionCellGlobalIds[i] - m_domainOffsets[domainId()];
11500 MInt isPartitionCell = 0;
11501 // Skip partition cell if not intersected by slice
11502 MFloat eps = 0.0;
11503 if(isAtCenterOfGravity) {
11504 eps = MFloatEps;
11505 }
11506 if(fabs((intercept + eps) - a_coordinate(partitionCellId, axisNum)) < halfCellLength(partitionCellId)) {
11507 isPartitionCell = 1;
11508 noSlicePartitionCells++;
11509 } else {
11510 // Skip all offsprings
11511 cellId = partitionCellId + a_noOffsprings(partitionCellId);
11512 }
11513
11514 // Search for all cells between two partitionCells and collect information
11515 const MInt nextPartitionCell = localPartitionCellGlobalIds[i + 1] - m_domainOffsets[domainId()];
11516 while(cellId < nextPartitionCell) {
11517 // Skip cell if not intersected by slice
11518 if(!(fabs((intercept + eps) - a_coordinate(cellId, axisNum)) < halfCellLength(cellId))) {
11519 cellId++;
11520 continue;
11521 }
11522
11523 // save partitionCell indicator
11524 if(cellId == partitionCellId) {
11525 sliceCellCollector[noSliceCells][2] = isPartitionCell;
11526 isPartitionCell = 0;
11527 }
11528
11529 // Count hilbertIds of slice in 2D grid
11530 const MFloat* const sliceCoord = &a_coordinate(cellId, 0);
11531 // Coordinates mapped to unit cube
11532 array<MFloat, 2> normedSliceCoord{};
11533 normedSliceCoord[0] =
11534 (sliceCoord[coordArray[0]] - targetGridCenterOfGravity[0] + 1e-12 + m_targetGridLengthLevel0 * 0.5)
11535 / m_targetGridLengthLevel0;
11536 normedSliceCoord[1] =
11537 (sliceCoord[coordArray[1]] - targetGridCenterOfGravity[1] + 1e-12 + m_targetGridLengthLevel0 * 0.5)
11538 / m_targetGridLengthLevel0;
11539 const MLong sliceHilbertId =
11540 maia::grid::hilbert::index<2>(&normedSliceCoord[0], static_cast<MLong>(m_targetGridMinLevel));
11541 // count cells per slice hilbertId
11542 hilbertIdCount[sliceHilbertId]++;
11543 sliceCellCollector[noSliceCells][1] = sliceHilbertId;
11544
11545 // Count solver-cells per slice hilbertId
11546 if(solverId > -1 && a_solver(cellId, solverId)) hilbertIdCountSolver[sliceHilbertId]++;
11547
11548 // Check if current cell is a minLevelCell
11549 if(a_level(cellId) == m_minLevel) {
11550 sliceCellCollector[noSliceCells][3] = 1;
11551 noSliceMinLevelCells++;
11552 }
11553
11554 // Count number of leaf cells
11555 if(a_isLeafCell(cellId)) {
11556 noLeafCells++;
11557 }
11558 // Add cell to list of slice cells
11559 sliceCellCollector[noSliceCells][0] = cellId;
11560 noSliceCells++;
11561
11562 // next cell id
11563 cellId++;
11564 }
11565 }
11566
11568 // Step 1.1: sort slice cells by slice hilbertÍd
11570
11571 // Allocate memory for slice partition cells
11572 MIntScratchSpace slicePartitionCells(noSlicePartitionCells + 1, AT_, "slicePartitionCells");
11573 // Allocate memory for slice minLevel cells
11574 MIntScratchSpace sliceMinLevelCells(noSliceMinLevelCells, AT_, "sliceMinLevelCells");
11575 // Allocate memory for slice cells
11576 MIntScratchSpace cellIdsInSlice(noSliceCells, AT_, "cellIdsInSlice");
11577 // save hilbert id keys for easy access and sort slice cell arrays
11578 MIntScratchSpace hIdKeys(hilbertIdCount.size(), AT_, "hIdKeys");
11579 // Count and save hilbertIds for partition and minLevelCells
11580 std::map<MInt, MInt> hilbertIdMinLevelCellsCount;
11581 std::map<MInt, MInt> hilbertIdPartitionCellsCount;
11582
11583 if(noSliceCells > 0) {
11584 // create hilbert key array for easy access
11585 MInt noHilbertIds = 0;
11586 for(auto const& ent1 : hilbertIdCount) {
11587 hIdKeys[noHilbertIds] = ent1.first;
11588 noHilbertIds++;
11589 }
11590
11591 // sort slice cell info by slice hilbertId
11592 std::stable_sort(sliceCellCollector.begin(), sliceCellCollector.begin() + noSliceCells,
11593 [](const array<MInt, 4>& a, const array<MInt, 4>& b) { return a[1] < b[1]; });
11594
11595 // Split slice cell info into seperate arrays
11596 for(MInt i = 0, j = 0, k = 0; i < noSliceCells; i++) {
11597 cellIdsInSlice[i] = sliceCellCollector[i][0];
11598 // fill slicePartitionCells
11599 if(sliceCellCollector[i][2] == 1) {
11600 slicePartitionCells[j] = sliceCellCollector[i][0];
11601 hilbertIdPartitionCellsCount[sliceCellCollector[i][1]]++;
11602 j++;
11603 }
11604 // fill sliceMinLevelCells
11605 if(sliceCellCollector[i][3] == 1) {
11606 sliceMinLevelCells[k] = sliceCellCollector[i][0];
11607 hilbertIdMinLevelCellsCount[sliceCellCollector[i][1]]++;
11608 k++;
11609 }
11610 }
11611 // Save total number of slice partitionCells (need for noOffsprings search)
11612 slicePartitionCells[noSlicePartitionCells] = cellIdsInSlice[noSliceCells - 1] + 1;
11613 }
11614
11615 if(solverId < 0) {
11616 // return noSliceCells and cellIds
11617 if(noSliceCellIds != nullptr) {
11618 *noSliceCellIds = noSliceCells;
11619 }
11620 if(sliceCellIds != nullptr && noSliceCells > 0) {
11621 std::copy_n(&cellIdsInSlice[0], noSliceCells, sliceCellIds);
11622 }
11623 } else {
11624 // Find number of cells and cell ids for given solverId
11625 MIntScratchSpace cellIdsInSliceSolver(noSliceCells, AT_, "cellIdsInSliceSolver");
11626 MInt noSliceCellsSolver = 0;
11627 for(MInt i = 0; i < noSliceCells; i++) {
11628 if(a_solver(cellIdsInSlice[i], solverId)) {
11629 cellIdsInSliceSolver[noSliceCellsSolver] = cellIdsInSlice[i];
11630 noSliceCellsSolver++;
11631 }
11632 }
11633
11634 // return number of cells for solver and cell ids
11635 if(noSliceCellIds != nullptr) {
11636 *noSliceCellIds = noSliceCellsSolver;
11637 }
11638 if(sliceCellIds != nullptr && noSliceCellsSolver > 0) {
11639 std::copy_n(&cellIdsInSliceSolver[0], noSliceCellsSolver, sliceCellIds);
11640 }
11641 }
11642
11644 // Step 2: create MPI communicator only for domains with slice cells
11646
11647 // Create a new MPI Communicator for all slice domains
11648 MPI_Comm mpiCommSlice = MPI_COMM_NULL;
11649 MIntScratchSpace sliceRanks(globalNoDomains(), AT_, "sliceRanks");
11650
11651 // Get Rank if points are relevant
11652 MInt rank = -1;
11653 if(noSliceCells > 0) {
11654 MPI_Comm_rank(mpiComm(), &rank);
11655 }
11656 // Combine all ranks to get relevant ranks
11657 MPI_Allgather(&rank, 1, type_traits<MInt>::mpiType(), &sliceRanks[0], 1, type_traits<MInt>::mpiType(), mpiComm(), AT_,
11658 "rank", "sliceRanks[0]");
11659
11660 // Check for relevant ranks and save them to create the new communicator
11661 const MInt noRelDomains = count_if(sliceRanks.begin(), sliceRanks.end(), [](const MInt a) { return a != -1; });
11662 MIntScratchSpace relDomains(noRelDomains, AT_, "relDomains");
11663 // A domainMap is needed to refer from sliceDomain to global domains
11664 map<MInt, MInt> domainMap;
11665 MInt position = 0;
11666 for(auto&& slice : sliceRanks) {
11667 if(slice != -1) {
11668 relDomains[position] = slice;
11669 domainMap[slice] = position;
11670 position++;
11671 }
11672 }
11673 // Create new point data mpi group
11674 MPI_Group globalGroup, localGroup;
11675 MPI_Comm_group(mpiComm(), &globalGroup, AT_, "globalGroup");
11676 MPI_Group_incl(globalGroup, noRelDomains, &relDomains[0], &localGroup, AT_);
11677
11678 // Create new communicator and clean up
11679 MPI_Comm_create(mpiComm(), localGroup, &mpiCommSlice, AT_, "mpiCommSlice");
11680
11681 MPI_Group_free(&globalGroup, AT_);
11682 MPI_Group_free(&localGroup, AT_);
11683
11684 // Leave function if not relevant to slice
11685 if(noSliceCells < 1) {
11686 return;
11687 }
11688
11689
11691 // Step 3: gather all data for slice grid file
11693
11694 // This part determines the new global cells order by slice hilbertId. Information about global
11695 // order must be avialable on all domains that the local order can be determined on every domain.
11696 // The new global order determines the offset for file writing and this is computed in this step.
11697
11698 // Determine total global number of slice cells for each domain
11699 MInt noSliceDomains = -1;
11700 MPI_Comm_size(mpiCommSlice, &noSliceDomains);
11701 // Get domainId in slice mpi comm to access domainOffset
11702 MInt sliceDomain = -1;
11703 MPI_Comm_rank(mpiCommSlice, &sliceDomain);
11704
11705 // Determine total no of slice hilbertIds
11706 MIntScratchSpace noLocalHilbertIds(noSliceDomains, AT_, "noLocalHilbertIds");
11707 noLocalHilbertIds[sliceDomain] = hilbertIdCount.size();
11708
11709 // Gather the number of unique number of slice hilbert ids on each domain
11710 MPI_Allgather(MPI_IN_PLACE, 1, MPI_INT, &noLocalHilbertIds[0], 1, MPI_INT, mpiCommSlice, AT_, "MPI_IN_PLACE",
11711 "noLocalHilbertIds[0]");
11712
11713 // Compute offsets for recieveing data
11714 MIntScratchSpace offsetsRecvData(noSliceDomains, AT_, "offsetsRecvData");
11715 offsetsRecvData[0] = 0;
11716 for(MInt i = 1; i < noSliceDomains; i++) {
11717 offsetsRecvData[i] = offsetsRecvData[i - 1] + noLocalHilbertIds[i - 1] * 6;
11718 }
11719 MInt noTotalRecvData = offsetsRecvData[noSliceDomains - 1] + noLocalHilbertIds[noSliceDomains - 1] * 6;
11720
11721 // Create exchange data (every domain gets info about hilbert ids and offsets for cellInfo,
11722 // partitionCells and minLevelCells). From this offsets for file writing are determined for every
11723 // variable indepently.
11724 MIntScratchSpace sendHilbertData(noLocalHilbertIds[sliceDomain] * 6, AT_, "sendHilbertData");
11725 for(MUlong i = 0; i < hIdKeys.size(); i++) {
11726 sendHilbertData[i * 6] = hIdKeys[i];
11727 sendHilbertData[i * 6 + 1] = domainId();
11728 sendHilbertData[i * 6 + 2] = hilbertIdCount[hIdKeys[i]];
11729 sendHilbertData[i * 6 + 3] = hilbertIdPartitionCellsCount[hIdKeys[i]];
11730 sendHilbertData[i * 6 + 4] = hilbertIdMinLevelCellsCount[hIdKeys[i]];
11731 sendHilbertData[i * 6 + 5] = hilbertIdCountSolver[hIdKeys[i]];
11732 }
11733
11734 // Exchange number of slice hilbert ids
11735 MIntScratchSpace recvHilbertData(noTotalRecvData, AT_, "recvHilbertData");
11736 MInt noSendData = sendHilbertData.size();
11737 MIntScratchSpace noRecvData(noSliceDomains, AT_, "noRecvData");
11738 for(MInt i = 0; i < noSliceDomains; i++) {
11739 noRecvData[i] = noLocalHilbertIds[i] * 6;
11740 }
11741 MPI_Allgatherv(&sendHilbertData[0], noSendData, MPI_INT, &recvHilbertData[0], &noRecvData[0], &offsetsRecvData[0],
11742 MPI_INT, mpiCommSlice, AT_, "sendHilbertData[0]", "recvHilbertData[0]");
11743
11744 // Create sliceGlobalHilbertInfo
11745 ScratchSpace<array<MInt, 6>> sliceGlobalHilbertInfo(noTotalRecvData / 6, AT_, "sliceGlobalHilbertInfo");
11746 // Store globally recieved data in sliceGlobalHilbertInfo
11747 for(MInt i = 0; i < noTotalRecvData / 6; i++) {
11748 sliceGlobalHilbertInfo[i][0] = recvHilbertData[i * 6];
11749 sliceGlobalHilbertInfo[i][1] = recvHilbertData[i * 6 + 1];
11750 sliceGlobalHilbertInfo[i][2] = recvHilbertData[i * 6 + 2];
11751 sliceGlobalHilbertInfo[i][3] = recvHilbertData[i * 6 + 3];
11752 sliceGlobalHilbertInfo[i][4] = recvHilbertData[i * 6 + 4];
11753 sliceGlobalHilbertInfo[i][5] = recvHilbertData[i * 6 + 5];
11754 }
11755
11756 // Sort sliceGlobalHilbertInfo by domainId...
11757 std::stable_sort(sliceGlobalHilbertInfo.begin(), sliceGlobalHilbertInfo.end(),
11758 [](const array<MInt, 6>& a, const array<MInt, 6>& b) { return a[1] < b[1]; });
11759 // ... and then sort stable by hilbertId -> list ordered by hilbertId and then nested for each hilbertId by domainId
11760 std::stable_sort(sliceGlobalHilbertInfo.begin(), sliceGlobalHilbertInfo.end(),
11761 [](const array<MInt, 6>& a, const array<MInt, 6>& b) { return a[0] < b[0]; });
11762
11763 // Determine hilbertId offsets (cells in slice file will be sorted by hilbertId)
11764 // For I/O it is necessary to call writeData() on all domains equally. If a slice has less then
11765 // maxNoHilbertIds then its array is filled with zero. Then a cell of writeData() will happen, but
11766 // without writing data.
11767 MInt maxNoHilbertIds = *std::max_element(noLocalHilbertIds.begin(), noLocalHilbertIds.end());
11768 MIntScratchSpace hilbertDomainOffset(maxNoHilbertIds, AT_, "hilbertDomainOffset");
11769 hilbertDomainOffset.fill(0);
11770 MIntScratchSpace hilbertPartitionDomainOffset(maxNoHilbertIds, AT_, "hilbertDomainOffset");
11771 hilbertPartitionDomainOffset.fill(0);
11772 MIntScratchSpace hilbertMinLevelDomainOffset(maxNoHilbertIds, AT_, "hilbertDomainOffset");
11773 hilbertMinLevelDomainOffset.fill(0);
11774
11775 // Offsets for solver cells
11776 MIntScratchSpace hilbertDomainOffsetSolver(maxNoHilbertIds, AT_, "hilbertDomainOffsetSolver");
11777 hilbertDomainOffsetSolver.fill(0);
11778
11779 // Calculate offsets by hilbert ids for output arrays
11780 // The data in sliceGlobalHilbertInfo is sorted by their hilbertId on the first level. On the
11781 // second level by domainId. Example:
11782 // [hilbertId, domaindId, noCells, noPartitionCells noMinLevelCells]
11783 // sliceGlobalHilbertInfo[0] = [0, 0, 20, 1, 1]
11784 // sliceGlobalHilbertInfo[1] = [0, 2, 10, 1, 0]
11785 // sliceGlobalHilbertInfo[2] = [1, 1, 6, 0 ,0]
11786 // sliceGlobalHilbertInfo[3] = [1, 2, 8, 1 ,1]
11787 //
11788 // To determine the global offset for the local data (for each local hilbertId), count the number
11789 // of cells of all hilbertIds below the current one. If this hilbertId is distributed on
11790 // multiple domains, then count also the next number of nodes till the local domainid is reached
11791 // in sliceGlobalHilbertInfo. In the example above the offset for hilbertId 1 on domain 1 would
11792 // be 30.
11793 for(MInt i = 0; i < noLocalHilbertIds[sliceDomain]; i++) {
11794 MInt offset = 0, offsetPart = 0, offsetMin = 0, offsetSolver = 0, j = 0;
11795 // Count until current hilbertId is reached
11796 while(sliceGlobalHilbertInfo[j][0] < hIdKeys[i]) {
11797 offset += sliceGlobalHilbertInfo[j][2];
11798 offsetPart += sliceGlobalHilbertInfo[j][3];
11799 offsetMin += sliceGlobalHilbertInfo[j][4];
11800 offsetSolver += sliceGlobalHilbertInfo[j][5];
11801 j++;
11802 }
11803 // Count until this domain is reached
11804 while(sliceGlobalHilbertInfo[j][1] < domainId()) {
11805 offset += sliceGlobalHilbertInfo[j][2];
11806 offsetPart += sliceGlobalHilbertInfo[j][3];
11807 offsetMin += sliceGlobalHilbertInfo[j][4];
11808 offsetSolver += sliceGlobalHilbertInfo[j][5];
11809 j++;
11810 }
11811 hilbertDomainOffset[i] = offset;
11812 hilbertPartitionDomainOffset[i] = offsetPart;
11813 hilbertMinLevelDomainOffset[i] = offsetMin;
11814
11815 if(solverId > -1) {
11816 hilbertDomainOffsetSolver[i] = offsetSolver;
11817 TERMM_IF_COND(!g_multiSolverGrid && offsetSolver != offset, "Error: fixme");
11818 }
11819 }
11820
11821 // Contains offset for every domain to determine global slice cellId
11822 MIntScratchSpace domainOffset(noSliceDomains + 1, AT_, "domainOffset");
11823 // Collect number of slice cells for each domain
11824 MPI_Allgather(&noSliceCells, 1, type_traits<MInt>::mpiType(), &domainOffset[0], 1, type_traits<MInt>::mpiType(),
11825 mpiCommSlice, AT_, "noSliceCells", "domainOffset[0]");
11826
11827 // Calculate offsets from received noSliceCells
11828 for(MInt i = 0, offset = 0, tmp = 0; i < (noSliceDomains + 1); i++) {
11829 tmp = domainOffset[i];
11830 domainOffset[i] = offset;
11831 offset += tmp;
11832 }
11833
11834 MInt noLocalHilbertIdsSolver = 0;
11835
11836 if(solverId < 0) {
11837 // return noHilbertIds
11838 if(noSliceHilbertIds != nullptr) {
11839 *noSliceHilbertIds = noLocalHilbertIds[sliceDomain];
11840 }
11841 // return HilbertInfo (how many cells per HilbertId and offset for file writing)
11842 if(sliceHilbertInfo != nullptr) {
11843 MIntScratchSpace hilbertInfo(noLocalHilbertIds[sliceDomain] * 3, AT_, "hilbertInfo");
11844 for(MInt i = 0; i < noLocalHilbertIds[sliceDomain]; i++) {
11845 hilbertInfo[i * 3] = hIdKeys[i];
11846 hilbertInfo[i * 3 + 1] = hilbertIdCount[hIdKeys[i]];
11847 hilbertInfo[i * 3 + 2] = hilbertDomainOffset[i];
11848 }
11849 std::copy_n(&hilbertInfo[0], noLocalHilbertIds[sliceDomain] * 3, sliceHilbertInfo);
11850 }
11851 } else {
11852 TERMM_IF_COND(noSliceHilbertIds == nullptr || sliceHilbertInfo == nullptr,
11853 "Error: solverId given, pointers need to be != nullptr.");
11854
11855 MIntScratchSpace hilbertInfoSolver(noLocalHilbertIds[sliceDomain] * 3, AT_, "hilbertInfo");
11856 MInt noHIdsSolver = 0;
11857 for(MInt i = 0; i < noLocalHilbertIds[sliceDomain]; i++) {
11858 if(hilbertIdCountSolver[hIdKeys[i]] > 0) {
11859 hilbertInfoSolver[noHIdsSolver * 3] = hIdKeys[i];
11860 hilbertInfoSolver[noHIdsSolver * 3 + 1] = hilbertIdCountSolver[hIdKeys[i]];
11861 hilbertInfoSolver[noHIdsSolver * 3 + 2] = hilbertDomainOffsetSolver[i];
11862 noHIdsSolver++;
11863 }
11864 }
11865
11866 noLocalHilbertIdsSolver = noHIdsSolver;
11867 // return noHilbertIds for solver
11868 *noSliceHilbertIds = noHIdsSolver;
11869 // return HilbertInfo (how many solver-cells per HilbertId and offset for file writing)
11870 std::copy_n(&hilbertInfoSolver[0], noHIdsSolver * 3, sliceHilbertInfo);
11871 }
11872
11873
11875 // Step 3.1: determine neighbor domains and compute partition data
11877
11878 // The neighbor domains are needed to create correct mapping of neighbor cells which are on other
11879 // domains
11880
11881 // Reference to relevant neighbor cells
11882 array<MInt, nDimSlice * 2> nghbrArray{};
11883 if(axis == "x") {
11884 nghbrArray = {{2, 3, 4, 5}};
11885 } else if(axis == "y") {
11886 nghbrArray = {{0, 1, 4, 5}};
11887 } else if(axis == "z") {
11888 nghbrArray = {{0, 1, 2, 3}};
11889 }
11890
11891 // Collector for related domains of slice to create global cellId map
11892 std::set<MInt> relatedDomains;
11893
11894 // Check for neighbor cell of all slice cells on other domain
11895 for(MInt i = 0; i < noSliceMinLevelCells; i++) { // TODO labels:GRID this is not exactly what the comment above states
11896 // Only check for neighbors in slice directions
11897 for(MInt j = 0; j < (nDimSlice * 2); j++) {
11898 // Skip if no neighbor in current direction
11899 if(!a_hasNeighbor(sliceMinLevelCells[i], nghbrArray[j])) {
11900 continue;
11901 }
11902 // Save related domain
11903 MInt nId = a_neighborId(sliceMinLevelCells[i], nghbrArray[j]);
11904 const MInt nghbrDomainId = findNeighborDomainId(a_globalId(nId));
11905 if(domainId() != nghbrDomainId) {
11906 relatedDomains.insert(nghbrDomainId);
11907 }
11908 }
11909 }
11910
11911 // Determine partition level shift in slice
11912 MInt partitionLevelShift = 0;
11913 for(MInt i = 0; i < noSlicePartitionCells; ++i) {
11914 const MInt levelDiff = a_level(slicePartitionCells[i]) - m_minLevel;
11915 partitionLevelShift = mMax(levelDiff, partitionLevelShift);
11916 }
11917
11918 // Determine partition level ancestors (parent cells of partition cells)
11919 set<MLong> partitionLevelAncestorIds;
11920 for(MInt i = 0; i < noSliceCells; i++) {
11921 const MInt cellId = cellIdsInSlice[i];
11922 if(a_hasProperty(cellId, Cell::IsPartLvlAncestor) && !a_hasProperty(cellId, Cell::IsHalo)) {
11923 partitionLevelAncestorIds.insert(cellId);
11924 }
11925 }
11926
11927
11929 // Step 3.2: Map (and exchange) globalIds to new globalIds in slice
11931
11932 // Create idMap to map from globalId to global slice cellId for the local cells
11933 map<MInt, MInt> idMap;
11934 // CellId -1 should be invariant
11935 idMap[-1] = -1;
11936 // Map global cellIds of locally founded slice cells to global slice cellIds
11937 for(MInt i = 0, j = 0, h = 0; i < noSliceCells; i++, j++) {
11938 // set next hilbertr offset and reset counter
11939 if(j == hilbertIdCount[hIdKeys[h]]) {
11940 h++;
11941 j = 0;
11942 }
11943 idMap[cellIdsInSlice[i] + m_domainOffsets[domainId()]] = j + hilbertDomainOffset[h];
11944 }
11945
11946 // If slice has no related domains, skip slice data exchange step
11947 if(!relatedDomains.empty()) {
11948 // Get unique domainIds form set
11949 std::vector<MInt> sliceRelatedDomains(relatedDomains.begin(), relatedDomains.end());
11950
11951 // All cellIds from other domains will be saved in a single vector.
11952 // Determine offset for this vector for each
11953 MInt noRecvCells = 0;
11954 MIntScratchSpace relDomainOffsets(sliceRelatedDomains.size() + 1, AT_, "relDomainOffsets");
11955 for(MUlong i = 0; i < sliceRelatedDomains.size(); i++) {
11956 relDomainOffsets[i] = noRecvCells;
11957 noRecvCells +=
11958 domainOffset[domainMap[sliceRelatedDomains[i]] + 1] - domainOffset[domainMap[sliceRelatedDomains[i]]];
11959 }
11960 // Save total number of cellIds which will be received
11961 relDomainOffsets[sliceRelatedDomains.size()] = noRecvCells;
11962
11963 // Scratch to save all cellIds from other domains
11964 MIntScratchSpace recvIds(noRecvCells, AT_, "recvIds");
11965 recvIds.fill(-1);
11966
11967 // Save mpi requests
11968 ScratchSpace<MPI_Request> sendRequests(sliceRelatedDomains.size(), AT_, "sendRequests");
11969 fill(sendRequests.begin(), sendRequests.end(), MPI_REQUEST_NULL);
11970 ScratchSpace<MPI_Request> recvRequests(sliceRelatedDomains.size(), AT_, "recvRequests");
11971 fill(recvRequests.begin(), recvRequests.end(), MPI_REQUEST_NULL);
11972
11973 // Start receiving
11974 for(MUlong i = 0; i < sliceRelatedDomains.size(); i++) {
11975 MInt d = sliceRelatedDomains[i];
11976 MPI_Irecv(&recvIds[relDomainOffsets[i]], domainOffset[domainMap[d] + 1] - domainOffset[domainMap[d]],
11977 type_traits<MInt>::mpiType(), domainMap[d], domainMap[d], mpiCommSlice, &recvRequests[i], AT_,
11978 "recvIds[relDomainOffsets[i]]");
11979 }
11980
11981 // Start sending
11982 for(MUlong i = 0; i < sliceRelatedDomains.size(); i++) {
11983 MPI_Isend(&cellIdsInSlice[0], noSliceCells, type_traits<MInt>::mpiType(), domainMap[sliceRelatedDomains[i]],
11984 domainMap[domainId()], mpiCommSlice, &sendRequests[i], AT_, "cellIdsInSlice[0]");
11985 }
11986
11987 // Finish receiving
11988 MPI_Waitall(sliceRelatedDomains.size(), &recvRequests[0], MPI_STATUSES_IGNORE, AT_);
11989
11990 // Finish sending
11991 MPI_Waitall(sliceRelatedDomains.size(), &sendRequests[0], MPI_STATUSES_IGNORE, AT_);
11992
11993 // Map relevant cellIds for each related domain
11994 for(MUlong j = 0; j < sliceRelatedDomains.size(); j++) {
11995 MInt d = sliceRelatedDomains[j];
11996 std::vector<MInt> hId(noLocalHilbertIds[domainMap[d]]);
11997 std::vector<MInt> hCount(noLocalHilbertIds[domainMap[d]]);
11998 std::vector<MInt> hOffsets(noLocalHilbertIds[domainMap[d]]);
11999
12000 // Calculate hilbertDomainOffset for related domain
12001 for(MUlong k = 0, i = 0; i < hId.size(); k++) {
12002 if(sliceGlobalHilbertInfo[k][1] == d) {
12003 hId[i] = sliceGlobalHilbertInfo[k][0];
12004 hCount[i] = sliceGlobalHilbertInfo[k][2];
12005
12006 MInt m = 0;
12007 MInt offset = 0;
12008 while(sliceGlobalHilbertInfo[m][0] < hId[i]) {
12009 offset += sliceGlobalHilbertInfo[m][2];
12010 m++;
12011 }
12012 while(sliceGlobalHilbertInfo[m][1] < d) {
12013 offset += sliceGlobalHilbertInfo[m][2];
12014 m++;
12015 }
12016 hOffsets[i] = offset;
12017 i++;
12018 }
12019 }
12020
12021 // Map global cellIds of related cellIds to global slice cellIds
12022 for(MInt i = 0, n = 0, h = 0; i < (relDomainOffsets[j + 1] - relDomainOffsets[j]); i++, n++) {
12023 if(n == hCount[h]) {
12024 h++;
12025 n = 0;
12026 }
12027 idMap[recvIds[i + relDomainOffsets[j]] + m_domainOffsets[d]] = n + hOffsets[h];
12028 }
12029 }
12030 }
12031 // Check if only cells where found, but no partitionCells (e.g. parent is located on this domain,
12032 // but the first partitionCell which belongs to the slice is on next domain
12033 MBool onlySliceCells = false;
12034 if(noSlicePartitionCells < 1) {
12035 onlySliceCells = true;
12036 }
12037
12038 // Create articifal mapped id for partitioncell on next domain, and update slicePartitionCells.
12039 // Needed for computation of workload.
12040 if(noSlicePartitionCells > 0) {
12041 std::map<MInt, MInt>::iterator it;
12042 MInt lastKeyId = hIdKeys.size() - 1;
12043 MInt val = hilbertIdCount[hIdKeys[lastKeyId]] + hilbertDomainOffset[lastKeyId];
12044
12045 it = idMap.find(slicePartitionCells[noSlicePartitionCells] + m_domainOffsets[domainId()]);
12046
12047 auto result = std::find_if(idMap.begin(), idMap.end(), [val](const auto& mo) { return mo.second == val; });
12048
12049 if(it == idMap.end() && result == idMap.end()) {
12050 idMap[slicePartitionCells[noSlicePartitionCells] + m_domainOffsets[domainId()]] =
12051 hilbertIdCount[hIdKeys[lastKeyId]] + hilbertDomainOffset[lastKeyId];
12052 } else if(result != idMap.end()) {
12053 MInt foundKey = result->first;
12054 slicePartitionCells[noSlicePartitionCells] = foundKey - m_domainOffsets[domainId()];
12055 }
12056 }
12057
12059 // Step 3.3: determine partition cell information and workload
12061
12062 // New number of childs in a refinement step
12063 const MUint noChildInRef = pow(2, nDimSlice);
12064
12065 // Create Variables to hold slice grid information
12066 MIntScratchSpace partitionCellsId(noSlicePartitionCells, AT_, "partitionCellsId");
12067 MFloatScratchSpace partitionCellsWorkload(noSlicePartitionCells, AT_, "partitionCellsWorkLoad");
12068 MInt maxNoOffsprings = 0;
12069 MFloat maxWorkload = 0.0;
12070 MFloat totalWorkload = 0.0;
12071
12072 for(MInt i = 0, c = 0, partitionCellNoOffsprings = 0; i < noSlicePartitionCells; i++) {
12073 partitionCellsId[i] = idMap[slicePartitionCells[i] + m_domainOffsets[domainId()]];
12074 // Count number of offsprings for partitionCells (start with 1)
12075 partitionCellNoOffsprings = 1;
12076 // Search all sliceCells between 2 slice partitionCells for offsprings. The id mapping is used
12077 // here since the mapped ids are sorted by the slice hilbertId
12078 while(idMap[a_globalId(cellIdsInSlice[c])] < idMap[slicePartitionCells[i + 1] + m_domainOffsets[domainId()]]) {
12079 // Only count cells which are on a higher level than current partitionCell
12080 if(a_level(cellIdsInSlice[c]) > a_level(slicePartitionCells[i])) {
12081 partitionCellNoOffsprings++;
12082 }
12083 c++;
12084 if(c == noSliceCells) {
12085 break;
12086 }
12087 }
12088
12089 // partitionCellsWorkload
12090 partitionCellsWorkload[i] = partitionCellNoOffsprings;
12091 // totalWorkload
12092 totalWorkload += partitionCellsWorkload[i];
12093 // maxNoOfssprings
12094 maxNoOffsprings = mMax(partitionCellNoOffsprings, maxNoOffsprings);
12095 // maxWorkload
12096 maxWorkload = mMax(partitionCellsWorkload[i], maxWorkload);
12097 }
12098
12099 // Get total number of partition level ancestors
12100 MLong totalNoPartitionLevelAncestors = 0;
12101 MLong localPartitionLevelAncestorCount = (signed)partitionLevelAncestorIds.size();
12102 MPI_Allreduce(&localPartitionLevelAncestorCount, &totalNoPartitionLevelAncestors, 1, MPI_LONG, MPI_SUM, mpiCommSlice,
12103 AT_, "localPartitionLevelAncestorCount", "totalNoPartitionLevelAncestors");
12104 // max partition level shift
12105 MInt maxPartitionLevelShift = -1;
12106 MPI_Allreduce(&partitionLevelShift, &maxPartitionLevelShift, 1, MPI_INT, MPI_MAX, mpiCommSlice, AT_,
12107 "partitionLevelShift", "maxPartitionLevelShift");
12108
12109 // Get max number of offsprings and calculate maxCPU
12110 MPI_Allreduce(MPI_IN_PLACE, &maxNoOffsprings, 1, MPI_INT, MPI_MAX, mpiCommSlice, AT_, "MPI_IN_PLACE",
12111 "maxNoOffsprings");
12112 // Get total workload by sum
12113 MPI_Allreduce(MPI_IN_PLACE, &totalWorkload, 1, MPI_DOUBLE, MPI_SUM, mpiCommSlice, AT_, "MPI_IN_PLACE",
12114 "totalWorkload");
12115 MPI_Allreduce(MPI_IN_PLACE, &maxWorkload, 1, MPI_DOUBLE, MPI_MAX, mpiCommSlice, AT_, "MPI_IN_PLACE", "maxWorkload");
12116 MFloat maxNoCPUs = totalWorkload / maxWorkload;
12117
12118
12120 // Step 3.4: determine min level cell information
12122
12123 // Mapping of child positions from 3D to 2D
12124 array<MInt, 8> childArray{};
12125 if(axis == "x") {
12126 childArray = {{0, 0, 1, 1, 2, 2, 3, 3}};
12127 } else if(axis == "y") {
12128 childArray = {{0, 1, 0, 1, 2, 3, 2, 3}};
12129 } else if(axis == "z") {
12130 childArray = {{0, 1, 2, 3, 0, 1, 2, 3}};
12131 }
12132
12133 // Get boundingBox and decisiveDirection for the slice
12134 const array<MFloat, nDimSlice* 2> boundingBox = {{m_boundingBox[coordArray[0]], m_boundingBox[coordArray[1]],
12135 m_boundingBox[coordArray[0] + 3],
12136 m_boundingBox[coordArray[1] + 3]}};
12137 const array<MFloat, nDimSlice* 2> targetGridBoundingBox = {
12138 {m_targetGridBoundingBox[coordArray[0]], m_targetGridBoundingBox[coordArray[1]],
12139 m_targetGridBoundingBox[coordArray[0] + 3], m_targetGridBoundingBox[coordArray[1] + 3]}};
12140 array<MFloat, nDimSlice * 2> geometryExtents{};
12141 MInt decisiveDirection = 0;
12142 for(MInt dir = 0; dir < nDimSlice; dir++) {
12143 geometryExtents[dir] = targetGridBoundingBox[dir + nDimSlice] - targetGridBoundingBox[dir];
12144 decisiveDirection = geometryExtents[dir] > geometryExtents[decisiveDirection] ? dir : decisiveDirection;
12145 }
12146
12147 // minLevelCellsTreeId
12148 MLongScratchSpace sliceMinLevelCellsTreeId(noSliceMinLevelCells, AT_, "sliceMinLevelCellsTreeId");
12149 for(MInt i = 0; i < noSliceMinLevelCells; ++i) {
12150 std::array<MFloat, nDimSlice> coord = {
12151 {a_coordinate(sliceMinLevelCells[i], coordArray[0]), a_coordinate(sliceMinLevelCells[i], coordArray[1])}};
12152 maia::grid::hilbert::coordinatesToTreeId<nDimSlice>(sliceMinLevelCellsTreeId[i], &coord[0],
12153 (MLong)m_targetGridMinLevel, &targetGridCenterOfGravity[0],
12154 m_targetGridLengthLevel0);
12155 }
12156
12157 // minLevelCellsNghbrIds
12158 MLongScratchSpace sliceMinLevelCellsNghbrIds(noSliceMinLevelCells, 2 * nDimSlice, AT_, "sliceMinLevelCellsNghbrIds");
12159 for(MInt i = 0; i < noSliceMinLevelCells; ++i) {
12160 for(MInt j = 0; j < (nDimSlice * 2); j++) {
12161 MInt nghbrId = a_neighborId(sliceMinLevelCells[i], nghbrArray[j]);
12162 if(nghbrId == -1) {
12163 sliceMinLevelCellsNghbrIds(i, j) = -1;
12164 } else {
12165 sliceMinLevelCellsNghbrIds(i, j) = idMap[a_globalId(nghbrId)];
12166 }
12167 }
12168 }
12169
12170
12172 // Step 3.5: determine cell information
12174
12175 MInt maxLevel = -1;
12176 ScratchSpace<MUchar> sliceCellInfo(noSliceCells, AT_, "sliceCellInfo");
12177 // cell info
12178 for(MInt i = 0; i < noSliceCells; ++i) {
12179 MLong cellId = cellIdsInSlice[i];
12180 MUint noChilds = 0;
12181 MUint curChildPos = 0;
12182 // Counting how many childs of current Cell belong to the slice
12183 for(MInt j = 0; j < pow(2, nDim) && curChildPos < noChildInRef; j++) {
12184 if(a_childId(cellIdsInSlice[i], j) != -1) {
12185 // Check to ensure that child cellId is also in slice
12186 if(fabs(intercept - a_coordinate(a_childId(cellIdsInSlice[i], j), axisNum))
12187 > halfCellLength(a_childId(cellIdsInSlice[i], j))) {
12188 continue;
12189 }
12190 noChilds += 1;
12191 curChildPos++;
12192 }
12193 }
12194 // Position of current cell in the child array of its parent
12195 MUint pos = 0;
12196 if(a_parentId(cellId) > -1) {
12197 MInt parentId = a_parentId(cellId);
12198 for(MUint j = 0; j < (unsigned)m_maxNoChilds; j++) {
12199 if(a_childId(parentId, j) == cellId) {
12200 pos = childArray[j];
12201 }
12202 }
12203 }
12204 // MinLevelCell indicator
12205 MUint isMinLvl = 0;
12206 if(a_level(cellId) == m_minLevel) {
12207 isMinLvl = (MUint)1;
12208 }
12209 MUint tmpBit = noChilds | (pos << 4) | (isMinLvl << 7);
12210 sliceCellInfo[i] = static_cast<MUchar>(tmpBit);
12211
12212 // Determine max level in slice
12213 if(a_level(cellId) > maxLevel) {
12214 maxLevel = a_level(cellId);
12215 }
12216 }
12217
12218
12220 // Step 4: write grid file with slice data
12222
12223 // Create slice grid flle
12224 using namespace maia::parallel_io;
12225
12226 // Create File
12227 const MString gridFileName = fileName + ParallelIo::fileExt();
12228 ParallelIo file(gridFileName, PIO_REPLACE, mpiCommSlice);
12229
12230 // Determine offsets and total number of cells
12231 ParallelIo::size_type sliceCellsOffset, noTotalCells;
12232 ParallelIo::calcOffset(noSliceCells, &sliceCellsOffset, &noTotalCells, mpiCommSlice);
12233 // If only none partitionCells are found, set noSlicePartitionCells to 0, to write no data in
12234 // partitionCell array
12235 if(onlySliceCells) {
12236 noSlicePartitionCells = 0;
12237 }
12238 // Determine offsets and total number of partition cells
12239 ParallelIo::size_type partitionOffset, noPartitionCells;
12240 ParallelIo::calcOffset(noSlicePartitionCells, &partitionOffset, &noPartitionCells, mpiCommSlice);
12241
12242 ParallelIo::size_type minLevelCellsOffset, noTotalMinLevelCells;
12243 ParallelIo::calcOffset(noSliceMinLevelCells, &minLevelCellsOffset, &noTotalMinLevelCells, mpiCommSlice);
12244
12245 MFloat avgWorkload = totalWorkload / ((MFloat)noPartitionCells);
12246 MFloat avgOffspring = ((MFloat)noTotalCells) / ((MFloat)noPartitionCells);
12247
12248 // Define arrays
12249 file.defineArray(PIO_LONG, "partitionCellsGlobalId", noPartitionCells);
12250 file.defineArray(PIO_FLOAT, "partitionCellsWorkload", noPartitionCells);
12251 file.defineArray(PIO_LONG, "minLevelCellsTreeId", noTotalMinLevelCells);
12252 file.defineArray(PIO_LONG, "minLevelCellsNghbrIds", 2 * nDimSlice * noTotalMinLevelCells);
12253 file.defineArray(PIO_UCHAR, "cellInfo", noTotalCells);
12254
12255 const MInt noSolvers = m_tree.noSolvers();
12256 const MBool writeSolver = (noSolvers > 1 || g_multiSolverGrid);
12257 ScratchSpace<MUchar> solverBits(max(noSliceCells, 1), AT_, "solverBits");
12258 if(writeSolver) {
12259 file.defineArray(PIO_UCHAR, "solver", noTotalCells);
12260
12261 for(MInt i = 0; i < noSliceCells; ++i) {
12262 const MLong cellId = cellIdsInSlice[i];
12263 MUint tmpBit = 0;
12264 for(MInt solver = 0; solver < noSolvers; solver++) {
12265 if(m_tree.solver(cellId, solver)) {
12266 tmpBit |= (1 << solver);
12267 }
12268 }
12269 solverBits[i] = static_cast<MUchar>(tmpBit);
12270 }
12271 }
12272
12273 // Set attributes
12274 MInt tstep = 0;
12275 MPI_Allreduce(MPI_IN_PLACE, &maxLevel, 1, MPI_INT, MPI_MAX, mpiCommSlice, AT_, "MPI_IN_PLACE", "maxLevel");
12276 // Get total number of leaf cells
12277 MPI_Allreduce(MPI_IN_PLACE, &noLeafCells, 1, MPI_LONG, MPI_SUM, mpiCommSlice, AT_, "MPI_IN_PLACE", "noLeafCells");
12278
12279 // Count number of cells per solver and set as attribute
12280 if(g_multiSolverGrid) {
12281 for(MInt b = 0; b < noSolvers; b++) {
12282 MLong solverCount = 0;
12283 for(MInt i = 0; i < noSliceCells; i++) {
12284 const MLong cellId = cellIdsInSlice[i];
12285 if(m_tree.solver(cellId, b) == true) {
12286 solverCount++;
12287 }
12288 }
12289 MPI_Allreduce(MPI_IN_PLACE, &solverCount, 1, MPI_LONG, MPI_SUM, mpiCommSlice, AT_, "MPI_IN_PLACE", "solverCount");
12290 file.setAttributes(&solverCount, "noCells_" + std::to_string(b), 1);
12291 }
12292 }
12293
12294 // Set all attributes
12295 file.setAttributes(&nDimSlice, "nDim", 1);
12296 file.setAttributes(&noSolvers, "noSolvers", 1);
12297 file.setAttributes(&tstep, "globalTimeStep", 1);
12298 file.setAttributes(&noTotalCells, "noCells", 1);
12299 file.setAttributes(&noLeafCells, "noLeafCells", 1);
12300 file.setAttributes(&noTotalMinLevelCells, "noMinLevelCells", 1);
12301 file.setAttributes(&noPartitionCells, "noPartitionCells", 1);
12302 file.setAttributes(&totalNoPartitionLevelAncestors, "noPartitionLevelAncestors", 1);
12303 file.setAttributes(&m_minLevel, "minLevel", 1);
12304 file.setAttributes(&maxLevel, "maxLevel", 1);
12305 file.setAttributes(&m_maxUniformRefinementLevel, "maxUniformRefinementLevel", 1);
12306 file.setAttributes(&maxPartitionLevelShift, "maxPartitionLevelShift", 1);
12307 file.setAttributes(&m_lengthLevel0, "lengthLevel0", 1);
12308 file.setAttributes(&centerOfGravity[0], "centerOfGravity", nDimSlice);
12309 file.setAttributes(&boundingBox[0], "boundingBox", 2 * nDimSlice);
12310
12311 // Add additional multisolver information if grid is reordered by a different Hilbert curve
12312 if(m_hasMultiSolverBoundingBox) {
12313 file.setAttributes(&m_targetGridLengthLevel0, "multiSolverLengthLevel0", 1);
12314 file.setAttributes(&m_targetGridMinLevel, "multiSolverMinLevel", 1);
12315 file.setAttributes(&targetGridCenterOfGravity[0], "multiSolverCenterOfGravity", nDimSlice);
12316 file.setAttributes(&targetGridBoundingBox[0], "multiSolverBoundingBox", 2 * nDimSlice);
12317 }
12318
12319 file.setAttributes(&m_reductionFactor, "reductionFactor", 1);
12320 file.setAttributes(&decisiveDirection, "decisiveDirection", 1);
12321 file.setAttributes(&totalWorkload, "totalWorkload", 1);
12322 file.setAttributes(&maxWorkload, "partitionCellMaxWorkload", 1);
12323 file.setAttributes(&avgWorkload, "partitionCellAverageWorkload", 1);
12324 file.setAttributes(&maxNoOffsprings, "partitionCellMaxNoOffspring", 1);
12325 file.setAttributes(&avgOffspring, "partitionCellAverageNoOffspring", 1);
12326 file.setAttributes(&m_partitionCellWorkloadThreshold, "partitionCellWorkloadThreshold", 1);
12327 file.setAttributes(&m_partitionCellOffspringThreshold, "partitionCellOffspringThreshold", 1);
12328 file.setAttributes(&maxNoCPUs, "maxNoBalancedCPUs", 1);
12329
12330 // Save slice axis and intercept to identify the grid file
12331 file.setAttribute(axis, "sliceAxis");
12332 file.setAttribute(intercept, "sliceIntercept");
12333
12334 MBool optimizedSliceIo = true;
12335 optimizedSliceIo = Context::getBasicProperty<MBool>("optimizedSliceIo", AT_, &optimizedSliceIo);
12336
12337 // Write data in chunks of contiguous hilbert-ids/min-cells
12338 if(optimizedSliceIo) {
12339 std::vector<MInt> contHilbertIdsPartitionCounts{};
12340 std::vector<MInt> contHilbertIdsPartitionOffset{};
12341
12342 std::vector<MInt> contHilbertIdCount{};
12343 std::vector<MInt> contHilbertIdOffset{};
12344
12345 std::vector<MInt> contHilbertIdMinCellCount{};
12346 std::vector<MInt> contHilbertIdMinCellOffset{};
12347
12348 { // Min-cells
12349 MInt contHIdMinCellCount = -1;
12350 std::vector<MInt> minCellCount{};
12351 std::vector<MInt> minCellOffset{};
12352 // Store min-cell counts/offsets
12353 for(MInt i = 0, localMinOffset = 0; i < noLocalHilbertIds[sliceDomain]; i++) {
12354 const MInt count = hilbertIdMinLevelCellsCount[hIdKeys[i]];
12355 const MInt offset = hilbertMinLevelDomainOffset[i];
12356 if(localMinOffset < noSliceMinLevelCells) {
12357 if(count > 0) {
12358 minCellCount.push_back(count);
12359 minCellOffset.push_back(offset);
12360 }
12361 }
12362 localMinOffset += count;
12363 }
12364
12365 contHIdMinCellCount = (noSliceMinLevelCells > 0) ? minCellCount[0] : -1;
12366 if(noSliceMinLevelCells == 1) { // Only one min-cell
12367 contHilbertIdMinCellCount.push_back(contHIdMinCellCount);
12368 contHilbertIdMinCellOffset.push_back(minCellOffset[0]);
12369 }
12370
12371 // Find contiguous min-cells and store counts/offsets for writing
12372 for(MInt h = 1, i = 0; h < noSliceMinLevelCells; h++) {
12373 if(minCellCount[h - 1] + minCellOffset[h - 1] == minCellOffset[h]) {
12374 contHIdMinCellCount += minCellCount[h];
12375 } else {
12376 contHilbertIdMinCellCount.push_back(contHIdMinCellCount);
12377 contHilbertIdMinCellOffset.push_back(minCellOffset[i]);
12378
12379 i = h;
12380 contHIdMinCellCount = minCellCount[h];
12381 }
12382
12383 // Last index
12384 if(h == noSliceMinLevelCells - 1) {
12385 contHilbertIdMinCellCount.push_back(contHIdMinCellCount);
12386 contHilbertIdMinCellOffset.push_back(minCellOffset[i]);
12387 }
12388 }
12389 }
12390
12391 { // Cells and partition cells
12392 MInt contHIdPartitionCellCount = hilbertIdPartitionCellsCount[hIdKeys[0]];
12393 MInt contHIdCount = hilbertIdCount[hIdKeys[0]];
12394
12395 if(noLocalHilbertIds[sliceDomain] == 1) { // Only one hilbert id
12396 contHilbertIdsPartitionCounts.push_back(contHIdPartitionCellCount);
12397 contHilbertIdsPartitionOffset.push_back(hilbertPartitionDomainOffset[0]);
12398
12399 contHilbertIdCount.push_back(contHIdCount);
12400 contHilbertIdOffset.push_back(hilbertDomainOffset[0]);
12401 }
12402
12403 // Find contiguous cells/partition-cells and store counts/offsets for writing
12404 for(MInt h = 1, i = 0; h < noLocalHilbertIds[sliceDomain]; h++) {
12405 // Check if current hilbertId is contiguous; if so increase the current cell counts
12406 if(hilbertIdPartitionCellsCount[hIdKeys[h - 1]] + hilbertPartitionDomainOffset[h - 1]
12407 == hilbertPartitionDomainOffset[h]) {
12408 TERMM_IF_NOT_COND(hilbertIdCount[hIdKeys[h - 1]] + hilbertDomainOffset[h - 1] == hilbertDomainOffset[h],
12409 "Error: cells not contiguous");
12410 contHIdPartitionCellCount += hilbertIdPartitionCellsCount[hIdKeys[h]];
12411 contHIdCount += hilbertIdCount[hIdKeys[h]];
12412 } else {
12413 // Not contiguous: store cell counts/offsets and start over with current cell
12414 contHilbertIdsPartitionCounts.push_back(contHIdPartitionCellCount);
12415 contHilbertIdsPartitionOffset.push_back(hilbertPartitionDomainOffset[i]);
12416
12417 contHilbertIdCount.push_back(contHIdCount);
12418 contHilbertIdOffset.push_back(hilbertDomainOffset[i]);
12419
12420 i = h;
12421 contHIdPartitionCellCount = hilbertIdPartitionCellsCount[hIdKeys[h]];
12422 contHIdCount = hilbertIdCount[hIdKeys[h]];
12423 }
12424
12425 // Last hilbert id, store final counts/offsets
12426 if(h == noLocalHilbertIds[sliceDomain] - 1) {
12427 contHilbertIdsPartitionCounts.push_back(contHIdPartitionCellCount);
12428 contHilbertIdsPartitionOffset.push_back(hilbertPartitionDomainOffset[i]);
12429
12430 contHilbertIdCount.push_back(contHIdCount);
12431 contHilbertIdOffset.push_back(hilbertDomainOffset[i]);
12432 }
12433 }
12434 }
12435 const MInt noContHilbertIds = contHilbertIdsPartitionCounts.size();
12436
12437 if(solverId > -1 && noSliceContHilbertIds != nullptr) {
12438 TERMM_IF_COND(sliceContiguousHilbertInfo == nullptr,
12439 "Error: sliceContiguousHilbertInfo is a nullptr but noSliceContHilbertIds is not.");
12440
12441 std::vector<MInt> hIdCountSolver;
12442 std::vector<MInt> hDomainOffsetSolver;
12443 for(MInt i = 0; i < noLocalHilbertIds[sliceDomain]; i++) {
12444 if(hilbertIdCountSolver[hIdKeys[i]] > 0) {
12445 hIdCountSolver.push_back(hilbertIdCountSolver[hIdKeys[i]]);
12446 hDomainOffsetSolver.push_back(hilbertDomainOffsetSolver[i]);
12447 }
12448 }
12449
12450 std::vector<MInt> contHilbertIdCountSolver{};
12451 std::vector<MInt> contHilbertIdOffsetSolver{};
12452
12453 // Note: can be empty if domain has no cells of this solver
12454 MInt contHIdCountSolver = (hIdCountSolver.size() > 0) ? hIdCountSolver[0] : 0;
12455
12456 if(noLocalHilbertIdsSolver == 1) { // Only one hilbert id
12457 contHilbertIdCountSolver.push_back(contHIdCountSolver);
12458 contHilbertIdOffsetSolver.push_back(hDomainOffsetSolver[0]);
12459 }
12460
12461 // Find contiguous cells and store counts/offsets for writing
12462 for(MInt h = 1, i = 0; h < noLocalHilbertIdsSolver; h++) {
12463 // Check if current hilbertId is contiguous; if so increase the current cell counts
12464 if(hIdCountSolver[h - 1] + hDomainOffsetSolver[h - 1] == hDomainOffsetSolver[h]) {
12465 contHIdCountSolver += hIdCountSolver[h];
12466 } else {
12467 // Not contiguous: store cell counts/offsets and start over with current cell
12468 contHilbertIdCountSolver.push_back(contHIdCountSolver);
12469 contHilbertIdOffsetSolver.push_back(hDomainOffsetSolver[i]);
12470
12471 i = h;
12472 contHIdCountSolver = hIdCountSolver[h];
12473 }
12474
12475 // Last hilbert id, store final count/offset
12476 if(h == noLocalHilbertIdsSolver - 1) {
12477 contHilbertIdCountSolver.push_back(contHIdCountSolver);
12478 contHilbertIdOffsetSolver.push_back(hDomainOffsetSolver[i]);
12479 }
12480 }
12481
12482 const MInt noContHilbertIdsSolver = contHilbertIdCountSolver.size();
12483 *noSliceContHilbertIds = noContHilbertIdsSolver;
12484
12485 if(noContHilbertIdsSolver > 0) {
12486 MIntScratchSpace contHilbertInfo(noContHilbertIdsSolver * 3, AT_, "contHilbertInfo");
12487 for(MInt i = 0; i < noContHilbertIdsSolver; i++) {
12488 contHilbertInfo[i * 3] = -1; // Note: cell id not required at the moment
12489 contHilbertInfo[i * 3 + 1] = contHilbertIdCountSolver[i];
12490 contHilbertInfo[i * 3 + 2] = contHilbertIdOffsetSolver[i];
12491 }
12492 std::copy_n(&contHilbertInfo[0], noContHilbertIdsSolver * 3, sliceContiguousHilbertInfo);
12493 }
12494 } else if(solverId < 0) {
12495 // return number of contiguous hilbert id chunks
12496 if(noSliceContHilbertIds != nullptr) {
12497 *noSliceContHilbertIds = noContHilbertIds;
12498 }
12499 // return contiguous HilbertInfo (how many cells per chunks of HilbertIds and offset for file writing)
12500 if(sliceContiguousHilbertInfo != nullptr) {
12501 MIntScratchSpace contHilbertInfo(noContHilbertIds * 3, AT_, "contHilbertInfo");
12502 for(MInt i = 0; i < noContHilbertIds; i++) {
12503 contHilbertInfo[i * 3] = -1; // Note: cell id not required at the moment
12504 contHilbertInfo[i * 3 + 1] = contHilbertIdCount[i];
12505 contHilbertInfo[i * 3 + 2] = contHilbertIdOffset[i];
12506 }
12507 std::copy_n(&contHilbertInfo[0], noContHilbertIds * 3, sliceContiguousHilbertInfo);
12508 }
12509 }
12510
12511 const MFloat writeTimeStart = wallTime();
12512 for(MInt i = 0, localOffset = 0, localPartOffset = 0; i < hilbertDomainOffset.size0(); i++) {
12513 // Write data for every contiguous range of hilbertIds on this domain
12514 if(i < noContHilbertIds) {
12515 // Write partitionCells
12516 file.setOffset(contHilbertIdsPartitionCounts[i], contHilbertIdsPartitionOffset[i]);
12517 file.writeArray(&partitionCellsId[0] + localPartOffset, "partitionCellsGlobalId");
12518 file.writeArray(&partitionCellsWorkload[0] + localPartOffset, "partitionCellsWorkload");
12519 localPartOffset += contHilbertIdsPartitionCounts[i];
12520
12521 // Write cellInfo
12522 file.setOffset(contHilbertIdCount[i], contHilbertIdOffset[i]);
12523 file.writeArray(&sliceCellInfo[0] + localOffset, "cellInfo");
12524 // Write solver bits
12525 if(writeSolver) {
12526 file.writeArray(&solverBits[0] + localOffset, "solver");
12527 }
12528 localOffset += contHilbertIdCount[i];
12529 } else {
12530 // dummy calls of writing function, if this domain has finished writing data, but other domains not
12531 file.setOffset(0, 0);
12532 file.writeArray(&partitionCellsId[0], "partitionCellsGlobalId");
12533 file.writeArray(&partitionCellsWorkload[0], "partitionCellsWorkload");
12534
12535 file.writeArray(&sliceCellInfo[0], "cellInfo");
12536 if(writeSolver) {
12537 file.writeArray(&solverBits[0], "solver");
12538 }
12539 }
12540 }
12541
12542 const MInt noContMinCells = contHilbertIdMinCellCount.size();
12543 for(MInt i = 0, localMinOffset = 0; i < hilbertDomainOffset.size0(); i++) {
12544 // Write data for every contiguous range of min-cells on this domain
12545 if(i < noContMinCells) {
12546 // Write min level cells tree id array
12547 file.setOffset(contHilbertIdMinCellCount[i], contHilbertIdMinCellOffset[i]);
12548 file.writeArray(&sliceMinLevelCellsTreeId[0] + localMinOffset, "minLevelCellsTreeId");
12549 // Write min level cells neighbor ids array
12550 file.setOffset(contHilbertIdMinCellCount[i] * 2 * nDimSlice, contHilbertIdMinCellOffset[i] * 2 * nDimSlice);
12551 file.writeArray(&sliceMinLevelCellsNghbrIds[0] + localMinOffset * 2 * nDimSlice, "minLevelCellsNghbrIds");
12552 localMinOffset += contHilbertIdMinCellCount[i];
12553 } else {
12554 // Dummy call if all minCell data is written or no minCell data is on this domain
12555 file.setOffset(0, 0);
12556 file.writeArray(&partitionCellsId[0], "minLevelCellsTreeId");
12557 file.writeArray(&partitionCellsId[0], "minLevelCellsNghbrIds");
12558 }
12559 }
12560
12561 const MFloat writeTimeTotal = wallTime() - writeTimeStart;
12562 if(sliceDomain == 0) std::cerr << "Slice grid " << gridFileName << " write time: " << writeTimeTotal << std::endl;
12563 } else { // !optimizedSliceIo
12564 // Note: this is quite inefficient on a large number of cores or just for many min/partition cells. The optimized
12565 // version above should be used instead, however, for debugging/checking the output the original slow version below
12566 // should be kept.
12567 const MFloat writeTimeStart = wallTime();
12568
12569 // Make sure anyone requesing the contiguous info uses the optimized version were the info is available
12570 if(noSliceContHilbertIds != nullptr || sliceContiguousHilbertInfo != nullptr) {
12571 TERMM(1, "Error: using non-optimized slice IO but contiguous Hilbert info requested by passing non-nullptr as "
12572 "arguments.");
12573 }
12574
12575 // Write data in chunks which have the same hilbertId. The number of calls of the writing function
12576 // is equal on all domains
12577 for(MInt i = 0, localOffset = 0, localPartOffset = 0, localMinOffset = 0; i < hilbertDomainOffset.size0(); i++) {
12578 if(i < noLocalHilbertIds[sliceDomain]) {
12579 // Write data for every hilbertId on this domain
12580
12581 // Write partitionCells
12582 file.setOffset(hilbertIdPartitionCellsCount[hIdKeys[i]], hilbertPartitionDomainOffset[i]);
12583 file.writeArray(&partitionCellsId[0] + localPartOffset, "partitionCellsGlobalId");
12584 file.writeArray(&partitionCellsWorkload[0] + localPartOffset, "partitionCellsWorkload");
12585 localPartOffset += hilbertIdPartitionCellsCount[hIdKeys[i]];
12586
12587 // NOTE: cellInfo was written at last before, however this seems to occassionally result in the cellInfo array
12588 // not being fully written to the file (observed for the case when each slice-rank writes only a single cellInfo
12589 // entry)
12590 // Write cellInfo
12591 file.setOffset(hilbertIdCount[hIdKeys[i]], hilbertDomainOffset[i]);
12592 file.writeArray(&sliceCellInfo[0] + localOffset, "cellInfo");
12593
12594 // Write solver bits
12595 if(writeSolver) {
12596 file.writeArray(&solverBits[0] + localOffset, "solver");
12597 }
12598
12599 // Write only minCell arrays if some are found
12600 if(localMinOffset < noSliceMinLevelCells) {
12601 // Write min level cells tree id array
12602 file.setOffset(hilbertIdMinLevelCellsCount[hIdKeys[i]], hilbertMinLevelDomainOffset[i]);
12603 file.writeArray(&sliceMinLevelCellsTreeId[0] + localMinOffset, "minLevelCellsTreeId");
12604 // Write min level cells neighbor ids array
12605 file.setOffset(hilbertIdMinLevelCellsCount[hIdKeys[i]] * 2 * nDimSlice,
12606 hilbertMinLevelDomainOffset[i] * 2 * nDimSlice);
12607 file.writeArray(&sliceMinLevelCellsNghbrIds[0] + localMinOffset * 2 * nDimSlice, "minLevelCellsNghbrIds");
12608 } else {
12609 // Dummy call if all minCell data is written or no minCell data is on this domain
12610 file.setOffset(0, 0);
12611 file.writeArray(&partitionCellsId[0], "minLevelCellsTreeId");
12612 file.writeArray(&partitionCellsId[0], "minLevelCellsNghbrIds");
12613 }
12614 localMinOffset += hilbertIdMinLevelCellsCount[hIdKeys[i]];
12615
12616 localOffset += hilbertIdCount[hIdKeys[i]];
12617 } else {
12618 // dummy calls of writing function, if this domain has finished writing data, but other
12619 // domains not
12620 file.setOffset(0, 0);
12621 file.writeArray(&partitionCellsId[0], "partitionCellsGlobalId");
12622 file.writeArray(&partitionCellsWorkload[0], "partitionCellsWorkload");
12623
12624 file.writeArray(&partitionCellsId[0], "minLevelCellsTreeId");
12625 file.writeArray(&partitionCellsId[0], "minLevelCellsNghbrIds");
12626
12627 file.writeArray(&sliceCellInfo[0], "cellInfo");
12628 if(writeSolver) {
12629 file.writeArray(&solverBits[0], "solver");
12630 }
12631 }
12632 }
12633 const MFloat writeTimeTotal = wallTime() - writeTimeStart;
12634 if(sliceDomain == 0) std::cerr << "Slice grid " << gridFileName << " write time: " << writeTimeTotal << std::endl;
12635 }
12636
12637 // Close the grid file explicitely here and finish writing before the MPI communicator is freed
12638 file.close();
12639
12640 // Free the now unneeded MPI communicator
12641 MPI_Comm_free(&mpiCommSlice, AT_, "mpiCommSlice");
12642
12643 m_log << "done!" << endl;
12644}
12645
12646
12654template <MInt nDim>
12655void CartesianGrid<nDim>::createLeafCellMapping(const MInt donorSolverId, const MInt gridCellId,
12656 std::vector<MInt>& mappedLeafCells, MBool allChilds) {
12657 TRACE();
12658
12659 mappedLeafCells.clear();
12660
12661 std::stack<MInt> cellStack;
12662
12663 // Check if this cell is used by the donor solver
12664 if(a_solver(gridCellId, donorSolverId)) {
12665 // Initialize cell stack to process
12666 cellStack.push(gridCellId);
12667
12668 // Iterate until cellstack is empty
12669 // else add all children to cell stack
12670 while(!cellStack.empty()) {
12671 // Get current cell to check and remove from stack
12672 const MInt cellId = cellStack.top();
12673 cellStack.pop();
12674
12675 // Check if this is a leaf cell of the donor solver
12676 if(a_isLeafCell(cellId, donorSolverId)) {
12677 // Add to mapped cells
12678 mappedLeafCells.push_back(cellId);
12679 } else {
12680 // Not a leaf cell, add all child cells to the cell stack and continue
12681 for(MInt childId = 0; childId < m_maxNoChilds; childId++) {
12682 if(a_hasChild(cellId, childId, donorSolverId)) {
12683 cellStack.push(a_childId(cellId, childId, donorSolverId));
12684 if(allChilds) {
12685 mappedLeafCells.push_back(a_childId(cellId, childId, donorSolverId));
12686 }
12687 }
12688 }
12689 }
12690 }
12691 } else {
12692 // Check parent cells until cell belonging to donor solver is found or there is no parent cell
12693 // anymore
12694 MBool foundMappedParent = false;
12695 MInt parentId = gridCellId;
12696 while(!foundMappedParent && a_hasParent(parentId)) {
12697 parentId = a_parentId(parentId);
12698 if(a_solver(parentId, donorSolverId) && a_isLeafCell(parentId, donorSolverId)) {
12699 foundMappedParent = true;
12700 }
12701 }
12702
12703 if(foundMappedParent) {
12704 mappedLeafCells.push_back(parentId);
12705 }
12706 }
12707
12708 // Sort mapped leaf cell ids
12709 std::sort(mappedLeafCells.begin(), mappedLeafCells.end());
12710}
12711
12712
12722template <MInt nDim>
12724 MLong* const partitionCellOffsets) {
12725 TRACE();
12726
12727 const MLong localNoPartitionCells = m_noPartitionCells;
12728 // Gather local number of partition cells on all domains
12729 MPI_Allgather(&localNoPartitionCells, 1, type_traits<MLong>::mpiType(), &noPartitionCells[0], 1,
12730 type_traits<MLong>::mpiType(), mpiComm(), AT_, "localNoPartitionCells", "noPartitionCells[0]");
12731
12732 // Partition cell offset on first domain is zero
12733 partitionCellOffsets[0] = 0;
12734 // Determine partition cell offsets for all domains (except first), last entry contains total
12735 // number of partition cells
12736 for(MInt i = 1; i < noDomains() + 1; i++) {
12737 partitionCellOffsets[i] = partitionCellOffsets[i - 1] + noPartitionCells[i - 1];
12738 }
12739
12740 // Check partition cell count
12741 if(partitionCellOffsets[noDomains()] != m_noPartitionCellsGlobal) {
12742 TERMM(1, "determineNoPartitionCellsAndOffsets(): Partition cell count does not match: "
12743 + std::to_string(partitionCellOffsets[noDomains()])
12744 + " != " + std::to_string(m_noPartitionCellsGlobal));
12745 }
12746}
12747
12748
12755template <MInt nDim>
12756void CartesianGrid<nDim>::savePartitionFile(const MString& partitionFileNameBase, const MLong partitionCellOffset) {
12757 TRACE();
12758
12759 using namespace maia::parallel_io;
12760
12761 stringstream partitionFileName;
12762 partitionFileName << m_outputDir << partitionFileNameBase << ParallelIo::fileExt();
12763
12764 // Open grid partition file and write to it
12765 ParallelIo file(partitionFileName.str(), PIO_REPLACE, mpiComm());
12766
12767 const MLong noPartitionCells = m_noPartitionCellsGlobal;
12768 file.setAttributes(&noPartitionCells, "noPartitionCells", 1);
12769
12770 file.defineArray(PIO_LONG, "partitionCellOffset", noDomains());
12771 file.setOffset(1, domainId());
12772 file.writeArray(&partitionCellOffset, "partitionCellOffset");
12773
12774 m_log << "Saved partition to file '" << partitionFileName.str() << "'." << endl;
12775}
12776
12777
12781template <MInt nDim>
12783 TRACE();
12784
12785 stringstream fileName;
12786 fileName << "partition_n" << noDomains() << "_" << globalTimeStep;
12787
12788 savePartitionFile(fileName.str(), m_localPartitionCellOffsets[0]);
12789}
12790
12791
12793template <MInt nDim>
12794void CartesianGrid<nDim>::loadPartitionFile(const MString& partitionFileName, MLong* partitionCellOffsets) {
12795 TRACE();
12796
12797 using namespace maia::parallel_io;
12798
12799 if(domainId() == 0) {
12800 // Open grid partition file
12801 ParallelIo file(partitionFileName, PIO_READ, MPI_COMM_SELF);
12802
12803 MLong noPartitionCells = -1;
12804 file.getAttribute(&noPartitionCells, "noPartitionCells");
12805 TERMM_IF_NOT_COND(noPartitionCells == m_noPartitionCellsGlobal, "global number of partition cell mismatch");
12806
12807 if(file.getArraySize("partitionCellOffset", 0) != noDomains()) {
12808 TERMM(1, "array size does not match number of domains");
12809 }
12810 file.setOffset(noDomains(), 0);
12811 file.readArray(partitionCellOffsets, "partitionCellOffset");
12812 }
12813
12814 // Distribute partition cells ids
12815 MPI_Bcast(&partitionCellOffsets[0], noDomains(), type_traits<MLong>::mpiType(), 0, mpiComm(), AT_,
12816 "partitionCellOffsets");
12817
12818 m_log << "Loaded partition from file '" << partitionFileName << "'." << endl;
12819}
12820
12821
12823template <MInt nDim>
12825 TRACE();
12826 using namespace parallel_io;
12827
12828 ParallelIo parallelIo(m_restartDir + gridInputFileName(), PIO_APPEND, mpiComm());
12829
12830 // Set offset and total size for partition-cell workloads
12831 const MInt noLocalPartitionCells = m_noPartitionCells;
12832 parallelIo.setOffset(noLocalPartitionCells, m_localPartitionCellOffsets[0]);
12833
12834 // Write new partition-cell workloads to grid file
12835 ScratchSpace<MFloat> partitionCellsWorkload(noLocalPartitionCells, AT_, "partitionCellsWorkload");
12836 MFloat totalWorkload = 0.0;
12837 for(MInt i = 0; i < noLocalPartitionCells; i++) {
12838 const MInt partitionCellId = m_localPartitionCellLocalIds[i];
12839 partitionCellsWorkload[i] = a_workload(partitionCellId);
12840 totalWorkload += a_workload(partitionCellId);
12841 }
12842
12843 MPI_Allreduce(MPI_IN_PLACE, &totalWorkload, 1, MPI_DOUBLE, MPI_SUM, mpiComm(), AT_, "MPI_IN_PLACE", "totalWorkload");
12844 // TODO labels:GRID,IO @ansgar set other attributes as well, e.g. partitionCellMaxWorkload, ...
12845 parallelIo.setAttributes(&totalWorkload, "totalWorkload", 1);
12846 parallelIo.writeArray(&partitionCellsWorkload[0], "partitionCellsWorkload");
12847}
12848
12849
12850template <MInt nDim>
12851template <MBool t_correct, MBool insideLimit>
12852MBool CartesianGrid<nDim>::pointWthCell(const MFloat* const coord, const MInt cellId,
12853 std::function<MFloat*(MInt, MFloat* const)> correctCellCoord) const {
12854 array<MFloat, nDim> tmp;
12855 const MFloat* tmpCoord = m_tree.coordinate(cellId);
12856 const MFloat* cellCenter = t_correct ? (correctCellCoord)(cellId, &tmp[0]) : tmpCoord;
12857 const MFloat _halfCellLength = halfCellLength(cellId);
12858 MBool isInCell = true;
12859
12860 if(insideLimit) {
12861 IF_CONSTEXPR(nDim == 3) {
12862 return fabs(cellCenter[0] - coord[0]) <= _halfCellLength && fabs(cellCenter[1] - coord[1]) <= _halfCellLength
12863 && fabs(cellCenter[2] - coord[2]) <= _halfCellLength;
12864 }
12865 return fabs(cellCenter[0] - coord[0]) <= _halfCellLength && fabs(cellCenter[1] - coord[1]) <= _halfCellLength;
12866 } else {
12867 for(MInt dimId = 0; dimId < nDim; dimId++) {
12868 if(coord[dimId] < cellCenter[dimId] - _halfCellLength || coord[dimId] >= cellCenter[dimId] + _halfCellLength) {
12869 isInCell = false;
12870 break;
12871 }
12872 }
12873 }
12874
12875 return isInCell;
12876}
12877
12879template <MInt nDim>
12881 TRACE();
12882
12883 for(MInt i = 0; i < nDim; i++) {
12884 bbox[i] = numeric_limits<MFloat>::max();
12885 bbox[nDim + i] = numeric_limits<MFloat>::lowest();
12886 }
12887
12888 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
12889 if(a_hasProperty(cellId, Cell::IsHalo)) {
12890 continue;
12891 }
12892
12893 const MFloat halfCellLength = F1B2 * cellLengthAtLevel(a_level(cellId));
12894 for(MInt i = 0; i < nDim; i++) {
12895 bbox[i] = mMin(bbox[i], a_coordinate(cellId, i) - halfCellLength);
12896 bbox[nDim + i] = mMax(bbox[nDim + i], a_coordinate(cellId, i) + halfCellLength);
12897 }
12898 }
12899 // See createMinLevelExchangeCells()
12900 const MFloat halfLength0 = 0.5 * ((F1 + F1 / FPOW2(30)) * m_lengthLevel0);
12901 for(MInt i = 0; i < nDim; i++) {
12902 ASSERT(bbox[i] > m_centerOfGravity[i] - halfLength0 && bbox[nDim + i] < m_centerOfGravity[i] + halfLength0,
12903 "local bounding box error");
12904 }
12905
12906 m_localBoundingBoxInit = true;
12907}
12908
12910template <MInt nDim>
12912 // Not initialized, fast check not possible
12913 if(!m_localBoundingBoxInit) {
12914 return true;
12915 }
12916
12917 const MFloat eps = 1e-12;
12918 for(MInt i = 0; i < nDim; i++) {
12919 if(coord[i] < m_localBoundingBox[i] - eps || coord[i] > m_localBoundingBox[nDim + i] + eps) {
12920 return false;
12921 }
12922 }
12923 return true;
12924}
12925
12926
12928template <MInt nDim>
12930 m_log << "Performing grid sanity checks... ";
12931
12932 const MInt noCells = m_tree.size();
12933
12934 // Check that number internal cells and the interval between domain offsets is consistent
12935 TERMM_IF_NOT_COND(m_noInternalCells == m_domainOffsets[domainId() + 1] - m_domainOffsets[domainId()],
12936 "Error: number of internal cells does not match with interval between domain offsets.");
12937
12938 // Partition cell checks
12939 {
12940 ScratchSpace<MBool> isPartitionCell(noCells, AT_, "isPartitionCell");
12941 isPartitionCell.fill(false);
12942
12943 MLong lastGlobalId = -1;
12944 for(MInt i = 0; i < m_noPartitionCells; i++) {
12945 const MInt localId = m_localPartitionCellLocalIds[i];
12946 const MLong globalId = m_localPartitionCellGlobalIds[i];
12947
12948 // Check that partition cell property is set
12949 TERMM_IF_NOT_COND(a_hasProperty(localId, Cell::IsPartitionCell), "Error: Cell is not a partition cell.");
12950 // Check the partition cell global id
12951 TERMM_IF_NOT_COND(globalId == a_globalId(localId), "Error: partition cell global id mismatch.");
12952 // Check that partition cells are sorted by global ids
12953 TERMM_IF_NOT_COND(globalId > lastGlobalId, "Error: partition cells not sorted by global id.");
12954
12955 isPartitionCell[localId] = true;
12956 lastGlobalId = a_globalId(localId);
12957 }
12958
12959 // Make sure that all other internal cells dont have the partition cell property set
12960 for(MInt i = 0; i < noCells; i++) {
12961 if(isPartitionCell[i] || a_isToDelete(i) || a_isHalo(i)) continue;
12962 TERMM_IF_COND(a_hasProperty(i, Cell::IsPartitionCell),
12963 "Error: cell marked as partition cell but not in partition cell list " + std::to_string(i));
12964 }
12965
12966 TERMM_IF_NOT_COND(m_localPartitionCellOffsets[2] == m_noPartitionCellsGlobal,
12967 "Error: global number of partition cells mismatch.");
12968
12969 MLongScratchSpace localPartitionCellCounts(noDomains(), AT_, "localPartitionCellCounts");
12970 MLongScratchSpace localPartitionCellOffsets(noDomains() + 1, AT_, "localPartitionCellOffsets");
12971 // Determine the number of partition cells on all domains and the partition cell offsets
12972 determineNoPartitionCellsAndOffsets(&localPartitionCellCounts[0], &localPartitionCellOffsets[0]);
12973
12974 TERMM_IF_NOT_COND(m_localPartitionCellOffsets[0] == localPartitionCellOffsets[domainId()],
12975 "Error: partition cell offset mismatch.");
12976 TERMM_IF_NOT_COND(m_localPartitionCellOffsets[1] == localPartitionCellOffsets[domainId() + 1],
12977 "Error: next partition cell offset mismatch.");
12978 }
12979
12980 // Check the global to local id mapping
12981 for(MInt i = 0; i < noCells; i++) {
12982 if(a_isToDelete(i) || a_hasProperty(i, Cell::IsPeriodic)) continue;
12983 TERMM_IF_NOT_COND(m_globalToLocalId[a_globalId(i)] == i,
12984 "Error in global to local id mapping: l" + to_string(i) + " g" + to_string(a_globalId(i)) + " g2l"
12985 + to_string(m_globalToLocalId[a_globalId(i)]));
12986 }
12987
12988 // Check partition level ancestors
12989 {
12990 ScratchSpace<MBool> isPartLvlAncestor(noCells, AT_, "isPartLvlAncestor");
12991 isPartLvlAncestor.fill(false);
12992
12993 MInt noLocalPartLvlAncestors = 0;
12994 MInt count = 0;
12995 for(auto& i : m_partitionLevelAncestorIds) {
12996 TERMM_IF_NOT_COND(a_hasProperty(i, Cell::IsPartLvlAncestor), "Error: cell is not a partition level ancestor.");
12997 TERMM_IF_COND(a_isToDelete(i), "Error: partition level ancestor marked for deletion.");
12998 isPartLvlAncestor[i] = true;
12999
13000 // Check global ids of childs
13001 for(MInt child = 0; child < m_maxNoChilds; child++) {
13002 const MInt childId = a_childId(i, child);
13003 const MLong childGlobalId = (childId > -1) ? a_globalId(childId) : -1;
13004 const MInt index = count * m_maxNoChilds + child;
13005 TERMM_IF_NOT_COND(m_partitionLevelAncestorChildIds[index] == childGlobalId,
13006 "Error: partition level ancestor child id mismatch. "
13007 + std::to_string(m_partitionLevelAncestorChildIds[index])
13008 + " != " + std::to_string(childGlobalId) + "; " + std::to_string(i) + "; "
13009 + std::to_string(child));
13010 }
13011 count++;
13012
13013 if(!a_isHalo(i)) {
13014 noLocalPartLvlAncestors++;
13015 }
13016 }
13017
13018 // Check the number of internal partition level ancestor cells
13019 TERMM_IF_NOT_COND(noLocalPartLvlAncestors == m_noPartitionLevelAncestors,
13020 "Error: number of local partition level ancestors mismatch.");
13021
13022 // All other internal cells shouldnt be marked
13023 for(MInt i = 0; i < noCells; i++) {
13024 if(isPartLvlAncestor[i] || a_isToDelete(i) || a_isHalo(i)) continue;
13025 TERMM_IF_COND(a_hasProperty(i, Cell::IsPartLvlAncestor),
13026 "Error: cell marked as partition level ancestor but is not in corresponding list.");
13027 }
13028
13029 // TODO labels:GRID check other partition level ancestor info
13030 }
13031
13032 // Check min-level cells
13033 {
13034 ScratchSpace<MBool> isMinLevelCell(noCells, AT_, "isMinLevelCell");
13035 isMinLevelCell.fill(false);
13036
13037 for(auto& minLevelCell : m_minLevelCells) {
13038 TERMM_IF_NOT_COND(a_level(minLevelCell) == m_minLevel, "Error: level of min-level cell mismatch.");
13039 isMinLevelCell[minLevelCell] = true;
13040 }
13041
13042 for(MInt i = 0; i < noCells; i++) {
13043 if(isMinLevelCell[i] || a_isToDelete(i)) continue;
13044 TERMM_IF_COND(a_level(i) <= m_minLevel, "Error: non-min-level cell has level <= minLevel");
13045 }
13046 }
13047
13048 m_log << "done" << std::endl;
13049}
13050
13051
13055template <MInt nDim>
13057 TRACE();
13058 if(globalNoDomains() == 1) {
13059 return;
13060 }
13061
13062 m_log << "checkWindowHaloConsistency... ";
13063
13064 // WH_old
13065 if(m_haloMode > 0) {
13066 ScratchSpace<MBool> isWindowCell(m_tree.size(), AT_, "isWindowCell");
13067 ScratchSpace<MBool> isHaloCell(m_tree.size(), AT_, "isHaloCell");
13068 isWindowCell.fill(false);
13069 isHaloCell.fill(false);
13070
13071 ScratchSpace<MPI_Request> recvRequests(std::max(1, noNeighborDomains()), AT_, "recvRequests");
13072 ScratchSpace<MPI_Request> sendRequests(std::max(1, noNeighborDomains()), AT_, "sendRequests");
13073
13074 for(MInt solver = 0; solver < treeb().noSolvers(); ++solver) {
13076 // Check #1: number of window/halo cells match
13078 // Start receiving number of window cells from each neighbor domain
13079 fill(recvRequests.begin(), recvRequests.end(), MPI_REQUEST_NULL);
13080 MIntScratchSpace noWindowCellsRecv(std::max(1, noNeighborDomains()), AT_, "noWindowCellsRecv");
13081 for(MInt d = 0; d < noNeighborDomains(); d++) {
13082 noWindowCellsRecv[d] = -1;
13083 MPI_Irecv(&noWindowCellsRecv[d], 1, type_traits<MInt>::mpiType(), neighborDomain(d), neighborDomain(d),
13084 mpiComm(), &recvRequests[d], AT_, "noWindowCellsRecv[d]");
13085 }
13086
13087 // Start sending number of window cells to each neighbor domain
13088 fill(sendRequests.begin(), sendRequests.end(), MPI_REQUEST_NULL);
13089 MIntScratchSpace noWindowCellsSend(std::max(1, noNeighborDomains()), AT_, "noWindowCellsSend");
13090 for(MInt d = 0; d < noNeighborDomains(); d++) {
13091 // //TODO_SS labels:GRID,COMM currently without duplicate window cells for periodic ...
13092 noWindowCellsSend[d] = noSolverWindowCells(d, solver); // noWindowCells(d);
13093 MPI_Isend(&noWindowCellsSend[d], 1, type_traits<MInt>::mpiType(), neighborDomain(d), domainId(), mpiComm(),
13094 &sendRequests[d], AT_, "noWindowCellsSend[d]");
13095 }
13096
13097 // Finish MPI communication
13098 MPI_Waitall(noNeighborDomains(), &recvRequests[0], MPI_STATUSES_IGNORE, AT_);
13099 MPI_Waitall(noNeighborDomains(), &sendRequests[0], MPI_STATUSES_IGNORE, AT_);
13100
13101 // Check if received number of window cells matches the local number of halo cells
13102 for(MInt d = 0; d < noNeighborDomains(); d++) {
13103 if(noWindowCellsRecv[d] != noSolverHaloCells(d, solver, true) /*noHaloCells(d)*/) {
13104 TERMM(1, "Cartesian Grid : Number of window cells from domain " + to_string(neighborDomain(d))
13105 + " does not match local number of halo cells; window: " + to_string(noWindowCellsRecv[d])
13106 + " ,halo: " + to_string(noSolverHaloCells(d, solver, true) /*noHaloCells(d)*/) + " " + a
13107 + " d=" + to_string(domainId()) + " solver=" + to_string(solver));
13108 }
13109 }
13110 }
13111
13113 // Check #2: grid global ids of window/halo cells match
13115 // Start receiving window cell global ids from each neighbor domain
13116 fill(recvRequests.begin(), recvRequests.end(), MPI_REQUEST_NULL);
13117 // const MInt totalNoWindowCellsRecv = accumulate(noWindowCellsRecv.begin(), noWindowCellsRecv.end(), 0);
13118 MInt totalNoWindowCellsRecv = 0;
13119 for(MInt d = 0; d < noNeighborDomains(); d++)
13120 totalNoWindowCellsRecv += noHaloCells(d);
13121
13122 MIntScratchSpace windowCellsRecv(max(1, totalNoWindowCellsRecv), AT_, "windowCellsRecv");
13123 fill(windowCellsRecv.begin(), windowCellsRecv.end(), -1);
13124 for(MInt d = 0, offset = 0; d < noNeighborDomains(); d++) {
13125 if(noHaloCells(d) > 0) {
13126 MPI_Irecv(&windowCellsRecv[offset], noHaloCells(d), type_traits<MInt>::mpiType(), neighborDomain(d),
13127 neighborDomain(d), mpiComm(), &recvRequests[d], AT_, "windowCellsRecv[offset]");
13128 }
13129 offset += noHaloCells(d);
13130 }
13131
13132 // Start sending window cell global ids to each neighbor domain
13133 fill(sendRequests.begin(), sendRequests.end(), MPI_REQUEST_NULL);
13134 // const MInt totalNoWindowCellsSend = accumulate(noWindowCellsSend.begin(), noWindowCellsSend.end(), 0);
13135 MInt totalNoWindowCellsSend = 0;
13136 for(MInt d = 0; d < noNeighborDomains(); d++)
13137 totalNoWindowCellsSend += noWindowCells(d);
13138 MIntScratchSpace windowCellsSend(max(1, totalNoWindowCellsSend), AT_, "windowCellsSend");
13139
13140 for(MInt d = 0, offset = 0; d < noNeighborDomains(); d++) {
13141 for(MInt c = 0; c < noWindowCells(d) /*noWindowCellsSend[d]*/; c++) {
13142 // TERMM_IF_NOT_COND(a_hasProperty(windowCell(d, c), Cell::IsWindow), a+"not a window cell");
13143 windowCellsSend[offset + c] = a_globalId(windowCell(d, c));
13144 isWindowCell(windowCell(d, c)) = true;
13145 }
13146 if(noWindowCells(d) > 0) {
13147 MPI_Isend(&windowCellsSend[offset], noWindowCells(d), type_traits<MInt>::mpiType(), neighborDomain(d),
13148 domainId(), mpiComm(), &sendRequests[d], AT_, "windowCellsSend[offset]");
13149 }
13150 offset += noWindowCells(d);
13151 }
13152
13153 // Finish MPI communication
13154 MPI_Waitall(noNeighborDomains(), &recvRequests[0], MPI_STATUSES_IGNORE, AT_);
13155 MPI_Waitall(noNeighborDomains(), &sendRequests[0], MPI_STATUSES_IGNORE, AT_);
13156
13157 // Check if received window cell global ids match the local halo cell global ids
13158 for(MInt d = 0, offset = 0; d < noNeighborDomains(); d++) {
13159 for(MInt c = 0; c < noHaloCells(d); c++) {
13160 const MInt cellId = haloCell(d, c);
13161 TERMM_IF_NOT_COND(a_isHalo(cellId), "not a halo cell");
13162 isHaloCell(haloCell(d, c)) = true;
13163 const MInt globalId = a_globalId(cellId);
13164 // If halo cell has periodic flag, its global id should be -1
13165 if(windowCellsRecv[offset + c] != globalId && !(a_hasProperty(cellId, Cell::IsPeriodic) && globalId == -1)) {
13166 TERMM(1, a + "Global id of window cell " + to_string(c) + " from domain " + to_string(neighborDomain(d))
13167 + " does not match local halo cell gobal id (" + to_string(windowCellsRecv[offset + c]) + " vs. "
13168 + to_string(a_globalId(haloCell(d, c))) + ")");
13169 }
13170 }
13171 offset += noHaloCells(d);
13172 }
13173
13174 // So if-statements below do not fail
13175 if(m_azimuthalPer) {
13176 for(MInt d = 0; d < noAzimuthalNeighborDomains(); d++) {
13177 for(MInt c = 0; c < noAzimuthalWindowCells(d); c++) {
13178 isWindowCell(azimuthalWindowCell(d, c)) = true;
13179 }
13180 for(MInt c = 0; c < noAzimuthalHaloCells(d); c++) {
13181 isHaloCell(azimuthalHaloCell(d, c)) = true;
13182 }
13183 }
13184 for(MInt c = 0; c < noAzimuthalUnmappedHaloCells(); c++) {
13185 isHaloCell(azimuthalUnmappedHaloCell(c)) = true;
13186 }
13187 }
13188
13189 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
13190 // check that only cells in m_windowCells are marked as window!
13191 if(!isWindowCell(cellId)) {
13192 TERMM_IF_NOT_COND(!a_hasProperty(cellId, Cell::IsWindow),
13193 "cell is marked as window but not in m_windowCells: " + std::to_string(cellId) + a);
13194 }
13195 // check that only cells in m_haloCells are marked as halo!
13196 if(!isHaloCell(cellId) && !a_hasProperty(cellId, Cell::IsPartLvlAncestor)) {
13197 TERMM_IF_NOT_COND(!a_isHalo(cellId),
13198 "cell is marked as halo but not in m_haloCells: " + std::to_string(cellId));
13199 }
13200 }
13201 } else {
13203 // Check #1: number of window/halo cells match
13205 // Start receiving number of window cells from each neighbor domain
13206 ScratchSpace<MPI_Request> recvRequests(std::max(1, noNeighborDomains()), AT_, "recvRequests");
13207 MIntScratchSpace noWindowCellsRecv(std::max(1, noNeighborDomains()), AT_, "noWindowCellsRecv");
13208 for(MInt d = 0; d < noNeighborDomains(); d++) {
13209 noWindowCellsRecv[d] = -1;
13210 MPI_Irecv(&noWindowCellsRecv[d], 1, type_traits<MInt>::mpiType(), neighborDomain(d), neighborDomain(d), mpiComm(),
13211 &recvRequests[d], AT_, "noWindowCellsRecv[d]");
13212 }
13213
13214 // Start sending number of window cells to each neighbor domain
13215 ScratchSpace<MPI_Request> sendRequests(std::max(1, noNeighborDomains()), AT_, "sendRequests");
13216 MIntScratchSpace noWindowCellsSend(std::max(1, noNeighborDomains()), AT_, "noWindowCellsSend");
13217 for(MInt d = 0; d < noNeighborDomains(); d++) {
13218 noWindowCellsSend[d] = noWindowCells(d);
13219 MPI_Isend(&noWindowCellsSend[d], 1, type_traits<MInt>::mpiType(), neighborDomain(d), domainId(), mpiComm(),
13220 &sendRequests[d], AT_, "noWindowCellsSend[d]");
13221 }
13222
13223 // Finish MPI communication
13224 MPI_Waitall(noNeighborDomains(), &recvRequests[0], MPI_STATUSES_IGNORE, AT_);
13225 MPI_Waitall(noNeighborDomains(), &sendRequests[0], MPI_STATUSES_IGNORE, AT_);
13226
13227 // Check if received number of window cells matches the local number of halo cells
13228 for(MInt d = 0; d < noNeighborDomains(); d++) {
13229 if(noWindowCellsRecv[d] != noHaloCells(d)) {
13230 TERMM(1, "Cartesian Grid : Number of window cells from domain " + to_string(neighborDomain(d))
13231 + " does not match local number of halo cells; window: " + to_string(noWindowCellsRecv[d])
13232 + " ,halo: " + to_string(noHaloCells(d)));
13233 }
13234 }
13235
13237 // Check #2: grid global ids of window/halo cells match
13239 // Start receiving window cell global ids from each neighbor domain
13240 fill(recvRequests.begin(), recvRequests.end(), MPI_REQUEST_NULL);
13241 const MInt totalNoWindowCellsRecv = accumulate(noWindowCellsRecv.begin(), noWindowCellsRecv.end(), 0);
13242
13243 MLongScratchSpace windowCellsRecv(max(1, totalNoWindowCellsRecv), AT_, "windowCellsRecv");
13244 fill(windowCellsRecv.begin(), windowCellsRecv.end(), -1);
13245 for(MInt d = 0, offset = 0; d < noNeighborDomains(); d++) {
13246 if(noHaloCells(d) > 0) {
13247 MPI_Irecv(&windowCellsRecv[offset], noHaloCells(d), type_traits<MLong>::mpiType(), neighborDomain(d),
13248 neighborDomain(d), mpiComm(), &recvRequests[d], AT_, "windowCellsRecv[offset]");
13249 }
13250 offset += noHaloCells(d);
13251 }
13252
13253 // Start sending window cell global ids to each neighbor domain
13254 fill(sendRequests.begin(), sendRequests.end(), MPI_REQUEST_NULL);
13255 const MInt totalNoWindowCellsSend = accumulate(noWindowCellsSend.begin(), noWindowCellsSend.end(), 0);
13256 MLongScratchSpace windowCellsSend(max(1, totalNoWindowCellsSend), AT_, "windowCellsSend");
13257
13258 ScratchSpace<MBool> isWindowCell(m_tree.size(), AT_, "isWindowCell");
13259 ScratchSpace<MBool> isHaloCell(m_tree.size(), AT_, "isHaloCell");
13260 isWindowCell.fill(false);
13261 isHaloCell.fill(false);
13262
13263 for(MInt d = 0, offset = 0; d < noNeighborDomains(); d++) {
13264 for(MInt c = 0; c < noWindowCellsSend[d]; c++) {
13265 TERMM_IF_NOT_COND(a_hasProperty(windowCell(d, c), Cell::IsWindow), "not a window cell");
13266 windowCellsSend[offset + c] = a_globalId(windowCell(d, c));
13267 isWindowCell(windowCell(d, c)) = true;
13268 }
13269 if(noWindowCells(d) > 0) {
13270 MPI_Isend(&windowCellsSend[offset], noWindowCells(d), type_traits<MLong>::mpiType(), neighborDomain(d),
13271 domainId(), mpiComm(), &sendRequests[d], AT_, "windowCellsSend[offset]");
13272 }
13273 offset += noWindowCells(d);
13274 }
13275
13276 // Finish MPI communication
13277 MPI_Waitall(noNeighborDomains(), &recvRequests[0], MPI_STATUSES_IGNORE, AT_);
13278 MPI_Waitall(noNeighborDomains(), &sendRequests[0], MPI_STATUSES_IGNORE, AT_);
13279
13280 // Check if received window cell global ids match the local halo cell global ids
13281 for(MInt d = 0, offset = 0; d < noNeighborDomains(); d++) {
13282 for(MInt c = 0; c < noHaloCells(d); c++) {
13283 const MInt cellId = haloCell(d, c);
13284 TERMM_IF_NOT_COND(a_isHalo(cellId), "not a halo cell");
13285 isHaloCell(haloCell(d, c)) = true;
13286 const MLong globalId = a_globalId(cellId);
13287 // If halo cell has periodic flag, its global id should be -1
13288 if(windowCellsRecv[offset + c] != globalId && !(a_hasProperty(cellId, Cell::IsPeriodic) && globalId == -1)) {
13289 TERMM(1, "Global id of window cell " + to_string(c) + " from domain " + to_string(neighborDomain(d))
13290 + " does not match local halo cell gobal id (" + to_string(windowCellsRecv[offset + c]) + " vs. "
13291 + to_string(a_globalId(haloCell(d, c))) + ")");
13292 }
13293 }
13294 offset += noHaloCells(d);
13295 }
13296
13297 // So if-statements below do not fail
13298 if(m_azimuthalPer) {
13299 for(MInt d = 0; d < noAzimuthalNeighborDomains(); d++) {
13300 for(MInt c = 0; c < noAzimuthalWindowCells(d); c++) {
13301 isWindowCell(azimuthalWindowCell(d, c)) = true;
13302 }
13303 for(MInt c = 0; c < noAzimuthalHaloCells(d); c++) {
13304 isHaloCell(azimuthalHaloCell(d, c)) = true;
13305 }
13306 }
13307 for(MInt c = 0; c < noAzimuthalUnmappedHaloCells(); c++) {
13308 isHaloCell(azimuthalUnmappedHaloCell(c)) = true;
13309 }
13310 }
13311
13312 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
13313 // check that only cells in m_windowCells are marked as window!
13314 if(!isWindowCell(cellId)) {
13315 TERMM_IF_NOT_COND(!a_hasProperty(cellId, Cell::IsWindow),
13316 "cell is marked as window but not in m_windowCells: " + std::to_string(cellId));
13317 }
13318 // check that only cells in m_haloCells are marked as halo!
13319 if(!isHaloCell(cellId) && !a_hasProperty(cellId, Cell::IsPartLvlAncestor)) {
13320 TERMM_IF_NOT_COND(!a_isHalo(cellId),
13321 "cell is marked as halo but not in m_haloCells: " + std::to_string(cellId));
13322 }
13323 }
13324 }
13325
13326 // Check parent/child/neighbor global ids (if != -1)
13327 // Note: not working if the global ids are not computed/updated yet (e.g. during adaptation)!
13328 // Note: assumes that internal and halo cells are separated
13329 if(fullCheck && noNeighborDomains() > 0) {
13330 ScratchSpace<MLong> parentData(m_tree.size(), AT_, "parentData");
13331 ScratchSpace<MLong> childData(m_tree.size(), m_maxNoChilds, AT_, "childData");
13332 parentData.fill(-1);
13333 childData.fill(-1);
13334
13335 for(MInt cellId = 0; cellId < m_noInternalCells; cellId++) {
13336 TERMM_IF_COND(a_isHalo(cellId), "cell is a halo" + a);
13337 const MInt parentId = a_parentId(cellId);
13338 parentData[cellId] = (parentId > -1) ? a_globalId(parentId) : -1;
13339
13340 for(MInt j = 0; j < m_maxNoChilds; j++) {
13341 const MInt childId = a_childId(cellId, j);
13342 childData(cellId, j) = (childId > -1) ? a_globalId(childId) : -1;
13343 }
13344 }
13345
13346 maia::mpi::exchangeData(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), parentData.getPointer(), 1);
13347
13348 maia::mpi::exchangeData(m_nghbrDomains, m_haloCells, m_windowCells, mpiComm(), childData.getPointer(),
13349 m_maxNoChilds);
13350
13351 for(MInt d = 0; d < noNeighborDomains(); d++) {
13352 for(MInt c = 0; c < noHaloCells(d); c++) {
13353 const MInt cellId = haloCell(d, c);
13354 TERMM_IF_NOT_COND(a_isHalo(cellId), "not a halo cell");
13355 const MInt parentId = a_parentId(cellId);
13356 if(parentId > -1) {
13357 TERMM_IF_NOT_COND(a_globalId(parentId) == parentData[cellId],
13358 "Halo cell parent global id mismatch: cell" + std::to_string(cellId) + " parent"
13359 + std::to_string(parentId) + "; " + std::to_string(a_globalId(parentId))
13360 + " != " + std::to_string(parentData[cellId]));
13361 }
13362
13363 for(MInt j = 0; j < m_maxNoChilds; j++) {
13364 const MInt childId = a_childId(cellId, j);
13365 if(childId > -1) {
13366 TERMM_IF_NOT_COND(a_globalId(childId) == childData(cellId, j),
13367 "Halo cell child global id mismatch: cell" + std::to_string(cellId) + " child"
13368 + std::to_string(childId) + "; " + std::to_string(a_globalId(childId))
13369 + " != " + std::to_string(childData(cellId, j)));
13370 }
13371 }
13372 }
13373 }
13374 }
13375
13376 m_log << "done" << std::endl;
13377
13378 if(m_azimuthalPer) {
13379 checkAzimuthalWindowHaloConsistency();
13380 }
13381}
13382
13383
13385template <MInt nDim>
13387 ofstream logfile;
13388 logfile.open(name + "_" + std::to_string(domainId()));
13389
13390 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
13391 logfile << cellId << " g" << a_globalId(cellId) << " l" << a_level(cellId) << " p" << a_parentId(cellId);
13392 for(MUint j = 0; j < (unsigned)m_maxNoChilds; j++) {
13393 logfile << " c" << a_childId(cellId, j);
13394 }
13395 for(MInt dirId = 0; dirId < m_noDirs; dirId++) {
13396 logfile << " n" << a_neighborId(cellId, dirId);
13397 }
13398 logfile << " " << a_propertiesToString(cellId);
13399 logfile << " w" << a_weight(cellId);
13400 logfile << " o" << a_noOffsprings(cellId) << std::endl;
13401 }
13402 logfile << std::endl;
13403
13404 for(MUint i = 0; i < m_minLevelCells.size(); i++) {
13405 logfile << "m" << i << " " << m_minLevelCells[i] << std::endl;
13406 }
13407 logfile << std::endl;
13408
13409 for(MInt i = 0; i < m_noPartitionCells; i++) {
13410 logfile << "p" << i << " g" << m_localPartitionCellGlobalIds[i] << " l" << m_localPartitionCellLocalIds[i]
13411 << " level" << a_level(m_localPartitionCellLocalIds[i]) << std::endl;
13412 }
13413 logfile << std::endl;
13414
13415 MInt count = 0;
13416 for(auto& id : m_globalToLocalId) {
13417 logfile << "g2l" << count++ << " g" << id.first << " l" << id.second << std::endl;
13418 }
13419 logfile << std::endl;
13420
13421 for(MInt i = 0; i < noNeighborDomains(); i++) {
13422 logfile << "window " << i << " " << m_nghbrDomains[i] << " " << m_windowCells[i].size() << std::endl;
13423 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
13424 logfile << "window " << m_nghbrDomains[i] << " " << j << " w" << m_windowCells[i][j] << " g"
13425 << a_globalId(m_windowCells[i][j]) << std::endl;
13426 }
13427 logfile << std::endl;
13428
13429 logfile << "halo " << i << " " << m_nghbrDomains[i] << " " << m_haloCells[i].size() << std::endl;
13430 for(MInt j = 0; j < (signed)m_haloCells[i].size(); j++) {
13431 logfile << "halo " << m_nghbrDomains[i] << " " << j << " h" << m_haloCells[i][j]
13432
13433 << " g" << a_globalId(m_haloCells[i][j]) << std::endl;
13434 }
13435 logfile << std::endl;
13436 }
13437}
13438
13439
13446template <MInt nDim>
13447MBool CartesianGrid<nDim>::updatePartitionCells(MInt offspringThreshold, MFloat workloadThreshold) {
13448 TRACE();
13449 m_log << "Update partition cells: offspringThreshold=" << offspringThreshold
13450 << "; workloadThreshold=" << workloadThreshold << std::endl;
13451
13452 MBool partitionCellChange = false;
13453
13454 // update noOffspring and workloads
13455 calculateNoOffspringsAndWorkload(static_cast<Collector<void>*>(nullptr), m_tree.size());
13456
13457 // Determine new partition cells (similar to gridio::saveGrid())
13458 // 1. Start with collecting min-level cells
13459 std::vector<MInt> tmpMinLevelCells;
13460 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
13461 if(a_level(cellId) == m_minLevel && !a_hasProperty(cellId, Cell::IsPeriodic)) {
13462 ASSERT(a_parentId(cellId) == -1, "");
13463 tmpMinLevelCells.push_back(cellId);
13464 }
13465 }
13466
13467 std::vector<MInt> partitionCellsFiltered;
13468 while(true) { // Repeat partition cell filtering until enough partition cells are present
13469 partitionCellsFiltered.clear();
13470 std::vector<MInt> tmpPartitionCells(tmpMinLevelCells);
13471
13472 // Iterate over all cells to consider
13473 while(!tmpPartitionCells.empty()) {
13474 const MInt cellId = tmpPartitionCells.front();
13475 tmpPartitionCells.erase(tmpPartitionCells.begin());
13476
13477 // Multisolver: make sure that children of solver leaf-cells cannot become partition cells!
13478 MBool isSolverLeafCell = false;
13479 for(MInt b = 0; b < treeb().noSolvers(); b++) {
13480 if(a_isLeafCell(cellId, b)) {
13481 isSolverLeafCell = true;
13482 break;
13483 }
13484 }
13485
13486 // Check if cell has too many offspring or too high workload (and is not a leaf cell)
13487 if((a_noOffsprings(cellId) > offspringThreshold || a_workload(cellId) > workloadThreshold) && !isSolverLeafCell) {
13488 MInt cnt = 0;
13489 // Add child cells to list of cells to consider as partition cells
13490 for(MInt j = 0; j < IPOW2(nDim); ++j) {
13491 if(a_childId(cellId, j) > -1) {
13492 tmpPartitionCells.insert(tmpPartitionCells.begin() + cnt, a_childId(cellId, j));
13493 cnt++;
13494 }
13495 }
13496 } else if(!a_isHalo(cellId)) {
13497 // Add as partition cell
13498 partitionCellsFiltered.push_back(cellId);
13499 }
13500 }
13501
13502 MLong globalNoNewPartitionCells = partitionCellsFiltered.size();
13503 MPI_Allreduce(MPI_IN_PLACE, &globalNoNewPartitionCells, 1, type_traits<MLong>::mpiType(), MPI_SUM, mpiComm(), AT_,
13504 "MPI_IN_PLACE", "globalNoNewPartitionCells");
13505 cerr0 << "Found " << globalNoNewPartitionCells << " new partition cells." << std::endl;
13506 m_log << "Found " << globalNoNewPartitionCells << " new partition cells." << std::endl;
13507
13508 // Make sure that there are enough partition cells (at least one per domain)
13509 // TODO labels:GRID add a minimum allowed number of partition cells on average per domain
13510 if(globalNoNewPartitionCells < noDomains()) {
13511 offspringThreshold /= 2;
13512 workloadThreshold *= 0.5;
13513 cerr0 << "Error: too few partition cells; Repeating filtering of partition cells with "
13514 "lowered thresholds..."
13515 << std::endl;
13516 } else {
13517 break;
13518 }
13519 }
13520
13521 const MInt oldNoPartCells = m_noPartitionCells;
13522 const MInt newNoPartCells = partitionCellsFiltered.size();
13523
13524 sort(partitionCellsFiltered.begin(), partitionCellsFiltered.end());
13525
13526 if(oldNoPartCells != newNoPartCells) {
13527 // Number of partition cells changed
13528 partitionCellChange = true;
13529 } else {
13530 // Number of partition cells did not change, check the partition cells for any change
13531 for(MInt i = 0; i < newNoPartCells; i++) {
13532 const MInt newPartCellId = partitionCellsFiltered[i];
13533 if(!a_hasProperty(newPartCellId, Cell::IsPartitionCell)) {
13534 partitionCellChange = true;
13535 break;
13536 }
13537 }
13538 }
13539
13540 MInt globalPartitionCellChange = partitionCellChange;
13541 MPI_Allreduce(MPI_IN_PLACE, &globalPartitionCellChange, 1, type_traits<MInt>::mpiType(), MPI_MAX, mpiComm(), AT_,
13542 "MPI_IN_PLACE", "globalPartitionCellChange");
13543
13544 // If there is no change of any partition cell globally, nothing to do
13545 if(!globalPartitionCellChange) {
13546 cerr0 << "Partition cells did not change." << std::endl;
13547 m_log << "Partition cells did not change." << std::endl;
13548 return false;
13549 }
13550
13551 // Set 'updating partition cells'-flag (allow case with 0 partition cells in eg. computeGlobalIds)
13552 m_updatingPartitionCells = true;
13553
13554 // Store if the current first min-level cell is a halo partition level ancestor (i.e. is the
13555 // ancestor of the first local partition cell)
13556 const MInt firstMinLvlCell = m_minLevelCells[0];
13557 MInt minLvlHaloPartLvlAncestor = -1;
13558 if(a_hasProperty(firstMinLvlCell, Cell::IsPartLvlAncestor) && a_hasProperty(firstMinLvlCell, Cell::IsHalo)) {
13559 minLvlHaloPartLvlAncestor = firstMinLvlCell;
13560 }
13561
13562 // Reset cell flags
13563 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
13564 a_hasProperty(cellId, Cell::IsPartitionCell) = false;
13565 a_hasProperty(cellId, Cell::IsPartLvlAncestor) = false;
13566 }
13567
13568 // Set new partition cell flags
13569 MInt maxLevelDiff = 0;
13570 for(MInt i = 0; i < newNoPartCells; i++) {
13571 const MInt newPartCellId = partitionCellsFiltered[i];
13572 a_hasProperty(newPartCellId, Cell::IsPartitionCell) = true;
13573
13574 maxLevelDiff = std::max(maxLevelDiff, a_level(newPartCellId) - m_minLevel);
13575 }
13576
13577 m_noPartitionCells = newNoPartCells;
13578
13579 m_noPartitionCellsGlobal = m_noPartitionCells;
13580 MPI_Allreduce(MPI_IN_PLACE, &m_noPartitionCellsGlobal, 1, type_traits<MLong>::mpiType(), MPI_SUM, mpiComm(), AT_,
13581 "MPI_IN_PLACE", "m_noPartitionCellsGlobal");
13582
13583 MPI_Allreduce(MPI_IN_PLACE, &maxLevelDiff, 1, type_traits<MInt>::mpiType(), MPI_MAX, mpiComm(), AT_, "MPI_IN_PLACE",
13584 "maxLevelDiff");
13585 cerr0 << "New maximum partition level shift: " << maxLevelDiff << std::endl;
13586 m_log << "New maximum partition level shift: " << maxLevelDiff << std::endl;
13587 m_maxPartitionLevelShift = maxLevelDiff;
13588
13589 // Reallocate partition cell arrays with new size
13590 // Note: data is set in computeGlobalIds, should not be required before
13591 mDeallocate(m_localPartitionCellGlobalIds);
13592 mAlloc(m_localPartitionCellGlobalIds, std::max(m_noPartitionCells, 1), "m_localPartitionCellGlobalIds",
13593 static_cast<MLong>(-1), AT_);
13594 mDeallocate(m_localPartitionCellLocalIds);
13595 mAlloc(m_localPartitionCellLocalIds, std::max(m_noPartitionCells, 1), "m_localPartitionCellLocalIds", -1, AT_);
13596
13597 MIntScratchSpace isPartitionLevelAncestor(m_noInternalCells, AT_, "isPartitionLevelAncestor");
13598 isPartitionLevelAncestor.fill(0);
13599
13600 MInt noPartitionLevelAncestorsLocal = 0;
13601 // Determine partition level ancestor cells and set the corresponding cell property
13602 for(MInt i = 0; i < newNoPartCells; i++) {
13603 const MInt cellId = partitionCellsFiltered[i];
13604 MInt parentId = a_parentId(cellId);
13605
13606 // Loop up the parent cells of all partition cells
13607 while(parentId > -1) {
13608 a_hasProperty(parentId, Cell::IsPartLvlAncestor) = true;
13609
13610 if(!a_isHalo(parentId)) {
13611 noPartitionLevelAncestorsLocal++;
13612 }
13613
13614 for(MInt b = 0; b < treeb().noSolvers(); b++) {
13615 TERMM_IF_COND(a_isLeafCell(parentId, b), "Error: partition level ancestor is the leaf cell of a solver.");
13616 }
13617
13618 parentId = a_parentId(parentId);
13619 }
13620 }
13621
13622 m_noPartitionLevelAncestors = noPartitionLevelAncestorsLocal;
13623 // Determine global number of partition level ancestor cells
13624 m_noPartitionLevelAncestorsGlobal = m_noPartitionLevelAncestors;
13625 MPI_Allreduce(MPI_IN_PLACE, &m_noPartitionLevelAncestorsGlobal, 1, type_traits<MLong>::mpiType(), MPI_SUM, mpiComm(),
13626 AT_, "MPI_IN_PLACE", "noPartitionLevelAncestorsGlobal");
13627
13628 std::vector<MInt>().swap(m_partitionLevelAncestorIds);
13629 std::vector<MLong>().swap(m_partitionLevelAncestorChildIds);
13630
13631 // Note: only the min-level halo partition level ancestor needs to be stored since it is required
13632 // for computeGlobalIds()
13633 if(minLvlHaloPartLvlAncestor > -1) {
13634 TERMM_IF_NOT_COND(a_isHalo(minLvlHaloPartLvlAncestor), "Error: not a halo cell.");
13635 a_hasProperty(minLvlHaloPartLvlAncestor, Cell::IsPartLvlAncestor) = true;
13636 m_partitionLevelAncestorIds.push_back(minLvlHaloPartLvlAncestor);
13637 }
13638
13639 // Update partition cell local/global ids and partition cell offsets
13640 updatePartitionCellInformation();
13641
13642 // exchange properties
13643 exchangeProperties();
13644
13645 m_updatingPartitionCells = false;
13646 // Set status, checked when writing a grid restart file to avoid a refiltering of partition cells
13647 m_updatedPartitionCells = true;
13648
13649 return true;
13650}
13651
13652
13654template <MInt nDim>
13656 std::vector<std::vector<MInt>>& partLvlAncestorHaloCells,
13657 std::vector<std::vector<MInt>>& partLvlAncestorWindowCells) {
13658 TRACE();
13659
13660 partLvlAncestorHaloCells.clear();
13661 partLvlAncestorWindowCells.clear();
13662
13663 partLvlAncestorHaloCells.resize(noNeighborDomains());
13664 partLvlAncestorWindowCells.resize(noNeighborDomains());
13665
13666 for(MInt i = 0; i < noNeighborDomains(); i++) {
13667 for(MInt j = 0; j < (signed)m_haloCells[i].size(); ++j) {
13668 if(a_hasProperty(m_haloCells[i][j], Cell::IsPartLvlAncestor)) {
13669 partLvlAncestorHaloCells[i].push_back(m_haloCells[i][j]);
13670 }
13671 }
13672 for(MInt j = 0; j < (signed)m_windowCells[i].size(); ++j) {
13673 if(a_hasProperty(m_windowCells[i][j], Cell::IsPartLvlAncestor)) {
13674 partLvlAncestorWindowCells[i].push_back(m_windowCells[i][j]);
13675 }
13676 }
13677 }
13678}
13679
13680// Sets the solver flag for cells which have been newly created in the grid
13681template <MInt nDim>
13683 const MInt cellId, const MInt solver,
13684 const std::function<MInt(const MFloat*, const MInt, const MInt)>& cellOutsideSolver) {
13685 TRACE();
13686
13687 static constexpr MFloat signStencil[8][3] = {{-F1, -F1, -F1}, {F1, -F1, -F1}, {-F1, F1, -F1}, {F1, F1, -F1},
13688 {-F1, -F1, F1}, {F1, -F1, F1}, {-F1, F1, F1}, {F1, F1, F1}};
13689
13690 const MInt childLevel = a_level(cellId) + 1;
13691 const MFloat childCellLength = cellLengthAtLevel(childLevel);
13692
13693 for(MInt c = 0; c < m_maxNoChilds; c++) {
13694 MInt childId = a_childId(cellId, c);
13695
13696 if(childId < 0) continue;
13697 if(treeb().solver(childId, solver)) continue;
13698
13699 MFloat coords[nDim];
13700 for(MInt i = 0; i < nDim; i++) {
13701 coords[i] = a_coordinate(cellId, i) + F1B2 * signStencil[c][i] * childCellLength;
13702 }
13703 MInt isOutside = cellOutsideSolver(coords, childLevel, cellId);
13704
13705 if(isOutside < 1) {
13706 treeb().solver(childId, solver) = true;
13707 }
13708 }
13709}
13710
13716template <MInt nDim>
13718 TRACE();
13719
13720 MFloat radius = F0;
13721
13722 for(MInt d = 0; d < nDim; d++) {
13723 if(m_periodicCartesianDir[d] == 0) {
13724 coordsCyl[nDim - 1] = coords[d];
13725 } else {
13726 radius += pow((coords[d] - m_azimuthalPerCenter[d]), 2);
13727 }
13728 }
13729 radius = sqrt(radius);
13730
13731 MFloat fac = F0;
13732 if(coords[m_azimuthalPeriodicDir[0]] >= F0) {
13733 fac = 1.0;
13734 } else {
13735 fac = -1.0;
13736 }
13737 MFloat phi = fac * acos(coords[m_azimuthalPeriodicDir[1]] / radius);
13738
13739 coordsCyl[0] = radius;
13740 coordsCyl[1] = phi;
13741}
13742
13747template <MInt nDim>
13749 TRACE();
13750
13751 MFloat tmpCoords[nDim];
13752
13753 for(MInt d = 0; d < nDim; d++) {
13754 tmpCoords[d] = coords[d] - m_azimuthalPerCenter[d];
13755 }
13756
13757 coords[m_azimuthalPeriodicDir[0]] =
13758 tmpCoords[m_azimuthalPeriodicDir[0]] * cos(angle) - tmpCoords[m_azimuthalPeriodicDir[1]] * sin(angle);
13759 coords[m_azimuthalPeriodicDir[1]] =
13760 tmpCoords[m_azimuthalPeriodicDir[0]] * sin(angle) + tmpCoords[m_azimuthalPeriodicDir[1]] * cos(angle);
13761
13762 for(MInt d = 0; d < nDim; d++) {
13763 coords[d] += m_azimuthalPerCenter[d];
13764 }
13765}
13766
13767
13772template <MInt nDim>
13774 vector<MLong>& recvChildIds,
13775 MInt level) {
13776 TRACE();
13777
13778 if(noAzimuthalNeighborDomains() == 0) return;
13779
13780 static constexpr MFloat cornerStencil[8][3] = {{-F1, -F1, -F1}, {F1, -F1, -F1}, {-F1, F1, -F1}, {F1, F1, -F1},
13781 {-F1, -F1, F1}, {F1, -F1, F1}, {-F1, F1, F1}, {F1, F1, F1}};
13782 MIntScratchSpace nghbrList(200, AT_, "nghbrList");
13783 MFloat childCoords[3] = {F0, F0, F0};
13784 MFloat coordsCyl[3] = {F0, F0, F0};
13785 MFloat dxCyl = m_azimuthalAngle;
13786
13787 MInt noCells = treeb().size();
13788 MBoolScratchSpace gridBndryCells(noCells, AT_, "gridBndryCells");
13789 gridBndryCells.fill(false);
13790 for(auto it = m_gridBndryCells.begin(); it != m_gridBndryCells.end(); it++) {
13791 MInt cellId = *it;
13792 gridBndryCells[cellId] = true;
13793 }
13794
13795 MInt windowCnt = 0;
13796 MInt haloCnt = 0;
13797 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
13798 windowCnt += m_azimuthalWindowCells[i].size();
13799 haloCnt += m_azimuthalHaloCells[i].size();
13800 }
13801
13802 refineChildIds.resize(windowCnt * m_maxNoChilds);
13803 recvChildIds.resize(haloCnt * m_maxNoChilds);
13804
13805 MIntScratchSpace refinedWindows(windowCnt, AT_, "refinedWindows");
13806 refinedWindows.fill(0);
13807 MIntScratchSpace refineCandidates(haloCnt, AT_, "refineCandidates");
13808 refineCandidates.fill(0);
13809
13810 // Check which azimuthal window allows possible halo refinement
13811 // Only halo cells for which the corresponding internal cell is refined
13812 // are allowed to be refined
13813 MInt cnt = 0;
13814 MIntScratchSpace windowRefineCnt(noAzimuthalNeighborDomains(), AT_, "windowRefineCnt");
13815 windowRefineCnt.fill(0);
13816 MInt windowRefineCntTotal = 0;
13817 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
13818 for(MInt j = 0; j < noAzimuthalWindowCells(i); j++) {
13819 MInt cellId = m_azimuthalWindowCells[i][j];
13820
13821 MBool refine = false;
13822 if(a_level(cellId) == level) {
13823 if(a_noChildren(cellId) > 0) {
13824 refine = true;
13825 }
13826 }
13827
13828 if(refine) {
13829 refinedWindows[cnt] = 1;
13830 windowRefineCnt[i]++;
13831 windowRefineCntTotal++;
13832 }
13833 cnt++;
13834 }
13835 }
13836
13837 MIntScratchSpace windowMap(windowRefineCntTotal, AT_, "windowMap");
13838 cnt = 0;
13839 windowRefineCntTotal = 0;
13840 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
13841 for(MInt j = 0; j < noAzimuthalWindowCells(i); j++) {
13842 if(refinedWindows[cnt] > 0) {
13843 MInt cellId = m_azimuthalWindowCells[i][j];
13844 windowMap[windowRefineCntTotal] = cellId;
13845 windowRefineCntTotal++;
13846 }
13847 cnt++;
13848 }
13849 }
13850
13851 maia::mpi::exchangeBuffer(m_azimuthalNghbrDomains, m_azimuthalHaloCells, m_azimuthalWindowCells, mpiComm(),
13852 refinedWindows.getPointer(), refineCandidates.getPointer());
13853
13854 // Compute hilbertIds of refineCandidates childs
13855 cnt = 0;
13856 MInt haloRefineCntTotal = 0;
13857 MIntScratchSpace haloRefineCnt(noAzimuthalNeighborDomains(), AT_, "haloRefineCnt");
13858 haloRefineCnt.fill(0);
13859 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
13860 for(MInt j = 0; j < noAzimuthalHaloCells(i); j++) {
13861 if(refineCandidates[cnt] > 0) {
13862 haloRefineCnt[i]++;
13863 haloRefineCntTotal++;
13864 }
13865 cnt++;
13866 }
13867 }
13868
13869 MIntScratchSpace haloMap(haloRefineCntTotal, AT_, "haloMap");
13870 MIntScratchSpace refineHalo(noCells, AT_, "refineHalo");
13871 cnt = 0;
13872 haloRefineCntTotal = 0;
13873 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
13874 for(MInt j = 0; j < noAzimuthalHaloCells(i); j++) {
13875 MInt cellId = m_azimuthalHaloCells[i][j];
13876 refineHalo[cellId] = refineCandidates[cnt];
13877 if(refineCandidates[cnt] > 0) {
13878 haloMap[haloRefineCntTotal] = cellId;
13879 haloRefineCntTotal++;
13880 }
13881 cnt++;
13882 }
13883 }
13884
13885 MIntScratchSpace layerId(noCells, AT_, "layerId");
13886 layerId.fill(-1);
13887 for(MInt cellId = 0; cellId < noCells; cellId++) {
13888 if(a_isHalo(cellId)) {
13889 continue;
13890 }
13891 if(a_level(cellId) != level) {
13892 continue;
13893 }
13894 for(MInt d = 0; d < m_noDirs; d++) {
13895 MInt nghbrId = a_neighborId(cellId, d);
13896 if(nghbrId <= -1) {
13897 continue;
13898 }
13899 if(!a_isHalo(nghbrId)) {
13900 continue;
13901 }
13902 layerId[nghbrId] = 1;
13903
13904 MInt addLayer = true;
13905 if(!a_hasProperty(nghbrId, Cell::IsPeriodic)) {
13906 if(a_noChildren(nghbrId) != 0) {
13907 addLayer = false;
13908 }
13909 } else {
13910 if(refineHalo[nghbrId] > 0) {
13911 addLayer = false;
13912 }
13913 }
13914
13915 for(MInt d2 = 0; d2 < m_noDirs; d2++) {
13916 if(!addLayer && d == d2) {
13917 continue;
13918 }
13919 MInt nghbrId2 = a_neighborId(nghbrId, d2);
13920 if(nghbrId2 <= -1) {
13921 continue;
13922 }
13923 if(a_isHalo(nghbrId2) && layerId[nghbrId2] <= -1) {
13924 layerId[nghbrId2] = 2;
13925 }
13926 for(MInt d3 = 0; d3 < m_noDirs; d3++) {
13927 if(d3 == d || d3 == d2) {
13928 continue;
13929 }
13930 MInt nghbrId3 = a_neighborId(nghbrId2, d3);
13931 if(nghbrId3 <= -1) {
13932 continue;
13933 }
13934 if(a_isHalo(nghbrId3) && layerId[nghbrId3] <= -1) {
13935 layerId[nghbrId3] = 3;
13936 }
13937 }
13938 }
13939 }
13940 }
13941
13942 MLongScratchSpace haloChildIds(2 * haloRefineCntTotal * m_maxNoChilds, AT_, "haloChildIds");
13943 cnt = 0;
13944 ASSERT(m_noHaloLayers == 2, ""); // If m_noHaloLayers != 2 everything will break!
13945 for(MInt i = 0; i < haloRefineCntTotal; i++) {
13946 MInt cellId = haloMap[i];
13947
13948 MBool refine = false;
13949
13950 // Only refine halo cells which has a refined internal cell in its vicinity.
13951 if(a_level(cellId) == level) {
13952 if(layerId[cellId] > 0) {
13953 refine = true;
13954 }
13955 }
13956
13957 if(!refine) {
13958 fill(&haloChildIds[i * 2 * m_maxNoChilds], &haloChildIds[i * 2 * m_maxNoChilds] + (2 * m_maxNoChilds), -2);
13959 continue;
13960 }
13961
13962 MInt parentId = cellId;
13963 for(MInt lvl = level; lvl > m_minLevel; lvl--) {
13964 parentId = a_parentId(parentId);
13965 }
13966
13967 cartesianToCylindric(&a_coordinate(parentId, 0), coordsCyl);
13968 MInt side = m_azimuthalBbox.azimuthalSide(coordsCyl[1]);
13969
13970 // If cell is supposed to be refined. Calculate hilbertIds of the rotated coordinate of possible childs
13971 MFloat hLength = F1B2 * cellLengthAtLevel(a_level(cellId));
13972 for(MInt child = 0; child < m_maxNoChilds; child++) {
13973 for(MInt d = 0; d < nDim; d++) {
13974 childCoords[d] = a_coordinate(cellId, d) + cornerStencil[child][d] * F1B2 * hLength;
13975 }
13976 rotateCartesianCoordinates(childCoords, side * dxCyl);
13977
13978 MLong hilbertId =
13979 hilbertIndexGeneric(&childCoords[0], m_targetGridCenterOfGravity, m_targetGridLengthLevel0, level + 1);
13980 haloChildIds[i * 2 * m_maxNoChilds + 2 * child] = hilbertId;
13981 hilbertId = hilbertIndexGeneric(&childCoords[0], m_targetGridCenterOfGravity, m_targetGridLengthLevel0, level);
13982 haloChildIds[i * 2 * m_maxNoChilds + 2 * child + 1] = hilbertId;
13983 }
13984 }
13985
13986 ScratchSpace<MPI_Request> mpi_send_req(noAzimuthalNeighborDomains(), AT_, "mpi_send_req");
13987 ScratchSpace<MPI_Request> mpi_recv_req(noAzimuthalNeighborDomains(), AT_, "mpi_recv_req");
13988 mpi_send_req.fill(MPI_REQUEST_NULL);
13989 mpi_recv_req.fill(MPI_REQUEST_NULL);
13990
13991 MLongScratchSpace windowChildIds(2 * windowRefineCntTotal * m_maxNoChilds, AT_, "windowChildIds");
13992 cnt = 0;
13993 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
13994 if(windowRefineCnt[i] > 0) {
13995 MInt bufSize = 2 * m_maxNoChilds * windowRefineCnt[i];
13996 MPI_Irecv(&windowChildIds[cnt], bufSize, MPI_LONG, m_azimuthalNghbrDomains[i], 2, mpiComm(), &mpi_recv_req[i],
13997 AT_, "windowChildIds[cnt]");
13998 cnt += bufSize;
13999 }
14000 }
14001
14002 cnt = 0;
14003 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
14004 if(haloRefineCnt[i] > 0) {
14005 MInt bufSize = 2 * m_maxNoChilds * haloRefineCnt[i];
14006 MPI_Isend(&haloChildIds[cnt], bufSize, MPI_LONG, m_azimuthalNghbrDomains[i], 2, mpiComm(), &mpi_send_req[i], AT_,
14007 "haloChildIds[cnt]");
14008 cnt += bufSize;
14009 }
14010 }
14011
14012 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
14013 if(windowRefineCnt[i] > 0) MPI_Wait(&mpi_recv_req[i], MPI_STATUSES_IGNORE, AT_);
14014 if(haloRefineCnt[i] > 0) MPI_Wait(&mpi_send_req[i], MPI_STATUSES_IGNORE, AT_);
14015 }
14016
14017 // Now an internal cell is searched with the same hilbertId as the rotate halo child coordinate
14018 for(MInt i = 0; i < windowRefineCntTotal; i++) {
14019 MInt cellId = windowMap[i];
14020
14021 MBool gridBndry = gridBndryCells[cellId];
14022 MBool noRefine = false;
14023
14024 for(MInt child = 0; child < m_maxNoChilds; child++) {
14025 MLong haloHilbertId_L1 = windowChildIds[i * 2 * m_maxNoChilds + 2 * child];
14026 MLong haloHilbertId_L0 = windowChildIds[i * 2 * m_maxNoChilds + 2 * child + 1];
14027 windowChildIds[i * 2 * m_maxNoChilds + 2 * child] = -2;
14028 windowChildIds[i * 2 * m_maxNoChilds + 2 * child + 1] = -2;
14029
14030 if(haloHilbertId_L1 < -1) {
14031 windowChildIds[i * m_maxNoChilds + child] = -2;
14032 continue;
14033 }
14034
14035 MBool found = false;
14036 // First check in the childs of the parent window cell
14037 for(MInt child2 = 0; child2 < m_maxNoChilds; child2++) {
14038 MInt childId = a_childId(cellId, child2);
14039 if(childId < 0) continue;
14040
14041 MLong hilbertId = hilbertIndexGeneric(&a_coordinate(childId, 0), m_targetGridCenterOfGravity,
14042 m_targetGridLengthLevel0, level + 1);
14043
14044 if(hilbertId == haloHilbertId_L1) {
14045 windowChildIds[i * m_maxNoChilds + child] = a_globalId(childId);
14046
14047 found = true;
14048 break;
14049 }
14050 }
14051 if(found) continue;
14052
14053 // Now the neighboring cells of the internal cell are searched
14054 // They can either be on the child level or the current level
14055 // An azimuthal periodic connection between to cells, which are not on the same grid level
14056 // is possible!
14057 const MInt counter = getAdjacentGridCells(cellId, nghbrList, level);
14058 for(MInt n = 0; n < counter; n++) {
14059 MInt nghbrId = nghbrList[n];
14060 if(nghbrId < 0) continue;
14061 if(a_level(nghbrId) == level + 1) {
14062 MLong hilbertId = hilbertIndexGeneric(&a_coordinate(nghbrId, 0), m_targetGridCenterOfGravity,
14063 m_targetGridLengthLevel0, level + 1);
14064 if(hilbertId == haloHilbertId_L1) {
14065 windowChildIds[i * m_maxNoChilds + child] = a_globalId(nghbrId);
14066
14067 found = true;
14068 break;
14069 }
14070 } else if(a_level(nghbrId) == level) {
14071 MLong hilbertId = hilbertIndexGeneric(&a_coordinate(nghbrId, 0), m_targetGridCenterOfGravity,
14072 m_targetGridLengthLevel0, level);
14073
14074 if(hilbertId == haloHilbertId_L0) {
14075 windowChildIds[i * m_maxNoChilds + child] = a_globalId(nghbrId);
14076 found = true;
14077 break;
14078 }
14079 }
14080 }
14081 // If no adequate internal cellis found for on of the possible halo childs.
14082 // the halo cell will not be refined at all, alas it is a gridBndryCell, meaing
14083 // the rotated coordinate of the possible halo cell child could lie outside the
14084 // cartesian grid. In this case this cells becomes an unmapped halo cell and requires
14085 // special treatment
14086 if(!found) {
14087 if(gridBndry) {
14088 windowChildIds[i * m_maxNoChilds + child] = -1;
14089 } else {
14090 noRefine = true;
14091 break;
14092 }
14093 }
14094 }
14095 if(noRefine) {
14096 for(MInt child = 0; child < m_maxNoChilds; child++) {
14097 windowChildIds[i * m_maxNoChilds + child] = -2;
14098 }
14099 }
14100 }
14101
14102 cnt = 0;
14103 windowRefineCntTotal = 0;
14104 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
14105 for(MInt j = 0; j < noAzimuthalWindowCells(i); j++) {
14106 if(refinedWindows[cnt] > 0) {
14107 for(MInt child = 0; child < m_maxNoChilds; child++) {
14108 refineChildIds[cnt * m_maxNoChilds + child] = windowChildIds[windowRefineCntTotal * m_maxNoChilds + child];
14109 }
14110 windowRefineCntTotal++;
14111 } else {
14112 auto it = refineChildIds.begin() + (cnt * m_maxNoChilds);
14113 fill(it, it + m_maxNoChilds, -1);
14114 }
14115 cnt++;
14116 }
14117 }
14118
14119 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
14120 mpi_send_req[i] = MPI_REQUEST_NULL;
14121 mpi_recv_req[i] = MPI_REQUEST_NULL;
14122 }
14123
14124
14125 cnt = 0;
14126 haloChildIds.fill(-2);
14127 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
14128 if(haloRefineCnt[i] > 0) {
14129 MInt bufSize = m_maxNoChilds * haloRefineCnt[i];
14130 MPI_Irecv(&haloChildIds[cnt], bufSize, MPI_DOUBLE, m_azimuthalNghbrDomains[i], 2, mpiComm(), &mpi_recv_req[i],
14131 AT_, "haloChildIds[cnt]");
14132 cnt += bufSize;
14133 }
14134 }
14135
14136 cnt = 0;
14137 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
14138 if(windowRefineCnt[i] > 0) {
14139 MInt bufSize = m_maxNoChilds * windowRefineCnt[i];
14140 MPI_Isend(&windowChildIds[cnt], bufSize, MPI_DOUBLE, m_azimuthalNghbrDomains[i], 2, mpiComm(), &mpi_send_req[i],
14141 AT_, "windowChildIds[cnt]");
14142 cnt += bufSize;
14143 }
14144 }
14145
14146 MPI_Waitall(noAzimuthalNeighborDomains(), &mpi_recv_req[0], MPI_STATUSES_IGNORE, AT_);
14147 MPI_Waitall(noAzimuthalNeighborDomains(), &mpi_send_req[0], MPI_STATUSES_IGNORE, AT_);
14148
14149 // GlobalIds of internal cells are send to halo rank.
14150 cnt = 0;
14151 haloRefineCntTotal = 0;
14152 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
14153 for(MInt j = 0; j < noAzimuthalHaloCells(i); j++) {
14154 if(refineCandidates[cnt] > 0) {
14155 for(MInt child = 0; child < m_maxNoChilds; child++) {
14156 recvChildIds[cnt * m_maxNoChilds + child] = haloChildIds[haloRefineCntTotal * m_maxNoChilds + child];
14157 }
14158 haloRefineCntTotal++;
14159 } else {
14160 auto it = recvChildIds.begin() + (cnt * m_maxNoChilds);
14161 fill(it, it + m_maxNoChilds, -2);
14162 }
14163 cnt++;
14164 }
14165 }
14166}
14167
14168
14173template <MInt nDim>
14174MInt CartesianGrid<nDim>::createAzimuthalHaloCell(const MInt cellId, const MInt dir, const MLong* hilbertOffsets,
14175 unordered_multimap<MLong, MInt>& hilbertToLocal, const MFloat* bbox,
14176 MInt* const noHalos, vector<vector<MInt>>& halos,
14177 vector<vector<MLong>>& hilbertIds) {
14178 static constexpr MInt revDir[6] = {1, 0, 3, 2, 5, 4};
14179 const MFloat cellLength = cellLengthAtCell(cellId);
14180
14181 MFloat coords[3];
14182 MFloat coordsCyl[3];
14183 MFloat dcoords[3];
14184 MFloat dummyCoords[3];
14185 MFloat dxCyl = F0;
14186 for(MInt i = 0; i < nDim; i++) {
14187 coords[i] = a_coordinate(cellId, i);
14188 }
14189 coords[dir / 2] += ((dir % 2) == 0 ? -F1 : F1) * cellLength;
14190
14191 if(!m_periodicCartesianDir[dir / 2] && (coords[dir / 2] < bbox[dir / 2] || coords[dir / 2] > bbox[nDim + dir / 2])) {
14192 return -1;
14193 }
14194
14195 MBool isPeriodic = false;
14196
14197 for(MInt i = 0; i < nDim; i++) {
14198 dummyCoords[i] = coords[i];
14199 }
14200
14201 cartesianToCylindric(coords, coordsCyl);
14202 dxCyl = m_azimuthalAngle;
14203
14204 if(coordsCyl[1] < m_azimuthalBbox.azimuthalBoundary(coords, -1) - MFloatEps) {
14205 isPeriodic = true;
14206 rotateCartesianCoordinates(dummyCoords, -dxCyl);
14207 } else if(coordsCyl[1] > m_azimuthalBbox.azimuthalBoundary(coords, 1) + MFloatEps) {
14208 isPeriodic = true;
14209 rotateCartesianCoordinates(dummyCoords, dxCyl);
14210 } else if(approx(dxCyl, 0.5 * PI, pow(10, -12))) {
14211 if(m_azimuthalBbox.azimuthalCenter(/*coords*/) > F0 && m_azimuthalBbox.azimuthalCenter(/*coords*/) < 0.5 * PI) {
14212 if(dir / 2 == 1 && coords[1] < bbox[1]) {
14213 isPeriodic = true;
14214 rotateCartesianCoordinates(dummyCoords, -dxCyl);
14215 }
14216 if(dir / 2 == 2 && coords[2] < bbox[2]) {
14217 isPeriodic = true;
14218 rotateCartesianCoordinates(dummyCoords, dxCyl);
14219 }
14220 } else {
14221 mTerm(1, AT_, "Do some implementing!");
14222 }
14223 }
14224
14225 if(!isPeriodic) return -1;
14226
14227 const MInt hilbertIndex = hilbertIndexGeneric(&dummyCoords[0]);
14228
14229 MInt ndom = -1;
14230 while(hilbertIndex >= hilbertOffsets[ndom + 1]) {
14231 ndom++;
14232 if(ndom == noDomains() - 1) break;
14233 }
14234 ASSERT(ndom > -1, "");
14235 ASSERT(hilbertIndex >= hilbertOffsets[ndom] && hilbertIndex < hilbertOffsets[ndom + 1],
14236 to_string(hilbertIndex) + " " + to_string(hilbertOffsets[ndom]) + " " + to_string(hilbertOffsets[ndom + 1]));
14237
14238 const MInt haloCellId = m_tree.size();
14239 m_tree.append();
14240
14241 for(MInt i = 0; i < nDim; i++) {
14242 a_level(haloCellId) = a_level(cellId);
14243 }
14244
14245 for(MInt i = 0; i < nDim; i++) {
14246 a_coordinate(haloCellId, i) = coords[i];
14247 }
14248
14249 hilbertToLocal.insert(make_pair(hilbertIndex, haloCellId));
14250
14251 for(MInt i = 0; i < m_noDirs; i++) {
14252 a_neighborId(haloCellId, i) = -1;
14253 }
14254 for(MInt i = 0; i < m_maxNoChilds; i++) {
14255 a_childId(haloCellId, i) = -1;
14256 }
14257 a_parentId(haloCellId) = -1;
14258
14259 a_neighborId(cellId, dir) = haloCellId;
14260 a_neighborId(haloCellId, revDir[dir]) = cellId;
14261 for(MInt otherDir = 0; otherDir < m_noDirs; otherDir++) {
14262 if(otherDir / 2 == dir / 2) continue;
14263 if(a_hasNeighbor(cellId, otherDir) == 0) continue;
14264 MInt nghbrId = a_neighborId(cellId, otherDir);
14265 if(a_hasNeighbor(nghbrId, dir) > 0) {
14266 nghbrId = a_neighborId(nghbrId, dir);
14267 a_neighborId(nghbrId, revDir[otherDir]) = haloCellId;
14268 a_neighborId(haloCellId, otherDir) = nghbrId;
14269 }
14270 }
14271
14272 for(MInt ndir = 0; ndir < m_noDirs; ndir++) {
14273 if(a_hasNeighbor(haloCellId, ndir) > 0) continue;
14274 for(MInt i = 0; i < nDim; i++) {
14275 dummyCoords[i] = a_coordinate(haloCellId, i);
14276 }
14277 dummyCoords[ndir / 2] += ((ndir % 2) == 0 ? -F1 : F1) * cellLength;
14278 for(MInt i = 0; i < nDim; i++) {
14279 dcoords[i] = dummyCoords[i];
14280 }
14281 MLong nghbrHilbertId = hilbertIndexGeneric(&dummyCoords[0]);
14282 MInt nghbrId = -1;
14283 auto range = hilbertToLocal.equal_range(nghbrHilbertId);
14284 for(auto it = range.first; it != range.second; ++it) {
14285 MFloat dist = F0;
14286 for(MInt i = 0; i < nDim; i++) {
14287 dist += POW2(a_coordinate(it->second, i) - dcoords[i]);
14288 }
14289 dist = sqrt(dist);
14290 if(dist < 0.001 * cellLength) {
14291 if(nghbrId > -1) cerr << "duplicate " << hilbertToLocal.count(nghbrHilbertId) << endl;
14292 nghbrId = it->second;
14293 }
14294 }
14295 if(nghbrId > -1) {
14296 a_neighborId(nghbrId, revDir[ndir]) = haloCellId;
14297 a_neighborId(haloCellId, ndir) = nghbrId;
14298#ifndef NDEBUG
14299 for(MInt i = 0; i < nDim; i++) {
14300 ASSERT(fabs(a_coordinate(nghbrId, i) - a_coordinate(haloCellId, i)) < cellLength * 1.0001, "");
14301 }
14302#endif
14303 continue;
14304 }
14305
14306 for(MInt i = 0; i < nDim; i++) {
14307 cartesianToCylindric(dummyCoords, coordsCyl);
14308 dxCyl = m_azimuthalAngle;
14309 if(coordsCyl[1] < m_azimuthalBbox.azimuthalBoundary(coords, -1)) {
14310 rotateCartesianCoordinates(dummyCoords, -dxCyl);
14311 } else if(coordsCyl[1] > m_azimuthalBbox.azimuthalBoundary(coords, 1)) {
14312 rotateCartesianCoordinates(dummyCoords, dxCyl);
14313 }
14314 }
14315 nghbrHilbertId = hilbertIndexGeneric(&dummyCoords[0]);
14316 nghbrId = -1;
14317 range = hilbertToLocal.equal_range(nghbrHilbertId);
14318 for(auto it = range.first; it != range.second; ++it) {
14319 MFloat dist = F0;
14320 for(MInt i = 0; i < nDim; i++) {
14321 dist += POW2(a_coordinate(it->second, i) - dcoords[i]);
14322 }
14323 dist = sqrt(dist);
14324 if(dist < 0.001 * cellLength) {
14325 if(nghbrId > -1) cerr << "duplicate " << hilbertToLocal.count(nghbrHilbertId) << endl;
14326 nghbrId = it->second;
14327 }
14328 }
14329 if(nghbrId > -1) {
14330 a_neighborId(nghbrId, revDir[ndir]) = haloCellId;
14331 a_neighborId(haloCellId, ndir) = nghbrId;
14332#ifndef NDEBUG
14333 for(MInt i = 0; i < nDim; i++) {
14334 ASSERT(fabs(a_coordinate(nghbrId, i) - a_coordinate(haloCellId, i)) < cellLength * 1.0001, "");
14335 }
14336#endif
14337 }
14338 }
14339
14340 a_resetProperties(haloCellId);
14341 a_hasProperty(haloCellId, Cell::IsHalo) = true;
14342 a_hasProperty(haloCellId, Cell::IsPeriodic) = isPeriodic;
14343
14344 MInt idx = setAzimuthalNeighborDomainIndex(ndom, halos, hilbertIds);
14345 ASSERT(idx > -1, "");
14346 halos[idx].push_back(haloCellId);
14347 hilbertIds[idx].push_back(hilbertIndex);
14348 noHalos[ndom]++;
14349
14350 return haloCellId;
14351}
14352
14357template <MInt nDim>
14358void CartesianGrid<nDim>::tagAzimuthalUnmappedHaloCells(vector<vector<MLong>>& shiftWindows,
14359 vector<MLong>& haloChildIds,
14360 vector<MInt>& haloDomainIds,
14361 MInt level) {
14362 TRACE();
14363
14364 static constexpr MFloat cornerStencil[8][3] = {{-F1, -F1, -F1}, {F1, -F1, -F1}, {-F1, F1, -F1}, {F1, F1, -F1},
14365 {-F1, -F1, F1}, {F1, -F1, F1}, {-F1, F1, F1}, {F1, F1, F1}};
14366 MFloat childCoords[3] = {F0, F0, F0};
14367 MFloat coordsCyl[3] = {F0, F0, F0};
14368 MFloat dxCyl = m_azimuthalAngle;
14369
14370 MInt cellCnt = 0;
14371 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
14372 if(a_level(cellId) != level + 1) continue;
14373 if(a_isHalo(cellId)) continue;
14374 cellCnt++;
14375 }
14376
14377 unordered_multimap<MLong, MInt> hilbertToLocal;
14378 hilbertToLocal.reserve(cellCnt * 1.5);
14379 ScratchSpace<MLong> hilbertOffsets(noDomains() + 1, AT_, "hilbertOffsets");
14380 MLong minHilbertIndex = std::numeric_limits<MLong>::max();
14381 for(MInt cellId = 0; cellId < m_tree.size(); cellId++) {
14382 if(a_isHalo(cellId)) continue;
14383 MLong hilbertId = hilbertIndexGeneric(&a_coordinate(cellId, 0), m_targetGridCenterOfGravity,
14384 m_targetGridLengthLevel0, m_minLevel);
14385 minHilbertIndex = mMin(minHilbertIndex, hilbertId);
14386 if(a_level(cellId) != level + 1) continue;
14387 hilbertId =
14388 hilbertIndexGeneric(&a_coordinate(cellId, 0), m_targetGridCenterOfGravity, m_targetGridLengthLevel0, level + 1);
14389 hilbertToLocal.insert(make_pair(hilbertId, cellId));
14390 }
14391 MPI_Allgather(&minHilbertIndex, 1, MPI_LONG, &hilbertOffsets[0], 1, MPI_LONG, mpiComm(), AT_, "minHilbertIndex",
14392 "hilbertOffsets[0]");
14393 hilbertOffsets[0] = 0;
14394 hilbertOffsets[noDomains()] = std::numeric_limits<MLong>::max();
14395
14396 if(m_noHaloLayers != 2) mTerm(1, AT_, "Only working with noHaloLayers=2");
14397
14398 MIntScratchSpace noSndHilberts(noDomains(), AT_, "noSndHilberts");
14399 noSndHilberts.fill(0);
14400
14401 shiftWindows.resize(noDomains());
14402
14403 haloDomainIds.resize(m_maxNoChilds * noAzimuthalUnmappedHaloCells());
14404 fill(haloDomainIds.begin(), haloDomainIds.end(), -1);
14405 haloChildIds.resize(m_maxNoChilds * noAzimuthalUnmappedHaloCells());
14406 fill(haloChildIds.begin(), haloChildIds.end(), -1);
14407
14408 MIntScratchSpace candidateMap(noAzimuthalUnmappedHaloCells(), AT_, "candidateMap");
14409 candidateMap.fill(-1);
14410 MLongScratchSpace candidateHilbertIds(m_maxNoChilds * noAzimuthalUnmappedHaloCells(), AT_, "candidateHilbertIds");
14411 candidateHilbertIds.fill(-1);
14412 MInt noRefineCandidates = 0;
14413
14414 // Almost Always refine unmapped halo cells
14415 MIntScratchSpace nghbrList(200, AT_, "nghbrList");
14416 for(MInt i = 0; i < noAzimuthalUnmappedHaloCells(); i++) {
14417 MInt cellId = azimuthalUnmappedHaloCell(i);
14418 if(a_level(cellId) != level) continue;
14419
14420 MBool refine = false;
14421 const MInt counter = getAdjacentGridCells(cellId, nghbrList, (level - 1), true);
14422 for(MInt n = 0; n < counter; n++) {
14423 MInt nghbrId = nghbrList[n];
14424 if(nghbrId < 0) continue;
14425 if(a_isHalo(nghbrId)) continue;
14426 if(a_noChildren(nghbrId) > 0) {
14427 refine = true;
14428 }
14429 }
14430
14431 // First the hilbertIds of the child cells are calculated.
14432 // The hilbertIds are communicated to the mpi rank which has the
14433 // respective hilbertIdRange.
14434 // Then, it is checked if an internal cell can be found with the same hilbertId
14435 // to create an azimuthal window halo mapping.
14436 if(refine) {
14437 candidateMap[noRefineCandidates] = i;
14438
14439 MInt parentId = cellId;
14440 for(MInt lvl = level; lvl > m_minLevel; lvl--) {
14441 parentId = a_parentId(parentId);
14442 }
14443
14444 // It is more robust to determine the side with the parent on the minLevel
14445 cartesianToCylindric(&a_coordinate(parentId, 0), coordsCyl);
14446 MInt side = m_azimuthalBbox.azimuthalSide(coordsCyl[1]);
14447
14448 MFloat hLength = F1B2 * cellLengthAtLevel(a_level(cellId));
14449 for(MInt child = 0; child < m_maxNoChilds; child++) {
14450 for(MInt d = 0; d < nDim; d++) {
14451 childCoords[d] = a_coordinate(cellId, d) + cornerStencil[child][d] * F1B2 * hLength;
14452 }
14453 rotateCartesianCoordinates(childCoords, side * dxCyl);
14454
14455 MLong hilbertId =
14456 hilbertIndexGeneric(childCoords, m_targetGridCenterOfGravity, m_targetGridLengthLevel0, m_minLevel);
14457
14458 MInt dom = -1;
14459 for(MInt d = 0; d < noDomains(); d++) {
14460 if(hilbertOffsets[d + 1] > hilbertId) {
14461 dom = d;
14462 break;
14463 }
14464 }
14465
14466 hilbertId = hilbertIndexGeneric(childCoords, m_targetGridCenterOfGravity, m_targetGridLengthLevel0, level + 1);
14467
14468 haloDomainIds[i * m_maxNoChilds + child] = dom;
14469 candidateHilbertIds[noRefineCandidates * m_maxNoChilds + child] = hilbertId;
14470 noSndHilberts[dom]++;
14471 }
14472 noRefineCandidates++;
14473 } else {
14474 fill(&haloChildIds[i * m_maxNoChilds], &haloChildIds[i * m_maxNoChilds] + m_maxNoChilds, -2);
14475 }
14476 }
14477
14478
14479 // Now the hilbertIds are communicated
14480 MIntScratchSpace sndOffsets(noDomains() + 1, AT_, "sndOffsets");
14481 sndOffsets[0] = 0;
14482 for(MInt d = 0; d < noDomains(); d++) {
14483 sndOffsets[d + 1] = sndOffsets[d] + noSndHilberts[d];
14484 noSndHilberts[d] = 0;
14485 }
14486
14487 MLongScratchSpace sndHilbertIds(m_maxNoChilds * noRefineCandidates, AT_, "sndHilbertIds");
14488 for(MInt i = 0; i < noRefineCandidates; i++) {
14489 MInt candidateId = candidateMap[i];
14490 for(MInt child = 0; child < m_maxNoChilds; child++) {
14491 MInt dom = haloDomainIds[candidateId * m_maxNoChilds + child];
14492 sndHilbertIds[sndOffsets[dom] + noSndHilberts[dom]] = candidateHilbertIds[i * m_maxNoChilds + child];
14493 noSndHilberts[dom]++;
14494 }
14495 }
14496
14497
14498 MIntScratchSpace noRcvHilberts(noDomains(), AT_, "noRcvHilberts");
14499 MPI_Alltoall(&noSndHilberts[0], 1, MPI_INT, &noRcvHilberts[0], 1, MPI_INT, mpiComm(), AT_, "noSndHilberts[0]",
14500 "noRcvHilberts[0]");
14501
14502
14503 MInt rcvHilbertsTotal = 0;
14504 MIntScratchSpace rcvOffsets(noDomains() + 1, AT_, "rcvOffsets");
14505 rcvOffsets[0] = 0;
14506 for(MInt d = 0; d < noDomains(); d++) {
14507 rcvOffsets[d + 1] = rcvOffsets[d] + noRcvHilberts[d];
14508 rcvHilbertsTotal += noRcvHilberts[d];
14509 }
14510 MLongScratchSpace rcvHilbertIds(rcvHilbertsTotal, AT_, "rcvHilbertIds");
14511
14512
14513 ScratchSpace<MPI_Request> sendReq(noDomains(), AT_, "sendReq");
14514 sendReq.fill(MPI_REQUEST_NULL);
14515
14516 for(MInt i = 0; i < noDomains(); i++) {
14517 if(noSndHilberts[i] == 0) continue;
14518 MPI_Issend(&sndHilbertIds[sndOffsets[i]], noSndHilberts[i], type_traits<MLong>::mpiType(), i, 24, mpiComm(),
14519 &sendReq[i], AT_, "sndHilbertIds[sndOffsets[i]");
14520 }
14521
14522 for(MInt i = 0; i < noDomains(); i++) {
14523 if(noRcvHilberts[i] == 0) continue;
14524 MPI_Recv(&rcvHilbertIds[rcvOffsets[i]], noRcvHilberts[i], type_traits<MLong>::mpiType(), i, 24, mpiComm(),
14525 MPI_STATUS_IGNORE, AT_, "rcvHilbertIds[rcvOffsets[i]]");
14526 }
14527
14528 for(MInt i = 0; i < noDomains(); i++) {
14529 if(noSndHilberts[i] == 0) continue;
14530 MPI_Wait(&sendReq[i], MPI_STATUSES_IGNORE, AT_);
14531 }
14532
14533
14534 // Check if internal cell can be found which has same hilbertId
14535 for(MInt i = 0; i < noDomains(); i++) {
14536 for(MInt j = 0; j < noRcvHilberts[i]; j++) {
14537 MLong hilbertId = rcvHilbertIds[rcvOffsets[i] + j];
14538
14539 MInt windowId = -1;
14540 auto range = hilbertToLocal.equal_range(hilbertId);
14541 for(auto it = range.first; it != range.second; ++it) {
14542 if(!a_hasProperty(it->second, Cell::IsPeriodic) && !a_hasProperty(it->second, Cell::IsHalo)) {
14543 windowId = it->second;
14544 }
14545 }
14546 ASSERT(windowId > -2, to_string(windowId) + " " + to_string(m_noInternalCells));
14547
14548 if(windowId > -1) {
14549 rcvHilbertIds[rcvOffsets[i] + j] = a_globalId(windowId);
14550 shiftWindows[i].push_back(rcvHilbertIds[rcvOffsets[i] + j]);
14551 } else {
14552 rcvHilbertIds[rcvOffsets[i] + j] = -1;
14553 }
14554 }
14555 }
14556
14557 // Communicated globalIds of found internal cells back to halo rank
14558 sendReq.fill(MPI_REQUEST_NULL);
14559
14560 for(MInt i = 0; i < noDomains(); i++) {
14561 if(noRcvHilberts[i] == 0) continue;
14562 MPI_Issend(&rcvHilbertIds[rcvOffsets[i]], noRcvHilberts[i], type_traits<MLong>::mpiType(), i, 12, mpiComm(),
14563 &sendReq[i], AT_, "rcvHilbertIds[rcvOffsets[i]]");
14564 }
14565
14566 for(MInt i = 0; i < noDomains(); i++) {
14567 if(noSndHilberts[i] == 0) continue;
14568 MPI_Recv(&sndHilbertIds[sndOffsets[i]], noSndHilberts[i], type_traits<MLong>::mpiType(), i, 12, mpiComm(),
14569 MPI_STATUS_IGNORE, AT_, "sndHilbertIds[sndOffsets[i]");
14570 }
14571
14572 for(MInt i = 0; i < noDomains(); i++) {
14573 if(noRcvHilberts[i] == 0) continue;
14574 MPI_Wait(&sendReq[i], MPI_STATUSES_IGNORE, AT_);
14575 }
14576
14577 for(MInt d = 0; d < noDomains(); d++) {
14578 noSndHilberts[d] = 0;
14579 }
14580
14581 for(MInt i = 0; i < noRefineCandidates; i++) {
14582 MInt candidateId = candidateMap[i];
14583 for(MInt child = 0; child < m_maxNoChilds; child++) {
14584 MInt dom = haloDomainIds[candidateId * m_maxNoChilds + child];
14585 haloChildIds[candidateId * m_maxNoChilds + child] = sndHilbertIds[sndOffsets[dom] + noSndHilberts[dom]++];
14586 }
14587 }
14588}
14589
14594template <MInt nDim>
14596 TRACE();
14597 if(globalNoDomains() == 1) {
14598 return;
14599 }
14600
14601 m_log << "checkWindowHaloConsistency azimuthal... ";
14602
14604 // Check #1: number of azimuthal window/halo cells match
14606 // Start receiving number of window cells from each neighbor domain
14607 ScratchSpace<MPI_Request> recvRequests(std::max(1, noAzimuthalNeighborDomains()), AT_, "recvRequests");
14608 MIntScratchSpace noWindowCellsRecv(std::max(1, noAzimuthalNeighborDomains()), AT_, "noWindowCellsRecv");
14609 for(MInt d = 0; d < noAzimuthalNeighborDomains(); d++) {
14610 noWindowCellsRecv[d] = -1;
14611 MPI_Irecv(&noWindowCellsRecv[d], 1, type_traits<MInt>::mpiType(), azimuthalNeighborDomain(d),
14612 azimuthalNeighborDomain(d), mpiComm(), &recvRequests[d], AT_, "noWindowCellsRecv[d]");
14613 }
14614
14615 // Start sending number of window cells to each neighbor domain
14616 ScratchSpace<MPI_Request> sendRequests(std::max(1, noAzimuthalNeighborDomains()), AT_, "sendRequests");
14617 MIntScratchSpace noWindowCellsSend(std::max(1, noAzimuthalNeighborDomains()), AT_, "noWindowCellsSend");
14618 for(MInt d = 0; d < noAzimuthalNeighborDomains(); d++) {
14619 noWindowCellsSend[d] = noAzimuthalWindowCells(d);
14620 MPI_Isend(&noWindowCellsSend[d], 1, type_traits<MInt>::mpiType(), azimuthalNeighborDomain(d), domainId(), mpiComm(),
14621 &sendRequests[d], AT_, "noWindowCellsSend[d]");
14622 }
14623
14624 // Finish MPI communication
14625 MPI_Waitall(noAzimuthalNeighborDomains(), &recvRequests[0], MPI_STATUSES_IGNORE, AT_);
14626 MPI_Waitall(noAzimuthalNeighborDomains(), &sendRequests[0], MPI_STATUSES_IGNORE, AT_);
14627
14628 // Check if received number of window cells matches the local number of halo cells
14629 for(MInt d = 0; d < noAzimuthalNeighborDomains(); d++) {
14630 if(noWindowCellsRecv[d] != noAzimuthalHaloCells(d)) {
14631 TERMM(1, "Cartesian Grid : Number of azimuthal window cells from domain " + to_string(azimuthalNeighborDomain(d))
14632 + " does not match local number of azimuthal halo cells; window: " + to_string(noWindowCellsRecv[d])
14633 + " ,halo: " + to_string(noAzimuthalHaloCells(d)));
14634 }
14635 }
14636
14638 // Check #2: grid global ids of window/halo cells match
14640 // Start receiving window cell global ids from each neighbor domain
14641 fill(recvRequests.begin(), recvRequests.end(), MPI_REQUEST_NULL);
14642 const MInt totalNoWindowCellsRecv = accumulate(noWindowCellsRecv.begin(), noWindowCellsRecv.end(), 0);
14643
14644 MIntScratchSpace windowCellsRecv(max(1, totalNoWindowCellsRecv), AT_, "windowCellsRecv");
14645 fill(windowCellsRecv.begin(), windowCellsRecv.end(), -1);
14646 for(MInt d = 0, offset = 0; d < noAzimuthalNeighborDomains(); d++) {
14647 if(noAzimuthalHaloCells(d) > 0) {
14648 MPI_Irecv(&windowCellsRecv[offset], noAzimuthalHaloCells(d), type_traits<MInt>::mpiType(),
14649 azimuthalNeighborDomain(d), azimuthalNeighborDomain(d), mpiComm(), &recvRequests[d], AT_,
14650 "windowCellsRecv[offset]");
14651 }
14652 offset += noAzimuthalHaloCells(d);
14653 }
14654
14655 // Start sending window cell global ids to each neighbor domain
14656 fill(sendRequests.begin(), sendRequests.end(), MPI_REQUEST_NULL);
14657 const MInt totalNoWindowCellsSend = accumulate(noWindowCellsSend.begin(), noWindowCellsSend.end(), 0);
14658 MIntScratchSpace windowCellsSend(max(1, totalNoWindowCellsSend), AT_, "windowCellsSend");
14659
14660 for(MInt d = 0, offset = 0; d < noAzimuthalNeighborDomains(); d++) {
14661 for(MInt c = 0; c < noWindowCellsSend[d]; c++) {
14662 TERMM_IF_NOT_COND(a_hasProperty(azimuthalWindowCell(d, c), Cell::IsWindow), "not a window cell");
14663 windowCellsSend[offset + c] = a_globalId(azimuthalWindowCell(d, c));
14664 }
14665 if(noAzimuthalWindowCells(d) > 0) {
14666 MPI_Isend(&windowCellsSend[offset], noAzimuthalWindowCells(d), type_traits<MInt>::mpiType(),
14667 azimuthalNeighborDomain(d), domainId(), mpiComm(), &sendRequests[d], AT_, "windowCellsSend[offset]");
14668 }
14669 offset += noAzimuthalWindowCells(d);
14670 }
14671
14672 // Finish MPI communication
14673 MPI_Waitall(noAzimuthalNeighborDomains(), &recvRequests[0], MPI_STATUSES_IGNORE, AT_);
14674 MPI_Waitall(noAzimuthalNeighborDomains(), &sendRequests[0], MPI_STATUSES_IGNORE, AT_);
14675
14676 // Check if received window cell global ids match the local halo cell global ids
14677 for(MInt d = 0, offset = 0; d < noAzimuthalNeighborDomains(); d++) {
14678 for(MInt c = 0; c < noAzimuthalHaloCells(d); c++) {
14679 const MInt cellId = azimuthalHaloCell(d, c);
14680 TERMM_IF_NOT_COND(a_isHalo(cellId), "not a halo cell");
14681 const MInt globalId = a_globalId(cellId);
14682 // If halo cell has periodic flag, its global id should be -1
14683 if(windowCellsRecv[offset + c] != globalId && !(a_hasProperty(cellId, Cell::IsPeriodic) && globalId == -1)) {
14684 TERMM(1, "Global id of window cell " + to_string(c) + " from domain " + to_string(azimuthalNeighborDomain(d))
14685 + " does not match local halo cell gobal id (" + to_string(windowCellsRecv[offset + c]) + " vs. "
14686 + to_string(a_globalId(azimuthalHaloCell(d, c))) + ")");
14687 }
14688 }
14689 offset += noAzimuthalHaloCells(d);
14690 }
14691
14692 m_log << "done" << std::endl;
14693}
14694
14702template <MInt nDim>
14704 TRACE();
14705
14706 MInt maxLvl = mMin(m_maxRfnmntLvl, m_maxLevel);
14707 for(MInt level = m_maxUniformRefinementLevel; level < maxLvl; level++) {
14708 for(MInt i = 0; i < noAzimuthalNeighborDomains(); i++) {
14709 for(MInt j = 0; j < (signed)m_azimuthalHaloCells[i].size(); j++) {
14710 MInt cellId = m_azimuthalHaloCells[i][j];
14711 if(a_level(cellId) != level) continue;
14712 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
14713 if(m_tree.solver(cellId, solver)) {
14714 MBool noRefine = false;
14715 for(MInt child = 0; child < m_maxNoChilds; child++) {
14716 MInt childId = a_childId(cellId, child);
14717 if(childId == -1) continue;
14718 if(!m_tree.solver(childId, solver)) {
14719 noRefine = true;
14720 break;
14721 }
14722 }
14723 if(noRefine) {
14724 for(MInt child = 0; child < m_maxNoChilds; child++) {
14725 MInt childId = a_childId(cellId, child);
14726 if(childId == -1) continue;
14727 m_tree.solver(childId, solver) = false;
14728 }
14729 }
14730 } else {
14731 for(MInt child = 0; child < m_maxNoChilds; child++) {
14732 MInt childId = a_childId(cellId, child);
14733 if(childId == -1) continue;
14734 m_tree.solver(childId, solver) = false;
14735 }
14736 }
14737 }
14738 }
14739 }
14740
14741
14742 for(MInt i = 0; i < (signed)m_azimuthalUnmappedHaloCells.size(); i++) {
14743 MInt cellId = m_azimuthalUnmappedHaloCells[i];
14744 if(a_level(cellId) != level) continue;
14745 for(MInt solver = 0; solver < treeb().noSolvers(); solver++) {
14746 if(m_tree.solver(cellId, solver)) {
14747 MBool noRefine = false;
14748 for(MInt child = 0; child < m_maxNoChilds; child++) {
14749 MInt childId = a_childId(cellId, child);
14750 if(childId == -1) continue;
14751 if(!m_tree.solver(childId, solver)) {
14752 noRefine = true;
14753 break;
14754 }
14755 }
14756 if(noRefine) {
14757 for(MInt child = 0; child < m_maxNoChilds; child++) {
14758 MInt childId = a_childId(cellId, child);
14759 if(childId == -1) continue;
14760 m_tree.solver(childId, solver) = false;
14761 }
14762 }
14763 } else {
14764 for(MInt child = 0; child < m_maxNoChilds; child++) {
14765 MInt childId = a_childId(cellId, child);
14766 if(childId == -1) continue;
14767 m_tree.solver(childId, solver) = false;
14768 }
14769 }
14770 }
14771 }
14772 }
14773}
14774
14775
14779template <MInt nDim>
14781 TRACE();
14782
14783 TERMM_IF_COND((signed)m_windowLayer_.size() < noNeighborDomains(), text);
14784 // Ensure that all cells in m_windowLayer_ have proper layer
14785 for(MInt d = 0; d < noNeighborDomains(); ++d) {
14786 for(const auto item : m_windowLayer_[d]) {
14787 const MInt cellId = item.first;
14788 TERMM_IF_NOT_COND(cellId > -1 && cellId < m_tree.size(), "");
14789 TERMM_IF_COND(item.second.all(), "This entry is unnecessary!");
14790
14791 for(MInt solver = 0; solver < treeb().noSolvers(); ++solver) {
14792 TERMM_IF_COND(item.second.get(solver) <= m_noSolverHaloLayers[solver] /*M32X4bit<>::MAX*/
14793 && !m_tree.solver(cellId, solver),
14794 text);
14795 }
14796 }
14797 }
14798
14799 // Check that all cells in m_windowCells are also in m_windowLayer_
14800 for(MInt i = 0; i < noNeighborDomains(); i++) {
14801 std::set<MInt> allWs(m_windowCells[i].begin(), m_windowCells[i].end());
14802 for(MInt j = 0; j < (signed)m_windowCells[i].size(); j++) {
14803 const MInt cellId = m_windowCells[i][j];
14804 if(m_windowLayer_[i].find(cellId) == m_windowLayer_[i].end()) {
14805 stringstream ss;
14806 ss << " domainId=" << domainId() << ": i=" << i << " j=" << j << " cellId=" << cellId
14807 << " globalId=" << a_globalId(cellId) << " nghbrDom=" << m_nghbrDomains[i] << endl;
14808 TERMM(1, text + ss.str());
14809 }
14810 }
14811
14812 // Check that all cells in m_windowLayers_ are also in m_windowCells
14813 std::set<std::pair<MInt, MInt>> del;
14814 for(auto m : m_windowLayer_[i]) {
14815 if(allWs.find(m.first) == allWs.end()) {
14816 // Note: If following assert fails, one could try to uncomment the next line
14817 // del.insert(make_pair(i,m.first)); continue;
14818 std::stringstream ss;
14819 ss << text << " d=" << domainId() << " nghbrDom=" << m_nghbrDomains[i] << "(" << i
14820 << ") minLevel=" << m_minLevel << " cell_level=" << a_level(m.first) << " isHalo=" << a_isHalo(m.first)
14821 << " cellId=" << m.first << " cellLayer=" << m.second.get(0) << "|" << m.second.get(1)
14822 << " parentId=" << a_parentId(m.first) << " parentIsHalo=" << a_isHalo(std::max(0, (int)a_parentId(m.first)))
14823 << " " << isSolverWindowCell(i, m.first, 0);
14824 TERMM(1, ss.str());
14825 }
14826 }
14827 for(auto d : del) {
14828 m_windowLayer_[d.first].erase(m_windowLayer_[d.first].find(d.second));
14829 }
14830
14831 if(m_noPeriodicCartesianDirs == 0) TERMM_IF_NOT_COND(m_windowCells[i].size() == m_windowLayer_[i].size(), text);
14832 }
14833}
MLong allocatedBytes()
Return the number of allocated bytes.
Definition: alloc.cpp:121
void mAlloc(T *&a, const MLong N, const MString &objectName, MString function)
allocates memory for one-dimensional array 'a' of size N
Definition: alloc.h:173
MBool mDeallocate(T *&a)
deallocates the memory previously allocated for element 'a'
Definition: alloc.h:544
void refineCell(const MInt cellId, const MLong *const refineChildIds=nullptr, const MBool mayHaveChildren=false, const std::vector< std::function< MInt(const MFloat *, const MInt, const MInt)> > &=std::vector< std::function< MInt(const MFloat *, const MInt, const MInt)> >(), const std::bitset< maia::grid::tree::Tree< nDim >::maxNoSolvers()> refineFlag=std::bitset< maia::grid::tree::Tree< nDim >::maxNoSolvers()>(1ul))
MInt createAzimuthalHaloCell(const MInt, const MInt, const MLong *, std::unordered_multimap< MLong, MInt > &, const MFloat *, MInt *const, std::vector< std::vector< MInt > > &, std::vector< std::vector< MLong > > &)
Create a new azimuthal halo cell as neighbor of cellId in direction dir and find matching neighbor do...
void getAdjacentGridCells1d5(std::set< MInt > &, const MInt, const MInt, const MInt dimNext=-1, const MInt dimNextNext=-1, const uint_fast8_t childCodes=(uint_fast8_t)~0)
MBool hollowBoxSphereIntersection(const MFloat *bMin, const MFloat *bMax, const MFloat *const sphereCenter, MFloat const radius)
MInt findIndex(ITERATOR, ITERATOR, const U &)
Find index in array [first,last) with matching entry 'val'.
void tagActiveWindowsOnLeafLvl3(const MInt maxLevel, const MBool duringMeshAdaptation, const std::vector< std::function< void(const MInt)> > &=std::vector< std::function< void(const MInt)> >())
Don't touch this function, because you don't know what you are doing NOTE: Actually the most clever w...
void rotateCartesianCoordinates(MFloat *, MFloat)
Rotate caresian coordinates by angle. Azimuthal periodic center is used.
void restorePeriodicConnection()
Delete the Connection of periodic-Halo-Cells at the bounding box.
void calculateNoOffspringsAndWorkload(Collector< CELLTYPE > *input_cells, MInt input_noCells)
Caluclate the number of offsprings and the workload for each cell.
void checkWindowHaloConsistency(const MBool fullCheck=false, const MString a="")
Checks consistency of window/halo cells.
void tagAzimuthalUnmappedHaloCells(std::vector< std::vector< MLong > > &, std::vector< MLong > &, std::vector< MInt > &, MInt)
Tag azimuthal halo cells for refinement.
void computeGlobalIds()
Update number of internal cells, recalculate domain offsets, recalculate global ids for all internal ...
void meshAdaptation(std::vector< std::vector< MFloat > > &, std::vector< MFloat > &, std::vector< std::bitset< 64 > > &, std::vector< MInt > &, const std::vector< std::function< void(const MInt)> > &, const std::vector< std::function< void(const MInt)> > &, const std::vector< std::function< void(const MInt)> > &, const std::vector< std::function< void(const MInt, const MInt)> > &, const std::vector< std::function< MInt(const MFloat *, const MInt, const MInt)> > &, const std::vector< std::function< void()> > &)
MInt ancestorPartitionCellId(const MInt cellId) const
Return the cell Id of the partition cell that has the given cellId as a descendant.
void loadGridFile(const MInt, const MInt)
Load grid from disk and setup communication.
void updateHaloCellCollectors()
Fill m_nghbrDomains, m_haloCells and m_windowCells with data from temporary buffers.
void determineNoPartitionCellsAndOffsets(MLong *const noPartitionCells, MLong *const partitionCellOffsets)
Determine the number of partition cells on each domain and the corresponding offsets.
MBool pointInLocalBoundingBox(const MFloat *coord)
Check if the given point lies in the local bounding box.
MInt globalIdToLocalId(const MLong &globalId, const MBool termIfNotExisting=false)
Global to local id mapping.
void savePartitionCellWorkloadsGridFile()
Update the partition cell workloads in the grid file.
void descendStoreGlobalId(MInt cellId, MInt &localCnt)
Recursively descend subtree to reset global id for internal cells.
MInt findContainingPartitionCell(const MFloat *const coord, const MInt solverId=-1, std::function< MFloat *(MInt, MFloat *const)> correctCellCoord=nullptr)
Return the cell Id of the partition cell containing the coords.
void meshAdaptationDefault(std::vector< std::vector< MFloat > > &, std::vector< MFloat > &, std::vector< std::bitset< 64 > > &, std::vector< MInt > &, const std::vector< std::function< void(const MInt)> > &, const std::vector< std::function< void(const MInt)> > &, const std::vector< std::function< void(const MInt)> > &, const std::vector< std::function< void(const MInt, const MInt)> > &, const std::vector< std::function< MInt(const MFloat *, const MInt, const MInt)> > &, const std::vector< std::function< void()> > &)
Performs mesh adaptation and continuously updates the window/halo cells.
void tagActiveWindows(std::vector< MLong > &, MInt)
Flag m_noHaloLayers layers of halo cells adjacent to internal cells on this domain.
MLong hilbertIndexGeneric(const MFloat *const coords, const MFloat *const center, const MFloat length0, const MInt baseLevel) const
Return the hilbert index for the given coordinates in 2D/3D.
MBool intersectingWithLocalBoundingBox(MFloat *const center, MFloat const radius)
MBool pointWthCell(const std::array< MFloat, nDim > &coord, const MInt cellId, std::function< MFloat *(MInt, MFloat *const)> correctCellCoord=nullptr) const
void determinePartLvlAncestorHaloWindowCells(std::vector< std::vector< MInt > > &partLvlAncestorHaloCells, std::vector< std::vector< MInt > > &partLvlAncestorWindowCells)
Store partition level ancestor window/halo cells.
void tagActiveWindowsAtMinLevel(const MInt)
Actually the most clever way of tagging is to go from highest level to lower levels,...
void compactCells(const std::vector< std::function< void(const MInt, const MInt)> > &=std::vector< std::function< void(const MInt, const MInt)> >())
Removes all holes in the cell collector and moves halo cells to the back of the collector.
void computeLocalBoundingBox(MFloat *const bbox)
Compute the bounding box of all local cells.
MInt findNeighborDomainId(const MLong globalId)
void saveGrid(const MChar *, const std::vector< std::vector< MInt > > &, const std::vector< std::vector< MInt > > &, const std::vector< std::vector< MInt > > &, const std::vector< MInt > &, const std::vector< std::vector< MInt > > &, MInt *recalcIdTree=nullptr)
void exchangeNotInPlace(DATATYPE *exchangeVar, DATATYPE *recvMem)
communicates a variable from windows to halos but does not overwrite halo values. Window values are s...
void changeGlobalToLocalIds()
Change global child/neighbor/parent cell ids into local cell ids. Requires that m_globalToLocalId con...
void saveDonorGridPartition(const MString &gridMapFileName, const MString &gridPartitionFileName)
Create and store donor grid partition for volume-coupled simulations.
void balance(const MInt *const noCellsToReceiveByDomain, const MInt *const noCellsToSendByDomain, const MInt *const sortedCellId, const MLong *const offset, const MLong *const globalIdOffsets)
Balance the grid according to the given cell send/recv counts.
void setGridInputFilename(MBool=false)
Read grid file name from properties or restart files.
void createMinLevelExchangeCells()
Create window-halo-connectivity based on hilbertIndex matching for regular and periodic exchange cell...
MInt findContainingHaloCell(const MFloat *const coord, const MInt solverId, MInt domainId, MBool onlyPartitionCells, std::function< MFloat *(MInt, MFloat *const)> correctCellCoord=nullptr)
Return the cell id of the halo partition cell containing the coords.
void correctAzimuthalSolverBits()
Corrects solver bits on azimuthal halo cells Ensure that azimuthal halo cells have all children or ar...
void createHigherLevelExchangeCells_old(const MInt onlyLevel=-1, const std::vector< std::function< void(const MInt)> > &=std::vector< std::function< void(const MInt)> >())
Iteratively create window and halo cells on levels m_minLevel+1...m_maxLevel, starting from m_minLeve...
void createPaths()
Necessary for extractPointIdsFromGrid function.
void descendNoOffsprings(const MLong cellId, const MLong offset=0)
void generalExchange(MInt noVars, const MInt *vars, DATATYPE **exchangeVar, MInt noDomSend, MInt *domSend, const MInt *noCellsSendPerDom, const MInt *cellIdsSend, MInt noDomRecv, MInt *domRecv, const MInt *noCellsRecvPerDom, const MInt *cellIdsRecv)
void exchangeProperties()
Exchange properties of window/halo cells.
MBool coarsenCheck(const MInt cellId, const MInt solverId)
checks if the given cell's children may be removed
MBool boxSphereIntersection(const MFloat *bMin, const MFloat *bMax, const MFloat *const sphereCenter, MFloat const radius)
void propagateDistance(std::vector< MInt > &list, MIntScratchSpace &distMem, MInt dist)
propagates a given distance from a given list of cells on equal level neighbours
void savePartitionFile()
Save current grid partitioning to file.
void storeMinLevelCells(const MBool updateMinlevel=false)
Store cell ids of all min-level cells.
MInt intersectingWithHaloCells(MFloat *const center, MFloat const radius, MInt domainId, MBool onlyPartition)
void removeChilds(const MInt cellId)
removes the children of the given cell
MInt setNeighborDomainIndex(const MInt, std::vector< TA > &, std::vector< TB > &, std::vector< TC > &)
Find neighbor domain index or create new if not existing.
void initGridMap()
void setLevel()
Set minimum und maximum cell levels.
void setupWindowHaloCellConnectivity()
Create window and halo cell connectivity between domains.
void tagActiveWindows2_(std::vector< MLong > &, const MInt)
NOTE: Actually the most clever way of tagging is to go from highest level to lower levels,...
MInt getAdjacentGridCells5(const MInt, MInt *const, const MInt level=-1, const MBool diagonalNeighbors=true)
void cartesianToCylindric(const MFloat *, MFloat *)
Transform cartesian cell coordinate to cylindrcal coordinats using the / using the azimuthal periodic...
MInt intersectingWithPartitioncells(MFloat *const center, MFloat const radius)
MInt findContainingHaloPartitionCell(const MFloat *const coors, const MInt solverId=-1)
Return the cell id of the halo partition cell containing the coords.
MBool updatePartitionCells(MInt offspringThreshold, MFloat workloadThreshold)
Determine new partition cells (i.e. in/decrease the partition level shifts) and change the grid accor...
void meshAdaptationLowMem(std::vector< std::vector< MFloat > > &, std::vector< MFloat > &, std::vector< std::bitset< 64 > > &, std::vector< MInt > &, const std::vector< std::function< void(const MInt)> > &, const std::vector< std::function< void(const MInt)> > &, const std::vector< std::function< void(const MInt)> > &, const std::vector< std::function< void(const MInt, const MInt)> > &, const std::vector< std::function< MInt(const MFloat *, const MInt, const MInt)> > &, const std::vector< std::function< void()> > &)
Performs mesh adaptation and continuously updates the window/halo cells.
void propagationStep(MInt cellId, MInt dist, MInt *distMem, MInt endDist)
starts the propagation on a cell and continues calling the function for the neighbours
void setChildSolverFlag(const MInt cellId, const MInt solver, const std::function< MInt(const MFloat *, const MInt, const MInt)> &cellOutsideSolver)
void deletePeriodicConnection(const MBool=true)
Delete the Connection of periodic-Halo-Cells at the bounding box.
void partitionParallel(const MInt tmpCount, const MLong tmpOffset, const MFloat *const partitionCellsWorkload, const MLong *const partitionCellsGlobalId, const MFloat totalWorkload, MLong *partitionCellOffsets, MLong *globalIdOffsets, MBool computeOnlyPartition=false)
MInt createAdjacentHaloCell(const MInt, const MInt, const MLong *, std::unordered_multimap< MLong, MInt > &, const MFloat *, MInt *const, std::vector< std::vector< MInt > > &, std::vector< std::vector< MLong > > &)
Create a new halo cell (candidate) as neighbor of cellId in direction dir and find matching neighbor ...
void createGridMap(const MString &donorGridFileName, const MString &gridMapFileName)
Create file that contains mapping from donor to target cell.
MBool refineCheck(const MInt cellId, const MInt solverId, const std::vector< std::function< MInt(const MFloat *, const MInt, const MInt)> > &cellOutsideSolver)
checks if the given cell may be refined
MInt deleteCell(const MInt cellId)
Deletes a cell (without collector fragmentation) and returns new collector size.
void createGridSlice(const MString &axis, const MFloat intercept, const MString &fileName)
Overload method of createGridSlice to provide usage with or without return of cellIds which are in th...
void exchangeSolverBitset(std::bitset< N > *const data, const MBool defaultVal=false)
MInt findContainingLeafCell(const MFloat *coord, std::function< MFloat *(MInt, MFloat *const)> correctCellCoord=nullptr, const MInt solverId=-1)
Return the cell Id of the leaf cell containing the coords.
void computeLeafLevel()
Compute distance to leaf level.
void dumpCellData(const MString name)
Write the cell data of the local tree to some file for debugging purposes.
void checkAzimuthalWindowHaloConsistency()
Checks consistency of aimuthal window/halo cells.
void removeCell(const MInt cellId)
removes the children of the given cell
MInt getAdjacentGridCells(MInt, MIntScratchSpace &, MInt, MBool diagonalNeighbors=true)
Retrieves all direct and diagonal neighboring cells of the given cell on the child level if available...
void swapCells(const MInt cellId, const MInt otherId)
swap two cells; the window/halo cell arrays have to be update elsewhere for performance reasons
void createGlobalToLocalIdMapping()
Create the mapping from global to local cell ids.
void tagAzimuthalHigherLevelExchangeCells(std::vector< MLong > &, std::vector< MLong > &, MInt)
Tag azimuthal halo cells for refinement.
void gridSanityChecks()
Perform grid sanity checks.
void localToGlobalIds()
Convert parent ids, neighbor ids, and child ids from local to global cell ids.
void resetCell(const MInt &cellId)
Reset cell to default values.
CartesianGrid(MInt maxCells, const MFloat *const bBox, const MPI_Comm comm, const MString &fileName="")
void loadDonorGridPartition(const MLong *const partitionCellsId, const MInt noPartitionCells)
Return partition cell offsets based on grid partition file.
void updatePartitionCellInformation()
Update the partition cell local/global ids and the partition cell global offsets.
MInt setAzimuthalNeighborDomainIndex(const MInt, std::vector< std::vector< MInt > > &, std::vector< std::vector< MInt > > &)
Find azimuthal neighbor domain index or create new if not existing.
void createLeafCellMapping(const MInt donorSolverId, const MInt gridCellId, std::vector< MInt > &mappedLeafCells, MBool allChilds=false)
Identify all leaf cells of one solver which are mapped to a given grid cell.
void createHigherLevelExchangeCells(const MInt onlyLevel=-1, const MBool duringMeshAdaptation=false, const std::vector< std::function< void(const MInt)> > &=std::vector< std::function< void(const MInt)> >(), const std::vector< std::function< void(const MInt)> > &=std::vector< std::function< void(const MInt)> >(), const MBool forceLeafLvlCorrection=false)
Iteratively create window and halo cells on levels m_minLevel+1...m_maxLevel, starting from m_minLeve...
void loadGrid(const MString &fileName)
Load a grid file writen with saveGridDomainPar.
void checkWindowLayer(const MString a)
Checks variable m_windowLayer_.
void loadPartitionFile(const MString &partitionFileName, MLong *partitionCellOffsets)
Load a grid partitioning from a file.
MInt size()
Definition: collector.h:29
static MInt propertyLength(const MString &name, MInt solverId=m_noSolvers)
Returns the number of elements of a property.
Definition: context.cpp:538
static MBool propertyExists(const MString &name, MInt solver=m_noSolvers)
This function checks if a property exists in general.
Definition: context.cpp:494
MInt get(const MInt index) const
Definition: maiatypes.h:91
MBool any() const
Definition: maiatypes.h:95
T type
Definition: maiatypes.h:82
T data() const
Definition: maiatypes.h:105
This class is a ScratchSpace.
Definition: scratch.h:758
size_type size() const
Definition: scratch.h:302
pointer data()
Definition: scratch.h:289
T * getPointer() const
Deprecated: use begin() instead!
Definition: scratch.h:316
void fill(T val)
fill the scratch with a given value
Definition: scratch.h:311
MInt size0() const
Definition: scratch.h:298
iterator end()
Definition: scratch.h:281
iterator begin()
Definition: scratch.h:273
Class that represents grid tree and contains all relevant per-node data.
static constexpr MInt maxNoSolvers()
Return maximum number of supported solvers.
void mTerm(const MInt errorCode, const MString &location, const MString &message)
Definition: functions.cpp:29
MBool fileExists(const MString &fileName)
Returns true if the file fileName exists, false otherwise.
Definition: functions.cpp:73
MBool isApproxInt(const T &, const T)
Definition: functions.h:284
constexpr Real POW2(const Real x)
Definition: functions.h:119
MBool approx(const T &, const U &, const T)
Definition: functions.h:272
constexpr T mMin(const T &x, const T &y)
Definition: functions.h:90
MInt ipow(MInt base, MInt exp)
Integer exponent function for non-negative exponents.
Definition: functions.h:317
constexpr T mMax(const T &x, const T &y)
Definition: functions.h:94
MFloat wallTime()
Definition: functions.h:80
const MInt m_revDir[6]
MInt globalTimeStep
void printAllocatedMemory(const MLong oldAllocatedBytes, const MString &solverName, const MPI_Comm comm)
Prints currently allocated memory.
MInt globalNoDomains()
Return global number of domains.
MBool g_multiSolverGrid
InfoOutFile m_log
std::ostream cerr0
constexpr MLong IPOW2(MInt x)
constexpr MFloat FPOW2(MInt x)
constexpr MFloat FFPOW2(MInt x)
int32_t MInt
Definition: maiatypes.h:62
uint32_t MUint
Definition: maiatypes.h:63
unsigned char MUchar
Definition: maiatypes.h:57
std::basic_string< char > MString
Definition: maiatypes.h:55
MInt noSolvers
Definition: maiatypes.h:73
double MFloat
Definition: maiatypes.h:52
int64_t MLong
Definition: maiatypes.h:64
bool MBool
Definition: maiatypes.h:58
char MChar
Definition: maiatypes.h:56
MInt id
Definition: maiatypes.h:71
uint64_t MUlong
Definition: maiatypes.h:65
int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status, const MString &name, const MString &varname)
same as MPI_Recv
int MPI_Isend(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request, const MString &name, const MString &varname)
same as MPI_Isend
int MPI_Allgatherv(const void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, const int recvcounts[], const int displs[], MPI_Datatype recvtype, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Allgatherv
int MPI_Comm_free(MPI_Comm *comm, const MString &name, const MString &varname)
same as MPI_Comm_free, but updates the number of MPI communicators
int MPI_Irecv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Request *request, const MString &name, const MString &varname)
same as MPI_Irecv
int MPI_Comm_create(MPI_Comm comm, MPI_Group group, MPI_Comm *newcomm, const MString &name, const MString &varname)
same as MPI_Comm_create, but updates the number of MPI communicators
int MPI_Issend(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request, const MString &name, const MString &varname)
same as MPI_Issend
int MPI_Group_incl(MPI_Group group, int n, const int ranks[], MPI_Group *newgroup, const MString &name)
same as MPI_Group_incl
int MPI_Comm_group(MPI_Comm comm, MPI_Group *group, const MString &name, const MString &varname)
same as MPI_Comm_group
int MPI_Wait(MPI_Request *request, MPI_Status *status, const MString &name)
same as MPI_Wait
int MPI_Waitall(int count, MPI_Request *request, MPI_Status *status, const MString &name)
same as MPI_Waitall
int MPI_Allreduce(const void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Allreduce
int MPI_Alltoall(const void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Alltoall
int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm, const MString &name, const MString &varname)
same as MPI_Bcast
int MPI_Allgather(const void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Allgather
int MPI_Group_free(MPI_Group *group, const MString &name)
same as MPI_Group_free
void const MInt const MInt const MInt const MInt maxNoCells
Definition: collector.h:240
void const MInt cellId
Definition: collector.h:239
constexpr std::underlying_type< GridCell >::type p(const GridCell property)
Converts property name to underlying integer value.
maia::grid::cell::BitsetType PropertyBitsetType
Underlying bitset type for property storage.
std::bitset< MAIA_MULTISOLVER_MAX_NO_SOLVERS > SolverBitsetType
Underlying bitset type for solver use storage (Note: If there are more solvers, change size here)
void partitionCellToGlobalOffsets(const IdType *const partitionCellOffsets, const IdType *const partitionCellToGlobalIds, const IdType noDomains, IdType *const globalIdOffsets)
Translates a list of partition cell offsets to global offsets.
Definition: partition.h:40
WeightType optimalPartitioningSerial(const WeightType *const weights, const IdType noWeights, const IdType noPartitions, IdType *const offsets)
Serial algorithm to find the optimal (not ideal) partitioning with given workloads based on a probing...
Definition: partition.h:61
void exchangeScattered(const std::vector< MInt > &nghbrDomains, std::vector< MInt > &sendDomainIndices, std::vector< U > &sendData, const MPI_Comm comm, std::vector< MInt > &recvOffsets, std::vector< U > &recvBuffer, const MInt noDat=1)
Generic exchange of data.
Definition: mpiexchange.h:613
void communicateData(const DataType *const data, const MInt noCells, const MInt *const sortedCellId, const MInt noDomains, const MInt domainId, const MPI_Comm mpiComm, const MInt *const noCellsToSendByDomain, const MInt *const noCellsToReceiveByDomain, const MInt dataBlockSize, DataType *const buffer)
Assemble given data in send buffer and communicate.
Definition: mpiexchange.h:858
void exchangeData(const MInt noNghbrDomains, const MInt *const nghbrDomains, const MInt *const noHaloCells, const MInt **const, const MInt *const noWindowCells, const MInt **const windowCells, const MPI_Comm comm, const U *const data, U *const haloBuffer, const MInt noDat=1)
Generic exchange of data.
Definition: mpiexchange.h:295
void exchangeBuffer(const MInt noExDomains, const MInt *const exDomainId, const MInt *const recvSize, const MInt *const sendSize, const MPI_Comm comm, U *const receiveBuffer, const U *const sendBuffer, const MInt noDat=1)
Generic exchange of data.
Definition: mpiexchange.h:44
void communicateBitsetData(const std::bitset< N > *const data, const MInt noCells, const MInt *const sortedCellId, const MInt noDomains, const MInt domainId, const MPI_Comm mpiComm, const MInt *const noCellsToSendByDomain, const MInt *const noCellsToReceiveByDomain, const MInt dataBlockSize, std::bitset< N > *const buffer)
Definition: mpiexchange.h:873
void reverseExchangeData(const MInt noNghbrDomains, const MInt *const nghbrDomains, const MInt *const noHaloCells, const MInt **const haloCells, const MInt *const noWindowCells, const MInt **const, const MPI_Comm comm, const U *const data, U *const windowBuffer, const MInt noDat=1)
Generic reverse exchange of data.
Definition: mpiexchange.h:400
void exchangeBitset(const std::vector< MInt > &nghbrDomains, const std::vector< std::vector< MInt > > &haloCellVec, const std::vector< std::vector< MInt > > &windowCellVec, const MPI_Comm comm, std::bitset< N > *const data, const MInt noCells, const MInt noDat=1)
Generic exchange of data.
Definition: mpiexchange.h:516
const MInt PIO_READ
Definition: parallelio.h:40
Namespace for auxiliary functions/classes.
PARALLELIO_DEFAULT_BACKEND ParallelIo
Definition: parallelio.h:292
MFloat dist(const Point< DIM > &p, const Point< DIM > &q)
Definition: pointbox.h:54
Definition: contexttypes.h:19
void logDuration_(const MFloat timeStart, const MString module, const MString comment, const MPI_Comm comm, const MInt domainId, const MInt noDomains)
Output the min/max/average duration of a code section over the ranks in a communicator Note: only use...
Definition: timer.cpp:171