IPPL API Reference
Independent Parallel Particle Layer C++ API
Loading...
Searching...
No Matches
BcTypes.hpp
1// This file contains the abstract base class for
2// field boundary conditions and other child classes
3// which represent specific BCs. At the moment the
4// following field BCs are supported
5//
6// 1. Periodic BC
7// 2. Zero BC
8// 3. Specifying a constant BC
9// 4. No BC (default option)
10// 5. Constant extrapolation BC
11// Only cell-centered field BCs are implemented
12// at the moment.
13//
14
15#include "Utility/IpplException.h"
16
17#include "Field/HaloCells.h"
18
19namespace ippl {
20 namespace detail {
21
22 template <typename Field>
23 BCondBase<Field>::BCondBase(unsigned int face)
24 : face_m(face)
25 , changePhysical_m(false) {}
26
27 template <typename Field>
28 inline std::ostream& operator<<(std::ostream& os, const BCondBase<Field>& bc) {
29 bc.write(os);
30 return os;
31 }
32
33 } // namespace detail
34
35 template <typename Field>
36 void ExtrapolateFace<Field>::apply(Field& field) {
37 // We only support constant extrapolation for the moment, other
38 // higher order extrapolation stuffs need to be added.
39
40 unsigned int face = this->face_m;
41 unsigned d = face / 2;
42 if (field.getCommunicator().size() > 1) {
43 const Layout_t& layout = field.getLayout();
44 const auto& lDomains = layout.getHostLocalDomains();
45 const auto& domain = layout.getDomain();
46 int myRank = field.getCommunicator().rank();
47
48 // Only apply the BC if this rank actually owns this face's global boundary.
49 // Lower face -> local min must match global min; upper face -> local max
50 // must match global max. Otherwise this side is an internal MPI interface
51 // and must be filled by halo exchange, not by the boundary update.
52 const bool isLowerFace = !(face & 1);
53
54 const bool isBoundary = isLowerFace ? (lDomains[myRank][d].min() == domain[d].min())
55 : (lDomains[myRank][d].max() == domain[d].max());
56
57 if (!isBoundary) {
58 return;
59 }
60 }
61
62 // If we are here then it is a processor with the face on the physical
63 // boundary or it is the single core case. Then the following code is same
64 // irrespective of either it is a single core or multi-core case as the
65 // non-periodic BC is local to apply.
66 typename Field::view_type& view = field.getView();
67 const int nghost = field.getNghost();
68 int src, dest;
69
70 // It is not clear what it exactly means to do extrapolate
71 // BC for nghost >1
72 if (nghost > 1) {
73 throw IpplException("ExtrapolateFace::apply", "nghost > 1 not supported");
74 }
75
76 if (d >= Dim) {
77 throw IpplException("ExtrapolateFace::apply", "face number wrong");
78 }
79
80 // If face & 1 is true, then it is an upper BC
81 if (face & 1) {
82 src = view.extent(d) - 2;
83 dest = src + 1;
84 } else {
85 src = 1;
86 dest = src - 1;
87 }
88
89 using exec_space = typename Field::execution_space;
90 using index_type = typename RangePolicy<Dim, exec_space>::index_type;
91 Kokkos::Array<index_type, Dim> begin, end;
92 for (unsigned i = 0; i < Dim; i++) {
93 begin[i] = nghost;
94 end[i] = view.extent(i) - nghost;
95 }
96 begin[d] = src;
97 end[d] = src + 1;
98 using index_array_type = typename RangePolicy<Dim, exec_space>::index_array_type;
99 ippl::parallel_for(
100 "Assign extrapolate BC", createRangePolicy<Dim, exec_space>(begin, end),
101 KOKKOS_CLASS_LAMBDA(index_array_type & args) {
102 // to avoid ambiguity with the member function
103 using ippl::apply;
104
105 T value = apply(view, args);
106
107 args[d] = dest;
108
109 apply(view, args) = slope_m * value + offset_m;
110 });
111 }
112
113 template <typename Field>
114 void ExtrapolateFace<Field>::write(std::ostream& out) const {
115 out << "Constant Extrapolation Face"
116 << ", Face = " << this->face_m;
117 }
118
119 template <typename Field>
120 void NoBcFace<Field>::write(std::ostream& out) const {
121 out << "NoBcFace"
122 << ", Face = " << this->face_m;
123 }
124
125 template <typename Field>
126 void ConstantFace<Field>::write(std::ostream& out) const {
127 out << "ConstantFace"
128 << ", Face = " << this->face_m << ", Constant = " << this->offset_m;
129 }
130
131 template <typename Field>
132 void ZeroFace<Field>::write(std::ostream& out) const {
133 out << "ZeroFace"
134 << ", Face = " << this->face_m;
135 }
136
137 template <typename Field>
138 void PeriodicFace<Field>::write(std::ostream& out) const {
139 out << "PeriodicFace"
140 << ", Face = " << this->face_m;
141 }
142
143 template <typename Field>
144 void PeriodicFace<Field>::findBCNeighbors(Field& field) {
145 auto& comm = field.getCommunicator();
146 // For cell centering only face neighbors are needed
147 unsigned int face = this->face_m;
148 unsigned int d = face / 2;
149 const int nghost = field.getNghost();
150 int myRank = comm.rank();
151 const Layout_t& layout = field.getLayout();
152 const auto& lDomains = layout.getHostLocalDomains();
153 const auto& domain = layout.getDomain();
154
155 for (auto& neighbors : faceNeighbors_m) {
156 neighbors.clear();
157 }
158
159 if (lDomains[myRank][d].length() < domain[d].length()) {
160 // Only along this dimension we need communication.
161
162 bool isBoundary = (lDomains[myRank][d].max() == domain[d].max())
163 || (lDomains[myRank][d].min() == domain[d].min());
164
165 if (isBoundary) {
166 // this face is on mesh/physical boundary
167 // get my local box
168 auto& nd = lDomains[myRank];
169
170 // grow the box by nghost cells in dimension d of face
171 auto gnd = nd.grow(nghost, d);
172
173 int offset;
174 if (face & 1) {
175 // upper face
176 offset = -domain[d].length();
177 } else {
178 // lower face
179 offset = domain[d].length();
180 }
181 // shift by offset
182 gnd[d] = gnd[d] + offset;
183
184 // Now, we are ready to intersect
185 for (int rank = 0; rank < comm.size(); ++rank) {
186 if (rank == myRank) {
187 continue;
188 }
189
190 if (gnd.touches(lDomains[rank])) {
191 faceNeighbors_m[face].push_back(rank);
192 }
193 }
194 }
195 }
196 }
197
198 template <typename Field>
199 void PeriodicFace<Field>::apply(Field& field) {
200 auto& comm = field.getCommunicator();
201 unsigned int face = this->face_m;
202 unsigned int d = face / 2;
203 typename Field::view_type& view = field.getView();
204 const Layout_t& layout = field.getLayout();
205 const int nghost = field.getNghost();
206 int myRank = comm.rank();
207 const auto& lDomains = layout.getHostLocalDomains();
208 const auto& domain = layout.getDomain();
209
210 // We have to put tag here so that the matchtag inside
211 // the if is proper.
212 int tag = comm.next_tag(mpi::tag::BC_PARALLEL_PERIODIC, mpi::tag::BC_CYCLE);
213
214 if (lDomains[myRank][d].length() < domain[d].length()) {
215 // Only along this dimension we need communication.
216
217 bool isBoundary = (lDomains[myRank][d].max() == domain[d].max())
218 || (lDomains[myRank][d].min() == domain[d].min());
219
220 if (isBoundary) {
221 // this face is on mesh/physical boundary
222 // get my local box
223 auto& nd = lDomains[myRank];
224
225 int offset, offsetRecv, matchtag;
226 if (face & 1) {
227 // upper face
228 offset = -domain[d].length();
229 offsetRecv = nghost;
230 matchtag = comm.preceding_tag(mpi::tag::BC_PARALLEL_PERIODIC);
231 } else {
232 // lower face
233 offset = domain[d].length();
234 offsetRecv = -nghost;
235 matchtag = comm.following_tag(mpi::tag::BC_PARALLEL_PERIODIC);
236 }
237
238 auto& neighbors = faceNeighbors_m[face];
239
240 using memory_space = typename Field::memory_space;
241 using buffer_type = mpi::Communicator::buffer_type<memory_space>;
242 std::vector<MPI_Request> requests(neighbors.size());
243
244 using HaloCells_t = typename Field::halo_type;
245 using range_t = typename HaloCells_t::bound_type;
246 HaloCells_t& halo = field.getHalo();
247 std::vector<range_t> rangeNeighbors;
248
249 for (size_t i = 0; i < neighbors.size(); ++i) {
250 int rank = neighbors[i];
251
252 auto ndNeighbor = lDomains[rank];
253 ndNeighbor[d] = ndNeighbor[d] - offset;
254
255 NDIndex<Dim> gndNeighbor = ndNeighbor.grow(nghost, d);
256
257 NDIndex<Dim> overlap = gndNeighbor.intersect(nd);
258
259 range_t range;
260
261 for (size_t j = 0; j < Dim; ++j) {
262 range.lo[j] = overlap[j].first() - nd[j].first() + nghost;
263 range.hi[j] = overlap[j].last() - nd[j].first() + nghost + 1;
264 }
265
266 rangeNeighbors.push_back(range);
267
268 detail::size_type nSends;
269 halo.pack(range, view, haloData_m, nSends);
270
271 buffer_type buf = comm.template getBuffer<memory_space, T>(nSends);
272
273 comm.isend(rank, tag, haloData_m, *buf, requests[i], nSends);
274 buf->resetWritePos();
275 }
276
277 for (size_t i = 0; i < neighbors.size(); ++i) {
278 int rank = neighbors[i];
279
280 range_t range = rangeNeighbors[i];
281
282 range.lo[d] = range.lo[d] + offsetRecv;
283 range.hi[d] = range.hi[d] + offsetRecv;
284
285 detail::size_type nRecvs = range.size();
286
287 buffer_type buf = comm.template getBuffer<memory_space, T>(nRecvs);
288 comm.recv(rank, matchtag, haloData_m, *buf, nRecvs * sizeof(T), nRecvs);
289 buf->resetReadPos();
290
291 using assign_t = typename HaloCells_t::assign;
292 halo.template unpack<assign_t>(range, view, haloData_m);
293 }
294 if (!requests.empty()) {
295 MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);
296 }
297 comm.freeAllBuffers();
298 }
299 // For all other processors do nothing
300 } else {
301 if (d >= Dim) {
302 throw IpplException("PeriodicFace::apply", "face number wrong");
303 }
304
305 auto N = view.extent(d) - 1;
306
307 using exec_space = typename Field::execution_space;
308 using index_type = typename RangePolicy<Dim, exec_space>::index_type;
309 Kokkos::Array<index_type, Dim> begin, end;
310
311 // For the axis along which BCs are being applied, iterate
312 // through only the ghost cells. For all other axes, iterate
313 // through all internal cells.
314 for (size_t i = 0; i < Dim; ++i) {
315 end[i] = view.extent(i) - nghost;
316 begin[i] = nghost;
317 }
318 begin[d] = 0;
319 end[d] = nghost;
320
321 using index_array_type = typename RangePolicy<Dim, exec_space>::index_array_type;
322 ippl::parallel_for(
323 "Assign periodic field BC", createRangePolicy<Dim, exec_space>(begin, end),
324 KOKKOS_CLASS_LAMBDA(index_array_type & coords) {
325 // The ghosts are filled starting from the inside of
326 // the domain proceeding outwards for both lower and
327 // upper faces.
328
329 // to avoid ambiguity with the member function
330 using ippl::apply;
331
332 // x -> nghost + x
333 coords[d] += nghost;
334 auto&& left = apply(view, coords);
335
336 // nghost + x -> N - (nghost + x) = N - nghost - x
337 coords[d] = N - coords[d];
338 auto&& right = apply(view, coords);
339
340 // N - nghost - x -> nghost - 1 - x
341 coords[d] += 2 * nghost - 1 - N;
342 apply(view, coords) = right;
343
344 // nghost - 1 - x -> N - (nghost - 1 - x)
345 // = N - (nghost - 1) + x
346 coords[d] = N - coords[d];
347 apply(view, coords) = left;
348 });
349 }
350 }
351
352 template <typename Field>
353 void PeriodicFace<Field>::assignGhostToPhysical(Field& field) {
354 unsigned int face = this->face_m;
355 unsigned int d = face / 2;
356 typename Field::view_type& view = field.getView();
357 const Layout_t& layout = field.getLayout();
358 const int nghost = field.getNghost();
359 const auto& ldom = layout.getLocalNDIndex();
360 const auto& domain = layout.getDomain();
361
362 if (d >= Dim) {
363 throw IpplException("PeriodicFace::apply", "face number wrong");
364 }
365
366 bool upperFace = (face & 1);
367 bool isBoundary = ((ldom[d].max() == domain[d].max()) && upperFace)
368 || ((ldom[d].min() == domain[d].min()) && !(upperFace));
369
370 if (isBoundary) {
371 auto N = view.extent(d) - 1;
372
373 using exec_space = typename Field::execution_space;
374 using index_type = typename RangePolicy<Dim, exec_space>::index_type;
375 Kokkos::Array<index_type, Dim> begin, end;
376
377 // For the axis along which BCs are being applied, iterate
378 // through only the ghost cells. For all other axes, iterate
379 // through all internal cells.
380 bool isCorner = (d != 0);
381 for (size_t i = 0; i < Dim; ++i) {
382 bool upperFace_i = (ldom[i].max() == domain[i].max());
383 bool lowerFace_i = (ldom[i].min() == domain[i].min());
384 end[i] = view.extent(i) - nghost - (upperFace_i) * (isCorner);
385 begin[i] = nghost + (lowerFace_i) * (isCorner);
386 }
387 begin[d] = ((0 + nghost - 1) * (1 - upperFace)) + (N * upperFace);
388 end[d] = begin[d] + 1;
389
390 using index_array_type = typename RangePolicy<Dim, exec_space>::index_array_type;
391 ippl::parallel_for(
392 "Assign periodic field BC", createRangePolicy<Dim, exec_space>(begin, end),
393 KOKKOS_CLASS_LAMBDA(index_array_type & coords) {
394 // we add the ghost cell values to the appropriate
395 // neighbouring physical boundary cell
396
397 // to avoid ambiguity with the member function
398 using ippl::apply;
399
400 // get the value at ghost cells
401 auto&& right = apply(view, coords);
402
403 // apply to the last physical cells (boundary)
404 int shift = 1 - (2 * upperFace);
405 coords[d] += shift;
406
407 apply(view, coords) += right;
408 });
409 }
410 }
411
412 template <typename Field>
413 void ExtrapolateFace<Field>::assignGhostToPhysical(Field& field) {
414 unsigned int face = this->face_m;
415 unsigned int d = face / 2;
416 typename Field::view_type& view = field.getView();
417 const Layout_t& layout = field.getLayout();
418 const int nghost = field.getNghost();
419 const auto& ldom = layout.getLocalNDIndex();
420 const auto& domain = layout.getDomain();
421
422 if (d >= Dim) {
423 throw IpplException("ExtrapolateFace::apply", "face number wrong");
424 }
425
426 bool upperFace = (face & 1);
427 bool isBoundary = ((ldom[d].max() == domain[d].max()) && upperFace)
428 || ((ldom[d].min() == domain[d].min()) && !(upperFace));
429
430 if (isBoundary) {
431 auto N = view.extent(d) - 1;
432
433 using exec_space = typename Field::execution_space;
434 using index_type = typename RangePolicy<Dim, exec_space>::index_type;
435 Kokkos::Array<index_type, Dim> begin, end;
436
437 // For the axis along which BCs are being applied, iterate
438 // through only the ghost cells. For all other axes, iterate
439 // through all internal cells.
440 for (size_t i = 0; i < Dim; ++i) {
441 end[i] = view.extent(i) - nghost;
442 begin[i] = nghost;
443 }
444 begin[d] = ((0 + nghost - 1) * (1 - upperFace)) + (N * upperFace);
445 end[d] = begin[d] + 1;
446
447 using index_array_type = typename RangePolicy<Dim, exec_space>::index_array_type;
448 ippl::parallel_for(
449 "Assign field BC", createRangePolicy<Dim, exec_space>(begin, end),
450 KOKKOS_CLASS_LAMBDA(index_array_type & coords) {
451 // we assign the ghost cell values to the appropriate
452 // neighbouring physical boundary cell
453
454 // to avoid ambiguity with the member function
455 using ippl::apply;
456
457 // get the value at ghost cells
458 auto&& right = apply(view, coords);
459
460 // apply to the last physical cells (boundary)
461 int shift = 1 - (2 * upperFace);
462
463 coords[d] += shift;
464
465 apply(view, coords) = right;
466 });
467 }
468 }
469} // namespace ippl
Definition IpplException.h:6
void pack(const bound_type &range, const view_type &view, databuffer_type &fd, size_type &nsends)
Definition HaloCells.hpp:158
Definition Archive.h:20
KOKKOS_INLINE_FUNCTION constexpr decltype(auto) apply(const View &view, const Coords &coords)
Definition IpplOperations.h:64