Skip to content

plasma_plots.plotting

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.

Attributes

NameDescription
LOGICALNo description.
ORBIT_CLASS_COLORSNo description.
OVERLAY_KEYSNo description.
PLANESNo description.
PLOT_STYLENo description.
REFERENCE_STYLESNo description.
loggerNo description.

Classes

NameDescription
InteractiveSliceViewerSlider view with the same rendering options as static and exported slices.
PlotResultAn already-drawn figure and what was drawn; saving never redraws it.
ViewA reusable selection and rendering recipe for an N-dimensional product.

Functions

NameDescription
animate_fieldsAnimate several fields side by side, frame by frame in sync over the same sweep.
animate_linesAnimate a one-dimensional profile over sweep, optionally with its exact profile.
animate_markersAnimate marker positions over time, optionally over a field animated in sync.
animate_slicesAnimate slices with fixed color limits over the selected sweep by default.
color_limitsColor limits of the finite values of data.
energy_namesThe energies among scalar names: en_* and *_energy, without totals and equilibria.
logical_gridsReturn 2-D logical coordinate grids and their labels.
physical_gridsReturn physical auxiliary coordinates already attached to a selected field.
plot_comparePlot a one-dimensional aligned difference or ratio of two arrays.
plot_continuous_spectrumPlot the continuum frequencies omega(x) of each mode.
plot_convergenceLog-log plot of an error norm against resolution or step size, e.g. from a convergence study.
plot_critical_pointsThe flux function's contours with its O-points (dots) and X-points (crosses).
plot_dispersionThe space-time power spectrum of a (t, dim) field, as a dispersion-relation plot.
plot_energy_budgetAn energy budget: the energy parts, the relative drift of the total, and exchanges.
plot_equilibrium_profilePlot radial profiles of a fluid equilibrium along eta1 (at eta2 = eta3 = 0).
plot_field_with_orbitsA 2-D field slice with marker orbit paths overlaid.
plot_lineoutPlot a one-dimensional profile along its one remaining coordinate.
plot_loss_mapWhich markers are lost, over their initial phase-space position, colored by when.
plot_lost_fractionThe fraction of markers lost from the domain, against time.
plot_marker_densityWhere the markers are against what they represent: the marker density along one coordinate.
plot_marker_pathsPaths of a few markers in a plane, with their start (circle) and end (cross).
plot_marker_scatterScatter marker positions from a Dataset (an orbits product, or any per-marker data).
plot_marker_trajectoriesPlot a static 3-D trajectory overview.
plot_measured_vs_theoryMeasured values against a theory curve over a parameter, with their relative error.
plot_orbit_classificationScatter markers in a phase-space plane, colored as passing, trapped or lost.
plot_orbit_gridOne small poloidal panel (R against z) per marker, sharing axes.
plot_orbit_poloidalMarker orbits projected onto the poloidal plane, R = √(x² + y²) against z.
plot_orbit_quantitiesSaved orbit quantities over time, one panel per quantity and one line per marker.
plot_panelsPlot snapshots with common color limits over the entire selected sweep by default.
plot_profilesSeveral one-dimensional profiles along x in one axes, one per value of over.
plot_scalarsPlot every scalar time series in one axes.
plot_sliceRender one selected two-dimensional slice.
plot_timeseriesPlot one or more time series, each on its own time grid.
plot_vectorRender two components of a selected vector field with Matplotlib quivers.
plot_volume_slicesShow three orthogonal midpoint slices of a selected scalar volume.
plot_weight_histogramThe distribution of the marker weights at one or several times, with their statistics.
prepare_compareAlign two arrays and compute their difference or ratio, without rendering it.
prepare_continuous_spectrumEvaluate a continuous spectrum omega(x) for each mode, as a (mode, branch, x) array.
prepare_lineoutCheck that a selected profile has one dimension left, and that it is x.
prepare_marker_scatterThe per-marker positions and colors plot_marker_scatter() draws.
prepare_orbit_classificationEach marker's x and y at time t together with its orbit class.
prepare_orbitsNormalize an orbits product to its Dataset form and keep only the first max_markers.
prepare_vectorSelect and stride two components of a vector field, without rendering it.
prepare_viewEvery frame of a slice view at once: the data panels, viewers and animations draw.
prepare_volume_slicesThree orthogonal midpoint (or chosen-index) planes through a scalar volume.
pyvista_volumeCreate a PyVista volume view from a selected scalar field with X/Y/Z coordinates.
resolve_marker_selectionSelect dimensions of a Dataset: an integer is a position, a float the nearest value.
save_all_scalarsWrite a table, scalar overview and one figure per scalar.
save_figureSave a figure in several formats at once, as <name>.<format>.
save_framesExport the configured sweep as PNGs, sharing color limits by default.
shared_run_labelThe run description shared by all arrays (attrs["run"]), or default.
show_equilibriumA PyVista cutaway view of a fluid equilibrium's scalar field over its domain.

LOGICALattributemodule attribute#

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

ORBIT_CLASS_COLORSattributemodule attribute#

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

OVERLAY_KEYSattributemodule attribute#

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

PLANESattributemodule attribute#

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$')
}

PLOT_STYLEattributemodule attribute#

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'
}

REFERENCE_STYLESattributemodule attribute#

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

loggerattributemodule attribute#

logger = logging.getLogger('plasma_plots')

InteractiveSliceViewerclass#

class InteractiveSliceViewer(data: xr.DataArray, *, view=None, vmin=None, vmax=None, run_label=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)

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

NameTypeDefaultDescription
dataxarray.DataArrayrequiredThe field; dimensions not drawn and not selected by view get sliders.
viewViewNoneWhich dimensions to select, which two to draw and in which coordinates (see View). Default: View(), the two remaining dimensions in logical coordinates.
vminfloatNoneThe lower color limit. Default: from the data (see symmetric and robust).
vmaxfloatNoneThe upper color limit. Default: from the data (see symmetric and robust).
run_labelstrNoneA run description shown as the figure’s suptitle. Default: the run shared by the data (see shared_run_label()); "" for none.
shared_climboolTrueTake 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.
cmapstr or matplotlib.colors.ColormapNoneThe colormap. Default: "viridis".
equal_aspectboolNoneDraw both axes to the same scale. Default: True in physical coordinates, False in logical ones.
titlestrNoneThe axes title. Default: the array’s label, followed by the slider values.
symmetricboolFalseCenter the color limits on zero (-v, v), as a diverging colormap for a perturbation needs. Default: False.
robustboolFalseTake 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.
levelsint or sequence of floatNoneContour 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.
fillboolTrueFill the slice with colors; False leaves it transparent, e.g. to show only the contour lines of levels. Default: True.
overlaysdictNone

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.

xlabelstrNoneThe horizontal axis label. Default: the coordinate’s name and units.
ylabelstrNoneThe vertical axis label. Default: the coordinate’s name and units.
colorbar_labelstrNoneThe color bar label. Default: the array’s label and units.

Examples

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

dataattributeinstance attribute#

data = validate_array(data)

optionsattributeinstance attribute#

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
)

resultattributeinstance attribute#

result = None

run_labelattributeinstance attribute#

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

slidersattributeinstance attribute#

sliders = {}

viewattributeinstance attribute#

view = view or View()

drawmethod#

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.

showmethod#

def show()

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

Returns

InteractiveSliceViewer
This viewer.

PlotResultclassdataclass#

class PlotResult(fig: object, ax: object = None, artists: list = list(), fit_results: list[FitResult | None] = list(), data: dict = dict())

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) the figure is a Plotly figure instead, and with backend="tikz" (see 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

NameTypeDescription
fig(matplotlib.figure.Figure, plotly.graph_objects.Figure or tikzfigure.TikzFigure)The figure.
axmatplotlib.axes.Axes, array of matplotlib.axes.Axes or NoneThe axes drawn into; an array (or list) of axes for multi-panel plots; None for a Plotly or TikZ figure.
artistslistThe drawn artists (lines, meshes, scatter collections, …); for a Plotly figure its traces; empty for a TikZ figure.
fit_resultslist of FitResult or NoneThe growth-rate fits of plot_timeseries(), one per series (None where no fit was made); empty for other plots.
datadictExtra results of the plot, e.g. "counts" of plot_orbit_classification() or "markers" of plot_marker_paths().

Examples

>>> 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")

artistsattributeclass attributeinstance attribute#

artists: list = field(default_factory=list)

axattributeclass attributeinstance attribute#

ax: object = None

dataattributeclass attributeinstance attribute#

data: dict = field(default_factory=dict)

figattributeinstance attribute#

fig: object

fit_resultsattributeclass attributeinstance attribute#

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

savemethod#

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

Save the figure to a file, as drawn.

Parameters

NameTypeDefaultDescription
pathstr or pathlib.PathrequiredThe 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).
closeboolFalseClose the Matplotlib figure afterwards, to free its memory. Default: False.
frameintNoneFor 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{}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).

showmethod#

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.

to_plotlymethod#

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

NameTypeDefaultDescription
closeboolFalseClose 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.

Examples

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

to_tikzmethod#

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

NameTypeDefaultDescription
closeboolFalseClose the Matplotlib figure afterwards. Default: False.
**options{}Passed to 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.

Examples

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

Viewclassdataclass#

class View(x: str | None = None, y: str | None = None, sweep: str = 't', select: dict[str, float] = dict(), isel: dict[str, int] = dict(), coordinates: Literal['logical', 'physical'] = 'logical', plane: Literal['XY', 'XZ', 'YZ', 'RZ'] = 'XY')

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

Attributes

NameTypeDescription
xstr or NoneThe dimension along the horizontal axis. Default: the first of the two dimensions left after the selection.
ystr or NoneThe dimension along the vertical axis. Default: the second of the two dimensions left.
sweepstrThe dimension that panels, animations, exported frames and sliders run over. Default: "t".
selectdict of str to floatDimensions to select by coordinate value (nearest), e.g. {"eta3": 0.5}.
iseldict of str to intDimensions 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()). Default: "XY".

Examples

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

coordinatesattributeclass attributeinstance attribute#

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

iselattributeclass attributeinstance attribute#

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

planeattributeclass attributeinstance attribute#

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

selectattributeclass attributeinstance attribute#

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

sweepattributeclass attributeinstance attribute#

sweep: str = 't'

xattributeclass attributeinstance attribute#

x: str | None = None

yattributeclass attributeinstance attribute#

y: str | None = None

animate_fieldsfunction#

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

NameTypeDefaultDescription
fieldssequence of xarray.DataArrayrequiredTwo or more arrays with the same dimensions and sweep coordinate (e.g. the vorticity and density of a Hasegawa-Wakatani run).
viewViewNoneWhich 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). Default: View().
intervalint100The delay between frames, in milliseconds. Default: 100.
stepint1Use every step-th value of the sweep. Default: 1.
max_framesintNoneKeep 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.
titlessequence of strNoneOne axes title per field. Default: the fields’ labels.
**options{}Rendering options applied to every field as in 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.

Examples

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

animate_linesfunction#

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

NameTypeDefaultDescription
dataxarray.DataArrayrequiredThe profiles, with the dimensions x and sweep (select the rest first).
xstrNoneThe dimension along the horizontal axis. Default: the one besides sweep.
sweepstr't'The dimension to animate over. Default: "t".
referencecallable, (x, y) pair or dictNoneThe 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_ofcallableNoneMaps the x coordinate to the plotted axis, e.g. lambda eta1: L * eta1.
xlabelstrNoneThe horizontal axis label. Default: the coordinate’s label, or "x" with x_of.
ylim(float, float)NoneThe fixed value axis limits. Default: the range of the data and references, padded by 5 %.
stepint1Use every step-th value of the sweep. Default: 1.
max_framesintNoneKeep 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.
intervalint100The delay between frames, in milliseconds. Default: 100.
titlestrNoneThe title, followed in each frame by the sweep value. Default: the array’s label.
alongsidesequenceNoneOne 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_logyboolFalseLogarithmic 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.

Examples

>>> 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]],
... )

animate_markersfunction#

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

NameTypeDefaultDescription
markersxarray.DatasetrequiredThe per-marker data over (t, marker), e.g. an orbits product.
xstrrequiredThe 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).
ystrrequiredThe data variable along the vertical axis, e.g. the position "y".
colorstrNoneA 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()), with a legend. Default: one color.
color_atint or floatNoneFix 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.
backgroundxarray.DataArrayNoneA 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_optionsdictNoneRendering options for the background, as for plot_slice() (cmap, symmetric, levels, …).
stepint1Use every step-th time. Default: 1.
max_framesintNoneKeep 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.
intervalint100The delay between frames, in milliseconds. Default: 100.
sint8The marker size, in points². Default: 8.
cmapstr or matplotlib.colors.ColormapNoneThe colormap for color. Default: "viridis".
trailintNoneDraw each marker’s last trail samples as a faint line behind it. Default: none.
pathsboolFalseDraw 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.

Examples

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

animate_slicesfunction#

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

NameTypeDefaultDescription
dataxarray.DataArrayrequiredThe field, with the sweep dimension; other dimensions not drawn are selected by view.
viewViewNoneWhich dimensions to select, which two to draw, in which coordinates, and the sweep dimension (view.sweep, default t) to run over (see View). Default: View().
intervalint100The delay between frames, in milliseconds. Default: 100.
stepint1Use every step-th value of the sweep. Default: 1.
max_framesintNoneKeep 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.
vminfloatNoneThe lower color limit. Default: from the data (see symmetric and robust).
vmaxfloatNoneThe upper color limit. Default: from the data (see symmetric and robust).
shared_climboolTrueTake 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.
cmapstr or matplotlib.colors.ColormapNoneThe colormap. Default: "viridis".
equal_aspectboolNoneDraw both axes to the same scale. Default: True in physical coordinates, False in logical ones.
titlestrNoneThe title, followed in each frame by the sweep value. Default: the array’s label.
symmetricboolFalseCenter the color limits on zero (-v, v), as a diverging colormap for a perturbation needs. Default: False.
robustboolFalseTake 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.
levelsint or sequence of floatNoneContour 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.
fillboolTrueFill the slice with colors; False leaves it transparent, e.g. to show only the contour lines of levels. Default: True.
overlaysdictNone

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.

xlabelstrNoneThe horizontal axis label. Default: the coordinate’s name and units.
ylabelstrNoneThe vertical axis label. Default: the coordinate’s name and units.
colorbar_labelstrNoneThe 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.

Examples

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

color_limitsfunction#

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

Color limits of the finite values of data.

Parameters

NameTypeDefaultDescription
dataarray_like or xarray.DataArrayrequiredThe values.
symmetricboolFalseCenter the color limits on zero (-v, v), as a diverging colormap for a perturbation needs. Default: False.
robustboolFalseTake 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.

energy_namesfunction#

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

NameTypeDescription
namesiterable of strScalar 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

>>> 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']

logical_gridsfunction#

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

Return 2-D logical coordinate grids and their labels.

Parameters

NameTypeDefaultDescription
dataxarray.DataArrayrequiredA slice with exactly the dimensions x and y.
xstrNoneThe first dimension. Default (with y): the first dimension of a 2-D data.
ystrNoneThe 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.

physical_gridsfunction#

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

Return physical auxiliary coordinates already attached to a selected field.

Parameters

NameTypeDefaultDescription
dataxarray.DataArrayrequiredA 2-D slice with the coordinates X, Y and Z attached.
plane('XY', 'XZ', 'YZ', 'RZ', 'X1X2')"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.

plot_comparefunction#

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

NameTypeDefaultDescription
firstxarray.DataArrayrequiredThe first array, one-dimensional.
secondxarray.DataArrayrequiredThe second array, one-dimensional; only coordinates both share are kept.
mode('difference', 'ratio')"difference"first - second, or first / second (NaN where second is zero). Default: "difference".
axmatplotlib.axes.AxesNoneThe axes to draw into. Default: a new figure.

Returns

PlotResult
The figure, the axes and the drawn line.

Examples

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

plot_continuous_spectrumfunction#

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

NameTypeDefaultDescription
spectrumcallablerequiredCalled 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()).
xarray_likerequiredThe points to evaluate at.
modessequence of tuplerequiredThe modes, each a tuple of mode numbers (a bare number is a 1-tuple).
frequenciesdict of str to floatNoneMeasured frequencies to mark as horizontal lines (a mapping of label to omega, e.g. a peak read off plot_dispersion()), to see whether a mode lies in a continuum gap or crosses a continuum, where it is damped.
mode_labelstr'(m, n)'How modes are named in the legend. Default: "(m, n)".
xlabelstr'x'The horizontal axis label. Default: "x".
axmatplotlib.axes.AxesNoneThe axes to draw into. Default: a new figure.
titlestr'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().

Raises

ValueError
If modes is empty.

Examples

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

plot_convergencefunction#

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

NameTypeDefaultDescription
sizesarray_likerequiredThe resolutions or step sizes.
errorsarray_likerequiredThe error norm at each size.
axmatplotlib.axes.AxesNoneThe axes to draw into. Default: a new figure.
orderfloatNoneWith None (default), fits and draws the observed order via 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.
labelstrNoneThe legend label of the errors. Default: none.
xlabelstr'resolution'The horizontal axis label. Default: "resolution".
titlestr'Convergence'The axes title. Default: "Convergence".

Returns

PlotResult
The figure, the axes, the error line and the fitted or reference line.

Examples

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

plot_critical_pointsfunction#

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

NameTypeDefaultDescription
dataxarray.DataArrayrequiredThe flux function, e.g. flux_function() of B, with every dimension but the plane’s two selected (view may do the selecting).
viewViewNoneThe slice to draw (see View): which dimensions, logical or physical coordinates. Default: the two logical directions left, in logical coordinates.
axmatplotlib.axes.AxesNoneThe axes to draw into. Default: a new figure.
levelsint or sequence of float14Contour lines of the flux, as for plot_slice(). Default: 14.
cmapstr or matplotlib.colors.ColormapNoneThe colormap. Default: "RdBu_r".
label_valuesboolFalseWrite the flux value next to each point. Default: False.
**options{}Further options of plot_slice() (symmetric, overlays, …).

Returns

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

Examples

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

plot_dispersionfunction#

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() (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

NameTypeDefaultDescription
dataxarray.DataArrayrequiredThe field, with the dimensions t and dim (select the rest first), or its power spectrum, with the dimensions omega and k.
dimstrNoneThe spatial dimension to transform. Default: the one besides t (see plasma_plots.analysis.power_spectrum()).
detrendboolTrueRemove the time-mean at each point of dim first, which otherwise dominates the spectrum as a spurious zero-frequency line. Default: True.
branchesdict or callableNoneTheoretical 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 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.
logboolTrueColor by log10 of the power. Default: True.
dynamic_rangefloat6.0With log, the number of decades below the peak that the default color limits cover. Default: 6.0.
kminfloatNoneShow only k >= kmin, e.g. 0 for the positive quadrant. Default: all k.
kmaxfloatNoneShow only |k| <= kmax. Default: all k.
omega_maxfloatNoneShow only ω <= omega_max. Default: all non-negative ω.
vminfloatNoneThe lower color limit (in log10 of the power with log). Default: the peak minus dynamic_range with log, else the minimum.
vmaxfloatNoneThe upper color limit (in log10 of the power with log). Default: the peak.
cmapstr or matplotlib.colors.ColormapNoneThe colormap. Default: Matplotlib’s default.
axmatplotlib.axes.AxesNoneThe axes to draw into. Default: a new figure.
titlestrNoneThe axes title. Default: "Dispersion relation of <label>".
frequenciesdict of str to floatNoneLabeled horizontal lines, e.g. cutoffs or resonances.
pointsdictNoneMeasured points to mark: a dict of labels to a (k, omega) pair or a trace_branch() result (an xarray.Dataset with k and omega).
fitssequence of BranchFit()Fitted straight branches from 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.

Examples

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

plot_energy_budgetfunction#

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

NameTypeDefaultDescription
scalarsxarray.Dataset or mapping of str to xarray.DataArrayrequiredThe time series (out.scalars), each with the only dimension t.
partssequence of strNoneThe energies drawn in the first panel. Default: the scalars energy_names() finds (en_* and *_energy), except total.
totalstr or None'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.
groupsdict of str to list of strNoneA 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.
logyboolFalseUse a logarithmic value axis in the first panel. Default: False.
run_labelstrNoneA run description shown as the figure’s suptitle. Default: the run shared by the parts (see 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

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

plot_equilibrium_profilefunction#

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

NameTypeDefaultDescription
equilstruphy.fields_background.base.FluidEquilibriumrequiredThe equilibrium, e.g. from out.equil.
domainstruphy.geometry.base.DomainrequiredIts mapping (out.domain).
n_pointsint100Number of points along eta1. Default: 100.
axmatplotlib.axes.AxesNoneThe axes to draw into. Default: a new figure.

Returns

PlotResult
The figure, the axes and the profile lines.

Examples

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

plot_field_with_orbitsfunction#

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

NameTypeDefaultDescription
fieldxarray.DataArrayrequiredThe field, drawn with plot_slice().
viewViewrequiredThe slice of field (see View); view.x and view.y must be given.
orbitsxarray.DatasetrequiredAn 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_markersint200Draw only the first max_markers markers. Default: 200.
axmatplotlib.axes.AxesNoneThe axes to draw into. Default: a new figure.
cmapstr or matplotlib.colors.ColormapNoneThe colormap of the field. Default: "viridis".

Returns

PlotResult
The figure, the axes, the mesh and the orbit paths.

Examples

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

plot_lineoutfunction#

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

NameTypeDefaultDescription
dataxarray.DataArrayrequiredThe profile: every dimension but one already selected.
xstrNoneThe remaining dimension, as a check. Default: whichever it is.
axmatplotlib.axes.AxesNoneThe axes to draw into. Default: a new figure.
titlestrNoneThe axes title. Default: the array’s label.
referencecallable, array, (x, y) pair or dictNoneExact 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_ofcallableNoneMaps the coordinate to the plotted axis, e.g. lambda eta1: L * eta1.
xlabelstrNoneThe horizontal axis label. Default: the coordinate’s label.
rationalsintNoneMark 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()). Default: none.
nfpintNoneWith 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.

Examples

>>> 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)

plot_loss_mapfunction#

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

NameTypeDefaultDescription
markersxarray.DatasetrequiredAn orbits product over (t, marker).
xstr'v_par'The quantity along the horizontal axis: a variable, or "energy", "pitch" or "speed" (see loss_map()). Default: "v_par".
ystrNoneThe vertical one. Default: "mu", or "v_perp" without mu.
tint or float0The time of the plotted positions: an integer position (default 0) or a float nearest value.
absBcallableNone|B|(x, y, z), for "energy" and "pitch".
axmatplotlib.axes.AxesNoneThe axes to draw into. Default: a new figure.
sint10The marker size, in points². Default: 10.
cmapstr or matplotlib.colors.ColormapNoneThe colormap of the loss time. Default: "plasma".
titlestrNoneThe title. Default: how many markers are lost.

Returns

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

Examples

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

plot_lost_fractionfunction#

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

NameTypeDefaultDescription
markersxarray.DatasetrequiredA marker Dataset over (t, marker), e.g. an orbits product.
weightstrNoneWeigh each marker by its initial value of this variable, i.e. count particles rather than markers. Default: count markers.
percentboolTrueShow percentages. Default: True.
axmatplotlib.axes.AxesNoneThe axes to draw into. Default: a new figure.
titlestrNoneThe title. Default: the final fraction.

Returns

PlotResult
The figure, the axes and the line; data["lost_fraction"] holds the fraction.

Examples

>>> plot_lost_fraction(orbits)

plot_marker_densityfunction#

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

NameTypeDefaultDescription
markersxarray.DatasetrequiredA marker Dataset with the position variable x over (t, marker).
xstr'eta1'The position variable to bin over, e.g. "eta1" or "x". Default: "eta1".
weightstr'weight'The weight variable, for the weighted density; None leaves it out. Default: "weight" (skipped when the Dataset has no such variable).
againstxarray.DataArrayNoneA reference profile over the same coordinate (every other dimension selected, or with t matching markers), drawn dashed. Default: none.
binsint32The number of bins. Default: 32.
normalizeboolTrueDivide 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.
axmatplotlib.axes.AxesNoneThe axes to draw into. Default: a new figure.
titlestrNoneThe title. Default: "marker density".
**selection{}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.

Examples

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

plot_marker_pathsfunction#

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

NameTypeDefaultDescription
orbitsxarray.Dataset or xarray.DataArrayrequiredAn orbits product with the quantities x and y (see prepare_orbits()).
xstr'x'The quantity along the horizontal axis. Default: "x".
ystr'y'The quantity along the vertical axis. Default: "y".
markersint or sequence of int6A number of markers (spread evenly over the saved markers) or a list of marker indices. Default: 6.
nearsequence of (float, float)NonePicks instead the marker starting closest to each of a list of (x, y) points, e.g. a row across the domain.
backgroundxarray.DataArrayNoneA 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_optionsdictNonePassed to plot_slice() for the background, e.g. dict(levels=12, fill=False) for the contour lines of a stream function.
tint or float0The time of the background: an integer position or a float value. Default: 0, the first.
axmatplotlib.axes.AxesNoneThe axes to draw into. Default: a new figure.
cmapstr or matplotlib.colors.Colormap'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

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

plot_marker_scatterfunction#

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

NameTypeDefaultDescription
markersxarray.DatasetrequiredThe per-marker data, with a marker dimension.
xstrrequiredThe data variable along the horizontal axis, e.g. the position "x".
ystrrequiredThe data variable along the vertical axis, e.g. the position "y".
colorstrNoneA data variable to color the markers by, e.g. a Lagrangian tracer, weight or density, with a color bar. Default: one color.
axmatplotlib.axes.AxesNoneThe axes to draw into. Default: a new figure.
cmapstr or matplotlib.colors.ColormapNoneThe colormap for color. Default: "viridis".
sint8The marker size, in points². Default: 8.
color_atint or floatNoneTake 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.
backgroundxarray.DataArrayNoneA 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_optionsdictNonePassed to plot_slice() for the background (e.g. cmap, levels, fill=False).
equal_aspectboolNoneDraw 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{}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.

Examples

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

plot_marker_trajectoriesfunction#

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

NameTypeDefaultDescription
orbitsxarray.Dataset or xarray.DataArrayrequiredAn 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.
axmpl_toolkits.mplot3d.Axes3DNoneA 3-D axes to draw into. Default: a new figure.
max_markersint200Draw only the first max_markers markers. Default: 200.
show_pathsboolNoneDraw 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

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

plot_measured_vs_theoryfunction#

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

NameTypeDefaultDescription
measuredxarray.DataArray, (x, y) pair or dictrequiredA 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.
theorycallable, (x, y) pair or dictNoneA 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) are compared by their real part; for growth or damping rates pass lambda k: f(k).imag.
show_errorboolTrueAdd 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.
xlabelstrNoneThe horizontal axis label. Default: the coordinate label of the first measured array.
ylabelstrNoneThe value axis label. Default: the value label of the first measured array.
titlestrNoneThe title. Default: "Measured against theory".
logxboolFalseUse a logarithmic parameter axis. Default: False.
logyboolFalseUse 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

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

plot_orbit_classificationfunction#

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() (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

NameTypeDefaultDescription
orbitsxarray.Dataset or xarray.DataArrayrequiredAn orbits product with v_par and the quantities x and y (see prepare_orbits()).
xstr'v_par'The quantity along the horizontal axis. Default: "v_par".
ystrNoneThe quantity along the vertical axis. Default: the magnetic moment mu (Particles5D), or v_perp if there is no mu (Particles5Dvperp).
v_parstr'v_par'The parallel velocity the classification uses. Default: "v_par".
tint or float0The 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.
axmatplotlib.axes.AxesNoneThe axes to draw into. Default: a new figure.
sint8The 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").

Examples

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

plot_orbit_gridfunction#

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

NameTypeDefaultDescription
orbitsxarray.Dataset or xarray.DataArrayrequiredAn orbits product with the quantities x, y and z (see prepare_orbits()).
markersint or sequence of int8A number of markers (spread over the classes when v_par is saved) or a list of marker indices. Default: 8.
ncolsint4The number of panels per row. Default: 4.
boundaryxarray.DataArrayNoneA field with physical coordinates whose outer (last eta1) surface is drawn in every panel, at its first eta3.
color_bystr or None'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.

Examples

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

plot_orbit_poloidalfunction#

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

NameTypeDefaultDescription
orbitsxarray.Dataset or xarray.DataArrayrequiredAn orbits product with the quantities x, y and z (see prepare_orbits()).
color_bystr or None'classification'"classification" colors by orbit class (needs v_par, see 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_markersint200Draw only the first max_markers markers. Default: 200.
boundaryxarray.DataArrayNoneAny field with physical coordinates, whose outer (last eta1) surface is drawn at its first eta3 as the domain boundary.
axmatplotlib.axes.AxesNoneThe axes to draw into. Default: a new figure.

Returns

PlotResult
The figure, the axes and the drawn lines.

Examples

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

plot_orbit_quantitiesfunction#

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

NameTypeDefaultDescription
orbitsxarray.Dataset or xarray.DataArrayrequiredAn orbits product with the quantities (see prepare_orbits()).
quantitiessequence of str('v_par', 'mu')The quantities, one panel each. Default: ("v_par", "mu").
markersint or sequence of int6A 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_ofbool or sequence of str('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

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

plot_panelsfunction#

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

NameTypeDefaultDescription
dataxarray.DataArrayrequiredThe field, with the sweep dimension; other dimensions not drawn are selected by view.
viewViewNoneWhich dimensions to select, which two to draw, in which coordinates, and the sweep dimension (view.sweep, default t) to run over (see View). Default: View().
nrowsint3The number of rows of panels. Default: 3.
ncolsint4The number of columns of panels. Default: 4.
shared_climboolTrueTake 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.
titlestrNoneThe figure title, above the panels. Default: the array’s label.
run_labelstrNoneA run description shown after the title. Default: the run shared by the data (see shared_run_label()); "" for none.
vminfloatNoneThe lower color limit. Default: from the data (see symmetric and robust).
vmaxfloatNoneThe upper color limit. Default: from the data (see symmetric and robust).
cmapstr or matplotlib.colors.ColormapNoneThe colormap. Default: "viridis".
equal_aspectboolNoneDraw both axes to the same scale. Default: True in physical coordinates, False in logical ones.
symmetricboolFalseCenter the color limits on zero (-v, v), as a diverging colormap for a perturbation needs. Default: False.
robustboolFalseTake 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.
levelsint or sequence of floatNoneContour 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.
fillboolTrueFill the slice with colors; False leaves it transparent, e.g. to show only the contour lines of levels. Default: True.
overlaysdictNone

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.

xlabelstrNoneThe horizontal axis label. Default: the coordinate’s name and units.
ylabelstrNoneThe vertical axis label. Default: the coordinate’s name and units.
colorbar_labelstrNoneThe 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.

Examples

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

plot_profilesfunction#

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

NameTypeDefaultDescription
dataxarray.DataArrayrequiredThe profiles, with exactly the dimensions x and over (select the rest first).
xstrrequiredThe dimension along the horizontal axis.
overstr't'The dimension to draw one profile per value of. Default: "t".
atint, float or sequence of theseNoneThe values of over: integers are positions, floats nearest values. Default: four evenly spaced positions.
x_ofcallableNoneMaps the x coordinate to the plotted axis, e.g. lambda eta1: 0.1 + 0.9 * eta1 for the minor radius of a hollow torus.
xlabelstrNoneThe horizontal axis label. Default: the coordinate’s label, or "r" with x_of.
axmatplotlib.axes.AxesNoneThe axes to draw into. Default: a new figure.
titlestrNoneThe axes title. Default: the array’s label.
referencecallable, array, (x, y) pair or dictNoneExact 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.

Examples

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

plot_scalarsfunction#

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

NameTypeDefaultDescription
scalarsxarray.Dataset or mapping of str to xarray.DataArrayrequiredThe time series (e.g. out.scalars), each with the dimension t.
namessequence of strNoneThe scalars to plot. Default: all but exclude.
excludesequence of strSCALARS_EXCLUDEScalars left out when names is not given. Default: ("time",).
relative_tostrNoneThe name of a scalar to divide every series by. Default: none.
logyboolFalseUse a logarithmic value axis. Default: False.
run_labelstrNoneA run description shown as the figure’s suptitle. Default: the run shared by the scalars (see 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.

Examples

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

plot_slicefunction#

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

NameTypeDefaultDescription
dataxarray.DataArrayrequiredThe field; dimensions not drawn are selected by view.
viewViewNoneWhich dimensions to select, which two to draw and in which coordinates (see View). Default: View(), the two remaining dimensions in logical coordinates.
axmatplotlib.axes.AxesNoneThe axes to draw into. Default: a new figure.
vminfloatNoneThe lower color limit. Default: from the data (see symmetric and robust).
vmaxfloatNoneThe upper color limit. Default: from the data (see symmetric and robust).
equal_aspectboolNoneDraw both axes to the same scale. Default: True in physical coordinates, False in logical ones.
titlestrNoneThe axes title. Default: the array’s label.
run_labelstrNoneA run description shown as the figure’s suptitle (only for a new figure). Default: the run shared by the data (see shared_run_label()); "" for none.
cmapstr or matplotlib.colors.ColormapNoneThe colormap. Default: "viridis".
shared_climboolTrueTake 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.
symmetricboolFalseCenter the color limits on zero (-v, v), as a diverging colormap for a perturbation needs. Default: False.
robustboolFalseTake 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.
levelsint or sequence of floatNoneContour 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.
fillboolTrueFill the slice with colors; False leaves it transparent, e.g. to show only the contour lines of levels. Default: True.
overlaysdictNone

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.

xlabelstrNoneThe horizontal axis label. Default: the coordinate’s name and units.
ylabelstrNoneThe vertical axis label. Default: the coordinate’s name and units.
colorbar_labelstrNoneThe 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.

Examples

>>> 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,
... )

plot_timeseriesfunction#

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

NameTypeDefaultDescription
dataxarray.DataArray or sequence of xarray.DataArrayrequiredThe time series, each with the only dimension t.
axmatplotlib.axes.AxesNoneThe axes to draw into. Default: a new figure.
logyboolTrueUse a logarithmic value axis. Default: True.
fitGrowthFitNoneFit an exponential growth rate to each series (see plasma_plots.analysis.growth_rate()) and draw it dashed, with the fit window shaded. Default: no fit.
titlestrNoneThe axes title. Default: the first series’ label.
run_labelstrNoneA run description shown as the figure’s suptitle (only for a new figure). Default: the run shared by all series (see shared_run_label()); "" for none.
referencecallable, array, (t, values) pair or dictNoneExact 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 (or None) per series.

Raises

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

Examples

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

plot_vectorfunction#

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

NameTypeDefaultDescription
dataxarray.DataArrayrequiredThe vector field, with exactly the dimensions component_dim, x and y.
xstrrequiredThe dimension along the horizontal axis.
ystrrequiredThe dimension along the vertical axis.
components(int, int)(0, 1)The positions along component_dim of the two components drawn. Default: (0, 1).
component_dimstr'component'The dimension holding the components. Default: "component".
axmatplotlib.axes.AxesNoneThe axes to draw into. Default: a new figure.
strideint1Draw every stride-th arrow along x and y. Default: 1.
coordinates('logical', 'physical')"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.

Examples

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

plot_volume_slicesfunction#

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

NameTypeDefaultDescription
dataxarray.DataArrayrequiredThe volume, with exactly the dimensions eta1, eta2 and eta3.
indicesdict of str to intNoneThe index at which each dimension is held fixed, e.g. {"eta3": 0}. Default: the middle index of every dimension.
cmapstr or matplotlib.colors.ColormapNoneThe colormap. Default: Matplotlib’s default.

Returns

PlotResult
The figure, the three axes and their meshes.

Examples

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

plot_weight_histogramfunction#

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()).

Parameters

NameTypeDefaultDescription
markersxarray.DatasetrequiredA marker Dataset with the weights over (t, marker), e.g. an orbits product.
weightstr'weight'The weight variable. Default: "weight".
t(int, float or sequence)-1The time(s): integer positions (-1 the last) or float nearest values, one or several. Default: -1.
binsint50The number of bins, shared by all times. Default: 50.
logboolTrueA logarithmic count axis. Default: True.
densityboolTrueNormalize each histogram to unit area. Default: True.
axmatplotlib.axes.AxesNoneThe axes to draw into. Default: a new figure.
titlestrNoneThe 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.

Examples

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

prepare_comparefunction#

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().

Parameters

NameTypeDefaultDescription
firstxarray.DataArrayrequiredThe first array.
secondxarray.DataArrayrequiredThe second array; only coordinates both share are kept.
mode('difference', 'ratio')"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.

prepare_continuous_spectrumfunction#

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().

Parameters

NameTypeDescription
spectrumcallableCalled 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.
xarray_likeThe points to evaluate at.
modessequence of tupleThe 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.

prepare_lineoutfunction#

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

NameTypeDefaultDescription
dataxarray.DataArrayrequiredThe profile: every dimension but one already selected.
xstrNoneThe dimension that should remain. Default: whichever it is.

Returns

xarray.DataArray
data itself, as plot_lineout() draws it.

Raises

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

prepare_marker_scatterfunction#

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() draws.

Parameters

NameTypeDefaultDescription
markersxarray.DatasetrequiredPer-marker variables with a marker dimension (and usually t).
xstrrequiredThe variables for the horizontal and vertical axes.
ystrrequiredThe variables for the horizontal and vertical axes.
colorstrNoneThe variable to color by. Default: none.
color_atint or floatNoneTake the colors at another time (an integer position such as 0, or a float value). Default: at the selected time.
**selection{}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.

prepare_orbit_classificationfunction#

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().

Parameters

NameTypeDefaultDescription
orbitsxarray.Dataset or xarray.DataArrayrequiredAn orbits product (see prepare_orbits()).
xstr'v_par'The first quantity. Default: "v_par".
ystrNoneThe second quantity. Default: the magnetic moment mu (Particles5D), or v_perp if there is no mu (Particles5Dvperp).
v_parstr'v_par'The parallel velocity the classification uses. Default: "v_par".
tint or float0The 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() (0 passing, 1 trapped, -1 lost).

Raises

ValueError
If x or y is not a data variable.

prepare_orbitsfunction#

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() and plot_field_with_orbits(); also available directly to get the same data without a plot.

Parameters

NameTypeDefaultDescription
orbitsxarray.Dataset or xarray.DataArrayrequiredThe orbits product.
max_markersint200Keep only the first max_markers markers. Default: 200.
requiredsequence of str()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.

prepare_vectorfunction#

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(); also available directly, e.g. to hand the same strided data to a different plotting library.

Parameters

NameTypeDefaultDescription
dataxarray.DataArrayrequiredThe vector field, with exactly the dimensions component_dim, x and y.
xstrrequiredThe first spatial dimension.
ystrrequiredThe second spatial dimension.
components(int, int)(0, 1)The positions along component_dim of the two components. Default: (0, 1).
component_dimstr'component'The dimension holding the components. Default: "component".
strideint1Keep 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.

prepare_viewfunction#

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

NameTypeDescription
dataxarray.DataArrayThe labeled array.
viewViewThe 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.

prepare_volume_slicesfunction#

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().

Parameters

NameTypeDefaultDescription
dataxarray.DataArrayrequiredThe volume, with exactly the dimensions eta1, eta2 and eta3.
indicesdict of str to intNoneThe 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.

pyvista_volumefunction#

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

NameTypeDefaultDescription
dataxarray.DataArrayrequiredThe field, with exactly the dimensions eta1, eta2 and eta3 and the mapped coordinates X, Y and Z.
namestrNoneThe name of the scalars in the PyVista grid. Default: the array’s label, else "value".
cmapstr'viridis'The colormap. Default: "viridis".
opacitystr or sequence of float'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

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

resolve_marker_selectionfunction#

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

NameTypeDescription
datasetxarray.DatasetThe data, e.g. an orbits product.
selectiondictDimension 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.

save_all_scalarsfunction#

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() and <name>.<file_format> from plot_timeseries() for each scalar into directory, which is created if needed.

Parameters

NameTypeDefaultDescription
scalarsxarray.Dataset or mapping of str to xarray.DataArrayrequiredThe time series (e.g. out.scalars), each with the only dimension t.
directorystr or pathlib.PathrequiredThe directory to write into.
namessequence of strNoneThe scalars to write. Default: all but exclude.
excludesequence of strSCALARS_EXCLUDEScalars left out when names is not given. Default: ("time",).
logyboolFalseUse logarithmic value axes. Default: False.
run_labelstrNoneA run description shown as each figure’s suptitle. Default: the run shared by the scalars (see shared_run_label()); "" for none.
tablestr'csv'The table format, "csv" or "npz"; None or "" writes no table. Default: "csv".
file_formatstr'png'The figure format. Default: "png".
dpiint110The figure resolution. Default: 110.

Returns

list of str
The written paths; empty if there are no scalars.

Examples

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

save_figurefunction#

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(), this does nothing on MPI ranks other than 0.

Parameters

NameTypeDefaultDescription
figure(PlotResult, Figure, plotly.graph_objects.Figure or matplotlib.figure.Figure)requiredWhat to save: the result of a plot (e.g. with backend="plotly"), a figure of plasma_plots.figure(), or a figure made some other way.
namestr or pathlib.PathrequiredThe files’ path without the format, e.g. "maxwell-wave" or "figures/energy".
formatssequence of str('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(). Default: ("html", "png", "plotly.json").
showboolFalseShow the figure first. Default: False.
frameintNoneFor 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.
stillplotly.graph_objects.FigureNoneA 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.
widthint1100Width of an image of a Plotly figure, in layout pixels. Default: 1100.
heightint650Height of an image of a Plotly figure, in layout pixels. Default: 650.
scalefloat2.0Resolution factor of an image of a Plotly figure. Default: 2.

Returns

list of str
The paths written; empty on MPI ranks other than 0.

Examples

>>> 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",)
... )

save_framesfunction#

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

NameTypeDefaultDescription
dataxarray.DataArrayrequiredThe field, with the sweep dimension; other dimensions not drawn are selected by view.
directorystr or pathlib.PathrequiredThe directory to write into.
viewViewNoneWhich dimensions to select, which two to draw, in which coordinates, and the sweep dimension (view.sweep, default t) to run over (see View). Default: View().
stepint1Use every step-th value of the sweep. Default: 1.
prefixstr'frame'The start of each file name. Default: "frame".
dpiint110The resolution of the PNGs. Default: 110.
vminfloatNoneThe lower color limit. Default: from the data (see symmetric and robust).
vmaxfloatNoneThe upper color limit. Default: from the data (see symmetric and robust).
shared_climboolTrueTake 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.
cmapstr or matplotlib.colors.ColormapNoneThe colormap. Default: "viridis".
equal_aspectboolNoneDraw both axes to the same scale. Default: True in physical coordinates, False in logical ones.
titlestrNoneThe title, followed in each frame by the sweep value. Default: the array’s label.
symmetricboolFalseCenter the color limits on zero (-v, v), as a diverging colormap for a perturbation needs. Default: False.
robustboolFalseTake 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.
levelsint or sequence of floatNoneContour 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.
fillboolTrueFill the slice with colors; False leaves it transparent, e.g. to show only the contour lines of levels. Default: True.
overlaysdictNone

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.

xlabelstrNoneThe horizontal axis label. Default: the coordinate’s name and units.
ylabelstrNoneThe vertical axis label. Default: the coordinate’s name and units.
colorbar_labelstrNoneThe 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.

Examples

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

shared_run_labelfunction#

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

NameTypeDefaultDescription
dataxarray.DataArray, xarray.Dataset or sequence of theserequiredThe arrays.
defaultstr''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.

show_equilibriumfunction#

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

NameTypeDefaultDescription
equilstruphy.fields_background.base.FluidEquilibriumrequiredThe equilibrium, e.g. from out.equil.
domainstruphy.geometry.base.DomainrequiredIts mapping (out.domain), called as domain(eta1, eta2, eta3, squeeze_out=False).
scalarsstr'p0'One of equil’s profile methods ("p0", "n0", …). Default: "p0".
cmapstr'viridis'The colormap. Default: "viridis".
n1int40Number of evaluation points along eta1. Default: 40.
n2int48Number of evaluation points along eta2. Default: 48.
n3int10Number of evaluation points along eta3. Default: 10.
clipboolTrueCut 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

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