# plasma_plots.plotting

*module*

Small, composable plotting functions for labeled Struphy output.

Every function takes labeled ``xarray`` objects (fields with dimensions ``t``, ``eta1``, ``eta2``,
``eta3``, ..., time series, marker datasets) and returns a [`PlotResult`][PlotResult] or, for animations, a
``matplotlib.animation.FuncAnimation``. They remain importable for plotting arbitrary labeled
arrays. The optional xarray accessor exposes them as ``array.plasma.plot.*``.

Slices of N-dimensional fields are described by a [`View`][View]: which dimensions to select, which
two to draw and whether in logical or physical coordinates. The slice functions
([`plot_slice()`][plot_slice], [`plot_panels()`][plot_panels], [`animate_slices()`][animate_slices], [`save_frames()`][save_frames],
[`InteractiveSliceViewer`][InteractiveSliceViewer]) share the same rendering options.

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

## plasma_plots.plotting.LOGICAL

*attribute* · *module attribute*

```python
LOGICAL = {name for names in LOGICAL_DIMS for name in names}
```

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

## plasma_plots.plotting.ORBIT_CLASS_COLORS

*attribute* · *module attribute*

```python
ORBIT_CLASS_COLORS = {'passing': 'C0', 'trapped': 'C1', 'lost': '0.55'}
```

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

## plasma_plots.plotting.OVERLAY_KEYS

*attribute* · *module attribute*

```python
OVERLAY_KEYS = {'contours_of', 'contour_levels', 'contour_color', 'boundary', 'boundary_color', 'grid_lines', 'coordinate_lines', 'coordinate_line_color', 'lines', 'line_color', 'points', 'point_color'}
```

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

## plasma_plots.plotting.PLANES

*attribute* · *module attribute*

```python
PLANES = {'XY': ('X', 'Y', 'X', 'Y'), 'XZ': ('X', 'Z', 'X', 'Z'), 'YZ': ('Y', 'Z', 'Y', 'Z'), 'RZ': ('R', 'Z', 'R', 'Z'), 'X1X2': ('X1', 'X2', '$X^1$', '$X^2$')}
```

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

## plasma_plots.plotting.PLOT_STYLE

*attribute* · *module attribute*

```python
PLOT_STYLE = {'figure.figsize': (8.0, 5.0), 'figure.dpi': 110, 'axes.grid': True, 'grid.alpha': 0.3, 'axes.titlesize': 'medium', 'legend.frameon': False, 'image.cmap': 'viridis'}
```

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

## plasma_plots.plotting.REFERENCE_STYLES

*attribute* · *module attribute*

```python
REFERENCE_STYLES = ('--', ':', '-.', (0, (5, 1, 1, 1)))
```

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

## plasma_plots.plotting.logger

*attribute* · *module attribute*

```python
logger = logging.getLogger('plasma_plots')
```

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

## plasma_plots.plotting.InteractiveSliceViewer

*class*

```python
class InteractiveSliceViewer
```

Slider view with the same rendering options as static and exported slices.

It draws on first display (or [`draw()`][draw], [`show()`][show]): ``view.x`` and ``view.y`` default
to the first two dimensions other than the sweep, and every other remaining dimension (the
sweep included) gets a slider over its positions, except dimensions of length one.

**Parameters**

- `data` (`xarray.DataArray`) — The field; dimensions not drawn and not selected by ``view`` get sliders.
- `view` (`View`) (default: `None`) — Which dimensions to select, which two to draw and in which coordinates (see [`View`][plasma_plots.plotting.View]). Default: ``View()``, the two remaining dimensions in logical coordinates.
- `vmin` (`float`) (default: `None`) — The lower color limit. Default: from the data (see ``symmetric`` and ``robust``).
- `vmax` (`float`) (default: `None`) — The upper color limit. Default: from the data (see ``symmetric`` and ``robust``).
- `run_label` (`str`) (default: `None`) — A run description shown as the figure's suptitle. Default: the run shared by the data (see [`shared_run_label()`][plasma_plots.plotting.shared_run_label]); ``""`` for none.
- `shared_clim` (`bool`) (default: `True`) — Take the color limits once from all the selected data (every value of the sweep), so that every slice shares them; ``False`` gives each slice its own. Default: ``True``.
- `cmap` (`str or matplotlib.colors.Colormap`) (default: `None`) — The colormap. Default: ``"viridis"``.
- `equal_aspect` (`bool`) (default: `None`) — Draw both axes to the same scale. Default: ``True`` in physical coordinates, ``False`` in logical ones.
- `title` (`str`) (default: `None`) — The axes title. Default: the array's label, followed by the slider values.
- `symmetric` (`bool`) (default: `False`) — Center the color limits on zero (``-v, v``), as a diverging colormap for a perturbation needs. Default: ``False``.
- `robust` (`bool`) (default: `False`) — Take the color limits from the 1st and 99th percentiles instead of the extremes, so a few outliers do not wash out the rest. Default: ``False``.
- `levels` (`int or sequence of float`) (default: `None`) — Contour lines of the slice: a number of levels spaced evenly between the color limits, or the levels themselves. Drawn in black over the colors, or in the colormap's colors with ``fill=False``. Default: no contour lines.
- `fill` (`bool`) (default: `True`) — Fill the slice with colors; ``False`` leaves it transparent, e.g. to show only the contour lines of ``levels``. Default: ``True``.
- `overlays` (`dict`) (default: `None`) — What to draw on top of the slice, by key (other keys raise a ``ValueError``):  - ``"contours_of"``: another field (``xarray.DataArray``) whose contour lines are drawn,   at the slice's sweep value and other selected coordinates (nearest); - ``"contour_levels"``: their number or levels (default ``10``); - ``"contour_color"``: their color (default black); - ``"boundary"``: ``True`` draws the edges of the grid, leaving out collapsed edges and   closed periodic seams; - ``"boundary_color"``: its color (default black); - ``"grid_lines"``: an integer ``n``, draws every ``n``-th grid line in gray; - ``"coordinate_lines"``: a dict of coordinate names to a number of lines or their values,   e.g. ``{"rho": 4, "theta": 8}``: lines of constant value of one of the two drawn   dimensions, or contour lines of another coordinate over them (e.g. GVEC's PEST angle   ``theta_P``); an angle (a ``period`` attribute) gets ``n`` lines spread over its period   and no line at its seam; - ``"coordinate_line_color"``: their color (default white); - ``"lines"``: a dict of labels to lines, each a function ``y(x)`` or an ``(x, y)``   pair, drawn in dashed styles; - ``"line_color"``: their color (default white); - ``"points"``: a dict of labels to ``(x, y)`` points, marked with crosses; - ``"point_color"``: their color (default white).  Lines and points do not widen the axes and are listed in a legend.
- `xlabel` (`str`) (default: `None`) — The horizontal axis label. Default: the coordinate's name and units.
- `ylabel` (`str`) (default: `None`) — The vertical axis label. Default: the coordinate's name and units.
- `colorbar_label` (`str`) (default: `None`) — The color bar label. Default: the array's label and units.

> **See Also**
>
> [`plot_slice()`][plasma_plots.plotting.plot_slice] : One static slice.

**Examples**

```pycon
>>> InteractiveSliceViewer(
...     phi, view=View(x="eta1", y="eta2"), symmetric=True
... ).show()
```

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

### plasma_plots.plotting.InteractiveSliceViewer.data

*attribute* · *instance attribute*

```python
data = validate_array(data)
```

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

### plasma_plots.plotting.InteractiveSliceViewer.options

*attribute* · *instance attribute*

```python
options = dict(vmin=vmin, vmax=vmax, shared_clim=shared_clim, cmap=cmap, equal_aspect=equal_aspect, title=title, symmetric=symmetric, robust=robust, levels=levels, fill=fill, overlays=overlays, xlabel=xlabel, ylabel=ylabel, colorbar_label=colorbar_label)
```

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

### plasma_plots.plotting.InteractiveSliceViewer.result

*attribute* · *instance attribute*

```python
result = None
```

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

### plasma_plots.plotting.InteractiveSliceViewer.run_label

*attribute* · *instance attribute*

```python
run_label = shared_run_label(data) if run_label is None else run_label
```

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

### plasma_plots.plotting.InteractiveSliceViewer.sliders

*attribute* · *instance attribute*

```python
sliders = {}
```

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

### plasma_plots.plotting.InteractiveSliceViewer.view

*attribute* · *instance attribute*

```python
view = view or View()
```

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

### plasma_plots.plotting.InteractiveSliceViewer.draw

*method*

```python
def draw()
```

Draw the slice and its sliders, once; later calls return the same result.

**Returns**

- (`PlotResult`) — The figure, the axes and the current mesh; ``data["viewer"]`` holds this viewer, which keeps the slider callbacks alive.

**Raises**

- `ValueError` — If fewer than two dimensions other than the sweep remain to draw.

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

### plasma_plots.plotting.InteractiveSliceViewer.show

*method*

```python
def show()
```

Draw the viewer if needed and show it with ``matplotlib.pyplot.show``.

**Returns**

- (`InteractiveSliceViewer`) — This viewer.

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

## plasma_plots.plotting.PlotResult

*class* · *dataclass*

```python
class PlotResult
```

An already-drawn figure and what was drawn; saving never redraws it.

Every plotting function returns one. As the last expression of a notebook cell it displays
its figure once; there is no need to write ``.fig``. With ``backend="plotly"`` (see
[`plasma_plots.plotly_backend`][plasma_plots.plotly_backend]) the figure is a Plotly figure instead, and with
``backend="tikz"`` (see [`plasma_plots.tikz_backend`][plasma_plots.tikz_backend]) a TikZ/pgfplots figure, with the
same ``fit_results`` and ``data``.

A figure made some other way (e.g. with ``plotly.graph_objects`` directly) is saved with the
same defaults as ``PlotResult(figure).save("page.html")``. Saving and showing do nothing on
MPI ranks other than 0, so a script can call them on every rank.

**Attributes**

- `fig` (`(matplotlib.figure.Figure, plotly.graph_objects.Figure or tikzfigure.TikzFigure)`) — The figure.
- `ax` (`matplotlib.axes.Axes, array of matplotlib.axes.Axes or None`) — The axes drawn into; an array (or list) of axes for multi-panel plots; ``None`` for a Plotly or TikZ figure.
- `artists` (`list`) — The drawn artists (lines, meshes, scatter collections, ...); for a Plotly figure its traces; empty for a TikZ figure.
- `fit_results` (`list of FitResult or None`) — The growth-rate fits of [`plot_timeseries()`][plasma_plots.plotting.plot_timeseries], one per series (``None`` where no fit was made); empty for other plots.
- `data` (`dict`) — Extra results of the plot, e.g. ``"counts"`` of [`plot_orbit_classification()`][plasma_plots.plotting.plot_orbit_classification] or ``"markers"`` of [`plot_marker_paths()`][plasma_plots.plotting.plot_marker_paths].

**Examples**

```pycon
>>> result = plot_lineout(phi.isel(t=-1, eta2=0, eta3=0))
>>> result.save("phi.png", dpi=200)
>>> # a figure of your own
>>> PlotResult(go.Figure(go.Scatter(x=t, y=energy))).save("energy.html")
```

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

### plasma_plots.plotting.PlotResult.artists

*attribute* · *class attribute* · *instance attribute*

```python
artists: list = field(default_factory=list)
```

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

### plasma_plots.plotting.PlotResult.ax

*attribute* · *class attribute* · *instance attribute*

```python
ax: object = None
```

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

### plasma_plots.plotting.PlotResult.data

*attribute* · *class attribute* · *instance attribute*

```python
data: dict = field(default_factory=dict)
```

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

### plasma_plots.plotting.PlotResult.fig

*attribute* · *instance attribute*

```python
fig: object
```

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

### plasma_plots.plotting.PlotResult.fit_results

*attribute* · *class attribute* · *instance attribute*

```python
fit_results: list[FitResult | None] = field(default_factory=list)
```

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

### plasma_plots.plotting.PlotResult.save

*method*

```python
def save(path, *, close=False, frame=None, **kwargs)
```

Save the figure to a file, as drawn.

**Parameters**

- `path` (`str or pathlib.Path`) — The file to write; its extension picks the format. A Plotly figure is written as a standalone page (``.html``, loading Plotly's JavaScript from its CDN, responsive, an animation not playing until asked), as figure JSON (``.json``), or as an image through kaleido (``.png``, ``.svg``, ``.pdf``, ...). A TikZ figure is written as its ``tikzpicture`` (``.tikz``, to ``\input`` in a document loading ``pgfplots``) or as a standalone document (``.tex``), with the images it refers to next to the file, or compiled with ``pdflatex`` (``.pdf``, ``.png``).
- `close` (`bool`) (default: `False`) — Close the Matplotlib figure afterwards, to free its memory. Default: ``False``.
- `frame` (`int`) (default: `None`) — For an image of a Plotly animation: the frame it shows, with the slider there (e.g. ``len(result.fig.frames) // 2``, when the first frame is still featureless). The page and the JSON keep the whole animation. Default: the first frame.
- `**kwargs` (default: `{}`) — Passed to ``matplotlib.figure.Figure.savefig`` (e.g. ``dpi``; ``bbox_inches="tight"`` unless given), or to Plotly's ``write_html``, ``write_json`` or ``write_image`` (e.g. ``width``, ``height``, ``scale``; ``dpi`` sets the ``scale``, 100 dpi per unit), or to ``tikzfigure.TikzFigure.savefig`` (e.g. ``dpi`` of a ``.png``).

**Returns**

- (`str`) — The path written (on MPI ranks other than 0 nothing is written).

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

### plasma_plots.plotting.PlotResult.show

*method*

```python
def show()
```

Show the figure with ``matplotlib.pyplot.show``, or a Plotly or TikZ figure with its ``show``.

A TikZ figure is compiled with ``pdflatex`` to be shown.

Afterwards a notebook no longer displays the result again as a cell result.

**Returns**

- (`PlotResult`) — This result.

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

### plasma_plots.plotting.PlotResult.to_plotly

*method*

```python
def to_plotly(close: bool = False) -> 'PlotResult'
```

The same result with the figure converted to an interactive Plotly figure.

For the plotting functions, whose results are Matplotlib figures; the accessor methods
take ``backend="plotly"`` instead.

**Parameters**

- `close` (`bool`) (default: `False`) — Close the Matplotlib figure afterwards. Default: ``False``.

**Returns**

- (`PlotResult`) — A new result: the Plotly figure, its traces as ``artists``, and this result's ``fit_results`` and ``data``.

> **See Also**
>
> [`plasma_plots.plotly_backend.to_plotly()`][plasma_plots.plotly_backend.to_plotly] : The conversion.

**Examples**

```pycon
>>> plot_timeseries(
...     energy, fit=GrowthFit(window=(5.0, 20.0))
... ).to_plotly().save("energy.html")
```

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

### plasma_plots.plotting.PlotResult.to_tikz

*method*

```python
def to_tikz(close: bool = False, **options) -> 'PlotResult'
```

The same result with the figure converted to TikZ/pgfplots code for LaTeX.

For the plotting functions, whose results are Matplotlib figures; the accessor methods
take ``backend="tikz"`` instead.

**Parameters**

- `close` (`bool`) (default: `False`) — Close the Matplotlib figure afterwards. Default: ``False``.
- `**options` (default: `{}`) — Passed to [`plasma_plots.tikz_backend.to_tikz()`][plasma_plots.tikz_backend.to_tikz], e.g. ``raster_dpi``.

**Returns**

- (`PlotResult`) — A new result: the ``tikzfigure.TikzFigure``, and this result's ``fit_results`` and ``data``.

> **See Also**
>
> [`plasma_plots.tikz_backend.to_tikz()`][plasma_plots.tikz_backend.to_tikz] : The conversion.

**Examples**

```pycon
>>> plot_timeseries(energy, fit=GrowthFit(window=(5.0, 20.0))).to_tikz().save(
...     "energy.tex"
... )
```

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

## plasma_plots.plotting.View

*class* · *dataclass*

```python
class View
```

A reusable selection and rendering recipe for an N-dimensional product.

**Attributes**

- `x` (`str or None`) — The dimension along the horizontal axis. Default: the first of the two dimensions left after the selection.
- `y` (`str or None`) — The dimension along the vertical axis. Default: the second of the two dimensions left.
- `sweep` (`str`) — The dimension that panels, animations, exported frames and sliders run over. Default: ``"t"``.
- `select` (`dict of str to float`) — Dimensions to select by coordinate value (nearest), e.g. ``{"eta3": 0.5}``.
- `isel` (`dict of str to int`) — Dimensions to select by integer position, e.g. ``{"eta3": 0}``. A dimension cannot appear in both ``select`` and ``isel``.
- `coordinates` (`{'logical', 'physical'}`) — Draw over the logical coordinates (``eta1``, ...) or over the physical ``X``, ``Y``, ``Z`` coordinates attached to the field. Default: ``"logical"``.
- `plane` (`{'XY', 'XZ', 'YZ', 'RZ', 'X1X2'}`) — The physical plane drawn with ``coordinates="physical"``; ``"RZ"`` uses ``R = √(X² + Y²)``, ``"X1X2"`` GVEC's reference coordinates ``X1``, ``X2`` (see [`plasma_plots.gvec.from_gvec()`][plasma_plots.gvec.from_gvec]). Default: ``"XY"``.

**Examples**

```pycon
>>> view = View(x="eta1", y="eta2", isel={"eta3": 0}, coordinates="physical")
>>> plot_slice(phi.isel(t=-1), view=view)
```

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

### plasma_plots.plotting.View.coordinates

*attribute* · *class attribute* · *instance attribute*

```python
coordinates: Literal['logical', 'physical'] = 'logical'
```

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

### plasma_plots.plotting.View.isel

*attribute* · *class attribute* · *instance attribute*

```python
isel: dict[str, int] = field(default_factory=dict)
```

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

### plasma_plots.plotting.View.plane

*attribute* · *class attribute* · *instance attribute*

```python
plane: Literal['XY', 'XZ', 'YZ', 'RZ'] = 'XY'
```

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

### plasma_plots.plotting.View.select

*attribute* · *class attribute* · *instance attribute*

```python
select: dict[str, float] = field(default_factory=dict)
```

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

### plasma_plots.plotting.View.sweep

*attribute* · *class attribute* · *instance attribute*

```python
sweep: str = 't'
```

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

### plasma_plots.plotting.View.x

*attribute* · *class attribute* · *instance attribute*

```python
x: str | None = None
```

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

### plasma_plots.plotting.View.y

*attribute* · *class attribute* · *instance attribute*

```python
y: str | None = None
```

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

## plasma_plots.plotting.animate_fields

*function*

```python
def animate_fields(fields, *, view=None, interval=100, step=1, max_frames=None, titles=None, **options)
```

Animate several fields side by side, frame by frame in sync over the same sweep.

Each field keeps its own color limits and color bar. Retain the returned animation, e.g. in
a variable, or it stops.

**Parameters**

- `fields` (`sequence of xarray.DataArray`) — Two or more arrays with the same dimensions and sweep coordinate (e.g. the vorticity and density of a Hasegawa-Wakatani run).
- `view` (`View`) (default: `None`) — Which dimensions to select, which two to draw, in which coordinates, and the sweep dimension (``view.sweep``, default ``t``) to run over, for every field (see [`View`][plasma_plots.plotting.View]). Default: ``View()``.
- `interval` (`int`) (default: `100`) — The delay between frames, in milliseconds. Default: ``100``.
- `step` (`int`) (default: `1`) — Use every ``step``-th value of the sweep. Default: ``1``.
- `max_frames` (`int`) (default: `None`) — Keep at most this many frames, evenly spaced over those ``step`` leaves (the first and last included), e.g. to keep a Plotly animation small. Default: all.
- `titles` (`sequence of str`) (default: `None`) — One axes title per field. Default: the fields' labels.
- `**options` (default: `{}`) — Rendering options applied to every field as in [`animate_slices()`][plasma_plots.plotting.animate_slices]: ``vmin``, ``vmax``, ``shared_clim``, ``cmap``, ``equal_aspect``, ``symmetric``, ``robust``, ``levels``, ``fill``, ``overlays``.

**Returns**

- (`matplotlib.animation.FuncAnimation`) — The animation, one frame per used sweep value.

**Raises**

- `ValueError` — If there are fewer than two fields, or they have different numbers of sweep values.

> **See Also**
>
> [`animate_slices()`][plasma_plots.plotting.animate_slices] : One field.

**Examples**

```pycon
>>> animation = animate_fields(
...     [vorticity.isel(eta3=0), density.isel(eta3=0)], symmetric=True
... )
```

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

## plasma_plots.plotting.animate_lines

*function*

```python
def animate_lines(data: xr.DataArray, *, x: str | None = None, sweep: str = 't', reference=None, x_of=None, xlabel: str | None = None, ylim=None, step: int = 1, max_frames: int | None = None, interval: int = 100, title: str | None = None, alongside=None, alongside_logy: bool = False)
```

Animate a one-dimensional profile over ``sweep``, optionally with its exact profile.

The value axis is fixed over the whole animation. With ``alongside``, further panels below it
run in sync: other profiles over the same sweep, or time series (e.g. the energy), drawn whole
with a marker at the current value of the sweep. Retain the returned animation, e.g. in a
variable, or it stops.

**Parameters**

- `data` (`xarray.DataArray`) — The profiles, with the dimensions ``x`` and ``sweep`` (select the rest first).
- `x` (`str`) (default: `None`) — The dimension along the horizontal axis. Default: the one besides ``sweep``.
- `sweep` (`str`) (default: `'t'`) — The dimension to animate over. Default: ``"t"``.
- `reference` (`callable, (x, y) pair or dict`) (default: `None`) — The exact profile of each frame, drawn dashed in black: a function of the plotted ``x`` and of the sweep value (``lambda x, t: ...``; a function of ``x`` alone is fixed), an ``(x, y)`` pair, a 1-D ``xarray.DataArray`` (drawn over its own coordinate), or a dict of labels to these.
- `x_of` (`callable`) (default: `None`) — Maps the ``x`` coordinate to the plotted axis, e.g. ``lambda eta1: L * eta1``.
- `xlabel` (`str`) (default: `None`) — The horizontal axis label. Default: the coordinate's label, or ``"x"`` with ``x_of``.
- `ylim` (`(float, float)`) (default: `None`) — The fixed value axis limits. Default: the range of the data and references, padded by 5 %.
- `step` (`int`) (default: `1`) — Use every ``step``-th value of the sweep. Default: ``1``.
- `max_frames` (`int`) (default: `None`) — Keep at most this many frames, evenly spaced over those ``step`` leaves (the first and last included), e.g. to keep a Plotly animation small. Default: all.
- `interval` (`int`) (default: `100`) — The delay between frames, in milliseconds. Default: ``100``.
- `title` (`str`) (default: `None`) — The title, followed in each frame by the sweep value. Default: the array's label.
- `alongside` (`sequence`) (default: `None`) — One panel below the profile per item, in sync with it: an array over ``sweep`` and one other dimension (another profile, animated, e.g. the density next to the velocity), or an array over ``sweep`` alone, or a list of those (time series such as energies, drawn whole with a marker at each frame's value of the sweep, nearest where the times differ). Default: none.
- `alongside_logy` (`bool`) (default: `False`) — Logarithmic value axes for the time-series panels. Default: ``False``.

**Returns**

- (`matplotlib.animation.FuncAnimation`) — The animation, one frame per used sweep value.

**Raises**

- `ValueError` — If ``sweep`` is missing, more than one other dimension remains, ``step`` or
``max_frames`` is not a positive integer, or an ``alongside`` array has other dimensions
than ``sweep`` and at most one more.

> **See Also**
>
> [`plot_profiles()`][plasma_plots.plotting.plot_profiles] : Several profiles in one axes.

**Examples**

```pycon
>>> animation = animate_lines(
...     phi.isel(eta2=0, eta3=0),
...     reference=lambda x, t: np.cos(t) * np.sin(np.pi * x),
... )
>>> animation = animate_lines(
...     u.isel(eta2=0, eta3=0),
...     alongside=[n.isel(eta2=0, eta3=0), [en_U, en_B]],
... )
```

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

## plasma_plots.plotting.animate_markers

*function*

```python
def animate_markers(markers: xr.Dataset, *, x: str, y: str, color: str | None = None, color_at=None, background: xr.DataArray | None = None, background_options: dict | None = None, step: int = 1, max_frames: int | None = None, interval: int = 100, s: int = 8, cmap=None, trail: int | None = None, paths: bool = False)
```

Animate marker positions over time, optionally over a field animated in sync.

Markers that have left the domain are hidden. The axes limits stay fixed over the whole
animation. Retain the returned animation, e.g. in a variable, or it stops.

**Parameters**

- `markers` (`xarray.Dataset`) — The per-marker data over ``(t, marker)``, e.g. an orbits product.
- `x` (`str`) — The data variable along the horizontal axis, e.g. the position ``"x"``; ``"R"`` is ``√(x² + y²)`` when the data has no ``R`` of its own (with ``y="z"`` the poloidal plane).
- `y` (`str`) — The data variable along the vertical axis, e.g. the position ``"y"``.
- `color` (`str`) (default: `None`) — A variable to color by: per frame, or fixed at the time ``color_at``; or ``"classification"``, the orbit class of each marker (passing, trapped, lost; needs ``v_par``, see [`classify_orbits()`][plasma_plots.analysis.classify_orbits]), with a legend. Default: one color.
- `color_at` (`int or float`) (default: `None`) — Fix the colors at this time (an integer position, e.g. ``0`` for the initial position, to follow fluid parcels, or a float value). Default: the colors of each frame.
- `background` (`xarray.DataArray`) (default: `None`) — A field drawn behind the markers: with a ``t`` dimension (other dimensions selected) at the nearest time of each frame, with shared color limits; without one, fixed (drawn once). On its own ``x``/``y`` dimensions if it has them (a Cartesian field), in logical coordinates if ``x``/``y`` are ``eta1``/``eta2``/``eta3``, else in the physical plane of ``x``/``y`` (the field then needs its ``X``, ``Y``, ``Z`` coordinates).
- `background_options` (`dict`) (default: `None`) — Rendering options for the background, as for [`plot_slice()`][plasma_plots.plotting.plot_slice] (``cmap``, ``symmetric``, ``levels``, ...).
- `step` (`int`) (default: `1`) — Use every ``step``-th time. Default: ``1``.
- `max_frames` (`int`) (default: `None`) — Keep at most this many frames, evenly spaced over those ``step`` leaves (the first and last included), e.g. to keep a Plotly animation small. Default: all.
- `interval` (`int`) (default: `100`) — The delay between frames, in milliseconds. Default: ``100``.
- `s` (`int`) (default: `8`) — The marker size, in points². Default: ``8``.
- `cmap` (`str or matplotlib.colors.Colormap`) (default: `None`) — The colormap for ``color``. Default: ``"viridis"``.
- `trail` (`int`) (default: `None`) — Draw each marker's last ``trail`` samples as a faint line behind it. Default: none.
- `paths` (`bool`) (default: `False`) — Draw each marker's whole path, fixed and faint, under the animation. Default: ``False``.

**Returns**

- (`matplotlib.animation.FuncAnimation`) — The animation, one frame per used time.

**Raises**

- `ValueError` — If ``step``, ``max_frames`` or ``trail`` is not a positive integer.

> **See Also**
>
> [`plot_marker_scatter()`][plasma_plots.plotting.plot_marker_scatter] : The markers at one time.

**Examples**

```pycon
>>> animation = animate_markers(
...     out.orbits["ions"], x="x", y="y", color="x", color_at=0
... )
```

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

## plasma_plots.plotting.animate_slices

*function*

```python
def animate_slices(data: xr.DataArray, *, view=None, interval=100, step=1, max_frames=None, vmin=None, vmax=None, shared_clim=True, cmap=None, equal_aspect=None, title=None, symmetric=False, robust=False, levels=None, fill=True, overlays=None, xlabel=None, ylabel=None, colorbar_label=None)
```

Animate slices with fixed color limits over the selected sweep by default.

Retain the returned animation, e.g. in a variable, or it stops.

**Parameters**

- `data` (`xarray.DataArray`) — The field, with the sweep dimension; other dimensions not drawn are selected by ``view``.
- `view` (`View`) (default: `None`) — Which dimensions to select, which two to draw, in which coordinates, and the sweep dimension (``view.sweep``, default ``t``) to run over (see [`View`][plasma_plots.plotting.View]). Default: ``View()``.
- `interval` (`int`) (default: `100`) — The delay between frames, in milliseconds. Default: ``100``.
- `step` (`int`) (default: `1`) — Use every ``step``-th value of the sweep. Default: ``1``.
- `max_frames` (`int`) (default: `None`) — Keep at most this many frames, evenly spaced over those ``step`` leaves (the first and last included), e.g. to keep a Plotly animation small. Default: all.
- `vmin` (`float`) (default: `None`) — The lower color limit. Default: from the data (see ``symmetric`` and ``robust``).
- `vmax` (`float`) (default: `None`) — The upper color limit. Default: from the data (see ``symmetric`` and ``robust``).
- `shared_clim` (`bool`) (default: `True`) — Take the color limits once from all the selected data (every value of the sweep), so that every slice shares them; ``False`` gives each slice its own. Default: ``True``.
- `cmap` (`str or matplotlib.colors.Colormap`) (default: `None`) — The colormap. Default: ``"viridis"``.
- `equal_aspect` (`bool`) (default: `None`) — Draw both axes to the same scale. Default: ``True`` in physical coordinates, ``False`` in logical ones.
- `title` (`str`) (default: `None`) — The title, followed in each frame by the sweep value. Default: the array's label.
- `symmetric` (`bool`) (default: `False`) — Center the color limits on zero (``-v, v``), as a diverging colormap for a perturbation needs. Default: ``False``.
- `robust` (`bool`) (default: `False`) — Take the color limits from the 1st and 99th percentiles instead of the extremes, so a few outliers do not wash out the rest. Default: ``False``.
- `levels` (`int or sequence of float`) (default: `None`) — Contour lines of the slice: a number of levels spaced evenly between the color limits, or the levels themselves. Drawn in black over the colors, or in the colormap's colors with ``fill=False``. Default: no contour lines.
- `fill` (`bool`) (default: `True`) — Fill the slice with colors; ``False`` leaves it transparent, e.g. to show only the contour lines of ``levels``. Default: ``True``.
- `overlays` (`dict`) (default: `None`) — What to draw on top of the slice, by key (other keys raise a ``ValueError``):  - ``"contours_of"``: another field (``xarray.DataArray``) whose contour lines are drawn,   at the slice's sweep value and other selected coordinates (nearest); - ``"contour_levels"``: their number or levels (default ``10``); - ``"contour_color"``: their color (default black); - ``"boundary"``: ``True`` draws the edges of the grid, leaving out collapsed edges and   closed periodic seams; - ``"boundary_color"``: its color (default black); - ``"grid_lines"``: an integer ``n``, draws every ``n``-th grid line in gray; - ``"coordinate_lines"``: a dict of coordinate names to a number of lines or their values,   e.g. ``{"rho": 4, "theta": 8}``: lines of constant value of one of the two drawn   dimensions, or contour lines of another coordinate over them (e.g. GVEC's PEST angle   ``theta_P``); an angle (a ``period`` attribute) gets ``n`` lines spread over its period   and no line at its seam; - ``"coordinate_line_color"``: their color (default white); - ``"lines"``: a dict of labels to lines, each a function ``y(x)`` or an ``(x, y)``   pair, drawn in dashed styles; - ``"line_color"``: their color (default white); - ``"points"``: a dict of labels to ``(x, y)`` points, marked with crosses; - ``"point_color"``: their color (default white).  Lines and points do not widen the axes and are listed in a legend.
- `xlabel` (`str`) (default: `None`) — The horizontal axis label. Default: the coordinate's name and units.
- `ylabel` (`str`) (default: `None`) — The vertical axis label. Default: the coordinate's name and units.
- `colorbar_label` (`str`) (default: `None`) — The color bar label. Default: the array's label and units.

**Returns**

- (`matplotlib.animation.FuncAnimation`) — The animation, one frame per used sweep value; save it with ``.save("phi.mp4")`` or show it with ``.to_jshtml()``.

**Raises**

- `ValueError` — If ``step`` is not a positive integer, the sweep dimension is missing or empty, or
``overlays`` has unknown keys.

> **See Also**
>
> [`animate_fields()`][plasma_plots.plotting.animate_fields] : Several fields side by side.
> [`save_frames()`][plasma_plots.plotting.save_frames] : The frames as PNG files.

**Examples**

```pycon
>>> animation = animate_slices(phi.isel(eta3=0), step=2, symmetric=True)
>>> animation.save("phi.mp4")
```

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

## plasma_plots.plotting.color_limits

*function*

```python
def color_limits(data, *, symmetric: bool = False, robust: bool = False) -> tuple[float, float]
```

Color limits of the finite values of ``data``.

**Parameters**

- `data` (`array_like or xarray.DataArray`) — The values.
- `symmetric` (`bool`) (default: `False`) — Center the color limits on zero (``-v, v``), as a diverging colormap for a perturbation needs. Default: ``False``.
- `robust` (`bool`) (default: `False`) — Take the color limits from the 1st and 99th percentiles instead of the extremes, so a few outliers do not wash out the rest. Default: ``False``. With ``symmetric``, ``v`` is the 99th percentile of the absolute values.

**Returns**

- (`(float, float)`) — The lower and upper limit.

**Raises**

- `ValueError` — If ``data`` has no finite values.

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

## plasma_plots.plotting.energy_names

*function*

```python
def energy_names(names) -> list[str]
```

The energies among scalar names: ``en_*`` and ``*_energy``, without totals and equilibria.

Struphy names a run's energies either way, depending on the model (``en_U``, ``en_B``,
``en_tot`` or ``electric_energy``, ``magnetic_energy``, ``total_energy``).

**Parameters**

- `names` (`iterable of str`) — Scalar names, e.g. ``out.scalars.data_vars``.

**Returns**

- (`list of str`) — The energy parts, in the order given; ``en_tot``, ``total_energy`` and ``*_eq`` / ``*_tot`` left out.

**Examples**

```pycon
>>> energy_names(["en_U", "en_B", "en_tot", "growth"])
['en_U', 'en_B']
>>> energy_names(["electric_energy", "magnetic_energy", "total_energy"])
['electric_energy', 'magnetic_energy']
```

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

## plasma_plots.plotting.logical_grids

*function*

```python
def logical_grids(data: xr.DataArray, *, x=None, y=None)
```

Return 2-D logical coordinate grids and their labels.

**Parameters**

- `data` (`xarray.DataArray`) — A slice with exactly the dimensions ``x`` and ``y``.
- `x` (`str`) (default: `None`) — The first dimension. Default (with ``y``): the first dimension of a 2-D ``data``.
- `y` (`str`) (default: `None`) — The second dimension. Default (with ``x``): the second dimension of a 2-D ``data``.

**Returns**

- (`tuple`) — ``(xgrid, ygrid, xlabel, ylabel)``: two 2-D arrays (``indexing="ij"``) and the axis labels.

**Raises**

- `ValueError` — If ``data`` does not have exactly the dimensions ``x`` and ``y``.

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

## plasma_plots.plotting.physical_grids

*function*

```python
def physical_grids(data: xr.DataArray, *, plane='XY')
```

Return physical auxiliary coordinates already attached to a selected field.

**Parameters**

- `data` (`xarray.DataArray`) — A 2-D slice with the coordinates ``X``, ``Y`` and ``Z`` attached.
- `plane` (`('XY', 'XZ', 'YZ', 'RZ', 'X1X2')`) (default: `"XY"`) — The physical plane; ``"RZ"`` uses ``R = √(X² + Y²)``, ``"X1X2"`` GVEC's reference coordinates ``X1``, ``X2``. Default: ``"XY"``.

**Returns**

- (`tuple`) — ``(xgrid, ygrid, xlabel, ylabel)``: two 2-D arrays and the axis labels.

**Raises**

- `ValueError` — If ``plane`` is unknown, a physical coordinate is missing, or the coordinates are not 2-D.

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

## plasma_plots.plotting.plot_compare

*function*

```python
def plot_compare(first: xr.DataArray, second: xr.DataArray, *, mode: Literal['difference', 'ratio'] = 'difference', ax=None)
```

Plot a one-dimensional aligned difference or ratio of two arrays.

**Parameters**

- `first` (`xarray.DataArray`) — The first array, one-dimensional.
- `second` (`xarray.DataArray`) — The second array, one-dimensional; only coordinates both share are kept.
- `mode` (`('difference', 'ratio')`) (default: `"difference"`) — ``first - second``, or ``first / second`` (NaN where ``second`` is zero). Default: ``"difference"``.
- `ax` (`matplotlib.axes.Axes`) (default: `None`) — The axes to draw into. Default: a new figure.

**Returns**

- (`PlotResult`) — The figure, the axes and the drawn line.

> **See Also**
>
> [`prepare_compare()`][plasma_plots.plotting.prepare_compare] : The difference or ratio, without plotting.
> [`plot_lineout()`][plasma_plots.plotting.plot_lineout] : Draws the result.

**Examples**

```pycon
>>> plot_compare(phi.isel(t=-1, eta2=0, eta3=0), phi.isel(t=0, eta2=0, eta3=0))
```

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

## plasma_plots.plotting.plot_continuous_spectrum

*function*

```python
def plot_continuous_spectrum(spectrum, x, modes, *, frequencies: dict[str, float] | None = None, mode_label: str = '(m, n)', xlabel: str = 'x', ax=None, title: str = 'Continuous spectrum')
```

Plot the continuum frequencies ``omega(x)`` of each mode.

One color per mode and one line style per branch (e.g. shear Alfvén solid, slow sound
dashed).

**Parameters**

- `spectrum` (`callable`) — Called as ``spectrum(x, *mode)``; must return a mapping of branch name to ``omega(x)``, e.g. Struphy's ``MhdContinousSpectraShearedSlab`` or ``MhdContinousSpectraCylinder`` from ``struphy.dispersion_relations.analytic``, whose modes are ``(m, n)`` pairs (see [`prepare_continuous_spectrum()`][plasma_plots.plotting.prepare_continuous_spectrum]).
- `x` (`array_like`) — The points to evaluate at.
- `modes` (`sequence of tuple`) — The modes, each a tuple of mode numbers (a bare number is a 1-tuple).
- `frequencies` (`dict of str to float`) (default: `None`) — Measured frequencies to mark as horizontal lines (a mapping of label to omega, e.g. a peak read off [`plot_dispersion()`][plasma_plots.plotting.plot_dispersion]), to see whether a mode lies in a continuum gap or crosses a continuum, where it is damped.
- `mode_label` (`str`) (default: `'(m, n)'`) — How modes are named in the legend. Default: ``"(m, n)"``.
- `xlabel` (`str`) (default: `'x'`) — The horizontal axis label. Default: ``"x"``.
- `ax` (`matplotlib.axes.Axes`) (default: `None`) — The axes to draw into. Default: a new figure.
- `title` (`str`) (default: `'Continuous spectrum'`) — The axes title. Default: ``"Continuous spectrum"``.

**Returns**

- (`PlotResult`) — The figure, the axes and the drawn lines; ``data["spectrum"]`` holds the evaluated spectrum from [`prepare_continuous_spectrum()`][plasma_plots.plotting.prepare_continuous_spectrum].

**Raises**

- `ValueError` — If ``modes`` is empty.

**Examples**

```pycon
>>> plot_continuous_spectrum(
...     spectrum,
...     np.linspace(0, 1, 200),
...     [(1, 1), (2, 1)],
...     frequencies={"measured": 0.42},
... )
```

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

## plasma_plots.plotting.plot_convergence

*function*

```python
def plot_convergence(sizes, errors, *, ax=None, order=None, label=None, xlabel='resolution', title='Convergence')
```

Log-log plot of an error norm against resolution or step size, e.g. from a convergence study.

**Parameters**

- `sizes` (`array_like`) — The resolutions or step sizes.
- `errors` (`array_like`) — The error norm at each size.
- `ax` (`matplotlib.axes.Axes`) (default: `None`) — The axes to draw into. Default: a new figure.
- `order` (`float`) (default: `None`) — With ``None`` (default), fits and draws the observed order via [`plasma_plots.analysis.convergence_order()`][plasma_plots.analysis.convergence_order]. Pass an explicit ``order`` (e.g. ``2`` for second-order) to draw a reference slope through the first point instead of fitting one.
- `label` (`str`) (default: `None`) — The legend label of the errors. Default: none.
- `xlabel` (`str`) (default: `'resolution'`) — The horizontal axis label. Default: ``"resolution"``.
- `title` (`str`) (default: `'Convergence'`) — The axes title. Default: ``"Convergence"``.

**Returns**

- (`PlotResult`) — The figure, the axes, the error line and the fitted or reference line.

**Examples**

```pycon
>>> plot_convergence([16, 32, 64, 128], errors, order=2)
```

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

## plasma_plots.plotting.plot_critical_points

*function*

```python
def plot_critical_points(data: xr.DataArray, *, view=None, ax=None, levels=14, cmap=None, label_values: bool = False, **options)
```

The flux function's contours with its O-points (dots) and X-points (crosses).

**Parameters**

- `data` (`xarray.DataArray`) — The flux function, e.g. [`flux_function()`][plasma_plots.analysis.flux_function] of ``B``, with every dimension but the plane's two selected (``view`` may do the selecting).
- `view` (`View`) (default: `None`) — The slice to draw (see [`View`][plasma_plots.plotting.View]): which dimensions, logical or physical coordinates. Default: the two logical directions left, in logical coordinates.
- `ax` (`matplotlib.axes.Axes`) (default: `None`) — The axes to draw into. Default: a new figure.
- `levels` (`int or sequence of float`) (default: `14`) — Contour lines of the flux, as for [`plot_slice()`][plasma_plots.plotting.plot_slice]. Default: 14.
- `cmap` (`str or matplotlib.colors.Colormap`) (default: `None`) — The colormap. Default: ``"RdBu_r"``.
- `label_values` (`bool`) (default: `False`) — Write the flux value next to each point. Default: False.
- `**options` (default: `{}`) — Further options of [`plot_slice()`][plasma_plots.plotting.plot_slice] (``symmetric``, ``overlays``, ...).

**Returns**

- (`PlotResult`) — The figure, the axes, the mesh, the contours and the two scatters; ``data["points"]`` holds the [`critical_points()`][plasma_plots.analysis.critical_points].

> **See Also**
>
> [`plasma_plots.analysis.critical_points()`][plasma_plots.analysis.critical_points] : The points.
> [`plasma_plots.analysis.reconnected_flux()`][plasma_plots.analysis.reconnected_flux] : The flux between them over time.

**Examples**

```pycon
>>> plot_critical_points(flux_function(B).isel(t=-1))
>>> plot_critical_points(
...     flux_function(B), view=View(isel={"t": -1}, coordinates="physical")
... )
```

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

## plasma_plots.plotting.plot_dispersion

*function*

```python
def plot_dispersion(data: xr.DataArray, *, dim: str | None = None, detrend: bool = True, branches: dict | None = None, log: bool = True, dynamic_range: float = 6.0, kmin: float | None = None, kmax: float | None = None, omega_max: float | None = None, vmin: float | None = None, vmax: float | None = None, cmap=None, ax=None, title: str | None = None, frequencies: dict | None = None, points: dict | None = None, fits=())
```

The space-time power spectrum of a ``(t, dim)`` field, as a dispersion-relation plot.

The spectrum may also be given directly, e.g. from [`power_spectrum()`][plasma_plots.analysis.power_spectrum]
(then ``dim`` and ``detrend`` are not used). Shows only non-negative frequencies (a real signal's spectrum is symmetric under
``(k, ω) → (-k, -ω)``, so every branch already appears on both sides of ``k = 0``).

A dispersion relation's power spans many orders of magnitude (the ridge against a mostly-empty
plane), so with ``log=True`` (default), color limits default to the top ``dynamic_range``
decades below the peak, rather than the full range down to numerical noise; override with
``vmin``/``vmax`` if the ridge still looks washed out or overly clipped.

**Parameters**

- `data` (`xarray.DataArray`) — The field, with the dimensions ``t`` and ``dim`` (select the rest first), or its power spectrum, with the dimensions ``omega`` and ``k``.
- `dim` (`str`) (default: `None`) — The spatial dimension to transform. Default: the one besides ``t`` (see [`plasma_plots.analysis.power_spectrum()`][plasma_plots.analysis.power_spectrum]).
- `detrend` (`bool`) (default: `True`) — Remove the time-mean at each point of ``dim`` first, which otherwise dominates the spectrum as a spurious zero-frequency line. Default: ``True``.
- `branches` (`dict or callable`) (default: `None`) — Theoretical curves to compare against, drawn dashed: a dict of labels to a callable ``omega(k)`` or an explicit ``(k, omega)`` pair of arrays; or one callable that returns a dict of branch names to frequencies, such as the dispersion relations of [`plasma_plots.theory`][plasma_plots.theory] or Struphy's ``struphy.dispersion_relations`` objects (a callable in the dict may return such a dict too). Complex frequencies are drawn by their real part.
- `log` (`bool`) (default: `True`) — Color by ``log10`` of the power. Default: ``True``.
- `dynamic_range` (`float`) (default: `6.0`) — With ``log``, the number of decades below the peak that the default color limits cover. Default: ``6.0``.
- `kmin` (`float`) (default: `None`) — Show only ``k >= kmin``, e.g. ``0`` for the positive quadrant. Default: all ``k``.
- `kmax` (`float`) (default: `None`) — Show only ``|k| <= kmax``. Default: all ``k``.
- `omega_max` (`float`) (default: `None`) — Show only ``ω <= omega_max``. Default: all non-negative ``ω``.
- `vmin` (`float`) (default: `None`) — The lower color limit (in ``log10`` of the power with ``log``). Default: the peak minus ``dynamic_range`` with ``log``, else the minimum.
- `vmax` (`float`) (default: `None`) — The upper color limit (in ``log10`` of the power with ``log``). Default: the peak.
- `cmap` (`str or matplotlib.colors.Colormap`) (default: `None`) — The colormap. Default: Matplotlib's default.
- `ax` (`matplotlib.axes.Axes`) (default: `None`) — The axes to draw into. Default: a new figure.
- `title` (`str`) (default: `None`) — The axes title. Default: ``"Dispersion relation of <label>"``.
- `frequencies` (`dict of str to float`) (default: `None`) — Labeled horizontal lines, e.g. cutoffs or resonances.
- `points` (`dict`) (default: `None`) — Measured points to mark: a dict of labels to a ``(k, omega)`` pair or a [`trace_branch()`][plasma_plots.spectral.trace_branch] result (an ``xarray.Dataset`` with ``k`` and ``omega``).
- `fits` (`sequence of BranchFit`) (default: `()`) — Fitted straight branches from [`fit_dispersion_branches()`][plasma_plots.analysis.fit_dispersion_branches], drawn dotted as ``omega = velocity * k`` over the shown ``k >= 0``. Default: none.

**Returns**

- (`PlotResult`) — The figure, the axes, the mesh and the drawn lines and points.

> **See Also**
>
> [`plasma_plots.analysis.power_spectrum()`][plasma_plots.analysis.power_spectrum] : The spectrum, without plotting.
> [`plasma_plots.analysis.fit_dispersion_branches()`][plasma_plots.analysis.fit_dispersion_branches] : Straight branches fitted to it, for ``fits``.
> [`plot_continuous_spectrum()`][plasma_plots.plotting.plot_continuous_spectrum] : Continuum frequencies to compare a measured frequency with.

**Examples**

```pycon
>>> plot_dispersion(
...     e_field.isel(eta2=0, eta3=0),
...     branches={"Langmuir": lambda k: np.sqrt(1 + 3 * k**2)},
... )
```

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

## plasma_plots.plotting.plot_energy_budget

*function*

```python
def plot_energy_budget(scalars, *, parts=None, total: str | None = 'en_tot', groups: dict | None = None, logy: bool = False, run_label=None)
```

An energy budget: the energy parts, the relative drift of the total, and exchanges.

The first panel shows the parts, with the total in black. The second panel is
``(total - total(0)) / total(0)``, which should stay flat for a conservative scheme. With
``groups``, a third panel shows each group's change since ``t = 0``, and for two groups also
minus the second one's (dashed), so that the curves overlap where energy only moves between
them.

**Parameters**

- `scalars` (`xarray.Dataset or mapping of str to xarray.DataArray`) — The time series (``out.scalars``), each with the only dimension ``t``.
- `parts` (`sequence of str`) (default: `None`) — The energies drawn in the first panel. Default: the scalars [`energy_names()`][plasma_plots.plotting.energy_names] finds (``en_*`` and ``*_energy``), except ``total``.
- `total` (`str or None`) (default: `'en_tot'`) — The total energy; the second panel is left out if it is ``None`` or not among the scalars. Default: ``"en_tot"``, or ``"total_energy"`` for a run that names its energies that way.
- `groups` (`dict of str to list of str`) (default: `None`) — A label to the names it sums, e.g. ``{"wave": ["en_U", "en_B", "en_p"], "energetic ions": ["en_fv", "en_fB"]}``. Default: no exchange panel.
- `logy` (`bool`) (default: `False`) — Use a logarithmic value axis in the first panel. Default: ``False``.
- `run_label` (`str`) (default: `None`) — A run description shown as the figure's suptitle. Default: the run shared by the parts (see [`shared_run_label()`][plasma_plots.plotting.shared_run_label]); ``""`` for none.

**Returns**

- (`PlotResult`) — The figure, the list of axes (one per panel) and the drawn lines.

**Raises**

- `ValueError` — If a used scalar is not a time series with the only dimension ``t``.

**Examples**

```pycon
>>> plot_energy_budget(
...     out.scalars,
...     groups={"wave": ["en_U", "en_B", "en_p"], "ions": ["en_fv"]},
... )
```

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

## plasma_plots.plotting.plot_equilibrium_profile

*function*

```python
def plot_equilibrium_profile(equil, domain, *, n_points=100, ax=None)
```

Plot radial profiles of a fluid equilibrium along ``eta1`` (at ``eta2 = eta3 = 0``).

Plots ``p0``, and ``n0`` and ``T0 = p0 / n0`` if ``equil`` has a density profile too,
against ``R = √(x² + y²)``.

**Parameters**

- `equil` (`struphy.fields_background.base.FluidEquilibrium`) — The equilibrium, e.g. from ``out.equil``.
- `domain` (`struphy.geometry.base.Domain`) — Its mapping (``out.domain``).
- `n_points` (`int`) (default: `100`) — Number of points along ``eta1``. Default: ``100``.
- `ax` (`matplotlib.axes.Axes`) (default: `None`) — The axes to draw into. Default: a new figure.

**Returns**

- (`PlotResult`) — The figure, the axes and the profile lines.

**Examples**

```pycon
>>> plot_equilibrium_profile(out.equil, out.domain)
```

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

## plasma_plots.plotting.plot_field_with_orbits

*function*

```python
def plot_field_with_orbits(field: xr.DataArray, view: View, orbits: xr.Dataset, *, max_markers=200, ax=None, cmap=None)
```

A 2-D field slice with marker orbit paths overlaid.

A Poincaré-style diagnostic for checking particle confinement or orbit topology against a
background field.

**Parameters**

- `field` (`xarray.DataArray`) — The field, drawn with [`plot_slice()`][plasma_plots.plotting.plot_slice].
- `view` (`View`) — The slice of ``field`` (see [`View`][plasma_plots.plotting.View]); ``view.x`` and ``view.y`` must be given.
- `orbits` (`xarray.Dataset`) — An orbits product with position variables named after ``view.x`` and ``view.y`` (e.g. its logical coordinates, to overlay directly on a logical-coordinates slice).
- `max_markers` (`int`) (default: `200`) — Draw only the first ``max_markers`` markers. Default: ``200``.
- `ax` (`matplotlib.axes.Axes`) (default: `None`) — The axes to draw into. Default: a new figure.
- `cmap` (`str or matplotlib.colors.Colormap`) (default: `None`) — The colormap of the field. Default: ``"viridis"``.

**Returns**

- (`PlotResult`) — The figure, the axes, the mesh and the orbit paths.

**Examples**

```pycon
>>> plot_field_with_orbits(
...     phi.isel(t=-1),
...     View(x="eta1", y="eta2", isel={"eta3": 0}),
...     out.orbits["ions"],
... )
```

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

## plasma_plots.plotting.plot_lineout

*function*

```python
def plot_lineout(data: xr.DataArray, *, x: str | None = None, ax=None, title=None, reference=None, x_of=None, xlabel=None, rationals: int | None = None, nfp: int | None = None)
```

Plot a one-dimensional profile along its one remaining coordinate.

**Parameters**

- `data` (`xarray.DataArray`) — The profile: every dimension but one already selected.
- `x` (`str`) (default: `None`) — The remaining dimension, as a check. Default: whichever it is.
- `ax` (`matplotlib.axes.Axes`) (default: `None`) — The axes to draw into. Default: a new figure.
- `title` (`str`) (default: `None`) — The axes title. Default: the array's label.
- `reference` (`callable, array, (x, y) pair or dict`) (default: `None`) — Exact or expected profiles, drawn dashed: a function of the plotted ``x`` (or of ``x`` and ``t``, taking the profile's time), a 1-D ``xarray.DataArray`` (drawn over its own coordinate), an ``(x, y)`` pair, or a dict of labels to these.
- `x_of` (`callable`) (default: `None`) — Maps the coordinate to the plotted axis, e.g. ``lambda eta1: L * eta1``.
- `xlabel` (`str`) (default: `None`) — The horizontal axis label. Default: the coordinate's label.
- `rationals` (`int`) (default: `None`) — Mark where a rotational transform (or safety factor) profile takes its ``rationals`` lowest-order rational values ``n/m``: a dotted line at each value, labeled, and a point at each crossing (see [`plasma_plots.analysis.rational_surfaces()`][plasma_plots.analysis.rational_surfaces]). Default: none.
- `nfp` (`int`) (default: `None`) — With ``rationals``: the numerators ``n`` are multiples of it. Default: the profile's ``nfp`` attribute, else 1.

**Returns**

- (`PlotResult`) — The figure, the axes and the drawn lines.

**Raises**

- `ValueError` — If more or fewer than one dimension remains, or ``x`` is not the remaining one.

> **See Also**
>
> [`plot_profiles()`][plasma_plots.plotting.plot_profiles] : Several profiles in one axes.

**Examples**

```pycon
>>> plot_lineout(
...     phi.isel(t=-1, eta2=0, eta3=0), reference=lambda x: np.sin(np.pi * x)
... )
>>> # GVEC's ι(ρ) with its 4 lowest-order n/m
>>> plot_lineout(ev.iota, rationals=4)
```

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

## plasma_plots.plotting.plot_loss_map

*function*

```python
def plot_loss_map(markers: xr.Dataset, *, x: str = 'v_par', y: str | None = None, t=0, absB=None, ax=None, s: int = 10, cmap=None, title: str | None = None)
```

Which markers are lost, over their initial phase-space position, colored by when.

Confined markers are grey; lost ones are colored by their loss time, so prompt losses
(the loss cone, unconfined orbits) stand out from slow ones (transport, collisions).

**Parameters**

- `markers` (`xarray.Dataset`) — An orbits product over ``(t, marker)``.
- `x` (`str`) (default: `'v_par'`) — The quantity along the horizontal axis: a variable, or ``"energy"``, ``"pitch"`` or ``"speed"`` (see [`loss_map()`][plasma_plots.analysis.loss_map]). Default: ``"v_par"``.
- `y` (`str`) (default: `None`) — The vertical one. Default: ``"mu"``, or ``"v_perp"`` without ``mu``.
- `t` (`int or float`) (default: `0`) — The time of the plotted positions: an integer position (default ``0``) or a float nearest value.
- `absB` (`callable`) (default: `None`) — ``|B|(x, y, z)``, for ``"energy"`` and ``"pitch"``.
- `ax` (`matplotlib.axes.Axes`) (default: `None`) — The axes to draw into. Default: a new figure.
- `s` (`int`) (default: `10`) — The marker size, in points². Default: 10.
- `cmap` (`str or matplotlib.colors.Colormap`) (default: `None`) — The colormap of the loss time. Default: ``"plasma"``.
- `title` (`str`) (default: `None`) — The title. Default: how many markers are lost.

**Returns**

- (`PlotResult`) — The figure, the axes and the two scatters; ``data["losses"]`` holds the [`loss_map()`][plasma_plots.analysis.loss_map].

> **See Also**
>
> [`plasma_plots.analysis.loss_map()`][plasma_plots.analysis.loss_map] : The values.
> [`plot_orbit_classification()`][plasma_plots.plotting.plot_orbit_classification] : Passing, trapped and lost markers.

**Examples**

```pycon
>>> plot_loss_map(orbits, x="energy", y="pitch", absB=absB)
```

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

## plasma_plots.plotting.plot_lost_fraction

*function*

```python
def plot_lost_fraction(markers: xr.Dataset, *, weight: str | None = None, percent: bool = True, ax=None, title: str | None = None)
```

The fraction of markers lost from the domain, against time.

**Parameters**

- `markers` (`xarray.Dataset`) — A marker Dataset over ``(t, marker)``, e.g. an orbits product.
- `weight` (`str`) (default: `None`) — Weigh each marker by its initial value of this variable, i.e. count particles rather than markers. Default: count markers.
- `percent` (`bool`) (default: `True`) — Show percentages. Default: True.
- `ax` (`matplotlib.axes.Axes`) (default: `None`) — The axes to draw into. Default: a new figure.
- `title` (`str`) (default: `None`) — The title. Default: the final fraction.

**Returns**

- (`PlotResult`) — The figure, the axes and the line; ``data["lost_fraction"]`` holds the fraction.

> **See Also**
>
> [`plasma_plots.analysis.lost_fraction()`][plasma_plots.analysis.lost_fraction] : The fraction.
> [`plot_loss_map()`][plasma_plots.plotting.plot_loss_map] : Which markers are lost, and when.

**Examples**

```pycon
>>> plot_lost_fraction(orbits)
```

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

## plasma_plots.plotting.plot_marker_density

*function*

```python
def plot_marker_density(markers: xr.Dataset, *, x: str = 'eta1', weight: str | None = 'weight', against: xr.DataArray | None = None, bins: int = 32, normalize: bool = True, ax=None, title: str | None = None, **selection)
```

Where the markers are against what they represent: the marker density along one coordinate.

Three profiles, each normalized to unit mean magnitude by default so that their shapes
compare: the
number of markers per unit length (the sampling density, where the markers were loaded),
the weighted density (what they represent: the physical density of a full-f run, the
perturbation of a δf run) and, given ``against``, a reference field such as the density
the code computed or the equilibrium profile. Importance sampling shows as a sampling
density that differs from the physical one.

**Parameters**

- `markers` (`xarray.Dataset`) — A marker Dataset with the position variable ``x`` over ``(t, marker)``.
- `x` (`str`) (default: `'eta1'`) — The position variable to bin over, e.g. ``"eta1"`` or ``"x"``. Default: ``"eta1"``.
- `weight` (`str`) (default: `'weight'`) — The weight variable, for the weighted density; ``None`` leaves it out. Default: ``"weight"`` (skipped when the Dataset has no such variable).
- `against` (`xarray.DataArray`) (default: `None`) — A reference profile over the same coordinate (every other dimension selected, or with ``t`` matching ``markers``), drawn dashed. Default: none.
- `bins` (`int`) (default: `32`) — The number of bins. Default: 32.
- `normalize` (`bool`) (default: `True`) — Divide each profile by the mean of its magnitude, so that the shapes compare (a δf perturbation sums to nearly nothing, so its plain mean would not do). Default: True.
- `ax` (`matplotlib.axes.Axes`) (default: `None`) — The axes to draw into. Default: a new figure.
- `title` (`str`) (default: `None`) — The title. Default: ``"marker density"``.
- `**selection` (default: `{}`) — The time: ``t=-1`` (default, the last) or a float nearest value.

**Returns**

- (`PlotResult`) — The figure, the axes and the lines; ``data`` holds the ``sampling`` and ``weighted`` densities.

> **See Also**
>
> [`plasma_plots.analysis.marker_density()`][plasma_plots.analysis.marker_density] : The binned densities.

**Examples**

```pycon
>>> plot_marker_density(orbits, x="eta1", against=n.isel(eta2=0, eta3=0), t=-1)
```

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

## plasma_plots.plotting.plot_marker_paths

*function*

```python
def plot_marker_paths(orbits, *, x: str = 'x', y: str = 'y', markers=6, near=None, background: xr.DataArray | None = None, background_options: dict | None = None, t=0, ax=None, cmap='viridis')
```

Paths of a few markers in a plane, with their start (circle) and end (cross).

Samples after a marker leaves the domain are dropped.

**Parameters**

- `orbits` (`xarray.Dataset or xarray.DataArray`) — An orbits product with the quantities ``x`` and ``y`` (see [`prepare_orbits()`][plasma_plots.plotting.prepare_orbits]).
- `x` (`str`) (default: `'x'`) — The quantity along the horizontal axis. Default: ``"x"``.
- `y` (`str`) (default: `'y'`) — The quantity along the vertical axis. Default: ``"y"``.
- `markers` (`int or sequence of int`) (default: `6`) — A number of markers (spread evenly over the saved markers) or a list of marker indices. Default: ``6``.
- `near` (`sequence of (float, float)`) (default: `None`) — Picks instead the marker starting closest to each of a list of ``(x, y)`` points, e.g. a row across the domain.
- `background` (`xarray.DataArray`) (default: `None`) — A field drawn behind the paths at the time ``t``: on its own ``x``/``y`` dimensions if it has them (a Cartesian field), in logical coordinates if ``x``/``y`` are ``eta1``/``eta2``/``eta3``, else in the physical plane of ``x``/``y`` (the field then needs its ``X``, ``Y``, ``Z`` coordinates).
- `background_options` (`dict`) (default: `None`) — Passed to [`plot_slice()`][plasma_plots.plotting.plot_slice] for the background, e.g. ``dict(levels=12, fill=False)`` for the contour lines of a stream function.
- `t` (`int or float`) (default: `0`) — The time of the background: an integer position or a float value. Default: ``0``, the first.
- `ax` (`matplotlib.axes.Axes`) (default: `None`) — The axes to draw into. Default: a new figure.
- `cmap` (`str or matplotlib.colors.Colormap`) (default: `'viridis'`) — The colormap the path colors are taken from. Default: ``"viridis"``.

**Returns**

- (`PlotResult`) — The figure, the axes, the background mesh (if any), the paths and the start and end markers; ``data["markers"]`` lists the chosen marker indices.

**Examples**

```pycon
>>> plot_marker_paths(
...     out.orbits["ions"],
...     near=[(0.2, 0.5), (0.5, 0.5), (0.8, 0.5)],
...     background=psi,
... )
```

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

## plasma_plots.plotting.plot_marker_scatter

*function*

```python
def plot_marker_scatter(markers: xr.Dataset, *, x: str, y: str, color: str | None = None, ax=None, cmap=None, s: int = 8, color_at=None, background: xr.DataArray | None = None, background_options: dict | None = None, equal_aspect: bool | None = None, **selection)
```

Scatter marker positions from a Dataset (an orbits product, or any per-marker data).

Useful for checking a marker loading scheme, or visualizing an SPH particle cloud colored by
density or a tracer.

**Parameters**

- `markers` (`xarray.Dataset`) — The per-marker data, with a ``marker`` dimension.
- `x` (`str`) — The data variable along the horizontal axis, e.g. the position ``"x"``.
- `y` (`str`) — The data variable along the vertical axis, e.g. the position ``"y"``.
- `color` (`str`) (default: `None`) — A data variable to color the markers by, e.g. a Lagrangian tracer, weight or density, with a color bar. Default: one color.
- `ax` (`matplotlib.axes.Axes`) (default: `None`) — The axes to draw into. Default: a new figure.
- `cmap` (`str or matplotlib.colors.Colormap`) (default: `None`) — The colormap for ``color``. Default: ``"viridis"``.
- `s` (`int`) (default: `8`) — The marker size, in points². Default: ``8``.
- `color_at` (`int or float`) (default: `None`) — Take the colors at another time (an integer position such as ``0``, or a float value), e.g. each marker's initial position, to follow where fluid parcels go. Default: the selected time.
- `background` (`xarray.DataArray`) (default: `None`) — A field drawn behind the markers at the same time (select its other dimensions first): on its own ``x``/``y`` dimensions if it has them (a Cartesian field), in logical coordinates if ``x``/``y`` are ``eta1``/``eta2``/``eta3``, else in the physical plane of ``x``/``y`` (``x``, ``y``, ``z``; the field then needs its ``X``, ``Y``, ``Z`` coordinates).
- `background_options` (`dict`) (default: `None`) — Passed to [`plot_slice()`][plasma_plots.plotting.plot_slice] for the background (e.g. ``cmap``, ``levels``, ``fill=False``).
- `equal_aspect` (`bool`) (default: `None`) — Draw both axes to the same scale. Default: when ``x`` and ``y`` have the same ``units`` attribute (e.g. two positions), not for a phase space such as ``x`` against ``vx``.
- `**selection` (default: `{}`) — The remaining dimensions, such as ``t``, exactly like [`ArrayPlots.lineout()`][ArrayPlots.lineout]: an integer is a position (``t=-1`` the last), a float the nearest coordinate value.

**Returns**

- (`PlotResult`) — The figure, the axes, the background mesh (if any) and the scatter.

**Raises**

- `ValueError` — If ``x`` or ``y`` is not a data variable, or dimensions other than ``marker`` remain.
- `TypeError` — If a selected name is not a dimension, or its value is neither an integer nor a float.

> **See Also**
>
> [`animate_markers()`][plasma_plots.plotting.animate_markers] : The markers over time.

**Examples**

```pycon
>>> plot_marker_scatter(
...     out.orbits["ions"], x="x", y="y", color="weights", t=-1
... )
```

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

## plasma_plots.plotting.plot_marker_trajectories

*function*

```python
def plot_marker_trajectories(orbits, *, ax=None, max_markers=200, show_paths=None)
```

Plot a static 3-D trajectory overview.

Interactive marker UI is intentionally separate. The last positions are marked with dots.

**Parameters**

- `orbits` (`xarray.Dataset or xarray.DataArray`) — An orbits product with the quantities ``x``, ``y`` and ``z``: an ``xarray.Dataset`` with one ``(t, marker)`` variable per saved quantity, as Struphy saves them; a single ``(t, marker, quantity)`` ``xarray.DataArray`` works too.
- `ax` (`mpl_toolkits.mplot3d.Axes3D`) (default: `None`) — A 3-D axes to draw into. Default: a new figure.
- `max_markers` (`int`) (default: `200`) — Draw only the first ``max_markers`` markers. Default: ``200``.
- `show_paths` (`bool`) (default: `None`) — Draw each marker's path, not only its last position. Default: ``True`` for up to 200 markers.

**Returns**

- (`PlotResult`) — The figure, the axes, the paths and the scatter of last positions.

**Examples**

```pycon
>>> plot_marker_trajectories(out.orbits["ions"], max_markers=50)
```

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

## plasma_plots.plotting.plot_measured_vs_theory

*function*

```python
def plot_measured_vs_theory(measured, theory=None, *, show_error: bool = True, xlabel: str | None = None, ylabel: str | None = None, title: str | None = None, logx: bool = False, logy: bool = False)
```

Measured values against a theory curve over a parameter, with their relative error.

**Parameters**

- `measured` (`xarray.DataArray, (x, y) pair or dict`) — A 1-D array over the parameter (e.g. ``trace_branch(...).omega`` over ``k``, growth rates over mode numbers), an ``(x, y)`` pair, or a dict of labels to these (e.g. several runs or methods), drawn as markers.
- `theory` (`callable, (x, y) pair or dict`) (default: `None`) — A function of the parameter, an ``(x, y)`` pair, or a dict of labels to these, drawn as lines over the measured range. Complex values (e.g. from [`plasma_plots.theory`][plasma_plots.theory]) are compared by their real part; for growth or damping rates pass ``lambda k: f(k).imag``.
- `show_error` (`bool`) (default: `True`) — Add a second panel with ``(measured - theory) / theory`` against the first theory, for every measured series. A theory given as points is interpolated linearly between them (NaN outside them). Default: ``True``.
- `xlabel` (`str`) (default: `None`) — The horizontal axis label. Default: the coordinate label of the first measured array.
- `ylabel` (`str`) (default: `None`) — The value axis label. Default: the value label of the first measured array.
- `title` (`str`) (default: `None`) — The title. Default: ``"Measured against theory"``.
- `logx` (`bool`) (default: `False`) — Use a logarithmic parameter axis. Default: ``False``.
- `logy` (`bool`) (default: `False`) — Use a logarithmic value axis. Default: ``False``.

**Returns**

- (`PlotResult`) — The figure, the axes (an array of two with the error panel), and the drawn lines and markers.

**Raises**

- `ValueError` — If there are no measured values, or a measured array is not one-dimensional.

**Examples**

```pycon
>>> plot_measured_vs_theory(
...     branch.omega, theory=lambda k: np.sqrt(1 + 3 * k**2), xlabel="k"
... )
```

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

## plasma_plots.plotting.plot_orbit_classification

*function*

```python
def plot_orbit_classification(orbits, *, x: str = 'v_par', y: str | None = None, v_par: str = 'v_par', t=0, ax=None, s: int = 8)
```

Scatter markers in a phase-space plane, colored as passing, trapped or lost.

The classification is [`classify_orbits()`][plasma_plots.analysis.classify_orbits] (Struphy's criteria:
``v_par`` reversing sign means trapped, a zeroed marker means lost). The default plane, initial
``v_par`` against ``mu``, shows the trapped-passing boundary directly; ``x="p_phi"`` gives the
usual canonical-momentum diagram when ``p_phi`` was saved. The legend gives each class's
marker count and fraction.

**Parameters**

- `orbits` (`xarray.Dataset or xarray.DataArray`) — An orbits product with ``v_par`` and the quantities ``x`` and ``y`` (see [`prepare_orbits()`][plasma_plots.plotting.prepare_orbits]).
- `x` (`str`) (default: `'v_par'`) — The quantity along the horizontal axis. Default: ``"v_par"``.
- `y` (`str`) (default: `None`) — The quantity along the vertical axis. Default: the magnetic moment ``mu`` (Particles5D), or ``v_perp`` if there is no ``mu`` (Particles5Dvperp).
- `v_par` (`str`) (default: `'v_par'`) — The parallel velocity the classification uses. Default: ``"v_par"``.
- `t` (`int or float`) (default: `0`) — The time of the plotted values: an integer position (default ``0``, the initial phase-space position, before any marker is lost; ``-1`` the last), or a float nearest value.
- `ax` (`matplotlib.axes.Axes`) (default: `None`) — The axes to draw into. Default: a new figure.
- `s` (`int`) (default: `8`) — The marker size, in points². Default: ``8``.

**Returns**

- (`PlotResult`) — The figure, the axes and one scatter per class present; ``data["counts"]`` holds the number of markers per class (``"passing"``, ``"trapped"``, ``"lost"``).

> **See Also**
>
> [`prepare_orbit_classification()`][plasma_plots.plotting.prepare_orbit_classification] : The same values, without plotting.
> [`plot_orbit_poloidal()`][plasma_plots.plotting.plot_orbit_poloidal] : The orbits in the poloidal plane, colored by class.

**Examples**

```pycon
>>> plot_orbit_classification(out.orbits["ions"], x="p_phi")
```

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

## plasma_plots.plotting.plot_orbit_grid

*function*

```python
def plot_orbit_grid(orbits, *, markers=8, ncols: int = 4, boundary: xr.DataArray | None = None, color_by: str | None = 'classification')
```

One small poloidal panel (``R`` against ``z``) per marker, sharing axes.

Colored by orbit class, for looking at individual orbits (bananas, passing, lost) side by
side. Samples where a marker is lost are dropped.

**Parameters**

- `orbits` (`xarray.Dataset or xarray.DataArray`) — An orbits product with the quantities ``x``, ``y`` and ``z`` (see [`prepare_orbits()`][plasma_plots.plotting.prepare_orbits]).
- `markers` (`int or sequence of int`) (default: `8`) — A number of markers (spread over the classes when ``v_par`` is saved) or a list of marker indices. Default: ``8``.
- `ncols` (`int`) (default: `4`) — The number of panels per row. Default: ``4``.
- `boundary` (`xarray.DataArray`) (default: `None`) — A field with physical coordinates whose outer (last ``eta1``) surface is drawn in every panel, at its first ``eta3``.
- `color_by` (`str or None`) (default: `'classification'`) — ``"classification"`` colors by orbit class (when ``v_par`` is saved); ``"t"`` or the name of a ``(t, marker)`` variable (e.g. ``"v_par"``) colors each orbit along its path, with one color bar for all panels; ``None`` draws every orbit in one color. The panel titles name the class whenever ``v_par`` is saved. Default: ``"classification"``.

**Returns**

- (`PlotResult`) — The figure, the 2-D array of axes and the drawn lines; ``data["markers"]`` lists the marker indices shown.

> **See Also**
>
> [`plot_orbit_poloidal()`][plasma_plots.plotting.plot_orbit_poloidal] : All orbits in one axes.

**Examples**

```pycon
>>> plot_orbit_grid(out.orbits["ions"], markers=12, boundary=phi)
```

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

## plasma_plots.plotting.plot_orbit_poloidal

*function*

```python
def plot_orbit_poloidal(orbits, *, color_by: str | None = 'classification', max_markers: int = 200, boundary: xr.DataArray | None = None, ax=None)
```

Marker orbits projected onto the poloidal plane, ``R = √(x² + y²)`` against ``z``.

Passing orbits circle the magnetic axis, trapped ones trace bananas. Samples where a marker
is lost are dropped.

**Parameters**

- `orbits` (`xarray.Dataset or xarray.DataArray`) — An orbits product with the quantities ``x``, ``y`` and ``z`` (see [`prepare_orbits()`][plasma_plots.plotting.prepare_orbits]).
- `color_by` (`str or None`) (default: `'classification'`) — ``"classification"`` colors by orbit class (needs ``v_par``, see [`classify_orbits()`][plasma_plots.analysis.classify_orbits]); ``"t"`` or the name of a ``(t, marker)`` variable (e.g. ``"v_par"``) colors each orbit along its path, with a color bar; ``None`` gives one color per marker. Default: ``"classification"``.
- `max_markers` (`int`) (default: `200`) — Draw only the first ``max_markers`` markers. Default: ``200``.
- `boundary` (`xarray.DataArray`) (default: `None`) — Any field with physical coordinates, whose outer (last ``eta1``) surface is drawn at its first ``eta3`` as the domain boundary.
- `ax` (`matplotlib.axes.Axes`) (default: `None`) — The axes to draw into. Default: a new figure.

**Returns**

- (`PlotResult`) — The figure, the axes and the drawn lines.

> **See Also**
>
> [`plot_orbit_grid()`][plasma_plots.plotting.plot_orbit_grid] : One panel per marker.

**Examples**

```pycon
>>> plot_orbit_poloidal(out.orbits["ions"], boundary=phi)
```

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

## plasma_plots.plotting.plot_orbit_quantities

*function*

```python
def plot_orbit_quantities(orbits, *, quantities=('v_par', 'mu'), markers=6, drift_of: bool | tuple = ('mu'))
```

Saved orbit quantities over time, one panel per quantity and one line per marker.

Samples where a marker is lost are dropped.

**Parameters**

- `orbits` (`xarray.Dataset or xarray.DataArray`) — An orbits product with the ``quantities`` (see [`prepare_orbits()`][plasma_plots.plotting.prepare_orbits]).
- `quantities` (`sequence of str`) (default: `('v_par', 'mu')`) — The quantities, one panel each. Default: ``("v_par", "mu")``.
- `markers` (`int or sequence of int`) (default: `6`) — A number of markers (spread over the classes if ``v_par`` is saved, so passing and trapped ones both show) or a list of marker indices. Default: ``6``.
- `drift_of` (`bool or sequence of str`) (default: `('mu')`) — The quantities shown as their change since ``t = 0`` (default ``("mu",)``, an invariant of guiding-center motion, so its drift measures the pusher's accuracy); ``True`` for all, ``False`` for none.

**Returns**

- (`PlotResult`) — The figure, the array of axes (one per quantity) and the drawn lines.

**Examples**

```pycon
>>> plot_orbit_quantities(
...     out.orbits["ions"],
...     quantities=("v_par", "mu", "p_phi"),
...     markers=[0, 5, 9],
... )
```

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

## plasma_plots.plotting.plot_panels

*function*

```python
def plot_panels(data: xr.DataArray, *, view=None, nrows=3, ncols=4, shared_clim=True, title=None, run_label=None, vmin=None, vmax=None, cmap=None, equal_aspect=None, symmetric=False, robust=False, levels=None, fill=True, overlays=None, xlabel=None, ylabel=None, colorbar_label=None)
```

Plot snapshots with common color limits over the entire selected sweep by default.

The ``nrows * ncols`` panels show evenly spaced values of the sweep dimension, from its
first to its last value.

**Parameters**

- `data` (`xarray.DataArray`) — The field, with the sweep dimension; other dimensions not drawn are selected by ``view``.
- `view` (`View`) (default: `None`) — Which dimensions to select, which two to draw, in which coordinates, and the sweep dimension (``view.sweep``, default ``t``) to run over (see [`View`][plasma_plots.plotting.View]). Default: ``View()``.
- `nrows` (`int`) (default: `3`) — The number of rows of panels. Default: ``3``.
- `ncols` (`int`) (default: `4`) — The number of columns of panels. Default: ``4``.
- `shared_clim` (`bool`) (default: `True`) — Take the color limits once from all the selected data (every value of the sweep), so that every slice shares them; ``False`` gives each slice its own. Default: ``True``.
- `title` (`str`) (default: `None`) — The figure title, above the panels. Default: the array's label.
- `run_label` (`str`) (default: `None`) — A run description shown after the title. Default: the run shared by the data (see [`shared_run_label()`][plasma_plots.plotting.shared_run_label]); ``""`` for none.
- `vmin` (`float`) (default: `None`) — The lower color limit. Default: from the data (see ``symmetric`` and ``robust``).
- `vmax` (`float`) (default: `None`) — The upper color limit. Default: from the data (see ``symmetric`` and ``robust``).
- `cmap` (`str or matplotlib.colors.Colormap`) (default: `None`) — The colormap. Default: ``"viridis"``.
- `equal_aspect` (`bool`) (default: `None`) — Draw both axes to the same scale. Default: ``True`` in physical coordinates, ``False`` in logical ones.
- `symmetric` (`bool`) (default: `False`) — Center the color limits on zero (``-v, v``), as a diverging colormap for a perturbation needs. Default: ``False``.
- `robust` (`bool`) (default: `False`) — Take the color limits from the 1st and 99th percentiles instead of the extremes, so a few outliers do not wash out the rest. Default: ``False``.
- `levels` (`int or sequence of float`) (default: `None`) — Contour lines of the slice: a number of levels spaced evenly between the color limits, or the levels themselves. Drawn in black over the colors, or in the colormap's colors with ``fill=False``. Default: no contour lines.
- `fill` (`bool`) (default: `True`) — Fill the slice with colors; ``False`` leaves it transparent, e.g. to show only the contour lines of ``levels``. Default: ``True``.
- `overlays` (`dict`) (default: `None`) — What to draw on top of the slice, by key (other keys raise a ``ValueError``):  - ``"contours_of"``: another field (``xarray.DataArray``) whose contour lines are drawn,   at the slice's sweep value and other selected coordinates (nearest); - ``"contour_levels"``: their number or levels (default ``10``); - ``"contour_color"``: their color (default black); - ``"boundary"``: ``True`` draws the edges of the grid, leaving out collapsed edges and   closed periodic seams; - ``"boundary_color"``: its color (default black); - ``"grid_lines"``: an integer ``n``, draws every ``n``-th grid line in gray; - ``"coordinate_lines"``: a dict of coordinate names to a number of lines or their values,   e.g. ``{"rho": 4, "theta": 8}``: lines of constant value of one of the two drawn   dimensions, or contour lines of another coordinate over them (e.g. GVEC's PEST angle   ``theta_P``); an angle (a ``period`` attribute) gets ``n`` lines spread over its period   and no line at its seam; - ``"coordinate_line_color"``: their color (default white); - ``"lines"``: a dict of labels to lines, each a function ``y(x)`` or an ``(x, y)``   pair, drawn in dashed styles; - ``"line_color"``: their color (default white); - ``"points"``: a dict of labels to ``(x, y)`` points, marked with crosses; - ``"point_color"``: their color (default white).  Lines and points do not widen the axes and are listed in a legend.
- `xlabel` (`str`) (default: `None`) — The horizontal axis label. Default: the coordinate's name and units.
- `ylabel` (`str`) (default: `None`) — The vertical axis label. Default: the coordinate's name and units.
- `colorbar_label` (`str`) (default: `None`) — The color bar label. Default: the array's label and units.

**Returns**

- (`PlotResult`) — The figure, the 2-D array of axes and the meshes.

**Raises**

- `ValueError` — If the sweep dimension is missing or empty, ``nrows`` or ``ncols`` is not positive, or
``overlays`` has unknown keys.

> **See Also**
>
> [`plot_slice()`][plasma_plots.plotting.plot_slice] : One slice.
> [`animate_slices()`][plasma_plots.plotting.animate_slices] : The slices as an animation.

**Examples**

```pycon
>>> plot_panels(phi.isel(eta3=0), nrows=2, ncols=3, symmetric=True)
```

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

## plasma_plots.plotting.plot_profiles

*function*

```python
def plot_profiles(data: xr.DataArray, *, x: str, over: str = 't', at=None, x_of=None, xlabel: str | None = None, ax=None, title: str | None = None, reference=None)
```

Several one-dimensional profiles along ``x`` in one axes, one per value of ``over``.

The profiles are colored from dark to light along the ``viridis`` colormap.

**Parameters**

- `data` (`xarray.DataArray`) — The profiles, with exactly the dimensions ``x`` and ``over`` (select the rest first).
- `x` (`str`) — The dimension along the horizontal axis.
- `over` (`str`) (default: `'t'`) — The dimension to draw one profile per value of. Default: ``"t"``.
- `at` (`int, float or sequence of these`) (default: `None`) — The values of ``over``: integers are positions, floats nearest values. Default: four evenly spaced positions.
- `x_of` (`callable`) (default: `None`) — Maps the ``x`` coordinate to the plotted axis, e.g. ``lambda eta1: 0.1 + 0.9 * eta1`` for the minor radius of a hollow torus.
- `xlabel` (`str`) (default: `None`) — The horizontal axis label. Default: the coordinate's label, or ``"r"`` with ``x_of``.
- `ax` (`matplotlib.axes.Axes`) (default: `None`) — The axes to draw into. Default: a new figure.
- `title` (`str`) (default: `None`) — The axes title. Default: the array's label.
- `reference` (`callable, array, (x, y) pair or dict`) (default: `None`) — Exact or expected profiles, drawn in each profile's color in dashed styles: a function of the plotted ``x`` (or of ``x`` and the value of ``over``), a 1-D ``xarray.DataArray`` (drawn over its own coordinate), an ``(x, y)`` pair, or a dict of labels to these.

**Returns**

- (`PlotResult`) — The figure, the axes and the drawn lines.

**Raises**

- `ValueError` — If ``data`` has dimensions other than ``x`` and ``over``.

> **See Also**
>
> [`plot_lineout()`][plasma_plots.plotting.plot_lineout] : One profile.
> [`animate_lines()`][plasma_plots.plotting.animate_lines] : The profiles as an animation.

**Examples**

```pycon
>>> plot_profiles(phi.isel(eta2=0, eta3=0), x="eta1", at=[0, 0.5, -1])
```

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

## plasma_plots.plotting.plot_scalars

*function*

```python
def plot_scalars(scalars, *, names=None, exclude=SCALARS_EXCLUDE, relative_to=None, logy=False, run_label=None)
```

Plot every scalar time series in one axes.

**Parameters**

- `scalars` (`xarray.Dataset or mapping of str to xarray.DataArray`) — The time series (e.g. ``out.scalars``), each with the dimension ``t``.
- `names` (`sequence of str`) (default: `None`) — The scalars to plot. Default: all but ``exclude``.
- `exclude` (`sequence of str`) (default: `SCALARS_EXCLUDE`) — Scalars left out when ``names`` is not given. Default: ``("time",)``.
- `relative_to` (`str`) (default: `None`) — The name of a scalar to divide every series by. Default: none.
- `logy` (`bool`) (default: `False`) — Use a logarithmic value axis. Default: ``False``.
- `run_label` (`str`) (default: `None`) — A run description shown as the figure's suptitle. Default: the run shared by the scalars (see [`shared_run_label()`][plasma_plots.plotting.shared_run_label]); ``""`` for none.

**Returns**

- (`PlotResult`) — The figure, the axes and one line per scalar.

**Raises**

- `ValueError` — If there are no scalars to plot.
- `KeyError` — If a name in ``names`` is not a scalar.

> **See Also**
>
> [`plot_energy_budget()`][plasma_plots.plotting.plot_energy_budget] : The energies and their conservation.
> [`save_all_scalars()`][plasma_plots.plotting.save_all_scalars] : A table and figures of all scalars, written to files.

**Examples**

```pycon
>>> plot_scalars(out.scalars, names=["en_E", "en_B"], logy=True)
```

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

## plasma_plots.plotting.plot_slice

*function*

```python
def plot_slice(data: xr.DataArray, *, view=None, ax=None, vmin=None, vmax=None, equal_aspect=None, title=None, run_label=None, cmap=None, shared_clim=True, symmetric=False, robust=False, levels=None, fill=True, overlays=None, xlabel=None, ylabel=None, colorbar_label=None)
```

Render one selected two-dimensional slice.

Every dimension but the two drawn must be selected, by the data itself or by ``view``; a
sweep dimension (``t``) left over must be selected too, or be drawn as ``x`` or ``y``.

**Parameters**

- `data` (`xarray.DataArray`) — The field; dimensions not drawn are selected by ``view``.
- `view` (`View`) (default: `None`) — Which dimensions to select, which two to draw and in which coordinates (see [`View`][plasma_plots.plotting.View]). Default: ``View()``, the two remaining dimensions in logical coordinates.
- `ax` (`matplotlib.axes.Axes`) (default: `None`) — The axes to draw into. Default: a new figure.
- `vmin` (`float`) (default: `None`) — The lower color limit. Default: from the data (see ``symmetric`` and ``robust``).
- `vmax` (`float`) (default: `None`) — The upper color limit. Default: from the data (see ``symmetric`` and ``robust``).
- `equal_aspect` (`bool`) (default: `None`) — Draw both axes to the same scale. Default: ``True`` in physical coordinates, ``False`` in logical ones.
- `title` (`str`) (default: `None`) — The axes title. Default: the array's label.
- `run_label` (`str`) (default: `None`) — A run description shown as the figure's suptitle (only for a new figure). Default: the run shared by the data (see [`shared_run_label()`][plasma_plots.plotting.shared_run_label]); ``""`` for none.
- `cmap` (`str or matplotlib.colors.Colormap`) (default: `None`) — The colormap. Default: ``"viridis"``.
- `shared_clim` (`bool`) (default: `True`) — Take the color limits once from all the selected data (every value of the sweep), so that every slice shares them; ``False`` gives each slice its own. Default: ``True``.
- `symmetric` (`bool`) (default: `False`) — Center the color limits on zero (``-v, v``), as a diverging colormap for a perturbation needs. Default: ``False``.
- `robust` (`bool`) (default: `False`) — Take the color limits from the 1st and 99th percentiles instead of the extremes, so a few outliers do not wash out the rest. Default: ``False``.
- `levels` (`int or sequence of float`) (default: `None`) — Contour lines of the slice: a number of levels spaced evenly between the color limits, or the levels themselves. Drawn in black over the colors, or in the colormap's colors with ``fill=False``. Default: no contour lines.
- `fill` (`bool`) (default: `True`) — Fill the slice with colors; ``False`` leaves it transparent, e.g. to show only the contour lines of ``levels``. Default: ``True``.
- `overlays` (`dict`) (default: `None`) — What to draw on top of the slice, by key (other keys raise a ``ValueError``):  - ``"contours_of"``: another field (``xarray.DataArray``) whose contour lines are drawn,   at the slice's sweep value and other selected coordinates (nearest); - ``"contour_levels"``: their number or levels (default ``10``); - ``"contour_color"``: their color (default black); - ``"boundary"``: ``True`` draws the edges of the grid, leaving out collapsed edges and   closed periodic seams; - ``"boundary_color"``: its color (default black); - ``"grid_lines"``: an integer ``n``, draws every ``n``-th grid line in gray; - ``"coordinate_lines"``: a dict of coordinate names to a number of lines or their values,   e.g. ``{"rho": 4, "theta": 8}``: lines of constant value of one of the two drawn   dimensions, or contour lines of another coordinate over them (e.g. GVEC's PEST angle   ``theta_P``); an angle (a ``period`` attribute) gets ``n`` lines spread over its period   and no line at its seam; - ``"coordinate_line_color"``: their color (default white); - ``"lines"``: a dict of labels to lines, each a function ``y(x)`` or an ``(x, y)``   pair, drawn in dashed styles; - ``"line_color"``: their color (default white); - ``"points"``: a dict of labels to ``(x, y)`` points, marked with crosses; - ``"point_color"``: their color (default white).  Lines and points do not widen the axes and are listed in a legend.
- `xlabel` (`str`) (default: `None`) — The horizontal axis label. Default: the coordinate's name and units.
- `ylabel` (`str`) (default: `None`) — The vertical axis label. Default: the coordinate's name and units.
- `colorbar_label` (`str`) (default: `None`) — The color bar label. Default: the array's label and units.

**Returns**

- (`PlotResult`) — The figure, the axes and the mesh.

**Raises**

- `ValueError` — If more or other dimensions than the two drawn remain, or ``overlays`` has unknown keys.

> **See Also**
>
> [`plot_panels()`][plasma_plots.plotting.plot_panels] : Several slices over the sweep.
> [`animate_slices()`][plasma_plots.plotting.animate_slices] : The slices as an animation.
> [`InteractiveSliceViewer`][plasma_plots.plotting.InteractiveSliceViewer] : The slices with sliders.

**Examples**

```pycon
>>> plot_slice(phi.isel(t=-1, eta3=0), symmetric=True, cmap="RdBu_r")
>>> plot_slice(
...     phi.isel(t=0),
...     view=View(isel={"eta3": 0}, coordinates="physical"),
...     levels=10,
... )
```

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

## plasma_plots.plotting.plot_timeseries

*function*

```python
def plot_timeseries(data, *, ax=None, logy=True, fit: GrowthFit | None = None, title=None, run_label=None, reference=None)
```

Plot one or more time series, each on its own time grid.

Series of different runs (``attrs["run_name"]``) are labeled by run.

**Parameters**

- `data` (`xarray.DataArray or sequence of xarray.DataArray`) — The time series, each with the only dimension ``t``.
- `ax` (`matplotlib.axes.Axes`) (default: `None`) — The axes to draw into. Default: a new figure.
- `logy` (`bool`) (default: `True`) — Use a logarithmic value axis. Default: ``True``.
- `fit` (`GrowthFit`) (default: `None`) — Fit an exponential growth rate to each series (see [`plasma_plots.analysis.growth_rate()`][plasma_plots.analysis.growth_rate]) and draw it dashed, with the fit window shaded. Default: no fit.
- `title` (`str`) (default: `None`) — The axes title. Default: the first series' label.
- `run_label` (`str`) (default: `None`) — A run description shown as the figure's suptitle (only for a new figure). Default: the run shared by all series (see [`shared_run_label()`][plasma_plots.plotting.shared_run_label]); ``""`` for none.
- `reference` (`callable, array, (t, values) pair or dict`) (default: `None`) — Exact or expected curves, drawn dashed in black: a function of ``t``, a 1-D ``xarray.DataArray`` over ``t``, a ``(t, values)`` pair, or a dict of labels to these, e.g. ``{"exact": lambda t: A * np.exp(-gamma * t)}`` or an envelope ``{"+e^(-γt)": ..., "−e^(-γt)": ...}``.

**Returns**

- (`PlotResult`) — The figure, the axes, the drawn lines, and in ``fit_results`` one [`FitResult`][plasma_plots.analysis.FitResult] (or ``None``) per series.

**Raises**

- `ValueError` — If there is no series, or a series has dimensions other than ``t``.

**Examples**

```pycon
>>> plot_timeseries(out.scalars.en_E, fit=GrowthFit(window=(5.0, 20.0)))
```

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

## plasma_plots.plotting.plot_vector

*function*

```python
def plot_vector(data: xr.DataArray, *, x: str, y: str, components: tuple[int, int] = (0, 1), component_dim: str = 'component', ax=None, stride: int = 1, coordinates: Literal['logical', 'physical'] = 'logical')
```

Render two components of a selected vector field with Matplotlib quivers.

**Parameters**

- `data` (`xarray.DataArray`) — The vector field, with exactly the dimensions ``component_dim``, ``x`` and ``y``.
- `x` (`str`) — The dimension along the horizontal axis.
- `y` (`str`) — The dimension along the vertical axis.
- `components` (`(int, int)`) (default: `(0, 1)`) — The positions along ``component_dim`` of the two components drawn. Default: ``(0, 1)``.
- `component_dim` (`str`) (default: `'component'`) — The dimension holding the components. Default: ``"component"``.
- `ax` (`matplotlib.axes.Axes`) (default: `None`) — The axes to draw into. Default: a new figure.
- `stride` (`int`) (default: `1`) — Draw every ``stride``-th arrow along ``x`` and ``y``. Default: ``1``.
- `coordinates` (`('logical', 'physical')`) (default: `"logical"`) — Place the arrows at the logical coordinates, or at the attached physical ``X``, ``Y``, ``Z`` (then ``x`` and ``y`` must be two of ``eta1``, ``eta2``, ``eta3``, and the axes have equal scales). Default: ``"logical"``.

**Returns**

- (`PlotResult`) — The figure, the axes and the quiver.

**Raises**

- `ValueError` — If other dimensions remain, or physical coordinates are requested for non-spatial ``x``
and ``y``.

> **See Also**
>
> [`prepare_vector()`][plasma_plots.plotting.prepare_vector] : The same selection, without plotting.

**Examples**

```pycon
>>> plot_vector(b_field.isel(t=-1, eta3=0), x="eta1", y="eta2", stride=4)
```

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

## plasma_plots.plotting.plot_volume_slices

*function*

```python
def plot_volume_slices(data: xr.DataArray, *, indices: dict[str, int] | None = None, cmap=None)
```

Show three orthogonal midpoint slices of a selected scalar volume.

**Parameters**

- `data` (`xarray.DataArray`) — The volume, with exactly the dimensions ``eta1``, ``eta2`` and ``eta3``.
- `indices` (`dict of str to int`) (default: `None`) — The index at which each dimension is held fixed, e.g. ``{"eta3": 0}``. Default: the middle index of every dimension.
- `cmap` (`str or matplotlib.colors.Colormap`) (default: `None`) — The colormap. Default: Matplotlib's default.

**Returns**

- (`PlotResult`) — The figure, the three axes and their meshes.

> **See Also**
>
> [`prepare_volume_slices()`][plasma_plots.plotting.prepare_volume_slices] : The three planes, without plotting.

**Examples**

```pycon
>>> plot_volume_slices(phi.isel(t=-1), indices={"eta3": 0})
```

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

## plasma_plots.plotting.plot_weight_histogram

*function*

```python
def plot_weight_histogram(markers: xr.Dataset, *, weight: str = 'weight', t=-1, bins: int = 50, log: bool = True, density: bool = True, ax=None, title: str | None = None)
```

The distribution of the marker weights at one or several times, with their statistics.

A δf scheme starts with weights near zero that spread as the perturbation grows; a growing
tail of large weights is where the noise of the estimate comes from. The legend gives the
mean and the standard deviation at each time, the title the relative noise of the total
(see [`weight_statistics()`][plasma_plots.analysis.weight_statistics]).

**Parameters**

- `markers` (`xarray.Dataset`) — A marker Dataset with the weights over ``(t, marker)``, e.g. an orbits product.
- `weight` (`str`) (default: `'weight'`) — The weight variable. Default: ``"weight"``.
- `t` (`(int, float or sequence)`) (default: `-1`) — The time(s): integer positions (``-1`` the last) or float nearest values, one or several. Default: ``-1``.
- `bins` (`int`) (default: `50`) — The number of bins, shared by all times. Default: 50.
- `log` (`bool`) (default: `True`) — A logarithmic count axis. Default: True.
- `density` (`bool`) (default: `True`) — Normalize each histogram to unit area. Default: True.
- `ax` (`matplotlib.axes.Axes`) (default: `None`) — The axes to draw into. Default: a new figure.
- `title` (`str`) (default: `None`) — The title. Default: the noise estimate at the last time shown.

**Returns**

- (`PlotResult`) — The figure, the axes and one outline per time; ``data["statistics"]`` holds the weight statistics at the times shown.

**Raises**

- `ValueError` — If ``markers`` has no variable ``weight``.

> **See Also**
>
> [`plasma_plots.analysis.weight_statistics()`][plasma_plots.analysis.weight_statistics] : The numbers.

**Examples**

```pycon
>>> plot_weight_histogram(orbits, t=[0, 0.5, -1])
```

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

## plasma_plots.plotting.prepare_compare

*function*

```python
def prepare_compare(first: xr.DataArray, second: xr.DataArray, *, mode: Literal['difference', 'ratio'] = 'difference') -> xr.DataArray
```

Align two arrays and compute their difference or ratio, without rendering it.

Used by [`plot_compare()`][plasma_plots.plotting.plot_compare].

**Parameters**

- `first` (`xarray.DataArray`) — The first array.
- `second` (`xarray.DataArray`) — The second array; only coordinates both share are kept.
- `mode` (`('difference', 'ratio')`) (default: `"difference"`) — ``first - second``, or ``first / second`` (NaN where ``second`` is zero). Default: ``"difference"``.

**Returns**

- (`xarray.DataArray`) — The difference or ratio, named after the first array's label and ``mode``.

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

## plasma_plots.plotting.prepare_continuous_spectrum

*function*

```python
def prepare_continuous_spectrum(spectrum, x, modes) -> xr.DataArray
```

Evaluate a continuous spectrum ``omega(x)`` for each mode, as a ``(mode, branch, x)`` array.

Used by [`plot_continuous_spectrum()`][plasma_plots.plotting.plot_continuous_spectrum].

**Parameters**

- `spectrum` (`callable`) — Called as ``spectrum(x, *mode)``; must return a mapping of branch name to ``omega(x)``, e.g. Struphy's ``MhdContinousSpectraShearedSlab`` or ``MhdContinousSpectraCylinder`` from ``struphy.dispersion_relations.analytic``, whose modes are ``(m, n)`` pairs.
- `x` (`array_like`) — The points to evaluate at.
- `modes` (`sequence of tuple`) — The modes, each a tuple of mode numbers (a bare number is a 1-tuple).

**Returns**

- (`xarray.DataArray`) — ``omega``, over ``(mode, branch, x)``; the ``mode`` labels are the mode numbers joined by commas (``"1, 2"``).

**Raises**

- `ValueError` — If ``modes`` is empty.

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

## plasma_plots.plotting.prepare_lineout

*function*

```python
def prepare_lineout(data: xr.DataArray, *, x: str | None = None) -> xr.DataArray
```

Check that a selected profile has one dimension left, and that it is ``x``.

**Parameters**

- `data` (`xarray.DataArray`) — The profile: every dimension but one already selected.
- `x` (`str`) (default: `None`) — The dimension that should remain. Default: whichever it is.

**Returns**

- (`xarray.DataArray`) — ``data`` itself, as [`plot_lineout()`][plasma_plots.plotting.plot_lineout] draws it.

**Raises**

- `ValueError` — If more or fewer than one dimension remains, or ``x`` is not the remaining one.

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

## plasma_plots.plotting.prepare_marker_scatter

*function*

```python
def prepare_marker_scatter(markers: xr.Dataset, *, x: str, y: str, color: str | None = None, color_at=None, **selection) -> xr.Dataset
```

The per-marker positions and colors [`plot_marker_scatter()`][plasma_plots.plotting.plot_marker_scatter] draws.

**Parameters**

- `markers` (`xarray.Dataset`) — Per-marker variables with a ``marker`` dimension (and usually ``t``).
- `x` (`str`) — The variables for the horizontal and vertical axes.
- `y` (`str`) — The variables for the horizontal and vertical axes.
- `color` (`str`) (default: `None`) — The variable to color by. Default: none.
- `color_at` (`int or float`) (default: `None`) — Take the colors at another time (an integer position such as ``0``, or a float value). Default: at the selected time.
- `**selection` (default: `{}`) — The other dimensions, e.g. ``t``: an integer is a position (``t=-1`` the last), a float the nearest coordinate value.

**Returns**

- (`xarray.Dataset`) — ``x``, ``y`` and ``color`` over ``marker``. The colors are named after their variable, or ``"color"`` when that is ``x`` or ``y`` itself (e.g. colored by the initial position).

**Raises**

- `ValueError` — If ``x``, ``y`` or ``color`` is not a variable, or dimensions other than ``marker`` remain.

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

## plasma_plots.plotting.prepare_orbit_classification

*function*

```python
def prepare_orbit_classification(orbits, *, x: str = 'v_par', y: str | None = None, v_par: str = 'v_par', t=0) -> xr.Dataset
```

Each marker's ``x`` and ``y`` at time ``t`` together with its orbit class.

Used by [`plot_orbit_classification()`][plasma_plots.plotting.plot_orbit_classification].

**Parameters**

- `orbits` (`xarray.Dataset or xarray.DataArray`) — An orbits product (see [`prepare_orbits()`][plasma_plots.plotting.prepare_orbits]).
- `x` (`str`) (default: `'v_par'`) — The first quantity. Default: ``"v_par"``.
- `y` (`str`) (default: `None`) — The second quantity. Default: the magnetic moment ``mu`` (Particles5D), or ``v_perp`` if there is no ``mu`` (Particles5Dvperp).
- `v_par` (`str`) (default: `'v_par'`) — The parallel velocity the classification uses. Default: ``"v_par"``.
- `t` (`int or float`) (default: `0`) — The time, selected like any other dimension: an integer position (default ``0``, the initial phase-space position, before any marker is lost; ``-1`` the last), or a float nearest value.

**Returns**

- (`xarray.Dataset`) — ``x`` and ``y`` over ``marker``, and ``classification`` from [`plasma_plots.analysis.classify_orbits()`][plasma_plots.analysis.classify_orbits] (0 passing, 1 trapped, -1 lost).

**Raises**

- `ValueError` — If ``x`` or ``y`` is not a data variable.

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

## plasma_plots.plotting.prepare_orbits

*function*

```python
def prepare_orbits(orbits, *, max_markers: int = 200, required=()) -> xr.Dataset
```

Normalize an orbits product to its Dataset form and keep only the first ``max_markers``.

``orbits`` is an ``xarray.Dataset`` with one ``(t, marker)`` variable per saved quantity, as
Struphy saves them; a single ``(t, marker, quantity)`` ``xarray.DataArray`` works too. Used by [`plot_marker_trajectories()`][plasma_plots.plotting.plot_marker_trajectories] and
[`plot_field_with_orbits()`][plasma_plots.plotting.plot_field_with_orbits]; also available directly to get the same data without a plot.

**Parameters**

- `orbits` (`xarray.Dataset or xarray.DataArray`) — The orbits product.
- `max_markers` (`int`) (default: `200`) — Keep only the first ``max_markers`` markers. Default: ``200``.
- `required` (`sequence of str`) (default: `()`) — Quantities that must be present, e.g. ``("x", "y", "z")``. Default: none.

**Returns**

- (`xarray.Dataset`) — The orbits, one variable per quantity, with at most ``max_markers`` markers.

**Raises**

- `ValueError` — If a required quantity or the ``marker`` dimension is missing.

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

## plasma_plots.plotting.prepare_vector

*function*

```python
def prepare_vector(data: xr.DataArray, *, x: str, y: str, components: tuple[int, int] = (0, 1), component_dim: str = 'component', stride: int = 1) -> xr.DataArray
```

Select and stride two components of a vector field, without rendering it.

Used by [`plot_vector()`][plasma_plots.plotting.plot_vector]; also available directly, e.g. to hand the same
strided data to a different plotting library.

**Parameters**

- `data` (`xarray.DataArray`) — The vector field, with exactly the dimensions ``component_dim``, ``x`` and ``y``.
- `x` (`str`) — The first spatial dimension.
- `y` (`str`) — The second spatial dimension.
- `components` (`(int, int)`) (default: `(0, 1)`) — The positions along ``component_dim`` of the two components. Default: ``(0, 1)``.
- `component_dim` (`str`) (default: `'component'`) — The dimension holding the components. Default: ``"component"``.
- `stride` (`int`) (default: `1`) — Keep every ``stride``-th point along ``x`` and ``y``. Default: ``1``.

**Returns**

- (`xarray.DataArray`) — The two components, with dimensions ``(component_dim, x, y)``.

**Raises**

- `ValueError` — If other dimensions remain, or ``stride`` is not positive.

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

## plasma_plots.plotting.prepare_view

*function*

```python
def prepare_view(data: xr.DataArray, view: View) -> xr.DataArray
```

Every frame of a slice view at once: the data panels, viewers and animations draw.

**Parameters**

- `data` (`xarray.DataArray`) — The labeled array.
- `view` (`View`) — The selection and presentation (``x``, ``y``, ``sweep``, ``coordinates``, ``plane``).

**Returns**

- (`xarray.DataArray`) — The selection ordered ``(sweep, x, y)`` (without ``sweep`` if it was selected). In physical coordinates, the periodic seam of a cell-centered grid is closed, as in every drawn frame.

**Raises**

- `ValueError` — If other dimensions than ``sweep``, ``x`` and ``y`` remain, or the physical coordinates
the plane needs are missing.

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

## plasma_plots.plotting.prepare_volume_slices

*function*

```python
def prepare_volume_slices(data: xr.DataArray, *, indices: dict[str, int] | None = None) -> dict[str, xr.DataArray]
```

Three orthogonal midpoint (or chosen-index) planes through a scalar volume.

Used by [`plot_volume_slices()`][plasma_plots.plotting.plot_volume_slices].

**Parameters**

- `data` (`xarray.DataArray`) — The volume, with exactly the dimensions ``eta1``, ``eta2`` and ``eta3``.
- `indices` (`dict of str to int`) (default: `None`) — The index at which each dimension is held fixed, e.g. ``{"eta3": 0}``. Default: the middle index of every dimension.

**Returns**

- (`dict of str to xarray.DataArray`) — One 2-D plane per dimension held fixed, keyed by that dimension (``"eta3"``, ``"eta2"``, ``"eta1"``); each has the fixed index in ``attrs["fixed_index"]``.

**Raises**

- `ValueError` — If ``data`` has other dimensions.

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

## plasma_plots.plotting.pyvista_volume

*function*

```python
def pyvista_volume(data: xr.DataArray, *, name: str | None = None, cmap='viridis', opacity='linear')
```

Create a PyVista volume view from a selected scalar field with ``X/Y/Z`` coordinates.

The returned plotter is not shown automatically; call ``plotter.show()`` in an
interactive session or use PyVista's off-screen rendering options in batch jobs.

**Parameters**

- `data` (`xarray.DataArray`) — The field, with exactly the dimensions ``eta1``, ``eta2`` and ``eta3`` and the mapped coordinates ``X``, ``Y`` and ``Z``.
- `name` (`str`) (default: `None`) — The name of the scalars in the PyVista grid. Default: the array's label, else ``"value"``.
- `cmap` (`str`) (default: `'viridis'`) — The colormap. Default: ``"viridis"``.
- `opacity` (`str or sequence of float`) (default: `'linear'`) — PyVista's opacity transfer function. Default: ``"linear"``.

**Returns**

- (`pyvista.Plotter`) — The plotter with the volume and axes added, not yet shown.

**Raises**

- `ValueError` — If ``data`` has other dimensions or lacks ``X``, ``Y`` or ``Z``.

**Examples**

```pycon
>>> pyvista_volume(phi.isel(t=-1)).show()
```

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

## plasma_plots.plotting.resolve_marker_selection

*function*

```python
def resolve_marker_selection(dataset: xr.Dataset, selection: dict) -> xr.Dataset
```

Select dimensions of a Dataset: an integer is a position, a float the nearest value.

**Parameters**

- `dataset` (`xarray.Dataset`) — The data, e.g. an orbits product.
- `selection` (`dict`) — Dimension names to an integer position (``t=-1`` the last) or a float coordinate value.

**Returns**

- (`xarray.Dataset`) — The selected data.

**Raises**

- `TypeError` — If a name is not a dimension of ``dataset``, or a value is neither an integer nor a float.

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

## plasma_plots.plotting.save_all_scalars

*function*

```python
def save_all_scalars(scalars, directory, *, names=None, exclude=SCALARS_EXCLUDE, logy=False, run_label=None, table='csv', file_format='png', dpi=110)
```

Write a table, scalar overview and one figure per scalar.

Writes ``scalars.<table>``, the overview ``scalars.<file_format>`` of [`plot_scalars()`][plasma_plots.plotting.plot_scalars]
and ``<name>.<file_format>`` from [`plot_timeseries()`][plasma_plots.plotting.plot_timeseries] for each scalar into
``directory``, which is created if needed.

**Parameters**

- `scalars` (`xarray.Dataset or mapping of str to xarray.DataArray`) — The time series (e.g. ``out.scalars``), each with the only dimension ``t``.
- `directory` (`str or pathlib.Path`) — The directory to write into.
- `names` (`sequence of str`) (default: `None`) — The scalars to write. Default: all but ``exclude``.
- `exclude` (`sequence of str`) (default: `SCALARS_EXCLUDE`) — Scalars left out when ``names`` is not given. Default: ``("time",)``.
- `logy` (`bool`) (default: `False`) — Use logarithmic value axes. Default: ``False``.
- `run_label` (`str`) (default: `None`) — A run description shown as each figure's suptitle. Default: the run shared by the scalars (see [`shared_run_label()`][plasma_plots.plotting.shared_run_label]); ``""`` for none.
- `table` (`str`) (default: `'csv'`) — The table format, ``"csv"`` or ``"npz"``; ``None`` or ``""`` writes no table. Default: ``"csv"``.
- `file_format` (`str`) (default: `'png'`) — The figure format. Default: ``"png"``.
- `dpi` (`int`) (default: `110`) — The figure resolution. Default: ``110``.

**Returns**

- (`list of str`) — The written paths; empty if there are no scalars.

**Examples**

```pycon
>>> save_all_scalars(out.scalars, "scalars", logy=True)
```

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

## plasma_plots.plotting.save_figure

*function*

```python
def save_figure(figure, name, *, formats=('html', 'png', 'plotly.json'), show: bool = False, frame: int | None = None, still=None, width: int = 1100, height: int = 650, scale: float = 2.0) -> list[str]
```

Save a figure in several formats at once, as ``<name>.<format>``.

The defaults write an interactive page, an image and the figure JSON of a Plotly figure: the
three files a web page needs. Like [`PlotResult.save()`][plasma_plots.plotting.PlotResult.save], this does nothing on MPI ranks
other than 0.

**Parameters**

- `figure` (`(PlotResult, Figure, plotly.graph_objects.Figure or matplotlib.figure.Figure)`) — What to save: the result of a plot (e.g. with ``backend="plotly"``), a figure of [`plasma_plots.figure()`][plasma_plots.figures.figure], or a figure made some other way.
- `name` (`str or pathlib.Path`) — The files' path without the format, e.g. ``"maxwell-wave"`` or ``"figures/energy"``.
- `formats` (`sequence of str`) (default: `('html', 'png', 'plotly.json')`) — The formats, each appended to ``name`` after a dot; the last extension picks how it is written, as in [`PlotResult.save()`][plasma_plots.plotting.PlotResult.save]. Default: ``("html", "png", "plotly.json")``.
- `show` (`bool`) (default: `False`) — Show the figure first. Default: ``False``.
- `frame` (`int`) (default: `None`) — For the image of a Plotly animation: the frame it shows (e.g. ``-1`` for the last). The page and the JSON keep the whole animation. Default: the first frame.
- `still` (`plotly.graph_objects.Figure`) (default: `None`) — A figure that the image shows instead, e.g. a view of an animation that is none of its frames. The page and the JSON keep ``figure``.
- `width` (`int`) (default: `1100`) — Width of an image of a Plotly figure, in layout pixels. Default: 1100.
- `height` (`int`) (default: `650`) — Height of an image of a Plotly figure, in layout pixels. Default: 650.
- `scale` (`float`) (default: `2.0`) — Resolution factor of an image of a Plotly figure. Default: 2.

**Returns**

- (`list of str`) — The paths written; empty on MPI ranks other than 0.

> **See Also**
>
> [`PlotResult.save()`][plasma_plots.plotting.PlotResult.save] : Save one file.

**Examples**

```pycon
>>> dispersion = spectrum.plasma.plot.dispersion(kmin=0, backend="plotly")
>>> # maxwell-wave.html, .png, .plotly.json
>>> save_figure(dispersion, "maxwell-wave", show=True)
>>> save_figure(movie, "phase-space", frame=len(movie.fig.frames) // 2)
>>> save_figure(
...     go.Figure(go.Scatter(x=t, y=energy)), "energy", formats=("html",)
... )
```

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

## plasma_plots.plotting.save_frames

*function*

```python
def save_frames(data: xr.DataArray, directory, *, view=None, step=1, prefix='frame', dpi=110, vmin=None, vmax=None, shared_clim=True, cmap=None, equal_aspect=None, title=None, symmetric=False, robust=False, levels=None, fill=True, overlays=None, xlabel=None, ylabel=None, colorbar_label=None)
```

Export the configured sweep as PNGs, sharing color limits by default.

The files are named ``{prefix}_0000.png``, ``{prefix}_0001.png``, ... in ``directory``,
which is created if needed.

**Parameters**

- `data` (`xarray.DataArray`) — The field, with the sweep dimension; other dimensions not drawn are selected by ``view``.
- `directory` (`str or pathlib.Path`) — The directory to write into.
- `view` (`View`) (default: `None`) — Which dimensions to select, which two to draw, in which coordinates, and the sweep dimension (``view.sweep``, default ``t``) to run over (see [`View`][plasma_plots.plotting.View]). Default: ``View()``.
- `step` (`int`) (default: `1`) — Use every ``step``-th value of the sweep. Default: ``1``.
- `prefix` (`str`) (default: `'frame'`) — The start of each file name. Default: ``"frame"``.
- `dpi` (`int`) (default: `110`) — The resolution of the PNGs. Default: ``110``.
- `vmin` (`float`) (default: `None`) — The lower color limit. Default: from the data (see ``symmetric`` and ``robust``).
- `vmax` (`float`) (default: `None`) — The upper color limit. Default: from the data (see ``symmetric`` and ``robust``).
- `shared_clim` (`bool`) (default: `True`) — Take the color limits once from all the selected data (every value of the sweep), so that every slice shares them; ``False`` gives each slice its own. Default: ``True``.
- `cmap` (`str or matplotlib.colors.Colormap`) (default: `None`) — The colormap. Default: ``"viridis"``.
- `equal_aspect` (`bool`) (default: `None`) — Draw both axes to the same scale. Default: ``True`` in physical coordinates, ``False`` in logical ones.
- `title` (`str`) (default: `None`) — The title, followed in each frame by the sweep value. Default: the array's label.
- `symmetric` (`bool`) (default: `False`) — Center the color limits on zero (``-v, v``), as a diverging colormap for a perturbation needs. Default: ``False``.
- `robust` (`bool`) (default: `False`) — Take the color limits from the 1st and 99th percentiles instead of the extremes, so a few outliers do not wash out the rest. Default: ``False``.
- `levels` (`int or sequence of float`) (default: `None`) — Contour lines of the slice: a number of levels spaced evenly between the color limits, or the levels themselves. Drawn in black over the colors, or in the colormap's colors with ``fill=False``. Default: no contour lines.
- `fill` (`bool`) (default: `True`) — Fill the slice with colors; ``False`` leaves it transparent, e.g. to show only the contour lines of ``levels``. Default: ``True``.
- `overlays` (`dict`) (default: `None`) — What to draw on top of the slice, by key (other keys raise a ``ValueError``):  - ``"contours_of"``: another field (``xarray.DataArray``) whose contour lines are drawn,   at the slice's sweep value and other selected coordinates (nearest); - ``"contour_levels"``: their number or levels (default ``10``); - ``"contour_color"``: their color (default black); - ``"boundary"``: ``True`` draws the edges of the grid, leaving out collapsed edges and   closed periodic seams; - ``"boundary_color"``: its color (default black); - ``"grid_lines"``: an integer ``n``, draws every ``n``-th grid line in gray; - ``"coordinate_lines"``: a dict of coordinate names to a number of lines or their values,   e.g. ``{"rho": 4, "theta": 8}``: lines of constant value of one of the two drawn   dimensions, or contour lines of another coordinate over them (e.g. GVEC's PEST angle   ``theta_P``); an angle (a ``period`` attribute) gets ``n`` lines spread over its period   and no line at its seam; - ``"coordinate_line_color"``: their color (default white); - ``"lines"``: a dict of labels to lines, each a function ``y(x)`` or an ``(x, y)``   pair, drawn in dashed styles; - ``"line_color"``: their color (default white); - ``"points"``: a dict of labels to ``(x, y)`` points, marked with crosses; - ``"point_color"``: their color (default white).  Lines and points do not widen the axes and are listed in a legend.
- `xlabel` (`str`) (default: `None`) — The horizontal axis label. Default: the coordinate's name and units.
- `ylabel` (`str`) (default: `None`) — The vertical axis label. Default: the coordinate's name and units.
- `colorbar_label` (`str`) (default: `None`) — The color bar label. Default: the array's label and units.

**Returns**

- (`list of str`) — The paths of the written files, in order.

**Raises**

- `ValueError` — If ``step`` is not a positive integer, the sweep dimension is missing or empty, or
``overlays`` has unknown keys.

> **See Also**
>
> [`animate_slices()`][plasma_plots.plotting.animate_slices] : The same frames as an animation.

**Examples**

```pycon
>>> save_frames(phi.isel(eta3=0), "frames", step=5, symmetric=True)
```

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

## plasma_plots.plotting.shared_run_label

*function*

```python
def shared_run_label(data, default='') -> str
```

The run description shared by all arrays (``attrs["run"]``), or ``default``.

Arrays loaded from a [`Output`][struphy.Output] carry it; arrays from different runs share none.

**Parameters**

- `data` (`xarray.DataArray, xarray.Dataset or sequence of these`) — The arrays.
- `default` (`str`) (default: `''`) — Returned when no array has a run description. Default: ``""``.

**Returns**

- (`str`) — The shared run description; ``""`` if the arrays come from different runs, ``default`` if none has one.

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

## plasma_plots.plotting.show_equilibrium

*function*

```python
def show_equilibrium(equil, domain, *, scalars: str = 'p0', cmap='viridis', n1=40, n2=48, n3=10, clip=True)
```

A PyVista cutaway view of a fluid equilibrium's scalar field over its domain.

**Parameters**

- `equil` (`struphy.fields_background.base.FluidEquilibrium`) — The equilibrium, e.g. from ``out.equil``.
- `domain` (`struphy.geometry.base.Domain`) — Its mapping (``out.domain``), called as ``domain(eta1, eta2, eta3, squeeze_out=False)``.
- `scalars` (`str`) (default: `'p0'`) — One of ``equil``'s profile methods (``"p0"``, ``"n0"``, ...). Default: ``"p0"``.
- `cmap` (`str`) (default: `'viridis'`) — The colormap. Default: ``"viridis"``.
- `n1` (`int`) (default: `40`) — Number of evaluation points along ``eta1``. Default: ``40``.
- `n2` (`int`) (default: `48`) — Number of evaluation points along ``eta2``. Default: ``48``.
- `n3` (`int`) (default: `10`) — Number of evaluation points along ``eta3``. Default: ``10``.
- `clip` (`bool`) (default: `True`) — Cut away half the domain (normal to the physical X axis) to reveal the profile's interior, since the outer surface alone is often close to uniform (e.g. the plasma edge). Default: ``True``.

**Returns**

- (`pyvista.Plotter`) — The plotter with the mesh and axes added, not yet shown; call ``plotter.show()``.

**Examples**

```pycon
>>> show_equilibrium(out.equil, out.domain, scalars="n0").show()
```

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