# plasma_plots.gallery

*module*

Output helpers of the struphy-hub example gallery: figure files, profiling exports and metadata.

The example scripts on https://struphy-hub.github.io/examples/ use these helpers to write
their results, so that a copied example runs on its own with ``pip install
"plasma-plots[gallery]"``. Every helper writes to the current directory and names its files
after the example's stem: ``<stem>.png``, ``<stem>.plotly.json`` and ``<stem>.html`` for each
figure, ``<stem>.metadata.json`` for the measured values, and ``<stem>-profile.h5`` with its
plot data for the profiling. The website's build reads exactly these files.

The scripts also run under MPI (``mpirun -n 4 python <script>.py``): the simulation, the
post-processing and the analysis run on every rank, and only rank 0 writes files. The
helpers take care of that, so a script needs no rank checks of its own.

Importing this module sets Struphy's logging level to INFO, which prints one block per time
step (step number, times, wall clock and scalar quantities), as one wants to see in a CI log.

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

## plasma_plots.gallery.GANTT_MAX_INTERVALS

*attribute* · *module attribute*

```python
GANTT_MAX_INTERVALS = 5000
```

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

## plasma_plots.gallery.barrier

*function*

```python
def barrier() -> None
```

Wait until every MPI rank got here; does nothing in a serial run.

Use it, for example, before rank 0 reads a file that the ranks wrote together.

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

## plasma_plots.gallery.export_profiling

*function*

```python
def export_profiling(sim, stem: str) -> dict
```

Export the scope-profiler data of a run made with ``profiling_activated=True``.

Writes the raw HDF5 (``<stem>-profile.h5``) and the plot data that the example page draws
as Plotly figures: durations, gantt and region statistics as JSON. Needs
``scope-profiler[pproc]``, which the ``gallery`` extra installs. Call it on every rank;
only rank 0 writes.

**Parameters**

- `sim` (`struphy.Simulation`) — The simulation, after ``sim.run(profiling_activated=True)``.
- `stem` (`str`) — The example's stem, which names the files.

**Returns**

- (`dict`) — The metadata fields that point to the files, for [`merge_metadata()`][plasma_plots.gallery.merge_metadata].

**Examples**

```pycon
>>> profiling = export_profiling(sim, "weak-landau-damping")
>>> merge_metadata("weak-landau-damping", **profiling)
```

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

## plasma_plots.gallery.heatmap_figure

*function*

```python
def heatmap_figure(data, *, x: str, y: str, title: str, xaxis_title: str, yaxis_title: str, colorbar_title: str = '', colorscale: str = 'Viridis', zmin=None, zmax=None, x_values=None, y_values=None)
```

Draw a two-dimensional array as a Plotly heatmap.

**Parameters**

- `data` (`xarray.DataArray`) — The values, with the two dimensions ``x`` and ``y``.
- `x` (`str`) — The dimension along the horizontal axis.
- `y` (`str`) — The dimension along the vertical axis.
- `title` (`str`) — Title of the figure.
- `xaxis_title` (`str`) — Title of the horizontal axis.
- `yaxis_title` (`str`) — Title of the vertical axis.
- `colorbar_title` (`str`) (default: `''`) — Title of the color bar.
- `colorscale` (`str`) (default: `'Viridis'`) — Plotly color scale. Default: ``"Viridis"``.
- `zmin` (`float`) (default: `None`) — Lower end of the color scale. Default: the data's minimum.
- `zmax` (`float`) (default: `None`) — Upper end of the color scale. Default: the data's maximum.
- `x_values` (`array`) (default: `None`) — Replaces the coordinate of ``x``, e.g. to plot a logical coordinate in physical length.
- `y_values` (`array`) (default: `None`) — Replaces the coordinate of ``y``.

**Returns**

- (`plotly.graph_objects.Figure`) — The heatmap.

**Examples**

```pycon
>>> heatmap_figure(
...     f.plasma.analysis.spatial_average(),
...     x="t",
...     y="v1",
...     title="f(v, t)",
...     xaxis_title="t [a.u.]",
...     yaxis_title="v [a.u.]",
... )
```

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

## plasma_plots.gallery.heatmap_movie

*function*

```python
def heatmap_movie(data, *, x: str, y: str, title: str, xaxis_title: str, yaxis_title: str, colorbar_title: str = '', sweep: str = 't', colorscale: str = 'Viridis', zmin=0.0, zmax=None, x_values=None, y_values=None, max_frames: int = 150)
```

Animate a three-dimensional array as a Plotly heatmap, one frame per ``sweep`` value.

A frame per saved step would embed tens of megabytes in the page, so at most
``max_frames`` evenly spaced frames are kept.

**Parameters**

- `data` (`xarray.DataArray`) — The values, with the dimensions ``x``, ``y`` and ``sweep``.
- `x` (`str`) — The dimension along the horizontal axis.
- `y` (`str`) — The dimension along the vertical axis.
- `title` (`str`) — Title of the figure.
- `xaxis_title` (`str`) — Title of the horizontal axis.
- `yaxis_title` (`str`) — Title of the vertical axis.
- `colorbar_title` (`str`) (default: `''`) — Title of the color bar.
- `sweep` (`str`) (default: `'t'`) — The dimension the frames run over. Default: ``"t"``.
- `colorscale` (`str`) (default: `'Viridis'`) — Plotly color scale. Default: ``"Viridis"``.
- `zmin` (`float`) (default: `0.0`) — Lower end of the color scale. Default: 0.
- `zmax` (`float`) (default: `None`) — Upper end of the color scale. Default: each frame's maximum.
- `x_values` (`array`) (default: `None`) — Replaces the coordinate of ``x``, e.g. in physical length.
- `y_values` (`array`) (default: `None`) — Replaces the coordinate of ``y``.
- `max_frames` (`int`) (default: `150`) — The most frames kept. Default: 150.

**Returns**

- `figure` (`plotly.graph_objects.Figure`) — The animated heatmap, with a play button and a slider.
- `static_z` (`numpy.ndarray`) — A well-developed frame from the middle of the sweep, for ``save_figure(..., static_z=static_z)``.

**Examples**

```pycon
>>> f = out.evaluate("kinetic_ions/e1_v1_density/f")
>>> movie, static_z = heatmap_movie(
...     f,
...     x="eta1",
...     y="v1",
...     title="f(x, v)",
...     xaxis_title="x [a.u.]",
...     yaxis_title="v [a.u.]",
... )
>>> save_figure(
...     movie,
...     "two-stream-instability",
...     suffix="-phase-space",
...     static_z=static_z,
... )
```

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

## plasma_plots.gallery.is_root

*function*

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

Tell whether this process writes files: MPI rank 0, or a serial run.

**Returns**

- (`bool`) — True on rank 0 and in a serial run.

**Examples**

```pycon
>>> if is_root():
...     print(f"measured rate: {rate:.4f}")
```

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

## plasma_plots.gallery.merge_metadata

*function*

```python
def merge_metadata(stem: str, **fields) -> Path
```

Add result fields to ``<stem>.metadata.json``, keeping the fields already there.

Only rank 0 writes.

**Parameters**

- `stem` (`str`) — The example's stem, which names the file.
- `**fields` (default: `{}`) — The fields to add or replace, e.g. measured values, ``figures`` from [`save_extra_figure()`][plasma_plots.gallery.save_extra_figure] and the fields [`export_profiling()`][plasma_plots.gallery.export_profiling] returns.

**Returns**

- (`pathlib.Path`) — The metadata file.

**Raises**

- `RuntimeError` — If a value is a non-finite float: NaN and Infinity are not valid JSON, and a
non-finite result is a broken run.

**Examples**

```pycon
>>> merge_metadata(
...     "weak-landau-damping", measuredDampingRate=rate, **profiling
... )
```

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

## plasma_plots.gallery.save_extra_figure

*function*

```python
def save_extra_figure(figure, stem: str, key: str, *, alt: str, caption: str, static_z=None, static_active=None) -> dict
```

Save an additional figure of an example and return its entry for the ``figures`` metadata.

Writes ``<stem>-<key>.png``, ``.plotly.json`` and ``.html`` with [`save_figure()`][plasma_plots.plotting.save_figure]. The
example page shows every entry of ``figures`` below its main figure: pass the list to
``merge_metadata(stem, figures=[...])``.

**Parameters**

- `figure` (`plotly.graph_objects.Figure`) — The figure to save.
- `stem` (`str`) — The example's stem.
- `key` (`str`) — Names the figure within the example, and its files.
- `alt` (`str`) — Alternative text of the image.
- `caption` (`str`) — Caption below the figure on the example page.
- `static_z` (`array`) (default: `None`) — Replaces the heatmap of the first trace in the PNG only; see [`save_figure()`][plasma_plots.plotting.save_figure].
- `static_active` (`int`) (default: `None`) — The slider position shown in the PNG; see [`save_figure()`][plasma_plots.plotting.save_figure].

**Returns**

- (`dict`) — The entry for the ``figures`` list of [`merge_metadata()`][plasma_plots.gallery.merge_metadata]: ``key``, ``interactive`` and ``thumbnail`` paths, ``alt`` and ``caption``.

**Examples**

```pycon
>>> figures = [
...     save_extra_figure(
...         space_time,
...         "weak-landau-damping",
...         "space-time",
...         alt="Space-time map of E",
...         caption="E(x, t) of the run.",
...     )
... ]
>>> merge_metadata("weak-landau-damping", figures=figures)
```

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

## plasma_plots.gallery.save_figure

*function*

```python
def save_figure(figure, stem: str, *, width: int = 1100, height: int = 650, suffix: str = '', static_z=None, static_data=None, static_active=None) -> None
```

Write static PNG, Plotly JSON and standalone HTML versions of a figure.

The files are ``<stem><suffix>.png``, ``.plotly.json`` and ``.html`` in the current
directory. The PNG is also copied to ``../images/examples/`` when that directory exists,
as the gallery thumbnail. Only rank 0 writes. Tick labels get scientific notation where
the figure has not chosen a format.

An animation that starts at t = 0 can still have an informative static image: ``static_z``
or ``static_data`` replace what the PNG shows, and the JSON and HTML keep the animation.

**Parameters**

- `figure` (`plotly.graph_objects.Figure`) — The figure to save.
- `stem` (`str`) — The example's stem, which names the files.
- `width` (`int`) (default: `1100`) — Width of the PNG in pixels, before the factor 2 of its scale. Default: 1100.
- `height` (`int`) (default: `650`) — Height of the PNG in pixels, before the factor 2 of its scale. Default: 650.
- `suffix` (`str`) (default: `''`) — Appended to the stem, e.g. ``"-space-time"`` for an additional figure.
- `static_z` (`array`) (default: `None`) — Replaces the heatmap of the first trace in the PNG only, e.g. the ``static_z`` that [`heatmap_movie()`][plasma_plots.gallery.heatmap_movie] returns.
- `static_data` (`list of plotly traces`) (default: `None`) — The traces of the PNG, e.g. one frame's ``data``, drawn with the figure's layout.
- `static_active` (`int`) (default: `None`) — The slider position shown in the PNG, with ``static_z`` or ``static_data``.

> **See Also**
>
> [`save_extra_figure()`][plasma_plots.gallery.save_extra_figure] : Save a further figure and return its metadata entry.

**Examples**

```pycon
>>> save_figure(figure, "weak-landau-damping")
```

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

## plasma_plots.gallery.space_time_figure

*function*

```python
def space_time_figure(data, *, space: str, title: str, colorbar_title: str, xaxis_title: str = 'x [a.u.]', x_values=None, colorscale: str = 'RdBu')
```

Draw a space-time map of a field: space along x, time up, colors symmetric about zero.

**Parameters**

- `data` (`xarray.DataArray`) — The field, with the dimensions ``t`` and ``space``.
- `space` (`str`) — The spatial dimension, e.g. ``"eta1"``.
- `title` (`str`) — Title of the figure.
- `colorbar_title` (`str`) — Title of the color bar.
- `xaxis_title` (`str`) (default: `'x [a.u.]'`) — Title of the horizontal axis. Default: ``"x [a.u.]"``.
- `x_values` (`array`) (default: `None`) — Replaces the coordinate of ``space``, e.g. in physical length.
- `colorscale` (`str`) (default: `'RdBu'`) — Plotly color scale. Default: ``"RdBu"``.

**Returns**

- (`plotly.graph_objects.Figure`) — The heatmap.

**Examples**

```pycon
>>> e_x = out.evaluate("em_fields/e_field").isel(component=0, eta2=0, eta3=0)
>>> space_time_figure(e_x, space="eta1", title="E(x, t)", colorbar_title="E_x")
```

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