# plasma_plots.mpi

*module*

Plot on MPI rank 0 only.

A post-processing script started with ``mpirun -n 4 python script.py`` runs every plot on every
rank; without care, four processes draw the same figure and write the same files at once. Every
plotting function here draws on rank 0 and returns a [`SkippedPlot`][plasma_plots.mpi.SkippedPlot] on the other ranks, so
the same script runs unchanged in serial and under MPI::

    out.plot.scalars().save("scalars.png")  # written once, by rank 0

The rank is read without importing ``mpi4py``, which would initialize MPI in a serial run: from
``mpi4py.MPI.COMM_WORLD`` when the application has already initialized MPI, otherwise from the
per-rank variables that MPI launchers export. As in Struphy, ``STRUPHY_MPI=0`` disables the
detection, and every process plots.

Nothing waits for rank 0: a rank that reads a file rank 0 writes must synchronize first, e.g.
with ``MPI.COMM_WORLD.Barrier()``.

[View source](https://github.com/max-models/plasma-plots/blob/devel/src/plasma_plots/mpi.py#L1-L1)

## plasma_plots.mpi.SkippedPlot

*class*

```python
class SkippedPlot
```

What a plot returns on MPI ranks other than 0, where nothing is drawn.

Any public attribute or call returns the same object, so ``plot(...).save(path)``,
``plotter.show()`` or ``animation.save(path)`` run on every rank but act only on rank 0. It is
false and iterates as empty, like the (empty) list of files it wrote.

**Parameters**

- `name` (`str`) — The skipped plot function, shown by ``repr``.
- `rank` (`int`) — The rank that skipped it.

**Examples**

```pycon
>>> skipped = SkippedPlot("plot_slice", rank=1)
>>> skipped.save("slice.png").fig is skipped  # nothing is written
True
>>> list(skipped), bool(skipped)
([], False)
```

[View source](https://github.com/max-models/plasma-plots/blob/devel/src/plasma_plots/mpi.py#L91-L148)

## plasma_plots.mpi.is_plotting_rank

*function*

```python
def is_plotting_rank() -> bool
```

Tell whether this process draws plots and writes their files.

**Returns**

- (`bool`) — ``True`` on rank 0 of an MPI job and in any process outside one.

> **See Also**
>
> [`mpi_rank()`][plasma_plots.mpi.mpi_rank] : The rank this is decided from.

[View source](https://github.com/max-models/plasma-plots/blob/devel/src/plasma_plots/mpi.py#L76-L88)

## plasma_plots.mpi.mpi_rank

*function*

```python
def mpi_rank() -> int
```

Return this process' rank in ``MPI_COMM_WORLD``, without initializing MPI.

The rank comes from ``mpi4py`` if the application has already initialized MPI, otherwise from
the per-rank variables MPI launchers export. ``STRUPHY_MPI=0`` makes every process rank 0.

**Returns**

- (`int`) — The rank, or 0 outside an MPI job.

**Examples**

```pycon
>>> mpi_rank()  # in a serial run
0
```

[View source](https://github.com/max-models/plasma-plots/blob/devel/src/plasma_plots/mpi.py#L38-L73)

## plasma_plots.mpi.rank_zero

*function*

```python
def rank_zero(func)
```

Decorate a plotting function so that it runs on the plotting rank only.

**Parameters**

- `func` (`callable`) — A function that draws a figure or writes files.

**Returns**

- (`callable`) — ``func`` on rank 0 and outside MPI; on other ranks a function that returns a [`SkippedPlot`][plasma_plots.mpi.SkippedPlot] without calling ``func``.

> **See Also**
>
> [`is_plotting_rank()`][plasma_plots.mpi.is_plotting_rank] : Whether this process is the plotting rank.

[View source](https://github.com/max-models/plasma-plots/blob/devel/src/plasma_plots/mpi.py#L151-L177)
