aboutsummaryrefslogtreecommitdiff
path: root/Source/Particles
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Particles')
-rw-r--r--Source/Particles/Make.package14
-rw-r--r--Source/Particles/MultiParticleContainer.H209
-rw-r--r--Source/Particles/MultiParticleContainer.cpp389
-rw-r--r--Source/Particles/PhysicalParticleContainer.H127
-rw-r--r--Source/Particles/PhysicalParticleContainer.cpp1941
-rw-r--r--Source/Particles/RigidInjectedParticleContainer.H81
-rw-r--r--Source/Particles/RigidInjectedParticleContainer.cpp462
-rw-r--r--Source/Particles/WarpXParticleContainer.H233
-rw-r--r--Source/Particles/WarpXParticleContainer.cpp1072
-rw-r--r--Source/Particles/deposit_cic.F90134
-rw-r--r--Source/Particles/interpolate_cic.F90371
-rw-r--r--Source/Particles/push_particles_ES.F90258
12 files changed, 5291 insertions, 0 deletions
diff --git a/Source/Particles/Make.package b/Source/Particles/Make.package
new file mode 100644
index 000000000..acbfe3390
--- /dev/null
+++ b/Source/Particles/Make.package
@@ -0,0 +1,14 @@
+F90EXE_sources += deposit_cic.F90
+F90EXE_sources += interpolate_cic.F90
+F90EXE_sources += push_particles_ES.F90
+CEXE_sources += PhysicalParticleContainer.cpp
+CEXE_sources += MultiParticleContainer.cpp
+CEXE_sources += WarpXParticleContainer.cpp
+CEXE_sources += RigidInjectedParticleContainer.cpp
+CEXE_headers += MultiParticleContainer.H
+CEXE_headers += WarpXParticleContainer.H
+CEXE_headers += RigidInjectedParticleContainer.H
+CEXE_headers += PhysicalParticleContainer.H
+
+INCLUDE_LOCATIONS += $(WARPX_HOME)/Source/Particles
+VPATH_LOCATIONS += $(WARPX_HOME)/Source/Particles
diff --git a/Source/Particles/MultiParticleContainer.H b/Source/Particles/MultiParticleContainer.H
new file mode 100644
index 000000000..b21247ff6
--- /dev/null
+++ b/Source/Particles/MultiParticleContainer.H
@@ -0,0 +1,209 @@
+
+#ifndef WARPX_ParticleContainer_H_
+#define WARPX_ParticleContainer_H_
+
+#include <memory>
+#include <map>
+#include <string>
+
+#include <AMReX_Particles.H>
+
+#include <WarpXParticleContainer.H>
+#include <PhysicalParticleContainer.H>
+#include <RigidInjectedParticleContainer.H>
+#include <LaserParticleContainer.H>
+
+//
+// MultiParticleContainer holds multiple (nspecies or npsecies+1 when
+// using laser) WarpXParticleContainer. WarpXParticleContainer is
+// derived from amrex::ParticleContainer, and it is
+// polymorphic. PhysicalParticleContainer and LaserParticleContainer are
+// concrete class dervied from WarpXParticlContainer.
+//
+
+class MultiParticleContainer
+{
+
+public:
+
+ MultiParticleContainer (amrex::AmrCore* amr_core);
+
+ ~MultiParticleContainer() {}
+
+ WarpXParticleContainer& GetParticleContainer (int ispecies) {
+ return *allcontainers[ispecies];
+ }
+
+ std::array<amrex::Real, 3> meanParticleVelocity(int ispecies) {
+ return allcontainers[ispecies]->meanParticleVelocity();
+ }
+
+ void AllocData ();
+
+ void InitData ();
+
+#ifdef WARPX_DO_ELECTROSTATIC
+ ///
+ /// Performs the field gather operation using the input field E, for all the species
+ /// in the MultiParticleContainer. This is the electrostatic version of the field gather.
+ ///
+ void FieldGatherES (const amrex::Vector<std::array<std::unique_ptr<amrex::MultiFab>, 3> >& E,
+ const amrex::Vector<std::unique_ptr<amrex::FabArray<amrex::BaseFab<int> > > >& masks);
+
+ ///
+ /// This evolves all the particles by one PIC time step, including charge deposition, the
+ /// field solve, and pushing the particles, for all the species in the MultiParticleContainer.
+ /// This is the electrostatic version.
+ ///
+ void EvolveES (const amrex::Vector<std::array<std::unique_ptr<amrex::MultiFab>, 3> >& E,
+ amrex::Vector<std::unique_ptr<amrex::MultiFab> >& rho,
+ amrex::Real t, amrex::Real dt);
+
+ ///
+ /// This pushes the particle positions by one half time step for all the species in the
+ /// MultiParticleContainer. It is used to desynchronize the particles after initializaton
+ /// or when restarting from a checkpoint. This is the electrostatic version.
+ ///
+ void PushXES (amrex::Real dt);
+
+ ///
+ /// This deposits the particle charge onto rho, accumulating the value for all the species
+ /// in the MultiParticleContainer. rho is assumed to contain node-centered multifabs.
+ /// This version is hard-coded for CIC deposition.
+ ///
+ void DepositCharge(amrex::Vector<std::unique_ptr<amrex::MultiFab> >& rho, bool local = false);
+
+ ///
+ /// This returns the total particle charge for all the species in this MultiParticleContainer.
+ /// This is needed to subtract the offset for periodic boundary conditions.
+ ///
+ amrex::Real sumParticleCharge(bool local = false);
+#endif // WARPX_DO_ELECTROSTATIC
+
+ ///
+ /// Performs the field gather operation using the input fields E and B, for all the species
+ /// in the MultiParticleContainer. This is the electromagnetic version of the field gather.
+ ///
+ void FieldGather (int lev,
+ const amrex::MultiFab& Ex, const amrex::MultiFab& Ey, const amrex::MultiFab& Ez,
+ const amrex::MultiFab& Bx, const amrex::MultiFab& By, const amrex::MultiFab& Bz);
+
+ ///
+ /// This evolves all the particles by one PIC time step, including current deposition, the
+ /// field solve, and pushing the particles, for all the species in the MultiParticleContainer.
+ /// This is the electromagnetic version.
+ ///
+ void Evolve (int lev,
+ const amrex::MultiFab& Ex, const amrex::MultiFab& Ey, const amrex::MultiFab& Ez,
+ const amrex::MultiFab& Bx, const amrex::MultiFab& By, const amrex::MultiFab& Bz,
+ amrex::MultiFab& jx, amrex::MultiFab& jy, amrex::MultiFab& jz,
+ amrex::MultiFab* cjx, amrex::MultiFab* cjy, amrex::MultiFab* cjz,
+ amrex::MultiFab* rho, amrex::MultiFab* crho,
+ const amrex::MultiFab* cEx, const amrex::MultiFab* cEy, const amrex::MultiFab* cEz,
+ const amrex::MultiFab* cBx, const amrex::MultiFab* cBy, const amrex::MultiFab* cBz,
+ amrex::Real t, amrex::Real dt);
+
+ ///
+ /// This pushes the particle positions by one half time step for all the species in the
+ /// MultiParticleContainer. It is used to desynchronize the particles after initializaton
+ /// or when restarting from a checkpoint. This is the electromagnetic version.
+ ///
+ void PushX (amrex::Real dt);
+
+ ///
+ /// This pushes the particle momenta by dt for all the species in the
+ /// MultiParticleContainer. It is used to desynchronize the particles after initializaton
+ /// or when restarting from a checkpoint. It is also used to synchronize particles at the
+ /// the end of the run. This is the electromagnetic version.
+ ///
+ void PushP (int lev, amrex::Real dt,
+ const amrex::MultiFab& Ex, const amrex::MultiFab& Ey, const amrex::MultiFab& Ez,
+ const amrex::MultiFab& Bx, const amrex::MultiFab& By, const amrex::MultiFab& Bz);
+
+ ///
+ /// This deposits the particle charge onto a node-centered MultiFab and returns a unique ptr
+ /// to it. The charge density is accumulated over all the particles in the MultiParticleContainer
+ /// This version uses PICSAR routines to perform the deposition.
+ ///
+ std::unique_ptr<amrex::MultiFab> GetChargeDensity(int lev, bool local = false);
+
+ void Checkpoint (const std::string& dir,
+ bool is_checkpoint,
+ const amrex::Vector<std::string>& varnames = amrex::Vector<std::string>()) const;
+
+ void Restart (const std::string& dir);
+
+ void PostRestart ();
+
+ void ReadHeader (std::istream& is);
+
+ void WriteHeader (std::ostream& os) const;
+
+ void SortParticlesByCell ();
+
+ void Redistribute ();
+
+ void RedistributeLocal (const int num_ghost);
+
+ amrex::Vector<long> NumberOfParticlesInGrid(int lev) const;
+
+ void Increment (amrex::MultiFab& mf, int lev);
+
+ void SetParticleBoxArray (int lev, amrex::BoxArray& new_ba);
+ void SetParticleDistributionMap (int lev, amrex::DistributionMapping& new_dm);
+
+ int nSpecies() const {return nspecies;}
+
+ int nSpeciesDepositOnMainGrid () const {
+ int r = 0;
+ for (int i : deposit_on_main_grid) {
+ if (i) ++r;
+ }
+ return r;
+ }
+
+ void GetLabFrameData(const std::string& snapshot_name,
+ const int i_lab, const int direction,
+ const amrex::Real z_old, const amrex::Real z_new,
+ const amrex::Real t_boost, const amrex::Real t_lab, const amrex::Real dt,
+ amrex::Vector<WarpXParticleContainer::DiagnosticParticleData>& parts) const;
+
+ //
+ // Parameters for the Cherenkov corrector in the FDTD solver.
+ // Both stencils are calculated ar runtime.
+ //
+ // Number of coefficients for the stencil of the NCI corrector.
+ // The stencil is applied in the z direction only.
+ static constexpr int nstencilz_fdtd_nci_corr=5;
+
+ amrex::Vector<amrex::Array<amrex::Real, nstencilz_fdtd_nci_corr> > fdtd_nci_stencilz_ex;
+ amrex::Vector<amrex::Array<amrex::Real, nstencilz_fdtd_nci_corr> > fdtd_nci_stencilz_by;
+
+ std::vector<std::string> GetSpeciesNames() const { return species_names; }
+
+ PhysicalParticleContainer& GetPCtmp () { return *pc_tmp; }
+
+protected:
+
+ // Particle container types
+ enum struct PCTypes {Physical, RigidInjected};
+
+ std::vector<std::string> species_names;
+
+ std::vector<int> deposit_on_main_grid;
+
+ std::vector<PCTypes> species_types;
+
+private:
+
+ // physical particles (+ laser)
+ amrex::Vector<std::unique_ptr<WarpXParticleContainer> > allcontainers;
+ // Temporary particle container, used e.g. for particle splitting.
+ std::unique_ptr<PhysicalParticleContainer> pc_tmp;
+
+ void ReadParameters ();
+
+ // runtime parameters
+ int nspecies = 1; // physical particles only. If WarpX::use_laser, nspecies+1 == allcontainers.size().
+};
+#endif /*WARPX_ParticleContainer_H_*/
diff --git a/Source/Particles/MultiParticleContainer.cpp b/Source/Particles/MultiParticleContainer.cpp
new file mode 100644
index 000000000..1b644b543
--- /dev/null
+++ b/Source/Particles/MultiParticleContainer.cpp
@@ -0,0 +1,389 @@
+#include <limits>
+#include <algorithm>
+#include <string>
+
+#include <MultiParticleContainer.H>
+#include <WarpX_f.H>
+#include <WarpX.H>
+
+using namespace amrex;
+
+constexpr int MultiParticleContainer::nstencilz_fdtd_nci_corr;
+
+MultiParticleContainer::MultiParticleContainer (AmrCore* amr_core)
+{
+ ReadParameters();
+
+ int n = WarpX::use_laser ? nspecies+1 : nspecies;
+ allcontainers.resize(n);
+ for (int i = 0; i < nspecies; ++i) {
+ if (species_types[i] == PCTypes::Physical) {
+ allcontainers[i].reset(new PhysicalParticleContainer(amr_core, i, species_names[i]));
+ }
+ else if (species_types[i] == PCTypes::RigidInjected) {
+ allcontainers[i].reset(new RigidInjectedParticleContainer(amr_core, i, species_names[i]));
+ }
+ allcontainers[i]->deposit_on_main_grid = deposit_on_main_grid[i];
+ }
+ if (WarpX::use_laser) {
+ allcontainers[n-1].reset(new LaserParticleContainer(amr_core,n-1));
+ }
+ pc_tmp.reset(new PhysicalParticleContainer(amr_core));
+}
+
+void
+MultiParticleContainer::ReadParameters ()
+{
+ static bool initialized = false;
+ if (!initialized)
+ {
+ ParmParse pp("particles");
+
+ pp.query("nspecies", nspecies);
+ BL_ASSERT(nspecies >= 0);
+
+ if (nspecies > 0) {
+ pp.getarr("species_names", species_names);
+ BL_ASSERT(species_names.size() == nspecies);
+
+ deposit_on_main_grid.resize(nspecies, 0);
+ std::vector<std::string> tmp;
+ pp.queryarr("deposit_on_main_grid", tmp);
+ for (auto const& name : tmp) {
+ auto it = std::find(species_names.begin(), species_names.end(), name);
+ AMREX_ALWAYS_ASSERT_WITH_MESSAGE(it != species_names.end(), "ERROR: species in particles.deposit_on_main_grid must be part of particles.species_names");
+ int i = std::distance(species_names.begin(), it);
+ deposit_on_main_grid[i] = 1;
+ }
+
+ species_types.resize(nspecies, PCTypes::Physical);
+
+ std::vector<std::string> rigid_injected_species;
+ pp.queryarr("rigid_injected_species", rigid_injected_species);
+
+ if (!rigid_injected_species.empty()) {
+ for (auto const& name : rigid_injected_species) {
+ auto it = std::find(species_names.begin(), species_names.end(), name);
+ AMREX_ALWAYS_ASSERT_WITH_MESSAGE(it != species_names.end(), "ERROR: species in particles.rigid_injected_species must be part of particles.species_names");
+ int i = std::distance(species_names.begin(), it);
+ species_types[i] = PCTypes::RigidInjected;
+ }
+ }
+ }
+ pp.query("use_fdtd_nci_corr", WarpX::use_fdtd_nci_corr);
+ pp.query("l_lower_order_in_v", WarpX::l_lower_order_in_v);
+ initialized = true;
+ }
+}
+
+void
+MultiParticleContainer::AllocData ()
+{
+ for (auto& pc : allcontainers) {
+ pc->AllocData();
+ }
+ pc_tmp->AllocData();
+}
+
+void
+MultiParticleContainer::InitData ()
+{
+ for (auto& pc : allcontainers) {
+ pc->InitData();
+ }
+ pc_tmp->InitData();
+}
+
+
+#ifdef WARPX_DO_ELECTROSTATIC
+void
+MultiParticleContainer::FieldGatherES (const Vector<std::array<std::unique_ptr<MultiFab>, 3> >& E,
+ const amrex::Vector<std::unique_ptr<amrex::FabArray<amrex::BaseFab<int> > > >& masks)
+{
+ for (auto& pc : allcontainers) {
+ pc->FieldGatherES(E, masks);
+ }
+}
+
+void
+MultiParticleContainer::EvolveES (const Vector<std::array<std::unique_ptr<MultiFab>, 3> >& E,
+ Vector<std::unique_ptr<MultiFab> >& rho,
+ Real t, Real dt)
+{
+
+ int nlevs = rho.size();
+ int ng = rho[0]->nGrow();
+
+ for (unsigned i = 0; i < nlevs; i++) {
+ rho[i]->setVal(0.0, ng);
+ }
+
+ for (auto& pc : allcontainers) {
+ pc->EvolveES(E, rho, t, dt);
+ }
+
+ for (unsigned i = 0; i < nlevs; i++) {
+ const Geometry& gm = allcontainers[0]->Geom(i);
+ rho[i]->SumBoundary(gm.periodicity());
+ }
+}
+
+void
+MultiParticleContainer::Evolve (int lev,
+ const MultiFab& Ex, const MultiFab& Ey, const MultiFab& Ez,
+ const MultiFab& Bx, const MultiFab& By, const MultiFab& Bz,
+ MultiFab& jx, MultiFab& jy, MultiFab& jz,
+ MultiFab* cjx, MultiFab* cjy, MultiFab* cjz,
+ MultiFab* rho,
+ const MultiFab* cEx, const MultiFab* cEy, const MultiFab* cEz,
+ const MultiFab* cBx, const MultiFab* cBy, const MultiFab* cBz,
+ Real t, Real dt)
+{
+ jx.setVal(0.0);
+ jy.setVal(0.0);
+ jz.setVal(0.0);
+ if (cjx) cjx->setVal(0.0);
+ if (cjy) cjy->setVal(0.0);
+ if (cjz) cjz->setVal(0.0);
+ if (rho) rho->setVal(0.0);
+ for (auto& pc : allcontainers) {
+ pc->Evolve(lev, Ex, Ey, Ez, Bx, By, Bz, jx, jy, jz, cjx, cjy, cjz,
+ rho, cEx, cEy, cEz, cBx, cBy, cBz, t, dt);
+ }
+}
+
+void
+MultiParticleContainer::PushXES (Real dt)
+{
+ for (auto& pc : allcontainers) {
+ pc->PushXES(dt);
+ }
+}
+
+void
+MultiParticleContainer::
+DepositCharge (Vector<std::unique_ptr<MultiFab> >& rho, bool local)
+{
+ int nlevs = rho.size();
+ int ng = rho[0]->nGrow();
+
+ for (unsigned i = 0; i < nlevs; i++) {
+ rho[i]->setVal(0.0, ng);
+ }
+
+ for (unsigned i = 0, n = allcontainers.size(); i < n; ++i) {
+ allcontainers[i]->DepositCharge(rho, true);
+ }
+
+ if (!local) {
+ for (unsigned i = 0; i < nlevs; i++) {
+ const Geometry& gm = allcontainers[0]->Geom(i);
+ rho[i]->SumBoundary(gm.periodicity());
+ }
+ }
+}
+
+amrex::Real
+MultiParticleContainer::sumParticleCharge (bool local)
+{
+ amrex::Real total_charge = allcontainers[0]->sumParticleCharge(local);
+ for (unsigned i = 1, n = allcontainers.size(); i < n; ++i) {
+ total_charge += allcontainers[i]->sumParticleCharge(local);
+ }
+ return total_charge;
+}
+
+#endif // WARPX_DO_ELECTROSTATIC
+
+void
+MultiParticleContainer::FieldGather (int lev,
+ const MultiFab& Ex, const MultiFab& Ey, const MultiFab& Ez,
+ const MultiFab& Bx, const MultiFab& By, const MultiFab& Bz)
+{
+ for (auto& pc : allcontainers) {
+ pc->FieldGather(lev, Ex, Ey, Ez, Bx, By, Bz);
+ }
+}
+
+void
+MultiParticleContainer::Evolve (int lev,
+ const MultiFab& Ex, const MultiFab& Ey, const MultiFab& Ez,
+ const MultiFab& Bx, const MultiFab& By, const MultiFab& Bz,
+ MultiFab& jx, MultiFab& jy, MultiFab& jz,
+ MultiFab* cjx, MultiFab* cjy, MultiFab* cjz,
+ MultiFab* rho, MultiFab* crho,
+ const MultiFab* cEx, const MultiFab* cEy, const MultiFab* cEz,
+ const MultiFab* cBx, const MultiFab* cBy, const MultiFab* cBz,
+ Real t, Real dt)
+{
+ jx.setVal(0.0);
+ jy.setVal(0.0);
+ jz.setVal(0.0);
+ if (cjx) cjx->setVal(0.0);
+ if (cjy) cjy->setVal(0.0);
+ if (cjz) cjz->setVal(0.0);
+ if (rho) rho->setVal(0.0);
+ if (crho) crho->setVal(0.0);
+ for (auto& pc : allcontainers) {
+ pc->Evolve(lev, Ex, Ey, Ez, Bx, By, Bz, jx, jy, jz, cjx, cjy, cjz,
+ rho, crho, cEx, cEy, cEz, cBx, cBy, cBz, t, dt);
+ }
+}
+
+void
+MultiParticleContainer::PushX (Real dt)
+{
+ for (auto& pc : allcontainers) {
+ pc->PushX(dt);
+ }
+}
+
+void
+MultiParticleContainer::PushP (int lev, Real dt,
+ const MultiFab& Ex, const MultiFab& Ey, const MultiFab& Ez,
+ const MultiFab& Bx, const MultiFab& By, const MultiFab& Bz)
+{
+ for (auto& pc : allcontainers) {
+ pc->PushP(lev, dt, Ex, Ey, Ez, Bx, By, Bz);
+ }
+}
+
+std::unique_ptr<MultiFab>
+MultiParticleContainer::GetChargeDensity (int lev, bool local)
+{
+ std::unique_ptr<MultiFab> rho = allcontainers[0]->GetChargeDensity(lev, true);
+ for (unsigned i = 1, n = allcontainers.size(); i < n; ++i) {
+ std::unique_ptr<MultiFab> rhoi = allcontainers[i]->GetChargeDensity(lev, true);
+ MultiFab::Add(*rho, *rhoi, 0, 0, 1, rho->nGrow());
+ }
+ if (!local) {
+ const Geometry& gm = allcontainers[0]->Geom(lev);
+ rho->SumBoundary(gm.periodicity());
+ }
+ return rho;
+}
+
+void
+MultiParticleContainer::SortParticlesByCell ()
+{
+ for (auto& pc : allcontainers) {
+ pc->SortParticlesByCell();
+ }
+}
+
+void
+MultiParticleContainer::Redistribute ()
+{
+ for (auto& pc : allcontainers) {
+ pc->Redistribute();
+ }
+}
+
+void
+MultiParticleContainer::RedistributeLocal (const int num_ghost)
+{
+ for (auto& pc : allcontainers) {
+ pc->Redistribute(0, 0, 0, num_ghost);
+ }
+}
+
+Vector<long>
+MultiParticleContainer::NumberOfParticlesInGrid(int lev) const
+{
+ const bool only_valid=true, only_local=true;
+ Vector<long> r = allcontainers[0]->NumberOfParticlesInGrid(lev,only_valid,only_local);
+ for (unsigned i = 1, n = allcontainers.size(); i < n; ++i) {
+ const auto& ri = allcontainers[i]->NumberOfParticlesInGrid(lev,only_valid,only_local);
+ for (unsigned j=0, m=ri.size(); j<m; ++j) {
+ r[j] += ri[j];
+ }
+ }
+ ParallelDescriptor::ReduceLongSum(r.data(),r.size());
+ return r;
+}
+
+void
+MultiParticleContainer::Increment (MultiFab& mf, int lev)
+{
+ for (auto& pc : allcontainers) {
+ pc->Increment(mf,lev);
+ }
+}
+
+void
+MultiParticleContainer::SetParticleBoxArray (int lev, BoxArray& new_ba)
+{
+ for (auto& pc : allcontainers) {
+ pc->SetParticleBoxArray(lev,new_ba);
+ }
+}
+
+void
+MultiParticleContainer::SetParticleDistributionMap (int lev, DistributionMapping& new_dm)
+{
+ for (auto& pc : allcontainers) {
+ pc->SetParticleDistributionMap(lev,new_dm);
+ }
+}
+
+void
+MultiParticleContainer::PostRestart ()
+{
+ for (auto& pc : allcontainers) {
+ pc->PostRestart();
+ }
+ pc_tmp->PostRestart();
+}
+
+void
+MultiParticleContainer
+::GetLabFrameData(const std::string& snapshot_name,
+ const int i_lab, const int direction,
+ const Real z_old, const Real z_new,
+ const Real t_boost, const Real t_lab, const Real dt,
+ Vector<WarpXParticleContainer::DiagnosticParticleData>& parts) const
+{
+
+ BL_PROFILE("MultiParticleContainer::GetLabFrameData");
+
+ for (int i = 0; i < nspecies; ++i)
+ {
+ WarpXParticleContainer* pc = allcontainers[i].get();
+ WarpXParticleContainer::DiagnosticParticles diagnostic_particles;
+ pc->GetParticleSlice(direction, z_old, z_new, t_boost, t_lab, dt, diagnostic_particles);
+
+ for (int lev = 0; lev <= pc->finestLevel(); ++lev)
+ {
+ for (auto it = diagnostic_particles[lev].begin(); it != diagnostic_particles[lev].end(); ++it)
+ {
+ parts[i].GetRealData(DiagIdx::w).insert( parts[i].GetRealData(DiagIdx::w ).end(),
+ it->second.GetRealData(DiagIdx::w ).begin(),
+ it->second.GetRealData(DiagIdx::w ).end());
+
+ parts[i].GetRealData(DiagIdx::x).insert( parts[i].GetRealData(DiagIdx::x ).end(),
+ it->second.GetRealData(DiagIdx::x ).begin(),
+ it->second.GetRealData(DiagIdx::x ).end());
+
+ parts[i].GetRealData(DiagIdx::y).insert( parts[i].GetRealData(DiagIdx::y ).end(),
+ it->second.GetRealData(DiagIdx::y ).begin(),
+ it->second.GetRealData(DiagIdx::y ).end());
+
+ parts[i].GetRealData(DiagIdx::z).insert( parts[i].GetRealData(DiagIdx::z ).end(),
+ it->second.GetRealData(DiagIdx::z ).begin(),
+ it->second.GetRealData(DiagIdx::z ).end());
+
+ parts[i].GetRealData(DiagIdx::ux).insert( parts[i].GetRealData(DiagIdx::ux).end(),
+ it->second.GetRealData(DiagIdx::ux).begin(),
+ it->second.GetRealData(DiagIdx::ux).end());
+
+ parts[i].GetRealData(DiagIdx::uy).insert( parts[i].GetRealData(DiagIdx::uy).end(),
+ it->second.GetRealData(DiagIdx::uy).begin(),
+ it->second.GetRealData(DiagIdx::uy).end());
+
+ parts[i].GetRealData(DiagIdx::uz).insert( parts[i].GetRealData(DiagIdx::uz).end(),
+ it->second.GetRealData(DiagIdx::uz).begin(),
+ it->second.GetRealData(DiagIdx::uz).end());
+ }
+ }
+ }
+}
diff --git a/Source/Particles/PhysicalParticleContainer.H b/Source/Particles/PhysicalParticleContainer.H
new file mode 100644
index 000000000..5847a6359
--- /dev/null
+++ b/Source/Particles/PhysicalParticleContainer.H
@@ -0,0 +1,127 @@
+#ifndef WARPX_PhysicalParticleContainer_H_
+#define WARPX_PhysicalParticleContainer_H_
+
+#include <map>
+
+#include <AMReX_IArrayBox.H>
+
+#include <PlasmaInjector.H>
+#include <WarpXParticleContainer.H>
+
+class PhysicalParticleContainer
+ : public WarpXParticleContainer
+{
+public:
+ PhysicalParticleContainer (amrex::AmrCore* amr_core,
+ int ispecies,
+ const std::string& name);
+
+ PhysicalParticleContainer (amrex::AmrCore* amr_core);
+
+ virtual ~PhysicalParticleContainer () {}
+
+ virtual void InitData () override;
+
+#ifdef WARPX_DO_ELECTROSTATIC
+ virtual void FieldGatherES(const amrex::Vector<std::array<std::unique_ptr<amrex::MultiFab>, 3> >& E,
+ const amrex::Vector<std::unique_ptr<amrex::FabArray<amrex::BaseFab<int> > > >& masks) override;
+
+ virtual void EvolveES (const amrex::Vector<std::array<std::unique_ptr<amrex::MultiFab>, 3> >& E,
+ amrex::Vector<std::unique_ptr<amrex::MultiFab> >& rho,
+ amrex::Real t, amrex::Real dt) override;
+#endif // WARPX_DO_ELECTROSTATIC
+
+ virtual void FieldGather(int lev,
+ const amrex::MultiFab& Ex,
+ const amrex::MultiFab& Ey,
+ const amrex::MultiFab& Ez,
+ const amrex::MultiFab& Bx,
+ const amrex::MultiFab& By,
+ const amrex::MultiFab& Bz) final;
+
+ virtual void Evolve (int lev,
+ const amrex::MultiFab& Ex,
+ const amrex::MultiFab& Ey,
+ const amrex::MultiFab& Ez,
+ const amrex::MultiFab& Bx,
+ const amrex::MultiFab& By,
+ const amrex::MultiFab& Bz,
+ amrex::MultiFab& jx,
+ amrex::MultiFab& jy,
+ amrex::MultiFab& jz,
+ amrex::MultiFab* cjx,
+ amrex::MultiFab* cjy,
+ amrex::MultiFab* cjz,
+ amrex::MultiFab* rho,
+ amrex::MultiFab* crho,
+ const amrex::MultiFab* cEx,
+ const amrex::MultiFab* cEy,
+ const amrex::MultiFab* cEz,
+ const amrex::MultiFab* cBx,
+ const amrex::MultiFab* cBy,
+ const amrex::MultiFab* cBz,
+ amrex::Real t,
+ amrex::Real dt) override;
+
+ virtual void PushPX(WarpXParIter& pti,
+ amrex::Cuda::DeviceVector<amrex::Real>& xp,
+ amrex::Cuda::DeviceVector<amrex::Real>& yp,
+ amrex::Cuda::DeviceVector<amrex::Real>& zp,
+ amrex::Cuda::DeviceVector<amrex::Real>& giv,
+ amrex::Real dt);
+
+ virtual void PushP (int lev, amrex::Real dt,
+ const amrex::MultiFab& Ex,
+ const amrex::MultiFab& Ey,
+ const amrex::MultiFab& Ez,
+ const amrex::MultiFab& Bx,
+ const amrex::MultiFab& By,
+ const amrex::MultiFab& Bz) override;
+
+ virtual void PostRestart () final {}
+
+ void SplitParticles(int lev);
+
+ // Inject particles in Box 'part_box'
+ virtual void AddParticles (int lev);
+ void AddPlasma(int lev, amrex::RealBox part_realbox = amrex::RealBox());
+ void AddPlasmaCPU (int lev, amrex::RealBox part_realbox);
+#ifdef AMREX_USE_GPU
+ void AddPlasmaGPU (int lev, amrex::RealBox part_realbox);
+#endif
+
+ void MapParticletoBoostedFrame(amrex::Real& x, amrex::Real& y, amrex::Real& z, std::array<amrex::Real, 3>& u);
+
+ void AddGaussianBeam(amrex::Real x_m, amrex::Real y_m, amrex::Real z_m,
+ amrex::Real x_rms, amrex::Real y_rms, amrex::Real z_rms,
+ amrex::Real q_tot, long npart);
+
+ virtual void GetParticleSlice(const int direction, const amrex::Real z_old,
+ const amrex::Real z_new, const amrex::Real t_boost,
+ const amrex::Real t_lab, const amrex::Real dt,
+ DiagnosticParticles& diagnostic_particles) final;
+
+ bool injected = false;
+
+protected:
+
+ std::string species_name;
+ std::unique_ptr<PlasmaInjector> plasma_injector;
+
+ // When true, adjust the transverse particle positions accounting
+ // for the difference between the Lorentz transformed time of the
+ // particle and the time of the boosted frame.
+ bool boost_adjust_transverse_positions = false;
+ bool do_backward_propagation = false;
+
+ long NumParticlesToAdd (const amrex::Box& overlap_box,
+ const amrex::RealBox& overlap_realbox,
+ const amrex::RealBox& tile_real_box,
+ const amrex::RealBox& particle_real_box);
+
+ int GetRefineFac(const amrex::Real x, const amrex::Real y, const amrex::Real z);
+ std::unique_ptr<amrex::IArrayBox> m_refined_injection_mask = nullptr;
+
+};
+
+#endif
diff --git a/Source/Particles/PhysicalParticleContainer.cpp b/Source/Particles/PhysicalParticleContainer.cpp
new file mode 100644
index 000000000..b534de916
--- /dev/null
+++ b/Source/Particles/PhysicalParticleContainer.cpp
@@ -0,0 +1,1941 @@
+#include <limits>
+#include <sstream>
+
+#include <MultiParticleContainer.H>
+#include <WarpX_f.H>
+#include <WarpX.H>
+#include <WarpXConst.H>
+#include <WarpXWrappers.h>
+
+
+using namespace amrex;
+
+long PhysicalParticleContainer::
+NumParticlesToAdd(const Box& overlap_box, const RealBox& overlap_realbox,
+ const RealBox& tile_realbox, const RealBox& particle_real_box)
+{
+ const int lev = 0;
+ const Geometry& geom = Geom(lev);
+ int num_ppc = plasma_injector->num_particles_per_cell;
+ const Real* dx = geom.CellSize();
+
+ long np = 0;
+ const auto& overlap_corner = overlap_realbox.lo();
+ for (IntVect iv = overlap_box.smallEnd(); iv <= overlap_box.bigEnd(); overlap_box.next(iv))
+ {
+ int fac;
+ if (injected) {
+#if ( AMREX_SPACEDIM == 3 )
+ Real x = overlap_corner[0] + (iv[0] + 0.5)*dx[0];
+ Real y = overlap_corner[1] + (iv[1] + 0.5)*dx[1];
+ Real z = overlap_corner[2] + (iv[2] + 0.5)*dx[2];
+#elif ( AMREX_SPACEDIM == 2 )
+ Real x = overlap_corner[0] + (iv[0] + 0.5)*dx[0];
+ Real y = 0;
+ Real z = overlap_corner[1] + (iv[1] + 0.5)*dx[1];
+#endif
+ fac = GetRefineFac(x, y, z);
+ } else {
+ fac = 1.0;
+ }
+
+ int ref_num_ppc = num_ppc * AMREX_D_TERM(fac, *fac, *fac);
+ for (int i_part=0; i_part<ref_num_ppc;i_part++) {
+ std::array<Real, 3> r;
+ plasma_injector->getPositionUnitBox(r, i_part, fac);
+#if ( AMREX_SPACEDIM == 3 )
+ Real x = overlap_corner[0] + (iv[0] + r[0])*dx[0];
+ Real y = overlap_corner[1] + (iv[1] + r[1])*dx[1];
+ Real z = overlap_corner[2] + (iv[2] + r[2])*dx[2];
+#elif ( AMREX_SPACEDIM == 2 )
+ Real x = overlap_corner[0] + (iv[0] + r[0])*dx[0];
+ Real y = 0;
+ Real z = overlap_corner[1] + (iv[1] + r[1])*dx[1];
+#endif
+ // If the new particle is not inside the tile box,
+ // go to the next generated particle.
+#if ( AMREX_SPACEDIM == 3 )
+ if(!tile_realbox.contains( RealVect{x, y, z} )) continue;
+#elif ( AMREX_SPACEDIM == 2 )
+ if(!tile_realbox.contains( RealVect{x, z} )) continue;
+#endif
+ ++np;
+ }
+ }
+
+ return np;
+}
+
+PhysicalParticleContainer::PhysicalParticleContainer (AmrCore* amr_core, int ispecies,
+ const std::string& name)
+ : WarpXParticleContainer(amr_core, ispecies),
+ species_name(name)
+{
+ plasma_injector.reset(new PlasmaInjector(species_id, species_name));
+ charge = plasma_injector->getCharge();
+ mass = plasma_injector->getMass();
+
+ ParmParse pp(species_name);
+
+ pp.query("boost_adjust_transverse_positions", boost_adjust_transverse_positions);
+ pp.query("do_backward_propagation", do_backward_propagation);
+ pp.query("do_splitting", do_splitting);
+ pp.query("split_type", split_type);
+}
+
+PhysicalParticleContainer::PhysicalParticleContainer (AmrCore* amr_core)
+ : WarpXParticleContainer(amr_core, 0)
+{
+ plasma_injector.reset(new PlasmaInjector());
+}
+
+void PhysicalParticleContainer::InitData()
+{
+ AddParticles(0); // Note - add on level 0
+ if (maxLevel() > 0) {
+ Redistribute(); // We then redistribute
+ }
+}
+
+void PhysicalParticleContainer::MapParticletoBoostedFrame(Real& x, Real& y, Real& z, std::array<Real, 3>& u)
+{
+ // Map the particles from the lab frame to the boosted frame.
+ // This boosts the particle to the lab frame and calculates
+ // the particle time in the boosted frame. It then maps
+ // the position to the time in the boosted frame.
+
+ // For now, start with the assumption that this will only happen
+ // at the start of the simulation.
+ const Real t_lab = 0.;
+
+ const Real uz_boost = WarpX::gamma_boost*WarpX::beta_boost*PhysConst::c;
+
+ // tpr is the particle's time in the boosted frame
+ Real tpr = WarpX::gamma_boost*t_lab - uz_boost*z/(PhysConst::c*PhysConst::c);
+
+ // The particle's transformed location in the boosted frame
+ Real xpr = x;
+ Real ypr = y;
+ Real zpr = WarpX::gamma_boost*z - uz_boost*t_lab;
+
+ // transform u and gamma to the boosted frame
+ Real gamma_lab = std::sqrt(1. + (u[0]*u[0] + u[1]*u[1] + u[2]*u[2])/(PhysConst::c*PhysConst::c));
+ // u[0] = u[0];
+ // u[1] = u[1];
+ u[2] = WarpX::gamma_boost*u[2] - uz_boost*gamma_lab;
+ Real gammapr = std::sqrt(1. + (u[0]*u[0] + u[1]*u[1] + u[2]*u[2])/(PhysConst::c*PhysConst::c));
+
+ Real vxpr = u[0]/gammapr;
+ Real vypr = u[1]/gammapr;
+ Real vzpr = u[2]/gammapr;
+
+ if (do_backward_propagation){
+ u[2] = -u[2];
+ }
+
+ // Move the particles to where they will be at t = 0 in the boosted frame
+ if (boost_adjust_transverse_positions) {
+ x = xpr - tpr*vxpr;
+ y = ypr - tpr*vypr;
+ }
+
+ z = zpr - tpr*vzpr;
+
+}
+
+void
+PhysicalParticleContainer::AddGaussianBeam(Real x_m, Real y_m, Real z_m,
+ Real x_rms, Real y_rms, Real z_rms,
+ Real q_tot, long npart) {
+
+ const Geometry& geom = m_gdb->Geom(0);
+ RealBox containing_bx = geom.ProbDomain();
+
+ std::mt19937_64 mt(0451);
+ std::normal_distribution<double> distx(x_m, x_rms);
+ std::normal_distribution<double> disty(y_m, y_rms);
+ std::normal_distribution<double> distz(z_m, z_rms);
+
+ std::array<Real,PIdx::nattribs> attribs;
+ attribs.fill(0.0);
+
+ if (ParallelDescriptor::IOProcessor()) {
+ std::array<Real, 3> u;
+ Real weight;
+ for (long i = 0; i < npart; ++i) {
+#if ( AMREX_SPACEDIM == 3 | WARPX_RZ)
+ weight = q_tot/npart/charge;
+ Real x = distx(mt);
+ Real y = disty(mt);
+ Real z = distz(mt);
+#elif ( AMREX_SPACEDIM == 2 )
+ weight = q_tot/npart/charge/y_rms;
+ Real x = distx(mt);
+ Real y = 0.;
+ Real z = distz(mt);
+#endif
+ if (plasma_injector->insideBounds(x, y, z)) {
+ plasma_injector->getMomentum(u, x, y, z);
+ if (WarpX::gamma_boost > 1.) {
+ MapParticletoBoostedFrame(x, y, z, u);
+ }
+ attribs[PIdx::ux] = u[0];
+ attribs[PIdx::uy] = u[1];
+ attribs[PIdx::uz] = u[2];
+ attribs[PIdx::w ] = weight;
+
+ AddOneParticle(0, 0, 0, x, y, z, attribs);
+ }
+ }
+ }
+ Redistribute();
+}
+
+void
+PhysicalParticleContainer::AddParticles (int lev)
+{
+ BL_PROFILE("PhysicalParticleContainer::AddParticles()");
+
+ if (plasma_injector->add_single_particle) {
+ AddNParticles(lev, 1,
+ &(plasma_injector->single_particle_pos[0]),
+ &(plasma_injector->single_particle_pos[1]),
+ &(plasma_injector->single_particle_pos[2]),
+ &(plasma_injector->single_particle_vel[0]),
+ &(plasma_injector->single_particle_vel[1]),
+ &(plasma_injector->single_particle_vel[2]),
+ 1, &(plasma_injector->single_particle_weight), 0);
+ return;
+ }
+
+ if (plasma_injector->gaussian_beam) {
+ AddGaussianBeam(plasma_injector->x_m,
+ plasma_injector->y_m,
+ plasma_injector->z_m,
+ plasma_injector->x_rms,
+ plasma_injector->y_rms,
+ plasma_injector->z_rms,
+ plasma_injector->q_tot,
+ plasma_injector->npart);
+
+
+ return;
+ }
+
+ if ( plasma_injector->doInjection() ) {
+ AddPlasma( lev );
+ }
+}
+
+/**
+ * Create new macroparticles for this species, with a fixed
+ * number of particles per cell (in the cells of `part_realbox`).
+ * The new particles are only created inside the intersection of `part_realbox`
+ * with the local grid for the current proc.
+ * @param lev the index of the refinement level
+ * @param part_realbox the box in which new particles should be created
+ * (this box should correspond to an integer number of cells in each direction,
+ * but its boundaries need not be aligned with the actual cells of the simulation)
+ */
+void
+PhysicalParticleContainer::AddPlasma (int lev, RealBox part_realbox)
+{
+#ifdef AMREX_USE_GPU
+ AddPlasmaGPU(lev, part_realbox);
+#else
+ AddPlasmaCPU(lev, part_realbox);
+#endif
+}
+
+void
+PhysicalParticleContainer::AddPlasmaCPU (int lev, RealBox part_realbox)
+{
+ BL_PROFILE("PhysicalParticleContainer::AddPlasmaCPU");
+
+ // If no part_realbox is provided, initialize particles in the whole domain
+ const Geometry& geom = Geom(lev);
+ if (!part_realbox.ok()) part_realbox = geom.ProbDomain();
+
+ int num_ppc = plasma_injector->num_particles_per_cell;
+
+ const Real* dx = geom.CellSize();
+
+ Real scale_fac;
+#if AMREX_SPACEDIM==3
+ scale_fac = dx[0]*dx[1]*dx[2]/num_ppc;
+#elif AMREX_SPACEDIM==2
+ scale_fac = dx[0]*dx[1]/num_ppc;
+#endif
+
+#ifdef _OPENMP
+ // First touch all tiles in the map in serial
+ for (MFIter mfi = MakeMFIter(lev); mfi.isValid(); ++mfi) {
+ const int grid_id = mfi.index();
+ const int tile_id = mfi.LocalTileIndex();
+ GetParticles(lev)[std::make_pair(grid_id, tile_id)];
+ }
+#endif
+
+ MultiFab* cost = WarpX::getCosts(lev);
+
+ if ( (not m_refined_injection_mask) and WarpX::do_moving_window)
+ {
+ Box mask_box = geom.Domain();
+ mask_box.setSmall(WarpX::moving_window_dir, 0);
+ mask_box.setBig(WarpX::moving_window_dir, 0);
+ m_refined_injection_mask.reset( new IArrayBox(mask_box));
+ m_refined_injection_mask->setVal(-1);
+ }
+
+ MFItInfo info;
+ if (do_tiling) {
+ info.EnableTiling(tile_size);
+ }
+ info.SetDynamic(true);
+
+#ifdef _OPENMP
+#pragma omp parallel if (not WarpX::serialize_ics)
+#endif
+ {
+ std::array<Real,PIdx::nattribs> attribs;
+ attribs.fill(0.0);
+
+ // Loop through the tiles
+ for (MFIter mfi = MakeMFIter(lev, info); mfi.isValid(); ++mfi) {
+
+ Real wt = amrex::second();
+
+ const Box& tile_box = mfi.tilebox();
+ const RealBox tile_realbox = WarpX::getRealBox(tile_box, lev);
+
+ // Find the cells of part_box that overlap with tile_realbox
+ // If there is no overlap, just go to the next tile in the loop
+ RealBox overlap_realbox;
+ Box overlap_box;
+ Real ncells_adjust;
+ bool no_overlap = 0;
+
+ for (int dir=0; dir<AMREX_SPACEDIM; dir++) {
+ if ( tile_realbox.lo(dir) <= part_realbox.hi(dir) ) {
+ ncells_adjust = std::floor( (tile_realbox.lo(dir) - part_realbox.lo(dir))/dx[dir] );
+ overlap_realbox.setLo( dir, part_realbox.lo(dir) + std::max(ncells_adjust, 0.) * dx[dir]);
+ } else {
+ no_overlap = 1; break;
+ }
+ if ( tile_realbox.hi(dir) >= part_realbox.lo(dir) ) {
+ ncells_adjust = std::floor( (part_realbox.hi(dir) - tile_realbox.hi(dir))/dx[dir] );
+ overlap_realbox.setHi( dir, part_realbox.hi(dir) - std::max(ncells_adjust, 0.) * dx[dir]);
+ } else {
+ no_overlap = 1; break;
+ }
+ // Count the number of cells in this direction in overlap_realbox
+ overlap_box.setSmall( dir, 0 );
+ overlap_box.setBig( dir,
+ int( round((overlap_realbox.hi(dir)-overlap_realbox.lo(dir))/dx[dir] )) - 1);
+ }
+ if (no_overlap == 1) {
+ continue; // Go to the next tile
+ }
+
+ const int grid_id = mfi.index();
+ const int tile_id = mfi.LocalTileIndex();
+
+ // Loop through the cells of overlap_box and inject
+ // the corresponding particles
+ const auto& overlap_corner = overlap_realbox.lo();
+ for (IntVect iv = overlap_box.smallEnd(); iv <= overlap_box.bigEnd(); overlap_box.next(iv))
+ {
+ int fac;
+ if (injected) {
+#if ( AMREX_SPACEDIM == 3 )
+ Real x = overlap_corner[0] + (iv[0] + 0.5)*dx[0];
+ Real y = overlap_corner[1] + (iv[1] + 0.5)*dx[1];
+ Real z = overlap_corner[2] + (iv[2] + 0.5)*dx[2];
+#elif ( AMREX_SPACEDIM == 2 )
+ Real x = overlap_corner[0] + (iv[0] + 0.5)*dx[0];
+ Real y = 0;
+ Real z = overlap_corner[1] + (iv[1] + 0.5)*dx[1];
+#endif
+ fac = GetRefineFac(x, y, z);
+ } else {
+ fac = 1.0;
+ }
+
+ int ref_num_ppc = num_ppc * AMREX_D_TERM(fac, *fac, *fac);
+ for (int i_part=0; i_part<ref_num_ppc;i_part++) {
+ std::array<Real, 3> r;
+ plasma_injector->getPositionUnitBox(r, i_part, fac);
+#if ( AMREX_SPACEDIM == 3 )
+ Real x = overlap_corner[0] + (iv[0] + r[0])*dx[0];
+ Real y = overlap_corner[1] + (iv[1] + r[1])*dx[1];
+ Real z = overlap_corner[2] + (iv[2] + r[2])*dx[2];
+#elif ( AMREX_SPACEDIM == 2 )
+ Real x = overlap_corner[0] + (iv[0] + r[0])*dx[0];
+ Real y = 0;
+ Real z = overlap_corner[1] + (iv[1] + r[1])*dx[1];
+#endif
+ // If the new particle is not inside the tile box,
+ // go to the next generated particle.
+#if ( AMREX_SPACEDIM == 3 )
+ if(!tile_realbox.contains( RealVect{x, y, z} )) continue;
+#elif ( AMREX_SPACEDIM == 2 )
+ if(!tile_realbox.contains( RealVect{x, z} )) continue;
+#endif
+
+ Real dens;
+ std::array<Real, 3> u;
+ if (WarpX::gamma_boost == 1.){
+ // Lab-frame simulation
+ // If the particle is not within the species's
+ // xmin, xmax, ymin, ymax, zmin, zmax, go to
+ // the next generated particle.
+ if (!plasma_injector->insideBounds(x, y, z)) continue;
+ plasma_injector->getMomentum(u, x, y, z);
+ dens = plasma_injector->getDensity(x, y, z);
+ } else {
+ // Boosted-frame simulation
+ Real c = PhysConst::c;
+ Real gamma_boost = WarpX::gamma_boost;
+ Real beta_boost = WarpX::beta_boost;
+ // Since the user provides the density distribution
+ // at t_lab=0 and in the lab-frame coordinates,
+ // we need to find the lab-frame position of this
+ // particle at t_lab=0, from its boosted-frame coordinates
+ // Assuming ballistic motion, this is given by:
+ // z0_lab = gamma*( z_boost*(1-beta*betaz_lab) - ct_boost*(betaz_lab-beta) )
+ // where betaz_lab is the speed of the particle in the lab frame
+ //
+ // In order for this equation to be solvable, betaz_lab
+ // is explicitly assumed to have no dependency on z0_lab
+ plasma_injector->getMomentum(u, x, y, 0.); // No z0_lab dependency
+ // At this point u is the lab-frame momentum
+ // => Apply the above formula for z0_lab
+ Real gamma_lab = std::sqrt( 1 + (u[0]*u[0] + u[1]*u[1] + u[2]*u[2])/(c*c) );
+ Real betaz_lab = u[2]/gamma_lab/c;
+ Real t = WarpX::GetInstance().gett_new(lev);
+ Real z0_lab = gamma_boost * ( z*(1-beta_boost*betaz_lab) - c*t*(betaz_lab-beta_boost) );
+ // If the particle is not within the lab-frame zmin, zmax, etc.
+ // go to the next generated particle.
+ if (!plasma_injector->insideBounds(x, y, z0_lab)) continue;
+ // call `getDensity` with lab-frame parameters
+ dens = plasma_injector->getDensity(x, y, z0_lab);
+ // At this point u and dens are the lab-frame quantities
+ // => Perform Lorentz transform
+ dens = gamma_boost * dens * ( 1 - beta_boost*betaz_lab );
+ u[2] = gamma_boost * ( u[2] -beta_boost*c*gamma_lab );
+ }
+ attribs[PIdx::w ] = dens * scale_fac / (AMREX_D_TERM(fac, *fac, *fac));
+ attribs[PIdx::ux] = u[0];
+ attribs[PIdx::uy] = u[1];
+ attribs[PIdx::uz] = u[2];
+
+#ifdef WARPX_STORE_OLD_PARTICLE_ATTRIBS
+ attribs[PIdx::xold] = x;
+ attribs[PIdx::yold] = y;
+ attribs[PIdx::zold] = z;
+
+ attribs[PIdx::uxold] = u[0];
+ attribs[PIdx::uyold] = u[1];
+ attribs[PIdx::uzold] = u[2];
+#endif
+
+ AddOneParticle(lev, grid_id, tile_id, x, y, z, attribs);
+ }
+ }
+
+ if (cost) {
+ wt = (amrex::second() - wt) / tile_box.d_numPts();
+ FArrayBox* costfab = cost->fabPtr(mfi);
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA ( tile_box, work_box,
+ {
+ costfab->plus(wt, work_box);
+ });
+ }
+ }
+ }
+}
+
+#ifdef AMREX_USE_GPU
+void
+PhysicalParticleContainer::AddPlasmaGPU (int lev, RealBox part_realbox)
+{
+ BL_PROFILE("PhysicalParticleContainer::AddPlasmaGPU");
+
+ // If no part_realbox is provided, initialize particles in the whole domain
+ const Geometry& geom = Geom(lev);
+ if (!part_realbox.ok()) part_realbox = geom.ProbDomain();
+
+ int num_ppc = plasma_injector->num_particles_per_cell;
+
+ const Real* dx = geom.CellSize();
+
+ Real scale_fac;
+#if AMREX_SPACEDIM==3
+ scale_fac = dx[0]*dx[1]*dx[2]/num_ppc;
+#elif AMREX_SPACEDIM==2
+ scale_fac = dx[0]*dx[1]/num_ppc;
+#endif
+
+#ifdef _OPENMP
+ // First touch all tiles in the map in serial
+ for (MFIter mfi = MakeMFIter(lev); mfi.isValid(); ++mfi) {
+ const int grid_id = mfi.index();
+ const int tile_id = mfi.LocalTileIndex();
+ GetParticles(lev)[std::make_pair(grid_id, tile_id)];
+ }
+#endif
+
+ MultiFab* cost = WarpX::getCosts(lev);
+
+ if ( (not m_refined_injection_mask) and WarpX::do_moving_window)
+ {
+ Box mask_box = geom.Domain();
+ mask_box.setSmall(WarpX::moving_window_dir, 0);
+ mask_box.setBig(WarpX::moving_window_dir, 0);
+ m_refined_injection_mask.reset( new IArrayBox(mask_box));
+ m_refined_injection_mask->setVal(-1);
+ }
+
+ MFItInfo info;
+ if (do_tiling) {
+ info.EnableTiling(tile_size);
+ }
+ info.SetDynamic(true);
+
+#ifdef _OPENMP
+#pragma omp parallel if (not WarpX::serialize_ics)
+#endif
+ {
+ std::array<Real,PIdx::nattribs> attribs;
+ attribs.fill(0.0);
+
+ // Loop through the tiles
+ for (MFIter mfi = MakeMFIter(lev, info); mfi.isValid(); ++mfi) {
+
+ Real wt = amrex::second();
+
+ const Box& tile_box = mfi.tilebox();
+ const RealBox tile_realbox = WarpX::getRealBox(tile_box, lev);
+
+ // Find the cells of part_box that overlap with tile_realbox
+ // If there is no overlap, just go to the next tile in the loop
+ RealBox overlap_realbox;
+ Box overlap_box;
+ Real ncells_adjust;
+ bool no_overlap = 0;
+
+ for (int dir=0; dir<AMREX_SPACEDIM; dir++) {
+ if ( tile_realbox.lo(dir) <= part_realbox.hi(dir) ) {
+ ncells_adjust = std::floor( (tile_realbox.lo(dir) - part_realbox.lo(dir))/dx[dir] );
+ overlap_realbox.setLo( dir, part_realbox.lo(dir) + std::max(ncells_adjust, 0.) * dx[dir]);
+ } else {
+ no_overlap = 1; break;
+ }
+ if ( tile_realbox.hi(dir) >= part_realbox.lo(dir) ) {
+ ncells_adjust = std::floor( (part_realbox.hi(dir) - tile_realbox.hi(dir))/dx[dir] );
+ overlap_realbox.setHi( dir, part_realbox.hi(dir) - std::max(ncells_adjust, 0.) * dx[dir]);
+ } else {
+ no_overlap = 1; break;
+ }
+ // Count the number of cells in this direction in overlap_realbox
+ overlap_box.setSmall( dir, 0 );
+ overlap_box.setBig( dir,
+ int( round((overlap_realbox.hi(dir)-overlap_realbox.lo(dir))/dx[dir] )) - 1);
+ }
+ if (no_overlap == 1) {
+ continue; // Go to the next tile
+ }
+
+ const int grid_id = mfi.index();
+ const int tile_id = mfi.LocalTileIndex();
+
+ Cuda::HostVector<ParticleType> host_particles;
+ std::array<Cuda::HostVector<Real>, PIdx::nattribs> host_attribs;
+
+ // Loop through the cells of overlap_box and inject
+ // the corresponding particles
+ const auto& overlap_corner = overlap_realbox.lo();
+ for (IntVect iv = overlap_box.smallEnd(); iv <= overlap_box.bigEnd(); overlap_box.next(iv))
+ {
+ int fac;
+ if (injected) {
+#if ( AMREX_SPACEDIM == 3 )
+ Real x = overlap_corner[0] + (iv[0] + 0.5)*dx[0];
+ Real y = overlap_corner[1] + (iv[1] + 0.5)*dx[1];
+ Real z = overlap_corner[2] + (iv[2] + 0.5)*dx[2];
+#elif ( AMREX_SPACEDIM == 2 )
+ Real x = overlap_corner[0] + (iv[0] + 0.5)*dx[0];
+ Real y = 0;
+ Real z = overlap_corner[1] + (iv[1] + 0.5)*dx[1];
+#endif
+ fac = GetRefineFac(x, y, z);
+ } else {
+ fac = 1.0;
+ }
+
+ int ref_num_ppc = num_ppc * AMREX_D_TERM(fac, *fac, *fac);
+ for (int i_part=0; i_part<ref_num_ppc;i_part++) {
+ std::array<Real, 3> r;
+ plasma_injector->getPositionUnitBox(r, i_part, fac);
+#if ( AMREX_SPACEDIM == 3 )
+ Real x = overlap_corner[0] + (iv[0] + r[0])*dx[0];
+ Real y = overlap_corner[1] + (iv[1] + r[1])*dx[1];
+ Real z = overlap_corner[2] + (iv[2] + r[2])*dx[2];
+#elif ( AMREX_SPACEDIM == 2 )
+ Real x = overlap_corner[0] + (iv[0] + r[0])*dx[0];
+ Real y = 0;
+ Real z = overlap_corner[1] + (iv[1] + r[1])*dx[1];
+#endif
+ // If the new particle is not inside the tile box,
+ // go to the next generated particle.
+#if ( AMREX_SPACEDIM == 3 )
+ if(!tile_realbox.contains( RealVect{x, y, z} )) continue;
+#elif ( AMREX_SPACEDIM == 2 )
+ if(!tile_realbox.contains( RealVect{x, z} )) continue;
+#endif
+
+ Real dens;
+ std::array<Real, 3> u;
+ if (WarpX::gamma_boost == 1.){
+ // Lab-frame simulation
+ // If the particle is not within the species's
+ // xmin, xmax, ymin, ymax, zmin, zmax, go to
+ // the next generated particle.
+ if (!plasma_injector->insideBounds(x, y, z)) continue;
+ plasma_injector->getMomentum(u, x, y, z);
+ dens = plasma_injector->getDensity(x, y, z);
+ } else {
+ // Boosted-frame simulation
+ Real c = PhysConst::c;
+ Real gamma_boost = WarpX::gamma_boost;
+ Real beta_boost = WarpX::beta_boost;
+ // Since the user provides the density distribution
+ // at t_lab=0 and in the lab-frame coordinates,
+ // we need to find the lab-frame position of this
+ // particle at t_lab=0, from its boosted-frame coordinates
+ // Assuming ballistic motion, this is given by:
+ // z0_lab = gamma*( z_boost*(1-beta*betaz_lab) - ct_boost*(betaz_lab-beta) )
+ // where betaz_lab is the speed of the particle in the lab frame
+ //
+ // In order for this equation to be solvable, betaz_lab
+ // is explicitly assumed to have no dependency on z0_lab
+ plasma_injector->getMomentum(u, x, y, 0.); // No z0_lab dependency
+ // At this point u is the lab-frame momentum
+ // => Apply the above formula for z0_lab
+ Real gamma_lab = std::sqrt( 1 + (u[0]*u[0] + u[1]*u[1] + u[2]*u[2])/(c*c) );
+ Real betaz_lab = u[2]/gamma_lab/c;
+ Real t = WarpX::GetInstance().gett_new(lev);
+ Real z0_lab = gamma_boost * ( z*(1-beta_boost*betaz_lab) - c*t*(betaz_lab-beta_boost) );
+ // If the particle is not within the lab-frame zmin, zmax, etc.
+ // go to the next generated particle.
+ if (!plasma_injector->insideBounds(x, y, z0_lab)) continue;
+ // call `getDensity` with lab-frame parameters
+ dens = plasma_injector->getDensity(x, y, z0_lab);
+ // At this point u and dens are the lab-frame quantities
+ // => Perform Lorentz transform
+ dens = gamma_boost * dens * ( 1 - beta_boost*betaz_lab );
+ u[2] = gamma_boost * ( u[2] -beta_boost*c*gamma_lab );
+ }
+ attribs[PIdx::w ] = dens * scale_fac / (AMREX_D_TERM(fac, *fac, *fac));
+ attribs[PIdx::ux] = u[0];
+ attribs[PIdx::uy] = u[1];
+ attribs[PIdx::uz] = u[2];
+
+#ifdef WARPX_STORE_OLD_PARTICLE_ATTRIBS
+ attribs[PIdx::xold] = x;
+ attribs[PIdx::yold] = y;
+ attribs[PIdx::zold] = z;
+
+ attribs[PIdx::uxold] = u[0];
+ attribs[PIdx::uyold] = u[1];
+ attribs[PIdx::uzold] = u[2];
+#endif
+
+ ParticleType p;
+ p.id() = ParticleType::NextID();
+ p.cpu() = ParallelDescriptor::MyProc();
+#if (AMREX_SPACEDIM == 3)
+ p.pos(0) = x;
+ p.pos(1) = y;
+ p.pos(2) = z;
+#elif (AMREX_SPACEDIM == 2)
+ p.pos(0) = x;
+ p.pos(1) = z;
+#endif
+
+ host_particles.push_back(p);
+ for (int kk = 0; kk < PIdx::nattribs; ++kk)
+ host_attribs[kk].push_back(attribs[kk]);
+ }
+ }
+
+ auto& particle_tile = GetParticles(lev)[std::make_pair(grid_id,tile_id)];
+ auto old_size = particle_tile.GetArrayOfStructs().size();
+ auto new_size = old_size + host_particles.size();
+ particle_tile.resize(new_size);
+
+ Cuda::thrust_copy(host_particles.begin(),
+ host_particles.end(),
+ particle_tile.GetArrayOfStructs().begin() + old_size);
+
+ for (int kk = 0; kk < PIdx::nattribs; ++kk) {
+ Cuda::thrust_copy(host_attribs[kk].begin(),
+ host_attribs[kk].end(),
+ particle_tile.GetStructOfArrays().GetRealData(kk).begin() + old_size);
+ }
+
+ if (cost) {
+ wt = (amrex::second() - wt) / tile_box.d_numPts();
+ FArrayBox* costfab = cost->fabPtr(mfi);
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA ( tile_box, work_box,
+ {
+ costfab->plus(wt, work_box);
+ });
+ }
+ }
+ }
+}
+#endif
+
+#ifdef WARPX_DO_ELECTROSTATIC
+void
+PhysicalParticleContainer::
+FieldGatherES (const amrex::Vector<std::array<std::unique_ptr<amrex::MultiFab>, 3> >& E,
+ const amrex::Vector<std::unique_ptr<amrex::FabArray<amrex::BaseFab<int> > > >& masks)
+{
+
+ const int num_levels = E.size();
+ const int ng = E[0][0]->nGrow();
+
+ if (num_levels == 1) {
+ const int lev = 0;
+ const auto& gm = m_gdb->Geom(lev);
+ const auto& ba = m_gdb->ParticleBoxArray(lev);
+
+ BoxArray nba = ba;
+ nba.surroundingNodes();
+
+ const Real* dx = gm.CellSize();
+ const Real* plo = gm.ProbLo();
+
+ BL_ASSERT(OnSameGrids(lev, *E[lev][0]));
+
+ for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti) {
+ const Box& box = nba[pti];
+
+ const auto& particles = pti.GetArrayOfStructs();
+ int nstride = particles.dataShape().first;
+ const long np = pti.numParticles();
+
+ auto& attribs = pti.GetAttribs();
+ auto& Exp = attribs[PIdx::Ex];
+ auto& Eyp = attribs[PIdx::Ey];
+#if AMREX_SPACEDIM == 3
+ auto& Ezp = attribs[PIdx::Ez];
+#endif
+ Exp.assign(np,0.0);
+ Eyp.assign(np,0.0);
+#if AMREX_SPACEDIM == 3
+ Ezp.assign(np,0.0);
+#endif
+
+ const FArrayBox& exfab = (*E[lev][0])[pti];
+ const FArrayBox& eyfab = (*E[lev][1])[pti];
+#if AMREX_SPACEDIM == 3
+ const FArrayBox& ezfab = (*E[lev][2])[pti];
+#endif
+
+ WRPX_INTERPOLATE_CIC(particles.dataPtr(), nstride, np,
+ Exp.dataPtr(), Eyp.dataPtr(),
+#if AMREX_SPACEDIM == 3
+ Ezp.dataPtr(),
+#endif
+ exfab.dataPtr(), eyfab.dataPtr(),
+#if AMREX_SPACEDIM == 3
+ ezfab.dataPtr(),
+#endif
+ box.loVect(), box.hiVect(), plo, dx, &ng);
+ }
+
+ return;
+ }
+
+ const BoxArray& fine_BA = E[1][0]->boxArray();
+ const DistributionMapping& fine_dm = E[1][0]->DistributionMap();
+ BoxArray coarsened_fine_BA = fine_BA;
+ coarsened_fine_BA.coarsen(IntVect(AMREX_D_DECL(2,2,2)));
+
+ MultiFab coarse_Ex(coarsened_fine_BA, fine_dm, 1, 1);
+ MultiFab coarse_Ey(coarsened_fine_BA, fine_dm, 1, 1);
+#if AMREX_SPACEDIM == 3
+ MultiFab coarse_Ez(coarsened_fine_BA, fine_dm, 1, 1);
+#endif
+
+ coarse_Ex.copy(*E[0][0], 0, 0, 1, 1, 1);
+ coarse_Ey.copy(*E[0][1], 0, 0, 1, 1, 1);
+#if AMREX_SPACEDIM == 3
+ coarse_Ez.copy(*E[0][2], 0, 0, 1, 1, 1);
+#endif
+
+ for (int lev = 0; lev < num_levels; ++lev) {
+ const auto& gm = m_gdb->Geom(lev);
+ const auto& ba = m_gdb->ParticleBoxArray(lev);
+
+ BoxArray nba = ba;
+ nba.surroundingNodes();
+
+ const Real* dx = gm.CellSize();
+ const Real* plo = gm.ProbLo();
+
+ BL_ASSERT(OnSameGrids(lev, *E[lev][0]));
+
+ for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti) {
+ const Box& box = nba[pti];
+
+ const auto& particles = pti.GetArrayOfStructs();
+ int nstride = particles.dataShape().first;
+ const long np = pti.numParticles();
+
+ auto& attribs = pti.GetAttribs();
+ auto& Exp = attribs[PIdx::Ex];
+ auto& Eyp = attribs[PIdx::Ey];
+#if AMREX_SPACEDIM == 3
+ auto& Ezp = attribs[PIdx::Ez];
+#endif
+ Exp.assign(np,0.0);
+ Eyp.assign(np,0.0);
+#if AMREX_SPACEDIM == 3
+ Ezp.assign(np,0.0);
+#endif
+
+ const FArrayBox& exfab = (*E[lev][0])[pti];
+ const FArrayBox& eyfab = (*E[lev][1])[pti];
+#if AMREX_SPACEDIM == 3
+ const FArrayBox& ezfab = (*E[lev][2])[pti];
+#endif
+
+ if (lev == 0) {
+ WRPX_INTERPOLATE_CIC(particles.dataPtr(), nstride, np,
+ Exp.dataPtr(), Eyp.dataPtr(),
+#if AMREX_SPACEDIM == 3
+ Ezp.dataPtr(),
+#endif
+ exfab.dataPtr(), eyfab.dataPtr(),
+#if AMREX_SPACEDIM == 3
+ ezfab.dataPtr(),
+#endif
+ box.loVect(), box.hiVect(), plo, dx, &ng);
+ } else {
+
+ const FArrayBox& exfab_coarse = coarse_Ex[pti];
+ const FArrayBox& eyfab_coarse = coarse_Ey[pti];
+#if AMREX_SPACEDIM == 3
+ const FArrayBox& ezfab_coarse = coarse_Ez[pti];
+#endif
+ const Box& coarse_box = coarsened_fine_BA[pti];
+ const Real* coarse_dx = Geom(0).CellSize();
+
+ WRPX_INTERPOLATE_CIC_TWO_LEVELS(particles.dataPtr(), nstride, np,
+ Exp.dataPtr(), Eyp.dataPtr(),
+#if AMREX_SPACEDIM == 3
+ Ezp.dataPtr(),
+#endif
+ exfab.dataPtr(), eyfab.dataPtr(),
+#if AMREX_SPACEDIM == 3
+ ezfab.dataPtr(),
+#endif
+ box.loVect(), box.hiVect(), dx,
+ exfab_coarse.dataPtr(), eyfab_coarse.dataPtr(),
+#if AMREX_SPACEDIM == 3
+ ezfab_coarse.dataPtr(),
+#endif
+ (*masks[1])[pti].dataPtr(),
+ coarse_box.loVect(), coarse_box.hiVect(), coarse_dx,
+ plo, &ng, &lev);
+ }
+ }
+ }
+}
+
+void
+PhysicalParticleContainer::EvolveES (const Vector<std::array<std::unique_ptr<MultiFab>, 3> >& E,
+ Vector<std::unique_ptr<MultiFab> >& rho,
+ Real t, Real dt)
+{
+ BL_PROFILE("PPC::EvolveES()");
+
+ int num_levels = rho.size();
+ for (int lev = 0; lev < num_levels; ++lev) {
+ BL_ASSERT(OnSameGrids(lev, *rho[lev]));
+ const auto& gm = m_gdb->Geom(lev);
+ const RealBox& prob_domain = gm.ProbDomain();
+ for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti) {
+ // Particle structs
+ auto& particles = pti.GetArrayOfStructs();
+ int nstride = particles.dataShape().first;
+ const long np = pti.numParticles();
+
+ // Particle attributes
+ auto& attribs = pti.GetAttribs();
+ auto& uxp = attribs[PIdx::ux];
+ auto& uyp = attribs[PIdx::uy];
+
+#if AMREX_SPACEDIM == 3
+ auto& uzp = attribs[PIdx::uz];
+#endif
+
+ auto& Exp = attribs[PIdx::Ex];
+ auto& Eyp = attribs[PIdx::Ey];
+
+#if AMREX_SPACEDIM == 3
+ auto& Ezp = attribs[PIdx::Ez];
+#endif
+ //
+ // Particle Push
+ //
+ WRPX_PUSH_LEAPFROG(particles.dataPtr(), nstride, np,
+ uxp.dataPtr(), uyp.dataPtr(),
+#if AMREX_SPACEDIM == 3
+ uzp.dataPtr(),
+#endif
+ Exp.dataPtr(), Eyp.dataPtr(),
+#if AMREX_SPACEDIM == 3
+ Ezp.dataPtr(),
+#endif
+ &this->charge, &this->mass, &dt,
+ prob_domain.lo(), prob_domain.hi());
+ }
+ }
+}
+#endif // WARPX_DO_ELECTROSTATIC
+
+void
+PhysicalParticleContainer::FieldGather (int lev,
+ const MultiFab& Ex, const MultiFab& Ey, const MultiFab& Ez,
+ const MultiFab& Bx, const MultiFab& By, const MultiFab& Bz)
+{
+ const std::array<Real,3>& dx = WarpX::CellSize(lev);
+
+ // WarpX assumes the same number of guard cells for Ex, Ey, Ez, Bx, By, Bz
+ long ng = Ex.nGrow();
+
+ BL_ASSERT(OnSameGrids(lev,Ex));
+
+ MultiFab* cost = WarpX::getCosts(lev);
+
+#ifdef _OPENMP
+#pragma omp parallel
+#endif
+ {
+ Cuda::DeviceVector<Real> xp, yp, zp;
+
+ for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti)
+ {
+ Real wt = amrex::second();
+
+ const Box& box = pti.validbox();
+
+ auto& attribs = pti.GetAttribs();
+
+ auto& Exp = attribs[PIdx::Ex];
+ auto& Eyp = attribs[PIdx::Ey];
+ auto& Ezp = attribs[PIdx::Ez];
+ auto& Bxp = attribs[PIdx::Bx];
+ auto& Byp = attribs[PIdx::By];
+ auto& Bzp = attribs[PIdx::Bz];
+
+ const long np = pti.numParticles();
+
+ // Data on the grid
+ const FArrayBox& exfab = Ex[pti];
+ const FArrayBox& eyfab = Ey[pti];
+ const FArrayBox& ezfab = Ez[pti];
+ const FArrayBox& bxfab = Bx[pti];
+ const FArrayBox& byfab = By[pti];
+ const FArrayBox& bzfab = Bz[pti];
+
+ Exp.assign(np,0.0);
+ Eyp.assign(np,0.0);
+ Ezp.assign(np,0.0);
+ Bxp.assign(np,0.0);
+ Byp.assign(np,0.0);
+ Bzp.assign(np,0.0);
+
+ //
+ // copy data from particle container to temp arrays
+ //
+ pti.GetPosition(xp, yp, zp);
+
+ const std::array<Real,3>& xyzmin = WarpX::LowerCorner(box, lev);
+ const int* ixyzmin = box.loVect();
+
+ //
+ // Field Gather
+ //
+ const int ll4symtry = false;
+ long lvect_fieldgathe = 64;
+ warpx_geteb_energy_conserving(
+ &np,
+ xp.dataPtr(),
+ yp.dataPtr(),
+ zp.dataPtr(),
+ Exp.dataPtr(),Eyp.dataPtr(),Ezp.dataPtr(),
+ Bxp.dataPtr(),Byp.dataPtr(),Bzp.dataPtr(),
+ ixyzmin,
+ &xyzmin[0], &xyzmin[1], &xyzmin[2],
+ &dx[0], &dx[1], &dx[2],
+ &WarpX::nox, &WarpX::noy, &WarpX::noz,
+ BL_TO_FORTRAN_ANYD(exfab),
+ BL_TO_FORTRAN_ANYD(eyfab),
+ BL_TO_FORTRAN_ANYD(ezfab),
+ BL_TO_FORTRAN_ANYD(bxfab),
+ BL_TO_FORTRAN_ANYD(byfab),
+ BL_TO_FORTRAN_ANYD(bzfab),
+ &ll4symtry, &WarpX::l_lower_order_in_v, &WarpX::do_nodal,
+ &lvect_fieldgathe, &WarpX::field_gathering_algo);
+
+ if (cost) {
+ const Box& tbx = pti.tilebox();
+ wt = (amrex::second() - wt) / tbx.d_numPts();
+ FArrayBox* costfab = cost->fabPtr(pti);
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA ( tbx, work_box,
+ {
+ costfab->plus(wt, work_box);
+ });
+ }
+ }
+ }
+}
+
+void
+PhysicalParticleContainer::Evolve (int lev,
+ const MultiFab& Ex, const MultiFab& Ey, const MultiFab& Ez,
+ const MultiFab& Bx, const MultiFab& By, const MultiFab& Bz,
+ MultiFab& jx, MultiFab& jy, MultiFab& jz,
+ MultiFab* cjx, MultiFab* cjy, MultiFab* cjz,
+ MultiFab* rho, MultiFab* crho,
+ const MultiFab* cEx, const MultiFab* cEy, const MultiFab* cEz,
+ const MultiFab* cBx, const MultiFab* cBy, const MultiFab* cBz,
+ Real t, Real dt)
+{
+ BL_PROFILE("PPC::Evolve()");
+ BL_PROFILE_VAR_NS("PPC::Evolve::Copy", blp_copy);
+ BL_PROFILE_VAR_NS("PICSAR::FieldGather", blp_pxr_fg);
+ BL_PROFILE_VAR_NS("PICSAR::ParticlePush", blp_pxr_pp);
+ BL_PROFILE_VAR_NS("PPC::Evolve::partition", blp_partition);
+
+ const std::array<Real,3>& dx = WarpX::CellSize(lev);
+ const std::array<Real,3>& cdx = WarpX::CellSize(std::max(lev-1,0));
+
+ const auto& mypc = WarpX::GetInstance().GetPartContainer();
+ const int nstencilz_fdtd_nci_corr = mypc.nstencilz_fdtd_nci_corr;
+
+ BL_ASSERT(OnSameGrids(lev,jx));
+
+ MultiFab* cost = WarpX::getCosts(lev);
+
+ const iMultiFab* current_masks = WarpX::CurrentBufferMasks(lev);
+ const iMultiFab* gather_masks = WarpX::GatherBufferMasks(lev);
+
+ bool has_buffer = cEx || cjx;
+
+#ifdef _OPENMP
+#pragma omp parallel
+#endif
+ {
+#ifdef _OPENMP
+ int thread_num = omp_get_thread_num();
+#else
+ int thread_num = 0;
+#endif
+ if (local_rho[thread_num] == nullptr) local_rho[thread_num].reset( new amrex::FArrayBox());
+ if (local_jx[thread_num] == nullptr) local_jx[thread_num].reset( new amrex::FArrayBox());
+ if (local_jy[thread_num] == nullptr) local_jy[thread_num].reset( new amrex::FArrayBox());
+ if (local_jz[thread_num] == nullptr) local_jz[thread_num].reset( new amrex::FArrayBox());
+
+ FArrayBox filtered_Ex, filtered_Ey, filtered_Ez;
+ FArrayBox filtered_Bx, filtered_By, filtered_Bz;
+ std::vector<bool> inexflag;
+ Vector<long> pid;
+ RealVector tmp;
+ ParticleVector particle_tmp;
+
+ for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti)
+ {
+ Real wt = amrex::second();
+
+ const Box& box = pti.validbox();
+
+ auto& attribs = pti.GetAttribs();
+
+ auto& wp = attribs[PIdx::w];
+ auto& uxp = attribs[PIdx::ux];
+ auto& uyp = attribs[PIdx::uy];
+ auto& uzp = attribs[PIdx::uz];
+ auto& Exp = attribs[PIdx::Ex];
+ auto& Eyp = attribs[PIdx::Ey];
+ auto& Ezp = attribs[PIdx::Ez];
+ auto& Bxp = attribs[PIdx::Bx];
+ auto& Byp = attribs[PIdx::By];
+ auto& Bzp = attribs[PIdx::Bz];
+
+ const long np = pti.numParticles();
+
+ // Data on the grid
+ FArrayBox const* exfab = &(Ex[pti]);
+ FArrayBox const* eyfab = &(Ey[pti]);
+ FArrayBox const* ezfab = &(Ez[pti]);
+ FArrayBox const* bxfab = &(Bx[pti]);
+ FArrayBox const* byfab = &(By[pti]);
+ FArrayBox const* bzfab = &(Bz[pti]);
+
+ if (WarpX::use_fdtd_nci_corr)
+ {
+#if (AMREX_SPACEDIM == 2)
+ const Box& tbox = amrex::grow(pti.tilebox(),{static_cast<int>(WarpX::nox),
+ static_cast<int>(WarpX::noz)});
+#else
+ const Box& tbox = amrex::grow(pti.tilebox(),{static_cast<int>(WarpX::nox),
+ static_cast<int>(WarpX::noy),
+ static_cast<int>(WarpX::noz)});
+#endif
+
+ // both 2d and 3d
+ filtered_Ex.resize(amrex::convert(tbox,WarpX::Ex_nodal_flag));
+ WRPX_PXR_GODFREY_FILTER(BL_TO_FORTRAN_BOX(filtered_Ex),
+ BL_TO_FORTRAN_ANYD(filtered_Ex),
+ BL_TO_FORTRAN_ANYD(Ex[pti]),
+ mypc.fdtd_nci_stencilz_ex[lev].data(),
+ &nstencilz_fdtd_nci_corr);
+ exfab = &filtered_Ex;
+
+ filtered_Ez.resize(amrex::convert(tbox,WarpX::Ez_nodal_flag));
+ WRPX_PXR_GODFREY_FILTER(BL_TO_FORTRAN_BOX(filtered_Ez),
+ BL_TO_FORTRAN_ANYD(filtered_Ez),
+ BL_TO_FORTRAN_ANYD(Ez[pti]),
+ mypc.fdtd_nci_stencilz_by[lev].data(),
+ &nstencilz_fdtd_nci_corr);
+ ezfab = &filtered_Ez;
+
+ filtered_By.resize(amrex::convert(tbox,WarpX::By_nodal_flag));
+ WRPX_PXR_GODFREY_FILTER(BL_TO_FORTRAN_BOX(filtered_By),
+ BL_TO_FORTRAN_ANYD(filtered_By),
+ BL_TO_FORTRAN_ANYD(By[pti]),
+ mypc.fdtd_nci_stencilz_by[lev].data(),
+ &nstencilz_fdtd_nci_corr);
+ byfab = &filtered_By;
+
+#if (AMREX_SPACEDIM == 3)
+ filtered_Ey.resize(amrex::convert(tbox,WarpX::Ey_nodal_flag));
+ WRPX_PXR_GODFREY_FILTER(BL_TO_FORTRAN_BOX(filtered_Ey),
+ BL_TO_FORTRAN_ANYD(filtered_Ey),
+ BL_TO_FORTRAN_ANYD(Ey[pti]),
+ mypc.fdtd_nci_stencilz_ex[lev].data(),
+ &nstencilz_fdtd_nci_corr);
+ eyfab = &filtered_Ey;
+
+ filtered_Bx.resize(amrex::convert(tbox,WarpX::Bx_nodal_flag));
+ WRPX_PXR_GODFREY_FILTER(BL_TO_FORTRAN_BOX(filtered_Bx),
+ BL_TO_FORTRAN_ANYD(filtered_Bx),
+ BL_TO_FORTRAN_ANYD(Bx[pti]),
+ mypc.fdtd_nci_stencilz_by[lev].data(),
+ &nstencilz_fdtd_nci_corr);
+ bxfab = &filtered_Bx;
+
+ filtered_Bz.resize(amrex::convert(tbox,WarpX::Bz_nodal_flag));
+ WRPX_PXR_GODFREY_FILTER(BL_TO_FORTRAN_BOX(filtered_Bz),
+ BL_TO_FORTRAN_ANYD(filtered_Bz),
+ BL_TO_FORTRAN_ANYD(Bz[pti]),
+ mypc.fdtd_nci_stencilz_ex[lev].data(),
+ &nstencilz_fdtd_nci_corr);
+ bzfab = &filtered_Bz;
+#endif
+ }
+
+ Exp.assign(np,0.0);
+ Eyp.assign(np,0.0);
+ Ezp.assign(np,0.0);
+ Bxp.assign(np,WarpX::B_external[0]);
+ Byp.assign(np,WarpX::B_external[1]);
+ Bzp.assign(np,WarpX::B_external[2]);
+
+ m_giv[thread_num].resize(np);
+
+ long nfine_current = np;
+ long nfine_gather = np;
+ if (has_buffer && !do_not_push)
+ {
+ BL_PROFILE_VAR_START(blp_partition);
+ inexflag.resize(np);
+ auto& aos = pti.GetArrayOfStructs();
+ // We need to partition the large buffer first
+ iMultiFab const* bmasks = (WarpX::n_field_gather_buffer >= WarpX::n_current_deposition_buffer) ?
+ gather_masks : current_masks;
+ int i = 0;
+ const auto& msk = (*bmasks)[pti];
+ for (const auto& p : aos) {
+ const IntVect& iv = Index(p, lev);
+ inexflag[i++] = msk(iv);
+ }
+
+ pid.resize(np);
+ std::iota(pid.begin(), pid.end(), 0L);
+
+ auto sep = std::stable_partition(pid.begin(), pid.end(),
+ [&inexflag](long id) { return inexflag[id]; });
+
+ if (WarpX::n_current_deposition_buffer == WarpX::n_field_gather_buffer) {
+ nfine_current = nfine_gather = std::distance(pid.begin(), sep);
+ } else if (sep != pid.end()) {
+ int n_buf;
+ if (bmasks == gather_masks) {
+ nfine_gather = std::distance(pid.begin(), sep);
+ bmasks = current_masks;
+ n_buf = WarpX::n_current_deposition_buffer;
+ } else {
+ nfine_current = std::distance(pid.begin(), sep);
+ bmasks = gather_masks;
+ n_buf = WarpX::n_field_gather_buffer;
+ }
+ if (n_buf > 0)
+ {
+ const auto& msk2 = (*bmasks)[pti];
+ for (auto it = sep; it != pid.end(); ++it) {
+ const long id = *it;
+ const IntVect& iv = Index(aos[id], lev);
+ inexflag[id] = msk2(iv);
+ }
+
+ auto sep2 = std::stable_partition(sep, pid.end(),
+ [&inexflag](long id) {return inexflag[id];});
+ if (bmasks == gather_masks) {
+ nfine_gather = std::distance(pid.begin(), sep2);
+ } else {
+ nfine_current = std::distance(pid.begin(), sep2);
+ }
+ }
+ }
+
+ if (deposit_on_main_grid && lev > 0) {
+ nfine_current = 0;
+ }
+
+ if (nfine_current != np || nfine_gather != np)
+ {
+ particle_tmp.resize(np);
+ for (long ip = 0; ip < np; ++ip) {
+ particle_tmp[ip] = aos[pid[ip]];
+ }
+ std::swap(aos(), particle_tmp);
+
+ tmp.resize(np);
+ for (long ip = 0; ip < np; ++ip) {
+ tmp[ip] = wp[pid[ip]];
+ }
+ std::swap(wp, tmp);
+
+ for (long ip = 0; ip < np; ++ip) {
+ tmp[ip] = uxp[pid[ip]];
+ }
+ std::swap(uxp, tmp);
+
+ for (long ip = 0; ip < np; ++ip) {
+ tmp[ip] = uyp[pid[ip]];
+ }
+ std::swap(uyp, tmp);
+
+ for (long ip = 0; ip < np; ++ip) {
+ tmp[ip] = uzp[pid[ip]];
+ }
+ std::swap(uzp, tmp);
+ }
+ BL_PROFILE_VAR_STOP(blp_partition);
+ }
+
+ const long np_current = (cjx) ? nfine_current : np;
+
+ //
+ // copy data from particle container to temp arrays
+ //
+ BL_PROFILE_VAR_START(blp_copy);
+ pti.GetPosition(m_xp[thread_num], m_yp[thread_num], m_zp[thread_num]);
+ BL_PROFILE_VAR_STOP(blp_copy);
+
+ if (rho) DepositCharge(pti, wp, rho, crho, 0, np_current, np, thread_num, lev);
+
+ if (! do_not_push)
+ {
+ //
+ // Field Gather of Aux Data (i.e., the full solution)
+ //
+ const int ll4symtry = false;
+ long lvect_fieldgathe = 64;
+
+ const std::array<Real,3>& xyzmin_grid = WarpX::LowerCorner(box, lev);
+ const int* ixyzmin_grid = box.loVect();
+
+ const long np_gather = (cEx) ? nfine_gather : np;
+
+ BL_PROFILE_VAR_START(blp_pxr_fg);
+
+ warpx_geteb_energy_conserving(
+ &np_gather,
+ m_xp[thread_num].dataPtr(),
+ m_yp[thread_num].dataPtr(),
+ m_zp[thread_num].dataPtr(),
+ Exp.dataPtr(),Eyp.dataPtr(),Ezp.dataPtr(),
+ Bxp.dataPtr(),Byp.dataPtr(),Bzp.dataPtr(),
+ ixyzmin_grid,
+ &xyzmin_grid[0], &xyzmin_grid[1], &xyzmin_grid[2],
+ &dx[0], &dx[1], &dx[2],
+ &WarpX::nox, &WarpX::noy, &WarpX::noz,
+ BL_TO_FORTRAN_ANYD(*exfab),
+ BL_TO_FORTRAN_ANYD(*eyfab),
+ BL_TO_FORTRAN_ANYD(*ezfab),
+ BL_TO_FORTRAN_ANYD(*bxfab),
+ BL_TO_FORTRAN_ANYD(*byfab),
+ BL_TO_FORTRAN_ANYD(*bzfab),
+ &ll4symtry, &WarpX::l_lower_order_in_v, &WarpX::do_nodal,
+ &lvect_fieldgathe, &WarpX::field_gathering_algo);
+
+ if (np_gather < np)
+ {
+ const IntVect& ref_ratio = WarpX::RefRatio(lev-1);
+ const Box& cbox = amrex::coarsen(box,ref_ratio);
+ const std::array<Real,3>& cxyzmin_grid = WarpX::LowerCorner(cbox, lev-1);
+ const int* cixyzmin_grid = cbox.loVect();
+
+ const FArrayBox* cexfab = &(*cEx)[pti];
+ const FArrayBox* ceyfab = &(*cEy)[pti];
+ const FArrayBox* cezfab = &(*cEz)[pti];
+ const FArrayBox* cbxfab = &(*cBx)[pti];
+ const FArrayBox* cbyfab = &(*cBy)[pti];
+ const FArrayBox* cbzfab = &(*cBz)[pti];
+
+ if (WarpX::use_fdtd_nci_corr)
+ {
+#if (AMREX_SPACEDIM == 2)
+ const Box& tbox = amrex::grow(cbox,{static_cast<int>(WarpX::nox),
+ static_cast<int>(WarpX::noz)});
+#else
+ const Box& tbox = amrex::grow(cbox,{static_cast<int>(WarpX::nox),
+ static_cast<int>(WarpX::noy),
+ static_cast<int>(WarpX::noz)});
+#endif
+
+ // both 2d and 3d
+ filtered_Ex.resize(amrex::convert(tbox,WarpX::Ex_nodal_flag));
+ WRPX_PXR_GODFREY_FILTER(BL_TO_FORTRAN_BOX(filtered_Ex),
+ BL_TO_FORTRAN_ANYD(filtered_Ex),
+ BL_TO_FORTRAN_ANYD((*cEx)[pti]),
+ mypc.fdtd_nci_stencilz_ex[lev-1].data(),
+ &nstencilz_fdtd_nci_corr);
+ cexfab = &filtered_Ex;
+
+ filtered_Ez.resize(amrex::convert(tbox,WarpX::Ez_nodal_flag));
+ WRPX_PXR_GODFREY_FILTER(BL_TO_FORTRAN_BOX(filtered_Ez),
+ BL_TO_FORTRAN_ANYD(filtered_Ez),
+ BL_TO_FORTRAN_ANYD((*cEz)[pti]),
+ mypc.fdtd_nci_stencilz_by[lev-1].data(),
+ &nstencilz_fdtd_nci_corr);
+ cezfab = &filtered_Ez;
+ filtered_By.resize(amrex::convert(tbox,WarpX::By_nodal_flag));
+ WRPX_PXR_GODFREY_FILTER(BL_TO_FORTRAN_BOX(filtered_By),
+ BL_TO_FORTRAN_ANYD(filtered_By),
+ BL_TO_FORTRAN_ANYD((*cBy)[pti]),
+ mypc.fdtd_nci_stencilz_by[lev-1].data(),
+ &nstencilz_fdtd_nci_corr);
+ cbyfab = &filtered_By;
+
+#if (AMREX_SPACEDIM == 3)
+ filtered_Ey.resize(amrex::convert(tbox,WarpX::Ey_nodal_flag));
+ WRPX_PXR_GODFREY_FILTER(BL_TO_FORTRAN_BOX(filtered_Ey),
+ BL_TO_FORTRAN_ANYD(filtered_Ey),
+ BL_TO_FORTRAN_ANYD((*cEy)[pti]),
+ mypc.fdtd_nci_stencilz_ex[lev-1].data(),
+ &nstencilz_fdtd_nci_corr);
+ ceyfab = &filtered_Ey;
+
+ filtered_Bx.resize(amrex::convert(tbox,WarpX::Bx_nodal_flag));
+ WRPX_PXR_GODFREY_FILTER(BL_TO_FORTRAN_BOX(filtered_Bx),
+ BL_TO_FORTRAN_ANYD(filtered_Bx),
+ BL_TO_FORTRAN_ANYD((*cBx)[pti]),
+ mypc.fdtd_nci_stencilz_by[lev-1].data(),
+ &nstencilz_fdtd_nci_corr);
+ cbxfab = &filtered_Bx;
+
+ filtered_Bz.resize(amrex::convert(tbox,WarpX::Bz_nodal_flag));
+ WRPX_PXR_GODFREY_FILTER(BL_TO_FORTRAN_BOX(filtered_Bz),
+ BL_TO_FORTRAN_ANYD(filtered_Bz),
+ BL_TO_FORTRAN_ANYD((*cBz)[pti]),
+ mypc.fdtd_nci_stencilz_ex[lev-1].data(),
+ &nstencilz_fdtd_nci_corr);
+ cbzfab = &filtered_Bz;
+#endif
+ }
+
+ long ncrse = np - nfine_gather;
+ warpx_geteb_energy_conserving(
+ &ncrse,
+ m_xp[thread_num].dataPtr()+nfine_gather,
+ m_yp[thread_num].dataPtr()+nfine_gather,
+ m_zp[thread_num].dataPtr()+nfine_gather,
+ Exp.dataPtr()+nfine_gather, Eyp.dataPtr()+nfine_gather, Ezp.dataPtr()+nfine_gather,
+ Bxp.dataPtr()+nfine_gather, Byp.dataPtr()+nfine_gather, Bzp.dataPtr()+nfine_gather,
+ cixyzmin_grid,
+ &cxyzmin_grid[0], &cxyzmin_grid[1], &cxyzmin_grid[2],
+ &cdx[0], &cdx[1], &cdx[2],
+ &WarpX::nox, &WarpX::noy, &WarpX::noz,
+ BL_TO_FORTRAN_ANYD(*cexfab),
+ BL_TO_FORTRAN_ANYD(*ceyfab),
+ BL_TO_FORTRAN_ANYD(*cezfab),
+ BL_TO_FORTRAN_ANYD(*cbxfab),
+ BL_TO_FORTRAN_ANYD(*cbyfab),
+ BL_TO_FORTRAN_ANYD(*cbzfab),
+ &ll4symtry, &WarpX::l_lower_order_in_v, &WarpX::do_nodal,
+ &lvect_fieldgathe, &WarpX::field_gathering_algo);
+ }
+
+ BL_PROFILE_VAR_STOP(blp_pxr_fg);
+
+ //
+ // Particle Push
+ //
+ BL_PROFILE_VAR_START(blp_pxr_pp);
+ PushPX(pti, m_xp[thread_num], m_yp[thread_num], m_zp[thread_num],
+ m_giv[thread_num], dt);
+ BL_PROFILE_VAR_STOP(blp_pxr_pp);
+
+ //
+ // Current Deposition
+ //
+ DepositCurrent(pti, wp, uxp, uyp, uzp, jx, jy, jz,
+ cjx, cjy, cjz, np_current, np, thread_num, lev, dt);
+
+ //
+ // copy particle data back
+ //
+ BL_PROFILE_VAR_START(blp_copy);
+ pti.SetPosition(m_xp[thread_num], m_yp[thread_num], m_zp[thread_num]);
+ BL_PROFILE_VAR_STOP(blp_copy);
+ }
+
+ if (rho) DepositCharge(pti, wp, rho, crho, 1, np_current, np, thread_num, lev);
+
+ if (cost) {
+ const Box& tbx = pti.tilebox();
+ wt = (amrex::second() - wt) / tbx.d_numPts();
+ FArrayBox* costfab = cost->fabPtr(pti);
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA ( tbx, work_box,
+ {
+ costfab->plus(wt, work_box);
+ });
+ }
+ }
+ }
+ // Split particles
+ if (do_splitting){ SplitParticles(lev); }
+}
+
+// Loop over all particles in the particle container and
+// split particles tagged with p.id()=DoSplitParticleID
+void
+PhysicalParticleContainer::SplitParticles(int lev)
+{
+ auto& mypc = WarpX::GetInstance().GetPartContainer();
+ auto& pctmp_split = mypc.GetPCtmp();
+ Cuda::DeviceVector<Real> xp, yp, zp;
+ RealVector psplit_x, psplit_y, psplit_z, psplit_w;
+ RealVector psplit_ux, psplit_uy, psplit_uz;
+ long np_split_to_add = 0;
+ long np_split;
+ if(split_type==0)
+ {
+ np_split = pow(2, AMREX_SPACEDIM);
+ } else {
+ np_split = 2*AMREX_SPACEDIM;
+ }
+
+ // Loop over particle interator
+ for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti)
+ {
+ pti.GetPosition(xp, yp, zp);
+ const std::array<Real,3>& dx = WarpX::CellSize(lev);
+ // particle Array Of Structs data
+ auto& particles = pti.GetArrayOfStructs();
+ // particle Struct Of Arrays data
+ auto& attribs = pti.GetAttribs();
+ auto& wp = attribs[PIdx::w ];
+ auto& uxp = attribs[PIdx::ux];
+ auto& uyp = attribs[PIdx::uy];
+ auto& uzp = attribs[PIdx::uz];
+ const long np = pti.numParticles();
+ for(int i=0; i<np; i++){
+ auto& p = particles[i];
+ if (p.id() == DoSplitParticleID){
+ // If particle is tagged, split it and put the
+ // split particles in local arrays psplit_x etc.
+ np_split_to_add += np_split;
+#if (AMREX_SPACEDIM==2)
+ if (split_type==0){
+ // Split particle in two along each axis
+ // 4 particles in 2d
+ for (int ishift = -1; ishift < 2; ishift +=2 ){
+ for (int kshift = -1; kshift < 2; kshift +=2 ){
+ // Add one particle with offset in x and z
+ psplit_x.push_back( xp[i] + ishift*dx[0]/2 );
+ psplit_y.push_back( yp[i] );
+ psplit_z.push_back( zp[i] + kshift*dx[2]/2 );
+ psplit_ux.push_back( uxp[i] );
+ psplit_uy.push_back( uyp[i] );
+ psplit_uz.push_back( uzp[i] );
+ psplit_w.push_back( wp[i]/np_split );
+ }
+ }
+ } else {
+ // Split particle in two along each diagonal
+ // 4 particles in 2d
+ for (int ishift = -1; ishift < 2; ishift +=2 ){
+ // Add one particle with offset in x
+ psplit_x.push_back( xp[i] + ishift*dx[0]/2 );
+ psplit_y.push_back( yp[i] );
+ psplit_z.push_back( zp[i] );
+ psplit_ux.push_back( uxp[i] );
+ psplit_uy.push_back( uyp[i] );
+ psplit_uz.push_back( uzp[i] );
+ psplit_w.push_back( wp[i]/np_split );
+ // Add one particle with offset in z
+ psplit_x.push_back( xp[i] );
+ psplit_y.push_back( yp[i] );
+ psplit_z.push_back( zp[i] + ishift*dx[2]/2 );
+ psplit_ux.push_back( uxp[i] );
+ psplit_uy.push_back( uyp[i] );
+ psplit_uz.push_back( uzp[i] );
+ psplit_w.push_back( wp[i]/np_split );
+ }
+ }
+#elif (AMREX_SPACEDIM==3)
+ if (split_type==0){
+ // Split particle in two along each axis
+ // 6 particles in 2d
+ for (int ishift = -1; ishift < 2; ishift +=2 ){
+ for (int jshift = -1; jshift < 2; jshift +=2 ){
+ for (int kshift = -1; kshift < 2; kshift +=2 ){
+ // Add one particle with offset in x, y and z
+ psplit_x.push_back( xp[i] + ishift*dx[0]/2 );
+ psplit_y.push_back( yp[i] + jshift*dx[1]/2 );
+ psplit_z.push_back( zp[i] + jshift*dx[2]/2 );
+ psplit_ux.push_back( uxp[i] );
+ psplit_uy.push_back( uyp[i] );
+ psplit_uz.push_back( uzp[i] );
+ psplit_w.push_back( wp[i]/np_split );
+ }
+ }
+ }
+ } else {
+ // Split particle in two along each diagonal
+ // 8 particles in 3d
+ for (int ishift = -1; ishift < 2; ishift +=2 ){
+ // Add one particle with offset in x
+ psplit_x.push_back( xp[i] + ishift*dx[0]/2 );
+ psplit_y.push_back( yp[i] );
+ psplit_z.push_back( zp[i] );
+ psplit_ux.push_back( uxp[i] );
+ psplit_uy.push_back( uyp[i] );
+ psplit_uz.push_back( uzp[i] );
+ psplit_w.push_back( wp[i]/np_split );
+ // Add one particle with offset in y
+ psplit_x.push_back( xp[i] );
+ psplit_y.push_back( yp[i] + ishift*dx[1]/2 );
+ psplit_z.push_back( zp[i] );
+ psplit_ux.push_back( uxp[i] );
+ psplit_uy.push_back( uyp[i] );
+ psplit_uz.push_back( uzp[i] );
+ psplit_w.push_back( wp[i]/np_split );
+ // Add one particle with offset in z
+ psplit_x.push_back( xp[i] );
+ psplit_y.push_back( yp[i] );
+ psplit_z.push_back( zp[i] + ishift*dx[2]/2 );
+ psplit_ux.push_back( uxp[i] );
+ psplit_uy.push_back( uyp[i] );
+ psplit_uz.push_back( uzp[i] );
+ psplit_w.push_back( wp[i]/np_split );
+ }
+ }
+#endif
+ // invalidate the particle
+ p.m_idata.id = -p.m_idata.id;
+ }
+ }
+ }
+ // Add local arrays psplit_x etc. to the temporary
+ // particle container pctmp_split. Split particles
+ // are tagged with p.id()=NoSplitParticleID so that
+ // they are not re-split when entering a higher level
+ // AddNParticles calls Redistribute, so that particles
+ // in pctmp_split are in the proper grids and tiles
+ pctmp_split.AddNParticles(lev,
+ np_split_to_add,
+ psplit_x.dataPtr(),
+ psplit_y.dataPtr(),
+ psplit_z.dataPtr(),
+ psplit_ux.dataPtr(),
+ psplit_uy.dataPtr(),
+ psplit_uz.dataPtr(),
+ 1,
+ psplit_w.dataPtr(),
+ 1, NoSplitParticleID);
+ // Copy particles from tmp to current particle container
+ addParticles(pctmp_split,1);
+ // Clear tmp container
+ pctmp_split.clearParticles();
+}
+
+void
+PhysicalParticleContainer::PushPX(WarpXParIter& pti,
+ Cuda::DeviceVector<Real>& xp,
+ Cuda::DeviceVector<Real>& yp,
+ Cuda::DeviceVector<Real>& zp,
+ Cuda::DeviceVector<Real>& giv,
+ Real dt)
+{
+
+ // This wraps the call to warpx_particle_pusher so that inheritors can modify the call.
+ auto& attribs = pti.GetAttribs();
+ auto& uxp = attribs[PIdx::ux];
+ auto& uyp = attribs[PIdx::uy];
+ auto& uzp = attribs[PIdx::uz];
+ auto& Exp = attribs[PIdx::Ex];
+ auto& Eyp = attribs[PIdx::Ey];
+ auto& Ezp = attribs[PIdx::Ez];
+ auto& Bxp = attribs[PIdx::Bx];
+ auto& Byp = attribs[PIdx::By];
+ auto& Bzp = attribs[PIdx::Bz];
+ const long np = pti.numParticles();
+
+#ifdef WARPX_STORE_OLD_PARTICLE_ATTRIBS
+ auto& xpold = attribs[PIdx::xold];
+ auto& ypold = attribs[PIdx::yold];
+ auto& zpold = attribs[PIdx::zold];
+ auto& uxpold = attribs[PIdx::uxold];
+ auto& uypold = attribs[PIdx::uyold];
+ auto& uzpold = attribs[PIdx::uzold];
+
+ warpx_copy_attribs(&np, xp.dataPtr(), yp.dataPtr(), zp.dataPtr(),
+ uxp.dataPtr(), uyp.dataPtr(), uzp.dataPtr(),
+ xpold.dataPtr(), ypold.dataPtr(), zpold.dataPtr(),
+ uxpold.dataPtr(), uypold.dataPtr(), uzpold.dataPtr());
+
+#endif
+
+ warpx_particle_pusher(&np,
+ xp.dataPtr(),
+ yp.dataPtr(),
+ zp.dataPtr(),
+ uxp.dataPtr(), uyp.dataPtr(), uzp.dataPtr(),
+ giv.dataPtr(),
+ Exp.dataPtr(), Eyp.dataPtr(), Ezp.dataPtr(),
+ Bxp.dataPtr(), Byp.dataPtr(), Bzp.dataPtr(),
+ &this->charge, &this->mass, &dt,
+ &WarpX::particle_pusher_algo);
+
+}
+
+void
+PhysicalParticleContainer::PushP (int lev, Real dt,
+ const MultiFab& Ex, const MultiFab& Ey, const MultiFab& Ez,
+ const MultiFab& Bx, const MultiFab& By, const MultiFab& Bz)
+{
+ BL_PROFILE("PhysicalParticleContainer::PushP");
+
+ if (do_not_push) return;
+
+ const std::array<Real,3>& dx = WarpX::CellSize(lev);
+
+#ifdef _OPENMP
+#pragma omp parallel
+#endif
+ {
+#ifdef _OPENMP
+ int thread_num = omp_get_thread_num();
+#else
+ int thread_num = 0;
+#endif
+ for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti)
+ {
+ const Box& box = pti.validbox();
+
+ auto& attribs = pti.GetAttribs();
+
+ auto& uxp = attribs[PIdx::ux];
+ auto& uyp = attribs[PIdx::uy];
+ auto& uzp = attribs[PIdx::uz];
+ auto& Exp = attribs[PIdx::Ex];
+ auto& Eyp = attribs[PIdx::Ey];
+ auto& Ezp = attribs[PIdx::Ez];
+ auto& Bxp = attribs[PIdx::Bx];
+ auto& Byp = attribs[PIdx::By];
+ auto& Bzp = attribs[PIdx::Bz];
+
+ const long np = pti.numParticles();
+
+ // Data on the grid
+ const FArrayBox& exfab = Ex[pti];
+ const FArrayBox& eyfab = Ey[pti];
+ const FArrayBox& ezfab = Ez[pti];
+ const FArrayBox& bxfab = Bx[pti];
+ const FArrayBox& byfab = By[pti];
+ const FArrayBox& bzfab = Bz[pti];
+
+ Exp.assign(np,0.0);
+ Eyp.assign(np,0.0);
+ Ezp.assign(np,0.0);
+ Bxp.assign(np,WarpX::B_external[0]);
+ Byp.assign(np,WarpX::B_external[1]);
+ Bzp.assign(np,WarpX::B_external[2]);
+
+ m_giv[thread_num].resize(np);
+
+ //
+ // copy data from particle container to temp arrays
+ //
+ pti.GetPosition(m_xp[thread_num], m_yp[thread_num], m_zp[thread_num]);
+
+ const std::array<Real,3>& xyzmin_grid = WarpX::LowerCorner(box, lev);
+ const int* ixyzmin_grid = box.loVect();
+
+ const int ll4symtry = false;
+ long lvect_fieldgathe = 64;
+
+ warpx_geteb_energy_conserving(
+ &np,
+ m_xp[thread_num].dataPtr(),
+ m_yp[thread_num].dataPtr(),
+ m_zp[thread_num].dataPtr(),
+ Exp.dataPtr(),Eyp.dataPtr(),Ezp.dataPtr(),
+ Bxp.dataPtr(),Byp.dataPtr(),Bzp.dataPtr(),
+ ixyzmin_grid,
+ &xyzmin_grid[0], &xyzmin_grid[1], &xyzmin_grid[2],
+ &dx[0], &dx[1], &dx[2],
+ &WarpX::nox, &WarpX::noy, &WarpX::noz,
+ BL_TO_FORTRAN_ANYD(exfab),
+ BL_TO_FORTRAN_ANYD(eyfab),
+ BL_TO_FORTRAN_ANYD(ezfab),
+ BL_TO_FORTRAN_ANYD(bxfab),
+ BL_TO_FORTRAN_ANYD(byfab),
+ BL_TO_FORTRAN_ANYD(bzfab),
+ &ll4symtry, &WarpX::l_lower_order_in_v, &WarpX::do_nodal,
+ &lvect_fieldgathe, &WarpX::field_gathering_algo);
+
+ warpx_particle_pusher_momenta(&np,
+ m_xp[thread_num].dataPtr(),
+ m_yp[thread_num].dataPtr(),
+ m_zp[thread_num].dataPtr(),
+ uxp.dataPtr(), uyp.dataPtr(), uzp.dataPtr(),
+ m_giv[thread_num].dataPtr(),
+ Exp.dataPtr(), Eyp.dataPtr(), Ezp.dataPtr(),
+ Bxp.dataPtr(), Byp.dataPtr(), Bzp.dataPtr(),
+ &this->charge, &this->mass, &dt,
+ &WarpX::particle_pusher_algo);
+ }
+ }
+}
+
+void PhysicalParticleContainer::GetParticleSlice(const int direction, const Real z_old,
+ const Real z_new, const Real t_boost,
+ const Real t_lab, const Real dt,
+ DiagnosticParticles& diagnostic_particles)
+{
+ BL_PROFILE("PhysicalParticleContainer::GetParticleSlice");
+
+#ifdef WARPX_STORE_OLD_PARTICLE_ATTRIBS
+ // Assume that the boost in the positive z direction.
+#if (AMREX_SPACEDIM == 2)
+ AMREX_ALWAYS_ASSERT(direction == 1);
+#else
+ AMREX_ALWAYS_ASSERT(direction == 2);
+#endif
+
+ // Note the the slice should always move in the negative boost direction.
+ AMREX_ALWAYS_ASSERT(z_new < z_old);
+
+ const int nlevs = std::max(0, finestLevel()+1);
+
+ // we figure out a box for coarse-grained rejection. If the RealBox corresponding to a
+ // given tile doesn't intersect with this, there is no need to check any particles.
+ const Real* base_dx = Geom(0).CellSize();
+ const Real z_min = z_new - base_dx[direction];
+ const Real z_max = z_old + base_dx[direction];
+
+ RealBox slice_box = Geom(0).ProbDomain();
+ slice_box.setLo(direction, z_min);
+ slice_box.setHi(direction, z_max);
+
+ diagnostic_particles.resize(finestLevel()+1);
+
+ for (int lev = 0; lev < nlevs; ++lev) {
+
+ const Real* dx = Geom(lev).CellSize();
+ const Real* plo = Geom(lev).ProbLo();
+
+ // first we touch each map entry in serial
+ for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti)
+ {
+ auto index = std::make_pair(pti.index(), pti.LocalTileIndex());
+ diagnostic_particles[lev][index];
+ }
+
+#ifdef _OPENMP
+#pragma omp parallel
+#endif
+ {
+ RealVector xp_new, yp_new, zp_new;
+
+ for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti)
+ {
+ const Box& box = pti.validbox();
+
+ auto index = std::make_pair(pti.index(), pti.LocalTileIndex());
+
+ const RealBox tile_real_box(box, dx, plo);
+
+ if ( !slice_box.intersects(tile_real_box) ) continue;
+
+ pti.GetPosition(xp_new, yp_new, zp_new);
+
+ auto& attribs = pti.GetAttribs();
+
+ auto& wp = attribs[PIdx::w ];
+
+ auto& uxp_new = attribs[PIdx::ux ];
+ auto& uyp_new = attribs[PIdx::uy ];
+ auto& uzp_new = attribs[PIdx::uz ];
+
+ auto& xp_old = attribs[PIdx::xold ];
+ auto& yp_old = attribs[PIdx::yold ];
+ auto& zp_old = attribs[PIdx::zold ];
+ auto& uxp_old = attribs[PIdx::uxold];
+ auto& uyp_old = attribs[PIdx::uyold];
+ auto& uzp_old = attribs[PIdx::uzold];
+
+ const long np = pti.numParticles();
+
+ Real uzfrm = -WarpX::gamma_boost*WarpX::beta_boost*PhysConst::c;
+ Real inv_c2 = 1.0/PhysConst::c/PhysConst::c;
+
+ for (long i = 0; i < np; ++i) {
+
+ // if the particle did not cross the plane of z_boost in the last
+ // timestep, skip it.
+ if ( not (((zp_new[i] >= z_new) && (zp_old[i] <= z_old)) ||
+ ((zp_new[i] <= z_new) && (zp_old[i] >= z_old))) ) continue;
+
+ // Lorentz transform particles to lab frame
+ Real gamma_new_p = std::sqrt(1.0 + inv_c2*(uxp_new[i]*uxp_new[i] + uyp_new[i]*uyp_new[i] + uzp_new[i]*uzp_new[i]));
+ Real t_new_p = WarpX::gamma_boost*t_boost - uzfrm*zp_new[i]*inv_c2;
+ Real z_new_p = WarpX::gamma_boost*(zp_new[i] + WarpX::beta_boost*PhysConst::c*t_boost);
+ Real uz_new_p = WarpX::gamma_boost*uzp_new[i] - gamma_new_p*uzfrm;
+
+ Real gamma_old_p = std::sqrt(1.0 + inv_c2*(uxp_old[i]*uxp_old[i] + uyp_old[i]*uyp_old[i] + uzp_old[i]*uzp_old[i]));
+ Real t_old_p = WarpX::gamma_boost*(t_boost - dt) - uzfrm*zp_old[i]*inv_c2;
+ Real z_old_p = WarpX::gamma_boost*(zp_old[i] + WarpX::beta_boost*PhysConst::c*(t_boost-dt));
+ Real uz_old_p = WarpX::gamma_boost*uzp_old[i] - gamma_old_p*uzfrm;
+
+ // interpolate in time to t_lab
+ Real weight_old = (t_new_p - t_lab) / (t_new_p - t_old_p);
+ Real weight_new = (t_lab - t_old_p) / (t_new_p - t_old_p);
+
+ Real xp = xp_old[i]*weight_old + xp_new[i]*weight_new;
+ Real yp = yp_old[i]*weight_old + yp_new[i]*weight_new;
+ Real zp = z_old_p *weight_old + z_new_p *weight_new;
+
+ Real uxp = uxp_old[i]*weight_old + uxp_new[i]*weight_new;
+ Real uyp = uyp_old[i]*weight_old + uyp_new[i]*weight_new;
+ Real uzp = uz_old_p *weight_old + uz_new_p *weight_new;
+
+ diagnostic_particles[lev][index].GetRealData(DiagIdx::w).push_back(wp[i]);
+
+ diagnostic_particles[lev][index].GetRealData(DiagIdx::x).push_back(xp);
+ diagnostic_particles[lev][index].GetRealData(DiagIdx::y).push_back(yp);
+ diagnostic_particles[lev][index].GetRealData(DiagIdx::z).push_back(zp);
+
+ diagnostic_particles[lev][index].GetRealData(DiagIdx::ux).push_back(uxp);
+ diagnostic_particles[lev][index].GetRealData(DiagIdx::uy).push_back(uyp);
+ diagnostic_particles[lev][index].GetRealData(DiagIdx::uz).push_back(uzp);
+ }
+ }
+ }
+ }
+#else
+ AMREX_ALWAYS_ASSERT_WITH_MESSAGE( false ,
+"ERROR: WarpX must be compiled with STORE_OLD_PARTICLE_ATTRIBS=TRUE to use the back-transformed diagnostics");
+#endif
+}
+
+int PhysicalParticleContainer::GetRefineFac(const Real x, const Real y, const Real z)
+{
+ if (finestLevel() == 0) return 1;
+ if (not WarpX::refine_plasma) return 1;
+
+ IntVect iv;
+ const Geometry& geom = Geom(0);
+
+ std::array<Real, 3> offset;
+
+#if ( AMREX_SPACEDIM == 3)
+ offset[0] = geom.ProbLo(0);
+ offset[1] = geom.ProbLo(1);
+ offset[2] = geom.ProbLo(2);
+#elif ( AMREX_SPACEDIM == 2 )
+ offset[0] = geom.ProbLo(0);
+ offset[1] = 0.0;
+ offset[2] = geom.ProbLo(1);
+#endif
+
+ AMREX_D_TERM(iv[0]=static_cast<int>(floor((x-offset[0])*geom.InvCellSize(0)));,
+ iv[1]=static_cast<int>(floor((y-offset[1])*geom.InvCellSize(1)));,
+ iv[2]=static_cast<int>(floor((z-offset[2])*geom.InvCellSize(2))););
+
+ iv += geom.Domain().smallEnd();
+
+ const int dir = WarpX::moving_window_dir;
+
+ IntVect iv2 = iv;
+ iv2[dir] = 0;
+
+ if ( (*m_refined_injection_mask)(iv2) != -1) return (*m_refined_injection_mask)(iv2);
+
+ int ref_fac = 1;
+ for (int lev = 0; lev < finestLevel(); ++lev)
+ {
+ const IntVect rr = m_gdb->refRatio(lev);
+ const BoxArray& fine_ba = this->ParticleBoxArray(lev+1);
+ const int num_boxes = fine_ba.size();
+ Vector<Box> stretched_boxes;
+ const int safety_factor = 4;
+ for (int i = 0; i < num_boxes; ++i)
+ {
+ Box bx = fine_ba[i];
+ bx.coarsen(ref_fac*rr[dir]);
+ bx.setSmall(dir, std::numeric_limits<int>::min()/safety_factor);
+ bx.setBig(dir, std::numeric_limits<int>::max()/safety_factor);
+ stretched_boxes.push_back(bx);
+ }
+
+ BoxArray stretched_ba(stretched_boxes.dataPtr(), stretched_boxes.size());
+
+ const int num_ghost = 0;
+ if ( stretched_ba.intersects(Box(iv, iv), num_ghost) )
+ {
+ ref_fac *= rr[dir];
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ (*m_refined_injection_mask)(iv2) = ref_fac;
+
+ return ref_fac;
+}
diff --git a/Source/Particles/RigidInjectedParticleContainer.H b/Source/Particles/RigidInjectedParticleContainer.H
new file mode 100644
index 000000000..8a9ac9e6e
--- /dev/null
+++ b/Source/Particles/RigidInjectedParticleContainer.H
@@ -0,0 +1,81 @@
+#ifndef WARPX_RigidInjectedParticleContainer_H_
+#define WARPX_RigidInjectedParticleContainer_H_
+
+#include <PhysicalParticleContainer.H>
+#include <AMReX_Vector.H>
+
+class RigidInjectedParticleContainer
+ : public PhysicalParticleContainer
+{
+public:
+ RigidInjectedParticleContainer (amrex::AmrCore* amr_core,
+ int ispecies,
+ const std::string& name);
+ virtual ~RigidInjectedParticleContainer () {}
+
+ virtual void InitData() override;
+
+ virtual void RemapParticles();
+ virtual void BoostandRemapParticles();
+
+ virtual void Evolve (int lev,
+ const amrex::MultiFab& Ex,
+ const amrex::MultiFab& Ey,
+ const amrex::MultiFab& Ez,
+ const amrex::MultiFab& Bx,
+ const amrex::MultiFab& By,
+ const amrex::MultiFab& Bz,
+ amrex::MultiFab& jx,
+ amrex::MultiFab& jy,
+ amrex::MultiFab& jz,
+ amrex::MultiFab* cjx,
+ amrex::MultiFab* cjy,
+ amrex::MultiFab* cjz,
+ amrex::MultiFab* rho,
+ amrex::MultiFab* crho,
+ const amrex::MultiFab* cEx,
+ const amrex::MultiFab* cEy,
+ const amrex::MultiFab* cEz,
+ const amrex::MultiFab* cBx,
+ const amrex::MultiFab* cBy,
+ const amrex::MultiFab* cBz,
+ amrex::Real t,
+ amrex::Real dt) override;
+
+ virtual void PushPX(WarpXParIter& pti,
+ amrex::Cuda::DeviceVector<amrex::Real>& xp,
+ amrex::Cuda::DeviceVector<amrex::Real>& yp,
+ amrex::Cuda::DeviceVector<amrex::Real>& zp,
+ amrex::Cuda::DeviceVector<amrex::Real>& giv,
+ amrex::Real dt) override;
+
+ virtual void PushP (int lev, amrex::Real dt,
+ const amrex::MultiFab& Ex,
+ const amrex::MultiFab& Ey,
+ const amrex::MultiFab& Ez,
+ const amrex::MultiFab& Bx,
+ const amrex::MultiFab& By,
+ const amrex::MultiFab& Bz) override;
+
+private:
+
+ // User input quantities
+ amrex::Real zinject_plane = 0.;
+ bool projected = true; // When true, particle transverse positions are directly projected (without adjusment)
+ bool focused = false; // When true, particle transverse positions are adjusted to account for distance between zinject and z=0
+ bool rigid_advance = true; // When true, particles are advance with vzbar before injection
+
+ amrex::Real vzbeam_ave_boosted;
+
+ amrex::Vector<int> done_injecting;
+ amrex::Vector<amrex::Real> zinject_plane_levels;
+
+ // Temporary quantites
+ amrex::Real zinject_plane_lev;
+ amrex::Real zinject_plane_lev_previous;
+ amrex::Vector<int> done_injecting_temp;
+ bool done_injecting_lev;
+
+};
+
+#endif
diff --git a/Source/Particles/RigidInjectedParticleContainer.cpp b/Source/Particles/RigidInjectedParticleContainer.cpp
new file mode 100644
index 000000000..db3623705
--- /dev/null
+++ b/Source/Particles/RigidInjectedParticleContainer.cpp
@@ -0,0 +1,462 @@
+#include <limits>
+#include <sstream>
+#include <algorithm>
+
+#ifdef _OPENMP
+#include <omp.h>
+#endif
+
+#include <RigidInjectedParticleContainer.H>
+#include <WarpX_f.H>
+#include <WarpX.H>
+#include <WarpXConst.H>
+
+using namespace amrex;
+
+RigidInjectedParticleContainer::RigidInjectedParticleContainer (AmrCore* amr_core, int ispecies,
+ const std::string& name)
+ : PhysicalParticleContainer(amr_core, ispecies, name)
+{
+
+ ParmParse pp(species_name);
+
+ pp.get("zinject_plane", zinject_plane);
+ pp.query("projected", projected);
+ pp.query("focused", focused);
+ pp.query("rigid_advance", rigid_advance);
+
+}
+
+void RigidInjectedParticleContainer::InitData()
+{
+ done_injecting.resize(finestLevel()+1, 0);
+ zinject_plane_levels.resize(finestLevel()+1, zinject_plane/WarpX::gamma_boost);
+
+ AddParticles(0); // Note - add on level 0
+
+ // Particles added by AddParticles should already be in the boosted frame
+ RemapParticles();
+
+ Redistribute(); // We then redistribute
+}
+
+void
+RigidInjectedParticleContainer::RemapParticles()
+{
+
+ AMREX_ALWAYS_ASSERT_WITH_MESSAGE(projected, "ERROR: projected = false is not supported with this particle loading");
+ AMREX_ALWAYS_ASSERT_WITH_MESSAGE(!focused, "ERROR: focused = true is not supported with this particle loading");
+
+ // For rigid_advance == false, nothing needs to be done
+
+ if (rigid_advance) {
+
+ // The particle z positions are adjusted to account for the difference between
+ // advancing with vzbar and wih vz[i] before injection
+
+ // For now, start with the assumption that this will only happen
+ // at the start of the simulation.
+ const Real t_lab = 0.;
+
+ const Real uz_boost = WarpX::gamma_boost*WarpX::beta_boost*PhysConst::c;
+ const Real csq = PhysConst::c*PhysConst::c;
+
+ vzbeam_ave_boosted = meanParticleVelocity(false)[2];
+
+ for (int lev = 0; lev <= finestLevel(); lev++) {
+
+#ifdef _OPENMP
+#pragma omp parallel
+#endif
+ {
+ // Get the average beam velocity in the boosted frame.
+ // Note that the particles are already in the boosted frame.
+ // This value is saved to advance the particles not injected yet
+
+ Cuda::DeviceVector<Real> xp, yp, zp;
+
+ for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti)
+ {
+
+ auto& attribs = pti.GetAttribs();
+ auto& uxp = attribs[PIdx::ux];
+ auto& uyp = attribs[PIdx::uy];
+ auto& uzp = attribs[PIdx::uz];
+
+ // Copy data from particle container to temp arrays
+ pti.GetPosition(xp, yp, zp);
+
+ // Loop over particles
+ const long np = pti.numParticles();
+ for (int i=0 ; i < np ; i++) {
+
+ const Real gammapr = std::sqrt(1. + (uxp[i]*uxp[i] + uyp[i]*uyp[i] + uzp[i]*uzp[i])/csq);
+ const Real vzpr = uzp[i]/gammapr;
+
+ // Back out the value of z_lab
+ const Real z_lab = (zp[i] + uz_boost*t_lab + WarpX::gamma_boost*t_lab*vzpr)/(WarpX::gamma_boost + uz_boost*vzpr/csq);
+
+ // Time of the particle in the boosted frame given its position in the lab frame at t=0.
+ const Real tpr = WarpX::gamma_boost*t_lab - uz_boost*z_lab/csq;
+
+ // Adjust the position, taking away its motion from its own velocity and adding
+ // the motion from the average velocity
+ zp[i] = zp[i] + tpr*vzpr - tpr*vzbeam_ave_boosted;
+
+ }
+
+ // Copy the data back to the particle container
+ pti.SetPosition(xp, yp, zp);
+
+ }
+ }
+ }
+ }
+}
+
+
+void
+RigidInjectedParticleContainer::BoostandRemapParticles()
+{
+
+ // Boost the particles into the boosted frame and map the particles
+ // to the t=0 in the boosted frame. If using rigid_advance, the z position
+ // is adjusted using vzbar, otherwise using vz[i]
+
+ if (rigid_advance) {
+ // Get the average beam velocity in the boosted frame
+ // This value is saved to advance the particles not injected yet
+ const Real vzbeam_ave_lab = meanParticleVelocity(false)[2];
+ vzbeam_ave_boosted = (vzbeam_ave_lab - WarpX::beta_boost*PhysConst::c)/(1. - vzbeam_ave_lab*WarpX::beta_boost/PhysConst::c);
+ }
+
+#ifdef _OPENMP
+#pragma omp parallel
+#endif
+ {
+ Cuda::DeviceVector<Real> xp, yp, zp;
+
+ for (WarpXParIter pti(*this, 0); pti.isValid(); ++pti)
+ {
+
+ auto& attribs = pti.GetAttribs();
+ auto& uxp = attribs[PIdx::ux];
+ auto& uyp = attribs[PIdx::uy];
+ auto& uzp = attribs[PIdx::uz];
+
+ // Copy data from particle container to temp arrays
+ pti.GetPosition(xp, yp, zp);
+
+ // Loop over particles
+ const long np = pti.numParticles();
+ for (int i=0 ; i < np ; i++) {
+
+ const Real gamma_lab = std::sqrt(1. + (uxp[i]*uxp[i] + uyp[i]*uyp[i] + uzp[i]*uzp[i])/(PhysConst::c*PhysConst::c));
+
+ const Real vx_lab = uxp[i]/gamma_lab;
+ const Real vy_lab = uyp[i]/gamma_lab;
+ const Real vz_lab = uzp[i]/gamma_lab;
+
+ // t0_lab is the time in the lab frame that the particles reaches z=0
+ // The location and time (z=0, t=0) is a synchronization point between the
+ // lab and boosted frames.
+ const Real t0_lab = -zp[i]/vz_lab;
+
+ if (!projected) {
+ xp[i] += t0_lab*vx_lab;
+ yp[i] += t0_lab*vy_lab;
+ }
+ if (focused) {
+ // Correct for focusing effect from shift from z=0 to zinject
+ const Real tfocus = -zinject_plane*WarpX::gamma_boost/vz_lab;
+ xp[i] -= tfocus*vx_lab;
+ yp[i] -= tfocus*vy_lab;
+ }
+
+ // Time of the particle in the boosted frame given its position in the lab frame at t=0.
+ const Real tpr = -WarpX::gamma_boost*WarpX::beta_boost*zp[i]/PhysConst::c;
+
+ // Position of the particle in the boosted frame given its position in the lab frame at t=0.
+ const Real zpr = WarpX::gamma_boost*zp[i];
+
+ // Momentum of the particle in the boosted frame (assuming that it is fixed).
+ uzp[i] = WarpX::gamma_boost*(uzp[i] - WarpX::beta_boost*PhysConst::c*gamma_lab);
+
+ // Put the particle at the location in the boosted frame at boost frame t=0,
+ if (rigid_advance) {
+ // with the particle moving at the average velocity
+ zp[i] = zpr - vzbeam_ave_boosted*tpr;
+ }
+ else {
+ // with the particle moving with its own velocity
+ const Real gammapr = std::sqrt(1. + (uxp[i]*uxp[i] + uyp[i]*uyp[i] + uzp[i]*uzp[i])/(PhysConst::c*PhysConst::c));
+ const Real vzpr = uzp[i]/gammapr;
+ zp[i] = zpr - vzpr*tpr;
+ }
+
+ }
+
+ // Copy the data back to the particle container
+ pti.SetPosition(xp, yp, zp);
+
+ }
+ }
+}
+
+void
+RigidInjectedParticleContainer::PushPX(WarpXParIter& pti,
+ Cuda::DeviceVector<Real>& xp,
+ Cuda::DeviceVector<Real>& yp,
+ Cuda::DeviceVector<Real>& zp,
+ Cuda::DeviceVector<Real>& giv,
+ Real dt)
+{
+
+ // This wraps the call to warpx_particle_pusher so that inheritors can modify the call.
+ auto& attribs = pti.GetAttribs();
+ auto& uxp = attribs[PIdx::ux];
+ auto& uyp = attribs[PIdx::uy];
+ auto& uzp = attribs[PIdx::uz];
+ auto& Exp = attribs[PIdx::Ex];
+ auto& Eyp = attribs[PIdx::Ey];
+ auto& Ezp = attribs[PIdx::Ez];
+ auto& Bxp = attribs[PIdx::Bx];
+ auto& Byp = attribs[PIdx::By];
+ auto& Bzp = attribs[PIdx::Bz];
+ const long np = pti.numParticles();
+
+#ifdef WARPX_STORE_OLD_PARTICLE_ATTRIBS
+ auto& xpold = attribs[PIdx::xold];
+ auto& ypold = attribs[PIdx::yold];
+ auto& zpold = attribs[PIdx::zold];
+ auto& uxpold = attribs[PIdx::uxold];
+ auto& uypold = attribs[PIdx::uyold];
+ auto& uzpold = attribs[PIdx::uzold];
+
+ warpx_copy_attribs(&np, xp.dataPtr(), yp.dataPtr(), zp.dataPtr(),
+ uxp.dataPtr(), uyp.dataPtr(), uzp.dataPtr(),
+ xpold.dataPtr(), ypold.dataPtr(), zpold.dataPtr(),
+ uxpold.dataPtr(), uypold.dataPtr(), uzpold.dataPtr());
+
+#endif
+
+ // Save the position and momenta, making copies
+ Cuda::DeviceVector<Real> xp_save, yp_save, zp_save;
+ RealVector uxp_save, uyp_save, uzp_save;
+
+ if (!done_injecting_lev) {
+ xp_save = xp;
+ yp_save = yp;
+ zp_save = zp;
+ uxp_save = uxp;
+ uyp_save = uyp;
+ uzp_save = uzp;
+ // Scale the fields of particles about to cross the injection plane.
+ // This only approximates what should be happening. The particles
+ // should by advanced a fraction of a time step instead.
+ // Scaling the fields is much easier and may be good enough.
+ for (int i=0 ; i < zp.size() ; i++) {
+ const Real dtscale = dt - (zinject_plane_lev_previous - zp[i])/(vzbeam_ave_boosted + WarpX::beta_boost*PhysConst::c);
+ if (0. < dtscale && dtscale < dt) {
+ Exp[i] *= dtscale;
+ Eyp[i] *= dtscale;
+ Ezp[i] *= dtscale;
+ Bxp[i] *= dtscale;
+ Byp[i] *= dtscale;
+ Bzp[i] *= dtscale;
+ }
+ }
+ }
+
+ warpx_particle_pusher(&np,
+ xp.dataPtr(),
+ yp.dataPtr(),
+ zp.dataPtr(),
+ uxp.dataPtr(), uyp.dataPtr(), uzp.dataPtr(),
+ giv.dataPtr(),
+ Exp.dataPtr(), Eyp.dataPtr(), Ezp.dataPtr(),
+ Bxp.dataPtr(), Byp.dataPtr(), Bzp.dataPtr(),
+ &this->charge, &this->mass, &dt,
+ &WarpX::particle_pusher_algo);
+
+ if (!done_injecting_lev) {
+#ifdef _OPENMP
+ const int tid = omp_get_thread_num();
+#else
+ const int tid = 0;
+#endif
+ // Undo the push for particles not injected yet.
+ // The zp are advanced a fixed amount.
+ for (int i=0 ; i < zp.size() ; i++) {
+ if (zp[i] <= zinject_plane_lev) {
+ uxp[i] = uxp_save[i];
+ uyp[i] = uyp_save[i];
+ uzp[i] = uzp_save[i];
+ giv[i] = 1./std::sqrt(1. + (uxp[i]*uxp[i] + uyp[i]*uyp[i] + uzp[i]*uzp[i])/(PhysConst::c*PhysConst::c));
+ xp[i] = xp_save[i];
+ yp[i] = yp_save[i];
+ if (rigid_advance) {
+ zp[i] = zp_save[i] + dt*vzbeam_ave_boosted;
+ }
+ else {
+ zp[i] = zp_save[i] + dt*uzp[i]*giv[i];
+ }
+ done_injecting_temp[tid] = 0;
+ }
+ }
+ }
+
+}
+
+void
+RigidInjectedParticleContainer::Evolve (int lev,
+ const MultiFab& Ex, const MultiFab& Ey, const MultiFab& Ez,
+ const MultiFab& Bx, const MultiFab& By, const MultiFab& Bz,
+ MultiFab& jx, MultiFab& jy, MultiFab& jz,
+ MultiFab* cjx, MultiFab* cjy, MultiFab* cjz,
+ MultiFab* rho, MultiFab* crho,
+ const MultiFab* cEx, const MultiFab* cEy, const MultiFab* cEz,
+ const MultiFab* cBx, const MultiFab* cBy, const MultiFab* cBz,
+ Real t, Real dt)
+{
+
+ // Update location of injection plane in the boosted frame
+ zinject_plane_lev_previous = zinject_plane_levels[lev];
+ zinject_plane_levels[lev] -= dt*WarpX::beta_boost*PhysConst::c;
+ zinject_plane_lev = zinject_plane_levels[lev];
+
+ // Setup check of whether more particles need to be injected
+#ifdef _OPENMP
+ const int nthreads = omp_get_max_threads();
+#else
+ const int nthreads = 1;
+#endif
+ done_injecting_temp.assign(nthreads, 1); // We do not use bool because vector<bool> is special.
+ done_injecting_lev = done_injecting[lev];
+
+ PhysicalParticleContainer::Evolve (lev,
+ Ex, Ey, Ez,
+ Bx, By, Bz,
+ jx, jy, jz,
+ cjx, cjy, cjz,
+ rho, crho,
+ cEx, cEy, cEz,
+ cBx, cBy, cBz,
+ t, dt);
+
+ // Check if all done_injecting_temp are still true.
+ done_injecting[lev] = std::all_of(done_injecting_temp.begin(), done_injecting_temp.end(),
+ [](int i) -> bool { return i; });
+}
+
+void
+RigidInjectedParticleContainer::PushP (int lev, Real dt,
+ const MultiFab& Ex, const MultiFab& Ey, const MultiFab& Ez,
+ const MultiFab& Bx, const MultiFab& By, const MultiFab& Bz)
+{
+ if (do_not_push) return;
+
+ const std::array<Real,3>& dx = WarpX::CellSize(lev);
+
+#ifdef _OPENMP
+#pragma omp parallel
+#endif
+ {
+ Cuda::DeviceVector<Real> xp, yp, zp, giv;
+
+ for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti)
+ {
+ const Box& box = pti.validbox();
+
+ auto& attribs = pti.GetAttribs();
+
+ auto& uxp = attribs[PIdx::ux];
+ auto& uyp = attribs[PIdx::uy];
+ auto& uzp = attribs[PIdx::uz];
+ auto& Exp = attribs[PIdx::Ex];
+ auto& Eyp = attribs[PIdx::Ey];
+ auto& Ezp = attribs[PIdx::Ez];
+ auto& Bxp = attribs[PIdx::Bx];
+ auto& Byp = attribs[PIdx::By];
+ auto& Bzp = attribs[PIdx::Bz];
+
+ const long np = pti.numParticles();
+
+ // Data on the grid
+ const FArrayBox& exfab = Ex[pti];
+ const FArrayBox& eyfab = Ey[pti];
+ const FArrayBox& ezfab = Ez[pti];
+ const FArrayBox& bxfab = Bx[pti];
+ const FArrayBox& byfab = By[pti];
+ const FArrayBox& bzfab = Bz[pti];
+
+ Exp.assign(np,0.0);
+ Eyp.assign(np,0.0);
+ Ezp.assign(np,0.0);
+ Bxp.assign(np,WarpX::B_external[0]);
+ Byp.assign(np,WarpX::B_external[1]);
+ Bzp.assign(np,WarpX::B_external[2]);
+
+ giv.resize(np);
+
+ //
+ // copy data from particle container to temp arrays
+ //
+ pti.GetPosition(xp, yp, zp);
+
+ const std::array<Real,3>& xyzmin_grid = WarpX::LowerCorner(box, lev);
+ const int* ixyzmin_grid = box.loVect();
+
+ const int ll4symtry = false;
+ const int l_lower_order_in_v = true;
+ long lvect_fieldgathe = 64;
+ warpx_geteb_energy_conserving(
+ &np,
+ xp.dataPtr(),
+ yp.dataPtr(),
+ zp.dataPtr(),
+ Exp.dataPtr(),Eyp.dataPtr(),Ezp.dataPtr(),
+ Bxp.dataPtr(),Byp.dataPtr(),Bzp.dataPtr(),
+ ixyzmin_grid,
+ &xyzmin_grid[0], &xyzmin_grid[1], &xyzmin_grid[2],
+ &dx[0], &dx[1], &dx[2],
+ &WarpX::nox, &WarpX::noy, &WarpX::noz,
+ BL_TO_FORTRAN_ANYD(exfab),
+ BL_TO_FORTRAN_ANYD(eyfab),
+ BL_TO_FORTRAN_ANYD(ezfab),
+ BL_TO_FORTRAN_ANYD(bxfab),
+ BL_TO_FORTRAN_ANYD(byfab),
+ BL_TO_FORTRAN_ANYD(bzfab),
+ &ll4symtry, &l_lower_order_in_v, &WarpX::do_nodal,
+ &lvect_fieldgathe, &WarpX::field_gathering_algo);
+
+ // Save the position and momenta, making copies
+ auto uxp_save = uxp;
+ auto uyp_save = uyp;
+ auto uzp_save = uzp;
+
+ warpx_particle_pusher_momenta(&np,
+ xp.dataPtr(),
+ yp.dataPtr(),
+ zp.dataPtr(),
+ uxp.dataPtr(), uyp.dataPtr(), uzp.dataPtr(),
+ giv.dataPtr(),
+ Exp.dataPtr(), Eyp.dataPtr(), Ezp.dataPtr(),
+ Bxp.dataPtr(), Byp.dataPtr(), Bzp.dataPtr(),
+ &this->charge, &this->mass, &dt,
+ &WarpX::particle_pusher_algo);
+
+ // Undo the push for particles not injected yet.
+ // It is assumed that PushP will only be called on the first and last steps
+ // and that no particles will cross zinject_plane.
+ for (int i=0 ; i < zp.size() ; i++) {
+ if (zp[i] <= zinject_plane_levels[lev]) {
+ uxp[i] = uxp_save[i];
+ uyp[i] = uyp_save[i];
+ uzp[i] = uzp_save[i];
+ }
+ }
+
+ }
+ }
+}
diff --git a/Source/Particles/WarpXParticleContainer.H b/Source/Particles/WarpXParticleContainer.H
new file mode 100644
index 000000000..d11ce49c9
--- /dev/null
+++ b/Source/Particles/WarpXParticleContainer.H
@@ -0,0 +1,233 @@
+#ifndef WARPX_WarpXParticleContainer_H_
+#define WARPX_WarpXParticleContainer_H_
+
+#include <memory>
+
+#include <AMReX_Particles.H>
+#include <AMReX_AmrCore.H>
+
+struct PIdx
+{
+ enum { // Particle Attributes stored in amrex::ParticleContainer's struct of array
+ w = 0, // weight
+ ux, uy, uz, Ex, Ey, Ez, Bx, By, Bz,
+#if (BL_SPACEDIM == 2) && WARPX_RZ
+ theta, // RZ needs all three position components
+#endif
+#ifdef WARPX_STORE_OLD_PARTICLE_ATTRIBS
+ xold, yold, zold, uxold, uyold, uzold,
+#endif
+ nattribs
+ };
+};
+
+struct DiagIdx
+{
+ enum {
+ w = 0,
+ x, y, z, ux, uy, uz,
+ nattribs
+ };
+};
+
+class WarpXParIter
+ : public amrex::ParIter<0,0,PIdx::nattribs>
+{
+public:
+ using amrex::ParIter<0,0,PIdx::nattribs>::ParIter;
+
+ WarpXParIter (ContainerType& pc, int level);
+
+#if (AMREX_SPACEDIM == 2)
+ void GetPosition (amrex::Cuda::DeviceVector<amrex::Real>& x,
+ amrex::Cuda::DeviceVector<amrex::Real>& y,
+ amrex::Cuda::DeviceVector<amrex::Real>& z) const;
+ void SetPosition (const amrex::Cuda::DeviceVector<amrex::Real>& x,
+ const amrex::Cuda::DeviceVector<amrex::Real>& y,
+ const amrex::Cuda::DeviceVector<amrex::Real>& z);
+#endif
+
+ const std::array<RealVector, PIdx::nattribs>& GetAttribs () const {
+ return GetStructOfArrays().GetRealData();
+ }
+
+ std::array<RealVector, PIdx::nattribs>& GetAttribs () {
+ return GetStructOfArrays().GetRealData();
+ }
+
+ const RealVector& GetAttribs (int comp) const {
+ return GetStructOfArrays().GetRealData(comp);
+ }
+
+ RealVector& GetAttribs (int comp) {
+ return GetStructOfArrays().GetRealData(comp);
+ }
+};
+
+class MultiParticleContainer;
+
+class WarpXParticleContainer
+ : public amrex::ParticleContainer<0,0,PIdx::nattribs>
+{
+public:
+ friend MultiParticleContainer;
+
+ using DiagnosticParticleData = amrex::StructOfArrays<DiagIdx::nattribs, 0>;
+ using DiagnosticParticles = amrex::Vector<std::map<std::pair<int, int>, DiagnosticParticleData> >;
+
+ WarpXParticleContainer (amrex::AmrCore* amr_core, int ispecies);
+ virtual ~WarpXParticleContainer() {}
+
+ virtual void InitData () = 0;
+
+ virtual void FieldGatherES (const amrex::Vector<std::array<std::unique_ptr<amrex::MultiFab>, 3> >& E,
+ const amrex::Vector<std::unique_ptr<amrex::FabArray<amrex::BaseFab<int> > > >& masks) {}
+
+ virtual void FieldGather (int lev,
+ const amrex::MultiFab& Ex, const amrex::MultiFab& Ey, const amrex::MultiFab& Ez,
+ const amrex::MultiFab& Bx, const amrex::MultiFab& By, const amrex::MultiFab& Bz) {}
+
+#ifdef WARPX_DO_ELECTROSTATIC
+ virtual void EvolveES (const amrex::Vector<std::array<std::unique_ptr<amrex::MultiFab>, 3> >& E,
+ amrex::Vector<std::unique_ptr<amrex::MultiFab> >& rho,
+ amrex::Real t, amrex::Real dt) = 0;
+#endif // WARPX_DO_ELECTROSTATIC
+
+ virtual void Evolve (int lev,
+ const amrex::MultiFab& Ex, const amrex::MultiFab& Ey, const amrex::MultiFab& Ez,
+ const amrex::MultiFab& Bx, const amrex::MultiFab& By, const amrex::MultiFab& Bz,
+ amrex::MultiFab& jx, amrex::MultiFab& jy, amrex::MultiFab& jz,
+ amrex::MultiFab* cjx, amrex::MultiFab* cjy, amrex::MultiFab* cjz,
+ amrex::MultiFab* rho, amrex::MultiFab* crho,
+ const amrex::MultiFab* cEx, const amrex::MultiFab* cEy, const amrex::MultiFab* cEz,
+ const amrex::MultiFab* cBx, const amrex::MultiFab* cBy, const amrex::MultiFab* cBz,
+ amrex::Real t, amrex::Real dt) = 0;
+
+ virtual void PostRestart () = 0;
+
+ virtual void GetParticleSlice(const int direction, const amrex::Real z_old,
+ const amrex::Real z_new, const amrex::Real t_boost,
+ const amrex::Real t_lab, const amrex::Real dt,
+ DiagnosticParticles& diagnostic_particles) {}
+
+ void AllocData ();
+
+ ///
+ /// This pushes the particle positions by one half time step.
+ /// It is used to desynchronize the particles after initializaton
+ /// or when restarting from a checkpoint.
+ /// This is the electrostatic version of the particle push.
+ ///
+ void PushXES (amrex::Real dt);
+
+ ///
+ /// This pushes the particle positions by one half time step.
+ /// It is used to desynchronize the particles after initializaton
+ /// or when restarting from a checkpoint.
+ /// This is the electromagnetic version of the particle push.
+ ///
+ void PushX ( amrex::Real dt);
+ void PushX (int lev, amrex::Real dt);
+
+ ///
+ /// This pushes the particle momenta by dt.
+ ///
+ virtual void PushP (int lev, amrex::Real dt,
+ const amrex::MultiFab& Ex,
+ const amrex::MultiFab& Ey,
+ const amrex::MultiFab& Ez,
+ const amrex::MultiFab& Bx,
+ const amrex::MultiFab& By,
+ const amrex::MultiFab& Bz) = 0;
+
+ void DepositCharge(amrex::Vector<std::unique_ptr<amrex::MultiFab> >& rho,
+ bool local = false);
+ std::unique_ptr<amrex::MultiFab> GetChargeDensity(int lev, bool local = false);
+
+ virtual void DepositCharge(WarpXParIter& pti,
+ RealVector& wp,
+ amrex::MultiFab* rhomf,
+ amrex::MultiFab* crhomf,
+ int icomp,
+ const long np_current,
+ const long np,
+ int thread_num,
+ int lev );
+
+ virtual void DepositCurrent(WarpXParIter& pti,
+ RealVector& wp,
+ RealVector& uxp,
+ RealVector& uyp,
+ RealVector& uzp,
+ amrex::MultiFab& jx,
+ amrex::MultiFab& jy,
+ amrex::MultiFab& jz,
+ amrex::MultiFab* cjx,
+ amrex::MultiFab* cjy,
+ amrex::MultiFab* cjz,
+ const long np_current,
+ const long np,
+ int thread_num,
+ int lev,
+ amrex::Real dt );
+
+ ///
+ /// This returns the total charge for all the particles in this ParticleContainer.
+ /// This is needed when solving Poisson's equation with periodic boundary conditions.
+ ///
+ amrex::Real sumParticleCharge(bool local = false);
+
+ std::array<amrex::Real, 3> meanParticleVelocity(bool local = false);
+
+ amrex::Real maxParticleVelocity(bool local = false);
+
+ void AddNParticles (int lev,
+ int n, const amrex::Real* x, const amrex::Real* y, const amrex::Real* z,
+ const amrex::Real* vx, const amrex::Real* vy, const amrex::Real* vz,
+ int nattr, const amrex::Real* attr, int uniqueparticles, int id=-1);
+
+ void AddOneParticle (int lev, int grid, int tile,
+ amrex::Real x, amrex::Real y, amrex::Real z,
+ const std::array<amrex::Real,PIdx::nattribs>& attribs);
+
+ void AddOneParticle (ParticleTileType& particle_tile,
+ amrex::Real x, amrex::Real y, amrex::Real z,
+ const std::array<amrex::Real,PIdx::nattribs>& attribs);
+
+ void ReadHeader (std::istream& is);
+
+ void WriteHeader (std::ostream& os) const;
+
+ static void ReadParameters ();
+
+ static int NextID () { return ParticleType::NextID(); }
+
+ bool do_splitting = false;
+
+ // split along axes (0) or diagonals (1)
+ int split_type = 0;
+
+protected:
+
+ int species_id;
+
+ amrex::Real charge;
+ amrex::Real mass;
+
+ bool deposit_on_main_grid = false;
+
+ static int do_not_push;
+
+ amrex::Vector<std::unique_ptr<amrex::FArrayBox> > local_rho;
+ amrex::Vector<std::unique_ptr<amrex::FArrayBox> > local_jx;
+ amrex::Vector<std::unique_ptr<amrex::FArrayBox> > local_jy;
+ amrex::Vector<std::unique_ptr<amrex::FArrayBox> > local_jz;
+
+ amrex::Vector<amrex::Cuda::DeviceVector<amrex::Real> > m_xp, m_yp, m_zp, m_giv;
+
+private:
+ virtual void particlePostLocate(ParticleType& p, const amrex::ParticleLocData& pld,
+ const int lev) override;
+};
+
+#endif
diff --git a/Source/Particles/WarpXParticleContainer.cpp b/Source/Particles/WarpXParticleContainer.cpp
new file mode 100644
index 000000000..1ba82e3d3
--- /dev/null
+++ b/Source/Particles/WarpXParticleContainer.cpp
@@ -0,0 +1,1072 @@
+
+#include <limits>
+
+#include <MultiParticleContainer.H>
+#include <WarpXParticleContainer.H>
+#include <AMReX_AmrParGDB.H>
+#include <WarpX_f.H>
+#include <WarpX.H>
+
+using namespace amrex;
+
+int WarpXParticleContainer::do_not_push = 0;
+
+WarpXParIter::WarpXParIter (ContainerType& pc, int level)
+ : ParIter(pc, level, MFItInfo().SetDynamic(WarpX::do_dynamic_scheduling))
+{
+}
+
+#if (AMREX_SPACEDIM == 2)
+void
+WarpXParIter::GetPosition (Cuda::DeviceVector<Real>& x, Cuda::DeviceVector<Real>& y, Cuda::DeviceVector<Real>& z) const
+{
+ amrex::ParIter<0,0,PIdx::nattribs>::GetPosition(x, z);
+#ifdef WARPX_RZ
+ const auto& attribs = GetAttribs();
+ const auto& theta = attribs[PIdx::theta];
+ y.resize(x.size());
+ for (unsigned int i=0 ; i < x.size() ; i++) {
+ // The x stored in the particles is actually the radius
+ y[i] = x[i]*std::sin(theta[i]);
+ x[i] = x[i]*std::cos(theta[i]);
+ }
+#else
+ y.resize(x.size(), std::numeric_limits<Real>::quiet_NaN());
+#endif
+}
+
+void
+WarpXParIter::SetPosition (const Cuda::DeviceVector<Real>& x, const Cuda::DeviceVector<Real>& y, const Cuda::DeviceVector<Real>& z)
+{
+#ifdef WARPX_RZ
+ const auto& attribs = GetAttribs();
+ const auto& theta = attribs[PIdx::theta];
+ for (unsigned int i=0 ; i < x.size() ; i++) {
+ theta[i] = std::atan2(y[i], x[i]);
+ x[i] = std::sqrt(x[i]*x[i] + y[i]*y[i]);
+ }
+#endif
+ amrex::ParIter<0,0,PIdx::nattribs>::SetPosition(x, z);
+}
+#endif
+
+WarpXParticleContainer::WarpXParticleContainer (AmrCore* amr_core, int ispecies)
+ : ParticleContainer<0,0,PIdx::nattribs>(amr_core->GetParGDB())
+ , species_id(ispecies)
+{
+ for (unsigned int i = PIdx::Ex; i <= PIdx::Bz; ++i) {
+ communicate_real_comp[i] = false; // Don't need to communicate E and B.
+ }
+ SetParticleSize();
+ ReadParameters();
+
+ // Initialize temporary local arrays for charge/current deposition
+ int num_threads = 1;
+ #ifdef _OPENMP
+ #pragma omp parallel
+ #pragma omp single
+ num_threads = omp_get_num_threads();
+ #endif
+ local_rho.resize(num_threads);
+ local_jx.resize(num_threads);
+ local_jy.resize(num_threads);
+ local_jz.resize(num_threads);
+ m_xp.resize(num_threads);
+ m_yp.resize(num_threads);
+ m_zp.resize(num_threads);
+ m_giv.resize(num_threads);
+ for (int i = 0; i < num_threads; ++i)
+ {
+ local_rho[i].reset(nullptr);
+ local_jx[i].reset(nullptr);
+ local_jy[i].reset(nullptr);
+ local_jz[i].reset(nullptr);
+ }
+
+}
+
+void
+WarpXParticleContainer::ReadParameters ()
+{
+ static bool initialized = false;
+ if (!initialized)
+ {
+ ParmParse pp("particles");
+
+#ifdef AMREX_USE_GPU
+ do_tiling = false; // By default, tiling is off on GPU
+#else
+ do_tiling = true;
+#endif
+ pp.query("do_tiling", do_tiling);
+ pp.query("do_not_push", do_not_push);
+
+ initialized = true;
+ }
+}
+
+void
+WarpXParticleContainer::AllocData ()
+{
+ // have to resize here, not in the constructor because grids have not
+ // been built when constructor was called.
+ reserveData();
+ resizeData();
+}
+
+void
+WarpXParticleContainer::AddOneParticle (int lev, int grid, int tile,
+ Real x, Real y, Real z,
+ const std::array<Real,PIdx::nattribs>& attribs)
+{
+ auto& particle_tile = GetParticles(lev)[std::make_pair(grid,tile)];
+ AddOneParticle(particle_tile, x, y, z, attribs);
+}
+
+void
+WarpXParticleContainer::AddOneParticle (ParticleTileType& particle_tile,
+ Real x, Real y, Real z,
+ const std::array<Real,PIdx::nattribs>& attribs)
+{
+ ParticleType p;
+ p.id() = ParticleType::NextID();
+ p.cpu() = ParallelDescriptor::MyProc();
+#if (AMREX_SPACEDIM == 3)
+ p.pos(0) = x;
+ p.pos(1) = y;
+ p.pos(2) = z;
+#elif (AMREX_SPACEDIM == 2)
+#ifdef WARPX_RZ
+ attribs[PIdx::theta] = std::atan2(y, x);
+ x = std::sqrt(x*x + y*y);
+#endif
+ p.pos(0) = x;
+ p.pos(1) = z;
+#endif
+
+ particle_tile.push_back(p);
+ particle_tile.push_back_real(attribs);
+}
+
+void
+WarpXParticleContainer::AddNParticles (int lev,
+ int n, const Real* x, const Real* y, const Real* z,
+ const Real* vx, const Real* vy, const Real* vz,
+ int nattr, const Real* attr, int uniqueparticles, int id)
+{
+ BL_ASSERT(nattr == 1);
+ const Real* weight = attr;
+
+ int ibegin, iend;
+ if (uniqueparticles) {
+ ibegin = 0;
+ iend = n;
+ } else {
+ int myproc = ParallelDescriptor::MyProc();
+ int nprocs = ParallelDescriptor::NProcs();
+ int navg = n/nprocs;
+ int nleft = n - navg * nprocs;
+ if (myproc < nleft) {
+ ibegin = myproc*(navg+1);
+ iend = ibegin + navg+1;
+ } else {
+ ibegin = myproc*navg + nleft;
+ iend = ibegin + navg;
+ }
+ }
+
+ // Add to grid 0 and tile 0
+ // Redistribute() will move them to proper places.
+ std::pair<int,int> key {0,0};
+ auto& particle_tile = GetParticles(lev)[key];
+
+ std::size_t np = iend-ibegin;
+
+#ifdef WARPX_RZ
+ Vector<Real> theta(np);
+#endif
+
+ for (int i = ibegin; i < iend; ++i)
+ {
+ ParticleType p;
+ if (id==-1)
+ {
+ p.id() = ParticleType::NextID();
+ } else {
+ p.id() = id;
+ }
+ p.cpu() = ParallelDescriptor::MyProc();
+#if (AMREX_SPACEDIM == 3)
+ p.pos(0) = x[i];
+ p.pos(1) = y[i];
+ p.pos(2) = z[i];
+#elif (AMREX_SPACEDIM == 2)
+#ifdef WARPX_RZ
+ theta[i-ibegin] = std::atan2(y[i], x[i]);
+ p.pos(0) = std::sqrt(x[i]*x[i] + y[i]*y[i]);
+#else
+ p.pos(0) = x[i];
+#endif
+ p.pos(1) = z[i];
+#endif
+ particle_tile.push_back(p);
+ }
+
+ if (np > 0)
+ {
+ particle_tile.push_back_real(PIdx::w , weight + ibegin, weight + iend);
+ particle_tile.push_back_real(PIdx::ux, vx + ibegin, vx + iend);
+ particle_tile.push_back_real(PIdx::uy, vy + ibegin, vy + iend);
+ particle_tile.push_back_real(PIdx::uz, vz + ibegin, vz + iend);
+
+ for (int comp = PIdx::uz+1; comp < PIdx::nattribs; ++comp)
+ {
+#ifdef WARPX_RZ
+ if (comp == PIdx::theta) {
+ particle_tile.push_back_real(comp, theta.data, theta.data+np);
+ }
+ else {
+ particle_tile.push_back_real(comp, np, 0.0);
+ }
+#else
+ particle_tile.push_back_real(comp, np, 0.0);
+#endif
+ }
+ }
+
+ Redistribute();
+}
+
+
+void
+WarpXParticleContainer::DepositCurrent(WarpXParIter& pti,
+ RealVector& wp, RealVector& uxp,
+ RealVector& uyp, RealVector& uzp,
+ MultiFab& jx, MultiFab& jy, MultiFab& jz,
+ MultiFab* cjx, MultiFab* cjy, MultiFab* cjz,
+ const long np_current, const long np,
+ int thread_num, int lev, Real dt )
+{
+ Real *jx_ptr, *jy_ptr, *jz_ptr;
+ const std::array<Real,3>& xyzmin_tile = WarpX::LowerCorner(pti.tilebox(), lev);
+ const std::array<Real,3>& dx = WarpX::CellSize(lev);
+ const std::array<Real,3>& cdx = WarpX::CellSize(std::max(lev-1,0));
+ const std::array<Real, 3>& xyzmin = xyzmin_tile;
+ const long lvect = 8;
+
+ BL_PROFILE_VAR_NS("PICSAR::CurrentDeposition", blp_pxr_cd);
+ BL_PROFILE_VAR_NS("PPC::Evolve::Accumulate", blp_accumulate);
+
+ Box tbx = convert(pti.tilebox(), WarpX::jx_nodal_flag);
+ Box tby = convert(pti.tilebox(), WarpX::jy_nodal_flag);
+ Box tbz = convert(pti.tilebox(), WarpX::jz_nodal_flag);
+
+ // WarpX assumes the same number of guard cells for Jx, Jy, Jz
+ long ngJ = jx.nGrow();
+
+ bool j_is_nodal = jx.is_nodal() and jy.is_nodal() and jz.is_nodal();
+
+ // Deposit charge for particles that are not in the current buffers
+ if (np_current > 0)
+ {
+ tbx.grow(ngJ);
+ tby.grow(ngJ);
+ tbz.grow(ngJ);
+
+ local_jx[thread_num]->resize(tbx);
+ local_jy[thread_num]->resize(tby);
+ local_jz[thread_num]->resize(tbz);
+
+ jx_ptr = local_jx[thread_num]->dataPtr();
+ jy_ptr = local_jy[thread_num]->dataPtr();
+ jz_ptr = local_jz[thread_num]->dataPtr();
+
+ FArrayBox* local_jx_ptr = local_jx[thread_num].get();
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA(tbx, b,
+ {
+ local_jx_ptr->setVal(0.0, b, 0, 1);
+ });
+
+ FArrayBox* local_jy_ptr = local_jy[thread_num].get();
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA(tby, b,
+ {
+ local_jy_ptr->setVal(0.0, b, 0, 1);
+ });
+
+ FArrayBox* local_jz_ptr = local_jz[thread_num].get();
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA(tbz, b,
+ {
+ local_jz_ptr->setVal(0.0, b, 0, 1);
+ });
+
+ auto jxntot = local_jx[thread_num]->length();
+ auto jyntot = local_jy[thread_num]->length();
+ auto jzntot = local_jz[thread_num]->length();
+
+ BL_PROFILE_VAR_START(blp_pxr_cd);
+ if (j_is_nodal) {
+ const Real* p_wp = wp.dataPtr();
+ const Real* p_gaminv = m_giv[thread_num].dataPtr();
+ const Real* p_uxp = uxp.dataPtr();
+ const Real* p_uyp = uyp.dataPtr();
+ const Real* p_uzp = uzp.dataPtr();
+ AsyncArray<Real> wptmp_aa(np_current);
+ Real* const wptmp = wptmp_aa.data();
+ const Box& tile_box = pti.tilebox();
+#if (AMREX_SPACEDIM == 3)
+ const long nx = tile_box.length(0);
+ const long ny = tile_box.length(1);
+ const long nz = tile_box.length(2);
+#else
+ const long nx = tile_box.length(0);
+ const long ny = 0;
+ const long nz = tile_box.length(1);
+#endif
+ amrex::ParallelFor (np_current, [=] AMREX_GPU_DEVICE (long ip) {
+ wptmp[ip] = p_wp[ip] * p_gaminv[ip] * p_uxp[ip];
+ });
+ warpx_charge_deposition(jx_ptr, &np_current,
+ m_xp[thread_num].dataPtr(),
+ m_yp[thread_num].dataPtr(),
+ m_zp[thread_num].dataPtr(),
+ wptmp,
+ &this->charge,
+ &xyzmin[0], &xyzmin[1], &xyzmin[2],
+ &dx[0], &dx[1], &dx[2], &nx, &ny, &nz,
+ &ngJ, &ngJ, &ngJ,
+ &WarpX::nox,&WarpX::noy,&WarpX::noz,
+ &lvect, &WarpX::current_deposition_algo);
+ amrex::ParallelFor (np_current, [=] AMREX_GPU_DEVICE (long ip) {
+ wptmp[ip] = p_wp[ip] * p_gaminv[ip] * p_uyp[ip];
+ });
+ warpx_charge_deposition(jy_ptr, &np_current,
+ m_xp[thread_num].dataPtr(),
+ m_yp[thread_num].dataPtr(),
+ m_zp[thread_num].dataPtr(),
+ wptmp,
+ &this->charge,
+ &xyzmin[0], &xyzmin[1], &xyzmin[2],
+ &dx[0], &dx[1], &dx[2], &nx, &ny, &nz,
+ &ngJ, &ngJ, &ngJ,
+ &WarpX::nox,&WarpX::noy,&WarpX::noz,
+ &lvect, &WarpX::current_deposition_algo);
+ amrex::ParallelFor (np_current, [=] AMREX_GPU_DEVICE (long ip) {
+ wptmp[ip] = p_wp[ip] * p_gaminv[ip] * p_uzp[ip];
+ });
+ warpx_charge_deposition(jz_ptr, &np_current,
+ m_xp[thread_num].dataPtr(),
+ m_yp[thread_num].dataPtr(),
+ m_zp[thread_num].dataPtr(),
+ wptmp,
+ &this->charge,
+ &xyzmin[0], &xyzmin[1], &xyzmin[2],
+ &dx[0], &dx[1], &dx[2], &nx, &ny, &nz,
+ &ngJ, &ngJ, &ngJ,
+ &WarpX::nox,&WarpX::noy,&WarpX::noz,
+ &lvect, &WarpX::current_deposition_algo);
+ } else {
+ warpx_current_deposition(
+ jx_ptr, &ngJ, jxntot.getVect(),
+ jy_ptr, &ngJ, jyntot.getVect(),
+ jz_ptr, &ngJ, jzntot.getVect(),
+ &np_current,
+ m_xp[thread_num].dataPtr(),
+ m_yp[thread_num].dataPtr(),
+ m_zp[thread_num].dataPtr(),
+ uxp.dataPtr(), uyp.dataPtr(), uzp.dataPtr(),
+ m_giv[thread_num].dataPtr(),
+ wp.dataPtr(), &this->charge,
+ &xyzmin[0], &xyzmin[1], &xyzmin[2],
+ &dt, &dx[0], &dx[1], &dx[2],
+ &WarpX::nox,&WarpX::noy,&WarpX::noz,
+ &lvect,&WarpX::current_deposition_algo);
+ }
+ BL_PROFILE_VAR_STOP(blp_pxr_cd);
+
+ BL_PROFILE_VAR_START(blp_accumulate);
+
+ FArrayBox const* local_jx_const_ptr = local_jx[thread_num].get();
+ FArrayBox* global_jx_ptr = jx.fabPtr(pti);
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA(tbx, thread_bx,
+ {
+ global_jx_ptr->atomicAdd(*local_jx_const_ptr, thread_bx, thread_bx, 0, 0, 1);
+ });
+
+ FArrayBox const* local_jy_const_ptr = local_jy[thread_num].get();
+ FArrayBox* global_jy_ptr = jy.fabPtr(pti);
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA(tby, thread_bx,
+ {
+ global_jy_ptr->atomicAdd(*local_jy_const_ptr, thread_bx, thread_bx, 0, 0, 1);
+ });
+
+ FArrayBox const* local_jz_const_ptr = local_jz[thread_num].get();
+ FArrayBox* global_jz_ptr = jz.fabPtr(pti);
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA(tbz, thread_bx,
+ {
+ global_jz_ptr->atomicAdd(*local_jz_const_ptr, thread_bx, thread_bx, 0, 0, 1);
+ });
+
+ BL_PROFILE_VAR_STOP(blp_accumulate);
+ }
+
+ // Deposit charge for particles that are in the current buffers
+ if (np_current < np)
+ {
+ const IntVect& ref_ratio = WarpX::RefRatio(lev-1);
+ const Box& ctilebox = amrex::coarsen(pti.tilebox(),ref_ratio);
+ const std::array<Real,3>& cxyzmin_tile = WarpX::LowerCorner(ctilebox, lev-1);
+
+ tbx = amrex::convert(ctilebox, WarpX::jx_nodal_flag);
+ tby = amrex::convert(ctilebox, WarpX::jy_nodal_flag);
+ tbz = amrex::convert(ctilebox, WarpX::jz_nodal_flag);
+ tbx.grow(ngJ);
+ tby.grow(ngJ);
+ tbz.grow(ngJ);
+
+ local_jx[thread_num]->resize(tbx);
+ local_jy[thread_num]->resize(tby);
+ local_jz[thread_num]->resize(tbz);
+
+ jx_ptr = local_jx[thread_num]->dataPtr();
+ jy_ptr = local_jy[thread_num]->dataPtr();
+ jz_ptr = local_jz[thread_num]->dataPtr();
+
+ FArrayBox* local_jx_ptr = local_jx[thread_num].get();
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA(tbx, b,
+ {
+ local_jx_ptr->setVal(0.0, b, 0, 1);
+ });
+
+ FArrayBox* local_jy_ptr = local_jy[thread_num].get();
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA(tby, b,
+ {
+ local_jy_ptr->setVal(0.0, b, 0, 1);
+ });
+
+ FArrayBox* local_jz_ptr = local_jz[thread_num].get();
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA(tbz, b,
+ {
+ local_jz_ptr->setVal(0.0, b, 0, 1);
+ });
+ auto jxntot = local_jx[thread_num]->length();
+ auto jyntot = local_jy[thread_num]->length();
+ auto jzntot = local_jz[thread_num]->length();
+
+ long ncrse = np - np_current;
+ BL_PROFILE_VAR_START(blp_pxr_cd);
+ if (j_is_nodal) {
+ const Real* p_wp = wp.dataPtr() + np_current;
+ const Real* p_gaminv = m_giv[thread_num].dataPtr() + np_current;
+ const Real* p_uxp = uxp.dataPtr() + np_current;
+ const Real* p_uyp = uyp.dataPtr() + np_current;
+ const Real* p_uzp = uzp.dataPtr() + np_current;
+ AsyncArray<Real> wptmp_aa(ncrse);
+ Real* const wptmp = wptmp_aa.data();
+ const Box& tile_box = pti.tilebox();
+#if (AMREX_SPACEDIM == 3)
+ const long nx = tile_box.length(0);
+ const long ny = tile_box.length(1);
+ const long nz = tile_box.length(2);
+#else
+ const long nx = tile_box.length(0);
+ const long ny = 0;
+ const long nz = tile_box.length(1);
+#endif
+ amrex::ParallelFor (ncrse, [=] AMREX_GPU_DEVICE (long ip) {
+ wptmp[ip] = p_wp[ip] * p_gaminv[ip] * p_uxp[ip];
+ });
+ warpx_charge_deposition(jx_ptr, &ncrse,
+ m_xp[thread_num].dataPtr() +np_current,
+ m_yp[thread_num].dataPtr() +np_current,
+ m_zp[thread_num].dataPtr() +np_current,
+ wptmp,
+ &this->charge,
+ &cxyzmin_tile[0], &cxyzmin_tile[1], &cxyzmin_tile[2],
+ &cdx[0], &cdx[1], &cdx[2], &nx, &ny, &nz,
+ &ngJ, &ngJ, &ngJ,
+ &WarpX::nox,&WarpX::noy,&WarpX::noz,
+ &lvect, &WarpX::current_deposition_algo);
+ amrex::ParallelFor (ncrse, [=] AMREX_GPU_DEVICE (long ip) {
+ wptmp[ip] = p_wp[ip] * p_gaminv[ip] * p_uyp[ip];
+ });
+ warpx_charge_deposition(jy_ptr, &ncrse,
+ m_xp[thread_num].dataPtr() +np_current,
+ m_yp[thread_num].dataPtr() +np_current,
+ m_zp[thread_num].dataPtr() +np_current,
+ wptmp,
+ &this->charge,
+ &cxyzmin_tile[0], &cxyzmin_tile[1], &cxyzmin_tile[2],
+ &cdx[0], &cdx[1], &cdx[2], &nx, &ny, &nz,
+ &ngJ, &ngJ, &ngJ,
+ &WarpX::nox,&WarpX::noy,&WarpX::noz,
+ &lvect, &WarpX::current_deposition_algo);
+ amrex::ParallelFor (ncrse, [=] AMREX_GPU_DEVICE (long ip) {
+ wptmp[ip] = p_wp[ip] * p_gaminv[ip] * p_uzp[ip];
+ });
+ warpx_charge_deposition(jz_ptr, &ncrse,
+ m_xp[thread_num].dataPtr() +np_current,
+ m_yp[thread_num].dataPtr() +np_current,
+ m_zp[thread_num].dataPtr() +np_current,
+ wptmp,
+ &this->charge,
+ &cxyzmin_tile[0], &cxyzmin_tile[1], &cxyzmin_tile[2],
+ &cdx[0], &cdx[1], &cdx[2], &nx, &ny, &nz,
+ &ngJ, &ngJ, &ngJ,
+ &WarpX::nox,&WarpX::noy,&WarpX::noz,
+ &lvect, &WarpX::current_deposition_algo);
+ } else {
+ warpx_current_deposition(
+ jx_ptr, &ngJ, jxntot.getVect(),
+ jy_ptr, &ngJ, jyntot.getVect(),
+ jz_ptr, &ngJ, jzntot.getVect(),
+ &ncrse,
+ m_xp[thread_num].dataPtr() +np_current,
+ m_yp[thread_num].dataPtr() +np_current,
+ m_zp[thread_num].dataPtr() +np_current,
+ uxp.dataPtr()+np_current,
+ uyp.dataPtr()+np_current,
+ uzp.dataPtr()+np_current,
+ m_giv[thread_num].dataPtr()+np_current,
+ wp.dataPtr()+np_current, &this->charge,
+ &cxyzmin_tile[0], &cxyzmin_tile[1], &cxyzmin_tile[2],
+ &dt, &cdx[0], &cdx[1], &cdx[2],
+ &WarpX::nox,&WarpX::noy,&WarpX::noz,
+ &lvect,&WarpX::current_deposition_algo);
+ }
+ BL_PROFILE_VAR_STOP(blp_pxr_cd);
+
+ BL_PROFILE_VAR_START(blp_accumulate);
+
+ FArrayBox const* local_jx_const_ptr = local_jx[thread_num].get();
+ FArrayBox* global_jx_ptr = cjx->fabPtr(pti);
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA(tbx, thread_bx,
+ {
+ global_jx_ptr->atomicAdd(*local_jx_const_ptr, thread_bx, thread_bx, 0, 0, 1);
+ });
+
+ FArrayBox const* local_jy_const_ptr = local_jy[thread_num].get();
+ FArrayBox* global_jy_ptr = cjy->fabPtr(pti);
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA(tby, thread_bx,
+ {
+ global_jy_ptr->atomicAdd(*local_jy_const_ptr, thread_bx, thread_bx, 0, 0, 1);
+ });
+
+ FArrayBox const* local_jz_const_ptr = local_jz[thread_num].get();
+ FArrayBox* global_jz_ptr = cjz->fabPtr(pti);
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA(tbz, thread_bx,
+ {
+ global_jz_ptr->atomicAdd(*local_jz_const_ptr, thread_bx, thread_bx, 0, 0, 1);
+ });
+
+ BL_PROFILE_VAR_STOP(blp_accumulate);
+ }
+};
+
+
+void
+WarpXParticleContainer::DepositCharge ( WarpXParIter& pti, RealVector& wp,
+ MultiFab* rhomf, MultiFab* crhomf, int icomp,
+ const long np_current,
+ const long np, int thread_num, int lev )
+{
+
+ BL_PROFILE_VAR_NS("PICSAR::ChargeDeposition", blp_pxr_chd);
+ BL_PROFILE_VAR_NS("PPC::Evolve::Accumulate", blp_accumulate);
+
+ const std::array<Real,3>& xyzmin_tile = WarpX::LowerCorner(pti.tilebox(), lev);
+ const long lvect = 8;
+
+ long ngRho = rhomf->nGrow();
+ Real* data_ptr;
+ Box tile_box = convert(pti.tilebox(), IntVect::TheUnitVector());
+
+ const std::array<Real,3>& dx = WarpX::CellSize(lev);
+ const std::array<Real,3>& cdx = WarpX::CellSize(std::max(lev-1,0));
+
+ // Deposit charge for particles that are not in the current buffers
+ if (np_current > 0)
+ {
+ const std::array<Real, 3>& xyzmin = xyzmin_tile;
+ tile_box.grow(ngRho);
+ local_rho[thread_num]->resize(tile_box);
+ FArrayBox* local_rho_ptr = local_rho[thread_num].get();
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA(tile_box, b,
+ {
+ local_rho_ptr->setVal(0.0, b, 0, 1);
+ });
+
+ data_ptr = local_rho[thread_num]->dataPtr();
+ auto rholen = local_rho[thread_num]->length();
+#if (AMREX_SPACEDIM == 3)
+ const long nx = rholen[0]-1-2*ngRho;
+ const long ny = rholen[1]-1-2*ngRho;
+ const long nz = rholen[2]-1-2*ngRho;
+#else
+ const long nx = rholen[0]-1-2*ngRho;
+ const long ny = 0;
+ const long nz = rholen[1]-1-2*ngRho;
+#endif
+ BL_PROFILE_VAR_START(blp_pxr_chd);
+ warpx_charge_deposition(data_ptr, &np_current,
+ m_xp[thread_num].dataPtr(),
+ m_yp[thread_num].dataPtr(),
+ m_zp[thread_num].dataPtr(),
+ wp.dataPtr(),
+ &this->charge,
+ &xyzmin[0], &xyzmin[1], &xyzmin[2],
+ &dx[0], &dx[1], &dx[2], &nx, &ny, &nz,
+ &ngRho, &ngRho, &ngRho,
+ &WarpX::nox,&WarpX::noy,&WarpX::noz,
+ &lvect, &WarpX::charge_deposition_algo);
+ BL_PROFILE_VAR_STOP(blp_pxr_chd);
+
+ const int ncomp = 1;
+ FArrayBox const* local_fab = local_rho[thread_num].get();
+ FArrayBox* global_fab = rhomf->fabPtr(pti);
+ BL_PROFILE_VAR_START(blp_accumulate);
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA(tile_box, tbx,
+ {
+ global_fab->atomicAdd(*local_fab, tbx, tbx, 0, icomp, ncomp);
+ });
+ BL_PROFILE_VAR_STOP(blp_accumulate);
+ }
+
+ // Deposit charge for particles that are in the current buffers
+ if (np_current < np)
+ {
+ const IntVect& ref_ratio = WarpX::RefRatio(lev-1);
+ const Box& ctilebox = amrex::coarsen(pti.tilebox(), ref_ratio);
+ const std::array<Real,3>& cxyzmin_tile = WarpX::LowerCorner(ctilebox, lev-1);
+
+ tile_box = amrex::convert(ctilebox, IntVect::TheUnitVector());
+ tile_box.grow(ngRho);
+ local_rho[thread_num]->resize(tile_box);
+ FArrayBox* local_rho_ptr = local_rho[thread_num].get();
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA(tile_box, b,
+ {
+ local_rho_ptr->setVal(0.0, b, 0, 1);
+ });
+
+ data_ptr = local_rho[thread_num]->dataPtr();
+ auto rholen = local_rho[thread_num]->length();
+#if (AMREX_SPACEDIM == 3)
+ const long nx = rholen[0]-1-2*ngRho;
+ const long ny = rholen[1]-1-2*ngRho;
+ const long nz = rholen[2]-1-2*ngRho;
+#else
+ const long nx = rholen[0]-1-2*ngRho;
+ const long ny = 0;
+ const long nz = rholen[1]-1-2*ngRho;
+#endif
+
+ long ncrse = np - np_current;
+ BL_PROFILE_VAR_START(blp_pxr_chd);
+ warpx_charge_deposition(data_ptr, &ncrse,
+ m_xp[thread_num].dataPtr() + np_current,
+ m_yp[thread_num].dataPtr() + np_current,
+ m_zp[thread_num].dataPtr() + np_current,
+ wp.dataPtr() + np_current,
+ &this->charge,
+ &cxyzmin_tile[0], &cxyzmin_tile[1], &cxyzmin_tile[2],
+ &cdx[0], &cdx[1], &cdx[2], &nx, &ny, &nz,
+ &ngRho, &ngRho, &ngRho,
+ &WarpX::nox,&WarpX::noy,&WarpX::noz,
+ &lvect, &WarpX::charge_deposition_algo);
+ BL_PROFILE_VAR_STOP(blp_pxr_chd);
+
+ const int ncomp = 1;
+ FArrayBox const* local_fab = local_rho[thread_num].get();
+ FArrayBox* global_fab = crhomf->fabPtr(pti);
+ BL_PROFILE_VAR_START(blp_accumulate);
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA(tile_box, tbx,
+ {
+ global_fab->atomicAdd(*local_fab, tbx, tbx, 0, icomp, ncomp);
+ });
+ BL_PROFILE_VAR_STOP(blp_accumulate);
+ }
+};
+
+void
+WarpXParticleContainer::DepositCharge (Vector<std::unique_ptr<MultiFab> >& rho, bool local)
+{
+
+ int num_levels = rho.size();
+ int finest_level = num_levels - 1;
+
+ // each level deposits it's own particles
+ const int ng = rho[0]->nGrow();
+ for (int lev = 0; lev < num_levels; ++lev) {
+
+ rho[lev]->setVal(0.0, ng);
+
+ const auto& gm = m_gdb->Geom(lev);
+ const auto& ba = m_gdb->ParticleBoxArray(lev);
+ const auto& dm = m_gdb->DistributionMap(lev);
+
+ const Real* dx = gm.CellSize();
+ const Real* plo = gm.ProbLo();
+ BoxArray nba = ba;
+ nba.surroundingNodes();
+
+ for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti) {
+ const Box& box = nba[pti];
+
+ auto& wp = pti.GetAttribs(PIdx::w);
+ const auto& particles = pti.GetArrayOfStructs();
+ int nstride = particles.dataShape().first;
+ const long np = pti.numParticles();
+
+ FArrayBox& rhofab = (*rho[lev])[pti];
+
+ WRPX_DEPOSIT_CIC(particles.dataPtr(), nstride, np,
+ wp.dataPtr(), &this->charge,
+ rhofab.dataPtr(), box.loVect(), box.hiVect(),
+ plo, dx, &ng);
+ }
+
+ if (!local) rho[lev]->SumBoundary(gm.periodicity());
+ }
+
+ // now we average down fine to crse
+ std::unique_ptr<MultiFab> crse;
+ for (int lev = finest_level - 1; lev >= 0; --lev) {
+ const BoxArray& fine_BA = rho[lev+1]->boxArray();
+ const DistributionMapping& fine_dm = rho[lev+1]->DistributionMap();
+ BoxArray coarsened_fine_BA = fine_BA;
+ coarsened_fine_BA.coarsen(m_gdb->refRatio(lev));
+
+ MultiFab coarsened_fine_data(coarsened_fine_BA, fine_dm, 1, 0);
+ coarsened_fine_data.setVal(0.0);
+
+ IntVect ratio(AMREX_D_DECL(2, 2, 2)); // FIXME
+
+ for (MFIter mfi(coarsened_fine_data); mfi.isValid(); ++mfi) {
+ const Box& bx = mfi.validbox();
+ const Box& crse_box = coarsened_fine_data[mfi].box();
+ const Box& fine_box = (*rho[lev+1])[mfi].box();
+ WRPX_SUM_FINE_TO_CRSE_NODAL(bx.loVect(), bx.hiVect(), ratio.getVect(),
+ coarsened_fine_data[mfi].dataPtr(), crse_box.loVect(), crse_box.hiVect(),
+ (*rho[lev+1])[mfi].dataPtr(), fine_box.loVect(), fine_box.hiVect());
+ }
+
+ rho[lev]->copy(coarsened_fine_data, m_gdb->Geom(lev).periodicity(), FabArrayBase::ADD);
+ }
+}
+
+std::unique_ptr<MultiFab>
+WarpXParticleContainer::GetChargeDensity (int lev, bool local)
+{
+ const auto& gm = m_gdb->Geom(lev);
+ const auto& ba = m_gdb->ParticleBoxArray(lev);
+ const auto& dm = m_gdb->DistributionMap(lev);
+ BoxArray nba = ba;
+ nba.surroundingNodes();
+
+ const std::array<Real,3>& dx = WarpX::CellSize(lev);
+
+ const int ng = WarpX::nox;
+
+ auto rho = std::unique_ptr<MultiFab>(new MultiFab(nba,dm,1,ng));
+ rho->setVal(0.0);
+
+#ifdef _OPENMP
+#pragma omp parallel
+#endif
+ {
+ Cuda::DeviceVector<Real> xp, yp, zp;
+ FArrayBox local_rho;
+
+ for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti)
+ {
+ const Box& box = pti.validbox();
+
+ auto& wp = pti.GetAttribs(PIdx::w);
+
+ const long np = pti.numParticles();
+
+ pti.GetPosition(xp, yp, zp);
+
+ const std::array<Real,3>& xyzmin_tile = WarpX::LowerCorner(pti.tilebox(), lev);
+ const std::array<Real,3>& xyzmin_grid = WarpX::LowerCorner(box, lev);
+
+ // Data on the grid
+ Real* data_ptr;
+ FArrayBox& rhofab = (*rho)[pti];
+#ifdef _OPENMP
+ Box tile_box = convert(pti.tilebox(), IntVect::TheUnitVector());
+ const std::array<Real, 3>& xyzmin = xyzmin_tile;
+ tile_box.grow(ng);
+ local_rho.resize(tile_box);
+ local_rho = 0.0;
+ data_ptr = local_rho.dataPtr();
+ auto rholen = local_rho.length();
+#else
+ const std::array<Real, 3>& xyzmin = xyzmin_grid;
+ data_ptr = rhofab.dataPtr();
+ auto rholen = rhofab.length();
+#endif
+
+#if (AMREX_SPACEDIM == 3)
+ const long nx = rholen[0]-1-2*ng;
+ const long ny = rholen[1]-1-2*ng;
+ const long nz = rholen[2]-1-2*ng;
+#else
+ const long nx = rholen[0]-1-2*ng;
+ const long ny = 0;
+ const long nz = rholen[1]-1-2*ng;
+#endif
+
+ long nxg = ng;
+ long nyg = ng;
+ long nzg = ng;
+ long lvect = 8;
+
+ warpx_charge_deposition(data_ptr,
+ &np,
+ xp.dataPtr(),
+ yp.dataPtr(),
+ zp.dataPtr(), wp.dataPtr(),
+ &this->charge, &xyzmin[0], &xyzmin[1], &xyzmin[2],
+ &dx[0], &dx[1], &dx[2], &nx, &ny, &nz,
+ &nxg, &nyg, &nzg, &WarpX::nox,&WarpX::noy,&WarpX::noz,
+ &lvect, &WarpX::charge_deposition_algo);
+
+#ifdef _OPENMP
+ rhofab.atomicAdd(local_rho);
+#endif
+ }
+
+ }
+
+ if (!local) rho->SumBoundary(gm.periodicity());
+
+ return rho;
+}
+
+Real WarpXParticleContainer::sumParticleCharge(bool local) {
+
+ amrex::Real total_charge = 0.0;
+
+ for (int lev = 0; lev < finestLevel(); ++lev)
+ {
+
+#ifdef _OPENMP
+#pragma omp parallel reduction(+:total_charge)
+#endif
+ for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti)
+ {
+ auto& wp = pti.GetAttribs(PIdx::w);
+ for (unsigned long i = 0; i < wp.size(); i++) {
+ total_charge += wp[i];
+ }
+ }
+ }
+
+ if (!local) ParallelDescriptor::ReduceRealSum(total_charge);
+ total_charge *= this->charge;
+ return total_charge;
+}
+
+std::array<Real, 3> WarpXParticleContainer::meanParticleVelocity(bool local) {
+
+ amrex::Real vx_total = 0.0;
+ amrex::Real vy_total = 0.0;
+ amrex::Real vz_total = 0.0;
+
+ long np_total = 0;
+
+ amrex::Real inv_clight_sq = 1.0/PhysConst::c/PhysConst::c;
+
+ for (int lev = 0; lev <= finestLevel(); ++lev) {
+
+#ifdef _OPENMP
+#pragma omp parallel reduction(+:vx_total, vy_total, vz_total, np_total)
+#endif
+ for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti)
+ {
+ auto& ux = pti.GetAttribs(PIdx::ux);
+ auto& uy = pti.GetAttribs(PIdx::uy);
+ auto& uz = pti.GetAttribs(PIdx::uz);
+
+ np_total += pti.numParticles();
+
+ for (unsigned long i = 0; i < ux.size(); i++) {
+ Real usq = (ux[i]*ux[i] + uy[i]*uy[i] + uz[i]*uz[i])*inv_clight_sq;
+ Real gaminv = 1.0/std::sqrt(1.0 + usq);
+ vx_total += ux[i]*gaminv;
+ vy_total += uy[i]*gaminv;
+ vz_total += uz[i]*gaminv;
+ }
+ }
+ }
+
+ if (!local) {
+ ParallelDescriptor::ReduceRealSum(vx_total);
+ ParallelDescriptor::ReduceRealSum(vy_total);
+ ParallelDescriptor::ReduceRealSum(vz_total);
+ ParallelDescriptor::ReduceLongSum(np_total);
+ }
+
+ std::array<Real, 3> mean_v;
+ if (np_total > 0) {
+ mean_v[0] = vx_total / np_total;
+ mean_v[1] = vy_total / np_total;
+ mean_v[2] = vz_total / np_total;
+ }
+
+ return mean_v;
+}
+
+Real WarpXParticleContainer::maxParticleVelocity(bool local) {
+
+ amrex::Real max_v = 0.0;
+
+ for (int lev = 0; lev <= finestLevel(); ++lev)
+ {
+
+#ifdef _OPENMP
+#pragma omp parallel reduction(max:max_v)
+#endif
+ for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti)
+ {
+ auto& ux = pti.GetAttribs(PIdx::ux);
+ auto& uy = pti.GetAttribs(PIdx::uy);
+ auto& uz = pti.GetAttribs(PIdx::uz);
+ for (unsigned long i = 0; i < ux.size(); i++) {
+ max_v = std::max(max_v, sqrt(ux[i]*ux[i] + uy[i]*uy[i] + uz[i]*uz[i]));
+ }
+ }
+ }
+
+ if (!local) ParallelDescriptor::ReduceRealMax(max_v);
+ return max_v;
+}
+
+void
+WarpXParticleContainer::PushXES (Real dt)
+{
+ BL_PROFILE("WPC::PushXES()");
+
+ int num_levels = finestLevel() + 1;
+
+ for (int lev = 0; lev < num_levels; ++lev) {
+ const auto& gm = m_gdb->Geom(lev);
+ const RealBox& prob_domain = gm.ProbDomain();
+ for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti) {
+ auto& particles = pti.GetArrayOfStructs();
+ int nstride = particles.dataShape().first;
+ const long np = pti.numParticles();
+
+ auto& attribs = pti.GetAttribs();
+ auto& uxp = attribs[PIdx::ux];
+ auto& uyp = attribs[PIdx::uy];
+ auto& uzp = attribs[PIdx::uz];
+
+ WRPX_PUSH_LEAPFROG_POSITIONS(particles.dataPtr(), nstride, np,
+ uxp.dataPtr(), uyp.dataPtr(),
+#if AMREX_SPACEDIM == 3
+ uzp.dataPtr(),
+#endif
+ &dt,
+ prob_domain.lo(), prob_domain.hi());
+ }
+ }
+}
+
+void
+WarpXParticleContainer::PushX (Real dt)
+{
+ for (int lev = 0; lev <= finestLevel(); ++lev) {
+ PushX(lev, dt);
+ }
+}
+
+void
+WarpXParticleContainer::PushX (int lev, Real dt)
+{
+ BL_PROFILE("WPC::PushX()");
+ BL_PROFILE_VAR_NS("WPC::PushX::Copy", blp_copy);
+ BL_PROFILE_VAR_NS("WPC:PushX::Push", blp_pxr_pp);
+
+ if (do_not_push) return;
+
+ MultiFab* cost = WarpX::getCosts(lev);
+
+#ifdef _OPENMP
+#pragma omp parallel
+#endif
+ {
+ Cuda::DeviceVector<Real> xp, yp, zp, giv;
+
+ for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti)
+ {
+ Real wt = amrex::second();
+
+ auto& attribs = pti.GetAttribs();
+
+ auto& uxp = attribs[PIdx::ux];
+ auto& uyp = attribs[PIdx::uy];
+ auto& uzp = attribs[PIdx::uz];
+
+ const long np = pti.numParticles();
+
+ giv.resize(np);
+
+ //
+ // copy data from particle container to temp arrays
+ //
+ BL_PROFILE_VAR_START(blp_copy);
+ pti.GetPosition(xp, yp, zp);
+ BL_PROFILE_VAR_STOP(blp_copy);
+
+ //
+ // Particle Push
+ //
+ BL_PROFILE_VAR_START(blp_pxr_pp);
+ warpx_particle_pusher_positions(&np,
+ xp.dataPtr(),
+ yp.dataPtr(),
+ zp.dataPtr(),
+ uxp.dataPtr(), uyp.dataPtr(), uzp.dataPtr(),
+ giv.dataPtr(), &dt);
+ BL_PROFILE_VAR_STOP(blp_pxr_pp);
+
+ //
+ // copy particle data back
+ //
+ BL_PROFILE_VAR_START(blp_copy);
+ pti.SetPosition(xp, yp, zp);
+ BL_PROFILE_VAR_STOP(blp_copy);
+
+ if (cost) {
+ const Box& tbx = pti.tilebox();
+ wt = (amrex::second() - wt) / tbx.d_numPts();
+ FArrayBox* costfab = cost->fabPtr(pti);
+ AMREX_LAUNCH_HOST_DEVICE_LAMBDA ( tbx, work_box,
+ {
+ costfab->plus(wt, work_box);
+ });
+ }
+ }
+ }
+}
+
+// This function is called in Redistribute, just after locate
+void
+WarpXParticleContainer::particlePostLocate(ParticleType& p,
+ const ParticleLocData& pld,
+ const int lev)
+{
+ // Tag particle if goes to higher level.
+ // It will be split later in the loop
+ if (pld.m_lev == lev+1
+ and p.m_idata.id != NoSplitParticleID
+ and p.m_idata.id >= 0)
+ {
+ p.m_idata.id = DoSplitParticleID;
+ }
+ // For the moment, do not do anything if particles goes
+ // to lower level.
+ if (pld.m_lev == lev-1){
+ }
+}
diff --git a/Source/Particles/deposit_cic.F90 b/Source/Particles/deposit_cic.F90
new file mode 100644
index 000000000..e4e1ace03
--- /dev/null
+++ b/Source/Particles/deposit_cic.F90
@@ -0,0 +1,134 @@
+module warpx_ES_deposit_cic
+
+ use iso_c_binding
+ use amrex_fort_module, only : amrex_real
+
+ implicit none
+
+contains
+
+! This routine computes the charge density due to the particles using cloud-in-cell
+! deposition. The Fab rho is assumed to be node-centered.
+!
+! Arguments:
+! particles : a pointer to the particle array-of-structs
+! ns : the stride length of particle struct (the size of the struct in number of reals)
+! np : the number of particles
+! weights : the particle weights
+! charge : the charge of this particle species
+! rho : a Fab that will contain the charge density on exit
+! lo : a pointer to the lo corner of this valid box for rho, in index space
+! hi : a pointer to the hi corner of this valid box for rho, in index space
+! plo : the real position of the left-hand corner of the problem domain
+! dx : the mesh spacing
+! ng : the number of ghost cells in rho
+!
+ subroutine warpx_deposit_cic_3d(particles, ns, np, &
+ weights, charge, rho, lo, hi, plo, dx, &
+ ng) &
+ bind(c,name='warpx_deposit_cic_3d')
+ integer, value, intent(in) :: ns, np
+ real(amrex_real), intent(in) :: particles(ns,np)
+ real(amrex_real), intent(in) :: weights(np)
+ real(amrex_real), intent(in) :: charge
+ integer, intent(in) :: lo(3)
+ integer, intent(in) :: hi(3)
+ integer, intent(in) :: ng
+ real(amrex_real), intent(inout) :: rho(lo(1)-ng:hi(1)+ng, lo(2)-ng:hi(2)+ng, lo(3)-ng:hi(3)+ng)
+ real(amrex_real), intent(in) :: plo(3)
+ real(amrex_real), intent(in) :: dx(3)
+
+ integer i, j, k, n
+ real(amrex_real) wx_lo, wy_lo, wz_lo, wx_hi, wy_hi, wz_hi
+ real(amrex_real) lx, ly, lz
+ real(amrex_real) inv_dx(3)
+ real(amrex_real) qp, inv_vol
+
+ inv_dx = 1.0d0/dx
+
+ inv_vol = inv_dx(1) * inv_dx(2) * inv_dx(3)
+
+ do n = 1, np
+
+ qp = weights(n) * charge * inv_vol
+
+ lx = (particles(1, n) - plo(1))*inv_dx(1)
+ ly = (particles(2, n) - plo(2))*inv_dx(2)
+ lz = (particles(3, n) - plo(3))*inv_dx(3)
+
+ i = floor(lx)
+ j = floor(ly)
+ k = floor(lz)
+
+ wx_hi = lx - i
+ wy_hi = ly - j
+ wz_hi = lz - k
+
+ wx_lo = 1.0d0 - wx_hi
+ wy_lo = 1.0d0 - wy_hi
+ wz_lo = 1.0d0 - wz_hi
+
+ rho(i, j, k ) = rho(i, j, k ) + wx_lo*wy_lo*wz_lo*qp
+ rho(i, j, k+1) = rho(i, j, k+1) + wx_lo*wy_lo*wz_hi*qp
+ rho(i, j+1, k ) = rho(i, j+1, k ) + wx_lo*wy_hi*wz_lo*qp
+ rho(i, j+1, k+1) = rho(i, j+1, k+1) + wx_lo*wy_hi*wz_hi*qp
+ rho(i+1, j, k ) = rho(i+1, j, k ) + wx_hi*wy_lo*wz_lo*qp
+ rho(i+1, j, k+1) = rho(i+1, j, k+1) + wx_hi*wy_lo*wz_hi*qp
+ rho(i+1, j+1, k ) = rho(i+1, j+1, k ) + wx_hi*wy_hi*wz_lo*qp
+ rho(i+1, j+1, k+1) = rho(i+1, j+1, k+1) + wx_hi*wy_hi*wz_hi*qp
+
+ end do
+
+ end subroutine warpx_deposit_cic_3d
+
+ subroutine warpx_deposit_cic_2d(particles, ns, np, &
+ weights, charge, rho, lo, hi, plo, dx, &
+ ng) &
+ bind(c,name='warpx_deposit_cic_2d')
+ integer, value, intent(in) :: ns, np
+ real(amrex_real), intent(in) :: particles(ns,np)
+ real(amrex_real), intent(in) :: weights(np)
+ real(amrex_real), intent(in) :: charge
+ integer, intent(in) :: lo(2)
+ integer, intent(in) :: hi(2)
+ integer, intent(in) :: ng
+ real(amrex_real), intent(inout) :: rho(lo(1)-ng:hi(1)+ng, lo(2)-ng:hi(2)+ng)
+ real(amrex_real), intent(in) :: plo(2)
+ real(amrex_real), intent(in) :: dx(2)
+
+ integer i, j, n
+ real(amrex_real) wx_lo, wy_lo, wx_hi, wy_hi
+ real(amrex_real) lx, ly
+ real(amrex_real) inv_dx(2)
+ real(amrex_real) qp, inv_vol
+
+ inv_dx = 1.0d0/dx
+
+ inv_vol = inv_dx(1) * inv_dx(2)
+
+ do n = 1, np
+
+ qp = weights(n) * charge * inv_vol
+
+ lx = (particles(1, n) - plo(1))*inv_dx(1)
+ ly = (particles(2, n) - plo(2))*inv_dx(2)
+
+ i = floor(lx)
+ j = floor(ly)
+
+ wx_hi = lx - i
+ wy_hi = ly - j
+
+ wx_lo = 1.0d0 - wx_hi
+ wy_lo = 1.0d0 - wy_hi
+
+ rho(i, j ) = rho(i, j ) + wx_lo*wy_lo*qp
+ rho(i, j+1) = rho(i, j+1) + wx_lo*wy_hi*qp
+ rho(i+1, j ) = rho(i+1, j ) + wx_hi*wy_lo*qp
+ rho(i+1, j+1) = rho(i+1, j+1) + wx_hi*wy_hi*qp
+
+ end do
+
+ end subroutine warpx_deposit_cic_2d
+
+end module warpx_ES_deposit_cic
diff --git a/Source/Particles/interpolate_cic.F90 b/Source/Particles/interpolate_cic.F90
new file mode 100644
index 000000000..bc2c4f37e
--- /dev/null
+++ b/Source/Particles/interpolate_cic.F90
@@ -0,0 +1,371 @@
+module warpx_ES_interpolate_cic
+
+ use iso_c_binding
+ use amrex_fort_module, only : amrex_real
+
+ implicit none
+
+contains
+
+! This routine interpolates the electric field to the particle positions
+! using cloud-in-cell interpolation. The electric fields are assumed to be
+! node-centered.
+!
+! Arguments:
+! particles : a pointer to the particle array-of-structs
+! ns : the stride length of particle struct (the size of the struct in number of reals)
+! np : the number of particles
+! Ex_p : the electric field in the x-direction at the particle positions (output)
+! Ey_p : the electric field in the y-direction at the particle positions (output)
+! Ez_p : the electric field in the z-direction at the particle positions (output)
+! Ex, Ey, Ez: Fabs conting the electric field on the mesh
+! lo : a pointer to the lo corner of this valid box, in index space
+! hi : a pointer to the hi corner of this valid box, in index space
+! plo : the real position of the left-hand corner of the problem domain
+! dx : the mesh spacing
+! ng : the number of ghost cells for the E-field
+!
+ subroutine warpx_interpolate_cic_3d(particles, ns, np, &
+ Ex_p, Ey_p, Ez_p, &
+ Ex, Ey, Ez, &
+ lo, hi, plo, dx, ng) &
+ bind(c,name='warpx_interpolate_cic_3d')
+ integer, value, intent(in) :: ns, np
+ real(amrex_real), intent(in) :: particles(ns,np)
+ real(amrex_real), intent(inout) :: Ex_p(np), Ey_p(np), Ez_p(np)
+ integer, intent(in) :: ng
+ integer, intent(in) :: lo(3)
+ integer, intent(in) :: hi(3)
+ real(amrex_real), intent(in) :: Ex(lo(1)-ng:hi(1)+ng, lo(2)-ng:hi(2)+ng, lo(3)-ng:hi(3)+ng)
+ real(amrex_real), intent(in) :: Ey(lo(1)-ng:hi(1)+ng, lo(2)-ng:hi(2)+ng, lo(3)-ng:hi(3)+ng)
+ real(amrex_real), intent(in) :: Ez(lo(1)-ng:hi(1)+ng, lo(2)-ng:hi(2)+ng, lo(3)-ng:hi(3)+ng)
+ real(amrex_real), intent(in) :: plo(3)
+ real(amrex_real), intent(in) :: dx(3)
+
+ integer i, j, k, n
+ real(amrex_real) wx_lo, wy_lo, wz_lo, wx_hi, wy_hi, wz_hi
+ real(amrex_real) lx, ly, lz
+ real(amrex_real) inv_dx(3)
+ inv_dx = 1.0d0/dx
+
+ do n = 1, np
+ lx = (particles(1, n) - plo(1))*inv_dx(1)
+ ly = (particles(2, n) - plo(2))*inv_dx(2)
+ lz = (particles(3, n) - plo(3))*inv_dx(3)
+
+ i = floor(lx)
+ j = floor(ly)
+ k = floor(lz)
+
+ wx_hi = lx - i
+ wy_hi = ly - j
+ wz_hi = lz - k
+
+ wx_lo = 1.0d0 - wx_hi
+ wy_lo = 1.0d0 - wy_hi
+ wz_lo = 1.0d0 - wz_hi
+
+ Ex_p(n) = wx_lo*wy_lo*wz_lo*Ex(i, j, k ) + &
+ wx_lo*wy_lo*wz_hi*Ex(i, j, k+1) + &
+ wx_lo*wy_hi*wz_lo*Ex(i, j+1, k ) + &
+ wx_lo*wy_hi*wz_hi*Ex(i, j+1, k+1) + &
+ wx_hi*wy_lo*wz_lo*Ex(i+1, j, k ) + &
+ wx_hi*wy_lo*wz_hi*Ex(i+1, j, k+1) + &
+ wx_hi*wy_hi*wz_lo*Ex(i+1, j+1, k ) + &
+ wx_hi*wy_hi*wz_hi*Ex(i+1, j+1, k+1)
+
+ Ey_p(n) = wx_lo*wy_lo*wz_lo*Ey(i, j, k ) + &
+ wx_lo*wy_lo*wz_hi*Ey(i, j, k+1) + &
+ wx_lo*wy_hi*wz_lo*Ey(i, j+1, k ) + &
+ wx_lo*wy_hi*wz_hi*Ey(i, j+1, k+1) + &
+ wx_hi*wy_lo*wz_lo*Ey(i+1, j, k ) + &
+ wx_hi*wy_lo*wz_hi*Ey(i+1, j, k+1) + &
+ wx_hi*wy_hi*wz_lo*Ey(i+1, j+1, k ) + &
+ wx_hi*wy_hi*wz_hi*Ey(i+1, j+1, k+1)
+
+ Ez_p(n) = wx_lo*wy_lo*wz_lo*Ez(i, j, k ) + &
+ wx_lo*wy_lo*wz_hi*Ez(i, j, k+1) + &
+ wx_lo*wy_hi*wz_lo*Ez(i, j+1, k ) + &
+ wx_lo*wy_hi*wz_hi*Ez(i, j+1, k+1) + &
+ wx_hi*wy_lo*wz_lo*Ez(i+1, j, k ) + &
+ wx_hi*wy_lo*wz_hi*Ez(i+1, j, k+1) + &
+ wx_hi*wy_hi*wz_lo*Ez(i+1, j+1, k ) + &
+ wx_hi*wy_hi*wz_hi*Ez(i+1, j+1, k+1)
+
+ end do
+
+ end subroutine warpx_interpolate_cic_3d
+
+
+ subroutine warpx_interpolate_cic_2d(particles, ns, np, &
+ Ex_p, Ey_p, &
+ Ex, Ey, &
+ lo, hi, plo, dx, ng) &
+ bind(c,name='warpx_interpolate_cic_2d')
+ integer, value, intent(in) :: ns, np
+ real(amrex_real), intent(in) :: particles(ns,np)
+ real(amrex_real), intent(inout) :: Ex_p(np), Ey_p(np)
+ integer, intent(in) :: ng
+ integer, intent(in) :: lo(2)
+ integer, intent(in) :: hi(2)
+ real(amrex_real), intent(in) :: Ex(lo(1)-ng:hi(1)+ng, lo(2)-ng:hi(2)+ng)
+ real(amrex_real), intent(in) :: Ey(lo(1)-ng:hi(1)+ng, lo(2)-ng:hi(2)+ng)
+ real(amrex_real), intent(in) :: plo(2)
+ real(amrex_real), intent(in) :: dx(2)
+
+ integer i, j, n
+ real(amrex_real) wx_lo, wy_lo, wx_hi, wy_hi
+ real(amrex_real) lx, ly
+ real(amrex_real) inv_dx(2)
+ inv_dx = 1.0d0/dx
+
+ do n = 1, np
+ lx = (particles(1, n) - plo(1))*inv_dx(1)
+ ly = (particles(2, n) - plo(2))*inv_dx(2)
+
+ i = floor(lx)
+ j = floor(ly)
+
+ wx_hi = lx - i
+ wy_hi = ly - j
+
+ wx_lo = 1.0d0 - wx_hi
+ wy_lo = 1.0d0 - wy_hi
+
+ Ex_p(n) = wx_lo*wy_lo*Ex(i, j ) + &
+ wx_lo*wy_hi*Ex(i, j+1) + &
+ wx_hi*wy_lo*Ex(i+1, j ) + &
+ wx_hi*wy_hi*Ex(i+1, j+1)
+
+ Ey_p(n) = wx_lo*wy_lo*Ey(i, j ) + &
+ wx_lo*wy_hi*Ey(i, j+1) + &
+ wx_hi*wy_lo*Ey(i+1, j ) + &
+ wx_hi*wy_hi*Ey(i+1, j+1)
+
+ end do
+
+ end subroutine warpx_interpolate_cic_2d
+
+
+ subroutine warpx_interpolate_cic_two_levels_3d(particles, ns, np, &
+ Ex_p, Ey_p, Ez_p, &
+ Ex, Ey, Ez, &
+ lo, hi, dx, &
+ cEx, cEy, cEz, &
+ mask, &
+ clo, chi, cdx, &
+ plo, ng, lev) &
+ bind(c,name='warpx_interpolate_cic_two_levels_3d')
+ integer, value, intent(in) :: ns, np
+ real(amrex_real), intent(in) :: particles(ns,np)
+ real(amrex_real), intent(inout) :: Ex_p(np), Ey_p(np), Ez_p(np)
+ integer, intent(in) :: ng, lev
+ integer, intent(in) :: lo(3), hi(3)
+ integer, intent(in) :: clo(3), chi(3)
+ real(amrex_real), intent(in) :: Ex(lo(1)-ng:hi(1)+ng, lo(2)-ng:hi(2)+ng, lo(3)-ng:hi(3)+ng)
+ real(amrex_real), intent(in) :: Ey(lo(1)-ng:hi(1)+ng, lo(2)-ng:hi(2)+ng, lo(3)-ng:hi(3)+ng)
+ real(amrex_real), intent(in) :: Ez(lo(1)-ng:hi(1)+ng, lo(2)-ng:hi(2)+ng, lo(3)-ng:hi(3)+ng)
+ real(amrex_real), intent(in) :: cEx(clo(1)-ng:chi(1)+ng, clo(2)-ng:chi(2)+ng, clo(3)-ng:chi(3)+ng)
+ real(amrex_real), intent(in) :: cEy(clo(1)-ng:chi(1)+ng, clo(2)-ng:chi(2)+ng, clo(3)-ng:chi(3)+ng)
+ real(amrex_real), intent(in) :: cEz(clo(1)-ng:chi(1)+ng, clo(2)-ng:chi(2)+ng, clo(3)-ng:chi(3)+ng)
+ integer(c_int), intent(in) :: mask (lo(1):hi(1),lo(2):hi(2),lo(3):hi(3))
+ real(amrex_real), intent(in) :: plo(3)
+ real(amrex_real), intent(in) :: dx(3), cdx(3)
+
+ integer i, j, k, n
+ real(amrex_real) wx_lo, wy_lo, wz_lo, wx_hi, wy_hi, wz_hi
+ real(amrex_real) lx, ly, lz
+ real(amrex_real) inv_dx(3), inv_cdx(3)
+ inv_dx = 1.0d0/dx
+ inv_cdx = 1.0d0/cdx
+
+ do n = 1, np
+
+ lx = (particles(1, n) - plo(1))*inv_dx(1)
+ ly = (particles(2, n) - plo(2))*inv_dx(2)
+ lz = (particles(3, n) - plo(3))*inv_dx(3)
+
+ i = floor(lx)
+ j = floor(ly)
+ k = floor(lz)
+
+! use the coarse E if near the level boundary
+ if (lev .eq. 1 .and. mask(i,j,k) .eq. 1) then
+
+ lx = (particles(1, n) - plo(1))*inv_cdx(1)
+ ly = (particles(2, n) - plo(2))*inv_cdx(2)
+ lz = (particles(3, n) - plo(3))*inv_cdx(3)
+
+ i = floor(lx)
+ j = floor(ly)
+ k = floor(lz)
+
+ wx_hi = lx - i
+ wy_hi = ly - j
+ wz_hi = lz - k
+
+ wx_lo = 1.0d0 - wx_hi
+ wy_lo = 1.0d0 - wy_hi
+ wz_lo = 1.0d0 - wz_hi
+
+ Ex_p(n) = wx_lo*wy_lo*wz_lo*cEx(i, j, k ) + &
+ wx_lo*wy_lo*wz_hi*cEx(i, j, k+1) + &
+ wx_lo*wy_hi*wz_lo*cEx(i, j+1, k ) + &
+ wx_lo*wy_hi*wz_hi*cEx(i, j+1, k+1) + &
+ wx_hi*wy_lo*wz_lo*cEx(i+1, j, k ) + &
+ wx_hi*wy_lo*wz_hi*cEx(i+1, j, k+1) + &
+ wx_hi*wy_hi*wz_lo*cEx(i+1, j+1, k ) + &
+ wx_hi*wy_hi*wz_hi*cEx(i+1, j+1, k+1)
+
+ Ey_p(n) = wx_lo*wy_lo*wz_lo*cEy(i, j, k ) + &
+ wx_lo*wy_lo*wz_hi*cEy(i, j, k+1) + &
+ wx_lo*wy_hi*wz_lo*cEy(i, j+1, k ) + &
+ wx_lo*wy_hi*wz_hi*cEy(i, j+1, k+1) + &
+ wx_hi*wy_lo*wz_lo*cEy(i+1, j, k ) + &
+ wx_hi*wy_lo*wz_hi*cEy(i+1, j, k+1) + &
+ wx_hi*wy_hi*wz_lo*cEy(i+1, j+1, k ) + &
+ wx_hi*wy_hi*wz_hi*cEy(i+1, j+1, k+1)
+
+ Ez_p(n) = wx_lo*wy_lo*wz_lo*cEz(i, j, k ) + &
+ wx_lo*wy_lo*wz_hi*cEz(i, j, k+1) + &
+ wx_lo*wy_hi*wz_lo*cEz(i, j+1, k ) + &
+ wx_lo*wy_hi*wz_hi*cEz(i, j+1, k+1) + &
+ wx_hi*wy_lo*wz_lo*cEz(i+1, j, k ) + &
+ wx_hi*wy_lo*wz_hi*cEz(i+1, j, k+1) + &
+ wx_hi*wy_hi*wz_lo*cEz(i+1, j+1, k ) + &
+ wx_hi*wy_hi*wz_hi*cEz(i+1, j+1, k+1)
+
+! otherwise use the fine
+ else
+
+ wx_hi = lx - i
+ wy_hi = ly - j
+ wz_hi = lz - k
+
+ wx_lo = 1.0d0 - wx_hi
+ wy_lo = 1.0d0 - wy_hi
+ wz_lo = 1.0d0 - wz_hi
+
+ Ex_p(n) = wx_lo*wy_lo*wz_lo*Ex(i, j, k ) + &
+ wx_lo*wy_lo*wz_hi*Ex(i, j, k+1) + &
+ wx_lo*wy_hi*wz_lo*Ex(i, j+1, k ) + &
+ wx_lo*wy_hi*wz_hi*Ex(i, j+1, k+1) + &
+ wx_hi*wy_lo*wz_lo*Ex(i+1, j, k ) + &
+ wx_hi*wy_lo*wz_hi*Ex(i+1, j, k+1) + &
+ wx_hi*wy_hi*wz_lo*Ex(i+1, j+1, k ) + &
+ wx_hi*wy_hi*wz_hi*Ex(i+1, j+1, k+1)
+
+ Ey_p(n) = wx_lo*wy_lo*wz_lo*Ey(i, j, k ) + &
+ wx_lo*wy_lo*wz_hi*Ey(i, j, k+1) + &
+ wx_lo*wy_hi*wz_lo*Ey(i, j+1, k ) + &
+ wx_lo*wy_hi*wz_hi*Ey(i, j+1, k+1) + &
+ wx_hi*wy_lo*wz_lo*Ey(i+1, j, k ) + &
+ wx_hi*wy_lo*wz_hi*Ey(i+1, j, k+1) + &
+ wx_hi*wy_hi*wz_lo*Ey(i+1, j+1, k ) + &
+ wx_hi*wy_hi*wz_hi*Ey(i+1, j+1, k+1)
+
+ Ez_p(n) = wx_lo*wy_lo*wz_lo*Ez(i, j, k ) + &
+ wx_lo*wy_lo*wz_hi*Ez(i, j, k+1) + &
+ wx_lo*wy_hi*wz_lo*Ez(i, j+1, k ) + &
+ wx_lo*wy_hi*wz_hi*Ez(i, j+1, k+1) + &
+ wx_hi*wy_lo*wz_lo*Ez(i+1, j, k ) + &
+ wx_hi*wy_lo*wz_hi*Ez(i+1, j, k+1) + &
+ wx_hi*wy_hi*wz_lo*Ez(i+1, j+1, k ) + &
+ wx_hi*wy_hi*wz_hi*Ez(i+1, j+1, k+1)
+
+ end if
+
+ end do
+
+ end subroutine warpx_interpolate_cic_two_levels_3d
+
+
+ subroutine warpx_interpolate_cic_two_levels_2d(particles, ns, np, &
+ Ex_p, Ey_p, &
+ Ex, Ey, &
+ lo, hi, dx, &
+ cEx, cEy, &
+ mask, &
+ clo, chi, cdx, &
+ plo, ng, lev) &
+ bind(c,name='warpx_interpolate_cic_two_levels_2d')
+ integer, value, intent(in) :: ns, np
+ real(amrex_real), intent(in) :: particles(ns,np)
+ real(amrex_real), intent(inout) :: Ex_p(np), Ey_p(np)
+ integer, intent(in) :: ng, lev
+ integer, intent(in) :: lo(2), hi(2)
+ integer, intent(in) :: clo(2), chi(2)
+ real(amrex_real), intent(in) :: Ex(lo(1)-ng:hi(1)+ng, lo(2)-ng:hi(2)+ng)
+ real(amrex_real), intent(in) :: Ey(lo(1)-ng:hi(1)+ng, lo(2)-ng:hi(2)+ng)
+ real(amrex_real), intent(in) :: cEx(clo(1)-ng:chi(1)+ng, clo(2)-ng:chi(2)+ng)
+ real(amrex_real), intent(in) :: cEy(clo(1)-ng:chi(1)+ng, clo(2)-ng:chi(2)+ng)
+ integer(c_int), intent(in) :: mask (lo(1):hi(1),lo(2):hi(2))
+ real(amrex_real), intent(in) :: plo(2)
+ real(amrex_real), intent(in) :: dx(2), cdx(2)
+
+ integer i, j, n
+ real(amrex_real) wx_lo, wy_lo, wx_hi, wy_hi
+ real(amrex_real) lx, ly
+ real(amrex_real) inv_dx(2), inv_cdx(2)
+ inv_dx = 1.0d0/dx
+ inv_cdx = 1.0d0/cdx
+
+ do n = 1, np
+
+ lx = (particles(1, n) - plo(1))*inv_dx(1)
+ ly = (particles(2, n) - plo(2))*inv_dx(2)
+
+ i = floor(lx)
+ j = floor(ly)
+
+! use the coarse E if near the level boundary
+ if (lev .eq. 1 .and. mask(i,j) .eq. 1) then
+
+ lx = (particles(1, n) - plo(1))*inv_cdx(1)
+ ly = (particles(2, n) - plo(2))*inv_cdx(2)
+
+ i = floor(lx)
+ j = floor(ly)
+
+ wx_hi = lx - i
+ wy_hi = ly - j
+
+ wx_lo = 1.0d0 - wx_hi
+ wy_lo = 1.0d0 - wy_hi
+
+ Ex_p(n) = wx_lo*wy_lo*cEx(i, j ) + &
+ wx_lo*wy_hi*cEx(i, j+1) + &
+ wx_hi*wy_lo*cEx(i+1, j ) + &
+ wx_hi*wy_hi*cEx(i+1, j+1)
+
+ Ey_p(n) = wx_lo*wy_lo*cEy(i, j ) + &
+ wx_lo*wy_hi*cEy(i, j+1) + &
+ wx_hi*wy_lo*cEy(i+1, j ) + &
+ wx_hi*wy_hi*cEy(i+1, j+1)
+
+! otherwise use the fine
+ else
+
+ wx_hi = lx - i
+ wy_hi = ly - j
+
+ wx_lo = 1.0d0 - wx_hi
+ wy_lo = 1.0d0 - wy_hi
+
+ Ex_p(n) = wx_lo*wy_lo*Ex(i, j ) + &
+ wx_lo*wy_hi*Ex(i, j+1) + &
+ wx_hi*wy_lo*Ex(i+1, j ) + &
+ wx_hi*wy_hi*Ex(i+1, j+1)
+
+ Ey_p(n) = wx_lo*wy_lo*Ey(i, j ) + &
+ wx_lo*wy_hi*Ey(i, j+1) + &
+ wx_hi*wy_lo*Ey(i+1, j ) + &
+ wx_hi*wy_hi*Ey(i+1, j+1)
+
+ end if
+
+ end do
+
+ end subroutine warpx_interpolate_cic_two_levels_2d
+
+end module warpx_ES_interpolate_cic
diff --git a/Source/Particles/push_particles_ES.F90 b/Source/Particles/push_particles_ES.F90
new file mode 100644
index 000000000..9c7bd9e92
--- /dev/null
+++ b/Source/Particles/push_particles_ES.F90
@@ -0,0 +1,258 @@
+module warpx_ES_push_particles
+
+ use iso_c_binding
+ use amrex_fort_module, only : amrex_real
+
+ implicit none
+
+contains
+
+!
+! This routine updates the particle positions and velocities using the
+! leapfrog time integration algorithm, given the electric fields at the
+! particle positions. It also enforces specular reflection off the domain
+! walls.
+!
+! Arguments:
+! particles : a pointer to the particle array-of-structs
+! ns : the stride length of particle struct (the size of the struct in number of reals)
+! np : the number of particles
+! vx_p : the particle x-velocities
+! vy_p : the particle y-velocities
+! vz_p : the particle z-velocities
+! Ex_p : the electric field in the x-direction at the particle positions
+! Ey_p : the electric field in the y-direction at the particle positions
+! Ez_p : the electric field in the z-direction at the particle positions
+! charge : the charge of this particle species
+! mass : the mass of this particle species
+! dt : the time step
+! prob_lo : the left-hand corner of the problem domain
+! prob_hi : the right-hand corner of the problem domain
+!
+ subroutine warpx_push_leapfrog_3d(particles, ns, np, &
+ vx_p, vy_p, vz_p, &
+ Ex_p, Ey_p, Ez_p, &
+ charge, mass, dt, &
+ prob_lo, prob_hi) &
+ bind(c,name='warpx_push_leapfrog_3d')
+ integer, value, intent(in) :: ns, np
+ real(amrex_real), intent(inout) :: particles(ns,np)
+ real(amrex_real), intent(inout) :: vx_p(np), vy_p(np), vz_p(np)
+ real(amrex_real), intent(in) :: Ex_p(np), Ey_p(np), Ez_p(np)
+ real(amrex_real), intent(in) :: charge
+ real(amrex_real), intent(in) :: mass
+ real(amrex_real), intent(in) :: dt
+ real(amrex_real), intent(in) :: prob_lo(3), prob_hi(3)
+
+ integer n
+ real(amrex_real) fac
+
+ fac = charge * dt / mass
+
+ do n = 1, np
+
+ vx_p(n) = vx_p(n) + fac * Ex_p(n)
+ vy_p(n) = vy_p(n) + fac * Ey_p(n)
+ vz_p(n) = vz_p(n) + fac * Ez_p(n)
+
+ particles(1, n) = particles(1, n) + dt * vx_p(n)
+ particles(2, n) = particles(2, n) + dt * vy_p(n)
+ particles(3, n) = particles(3, n) + dt * vz_p(n)
+
+! bounce off the walls in the x...
+ do while (particles(1, n) .lt. prob_lo(1) .or. particles(1, n) .gt. prob_hi(1))
+ if (particles(1, n) .lt. prob_lo(1)) then
+ particles(1, n) = 2.d0*prob_lo(1) - particles(1, n)
+ else
+ particles(1, n) = 2.d0*prob_hi(1) - particles(1, n)
+ end if
+ vx_p(n) = -vx_p(n)
+ end do
+
+! ... y...
+ do while (particles(2, n) .lt. prob_lo(2) .or. particles(2, n) .gt. prob_hi(2))
+ if (particles(2, n) .lt. prob_lo(2)) then
+ particles(2, n) = 2.d0*prob_lo(2) - particles(2, n)
+ else
+ particles(2, n) = 2.d0*prob_hi(2) - particles(2, n)
+ end if
+ vy_p(n) = -vy_p(n)
+ end do
+
+! ... and z directions
+ do while (particles(3, n) .lt. prob_lo(3) .or. particles(3, n) .gt. prob_hi(3))
+ if (particles(3, n) .lt. prob_lo(3)) then
+ particles(3, n) = 2.d0*prob_lo(3) - particles(3, n)
+ else
+ particles(3, n) = 2.d0*prob_hi(3) - particles(3, n)
+ end if
+ vz_p(n) = -vz_p(n)
+ end do
+
+ end do
+
+ end subroutine warpx_push_leapfrog_3d
+
+ subroutine warpx_push_leapfrog_2d(particles, ns, np, &
+ vx_p, vy_p, &
+ Ex_p, Ey_p, &
+ charge, mass, dt, &
+ prob_lo, prob_hi) &
+ bind(c,name='warpx_push_leapfrog_2d')
+ integer, value, intent(in) :: ns, np
+ real(amrex_real), intent(inout) :: particles(ns,np)
+ real(amrex_real), intent(inout) :: vx_p(np), vy_p(np)
+ real(amrex_real), intent(in) :: Ex_p(np), Ey_p(np)
+ real(amrex_real), intent(in) :: charge
+ real(amrex_real), intent(in) :: mass
+ real(amrex_real), intent(in) :: dt
+ real(amrex_real), intent(in) :: prob_lo(2), prob_hi(2)
+
+ integer n
+ real(amrex_real) fac
+
+ fac = charge * dt / mass
+
+ do n = 1, np
+
+ vx_p(n) = vx_p(n) + fac * Ex_p(n)
+ vy_p(n) = vy_p(n) + fac * Ey_p(n)
+
+ particles(1, n) = particles(1, n) + dt * vx_p(n)
+ particles(2, n) = particles(2, n) + dt * vy_p(n)
+
+! bounce off the walls in the x...
+ do while (particles(1, n) .lt. prob_lo(1) .or. particles(1, n) .gt. prob_hi(1))
+ if (particles(1, n) .lt. prob_lo(1)) then
+ particles(1, n) = 2.d0*prob_lo(1) - particles(1, n)
+ else
+ particles(1, n) = 2.d0*prob_hi(1) - particles(1, n)
+ end if
+ vx_p(n) = -vx_p(n)
+ end do
+
+! ... y...
+ do while (particles(2, n) .lt. prob_lo(2) .or. particles(2, n) .gt. prob_hi(2))
+ if (particles(2, n) .lt. prob_lo(2)) then
+ particles(2, n) = 2.d0*prob_lo(2) - particles(2, n)
+ else
+ particles(2, n) = 2.d0*prob_hi(2) - particles(2, n)
+ end if
+ vy_p(n) = -vy_p(n)
+ end do
+
+ end do
+
+ end subroutine warpx_push_leapfrog_2d
+
+
+!
+! This routine advances the particle positions using the current
+! velocity. This is needed to desynchronize the particle positions
+! from the velocities after particle initialization.
+!
+! Arguments:
+! particles : a pointer to the particle array-of-structs
+! ns : the stride length of particle struct (the size of the struct in number of reals)
+! np : the number of particles
+! xx_p : the electric field in the x-direction at the particle positions
+! vy_p : the electric field in the y-direction at the particle positions
+! vz_p : the electric field in the z-direction at the particle positions
+! dt : the time step
+! prob_lo : the left-hand corner of the problem domain
+! prob_hi : the right-hand corner of the problem domain
+!
+ subroutine warpx_push_leapfrog_positions_3d(particles, ns, np, &
+ vx_p, vy_p, vz_p, dt, &
+ prob_lo, prob_hi) &
+ bind(c,name='warpx_push_leapfrog_positions_3d')
+ integer, value, intent(in) :: ns, np
+ real(amrex_real), intent(inout) :: particles(ns,np)
+ real(amrex_real), intent(inout) :: vx_p(np), vy_p(np), vz_p(np)
+ real(amrex_real), intent(in) :: dt
+ real(amrex_real), intent(in) :: prob_lo(3), prob_hi(3)
+
+ integer n
+
+ do n = 1, np
+
+ particles(1, n) = particles(1, n) + dt * vx_p(n)
+ particles(2, n) = particles(2, n) + dt * vy_p(n)
+ particles(3, n) = particles(3, n) + dt * vz_p(n)
+
+! bounce off the walls in the x...
+ do while (particles(1, n) .lt. prob_lo(1) .or. particles(1, n) .gt. prob_hi(1))
+ if (particles(1, n) .lt. prob_lo(1)) then
+ particles(1, n) = 2.d0*prob_lo(1) - particles(1, n)
+ else
+ particles(1, n) = 2.d0*prob_hi(1) - particles(1, n)
+ end if
+ vx_p(n) = -vx_p(n)
+ end do
+
+! ... y...
+ do while (particles(2, n) .lt. prob_lo(2) .or. particles(2, n) .gt. prob_hi(2))
+ if (particles(2, n) .lt. prob_lo(2)) then
+ particles(2, n) = 2.d0*prob_lo(2) - particles(2, n)
+ else
+ particles(2, n) = 2.d0*prob_hi(2) - particles(2, n)
+ end if
+ vy_p(n) = -vy_p(n)
+ end do
+
+! ... and z directions
+ do while (particles(3, n) .lt. prob_lo(3) .or. particles(3, n) .gt. prob_hi(3))
+ if (particles(3, n) .lt. prob_lo(3)) then
+ particles(3, n) = 2.d0*prob_lo(3) - particles(3, n)
+ else
+ particles(3, n) = 2.d0*prob_hi(3) - particles(3, n)
+ end if
+ vz_p(n) = -vz_p(n)
+ end do
+
+ end do
+
+ end subroutine warpx_push_leapfrog_positions_3d
+
+ subroutine warpx_push_leapfrog_positions_2d(particles, ns, np, &
+ vx_p, vy_p, dt, &
+ prob_lo, prob_hi) &
+ bind(c,name='warpx_push_leapfrog_positions_2d')
+ integer, value, intent(in) :: ns, np
+ real(amrex_real), intent(inout) :: particles(ns,np)
+ real(amrex_real), intent(inout) :: vx_p(np), vy_p(np)
+ real(amrex_real), intent(in) :: dt
+ real(amrex_real), intent(in) :: prob_lo(2), prob_hi(2)
+
+ integer n
+
+ do n = 1, np
+
+ particles(1, n) = particles(1, n) + dt * vx_p(n)
+ particles(2, n) = particles(2, n) + dt * vy_p(n)
+
+! bounce off the walls in the x...
+ do while (particles(1, n) .lt. prob_lo(1) .or. particles(1, n) .gt. prob_hi(1))
+ if (particles(1, n) .lt. prob_lo(1)) then
+ particles(1, n) = 2.d0*prob_lo(1) - particles(1, n)
+ else
+ particles(1, n) = 2.d0*prob_hi(1) - particles(1, n)
+ end if
+ vx_p(n) = -vx_p(n)
+ end do
+
+! ... y...
+ do while (particles(2, n) .lt. prob_lo(2) .or. particles(2, n) .gt. prob_hi(2))
+ if (particles(2, n) .lt. prob_lo(2)) then
+ particles(2, n) = 2.d0*prob_lo(2) - particles(2, n)
+ else
+ particles(2, n) = 2.d0*prob_hi(2) - particles(2, n)
+ end if
+ vy_p(n) = -vy_p(n)
+ end do
+
+ end do
+
+ end subroutine warpx_push_leapfrog_positions_2d
+
+end module warpx_ES_push_particles