MAIA bb96820c
Multiphysics at AIA
Loading...
Searching...
No Matches
cartesiangridproxy.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
8
9#include <algorithm>
10#include <numeric>
11#include "COMM/mpiexchange.h"
12#include "COMM/mpioverride.h"
13#include "IO/context.h"
14
15using namespace std;
16
17
18namespace maia {
19namespace grid {
20
21template <MInt nDim>
22Proxy<nDim>::Proxy(const MInt solverId, Grid& grid_, Geometry<nDim>& geometry_)
23 : m_solverId(solverId), m_grid(grid_), m_tree(solverId, grid_.treeb()), m_geometry(&geometry_) {
24 if(raw().addSolverToGrid()) {
25 MBool useSolverGeometry = false;
26 useSolverGeometry = Context::getSolverProperty<MBool>("useSolverGeometry", solverId, AT_, &useSolverGeometry);
27 if(useSolverGeometry && solverId == (raw().treeb().noSolvers() - 1)) {
29 }
30 }
31
32 update();
33
35
36 if(!raw().paraViewPlugin()) {
37 m_maxRefinementLevel = Context::getSolverProperty<MInt>("maxRfnmntLvl", solverId, AT_);
38
40 TERMM(1, "Error: property maxRfnmntLvl is smaller than the maxLevel in the grid for solver #"
41 + std::to_string(solverId) + " (" + std::to_string(m_maxRefinementLevel) + " < "
42 + std::to_string(m_maxLevel) + ")");
43 }
44 m_maxNoCells = Context::getSolverProperty<MInt>("maxNoCells", solverId, AT_, &m_maxNoCells);
45 if(Context::propertyExists("noDomains")) {
46 const MInt testNoDomains = Context::getBasicProperty<MInt>("noDomains", AT_, 0);
47 if(globalNoDomains() < testNoDomains) {
48 // Here, the number of maxNoCells is scaled. This is useful if a test
49 // case is specified to run with a certain number of ranks
50 // ('noDomains' property in run.toml) but needs to be run on a lower
51 // number of mpiranks, p.e. by running maia on an accelerator.
52 m_maxNoCells *= testNoDomains / (MFloat)globalNoDomains();
53 cerr0 << "noDomain > number of used mpi ranks! Therefore, increasing maxNoCells: " << m_maxNoCells << std::endl;
54 }
55 }
56 }
57}
58
59template <MInt nDim>
60Proxy<nDim>::Proxy(const MInt solverId, Grid& grid_)
61 : m_solverId(solverId), m_grid(grid_), m_tree(solverId, grid_.treeb()) {
62 update();
63
64 if(!raw().paraViewPlugin()) {
65 TERMM(1, "This constructor should only be called by the ParaView plugin");
66 }
67}
68
69template <MInt nDim>
71 TRACE();
72
73 // Delete created communicator if existing and if it is not MPI_COMM_WORLD
74 if(m_mpiComm != MPI_COMM_NULL && m_mpiComm != MPI_COMM_WORLD) {
75 MPI_Comm_free(&m_mpiComm, AT_, "m_mpiComm");
76 }
77}
78
79
84template <MInt nDim>
86 TRACE();
87
88 // Update grid map
89 updateGridMap();
90
91 // Update information on parallelization including neighbor domains and window/halo cells
92 updateParallelizationInfo();
93
94 // Perform the remaining steps only if the solver is active on the current domain
95 if(isActive()) {
96 // Update general tree data
97 updateTreeData();
98
99 // Update tree-internal data
100 // empty!
101 m_tree.update(*this);
102
103 // Update other grid-related information
104 updateGridInfo();
105
106 // update cutOff
107 if(!raw().paraViewPlugin()) {
108 updateCutOff();
109 }
110 }
111}
112
113
114template <MInt nDim>
116 TRACE();
117
118 // Update information on parallelization including neighbor domains and window/halo cells
119 updateParallelizationInfo();
120
121 // Perform the remaining steps only if the solver is active on the current domain
122 if(isActive()) {
123 // Update general tree data
124 updateTreeData();
125
126 // Update tree-internal data
127 m_tree.update(*this);
128
129 // Update other grid-related information
130 updateGridInfo();
131
132 // update cutOff
133 updateCutOff();
134 }
135}
136
137template <MInt nDim>
138void Proxy<nDim>::resizeGridMap(const MInt solverSize) {
139 TRACE();
140 m_tree.m_solver2grid.resize(solverSize, -1);
141 m_tree.m_grid2solver.resize(raw().treeb().size() + 1, -1); // +1 is to map "-1" to "-1"
142
143 // ASSERT( m_tree.m_grid2solver[0] == -1, "" );
144 m_tree.m_grid2solver[0] = -1;
145}
146
147
148template <MInt nDim>
150 TRACE();
151 // Properly size maps
153 m_tree.m_solver2grid.assign(raw().treeb().noNodesBySolver(solverId()), -1);
154 } else {
155 // FIXME/TODO labels:GRID Use proper solverId once multiple solvers can use a single grid
156 m_tree.m_solver2grid.assign(raw().treeb().noNodesBySolver(0), -1);
157 }
158 m_tree.m_grid2solver.assign(raw().treeb().size() + 1, -1); // +1 is to map "-1" to "-1"
159 m_tree.m_grid2solver[0] = -1;
160}
161
162template <MInt nDim>
164 TRACE();
165
166 // The grid is unaware of the solver geometry. Therefore, the solver flags
167 // of the azimuthal halo cells need to be corrected in the proxy.
168 if(azimuthalPeriodicity()) correctAzimuthalHaloCells();
169
170 // Properly size maps
172 m_tree.m_solver2grid.assign(raw().treeb().noNodesBySolver(solverId()), -1);
173 } else {
174 // FIXME/TODO labels:GRID Use proper solverId once multiple solvers can use a single grid
175 m_tree.m_solver2grid.assign(raw().treeb().noNodesBySolver(0), -1);
176 }
177 m_tree.m_grid2solver.assign(raw().treeb().size() + 1, -1); // +1 is to map "-1" to "-1"
178
179 // Create forward map (solver -> grid) (only if there are any cells on the present domain)
180 if(m_tree.m_solver2grid.size() > 0) {
182 raw().treeb().nodesBySolver(solverId(), m_tree.m_solver2grid.data());
183 } else {
184 // FIXME/TODO labels:GRID Use proper solverId once multiple solvers can use a single grid
185 raw().treeb().nodesBySolver(0, m_tree.m_solver2grid.data());
186 }
187 }
188
189 // Create reverse map (grid -> solver)
190 for(MInt cellId = 0; cellId < static_cast<MInt>(m_tree.m_solver2grid.size()); cellId++) {
191 const MInt gridCellId = m_tree.m_solver2grid[cellId];
192 m_tree.m_grid2solver[gridCellId + 1] = cellId; // +1 is to map "-1" to "-1"
193 }
194}
195
196
197template <MInt nDim>
199 TRACE();
200
201 // Update internal cell count
202 m_noInternalCells = 0;
203 for(MInt i = 0; i < raw().noInternalCells(); i++) {
204 m_noInternalCells += (m_tree.grid2solver(i) > -1);
205 }
206
207 // Create new MPI communicator (but first delete existing communicator if present)
208 if(m_mpiComm != MPI_COMM_NULL && m_mpiComm != MPI_COMM_WORLD) {
209 MPI_Comm_free(&m_mpiComm, AT_, "m_mpiComm");
210 }
211
212 // Determine number of internal cells on all domain
213 MIntScratchSpace noInternalCells_(raw().noDomains(), AT_, "noInternalCells_");
214 noInternalCells_[raw().domainId()] = noInternalCells();
215 MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, &noInternalCells_[0], 1, type_traits<MInt>::mpiType(),
216 raw().mpiComm(), AT_, "MPI_IN_PLACE", "noInternalCells_[0]");
217
218 // Determine list of subdomains with cells
219 MIntScratchSpace domains(raw().noDomains(), AT_, "domains");
220 MInt noDomains_ = 0;
221 for(MInt d = 0; d < raw().noDomains(); d++) {
222 if(noInternalCells_[d] > 0) {
223 domains[noDomains_] = d;
224 noDomains_++;
225 }
226 }
227
228 // Obtain new group
229 MPI_Group globalGroup, localGroup;
230 MPI_Comm_group(raw().mpiComm(), &globalGroup, AT_, "globalGroup");
231
232 // @ansgar_largeJob debug stack memory usage
233 // writeMemoryStatistics(MPI_COMM_WORLD, globalNoDomains(), globalDomainId(), AT_, "grid proxy before
234 // MPI_Group_incl");
235
236 // NOTE: allocates large memory chunk on stack on O(100000) ranks (at least on Hawk, 12/21) and scales linear!
237 MPI_Group_incl(globalGroup, noDomains_, &domains[0], &localGroup, AT_);
238
239 // @ansgar_largeJob debug stack memory usage
240 // writeMemoryStatistics(MPI_COMM_WORLD, globalNoDomains(), globalDomainId(), AT_, "grid proxy after MPI_Group_incl");
241
242 // Create new communicator and clean up
243 MPI_Comm_create(raw().mpiComm(), localGroup, &m_mpiComm, AT_, "m_mpiComm");
244
245 MPI_Group_free(&globalGroup, AT_);
246 MPI_Group_free(&localGroup, AT_);
247
248 if(noDomains_ == raw().noDomains()) {
249 m_hasInactiveRanks = false;
250 } else {
251 m_hasInactiveRanks = true;
252 }
253
254 // If solver is active on current domain, update domain info, otherwise return early.
255 if(noInternalCells() > 0) {
256 // Store new domain id and number of domains in communicator
257 MPI_Comm_rank(mpiComm(), &m_domainId);
258 MPI_Comm_size(mpiComm(), &m_noDomains);
259
260 if(noDomains() != noDomains_) {
261 TERMM(1, "an error occurred while creating the solver-specific MPI communicator");
262 }
263
264#ifndef NDEBUG
265 if(g_multiSolverGrid && domainId() == 0 && noDomains() < 50) {
266 cout << "Solver " << solverId() << ": Global domain " << globalDomainId()
267 << " is the root domain of a new solver-specific communicator with the following " << noDomains_
268 << " domain(s): " << domains[0];
269 for(MInt i = 1; i < noDomains_; i++) {
270 cout << ", " << domains[i];
271 }
272 cout << endl;
273 }
274#endif
275
276 // Mark domain as active
277 m_isActive = true;
278 } else {
279 // If there are no local internal cells, mark this domain as inactive and reset domain info
280 m_isActive = false;
281 m_domainId = -1;
282 m_noDomains = -1;
283 m_noInternalCells = -1;
284 std::vector<MLong>().swap(m_domainOffsets);
285 std::vector<MInt>().swap(m_neighborDomains);
286 std::vector<MInt>().swap(m_neighborDomainIndex);
287
288 std::vector<std::vector<MInt>>().swap(m_windowCells);
289 std::vector<std::vector<MInt>>().swap(m_haloCells);
290 std::map<MInt, MInt>().swap(m_global2solver);
291 m_maxLevel = -1;
292
293 return;
294 }
295
296 // Determine domain offsets (last entry contains global number of cells)
297 m_domainOffsets.assign(noDomains() + 1, -1);
298 // m_domainOffsets[0] = 0;
299 m_domainOffsets[0] = bitOffset();
300 for(MInt d = 1; d < noDomains() + 1; d++) {
301 m_domainOffsets[d] = m_domainOffsets[d - 1] + (MLong)noInternalCells_[domains[d - 1]];
302 }
303
304#ifndef NDEBUG
305 // Sanity check: domain offsets are the same on all domains
306 checkOffsetConsistency();
307#endif
308
309 // Create map from global to solver domain ids
310 std::map<MInt, MInt>().swap(m_global2solver); // Clear map first
311 for(MInt d = 0; d < noDomains(); d++) {
312 m_global2solver[domains[d]] = d;
313 }
314
315 if(!g_multiSolverGrid) {
316 for(MInt d = 0; d < noDomains(); d++) {
317 ASSERT(m_global2solver[d] == d, "");
318 }
319 }
320
321 // Determine neighbor domains
322 // Map from old position to new position
323 std::map<MInt, MInt> neighborDomains;
324 for(MInt d = 0; d < raw().noNeighborDomains(); d++) {
325 // Skip domain if it is not among solver-specific domains
326 if(m_global2solver.count(raw().neighborDomain(d)) == 0) {
327 continue;
328 }
329
330 // Check in window cells
331 for(MInt c = 0; c < raw().noWindowCells(d); c++) {
332 if((raw().haloMode() == 0 && m_tree.grid2solver(raw().windowCell(d, c)) > -1)
333 || (raw().haloMode() > 0 && m_tree.grid2solver(raw().windowCell(d, c)) > -1
334 && raw().isSolverWindowCell(d, raw().windowCell(d, c), solverId()))) { // WH_old
335 const MInt position = neighborDomains.size();
336 neighborDomains[d] = position;
337 break;
338 }
339 }
340
341 // Continue early if domain already found
342 if(neighborDomains.count(d) > 0) {
343 continue;
344 }
345
346 // Check in halo cells
347 for(MInt c = 0; c < raw().noHaloCells(d); c++) {
348 if(m_tree.grid2solver(raw().haloCell(d, c)) > -1) {
349 const MInt position = neighborDomains.size();
350 neighborDomains[d] = position;
351 break;
352 }
353 }
354 }
355 m_neighborDomains.assign(neighborDomains.size(), -1);
356 m_neighborDomainIndex.assign(noDomains(), -1);
357 for(auto&& m : neighborDomains) {
358 // m.second holds new position, m.first holds position in the global neighbor domain arrays
359 m_neighborDomains[m.second] = raw().neighborDomain(m.first);
360 }
361
362 // Update list of neighbor domain ids using a map from global domain id to solver domain id
363 for(MInt d = 0; d < noNeighborDomains(); d++) {
364 m_neighborDomains[d] = m_global2solver[m_neighborDomains[d]];
365 m_neighborDomainIndex[m_neighborDomains[d]] = d;
366 }
367
368#ifndef NDEBUG
369 // Sanity check: neighbor domains are consistent on all domains
370 checkNeighborConsistency();
371#endif
372
373 setupWindowHaloConnectivityOnLeafLvl(neighborDomains);
374
375#ifndef NDEBUG
376 // Sanity check: window and halo cells are consistent on all domains
377 checkWindowHaloConsistency();
378#endif
379
380
381 // Setup azimuthal periodic connection
382 m_azimuthalNeighborDomains.clear();
383 if(azimuthalPeriodicity()) {
384 neighborDomains.clear();
385
386 MInt windowCnt = 0;
387 MInt haloCnt = 0;
388 MInt tmpCnt = 0;
389 for(MInt d = 0; d < raw().noAzimuthalNeighborDomains(); d++) {
390 // Skip domain if it is not among solver-specific domains
391 if(m_global2solver.count(raw().azimuthalNeighborDomain(d)) == 0) {
392 windowCnt += raw().noAzimuthalWindowCells(d);
393 haloCnt += raw().noAzimuthalHaloCells(d);
394 continue;
395 }
396
397 // Check in window cells
398 tmpCnt = windowCnt;
399 for(MInt c = 0; c < raw().noAzimuthalWindowCells(d); c++) {
400 if(m_tree.grid2solver(raw().azimuthalWindowCell(d, c)) > -1 && m_isOutsideWindow[tmpCnt] == false) {
401 const MInt position = neighborDomains.size();
402 neighborDomains[d] = position;
403 break;
404 }
405 tmpCnt++;
406 }
407 windowCnt += raw().noAzimuthalWindowCells(d);
408
409 // Continue early if domain already found
410 if(neighborDomains.count(d) > 0) {
411 haloCnt += raw().noAzimuthalHaloCells(d);
412 continue;
413 }
414
415 // Check in halo cells
416 tmpCnt = haloCnt;
417 for(MInt c = 0; c < raw().noAzimuthalHaloCells(d); c++) {
418 if(m_tree.grid2solver(raw().azimuthalHaloCell(d, c)) > -1 && m_isOutsideHalo[tmpCnt] == false) {
419 const MInt position = neighborDomains.size();
420 neighborDomains[d] = position;
421 break;
422 }
423 tmpCnt++;
424 }
425 haloCnt += raw().noAzimuthalHaloCells(d);
426 }
427 m_azimuthalNeighborDomains.assign(neighborDomains.size(), -1);
428 m_azimuthalNeighborDomainIndex.assign(noDomains(), -1);
429 for(auto&& m : neighborDomains) {
430 // m.second holds new position, m.first holds position in the global neighbor domain arrays
431 m_azimuthalNeighborDomains[m.second] = raw().azimuthalNeighborDomain(m.first);
432 }
433
434 // Update list of neighbor domain ids using a map from global domain id to solver domain id
435 for(MInt d = 0; d < noAzimuthalNeighborDomains(); d++) {
436 m_azimuthalNeighborDomains[d] = m_global2solver[m_azimuthalNeighborDomains[d]];
437 m_azimuthalNeighborDomainIndex[m_azimuthalNeighborDomains[d]] = d;
438 }
439
440#ifndef NDEBUG
441 // Sanity check: neighbor domains are consistent on all domains
442 checkNeighborConsistencyAzimuthal();
443#endif
444
445 m_azimuthalWindowCells.clear();
446 m_azimuthalWindowCells.resize(noAzimuthalNeighborDomains());
447 m_azimuthalHaloCells.clear();
448 m_azimuthalHaloCells.resize(noAzimuthalNeighborDomains());
449 m_azimuthalUnmappedHaloCells.clear();
450 m_azimuthalUnmappedHaloDomains.clear();
451
452 windowCnt = 0;
453 haloCnt = 0;
454 tmpCnt = 0;
455 for(MInt d = 0; d < raw().noAzimuthalNeighborDomains(); d++) {
456 if(m_global2solver.count(raw().azimuthalNeighborDomain(d)) == 0) {
457 windowCnt += raw().noAzimuthalWindowCells(d);
458 haloCnt += raw().noAzimuthalHaloCells(d);
459 continue;
460 }
461
462 // Store old and new position for convenience
463 const MInt oldDomainPosition = d;
464 const MInt newDomainPosition = m_azimuthalNeighborDomainIndex[m_global2solver[raw().azimuthalNeighborDomain(d)]];
465 if(newDomainPosition < 0) {
466 windowCnt += raw().noAzimuthalWindowCells(d);
467 haloCnt += raw().noAzimuthalHaloCells(d);
468 continue;
469 }
470
471 // Determine window cells
472 tmpCnt = windowCnt;
473 std::vector<MInt> windowCells;
474 windowCells.reserve(raw().noAzimuthalWindowCells(oldDomainPosition));
475 for(MInt c = 0; c < raw().noAzimuthalWindowCells(oldDomainPosition); c++) {
476 const MInt windowCellId = m_tree.grid2solver(raw().azimuthalWindowCell(oldDomainPosition, c));
477 if(windowCellId > -1 && m_isOutsideWindow[tmpCnt] == false) {
478 windowCells.push_back(windowCellId);
479 }
480 tmpCnt++;
481 }
482 m_azimuthalWindowCells[newDomainPosition] = windowCells;
483 windowCnt += raw().noAzimuthalWindowCells(d);
484
485 // Determine halo cells
486 tmpCnt = haloCnt;
487 std::vector<MInt> haloCells;
488 haloCells.reserve(raw().noAzimuthalHaloCells(oldDomainPosition));
489 for(MInt c = 0; c < raw().noAzimuthalHaloCells(oldDomainPosition); c++) {
490 const MInt haloCellId = m_tree.grid2solver(raw().azimuthalHaloCell(oldDomainPosition, c));
491 if(haloCellId > -1) {
492 if(m_isOutsideHalo[tmpCnt] == false) {
493 haloCells.push_back(haloCellId);
494 } else {
495 m_azimuthalUnmappedHaloCells.push_back(haloCellId);
496 m_azimuthalUnmappedHaloDomains.push_back(m_global2solver[raw().azimuthalNeighborDomain(d)]);
497 }
498 }
499 tmpCnt++;
500 }
501 m_azimuthalHaloCells[newDomainPosition] = haloCells;
502 haloCnt += raw().noAzimuthalHaloCells(d);
503 }
504
505 for(MInt c = 0; c < raw().noAzimuthalUnmappedHaloCells(); c++) {
506 const MInt haloCellId = m_tree.grid2solver(raw().azimuthalUnmappedHaloCell(c));
507 if(haloCellId > -1) {
508 m_azimuthalUnmappedHaloCells.push_back(haloCellId);
509 m_azimuthalUnmappedHaloDomains.push_back(m_global2solver[raw().azimuthalUnmappedHaloDomain(c)]);
510 }
511 }
512
513#ifndef NDEBUG
514 // Sanity check: window and halo cells are consistent on all domains
515 checkWindowHaloConsistencyAzimuthal();
516#endif
517 }
518
519 // The following output may be helpful until the multi-solver capabilities are sufficiently tested
520 // stringstream ss;
521 // ss << "---------------------------------------------------------------------------------------"
522 // << endl;
523 // ss << solverId() << " " << domainId() << " Window/halo information" << endl;
524 // ss << solverId() << " " << domainId() << " =======================" << endl;
525 // ss << solverId() << " " << domainId() << " noNeighborDomains(): " << noNeighborDomains()
526 // << endl;
527 // ss << solverId() << " " << domainId() << " neighbor domains:";
528 // for (MInt i = 0; i < noNeighborDomains(); i++) {
529 // ss << " " << neighborDomain(i);
530 // }
531 // ss << endl;
532 // for (MInt i = 0; i < noNeighborDomains(); i++) {
533 // // Window cells
534 // ss << solverId() << " " << domainId() << " has " << noWindowCells(i)
535 // << " window cells for neighbor " << i << " (domain id: " << neighborDomain(i) << "):";
536 // for (MInt j = 0; j < noWindowCells(i); j++) {
537 // ss << " " << raw().treeb().globalId(m_tree.solver2grid(windowCell(i, j)));
538 // }
539 // ss << endl;
540
541 // // Halo cells
542 // ss << solverId() << " " << domainId() << " has " << noHaloCells(i)
543 // << " halo cells for neighbor " << i << " (domain id: " << neighborDomain(i) << "):";
544 // for (MInt j = 0; j < noHaloCells(i); j++) {
545 // ss << " " << raw().treeb().globalId(m_tree.solver2grid(haloCell(i, j)));
546 // }
547 // ss << endl;
548 // }
549 // ss << "---------------------------------------------------------------------------------------"
550 // << endl;
551 // cout << ss.str() << endl;
552 // MInt totalHaloCells = 0;
553 // for (MInt i = 0; i < noNeighborDomains(); i++)
554 // totalHaloCells += noHaloCells(i);
555 // MPI_Allreduce(MPI_IN_PLACE, &totalHaloCells, 1, type_traits<MInt>::mpiType(), MPI_SUM, mpiComm(), AT_,
556 // "MPI_IN_PLACE", "totalHaloCells");
557 // if (domainId()==0)
558 // cout << " totalNoHaloCells of solver=" << solverId() << ": " << totalHaloCells << endl;
559}
560
561
562template <MInt nDim>
564 TRACE();
565
566 // reset info
567 std::vector<std::vector<MInt>>().swap(m_leafWindowCells);
568 m_leafWindowCells.resize(noNeighborDomains());
569 std::vector<std::vector<MInt>>().swap(m_leafHaloCells);
570 m_leafHaloCells.resize(noNeighborDomains());
571
572 m_leafRecvSize = 0;
573 m_leafSendSize = 0;
574
575 std::vector<MInt>().swap(m_leafSendNeighborDomains);
576 std::vector<MInt>().swap(m_leafRecvNeighborDomains);
577
578 if(noNeighborDomains() == 0) {
579 return;
580 }
581 // meaning only active-rangs for parallel computation
582
583 ScratchSpace<MPI_Request> sendReq(noNeighborDomains(), AT_, "sendReq");
584 ScratchSpace<MPI_Request> recvReq(noNeighborDomains(), AT_, "recvReq");
585 sendReq.fill(MPI_REQUEST_NULL);
586 recvReq.fill(MPI_REQUEST_NULL);
587
588 MInt noHaloSums = 0;
589 MInt noWindowSums = 0;
590 for(MInt d = 0; d < noNeighborDomains(); d++) {
591 noHaloSums += noHaloCells(d);
592 noWindowSums += noWindowCells(d);
593 }
594
595 MIntScratchSpace sendBuffer(noHaloSums, AT_, "sendBuffer");
596 sendBuffer.fill(-1);
597 MIntScratchSpace receiveBuffer(noWindowSums, AT_, "receiveBuffer");
598 receiveBuffer.fill(-1);
599
600 // set leaf-cell property for halos
601 MInt haloId = 0;
602 for(MInt i = 0; i < noNeighborDomains(); i++) {
603 std::vector<MInt> leafHaloCells;
604 leafHaloCells.reserve(noHaloCells(i));
605
606 for(MInt j = 0; j < noHaloCells(i); j++) {
607 const MInt cellId = haloCell(i, j);
608 if(!tree().isLeafCell(cellId)) {
609 haloId++;
610 continue;
611 }
612 leafHaloCells.push_back(cellId);
613 sendBuffer[haloId++] = cellId;
614 }
615 m_leafHaloCells[i] = leafHaloCells;
616 if(!leafHaloCells.empty()) {
617 m_leafRecvNeighborDomains.push_back(i);
618 }
619 }
620
621 // sending from halo -> window
622 MInt sendOffset = 0;
623 MInt sendCnt = 0;
624 for(MInt i = 0; i < noNeighborDomains(); i++) {
625 const MInt bufSize = noHaloCells(i);
626 if(bufSize == 0) {
627 continue;
628 }
629 MPI_Issend(&sendBuffer[sendOffset], bufSize, MPI_INT, neighborDomain(i), 0, mpiComm(), &sendReq[i], AT_,
630 "sendBuffer[sendOffset]");
631 sendOffset += bufSize;
632 sendCnt++;
633 }
634
635 MInt receiveOffset = 0;
636 for(MInt i = 0; i < noNeighborDomains(); i++) {
637 const MInt bufSize = noWindowCells(i);
638 if(bufSize == 0) {
639 continue;
640 }
641 MPI_Recv(&receiveBuffer[receiveOffset], bufSize, MPI_INT, neighborDomain(i), 0, mpiComm(), MPI_STATUS_IGNORE, AT_,
642 "receiveBuffer[receiveOffset]");
643 receiveOffset += bufSize;
644 }
645 if(sendCnt > 0) {
646 MPI_Waitall(noNeighborDomains(), &sendReq[0], MPI_STATUSES_IGNORE, AT_);
647 }
648
649 // write window cell information
650 MInt windowId = 0;
651 for(MInt i = 0; i < noNeighborDomains(); i++) {
652 std::vector<MInt> leafWindowCells;
653 leafWindowCells.reserve(noWindowCells(i));
654
655 for(MInt j = 0; j < noWindowCells(i); j++) {
656 if(receiveBuffer[windowId++] != -1) {
657 ASSERT(windowCell(i, j) > -1, "");
658 leafWindowCells.push_back(windowCell(i, j));
659 }
660 }
661 m_leafWindowCells[i] = leafWindowCells;
662 if(!leafWindowCells.empty()) {
663 m_leafSendNeighborDomains.push_back(i);
664 }
665 }
666
667 // reduce neighbor-domains for leaf-cell communication
668 for(MInt n = 0; n < noLeafRecvNeighborDomains(); n++) {
669 const MInt d = leafRecvNeighborDomain(n);
670 m_leafRecvSize += noLeafHaloCells(d);
671 }
672
673 for(MInt n = 0; n < noLeafSendNeighborDomains(); n++) {
674 const MInt d = leafSendNeighborDomain(n);
675 m_leafSendSize += noLeafWindowCells(d);
676 }
677}
678
679
684template <MInt nDim>
686 TRACE();
687
688 // Determine solver-specific global ids for internal cells
689 m_tree.m_globalIds.assign(noCells(), -1);
690
691 MInt localCnt = 0;
692 const MInt firstGridMinCell = raw().minCell(0);
693
694 // Partition level shift: check if the first min-cell is a partition level ancestor halo min-cell
695 // (i.e. located on another domain and the first local partition cell corresponding to the
696 // domain-offset is an offspring of this min-cell) (similar to
697 // cartesiangrid::computeGlobalIds())
698 if(raw().a_hasProperty(firstGridMinCell, Cell::IsPartLvlAncestor)
699 && raw().a_hasProperty(firstGridMinCell, Cell::IsHalo)) {
700 descendStoreGlobalId(firstGridMinCell, localCnt);
701 }
702
703 // Loop over all min-level cells, start with min-cell at position 1 if there is a partition level
704 // shift handled by the case above
705 for(MInt i = std::max(0, std::min(1, localCnt)); i < raw().noMinCells(); i++) {
706 const MInt gridCellId = raw().minCell(i);
707
708 // Skip min-level cells not belonging to this solver or if its a halo
709 if(!solverFlag(gridCellId, solverId())) continue;
710 if(raw().a_hasProperty(gridCellId, Cell::IsHalo)) continue;
711 descendStoreGlobalId(gridCellId, localCnt);
712 }
713
714 // Determine solver-specific global ids for halo cells
715 if(m_neighborDomains.size() > 0) {
716 mpi::exchangeData(m_neighborDomains, m_haloCells, m_windowCells, mpiComm(), m_tree.m_globalIds.data());
717 }
718 if(m_azimuthalNeighborDomains.size() > 0) {
719 mpi::exchangeData(m_azimuthalNeighborDomains, m_azimuthalHaloCells, m_azimuthalWindowCells, mpiComm(),
720 m_tree.m_globalIds.data());
721 }
722
723 // If this is a single solver case the determined global ids have to match the ones in the grid
724 // Note: this check was not suitable for a targetGridMinLevel != minLevel because the min-level
725 // cell hilbert ids were not determined correctly such that the ordering was wrong
726 if(!g_multiSolverGrid) { // && raw().targetGridMinLevel() == minLevel()) {
727 for(MInt i = 0; i < noInternalCells(); i++) {
728 ASSERT(m_tree.m_globalIds[i] == raw().a_globalId(i),
729 std::to_string(domainId()) + " globalId mismatch: " + std::to_string(i) + ", "
730 + std::to_string(m_tree.m_globalIds[i]) + " != " + std::to_string(raw().a_globalId(i)));
731 }
732 }
733
734 m_tree.createGlobalToLocalIdMapping();
735}
736
737
742template <MInt nDim>
743void Proxy<nDim>::descendStoreGlobalId(MInt gridCellId, MInt& localCnt) {
744 if(!solverFlag(gridCellId, solverId())) return;
745
746 // Update global id (unless it is a halo cell)
747 if(!raw().a_hasProperty(gridCellId, Cell::IsHalo)) {
748 ASSERT(solverFlag(gridCellId, solverId()), "");
749 ASSERT(localCnt < noInternalCells(), "");
750
751 const MInt solverCellId = m_tree.grid2solver(gridCellId);
752 m_tree.m_globalIds[solverCellId] = m_domainOffsets[domainId()] + localCnt++;
753
754 ASSERT(m_tree.m_globalIds[solverCellId] >= m_domainOffsets[domainId()]
755 && m_tree.m_globalIds[solverCellId] < m_domainOffsets[domainId() + 1]
756 && m_tree.m_globalIds[solverCellId] < m_domainOffsets[domainId()] + noInternalCells(),
757 "");
758 }
759
760 // Descend tree to all children that belong to this solver
761 for(MInt child = 0; child < ipow(2, nDim); child++) {
762 if(raw().a_childId(gridCellId, child) < 0) continue;
763 if(!solverFlag(gridCellId, solverId())) continue;
764 descendStoreGlobalId(raw().a_childId(gridCellId, child), localCnt);
765 }
766}
767
772template <MInt nDim>
774 const MInt domain = raw().findNeighborDomainId(globalId, noDomains(), &m_domainOffsets[0]);
775 if(!g_multiSolverGrid) {
776 ASSERT(domain == raw().findNeighborDomainId(globalId), "");
777 }
778 return domain;
779}
780
781
786template <MInt nDim>
788 TRACE();
789
790 // Update maxLevel
791 m_maxLevel = -1;
792
793 for(MInt i = 0; i < noInternalCells(); i++) {
794 m_maxLevel = max(m_maxLevel, m_tree.level(i));
795 }
796 MPI_Allreduce(MPI_IN_PLACE, &m_maxLevel, 1, type_traits<MInt>::mpiType(), MPI_MAX, mpiComm(), AT_, "MPI_IN_PLACE",
797 "m_maxLevel");
798
799 if(minLevel() < 0 || minLevel() > m_maxLevel) {
800 mTerm(1, AT_, "Inconsistent min/max levels: " + to_string(minLevel()) + "/" + to_string(m_maxLevel));
801 }
802}
803
804
815template <MInt nDim>
817 TRACE();
818
819 // Check if first offset is zero
820 if(m_domainOffsets[0] != bitOffset()) {
821 TERMM(1, "Domain offset for domain 0 must be equal to the 32-Bit-Offset (0)!");
822 }
823
824 // Check if offsets are strictly monotonically increasing
825 for(MInt i = 1; i < noDomains() + 1; i++) {
826 if(m_domainOffsets[i] <= m_domainOffsets[i - 1]) {
827 TERMM(1, "Domain offsets are not strictly monotonically increasing");
828 }
829 }
830
831 // Check if offsets are the same on all domains
832 MLongScratchSpace rootOffsets(noDomains() + 1, AT_, "rootOffsets");
833 copy(m_domainOffsets.begin(), m_domainOffsets.end(), rootOffsets.begin());
834 MPI_Bcast(&rootOffsets[0], noDomains() + 1, type_traits<MLong>::mpiType(), 0, mpiComm(), AT_, "rootOffsets[0]");
835 for(MInt i = 0; i < noDomains() + 1; i++) {
836 if(m_domainOffsets[i] != rootOffsets[i]) {
837 TERMM(1, "Domain offsets differ from root domain");
838 }
839 }
840
841 if(!g_multiSolverGrid) {
842 for(MInt i = 1; i < noDomains() + 1; i++) {
843 ASSERT(m_domainOffsets[i] == raw().domainOffset(i), "");
844 }
845 }
846}
847
848
858template <MInt nDim>
860 TRACE();
861
862 // Check if neighbor domains are sorted ascendingly
863 if(!std::is_sorted(m_neighborDomains.begin(), m_neighborDomains.end())) {
864 TERMM(1, "Neighbor domains are not sorted in ascending order.");
865 }
866
867 // Determine all neighbor domains of current domain
868 MCharScratchSpace isNeighborDomain(noDomains(), AT_, "isNeighborDomain");
869 fill(isNeighborDomain.begin(), isNeighborDomain.end(), 0);
870 for(MInt d = 0; d < noNeighborDomains(); d++) {
871 isNeighborDomain[neighborDomain(d)] = 1;
872 }
873
874 // Exchange with all domains in solver-local communicator
875 MPI_Alltoall(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, &isNeighborDomain[0], 1, type_traits<MChar>::mpiType(), mpiComm(),
876 AT_, "MPI_IN_PLACE", "isNeighborDomain[0]");
877
878 // Check if all domains that the current domain considers as neighbors also reciprocate
879 for(MInt d = 0; d < noNeighborDomains(); d++) {
880 if(!isNeighborDomain[neighborDomain(d)]) {
881 TERMM(1, "Domain " + to_string(domainId()) + " has domain " + to_string(neighborDomain(d))
882 + " as a neighbor but not the other way around.");
883 }
884 }
885}
886
887
893template <MInt nDim>
894void Proxy<nDim>::setupWindowHaloConnectivityOnLeafLvl(std::map<MInt, MInt>& neighborDomains) {
895 TRACE();
896
897 std::vector<std::vector<MInt>>().swap(m_windowCells);
898 std::vector<std::vector<MInt>>().swap(m_haloCells);
899
900 // WH_old
901 if(raw().haloMode() > 0) {
902 // Determine window/halo cells
903 m_windowCells.resize(noNeighborDomains());
904 m_haloCells.resize(noNeighborDomains());
905 for(auto&& m : neighborDomains) {
906 // Store old and new position for convenience
907 const MInt oldDomainPosition = m.first;
908 const MInt newDomainPosition = m.second;
909
910
911 // Determine window cells
912 std::vector<MInt> windowCells;
913 windowCells.reserve(raw().noWindowCells(oldDomainPosition));
914 for(MInt c = 0; c < raw().noWindowCells(oldDomainPosition); c++) {
915 const MInt windowCellId = m_tree.grid2solver(raw().windowCell(oldDomainPosition, c));
916 if(windowCellId > -1) {
917 ASSERT(m_tree.solver2grid(windowCellId) == raw().windowCell(oldDomainPosition, c),
918 std::to_string(raw().windowCell(oldDomainPosition, c)) + "|" + std::to_string(windowCellId) + "|"
919 + std::to_string(m_tree.solver2grid(windowCellId)));
920 if(raw().isSolverWindowCell(oldDomainPosition, raw().windowCell(oldDomainPosition, c), solverId())) {
921 windowCells.push_back(windowCellId);
922 }
923 }
924 }
925 m_windowCells[newDomainPosition] = windowCells;
926 // Check won't work in case of periodicity
927 // TERMM_IF_NOT_COND((signed)m_windowCells[newDomainPosition].size()==raw().noSolverWindowCells(oldDomainPosition,m_solverId),
928 // std::to_string((signed)m_windowCells[newDomainPosition].size()) + " " +
929 // std::to_string(raw().noSolverWindowCells(oldDomainPosition,m_solverId)));
930
931 // Determine halo cells
932 std::vector<MInt> haloCells;
933 haloCells.reserve(raw().noHaloCells(oldDomainPosition));
934 for(MInt c = 0; c < raw().noHaloCells(oldDomainPosition); c++) {
935 const MInt haloCellId = m_tree.grid2solver(raw().haloCell(oldDomainPosition, c));
936 ASSERT((haloCellId > -1) == raw().a_solver(raw().haloCell(oldDomainPosition, c), m_solverId),
937 "Shut down your PC!");
938 // TODO_SS labels:GRID the last check is currently necessary because of tagActiveWindowsOnLeafLvl
939 if(haloCellId > -1 && raw().a_solver(raw().haloCell(oldDomainPosition, c), m_solverId)) {
940 ASSERT(m_tree.solver2grid(haloCellId) == raw().haloCell(oldDomainPosition, c), "");
941 haloCells.push_back(haloCellId);
942 }
943 }
944 m_haloCells[newDomainPosition] = haloCells;
945 ASSERT((signed)m_haloCells[newDomainPosition].size()
946 == raw().noSolverHaloCells(oldDomainPosition, m_solverId, true),
947 std::to_string(m_haloCells[newDomainPosition].size()) + " "
948 + std::to_string(raw().noSolverHaloCells(oldDomainPosition, m_solverId, true)));
949 }
950 } else {
951 // Determine window/halo cells
952 m_windowCells.resize(noNeighborDomains());
953 m_haloCells.resize(noNeighborDomains());
954 for(auto&& m : neighborDomains) {
955 // Store old and new position for convenience
956 const MInt oldDomainPosition = m.first;
957 const MInt newDomainPosition = m.second;
958
959
960 // Determine window cells
961 std::vector<MInt> windowCells;
962 windowCells.reserve(raw().noWindowCells(oldDomainPosition));
963 for(MInt c = 0; c < raw().noWindowCells(oldDomainPosition); c++) {
964 const MInt windowCellId = m_tree.grid2solver(raw().windowCell(oldDomainPosition, c));
965 if(windowCellId > -1) {
966 windowCells.push_back(windowCellId);
967 }
968 }
969 m_windowCells[newDomainPosition] = windowCells;
970
971 // Determine halo cells
972 std::vector<MInt> haloCells;
973 haloCells.reserve(raw().noHaloCells(oldDomainPosition));
974 for(MInt c = 0; c < raw().noHaloCells(oldDomainPosition); c++) {
975 const MInt haloCellId = m_tree.grid2solver(raw().haloCell(oldDomainPosition, c));
976 if(haloCellId > -1) {
977 haloCells.push_back(haloCellId);
978 }
979 }
980 m_haloCells[newDomainPosition] = haloCells;
981 }
982 }
983}
984
985
994template <MInt nDim>
996 TRACE();
997 if(globalNoDomains() == 1) {
998 return;
999 }
1000
1002 // Check #1: number of window/halo cells match
1004 // Start receiving number of window cells from each neighbor domain
1005 ScratchSpace<MPI_Request> recvRequests(max(1, noNeighborDomains()), AT_, "recvRequests");
1006 MIntScratchSpace noWindowCellsRecv(max(1, noNeighborDomains()), AT_, "noWindowCellsRecv");
1007 for(MInt d = 0; d < noNeighborDomains(); d++) {
1008 noWindowCellsRecv[d] = -1;
1009 MPI_Irecv(&noWindowCellsRecv[d], 1, type_traits<MInt>::mpiType(), neighborDomain(d), neighborDomain(d), mpiComm(),
1010 &recvRequests[d], AT_, "noWindowCellsRecv[d]");
1011 }
1012
1013 // Start sending number of window cells to each neighbor domain
1014 ScratchSpace<MPI_Request> sendRequests(max(1, noNeighborDomains()), AT_, "sendRequests");
1015 MIntScratchSpace noWindowCellsSend(max(1, noNeighborDomains()), AT_, "noWindowCellsSend");
1016 for(MInt d = 0; d < noNeighborDomains(); d++) {
1017 noWindowCellsSend[d] = noWindowCells(d);
1018 MPI_Isend(&noWindowCellsSend[d], 1, type_traits<MInt>::mpiType(), neighborDomain(d), domainId(), mpiComm(),
1019 &sendRequests[d], AT_, "noWindowCellsSend[d]");
1020 }
1021
1022 // Finish MPI communication
1023 MPI_Waitall(noNeighborDomains(), &recvRequests[0], MPI_STATUSES_IGNORE, AT_);
1024 MPI_Waitall(noNeighborDomains(), &sendRequests[0], MPI_STATUSES_IGNORE, AT_);
1025
1026 // Check if received number of window cells matches the local number of halo cells
1027 for(MInt d = 0; d < noNeighborDomains(); d++) {
1028 if(noWindowCellsRecv[d] != noHaloCells(d)) {
1029 TERMM(1, "Solver " + to_string(m_solverId) + " d=" + to_string(domainId())
1030 + ": Number of window cells from domain " + to_string(neighborDomain(d))
1031 + " does not match local number of halo cells; window: " + to_string(noWindowCellsRecv[d])
1032 + " ,halo: " + to_string(noHaloCells(d)));
1033 }
1034 }
1035
1037 // Check #2: grid global ids of window/halo cells match
1039 // Start receiving window cell global ids from each neighbor domain
1040 fill(recvRequests.begin(), recvRequests.end(), MPI_REQUEST_NULL);
1041 const MInt totalNoWindowCellsRecv = accumulate(noWindowCellsRecv.begin(), noWindowCellsRecv.end(), 0);
1042 MIntScratchSpace windowCellsRecv(max(1, totalNoWindowCellsRecv), AT_, "windowCellsRecv");
1043 fill(windowCellsRecv.begin(), windowCellsRecv.end(), -1);
1044 for(MInt d = 0, offset = 0; d < noNeighborDomains(); d++) {
1045 if(noHaloCells(d) > 0) {
1046 MPI_Irecv(&windowCellsRecv[offset], noHaloCells(d), type_traits<MInt>::mpiType(), neighborDomain(d),
1047 neighborDomain(d), mpiComm(), &recvRequests[d], AT_, "windowCellsRecv[offset]");
1048 }
1049 offset += noHaloCells(d);
1050 }
1051
1052 // Start sending window cell global ids to each neighbor domain
1053 fill(sendRequests.begin(), sendRequests.end(), MPI_REQUEST_NULL);
1054 const MInt totalNoWindowCellsSend = accumulate(noWindowCellsSend.begin(), noWindowCellsSend.end(), 0);
1055 MIntScratchSpace windowCellsSend(max(1, totalNoWindowCellsSend), AT_, "windowCellsSend");
1056 for(MInt d = 0, offset = 0; d < noNeighborDomains(); d++) {
1057 for(MInt c = 0; c < noWindowCellsSend[d]; c++) {
1058 windowCellsSend[offset + c] = raw().treeb().globalId(m_tree.solver2grid(windowCell(d, c)));
1059 }
1060 if(noWindowCells(d) > 0) {
1061 MPI_Isend(&windowCellsSend[offset], noWindowCells(d), type_traits<MInt>::mpiType(), neighborDomain(d), domainId(),
1062 mpiComm(), &sendRequests[d], AT_, "windowCellsSend[offset]");
1063 }
1064 offset += noWindowCells(d);
1065 }
1066
1067 // Finish MPI communication
1068 MPI_Waitall(noNeighborDomains(), &recvRequests[0], MPI_STATUSES_IGNORE, AT_);
1069 MPI_Waitall(noNeighborDomains(), &sendRequests[0], MPI_STATUSES_IGNORE, AT_);
1070
1071 // Check if received window cell global ids match the local halo cell global ids
1072 for(MInt d = 0, offset = 0; d < noNeighborDomains(); d++) {
1073 for(MInt c = 0; c < noHaloCells(d); c++) {
1074 const MInt cellId = m_tree.solver2grid(haloCell(d, c));
1075 const MInt globalId = raw().treeb().globalId(cellId);
1076 // If halo cell has periodic flag, its global id should be -1
1077 if(windowCellsRecv[offset + c] != globalId
1078 && !(raw().treeb().hasProperty(cellId, Cell::IsPeriodic) && globalId == -1)) {
1079 TERMM(1, "Global id of window cell " + to_string(c) + " from domain " + to_string(neighborDomain(d))
1080 + " does not match local halo cell gobal id (" + to_string(windowCellsRecv[offset + c]) + " vs. "
1081 + to_string(raw().treeb().globalId(m_tree.solver2grid(haloCell(d, c)))) + ")");
1082 }
1083 }
1084 offset += noHaloCells(d);
1085 }
1086}
1087
1088
1094template <MInt nDim>
1096 const MInt* const polyDegs,
1097 const MInt* const dataOffsets) {
1098 std::vector<MInt> dataSizeSend(noNeighborDomains(), 0);
1099 std::vector<MInt> dataSizeRecv(noNeighborDomains(), 0);
1100 MInt totalSendSize = 0;
1101 MInt totalRecvSize = 0;
1102
1103 for(MInt nghbrId = 0; nghbrId < noNeighborDomains(); nghbrId++) {
1104 const MInt noWindowSend = noWindowCells(nghbrId);
1105 const MInt noHaloRecv = noHaloCells(nghbrId);
1106
1107 // Determine number of window cell DOFs
1108 MInt noDOFsSend = 0;
1109 for(MInt i = 0; i < noWindowSend; i++) {
1110 noDOFsSend += ipow(polyDegs[windowCell(nghbrId, i)] + 1, nDim);
1111 }
1112 dataSizeSend[nghbrId] = noDOFsSend;
1113 totalSendSize += noDOFsSend;
1114
1115 // Determine number of halo cell DOFs
1116 MInt noDOFsRecv = 0;
1117 for(MInt i = 0; i < noHaloRecv; i++) {
1118 noDOFsRecv += ipow(polyDegs[haloCell(nghbrId, i)] + 1, nDim);
1119 }
1120 dataSizeRecv[nghbrId] = noDOFsRecv;
1121 totalRecvSize += noDOFsRecv;
1122 }
1123
1124 // Storage for send data
1125 std::vector<MFloat> windowData(totalSendSize, std::numeric_limits<MFloat>::infinity());
1126
1127 // Copy data to send buffer
1128 MInt sendBufCtr = 0;
1129 for(MInt nghbrId = 0; nghbrId < noNeighborDomains(); nghbrId++) {
1130 const MInt noWindowSend = noWindowCells(nghbrId);
1131
1132 for(MInt i = 0; i < noWindowSend; i++) {
1133 const MInt cellId = windowCell(nghbrId, i);
1134 const MInt noDOFs = ipow(polyDegs[cellId] + 1, nDim);
1135
1136 std::copy(&data[dataOffsets[cellId]], &data[dataOffsets[cellId] + noDOFs], &windowData[sendBufCtr]);
1137
1138 sendBufCtr += noDOFs;
1139 }
1140 }
1141
1142 if(sendBufCtr != totalSendSize) {
1143 TERMM(1, "Send buffer counter does not match total send size.");
1144 }
1145
1146 // Receive buffer for halo data
1147 std::vector<MFloat> haloData(totalRecvSize, std::numeric_limits<MFloat>::infinity());
1148 // Communicate data with neighbor domains, data is stored ordered by neighbor domain in the buffer
1149 maia::mpi::exchangeData(&windowData[0], m_domainId, noNeighborDomains(), &m_neighborDomains[0], mpiComm(), 1,
1150 &dataSizeSend[0], &dataSizeRecv[0], &haloData[0]);
1151
1152 // Reorder data from halo cell buffer
1153 MInt haloDataOffset = 0;
1154 for(MInt nghbrId = 0; nghbrId < noNeighborDomains(); nghbrId++) {
1155 const MInt noHaloRecv = noHaloCells(nghbrId);
1156
1157 // Loop over all halos from this neighbor domain and copy to corresponding position in data
1158 for(MInt i = 0; i < noHaloRecv; i++) {
1159 const MInt haloCellId = haloCell(nghbrId, i);
1160 const MInt noDOFsHalo = ipow(polyDegs[haloCellId] + 1, nDim);
1161 std::copy_n(&haloData[haloDataOffset], noDOFsHalo, &data[dataOffsets[haloCellId]]);
1162 haloDataOffset += noDOFsHalo;
1163 }
1164 }
1165}
1166
1172template <MInt nDim>
1174 const MInt* const noNodes1D,
1175 const MInt* const dataOffsets) {
1176 std::vector<MInt> dataSizeSend(noNeighborDomains(), 0);
1177 std::vector<MInt> dataSizeRecv(noNeighborDomains(), 0);
1178 MInt totalSendSize = 0;
1179 MInt totalRecvSize = 0;
1180
1181 for(MInt nghbrId = 0; nghbrId < noNeighborDomains(); nghbrId++) {
1182 const MInt noWindowSend = noWindowCells(nghbrId);
1183 const MInt noHaloRecv = noHaloCells(nghbrId);
1184
1185 // Determine number of window cell DOFs
1186 MInt noDOFsSend = 0;
1187 for(MInt i = 0; i < noWindowSend; i++) {
1188 noDOFsSend += ipow(noNodes1D[windowCell(nghbrId, i)], nDim);
1189 }
1190 dataSizeSend[nghbrId] = noDOFsSend;
1191 totalSendSize += noDOFsSend;
1192
1193 // Determine number of halo cell DOFs
1194 MInt noDOFsRecv = 0;
1195 for(MInt i = 0; i < noHaloRecv; i++) {
1196 noDOFsRecv += ipow(noNodes1D[haloCell(nghbrId, i)], nDim);
1197 }
1198 dataSizeRecv[nghbrId] = noDOFsRecv;
1199 totalRecvSize += noDOFsRecv;
1200 }
1201
1202 // Storage for send data
1203 std::vector<MFloat> windowData(totalSendSize, std::numeric_limits<MFloat>::infinity());
1204
1205 // Copy data to send buffer
1206 MInt sendBufCtr = 0;
1207 for(MInt nghbrId = 0; nghbrId < noNeighborDomains(); nghbrId++) {
1208 const MInt noWindowSend = noWindowCells(nghbrId);
1209
1210 for(MInt i = 0; i < noWindowSend; i++) {
1211 const MInt cellId = windowCell(nghbrId, i);
1212 const MInt noDOFs = ipow(noNodes1D[cellId], nDim);
1213
1214 std::copy(&data[dataOffsets[cellId]], &data[dataOffsets[cellId] + noDOFs], &windowData[sendBufCtr]);
1215
1216 sendBufCtr += noDOFs;
1217 }
1218 }
1219
1220 if(sendBufCtr != totalSendSize) {
1221 TERMM(1, "Send buffer counter does not match total send size.");
1222 }
1223
1224 // Receive buffer for halo data
1225 std::vector<MFloat> haloData(totalRecvSize, std::numeric_limits<MFloat>::infinity());
1226 // Communicate data with neighbor domains, data is stored ordered by neighbor domain in the buffer
1227 maia::mpi::exchangeData(&windowData[0], m_domainId, noNeighborDomains(), &m_neighborDomains[0], mpiComm(), 1,
1228 &dataSizeSend[0], &dataSizeRecv[0], &haloData[0]);
1229
1230 // Reorder data from halo cell buffer
1231 MInt haloDataOffset = 0;
1232 for(MInt nghbrId = 0; nghbrId < noNeighborDomains(); nghbrId++) {
1233 const MInt noHaloRecv = noHaloCells(nghbrId);
1234
1235 // Loop over all halos from this neighbor domain and copy to corresponding position in data
1236 for(MInt i = 0; i < noHaloRecv; i++) {
1237 const MInt haloCellId = haloCell(nghbrId, i);
1238 const MInt noDOFsHalo = ipow(noNodes1D[haloCellId], nDim);
1239 std::copy_n(&haloData[haloDataOffset], noDOFsHalo, &data[dataOffsets[haloCellId]]);
1240 haloDataOffset += noDOFsHalo;
1241 }
1242 }
1243}
1244
1248template <MInt nDim>
1250 TRACE();
1251
1252 MFloat side = 0;
1253 MFloat coordsCyl[3];
1254
1255 raw().cartesianToCylindric(coords, coordsCyl);
1256
1257 side = raw().m_azimuthalBbox.azimuthalSide(/*coords,*/ coordsCyl[1]);
1258
1259 return side;
1260}
1261
1267template <MInt nDim>
1269 TRACE();
1270
1271 m_tree.m_isCutOffCell.assign(noCells(), -1);
1272
1273 if(hasCutOff()) {
1283 const MInt noCutOffDirections = Context::propertyLength("cutOffDirections", solverId());
1284
1285 ScratchSpace<MInt> isCutOff(m_tree.size(), AT_, "isCutOff");
1286 isCutOff.fill(-1);
1287
1288 // mark internal cutOff cells
1289 for(MInt i = 0; i < noCutOffDirections; i++) {
1290 const MInt dir = Context::getSolverProperty<MInt>("cutOffDirections", solverId(), AT_, i);
1291
1292 for(MInt cellId = 0; cellId < noInternalCells(); cellId++) {
1293 if(tree().hasNeighbor(cellId, dir) != 0) continue;
1294 if(!tree().isLeafCell(cellId)) continue;
1295 if(tree().parent(cellId) > -1) {
1296 if(tree().hasNeighbor(tree().parent(cellId), dir) != 0) continue;
1297 }
1298 if(m_tree.m_isCutOffCell[cellId] < 0) {
1299 m_tree.m_isCutOffCell[cellId] = dir;
1300 isCutOff[cellId] = dir;
1301 } else {
1302 // NOTE: if instead of the direction prime numbers are used
1303 // the addition of the prime number leads to the recalculation of the used directions!
1304 // for cells which are part of multiple cutOffs!
1305 m_tree.m_isCutOffCell[cellId] += dir;
1306 isCutOff[cellId] += dir;
1307 }
1308 }
1309 }
1310
1311 // exchange property to mark halo cutOff cells
1312 if(m_neighborDomains.size() > 0) {
1313 mpi::exchangeData(m_neighborDomains, m_haloCells, m_windowCells, mpiComm(), &isCutOff[0], 1);
1314 }
1315
1316 for(MInt cellId = noInternalCells(); cellId < m_tree.size(); cellId++) {
1317 m_tree.m_isCutOffCell[cellId] = isCutOff[cellId];
1318 }
1319 }
1320}
1321
1322
1323template <MInt nDim>
1325 static constexpr MInt maxNoNeighbors = (nDim == 2) ? 8 : 26;
1326
1327 m_log << "detecting neighbors for " << noCells() << " cells ... ";
1328
1329 // Allocate neighbor list and neighbor element
1330 m_log << "allocating new array (" << noCells() << " entries)...";
1331 mDeallocate(m_neighborList);
1332 mAlloc(m_neighborList, noCells(), maxNoNeighbors, "m_neighborList", -1, AT_);
1333 m_log << " allocated... ";
1334
1335 static constexpr MInt twoPowers[6] = {1, 2, 4, 8, 16, 32};
1336
1337 IF_CONSTEXPR(nDim == 2) {
1338#ifdef _OPENMP
1339#pragma omp parallel for
1340#endif
1341 for(MInt id = 0; id < noCells(); id++) { // m_noRegularCells;
1342
1343 // -----------------------------
1344 // Find all equal level neighbors
1345 for(MInt i = 0; i < raw().m_counter2D; i++) {
1346 // First step
1347 MInt current = id;
1348
1349 if(tree().hasNeighbor(current, raw().m_paths2D[i][0]) == 1) {
1350 // take the global Id
1351 MInt currentGlobal = tree().neighbor(current, raw().m_paths2D[i][0]);
1352
1353 MInt nCode = raw().m_neighborCode[twoPowers[raw().m_paths2D[i][0]]];
1354 // save also global neighbor Ids laying in other domains (here: no exception for outer laying domain cells)
1355 m_neighborList[id][nCode] = currentGlobal;
1356
1357 if(currentGlobal == -1) {
1358 continue;
1359 }
1360
1361 // continue if cell is laying in other domain
1362 if(idsAreGlobal && raw().m_globalToLocalId.find(currentGlobal) == raw().m_globalToLocalId.end()) {
1363 continue;
1364 }
1365
1366 // take the local Id
1367 current = idsAreGlobal ? raw().m_globalToLocalId.at(tree().neighbor(current, raw().m_paths2D[i][0]))
1368 : tree().neighbor(current, raw().m_paths2D[i][0]);
1369
1370 // Second step
1371 if(tree().hasNeighbor(current, raw().m_paths2D[i][1]) == 1) {
1372 // save also global neighbor Ids laying in other domains (here: no exception for outer laying domain cells)
1373 nCode = raw().m_neighborCode[twoPowers[raw().m_paths2D[i][0]] + twoPowers[raw().m_paths2D[i][1]]];
1374 m_neighborList[id][nCode] = tree().neighbor(current, raw().m_paths2D[i][1]);
1375 }
1376 }
1377 } // End loop over neighbor directions
1378
1379 // For LB: Find also diagonal neighbors that are not connected via cartesian neighbors (2D)!
1380 // Some mapping that is needed:
1381 // E.g. For child on position 0, child on position 3 in parent is the wanted diagonal neighbor
1382 if(raw().m_lbGridChecks) {
1383 MInt posPosMap[4] = {3, 2, 1, 0};
1384
1385 // E.g. For child 0, the neighbor that can be affected by this exceptional case is neighbor 6
1386 MInt posDiagNghbrMap[4] = {6, 5, 7, 4};
1387
1388 // E.g. For child 0, the paths on paren level are 0,2 or 2,0
1389 MInt parentPaths[4][2][2] = {{{0, 2}, {2, 0}}, {{1, 2}, {2, 1}}, {{0, 3}, {3, 0}}, {{1, 3}, {3, 1}}};
1390 MInt numberOfPossiblePaths = 2;
1391
1392 // This case can only happen if cells are not on maxUniformRefinementLevel
1393 if(tree().level(id) > maxUniformRefinementLevel()) {
1394 MInt parent = tree().parent(id);
1395
1396 // Get postion of child in parent
1397 MInt position = 0;
1398 for(MInt dim = 0; dim < 2; dim++) {
1399 if(tree().coordinate(id, dim) < tree().coordinate(parent, dim)) {
1400 position += 0;
1401 } else {
1402 position += IPOW2(dim);
1403 }
1404 }
1405
1406 // Check if diagonal neighbor has not been already found via cartesian neighbor link
1407 for(MInt j = 0; j < numberOfPossiblePaths; j++) {
1408 if(m_neighborList[id][posDiagNghbrMap[position]] == -1) {
1409 // Perform first step on parent level
1410 if(tree().hasNeighbor(parent, parentPaths[position][j][0]) == 1) {
1411 MInt firstParentNghbrGlobal = tree().neighbor(parent, parentPaths[position][j][0]);
1412 if(firstParentNghbrGlobal == -1) continue;
1413 if(idsAreGlobal
1414 && raw().m_globalToLocalId.find(firstParentNghbrGlobal) == raw().m_globalToLocalId.end())
1415 continue;
1416 MInt firstParentNghbr =
1417 idsAreGlobal ? raw().m_globalToLocalId.at(tree().neighbor(parent, parentPaths[position][j][0]))
1418 : tree().neighbor(parent, parentPaths[position][j][0]);
1419
1420 // Perform second step on parent level
1421 if(tree().hasNeighbor(firstParentNghbr, parentPaths[position][j][1]) == 1) {
1422 MInt secondParentNghbrGlobal = tree().neighbor(firstParentNghbr, parentPaths[position][j][1]);
1423 if(secondParentNghbrGlobal == -1) continue;
1424 if(idsAreGlobal
1425 && raw().m_globalToLocalId.find(secondParentNghbrGlobal) == raw().m_globalToLocalId.end())
1426 continue;
1427 MInt secondParentNghbr =
1428 idsAreGlobal
1429 ? raw().m_globalToLocalId.at(tree().neighbor(firstParentNghbr, parentPaths[position][j][1]))
1430 : tree().neighbor(firstParentNghbr, parentPaths[position][j][1]);
1431 if(tree().hasChildren(secondParentNghbr)) {
1432 if(tree().child(secondParentNghbr, posPosMap[position]) != -1) {
1433 MInt nCode = posDiagNghbrMap[position];
1434 m_neighborList[id][nCode] = tree().child(secondParentNghbr, posPosMap[position]);
1435 break;
1436 }
1437 }
1438 }
1439 }
1440 }
1441 }
1442 }
1443 }
1444 }
1445 }
1446 else {
1447#ifdef _OPENMP
1448#pragma omp parallel for
1449#endif
1450 for(MInt id = 0; id < noCells(); id++) { // m_noRegularCells
1451
1452 // -----------------------------
1453 // Find all equal level neighbors
1454 for(MInt i = 0; i < raw().m_counter3D; i++) {
1455 // First step
1456 MInt current = id;
1457 if(tree().hasNeighbor(current, raw().m_paths3D[i][0]) == 1) {
1458 // take the global Id
1459 MInt currentGlobal = tree().neighbor(current, raw().m_paths3D[i][0]);
1460
1461 MInt nCode = raw().m_neighborCode[twoPowers[raw().m_paths3D[i][0]]];
1462 // save also global neighbor Ids laying in other domains (here: no exception for outer laying domain cells)
1463 m_neighborList[id][nCode] = currentGlobal;
1464
1465 if(currentGlobal == -1) {
1466 continue;
1467 }
1468
1469 // continue if cell is laying in other domain
1470 if(idsAreGlobal && raw().m_globalToLocalId.find(currentGlobal) == raw().m_globalToLocalId.end()) {
1471 continue;
1472 }
1473
1474 // take the local Id
1475 current = idsAreGlobal ? raw().m_globalToLocalId.at(tree().neighbor(current, raw().m_paths3D[i][0]))
1476 : tree().neighbor(current, raw().m_paths3D[i][0]);
1477
1478 // Second step
1479 if(tree().hasNeighbor(current, raw().m_paths3D[i][1]) == 1) {
1480 // take the global Id
1481 currentGlobal = tree().neighbor(current, raw().m_paths3D[i][1]);
1482
1483 nCode = raw().m_neighborCode[twoPowers[raw().m_paths3D[i][0]] + twoPowers[raw().m_paths3D[i][1]]];
1484 // save also global neighbor Ids laying in other domains (here: no exception for outer laying domain cells)
1485 m_neighborList[id][nCode] = currentGlobal;
1486
1487 if(currentGlobal == -1) {
1488 continue;
1489 }
1490
1491 // continue if cell is laying in other domain
1492 if(idsAreGlobal && raw().m_globalToLocalId.find(currentGlobal) == raw().m_globalToLocalId.end()) {
1493 continue;
1494 }
1495
1496 // take the local Id
1497 current = idsAreGlobal ? raw().m_globalToLocalId.at(tree().neighbor(current, raw().m_paths3D[i][1]))
1498 : tree().neighbor(current, raw().m_paths3D[i][1]);
1499
1500 // Third step
1501 if(tree().hasNeighbor(current, raw().m_paths3D[i][2]) == 1) {
1502 // save also global neighbor Ids laying in other domains (here: no exception for outer laying domain
1503 // cells)
1504 nCode = raw().m_neighborCode[twoPowers[raw().m_paths3D[i][0]] + twoPowers[raw().m_paths3D[i][1]]
1505 + twoPowers[raw().m_paths3D[i][2]]];
1506 m_neighborList[id][nCode] = tree().neighbor(current, raw().m_paths3D[i][2]);
1507 }
1508 }
1509 }
1510 }
1511
1512 // For LB: Find also diagonal neighbors that are not connected via cartesian neighbors (3D).
1513 // Some mapping:
1514 // E.g. child at pos. 0 has neighbors 6,10,14,18 that can be affected by the exception
1515 if(raw().m_lbGridChecks) {
1516 MInt posDiagNghbrMap[8][4] = {{6, 10, 14, 18}, {8, 12, 14, 22}, {7, 10, 16, 20}, {9, 12, 16, 24},
1517 {6, 11, 15, 19}, {8, 13, 15, 23}, {7, 11, 17, 21}, {9, 13, 17, 25}};
1518
1519 // E.g. the neighbors of child 0 in the parent neighbors 6,10,14,18 are the children 3,5,6,7
1520 MInt posPosMap[8][4] = {{3, 5, 6, 7}, {2, 4, 7, 6}, {1, 7, 4, 5}, {0, 6, 5, 4},
1521 {7, 1, 2, 3}, {6, 0, 3, 2}, {5, 3, 0, 1}, {4, 2, 1, 0}};
1522
1523 // Index is diag direction id minus 6.
1524 // E.g. path to diag neighbor 6 can be 2,0 or 0,2
1525 MInt diagNghbrPathMap[20][4][3] = {
1526 {{2, 0, -1}, {0, 2, -1}, {-1, -1, -1}, {-1, -1, -1}}, {{3, 0, -1}, {0, 3, -1}, {-1, -1, -1}, {-1, -1, -1}},
1527 {{2, 1, -1}, {1, 2, -1}, {-1, -1, -1}, {-1, -1, -1}}, {{3, 1, -1}, {1, 3, -1}, {-1, -1, -1}, {-1, -1, -1}},
1528 {{4, 0, -1}, {0, 4, -1}, {-1, -1, -1}, {-1, -1, -1}}, {{5, 0, -1}, {0, 5, -1}, {-1, -1, -1}, {-1, -1, -1}},
1529 {{4, 1, -1}, {1, 4, -1}, {-1, -1, -1}, {-1, -1, -1}}, {{5, 1, -1}, {1, 5, -1}, {-1, -1, -1}, {-1, -1, -1}},
1530 {{4, 2, -1}, {2, 4, -1}, {-1, -1, -1}, {-1, -1, -1}}, {{5, 2, -1}, {2, 5, -1}, {-1, -1, -1}, {-1, -1, -1}},
1531 {{4, 3, -1}, {3, 4, -1}, {-1, -1, -1}, {-1, -1, -1}}, {{5, 3, -1}, {3, 5, -1}, {-1, -1, -1}, {-1, -1, -1}},
1532 {{0, 2, 4}, {2, 0, 4}, {4, 2, 0}, {4, 0, 2}}, {{0, 2, 5}, {2, 0, 5}, {5, 0, 2}, {5, 2, 0}},
1533 {{0, 3, 4}, {3, 0, 4}, {4, 0, 3}, {4, 3, 0}}, {{0, 3, 5}, {3, 0, 5}, {5, 0, 3}, {5, 3, 0}},
1534 {{1, 2, 4}, {2, 1, 4}, {4, 1, 2}, {4, 2, 1}}, {{1, 2, 5}, {2, 1, 5}, {5, 1, 2}, {5, 2, 1}},
1535 {{1, 3, 4}, {3, 1, 4}, {4, 1, 3}, {4, 3, 1}}, {{1, 3, 5}, {3, 1, 5}, {5, 1, 3}, {5, 3, 1}}};
1536
1537 // This case can only happen if cells are not on maxUniformRefinementlevel
1538 if(tree().level(id) > maxUniformRefinementLevel()) {
1539 MInt parent = tree().parent(id);
1540
1541 // Get position of child in parent
1542 MInt position = 0;
1543 for(MInt dim = 0; dim < nDim; dim++) {
1544 if(tree().coordinate(id, dim) < tree().coordinate(parent, dim)) {
1545 position += 0;
1546 } else {
1547 position += IPOW2(dim);
1548 }
1549 }
1550
1551 // Loop over possible affected neighbors
1552 for(MInt i = 0; i < 4; i++) {
1553 MInt neighbor = posDiagNghbrMap[position][i];
1554
1555 // Check if diagonal neighbor has not been already found via Cartesian neighbor link
1556 if(m_neighborList[id][neighbor] == -1) { // not equal for testing :) --> Overwrites above. Thus, must be the
1557 // same as without this extension!
1558
1559 // Loop over paths
1560 for(MInt j = 0; j < 4; j++) {
1561 // Continue if there is no path left
1562 if(diagNghbrPathMap[neighbor - 6][j][0] == -1) continue;
1563
1564 // Perform first step on parent level
1565 if(tree().hasNeighbor(parent, diagNghbrPathMap[neighbor - 6][j][0]) == 1) {
1566 MInt firstParentNghbrGlobal = tree().neighbor(parent, diagNghbrPathMap[neighbor - 6][j][0]);
1567 if(firstParentNghbrGlobal == -1) continue;
1568 if(idsAreGlobal
1569 && raw().m_globalToLocalId.find(firstParentNghbrGlobal) == raw().m_globalToLocalId.end())
1570 continue;
1571 MInt firstParentNghbr =
1572 idsAreGlobal
1573 ? raw().m_globalToLocalId.at(tree().neighbor(parent, diagNghbrPathMap[neighbor - 6][j][0]))
1574 : tree().neighbor(parent, diagNghbrPathMap[neighbor - 6][j][0]);
1575
1576 // Perform second step on parent level
1577 if(tree().hasNeighbor(firstParentNghbr, diagNghbrPathMap[neighbor - 6][j][1]) == 1) {
1578 MInt secondParentNghbrGlobal =
1579 tree().neighbor(firstParentNghbr, diagNghbrPathMap[neighbor - 6][j][1]);
1580 if(secondParentNghbrGlobal == -1) continue;
1581 if(idsAreGlobal
1582 && raw().m_globalToLocalId.find(secondParentNghbrGlobal) == raw().m_globalToLocalId.end())
1583 continue;
1584 MInt secondParentNghbr =
1585 idsAreGlobal ? raw().m_globalToLocalId.at(
1586 tree().neighbor(firstParentNghbr, diagNghbrPathMap[neighbor - 6][j][1]))
1587 : tree().neighbor(firstParentNghbr, diagNghbrPathMap[neighbor - 6][j][1]);
1588
1589 // Check if a third step is necessary
1590 if(diagNghbrPathMap[neighbor - 6][j][2] != -1) {
1591 if(tree().hasNeighbor(secondParentNghbr, diagNghbrPathMap[neighbor - 6][j][2]) == 1) {
1592 MInt thirdParentNghbrGlobal =
1593 tree().neighbor(secondParentNghbr, diagNghbrPathMap[neighbor - 6][j][2]);
1594 if(thirdParentNghbrGlobal == -1) continue;
1595 if(idsAreGlobal
1596 && raw().m_globalToLocalId.find(thirdParentNghbrGlobal) == raw().m_globalToLocalId.end())
1597 continue;
1598 MInt thirdParentNghbr =
1599 idsAreGlobal ? raw().m_globalToLocalId.at(
1600 tree().neighbor(secondParentNghbr, diagNghbrPathMap[neighbor - 6][j][2]))
1601 : tree().neighbor(secondParentNghbr, diagNghbrPathMap[neighbor - 6][j][2]);
1602 if(tree().hasChildren(thirdParentNghbr)) {
1603 if(tree().child(thirdParentNghbr, posPosMap[position][i]) != -1) {
1604 MInt nCode = neighbor;
1605 m_neighborList[id][nCode] = tree().child(thirdParentNghbr, posPosMap[position][i]);
1606 break;
1607 }
1608 }
1609 }
1610 } else {
1611 if(tree().hasChildren(secondParentNghbr)) {
1612 if(tree().child(secondParentNghbr, posPosMap[position][i]) != -1) {
1613 MInt nCode = neighbor;
1614 m_neighborList[id][nCode] = tree().child(secondParentNghbr, posPosMap[position][i]);
1615 break;
1616 }
1617 }
1618 }
1619 }
1620 }
1621 }
1622 }
1623 }
1624 }
1625 }
1626 }
1627 }
1628}
1629
1630
1640template <MInt nDim>
1642 TRACE();
1643
1644 if(m_storeNghbrIds != nullptr) {
1645 mDeallocate(m_storeNghbrIds);
1646 }
1647 mAlloc(m_storeNghbrIds, m_noDirs * IPOW2(nDim) * noCells(), "m_storeNghbrIds", -1, AT_);
1648
1649 if(m_identNghbrIds != nullptr) {
1650 mDeallocate(m_identNghbrIds);
1651 }
1652 mAlloc(m_identNghbrIds, m_noDirs * noCells() + 1, "m_identNghbrIds", -1, AT_);
1653
1654 MInt pos = 0;
1655
1656 // --------- loop over all cartesian cells ----------
1657 for(MInt cellId = 0; cellId < noCells(); cellId++) {
1658 for(MInt dirId = 0; dirId < m_noDirs; dirId++) {
1659 m_identNghbrIds[cellId * m_noDirs + dirId] = pos;
1660 }
1661 if(!tree().isLeafCell(cellId)) continue;
1662 for(MInt dirId = 0; dirId < m_noDirs; dirId++) {
1663 const MInt spaceId = dirId / 2;
1664 const MInt sideId = dirId % 2;
1665 const MInt nghbrSideId = (sideId + 1) % 2;
1666
1667 // set the location in m_storeNghbrIds, where neighbors of cellId are
1668 // stored
1669 m_identNghbrIds[cellId * m_noDirs + dirId] = pos;
1670
1671 // ----------------------------------------------------------------
1672 // --- identify neighbor in considered direction ------------------
1673 // *-> this can be a neighbor of the cell itself or a neighbor of *
1674 // * its parent, grandparent, and so forth *
1675 // ----------------------------------------------------------------
1676
1677 // check if cell has a neighbor in the investigated
1678 // direction
1679 if(tree().hasNeighbor(cellId, dirId) > 0) {
1680 // -> cell has a neighbor
1681 // this neighbor:
1682 MInt nghbrId = tree().neighbor(cellId, dirId);
1683 const MInt nghbrLvl = tree().level(nghbrId);
1684
1685 // --------------------------------------------------------------
1686 // --- investigate neighbor -------------------------------------
1687 // *-> check if neighbor has children, grandchildren, and so *
1688 // * forth, which are direct neighbors of the considered cell *
1689 // --------------------------------------------------------------
1690
1691 if(tree().isLeafCell(nghbrId)) {
1692 // neighbor is not a parent and can be stored as neighbor of the cell
1693 m_storeNghbrIds[pos] = nghbrId;
1694 pos++;
1695 } else {
1696 // neighbor is a parent
1697 // investigate all children and grandchildren
1698 // temporary array that holds all parent cells to be
1699 // investigated
1700 MInt tempNghbrs[16];
1701 MInt tempNghbrs1[16];
1702 tempNghbrs[0] = nghbrId;
1703 MBool nghbrsFound = false;
1704
1705 // ----------------------------------------------------------
1706 // --- investigate all neighbors that are parents -----------
1707 // ----------------------------------------------------------
1708
1709 MInt maxIndex = 1;
1710 while(nghbrsFound == false) {
1711 // initialize counter for the number of investigated
1712 // neighboring children that are parents themselves and
1713 // thus added to a temporary array
1714 MInt k = 0;
1715
1716 // ------ loop over all neighbor parents ------------------
1717 for(MInt index = 0; index < maxIndex; index++) {
1718 // investigated neighbor parent cell
1719 ASSERT(index > -1 && index < 16, "");
1720 nghbrId = tempNghbrs[index];
1721
1722 // check the location of the children within the parent cell
1723 // by means of comparison of the grid cell cogs of the
1724 // parent cell and the children in considered space direction
1725
1726 // in case none of the children is identified
1727 // as a direct neighbor, do nothing, since then
1728 // the considered cell side is a non-fluid side
1729
1730 // --------- loop over neighbor children ----------------
1731 for(MInt childId = 0; childId < IPOW2(nDim); childId++) {
1732 const MInt nghbrChildId = tree().child(nghbrId, childId);
1733 if(nghbrChildId < 0) continue;
1734 if((tree().coordinate(nghbrChildId, spaceId) - tree().coordinate(nghbrId, spaceId))
1735 * ((MFloat)nghbrSideId - 0.5)
1736 > 0) {
1737 if(tree().isLeafCell(nghbrChildId)) {
1738 // child cell is not a parent and stored as a neighbor
1739 m_storeNghbrIds[pos] = nghbrChildId;
1740 pos++;
1741 } else {
1742 // add child cell to temporary array for
1743 // further investigation
1744 // ASSERT(k > -1 && k < 16,
1745 // "Too many local child neighbors. Something might be wrong with "
1746 // "your mesh!");
1747 if(k > 14) {
1748 cerr << "Large neighbor count " << k << " " << nghbrLvl << " " << tree().level(nghbrChildId)
1749 << endl;
1750 }
1751 tempNghbrs1[k] = nghbrChildId;
1752 k++;
1753 }
1754 }
1755 }
1756 }
1757
1758 maxIndex = k;
1759 if(k == 0) {
1760 nghbrsFound = true;
1761 } else {
1762 // transfer the cells collected in tempNghbrs1 to
1763 // tempNghbrs
1764 for(MInt l = 0; l < k; l++) {
1765 ASSERT(l > -1 && l < 16, "");
1766 tempNghbrs[l] = tempNghbrs1[l];
1767 }
1768 }
1769 }
1770 }
1771 } else {
1772 // -> cell does not have a neighbor of equivalent level
1773 MInt currentCellId = cellId;
1774 MBool nghbrExist = false;
1775 // loop until a parent is found who has a neighbor in considered direction
1776 // break when the parent ID becomes -1 (boundary/cutOff in that direction)
1777 while(nghbrExist == false) {
1778 const MInt parentId = tree().parent(currentCellId);
1779 // if parent does not exist, break
1780 if(parentId < 0 || parentId >= noCells()) break;
1781
1782
1783 // break if currentCell does not lie at the parent cell face
1784 // whose normal points to considered direction
1785 if((tree().coordinate(currentCellId, spaceId) - tree().coordinate(parentId, spaceId)) * ((float)sideId - 0.5)
1786 < 0) {
1787 break;
1788 }
1789
1790 // identify parentId of current cell and store it as current cell
1791 currentCellId = parentId;
1792
1793 // check if current cell has a neighbor in regarded direction
1794 if(tree().hasNeighbor(currentCellId, dirId) > 0) {
1795 nghbrExist = true;
1796 const MInt nghbrId = tree().neighbor(currentCellId, dirId);
1797 // store this neighbor if it is regarded - multilevel
1798 if(tree().isLeafCell(nghbrId)) {
1799 m_storeNghbrIds[pos] = nghbrId;
1800 pos++;
1801 }
1802 }
1803 }
1804 }
1805 }
1806 }
1807 m_identNghbrIds[noCells() * m_noDirs] = pos;
1808}
1809
1819template <MInt nDim>
1820void Proxy<nDim>::findDirectNghbrs(const MInt cellId, vector<MInt>& nghbrList) {
1821 ASSERT(cellId >= 0, "ERROR: Invalid cellId!");
1822 ASSERT(m_storeNghbrIds != nullptr, "");
1823 ASSERT(m_identNghbrIds != nullptr, "");
1824
1825 nghbrList.push_back(cellId);
1826
1827 // this prevents neighbors to be identified multiple times
1828 auto uniqueAdd = [&](MInt newElement) {
1829 auto it = find(nghbrList.begin(), nghbrList.end(), newElement);
1830 if(it == nghbrList.end() && newElement >= 0) {
1831 nghbrList.push_back(newElement);
1832 return true;
1833 }
1834 return false;
1835 };
1836
1840 static constexpr MInt diagDirs[4]{3, 2, 0, 1};
1841 for(MInt dir = 0; dir < 2 * nDim; dir++) {
1842 // iterate overall direct neighbors
1843 for(MInt j = m_identNghbrIds[2 * cellId * nDim + dir]; j < m_identNghbrIds[2 * cellId * nDim + dir + 1]; j++) {
1844 const MInt nghbrId = m_storeNghbrIds[j];
1845 uniqueAdd(nghbrId);
1846 if(dir < 4) {
1847 // add diagonal neighbors in x-y plane
1848 for(MInt t = m_identNghbrIds[2 * nghbrId * nDim + diagDirs[dir]];
1849 t < m_identNghbrIds[2 * nghbrId * nDim + diagDirs[dir] + 1];
1850 t++) {
1851 const MInt diagNghbr = m_storeNghbrIds[t];
1852 uniqueAdd(diagNghbr);
1853 }
1854 }
1855 }
1856 }
1857
1862 IF_CONSTEXPR(nDim == 3) {
1863 for(MInt dirZ = 4; dirZ < 6; dirZ++) {
1864 for(MInt j = m_identNghbrIds[2 * cellId * nDim + dirZ]; j < m_identNghbrIds[2 * cellId * nDim + dirZ + 1]; j++) {
1865 const MInt nghbrIdZ = m_storeNghbrIds[j];
1866 for(MInt dir = 0; dir < 4; dir++) {
1867 for(MInt t = m_identNghbrIds[2 * nghbrIdZ * nDim + dir]; t < m_identNghbrIds[2 * nghbrIdZ * nDim + dir + 1];
1868 t++) {
1869 const MInt nghbrId = m_storeNghbrIds[t];
1870 uniqueAdd(nghbrId);
1871 // tridiagonal neighbors
1872 for(MInt v = m_identNghbrIds[2 * nghbrId * nDim + diagDirs[dir]];
1873 v < m_identNghbrIds[2 * nghbrId * nDim + diagDirs[dir] + 1];
1874 v++) {
1875 const MInt triDiagNghbrId = m_storeNghbrIds[v];
1876 uniqueAdd(triDiagNghbrId);
1877 }
1878 }
1879 }
1880 }
1881 }
1882 }
1883}
1884
1894template <MInt nDim>
1895void Proxy<nDim>::findNeighborHood(const MInt cellId, const MInt layer, vector<MInt>& nghbrList) {
1896 ASSERT(layer > 0, "ERROR: Invalid layer!");
1897 ASSERT(cellId >= 0, "ERROR: Invalid cellId!");
1898 ASSERT(m_storeNghbrIds != nullptr, "");
1899 ASSERT(m_identNghbrIds != nullptr, "");
1900
1901 // this prevents neighbors to be identified multiple times
1902 auto uniqueAdd = [&](MInt newElement) {
1903 auto it = find(nghbrList.begin(), nghbrList.end(), newElement);
1904 if(it == nghbrList.end() && newElement >= 0) {
1905 nghbrList.push_back(newElement);
1906 return true;
1907 }
1908 return false;
1909 };
1910
1911 vector<MInt> tempNghbrCollection{};
1912
1916 findDirectNghbrs(cellId, nghbrList);
1917
1921 for(MInt currEx = 1; currEx < layer; currEx++) {
1922 // for each newly found neighbor find all neighbors
1923 for(auto& nghbr : nghbrList) {
1924 findDirectNghbrs(nghbr, tempNghbrCollection);
1925 }
1926 nghbrList.clear();
1927 // add all new unique neighbor ids to vector
1928 for(auto& nghbr : tempNghbrCollection) {
1929 if(uniqueAdd(nghbr) && currEx + 1 < layer) {
1930 // optimisation
1931 nghbrList.push_back(nghbr);
1932 }
1933 }
1934 }
1935}
1936
1937
1938template <MInt nDim>
1940 TRACE();
1941 static constexpr MInt cornerIndices[8][3] = {{-1, -1, -1}, {1, -1, -1}, {-1, 1, -1}, {1, 1, -1},
1942 {-1, -1, 1}, {1, -1, 1}, {-1, 1, 1}, {1, 1, 1}};
1943 MFloat corner[3] = {0, 0, 0};
1944 MFloat cellHalfLength = raw().halfCellLength(gridId);
1945
1946 MInt cnt = 0;
1947#ifndef PMODEC
1948 for(MInt i = 0; i < IPOW2(nDim); i++) {
1949 for(MInt dim = 0; dim < nDim; dim++) {
1950 corner[dim] = raw().a_coordinate(gridId, dim) + cornerIndices[i][dim] * cellHalfLength;
1951 }
1952 if(nDim == 2) {
1953 if(!m_geometry->pointIsInside(corner)) {
1954 cnt++; // pointIsInside == true if Point is outside fluid domain
1955 break;
1956 }
1957 } else {
1958 if(!m_geometry->pointIsInside2(corner)) {
1959 cnt++; // pointIsInside == true if Point is outside fluid domain
1960 break;
1961 }
1962 }
1963 }
1964#endif
1965 MInt outside = false;
1966 if(cnt == 0) {
1967 outside = true;
1968 }
1969 return outside;
1970}
1971
1976template <MInt nDim>
1978 MInt diagonalNeighbors) {
1979 MInt noDirs = 2 * nDim;
1980
1981 std::array<std::array<MInt, 4>, nDim> tmpNghbrs;
1982 set<MInt> nghbrs;
1983
1984 nghbrs.insert(cellId);
1985 for(MInt layer = 1; layer <= noLayers; layer++) {
1986 set<MInt> nextLayer;
1987 for(auto& nghbrId : nghbrs) {
1988 for(MInt dir0 = 0; dir0 < noDirs; dir0++) {
1989 const MInt cnt0 = getNghbrCells(nghbrId, level, &tmpNghbrs[0][0], dir0);
1990 for(MInt c0 = 0; c0 < cnt0; c0++) {
1991 nextLayer.insert(tmpNghbrs[0][c0]);
1992
1993 if(diagonalNeighbors > 0) {
1994 for(MInt dir1 = 0; dir1 < noDirs; dir1++) {
1995 if((dir1 / 2) == (dir0 / 2)) continue;
1996 const MInt cnt1 = getNghbrCells(tmpNghbrs[0][c0], level, &tmpNghbrs[1][0], dir1, dir0);
1997 for(MInt c1 = 0; c1 < cnt1; c1++) {
1998 nextLayer.insert(tmpNghbrs[1][c1]);
1999
2000 if(nDim == 3 && diagonalNeighbors == 2) {
2001 for(MInt dir2 = 0; dir2 < noDirs; dir2++) {
2002 if(((dir2 / 2) == (dir0 / 2)) || ((dir2 / 2) == (dir1 / 2))) continue;
2003 const MInt cnt2 = getNghbrCells(tmpNghbrs[1][c1], level, &tmpNghbrs[2][0], dir2, dir1, dir0);
2004 for(MInt c2 = 0; c2 < cnt2; c2++) {
2005 nextLayer.insert(tmpNghbrs[2][c2]);
2006 }
2007 }
2008 }
2009 }
2010 }
2011 }
2012 }
2013 }
2014 }
2015 for(MInt it : nextLayer) {
2016 nghbrs.insert(it);
2017 }
2018 }
2019 nghbrs.erase(cellId);
2020
2021 MInt cnt = 0;
2022 for(MInt nghbr : nghbrs) {
2023 ASSERT(tree().level(nghbr) <= level + 1, "");
2024 ASSERT(cnt < (signed)adjacentCells.size(), to_string(nghbrs.size()) + " " + to_string(adjacentCells.size()));
2025 adjacentCells[cnt] = nghbr;
2026 cnt++;
2027 }
2028 return cnt;
2029}
2030
2031
2032template <MInt nDim>
2033MInt Proxy<nDim>::getNghbrCells(MInt cellId, MInt level, MInt* nghbrs, MInt dir0, MInt dir1, MInt dir2) {
2034 MInt count = 0;
2035 MInt nghbrId0 = -1;
2036 if(tree().hasNeighbor(cellId, dir0) > 0) {
2037 nghbrId0 = tree().neighbor(cellId, dir0);
2038 } else if(tree().parent(cellId) > -1) {
2039 if(tree().hasNeighbor(tree().parent(cellId), dir0) > 0) {
2040 nghbrId0 = tree().neighbor(tree().parent(cellId), dir0);
2041 }
2042 }
2043 if(nghbrId0 < 0) return 0;
2044
2045 if(tree().noChildren(nghbrId0) > 0 && tree().level(nghbrId0) <= level) {
2046 for(MInt child = 0; child < m_maxNoChilds; child++) {
2047 if(!childCode[dir0][child]) continue;
2048 if(dir1 > -1 && !childCode[dir1][child]) continue;
2049 if(dir2 > -1 && !childCode[dir2][child]) continue;
2050 if(tree().child(nghbrId0, child) > -1) {
2051 nghbrs[count] = tree().child(nghbrId0, child);
2052 count++;
2053 }
2054 }
2055 } else {
2056 nghbrs[count] = nghbrId0;
2057 count++;
2058 }
2059
2060 return count;
2061}
2062
2072template <MInt nDim>
2074 TRACE();
2075
2076 // Check if neighbor domains are sorted ascendingly
2077 if(!std::is_sorted(m_azimuthalNeighborDomains.begin(), m_azimuthalNeighborDomains.end())) {
2078 TERMM(1, "Neighbor domains are not sorted in ascending order.");
2079 }
2080
2081 // Determine all neighbor domains of current domain
2082 MCharScratchSpace isNeighborDomain(noDomains(), AT_, "isNeighborDomain");
2083 fill(isNeighborDomain.begin(), isNeighborDomain.end(), 0);
2084 for(MInt d = 0; d < noAzimuthalNeighborDomains(); d++) {
2085 isNeighborDomain[azimuthalNeighborDomain(d)] = 1;
2086 }
2087
2088 // Exchange with all domains in solver-local communicator
2089 MPI_Alltoall(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, &isNeighborDomain[0], 1, type_traits<MChar>::mpiType(), mpiComm(),
2090 AT_, "MPI_IN_PLACE", "isNeighborDomain[0]");
2091
2092 // Check if all domains that the current domain considers as neighbors also reciprocate
2093 for(MInt d = 0; d < noAzimuthalNeighborDomains(); d++) {
2094 if(!isNeighborDomain[azimuthalNeighborDomain(d)]) {
2095 TERMM(1, "Domain " + to_string(domainId()) + " has domain " + to_string(neighborDomain(d))
2096 + " as a neighbor but not the other way around.");
2097 }
2098 }
2099}
2100
2101
2110template <MInt nDim>
2112 TRACE();
2113
2115 // Check #1: number of azimuthal window/halo cells match
2117 // Start receiving number of azimuthal window cells from each azimuthal neighbor domain
2118 ScratchSpace<MPI_Request> recvRequests(max(1, noAzimuthalNeighborDomains()), AT_, "recvRequests");
2119 MIntScratchSpace noWindowCellsRecv(max(1, noAzimuthalNeighborDomains()), AT_, "noWindowCellsRecv");
2120 for(MInt d = 0; d < noAzimuthalNeighborDomains(); d++) {
2121 noWindowCellsRecv[d] = -1;
2122 MPI_Irecv(&noWindowCellsRecv[d], 1, type_traits<MInt>::mpiType(), azimuthalNeighborDomain(d),
2123 azimuthalNeighborDomain(d), mpiComm(), &recvRequests[d], AT_, "noWindowCellsRecv[d]");
2124 }
2125
2126 // Start sending number of azimuthal window cells to each azimuthal neighbor domain
2127 ScratchSpace<MPI_Request> sendRequests(max(1, noAzimuthalNeighborDomains()), AT_, "sendRequests");
2128 MIntScratchSpace noWindowCellsSend(max(1, noAzimuthalNeighborDomains()), AT_, "noWindowCellsSend");
2129 for(MInt d = 0; d < noAzimuthalNeighborDomains(); d++) {
2130 noWindowCellsSend[d] = noAzimuthalWindowCells(d);
2131 MPI_Isend(&noWindowCellsSend[d], 1, type_traits<MInt>::mpiType(), azimuthalNeighborDomain(d), domainId(), mpiComm(),
2132 &sendRequests[d], AT_, "noWindowCellsSend[d]");
2133 }
2134
2135 // Finish MPI communication
2136 MPI_Waitall(noAzimuthalNeighborDomains(), &recvRequests[0], MPI_STATUSES_IGNORE, AT_);
2137 MPI_Waitall(noAzimuthalNeighborDomains(), &sendRequests[0], MPI_STATUSES_IGNORE, AT_);
2138
2139 // Check if received number of azimuthal window cells matches the local number of azimuthal halo cells
2140 for(MInt d = 0; d < noAzimuthalNeighborDomains(); d++) {
2141 if(noWindowCellsRecv[d] != noAzimuthalHaloCells(d)) {
2142 TERMM(1, "Solver " + to_string(m_solverId) + ": Number of azimuthal window cells from domain "
2143 + to_string(azimuthalNeighborDomain(d))
2144 + " does not match local number of azimuthal halo cells; window: " + to_string(noWindowCellsRecv[d])
2145 + " ,halo: " + to_string(noAzimuthalHaloCells(d)));
2146 }
2147 }
2148
2150 // Check #2: grid global ids of azimuthal window/halo cells match
2152 // Start receiving azimuthal window cell global ids from each azimuthal neighbor domain
2153 fill(recvRequests.begin(), recvRequests.end(), MPI_REQUEST_NULL);
2154 const MInt totalNoWindowCellsRecv = accumulate(noWindowCellsRecv.begin(), noWindowCellsRecv.end(), 0);
2155 MIntScratchSpace windowCellsRecv(max(1, totalNoWindowCellsRecv), AT_, "windowCellsRecv");
2156 fill(windowCellsRecv.begin(), windowCellsRecv.end(), -1);
2157 for(MInt d = 0, offset = 0; d < noAzimuthalNeighborDomains(); d++) {
2158 if(noAzimuthalHaloCells(d) > 0) {
2159 MPI_Irecv(&windowCellsRecv[offset], noAzimuthalHaloCells(d), type_traits<MInt>::mpiType(),
2160 azimuthalNeighborDomain(d), azimuthalNeighborDomain(d), mpiComm(), &recvRequests[d], AT_,
2161 "windowCellsRecv[offset]");
2162 }
2163 offset += noAzimuthalHaloCells(d);
2164 }
2165
2166 // Start sending window cell global ids to each neighbor domain
2167 fill(sendRequests.begin(), sendRequests.end(), MPI_REQUEST_NULL);
2168 const MInt totalNoWindowCellsSend = accumulate(noWindowCellsSend.begin(), noWindowCellsSend.end(), 0);
2169 MIntScratchSpace windowCellsSend(max(1, totalNoWindowCellsSend), AT_, "windowCellsSend");
2170 for(MInt d = 0, offset = 0; d < noAzimuthalNeighborDomains(); d++) {
2171 for(MInt c = 0; c < noWindowCellsSend[d]; c++) {
2172 windowCellsSend[offset + c] = raw().treeb().globalId(m_tree.solver2grid(azimuthalWindowCell(d, c)));
2173 }
2174 if(noAzimuthalWindowCells(d) > 0) {
2175 MPI_Isend(&windowCellsSend[offset], noAzimuthalWindowCells(d), type_traits<MInt>::mpiType(),
2176 azimuthalNeighborDomain(d), domainId(), mpiComm(), &sendRequests[d], AT_, "windowCellsSend[offset]");
2177 }
2178 offset += noAzimuthalWindowCells(d);
2179 }
2180
2181 // Finish MPI communication
2182 MPI_Waitall(noAzimuthalNeighborDomains(), &recvRequests[0], MPI_STATUSES_IGNORE, AT_);
2183 MPI_Waitall(noAzimuthalNeighborDomains(), &sendRequests[0], MPI_STATUSES_IGNORE, AT_);
2184
2185 // Check if received window cell global ids match the local halo cell global ids
2186 for(MInt d = 0, offset = 0; d < noAzimuthalNeighborDomains(); d++) {
2187 for(MInt c = 0; c < noAzimuthalHaloCells(d); c++) {
2188 const MInt cellId = m_tree.solver2grid(azimuthalHaloCell(d, c));
2189 const MInt globalId = raw().treeb().globalId(cellId);
2190 if(windowCellsRecv[offset + c] != globalId) {
2191 TERMM(1, "Global id of azimuthal window cell " + to_string(c) + " from domain "
2192 + to_string(azimuthalNeighborDomain(d)) + " does not match local azimuthal halo cell gobal id ("
2193 + to_string(windowCellsRecv[offset + c]) + " vs. "
2194 + to_string(raw().treeb().globalId(m_tree.solver2grid(azimuthalHaloCell(d, c)))) + ")");
2195 }
2196 }
2197 offset += noAzimuthalHaloCells(d);
2198 }
2199}
2200
2201
2202template <MInt nDim>
2204 TRACE();
2205
2206 if(raw().noAzimuthalNeighborDomains() > 0) {
2207 MUint haloCnt = maia::mpi::getBufferSize(raw().azimuthalHaloCells());
2208 MUint windowCnt = maia::mpi::getBufferSize(raw().azimuthalWindowCells());
2209 m_isOutsideWindow.resize(windowCnt);
2210 m_isOutsideHalo.resize(haloCnt);
2211
2212 MInt cnt = 0;
2213 for(MInt d = 0; d < raw().noAzimuthalNeighborDomains(); d++) {
2214 for(MInt c = 0; c < raw().noAzimuthalWindowCells(d); c++) {
2215 const MInt gridWindowId = raw().azimuthalWindowCell(d, c);
2216 MBool outside = checkOutsideGeometry(gridWindowId);
2217 if(outside) { // Fully outside solver domain
2218 m_isOutsideWindow[cnt] = true;
2219 } else {
2220 m_isOutsideWindow[cnt] = false;
2221 }
2222 cnt++;
2223 }
2224 }
2225 mpi::exchangeBuffer(raw().azimuthalNeighborDomains(), raw().azimuthalHaloCells(), raw().azimuthalWindowCells(),
2226 raw().mpiComm(), m_isOutsideWindow.data(), m_isOutsideHalo.data());
2227
2228 cnt = 0;
2229 for(MInt d = 0; d < raw().noAzimuthalNeighborDomains(); d++) {
2230 for(MInt c = 0; c < raw().noAzimuthalHaloCells(d); c++) {
2231 const MInt gridHaloId = raw().azimuthalHaloCell(d, c);
2232 ASSERT(raw().treeb().hasProperty(gridHaloId, Cell::IsPeriodic), "");
2233 MBool outside = checkOutsideGeometry(gridHaloId);
2234 if(outside) { // Halo is fully outside domain
2235 raw().treeb().solver(gridHaloId, solverId()) = false;
2236 m_isOutsideHalo[cnt] = true;
2237 } else {
2238 if(!raw().treeb().solver(gridHaloId, solverId())) {
2239 if(m_isOutsideHalo[cnt]) { // Window is not in solver domain - Make it unmapped
2240 raw().treeb().solver(gridHaloId, solverId()) = true;
2241 m_isOutsideHalo[cnt] = true;
2242 } else { // Window is not refined for solver
2243 m_isOutsideHalo[cnt] = true;
2244 }
2245 } else {
2246 m_isOutsideHalo[cnt] = false;
2247 }
2248 }
2249 cnt++;
2250 }
2251 }
2252 mpi::exchangeBuffer(raw().azimuthalNeighborDomains(), raw().azimuthalWindowCells(), raw().azimuthalHaloCells(),
2253 raw().mpiComm(), m_isOutsideHalo.data(), m_isOutsideWindow.data());
2254 }
2255
2256 for(MInt c = 0; c < raw().noAzimuthalUnmappedHaloCells(); c++) {
2257 const MInt gridHaloId = raw().azimuthalUnmappedHaloCell(c);
2258 ASSERT(raw().treeb().hasProperty(gridHaloId, Cell::IsPeriodic), "");
2259 ASSERT(raw().a_globalId(gridHaloId) == -1, "Unmapped grid halo cell " + std::to_string(gridHaloId)
2260 + " globalId not -1: " + to_string(raw().a_globalId(gridHaloId)));
2261 MBool outside = checkOutsideGeometry(gridHaloId);
2262 if(outside) {
2263 raw().treeb().solver(gridHaloId, solverId()) = false;
2264 }
2265 }
2266 // Correct leaf level
2267 for(MInt c = 0; c < raw().noAzimuthalUnmappedHaloCells(); c++) {
2268 const MInt gridHaloId = raw().azimuthalUnmappedHaloCell(c);
2269 raw().a_isLeafCell(gridHaloId, solverId()) = false;
2270 if(raw().treeb().solver(gridHaloId, solverId())) {
2271 raw().a_isLeafCell(gridHaloId, solverId()) =
2272 !raw().a_hasChildren(gridHaloId, solverId()) && !raw().a_hasProperty(gridHaloId, Cell::IsPartLvlAncestor);
2273 }
2274 }
2275}
2276
2281template <MInt nDim>
2282void Proxy<nDim>::smoothFilter(const MInt level, MFloat** value) {
2283 vector<MInt> levelCellId;
2284 // cout << "finding all cells on level " << level - 1 << endl;
2285 getLocalSameLevelCellIds(levelCellId, level - 1);
2286
2287 // cerr << "test2 " << value[0][0] << endl;
2288 // cerr << "test3 " << value[38252][0] << endl;
2289 // cerr << "test "<< value[38253][0] << endl;
2290
2291 for(auto& cellId : levelCellId) {
2292 const MInt noChilds = tree().noChildren(cellId);
2293 if(noChilds == 0) {
2294 continue;
2295 }
2296
2297 for(MInt childId = 0; childId < noChilds; childId++) {
2298 const MInt noChilds2 = tree().noChildren(tree().child(cellId, childId));
2299 if(noChilds2 == 0) {
2300 continue;
2301 }
2302
2303 for(MInt childId2 = 0; childId2 < noChilds2; childId2++) {
2304 for(MInt dimId = 0; dimId < nDim; dimId++) {
2305 const MInt childCellId2 = tree().child(tree().child(cellId, childId), childId2);
2306 // cerr << "cellId " << cellId << endl;
2307 // cerr << "childId " << tree().child(cellId, childId) << endl;
2308 // cerr << value[cellId][0] << endl;
2309 value[tree().child(cellId, childId)][dimId] += value[childCellId2][dimId] * 1.0 / noChilds2;
2310 // value[cellId][dimId] += 0;
2311 }
2312 }
2313 for(MInt dimId = 0; dimId < nDim; dimId++) {
2314 value[cellId][dimId] += value[tree().child(cellId, childId)][dimId] * 1.0 / noChilds;
2315 }
2316 }
2317
2318 for(MInt childId = 0; childId < noChilds; childId++) {
2319 const MInt noChilds2 = tree().noChildren(tree().child(cellId, childId));
2320 for(MInt childId2 = 0; childId2 < noChilds2; childId2++) {
2321 for(MInt dimId = 0; dimId < nDim; dimId++) {
2322 const MInt childCellId2 = tree().child(tree().child(cellId, childId), childId2);
2323
2324 value[childCellId2][dimId] = 0.4 * value[cellId][dimId] + 0.4 * value[tree().child(cellId, childId)][dimId]
2325 + 0.2 * value[childCellId2][dimId];
2326 // value[tree().child(cellId, childId)][dimId] = value[cellId][dimId];
2327 }
2328 }
2329 for(MInt dimId = 0; dimId < nDim; dimId++) {
2330 value[tree().child(cellId, childId)][dimId] = 0;
2331 }
2332 }
2333
2334 for(MInt dimId = 0; dimId < nDim; dimId++) {
2335 value[cellId][dimId] = 0;
2336 }
2337
2338 // if(domainId() == 0) cout << " cellId " << cellId << " with level " << tree().level(cellId) << " " <<
2339 // value[cellId]
2340 // << endl;
2341 }
2342}
2343
2348template <MInt nDim>
2349void Proxy<nDim>::getLocalSameLevelCellIds(vector<MInt>& levelCellId, const MInt level) {
2350 const MInt noLocalPartitionCells = localPartitionCellOffsets(1) - localPartitionCellOffsets(0);
2351
2352 // go down to searched for level using recursion
2353 std::function<void(MInt)> goDownAndPush = [&](const MInt cellId) {
2354 const MInt cellLevel = tree().level(cellId);
2355 if(level == cellLevel) {
2356 levelCellId.push_back(cellId);
2357 } else {
2358 for(MInt childId = 0; childId < tree().noChildren(cellId); childId++) {
2359 const MInt childCellId = tree().child(cellId, childId);
2360 goDownAndPush(childCellId);
2361 }
2362 }
2363 };
2364
2365 // loop over partition cells
2366 for(MInt i = 0; i < noLocalPartitionCells; i++) {
2367 const MInt partionCellId = localPartitionCellLocalIds(i);
2368 const MInt partionLevel = tree().level(partionCellId);
2369 if(partionLevel == level) {
2370 levelCellId.push_back(partionCellId);
2371 } else if(partionLevel >= level) {
2372 // TODO labels:GRID replace by a more sensible check before the loop (e.g. partitionLevel....)
2373 TERMM(-1, "ERROR: Invalid level!");
2374 }
2375
2376 if(!tree().hasChildren(partionCellId)) {
2377 continue;
2378 }
2379
2380 // start of recursion
2381 for(MInt childId = 0; childId < tree().noChildren(partionCellId); childId++) {
2382 const MInt partitionChildId = tree().child(partionCellId, childId);
2383 goDownAndPush(partitionChildId);
2384 }
2385 }
2386}
2387
2391template <MInt nDim>
2392void Proxy<nDim>::getAllLeafChilds(const MInt parentId, vector<MInt>& levelChilds) {
2393 if(tree().isLeafCell(parentId)) {
2394 levelChilds.push_back(parentId);
2395 return;
2396 }
2397
2398 for(MInt child = 0; child < m_maxNoChilds; child++) {
2399 if(!tree().hasChild(parentId, child)) continue;
2400 const MInt childId = tree().child(parentId, child);
2401 getAllLeafChilds(childId, levelChilds);
2402 }
2403}
2404
2408template <MInt nDim>
2410 TRACE();
2411
2412 MIntScratchSpace solverFlag(raw().maxNoCells(), AT_, "solverFlag");
2413 solverFlag.fill(-1);
2414
2415 for(MInt gridId = 0; gridId < raw().noInternalCells(); gridId++) {
2416 if(raw().a_level(gridId) != raw().minLevel()) {
2417 continue;
2418 }
2419 MBool outside = checkOutsideGeometry(gridId);
2420 MInt flag = (!outside ? 1 : -1);
2421 solverFlag[gridId] = flag;
2422 std::vector<MInt> offsprings(raw().maxNoCells());
2423 raw().createLeafCellMapping(solverId(), gridId, offsprings, true);
2424 for(auto it = offsprings.begin(); it != offsprings.end(); it++) {
2425 solverFlag[*it] = flag;
2426 }
2427 }
2428
2429 for(MInt gridId = 0; gridId < raw().noInternalCells(); gridId++) {
2430 if(solverFlag[gridId] == 1) {
2431 raw().treeb().solver(gridId, solverId()) = true;
2432 } else {
2433 raw().treeb().solver(gridId, solverId()) = false;
2434 }
2435 }
2436
2437 maia::mpi::exchangeData(raw().neighborDomains(), raw().haloCells(), raw().windowCells(), raw().mpiComm(),
2438 solverFlag.data());
2439
2440 for(MInt gridId = raw().noInternalCells(); gridId < raw().maxNoCells(); gridId++) {
2441 if(solverFlag[gridId] == 1) {
2442 raw().treeb().solver(gridId, solverId()) = true;
2443 }
2444 }
2445}
2446
2450template <MInt nDim>
2452 TRACE();
2453
2454 if(m_partitionCellGlobalId != nullptr) {
2455 mDeallocate(m_partitionCellGlobalId);
2456 }
2457
2458 MLong localPartitionCellOffsetsGrid[3] = {static_cast<MLong>(-1), static_cast<MLong>(-1), static_cast<MLong>(-1)};
2459
2460 MBool useRestartOffsets = !raw().updatedPartitionCells();
2461 if(raw().m_localPartitionCellOffsetsRestart[2] < 0) useRestartOffsets = false;
2462
2463 if(useRestartOffsets) {
2464 for(MInt i = 0; i < 3; i++) {
2465 localPartitionCellOffsetsGrid[i] = raw().m_localPartitionCellOffsetsRestart[i];
2466 }
2467 } else {
2468 for(MInt i = 0; i < 3; i++) {
2469 localPartitionCellOffsetsGrid[i] = raw().localPartitionCellOffsets(i);
2470 }
2471 }
2472
2473 mAlloc(m_partitionCellGlobalId, localPartitionCellOffsetsGrid[2], "m_partitionCellGlobalId", static_cast<MLong>(-1),
2474 AT_);
2475
2476 MInt noLocalPartitionCellsGrid = localPartitionCellOffsetsGrid[1] - localPartitionCellOffsetsGrid[0];
2477 MInt noLocalPartitionCells = 0;
2478 MInt partitionGridCellId;
2479 for(MInt id = 0; id < noLocalPartitionCellsGrid; id++) {
2480 if(useRestartOffsets) {
2481 partitionGridCellId = raw().m_localPartitionCellLocalIdsRestart[id];
2482 } else {
2483 partitionGridCellId = raw().localPartitionCellLocalIds(id);
2484 }
2485 if(partitionGridCellId < 0) continue;
2486 if(m_tree.grid2solver(partitionGridCellId) < 0) continue;
2487 m_partitionCellGlobalId[noLocalPartitionCells] = m_tree.globalId(m_tree.grid2solver(partitionGridCellId));
2488 noLocalPartitionCells++;
2489 }
2490
2491 MIntScratchSpace localPartitionCellCounts(noDomains(), AT_, "localPartitionCellCounts");
2492 MPI_Allgather(&noLocalPartitionCells, 1, MPI_INT, &localPartitionCellCounts[0], 1, MPI_INT, mpiComm(), AT_,
2493 "noLocalPartitionCells", "localPartitionCellCounts[0]");
2494 MLong noPartitionCellsGlobal = noLocalPartitionCells;
2495 MPI_Allreduce(MPI_IN_PLACE, &noPartitionCellsGlobal, 1, type_traits<MLong>::mpiType(), MPI_SUM, mpiComm(), AT_,
2496 "MPI_IN_PLACE", "noPartitionCellsGlobal");
2497
2498 MInt offset = 0;
2499 for(MInt dId = 0; dId < domainId(); dId++) {
2500 offset += localPartitionCellCounts[dId];
2501 }
2502
2503 m_localPartitionCellOffsets[0] = offset;
2504 m_localPartitionCellOffsets[1] = offset + noLocalPartitionCells;
2505 m_localPartitionCellOffsets[2] = noPartitionCellsGlobal;
2506}
2507
2517template <MInt nDim>
2519 MInt domain = -1;
2520 for(MInt domainId_ = 0; domainId_ < noDomains(); domainId_++) {
2521 if(globalId >= m_domainOffsets[domainId_] && globalId < m_domainOffsets[domainId_ + 1]) {
2522 domain = domainId_;
2523 break;
2524 }
2525 }
2526 return domain;
2527}
2528
2529} // namespace grid
2530} // namespace maia
2531
2532// Explicit instantiations
2533template class maia::grid::Proxy<2>;
2534template class maia::grid::Proxy<3>;
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
MInt maxNoCells() const
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
This class is a ScratchSpace.
Definition: scratch.h:758
size_type size() const
Definition: scratch.h:302
pointer data()
Definition: scratch.h:289
void fill(T val)
fill the scratch with a given value
Definition: scratch.h:311
iterator end()
Definition: scratch.h:281
iterator begin()
Definition: scratch.h:273
void getLocalSameLevelCellIds(std::vector< MInt > &levelCellId, const MInt level)
find all cells on the same level used by the smoothFilter!
MInt domainContainingCell(MInt globalId) const
domain id containing a given global cell id
MInt determineAzimuthalBoundarySide(const MFloat *coords)
void checkWindowHaloConsistency() const
MInt solverId() const
Return solver id.
void updatePartitionCellOffsets()
updates solver-grid partition cell offsets and globalIds!
void setupWindowHaloConnectivityOnLeafLvl(std::map< MInt, MInt > &)
void findEqualLevelNeighborsParDiagonal(MBool idsAreGlobal=true)
MInt getNghbrCells(MInt cellId, MInt level, MInt *nghbrs, MInt dir0, MInt dir1=-1, MInt dir2=-1)
void descendStoreGlobalId(MInt cellId, MInt &localCnt)
void checkNeighborConsistency() const
void exchangeHaloCellsForVisualizationDG(MFloat *data, const MInt *const polyDegs, const MInt *const dataOffsets)
Exchange DG halo/window cell data for visualization with ParaView.
void exchangeHaloCellsForVisualizationSBP(MFloat *data, const MInt *const noNodes1D, const MInt *const dataOffsets)
Exchange DG-SBP halo/window cell data for visualization with ParaView.
MInt getAdjacentGridCells(MInt cellId, MInt noLayers, MIntScratchSpace &adjacentCells, MInt level, MInt diagonalNeighbors=0)
Retrieves all direct and diagonal neighboring cells of the given cell on the child level if available...
void resizeGridMap(const MInt solverSize)
void setSolverFlagsForAddedSolver()
Set solver flags for newly added solver.
MInt findNeighborDomainId(const MLong globalId)
Find neighbor domain id corresponding to given solver-specific globalId.
Proxy(const MInt solverId, Grid &grid_, Geometry< nDim > &geometry_)
MBool checkOutsideGeometry(MInt gridId)
void smoothFilter(const MInt level, MFloat **value)
smooth the grid-based values over neighboring leaf cell asure conservation of the total value
void checkWindowHaloConsistencyAzimuthal() const
void findNeighborHood(const MInt, const MInt, std::vector< MInt > &)
Obtain list of neighbors for the given extend, using m_storeNghbrIds and m_identNghbrIds.
void checkOffsetConsistency() const
void getAllLeafChilds(const MInt, std::vector< MInt > &)
gathers all leaf child cells for a given parentId
void checkNeighborConsistencyAzimuthal() const
void findDirectNghbrs(const MInt, std::vector< MInt > &)
Obtain list of direct neighbors of given cell. Requires m_identNghbrIds, as such findCartesianNghbIds...
void mTerm(const MInt errorCode, const MString &location, const MString &message)
Definition: functions.cpp:29
MInt ipow(MInt base, MInt exp)
Integer exponent function for non-negative exponents.
Definition: functions.h:317
MInt globalNoDomains()
Return global number of domains.
MInt globalDomainId()
Return global domain id.
MBool g_multiSolverGrid
InfoOutFile m_log
std::ostream cerr0
constexpr MLong IPOW2(MInt x)
int32_t MInt
Definition: maiatypes.h:62
uint32_t MUint
Definition: maiatypes.h:63
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
MInt id
Definition: maiatypes.h:71
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_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_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 cellId
Definition: collector.h:239
MUint getBufferSize(const std::vector< std::vector< MInt > > &exchangeCells)
Generic exchange of data.
Definition: mpiexchange.h:681
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
Namespace for auxiliary functions/classes.