aboutsummaryrefslogtreecommitdiff
path: root/Source/FieldSolver/SpectralSolver
diff options
context:
space:
mode:
authorGravatar Edoardo Zoni <59625522+EZoni@users.noreply.github.com> 2021-03-04 13:00:13 -0800
committerGravatar GitHub <noreply@github.com> 2021-03-04 13:00:13 -0800
commita0870a3063e9e655e281cc31e2d1b6580294696e (patch)
tree2f52fa0d2efa77d77f92c558c92b534bf2c021bd /Source/FieldSolver/SpectralSolver
parentbca858c89e9012f15c94ce896dbe7ea4fedbc322 (diff)
downloadWarpX-a0870a3063e9e655e281cc31e2d1b6580294696e.tar.gz
WarpX-a0870a3063e9e655e281cc31e2d1b6580294696e.tar.zst
WarpX-a0870a3063e9e655e281cc31e2d1b6580294696e.zip
Implement averaged algo on staggered grids & merge spectral classes (#1544)
* Refactor and clean up some spectral classes * Abort when current correction or Vay deposition are not implemented * Implement general equations for averaged Galilean * Allocate averaged MultiFabs also when aux_is_nodal=1 and do_nodal=0 * Allocate +ngextra guard cells also for averaged MultiFabs * Make alias MultiFabs for averaged aux data * With averaging, interpolate from avg_fp (not fp) to aux * Fix some limits of the coefficients * Fix bug causing NaNs in spectral coefficients * Add 2D CI test with same analysis as nodal test * Add 3D CI test with same analysis as nodal test * Add limit that was not covered (knorm=0 && knorm_c!=0 && nu=0) * Allocate T2_coef only if Galilean algorithm is used * Allocate X4_coef only if Galilean algorithm is used * Remove extra ghost cell from 'avg_fp' MultiFabs
Diffstat (limited to 'Source/FieldSolver/SpectralSolver')
-rw-r--r--Source/FieldSolver/SpectralSolver/SpectralAlgorithms/AvgGalileanAlgorithm.cpp376
-rw-r--r--Source/FieldSolver/SpectralSolver/SpectralAlgorithms/CMakeLists.txt4
-rw-r--r--Source/FieldSolver/SpectralSolver/SpectralAlgorithms/GalileanAlgorithm.cpp592
-rw-r--r--Source/FieldSolver/SpectralSolver/SpectralAlgorithms/Make.package2
-rw-r--r--Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.H89
-rw-r--r--Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.cpp1005
-rw-r--r--Source/FieldSolver/SpectralSolver/SpectralSolver.cpp27
7 files changed, 922 insertions, 1173 deletions
diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/AvgGalileanAlgorithm.cpp b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/AvgGalileanAlgorithm.cpp
deleted file mode 100644
index ea381235e..000000000
--- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/AvgGalileanAlgorithm.cpp
+++ /dev/null
@@ -1,376 +0,0 @@
-#include "FieldSolver/SpectralSolver/SpectralAlgorithms/AvgGalileanAlgorithm.H"
-#include "Utils/WarpXConst.H"
-#include <cmath>
-
-using namespace amrex;
-
-/* \brief Initialize coefficients for the update equation */
-AvgGalileanAlgorithm::AvgGalileanAlgorithm(const SpectralKSpace& spectral_kspace,
- const DistributionMapping& dm,
- const int norder_x, const int norder_y,
- const int norder_z, const bool nodal,
- const amrex::Array<amrex::Real,3>& v_galilean,
- const Real dt)
- // Initialize members of base class
- : SpectralBaseAlgorithm( spectral_kspace, dm,
- norder_x, norder_y, norder_z, nodal )
-{
- const BoxArray& ba = spectral_kspace.spectralspace_ba;
-
- // Allocate the arrays of coefficients
- C_coef = SpectralRealCoefficients(ba, dm, 1, 0);
- S_ck_coef = SpectralRealCoefficients(ba, dm, 1, 0);
-
- C1_coef = SpectralRealCoefficients(ba, dm, 1, 0);
- S1_coef = SpectralRealCoefficients(ba, dm, 1, 0);
- C3_coef = SpectralRealCoefficients(ba, dm, 1, 0);
- S3_coef = SpectralRealCoefficients(ba, dm, 1, 0);
-
- Psi1_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
- Psi2_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
- Psi3_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
-
- X1_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
- X2_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
- X3_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
- X4_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
- Theta2_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
-
- A1_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
- A2_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
-
- Rhoold_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
- Rhonew_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
- Jcoef_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
-
- InitializeSpectralCoefficients(spectral_kspace, dm, v_galilean, dt);
-
-}
-
-void AvgGalileanAlgorithm::InitializeSpectralCoefficients(
- const SpectralKSpace& spectral_kspace,
- const amrex::DistributionMapping& dm,
- const Array<Real, 3>& v_galilean,
- const amrex::Real dt)
-{
- const BoxArray& ba = spectral_kspace.spectralspace_ba;
- // Fill them with the right values:
- // Loop over boxes and allocate the corresponding coefficients
- // for each box owned by the local MPI proc
- for (MFIter mfi(ba, dm); mfi.isValid(); ++mfi){
-
- const Box& bx = ba[mfi];
-
- // Extract pointers for the k vectors
- const Real* modified_kx = modified_kx_vec[mfi].dataPtr();
-#if (AMREX_SPACEDIM==3)
- const Real* modified_ky = modified_ky_vec[mfi].dataPtr();
-#endif
- const Real* modified_kz = modified_kz_vec[mfi].dataPtr();
- // Extract arrays for the coefficients
- Array4<Real> C = C_coef[mfi].array();
- Array4<Real> S_ck = S_ck_coef[mfi].array();
- Array4<Real> C1 = C1_coef[mfi].array();
- Array4<Real> S1 = S1_coef[mfi].array();
- Array4<Real> C3 = C3_coef[mfi].array();
- Array4<Real> S3 = S3_coef[mfi].array();
-
- Array4<Complex> Psi1 = Psi1_coef[mfi].array();
- Array4<Complex> Psi2 = Psi2_coef[mfi].array();
- Array4<Complex> Psi3 = Psi3_coef[mfi].array();
- Array4<Complex> X1 = X1_coef[mfi].array();
- Array4<Complex> X2 = X2_coef[mfi].array();
- Array4<Complex> X3 = X3_coef[mfi].array();
- Array4<Complex> X4 = X4_coef[mfi].array();
- Array4<Complex> Theta2 = Theta2_coef[mfi].array();
- Array4<Complex> A1 = A1_coef[mfi].array();
- Array4<Complex> A2 = A2_coef[mfi].array();
-
- Array4<Complex> CRhoold = Rhoold_coef[mfi].array();
- Array4<Complex> CRhonew = Rhonew_coef[mfi].array();
- Array4<Complex> Jcoef = Jcoef_coef[mfi].array();
- // Extract reals (for portability on GPU)
- Real vx = v_galilean[0];
-#if (AMREX_SPACEDIM==3)
- Real vy = v_galilean[1];
-#endif
- Real vz = v_galilean[2];
-
- // Loop over indices within one box
- ParallelFor(bx,
- [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
- {
- // Calculate norm of vector
- const Real k_norm = std::sqrt(
- std::pow(modified_kx[i], 2) +
-#if (AMREX_SPACEDIM==3)
- std::pow(modified_ky[j], 2) +
- std::pow(modified_kz[k], 2));
-#else
- std::pow(modified_kz[j], 2));
-#endif
-
- // Calculate coefficients
- constexpr Real c = PhysConst::c;
- constexpr Real c2 = PhysConst::c*PhysConst::c;
- constexpr Real ep0 = PhysConst::ep0;
- const Complex I{0.,1.};
- if (k_norm != 0){
-
- C(i,j,k) = std::cos(c*k_norm*dt);
- S_ck(i,j,k) = std::sin(c*k_norm*dt)/(c*k_norm);
-
- C1(i,j,k) = std::cos(0.5_rt*c*k_norm*dt);
- S1(i,j,k) = std::sin(0.5_rt*c*k_norm*dt);
- C3(i,j,k) = std::cos(1.5_rt*c*k_norm*dt);
- S3(i,j,k) = std::sin(1.5_rt*c*k_norm*dt);
-
- // Calculate dot product with galilean velocity
- const Real kv = modified_kx[i]*vx +
-#if (AMREX_SPACEDIM==3)
- modified_ky[j]*vy +
- modified_kz[k]*vz;
-#else
- modified_kz[j]*vz;
-#endif
-
- const Real nu = kv/(k_norm*c);
- const Complex theta = amrex::exp( 0.5_rt*I*kv*dt );
- const Complex theta_star = amrex::exp( -0.5_rt*I*kv*dt );
- const Complex e_theta = amrex::exp( I*c*k_norm*dt );
-
- Theta2(i,j,k) = theta*theta;
-
- if ( (nu != 1.) && (nu != 0) ) {
-
- // Note: the coefficients X1, X2, X3 do not correspond
- // exactly to the original Galilean paper, but the
- // update equation have been modified accordingly so that
- // the expressions/ below (with the update equations)
- // are mathematically equivalent to those of the paper.
- Complex x1 = 1._rt/(1._rt-nu*nu) *
- (theta_star - C(i,j,k)*theta + I*kv*S_ck(i,j,k)*theta);
-
- Complex C_rho = I* c2 /( (1._rt-theta*theta) * ep0);
-
- Psi1(i,j,k) = theta * ((S1(i,j,k) + I*nu*C1(i,j,k))
- - Theta2(i,j,k) * (S3(i,j,k) + I*nu*C3(i,j,k))) /(c*k_norm*dt * (nu*nu - 1._rt));
- Psi2(i,j,k) = theta * ((C1(i,j,k) - I*nu*S1(i,j,k))
- - Theta2(i,j,k) * (C3(i,j,k) - I*nu*S3(i,j,k))) /(c2*k_norm*k_norm*dt * (nu*nu - 1._rt));
- Psi3(i,j,k) = I * theta * (1._rt - theta*theta) /(c*k_norm*dt*nu);
-
- A1(i,j,k) = (Psi1(i,j,k) - 1._rt + I * kv*Psi2(i,j,k) )/ (c2* k_norm*k_norm * (nu*nu - 1._rt));
- A2(i,j,k) = (Psi3(i,j,k) - Psi1(i,j,k)) / (c2*k_norm*k_norm);
-
- CRhoold(i,j,k) = C_rho * (theta*theta * A1(i,j,k) - A2(i,j,k));
- CRhonew(i,j,k) = C_rho * (A2(i,j,k) - A1(i,j,k));
- Jcoef(i,j,k) = (I*kv*A1(i,j,k) + Psi2(i,j,k))/ep0;
- // x1, above, is identical to the original paper
- X1(i,j,k) = theta*x1/(ep0*c*c*k_norm*k_norm);
- // The difference betwen X2 and X3 below, and those
- // from the original paper is the factor ep0*k_norm*k_norm
- X2(i,j,k) = (x1 - theta*(1._rt - C(i,j,k)))
- /(theta_star-theta)/(ep0*k_norm*k_norm);
- X3(i,j,k) = (x1 - theta_star*(1._rt - C(i,j,k)))
- /(theta_star-theta)/(ep0*k_norm*k_norm);
- X4(i,j,k) = I*kv*X1(i,j,k) - theta*theta*S_ck(i,j,k)/ep0;
- }
- if ( nu == 0) {
- X1(i,j,k) = (1._rt - C(i,j,k)) / (ep0*c*c*k_norm*k_norm);
- X2(i,j,k) = (1._rt - S_ck(i,j,k)/dt) / (ep0*k_norm*k_norm);
- X3(i,j,k) = (C(i,j,k) - S_ck(i,j,k)/dt) / (ep0*k_norm*k_norm);
- X4(i,j,k) = -S_ck(i,j,k)/ep0;
-
- Psi1(i,j,k) = (-S1(i,j,k) + S3(i,j,k)) / (c*k_norm*dt);
- Psi2(i,j,k) = (-C1(i,j,k) + C3(i,j,k)) / (c2*k_norm*k_norm*dt);
- Psi3(i,j,k) = 1._rt;
- A1(i,j,k) = (c*k_norm*dt + S1(i,j,k) - S3(i,j,k)) / (c*c2 * k_norm*k_norm*k_norm * dt);
- A2(i,j,k) = (c*k_norm*dt + S1(i,j,k) - S3(i,j,k)) / (c*c2 * k_norm*k_norm*k_norm * dt);
- CRhoold(i,j,k) = 2._rt * I * S1(i,j,k) * ( dt*C(i,j,k) - S_ck(i,j,k))
- / (c*k_norm*k_norm*k_norm*dt*dt*ep0);
- CRhonew(i,j,k) = - I * (c2* k_norm*k_norm * dt*dt - C1(i,j,k) + C3(i,j,k))
- / (c2 * k_norm*k_norm*k_norm*k_norm * ep0 * dt*dt);
- Jcoef(i,j,k) = (-C1(i,j,k) + C3(i,j,k)) / (c2*ep0*k_norm*k_norm*dt);
- }
- if ( nu == 1.) {
- X1(i,j,k) = (1._rt - e_theta*e_theta + 2._rt*I*c*k_norm*dt) / (4._rt*c*c*ep0*k_norm*k_norm);
- X2(i,j,k) = (3._rt - 4._rt*e_theta + e_theta*e_theta + 2._rt*I*c*k_norm*dt) / (4._rt*ep0*k_norm*k_norm*(1._rt- e_theta));
- X3(i,j,k) = (3._rt - 2._rt/e_theta - 2._rt*e_theta + e_theta*e_theta - 2._rt*I*c*k_norm*dt) / (4._rt*ep0*(e_theta - 1._rt)*k_norm*k_norm);
- X4(i,j,k) = I*(-1._rt + e_theta*e_theta + 2._rt*I*c*k_norm*dt) / (4._rt*ep0*c*k_norm);
- }
-
- } else { // Handle k_norm = 0, by using the analytical limit
- C(i,j,k) = 1._rt;
- S_ck(i,j,k) = dt;
- C1(i,j,k) = 1._rt;
- S1(i,j,k) = 0._rt;
- C3(i,j,k) = 1._rt;
- S3(i,j,k) = 0._rt;
-
- X1(i,j,k) = dt*dt/(2._rt * ep0);
- X2(i,j,k) = c2*dt*dt/(6._rt * ep0);
- X3(i,j,k) = - c2*dt*dt/(3._rt * ep0);
- X4(i,j,k) = -dt/ep0;
- Theta2(i,j,k) = 1._rt;
-
- Psi1(i,j,k) = 1._rt;
- Psi2(i,j,k) = -dt;
- Psi3(i,j,k) = 1._rt;
- A1(i,j,k) = 13._rt * dt*dt /24._rt;
- A2(i,j,k) = 13._rt * dt*dt /24._rt;
- CRhoold(i,j,k) = -I*c2 * dt*dt / (3._rt * ep0);
- CRhonew(i,j,k) = -5._rt*I*c2 * dt*dt / (24._rt * ep0);
- Jcoef(i,j,k) = -dt/ep0;
- }
-
- });
- }
-}
-
-/* Advance the E and B field in spectral space (stored in `f`)
- * over one time step */
-void
-AvgGalileanAlgorithm::pushSpectralFields(SpectralFieldData& f) const{
-
- // Loop over boxes
- for (MFIter mfi(f.fields); mfi.isValid(); ++mfi){
-
- const Box& bx = f.fields[mfi].box();
-
- // Extract arrays for the fields to be updated
- Array4<Complex> fields = f.fields[mfi].array();
- // Extract arrays for the coefficients
- Array4<const Real> C_arr = C_coef[mfi].array();
- Array4<const Real> S_ck_arr = S_ck_coef[mfi].array();
- Array4<const Complex> X1_arr = X1_coef[mfi].array();
- Array4<const Complex> X2_arr = X2_coef[mfi].array();
- Array4<const Complex> X3_arr = X3_coef[mfi].array();
- Array4<const Complex> X4_arr = X4_coef[mfi].array();
- Array4<const Complex> Theta2_arr = Theta2_coef[mfi].array();
- Array4<const Complex> Psi1_arr = Psi1_coef[mfi].array();
- Array4<const Complex> Psi2_arr = Psi2_coef[mfi].array();
-
- Array4<const Complex> A1_arr = A1_coef[mfi].array();
- Array4<const Complex> Rhonew_arr = Rhonew_coef[mfi].array();
- Array4<const Complex> Rhoold_arr = Rhoold_coef[mfi].array();
- Array4<const Complex> Jcoef_arr =Jcoef_coef[mfi].array();
- // Extract pointers for the k vectors
- const Real* modified_kx_arr = modified_kx_vec[mfi].dataPtr();
-#if (AMREX_SPACEDIM==3)
- const Real* modified_ky_arr = modified_ky_vec[mfi].dataPtr();
-#endif
- const Real* modified_kz_arr = modified_kz_vec[mfi].dataPtr();
-
- // Loop over indices within one box
- ParallelFor(bx,
- [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
- {
- // Record old values of the fields to be updated
- using Idx = SpectralAvgFieldIndex;
-
- const Complex Ex_old = fields(i,j,k,Idx::Ex);
- const Complex Ey_old = fields(i,j,k,Idx::Ey);
- const Complex Ez_old = fields(i,j,k,Idx::Ez);
- const Complex Bx_old = fields(i,j,k,Idx::Bx);
- const Complex By_old = fields(i,j,k,Idx::By);
- const Complex Bz_old = fields(i,j,k,Idx::Bz);
-
- // Shortcut for the values of J and rho
- const Complex Jx = fields(i,j,k,Idx::Jx);
- const Complex Jy = fields(i,j,k,Idx::Jy);
- const Complex Jz = fields(i,j,k,Idx::Jz);
- const Complex rho_old = fields(i,j,k,Idx::rho_old);
- const Complex rho_new = fields(i,j,k,Idx::rho_new);
-
- // k vector values, and coefficients
- const Real kx = modified_kx_arr[i];
-
-#if (AMREX_SPACEDIM==3)
- const Real ky = modified_ky_arr[j];
- const Real kz = modified_kz_arr[k];
-#else
- constexpr Real ky = 0;
- const Real kz = modified_kz_arr[j];
-#endif
- constexpr Real c2 = PhysConst::c*PhysConst::c;
- constexpr Real inv_ep0 = 1._rt/PhysConst::ep0;
- constexpr Complex I = Complex{0,1};
-
- const Real C = C_arr(i,j,k);
- const Real S_ck = S_ck_arr(i,j,k);
-
- const Complex X1 = X1_arr(i,j,k);
- const Complex X2 = X2_arr(i,j,k);
- const Complex X3 = X3_arr(i,j,k);
- const Complex X4 = X4_arr(i,j,k);
- const Complex T2 = Theta2_arr(i,j,k);
-
- const Complex Psi1 = Psi1_arr(i,j,k);
- const Complex Psi2 = Psi2_arr(i,j,k);
- const Complex A1 = A1_arr(i,j,k);
- const Complex CRhoold= Rhoold_arr(i,j,k);
- const Complex CRhonew= Rhonew_arr(i,j,k);
- const Complex Jcoef = Jcoef_arr(i,j,k);
-
- //Update E (see the original Galilean article)
- fields(i,j,k,Idx::Ex) = T2*C*Ex_old
- + T2*S_ck*c2*I*(ky*Bz_old - kz*By_old)
- + X4*Jx - I*(X2*rho_new - T2*X3*rho_old)*kx;
- fields(i,j,k,Idx::Ey) = T2*C*Ey_old
- + T2*S_ck*c2*I*(kz*Bx_old - kx*Bz_old)
- + X4*Jy - I*(X2*rho_new - T2*X3*rho_old)*ky;
- fields(i,j,k,Idx::Ez) = T2*C*Ez_old
- + T2*S_ck*c2*I*(kx*By_old - ky*Bx_old)
- + X4*Jz - I*(X2*rho_new - T2*X3*rho_old)*kz;
-
- // Update B (see the original Galilean article)
- // Note: here X1 is T2*x1/(ep0*c*c*k_norm*k_norm), where
- // x1 has the same definition as in the original paper
- fields(i,j,k,Idx::Bx) = T2*C*Bx_old
- - T2*S_ck*I*(ky*Ez_old - kz*Ey_old)
- + X1*I*(ky*Jz - kz*Jy);
- fields(i,j,k,Idx::By) = T2*C*By_old
- - T2*S_ck*I*(kz*Ex_old - kx*Ez_old)
- + X1*I*(kz*Jx - kx*Jz);
- fields(i,j,k,Idx::Bz) = T2*C*Bz_old
- - T2*S_ck*I*(kx*Ey_old - ky*Ex_old)
- + X1*I*(kx*Jy - ky*Jx);
-
- //Update the averaged E,B fields in time on the interval [(n-1/2)dx, (n+1/2)dx]
- fields(i,j,k,Idx::Ex_avg) = Psi1*Ex_old
- - Psi2*c2*I*(ky*Bz_old - kz*By_old)
- + Jcoef*Jx + ( CRhonew * rho_new + CRhoold*rho_old )*kx;
- fields(i,j,k,Idx::Ey_avg) = Psi1*Ey_old
- - Psi2*c2*I*(kz*Bx_old - kx*Bz_old)
- + Jcoef*Jy +( CRhonew * rho_new + CRhoold*rho_old )*ky;
- fields(i,j,k,Idx::Ez_avg) = Psi1*Ez_old
- - Psi2*c2*I*(kx*By_old - ky*Bx_old)
- + Jcoef*Jz + ( CRhonew * rho_new + CRhoold*rho_old )*kz;
-
- fields(i,j,k,Idx::Bx_avg) = Psi1*Bx_old
- + I*Psi2*(ky*Ez_old - kz*Ey_old)
- + A1*I*(ky*Jz - kz*Jy)*inv_ep0;
- fields(i,j,k,Idx::By_avg) = Psi1*By_old
- + I*Psi2*(kz*Ex_old - kx*Ez_old)
- + A1*I*(kz*Jx - kx*Jz)*inv_ep0;
- fields(i,j,k,Idx::Bz_avg) = Psi1*Bz_old
- + I*Psi2*(kx*Ey_old - ky*Ex_old)
- + A1*I*(kx*Jy - ky*Jx)*inv_ep0;
- });
- }
-}
-
-void
-AvgGalileanAlgorithm::CurrentCorrection (SpectralFieldData& /*field_data*/,
- std::array<std::unique_ptr<amrex::MultiFab>,3>& /*current*/,
- const std::unique_ptr<amrex::MultiFab>& /*rho*/)
-{
- amrex::Abort("Current correction not implemented for averaged Galilean PSATD");
-}
-
-void
-AvgGalileanAlgorithm::VayDeposition (SpectralFieldData& /*field_data*/,
- std::array<std::unique_ptr<amrex::MultiFab>,3>& /*current*/)
-{
- amrex::Abort("Vay deposition not implemented for averaged Galilean PSATD");
-}
diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/CMakeLists.txt b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/CMakeLists.txt
index ab8e439ed..f46fd9eb9 100644
--- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/CMakeLists.txt
+++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/CMakeLists.txt
@@ -1,10 +1,8 @@
target_sources(WarpX
PRIVATE
- GalileanAlgorithm.cpp
- PMLPsatdAlgorithm.cpp
PsatdAlgorithm.cpp
+ PMLPsatdAlgorithm.cpp
SpectralBaseAlgorithm.cpp
- AvgGalileanAlgorithm.cpp
ComovingPsatdAlgorithm.cpp
)
diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/GalileanAlgorithm.cpp b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/GalileanAlgorithm.cpp
deleted file mode 100644
index ea5aa7ec0..000000000
--- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/GalileanAlgorithm.cpp
+++ /dev/null
@@ -1,592 +0,0 @@
-#include "GalileanAlgorithm.H"
-#include "Utils/WarpXConst.H"
-
-#include <cmath>
-
-
-#if WARPX_USE_PSATD
-
-using namespace amrex;
-
-/* \brief Initialize coefficients for the update equation */
-GalileanAlgorithm::GalileanAlgorithm(const SpectralKSpace& spectral_kspace,
- const DistributionMapping& dm,
- const int norder_x, const int norder_y,
- const int norder_z, const bool nodal,
- const Array<Real, 3>& v_galilean,
- const Real dt,
- const bool update_with_rho)
- // Initialize members of base class
- : SpectralBaseAlgorithm(spectral_kspace, dm, norder_x, norder_y, norder_z, nodal),
- // Initialize the centered finite-order modified k vectors: these are computed
- // always with the assumption of centered grids (argument nodal = true),
- // for both nodal and staggered simulations
- modified_kx_vec_centered(spectral_kspace.getModifiedKComponent(dm,0,norder_x,true)),
-#if (AMREX_SPACEDIM==3)
- modified_ky_vec_centered(spectral_kspace.getModifiedKComponent(dm,1,norder_y,true)),
- modified_kz_vec_centered(spectral_kspace.getModifiedKComponent(dm,2,norder_z,true)),
-#else
- modified_kz_vec_centered(spectral_kspace.getModifiedKComponent(dm,1,norder_z,true)),
-#endif
- m_v_galilean(v_galilean),
- m_dt(dt),
- m_update_with_rho(update_with_rho)
-{
- const BoxArray& ba = spectral_kspace.spectralspace_ba;
-
- // Allocate the arrays of coefficients
- C_coef = SpectralRealCoefficients(ba, dm, 1, 0);
- S_ck_coef = SpectralRealCoefficients(ba, dm, 1, 0);
- X1_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
- X2_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
- X3_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
- X4_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
- Theta2_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
-
- InitializeSpectralCoefficients(spectral_kspace, dm, dt);
-}
-
-/* Advance the E and B field in spectral space (stored in `f`) over one time step */
-void
-GalileanAlgorithm::pushSpectralFields (SpectralFieldData& f) const
-{
- const bool update_with_rho = m_update_with_rho;
-
- // Loop over boxes
- for (MFIter mfi(f.fields); mfi.isValid(); ++mfi){
-
- const Box& bx = f.fields[mfi].box();
-
- // Extract arrays for the fields to be updated
- Array4<Complex> fields = f.fields[mfi].array();
-
- // Extract arrays for the coefficients
- Array4<const Real> C_arr = C_coef[mfi].array();
- Array4<const Real> S_ck_arr = S_ck_coef[mfi].array();
- Array4<const Complex> X1_arr = X1_coef[mfi].array();
- Array4<const Complex> X2_arr = X2_coef[mfi].array();
- Array4<const Complex> X3_arr = X3_coef[mfi].array();
- Array4<const Complex> X4_arr = X4_coef[mfi].array();
- Array4<const Complex> Theta2_arr = Theta2_coef[mfi].array();
-
- // Extract pointers for the k vectors
- const Real* modified_kx_arr = modified_kx_vec[mfi].dataPtr();
-#if (AMREX_SPACEDIM==3)
- const Real* modified_ky_arr = modified_ky_vec[mfi].dataPtr();
-#endif
- const Real* modified_kz_arr = modified_kz_vec[mfi].dataPtr();
-
- // Loop over indices within one box
- ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
- {
- // Record old values of the fields to be updated
- using Idx = SpectralFieldIndex;
- const Complex Ex_old = fields(i,j,k,Idx::Ex);
- const Complex Ey_old = fields(i,j,k,Idx::Ey);
- const Complex Ez_old = fields(i,j,k,Idx::Ez);
- const Complex Bx_old = fields(i,j,k,Idx::Bx);
- const Complex By_old = fields(i,j,k,Idx::By);
- const Complex Bz_old = fields(i,j,k,Idx::Bz);
-
- // Shortcuts for the values of J and rho
- const Complex Jx = fields(i,j,k,Idx::Jx);
- const Complex Jy = fields(i,j,k,Idx::Jy);
- const Complex Jz = fields(i,j,k,Idx::Jz);
- const Complex rho_old = fields(i,j,k,Idx::rho_old);
- const Complex rho_new = fields(i,j,k,Idx::rho_new);
-
- // k vector values
- const Real kx = modified_kx_arr[i];
-#if (AMREX_SPACEDIM==3)
- const Real ky = modified_ky_arr[j];
- const Real kz = modified_kz_arr[k];
-#else
- constexpr Real ky = 0._rt;
- const Real kz = modified_kz_arr[j];
-#endif
- // Physical constant c**2 and imaginary unit
- constexpr Real c2 = PhysConst::c*PhysConst::c;
- constexpr Complex I = Complex{0._rt,1._rt};
-
- // The definition of these coefficients is explained in more detail
- // in the function InitializeSpectralCoefficients below
- const Real C = C_arr(i,j,k);
- const Real S_ck = S_ck_arr(i,j,k);
- const Complex X1 = X1_arr(i,j,k);
- const Complex X2 = X2_arr(i,j,k);
- const Complex X3 = X3_arr(i,j,k);
- const Complex X4 = X4_arr(i,j,k);
- const Complex T2 = Theta2_arr(i,j,k);
-
- // The equations in the following are the update equations for B and E,
- // equations (11a) and (11b) of (Lehe et al, PRE 94, 2016), respectively,
- // (or their rho-free formulation)
-
- // Update E (equation (11b) or its rho-free formulation):
- if (update_with_rho) {
-
- // Ex
- fields(i,j,k,Idx::Ex) = T2*C*Ex_old
- + T2*S_ck*c2*I*(ky*Bz_old - kz*By_old)
- + X4*Jx - I*(X2*rho_new - T2*X3*rho_old)*kx;
- // Ey
- fields(i,j,k,Idx::Ey) = T2*C*Ey_old
- + T2*S_ck*c2*I*(kz*Bx_old - kx*Bz_old)
- + X4*Jy - I*(X2*rho_new - T2*X3*rho_old)*ky;
- // Ez
- fields(i,j,k,Idx::Ez) = T2*C*Ez_old
- + T2*S_ck*c2*I*(kx*By_old - ky*Bx_old)
- + X4*Jz - I*(X2*rho_new - T2*X3*rho_old)*kz;
- } else {
-
- Complex k_dot_J = kx * Jx + ky * Jy + kz * Jz;
- Complex k_dot_E = kx * Ex_old + ky * Ey_old + kz * Ez_old;
-
- // Ex
- fields(i,j,k,Idx::Ex) = T2 * C * Ex_old + I * T2 * S_ck * c2 * (ky * Bz_old - kz * By_old)
- + X4 * Jx + X2 * k_dot_E * kx + X3 * k_dot_J * kx;
- // Ey
- fields(i,j,k,Idx::Ey) = T2 * C * Ey_old + I * T2 * S_ck * c2 * (kz * Bx_old - kx * Bz_old)
- + X4 * Jy + X2 * k_dot_E * ky + X3 * k_dot_J * ky;
- // Ez
- fields(i,j,k,Idx::Ez) = T2 * C * Ez_old + I * T2 * S_ck * c2 * (kx * By_old - ky * Bx_old)
- + X4 * Jz + X2 * k_dot_E * kz + X3 * k_dot_J * kz;
- }
-
- // Update B (equation (11a) with X1 rescaled by theta/(epsilon_0*c**2*k**2)):
- // Bx
- fields(i,j,k,Idx::Bx) = T2*C*Bx_old - T2*S_ck*I*(ky*Ez_old - kz*Ey_old) + X1*I*(ky*Jz - kz*Jy);
- // By
- fields(i,j,k,Idx::By) = T2*C*By_old - T2*S_ck*I*(kz*Ex_old - kx*Ez_old) + X1*I*(kz*Jx - kx*Jz);
- // Bz
- fields(i,j,k,Idx::Bz) = T2*C*Bz_old - T2*S_ck*I*(kx*Ey_old - ky*Ex_old) + X1*I*(kx*Jy - ky*Jx);
- });
- }
-}
-
-void GalileanAlgorithm::InitializeSpectralCoefficients (const SpectralKSpace& spectral_kspace,
- const amrex::DistributionMapping& dm,
- const amrex::Real dt)
-{
- const bool update_with_rho = m_update_with_rho;
-
- const BoxArray& ba = spectral_kspace.spectralspace_ba;
-
- // Loop over boxes and allocate the corresponding coefficients for each box
- for (MFIter mfi(ba, dm); mfi.isValid(); ++mfi) {
-
- const Box& bx = ba[mfi];
-
- // Extract pointers for the k vectors
- const Real* kx = modified_kx_vec[mfi].dataPtr();
- const Real* kx_c = modified_kx_vec_centered[mfi].dataPtr();
-#if (AMREX_SPACEDIM==3)
- const Real* ky = modified_ky_vec[mfi].dataPtr();
- const Real* ky_c = modified_ky_vec_centered[mfi].dataPtr();
-#endif
- const Real* kz = modified_kz_vec[mfi].dataPtr();
- const Real* kz_c = modified_kz_vec_centered[mfi].dataPtr();
-
- // Extract arrays for the coefficients
- Array4<Real> C = C_coef[mfi].array();
- Array4<Real> S_ck = S_ck_coef[mfi].array();
- Array4<Complex> X1 = X1_coef[mfi].array();
- Array4<Complex> X2 = X2_coef[mfi].array();
- Array4<Complex> X3 = X3_coef[mfi].array();
- Array4<Complex> X4 = X4_coef[mfi].array();
- Array4<Complex> T2 = Theta2_coef[mfi].array();
-
- // Extract Galilean velocity
- Real vx = m_v_galilean[0];
-#if (AMREX_SPACEDIM==3)
- Real vy = m_v_galilean[1];
-#endif
- Real vz = m_v_galilean[2];
-
- // Loop over indices within one box
- ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
- {
- // Calculate norm of vector
- const Real knorm = std::sqrt(
- std::pow(kx[i], 2) +
-#if (AMREX_SPACEDIM==3)
- std::pow(ky[j], 2) +
- std::pow(kz[k], 2));
-#else
- std::pow(kz[j], 2));
-#endif
- // Calculate norm of vector
- const Real knorm_c = std::sqrt(
- std::pow(kx_c[i], 2) +
-#if (AMREX_SPACEDIM==3)
- std::pow(ky_c[j], 2) +
- std::pow(kz_c[k], 2));
-#else
- std::pow(kz_c[j], 2));
-#endif
- // Physical constants c, c**2, and epsilon_0, and imaginary unit
- constexpr Real c = PhysConst::c;
- constexpr Real c2 = c*c;
- constexpr Real ep0 = PhysConst::ep0;
- constexpr Complex I = Complex{0._rt,1._rt};
-
- // Auxiliary coefficients used when update_with_rho=false
- const Real dt2 = dt * dt;
- const Real dt3 = dt * dt2;
- Complex X2_old, X3_old;
-
- // Calculate dot product of k vector with Galilean velocity:
- // this has to be computed always with the centered finite-order modified k
- // vectors, in order to work correctly for both nodal and staggered simulations
- const Real kv = kx_c[i]*vx +
-#if (AMREX_SPACEDIM==3)
- ky_c[j]*vy + kz_c[k]*vz;
-#else
- kz_c[j]*vz;
-#endif
- // The coefficients in the following refer to the ones given in equations
- // (12a)-(12d) of (Lehe et al, PRE 94, 2016), used to update B and E
- // (equations (11a) and (11b) of the same reference, respectively)
-
- if (knorm != 0. && knorm_c != 0.) {
-
- // Auxiliary coefficients
- const Real om = c * knorm;
- const Real om2 = om * om;
- const Real om3 = om * om2;
- const Real om_c = c * knorm_c;
- const Real om2_c = om_c * om_c;
- const Real om3_c = om_c * om2_c;
- const Complex tmp1 = amrex::exp( I * om * dt);
- const Complex tmp2 = amrex::exp(- I * om * dt);
-
- // See equation (12a)
- C (i,j,k) = std::cos(om * dt);
- S_ck(i,j,k) = std::sin(om * dt) / om;
-
- // See equation (12b)
- const Real nu = kv / om_c;
- const Complex theta = amrex::exp( I * nu * om_c * dt * 0.5_rt);
- const Complex theta_star = amrex::exp(- I * nu * om_c * dt * 0.5_rt);
-
- // This is exp(i*(k \dot v_gal)*dt)
- T2(i,j,k) = theta * theta;
-
- if ( (nu != om/om_c) && (nu != -om/om_c) && (nu != 0.) ) {
-
- // x1 is the coefficient chi_1 in equation (12c)
- Complex x1 = om2_c / (om2 - nu * nu * om2_c)
- * (theta_star - theta * C(i,j,k) + I * nu * om_c * theta * S_ck(i,j,k));
-
- // X1 multiplies i*(k \times J) in the update equation for B
- X1(i,j,k) = theta * x1 / (ep0 * om2_c);
-
- if (update_with_rho) {
- // X2 multiplies rho_new in the update equation for E
- // X3 multiplies rho_old in the update equation for E
- X2(i,j,k) = c2 * (x1 * om2 - theta * (1._rt - C(i,j,k)) * om2_c)
- / (theta_star - theta) / (ep0 * om2_c * om2);
- X3(i,j,k) = c2 * (x1 * om2 - theta_star * (1._rt - C(i,j,k)) * om2_c)
- / (theta_star - theta) / (ep0 * om2_c * om2);
- } else {
- // X2_old is the coefficient chi_2 in equation (12d)
- // X3_old is the coefficient chi_3 in equation (12d)
- // X2 multiplies (k \dot E) in the update equation for E
- // X3 multiplies (k \dot J) in the update equation for E
- X2_old = (x1 * om2 - theta * (1._rt - C(i,j,k)) * om2_c)
- / (theta_star - theta);
- X3_old = (x1 * om2 - theta_star * (1._rt - C(i,j,k)) * om2_c)
- / (theta_star - theta);
- X2(i,j,k) = c2 * T2(i,j,k) * (X2_old - X3_old)
- / (om2_c * om2);
- X3(i,j,k) = I * c2 * X2_old * (T2(i,j,k) - 1._rt)
- / (ep0 * nu * om3_c * om2);
- }
-
- // X4 multiplies J in the update equation for E
- X4(i,j,k) = I * nu * om_c * X1(i,j,k) - T2(i,j,k) * S_ck(i,j,k) / ep0;
- }
-
- // Limits for nu = 0
- if (nu == 0.) {
-
- // X1 multiplies i*(k \times J) in the update equation for B
- X1(i,j,k) = (1._rt - C(i,j,k)) / (ep0 * om2);
-
- if (update_with_rho) {
- // X2 multiplies rho_new in the update equation for E
- // X3 multiplies rho_old in the update equation for E
- X2(i,j,k) = c2 * (1._rt - S_ck(i,j,k) / dt) / (ep0 * om2);
- X3(i,j,k) = c2 * (C(i,j,k) - S_ck(i,j,k) / dt) / (ep0 * om2);
- } else {
- // X2 multiplies (k \dot E) in the update equation for E
- // X3 multiplies (k \dot J) in the update equation for E
- X2(i,j,k) = c2 * (1._rt - C(i,j,k)) / om2;
- X3(i,j,k) = c2 * (S_ck(i,j,k) / dt - 1._rt) * dt / (ep0 * om2);
- }
-
- // Coefficient multiplying J in update equation for E
- X4(i,j,k) = - S_ck(i,j,k) / ep0;
- }
-
- // Limits for nu = omega/omega_c
- if (nu == om/om_c) {
-
- // X1 multiplies i*(k \times J) in the update equation for B
- X1(i,j,k) = (1._rt - tmp1 * tmp1 + 2._rt * I * om * dt) / (4._rt * ep0 * om2);
-
- if (update_with_rho) {
- // X2 multiplies rho_new in the update equation for E
- // X3 multiplies rho_old in the update equation for E
- X2(i,j,k) = c2 * (- 3._rt + 4._rt * tmp1 - tmp1 * tmp1 - 2._rt * I * om * dt)
- / (4._rt * ep0 * om2 * (tmp1 - 1._rt));
- X3(i,j,k) = c2 * (3._rt - 2._rt * tmp2 - 2._rt * tmp1 + tmp1 * tmp1 - 2._rt * I * om * dt)
- / (4._rt * ep0 * om2 * (tmp1 - 1._rt));
- } else {
- // X2 multiplies (k \dot E) in the update equation for E
- // X3 multiplies (k \dot J) in the update equation for E
- X2(i,j,k) = c2 * (1._rt - C(i,j,k)) * tmp1 / om2;
- X3(i,j,k) = c2 * (2._rt * om * dt - I * tmp1 * tmp1 + 4._rt * I * tmp1 - 3._rt * I)
- / (4._rt * ep0 * om3);
- }
-
- // Coefficient multiplying J in update equation for E
- X4(i,j,k) = (- I + I * tmp1 * tmp1 - 2._rt * om * dt) / (4._rt * ep0 * om);
- }
-
- // Limits for nu = -omega/omega_c
- if (nu == -om/om_c) {
-
- // X1 multiplies i*(k \times J) in the update equation for B
- X1(i,j,k) = (1._rt - tmp2 * tmp2 - 2._rt * I * om * dt) / (4._rt * ep0 * om2);
-
- if (update_with_rho) {
- // X2 multiplies rho_new in the update equation for E
- // X3 multiplies rho_old in the update equation for E
- X2(i,j,k) = c2 * (- 4._rt + 3._rt * tmp1 + tmp2 - 2._rt * I * om * dt * tmp1)
- / (4._rt * ep0 * om2 * (tmp1 - 1._rt));
- X3(i,j,k) = c2 * (2._rt - tmp2 - 3._rt * tmp1 + 2._rt * tmp1 * tmp1 - 2._rt * I * om * dt * tmp1)
- / (4._rt * ep0 * om2 * (tmp1 - 1._rt));
- } else {
- // X2 multiplies (k \dot E) in the update equation for E
- // X3 multiplies (k \dot J) in the update equation for E
- X2(i,j,k) = c2 * (1._rt - C(i,j,k)) * tmp2 / om2;
- X3(i,j,k) = c2 * (2._rt * om * dt + I * tmp2 * tmp2 - 4._rt * I * tmp2 + 3._rt * I)
- / (4._rt * ep0 * om3);
- }
-
- // Coefficient multiplying J in update equation for E
- X4(i,j,k) = (I - I * tmp2 * tmp2 - 2._rt * om * dt) / (4._rt * ep0 * om);
- }
- }
-
- // Limits for omega_c = 0 only
- else if (knorm != 0. && knorm_c == 0.) {
-
- const Real om = c * knorm;
- const Real om2 = om * om;
-
- C (i,j,k) = std::cos(om * dt);
- S_ck(i,j,k) = std::sin(om * dt) / om;
- T2(i,j,k) = 1._rt;
-
- // X1 multiplies i*(k \times J) in the update equation for B
- X1(i,j,k) = (1._rt - C(i,j,k)) / (ep0 * om2);
-
- if (update_with_rho) {
- // X2 multiplies rho_new in the update equation for E
- // X3 multiplies rho_old in the update equation for E
- X2(i,j,k) = c2 * (1._rt - S_ck(i,j,k) / dt) / (ep0 * om2);
- X3(i,j,k) = c2 * (C(i,j,k) - S_ck(i,j,k) / dt) / (ep0 * om2);
- } else {
- // X2 multiplies (k \dot E) in the update equation for E
- // X3 multiplies (k \dot J) in the update equation for E
- X2(i,j,k) = c2 * (1._rt - C(i,j,k)) / om2;
- X3(i,j,k) = c2 * (S_ck(i,j,k) / dt - 1._rt) * dt / (ep0 * om2);
- }
-
- // Coefficient multiplying J in update equation for E
- X4(i,j,k) = - S_ck(i,j,k) / ep0;
-
- }
-
- // Limits for omega = 0 only
- else if (knorm == 0. && knorm_c != 0.) {
-
- const Real om_c = c * knorm_c;
- const Real om2_c = om_c * om_c;
- const Real om3_c = om_c * om2_c;
- const Real nu = kv / om_c;
- const Complex theta = amrex::exp(I * nu * om_c * dt * 0.5_rt);
-
- C(i,j,k) = 1._rt;
- S_ck(i,j,k) = dt;
- T2(i,j,k) = theta * theta;
-
- // X1 multiplies i*(k \times J) in the update equation for B
- X1(i,j,k) = (-1._rt + T2(i,j,k) - I * nu * om_c * dt * T2(i,j,k))
- / (ep0 * nu * nu * om2_c);
-
- if (update_with_rho) {
- // X2 multiplies rho_new in the update equation for E
- // X3 multiplies rho_old in the update equation for E
- X2(i,j,k) = c2 * (1._rt - T2(i,j,k) + I * nu * om_c * dt * T2(i,j,k)
- + 0.5_rt * nu * nu * om2_c * dt * dt * T2(i,j,k))
- / (ep0 * nu * nu * om2_c * (T2(i,j,k) - 1._rt));
- X3(i,j,k) = c2 * (1._rt - T2(i,j,k) + I * nu * om_c * dt * T2(i,j,k)
- + 0.5_rt * nu * nu * om2_c * dt * dt)
- / (ep0 * nu * nu * om2_c * (T2(i,j,k) - 1._rt));
- } else {
- // X2 multiplies (k \dot E) in the update equation for E
- // X3 multiplies (k \dot J) in the update equation for E
- X2(i,j,k) = c2 * dt * dt * T2(i,j,k) * 0.5_rt;
- X3(i,j,k) = c2 * (2._rt * I - 2._rt * nu * om_c * dt * T2(i,j,k)
- + I * nu * nu * om2_c * dt * dt * T2(i,j,k))
- / (2._rt * ep0 * nu * nu * nu * om3_c);
- }
-
- // Coefficient multiplying J in update equation for E
- X4(i,j,k) = I * (T2(i,j,k) - 1._rt) / (ep0 * nu * om_c);
- }
-
- // Limits for omega = 0 and omega_c = 0
- else if (knorm == 0. && knorm_c == 0.) {
-
- // Limits of cos(c*k*dt) and sin(c*k*dt)/(c*k)
- C(i,j,k) = 1._rt;
- S_ck(i,j,k) = dt;
-
- // X1 multiplies i*(k \times J) in the update equation for B
- X1(i,j,k) = dt2 / (2._rt * ep0);
-
- if (update_with_rho) {
- // X2 multiplies rho_new in the update equation for E
- // X3 multiplies rho_old in the update equation for E
- X2(i,j,k) = c2 * dt2 / (6._rt * ep0);
- X3(i,j,k) = - c2 * dt2 / (3._rt * ep0);
- } else {
- // X2 multiplies (k \dot E) in the update equation for E
- // X3 multiplies (k \dot J) in the update equation for E
- X2(i,j,k) = c2 * dt2 * 0.5_rt;
- X3(i,j,k) = - c2 * dt3 / (6._rt * ep0);
- }
-
- // Coefficient multiplying J in update equation for E
- X4(i,j,k) = -dt / ep0;
-
- // Limit of exp(I*(k \dot v_gal)*dt)
- T2(i,j,k) = 1._rt;
- }
- });
- }
-}
-
-void
-GalileanAlgorithm::CurrentCorrection (SpectralFieldData& field_data,
- std::array<std::unique_ptr<amrex::MultiFab>,3>& current,
- const std::unique_ptr<amrex::MultiFab>& rho) {
- // Profiling
- BL_PROFILE("GalileanAlgorithm::CurrentCorrection");
-
- using Idx = SpectralFieldIndex;
-
- // Forward Fourier transform of J and rho
- field_data.ForwardTransform(*current[0], Idx::Jx, 0);
- field_data.ForwardTransform(*current[1], Idx::Jy, 0);
- field_data.ForwardTransform(*current[2], Idx::Jz, 0);
- field_data.ForwardTransform(*rho, Idx::rho_old, 0);
- field_data.ForwardTransform(*rho, Idx::rho_new, 1);
-
- // Loop over boxes
- for (amrex::MFIter mfi(field_data.fields); mfi.isValid(); ++mfi){
-
- const amrex::Box& bx = field_data.fields[mfi].box();
-
- // Extract arrays for the fields to be updated
- amrex::Array4<Complex> fields = field_data.fields[mfi].array();
-
- // Extract pointers for the k vectors
- const amrex::Real* const modified_kx_arr = modified_kx_vec[mfi].dataPtr();
- const amrex::Real* const modified_kx_arr_c = modified_kx_vec_centered[mfi].dataPtr();
-#if (AMREX_SPACEDIM==3)
- const amrex::Real* const modified_ky_arr = modified_ky_vec[mfi].dataPtr();
- const amrex::Real* const modified_ky_arr_c = modified_ky_vec_centered[mfi].dataPtr();
-#endif
- const amrex::Real* const modified_kz_arr = modified_kz_vec[mfi].dataPtr();
- const amrex::Real* const modified_kz_arr_c = modified_kz_vec_centered[mfi].dataPtr();
-
- // Local copy of member variables before GPU loop
- const amrex::Real dt = m_dt;
-
- // Galilean velocity
- const amrex::Real vgx = m_v_galilean[0];
- const amrex::Real vgy = m_v_galilean[1];
- const amrex::Real vgz = m_v_galilean[2];
-
- // Loop over indices within one box
- ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
- {
- // Shortcuts for the values of J and rho
- const Complex Jx = fields(i,j,k,Idx::Jx);
- const Complex Jy = fields(i,j,k,Idx::Jy);
- const Complex Jz = fields(i,j,k,Idx::Jz);
- const Complex rho_old = fields(i,j,k,Idx::rho_old);
- const Complex rho_new = fields(i,j,k,Idx::rho_new);
-
- // k vector values, and coefficients
- const amrex::Real kx = modified_kx_arr[i];
- const amrex::Real kx_c = modified_kx_arr_c[i];
-#if (AMREX_SPACEDIM==3)
- const amrex::Real ky = modified_ky_arr[j];
- const amrex::Real kz = modified_kz_arr[k];
- const amrex::Real ky_c = modified_ky_arr_c[j];
- const amrex::Real kz_c = modified_kz_arr_c[k];
-#else
- constexpr amrex::Real ky = 0._rt;
- const amrex::Real kz = modified_kz_arr[j];
- constexpr amrex::Real ky_c = 0._rt;
- const amrex::Real kz_c = modified_kz_arr_c[j];
-#endif
- constexpr Complex I = Complex{0._rt,1._rt};
-
- const amrex::Real k_norm = std::sqrt(kx * kx + ky * ky + kz * kz);
-
- // Correct J
- if (k_norm != 0._rt)
- {
- const Complex k_dot_J = kx * Jx + ky * Jy + kz * Jz;
- const amrex::Real k_dot_vg = kx_c * vgx + ky_c * vgy + kz_c * vgz;
-
- if ( k_dot_vg != 0._rt ) {
-
- const Complex rho_old_mod = rho_old * amrex::exp(I * k_dot_vg * dt);
- const Complex den = 1._rt - amrex::exp(I * k_dot_vg * dt);
-
- fields(i,j,k,Idx::Jx) = Jx - (k_dot_J - k_dot_vg * (rho_new - rho_old_mod) / den) * kx / (k_norm * k_norm);
- fields(i,j,k,Idx::Jy) = Jy - (k_dot_J - k_dot_vg * (rho_new - rho_old_mod) / den) * ky / (k_norm * k_norm);
- fields(i,j,k,Idx::Jz) = Jz - (k_dot_J - k_dot_vg * (rho_new - rho_old_mod) / den) * kz / (k_norm * k_norm);
-
- } else {
-
- fields(i,j,k,Idx::Jx) = Jx - (k_dot_J - I * (rho_new - rho_old) / dt) * kx / (k_norm * k_norm);
- fields(i,j,k,Idx::Jy) = Jy - (k_dot_J - I * (rho_new - rho_old) / dt) * ky / (k_norm * k_norm);
- fields(i,j,k,Idx::Jz) = Jz - (k_dot_J - I * (rho_new - rho_old) / dt) * kz / (k_norm * k_norm);
- }
- }
- });
- }
-
- // Backward Fourier transform of J
- field_data.BackwardTransform(*current[0], Idx::Jx, 0);
- field_data.BackwardTransform(*current[1], Idx::Jy, 0);
- field_data.BackwardTransform(*current[2], Idx::Jz, 0);
-}
-
-void
-GalileanAlgorithm::VayDeposition (SpectralFieldData& /*field_data*/,
- std::array<std::unique_ptr<amrex::MultiFab>,3>& /*current*/)
-{
- amrex::Abort("Vay deposition not implemented for Galilean PSATD");
-}
-
-#endif // WARPX_USE_PSATD
diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/Make.package b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/Make.package
index 71b97b8bc..11a10bb91 100644
--- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/Make.package
+++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/Make.package
@@ -1,7 +1,5 @@
CEXE_sources += SpectralBaseAlgorithm.cpp
CEXE_sources += PsatdAlgorithm.cpp
-CEXE_sources += GalileanAlgorithm.cpp
-CEXE_sources += AvgGalileanAlgorithm.cpp
CEXE_sources += PMLPsatdAlgorithm.cpp
CEXE_sources += ComovingPsatdAlgorithm.cpp
diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.H b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.H
index 4bfcd9fc9..1ca4aaf31 100644
--- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.H
+++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.H
@@ -1,5 +1,4 @@
-/* Copyright 2019 Maxence Thevenet, Remi Lehe, Revathi Jambunathan, Edoardo Zoni
- *
+/* Copyright 2019
*
* This file is part of WarpX.
*
@@ -10,32 +9,45 @@
#include "SpectralBaseAlgorithm.H"
-
#if WARPX_USE_PSATD
-
-/**
- * \brief Class that updates the field in spectral space
+/* \brief Class that updates the field in spectral space
* and stores the coefficients of the corresponding update equation.
*/
class PsatdAlgorithm : public SpectralBaseAlgorithm
{
-
public:
- PsatdAlgorithm(const SpectralKSpace& spectral_kspace,
- const amrex::DistributionMapping& dm,
- const int norder_x, const int norder_y,
- const int norder_z, const bool nodal,
- const amrex::Real dt,
- const bool update_with_rho);
- // Redefine functions from base class
- virtual void pushSpectralFields(SpectralFieldData& f) const override final;
- virtual int getRequiredNumberOfFields() const override final {
- return SpectralFieldIndex::n_fields;
- }
- void InitializeSpectralCoefficients(const SpectralKSpace& spectral_kspace,
- const amrex::DistributionMapping& dm,
- const amrex::Real dt);
+ // TODO Add Doxygen docs
+ PsatdAlgorithm (
+ const SpectralKSpace& spectral_kspace,
+ const amrex::DistributionMapping& dm,
+ const int norder_x,
+ const int norder_y,
+ const int norder_z,
+ const bool nodal,
+ const amrex::Array<amrex::Real,3>& v_galilean,
+ const amrex::Real dt,
+ const bool update_with_rho,
+ const bool time_averaging);
+
+ // TODO Add Doxygen docs
+ virtual void pushSpectralFields (SpectralFieldData& f) const override final;
+
+ // TODO Add Doxygen docs
+ virtual int getRequiredNumberOfFields () const override final
+ {
+ if (m_time_averaging) {
+ return SpectralAvgFieldIndex::n_fields;
+ } else {
+ return SpectralFieldIndex::n_fields;
+ }
+ };
+
+ // TODO Add Doxygen docs
+ void InitializeSpectralCoefficients (
+ const SpectralKSpace& spectral_kspace,
+ const amrex::DistributionMapping& dm,
+ const amrex::Real dt);
/**
* \brief Virtual function for current correction in Fourier space
@@ -49,9 +61,10 @@ class PsatdAlgorithm : public SpectralBaseAlgorithm
* the three components of the current density
* \param[in] rho Unique pointer to \c MultiFab storing the charge density
*/
- virtual void CurrentCorrection (SpectralFieldData& field_data,
- std::array<std::unique_ptr<amrex::MultiFab>,3>& current,
- const std::unique_ptr<amrex::MultiFab>& rho) override final;
+ virtual void CurrentCorrection (
+ SpectralFieldData& field_data,
+ std::array<std::unique_ptr<amrex::MultiFab>,3>& current,
+ const std::unique_ptr<amrex::MultiFab>& rho) override final;
/**
* \brief Virtual function for Vay current deposition in Fourier space
@@ -64,14 +77,34 @@ class PsatdAlgorithm : public SpectralBaseAlgorithm
* \param[in,out] current Array of unique pointers to \c MultiFab storing
* the three components of the current density
*/
- virtual void VayDeposition (SpectralFieldData& field_data,
- std::array<std::unique_ptr<amrex::MultiFab>,3>& current) override final;
+ virtual void VayDeposition (
+ SpectralFieldData& field_data,
+ std::array<std::unique_ptr<amrex::MultiFab>,3>& current) override final;
private:
- SpectralRealCoefficients C_coef, S_ck_coef, X1_coef, X2_coef, X3_coef;
+
+ // These real and complex coefficients are always allocated
+ SpectralRealCoefficients C_coef, S_ck_coef;
+ SpectralComplexCoefficients T2_coef, X1_coef, X2_coef, X3_coef, X4_coef;
+
+ // These real and complex coefficients are allocated only with averaged Galilean PSATD
+ SpectralRealCoefficients C1_coef, C3_coef, S1_coef, S3_coef;
+ SpectralComplexCoefficients Psi1_coef, Psi2_coef, Psi3_coef, Psi4_coef,
+ A1_coef, A2_coef, Rhoold_coef, Rhonew_coef, Jcoef_coef;
+
+ // Centered modified finite-order k vectors
+ KVectorComponent modified_kx_vec_centered;
+#if (AMREX_SPACEDIM==3)
+ KVectorComponent modified_ky_vec_centered;
+#endif
+ KVectorComponent modified_kz_vec_centered;
+
+ // Other member variables
+ amrex::Array<amrex::Real,3> m_v_galilean;
amrex::Real m_dt;
bool m_update_with_rho;
+ bool m_time_averaging;
+ bool m_is_galilean;
};
-
#endif // WARPX_USE_PSATD
#endif // WARPX_PSATD_ALGORITHM_H_
diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.cpp b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.cpp
index ce0f466aa..0a278a54a 100644
--- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.cpp
+++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.cpp
@@ -1,67 +1,132 @@
-/* Copyright 2019 Remi Lehe, Revathi Jambunathan, Edoardo Zoni
+/* Copyright 2019
*
* This file is part of WarpX.
*
* License: BSD-3-Clause-LBNL
*/
-#include "WarpX.H"
#include "PsatdAlgorithm.H"
#include "Utils/WarpXConst.H"
-#include "Utils/WarpXProfilerWrapper.H"
#include <cmath>
-
#if WARPX_USE_PSATD
+
using namespace amrex;
-/**
- * \brief Constructor
- */
-PsatdAlgorithm::PsatdAlgorithm(const SpectralKSpace& spectral_kspace,
- const DistributionMapping& dm,
- const int norder_x, const int norder_y,
- const int norder_z, const bool nodal, const Real dt,
- const bool update_with_rho)
- // Initialize members of base class
+PsatdAlgorithm::PsatdAlgorithm(
+ const SpectralKSpace& spectral_kspace,
+ const DistributionMapping& dm,
+ const int norder_x,
+ const int norder_y,
+ const int norder_z,
+ const bool nodal,
+ const Array<Real,3>& v_galilean,
+ const Real dt,
+ const bool update_with_rho,
+ const bool time_averaging)
+ // Initializer list
: SpectralBaseAlgorithm(spectral_kspace, dm, norder_x, norder_y, norder_z, nodal),
- m_dt(dt),
- m_update_with_rho(update_with_rho)
+ // Initialize the centered finite-order modified k vectors: these are computed
+ // always with the assumption of centered grids (argument nodal = true),
+ // for both nodal and staggered simulations
+ modified_kx_vec_centered(spectral_kspace.getModifiedKComponent(dm, 0, norder_x, true)),
+#if (AMREX_SPACEDIM==3)
+ modified_ky_vec_centered(spectral_kspace.getModifiedKComponent(dm, 1, norder_y, true)),
+ modified_kz_vec_centered(spectral_kspace.getModifiedKComponent(dm, 2, norder_z, true)),
+#else
+ modified_kz_vec_centered(spectral_kspace.getModifiedKComponent(dm, 1, norder_z, true)),
+#endif
+ m_v_galilean(v_galilean),
+ m_dt(dt),
+ m_update_with_rho(update_with_rho),
+ m_time_averaging(time_averaging)
{
const BoxArray& ba = spectral_kspace.spectralspace_ba;
- // Allocate the arrays of coefficients
+ m_is_galilean = (v_galilean[0] != 0.) || (v_galilean[1] != 0.) || (v_galilean[2] != 0.);
+
+ // Always allocate these coefficients
C_coef = SpectralRealCoefficients(ba, dm, 1, 0);
S_ck_coef = SpectralRealCoefficients(ba, dm, 1, 0);
- X1_coef = SpectralRealCoefficients(ba, dm, 1, 0);
- X2_coef = SpectralRealCoefficients(ba, dm, 1, 0);
- X3_coef = SpectralRealCoefficients(ba, dm, 1, 0);
+ X1_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
+ X2_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
+ X3_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
+
+ if (m_is_galilean)
+ {
+ X4_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
+ T2_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
+ }
- // Initialize coefficients for update equations
+ // Allocate these coefficients only with averaged Galilean PSATD
+ if (time_averaging)
+ {
+ C1_coef = SpectralRealCoefficients(ba, dm, 1, 0);
+ S1_coef = SpectralRealCoefficients(ba, dm, 1, 0);
+ C3_coef = SpectralRealCoefficients(ba, dm, 1, 0);
+ S3_coef = SpectralRealCoefficients(ba, dm, 1, 0);
+ Psi1_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
+ Psi2_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
+ Psi3_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
+ A1_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
+ A2_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
+ Rhoold_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
+ Rhonew_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
+ Jcoef_coef = SpectralComplexCoefficients(ba, dm, 1, 0);
+ }
+
+ // Initialize coefficients
InitializeSpectralCoefficients(spectral_kspace, dm, dt);
}
-/**
- * \brief Advance E and B fields in spectral space (stored in `f`) over one time step
- */
void
-PsatdAlgorithm::pushSpectralFields(SpectralFieldData& f) const{
-
+PsatdAlgorithm::pushSpectralFields (SpectralFieldData& f) const
+{
const bool update_with_rho = m_update_with_rho;
+ const bool time_averaging = m_time_averaging;
+ const bool is_galilean = m_is_galilean;
// Loop over boxes
- for (MFIter mfi(f.fields); mfi.isValid(); ++mfi){
-
+ for (MFIter mfi(f.fields); mfi.isValid(); ++mfi)
+ {
const Box& bx = f.fields[mfi].box();
// Extract arrays for the fields to be updated
Array4<Complex> fields = f.fields[mfi].array();
- // Extract arrays for the coefficients
+
+ // These coefficients are always allocated
Array4<const Real> C_arr = C_coef[mfi].array();
Array4<const Real> S_ck_arr = S_ck_coef[mfi].array();
- Array4<const Real> X1_arr = X1_coef[mfi].array();
- Array4<const Real> X2_arr = X2_coef[mfi].array();
- Array4<const Real> X3_arr = X3_coef[mfi].array();
+ Array4<const Complex> X1_arr = X1_coef[mfi].array();
+ Array4<const Complex> X2_arr = X2_coef[mfi].array();
+ Array4<const Complex> X3_arr = X3_coef[mfi].array();
+
+ Array4<const Complex> X4_arr;
+ Array4<const Complex> T2_arr;
+ if (is_galilean)
+ {
+ X4_arr = X4_coef[mfi].array();
+ T2_arr = T2_coef[mfi].array();
+ }
+
+ // These coefficients are allocated only with averaged Galilean PSATD
+ Array4<const Complex> Psi1_arr;
+ Array4<const Complex> Psi2_arr;
+ Array4<const Complex> A1_arr;
+ Array4<const Complex> Rhonew_arr;
+ Array4<const Complex> Rhoold_arr;
+ Array4<const Complex> Jcoef_arr;
+
+ if (time_averaging)
+ {
+ Psi1_arr = Psi1_coef[mfi].array();
+ Psi2_arr = Psi2_coef[mfi].array();
+ A1_arr = A1_coef[mfi].array();
+ Rhonew_arr = Rhonew_coef[mfi].array();
+ Rhoold_arr = Rhoold_coef[mfi].array();
+ Jcoef_arr = Jcoef_coef[mfi].array();
+ }
+
// Extract pointers for the k vectors
const Real* modified_kx_arr = modified_kx_vec[mfi].dataPtr();
#if (AMREX_SPACEDIM==3)
@@ -70,10 +135,12 @@ PsatdAlgorithm::pushSpectralFields(SpectralFieldData& f) const{
const Real* modified_kz_arr = modified_kz_vec[mfi].dataPtr();
// Loop over indices within one box
- ParallelFor( bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
+ ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
{
using Idx = SpectralFieldIndex;
+ using AvgIdx = SpectralAvgFieldIndex;
+ // Record old values of the fields to be updated
const Complex Ex_old = fields(i,j,k,Idx::Ex);
const Complex Ey_old = fields(i,j,k,Idx::Ey);
const Complex Ez_old = fields(i,j,k,Idx::Ez);
@@ -81,180 +148,783 @@ PsatdAlgorithm::pushSpectralFields(SpectralFieldData& f) const{
const Complex By_old = fields(i,j,k,Idx::By);
const Complex Bz_old = fields(i,j,k,Idx::Bz);
- // Shortcut for the values of J and rho
+ // Shortcuts for the values of J and rho
const Complex Jx = fields(i,j,k,Idx::Jx);
const Complex Jy = fields(i,j,k,Idx::Jy);
const Complex Jz = fields(i,j,k,Idx::Jz);
const Complex rho_old = fields(i,j,k,Idx::rho_old);
const Complex rho_new = fields(i,j,k,Idx::rho_new);
- // k vector values, and coefficients
+ // k vector values
const Real kx = modified_kx_arr[i];
#if (AMREX_SPACEDIM==3)
const Real ky = modified_ky_arr[j];
const Real kz = modified_kz_arr[k];
#else
- constexpr Real ky = 0;
- const Real kz = modified_kz_arr[j];
+ constexpr Real ky = 0._rt;
+ const Real kz = modified_kz_arr[j];
#endif
- constexpr Real c2 = PhysConst::c*PhysConst::c;
- constexpr Real inv_eps0 = 1.0_rt/PhysConst::ep0;
- const Complex I = Complex{0,1};
+ // Physical constants and imaginary unit
+ constexpr Real c2 = PhysConst::c * PhysConst::c;
+ constexpr Real inv_ep0 = 1._rt / PhysConst::ep0;
+ constexpr Complex I = Complex{0._rt, 1._rt};
+ // These coefficients are initialized in the function InitializeSpectralCoefficients
const Real C = C_arr(i,j,k);
const Real S_ck = S_ck_arr(i,j,k);
- const Real X1 = X1_arr(i,j,k);
- const Real X2 = X2_arr(i,j,k);
- const Real X3 = X3_arr(i,j,k);
+ const Complex X1 = X1_arr(i,j,k);
+ const Complex X2 = X2_arr(i,j,k);
+ const Complex X3 = X3_arr(i,j,k);
+ const Complex X4 = (is_galilean) ? X4_arr(i,j,k) : - S_ck * inv_ep0;
+ const Complex T2 = (is_galilean) ? T2_arr(i,j,k) : 1.0_rt;
- // Update E (see WarpX online documentation: theory section)
+ // Update equations for E in the formulation with rho
+ // T2 = 1 always with standard PSATD (zero Galilean velocity)
- if (update_with_rho) {
+ if (update_with_rho)
+ {
+ fields(i,j,k,Idx::Ex) = T2 * C * Ex_old
+ + I * c2 * T2 * S_ck * (ky * Bz_old - kz * By_old)
+ + X4 * Jx - I * (X2 * rho_new - T2 * X3 * rho_old) * kx;
+
+ fields(i,j,k,Idx::Ey) = T2 * C * Ey_old
+ + I * c2 * T2 * S_ck * (kz * Bx_old - kx * Bz_old)
+ + X4 * Jy - I * (X2 * rho_new - T2 * X3 * rho_old) * ky;
- fields(i,j,k,Idx::Ex) = C*Ex_old + S_ck*(c2*I*(ky*Bz_old-kz*By_old)-inv_eps0*Jx)
- - I*(X2*rho_new-X3*rho_old)*kx;
+ fields(i,j,k,Idx::Ez) = T2 * C * Ez_old
+ + I * c2 * T2 * S_ck * (kx * By_old - ky * Bx_old)
+ + X4 * Jz - I * (X2 * rho_new - T2 * X3 * rho_old) * kz;
+ }
- fields(i,j,k,Idx::Ey) = C*Ey_old + S_ck*(c2*I*(kz*Bx_old-kx*Bz_old)-inv_eps0*Jy)
- - I*(X2*rho_new-X3*rho_old)*ky;
+ // Update equations for E in the formulation without rho
+ // T2 = 1 always with standard PSATD (zero Galilean velocity)
- fields(i,j,k,Idx::Ez) = C*Ez_old + S_ck*(c2*I*(kx*By_old-ky*Bx_old)-inv_eps0*Jz)
- - I*(X2*rho_new-X3*rho_old)*kz;
- } else {
+ else {
- Complex k_dot_J = kx*Jx + ky*Jy + kz*Jz;
- Complex k_dot_E = kx*Ex_old + ky*Ey_old + kz*Ez_old;
+ Complex k_dot_J = kx * Jx + ky * Jy + kz * Jz;
+ Complex k_dot_E = kx * Ex_old + ky * Ey_old + kz * Ez_old;
- fields(i,j,k,Idx::Ex) = C*Ex_old + S_ck*(c2*I*(ky*Bz_old-kz*By_old)-inv_eps0*Jx)
- + X2*k_dot_E*kx + X3*inv_eps0*k_dot_J*kx;
+ fields(i,j,k,Idx::Ex) = T2 * C * Ex_old
+ + I * c2 * T2 * S_ck * (ky * Bz_old - kz * By_old)
+ + X4 * Jx + X2 * k_dot_E * kx + X3 * k_dot_J * kx;
- fields(i,j,k,Idx::Ey) = C*Ey_old + S_ck*(c2*I*(kz*Bx_old-kx*Bz_old)-inv_eps0*Jy)
- + X2*k_dot_E*ky + X3*inv_eps0*k_dot_J*ky;
+ fields(i,j,k,Idx::Ey) = T2 * C * Ey_old
+ + I * c2 * T2 * S_ck * (kz * Bx_old - kx * Bz_old)
+ + X4 * Jy + X2 * k_dot_E * ky + X3 * k_dot_J * ky;
- fields(i,j,k,Idx::Ez) = C*Ez_old + S_ck*(c2*I*(kx*By_old-ky*Bx_old)-inv_eps0*Jz)
- + X2*k_dot_E*kz + X3*inv_eps0*k_dot_J*kz;
+ fields(i,j,k,Idx::Ez) = T2 * C * Ez_old
+ + I * c2 * T2 * S_ck * (kx * By_old - ky * Bx_old)
+ + X4 * Jz + X2 * k_dot_E * kz + X3 * k_dot_J * kz;
}
- // Update B (see WarpX online documentation: theory section)
+ // Update equations for B
+ // T2 = 1 always with standard PSATD (zero Galilean velocity)
+
+ fields(i,j,k,Idx::Bx) = T2 * C * Bx_old - I * T2 * S_ck * (ky * Ez_old - kz * Ey_old)
+ + I * X1 * (ky * Jz - kz * Jy);
- fields(i,j,k,Idx::Bx) = C*Bx_old - S_ck*I*(ky*Ez_old-kz*Ey_old) + X1*I*(ky*Jz-kz*Jy);
+ fields(i,j,k,Idx::By) = T2 * C * By_old - I * T2 * S_ck * (kz * Ex_old - kx * Ez_old)
+ + I * X1 * (kz * Jx - kx * Jz);
- fields(i,j,k,Idx::By) = C*By_old - S_ck*I*(kz*Ex_old-kx*Ez_old) + X1*I*(kz*Jx-kx*Jz);
+ fields(i,j,k,Idx::Bz) = T2 * C * Bz_old - I * T2 * S_ck * (kx * Ey_old - ky * Ex_old)
+ + I * X1 * (kx * Jy - ky * Jx);
- fields(i,j,k,Idx::Bz) = C*Bz_old - S_ck*I*(kx*Ey_old-ky*Ex_old) + X1*I*(kx*Jy-ky*Jx);
- } );
+ // Additional update equations for averaged Galilean algorithm
+
+ if (time_averaging)
+ {
+ // These coefficients are initialized in the function InitializeSpectralCoefficients below
+ const Complex Psi1 = Psi1_arr(i,j,k);
+ const Complex Psi2 = Psi2_arr(i,j,k);
+ const Complex A1 = A1_arr(i,j,k);
+ const Complex CRhoold = Rhoold_arr(i,j,k);
+ const Complex CRhonew = Rhonew_arr(i,j,k);
+ const Complex Jcoef = Jcoef_arr(i,j,k);
+
+ fields(i,j,k,AvgIdx::Ex_avg) = Psi1 * Ex_old
+ - I * c2 * Psi2 * (ky * Bz_old - kz * By_old)
+ + Jcoef * Jx + (CRhonew * rho_new + CRhoold * rho_old) * kx;
+
+ fields(i,j,k,AvgIdx::Ey_avg) = Psi1 * Ey_old
+ - I * c2 * Psi2 * (kz * Bx_old - kx * Bz_old)
+ + Jcoef * Jy + (CRhonew * rho_new + CRhoold * rho_old) * ky;
+
+ fields(i,j,k,AvgIdx::Ez_avg) = Psi1 * Ez_old
+ - I * c2 * Psi2 * (kx * By_old - ky * Bx_old)
+ + Jcoef * Jz + (CRhonew * rho_new + CRhoold * rho_old) * kz;
+
+ fields(i,j,k,AvgIdx::Bx_avg) = Psi1 * Bx_old + I * Psi2 * (ky * Ez_old - kz * Ey_old)
+ + I * inv_ep0 * A1 * (ky * Jz - kz * Jy);
+
+ fields(i,j,k,AvgIdx::By_avg) = Psi1 * By_old + I * Psi2 * (kz * Ex_old - kx * Ez_old)
+ + I * inv_ep0 * A1 * (kz * Jx - kx * Jz);
+
+ fields(i,j,k,AvgIdx::Bz_avg) = Psi1 * Bz_old + I * Psi2 * (kx * Ey_old - ky * Ex_old)
+ + I * inv_ep0 * A1 * (kx * Jy - ky * Jx);
+ }
+ });
}
}
-/**
- * \brief Initialize coefficients for update equations
- */
-void PsatdAlgorithm::InitializeSpectralCoefficients(const SpectralKSpace& spectral_kspace,
- const amrex::DistributionMapping& dm,
- const amrex::Real dt)
+void PsatdAlgorithm::InitializeSpectralCoefficients (
+ const SpectralKSpace& spectral_kspace,
+ const amrex::DistributionMapping& dm,
+ const amrex::Real dt)
{
const bool update_with_rho = m_update_with_rho;
+ const bool time_averaging = m_time_averaging;
+ const bool is_galilean = m_is_galilean;
const BoxArray& ba = spectral_kspace.spectralspace_ba;
- // Loop over boxes and allocate the corresponding coefficients
- // for each box owned by the local MPI proc
- for (MFIter mfi(ba, dm); mfi.isValid(); ++mfi){
-
+ // Loop over boxes and allocate the corresponding coefficients for each box
+ for (MFIter mfi(ba, dm); mfi.isValid(); ++mfi)
+ {
const Box& bx = ba[mfi];
// Extract pointers for the k vectors
- const Real* modified_kx = modified_kx_vec[mfi].dataPtr();
+ const Real* kx = modified_kx_vec[mfi].dataPtr();
+ const Real* kx_c = modified_kx_vec_centered[mfi].dataPtr();
#if (AMREX_SPACEDIM==3)
- const Real* modified_ky = modified_ky_vec[mfi].dataPtr();
+ const Real* ky = modified_ky_vec[mfi].dataPtr();
+ const Real* ky_c = modified_ky_vec_centered[mfi].dataPtr();
#endif
- const Real* modified_kz = modified_kz_vec[mfi].dataPtr();
+ const Real* kz = modified_kz_vec[mfi].dataPtr();
+ const Real* kz_c = modified_kz_vec_centered[mfi].dataPtr();
- // Extract arrays for the coefficients
+ // Coefficients always allocated
Array4<Real> C = C_coef[mfi].array();
Array4<Real> S_ck = S_ck_coef[mfi].array();
- Array4<Real> X1 = X1_coef[mfi].array();
- Array4<Real> X2 = X2_coef[mfi].array();
- Array4<Real> X3 = X3_coef[mfi].array();
+ Array4<Complex> X1 = X1_coef[mfi].array();
+ Array4<Complex> X2 = X2_coef[mfi].array();
+ Array4<Complex> X3 = X3_coef[mfi].array();
+
+ Array4<Complex> X4;
+ Array4<Complex> T2;
+ if (is_galilean)
+ {
+ X4 = X4_coef[mfi].array();
+ T2 = T2_coef[mfi].array();
+ }
+
+ // Coefficients allocated only with averaged Galilean PSATD
+ Array4<Real> C1;
+ Array4<Real> S1;
+ Array4<Real> C3;
+ Array4<Real> S3;
+ Array4<Complex> Psi1;
+ Array4<Complex> Psi2;
+ Array4<Complex> Psi3;
+ Array4<Complex> A1;
+ Array4<Complex> A2;
+ Array4<Complex> CRhoold;
+ Array4<Complex> CRhonew;
+ Array4<Complex> Jcoef;
+
+ if (time_averaging)
+ {
+ C1 = C1_coef[mfi].array();
+ S1 = S1_coef[mfi].array();
+ C3 = C3_coef[mfi].array();
+ S3 = S3_coef[mfi].array();
+ Psi1 = Psi1_coef[mfi].array();
+ Psi2 = Psi2_coef[mfi].array();
+ Psi3 = Psi3_coef[mfi].array();
+ A1 = A1_coef[mfi].array();
+ A2 = A2_coef[mfi].array();
+ CRhoold = Rhoold_coef[mfi].array();
+ CRhonew = Rhonew_coef[mfi].array();
+ Jcoef = Jcoef_coef[mfi].array();
+ }
+
+ // Extract Galilean velocity
+ Real vx = m_v_galilean[0];
+#if (AMREX_SPACEDIM==3)
+ Real vy = m_v_galilean[1];
+#endif
+ Real vz = m_v_galilean[2];
// Loop over indices within one box
- ParallelFor( bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
+ ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
{
- // Calculate norm of vector
- const Real k_norm = std::sqrt(
- std::pow(modified_kx[i],2) +
+ // Calculate norm of k vector
+ const Real knorm = std::sqrt(
+ std::pow(kx[i], 2) +
+#if (AMREX_SPACEDIM==3)
+ std::pow(ky[j], 2) + std::pow(kz[k], 2));
+#else
+ std::pow(kz[j], 2));
+#endif
+ // Calculate norm of k vector (centered)
+ const Real knorm_c = std::sqrt(
+ std::pow(kx_c[i], 2) +
#if (AMREX_SPACEDIM==3)
- std::pow(modified_ky[j],2) + std::pow(modified_kz[k],2));
+ std::pow(ky_c[j], 2) + std::pow(kz_c[k], 2));
#else
- std::pow(modified_kz[j],2));
+ std::pow(kz_c[j], 2));
#endif
- // Calculate coefficients
+ // Physical constants and imaginary unit
constexpr Real c = PhysConst::c;
- constexpr Real eps0 = PhysConst::ep0;
-
- if (k_norm != 0) {
- C(i,j,k) = std::cos(c*k_norm*dt);
- S_ck(i,j,k) = std::sin(c*k_norm*dt)/(c*k_norm);
- X1(i,j,k) = (1.0_rt-C(i,j,k))/(eps0*c*c*k_norm*k_norm);
- if (update_with_rho) {
- X2(i,j,k) = (1.0_rt-S_ck(i,j,k)/dt)/(eps0*k_norm*k_norm);
- X3(i,j,k) = (C(i,j,k)-S_ck(i,j,k)/dt)/(eps0*k_norm*k_norm);
- } else {
- X2(i,j,k) = (1.0_rt-C(i,j,k))/(k_norm*k_norm);
- X3(i,j,k) = (S_ck(i,j,k)-dt)/(k_norm*k_norm);
+ constexpr Real c2 = c*c;
+ constexpr Real ep0 = PhysConst::ep0;
+ constexpr Complex I = Complex{0._rt, 1._rt};
+
+ // Auxiliary coefficients used when update_with_rho=false
+ const Real dt2 = dt * dt;
+ const Real dt3 = dt * dt2;
+ Complex X2_old, X3_old;
+
+ // Calculate the dot product of the k vector with the Galilean velocity.
+ // This has to be computed always with the centered (that is, nodal) finite-order
+ // modified k vectors, to work correctly for both nodal and staggered simulations.
+ // kv = 0 always with standard PSATD (zero Galilean velocity).
+ const Real kv = kx_c[i]*vx +
+#if (AMREX_SPACEDIM==3)
+ ky_c[j]*vy + kz_c[k]*vz;
+#else
+ kz_c[j]*vz;
+#endif
+
+ // Note that:
+ // - X1 multiplies i*(k \times J) in the update equation for B
+ // - X2 multiplies rho_new if update_with_rho = 1 or (k \dot E)
+ // if update_with_rho = 0 in the update equation for E
+ // - X3 multiplies rho_old if update_with_rho = 1 or (k \dot J)
+ // if update_with_rho = 0 in the update equation for E
+ // - X4 multiplies J in the update equation for E
+
+ if (knorm != 0. && knorm_c != 0.)
+ {
+ // Auxiliary coefficients
+ const Real om = c * knorm;
+ const Real om2 = om * om;
+ const Real om3 = om * om2;
+ const Real om_c = c * knorm_c;
+ const Real om2_c = om_c * om_c;
+ const Real om3_c = om_c * om2_c;
+ const Complex tmp1 = amrex::exp( I * om * dt);
+ const Complex tmp2 = amrex::exp(- I * om * dt);
+
+ C (i,j,k) = std::cos(om * dt);
+ S_ck(i,j,k) = std::sin(om * dt) / om;
+
+ if (time_averaging)
+ {
+ C1(i,j,k) = std::cos(0.5_rt * om * dt);
+ S1(i,j,k) = std::sin(0.5_rt * om * dt);
+ C3(i,j,k) = std::cos(1.5_rt * om * dt);
+ S3(i,j,k) = std::sin(1.5_rt * om * dt);
+ }
+
+ const Real nu = kv / om_c;
+ const Real nu2 = nu * nu;
+ const Complex theta = amrex::exp(I * nu * om_c * dt * 0.5_rt);
+ const Complex theta_star = amrex::exp(- I * nu * om_c * dt * 0.5_rt);
+
+ // T2 = 1 always with standard PSATD (zero Galilean velocity)
+ if (is_galilean)
+ {
+ T2(i,j,k) = theta * theta;
+ }
+ const Complex T2_tmp = (is_galilean) ? T2(i,j,k) : 1.0_rt;
+
+ // nu = 0 always with standard PSATD (zero Galilean velocity): skip this block
+ if (nu != om/om_c && nu != -om/om_c && nu != 0.)
+ {
+ // x1 is the coefficient chi_1 in equation (12c)
+ Complex x1 = om2_c / (om2 - nu2 * om2_c)
+ * (theta_star - theta * C(i,j,k) + I * nu * om_c * theta * S_ck(i,j,k));
+
+ X1(i,j,k) = theta * x1 / (ep0 * om2_c);
+
+ if (update_with_rho)
+ {
+ X2(i,j,k) = c2 * (x1 * om2 - theta * (1._rt - C(i,j,k)) * om2_c)
+ / (theta_star - theta) / (ep0 * om2_c * om2);
+
+ X3(i,j,k) = c2 * (x1 * om2 - theta_star * (1._rt - C(i,j,k)) * om2_c)
+ / (theta_star - theta) / (ep0 * om2_c * om2);
+ }
+
+ else // update_with_rho = 0
+ {
+ X2_old = (x1 * om2 - theta * (1._rt - C(i,j,k)) * om2_c)
+ / (theta_star - theta);
+
+ X3_old = (x1 * om2 - theta_star * (1._rt - C(i,j,k)) * om2_c)
+ / (theta_star - theta);
+
+ X2(i,j,k) = c2 * T2_tmp * (X2_old - X3_old) / (om2_c * om2);
+
+ X3(i,j,k) = I * c2 * X2_old * (T2_tmp - 1._rt) / (ep0 * nu * om3_c * om2);
+ }
+
+ if (is_galilean)
+ {
+ X4(i,j,k) = I * nu * om_c * X1(i,j,k) - T2_tmp * S_ck(i,j,k) / ep0;
+ }
+
+ // Averaged Galilean algorithm
+ if (time_averaging)
+ {
+ Complex C_rho = I * c2 / ((1._rt - T2_tmp) * ep0);
+
+ Psi1(i,j,k) = theta * ((om * S1(i,j,k) + I * nu * om_c * C1(i,j,k))
+ - T2_tmp * (om * S3(i,j,k) + I * nu * om_c * C3(i,j,k)))
+ / (dt * (nu2 * om2_c - om2));
+
+ Psi2(i,j,k) = theta * ((om * C1(i,j,k) - I * nu * om_c * S1(i,j,k))
+ - T2_tmp * (om * C3(i,j,k) - I * nu * om_c * S3(i,j,k)))
+ / (om * dt * (nu2 * om2_c - om2));
+
+ Psi3(i,j,k) = I * theta * (1._rt - T2_tmp) / (nu * om_c * dt);
+
+ A1(i,j,k) = (Psi1(i,j,k) - 1._rt + I * nu * om_c * Psi2(i,j,k))
+ / (nu2 * om2_c - om2);
+
+ A2(i,j,k) = (Psi3(i,j,k) - Psi1(i,j,k)) / om2;
+
+ CRhoold(i,j,k) = C_rho * (T2_tmp * A1(i,j,k) - A2(i,j,k));
+
+ CRhonew(i,j,k) = C_rho * (A2(i,j,k) - A1(i,j,k));
+
+ Jcoef(i,j,k) = (I * nu * om_c * A1(i,j,k) + Psi2(i,j,k)) / ep0;
+ }
+ }
+
+ // nu = 0 always with standard PSATD (zero Galilean velocity)
+ if (nu == 0.)
+ {
+ X1(i,j,k) = (1._rt - C(i,j,k)) / (ep0 * om2);
+
+ if (update_with_rho)
+ {
+ X2(i,j,k) = c2 * (1._rt - S_ck(i,j,k) / dt) / (ep0 * om2);
+
+ X3(i,j,k) = c2 * (C(i,j,k) - S_ck(i,j,k) / dt) / (ep0 * om2);
+ }
+
+ else // update_with_rho = 0
+ {
+ X2(i,j,k) = c2 * (1._rt - C(i,j,k)) / om2;
+
+ X3(i,j,k) = c2 * (S_ck(i,j,k) / dt - 1._rt) * dt / (ep0 * om2);
+ }
+
+ if (is_galilean)
+ {
+ X4(i,j,k) = - S_ck(i,j,k) / ep0;
+ }
+
+ // Averaged Galilean algorithm
+ if (time_averaging)
+ {
+ Psi1(i,j,k) = (S3(i,j,k) - S1(i,j,k)) / (om * dt);
+
+ Psi2(i,j,k) = (C3(i,j,k) - C1(i,j,k)) / (om2 * dt);
+
+ Psi3(i,j,k) = 1._rt;
+
+ A1(i,j,k) = (om * dt + S1(i,j,k) - S3(i,j,k)) / (om3 * dt);
+
+ A2(i,j,k) = (om * dt + S1(i,j,k) - S3(i,j,k)) / (om3 * dt);
+
+ CRhoold(i,j,k) = 2._rt * I * c2 * S1(i,j,k) * (dt * C(i,j,k) - S_ck(i,j,k))
+ / (om3 * dt2 * ep0);
+
+ CRhonew(i,j,k) = - I * c2 * (om2 * dt2 - C1(i,j,k) + C3(i,j,k))
+ / (om2 * om2 * dt2 * ep0);
+
+ Jcoef(i,j,k) = (I * nu * om_c * A1(i,j,k) + Psi2(i,j,k)) / ep0;
+ }
+ }
+
+ // nu = 0 always with standard PSATD (zero Galilean velocity): skip this block
+ if (nu == om/om_c)
+ {
+ X1(i,j,k) = (1._rt - tmp1 * tmp1 + 2._rt * I * om * dt) / (4._rt * ep0 * om2);
+
+ if (update_with_rho)
+ {
+ X2(i,j,k) = c2 * (- 3._rt + 4._rt * tmp1 - tmp1 * tmp1 - 2._rt * I * om * dt)
+ / (4._rt * ep0 * om2 * (tmp1 - 1._rt));
+
+ X3(i,j,k) = c2 * (3._rt - 2._rt * tmp2 - 2._rt * tmp1 + tmp1 * tmp1
+ - 2._rt * I * om * dt) / (4._rt * ep0 * om2 * (tmp1 - 1._rt));
+ }
+
+ else // update_with_rho = 0
+ {
+ X2(i,j,k) = c2 * (1._rt - C(i,j,k)) * tmp1 / om2;
+
+ X3(i,j,k) = c2 * (2._rt * om * dt - I * tmp1 * tmp1 + 4._rt * I * tmp1 - 3._rt * I)
+ / (4._rt * ep0 * om3);
+ }
+
+ if (is_galilean)
+ {
+ X4(i,j,k) = (- I + I * tmp1 * tmp1 - 2._rt * om * dt) / (4._rt * ep0 * om);
+ }
+
+ // Averaged Galilean algorithm
+ if (time_averaging)
+ {
+ Complex C_rho = I * c2 / ((1._rt - T2_tmp) * ep0);
+
+ Psi1(i,j,k) = (2._rt * om * dt + I * tmp1 - I * tmp1 * tmp1 * tmp1)
+ / (4._rt * om * dt);
+
+ Psi2(i,j,k) = (- 2._rt * I * om * dt - tmp1 + tmp1 * tmp1 * tmp1)
+ / (4._rt * om2 * dt);
+
+ Psi3(i,j,k) = I * theta * (1._rt - T2_tmp) / (nu * om_c * dt);
+
+ A1(i,j,k) = (2._rt * om * dt + I * (4._rt * om2 * dt2 - tmp1 + tmp1 * tmp1 * tmp1))
+ / (8._rt * om3 * dt);
+
+ A2(i,j,k) = (Psi3(i,j,k) - Psi1(i,j,k)) / om2;
+
+ CRhoold(i,j,k) = C_rho * (T2_tmp * A1(i,j,k) - A2(i,j,k));
+
+ CRhonew(i,j,k) = C_rho * (A2(i,j,k) - A1(i,j,k));
+
+ Jcoef(i,j,k) = (I * nu * om_c * A1(i,j,k) + Psi2(i,j,k)) / ep0;
+ }
+ }
+
+ // nu = 0 always with standard PSATD (zero Galilean velocity): skip this block
+ if (nu == -om/om_c)
+ {
+ X1(i,j,k) = (1._rt - tmp2 * tmp2 - 2._rt * I * om * dt) / (4._rt * ep0 * om2);
+
+ if (update_with_rho)
+ {
+ X2(i,j,k) = c2 * (- 4._rt + 3._rt * tmp1 + tmp2 - 2._rt * I * om * dt * tmp1)
+ / (4._rt * ep0 * om2 * (tmp1 - 1._rt));
+
+ X3(i,j,k) = c2 * (2._rt - tmp2 - 3._rt * tmp1 + 2._rt * tmp1 * tmp1
+ - 2._rt * I * om * dt * tmp1) / (4._rt * ep0 * om2 * (tmp1 - 1._rt));
+ }
+
+ else // update_with_rho = 0
+ {
+ X2(i,j,k) = c2 * (1._rt - C(i,j,k)) * tmp2 / om2;
+
+ X3(i,j,k) = c2 * (2._rt * om * dt + I * tmp2 * tmp2 - 4._rt * I * tmp2 + 3._rt * I)
+ / (4._rt * ep0 * om3);
+ }
+
+ if (is_galilean)
+ {
+ X4(i,j,k) = (I - I * tmp2 * tmp2 - 2._rt * om * dt) / (4._rt * ep0 * om);
+ }
+
+ // Averaged Galilean algorithm
+ if (time_averaging)
+ {
+ Complex C_rho = I * c2 / ((1._rt - T2_tmp) * ep0);
+
+ Psi1(i,j,k) = (2._rt * om * dt - I * tmp2 + I * tmp2 * tmp2 * tmp2)
+ / (4._rt * om * dt);
+
+ Psi2(i,j,k) = (2._rt * I * om * dt - tmp2 + tmp2 * tmp2 * tmp2)
+ / (4._rt * om2 * dt);
+
+ Psi3(i,j,k) = I * theta * (1._rt - T2_tmp) / (nu * om_c * dt);
+
+ A1(i,j,k) = (2._rt * om * dt * (1._rt - 2._rt * I * om * dt)
+ + I * (tmp2 - tmp2 * tmp2 * tmp2)) / (8._rt * om3 * dt);
+
+ A2(i,j,k) = (Psi3(i,j,k) - Psi1(i,j,k)) / om2;
+
+ CRhoold(i,j,k) = C_rho * (T2_tmp * A1(i,j,k) - A2(i,j,k));
+
+ CRhonew(i,j,k) = C_rho * (A2(i,j,k) - A1(i,j,k));
+
+ Jcoef(i,j,k) = (I * nu * om_c * A1(i,j,k) + Psi2(i,j,k)) / ep0;
+ }
+ }
+ }
+
+ else if (knorm != 0. && knorm_c == 0.)
+ {
+ const Real om = c * knorm;
+ const Real om2 = om * om;
+ const Real om3 = om * om2;
+
+ C(i,j,k) = std::cos(om * dt);
+
+ S_ck(i,j,k) = std::sin(om * dt) / om;
+
+ X1(i,j,k) = (1._rt - C(i,j,k)) / (ep0 * om2);
+
+ if (update_with_rho)
+ {
+ X2(i,j,k) = c2 * (1._rt - S_ck(i,j,k) / dt) / (ep0 * om2);
+
+ X3(i,j,k) = c2 * (C(i,j,k) - S_ck(i,j,k) / dt) / (ep0 * om2);
+ }
+
+ else // update_with_rho = 0
+ {
+ X2(i,j,k) = c2 * (1._rt - C(i,j,k)) / om2;
+
+ X3(i,j,k) = c2 * (S_ck(i,j,k) / dt - 1._rt) * dt / (ep0 * om2);
+ }
+
+ if (is_galilean)
+ {
+ X4(i,j,k) = - S_ck(i,j,k) / ep0;
+
+ T2(i,j,k) = 1._rt;
}
- } else { // Handle k_norm = 0 with analytical limit
- C(i,j,k) = 1.0_rt;
+
+ // Averaged Galilean algorithm
+ if (time_averaging)
+ {
+ C1(i,j,k) = std::cos(0.5_rt * om * dt);
+ S1(i,j,k) = std::sin(0.5_rt * om * dt);
+ C3(i,j,k) = std::cos(1.5_rt * om * dt);
+ S3(i,j,k) = std::sin(1.5_rt * om * dt);
+
+ Psi1(i,j,k) = (S3(i,j,k) - S1(i,j,k)) / (om * dt);
+
+ Psi2(i,j,k) = (C3(i,j,k) - C1(i,j,k)) / (om2 * dt);
+
+ Psi3(i,j,k) = 1._rt;
+
+ A1(i,j,k) = (om * dt + S1(i,j,k) - S3(i,j,k)) / (om3 * dt);
+
+ A2(i,j,k) = (om * dt + S1(i,j,k) - S3(i,j,k)) / (om3 * dt);
+
+ CRhoold(i,j,k) = 2._rt * I * c2 * S1(i,j,k) * (dt * C(i,j,k) - S_ck(i,j,k))
+ / (om3 * dt2 * ep0);
+
+ CRhonew(i,j,k) = - I * c2 * (om2 * dt2 - C1(i,j,k) + C3(i,j,k))
+ / (om2 * om2 * dt2 * ep0);
+
+ Jcoef(i,j,k) = Psi2(i,j,k) / ep0;
+ }
+ }
+
+ else if (knorm == 0. && knorm_c != 0.)
+ {
+ const Real om_c = c * knorm_c;
+ const Real om2_c = om_c * om_c;
+ const Real om3_c = om_c * om2_c;
+ const Real nu = kv / om_c;
+ const Real nu2 = nu * nu;
+ const Complex theta = amrex::exp(I * nu * om_c * dt * 0.5_rt);
+
+ C(i,j,k) = 1._rt;
+
S_ck(i,j,k) = dt;
- X1(i,j,k) = 0.5_rt*dt*dt/eps0;
- if (update_with_rho) {
- X2(i,j,k) = c*c*dt*dt/(6.0_rt*eps0);
- X3(i,j,k) = -c*c*dt*dt/(3.0_rt*eps0);
- } else {
- X2(i,j,k) = 0.5_rt*dt*dt*c*c;
- X3(i,j,k) = -c*c*dt*dt*dt/6.0_rt;
+
+ if (is_galilean)
+ {
+ T2(i,j,k) = theta * theta;
+ }
+ const Complex T2_tmp = (is_galilean) ? T2(i,j,k) : 1.0_rt;
+
+ // nu = 0 always with standard PSATD (zero Galilean velocity): skip this block
+ if (nu != 0.)
+ {
+ X1(i,j,k) = (- 1._rt + T2_tmp - I * nu * om_c * dt * T2_tmp) / (ep0 * nu2 * om2_c);
+
+ if (update_with_rho)
+ {
+ X2(i,j,k) = c2 * (1._rt - T2_tmp + I * nu * om_c * dt * T2_tmp
+ + 0.5_rt * nu2 * om2_c * dt2 * T2_tmp) / (ep0 * nu2 * om2_c * (T2_tmp - 1._rt));
+
+ X3(i,j,k) = c2 * (1._rt - T2_tmp + I * nu * om_c * dt * T2_tmp
+ + 0.5_rt * nu2 * om2_c * dt2) / (ep0 * nu2 * om2_c * (T2_tmp - 1._rt));
+ }
+
+ else // update_with_rho = 0
+ {
+ X2(i,j,k) = c2 * dt2 * T2_tmp * 0.5_rt;
+
+ X3(i,j,k) = c2 * (2._rt * I - 2._rt * nu * om_c * dt * T2_tmp
+ + I * nu2 * om2_c * dt2 * T2_tmp) / (2._rt * ep0 * nu2 * nu * om3_c);
+ }
+
+ if (is_galilean)
+ {
+ X4(i,j,k) = I * (T2_tmp - 1._rt) / (ep0 * nu * om_c);
+ }
+
+ // Averaged Galilean algorithm
+ if (time_averaging)
+ {
+ Complex C_rho = I * c2 / ((1._rt - T2_tmp) * ep0);
+
+ Psi1(i,j,k) = I * theta * (1._rt - T2_tmp) / (nu * om_c * dt);
+
+ Psi2(i,j,k) = theta * (2._rt - I * nu * om_c * dt + T2_tmp * (3._rt * I * nu * om_c * dt - 2._rt))
+ / (2._rt * nu2 * om2_c * dt);
+
+ Psi3(i,j,k) = I * theta * (1._rt - T2_tmp) / (nu * om_c * dt);
+
+ A1(i,j,k) = (Psi1(i,j,k) - 1._rt + I * nu * om_c * Psi2(i,j,k)) / (nu2 * om2_c);
+
+ A2(i,j,k) = theta * (8._rt * I * (T2_tmp - 1._rt) + 4._rt * nu * om_c * dt
+ * (3._rt * T2_tmp - 1._rt) + I * nu2 * om2_c * dt2 * (1._rt - 9._rt * T2_tmp))
+ / (8._rt * nu2 * nu * om2_c * om_c * dt);
+
+ CRhoold(i,j,k) = C_rho * (T2_tmp * A1(i,j,k) - A2(i,j,k));
+
+ CRhonew(i,j,k) = C_rho * (A2(i,j,k) - A1(i,j,k));
+
+ Jcoef(i,j,k) = (I * nu * om_c * A1(i,j,k) + Psi2(i,j,k)) / ep0;
+ }
+ }
+
+ else // nu = 0
+ {
+ X1(i,j,k) = dt2 / (2._rt * ep0);
+
+ if (update_with_rho)
+ {
+ X2(i,j,k) = c2 * dt2 / (6._rt * ep0);
+
+ X3(i,j,k) = - c2 * dt2 / (3._rt * ep0);
+ }
+
+ else // update_with_rho = 0
+ {
+ X2(i,j,k) = c2 * dt2 * 0.5_rt;
+
+ X3(i,j,k) = - c2 * dt3 / (6._rt * ep0);
+ }
+
+ if (is_galilean)
+ {
+ X4(i,j,k) = - dt / ep0;
+ }
+
+ // Averaged Galilean algorithm
+ if (time_averaging)
+ {
+ Psi1(i,j,k) = 1._rt;
+
+ Psi2(i,j,k) = - dt;
+
+ Psi3(i,j,k) = 1._rt;
+
+ A1(i,j,k) = 13._rt * dt2 / 24._rt;
+
+ A2(i,j,k) = 13._rt * dt2 / 24._rt;
+
+ CRhoold(i,j,k) = - I * c2 * dt2 / (3._rt * ep0);
+
+ CRhonew(i,j,k) = - 5._rt * I * c2 * dt2 / (24._rt * ep0);
+
+ Jcoef(i,j,k) = - dt / ep0;
+ }
+ }
+ }
+
+ else if (knorm == 0. && knorm_c == 0.)
+ {
+ C(i,j,k) = 1._rt;
+
+ S_ck(i,j,k) = dt;
+
+ X1(i,j,k) = dt2 / (2._rt * ep0);
+
+ if (update_with_rho)
+ {
+ X2(i,j,k) = c2 * dt2 / (6._rt * ep0);
+
+ X3(i,j,k) = - c2 * dt2 / (3._rt * ep0);
+ }
+
+ else // update_with_rho = 0
+ {
+ X2(i,j,k) = c2 * dt2 * 0.5_rt;
+
+ X3(i,j,k) = - c2 * dt3 / (6._rt * ep0);
+ }
+
+ // T2 = 1 always with standard PSATD (zero Galilean velocity)
+ if (is_galilean)
+ {
+ X4(i,j,k) = - dt / ep0;
+
+ T2(i,j,k) = 1._rt;
+ }
+
+ // Averaged Galilean algorithm
+ if (time_averaging)
+ {
+ Psi1(i,j,k) = 1._rt;
+
+ Psi2(i,j,k) = - dt;
+
+ Psi3(i,j,k) = 1._rt;
+
+ A1(i,j,k) = 13._rt * dt2 / 24._rt;
+
+ A2(i,j,k) = 13._rt * dt2 / 24._rt;
+
+ CRhoold(i,j,k) = - I * c2 * dt2 / (3._rt * ep0);
+
+ CRhonew(i,j,k) = - 5._rt * I * c2 * dt2 / (24._rt * ep0);
+
+ Jcoef(i,j,k) = - dt / ep0;
}
}
- } );
- }
+ });
+ }
}
void
-PsatdAlgorithm::CurrentCorrection (SpectralFieldData& field_data,
- std::array<std::unique_ptr<amrex::MultiFab>,3>& current,
- const std::unique_ptr<amrex::MultiFab>& rho) {
+PsatdAlgorithm::CurrentCorrection (
+ SpectralFieldData& field_data,
+ std::array<std::unique_ptr<amrex::MultiFab>,3>& current,
+ const std::unique_ptr<amrex::MultiFab>& rho)
+{
// Profiling
- WARPX_PROFILE("PsatdAlgorithm::CurrentCorrection()");
+ BL_PROFILE("PsatdAlgorithm::CurrentCorrection");
using Idx = SpectralFieldIndex;
// Forward Fourier transform of J and rho
- field_data.ForwardTransform( *current[0], Idx::Jx, 0 );
- field_data.ForwardTransform( *current[1], Idx::Jy, 0 );
- field_data.ForwardTransform( *current[2], Idx::Jz, 0 );
- field_data.ForwardTransform( *rho, Idx::rho_old, 0 );
- field_data.ForwardTransform( *rho, Idx::rho_new, 1 );
+ field_data.ForwardTransform(*current[0], Idx::Jx, 0);
+ field_data.ForwardTransform(*current[1], Idx::Jy, 0);
+ field_data.ForwardTransform(*current[2], Idx::Jz, 0);
+ field_data.ForwardTransform(*rho, Idx::rho_old, 0);
+ field_data.ForwardTransform(*rho, Idx::rho_new, 1);
// Loop over boxes
- for (MFIter mfi(field_data.fields); mfi.isValid(); ++mfi){
+ for (amrex::MFIter mfi(field_data.fields); mfi.isValid(); ++mfi){
- const Box& bx = field_data.fields[mfi].box();
+ const amrex::Box& bx = field_data.fields[mfi].box();
// Extract arrays for the fields to be updated
- Array4<Complex> fields = field_data.fields[mfi].array();
+ amrex::Array4<Complex> fields = field_data.fields[mfi].array();
// Extract pointers for the k vectors
- const Real* const modified_kx_arr = modified_kx_vec[mfi].dataPtr();
+ const amrex::Real* const modified_kx_arr = modified_kx_vec[mfi].dataPtr();
+ const amrex::Real* const modified_kx_arr_c = modified_kx_vec_centered[mfi].dataPtr();
#if (AMREX_SPACEDIM==3)
- const Real* const modified_ky_arr = modified_ky_vec[mfi].dataPtr();
+ const amrex::Real* const modified_ky_arr = modified_ky_vec[mfi].dataPtr();
+ const amrex::Real* const modified_ky_arr_c = modified_ky_vec_centered[mfi].dataPtr();
#endif
- const Real* const modified_kz_arr = modified_kz_vec[mfi].dataPtr();
+ const amrex::Real* const modified_kz_arr = modified_kz_vec[mfi].dataPtr();
+ const amrex::Real* const modified_kz_arr_c = modified_kz_vec_centered[mfi].dataPtr();
// Local copy of member variables before GPU loop
- const Real dt = m_dt;
+ const amrex::Real dt = m_dt;
+
+ // Galilean velocity
+ const amrex::Real vgx = m_v_galilean[0];
+ const amrex::Real vgy = m_v_galilean[1];
+ const amrex::Real vgz = m_v_galilean[2];
// Loop over indices within one box
- ParallelFor( bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
+ ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
{
// Shortcuts for the values of J and rho
const Complex Jx = fields(i,j,k,Idx::Jx);
@@ -264,42 +934,73 @@ PsatdAlgorithm::CurrentCorrection (SpectralFieldData& field_data,
const Complex rho_new = fields(i,j,k,Idx::rho_new);
// k vector values, and coefficients
- const Real kx = modified_kx_arr[i];
+ const amrex::Real kx = modified_kx_arr[i];
+ const amrex::Real kx_c = modified_kx_arr_c[i];
#if (AMREX_SPACEDIM==3)
- const Real ky = modified_ky_arr[j];
- const Real kz = modified_kz_arr[k];
+ const amrex::Real ky = modified_ky_arr[j];
+ const amrex::Real kz = modified_kz_arr[k];
+ const amrex::Real ky_c = modified_ky_arr_c[j];
+ const amrex::Real kz_c = modified_kz_arr_c[k];
#else
- constexpr Real ky = 0;
- const Real kz = modified_kz_arr[j];
+ constexpr amrex::Real ky = 0._rt;
+ const amrex::Real kz = modified_kz_arr[j];
+ constexpr amrex::Real ky_c = 0._rt;
+ const amrex::Real kz_c = modified_kz_arr_c[j];
#endif
- const Real k_norm = std::sqrt( kx*kx + ky*ky + kz*kz );
-
- constexpr Complex I = Complex{0,1};
+ constexpr Complex I = Complex{0._rt, 1._rt};
- // div(J) in Fourier space
- const Complex k_dot_J = kx*Jx + ky*Jy + kz*Jz;
+ const amrex::Real k_norm = std::sqrt(kx * kx + ky * ky + kz * kz);
// Correct J
- if ( k_norm != 0 )
+ if (k_norm != 0._rt)
{
- fields(i,j,k,Idx::Jx) = Jx - (k_dot_J-I*(rho_new-rho_old)/dt)*kx/(k_norm*k_norm);
- fields(i,j,k,Idx::Jy) = Jy - (k_dot_J-I*(rho_new-rho_old)/dt)*ky/(k_norm*k_norm);
- fields(i,j,k,Idx::Jz) = Jz - (k_dot_J-I*(rho_new-rho_old)/dt)*kz/(k_norm*k_norm);
+ const Complex k_dot_J = kx * Jx + ky * Jy + kz * Jz;
+ const amrex::Real k_dot_vg = kx_c * vgx + ky_c * vgy + kz_c * vgz;
+
+ // k_dot_vg = 0 always with standard PSATD (zero Galilean velocity)
+ if ( k_dot_vg != 0._rt )
+ {
+ const Complex rho_old_mod = rho_old * amrex::exp(I * k_dot_vg * dt);
+ const Complex den = 1._rt - amrex::exp(I * k_dot_vg * dt);
+
+ fields(i,j,k,Idx::Jx) = Jx - (k_dot_J - k_dot_vg * (rho_new - rho_old_mod) / den)
+ * kx / (k_norm * k_norm);
+
+ fields(i,j,k,Idx::Jy) = Jy - (k_dot_J - k_dot_vg * (rho_new - rho_old_mod) / den)
+ * ky / (k_norm * k_norm);
+
+ fields(i,j,k,Idx::Jz) = Jz - (k_dot_J - k_dot_vg * (rho_new - rho_old_mod) / den)
+ * kz / (k_norm * k_norm);
+ }
+
+ else
+ {
+ fields(i,j,k,Idx::Jx) = Jx - (k_dot_J - I * (rho_new - rho_old) / dt)
+ * kx / (k_norm * k_norm);
+
+ fields(i,j,k,Idx::Jy) = Jy - (k_dot_J - I * (rho_new - rho_old) / dt)
+ * ky / (k_norm * k_norm);
+
+ fields(i,j,k,Idx::Jz) = Jz - (k_dot_J - I * (rho_new - rho_old) / dt)
+ * kz / (k_norm * k_norm);
+ }
}
- } );
+ });
}
// Backward Fourier transform of J
- field_data.BackwardTransform( *current[0], Idx::Jx, 0 );
- field_data.BackwardTransform( *current[1], Idx::Jy, 0 );
- field_data.BackwardTransform( *current[2], Idx::Jz, 0 );
+ field_data.BackwardTransform(*current[0], Idx::Jx, 0);
+ field_data.BackwardTransform(*current[1], Idx::Jy, 0);
+ field_data.BackwardTransform(*current[2], Idx::Jz, 0);
}
void
-PsatdAlgorithm::VayDeposition (SpectralFieldData& field_data,
- std::array<std::unique_ptr<amrex::MultiFab>,3>& current) {
+PsatdAlgorithm::VayDeposition (
+ SpectralFieldData& field_data,
+ std::array<std::unique_ptr<amrex::MultiFab>,3>& current)
+{
// Profiling
- WARPX_PROFILE("PsatdAlgorithm::VayDeposition()");
+ BL_PROFILE("PsatdAlgorithm::VayDeposition()");
using Idx = SpectralFieldIndex;
@@ -311,8 +1012,8 @@ PsatdAlgorithm::VayDeposition (SpectralFieldData& field_data,
field_data.ForwardTransform(*current[2], Idx::Jz, 0, IntVect(1));
// Loop over boxes
- for (amrex::MFIter mfi(field_data.fields); mfi.isValid(); ++mfi) {
-
+ for (amrex::MFIter mfi(field_data.fields); mfi.isValid(); ++mfi)
+ {
const amrex::Box& bx = field_data.fields[mfi].box();
// Extract arrays for the fields to be updated
@@ -360,7 +1061,6 @@ PsatdAlgorithm::VayDeposition (SpectralFieldData& field_data,
// Compute Jz
if (kz_mod != 0._rt) fields(i,j,k,Idx::Jz) = I * Dz / kz_mod;
else fields(i,j,k,Idx::Jz) = 0._rt;
-
});
}
@@ -369,4 +1069,5 @@ PsatdAlgorithm::VayDeposition (SpectralFieldData& field_data,
field_data.BackwardTransform(*current[1], Idx::Jy, 0);
field_data.BackwardTransform(*current[2], Idx::Jz, 0);
}
+
#endif // WARPX_USE_PSATD
diff --git a/Source/FieldSolver/SpectralSolver/SpectralSolver.cpp b/Source/FieldSolver/SpectralSolver/SpectralSolver.cpp
index 4c9cb6967..725cfcf92 100644
--- a/Source/FieldSolver/SpectralSolver/SpectralSolver.cpp
+++ b/Source/FieldSolver/SpectralSolver/SpectralSolver.cpp
@@ -7,8 +7,6 @@
#include "SpectralKSpace.H"
#include "SpectralSolver.H"
#include "SpectralAlgorithms/PsatdAlgorithm.H"
-#include "SpectralAlgorithms/GalileanAlgorithm.H"
-#include "SpectralAlgorithms/AvgGalileanAlgorithm.H"
#include "SpectralAlgorithms/PMLPsatdAlgorithm.H"
#include "SpectralAlgorithms/ComovingPsatdAlgorithm.H"
#include "WarpX.H"
@@ -61,26 +59,15 @@ SpectralSolver::SpectralSolver(
k_space, dm, norder_x, norder_y, norder_z, nodal, dt);
}
else {
- if (fft_do_time_averaging){
- algorithm = std::make_unique<AvgGalileanAlgorithm>(
- k_space, dm, norder_x, norder_y, norder_z, nodal, v_galilean, dt);
+ // Comoving PSATD algorithm
+ if (v_comoving[0] != 0. || v_comoving[1] != 0. || v_comoving[2] != 0.) {
+ algorithm = std::make_unique<ComovingPsatdAlgorithm>(
+ k_space, dm, norder_x, norder_y, norder_z, nodal, v_comoving, dt, update_with_rho);
}
+ // PSATD algorithms: standard, Galilean, or averaged Galilean
else {
- // Galilean PSATD algorithm
- if (v_galilean[0] != 0. || v_galilean[1] != 0. || v_galilean[2] != 0.) {
- algorithm = std::make_unique<GalileanAlgorithm>(
- k_space, dm, norder_x, norder_y, norder_z, nodal, v_galilean, dt, update_with_rho);
- }
- // Comoving PSATD algorithm
- else if (v_comoving[0] != 0. || v_comoving[1] != 0. || v_comoving[2] != 0.) {
- algorithm = std::make_unique<ComovingPsatdAlgorithm>(
- k_space, dm, norder_x, norder_y, norder_z, nodal, v_comoving, dt, update_with_rho);
- }
- // Standard PSATD algorithm
- else {
- algorithm = std::make_unique<PsatdAlgorithm>(
- k_space, dm, norder_x, norder_y, norder_z, nodal, dt, update_with_rho);
- }
+ algorithm = std::make_unique<PsatdAlgorithm>(
+ k_space, dm, norder_x, norder_y, norder_z, nodal, v_galilean, dt, update_with_rho, fft_do_time_averaging);
}
}