Skip to content

Commit de5215b

Browse files
committed
Doc: Compute Pure SoA Layout
Add an explicit and GPU/CPU portable example to compute on the new, pure SoA particle layout.
1 parent 3a72a95 commit de5215b

File tree

2 files changed

+83
-10
lines changed

2 files changed

+83
-10
lines changed

docs/source/usage/compute.rst

+18-6
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,27 @@ AMReX `Particles <https://amrex-codes.github.io/amrex/docs_html/Particle_Chapter
6161
There are a few small differences to the `iteration over a ParticleContainer <https://amrex-codes.github.io/amrex/docs_html/Particle.html#iterating-over-particles>`__ compared to a ``MultiFab``:
6262

6363
* ``ParticleContainer`` is aware of mesh-refinement levels,
64-
* AMReX supports a variety of data layouts for particles (AoS and SoA + runtime SoA attributes), which requires a few more calls to access.
64+
* AMReX supports a variety of data layouts for particles, the modern pure SoA + runtime attribute layout and the legacy AoS + SoA + runtime SoA attributes layout.
6565

6666
Here is the general structure for computing on particles:
6767

68-
.. literalinclude:: ../../../tests/test_particleContainer.py
69-
:language: python3
70-
:dedent: 4
71-
:start-after: # Manual: Compute PC START
72-
:end-before: # Manual: Compute PC END
68+
.. tab-set::
69+
70+
.. tab-item:: Modern (pure SoA) Layout
71+
72+
.. literalinclude:: ../../../tests/test_particleContainer.py
73+
:language: python3
74+
:dedent: 4
75+
:start-after: # Manual: Pure SoA Compute PC START
76+
:end-before: # Manual: Pure SoA Compute PC END
77+
78+
.. tab-item:: Legacy (AoS + SoA) Layout
79+
80+
.. literalinclude:: ../../../tests/test_particleContainer.py
81+
:language: python3
82+
:dedent: 4
83+
:start-after: # Manual: Legacy Compute PC START
84+
:end-before: # Manual: Legacy Compute PC END
7385

7486
For many small CPU and GPU examples on how to compute on particles, see the following test cases:
7587

tests/test_particleContainer.py

+65-4
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,18 @@ def particle_container(Npart, std_geometry, distmap, boxarr, std_real_box):
4343
return pc
4444

4545

46+
@pytest.fixture(scope="function")
47+
def soa_particle_container(Npart, std_geometry, distmap, boxarr, std_real_box):
48+
pc = amr.ParticleContainer_pureSoA_8_0_default(std_geometry, distmap, boxarr)
49+
myt = amr.ParticleInitType_pureSoA_8_0()
50+
myt.real_array_data = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]
51+
myt.int_array_data = []
52+
53+
iseed = 1
54+
pc.InitRandom(Npart, iseed, myt, False, std_real_box)
55+
return pc
56+
57+
4658
def test_particleInitType():
4759
myt = amr.ParticleInitType_1_1_2_1()
4860
print(myt.real_struct_data)
@@ -276,24 +288,73 @@ def test_per_cell(empty_particle_container, std_geometry, std_particle):
276288
assert ncells * std_particle.real_array_data[1] == sum_1
277289

278290

291+
def test_soa_pc_numpy(soa_particle_container, Npart):
292+
"""Used in docs/source/usage/compute.rst"""
293+
pc = soa_particle_container
294+
295+
class Config:
296+
have_gpu = False
297+
298+
# Manual: Pure SoA Compute PC START
299+
# code-specific getter function, e.g.:
300+
# pc = sim.get_particles()
301+
# Config = sim.extension.Config
302+
303+
# iterate over every mesh-refinement levels (no MR: lev=0)
304+
for lvl in range(pc.finest_level + 1):
305+
# get every local chunk of particles
306+
for pti in pc.iterator(pc, level=lvl):
307+
# additional compile-time and runtime attributes in SoA format
308+
soa = pti.soa().to_cupy() if Config.have_gpu else pti.soa().to_numpy()
309+
310+
# notes:
311+
# Only the next lines are the "HOT LOOP" of the computation.
312+
# For efficiency, use numpy array operation for speed.
313+
314+
# write to all particles in the chunk
315+
# note: careful, if you change particle positions, you need to
316+
# redistribute particles before continuing the simulation step
317+
print(soa.real)
318+
soa.real[0][()] = 0.30 # x
319+
soa.real[1][()] = 0.35 # y
320+
soa.real[2][()] = 0.40 # z
321+
322+
# all other real attributes
323+
for soa_real in soa.real[3:]:
324+
soa_real[()] = 42.0
325+
326+
# all int attributes
327+
for soa_int in soa.int:
328+
soa_int[()] = 12
329+
# Manual: Pure SoA Compute PC END
330+
331+
279332
def test_pc_numpy(particle_container, Npart):
280333
"""Used in docs/source/usage/compute.rst"""
281334
pc = particle_container
282335

283-
# Manual: Compute PC START
336+
class Config:
337+
have_gpu = False
338+
339+
# Manual: Legacy Compute PC START
284340
# code-specific getter function, e.g.:
285341
# pc = sim.get_particles()
342+
# Config = sim.extension.Config
286343

287344
# iterate over every mesh-refinement levels (no MR: lev=0)
288345
for lvl in range(pc.finest_level + 1):
289346
# get every local chunk of particles
290347
for pti in pc.iterator(pc, level=lvl):
291348
# default layout: AoS with positions and cpuid
292349
# note: not part of the new PureSoA particle container layout
293-
aos = pti.aos().to_numpy()
350+
aos = (
351+
pti.aos().to_numpy(copy=True)
352+
if Config.have_gpu
353+
else pti.aos().to_numpy()
354+
)
294355

295356
# additional compile-time and runtime attributes in SoA format
296-
soa = pti.soa().to_numpy()
357+
soa = pti.soa().to_cupy() if Config.have_gpu else pti.soa().to_numpy()
297358

298359
# notes:
299360
# Only the next lines are the "HOT LOOP" of the computation.
@@ -313,7 +374,7 @@ def test_pc_numpy(particle_container, Npart):
313374

314375
for soa_int in soa.int:
315376
soa_int[()] = 12
316-
# Manual: Compute PC END
377+
# Manual: Legacy Compute PC END
317378

318379

319380
@pytest.mark.skipif(

0 commit comments

Comments
 (0)