WarpX
WarpX_PEC.H
Go to the documentation of this file.
1 #ifndef WARPX_PEC_KERNELS_H_
2 #define WARPX_PEC_KERNELS_H_
3 
4 #include "WarpX.H"
6 
7 #include <AMReX_Array.H>
8 #include <AMReX_Array4.H>
9 #include <AMReX_Config.H>
10 #include <AMReX_Extension.H>
11 #include <AMReX_GpuQualifiers.H>
12 #include <AMReX_IntVect.H>
13 #include <AMReX_REAL.H>
14 
15 #include <AMReX_BaseFwd.H>
16 
17 #include <array>
18 #include <memory>
19 
20 namespace PEC {
21 using namespace amrex;
32  bool is_boundary_PEC (amrex::GpuArray<int, 3> const& fboundary, int dir) {
33  return ( fboundary[dir] == FieldBoundaryType::PEC );
34  }
35 
47  return ( pboundary[dir] == ParticleBoundaryType::Reflecting );
48  }
49 
68  const amrex::IntVect& dom_hi, const amrex::IntVect& ijk_vec,
69  const amrex::IntVect& is_nodal, const int idim, const int iside)
70  {
71  return ((iside == 0) ? (dom_lo[idim] - ijk_vec[idim])
72  : (ijk_vec[idim] - (dom_hi[idim] + is_nodal[idim])));
73  }
74 
142  void SetEfieldOnPEC (const int icomp, const amrex::IntVect & dom_lo,
143  const amrex::IntVect &dom_hi,
144  const amrex::IntVect &ijk_vec, const int n,
145  amrex::Array4<amrex::Real> const& Efield,
146  const amrex::IntVect& is_nodal,
147  amrex::GpuArray<int, 3> const& fbndry_lo,
148  amrex::GpuArray<int, 3> const& fbndry_hi )
149  {
150  // Tangential Efield components in guard cells set equal and opposite to cells
151  // in the mirror locations across the PEC boundary, whereas normal E-field
152  // components are set equal to values in the mirror locations across the PEC
153  // boundary. Here we just initialize it.
154  amrex::IntVect ijk_mirror = ijk_vec;
155  bool OnPECBoundary = false;
156  bool GuardCell = false;
157  amrex::Real sign = 1._rt;
158  // Loop over all the dimensions
159  for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
160  // Loop over sides, iside = 0 (lo), iside = 1 (hi)
161  for (int iside = 0; iside < 2; ++iside) {
162  const bool isPECBoundary = ( (iside == 0)
163  ? is_boundary_PEC(fbndry_lo, idim)
164  : is_boundary_PEC(fbndry_hi, idim) );
165 #if (defined WARPX_DIM_XZ) || (defined WARPX_DIM_RZ)
166  // For 2D : for icomp==1, (Ey in XZ, Etheta in RZ),
167  // icomp=1 is tangential to both x and z boundaries
168  // The logic below ensures that the flags are set right for 2D
169  const bool is_tangent_to_PEC = (icomp != AMREX_SPACEDIM*idim);
170 #elif (defined WARPX_DIM_1D_Z)
171  // For 1D : icomp=0 and icomp=1 (Ex and Ey are tangential to the z boundary)
172  // The logic below ensures that the flags are set right for 1D
173  const bool is_tangent_to_PEC = (icomp != idim+2);
174 #else
175  const bool is_tangent_to_PEC = (icomp != idim);
176 #endif
177  if (isPECBoundary) {
178  // grid point ijk_vec is ig number of points pass the
179  // domain boundary in direction, idim
180  const int ig = get_cell_count_to_boundary(
181  dom_lo, dom_hi, ijk_vec, is_nodal, idim, iside);
182 
183  if (ig == 0) {
184  if (is_tangent_to_PEC && is_nodal[idim] == 1) {
185  OnPECBoundary = true;
186  }
187  } else if (ig > 0) {
188  // Find mirror location across PEC boundary
189  ijk_mirror[idim] = ( ( iside == 0)
190  ? (dom_lo[idim] + ig - (1 - is_nodal[idim]))
191  : (dom_hi[idim] + 1 - ig));
192  GuardCell = true;
193  // tangential components are inverted across PEC boundary
194  if (is_tangent_to_PEC) { sign *= -1._rt; }
195 #if (defined WARPX_DIM_RZ)
196  if (icomp == 0 && idim == 0 && iside == 1) {
197  // Add radial scale so that drEr/dr = 0.
198  // This only works for the first guard cell and with
199  // Er cell centered in r.
200  const amrex::Real rguard = ijk_vec[idim] + 0.5_rt*(1._rt - is_nodal[idim]);
201  const amrex::Real rmirror = ijk_mirror[idim] + 0.5_rt*(1._rt - is_nodal[idim]);
202  sign *= rmirror/rguard;
203  }
204 #endif
205  }
206  } // is PEC boundary
207  } // loop over iside
208  } // loop over dimensions
209  if (OnPECBoundary) {
210  // if ijk_vec is on a PEC boundary in any direction, set Etangential to 0.
211  Efield(ijk_vec,n) = 0._rt;
212  } else if (GuardCell) {
213  Efield(ijk_vec,n) = sign * Efield(ijk_mirror,n);
214  }
215  }
216 
217 
281  void SetBfieldOnPEC (const int icomp, const amrex::IntVect & dom_lo,
282  const amrex::IntVect & dom_hi,
283  const amrex::IntVect & ijk_vec, const int n,
284  amrex::Array4<amrex::Real> const& Bfield,
285  const amrex::IntVect & is_nodal,
286  amrex::GpuArray<int, 3> const& fbndry_lo,
287  amrex::GpuArray<int, 3> const& fbndry_hi )
288  {
289  amrex::IntVect ijk_mirror = ijk_vec;
290  bool OnPECBoundary = false;
291  bool GuardCell = false;
292  amrex::Real sign = 1._rt;
293  // Loop over all dimensions
294  for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
295  // Loop over sides, iside = 0 (lo), iside = 1 (hi)
296  for (int iside = 0; iside < 2; ++iside) {
297  const bool isPECBoundary = ( (iside == 0 )
298  ? is_boundary_PEC(fbndry_lo, idim)
299  : is_boundary_PEC(fbndry_hi, idim) );
300  if (isPECBoundary) {
301 #if (defined WARPX_DIM_XZ) || (defined WARPX_DIM_RZ)
302  // For 2D : for icomp==1, (By in XZ, Btheta in RZ),
303  // icomp=1 is not normal to x or z boundary
304  // The logic below ensures that the flags are set right for 2D
305  const bool is_normal_to_PEC = (icomp == (AMREX_SPACEDIM*idim));
306 #elif (defined WARPX_DIM_1D_Z)
307  // For 1D : icomp=0 and icomp=1 (Bx and By are not normal to the z boundary)
308  // The logic below ensures that the flags are set right for 1D
309  const bool is_normal_to_PEC = (icomp == (idim+2));
310 #else
311  const bool is_normal_to_PEC = (icomp == idim);
312 #endif
313 
314  // grid point ijk_vec is ig number of points pass the
315  // domain boundary in direction, idim
316  const int ig = get_cell_count_to_boundary(
317  dom_lo, dom_hi, ijk_vec, is_nodal, idim, iside);
318 
319  if (ig == 0) {
320  // Only normal component is set to 0
321  if (is_normal_to_PEC && is_nodal[idim]==1) {
322  OnPECBoundary = true;
323  }
324  } else if ( ig > 0) {
325  // Mirror location inside the domain by "ig" number of cells
326  // across PEC boundary in direction, idim, and side, iside
327  ijk_mirror[idim] = ( (iside == 0)
328  ? (dom_lo[idim] + ig - (1 - is_nodal[idim]))
329  : (dom_hi[idim] + 1 - ig));
330  GuardCell = true;
331  // Sign of the normal component in guard cell is inverted
332  if (is_normal_to_PEC) { sign *= -1._rt; }
333 #if (defined WARPX_DIM_RZ)
334  if (icomp == 0 && idim == 0 && iside == 1) {
335  // Add radial scale so that drBr/dr = 0.
336  const amrex::Real rguard = ijk_vec[idim] + 0.5_rt*(1._rt - is_nodal[idim]);
337  const amrex::Real rmirror = ijk_mirror[idim] + 0.5_rt*(1._rt - is_nodal[idim]);
338  sign *= rmirror/rguard;
339  }
340 #endif
341  }
342  } // if PEC Boundary
343  } // loop over sides
344  } // loop of dimensions
345 
346  if (OnPECBoundary) {
347  // if ijk_vec is on a PEC boundary in any direction, set Bnormal to 0.
348  Bfield(ijk_vec,n) = 0._rt;
349  } else if (GuardCell) {
350  // Bnormal and Btangential is set opposite and equal to the value
351  // in the mirror location, respectively.
352  Bfield(ijk_vec,n) = sign * Bfield(ijk_mirror,n);
353  }
354  }
355 
356 
378  void SetRhoOrJfieldFromPEC (const int n,
379  const amrex::IntVect & ijk_vec,
381  amrex::GpuArray<GpuArray<int, 2>, AMREX_SPACEDIM> const& mirrorfac,
382  amrex::GpuArray<GpuArray<amrex::Real, 2>, AMREX_SPACEDIM> const& psign,
383  amrex::GpuArray<GpuArray<bool, 2>, AMREX_SPACEDIM> const& is_pec,
384  amrex::GpuArray<bool, AMREX_SPACEDIM> const& tangent_to_bndy,
385  amrex::Box const& fabbox)
386  {
387  // The boundary is handled in 2 steps:
388  // 1) The cells internal to the domain are updated using the
389  // current deposited in the guard cells
390  for (int idim = 0; idim < AMREX_SPACEDIM; ++idim)
391  {
392  for (int iside = 0; iside < 2; ++iside)
393  {
394  if (!is_pec[idim][iside]) { continue; }
395 
396  // Get the mirror guard cell index
397  amrex::IntVect iv_mirror = ijk_vec;
398  iv_mirror[idim] = mirrorfac[idim][iside] - ijk_vec[idim];
399 
400  // On the PEC boundary the charge/current density is set to 0
401  if (ijk_vec == iv_mirror) {
402  field(ijk_vec, n) = 0._rt;
403  // otherwise update the internal cell if the mirror guard cell exists
404  } else if (fabbox.contains(iv_mirror)) {
405  field(ijk_vec,n) += psign[idim][iside] * field(iv_mirror,n);
406  }
407  }
408  }
409  // 2) The guard cells are updated with the appropriate image
410  // charge based on the charge/current in the valid cells
411  for (int idim = 0; idim < AMREX_SPACEDIM; ++idim)
412  {
413  for (int iside = 0; iside < 2; ++iside)
414  {
415  if (!is_pec[idim][iside]) { continue; }
416 
417  amrex::IntVect iv_mirror = ijk_vec;
418  iv_mirror[idim] = mirrorfac[idim][iside] - ijk_vec[idim];
419  if (ijk_vec != iv_mirror && fabbox.contains(iv_mirror))
420  {
421  if (tangent_to_bndy[idim]) {
422  field(iv_mirror, n) = -field(ijk_vec, n);
423  } else {
424  field(iv_mirror, n) = field(ijk_vec, n);
425  }
426  }
427  }
428  }
429  }
430 
431 
447  void SetNeumannOnPEC (const int n,
448  const amrex::IntVect & ijk_vec,
450  amrex::GpuArray<GpuArray<int, 2>, AMREX_SPACEDIM> const& mirrorfac,
451  amrex::GpuArray<GpuArray<bool, 2>, AMREX_SPACEDIM> const& is_pec,
452  amrex::Box const& fabbox )
453  {
454  for (int idim = 0; idim < AMREX_SPACEDIM; ++idim)
455  {
456  for (int iside = 0; iside < 2; ++iside)
457  {
458  if (!is_pec[idim][iside]) { continue; }
459 
460  // Get the mirror guard cell index
461  amrex::IntVect iv_mirror = ijk_vec;
462  iv_mirror[idim] = mirrorfac[idim][iside] - ijk_vec[idim];
463 
464  // On the PEC boundary the field value is set equal to the
465  // first value in the domain (nodal fields)
466  if (ijk_vec == iv_mirror) {
467  iv_mirror[idim] += (iside == 0) ? 1 : -1;
468  if (fabbox.contains(iv_mirror)) { field(ijk_vec, n) = field(iv_mirror, n); }
469  }
470  // otherwise set the mirror guard cell equal to the internal cell value
471  else if (fabbox.contains(iv_mirror))
472  {
473  field(iv_mirror, n) = field(ijk_vec, n);
474  }
475  }
476  }
477  }
478 
479 
481  bool isAnyBoundaryPEC();
493  void ApplyPECtoEfield ( std::array<amrex::MultiFab*, 3> Efield,
494  int lev, PatchType patch_type,
495  bool split_pml_field = false);
505  void ApplyPECtoBfield ( std::array<amrex::MultiFab*, 3> Bfield,
506  int lev, PatchType patch_type);
507 
516  void ApplyPECtoRhofield(amrex::MultiFab* rho, int lev,
517  PatchType patch_type);
518 
528  amrex::MultiFab* Jz, int lev,
529  PatchType patch_type);
530 
539  int lev, PatchType patch_type);
540 }
541 #endif // WarpX_PEC_KERNELS_H_
#define AMREX_FORCE_INLINE
#define AMREX_GPU_DEVICE
PatchType
Definition: WarpX.H:80
@ Reflecting
particles are reflected
AMREX_GPU_HOST_DEVICE bool contains(const IntVect &p) const noexcept
Definition: WarpX_PEC.H:20
void ApplyPECtoRhofield(amrex::MultiFab *rho, int lev, PatchType patch_type)
Reflects charge density deposited over the PEC boundary back into the simulation domain.
Definition: WarpX_PEC.cpp:225
AMREX_GPU_DEVICE AMREX_FORCE_INLINE void SetRhoOrJfieldFromPEC(const int n, const amrex::IntVect &ijk_vec, amrex::Array4< amrex::Real > const &field, amrex::GpuArray< GpuArray< int, 2 >, AMREX_SPACEDIM > const &mirrorfac, amrex::GpuArray< GpuArray< amrex::Real, 2 >, AMREX_SPACEDIM > const &psign, amrex::GpuArray< GpuArray< bool, 2 >, AMREX_SPACEDIM > const &is_pec, amrex::GpuArray< bool, AMREX_SPACEDIM > const &tangent_to_bndy, amrex::Box const &fabbox)
Sets the rho or J field value in cells close to and on a PEC boundary. The charge/current density dep...
Definition: WarpX_PEC.H:378
AMREX_GPU_DEVICE AMREX_FORCE_INLINE void SetNeumannOnPEC(const int n, const amrex::IntVect &ijk_vec, amrex::Array4< amrex::Real > const &field, amrex::GpuArray< GpuArray< int, 2 >, AMREX_SPACEDIM > const &mirrorfac, amrex::GpuArray< GpuArray< bool, 2 >, AMREX_SPACEDIM > const &is_pec, amrex::Box const &fabbox)
This function sets the given field value on a PEC boundary to enforce a Neumann boundary condition (z...
Definition: WarpX_PEC.H:447
void ApplyPECtoJfield(amrex::MultiFab *Jx, amrex::MultiFab *Jy, amrex::MultiFab *Jz, int lev, PatchType patch_type)
Reflects current density deposited over the PEC boundary back into the simulation domain.
Definition: WarpX_PEC.cpp:305
void ApplyPECtoBfield(std::array< amrex::MultiFab *, 3 > Bfield, int lev, PatchType patch_type)
Sets the normal component of the magnetic field at the PEC boundary to zero. The guard cell values ar...
Definition: WarpX_PEC.cpp:124
void ApplyPECtoElectronPressure(amrex::MultiFab *Pefield, int lev, PatchType patch_type)
Apply the PEC boundary to the electron pressure field.
Definition: WarpX_PEC.cpp:496
void ApplyPECtoEfield(std::array< amrex::MultiFab *, 3 > Efield, int lev, PatchType patch_type, bool split_pml_field=false)
Sets the tangential electric field at the PEC boundary to zero. The guard cell values are set equal a...
Definition: WarpX_PEC.cpp:28
AMREX_GPU_DEVICE AMREX_FORCE_INLINE int get_cell_count_to_boundary(const amrex::IntVect &dom_lo, const amrex::IntVect &dom_hi, const amrex::IntVect &ijk_vec, const amrex::IntVect &is_nodal, const int idim, const int iside)
Calculates the number of grid points the given index is pass the domain boundary i....
Definition: WarpX_PEC.H:67
AMREX_GPU_DEVICE AMREX_FORCE_INLINE void SetEfieldOnPEC(const int icomp, const amrex::IntVect &dom_lo, const amrex::IntVect &dom_hi, const amrex::IntVect &ijk_vec, const int n, amrex::Array4< amrex::Real > const &Efield, const amrex::IntVect &is_nodal, amrex::GpuArray< int, 3 > const &fbndry_lo, amrex::GpuArray< int, 3 > const &fbndry_hi)
Sets the electric field value tangential to the PEC boundary to zero. The tangential Efield component...
Definition: WarpX_PEC.H:142
AMREX_GPU_DEVICE AMREX_FORCE_INLINE bool is_boundary_PEC(amrex::GpuArray< int, 3 > const &fboundary, int dir)
Determines if the field boundary condition stored in fboundary in direction, dir, is PEC.
Definition: WarpX_PEC.H:32
AMREX_GPU_DEVICE AMREX_FORCE_INLINE bool is_boundary_reflecting(amrex::GpuArray< ParticleBoundaryType, 3 > const &pboundary, int dir)
Determines if the particle boundary condition stored in pboundary in direction, dir,...
Definition: WarpX_PEC.H:46
AMREX_GPU_DEVICE AMREX_FORCE_INLINE void SetBfieldOnPEC(const int icomp, const amrex::IntVect &dom_lo, const amrex::IntVect &dom_hi, const amrex::IntVect &ijk_vec, const int n, amrex::Array4< amrex::Real > const &Bfield, const amrex::IntVect &is_nodal, amrex::GpuArray< int, 3 > const &fbndry_lo, amrex::GpuArray< int, 3 > const &fbndry_hi)
Sets the magnetic field value normal to the PEC boundary to zero. The tangential (and normal) field v...
Definition: WarpX_PEC.H:281
bool isAnyBoundaryPEC()
Definition: WarpX_PEC.cpp:19
int n
Definition: run_libensemble_on_warpx.py:70
string field
Definition: video_yt.py:31
@ PEC
perfect electric conductor (PEC) with E_tangential=0
Definition: WarpXAlgorithmSelection.H:146