Skip to content

Commit 552abe3

Browse files
authored
Python: Remove PC Wrapper in Usage (BLAST-WarpX#6357)
Modernize examples to use the new `sim.particles` access to `MultiParticleContainer` directly.
1 parent 47eba2a commit 552abe3

18 files changed

+144
-114
lines changed

Examples/Physics_applications/capacitive_discharge/inputs_base_1d_picmi.py

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from scipy.sparse import csc_matrix
1212
from scipy.sparse import linalg as sla
1313

14-
from pywarpx import callbacks, libwarpx, particle_containers, picmi
14+
from pywarpx import callbacks, libwarpx, picmi
1515
from pywarpx.LoadThirdParty import load_cupy
1616

1717
constants = picmi.constants
@@ -409,36 +409,32 @@ def rethermalize_neutrals(self):
409409
if step % 1000 != 10:
410410
return
411411

412-
if not hasattr(self, "neutral_cont"):
413-
self.neutral_cont = particle_containers.ParticleContainerWrapper(
414-
self.neutrals.name
415-
)
416-
417-
ux_arrays = self.neutral_cont.uxp
418-
uy_arrays = self.neutral_cont.uyp
419-
uz_arrays = self.neutral_cont.uzp
412+
if not hasattr(self, "neutral_particles"):
413+
self.neutral_particles = self.sim.particles.get(self.neutrals.name)
420414

421415
xp, _ = load_cupy()
422416

423417
vel_std = np.sqrt(constants.kb * self.gas_temp / self.m_ion)
424-
for ii in range(len(ux_arrays)):
425-
nps = len(ux_arrays[ii])
426-
ux_arrays[ii][:] = xp.array(vel_std * self.rng.normal(size=nps))
427-
uy_arrays[ii][:] = xp.array(vel_std * self.rng.normal(size=nps))
428-
uz_arrays[ii][:] = xp.array(vel_std * self.rng.normal(size=nps))
418+
for pti in self.neutral_particles.iterator(level=0):
419+
nps = pti.size
420+
pti["ux"][:] = xp.array(vel_std * self.rng.normal(size=nps))
421+
pti["uy"][:] = xp.array(vel_std * self.rng.normal(size=nps))
422+
pti["uz"][:] = xp.array(vel_std * self.rng.normal(size=nps))
429423

430424
def _get_rho_ions(self):
431425
# deposit the ion density in rho_fp
432-
he_ions_wrapper = particle_containers.ParticleContainerWrapper("he_ions")
433-
he_ions_wrapper.deposit_charge_density(level=0)
426+
self.rho.set_val(0.0)
427+
he_ions = self.sim.particles.get("he_ions")
428+
he_ions.deposit_charge(self.rho, lev=0)
429+
libwarpx.warpx.sync_rho()
434430

435-
rho_data = self.rho_wrapper[...]
431+
rho_data = self.rho[...]
436432
self.ion_density_array += rho_data / constants.q_e / self.diag_steps
437433

438434
def run_sim(self):
439435
self.sim.step(self.max_steps - self.diag_steps)
440436

441-
self.rho_wrapper = self.sim.fields.get("rho_fp", level=0)
437+
self.rho = self.sim.fields.get("rho_fp", level=0)
442438
callbacks.installafterstep(self._get_rho_ions)
443439

444440
self.sim.step(self.diag_steps)
@@ -453,9 +449,11 @@ def run_sim(self):
453449
# query the particle z-coordinates if this is run during CI testing
454450
# to cover that functionality
455451
if self.test:
456-
he_ions_wrapper = particle_containers.ParticleContainerWrapper("he_ions")
457-
nparts = he_ions_wrapper.get_particle_count(local=True)
458-
z_coords = np.concatenate(he_ions_wrapper.zp)
452+
he_ions = self.sim.particles.get("he_ions")
453+
nparts = he_ions.number_of_particles(only_local=True)
454+
z_coords = np.concatenate(
455+
list(pti["z"] for pti in he_ions.iterator(level=0))
456+
)
459457
assert len(z_coords) == nparts
460458
assert np.all(z_coords >= 0.0) and np.all(z_coords <= self.gap)
461459

Examples/Physics_applications/laser_acceleration/inputs_test_3d_laser_acceleration_python.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,10 @@ def my_simple_callback():
1818
and
1919
https://warpx.readthedocs.io/en/latest/usage/workflows/python_extend.html#fields
2020
"""
21-
from pywarpx import particle_containers
22-
2321
print(" my_simple_callback")
2422

2523
# electrons: access (and potentially manipulate)
26-
electrons = particle_containers.ParticleContainerWrapper("electrons")
24+
electrons = sim.particles.get("electrons")
2725
print(f" {electrons}")
2826

2927
# electric field: access (and potentially manipulate)
@@ -42,14 +40,9 @@ def my_advanced_callback():
4240
amr = sim.extension.amr
4341
amr.Print(f" {amr.ParallelDescriptor.NProcs()} MPI process(es) active")
4442

45-
# the active simulation
46-
warpx = sim.extension.warpx
47-
4843
# electrons: access (and potentially manipulate)
49-
electrons_pc = warpx.multi_particle_container().get_particle_container_from_name(
50-
"electrons"
51-
)
52-
print(f" {electrons_pc}")
44+
electrons = sim.particles.get("electrons")
45+
print(f" {electrons}")
5346

5447
# electric field: access (and potentially manipulate)
5548
Ex_mf = sim.fields.get("Efield_fp", dir="x", level=0)

Examples/Tests/ohm_solver_ion_Landau_damping/inputs_test_2d_ohm_solver_landau_damping_picmi.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import numpy as np
1515
from mpi4py import MPI as mpi
1616

17-
from pywarpx import callbacks, libwarpx, particle_containers, picmi
17+
from pywarpx import callbacks, libwarpx, picmi
1818

1919
constants = picmi.constants
2020

@@ -254,7 +254,7 @@ def setup_run(self):
254254
simulation.initialize_warpx()
255255

256256
# get ion particle container wrapper
257-
self.ion_part_container = particle_containers.ParticleContainerWrapper("ions")
257+
self.ions = simulation.particles.get("ions")
258258

259259
def text_diag(self):
260260
"""Diagnostic function to print out timing data and particle numbers."""
@@ -269,7 +269,7 @@ def text_diag(self):
269269

270270
status_dict = {
271271
"step": step,
272-
"nplive ions": self.ion_part_container.nps,
272+
"nplive ions": self.ions.size,
273273
"wall_time": wall_time,
274274
"step_rate": step_rate,
275275
"diag_steps": self.diag_steps,

Examples/Tests/ohm_solver_ion_beam_instability/inputs_test_1d_ohm_solver_ion_beam_picmi.py

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import numpy as np
1616
from mpi4py import MPI as mpi
1717

18-
from pywarpx import callbacks, libwarpx, particle_containers, picmi
18+
from pywarpx import callbacks, libwarpx, picmi
1919

2020
constants = picmi.constants
2121

@@ -315,12 +315,8 @@ def setup_run(self):
315315

316316
# create particle container wrapper for the ion species to access
317317
# particle data
318-
self.ion_container_wrapper = particle_containers.ParticleContainerWrapper(
319-
self.ions.name
320-
)
321-
self.beam_ion_container_wrapper = particle_containers.ParticleContainerWrapper(
322-
self.beam_ions.name
323-
)
318+
self.ion_container = simulation.particles.get(self.ions.name)
319+
self.beam_ion_container = simulation.particles.get(self.beam_ions.name)
324320

325321
def _create_data_arrays(self):
326322
self.prev_time = time.time()
@@ -347,8 +343,8 @@ def text_diag(self):
347343

348344
status_dict = {
349345
"step": step,
350-
"nplive beam ions": self.ion_container_wrapper.nps,
351-
"nplive ions": self.beam_ion_container_wrapper.nps,
346+
"nplive ions": self.ion_container.size,
347+
"nplive beam ions": self.beam_ion_container.size,
352348
"wall_time": wall_time,
353349
"step_rate": step_rate,
354350
"diag_steps": self.diag_steps,
@@ -383,8 +379,8 @@ def energy_diagnostic(self):
383379
self._create_data_arrays()
384380

385381
# get the simulation energies
386-
Ec_par, Ec_perp = self._get_kinetic_energy(self.ion_container_wrapper)
387-
Eb_par, Eb_perp = self._get_kinetic_energy(self.beam_ion_container_wrapper)
382+
Ec_par, Ec_perp = self._get_kinetic_energy(self.ion_container)
383+
Eb_par, Eb_perp = self._get_kinetic_energy(self.beam_ion_container)
388384

389385
if libwarpx.amr.ParallelDescriptor.MyProc() != 0:
390386
return
@@ -400,18 +396,17 @@ def energy_diagnostic(self):
400396
def _get_kinetic_energy(self, container_wrapper):
401397
"""Utility function to retrieve the total kinetic energy in the
402398
simulation."""
403-
try:
404-
ux = np.concatenate(container_wrapper.get_particle_ux())
405-
uy = np.concatenate(container_wrapper.get_particle_uy())
406-
uz = np.concatenate(container_wrapper.get_particle_uz())
407-
w = np.concatenate(container_wrapper.get_particle_weight())
408-
except ValueError:
409-
return 0.0, 0.0
410-
411-
my_E_perp = 0.5 * self.M * np.sum(w * (ux**2 + uy**2))
412-
E_perp = comm.allreduce(my_E_perp, op=mpi.SUM)
399+
my_E_perp = 0
400+
my_E_par = 0
401+
for pti in container_wrapper.iterator(level=0):
402+
ux = pti["ux"]
403+
uy = pti["uy"]
404+
uz = pti["uz"]
405+
w = pti["w"]
406+
my_E_perp += 0.5 * self.M * np.sum(w * (ux**2 + uy**2))
407+
my_E_par += 0.5 * self.M * np.sum(w * uz**2)
413408

414-
my_E_par = 0.5 * self.M * np.sum(w * uz**2)
409+
E_perp = comm.allreduce(my_E_perp, op=mpi.SUM)
415410
E_par = comm.allreduce(my_E_par, op=mpi.SUM)
416411

417412
return E_par, E_perp

Examples/Tests/particle_boundary_interaction/inputs_test_rz_particle_boundary_interaction_picmi.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,16 +148,14 @@ def mirror_reflection():
148148
nz = concat(buffer.get_particle_scraped_this_step("electrons", "eb", "nz", lev))
149149

150150
# STEP 2: use these parameters to inject particle from the same position in the plasma
151-
elect_pc = particle_containers.ParticleContainerWrapper(
152-
"electrons"
153-
) # general particle container
151+
electrons = sim.particles.get("electrons") # general particle container
154152

155153
####this part is specific to the case of simple reflection.
156154
un = ux * nx + uy * ny + uz * nz
157155
ux_reflect = -2 * un * nx + ux # for a "mirror reflection" u(sym)=-2(u.n)n+u
158156
uy_reflect = -2 * un * ny + uy
159157
uz_reflect = -2 * un * nz + uz
160-
elect_pc.add_particles(
158+
electrons.add_particles(
161159
x=x + (dt - delta_t) * ux_reflect,
162160
y=y + (dt - delta_t) * uy_reflect,
163161
z=z + (dt - delta_t) * uz_reflect,

Examples/Tests/particle_data_python/inputs_test_2d_particle_attr_access_picmi.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import numpy as np
66

7-
from pywarpx import callbacks, libwarpx, particle_containers, picmi
7+
from pywarpx import callbacks, libwarpx, picmi
88

99
# Create the parser and add the argument
1010
parser = argparse.ArgumentParser()
@@ -106,8 +106,8 @@
106106
# below will be reproducible from run to run
107107
np.random.seed(30025025)
108108

109-
elec_wrapper = particle_containers.ParticleContainerWrapper("electrons")
110-
elec_wrapper.add_real_comp("newPid")
109+
electrons = sim.particles.get("electrons")
110+
electrons.add_real_comp("newPid")
111111

112112
my_id = libwarpx.amr.ParallelDescriptor.MyProc()
113113

@@ -123,7 +123,7 @@ def add_particles():
123123
w = np.ones(nps) * 2.0
124124
newPid = 5.0
125125

126-
elec_wrapper.add_particles(
126+
electrons.add_particles(
127127
x=x,
128128
y=y,
129129
z=z,
@@ -149,12 +149,12 @@ def add_particles():
149149
# are properly set
150150
##########################
151151

152-
assert elec_wrapper.nps == 270 / (2 - args.unique)
153-
assert elec_wrapper.particle_container.get_real_comp_index("w") == 2
154-
assert elec_wrapper.particle_container.get_real_comp_index("newPid") == 6
152+
assert electrons.size == 270 / (2 - args.unique)
153+
assert electrons.get_real_comp_index("w") == 2
154+
assert electrons.get_real_comp_index("newPid") == 6
155155

156-
new_pid_vals = elec_wrapper.get_particle_real_arrays("newPid", 0)
157-
for vals in new_pid_vals:
156+
for pti in electrons.iterator(level=0):
157+
vals = pti["newPid"]
158158
assert np.allclose(vals, 5)
159159

160160
##########################

Examples/Tests/particle_data_python/inputs_test_2d_prev_positions_picmi.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import numpy as np
66

7-
from pywarpx import particle_containers, picmi
7+
from pywarpx import picmi
88

99
constants = picmi.constants
1010

@@ -107,23 +107,21 @@
107107
# exist
108108
##########################
109109

110-
elec_wrapper = particle_containers.ParticleContainerWrapper("electrons")
111-
elec_count = elec_wrapper.nps
110+
electrons = sim.particles.get("electrons")
112111

113112
# check that the runtime attributes have the right indices
114-
assert elec_wrapper.particle_container.get_real_comp_index("prev_x") == 6
115-
assert elec_wrapper.particle_container.get_real_comp_index("prev_z") == 7
113+
assert electrons.get_real_comp_index("prev_x") == 6
114+
assert electrons.get_real_comp_index("prev_z") == 7
116115

117116
# sanity check that the prev_z values are reasonable and
118117
# that the correct number of values are returned
119-
prev_z_vals = elec_wrapper.get_particle_real_arrays("prev_z", 0)
120118
running_count = 0
121-
122-
for z_vals in prev_z_vals:
119+
for pti in electrons.iterator(level=0):
120+
z_vals = pti["prev_z"]
123121
running_count += len(z_vals)
124122
assert np.all(z_vals < zmax)
125123

126-
assert running_count == elec_wrapper.get_particle_count(True)
124+
assert running_count == electrons.number_of_particles(only_local=True)
127125

128126
##########################
129127
# take the final sim step

Examples/Tests/restart/inputs_test_2d_id_cpu_read_picmi.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import numpy as np
99

10-
from pywarpx import callbacks, particle_containers, picmi
10+
from pywarpx import callbacks, libwarpx, picmi
1111

1212
##########################
1313
# physics parameters
@@ -112,8 +112,8 @@
112112
np.random.seed(30025025)
113113

114114
# wrap the electrons particle container
115-
electron_wrapper = particle_containers.ParticleContainerWrapper("electrons")
116-
electron_wrapper.add_real_comp("newPid")
115+
electrons = sim.particles.get("electrons")
116+
electrons.add_real_comp("newPid")
117117

118118

119119
def add_particles():
@@ -127,9 +127,7 @@ def add_particles():
127127
w = np.ones(nps) * 2.0
128128
newPid = 5.0
129129

130-
electron_wrapper.add_particles(
131-
x=x, y=y, z=z, ux=ux, uy=uy, uz=uz, w=w, newPid=newPid
132-
)
130+
electrons.add_particles(x=x, y=y, z=z, ux=ux, uy=uy, uz=uz, w=w, newPid=newPid)
133131

134132

135133
callbacks.installbeforestep(add_particles)
@@ -145,5 +143,15 @@ def add_particles():
145143
# check that the ids and cpus are read properly
146144
###############################################
147145

148-
assert np.sum(np.concatenate(electron_wrapper.get_particle_id())) == 5050
149-
assert np.sum(np.concatenate(electron_wrapper.get_particle_cpu())) == 0
146+
ids_sum = 0
147+
cpu_sum = 0
148+
for pti in electrons.iterator(level=0):
149+
idcpu = pti["idcpu"]
150+
ids = libwarpx.amr.unpack_ids(idcpu)
151+
cpu = libwarpx.amr.unpack_cpus(idcpu)
152+
153+
ids_sum += np.sum(ids)
154+
cpu_sum += np.sum(cpu)
155+
156+
assert ids_sum == 5050
157+
assert cpu_sum == 0

0 commit comments

Comments
 (0)