IPPL (Independent Parallel Particle Layer)
IPPL
Loading...
Searching...
No Matches
AbsorbingBC.h
Go to the documentation of this file.
1#ifndef ABC_H
2#define ABC_H
3#include <Kokkos_Core.hpp>
4
5#include "Types/Vector.h"
6
7#include "Field/Field.h"
8
9template <unsigned a, unsigned b>
10constexpr KOKKOS_INLINE_FUNCTION auto first() {
11 return a;
12}
13template <unsigned a, unsigned b>
14constexpr KOKKOS_INLINE_FUNCTION auto second() {
15 return b;
16}
17
18/* @struct second_order_abc_face
19 * @brief Implements a second-order absorbing boundary condition (ABC) for a face of a 3D
20 * computational domain.
21 *
22 * This struct computes and applies the second-order Mur ABC on a specified face of the domain,
23 * minimizing reflections for electromagnetic field solvers. The face is defined by its main axis
24 * (the normal direction) and two side axes (the tangential directions).
25 *
26 * The struct precomputes weights based on mesh spacing and time step, and provides an operator()
27 * to update the field at the boundary using current, previous, and next time-step values.
28 *
29 * @tparam _scalar Scalar type (e.g., float or double).
30 * @tparam _main_axis The main axis (0=x, 1=y, 2=z) normal to the face.
31 * @tparam _side_axes The two side axes (tangential to the face).
32 */
33template <typename _scalar, unsigned _main_axis, unsigned... _side_axes>
35 using scalar = _scalar; // Define the scalar type for the weights and calculations
36 scalar Cweights[5]; // Weights for the second-order ABC, precomputed based on mesh spacing and
37 // time step
38 int sign; // Sign of the main axis (1 for lower boundary, -1 for upper).
39 constexpr static unsigned main_axis =
40 _main_axis; // Main axis normal to the face (0=x, 1=y, 2=z)
41
48 KOKKOS_FUNCTION second_order_abc_face(ippl::Vector<scalar, 3> hr, scalar dt, int _sign)
49 : sign(_sign) {
50 // Speed of light in the medium
51 constexpr scalar c = 1;
52
53 // Check that the main axis is one of the three axes and the side axes are different from
54 // each other and from the main axis
55 constexpr unsigned side_axes[2] = {_side_axes...};
56 static_assert(
57 (main_axis == 0 && first<_side_axes...>() == 1 && second<_side_axes...>() == 2)
58 || (main_axis == 1 && first<_side_axes...>() == 0 && second<_side_axes...>() == 2)
59 || (main_axis == 2 && first<_side_axes...>() == 0 && second<_side_axes...>() == 1));
60 assert(_main_axis != side_axes[0]);
61 assert(_main_axis != side_axes[1]);
62 assert(side_axes[0] != side_axes[1]);
63
64 // Calculate prefactor for the weights used to calculate A_np1 at the boundary
65 scalar d = 1.0 / (2.0 * dt * hr[main_axis]) + 1.0 / (2.0 * c * dt * dt);
66
67 // Calculate the weights for the boundary condition
68 Cweights[0] = (1.0 / (2.0 * dt * hr[main_axis]) - 1.0 / (2.0 * c * dt * dt)) / d;
69 Cweights[1] = (-1.0 / (2.0 * dt * hr[main_axis]) - 1.0 / (2.0 * c * dt * dt)) / d;
70 assert(abs(Cweights[1] + 1) < 1e-6); // Cweight[1] should always be -1
71 Cweights[2] = (1.0 / (c * dt * dt) - c / (2.0 * hr[side_axes[0]] * hr[side_axes[0]])
72 - c / (2.0 * hr[side_axes[1]] * hr[side_axes[1]]))
73 / d;
74 Cweights[3] = (c / (4.0 * hr[side_axes[0]] * hr[side_axes[0]])) / d;
75 Cweights[4] = (c / (4.0 * hr[side_axes[1]] * hr[side_axes[1]])) / d;
76 }
77
86 template <typename view_type, typename Coords>
87 KOKKOS_INLINE_FUNCTION auto operator()(const view_type& A_n, const view_type& A_nm1,
88 const view_type& A_np1, const Coords& c) const ->
89 typename view_type::value_type {
90 uint32_t i = c[0];
91 uint32_t j = c[1];
92 uint32_t k = c[2];
93 using ippl::apply;
94 constexpr unsigned side_axes[2] = {_side_axes...};
95
96 // Create vectors with 1 in the direction of the specified axes and 0 in the others
97 ippl::Vector<uint32_t, 3> side_axis1_onehot =
98 ippl::Vector<uint32_t, 3>{side_axes[0] == 0, side_axes[0] == 1, side_axes[0] == 2};
99 ippl::Vector<uint32_t, 3> side_axis2_onehot =
100 ippl::Vector<uint32_t, 3>{side_axes[1] == 0, side_axes[1] == 1, side_axes[1] == 2};
101 // Create a vector in the direction of the main axis, the sign is used to determine the
102 // direction of the offset depending on which side of the boundary we are at
104 (main_axis == 0) * sign, (main_axis == 1) * sign, (main_axis == 2) * sign};
105
106 // Apply the boundary condition
107 // The main axis is the one that is normal to the face, so we need to apply the boundary
108 // condition in the direction of the main axis, this means we need to offset the coordinates
109 // by the sign of the main axis
110 return advanceBoundaryS(
111 A_nm1(i, j, k), A_n(i, j, k), apply(A_nm1, c + mainaxis_off),
112 apply(A_n, c + mainaxis_off), apply(A_np1, c + mainaxis_off),
113 apply(A_n, c + side_axis1_onehot + mainaxis_off),
114 apply(A_n, c - side_axis1_onehot + mainaxis_off),
115 apply(A_n, c + side_axis2_onehot + mainaxis_off),
116 apply(A_n, c - side_axis2_onehot + mainaxis_off), apply(A_n, c + side_axis1_onehot),
117 apply(A_n, c - side_axis1_onehot), apply(A_n, c + side_axis2_onehot),
118 apply(A_n, c - side_axis2_onehot));
119 }
120
124 template <typename value_type>
125 KOKKOS_FUNCTION value_type advanceBoundaryS(const value_type& v1, const value_type& v2,
126 const value_type& v3, const value_type& v4,
127 const value_type& v5, const value_type& v6,
128 const value_type& v7, const value_type& v8,
129 const value_type& v9, const value_type& v10,
130 const value_type& v11, const value_type& v12,
131 const value_type& v13) const noexcept {
132 value_type v0 = Cweights[0] * (v1 + v5) + (Cweights[1]) * v3 + (Cweights[2]) * (v2 + v4)
133 + (Cweights[3]) * (v6 + v7 + v10 + v11)
134 + (Cweights[4]) * (v8 + v9 + v12 + v13);
135 return v0;
136 }
137};
138
139/* @struct second_order_abc_edge
140 * @brief Implements a second-order absorbing boundary condition (ABC) for an edge of a 3D
141 * computational domain.
142 *
143 * This struct computes and applies the second-order Mur ABC on a specified edge of the domain,
144 * minimizing reflections for electromagnetic field solvers. The edge is defined by its axis (the
145 * edge direction) and two normal axes (the directions normal to the edge).
146 *
147 * The struct precomputes weights based on mesh spacing and time step, and provides an operator()
148 * to update the field at the boundary using current, previous, and next time-step values.
149 *
150 * @tparam _scalar Scalar type (e.g., float or double).
151 * @tparam edge_axis The axis (0=x, 1=y, 2=z) along the edge.
152 * @tparam normal_axis1 The first normal axis to the edge.
153 * @tparam normal_axis2 The second normal axis to the edge.
154 * @tparam na1_zero Boolean indicating direction for normal_axis1 (true for lower boundary,
155 * false for upper).
156 * @tparam na2_zero Boolean indicating direction for normal_axis2 (true for lower boundary,
157 * false for upper).
158 */
159template <typename _scalar, unsigned edge_axis, unsigned normal_axis1, unsigned normal_axis2,
160 bool na1_zero, bool na2_zero>
162 using scalar = _scalar; // Define the scalar type for the weights and calculations
163 scalar Eweights[5]; // Weights for the second-order ABC, precomputed based on mesh spacing and
164 // time step
165
172 // Check that the edge axis is one of the three axes and the normal axes are different from
173 // each other and from the edge axis
174 static_assert(normal_axis1 != normal_axis2);
175 static_assert(edge_axis != normal_axis2);
176 static_assert(edge_axis != normal_axis1);
177 // Check that the axes are in the correct order
178 static_assert((edge_axis == 2 && normal_axis1 == 0 && normal_axis2 == 1)
179 || (edge_axis == 0 && normal_axis1 == 1 && normal_axis2 == 2)
180 || (edge_axis == 1 && normal_axis1 == 2 && normal_axis2 == 0));
181
182 // Speed of light in the medium
183 constexpr scalar c0_ = scalar(1);
184
185 // Calculate prefactor for the weights used to calculate A_np1 at the boundary
186 scalar d = (1.0 / hr[normal_axis1] + 1.0 / hr[normal_axis2]) / (4.0 * dt)
187 + 3.0 / (8.0 * c0_ * dt * dt);
188
189 // Calculate the weights for the boundary condition
190 Eweights[0] = (-(1.0 / hr[normal_axis2] - 1.0 / hr[normal_axis1]) / (4.0 * dt)
191 - 3.0 / (8.0 * c0_ * dt * dt))
192 / d;
193 Eweights[1] = ((1.0 / hr[normal_axis2] - 1.0 / hr[normal_axis1]) / (4.0 * dt)
194 - 3.0 / (8.0 * c0_ * dt * dt))
195 / d;
196 Eweights[2] = ((1.0 / hr[normal_axis2] + 1.0 / hr[normal_axis1]) / (4.0 * dt)
197 - 3.0 / (8.0 * c0_ * dt * dt))
198 / d;
199 Eweights[3] =
200 (3.0 / (4.0 * c0_ * dt * dt) - c0_ / (4.0 * hr[edge_axis] * hr[edge_axis])) / d;
201 Eweights[4] = c0_ / (8.0 * hr[edge_axis] * hr[edge_axis]) / d;
202 }
203
212 template <typename view_type, typename Coords>
213 KOKKOS_INLINE_FUNCTION auto operator()(const view_type& A_n, const view_type& A_nm1,
214 const view_type& A_np1, const Coords& c) const ->
215 typename view_type::value_type {
216 uint32_t i = c[0];
217 uint32_t j = c[1];
218 uint32_t k = c[2];
219 using ippl::apply;
220
221 // Get the opposite direction of the normal vectors for the two normal axes (direction to
222 // the interior of the domain), 1 or -1 in the direction of the normal axis and 0 in the others
223 ippl::Vector<int32_t, 3> normal_axis1_onehot =
224 ippl::Vector<int32_t, 3>{normal_axis1 == 0, normal_axis1 == 1, normal_axis1 == 2}
225 * int32_t(na1_zero ? 1 : -1);
226 ippl::Vector<int32_t, 3> normal_axis2_onehot =
227 ippl::Vector<int32_t, 3>{normal_axis2 == 0, normal_axis2 == 1, normal_axis2 == 2}
228 * int32_t(na2_zero ? 1 : -1);
229
230 // Calculate the coordinates of the neighboring points in the interior of the domain
231 ippl::Vector<uint32_t, 3> acc0 = {i, j, k};
232 ippl::Vector<uint32_t, 3> acc1 = acc0 + normal_axis1_onehot;
233 ippl::Vector<uint32_t, 3> acc2 = acc0 + normal_axis2_onehot;
234 ippl::Vector<uint32_t, 3> acc3 = acc0 + normal_axis1_onehot + normal_axis2_onehot;
235
236 // Create a vector in the direction of the edge axis
237 ippl::Vector<uint32_t, 3> axisp{edge_axis == 0, edge_axis == 1, edge_axis == 2};
238
239 // Apply the boundary condition
240 return advanceEdgeS(A_n(i, j, k), A_nm1(i, j, k), apply(A_np1, acc1), apply(A_n, acc1),
241 apply(A_nm1, acc1), apply(A_np1, acc2), apply(A_n, acc2),
242 apply(A_nm1, acc2), apply(A_np1, acc3), apply(A_n, acc3),
243 apply(A_nm1, acc3), apply(A_n, acc0 - axisp), apply(A_n, acc1 - axisp),
244 apply(A_n, acc2 - axisp), apply(A_n, acc3 - axisp),
245 apply(A_n, acc0 + axisp), apply(A_n, acc1 + axisp),
246 apply(A_n, acc2 + axisp), apply(A_n, acc3 + axisp));
247 }
248
252 template <typename value_type>
253 KOKKOS_INLINE_FUNCTION value_type advanceEdgeS(value_type v1, value_type v2, value_type v3,
254 value_type v4, value_type v5, value_type v6,
255 value_type v7, value_type v8, value_type v9,
256 value_type v10, value_type v11, value_type v12,
257 value_type v13, value_type v14, value_type v15,
258 value_type v16, value_type v17, value_type v18,
259 value_type v19) const noexcept {
260 value_type v0 = Eweights[0] * (v3 + v8) + Eweights[1] * (v5 + v6) + Eweights[2] * (v2 + v9)
261 + Eweights[3] * (v1 + v4 + v7 + v10)
262 + Eweights[4] * (v12 + v13 + v14 + v15 + v16 + v17 + v18 + v19) - v11;
263 return v0;
264 }
265};
266
267/* @struct second_order_abc_corner
268 * @brief Implements a second-order absorbing boundary condition (ABC) for a corner of a 3D
269 * computational domain.
270 *
271 * This struct computes and applies the second-order Mur ABC on a specified corner of the domain,
272 * minimizing reflections for electromagnetic field solvers. The corner is defined by its three
273 * axes, with each axis having a boolean indicating whether it is at the lower or upper boundary.
274 *
275 * The struct precomputes weights based on mesh spacing and time step, and provides an operator()
276 * to update the field at the boundary using current, previous, and next time-step values.
277 *
278 * @tparam _scalar Scalar type (e.g., float or double).
279 * @tparam x0 Boolean indicating direction for x-axis (true for lower boundary, false for
280 * upper).
281 * @tparam y0 Boolean indicating direction for y-axis (true for lower boundary, false for
282 * upper).
283 * @tparam z0 Boolean indicating direction for z-axis (true for lower boundary, false for
284 * upper).
285 */
286template <typename _scalar, bool x0, bool y0, bool z0>
288 using scalar = _scalar; // Define the scalar type for the weights and calculations
289 scalar Cweights[17]; // Weights for the second-order ABC, precomputed based on mesh spacing and
290 // time step
291
298 constexpr scalar c0_ = scalar(1);
299 Cweights[0] =
300 (-1.0 / hr[0] - 1.0 / hr[1] - 1.0 / hr[2]) / (8.0 * dt) - 1.0 / (4.0 * c0_ * dt * dt);
301 Cweights[1] =
302 (1.0 / hr[0] - 1.0 / hr[1] - 1.0 / hr[2]) / (8.0 * dt) - 1.0 / (4.0 * c0_ * dt * dt);
303 Cweights[2] =
304 (-1.0 / hr[0] + 1.0 / hr[1] - 1.0 / hr[2]) / (8.0 * dt) - 1.0 / (4.0 * c0_ * dt * dt);
305 Cweights[3] =
306 (-1.0 / hr[0] - 1.0 / hr[1] + 1.0 / hr[2]) / (8.0 * dt) - 1.0 / (4.0 * c0_ * dt * dt);
307 Cweights[4] =
308 (1.0 / hr[0] + 1.0 / hr[1] - 1.0 / hr[2]) / (8.0 * dt) - 1.0 / (4.0 * c0_ * dt * dt);
309 Cweights[5] =
310 (1.0 / hr[0] - 1.0 / hr[1] + 1.0 / hr[2]) / (8.0 * dt) - 1.0 / (4.0 * c0_ * dt * dt);
311 Cweights[6] =
312 (-1.0 / hr[0] + 1.0 / hr[1] + 1.0 / hr[2]) / (8.0 * dt) - 1.0 / (4.0 * c0_ * dt * dt);
313 Cweights[7] =
314 (1.0 / hr[0] + 1.0 / hr[1] + 1.0 / hr[2]) / (8.0 * dt) - 1.0 / (4.0 * c0_ * dt * dt);
315 Cweights[8] =
316 -(-1.0 / hr[0] - 1.0 / hr[1] - 1.0 / hr[2]) / (8.0 * dt) - 1.0 / (4.0 * c0_ * dt * dt);
317 Cweights[9] =
318 -(1.0 / hr[0] - 1.0 / hr[1] - 1.0 / hr[2]) / (8.0 * dt) - 1.0 / (4.0 * c0_ * dt * dt);
319 Cweights[10] =
320 -(-1.0 / hr[0] + 1.0 / hr[1] - 1.0 / hr[2]) / (8.0 * dt) - 1.0 / (4.0 * c0_ * dt * dt);
321 Cweights[11] =
322 -(-1.0 / hr[0] - 1.0 / hr[1] + 1.0 / hr[2]) / (8.0 * dt) - 1.0 / (4.0 * c0_ * dt * dt);
323 Cweights[12] =
324 -(1.0 / hr[0] + 1.0 / hr[1] - 1.0 / hr[2]) / (8.0 * dt) - 1.0 / (4.0 * c0_ * dt * dt);
325 Cweights[13] =
326 -(1.0 / hr[0] - 1.0 / hr[1] + 1.0 / hr[2]) / (8.0 * dt) - 1.0 / (4.0 * c0_ * dt * dt);
327 Cweights[14] =
328 -(-1.0 / hr[0] + 1.0 / hr[1] + 1.0 / hr[2]) / (8.0 * dt) - 1.0 / (4.0 * c0_ * dt * dt);
329 Cweights[15] =
330 -(1.0 / hr[0] + 1.0 / hr[1] + 1.0 / hr[2]) / (8.0 * dt) - 1.0 / (4.0 * c0_ * dt * dt);
331 Cweights[16] = 1.0 / (2.0 * c0_ * dt * dt);
332 }
333
342 template <typename view_type, typename Coords>
343 KOKKOS_INLINE_FUNCTION auto operator()(const view_type& A_n, const view_type& A_nm1,
344 const view_type& A_np1, const Coords& c) const ->
345 typename view_type::value_type {
346 // Get direction of the vector to the interior of the domain
347 constexpr uint32_t xoff = (x0) ? 1 : uint32_t(-1);
348 constexpr uint32_t yoff = (y0) ? 1 : uint32_t(-1);
349 constexpr uint32_t zoff = (z0) ? 1 : uint32_t(-1);
350 using ippl::apply;
351
352 // Calculate the offset vectors for the neighboring points in the interior of the domain
353 const ippl::Vector<uint32_t, 3> offsets[8] = {
356 ippl::Vector<uint32_t, 3>{xoff, yoff, 0}, ippl::Vector<uint32_t, 3>{xoff, 0, zoff},
357 ippl::Vector<uint32_t, 3>{0, yoff, zoff}, ippl::Vector<uint32_t, 3>{xoff, yoff, zoff},
358 };
359
360 // Apply the boundary condition
361 return advanceCornerS(
362 apply(A_n, c), apply(A_nm1, c), apply(A_np1, c + offsets[1]),
363 apply(A_n, c + offsets[1]), apply(A_nm1, c + offsets[1]), apply(A_np1, c + offsets[2]),
364 apply(A_n, c + offsets[2]), apply(A_nm1, c + offsets[2]), apply(A_np1, c + offsets[3]),
365 apply(A_n, c + offsets[3]), apply(A_nm1, c + offsets[3]), apply(A_np1, c + offsets[4]),
366 apply(A_n, c + offsets[4]), apply(A_nm1, c + offsets[4]), apply(A_np1, c + offsets[5]),
367 apply(A_n, c + offsets[5]), apply(A_nm1, c + offsets[5]), apply(A_np1, c + offsets[6]),
368 apply(A_n, c + offsets[6]), apply(A_nm1, c + offsets[6]), apply(A_np1, c + offsets[7]),
369 apply(A_n, c + offsets[7]), apply(A_nm1, c + offsets[7]));
370 }
371
375 template <typename value_type>
376 KOKKOS_INLINE_FUNCTION value_type
377 advanceCornerS(value_type v1, value_type v2, value_type v3, value_type v4, value_type v5,
378 value_type v6, value_type v7, value_type v8, value_type v9, value_type v10,
379 value_type v11, value_type v12, value_type v13, value_type v14, value_type v15,
380 value_type v16, value_type v17, value_type v18, value_type v19, value_type v20,
381 value_type v21, value_type v22, value_type v23) const noexcept {
382 return -(v1 * (Cweights[16]) + v2 * (Cweights[8]) + v3 * Cweights[1] + v4 * Cweights[16]
383 + v5 * Cweights[9] + v6 * Cweights[2] + v7 * Cweights[16] + v8 * Cweights[10]
384 + v9 * Cweights[3] + v10 * Cweights[16] + v11 * Cweights[11] + v12 * Cweights[4]
385 + v13 * Cweights[16] + v14 * Cweights[12] + v15 * Cweights[5] + v16 * Cweights[16]
386 + v17 * Cweights[13] + v18 * Cweights[6] + v19 * Cweights[16] + v20 * Cweights[14]
387 + v21 * Cweights[7] + v22 * Cweights[16] + v23 * Cweights[15])
388 / Cweights[0];
389 }
390};
391
392/* @struct second_order_mur_boundary_conditions
393 * @brief Applies second-order Mur absorbing boundary conditions (ABC) to all boundaries of a 3D
394 * computational domain.
395 *
396 * This struct coordinates the application of second-order Mur ABCs on faces, edges, and corners of
397 * the domain, minimizing reflections for electromagnetic field solvers. It uses the appropriate
398 * face, edge, or corner ABC implementation depending on the location of each boundary point.
399 *
400 * The apply() method detects the type of boundary (face, edge, or corner) for each point and
401 * applies the corresponding second-order ABC using precomputed weights based on mesh spacing and
402 * time step.
403 *
404 * @tparam field_type The field type (must provide getView() and get_mesh().getMeshSpacing()).
405 * @tparam dt_type The type of the time step.
406 */
417 template <typename field_type, typename dt_type>
418 void apply(field_type& FA_n, field_type& FA_nm1, field_type& FA_np1, dt_type dt,
420 using scalar = decltype(dt);
421
422 // Get the mesh spacing
423 const ippl::Vector<scalar, 3> hr = FA_n.get_mesh().getMeshSpacing();
424
425 // Get views of the fields
426 auto A_n = FA_n.getView();
427 auto A_np1 = FA_np1.getView();
428 auto A_nm1 = FA_nm1.getView();
429
430 // Get the local number of grid points in each dimension
431 ippl::Vector<uint32_t, 3> local_nr{uint32_t(A_n.extent(0)), uint32_t(A_n.extent(1)),
432 uint32_t(A_n.extent(2))};
433
434 constexpr uint32_t min_abc_boundary = 1;
435 constexpr uint32_t max_abc_boundary_sub = min_abc_boundary + 1;
436
437 // Apply second-order ABC to faces
438 Kokkos::parallel_for(
439 ippl::getRangePolicy(A_n, 1), KOKKOS_LAMBDA(uint32_t i, uint32_t j, uint32_t k) {
440 // Get the global indices of the current point
441 uint32_t ig = i + lDom.first()[0];
442 uint32_t jg = j + lDom.first()[1];
443 uint32_t kg = k + lDom.first()[2];
444
445 // Check on how many boundaries of the local domain the current point is located
446 uint32_t lval = uint32_t(i == 0) + (uint32_t(j == 0) << 1) + (uint32_t(k == 0) << 2)
447 + (uint32_t(i == local_nr[0] - 1) << 3)
448 + (uint32_t(j == local_nr[1] - 1) << 4)
449 + (uint32_t(k == local_nr[2] - 1) << 5);
450
451 // Exits current iteration if the point is on an edge or corner of the local domain
452 if (Kokkos::popcount(lval) > 1)
453 return;
454
455 // Check on how many boundaries of the global domain the current point is located
456 uint32_t val = uint32_t(ig == min_abc_boundary)
457 + (uint32_t(jg == min_abc_boundary) << 1)
458 + (uint32_t(kg == min_abc_boundary) << 2)
459 + (uint32_t(ig == true_nr[0] - max_abc_boundary_sub) << 3)
460 + (uint32_t(jg == true_nr[1] - max_abc_boundary_sub) << 4)
461 + (uint32_t(kg == true_nr[2] - max_abc_boundary_sub) << 5);
462
463 // If the point is on a global face, apply the second-order ABC
464 if (Kokkos::popcount(val) == 1) {
465 if (ig == min_abc_boundary) {
467 A_np1(i, j, k) = soa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
468 }
469 if (jg == min_abc_boundary) {
471 A_np1(i, j, k) = soa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
472 }
473 if (kg == min_abc_boundary) {
475 A_np1(i, j, k) = soa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
476 }
477 if (ig == true_nr[0] - max_abc_boundary_sub) {
479 A_np1(i, j, k) = soa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
480 }
481 if (jg == true_nr[1] - max_abc_boundary_sub) {
483 A_np1(i, j, k) = soa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
484 }
485 if (kg == true_nr[2] - max_abc_boundary_sub) {
487 A_np1(i, j, k) = soa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
488 }
489 }
490 });
491 Kokkos::fence();
492
493 // Apply second-order ABC to edges
494 Kokkos::parallel_for(
495 ippl::getRangePolicy(A_n, 1), KOKKOS_LAMBDA(uint32_t i, uint32_t j, uint32_t k) {
496 // Get the global indices of the current point
497 uint32_t ig = i + lDom.first()[0];
498 uint32_t jg = j + lDom.first()[1];
499 uint32_t kg = k + lDom.first()[2];
500
501 // Check on how many boundaries of the local domain the current point is located
502 uint32_t lval = uint32_t(i == 0) + (uint32_t(j == 0) << 1) + (uint32_t(k == 0) << 2)
503 + (uint32_t(i == local_nr[0] - 1) << 3)
504 + (uint32_t(j == local_nr[1] - 1) << 4)
505 + (uint32_t(k == local_nr[2] - 1) << 5);
506
507 // Exits current iteration if the point is on a corner of the local domain
508 if (Kokkos::popcount(lval) > 2)
509 return;
510
511 // Check on how many boundaries of the global domain the current point is located
512 uint32_t val = uint32_t(ig == min_abc_boundary)
513 + (uint32_t(jg == min_abc_boundary) << 1)
514 + (uint32_t(kg == min_abc_boundary) << 2)
515 + (uint32_t(ig == true_nr[0] - max_abc_boundary_sub) << 3)
516 + (uint32_t(jg == true_nr[1] - max_abc_boundary_sub) << 4)
517 + (uint32_t(kg == true_nr[2] - max_abc_boundary_sub) << 5);
518
519 // If the point is on a global edge, apply the second-order ABC
520 if (Kokkos::popcount(val) == 2) {
521 if (ig == min_abc_boundary && kg == min_abc_boundary) {
523 A_np1(i, j, k) = soa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
524 } else if (ig == min_abc_boundary && jg == min_abc_boundary) {
526 A_np1(i, j, k) = soa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
527 } else if (jg == min_abc_boundary && kg == min_abc_boundary) {
529 A_np1(i, j, k) = soa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
530 } else if (ig == min_abc_boundary && kg == true_nr[2] - max_abc_boundary_sub) {
532 A_np1(i, j, k) = soa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
533 } else if (ig == min_abc_boundary && jg == true_nr[1] - max_abc_boundary_sub) {
535 A_np1(i, j, k) = soa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
536 } else if (jg == min_abc_boundary && kg == true_nr[2] - max_abc_boundary_sub) {
538 A_np1(i, j, k) = soa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
539 } else if (ig == true_nr[0] - max_abc_boundary_sub && kg == min_abc_boundary) {
541 A_np1(i, j, k) = soa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
542 } else if (ig == true_nr[0] - max_abc_boundary_sub && jg == min_abc_boundary) {
544 A_np1(i, j, k) = soa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
545 } else if (jg == true_nr[1] - max_abc_boundary_sub && kg == min_abc_boundary) {
547 A_np1(i, j, k) = soa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
548 } else if (ig == true_nr[0] - max_abc_boundary_sub
549 && kg == true_nr[2] - max_abc_boundary_sub) {
551 A_np1(i, j, k) = soa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
552 } else if (ig == true_nr[0] - max_abc_boundary_sub
553 && jg == true_nr[1] - max_abc_boundary_sub) {
555 A_np1(i, j, k) = soa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
556 } else if (jg == true_nr[1] - max_abc_boundary_sub
557 && kg == true_nr[2] - max_abc_boundary_sub) {
559 A_np1(i, j, k) = soa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
560 } else {
561 assert(false);
562 }
563 }
564 });
565 Kokkos::fence();
566
567 // Apply second-order ABC to corners
568 Kokkos::parallel_for(
569 ippl::getRangePolicy(A_n, 1), KOKKOS_LAMBDA(uint32_t i, uint32_t j, uint32_t k) {
570 // Get the global indices of the current point
571 uint32_t ig = i + lDom.first()[0];
572 uint32_t jg = j + lDom.first()[1];
573 uint32_t kg = k + lDom.first()[2];
574
575 // Check on how many boundaries of the global domain the current point is located
576 uint32_t val = uint32_t(ig == min_abc_boundary)
577 + (uint32_t(jg == min_abc_boundary) << 1)
578 + (uint32_t(kg == min_abc_boundary) << 2)
579 + (uint32_t(ig == true_nr[0] - max_abc_boundary_sub) << 3)
580 + (uint32_t(jg == true_nr[1] - max_abc_boundary_sub) << 4)
581 + (uint32_t(kg == true_nr[2] - max_abc_boundary_sub) << 5);
582
583 // If the point is on a global corner, apply the second-order ABC
584 if (Kokkos::popcount(val) == 3) {
585 if (ig == min_abc_boundary && jg == min_abc_boundary
586 && kg == min_abc_boundary) {
588 A_np1(i, j, k) = coa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
589 } else if (ig == true_nr[0] - max_abc_boundary_sub && jg == min_abc_boundary
590 && kg == min_abc_boundary) {
592 A_np1(i, j, k) = coa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
593 } else if (ig == min_abc_boundary && jg == true_nr[1] - max_abc_boundary_sub
594 && kg == min_abc_boundary) {
596 A_np1(i, j, k) = coa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
597 } else if (ig == true_nr[0] - max_abc_boundary_sub
598 && jg == true_nr[1] - max_abc_boundary_sub
599 && kg == min_abc_boundary) {
601 A_np1(i, j, k) = coa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
602 } else if (ig == min_abc_boundary && jg == min_abc_boundary
603 && kg == true_nr[2] - max_abc_boundary_sub) {
605 A_np1(i, j, k) = coa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
606 } else if (ig == true_nr[0] - max_abc_boundary_sub && jg == min_abc_boundary
607 && kg == true_nr[2] - max_abc_boundary_sub) {
609 A_np1(i, j, k) = coa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
610 } else if (ig == min_abc_boundary && jg == true_nr[1] - max_abc_boundary_sub
611 && kg == true_nr[2] - max_abc_boundary_sub) {
613 A_np1(i, j, k) = coa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
614 } else if (ig == true_nr[0] - max_abc_boundary_sub
615 && jg == true_nr[1] - max_abc_boundary_sub
616 && kg == true_nr[2] - max_abc_boundary_sub) {
618 A_np1(i, j, k) = coa(A_n, A_nm1, A_np1, ippl::Vector<uint32_t, 3>{i, j, k});
619 } else {
620 assert(false);
621 }
622 }
623 });
624 Kokkos::fence();
625 }
626};
627#endif
typename ippl::detail::ViewType< ippl::Vector< double, Dim >, 1 >::view_type view_type
constexpr KOKKOS_INLINE_FUNCTION auto second()
Definition AbsorbingBC.h:14
constexpr KOKKOS_INLINE_FUNCTION auto first()
Definition AbsorbingBC.h:10
KOKKOS_INLINE_FUNCTION constexpr decltype(auto) apply(const View &view, const Coords &coords)
RangePolicy< View::rank, typenameView::execution_space, PolicyArgs... >::policy_type getRangePolicy(const View &view, int shift=0)
KOKKOS_INLINE_FUNCTION Vector< int, Dim > first() const
Definition NDIndex.hpp:170
KOKKOS_INLINE_FUNCTION auto operator()(const view_type &A_n, const view_type &A_nm1, const view_type &A_np1, const Coords &c) const -> typename view_type::value_type
Applies the second-order ABC to the boundary of the field.
Definition AbsorbingBC.h:87
KOKKOS_FUNCTION value_type advanceBoundaryS(const value_type &v1, const value_type &v2, const value_type &v3, const value_type &v4, const value_type &v5, const value_type &v6, const value_type &v7, const value_type &v8, const value_type &v9, const value_type &v10, const value_type &v11, const value_type &v12, const value_type &v13) const noexcept
Advances the boundary condition using the precomputed weights.
KOKKOS_FUNCTION second_order_abc_face(ippl::Vector< scalar, 3 > hr, scalar dt, int _sign)
Constructor for the second-order ABC face.
Definition AbsorbingBC.h:48
static constexpr unsigned main_axis
Definition AbsorbingBC.h:39
KOKKOS_INLINE_FUNCTION value_type advanceEdgeS(value_type v1, value_type v2, value_type v3, value_type v4, value_type v5, value_type v6, value_type v7, value_type v8, value_type v9, value_type v10, value_type v11, value_type v12, value_type v13, value_type v14, value_type v15, value_type v16, value_type v17, value_type v18, value_type v19) const noexcept
Advances the edge boundary condition using the precomputed weights.
KOKKOS_INLINE_FUNCTION auto operator()(const view_type &A_n, const view_type &A_nm1, const view_type &A_np1, const Coords &c) const -> typename view_type::value_type
Applies the second-order ABC to the edge of the field.
KOKKOS_FUNCTION second_order_abc_edge(ippl::Vector< scalar, 3 > hr, scalar dt)
Constructor for the second-order ABC edge.
KOKKOS_FUNCTION second_order_abc_corner(ippl::Vector< scalar, 3 > hr, scalar dt)
Constructor for the second-order ABC corner.
KOKKOS_INLINE_FUNCTION value_type advanceCornerS(value_type v1, value_type v2, value_type v3, value_type v4, value_type v5, value_type v6, value_type v7, value_type v8, value_type v9, value_type v10, value_type v11, value_type v12, value_type v13, value_type v14, value_type v15, value_type v16, value_type v17, value_type v18, value_type v19, value_type v20, value_type v21, value_type v22, value_type v23) const noexcept
Advances the corner boundary condition using the precomputed weights.
KOKKOS_INLINE_FUNCTION auto operator()(const view_type &A_n, const view_type &A_nm1, const view_type &A_np1, const Coords &c) const -> typename view_type::value_type
Applies the second-order ABC to the corner of the field.
void apply(field_type &FA_n, field_type &FA_nm1, field_type &FA_np1, dt_type dt, ippl::Vector< uint32_t, 3 > true_nr, ippl::NDIndex< 3 > lDom)
Applies second-order Mur ABC to the boundaries of the field.