parkipy.distributed.ewald.stokes_sl

parkipy.distributed.ewald.stokes_sl(targets, sources, densities, options)

Compute the distributed Stokes single-layer potential.

\[\boldsymbol{u}(\boldsymbol{x}_i) = \sum_{j=1}^{N} \sum_{p \in P} \left( \frac{\boldsymbol{q}_j}{\lVert \boldsymbol{r}_{ij} \rVert} + \frac{\boldsymbol{r}_{ij} (\boldsymbol{r}_{ij} \cdot \boldsymbol{q}_j)} {\lVert \boldsymbol{r}_{ij} \rVert^3} \right),\]

where \(\boldsymbol{r}_{ij} = \boldsymbol{x}_i - \boldsymbol{y}_j\) and \(P\) is the specified periodicity.

The computation is distributed across MPI ranks. Each rank provides its local slice of targets, sources, and densities. If options.scatter == True (the default), particles are redistributed across ranks via an all-to-all before the Ewald sum; set it to False if the particles are already slab-distributed.

Warning

Only periodicity=1 is currently supported.

Parameters:
  • targets (ndarray) – Local target positions \(\boldsymbol{x}_i\), shape (3, nt_local) with default array ordering ('C' for cupy).

  • sources (ndarray) – Local source positions \(\boldsymbol{y}_j\), shape (3, ns_local) with default array ordering.

  • densities (ndarray) – Single-layer density \(\boldsymbol{q}_j\) at each local source point, shape (3, ns_local) with default array ordering.

  • options (DistributedEwaldOptions) – Dataclass specifying Ewald and distributed execution parameters. See DistributedEwaldOptions.

Returns:

  • potential (ndarray) – Local Stokes single-layer potential at the (redistributed) target points, shape (3, nt_local_out).

  • targets (ndarray) – Redistributed target positions after the slab scatter, shape (3, nt_local_out). Coordinates are in the global frame.

  • walltime (dict, optional) – Per-stage wall-clock times ('p2p', 'p2g', 'fft', 'cnv', 'ifft', 'g2p', and MPI communication stages). Only returned if options.return_walltime == True.

Raises:

NotImplementedError – If options.periodicity is not 1.

Notes

Parameter selection based off [1].

References

Examples

>>> from mpi4py import MPI
>>> import parkipy
>>> import numpy as np
>>> mpi_comm = MPI.COMM_WORLD
>>> size = mpi_comm.Get_size()
>>> am = parkipy.utils.get_array_module(
...     parkipy.utils.get_execution_space("Cuda")
... )
>>> box = [size, 1, 1]
>>> ns = nt = 10000
>>> src  = am.random.rand(3, ns) * am.array(box).reshape(3, 1)
>>> trg  = am.random.rand(3, nt) * am.array(box).reshape(3, 1)
>>> dens = am.random.randn(3, ns)
>>> options = parkipy.distributed.ewald.DistributedEwaldOptions(
...     box=box, periodicity=1, tolerance=1e-4,
...     execution_space="Cuda", cell_size=224,
... )
>>> pot, trg_out = parkipy.distributed.ewald.stokes_sl(trg, src, dens, options)