Skip to content

Commit

Permalink
Update Particle Container to Pure SoA
Browse files Browse the repository at this point in the history
Transition particle containers to pure SoA layouts.
  • Loading branch information
ax3l committed Jan 13, 2024
1 parent 82f7f1e commit 1c709de
Show file tree
Hide file tree
Showing 42 changed files with 496 additions and 540 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -143,19 +143,19 @@ struct LorentzTransformParticles
const amrex::ParticleReal uzp = uz_old_p * weight_old
+ uz_new_p * weight_new;
#if defined (WARPX_DIM_3D)
dst.m_aos[i_dst].pos(0) = xp;
dst.m_aos[i_dst].pos(1) = yp;
dst.m_aos[i_dst].pos(2) = zp;
dst.m_rdata[PIdx::x][i_dst] = xp;
dst.m_rdata[PIdx::y][i_dst] = yp;
dst.m_rdata[PIdx::z][i_dst] = zp;
#elif defined (WARPX_DIM_RZ)
dst.m_aos[i_dst].pos(0) = std::sqrt(xp*xp + yp*yp);
dst.m_aos[i_dst].pos(1) = zp;
dst.m_rdata[PIdx::x][i_dst] = std::sqrt(xp*xp + yp*yp);
dst.m_rdata[PIdx::z][i_dst] = zp;
dst.m_rdata[PIdx::theta][i_dst] = std::atan2(yp, xp);
#elif defined (WARPX_DIM_XZ)
dst.m_aos[i_dst].pos(0) = xp;
dst.m_aos[i_dst].pos(1) = zp;
dst.m_rdata[PIdx::x][i_dst] = xp;
dst.m_rdata[PIdx::z][i_dst] = zp;
amrex::ignore_unused(yp);
#elif defined (WARPX_DIM_1D_Z)
dst.m_aos[i_dst].pos(0) = zp;
dst.m_rdata[PIdx::z][i_dst] = zp;
amrex::ignore_unused(xp, yp);
#else
amrex::ignore_unused(xp, yp, zp);
Expand Down
3 changes: 0 additions & 3 deletions Source/Diagnostics/FlushFormats/FlushFormatAscent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,6 @@ FlushFormatAscent::WriteParticles(const amrex::Vector<ParticleDiag>& particle_di
// get names of real comps
std::map<std::string, int> real_comps_map = pc->getParticleComps();

// WarpXParticleContainer compile-time extra AoS attributes (Real): 0
// WarpXParticleContainer compile-time extra AoS attributes (int): 0

// WarpXParticleContainer compile-time extra SoA attributes (Real): PIdx::nattribs
// not an efficient search, but N is small...
for(int j = 0; j < PIdx::nattribs; ++j)
Expand Down
6 changes: 3 additions & 3 deletions Source/Diagnostics/ReducedDiags/FieldProbe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -431,8 +431,6 @@ void FieldProbe::ComputeDiags (int step)
{
const auto getPosition = GetParticlePosition<FieldProbePIdx>(pti);
auto setPosition = SetParticlePosition<FieldProbePIdx>(pti);
const auto& aos = pti.GetArrayOfStructs();
const auto* AMREX_RESTRICT m_structs = aos().dataPtr();

auto const np = pti.numParticles();
if (update_particles_moving_window)
Expand Down Expand Up @@ -482,6 +480,8 @@ void FieldProbe::ComputeDiags (int step)
ParticleReal* const AMREX_RESTRICT part_Bz = attribs[FieldProbePIdx::Bz].dataPtr();
ParticleReal* const AMREX_RESTRICT part_S = attribs[FieldProbePIdx::S].dataPtr();

auto * const AMREX_RESTRICT idcpu = pti.GetStructOfArrays().GetIdCPUData().data();

const auto &xyzmin = WarpX::LowerCorner(box, lev, 0._rt);
const std::array<Real, 3> &dx = WarpX::CellSize(lev);

Expand Down Expand Up @@ -556,7 +556,7 @@ void FieldProbe::ComputeDiags (int step)
amrex::ParticleReal xp, yp, zp;
getPosition(ip, xp, yp, zp);
long idx = ip*noutputs;
dvp[idx++] = m_structs[ip].id();
dvp[idx++] = idcpu[ip];
dvp[idx++] = xp;
dvp[idx++] = yp;
dvp[idx++] = zp;
Expand Down
20 changes: 16 additions & 4 deletions Source/Diagnostics/ReducedDiags/FieldProbeParticleContainer.H
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,14 @@ struct FieldProbePIdx
{
enum
{
Ex = 0, Ey, Ez,
#if !defined (WARPX_DIM_1D_Z)
x,
#endif
#if defined (WARPX_DIM_3D)
y,
#endif
z,
Ex, Ey, Ez,
Bx, By, Bz,
S, //!< the Poynting vector
#ifdef WARPX_DIM_RZ
Expand All @@ -40,9 +47,14 @@ struct FieldProbePIdx
* nattribs tells the particle container to allot 7 SOA values.
*/
class FieldProbeParticleContainer
: public amrex::ParticleContainer<0, 0, FieldProbePIdx::nattribs>
: public amrex::ParticleContainerPureSoA<FieldProbePIdx::nattribs, 0>
{
public:
static constexpr int NStructReal = 0;
static constexpr int NStructInt = 0;
static constexpr int NReal = FieldProbePIdx::nattribs;
static constexpr int NInt = 0;

FieldProbeParticleContainer (amrex::AmrCore* amr_core);
~FieldProbeParticleContainer() override = default;

Expand All @@ -52,9 +64,9 @@ public:
FieldProbeParticleContainer& operator= ( FieldProbeParticleContainer&& ) = default;

//! amrex iterator for our number of attributes
using iterator = amrex::ParIter<0, 0, FieldProbePIdx::nattribs, 0>;
using iterator = amrex::ParIterSoA<FieldProbePIdx::nattribs, 0>;
//! amrex iterator for our number of attributes (read-only)
using const_iterator = amrex::ParConstIter<0, 0, FieldProbePIdx::nattribs, 0>;
using const_iterator = amrex::ParConstIterSoA<FieldProbePIdx::nattribs, 0>;

//! similar to WarpXParticleContainer::AddNParticles but does not include u(x,y,z)
void AddNParticles (int lev, amrex::Vector<amrex::ParticleReal> const & x, amrex::Vector<amrex::ParticleReal> const & y, amrex::Vector<amrex::ParticleReal> const & z);
Expand Down
36 changes: 13 additions & 23 deletions Source/Diagnostics/ReducedDiags/FieldProbeParticleContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
using namespace amrex;

FieldProbeParticleContainer::FieldProbeParticleContainer (AmrCore* amr_core)
: ParticleContainer<0, 0, FieldProbePIdx::nattribs>(amr_core->GetParGDB())
: ParticleContainerPureSoA<FieldProbePIdx::nattribs, 0>(amr_core->GetParGDB())
{
SetParticleSize();
}
Expand Down Expand Up @@ -89,33 +89,17 @@ FieldProbeParticleContainer::AddNParticles (int lev,
* is then coppied to the permament tile which is stored on the particle
* (particle_tile).
*/
using PinnedTile = typename ContainerLike<amrex::PinnedArenaAllocator>::ParticleTileType;

using PinnedTile = ParticleTile<amrex::Particle<NStructReal, NStructInt>,
NArrayReal, NArrayInt,
amrex::PinnedArenaAllocator>;
PinnedTile pinned_tile;
pinned_tile.define(NumRuntimeRealComps(), NumRuntimeIntComps());

for (int i = 0; i < np; i++)
{
ParticleType p;
p.id() = ParticleType::NextID();
p.cpu() = ParallelDescriptor::MyProc();
#if defined(WARPX_DIM_3D)
p.pos(0) = x[i];
p.pos(1) = y[i];
p.pos(2) = z[i];
#elif defined(WARPX_DIM_XZ) || defined(WARPX_DIM_RZ)
amrex::ignore_unused(y);
p.pos(0) = x[i];
p.pos(1) = z[i];
#elif defined(WARPX_DIM_1D_Z)
amrex::ignore_unused(x, y);
p.pos(0) = z[i];
#endif

// write position, cpu id, and particle id to particle
pinned_tile.push_back(p);
auto & idcpu_data = pinned_tile.GetStructOfArrays().GetIdCPUData();
idcpu_data.push_back(0);
amrex::ParticleIDWrapper{idcpu_data.back()} = ParticleType::NextID();
amrex::ParticleCPUWrapper(idcpu_data.back()) = ParallelDescriptor::MyProc();
}

// write Real attributes (SoA) to particle initialized zero
Expand All @@ -125,7 +109,13 @@ FieldProbeParticleContainer::AddNParticles (int lev,
#ifdef WARPX_DIM_RZ
pinned_tile.push_back_real(FieldProbePIdx::theta, np, 0.0);
#endif

#if !defined (WARPX_DIM_1D_Z)
pinned_tile.push_back_real(FieldProbePIdx::x, x);
#endif
#if defined (WARPX_DIM_3D)
pinned_tile.push_back_real(FieldProbePIdx::y, y);
#endif
pinned_tile.push_back_real(FieldProbePIdx::z, z);
pinned_tile.push_back_real(FieldProbePIdx::Ex, np, 0.0);
pinned_tile.push_back_real(FieldProbePIdx::Ey, np, 0.0);
pinned_tile.push_back_real(FieldProbePIdx::Ez, np, 0.0);
Expand Down
3 changes: 1 addition & 2 deletions Source/Diagnostics/ReducedDiags/LoadBalanceCosts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ namespace
auto const & plev = pc.GetParticles(lev);

auto const & ptile = plev.at(box_index);
auto const & aos = ptile.GetArrayOfStructs();
auto const np = aos.numParticles();
auto const np = ptile.numParticles();
num_macro_particles += np;
}

Expand Down
4 changes: 2 additions & 2 deletions Source/Diagnostics/WarpXOpenPMD.H
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class WarpXParticleCounter
{
public:
using ParticleContainer = typename WarpXParticleContainer::ContainerLike<amrex::PinnedArenaAllocator>;
using ParticleIter = typename amrex::ParIter<0, 0, PIdx::nattribs, 0, amrex::PinnedArenaAllocator>;
using ParticleIter = typename amrex::ParIterSoA<PIdx::nattribs, 0, amrex::PinnedArenaAllocator>;

WarpXParticleCounter (ParticleContainer* pc);
[[nodiscard]] unsigned long GetTotalNumParticles () const {return m_Total;}
Expand Down Expand Up @@ -77,7 +77,7 @@ class WarpXOpenPMDPlot
{
public:
using ParticleContainer = typename WarpXParticleContainer::ContainerLike<amrex::PinnedArenaAllocator>;
using ParticleIter = typename amrex::ParConstIter<0, 0, PIdx::nattribs, 0, amrex::PinnedArenaAllocator>;
using ParticleIter = typename amrex::ParConstIterSoA<PIdx::nattribs, 0, amrex::PinnedArenaAllocator>;

/** Initialize openPMD I/O routines
*
Expand Down
132 changes: 19 additions & 113 deletions Source/Diagnostics/WarpXOpenPMD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@
#include "WarpX.H"
#include "OpenPMDHelpFunction.H"

#include <ablastr/particles/IndexHandling.H>
#include <ablastr/warn_manager/WarnManager.H>

#include <AMReX.H>
#include <AMReX_ArrayOfStructs.H>
#include <AMReX_BLassert.H>
#include <AMReX_Box.H>
#include <AMReX_Config.H>
Expand Down Expand Up @@ -732,77 +730,7 @@ WarpXOpenPMDPlot::DumpToFile (ParticleContainer* pc,

contributed_particles = true;

// get position and particle ID from aos
// note: this implementation iterates the AoS 4x...
// if we flush late as we do now, we can also copy out the data in one go
const auto &aos = pti.GetArrayOfStructs(); // size = numParticlesOnTile
{
// Save positions
#if defined(WARPX_DIM_RZ)
{
const std::shared_ptr<amrex::ParticleReal> z(
new amrex::ParticleReal[numParticleOnTile],
[](amrex::ParticleReal const *p) { delete[] p; }
);
for (auto i = 0; i < numParticleOnTile; i++) {
z.get()[i] = aos[i].pos(1); // {0: "r", 1: "z"}
}
std::string const positionComponent = "z";
currSpecies["position"]["z"].storeChunk(z, {offset}, {numParticleOnTile64});
}

// reconstruct x and y from polar coordinates r, theta
auto const& soa = pti.GetStructOfArrays();
amrex::ParticleReal const* theta = soa.GetRealData(PIdx::theta).dataPtr();
WARPX_ALWAYS_ASSERT_WITH_MESSAGE(theta != nullptr, "openPMD: invalid theta pointer.");
WARPX_ALWAYS_ASSERT_WITH_MESSAGE(int(soa.GetRealData(PIdx::theta).size()) == numParticleOnTile,
"openPMD: theta and tile size do not match");
{
const std::shared_ptr< amrex::ParticleReal > x(
new amrex::ParticleReal[numParticleOnTile],
[](amrex::ParticleReal const *p){ delete[] p; }
);
const std::shared_ptr< amrex::ParticleReal > y(
new amrex::ParticleReal[numParticleOnTile],
[](amrex::ParticleReal const *p){ delete[] p; }
);
for (auto i=0; i<numParticleOnTile; i++) {
auto const r = aos[i].pos(0); // {0: "r", 1: "z"}
x.get()[i] = r * std::cos(theta[i]);
y.get()[i] = r * std::sin(theta[i]);
}
currSpecies["position"]["x"].storeChunk(x, {offset}, {numParticleOnTile64});
currSpecies["position"]["y"].storeChunk(y, {offset}, {numParticleOnTile64});
}
#else
auto const positionComponents = detail::getParticlePositionComponentLabels();
for (auto currDim = 0; currDim < AMREX_SPACEDIM; currDim++) {
const std::shared_ptr<amrex::ParticleReal> curr(
new amrex::ParticleReal[numParticleOnTile],
[](amrex::ParticleReal const *p) { delete[] p; }
);
for (auto i = 0; i < numParticleOnTile; i++) {
curr.get()[i] = aos[i].pos(currDim);
}
std::string const positionComponent = positionComponents[currDim];
currSpecies["position"][positionComponent].storeChunk(curr, {offset},
{numParticleOnTile64});
}
#endif

// save particle ID after converting it to a globally unique ID
const std::shared_ptr<uint64_t> ids(
new uint64_t[numParticleOnTile],
[](uint64_t const *p) { delete[] p; }
);
for (auto i = 0; i < numParticleOnTile; i++) {
ids.get()[i] = ablastr::particles::localIDtoGlobal(static_cast<int>(aos[i].id()), static_cast<int>(aos[i].cpu()));
}
const auto *const scalar = openPMD::RecordComponent::SCALAR;
currSpecies["id"][scalar].storeChunk(ids, {offset}, {numParticleOnTile64});

}
// save "extra" particle properties in AoS and SoA
// save particle properties
SaveRealProperty(pti,
currSpecies,
offset,
Expand Down Expand Up @@ -903,10 +831,9 @@ WarpXOpenPMDPlot::SetupRealProperties (ParticleContainer const * pc,

std::set< std::string > addedRecords; // add meta-data per record only once
for (auto idx=0; idx<pc->NumRealComps(); idx++) {
auto ii = ParticleContainer::NStructReal + idx; // jump over extra AoS names
if (write_real_comp[ii]) {
if (write_real_comp[idx]) {
// handle scalar and non-scalar records by name
const auto [record_name, component_name] = detail::name2openPMD(real_comp_names[ii]);
const auto [record_name, component_name] = detail::name2openPMD(real_comp_names[idx]);
auto currRecord = currSpecies[record_name];

// meta data for ED-PIC extension
Expand All @@ -927,10 +854,9 @@ WarpXOpenPMDPlot::SetupRealProperties (ParticleContainer const * pc,
}
}
for (auto idx=0; idx<int_counter; idx++) {
auto ii = ParticleContainer::NStructInt + idx; // jump over extra AoS names
if (write_int_comp[ii]) {
if (write_int_comp[idx]) {
// handle scalar and non-scalar records by name
const auto [record_name, component_name] = detail::name2openPMD(int_comp_names[ii]);
const auto [record_name, component_name] = detail::name2openPMD(int_comp_names[idx]);
auto currRecord = currSpecies[record_name];

// meta data for ED-PIC extension
Expand Down Expand Up @@ -959,58 +885,38 @@ WarpXOpenPMDPlot::SaveRealProperty (ParticleIter& pti,

{
auto const numParticleOnTile = pti.numParticles();
auto const numParticleOnTile64 = static_cast<uint64_t>( numParticleOnTile );
auto const& aos = pti.GetArrayOfStructs(); // size = numParticlesOnTile
auto const numParticleOnTile64 = static_cast<uint64_t>(numParticleOnTile);
auto const& soa = pti.GetStructOfArrays();
// first we concatenate the AoS into contiguous arrays
{
// note: WarpX does not yet use extra AoS Real attributes
for( auto idx=0; idx<ParticleIter::ContainerType::NStructReal; idx++ ) { // lgtm [cpp/constant-comparison]
if( write_real_comp[idx] ) {
// handle scalar and non-scalar records by name
const auto [record_name, component_name] = detail::name2openPMD(real_comp_names[idx]);
auto currRecord = currSpecies[record_name];
auto currRecordComp = currRecord[component_name];

const std::shared_ptr< amrex::ParticleReal > d(
new amrex::ParticleReal[numParticleOnTile],
[](amrex::ParticleReal const *p){ delete[] p; }
);

for( auto kk=0; kk<numParticleOnTile; kk++ ) {
d.get()[kk] = aos[kk].rdata(idx);
}

currRecordComp.storeChunk(d,
{offset}, {numParticleOnTile64});
}
}
}

auto const getComponentRecord = [&currSpecies](std::string const comp_name) {
// handle scalar and non-scalar records by name
const auto [record_name, component_name] = detail::name2openPMD(comp_name);
return currSpecies[record_name][component_name];
};

// here we the save the SoA properties (idcpu)
{
// todo: add support to not write the particle index
getComponentRecord("id").storeChunkRaw(
soa.GetIdCPUData().data(), {offset}, {numParticleOnTile64});
}

// here we the save the SoA properties (real)
{
auto const real_counter = std::min(write_real_comp.size(), real_comp_names.size());
for (auto idx=0; idx<real_counter; idx++) {
auto ii = ParticleIter::ContainerType::NStructReal + idx; // jump over extra AoS names
if (write_real_comp[ii]) {
getComponentRecord(real_comp_names[ii]).storeChunkRaw(
soa.GetRealData(idx).data(), {offset}, {numParticleOnTile64});
}
if (write_real_comp[idx]) {
getComponentRecord(real_comp_names[idx]).storeChunkRaw(
soa.GetRealData(idx).data(), {offset}, {numParticleOnTile64});
}
}
}
// and now SoA int properties
{
auto const int_counter = std::min(write_int_comp.size(), int_comp_names.size());
for (auto idx=0; idx<int_counter; idx++) {
auto ii = ParticleIter::ContainerType::NStructInt + idx; // jump over extra AoS names
if (write_int_comp[ii]) {
getComponentRecord(int_comp_names[ii]).storeChunkRaw(
if (write_int_comp[idx]) {
getComponentRecord(int_comp_names[idx]).storeChunkRaw(
soa.GetIntData(idx).data(), {offset}, {numParticleOnTile64});
}
}
Expand Down
Loading

0 comments on commit 1c709de

Please sign in to comment.