pyplot -contour <z> <x> <y> renders a 2-D contour map of a quantity z
over the (x, y) plane — the natural view of a 2-D parameter sweep:
pyplot -contour i(vd) vgs vds # a device current over a (Vgs, Vds) grid
pyplot -contour gain rval cval # a gain surface over an (R, C) grid
pyplot mymap -contour p x y # named output (mymap.py/.data/.png)
The three arguments are the height/colour (z) and the two axes (x,
y), each a flattened, equal-length sweep vector. matplotlib triangulates the
(x, y) points (tricontourf), so gridded or scattered sweep data plots
with no grid-dimension metadata needed. Where do x, y, z come from? Any
2-D sweep that leaves three equal-length vectors — a nested .dc, the sweep
command family (E-146,
E-190), a .step grid, or vectors
you build in .control.
It reuses the entire pyplot pipeline
(E-94 onward) — the matplotlib back
end, set pyplot_terminal=png|svg|pdf for a headless render, styles, figure
size, backend. Three contour-specific knobs are added:
set pyplot_contour_levels=<N>— number of contour levels (default: let matplotlib choose);set pyplot_contour_lines— overlay labelled black contour lines on the filled map;set pyplot_contour_cmap=<name>— the colormap (defaultviridis).
(A) pyplotcontour_demo.cir — an analytic surface. A grid built in
.control with a known surface so the contour can be checked, not just
rendered:
z = x² + y²overx, y ∈ [-2, 2]→ concentric circular contours (a paraboloid:z = 0at the centre,z = 8at the corners).
(B) bridge_dc_demo.cir — a real nested .dc sweep. The everyday use: a
diode-OR bridge whose output V(c) follows whichever of its two inputs is
higher. A nested .dc sweeps both inputs; since v1 drives node a and v2
drives node b, V(a)/V(b) are the two swept values at every point, so
pyplot -contour v(c) v(a) v(b) maps the output over the (V(a), V(b)) plane —
a max-like corner surface rising toward the top and right edges. This shows the
feature on genuine simulation output (not just .control math), with the
turbo colormap and labelled contour lines.
verify_pyplotcontour.py (19 checks, both solvers) runs both decks and
parses the generated .data/.py and PNGs.
For (A) the analytic surface:
- the
-contourpath is taken (tricontourf, notplot/hist; a colorbar labelledz; axes labelledx/y); - the data table has three columns
(x, y, z), allNrows; - the column mapping is correct —
zreconstructsx² + y²from the.data; - the sweep is genuinely 2-D —
xandyeach span a real range, andzruns from ~0 at the grid centre to ~8 at a corner; - a valid, non-trivial PNG is rendered.
For (B) the real nested .dc:
- the
-contourpath is taken with the requested knobs (tricontourf,cmap=turbo, overlaid lines; colorbarv(c), axesv(a)/v(b)); - the nested
.dcproduced the flattened 51×51 = 2601-row, 3-column grid; - the axes are the two real swept sources (
V(a),V(b)each span[-1, 1]); - the output is the diode-OR surface — ~0 when both inputs are low, rising when either input is high, maximal at the both-high corner (confirms the columns map correctly onto a genuine circuit result);
- a valid PNG is rendered.
python3 verify_pyplotcontour.py