Skip to content

Commit 6e332e9

Browse files
authored
Particle Container to Pure SoA Again (#4653)
* AMReX & pyAMReX: Latest `development` More pure SoA and id handling goodness. * Particle Container to Pure SoA Again Transition to new, purely SoA particle containers. This was originally merged in #3850 and reverted in #4652, since we discovered issues loosing particles & laser particles on GPU. * Modernize `idcpu` Treatment - faster: less emitted operations, no jumps - cheaper: less used registers - safer: no read-before-write warnings - cooler: no explanation needed
1 parent 60bf000 commit 6e332e9

File tree

63 files changed

+703
-754
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+703
-754
lines changed

.github/workflows/cuda.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ jobs:
115115
which nvcc || echo "nvcc not in PATH!"
116116
117117
git clone https://github.com/AMReX-Codes/amrex.git ../amrex
118-
cd ../amrex && git checkout --detach 24.02 && cd -
118+
cd ../amrex && git checkout --detach 296ed40e16ae1877640f5b78e9162dbd4ba1c279 && cd -
119119
make COMP=gcc QED=FALSE USE_MPI=TRUE USE_GPU=TRUE USE_OMP=FALSE USE_PSATD=TRUE USE_CCACHE=TRUE -j 2
120120
121121
ccache -s

Docs/source/developers/amrex_basics.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ WarpX is built on the Adaptive Mesh Refinement (AMR) library `AMReX <https://git
1313

1414
* ``amrex::MultiFab``: Collection of `FAB` (= ``FArrayBox``) on a single AMR level, distributed over MPI ranks. The concept of `ghost cells` is defined at the ``MultiFab`` level.
1515

16-
* ``amrex::ParticleContainer``: A collection of particles, typically for particles of a physical species. Particles in a ``ParticleContainer`` are organized per ``Box``. Particles in a ``Box`` are organized per tile (this feature is off when running on GPU). Particles within a tile are stored in several structures, each being contiguous in memory: (i) an Array-Of-Struct (AoS) (often called `data`, they are the 3D position, the particle ID and the index of the CPU owning the particle), where the Struct is an ``amrex::Particle`` and (ii) Struct-Of-Arrays (SoA) for extra variables (often called ``attribs``, in WarpX they are the momentum, field on particle etc.).
16+
* ``amrex::ParticleContainer``: A collection of particles, typically for particles of a physical species. Particles in a ``ParticleContainer`` are organized per ``Box``. Particles in a ``Box`` are organized per tile (this feature is off when running on GPU). Particles within a tile are stored in several structures, each being contiguous in memory: (i) a Struct-of-Array (SoA) for ``amrex::ParticleReal`` data such as positions, weight, momentum, etc., (ii) a Struct-of-Array (SoA) for ``int`` data, such as ionization levels, and (iii) a Struct-of-Array (SoA) for a ``uint64_t`` unique identifier index per particle (containing a 40bit id and 24bit cpu sub-identifier as assigned at particle creation time). This id is also used to check if a particle is active/valid or marked for removal.
1717

1818
The simulation domain is decomposed in several ``Box``, and each MPI rank owns (and performs operations on) the fields and particles defined on a few of these ``Box``, but has the metadata of all of them. For convenience, AMReX provides iterators, to easily iterate over all ``FArrayBox`` (or even tile-by-tile, optionally) in a ``MultiFab`` own by the MPI rank (``MFIter``), or over all particles in a ``ParticleContainer`` on a per-box basis (``ParIter``, or its derived class ``WarpXParIter``). These are respectively done in loops like:
1919

Docs/source/developers/dimensionality.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,12 @@ WarpX axis labels ``x, y, z`` ``x, z`` ``z`` ``x, z``
4949
-------------------- ----------- ----------- ----------- -----------
5050
*Particles*
5151
------------------------------------------------------------------------
52-
AMReX AoS ``.pos()`` ``0, 1, 2`` ``0, 1`` ``0`` ``0, 1``
52+
AMReX ``.pos()`` ``0, 1, 2`` ``0, 1`` ``0`` ``0, 1``
5353
WarpX position names ``x, y, z`` ``x, z`` ``z`` ``r, z``
5454
extra SoA attribute ``theta``
5555
==================== =========== =========== =========== ===========
5656

57-
Please see the following sections for particle AoS and SoA details.
57+
Please see the following sections for particle SoA details.
5858

5959
Conventions
6060
-----------

Docs/source/developers/particles.rst

+10-8
Original file line numberDiff line numberDiff line change
@@ -109,24 +109,26 @@ Particle attributes
109109
-------------------
110110

111111
WarpX adds the following particle attributes by default to WarpX particles.
112-
These attributes are either stored in an Array-of-Struct (AoS) or Struct-of-Array (SoA) location of the AMReX particle containers.
112+
These attributes are stored in Struct-of-Array (SoA) locations of the AMReX particle containers: one SoA for ``amrex::ParticleReal`` attributes, one SoA for ``int`` attributes and one SoA for a ``uint64_t`` global particle index per particle.
113113
The data structures for those are either pre-described at compile-time (CT) or runtime (RT).
114114

115-
==================== ================ ================================== ===== ==== =====================
115+
==================== ================ ================================== ===== ==== ======================
116116
Attribute name ``int``/``real`` Description Where When Notes
117-
==================== ================ ================================== ===== ==== =====================
118-
``position_x/y/z`` ``real`` Particle position. AoS CT
119-
``cpu`` ``int`` CPU index where the particle AoS CT
117+
==================== ================ ================================== ===== ==== ======================
118+
``position_x/y/z`` ``real`` Particle position. SoA CT
119+
``weight`` ``real`` Particle position. SoA CT
120+
``momentum_x/y/z`` ``real`` Particle position. SoA CT
121+
``id`` ``amrex::Long`` CPU-local particle index SoA CT First 40 bytes of
122+
where the particle was created. idcpu
123+
``cpu`` ``int`` CPU index where the particle SoA CT Last 24 bytes of idcpu
120124
was created.
121-
``id`` ``int`` CPU-local particle index AoS CT
122-
where the particle was created.
123125
``ionizationLevel`` ``int`` Ion ionization level SoA RT Added when ionization
124126
physics is used.
125127
``opticalDepthQSR`` ``real`` QED: optical depth of the Quantum- SoA RT Added when PICSAR QED
126128
Synchrotron process physics is used.
127129
``opticalDepthBW`` ``real`` QED: optical depth of the Breit- SoA RT Added when PICSAR QED
128130
Wheeler process physics is used.
129-
==================== ================ ================================== ===== ==== =====================
131+
==================== ================ ================================== ===== ==== ======================
130132

131133
WarpX allows extra runtime attributes to be added to particle containers (through ``AddRealComp("attrname")`` or ``AddIntComp("attrname")``).
132134
The attribute name can then be used to access the values of that attribute.

Docs/source/usage/workflows/python_extend.rst

+15-13
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ Particles
190190
@callfromafterstep
191191
def my_after_step_callback():
192192
warpx = sim.extension.warpx
193+
Config = sim.extension.Config
193194
194195
# data access
195196
multi_pc = warpx.multi_particle_container()
@@ -200,27 +201,27 @@ Particles
200201
for lvl in range(pc.finest_level + 1):
201202
# get every local chunk of particles
202203
for pti in pc.iterator(pc, level=lvl):
203-
# default layout: AoS with positions and cpuid
204-
aos = pti.aos().to_numpy()
205-
206-
# additional compile-time and runtime attributes in SoA format
207-
soa = pti.soa().to_numpy()
204+
# compile-time and runtime attributes in SoA format
205+
soa = pti.soa().to_cupy() if Config.have_gpu else \
206+
pti.soa().to_numpy()
208207
209208
# notes:
210209
# Only the next lines are the "HOT LOOP" of the computation.
211-
# For efficiency, use numpy array operation for speed on CPUs.
212-
# For GPUs use .to_cupy() above and compute with cupy or numba.
210+
# For speed, use array operation.
213211
214212
# write to all particles in the chunk
215-
# note: careful, if you change particle positions, you need to
213+
# note: careful, if you change particle positions, you might need to
216214
# redistribute particles before continuing the simulation step
217-
# aos[()]["x"] = 0.30
218-
# aos[()]["y"] = 0.35
219-
# aos[()]["z"] = 0.40
215+
soa.real[0][()] = 0.30 # x
216+
soa.real[1][()] = 0.35 # y
217+
soa.real[2][()] = 0.40 # z
220218
221-
for soa_real in soa.real:
219+
# all other attributes: weight, momentum x, y, z, ...
220+
for soa_real in soa.real[3:]:
222221
soa_real[()] = 42.0
223222
223+
# by default empty unless ionization or QED physics is used
224+
# or other runtime attributes were added manually
224225
for soa_int in soa.int:
225226
soa_int[()] = 12
226227
@@ -252,7 +253,8 @@ Particles can be added to the simulation at specific positions and with specific
252253
.. autoclass:: pywarpx.particle_containers.ParticleContainerWrapper
253254
:members:
254255

255-
The ``get_particle_structs()`` and ``get_particle_arrays()`` functions are called
256+
The ``get_particle_real_arrays()``, ``get_particle_int_arrays()`` and
257+
``get_particle_idcpu_arrays()`` functions are called
256258
by several utility functions of the form ``get_particle_{comp_name}`` where
257259
``comp_name`` is one of ``x``, ``y``, ``z``, ``r``, ``theta``, ``id``, ``cpu``,
258260
``weight``, ``ux``, ``uy`` or ``uz``.

Examples/Tests/particle_data_python/PICMI_inputs_2d.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,10 @@ def add_particles():
153153
##########################
154154

155155
assert (elec_wrapper.nps == 270 / (2 - args.unique))
156-
assert (elec_wrapper.particle_container.get_comp_index('w') == 0)
157-
assert (elec_wrapper.particle_container.get_comp_index('newPid') == 4)
156+
assert (elec_wrapper.particle_container.get_comp_index('w') == 2)
157+
assert (elec_wrapper.particle_container.get_comp_index('newPid') == 6)
158158

159-
new_pid_vals = elec_wrapper.get_particle_arrays('newPid', 0)
159+
new_pid_vals = elec_wrapper.get_particle_real_arrays('newPid', 0)
160160
for vals in new_pid_vals:
161161
assert np.allclose(vals, 5)
162162

Examples/Tests/particle_data_python/PICMI_inputs_prev_pos_2d.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,12 @@
120120
elec_count = elec_wrapper.nps
121121

122122
# check that the runtime attributes have the right indices
123-
assert (elec_wrapper.particle_container.get_comp_index('prev_x') == 4)
124-
assert (elec_wrapper.particle_container.get_comp_index('prev_z') == 5)
123+
assert (elec_wrapper.particle_container.get_comp_index('prev_x') == 6)
124+
assert (elec_wrapper.particle_container.get_comp_index('prev_z') == 7)
125125

126126
# sanity check that the prev_z values are reasonable and
127127
# that the correct number of values are returned
128-
prev_z_vals = elec_wrapper.get_particle_arrays('prev_z', 0)
128+
prev_z_vals = elec_wrapper.get_particle_real_arrays('prev_z', 0)
129129
running_count = 0
130130

131131
for z_vals in prev_z_vals:

Examples/Tests/restart/PICMI_inputs_runtime_component_analyze.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,10 @@ def add_particles():
158158
##########################
159159

160160
assert electron_wrapper.nps == 90
161-
assert electron_wrapper.particle_container.get_comp_index("w") == 0
162-
assert electron_wrapper.particle_container.get_comp_index("newPid") == 4
161+
assert electron_wrapper.particle_container.get_comp_index("w") == 2
162+
assert electron_wrapper.particle_container.get_comp_index("newPid") == 6
163163

164-
new_pid_vals = electron_wrapper.get_particle_arrays("newPid", 0)
164+
new_pid_vals = electron_wrapper.get_particle_real_arrays("newPid", 0)
165165
for vals in new_pid_vals:
166166
assert np.allclose(vals, 5)
167167

Python/pywarpx/_libwarpx.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
#
66
# NOTE: We will reduce the libwarpx.py level of abstraction eventually!
77
# Please add new functionality directly to pybind11-bound modules
8-
# and call them via sim.extension.libwarpx_so. ... and sim.extension.warpx.
9-
# ... from user code.
8+
# and call them via sim.extension.libwarpx_so. ... and sim.extension.Config and
9+
# sim.extension.warpx. ... from user code.
1010
#
1111
# Authors: Axel Huebl, Andrew Myers, David Grote, Remi Lehe, Weiqun Zhang
1212
#
@@ -108,6 +108,8 @@ def load_library(self):
108108
from . import warpx_pybind_3d as cxx_3d
109109
self.libwarpx_so = cxx_3d
110110
self.dim = 3
111+
112+
self.Config = self.libwarpx_so.Config
111113
except ImportError:
112114
raise Exception(f"Dimensionality '{self.geometry_dim}' was not compiled in this Python install. Please recompile with -DWarpX_DIMS={_dims}")
113115

0 commit comments

Comments
 (0)