OPAL (Object Oriented Parallel Accelerator Library) 2024.2
OPAL
BoxLibLayout.hpp
Go to the documentation of this file.
1//
2// Class BoxLibLayout
3// In contrast to AMReX, OPAL is optimized for the
4// distribution of particles to cores. In AMReX the ParGDB object
5// is responsible for the particle to core distribution. This
6// layout is derived from this object and does all important
7// bunch updates. It is the interface for AMReX and Ippl.
8//
9// In AMReX, the geometry, i.e. physical domain, is fixed
10// during the whole computation. Particles leaving the domain
11// would be deleted. In order to prevent this we map the particles
12// onto the domain [-1, 1]^3. Furthermore, it makes sure
13// that we have enougth grid points to represent the bunch
14// when its charges are scattered on the grid for the self-field
15// computation.
16//
17// The self-field computation and the particle-to-core update
18// are performed in the particle mapped domain.
19//
20// Copyright (c) 2016 - 2020, Matthias Frey, Uldis Locans, Paul Scherrer Institut, Villigen PSI, Switzerland
21// All rights reserved
22//
23// Implemented as part of the PhD thesis
24// "Precise Simulations of Multibunches in High Intensity Cyclotrons"
25//
26// This file is part of OPAL.
27//
28// OPAL is free software: you can redistribute it and/or modify
29// it under the terms of the GNU General Public License as published by
30// the Free Software Foundation, either version 3 of the License, or
31// (at your option) any later version.
32//
33// You should have received a copy of the GNU General Public License
34// along with OPAL. If not, see <https://www.gnu.org/licenses/>.
35//
36#ifndef BoxLibLayout_HPP
37#define BoxLibLayout_HPP
38
39#include "Amr/BoxLibLayout.h"
40
41#include "Algorithms/Vektor.h"
42#include "Message/Format.h"
43#include "Message/MsgBuffer.h"
44#include "Utility/PAssert.h"
46
47#include <cmath>
48#include <utility>
49#include <vector>
50
51
52template <class T, unsigned Dim>
54
55
56template <class T, unsigned Dim>
58
59
60template<class T, unsigned Dim>
62 : ParticleAmrLayout<T, Dim>(),
63 ParGDB(),
64 refRatio_m(0)
65{
66 /* FIXME There might be a better solution
67 *
68 *
69 * Figure out the number of grid points in each direction
70 * such that all processes have some data at the beginning
71 *
72 * ( nGridPoints / maxGridSize ) ^3 = max. #procs
73 *
74 */
75 int nProcs = Ippl::getNodes();
76 int maxGridSize = 16;
77
78 int nGridPoints = std::ceil( std::cbrt( nProcs ) ) * maxGridSize;
79
80 this->initBaseBox_m(nGridPoints, maxGridSize);
81}
82
83
84template<class T, unsigned Dim>
86 : ParticleAmrLayout<T, Dim>(),
87 ParGDB(layout_p->m_geom,
88 layout_p->m_dmap,
89 layout_p->m_ba,
90 layout_p->m_rr)
91{
92 this->maxLevel_m = layout_p->maxLevel_m;
93 refRatio_m.resize(layout_p->m_geom.size()-1);
94 for (int i = 0; i < refRatio_m.size(); ++i)
95 refRatio_m[i] = layout_p->refRatio(i);
96}
97
98
99template<class T, unsigned Dim>
100BoxLibLayout<T, Dim>::BoxLibLayout(int nGridPoints, int maxGridSize)
101 : ParticleAmrLayout<T, Dim>(),
102 ParGDB(),
103 refRatio_m(0)
104{
105 this->initBaseBox_m(nGridPoints, maxGridSize);
106}
107
108
109template<class T, unsigned Dim>
111 const AmrProcMap_t &dmap,
112 const AmrGrid_t &ba)
113 : ParticleAmrLayout<T, Dim>(),
114 ParGDB(geom, dmap, ba),
115 refRatio_m(0)
116{ }
117
118
119template<class T, unsigned Dim>
121 const AmrProcMapContainer_t &dmap,
122 const AmrGridContainer_t &ba,
123 const AmrIntArray_t &rr)
124 : ParticleAmrLayout<T, Dim>(),
125 ParGDB(geom, dmap, ba, rr),
126 refRatio_m(0)
127{ }
128
129
130template<class T, unsigned Dim>
132
133 // cubic box
134 int nGridPoints = this->m_geom[0].Domain().length(0);
135 int maxGridSize = this->m_ba[0][0].length(0);
136
137 this->initBaseBox_m(nGridPoints, maxGridSize, dh);
138}
139
140
141template <class T, unsigned Dim>
142void BoxLibLayout<T, Dim>::setDomainRatio(const std::vector<double>& ratio) {
143
144 static bool isCalled = false;
145
146 if ( isCalled ) {
147 return;
148 }
149
150 isCalled = true;
151
152 if ( ratio.size() != Dim ) {
153 throw OpalException("BoxLibLayout::setDomainRatio() ",
154 "Length " + std::to_string(ratio.size()) +
155 " != " + std::to_string(Dim));
156 }
157
158 for (unsigned int i = 0; i < Dim; ++i) {
159 if ( ratio[i] <= 0.0 ) {
160 throw OpalException("BoxLibLayout::setDomainRatio() ",
161 "The ratio has to be larger than zero.");
162 }
163
164 lowerBound[i] *= ratio[i];
165 upperBound[i] *= ratio[i];
166 }
167}
168
169
170template<class T, unsigned Dim>
172 const ParticleAttrib<char>* /*canSwap*/)
173{
174 /* Exit since we need AmrParticleBase with grids and levels for particles for this layout
175 * if IpplParticleBase is used something went wrong
176 */
177 throw OpalException("BoxLibLayout::update(IpplParticleBase, ParticleAttrib) ",
178 "Wrong update method called.");
179}
180
181
182// // Function from AMReX adjusted to work with Ippl AmrParticleBase class
183// // redistribute the particles using BoxLibs ParGDB class to determine where particle should go
184template<class T, unsigned Dim>
186 int lev_min, int lev_max, bool isRegrid)
187{
188 // in order to avoid transforms when already done
189 if ( !PData.isForbidTransform() ) {
190 /* we need to update on Amr domain + boosted frame (Lorentz transform,
191 * has to be undone at end of function
192 */
193 PData.domainMapping();
194 }
195
196 int nGrow = 0;
197
198 unsigned N = Ippl::getNodes();
199 unsigned myN = Ippl::myNode();
200
201 int theEffectiveFinestLevel = this->finestLevel();
202 while (!this->LevelDefined(theEffectiveFinestLevel)) {
203 theEffectiveFinestLevel--;
204 }
205
206 if (lev_max == -1)
207 lev_max = theEffectiveFinestLevel;
208 else if ( lev_max > theEffectiveFinestLevel )
209 lev_max = theEffectiveFinestLevel;
210
211 //loop trough the particles and assign the grid and level where each particle belongs
212 size_t LocalNum = PData.getLocalNum();
213
214 auto& LocalNumPerLevel = PData.getLocalNumPerLevel();
215
216 if ( LocalNum != LocalNumPerLevel.getLocalNumAllLevel() )
217 throw OpalException("BoxLibLayout::update()",
218 "Local #particles disagrees with sum over levels");
219
220 std::multimap<unsigned, unsigned> p2n; //node ID, particle
221
222 std::vector<int> msgsend(N);
223 std::vector<int> msgrecv(N);
224
225 unsigned sent = 0;
226 size_t lBegin = LocalNumPerLevel.begin(lev_min);
227 size_t lEnd = LocalNumPerLevel.end(lev_max);
228
229 /* in the case of regrid we might lose a level
230 * and therefore the level counter is invalidated
231 */
232 if ( isRegrid ) {
233 lBegin = 0;
234 lEnd = LocalNum;
235 }
236
237
238 //loop trough particles and assign grid and level to each particle
239 //if particle doesn't belong to this process save the index of the particle to be sent
240 for (unsigned int ip = lBegin; ip < lEnd; ++ip) {
241 // old level
242 const size_t& lold = PData.Level[ip];
243
244// /*
245// * AMReX sets m_grid = -1 and m_lev = -1
246// */
247// PData.Level[ip] = -1;
248// PData.Grid[ip] = -1;
249
250 //check to which level and grid the particle belongs to
251 locateParticle(PData, ip, lev_min, lev_max, nGrow);
252
253 // The owner of the particle is the CPU owning the finest grid
254 // in state data that contains the particle.
255 const size_t& lnew = PData.Level[ip];
256
257 const unsigned int who = ParticleDistributionMap(lnew)[PData.Grid[ip]];
258
259 --LocalNumPerLevel[lold];
260
261 if (who != myN) {
262 // we lost the particle to another process
263 msgsend[who] = 1;
264 p2n.insert(std::pair<unsigned, unsigned>(who, ip));
265 sent++;
266 } else {
267 /* if we still own the particle it may have moved to
268 * another level
269 */
270 ++LocalNumPerLevel[lnew];
271 }
272 }
273
274 //reduce message count so every node knows how many messages to receive
275 allreduce(msgsend.data(), msgrecv.data(), N, std::plus<int>());
276
278
279 typename std::multimap<unsigned, unsigned>::iterator i = p2n.begin();
280
281 Format *format = PData.getFormat();
282
283 std::vector<MPI_Request> requests;
284 std::vector<MsgBuffer*> buffers;
285
286 //create a message and send particles to nodes they belong to
287 while (i!=p2n.end()) {
288 unsigned cur_destination = i->first;
289
290 MsgBuffer *msgbuf = new MsgBuffer(format, p2n.count(i->first));
291
292 for (; i!=p2n.end() && i->first == cur_destination; ++i) {
293 Message msg;
294 PData.putMessage(msg, i->second);
295 PData.destroy(1, i->second);
296 msgbuf->add(&msg);
297 }
298
299 MPI_Request request = Ippl::Comm->raw_isend(msgbuf->getBuffer(),
300 msgbuf->getSize(),
301 cur_destination, tag);
302
303 //remember request and buffer so we can delete them later
304 requests.push_back(request);
305 buffers.push_back(msgbuf);
306
307 }
308
309 //destroy the particles that are sent to other domains
310 if ( LocalNum < PData.getDestroyNum() ) {
311 throw OpalException("BoxLibLayout::update()",
312 "Rank " + std::to_string(myN) +
313 " can't destroy more particles than possessed.");
314 } else {
315 LocalNum -= PData.getDestroyNum(); // update local num
316 PData.performDestroy();
317 }
318
319 for (int lev = lev_min; lev <= lev_max; ++lev) {
320 if ( LocalNumPerLevel[lev] < 0 ) {
321 throw OpalException("BoxLibLayout::update()",
322 "Negative particle level count.");
323 }
324 }
325
326 //receive new particles
327 for (int k = 0; k<msgrecv[myN]; ++k) {
329 char *buffer = 0;
330 int bufsize = Ippl::Comm->raw_probe_receive(buffer, node, tag);
331 MsgBuffer recvbuf(format, buffer, bufsize);
332
333 Message *msg = recvbuf.get();
334 while (msg != 0) {
335 /* pBeginIdx is the start index of the new particle data
336 * pEndIdx is the last index of the new particle data
337 */
338 size_t pBeginIdx = LocalNum;
339
340 LocalNum += PData.getSingleMessage(*msg);
341
342 size_t pEndIdx = LocalNum;
343
344 for (size_t idx = pBeginIdx; idx < pEndIdx; ++idx)
345 ++LocalNumPerLevel[ PData.Level[idx] ];
346
347 delete msg;
348 msg = recvbuf.get();
349 }
350 }
351
352 //wait for communication to finish and clean up buffers
353 MPI_Request* requests_ptr = requests.empty()? static_cast<MPI_Request*>(0): &(requests[0]);
354 MPI_Waitall(requests.size(), requests_ptr, MPI_STATUSES_IGNORE);
355 for (unsigned int j = 0; j<buffers.size(); ++j) {
356 delete buffers[j];
357 }
358
359 delete format;
360
361 // there is extra work to do if there are multipple nodes, to distribute
362 // the particle layout data to all nodes
363 //TODO: do we need info on how many particles are on each node?
364
365 //save how many total particles we have
366 size_t TotalNum = 0;
367 allreduce(&LocalNum, &TotalNum, 1, std::plus<size_t>());
368
369 // update our particle number counts
370 PData.setTotalNum(TotalNum); // set the total atom count
371 PData.setLocalNum(LocalNum); // set the number of local atoms
372
373 // final check
374 if ( LocalNum != LocalNumPerLevel.getLocalNumAllLevel() )
375 throw OpalException("BoxLibLayout::update()",
376 "Local #particles disagrees with sum over levels");
377
378 if ( !PData.isForbidTransform() ) {
379 // undo domain transformation + undo Lorentz transform
380 PData.domainMapping(true);
381 }
382}
383
384
385// Function from AMReX adjusted to work with Ippl AmrParticleBase class
386//get the cell where particle is located - uses AmrParticleBase object and particle id
387template <class T, unsigned Dim>
390 const unsigned int ip,
391 int lev) const
392{
393 return Index(p.R[ip], lev);
394}
395
396//get the cell where particle is located - uses the particle position vector R
397template <class T, unsigned Dim>
400 int lev) const
401{
402 AmrIntVect_t iv;
403 const AmrGeometry_t& geom = Geom(lev);
404
405 D_TERM(iv[0]=floor((R[0]-geom.ProbLo(0))/geom.CellSize(0));,
406 iv[1]=floor((R[1]-geom.ProbLo(1))/geom.CellSize(1));,
407 iv[2]=floor((R[2]-geom.ProbLo(2))/geom.CellSize(2)););
408
409 iv += geom.Domain().smallEnd();
410
411 return iv;
412}
413
414
415template <class T, unsigned Dim>
416void BoxLibLayout<T, Dim>::buildLevelMask(int lev, const int ncells) {
417 int covered = 0;
418 int notcovered = 1;
419 int physbnd = 1;
420 int interior = 0;
421
422 if ( lev >= (int)masks_m.size() )
423 masks_m.resize(lev + 1);
424
425 masks_m[lev].reset(new mask_t(ParticleBoxArray(lev),
426 ParticleDistributionMap(lev), 1, 1));
427
428 masks_m[lev]->setVal(1, 1);
429
430 mask_t tmp_mask(ParticleBoxArray(lev),
431 ParticleDistributionMap(lev),
432 1, ncells);
433
434 tmp_mask.setVal(0, ncells);
435
436 tmp_mask.BuildMask(Geom(lev).Domain(), Geom(lev).periodicity(),
437 covered, notcovered, physbnd, interior);
438
439 tmp_mask.FillBoundary(Geom(lev).periodicity());
440
441 for (amrex::MFIter mfi(tmp_mask); mfi.isValid(); ++mfi) {
442 const AmrBox_t& bx = mfi.validbox();
443 const int* lo = bx.loVect();
444 const int* hi = bx.hiVect();
445
446 basefab_t& mfab = (*masks_m[lev])[mfi];
447 const basefab_t& fab = tmp_mask[mfi];
448
449 for (int i = lo[0]; i <= hi[0]; ++i) {
450 for (int j = lo[1]; j <= hi[1]; ++j) {
451 for (int k = lo[2]; k <= hi[2]; ++k) {
452 int total = 0;
453
454 for (int ii = i - ncells; ii <= i + ncells; ++ii) {
455 for (int jj = j - ncells; jj <= j + ncells; ++jj) {
456 for (int kk = k - ncells; kk <= k + ncells; ++kk) {
457 AmrIntVect_t iv(ii, jj, kk);
458 total += fab(iv);
459 }
460 }
461 }
462
463 AmrIntVect_t iv(i, j, k);
464 if (total == 0) {
465 mfab(iv) = 0;
466 }
467 }
468 }
469 }
470 }
471
472 masks_m[lev]->FillBoundary(Geom(lev).periodicity());
473}
474
475
476template <class T, unsigned Dim>
478 PAssert(lev < (int)masks_m.size());
479 masks_m[lev].reset(nullptr);
480}
481
482
483template <class T, unsigned Dim>
484const std::unique_ptr<typename BoxLibLayout<T, Dim>::mask_t>&
486 if ( lev >= (int)masks_m.size() ) {
487 throw OpalException("BoxLibLayout::getLevelMask()",
488 "Unable to access level " + std::to_string(lev) + ".");
489 }
490 return masks_m[lev];
491}
492
493
494// template <class T, unsigned Dim>
495// int BoxLibLayout<T, Dim>::getTileIndex(const AmrIntVect_t& iv, const Box& box, Box& tbx) {
496// if (do_tiling == false) {
497// tbx = box;
498// return 0;
499// } else {
500// //
501// // This function must be consistent with FabArrayBase::buildTileArray function!!!
502// //
503// auto tiling_1d = [](int i, int lo, int hi, int tilesize,
504// int& ntile, int& tileidx, int& tlo, int& thi) {
505// int ncells = hi-lo+1;
506// ntile = std::max(ncells/tilesize, 1);
507// int ts_right = ncells/ntile;
508// int ts_left = ts_right+1;
509// int nleft = ncells - ntile*ts_right;
510// int ii = i - lo;
511// int nbndry = nleft*ts_left;
512// if (ii < nbndry) {
513// tileidx = ii / ts_left; // tiles on the left of nbndry have size of ts_left
514// tlo = lo + tileidx * ts_left;
515// thi = tlo + ts_left - 1;
516// } else {
517// tileidx = nleft + (ii-nbndry) / ts_right; // tiles on the right: ts_right
518// tlo = lo + tileidx * ts_right + nleft;
519// thi = tlo + ts_right - 1;
520// }
521// };
522// const AmrIntVect_t& small = box.smallEnd();
523// const AmrIntVect_t& big = box.bigEnd();
524// AmrIntVect_t ntiles, ivIndex, tilelo, tilehi;
525//
526// D_TERM(int iv0 = std::min(std::max(iv[0], small[0]), big[0]);,
527// int iv1 = std::min(std::max(iv[1], small[1]), big[1]);,
528// int iv2 = std::min(std::max(iv[2], small[2]), big[2]););
529//
530// D_TERM(tiling_1d(iv0, small[0], big[0], tile_size[0], ntiles[0], ivIndex[0], tilelo[0], tilehi[0]);,
531// tiling_1d(iv1, small[1], big[1], tile_size[1], ntiles[1], ivIndex[1], tilelo[1], tilehi[1]);,
532// tiling_1d(iv2, small[2], big[2], tile_size[2], ntiles[2], ivIndex[2], tilelo[2], tilehi[2]););
533//
534// tbx = Box(tilelo, tilehi);
535//
536// return D_TERM(ivIndex[0], + ntiles[0]*ivIndex[1], + ntiles[0]*ntiles[1]*ivIndex[2]);
537// }
538// }
539
540
541//sets the grid and level where particle belongs - returns false if particle is outside the domain
542template <class T, unsigned Dim>
544 const unsigned int ip,
545 int lev_min,
546 int lev_max,
547 int nGrow) const
548{
549
550 if (lev_max == -1)
551 lev_max = finestLevel();
552
553 PAssert(lev_max <= finestLevel());
554
555 PAssert(nGrow == 0 || (nGrow >= 0 && lev_min == lev_max));
556
557 std::vector< std::pair<int, AmrBox_t> > isects;
558
559 for (int lev = lev_max; lev >= lev_min; lev--)
560 {
561 const AmrIntVect_t& iv = Index(p, ip, lev);
562 const AmrGrid_t& ba = ParticleBoxArray(lev);
563 PAssert(ba.ixType().cellCentered());
564
565 if (lev == (int)p.Level[ip]) {
566 // The fact that we are here means this particle does not belong to any finer grids.
567 if (0 <= p.Grid[ip] && p.Grid[ip] < ba.size()) {
568 const AmrBox_t& bx = ba.getCellCenteredBox(p.Grid[ip]);
569 const AmrBox_t& gbx = amrex::grow(bx,nGrow);
570 if (gbx.contains(iv)) {
571// if (bx != pld.m_gridbox || !pld.m_tilebox.contains(iv)) {
572// pld.m_tile = getTileIndex(iv, bx, pld.m_tilebox);
573// pld.m_gridbox = bx;
574// }
575 return true;
576 }
577 }
578 }
579
580 ba.intersections(AmrBox_t(iv, iv), isects, true, nGrow);
581
582 if (!isects.empty()) {
583 p.Level[ip] = lev;
584 p.Grid[ip] = isects[0].first;
585
586 return true;
587 }
588 }
589 return false;
590}
591
592
593//Function from AMReX adjusted to work with Ippl AmrParticleBase class
594//Checks/sets whether the particle has crossed a periodic boundary in such a way
595//that it is on levels lev_min and higher.
596template <class T, unsigned Dim>
598 const unsigned int ip,
599 int lev_min,
600 int lev_max) const
601{
602 if (!Geom(0).isAnyPeriodic()) return false;
603
604 if (lev_max == -1)
605 lev_max = finestLevel();
606
607 PAssert(lev_max <= finestLevel());
608 //
609 // Create a copy "dummy" particle to check for periodic outs.
610 //
611 SingleParticlePos_t R = p.R[ip];
612
613 if (PeriodicShift(R)) {
614 std::vector< std::pair<int, AmrBox_t> > isects;
615
616 for (int lev = lev_max; lev >= lev_min; lev--) {
617 const AmrIntVect_t& iv = Index(R, lev);
618 const AmrGrid_t& ba = ParticleBoxArray(lev);
619
620 ba.intersections(AmrBox_t(iv,iv),isects,true,0);
621
622 if (!isects.empty()) {
623 D_TERM(p.R[ip][0] = R[0];,
624 p.R[ip][1] = R[1];,
625 p.R[ip][2] = R[2];);
626
627 p.Level[ip] = lev;
628 p.Grid[ip] = isects[0].first;
629
630 return true;
631 }
632 }
633 }
634
635 return false;
636}
637
638
639// Function from AMReX adjusted to work with Ippl AmrParticleBase class
640// Returns true if the particle was shifted.
641template <class T, unsigned Dim>
643{
644 //
645 // This routine should only be called when Where() returns false.
646 //
647 //
648 // We'll use level 0 stuff since ProbLo/ProbHi are the same for every level.
649 //
650 const AmrGeometry_t& geom = Geom(0);
651 const AmrBox_t& dmn = geom.Domain();
652 const AmrIntVect_t& iv = Index(R, 0);
653 bool shifted = false;
654
655 for (int i = 0; i < AMREX_SPACEDIM; i++) {
656 if (!geom.isPeriodic(i)) continue;
657
658 if (iv[i] > dmn.bigEnd(i)) {
659 if (R[i] == geom.ProbHi(i)) {
660 //
661 // Don't let particles lie exactly on the domain face.
662 // Force the particle to be outside the domain so the
663 // periodic shift will bring it back inside.
664 //
665 R[i] += .125*geom.CellSize(i);
666 }
667 R[i] -= geom.ProbLength(i);
668
669 if (R[i] <= geom.ProbLo(i))
670 //
671 // This can happen due to precision issues.
672 //
673 R[i] += .125*geom.CellSize(i);
674
675 PAssert(R[i] >= geom.ProbLo(i));
676
677 shifted = true;
678
679 } else if (iv[i] < dmn.smallEnd(i)) {
680 if (R[i] == geom.ProbLo(i)) {
681 //
682 // Don't let particles lie exactly on the domain face.
683 // Force the particle to be outside the domain so the
684 // periodic shift will bring it back inside.
685 //
686 R[i] -= .125*geom.CellSize(i);
687 }
688 R[i] += geom.ProbLength(i);
689
690 if (R[i] >= geom.ProbHi(i)) {
691 //
692 // This can happen due to precision issues.
693 //
694 R[i] -= .125*geom.CellSize(i);
695 }
696 PAssert(R[i] <= geom.ProbHi(i));
697
698 shifted = true;
699 }
700 }
701 //
702 // The particle may still be outside the domain in the case
703 // where we aren't periodic on the face out which it travelled.
704 //
705 return shifted;
706}
707
708
709template <class T, unsigned Dim>
712 const unsigned int ip,
713 int lev_min, int lev_max, int nGrow) const
714{
715 bool outside = D_TERM( p.R[ip](0) < AmrGeometry_t::ProbLo(0)
716 || p.R[ip](0) >= AmrGeometry_t::ProbHi(0),
717 || p.R[ip](1) < AmrGeometry_t::ProbLo(1)
718 || p.R[ip](1) >= AmrGeometry_t::ProbHi(1),
719 || p.R[ip](2) < AmrGeometry_t::ProbLo(2)
720 || p.R[ip](2) >= AmrGeometry_t::ProbHi(2));
721
722 bool success = false;
723
724 if (outside) {
725 // Note that EnforcePeriodicWhere may shift the particle if it is successful.
726 success = EnforcePeriodicWhere(p, ip, lev_min, lev_max);
727 if (!success && lev_min == 0) {
728 // The particle has left the domain; invalidate it.
729 p.destroy(1, ip);
730 success = true;
731
732 /* We shouldn't lose particles since they are mapped to be within
733 * [-1, 1]^3.
734 */
735 throw OpalException("BoxLibLayout::locateParticle()",
736 "We're losing particles although we shouldn't");
737
738 }
739 } else {
740 success = Where(p, ip, lev_min, lev_max);
741 }
742
743 if (!success) {
744 success = (nGrow > 0) && Where(p, ip, lev_min, lev_min, nGrow);
745 }
746
747 if (!success) {
748 std::stringstream ss;
749 ss << "Invalid particle with ID " << ip << " at position " << p.R[ip] << ".";
750 throw OpalException("BoxLibLayout::locateParticle()", ss.str());
751 }
752}
753
754
755// overwritten functions
756template <class T, unsigned Dim>
758 return level <= this->maxLevel_m && !m_ba[level].empty() && !m_dmap[level].empty();
759}
760
761
762template <class T, unsigned Dim>
764 return this->finestLevel_m;
765}
766
767
768template <class T, unsigned Dim>
770 return this->maxLevel_m;
771}
772
773
774template <class T, unsigned Dim>
777 return refRatio_m[level];
778}
779
780
781template <class T, unsigned Dim>
783 int maxval = 0;
784 for (int n = 0; n<AMREX_SPACEDIM; n++)
785 maxval = std::max(maxval, refRatio_m[level][n]);
786 return maxval;
787}
788
789
790template <class T, unsigned Dim>
792 int maxGridSize,
793 double dh) {
794 // physical box (in meters)
795 AmrDomain_t real_box;
796 for (int d = 0; d < AMREX_SPACEDIM; ++d) {
797
798 PAssert(lowerBound[d] < 0);
799 PAssert(upperBound[d] > 0);
800
801 real_box.setLo(d, lowerBound[d] * (1.0 + dh));
802 real_box.setHi(d, upperBound[d] * (1.0 + dh));
803 }
804
805 AmrGeometry_t::ProbDomain(real_box);
806
807 // define underlying box for physical domain
808 AmrIntVect_t domain_lo(0 , 0, 0);
809 AmrIntVect_t domain_hi(nGridPoints - 1, nGridPoints - 1, nGridPoints - 1);
810 const AmrBox_t domain(domain_lo, domain_hi);
811
812 // use Cartesian coordinates
813 int coord = 0;
814
815 // Dirichlet boundary conditions
816 int is_per[AMREX_SPACEDIM];
817 for (int i = 0; i < AMREX_SPACEDIM; i++)
818 is_per[i] = 0;
819
820 AmrGeometry_t geom;
821 geom.define(domain, &real_box, coord, is_per);
822
823 AmrGrid_t ba;
824 ba.define(domain);
825 // break the BoxArrays at both levels into max_grid_size^3 boxes
826 ba.maxSize(maxGridSize);
827
828 AmrProcMap_t dmap;
829 dmap.define(ba, Ippl::getNodes());
830
831 // set protected ParGDB member variables
832 this->m_geom.resize(1);
833 this->m_geom[0] = geom;
834
835 this->m_dmap.resize(1);
836 this->m_dmap[0] = dmap;
837
838 this->m_ba.resize(1);
839 this->m_ba[0] = ba;
840
841 this->m_nlevels = ba.size();
842}
843
844#endif
const unsigned Dim
void allreduce(const T *input, T *output, int count, Op op)
#define P_SPATIAL_TRANSFER_TAG
Definition Tags.h:82
#define P_LAYOUT_CYCLE
Definition Tags.h:86
PETE_TUTree< FnFloor, typename T::PETE_Expr_t > floor(const PETE_Expr< T > &l)
Definition PETE.h:733
#define PAssert(c)
Definition PAssert.h:102
void setBoundingBox(double dh)
amr::AmrBox_t AmrBox_t
amr::AmrProcMapContainer_t AmrProcMapContainer_t
amr::AmrDomain_t AmrDomain_t
const std::unique_ptr< mask_t > & getLevelMask(int lev) const
void setDomainRatio(const std::vector< double > &ratio)
void locateParticle(AmrParticleBase< BoxLibLayout< T, Dim > > &p, const unsigned int ip, int lev_min, int lev_max, int nGrow) const
AmrIntVectContainer_t refRatio_m
static Vector_t lowerBound
void buildLevelMask(int lev, const int ncells=1)
void initBaseBox_m(int nGridPoints, int maxGridSize, double dh=0.04)
bool LevelDefined(int level) const
void clearLevelMask(int lev)
amr::AmrIntVect_t AmrIntVect_t
amr::AmrIntArray_t AmrIntArray_t
amr::AmrGeometry_t AmrGeometry_t
ParticleAmrLayout< T, Dim >::SingleParticlePos_t SingleParticlePos_t
int maxLevel() const
amrex::BaseFab< int > basefab_t
amrex::FabArray< basefab_t > mask_t
amr::AmrGeomContainer_t AmrGeomContainer_t
bool PeriodicShift(SingleParticlePos_t R) const
static Vector_t upperBound
amr::AmrGrid_t AmrGrid_t
void update(IpplParticleBase< BoxLibLayout< T, Dim > > &PData, const ParticleAttrib< char > *canSwap=0)
amr::AmrGridContainer_t AmrGridContainer_t
bool EnforcePeriodicWhere(AmrParticleBase< BoxLibLayout< T, Dim > > &prt, const unsigned int ip, int lev_min=0, int lev_max=-1) const
AmrIntVect_t Index(AmrParticleBase< BoxLibLayout< T, Dim > > &p, const unsigned int ip, int level) const
std::vector< std::unique_ptr< mask_t > > masks_m
Refinement ratios [0:finest_level-1].
int finestLevel() const
amr::AmrProcMap_t AmrProcMap_t
int MaxRefRatio(int level) const
AmrIntVect_t refRatio(int level) const
bool Where(AmrParticleBase< BoxLibLayout< T, Dim > > &p, const unsigned int ip, int lev_min=0, int lev_max=-1, int nGrow=0) const
The base class for all OPAL exceptions.
int finestLevel_m
Current finest level of simluation.
int maxLevel_m
Maximum level allowed.
void * getBuffer()
Definition MsgBuffer.h:54
bool add(Message *)
Definition MsgBuffer.cpp:44
int getSize()
Definition MsgBuffer.h:50
Message * get()
Definition MsgBuffer.cpp:71
static int getNodes()
Definition IpplInfo.cpp:670
static int myNode()
Definition IpplInfo.cpp:691
static Communicate * Comm
Definition IpplInfo.h:84
Vektor< double, 3 > Vector_t