Plots

A result’s figures. PlotConfig describes what is in a panel — its traces and its axis labels, with no styling — and the renderers here draw it. A fit’s own panels and a result decoded from the API therefore produce the same figure.

Reach for these directly only when building panels by hand; a result object plots itself with plot_fit_results(), which picks a renderer by the objective’s type.

Typed plot configs and the matplotlib renderers that draw them.

A PlotConfig declares what is in a panel (its traces and labels), not how it looks; style is decided here by trace label, so the same config renders identically from an in-process fit and from a result decoded over the API. render() dispatches on the objective’s type and falls back to a plain panel grid for an unknown type, so a new server objective cannot break plotting for an older client. Matplotlib is an optional dependency (the plot extra).

class ionworks.pipeline.plots.PlotConfig(*, x_label: str = '', y_label: str = '', x_range: tuple[float | None, float | None] | None=None, y_range: tuple[float | None, float | None] | None=None, traces: list[PlotTrace] = <factory>)[source]

Bases: BaseModel

One panel: what to draw and how to label it, with no styling.

model_config = {'arbitrary_types_allowed': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

x_label: str
y_label: str
x_range: tuple[float | None, float | None] | None

Explicit (min, max) axis bounds; either side may be None to auto-scale.

y_range: tuple[float | None, float | None] | None
traces: list[PlotTrace]
classmethod from_wire(payload: dict) → PlotConfig[source]

Build a config from a stored plot payload.

The backend persists each plot in plotly’s vocabulary ({"type", "traces": [{"name", "x", "y"}], "layout": {"xaxis": ...}}); this maps that onto the fields above.

Parameters:

payload (dict) – One plot as returned by GET /datafits/{id}/plot_data.

Returns:

The equivalent config.

Return type:

PlotConfig

class ionworks.pipeline.plots.PlotTrace(*, label: str = '', x: Any = None, y: Any = None, color: str | None = None)[source]

Bases: BaseModel

One labelled series in a panel.

model_config = {'arbitrary_types_allowed': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

label: str

Legend label. Its first word selects the style (“Data”, “Model”, “Fit”, “Error”).

x: Any

x values. Any array-like matplotlib accepts.

y: Any

y values, as for x.

color: str | None

Overrides the label-derived colour, to distinguish series sharing a role.

ionworks.pipeline.plots.error_label(variable: str) → str[source]

Axis label for a variable’s residual, carrying its unit when it has one.

ionworks.pipeline.plots.fit_panels(variables: list[str], x_key: str, data: dict, model: dict, *, x_label: str | None = None) → list[PlotConfig][source]

Panels for a fit: measured against fitted per variable, then the residuals.

Residuals come last as a block, so laying the result out in len(variables) columns puts each residual under the curve it came from.

Parameters:
  • variables (list of str) – Keys to plot, one panel each. A "Name [unit]" key gives the residual panel its unit.

  • x_key (str) – Key of the independent variable. Taken from model when it carries its own, else shared with data.

  • data (dict) – The measurement, keyed by variable.

  • model (dict) – The fitted model’s output, keyed the same way.

  • x_label (str, optional) – Axis label for the independent variable. Defaults to x_key.

Returns:

One config per variable, then one residual config per variable.

Return type:

list of PlotConfig

ionworks.pipeline.plots.plot_eis(plots: list[PlotConfig | dict], **kwargs) → tuple[source]

Nyquist plot(s) for an EIS objective.

Impedance is plotted with an equal aspect ratio: a distorted Nyquist arc misrepresents the semicircle a reader is judging.

Parameters:

plots (list of PlotConfig or dict) – The objective’s panels.

Returns:

(fig, axes).

Return type:

tuple

ionworks.pipeline.plots.plot_panels(plots: list[PlotConfig | dict], *, equal_aspect: bool = False, fig_axes: tuple | None = None, n_cols: int | None = None, share_x: bool = False) → tuple[source]

Draw one panel per plot config, in a grid.

Parameters:
  • plots (list of PlotConfig or dict) – The panels, in the order they should appear. A dict is coerced — a stored plot payload via PlotConfig.from_wire(), otherwise as PlotConfig fields.

  • equal_aspect (bool, optional) – Force a 1:1 data aspect ratio on every panel. Defaults to False.

  • fig_axes (tuple, optional) – An existing (fig, axes) to draw onto, so several datasets can be overlaid on shared panels. A new figure is created when omitted.

  • n_cols (int, optional) – Column count. Defaults to a roughly square grid. Panels fill row by row, so pass it when the order groups panels into columns.

  • share_x (bool, optional) – Share the x axis down each column and label only the bottom panel of each. Only meaningful with n_cols, which is what puts panels of the same quantity in one column. Defaults to False.

Returns:

(fig, axes) where axes is a flat list, one entry per panel.

Return type:

tuple

Raises:

ValueError – If plots is empty.

ionworks.pipeline.plots.render(objective_type: str | None, plots: list[PlotConfig | dict], **kwargs) → tuple[source]

Draw an objective’s plots with the renderer for its type.

Parameters:
  • objective_type (str or None) – The objective’s type discriminator (e.g. "EIS"). An unknown or missing type falls back to a plain panel grid.

  • plots (list of PlotConfig or dict) – The objective’s panels.

Returns:

(fig, axes).

Return type:

tuple

Raises:

ValueError – If plots is empty.