11"""
22particles.py
33
4- Defines the Species class: a Structure-of-Arrays (SoA) container that stores
5- properties of particles belonging to one species (e.g., electrons or ions).
64
5+ Defines the ``Species`` class: a Structure-of-Arrays (SoA) container that stores
6+ properties of particles belonging to one species (e.g., electrons or ions) in a
7+ 1D-in-space, 3D-in-velocity (1D3V) plasma simulation.
8+
9+
10+ The class keeps positions, velocity components, weights, and alive flags in
11+ separate NumPy arrays for efficient vectorized operations.
12+
13+
14+ Classes
15+ -------
16+ Species
17+ Container for one particle species (e.g., electrons, ions).
18+
19+
20+ Examples
21+ --------
22+ Create an electron species and populate it with uniformly distributed particles:
23+
24+
25+ >>> import numpy as np
26+ >>> from test_particle_sim_1d.particles import Species
27+ >>> electrons = Species(q=-1.0, m=1.0, name="electron", capacity=100)
28+ >>> electrons.initialize_uniform(n=10, z_min=-0.1, z_max=0.1, v0=0.0, seed=42)
29+ >>> len(electrons)
30+ 10
31+ >>> electrons.z.shape
32+ (100,)
733"""
834
9- # import statements
1035from __future__ import annotations
1136
1237import numpy as np
1540# species class definition
1641class Species :
1742 """
18- A simple Structure-of-Arrays (SoA) representation of a particle species.
43+ Structure-of-Arrays (SoA) container for a single particle species.
44+
45+
46+ Each physical quantity (position, velocity components, etc.) is stored in
47+ its own contiguous NumPy array for efficient vectorized operations in a
48+ 1D-in-space, 3D-in-velocity (1D3V) simulation.
1949
20- Each physical quantity (position, velocity, etc.) is stored in its own
21- contiguous NumPy array for efficient vectorized operations.
2250
2351 Attributes
2452 ----------
@@ -51,7 +79,29 @@ def __init__(
5179 capacity : int = 0 ,
5280 dtype = np .float64 ,
5381 ) -> None :
54- """Initialize empty species container"""
82+ """
83+ Initialize an empty species container.
84+
85+
86+ Parameters
87+ ----------
88+ q : float
89+ Charge of one particle in Coulombs.
90+ m : float
91+ Mass of one particle in kilograms.
92+ name : str, optional
93+ Label for this species, e.g., "electron" or "ion".
94+ capacity : int, optional
95+ Initial capacity (number of particle slots) to allocate.
96+ dtype : numpy.dtype, optional
97+ Floating-point dtype used for internal arrays (default numpy.float64).
98+
99+
100+ Returns
101+ -------
102+ None
103+ """
104+
55105 # store physical constants and identifiers
56106 self .q = float (q ) # float
57107 self .m = float (m ) # float
@@ -74,19 +124,27 @@ def __init__(
74124 # public methods
75125 def add_particles (self , z_init : np .ndarray , v_init : np .ndarray ) -> None :
76126 """
77- Append new particles to this species
127+ Append new particles to this species.
128+
78129
79130 Parameters
80131 ----------
81132 z_init : np.ndarray
82- Initial positions of new particles
83- vx_init : np.ndarray
84- Initial velocities of new particles
133+ Initial positions of new particles, shape ``(N,)``.
134+ v_init : np.ndarray
135+ Initial velocities of new particles, shape ``(N, 3)``, ordered
136+ as ``(vx, vy, vz)``.
137+
138+
139+ Returns
140+ -------
141+ None
142+
85143
86144 Notes
87145 -----
88- function assumes both arrays are the same length
89- arrays are appended to the end of the current data
146+ The two input arrays must have the same leading dimension ``N``.
147+ New particles are appended to the end of the existing data.
90148 """
91149
92150 # number of new particles being added
@@ -122,18 +180,28 @@ def initialize_uniform(
122180 seed : int | None = None ,
123181 ) -> None :
124182 """
125- Create N particles uniformly distributed in space
183+ Create particles uniformly distributed in space.
184+
126185
127186 Parameters
128187 ----------
129188 n : int
130- number of particles to create
131- x_min, x_max : float
132- spatial bounds for uniform distribution
133- v0 : float, optional
134- initial velocity assigned to all particles (default 0)
189+ Number of particles to create.
190+ z_min : float
191+ Lower bound of the position domain.
192+ z_max : float
193+ Upper bound of the position domain.
194+ v0 : float or array_like, optional
195+ Initial velocity assigned to all particles. If scalar, it is
196+ interpreted as a single component value; if array-like, it should
197+ broadcast to shape ``(3,)``. Default is 0.0.
135198 seed : int or None, optional
136- random seed for reproducibility
199+ Random seed for reproducibility.
200+
201+
202+ Returns
203+ -------
204+ None
137205 """
138206 # initialize random number generator (NumPy's modern API)
139207 rng = np .random .default_rng (seed )
@@ -142,20 +210,41 @@ def initialize_uniform(
142210 z_new = rng .uniform (z_min , z_max , size = n ).astype (self .dtype )
143211
144212 # all velocities start with the same value v0
145- if v0 is None :
146- v_new = np .zeros ((n , 3 ), dtype = self .dtype )
147- else :
148- v_new = np .tile (v0 , (n , 1 ))
213+ v_new = (
214+ np .zeros ((n , 3 ), dtype = self .dtype ) if v0 is None else np .tile (v0 , (n , 1 ))
215+ )
149216
150217 # reuse add_particles to avoid repeating logic (DRY principle)
151218 self .add_particles (z_new , v_new )
152219
153220 def __len__ (self ) -> int :
221+ """
222+ Number of particles currently stored in this species.
223+
224+
225+ Returns
226+ -------
227+ int
228+ The number of particles ``N``.
229+ """
154230 return self .N
155231
156232 # internal helpers
157233 def _ensure_capacity (self , needed : int ) -> None :
158- """Expand storage arrays if needed"""
234+ """
235+ Ensure that internal arrays can hold at least ``needed`` particles.
236+
237+
238+ Parameters
239+ ----------
240+ needed : int
241+ Required number of particle slots.
242+
243+
244+ Returns
245+ -------
246+ None
247+ """
159248
160249 # guard pattern: early return if no expansion needed
161250 if needed <= self .capacity :
@@ -183,24 +272,30 @@ def _grow(
183272 array_dtype = None ,
184273 ) -> np .ndarray :
185274 """
186- Create a larger copy of existing array
275+ Create a larger copy of an existing 1D array.
276+
187277
188278 Parameters
189279 ----------
190280 arr : np.ndarray
191- old array to copy from
281+ Original array to copy from.
192282 new_cap : int
193- desired total length
194- fill : scalar
195- value to fill the new (empty) portion with
196- array_dtype : np.dtype or None
197- optionally override dtype (used for bool arrays)
283+ Desired total length of the returned array.
284+ fill : scalar, optional
285+ Value used to initialize the newly allocated portion
286+ (elements from ``len(arr)`` to ``new_cap - 1``).
287+ array_dtype : numpy.dtype or None, optional
288+ Optional dtype override for the returned array. If None,
289+ ``arr.dtype`` is used.
290+
198291
199292 Returns
200293 -------
201294 np.ndarray
202- new array containing the old data plus new filler elements
295+ New array of length ``new_cap`` containing the original data in
296+ the first ``len(arr)`` entries and the ``fill`` value in the rest.
203297 """
298+
204299 # pick the right data type
205300 dtype = array_dtype or arr .dtype
206301
0 commit comments