/* Copyright 2020 Remi Lehe * * This file is part of WarpX. * * License: BSD-3-Clause-LBNL */ #include "FiniteDifferenceSolver.H" #ifndef WARPX_DIM_RZ # include "FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/CartesianYeeAlgorithm.H" # include "FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/CartesianCKCAlgorithm.H" # include "FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/CartesianNodalAlgorithm.H" #else # include "FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/CylindricalYeeAlgorithm.H" #endif #include "Utils/TextMsg.H" #include "Utils/WarpXAlgorithmSelection.H" #include "Utils/WarpXConst.H" #include "WarpX.H" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace amrex; /** * \brief Update the E field, over one timestep */ void FiniteDifferenceSolver::EvolveE ( std::array< std::unique_ptr, 3 >& Efield, std::array< std::unique_ptr, 3 > const& Bfield, std::array< std::unique_ptr, 3 > const& Jfield, std::array< std::unique_ptr, 3 > const& edge_lengths, std::array< std::unique_ptr, 3 > const& face_areas, std::array< std::unique_ptr, 3 >& ECTRhofield, std::unique_ptr const& Ffield, int lev, amrex::Real const dt ) { #ifdef AMREX_USE_EB if (m_fdtd_algo != ElectromagneticSolverAlgo::ECT) { amrex::ignore_unused(face_areas, ECTRhofield); } #else amrex::ignore_unused(face_areas, ECTRhofield); #endif // Select algorithm (The choice of algorithm is a runtime option, // but we compile code for each algorithm, using templates) #ifdef WARPX_DIM_RZ if (m_fdtd_algo == ElectromagneticSolverAlgo::Yee){ ignore_unused(edge_lengths); EvolveECylindrical ( Efield, Bfield, Jfield, Ffield, lev, dt ); #else if (m_grid_type == GridType::Collocated) { EvolveECartesian ( Efield, Bfield, Jfield, edge_lengths, Ffield, lev, dt ); } else if (m_fdtd_algo == ElectromagneticSolverAlgo::Yee || m_fdtd_algo == ElectromagneticSolverAlgo::ECT) { EvolveECartesian ( Efield, Bfield, Jfield, edge_lengths, Ffield, lev, dt ); } else if (m_fdtd_algo == ElectromagneticSolverAlgo::CKC) { EvolveECartesian ( Efield, Bfield, Jfield, edge_lengths, Ffield, lev, dt ); #endif } else { WARPX_ABORT_WITH_MESSAGE("EvolveE: Unknown algorithm"); } } #ifndef WARPX_DIM_RZ template void FiniteDifferenceSolver::EvolveECartesian ( std::array< std::unique_ptr, 3 >& Efield, std::array< std::unique_ptr, 3 > const& Bfield, std::array< std::unique_ptr, 3 > const& Jfield, std::array< std::unique_ptr, 3 > const& edge_lengths, std::unique_ptr const& Ffield, int lev, amrex::Real const dt ) { #ifndef AMREX_USE_EB amrex::ignore_unused(edge_lengths); #endif amrex::LayoutData* cost = WarpX::getCosts(lev); Real constexpr c2 = PhysConst::c * PhysConst::c; // Loop through the grids, and over the tiles within each grid #ifdef AMREX_USE_OMP #pragma omp parallel if (amrex::Gpu::notInLaunchRegion()) #endif for ( MFIter mfi(*Efield[0], TilingIfNotGPU()); mfi.isValid(); ++mfi ) { if (cost && WarpX::load_balance_costs_update_algo == LoadBalanceCostsUpdateAlgo::Timers) { amrex::Gpu::synchronize(); } Real wt = amrex::second(); // Extract field data for this grid/tile Array4 const& Ex = Efield[0]->array(mfi); Array4 const& Ey = Efield[1]->array(mfi); Array4 const& Ez = Efield[2]->array(mfi); Array4 const& Bx = Bfield[0]->array(mfi); Array4 const& By = Bfield[1]->array(mfi); Array4 const& Bz = Bfield[2]->array(mfi); Array4 const& jx = Jfield[0]->array(mfi); Array4 const& jy = Jfield[1]->array(mfi); Array4 const& jz = Jfield[2]->array(mfi); #ifdef AMREX_USE_EB amrex::Array4 const& lx = edge_lengths[0]->array(mfi); amrex::Array4 const& ly = edge_lengths[1]->array(mfi); amrex::Array4 const& lz = edge_lengths[2]->array(mfi); #endif // Extract stencil coefficients Real const * const AMREX_RESTRICT coefs_x = m_stencil_coefs_x.dataPtr(); int const n_coefs_x = m_stencil_coefs_x.size(); Real const * const AMREX_RESTRICT coefs_y = m_stencil_coefs_y.dataPtr(); int const n_coefs_y = m_stencil_coefs_y.size(); Real const * const AMREX_RESTRICT coefs_z = m_stencil_coefs_z.dataPtr(); int const n_coefs_z = m_stencil_coefs_z.size(); // Extract tileboxes for which to loop Box const& tex = mfi.tilebox(Efield[0]->ixType().toIntVect()); Box const& tey = mfi.tilebox(Efield[1]->ixType().toIntVect()); Box const& tez = mfi.tilebox(Efield[2]->ixType().toIntVect()); // Loop over the cells and update the fields amrex::ParallelFor(tex, tey, tez, [=] AMREX_GPU_DEVICE (int i, int j, int k){ #ifdef AMREX_USE_EB // Skip field push if this cell is fully covered by embedded boundaries if (lx(i, j, k) <= 0) return; #endif Ex(i, j, k) += c2 * dt * ( - T_Algo::DownwardDz(By, coefs_z, n_coefs_z, i, j, k) + T_Algo::DownwardDy(Bz, coefs_y, n_coefs_y, i, j, k) - PhysConst::mu0 * jx(i, j, k) ); }, [=] AMREX_GPU_DEVICE (int i, int j, int k){ #ifdef AMREX_USE_EB // Skip field push if this cell is fully covered by embedded boundaries #ifdef WARPX_DIM_3D if (ly(i,j,k) <= 0) return; #elif defined(WARPX_DIM_XZ) //In XZ Ey is associated with a mesh node, so we need to check if the mesh node is covered amrex::ignore_unused(ly); if (lx(i, j, k)<=0 || lx(i-1, j, k)<=0 || lz(i, j-1, k)<=0 || lz(i, j, k)<=0) return; #endif #endif Ey(i, j, k) += c2 * dt * ( - T_Algo::DownwardDx(Bz, coefs_x, n_coefs_x, i, j, k) + T_Algo::DownwardDz(Bx, coefs_z, n_coefs_z, i, j, k) - PhysConst::mu0 * jy(i, j, k) ); }, [=] AMREX_GPU_DEVICE (int i, int j, int k){ #ifdef AMREX_USE_EB // Skip field push if this cell is fully covered by embedded boundaries if (lz(i,j,k) <= 0) return; #endif Ez(i, j, k) += c2 * dt * ( - T_Algo::DownwardDy(Bx, coefs_y, n_coefs_y, i, j, k) + T_Algo::DownwardDx(By, coefs_x, n_coefs_x, i, j, k) - PhysConst::mu0 * jz(i, j, k) ); } ); // If F is not a null pointer, further update E using the grad(F) term // (hyperbolic correction for errors in charge conservation) if (Ffield) { // Extract field data for this grid/tile Array4 F = Ffield->array(mfi); // Loop over the cells and update the fields amrex::ParallelFor(tex, tey, tez, [=] AMREX_GPU_DEVICE (int i, int j, int k){ Ex(i, j, k) += c2 * dt * T_Algo::UpwardDx(F, coefs_x, n_coefs_x, i, j, k); }, [=] AMREX_GPU_DEVICE (int i, int j, int k){ Ey(i, j, k) += c2 * dt * T_Algo::UpwardDy(F, coefs_y, n_coefs_y, i, j, k); }, [=] AMREX_GPU_DEVICE (int i, int j, int k){ Ez(i, j, k) += c2 * dt * T_Algo::UpwardDz(F, coefs_z, n_coefs_z, i, j, k); } ); } if (cost && WarpX::load_balance_costs_update_algo == LoadBalanceCostsUpdateAlgo::Timers) { amrex::Gpu::synchronize(); wt = amrex::second() - wt; amrex::HostDevice::Atomic::Add( &(*cost)[mfi.index()], wt); } } } #else // corresponds to ifndef WARPX_DIM_RZ template void FiniteDifferenceSolver::EvolveECylindrical ( std::array< std::unique_ptr, 3 >& Efield, std::array< std::unique_ptr, 3 > const& Bfield, std::array< std::unique_ptr, 3 > const& Jfield, std::unique_ptr const& Ffield, int lev, amrex::Real const dt ) { amrex::LayoutData* cost = WarpX::getCosts(lev); // Loop through the grids, and over the tiles within each grid #ifdef AMREX_USE_OMP #pragma omp parallel if (amrex::Gpu::notInLaunchRegion()) #endif for ( MFIter mfi(*Efield[0], TilingIfNotGPU()); mfi.isValid(); ++mfi ) { if (cost && WarpX::load_balance_costs_update_algo == LoadBalanceCostsUpdateAlgo::Timers) { amrex::Gpu::synchronize(); } Real wt = amrex::second(); // Extract field data for this grid/tile Array4 const& Er = Efield[0]->array(mfi); Array4 const& Et = Efield[1]->array(mfi); Array4 const& Ez = Efield[2]->array(mfi); Array4 const& Br = Bfield[0]->array(mfi); Array4 const& Bt = Bfield[1]->array(mfi); Array4 const& Bz = Bfield[2]->array(mfi); Array4 const& jr = Jfield[0]->array(mfi); Array4 const& jt = Jfield[1]->array(mfi); Array4 const& jz = Jfield[2]->array(mfi); // Extract stencil coefficients Real const * const AMREX_RESTRICT coefs_r = m_stencil_coefs_r.dataPtr(); int const n_coefs_r = m_stencil_coefs_r.size(); Real const * const AMREX_RESTRICT coefs_z = m_stencil_coefs_z.dataPtr(); int const n_coefs_z = m_stencil_coefs_z.size(); // Extract cylindrical specific parameters Real const dr = m_dr; int const nmodes = m_nmodes; Real const rmin = m_rmin; // Extract tileboxes for which to loop Box const& ter = mfi.tilebox(Efield[0]->ixType().toIntVect()); Box const& tet = mfi.tilebox(Efield[1]->ixType().toIntVect()); Box const& tez = mfi.tilebox(Efield[2]->ixType().toIntVect()); Real const c2 = PhysConst::c * PhysConst::c; // Loop over the cells and update the fields amrex::ParallelFor(ter, tet, tez, [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/){ Real const r = rmin + (i + 0.5)*dr; // r on cell-centered point (Er is cell-centered in r) Er(i, j, 0, 0) += c2 * dt*( - T_Algo::DownwardDz(Bt, coefs_z, n_coefs_z, i, j, 0, 0) - PhysConst::mu0 * jr(i, j, 0, 0) ); // Mode m=0 for (int m=1; m F = Ffield->array(mfi); // Loop over the cells and update the fields amrex::ParallelFor(ter, tet, tez, [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/){ Er(i, j, 0, 0) += c2 * dt * T_Algo::UpwardDr(F, coefs_r, n_coefs_r, i, j, 0, 0); for (int m=1; m= 2) { // needs to have at least m=0 and m=1 int const m=1; Et(i, j, 0, 2*m-1) += c2 * dt * m * F(i+1, j, 0, 2*m )/dr; // Real part Et(i, j, 0, 2*m ) += c2 * dt * -m * F(i+1, j, 0, 2*m-1)/dr; // Imaginary part } } }, [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/){ Ez(i, j, 0, 0) += c2 * dt * T_Algo::UpwardDz(F, coefs_z, n_coefs_z, i, j, 0, 0); for (int m=1; m