Particle Swarms¶
Swarm¶
- class underworld3.swarm.Swarm[source]¶
Bases:
Stateful,uw_objectA basic particle swarm implementation for Lagrangian particle tracking and data storage.
The UW Swarm class provides a simplified particle management system that uses PETSc’s DMSWARM_BASIC type. Unlike the standard Swarm class, this implementation does not rely on PETSc to determine ranks for particle migration but instead uses our own kdtree neighbour-domain computations.
This class is preferred for most operations except where particle / cell relationships are always required.
- Parameters:
mesh (uw.discretisation.Mesh) – The mesh object that defines the computational domain for particle operations. Particles will be associated with this mesh for spatial queries and operations.
recycle_rate (int, optional) – Particle recycling (streak swarms) is NOT implemented: values > 1 raise
NotImplementedError. The parameter is retained so that existing calls passing the default (0 or 1, meaning no recycling) keep working.verbose (bool, optional) – Enable verbose output for debugging and monitoring particle operations. Default is False.
Examples
Create a basic swarm and populate with particles:
>>> import underworld3 as uw >>> mesh = uw.meshing.UnstructuredSimplexBox(minCoords=(0,0), maxCoords=(1,1)) >>> swarm = uw.swarm.Swarm(mesh=mesh) >>> swarm.populate(fill_param=2)
Add custom particle data:
>>> temperature = swarm.add_variable("temperature", 1) >>> velocity = swarm.add_variable("velocity", mesh.dim)
Particle migration after coordinate updates:
Note: writing particle coordinates (via
swarm._particle_coordinates.dataor thecoordssetter) marks the swarm for migration; the collectivemigrate()itself is DEFERRED to the next collective point — amigration_control()context exit, an explicitswarm.migrate(), or solve entry — never run per-write (uneven writes would deadlock).Note: swarm.populate uses a the mesh point locations for discontinuous interpolants to determine the particle locations.
- instances = 0¶
- property mesh¶
The mesh this swarm operates on
- property local_size¶
Number of particles on this MPI rank.
- Returns:
Local particle count.
- Return type:
See also
dm.getLocalSizeUnderlying PETSc method.
- property data¶
Particle coordinates (alias for
points; read-only snapshot).Deprecated since version 0.99.0: Use
coordsinstead.- Returns:
Particle coordinate array of shape
(n_particles, dim).- Return type:
- property points¶
Swarm particle coordinates in physical units (read-only snapshot).
Deprecated since version 0.99.0: Read coordinates via
coords; write them via thecoordssetter (physical units) orswarm._particle_coordinates.data(model units).The returned array is a detached, read-only copy. The previous writable wrapper ran collective particle migration from inside a per-write callback, which deadlocks when ranks write unevenly, and reading it could force a collective migration after mesh changes — so a read performed on some ranks only could hang (#379). Like
mesh.points(BF-18), the write path is removed rather than repaired.- Returns:
Particle coordinates (with units if mesh.units is set).
- Return type:
- property coords¶
Swarm particle coordinates in physical units.
This is the primary public interface for accessing particle coordinates. Coordinates are automatically converted from internal model units to physical units based on the model’s reference quantities.
- Returns:
Particle coordinates in physical units with shape (n_particles, dim). If model has reference quantities, returns UWQuantity with appropriate length units. Otherwise returns plain array.
- Return type:
Notes
Coordinates are converted from model units to physical units automatically
For internal use with model units, access swarm._particle_coordinates.data
Setting coordinates accepts either physical units or plain numbers
Examples
>>> coords_physical = swarm.coords # Get physical coordinates >>> swarm.coords = new_coords_with_units # Set from physical units
See also
swarm.unitsGet the unit specification for coordinates
- property units¶
Unit specification for swarm coordinates.
Returns the physical unit string for coordinates based on the model’s reference quantities. This indicates what units the coordinates are in when accessed via the coords property.
- Returns:
Unit string for coordinates (e.g., ‘kilometer’, ‘meter’), or None if no reference quantities are set
- Return type:
str or None
Examples
>>> print(swarm.units) # 'kilometer' if length_scale was set in km >>> coords = swarm.coords # Coordinates in kilometers
- property clip_to_mesh¶
Whether particles are clipped to remain within mesh boundaries.
When True (default), particles that move outside the mesh domain during advection or coordinate updates are removed or repositioned to stay within bounds. When False, particles can exist outside the mesh domain.
- Returns:
Current clipping state.
- Return type:
See also
dont_clip_to_meshContext manager to temporarily disable clipping.
- dont_clip_to_mesh()[source]¶
Context manager that temporarily disables mesh clipping for the swarm. swarm.migrate is called automatically when exiting the context.
- Usage:
- with swarm.dont_clip_to_mesh():
# swarm operations that should not be clipped to mesh swarm.data = new_positions
- migration_disabled()[source]¶
Legacy context manager that completely disables migration. Use migration_control(disable=True) for new code.
Context manager that temporarily disables particle migration for the swarm. Migration is NOT called when exiting the context. Writes made inside the context are packed to PETSc at exit (only the migration is suppressed — data is never discarded).
- Usage:
- with swarm.migration_disabled():
# swarm operations that should not trigger migration swarm.data = new_positions # … other operations … # migrate() will be skipped during these operations
- migration_control(disable=False)[source]¶
Context manager to control particle migration behavior.
- Parameters:
disable (bool) – If False (default), migration is deferred until context exit. If True, migration is completely disabled.
Examples
Defer migration until end (default):
coords = swarm._particle_coordinates.data with swarm.migration_control(): coords[mask1] += delta1 coords[mask2] *= scale # Migration happens HERE on exit
Completely disable migration:
with swarm.migration_control(disable=True): # Operations where migration should never happen # No migration on exit
In both modes, variable/coordinate writes made inside the context are flushed to the underlying DMSwarm at exit — only the migration itself is deferred (default) or skipped (
disable=True).
- populate(**kwargs)¶
Populate the swarm with particles throughout the domain.
- Parameters:
fill_param – Parameter determining the particle count per cell (per dimension) for the given layout, using the mesh degree.
- Raises:
RuntimeError – If the swarm has already been initialized with particles.
- migrate(**kwargs)¶
Migrate swarm across processes after coordinates have been updated.
The algorithm uses a global kD-tree for the centroids of the domains to decide the particle mpi.rank (send to the closest) If the particles are mis-assigned to a particular mpi.rank, the next choice is the second-closest and so on.
A few particles are still not found after this distribution process which probably means they are just outside the mesh. If some points remain lost, they will be deleted if delete_lost_points is set.
- Implementation note:
We retained (above) the name DMSwarmPIC_coor for the particle field to allow this routine to be inherited by a PIC swarm which has this field pre-defined. (We’d need to add a cellid field as well, and re-compute it upon landing)
Note: This is a COLLECTIVE operation - all MPI ranks must call it.
- add_particles_with_coordinates(coordinatesArray)[source]¶
Add particles at given coordinates, keeping only locally-owned points.
Each rank filters the input array and adds only the points that fall within its local domain partition. Non-local points are silently ignored, so it is safe to pass the same global coordinate array to every rank — no duplicates will be created.
This is the recommended method for user code. For pre-partitioned data where each rank already holds only its own points, this method also works correctly.
- Parameters:
coordinatesArray (numpy.ndarray) – Coordinates of new particles, shape
(n, dim).- Returns:
npoints – Number of points actually added on this rank.
- Return type:
- add_particles_with_global_coordinates(globalCoordinatesArray, migrate=True, delete_lost_points=True)[source]¶
Insert the full coordinate array on every rank (low-level primitive).
Every rank inserts all supplied points into its local swarm. There is no locality filtering and no deduplication: migration is a scatter that routes each inserted particle to the rank owning its location, so passing the same array on every rank at
npprocesses yieldsnpcopies of every point.Correct usage is one of:
migrate=False, where each rank deliberately keeps a full copy of the points (the global-evaluation / mesh-transfer pattern), orinput that is pre-partitioned across ranks — or supplied on rank 0 only, with empty
(0, dim)arrays elsewhere — followed by migration to route each point to its owner.
For general use, prefer
add_particles_with_coordinates(), which accepts a rank-identical array and filters non-local points so each point is added exactly once.- Parameters:
globalCoordinatesArray (numpy.ndarray) – Coordinates of new particles, shape
(n, dim).migrate (bool) – Run PETSc swarm migration after insertion (default True).
delete_lost_points (bool) – Remove particles that fall outside any rank’s domain during migration (default True).
- Returns:
npoints – Number of points added on this rank before migration.
- Return type:
- save(filename, compression=False, compressionType='gzip', force_sequential=False)[source]¶
Save the swarm coordinates to a h5 file.
- Parameters:
filename (int) – The filename of the swarm checkpoint file to save to disk.
compression (bool | None) – Add compression to the h5 files (saves space but increases write times with increasing no. of processors)
compressionType (str | None) – Type of compression to use, ‘gzip’ and ‘lzf’ supported. ‘gzip’ is default. Compression also needs to be set to ‘True’.
- read_timestep(base_filename, swarm_id, index, outputPath='', migrate=True)[source]¶
Restore the swarm’s particle coordinates from a saved timestep.
Adds particles at the coordinates saved by
Swarm.write_timestep; call this on a freshly-built swarm before restoring variable values withSwarmVariable.read_timestep. File read (matchingwrite_timestep(filename, swarmname, index, outputPath=...)):{outputPath}/{base_filename}.{swarm_id}.{index:05}.h5- Parameters:
base_filename (str) – Base name used when the checkpoint was written (
filenameonwrite_timestep; noteSwarmVariable.read_timestepspells itdata_filename— the two signatures predate a common convention).swarm_id (str) – Swarm identifier used when the checkpoint was written (
swarmnameonwrite_timestep).index (int) – Timestep index (zero-padded to five digits in the filename).
outputPath (str, optional) – Directory holding the checkpoint files (default: current directory).
migrate (bool, optional) –
True(default): every rank reads the saved coordinates and keeps only the points it owns — the parallel restore path.False: every rank keeps a full copy of the saved swarm, including points outside the local (or entire) domain — useful for debugging, visualisation, or when the mesh has been adapted since the save.
- add_variable(name, size=1, dtype=<class 'float'>, proxy_degree=2, units=None)[source]¶
Add a variable to the swarm.
Variables must be created before the swarm is populated with particles. Once swarm.populate() or similar methods are called, PETSc finalizes field registration and no new variables can be added.
- Parameters:
name (str) – Variable name
size (int, default 1) – Number of components (1 for scalar, 2-3 for vector, etc.)
dtype (type, default float) – Data type (float or int)
proxy_degree (int, default 2) – Degree for mesh proxy variable interpolation
units (str, optional) – Physical units for this variable (e.g., “kg/m^3”, “m/s”)
- Returns:
The created swarm variable
- Return type:
- Raises:
RuntimeError – If swarm is already populated with particles
Examples
Correct usage: >>> swarm = uw.swarm.Swarm(mesh) >>> material = swarm.add_variable(“material”, 1, dtype=int) >>> temperature = swarm.add_variable(“temperature”, 1) >>> swarm.populate(fill_param=3) # Populate after creating variables
Incorrect usage (will raise error): >>> swarm = uw.swarm.Swarm(mesh) >>> swarm.populate(fill_param=3) >>> material = swarm.add_variable(“material”, 1) # ERROR!
- petsc_save_checkpoint(swarmName, index, outputPath='')[source]¶
Use PETSc to save the swarm and attached data to a .pbin and xdmf file.
- write_timestep(filename, swarmname, index, swarmVars=None, outputPath='', time=None, compression=False, compressionType='gzip', force_sequential=False)[source]¶
Save data to h5 and a corresponding xdmf for visualisation using h5py.
- Parameters:
swarmName – Name of the swarm to save.
swarmVars (list | None) – List of swarm objects to save.
index (int) – An index which might correspond to the timestep or output number (for example).
outputPath (str | None) – Path to save the data. If left empty it will save the data in the current working directory.
time (int | None) – Attach the time to the generated xdmf.
compression (bool | None) – Whether to compress the h5 files [bool].
compressionType (str | None) – The type of compression to use. ‘gzip’ and ‘lzf’ are the supported types, with ‘gzip’ as the default.
filename (str)
swarmname (str)
force_sequential (bool | None)
- property vars¶
List of SwarmVariables attached to this swarm.
- Returns:
List of
SwarmVariableobjects defined on this swarm.- Return type:
- snapshot_payload()[source]¶
Return a self-contained dict describing this swarm’s state.
Captured: per-rank particle coordinates (from
DMSwarmPIC_coor) and every user swarm-variable’s data array. PETSc-internal variables (DMSwarmPIC_coor,DMSwarm_X0) are excluded — their contents either come from the captured coords or are regenerated on the next solve.- Return type:
- apply_snapshot_payload(payload)[source]¶
Rebuild this swarm’s local particle population from a payload.
Algorithm:
Drop every current local particle (
dm.removePointfrom the end is O(1) per call, O(N) total).Add the captured-rank’s particles back via the raw PETSc primitives —
addNPointsthen writing the coord field directly. We deliberately bypassadd_particles_with_coordinates()because that method filters viapoints_in_domain(slow) and triggersdm.migrate(unnecessary — saved coords were already local at capture time, and the mesh hasn’t changed).Write captured per-variable data back. The local particle count matches the captured count because we just put the same particles back in the same order.
This bumps
_population_generationonce (from the addNPoints step in restore), which is correct: the population did just change. Downstream consumers that care can compare against the captured value inpayload['captured_population_generation'].- Parameters:
payload (dict)
- Return type:
None
- access(*writeable_vars)[source]¶
Dummy access manager that provides deferred sync for backward compatibility. Uses NDArray_With_Callback.delay_callbacks_global() internally.
This is a compatibility wrapper that allows existing code using the access() context manager to work with the new direct-access variable interfaces. All variable modifications are deferred and synchronized at context exit.
- Parameters:
writeable_vars (SwarmVariable) – Variables that will be modified (ignored - all variables are writable with the new interface, this parameter is kept for API compatibility)
- Return type:
Context manager that defers variable synchronization until exit
Notes
This method is deprecated. New code should access variable.data or variable.array directly without requiring an access context.
- advection(V_fn, delta_t, order=2, corrector=False, restore_points_to_domain_func=None, evalf=False, step_limit=False)[source]¶
Advect the particle swarm through one timestep of a velocity field.
Particle positions are updated with an explicit Runge-Kutta step of \(\dot{\mathbf{x}} = \mathbf{V}(\mathbf{x})\). Velocity is sampled with a global evaluation (off-rank sample points are resolved collectively), so the call is MPI-collective: every rank must make it, including ranks holding no particles. On completion the swarm is migrated so each particle lands on the rank owning its new location; particles that leave the domain are removed (unless
restore_points_to_domain_funcor the mesh’s ownreturn_coords_to_boundsreturns them).- Parameters:
V_fn (vector UW function / sympy expression) – Velocity field, evaluated at particle locations. Any expression accepted by
uw.function.evaluate(), e.g.stokes.u.sym.delta_t (float or UWQuantity) – Timestep. Non-dimensionalised internally (
uw.scaling.non_dimensionalise), so a dimensional time (e.g.uw.quantity(1000, "year")) is valid when scaling is active; a plain float is taken to be in model units, consistent with the model-unit velocity.order (int, optional) – Runge-Kutta order:
2(default) is the midpoint scheme; any other value falls back to first-order forward Euler.corrector (bool, optional) – Historical predictor-corrector option; currently inert (the corrector block is disabled). Retained for call compatibility. Default
False.restore_points_to_domain_func (callable, optional) – Maps an
(n, dim)coordinate array back into the domain (periodic wrap, boundary projection, …). Applied to the updated positions each substep, in addition to the mesh’s ownreturn_coords_to_bounds.evalf (bool, optional) – Historical flag selecting RBF (
evalf) velocity sampling; currently inert — velocity is always sampled withuw.function.global_evaluate. DefaultFalse.step_limit (bool, optional) – If
True, splitdelta_tinto substeps no larger thanestimate_dt()(roughly one element crossing per substep). DefaultFalsehere; noteNodalPointSwarm.advection()defaults it toTrue.
SwarmVariable¶
- class underworld3.swarm.SwarmVariable[source]¶
Bases:
DimensionalityMixin,MathematicalMixin,Stateful,uw_objectVariable supported by a particle swarm (point cloud).
A SwarmVariable stores values at discrete particle locations and provides a mesh-based proxy representation for use in symbolic expressions. This enables Lagrangian tracking of material properties through deformation.
- Parameters:
name (str) – Identifier for this variable (must be unique within the swarm).
swarm (Swarm) – The supporting particle swarm.
size (int or tuple, optional) – Shape specification: int for vectors, tuple for matrices. If None, inferred from
vtype.vtype (VarType, optional) – Variable type (SCALAR, VECTOR, TENSOR, SYM_TENSOR, MATRIX). If None, inferred from
size.dtype (type, default=float) – Data type for storage (float or int).
proxy_degree (int, default=1) – Polynomial degree for the mesh proxy variable.
proxy_continuous (bool, default=True) – Whether the proxy uses continuous (True) or discontinuous (False) interpolation.
varsymbol (str, optional) – LaTeX symbol for display. Defaults to
name.rebuild_on_cycle (bool, default=True) – No effect. Retained for backward compatibility with the removed particle-recycling (streak swarm) feature.
units (str or pint.Unit, optional) – Physical units for this variable (e.g., ‘kelvin’, ‘Pa’). Requires reference quantities to be set on the model.
See also
MeshVariableVariable supported by mesh nodes.
SwarmContainer for particle locations.
Examples
Create a temperature field on a swarm:
>>> swarm = uw.swarm.Swarm(mesh) >>> T = swarm.add_variable("T", size=1, vtype=uw.VarType.SCALAR) >>> T.data[:] = 1600.0 # Set initial temperature
Create a velocity field:
>>> v = swarm.add_variable("v", size=mesh.dim, vtype=uw.VarType.VECTOR)
Notes
SwarmVariables are essential for tracking material properties that advect with the flow. The mesh proxy enables their use in finite element formulations while particle storage preserves Lagrangian history.
- __init__(name, swarm, size=None, vtype=None, dtype=<class 'float'>, proxy_degree=1, proxy_continuous=True, _register=True, _proxy=True, varsymbol=None, rebuild_on_cycle=True, units=None, units_backend=None)[source]¶
Initialize dimensionality tracking attributes
- property units¶
Return the units associated with this variable.
- property has_units¶
Check if this variable has units.
- use_enhanced_array()[source]¶
Deprecated: Array interface is now unified using NDArray_With_Callback
- sync_disabled(description='batch operation')[source]¶
Context manager to disable automatic synchronization for batch operations. Now uses NDArray_With_Callback’s delay_callback mechanism.
- Parameters:
description (str) – Description of the batch operation for debugging
- pack_uw_data_to_petsc(data_array, sync=None)[source]¶
Enhanced pack method that directly accesses PETSc field without access() context. Designed for the new swarmVariable.array interface.
- Parameters:
data_array (numpy.ndarray) – Array data to pack into PETSc field
sync (deprecated) – Never had an effect; deprecated (one DeprecationWarning if passed).
- unpack_uw_data_from_petsc(squeeze=True, sync=None)[source]¶
Enhanced unpack method that directly accesses PETSc field without access() context. Designed for the new swarmVariable.array interface.
- Parameters:
squeeze (bool) – Whether to squeeze singleton dimensions (default True)
sync (deprecated) – Never had an effect; deprecated (one DeprecationWarning if passed).
- pack_raw_data_to_petsc(data_array, sync=None)[source]¶
Pack data array to PETSc using traditional data shape (-1, num_components). Direct PETSc access without access() context for backward compatibility.
- Parameters:
data_array (numpy.ndarray) – Array data in traditional flat format (-1, num_components)
sync (deprecated) – Never had an effect; deprecated (one DeprecationWarning if passed).
- unpack_raw_data_from_petsc(squeeze=True, sync=None)[source]¶
Unpack data from PETSc in traditional data shape (-1, num_components). Direct PETSc access without access() context for backward compatibility.
- Parameters:
squeeze (bool) – Whether to remove singleton dimensions (default True)
sync (deprecated) – Never had an effect; deprecated (one DeprecationWarning if passed).
- Returns:
Array data in traditional flat format (-1, num_components)
- Return type:
- rbf_interpolate(new_coords, verbose=False, nnn=None, order=1, monotone=False)[source]¶
Radial basis function interpolation of particle data to arbitrary points.
By default this reproduces constant and linear fields exactly (
order=1): a polyharmonic kernel with an affine tail over thennnnearest particles. Inverse-distance weighting (order=0) reproduces only constants, so any field with a gradient is smeared by an error that does not vanish as the particles crowd together.- Parameters:
new_coords (numpy.ndarray) – Target coordinates of shape (N, dim) to interpolate to.
verbose (bool, default=False) – Print diagnostic information during interpolation.
nnn (int, optional) – Number of nearest neighbours to use. Defaults to
2 * (mesh.dim + 1)— comfortably above thedim + 2that the affine tail needs, so that near-degenerate particle neighbourhoods do not have to fall back.order (int, default=1) – Polynomial reproduction order: 1 (constants and linears exact) or 0 (constants only, inverse distance). Drops to 0 automatically on a rank holding too few particles to determine the affine tail.
monotone (bool or str, default=False) – Limit the non-affine part of the interpolant to the non-affine variation present in the particle stencil. The local linear trend is preserved, so this does not cost linear exactness.
- Returns:
Interpolated values at the target coordinates.
- Return type:
- property swarm¶
The swarm this variable belongs to (accessed via weak reference). Raises RuntimeError if the swarm has been garbage collected.
- property old_data¶
Original data property implementation.
- Type:
TESTING
- property data¶
Canonical data storage in flat format for internal operations.
Returns particle data in shape
(-1, num_components)regardless of variable type. Values are always non-dimensional (no unit conversion).This property handles PETSc synchronization. The
.arrayproperty is a view of this with shape conversion.When to Use¶
Variable-to-variable transfers: Copying data between swarm variables avoids redundant unit conversions
Low-level operations: Direct access to particle data
Backward compatibility: Existing code using flat format
For general user access, prefer
.arraywhich provides a structured shape.- returns:
Array with shape
(-1, num_components)with automatic PETSc sync.- rtype:
NDArray_With_Callback
Examples
>>> # Efficient variable-to-variable copy >>> new_material.data[...] = old_material.data[...]
>>> # Check data shape >>> scalar_var.data.shape # (n_particles, 1) >>> vector_var.data.shape # (n_particles, dim)
- property array¶
Primary interface for reading and writing particle data.
Returns a structured array view with shape
(N, a, b)whereNis the number of particles and(a, b)depends on the variable type:Scalar:
(N, 1, 1)Vector:
(N, 1, dim)Tensor:
(N, dim, dim)
- Returns:
Array view that delegates changes back to canonical storage.
- Return type:
NDArray
Examples
>>> # Scalar field initialization >>> material_property.array[:, 0, 0] = 1e21 # Set viscosity
>>> # Vector field >>> velocity.array[:, 0, 0] = vx_values # x-component >>> velocity.array[:, 0, 1] = vy_values # y-component
>>> # Reading values >>> max_visc = material_property.array[:, 0, 0].max()
Notes
This property is a view of the canonical
.dataproperty with automatic shape conversion. All modifications trigger proxy variable updates for mesh interpolation.
- property sym¶
Symbolic representation for use in equations.
Returns the symbolic expression from the proxy mesh variable, which can be used in SymPy expressions for constitutive models, boundary conditions, etc.
- Returns:
Symbolic matrix expression.
- Return type:
sympy.Matrix
Notes
The proxy is automatically updated if particle data has changed.
- property sym_1d¶
Flattened symbolic representation.
Returns the symbolic expression as a 1D (column) vector form, useful for Voigt notation in tensor calculations.
- Returns:
Flattened symbolic expression.
- Return type:
sympy.Matrix
- global_max(**kwargs)¶
Maximum value across all MPI ranks.
Finds the maximum value of the particle property across all processors. Useful for finding extreme values in particle swarm data.
- Parameters:
axis (None, int, or tuple of ints, optional) – Axis or axes along which to operate. By default, flattened input is used.
out (None, optional) – Alternative output array (not supported, kept for API compatibility).
keepdims (bool, optional) – If True, reduced axes are left as dimensions with size one.
- Returns:
Maximum value with units preserved (if variable has units).
- Return type:
UWQuantity or scalar
Examples
>>> max_temp = temperature_swarm.global_max() >>> print(f"Maximum temperature: {max_temp}")
Notes
This is a collective operation - all ranks must call it. The result is identical on all ranks.
- global_min(**kwargs)¶
Minimum value across all MPI ranks.
Finds the minimum value of the particle property across all processors. Useful for finding extreme values in particle swarm data.
- Parameters:
axis (None, int, or tuple of ints, optional) – Axis or axes along which to operate. By default, flattened input is used.
out (None, optional) – Alternative output array (not supported, kept for API compatibility).
keepdims (bool, optional) – If True, reduced axes are left as dimensions with size one.
- Returns:
Minimum value with units preserved (if variable has units).
- Return type:
UWQuantity or scalar
Examples
>>> min_pressure = pressure_swarm.global_min() >>> print(f"Minimum pressure: {min_pressure}")
Notes
This is a collective operation - all ranks must call it. The result is identical on all ranks.
- global_sum(**kwargs)¶
Sum of values across all MPI ranks.
Computes the sum of particle property values across all processors.
- Parameters:
axis (None, int, or tuple of ints, optional) – Axis or axes along which to operate. By default, flattened input is used.
out (None, optional) – Alternative output array (not supported, kept for API compatibility).
keepdims (bool, optional) – If True, reduced axes are left as dimensions with size one.
- Returns:
Sum with units preserved (if variable has units).
- Return type:
UWQuantity or scalar
Notes
This is a collective operation - all ranks must call it. The result is identical on all ranks.
Warning: This sum is NOT a physical domain-integrated quantity because particles are non-uniformly distributed. For domain integration, use the proxy mesh variable with uw.maths.Integral().
- global_norm(**kwargs)¶
L2 norm (Frobenius norm) across all MPI ranks.
Computes the L2 norm of particle property values: sqrt(sum(x**2)) across all processors.
- Parameters:
ord ({non-zero int, inf, -inf, 'fro', 'nuc'}, optional) – Order of the norm (default: None = 2-norm)
- Returns:
L2 norm with units preserved (if variable has units).
- Return type:
UWQuantity or scalar
Notes
This is a collective operation - all ranks must call it. The result is identical on all ranks.
For vectors, computes the Frobenius norm treating the array as flattened.
Warning: This norm is NOT a physical domain-integrated quantity because particles are non-uniformly distributed.
- global_size(**kwargs)¶
Total particle count across all MPI ranks.
Returns the total number of particles across all processors. Useful for population monitoring and load balancing diagnostics.
- Returns:
Total number of particles across all ranks.
- Return type:
Examples
>>> total_particles = swarm_var.global_size() >>> local_particles = swarm_var.data.shape[0] >>> print(f"Rank has {local_particles} of {total_particles} particles")
Notes
This is a collective operation - all ranks must call it. The result is identical on all ranks.
- save(filename, compression=False, compressionType='gzip', force_sequential=False)[source]¶
Save the swarm variable to a h5 file.
- Parameters:
filename (int) – The filename of the swarm variable to save to disk.
compression (bool | None) – Add compression to the h5 files (saves space but increases write times with increasing no. of processors)
compressionType (str | None) – Type of compression to use, ‘gzip’ and ‘lzf’ supported. ‘gzip’ is default. Compression also needs to be set to ‘True’.
force_sequential (activate the serial version of hdf5)
- write_proxy(filename)[source]¶
Write this variable’s proxy mesh variable to an HDF5 file.
The proxy is the RBF-interpolated mesh-variable image of the particle data (built when the variable was created with
proxy_degree > 0); this writes that mesh field viaMeshVariable.write, which is often the most convenient checkpoint/visualisation form of particle data.- Parameters:
filename (str) – Output file path, passed directly to
MeshVariable.write(conventionally*.h5).
Notes
If the variable has no proxy (
proxy_degree=0/_proxy=False), nothing is written; a message is printed and the call returns.
- read_timestep(data_filename, swarmID, data_name, index, outputPath='')[source]¶
Restore this variable’s values from a saved timestep.
Reads the checkpoint files written by
Swarm.write_timestepand maps the saved (coordinate, value) pairs onto the current particles by nearest-neighbour interpolation — the live swarm need not have the same particle count or positions as the saved one.Filename convention (matching
Swarm.write_timestep( data_filename, swarmID, swarmVars=[...], outputPath=..., index=...)):swarm coordinates:
{outputPath}/{data_filename}.{swarmID}.{index:05}.h5this variable’s data:
{outputPath}/{data_filename}.{swarmID}.{data_name}.{index:05}.h5
Both files must exist (
RuntimeErrorotherwise).- Parameters:
data_filename (str) – Base name used when the checkpoint was written. (Note: the corresponding parameter on
Swarm.read_timestepis spelledbase_filename— the two signatures predate a common convention.)swarmID (str) – Swarm identifier used when the checkpoint was written (
swarm_idonSwarm.read_timestep).data_name (str) – The saved variable’s
name(Swarm.write_timestepembeds each variable’s.namein its data filename).index (int) – Timestep index (zero-padded to five digits in the filename).
outputPath (str, optional) – Directory holding the checkpoint files (default: current directory).
Notes
MPI-collective. Rank 0 reads the saved data in one shot; the saved points are then routed to their owning ranks with the same migration rule the live swarm uses, so the rank-local nearest-neighbour lookup always sees the correct neighbours.
Index Swarm Variable¶
- class underworld3.swarm.IndexSwarmVariable[source]¶
Bases:
SwarmVariableInteger-valued swarm variable for material tracking.
IndexSwarmVariable stores integer indices at particle locations, typically used for tracking distinct material types. It automatically generates symbolic mask expressions for each material index, enabling material- dependent properties in constitutive models.
- Parameters:
name (str) – Variable name for identification and I/O.
swarm (Swarm) – Parent swarm object.
indices (int) – Number of distinct material indices (default 1).
proxy_degree (int) – Polynomial degree for mesh projection (default 1).
proxy_continuous (bool) – Whether mesh proxy is continuous (default True).
Examples
>>> material = IndexSwarmVariable("M", swarm, indices=3) >>> material.data[:] = 0 # Set all particles to material 0 >>> # Use sym[i] as multiplier for material i properties >>> viscosity = material.sym[0] * 1e20 + material.sym[1] * 1e21
See also
SwarmVariableBase class for particle-supported variables.
- __init__(name, swarm, indices=1, proxy_degree=1, proxy_continuous=True, update_type=0, npoints=5, radius=0.5, npoints_bc=2, ind_bc=None, varsymbol=None)[source]¶
Initialize dimensionality tracking attributes
- property sym¶
Lazy evaluation of symbolic mask array.
Only updates proxy variables when they’re actually needed (when sym is accessed) and only if the proxy variables are marked as stale due to data changes. This avoids expensive RBF interpolation during data assignment operations.
- property sym_1d¶
One-dimensional symbolic mask array (alias for
sym).Returns the same symbolic mask array as
sym, provided for API compatibility with other variable types that distinguish between multi-dimensional and flattened representations.- Returns:
Symbolic mask array of shape (indices, 1).
- Return type:
sympy.Matrix
See also
symPrimary symbolic mask array access.
- createMask(funcsList)[source]¶
Create a material-weighted symbolic expression from per-material values.
This method creates a SymPy expression that combines multiple material properties using the index variable’s symbolic masks. The result can be used directly in Underworld’s solver equations.
- Parameters:
funcsList (list or tuple) – List of values or symbolic expressions, one per material index. Length must equal
indices.- Returns:
Symbolic expression:
sum(funcsList[i] * mask[i] for i in indices).- Return type:
sympy.Basic
- Raises:
RuntimeError – If
funcsListis not a list/tuple or has wrong length.
Examples
>>> # Define viscosity per material >>> viscosity = material.createMask([1e21, 1e20, 1e22]) # 3 materials >>> # Use in solver >>> stokes.constitutive_model.viscosity = viscosity
See also
visMaskCreate visualization mask showing material indices.
- viewMask(expr)[source]¶
Decompose a masked expression into per-material components.
Note
This method is not yet implemented. Currently returns None.
Takes a symbolic expression created by
createMask()and extracts the individual material-specific components.- Parameters:
expr (sympy.Basic) – A masked symbolic expression created by
createMask().- Returns:
List of symbolic expressions, one per material index. Currently returns None (not implemented).
- Return type:
list or None
See also
createMaskCreate a masked expression from per-material values.
- visMask()[source]¶
Create a visualization mask showing material indices.
Returns a symbolic expression where each material region shows its index value (0, 1, 2, …). Useful for visualization and debugging of material distributions.
- Returns:
Symbolic expression evaluating to material index at each point.
- Return type:
sympy.Basic
Examples
>>> vis_field = material.visMask() >>> values = uw.function.evaluate(vis_field, swarm.data) >>> # values[i] gives material index at particle i
See also
createMaskCreate arbitrary material-weighted expressions.
Nodal Point Swarm¶
A swarm type where particles are located at mesh nodes.
- class underworld3.swarm.NodalPointSwarm[source]¶
Bases:
SwarmBASIC_Swarm with particles located at the coordinate points of a meshVariable
Deprecated since version 2026-07:
NodalPointSwarmis deprecated and will be removed in the next release cycle. The semi-Lagrangian history managers inuw.systems.ddtno longer use it and there are no remaining internal callers.The swarmVariable X0 is defined so that the particles can “snap back” to their original locations after they have been moved.
The purpose of this Swarm is to manage sample points for advection schemes based on upstream sampling (method of characteristics etc)
- __init__(trackedVariable, verbose=False)[source]¶
- Parameters:
trackedVariable (EnhancedMeshVariable)
- advection(V_fn, delta_t, order=2, corrector=False, restore_points_to_domain_func=None, evalf=False, step_limit=True)[source]¶
Advect the nodal-point swarm one timestep (semi-Lagrangian sweep).
Records each particle’s launch point (its home mesh node) and origin rank, then delegates to
Swarm.advection(). The recorded launch data is what lets semi-Lagrangian schemes return sampled values to the node the particle departed from after the (possibly off-rank) trajectory.Parameters are those of
Swarm.advection(), with one difference:step_limitdefaults toTrue— trajectories are substepped to at most ~one element crossing per substep (seeSwarm.estimate_dt()), which keeps the node-return bookkeeping robust for largedelta_t.