# ngspice-jit — command reference

Reference manual for ngspice-jit, a fork of ngspice (ngspice-46 base) that JIT-compiles behavioral foundry R/C into native OSDI devices at parse time. A command-by-command reference with full `cp_coms` catalog coverage; every example is executed against the ngspice-jit build and its real output shown verbatim. Fork-specific features (OSDI codegen, regex device/vector selection, the `devices` command) are flagged as build extensions where they appear.

Notation. Each entry carries a mode badge: batch + ui the command works both in batch (`ngspice -b`) and interactively; ui only it exists only inside `.control … .endc` or the interactive prompt. The badge is taken directly from the `spiceonly` field of the command table in the source, so it is authoritative.

## 1. General netlist & language syntax

This chapter assumes you know what a SPICE netlist is; it is the reference for ngspice’s specific syntax rules — the ones that bite. Individual commands are in §3.

### Deck structure

A deck is a title line, a body of cards, and `.end`:

- The first line is always the title and is ignored — even if it looks like a valid card. Putting a real card (e.g. `.save`) on line 1 silently does nothing. Start every deck with a comment or title line.
- Comments: a line starting with `*` is a full-line comment; `$` (with a preceding space) starts an in-line comment to end of line.
- Line continuation: a line beginning with `+` is appended to the previous card.
- Case-insensitive: card names, node names, and parameters are not case-sensitive (`VDD` = `vdd`).
- `.end` terminates the deck; text after it is ignored.

### Element cards

Every instance card begins with a letter that selects the device type, followed by the instance name, its terminal nodes, and a value or model plus parameters:

    Rload  out 0    1k                  $ R + name; nodes out,0; value 1k
    M1     d g s b  nch  w=1u l=0.18u    $ M + name; 4 nodes; model; params
    X1     in out   amp                  $ X = subcircuit call (see .subckt)

letter | device | letter | device
--- | --- | --- | ---
`R C L` | resistor, capacitor, inductor (`K`=mutual) | `V I` | independent voltage / current source
`D Q` | diode, BJT | `M J Z` | MOSFET, JFET, MESFET
`E G` | VCVS, VCCS (linear controlled) | `F H` | CCCS, CCVS
`B` | arbitrary (nonlinear) source `V=`/`I=` | `X` | subcircuit call → `.subckt`
`S W` | voltage / current switch | `A` | XSPICE code model

### Nodes

Nodes are named or numbered; `0` (or `gnd`) is ground and is global. Names are case-insensitive. A node reachable everywhere without wiring is declared with `.global`; a node inside a subcircuit instance is addressed hierarchically as `Xinst.node` (see `.subckt`).

### Numbers & unit suffixes

A number may carry a scale suffix (case-insensitive); trailing letters after the suffix are ignored (`1kOhm` = `1k`). The classic trap: `m` is milli, not mega — mega is `meg`.

`T` | `G` | `Meg` | `k` | `m` | `u` | `n` | `p` | `f` | `a`
--- | --- | --- | --- | --- | --- | --- | --- | --- | ---
1e12 | 1e9 | 1e6 | 1e3 | 1e-3 | 1e-6 | 1e-9 | 1e-12 | 1e-15 | 1e-18

syntax.cir — run: `ngspice -b syntax.cir` (`meg` vs `m`, and `+` continuation)

    * first line is the TITLE and is ignored
    .options savecurrents
    V1 in 0 DC 1
    R1 in 0 1meg      $ 1,000,000 ohm  -> I = 1 uA
    R2 in 0 1k        $ 1,000 ohm      -> I = 1 mA
    R3 in 0 1m        $ 0.001 ohm  (m = MILLI!)  -> I = 1000 A
    Rcont in 0
    + 2k              $ line continued with '+'  -> I = 0.5 mA
    .op
    .control
      run
      print @R1[i] @R2[i] @R3[i] @Rcont[i]
    .endc
    .end

    @r1[i] = 1.000000e-06     (1 V / 1 MΩ)
    @r2[i] = 1.000000e-03     (1 V / 1 kΩ)
    @r3[i] = 1.000000e+03     (1 V / 1 mΩ  ← m is milli!)
    @rcont[i] = 5.000000e-04  (1 V / 2 kΩ, value read off the + line)

Gotcha — `m` vs `meg`. A “1 mΩ” typo for a 1 MΩ resistor (`1m` instead of `1meg`) is off by 10⁹ — the run above draws 1000 A instead of 1 µA. This is the single most common SPICE numeric error.

### Parameter expressions

Expressions in a value are wrapped in `{ … }` or `' … '` and are evaluated at parse time (before the run) over `.param`/`.func` values and constants — they cannot see simulation results. Run-time math on vectors is done instead with `let`/`define` inside `.control` (see §2).

### Two languages in one file

A deck mixes two sub-languages: dot-cards (the netlist — `.tran`, `.model`, `.param`, …) processed by the parser, and commands inside a `.control … .endc` block (`run`, `let`, `print`, loops) executed by the interpreter. Which one runs a given verb, and when, is the source of most surprises — covered in §2 · `.control` vs. a pure dot-card deck (why ngspice differs from Eldo/HSPICE, when you need `.control`, and what works without it).

## 2. Key concepts & gotchas

Cross-cutting behaviors that trip people up; command entries link back here.

Batch mode needs an output directive. In batch (`ngspice -b`), an analysis card only runs if the deck also produces output — a `.control { run … }` block, or a `.print`/`.plot`/`.four` line. A deck with just `.pz` (or `.sens`, `.ac`, …) and nothing else prints “no .plot, .print, or .fourier lines in batch mode; no simulations run!” and exits without computing anything. `.op`, `.tf` and `.four` are exceptions — they emit their result directly. Interactively (or with `-r`) this restriction does not apply.

Which nodes end up in the raw file, and whether a file is even produced, depends on three interacting choices: the `-r` command-line flag, whether you used `.save`/`save`, and whether a `.control` block calls `write`. The table is the mental model; the examples verify it.

command | mode | behavior
--- | --- | ---
`.save node …` | batch + ui | restricts the `-r` raw file to the listed vectors (plus the `time` scale)
`save node …` | batch + ui | same restriction, issued as a command (e.g. inside `.control`)
`write file expr …` | ui only | writes selected vectors to its own file; has no dot-card form
`.probe` / `probe` | batch | adds device currents to the raw file; no-op without `-r`

Inside `.control`, ngspice has two separate namespaces that look alike but are not: vectors (numeric simulation data — nodes, currents, anything you compute with `let`) and shell variables (text/flags set with `set`). The `$` / `$&` sigils and the `echo`/`print` split all come down to which namespace you mean.

The one rule that removes most confusion: `echo` is literal, `print` evaluates. `echo` prints its words verbatim (expanding only `$shellvar`); it will happily print `v(out)` or `@R1[resistance]` as plain text. `print` evaluates them as vectors/expressions. If a value comes out as its own name, you used `echo` where you needed `print`.

These are control-mode constructs — they live inside a `.control … .endc` block (or the interactive prompt), never as dot-cards. They turn a netlist into a small script: sweep a value, retry until convergence, branch on a result. Each loop body is closed by `end`. They rely on the shell variables and vectors covered in the Vectors chapter (`let`, `set`, `$var`, `$&vec`).

Loop vs. sweep. A `foreach`/`while` loop that re-runs an analysis is not the same as `.dc`/`.step`: the loop runs a full separate analysis each pass (you control saving/plotting), whereas a sweep produces one dataset indexed by the swept variable. Use a loop when each pass needs different topology/parameters or its own output file; use a sweep for one parametric curve.

Commands that load, run, and modify the circuit between runs — the basis of interactive what-if sweeps without re-parsing the deck. `run` starts the defined analysis; `resume` continues a paused one; `reset` reloads the circuit to its initial state; `source file` loads another deck. The two most useful for scripting are `alter` and `altermod`.

These dot-cards give a netlist its structure: named parameters, reusable functions, hierarchical subcircuits, and file inclusion. Parameter/function expressions are wrapped in `{ … }` (or `' … '`) and are evaluated at parse time, before the simulation runs.

`.control … .endc` vs. a pure dot-card deck — why ngspice has two languages. A single ngspice file mixes two languages, and it helps to know which one you are writing in. This is the biggest structural difference from Eldo and HSPICE, which use one unified deck language and push scripting/plotting into dedicated dot-cards (`.STEP`, `.ALTER`, `.MEASURE`) and an external waveform viewer.

ngspice descends from Berkeley SPICE3 + nutmeg: the simulator reads a declarative netlist, while nutmeg is an interactive command shell (type `let`, `plot`, `foreach`… at the `ngspice ->` prompt). The `.control … .endc` block is simply “run these nutmeg commands automatically” embedded in the file — an imperative script that runs after the circuit is parsed.

| netlist / dot-card language | control (nutmeg) language
--- | --- | ---
style | declarative — a circuit + what to analyze | imperative — a script, run top to bottom
examples | `.tran` `.ac` `.dc` `.model` `.param` `.print` `.save` | `run` `let` `foreach` `while` `if` `alter` `meas` `fft` `write` `plot`
runs | during the simulator pass | after parse, on demand, inside `.control … .endc`

Can you do everything without `.control`? Basic simulation, yes; the full tool, no — the whole scripting/post-processing half of ngspice lives only inside `.control`. ngspice deliberately ships both forms of several features so HSPICE-style batch decks work unchanged (`.four` card / `fourier` command, `.plot` ASCII card / graphical `plot`, `.meas` card / `meas` command) — that duplication is the compatibility bridge.

works as pure dot-cards (batch `-b`, no `.control`) | only available inside `.control`
--- | ---
analyses `.op .tran .ac .dc .noise .tf .disto .pz .sens .four`; output `.print .plot .save .meas .options .param .temp .model .subckt .include .ic`; rawfile via `-r`, log via `-o` | scripting `let set foreach while if repeat`; `alter`/`altermod` between runs; vector math `fft spec psd linearize load`; chosen-set `write`/`wrdata`; graphical `plot`/`hardcopy`/`gnuplot`; `diff`; sequenced multi-analysis; Monte Carlo (loop + `mc_source`)

Rule of thumb. A plain dot-card deck behaves like HSPICE batch mode — reach for it when one fixed analysis with standard output is all you need. Add `.control` the moment you need loops, logic, several analyses in sequence, or post-processing beyond `.print`/`.plot`. Two traps at the boundary: a `.param` is not visible inside `.control` (promote it with `.csparam`), and batch mode still needs an output directive (`.print`/`.plot`/`.four` or `-r`) or nothing runs. To accept more HSPICE deck syntax overall, set `ngbehavior=hs` in `spinit`/`.spiceinit`.

`.spiceinit` / `spinit` — the startup files. ngspice runs a startup script at launch: a list of front-end commands executed as if typed at the `ngspice ->` prompt. It is not a netlist — there is no circuit yet, so device cards and analysis dot-cards do not belong here, and no `.control … .endc` wrapper is needed (the file is already in command context; comment lines start with `*`). Two files share the same syntax: `spinit` is system-wide (in the install/share dir — it loads code models and sets machine defaults), and `.spiceinit` is yours. ngspice searches for `.spiceinit` in the current directory first, then your home directory, so a project can carry its own. Both are sourced on every start, batch or interactive.

It configures the tool; the deck describes the circuit. Anything valid as a front-end command works — `set`/`unset`, `option`, `alias`, `codemodel`/`osdi`/`use`, `if/end`:

category | examples | what they do
--- | --- | ---
dialect | `set ngbehavior=hs` (also ps / lt / hsa / psa / lta) | accept HSPICE / PSPICE / LTspice deck syntax
performance | `set num_threads=8` · `option klu` | OpenMP thread count · pick the KLU solver as default
output defaults | `set filetype=ascii` · `set hcopypscolor=1` · `option noinit` | ASCII rawfiles · colour hardcopy · suppress the OP dump
option defaults | `option reltol=1e-4` · `set nostepsizelimit` · `set ng_nomodcheck` | tolerances / behavior applied to circuits loaded afterward
aliases | `alias exit quit` · `alias acct rusage all` | command shortcuts
load models / libs | `codemodel …/analog.cm` · `osdi …/BSIMCMG.osdi` · `use lib` | load XSPICE code models / OSDI compiled models / device libraries
enable / disable OSDI | `set osdi_enabled` · `unset osdi_enabled` | the switch the shipped `spinit` tests to load (or skip) the `.osdi` model libraries
front-end vars | `set rndseed=12` · `set askquit` · `set ngdebug` · `set sourcepath=( … )` | fix the RNG seed · confirm on quit · debug trace · `source`/`.include` search path
diagnostics | `set topo_verbose` · `set iter_verbose` | report what topology reduction pruned or tied · report every Newton iteration and its wall-clock cost — see iteration limits
control flow | `if $?xspice_enabled … end` · `unset osdi_enabled` | conditional setup (the shipped `spinit` guards code-model loading this way)

Put the diagnostics in `.spiceinit` for a long run. A big extracted deck can spend many minutes in setup, and the solver then prints nothing at all until the analysis either converges or gives up — there is no way to distinguish a slow solve from a hang, and no way to learn what an iteration actually costs. `set iter_verbose` makes every iteration report itself (flushed immediately, so it survives a redirected `-o` log); `set topo_verbose` names the nodes the reducer tied or removed and writes them to `float_nodes.txt`.

What must not go in it. Circuit content — device lines, `.model`, `.subckt`, `.param`, and analysis cards (`.tran`/`.ac`/`.dc`) — has no circuit to attach to at startup; keep it in the deck. Use the `option …` command form here, not the `.options` dot-card. And don’t wrap the file in `.control … .endc`: it is redundant. Verified: a `set myflag=42` in `./.spiceinit` is readable as `$myflag` inside a later deck’s `.control`.
Turning OSDI on and off. The shipped `spinit` wraps its model loads in a guard — `if $?osdi_enabled … osdi lib1.osdi … end` — so a single variable decides whether the compiled `.osdi` libraries load. On this build `osdi_enabled` is not defined by default (`$?osdi_enabled` = 0), so OSDI loading is opt-in: add `set osdi_enabled` before the block to enable it, or `unset osdi_enabled` (the shipped default) to skip it. The XSPICE code models work the same way via `$?xspice_enabled`. Two caveats: the variable is only a `spinit` convention — a bare `osdi foo.osdi` line loads that library regardless — and this switch governs loading external OSDI/OpenVAF model libraries, which is distinct from the parse-time §4 behavioral-passive codegen (that one triggers on behavioral `R=`/`C=` expressions in the netlist and has its own opt-out, `set osdi_res_bsource` — also placed in `.spiceinit` because it acts at parse time).

## 3. Command reference (A–Z)

Every dot-card and control command in one alphabetical sequence (leading dot ignored for ordering). See the index above to jump.

### `.ac` — small-signal AC (frequency) analysis batch + ui

Synopsis

    .ac  dec|oct|lin  n  fstart  fstop

Description

Linearizes the circuit about its DC operating point and sweeps frequency, giving complex node voltages (magnitude/phase). Stimulus comes from sources carrying an `AC` value.

Parameters

parameter | meaning
--- | ---
`dec` / `oct` / `lin` | points spaced per decade / per octave / linearly
`n` | points per decade/octave, or total points for `lin`
`fstart fstop` | frequency range (Hz)

Example — RC low-pass corner
ac.cir — run: `ngspice -b ac.cir`

    * RC low-pass AC magnitude, find -3 dB corner
    V1 in 0 AC 1
    R1 in out 1k
    C1 out 0 100n
    .ac dec 50 10 1meg
    .control
      run
      meas ac fc WHEN vdb(out)=-3.0103
    .endc
    .end

    fc                  =  1.59155e+03

fc = 1/(2π·R·C) = 1/(2π·1 kΩ·100 nF) = 1591.55 Hz ✓. Note `vdb()` gives magnitude in dB; `-3.0103 dB` is the half-power point.

Example — phase crosses −45° at the corner
ac_phase.cir — run: `ngspice -b ac_phase.cir`

    * phase = -45 deg (= -pi/4 rad) exactly at the corner
    V1 in 0 AC 1
    R1 in out 1k
    C1 out 0 100n
    .ac dec 50 10 1meg
    .control
      run
      meas ac f45 WHEN vp(out)=-0.7853982   $ vp() is in RADIANS
    .endc
    .end

    f45                 =  1.59169e+03

Same 1592 Hz corner, cross-checked through the phase instead of the magnitude ✓.

Gotcha — `vp()` returns radians, not degrees. A single-pole low-pass is −45° at its corner, but `meas … WHEN vp(out)=-45` fails with out of interval because `vp()` ranges only −π/2…0. Use the radian value `-0.7853982` (= −π/4), or take `cph()` for continuous (unwrapped) phase.
Gotcha — no `AC` value, no stimulus. `.ac` only injects signal through sources given an `AC` magnitude (e.g. `V1 in 0 AC 1`). A source with only a `DC` or transient (`SIN`/`PULSE`) spec contributes nothing in AC — the output is silently zero.
See also `.noise`, `.tf`, `.op`.

### `A` device / `codemodel` — XSPICE code models batch + ui

Synopsis

    Aname  in-ports …  out-ports …  modelname
    .model modelname  codemodel_type ( params )
    codemodel  library.cm                 $ load an external code-model library

Description

The `A` card instantiates an XSPICE code model — a compiled behavioral block (gain, summer, limiter, s-domain transfer function, ADC/DAC, a file-driven source, …) beyond the built-in analog primitives. The code models are not linked into ngspice; they live in shared-library files (`analog.cm`, `digital.cm`, `xtradev.cm`, …) and are loaded at run time by the `codemodel` command. In a normal install `spinit` already issues those `codemodel` lines at start-up (guarded by `$?xspice_enabled`), so a deck can use `gain`, `filesource`, etc. with no `codemodel` line of its own.

amodel.cir — a `gain` block; run: `ngspice -b amodel.cir`

    V1 in 0 DC 0.5
    A1 in out gainblk
    .model gainblk gain(gain=3.0)
    .control
      op
      print v(out)      $ 3.0 * 0.5 = 1.5
    .endc
    .end

    v(out) = 1.500000e+00

Example — a vector pattern generator (`filesource` driving a whole bus)
buspattern.cir + pattern4.dat — run: `ngspice -b buspattern.cir`

    * one A-device drives a 4-bit bus from a multi-column file: a binary counter
    a1 %v([b3 b2 b1 b0]) bus4               $ a VECTOR port -> four nodes at once
    .model bus4 filesource (file="pattern4.dat"
    + amploffset=[0 0 0 0] amplscale=[1.8 1.8 1.8 1.8]   $ per-bit: logic 1 = 1.8 V
    + timeoffset=0 timescale=1 timerelative=false amplstep=TRUE)
    rb3 b3 0 1meg
    rb2 b2 0 1meg
    rb1 b1 0 1meg
    rb0 b0 0 1meg
    .tran 5n 1.5u
    .control
      run
      meas tran b3_7  FIND v(b3) AT=750n    $ counter=7  (0111): b3=0,   b0=1.8
      meas tran b0_7  FIND v(b0) AT=750n
      meas tran b3_12 FIND v(b3) AT=1250n   $ counter=12 (1100): b3=1.8, b0=0
      meas tran b0_12 FIND v(b0) AT=1250n
    .endc
    .end

    b3_7                =  0.00000e+00
    b0_7                =  1.80000e+00
    b3_12               =  1.80000e+00
    b0_12               =  0.00000e+00

This is where code models earn their keep. A vector port `%v([b3 b2 b1 b0])` lets one `filesource` drive a whole bus: each column of `pattern4.dat` feeds one bit, scaled independently by the per-port `amplscale`/`amploffset` vectors — here a 4-bit binary counter with logic-1 = 1.8 V. `amplstep=TRUE` holds each level (clean digital edges); `false` would linearly interpolate (analog PWL). At t = 750 ns the file’s row is `0 1 1 1` and t = 1250 ns is `1 1 0 0` — both read back exactly ✓. Ports carry a type sigil: `%v([n])` single-ended, `%vd([p n])` differential, `%i` current, `%vnam` named. A fuller version — differential sine/cosine plus an 8-bit bus — is at `/users/design/ngspice/code_model_pattern.sp`.

The four bits, stacked — the classic binary cascade (`b0` toggles every 100 ns, each higher bit half as often). bus4.gp: `wrdata bus4.txt v(b3)…v(b0)` then `gnuplot bus4.gp`.
Example — a quadrature (cos/sin) generator via a differential port
sincos.cir + sincos.dat — run: `ngspice -b sincos.cir`

    * two quadrature channels from a 3-column file (time, cos, sin)
    a1 %vd([c 0 s 0]) sincos                $ %vd pairs nodes -> differential ports
    .model sincos filesource (file="sincos.dat"
    + amploffset=[0 0] amplscale=[1 1]
    + timeoffset=0 timescale=1 timerelative=false amplstep=false)
    rc c 0 1meg
    rs s 0 1meg
    .tran 2u 2m
    .control
      run
      meas tran c_0 FIND v(c) AT=0          $ cos(0)   = 1
      meas tran s_0 FIND v(s) AT=0          $ sin(0)   = 0
      meas tran c_q FIND v(c) AT=250u       $ cos(90d) = 0
      meas tran s_q FIND v(s) AT=250u       $ sin(90d) = 1
    .endc
    .end

    c_0                 =  1.00000e+00
    s_0                 =  0.00000e+00
    c_q                 =  1.73472e-18     (= 0)
    s_q                 =  9.99920e-01     (= 1)

Here `%vd([c 0 s 0])` declares two differential output ports — one across (`c`,0), one across (`s`,0) — fed by columns 2 and 3 of the file. With `amplstep=false` the samples are linearly interpolated, giving smooth analog waveforms; the two channels are 90° apart (cos and sin) ✓. Because each port’s reference here is ground, the pair acts single-ended — give a non-zero second node to drive a genuinely floating differential output.

The two channels — `v(c)` and `v(s)` in quadrature (90° apart), smoothly interpolated from the file. sincos.gp: `wrdata sincos.txt v(c) v(s)` then `gnuplot sincos.gp`.
Example — an 8-bit bus with per-bit offsets (stacked logic traces)
bus8.cir + pattern8.dat — run: `ngspice -b bus8.cir`

    * 8 bits, each parked at a distinct DC level so the traces don't overlap
    a1 %v([a7 a6 a5 a4 a3 a2 a1 a0]) bus8
    .model bus8 filesource (file="pattern8.dat"
    + amploffset=[0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8]      $ per-bit DC level
    + amplscale=[0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05] $ small swing
    + timeoffset=0 timescale=1 timerelative=false amplstep=TRUE)
    ra7 a7 0 1meg
    ra0 a0 0 1meg
    .tran 5n 850n
    .control
      run
      meas tran a0_hi FIND v(a0) AT=50n    $ a0 hot: 0.8 + 0.05 = 0.85
      meas tran a7_lo FIND v(a7) AT=50n    $ a7 low: 0.1 + 0    = 0.10
      meas tran a7_hi FIND v(a7) AT=750n   $ a7 hot: 0.1 + 0.05 = 0.15
      meas tran a0_lo FIND v(a0) AT=750n   $ a0 low: 0.8 + 0    = 0.80
    .endc
    .end

    a0_hi               =  8.50000e-01
    a7_lo               =  1.00000e-01
    a7_hi               =  1.50000e-01
    a0_lo               =  8.00000e-01

The eight-column file is a walking-`1`; the per-bit `amploffset` vector (0.1…0.8 V) parks each bit at its own DC level while `amplscale` keeps the swing to 0.05 V, so plotting the bus shows eight non-overlapping stacked traces — the usual way to eyeball a digital vector. All four probes match their `offset + scale·bit` ✓.

The eight bus traces — the walking `1` climbs diagonally while each bit stays parked at its own level. Generated with bus8.gp: `wrdata bus8.txt v(a7)…v(a0)` then `gnuplot bus8.gp`.
Gotcha — `filesource` outputs 0 past the last sample. Beyond the final row’s time the source drops to 0 (offsets included), not the last held value. Add a terminating row (here at 800 ns) if you need the pattern to persist to the end of the `.tran` — without it the `a7`/`a0` probes at 750 ns read 0 instead of their levels.
`codemodel` — loading a library

You only call `codemodel path/lib.cm` yourself to load a library that isn’t already in — typically a custom, user-compiled `.cm`, placed in `spinit`/`.spiceinit` (or a script sourced) before the circuit that uses it. That is why there is no standalone deck example here: the standard libraries are loaded for you at start-up, and the model just works.

Gotcha — don’t re-load a library that’s already loaded. Adding `codemodel …/analog.cm` to a deck when `spinit` already loaded it double-registers the models and segfaults this build. Load each `.cm` exactly once (leave the standard set to `spinit`); use an explicit `codemodel` only for a library not already present.
See also `.model`, `.spiceinit` (where `codemodel` normally lives), OSDI (compiled foundry passives, a different route).

### `alias` / `unalias` — command shortcuts ui only

Synopsis

    alias [ name [ definition ] ]        unalias name

Description

Defines a control-mode shortcut for a command and its arguments. `alias` with no arguments lists all aliases; `unalias` removes one.

alias.cir — run: `ngspice -b alias.cir`

    R1 1 0 1k
    .control
      alias pp print
      let x = 6*7
      pp x               $ 'pp' now means 'print'
    .endc
    .end

    x = 4.200000e+01

See also `define`.

### `alter` — change a device value between runs batch + ui

Synopsis

    alter  devname  value
    alter  devname  param = value

Description

Changes an instance parameter of an already-loaded device (a resistance, capacitance, source level, …) without editing or re-parsing the netlist. Follow it with `run` to resimulate. Ideal inside a `foreach`/`while` loop for parameter studies.

alter.cir — run: `ngspice -b alter.cir`

    V1 in 0 DC 1
    R1 in out 1k
    R2 out 0 1k
    .tran 1u 5u
    .control
      run
      print v(out)[0]        $ 0.5  (equal divider)
      alter R2 = 3k
      run
      print v(out)[0]        $ 0.75 (now a 1k:3k divider)
    .endc
    .end

    v(out)[0] = 5.000000e-01
    v(out)[0] = 7.500000e-01

Example — `alterparam`: change a `.param`, not a device
alterparam.cir — run: `ngspice -b alterparam.cir`

    .param rval=1k
    V1 in 0 DC 1
    R1 in 0 {rval}
    .op
    .control
      run
      print @r1[resistance]
      alterparam rval=2k
      mc_source              $ rebuild the deck so the new .param takes effect
      op
      print @r1[resistance]
    .endc
    .end

    @r1[resistance] = 1.000000e+03
    @r1[resistance] = 2.000000e+03

Where `alter`/`altermod` poke a device or model directly, `alterparam` edits a `.param` value. Because parameters are resolved when the netlist is expanded, it only takes effect after a re-expansion — `mc_source` (or `reset`) — then the next `run`/`op` sees the new value.

See also `altermod`, `foreach`, `@dev[param]`, `.param`.

### `altermod` — change a model parameter between runs batch + ui

Synopsis

    altermod  modelname  param = value

Description

Like `alter`, but targets a `.model` parameter — the change affects every device using that model. Useful for corner/temperature what-ifs.

altermod.cir — run: `ngspice -b altermod.cir`

    .model DMOD D(IS=1e-14)
    V1 a 0 DC 0.7
    R1 a d 100
    D1 d 0 DMOD
    .tran 1u 5u
    .control
      run
      print v(d)[0]
      altermod DMOD IS=1e-11
      run
      print v(d)[0]          $ larger IS -> smaller forward drop
    .endc
    .end

    v(d)[0] = 6.413270e-01
    v(d)[0] = 4.950148e-01

Raising the diode saturation current by 1000× lowers its forward drop from 0.641 V to 0.495 V (≈ 60 mV/decade × 3 decades) ✓.

See also `alter`.

### `@device[param]` — read a device/model parameter ui only

Synopsis

    @devicename[parameter]        $ e.g. @R1[resistance], @M1[gm], @Q1[ic]

Description

The `@` accessor reads a live device (or model) parameter or operating-point quantity into an expression — resistance of a resistor, `gm`/`vds` of a MOSFET, `ic` of a BJT, etc. It must be evaluated (via `let`/`print`), not `echo`ed. Which parameters exist for a device is listed by `devhelp` / `showmod`.

vectors.cir (same deck)

      print @R1[resistance]    $ 1000

    @r1[resistance] = 1.000000e+03

See also `let`, `devhelp`, `alter`.

### `compose` — build a vector ui only

Synopsis

    compose  name  values v1 v2 …
    compose  name  start=a stop=b step=d      (or  lin=n / log=n)

Description

Creates a vector from an explicit list of values, or as a linear/log ramp. Handy for building a sweep axis or a lookup table inside `.control`.

compose.cir — run: `ngspice -b compose.cir`

    R1 1 0 1k
    .control
      compose p values 1 2 4 8
      print p
      compose ramp start=0 stop=1 step=0.25
      print ramp
    .endc
    .end

    p:    1  2  4  8
    ramp: 0  0.25  0.5  0.75  1

See also `let`.

### `.csparam` — expose a parameter to `.control` batch + ui

Synopsis

    .csparam  name = { expression }

Description

Copies a netlist parameter into the `.control` namespace (as a vector), where plain `.param` values are otherwise invisible. The bridge between parse-time parameters and the control script.

csparam.cir — run: `ngspice -b csparam.cir`

    .param vsup=3.3
    .csparam vsupply={vsup}
    .control
      echo "supply is" $&vsupply     $ 3.3
    .endc
    .end

    supply is 3.3

Gotcha — `.param` is not visible in `.control`. Inside a control block, `$vsup` / `$&vsup` do not see a netlist `.param vsup`. Use `.csparam` to copy it across (or `set`/`let` a fresh value in the control block).
See also `.param`, `set`, `let`.

### `.dc` — DC sweep batch + ui

Synopsis

    .dc  srcname  start  stop  step  [ srcname2 start2 stop2 step2 ]

Description

Sweeps a source (voltage or current) — or a nested pair of sources — and records the DC solution at each step, producing curves vs. the swept value. The classic use is a device I–V characteristic.

Parameters

parameter | meaning
--- | ---
`srcname` | independent source to sweep (e.g. `Vs`)
`start stop step` | sweep range and increment
second source | optional outer sweep → a family of curves

Example — diode I–V
dc.cir — run: `ngspice -b dc.cir`

    * Diode I–V curve via .dc source sweep
    Vs a 0 DC 0
    R1 a d 100
    D1 d 0 DMOD
    .model DMOD D(IS=1e-14 N=1)
    .dc Vs 0 1 0.05
    .control
      run
      meas dc vd_1v FIND v(d)  AT=1.0
      meas dc id_1v FIND i(vs) AT=1.0
    .endc
    .end

    vd_1v               =  6.84819e-01
    id_1v               = -3.15181e-03

At Vs = 1 V the diode drops 0.685 V, so I = (1 − 0.685)/100 Ω = 3.15 mA ✓.

Example 2 — nested sweep (a family of curves)
dc_nested.cir — run: `ngspice -b dc_nested.cir`

    .model NM NMOS(level=1 kp=200u vto=0.5)
    Vd d 0 DC 1
    Vg g 0 DC 0
    M1 d g 0 0 NM w=10u l=1u
    .dc Vg 0 2 0.5  Vd 1 2 1      $ inner Vg sweep, outer Vd -> a family
    .control
      run
      let id = i(vd)
      print id[3]                 $ Vgs=1.5 on the first Vds curve
    .endc
    .end

    id[3] = -1.00000e-03

The outer `Vd` sweep repeats the inner `Vg` sweep for each drain voltage — the MOSFET ID–VGS family. At VGS=1.5 V (saturation) ID = ½·kp·(W/L)·(VGS−Vto)² = 1.0 mA ✓ (`i(vd)` negative by source convention).

Gotcha — source-current sign. `i(vs)` is negative here (−3.15 mA): a voltage source’s branch current is measured flowing into its + terminal, so current delivered to the circuit reads negative. Negate it (`-i(vs)`) for the physical current — but note `meas … FIND -i(vs)` fails to parse; measure `i(vs)` and negate the result, or sense the current through a series element.
See also `.op`, `.tran`.

### `define` — user functions ui only

Synopsis

    define  name(args)  expression

Description

Defines a reusable function of one or more arguments, usable anywhere an expression is (in `let`, `print`, plotting). Handy for repeated post-processing (dB, RMS, a custom figure of merit).

vectors.cir (same deck) — the `define` lines

      define sq(y) y*y
      print sq(4)              $ 16

    sq(4) = 1.600000e+01

See also `let`.

### `devhelp` / `devices` / `show` / `showmod` — device & model introspection batch + ui

Synopsis

    devhelp [ devtype [ param ] ]      $ list a device type's parameters
    devices [ regexp … ]               $ list device instance NAMES only
    show    device|regexp … [ : param … ]   $ instance operating-point values
    showmod model|regexp … [ : param … ]    $ model parameter values

Description

`devhelp` lists what parameters a device type accepts and reports; `show`/`showmod` print the live values for instances/models after a run — the readable companion to the `@dev[param]` accessor.

Taming the output — regex devices, chosen params

On a real design `show` dumps ~50 parameters per BSIM device × dozens of devices — pages of scroll. Two features cut that down, and they combine:

- Select devices by regular expression: `show r.*` = every resistor, `show m.*` = every MOSFET, `show m.x1.*` = every device in subcircuit X1. (Plain names, `all`, and the `type:subckt:dev` syntax still work; a name with a bare `.` stays literal, so `show m.x1.m7` is exact.)
- Select parameters after a `:`: everything past the colon names the parameters to print — `show m1 : vgs vds gm id` shows only those four. (An invalid name for that model prints `?????????`.)

Together: `show m.x1.* : id vgs vds gm` → those four values for every MOSFET in X1, one tidy table instead of pages.

`devices` — just the names

When you only want to know which devices exist (not their values), `devices` lists the instance names, one per line — the equivalent of piping `show` through `grep`. No argument lists them all; each argument is a regex (grep-style):

    devices              $ every device instance
    devices m.*          $ every MOSFET:  m2  m1  m.x1.m3 …
    devices m.x1.*       $ the devices inside subcircuit X1
    devices ^r ^c        $ every resistor and capacitor

Handy to discover names before a `show … : params` or an `@dev[param]` query. (New command in this build.)

devhelp.cir — run: `ngspice -b devhelp.cir`

    .control
      devhelp resistor
    .endc

    Resistor - Simple linear resistor
      103  rsh      inout  Sheet resistance
      106  narrow   inout  Narrowing of resistor
      109  short    inout  Shortening of resistor
      …

Example — regex device selection + chosen parameters
show_regex.cir — run: `ngspice -b show_regex.cir`

    .model NM NMOS(LEVEL=1 VTO=0.7 KP=120u)
    V1 in 0 DC 1
    R1 in a 1k
    R2 a  b 2k
    M1 b in 0 0 NM W=10u L=1u
    M2 a in 0 0 NM W=4u  L=1u
    .tran 1u 2u
    .control
      run
      show r.*                    $ every resistor (all default params)
      show m.* : vgs vds gm id    $ every MOSFET, only these four params
    .endc
    .end

         device                    r2                    r1
     resistance                  2000                  1000
         device                    m2                    m1
            vgs                     1                     1
            vds                0.9244                0.8164
             gm              0.000144               0.00036
             id              2.16e-05               5.4e-05

On the two_balance chip, `show m.x1.* : id vgs vds gm` reads back all seven MOSFETs of instance X1 with four columns each. (Regex device selection is an extension in this build; the `: param` selection is standard ngspice.)

See also `@dev[param]`, `.model`, `display`.

### `diff` — compare two plots ui only

Synopsis

    diff  plot1  plot2  [ vec … ]

Description

Compares two saved plots vector by vector and reports only the entries that differ by more than the tolerances (`set diff_abstol`/`diff_reltol`/`diff_vntol`). The quick way to see what a parameter change actually moved.

Example
diff.cir — run: `ngspice -b diff.cir`

    V1 in 0 DC 1
    R1 in out 1k
    R2 out 0 1k
    .op
    .control
      run                    $ -> plot op1 : v(out) = 0.5
      alter r2 2k
      run                    $ -> plot op2 : v(out) = 0.667
      diff op1 op2
    .endc
    .end

    op1.out[0] = 5.000000e-01    op2.out[0] = 6.666667e-01

Each `run` stores a plot (`op1`, `op2`); `diff` then prints exactly the node that changed when R2 doubled — the divider output moved from 0.5 to 0.667 ✓.

See also `setplot`, `alter`, `display`.

### `display` — list vectors in the current plot ui only

Synopsis

    display [ name | keyword | regexp … ]

Description

Shows the status (not the values) of the vectors in the current plot: each line is `name : type, real/complex, length`, with the scale tagged `[default scale]`. The quickest way to see what a run produced or what an analysis created (e.g. the sensitivity vectors of `.sens`). For the numbers use `print`; for a waveform, `plot`. It reports the current plot only — `setplot` first to inspect another.

Selecting what to list

argument | lists
--- | ---
(none) | every vector in the current plot (alphabetical)
`v(out) i(v1) …` | just the named vectors
`all` · `allv` · `alli` · `ally` · `alle` | all vectors / voltages / currents / other / events
`<regexp>` | every name matching a regular expression (see below)

The variable `set nosort` lists in internal (creation) order instead of alphabetical.

Example — list by type, and by regex
display.cir — run: `ngspice -b display.cir`

    V1 in 0 SIN(0 1 1k)
    Vdd vdd 0 DC 5
    R1 in out 1k
    C1 out 0 100n
    Rl out vdd 2k
    .tran 20u 1m
    .control
      run
      display allv        $ voltages only
      display .*branch    $ regex: every branch current
    .endc
    .end

        in         : voltage, real, 59 long      <- display allv
        out        : voltage, real, 59 long
        vdd        : voltage, real, 59 long
        v1#branch  : current, real, 59 long      <- display .*branch
        vdd#branch : current, real, 59 long

An argument that isn’t an exact name (or a `v()`/`i()` form or an `all*` keyword) and contains regex metacharacters is treated as a POSIX extended, case-insensitive, grep-style regular expression matched against vector names: `display .*branch` → all branch currents, `display ^v` → everything starting with `v`, `display o.*t` → `out`. Plain names still match exactly. (Regex selection is an extension in this build; stock ngspice-46 accepts only names and the `all*` keywords.)

Example — a hierarchical circuit (where regex earns its keep)
regex_hier.cir — run: `ngspice -b regex_hier.cir`

    * two instances X1, X2 of a stage -> internal nodes x1.n1, x2.n1, ...
    Vin in 0 DC 3
    X1 in  mid stage
    X2 mid out stage
    RL out 0 1k
    .subckt stage a z
      R1 a n1 1k
      R2 n1 n2 1k
      R3 n2 z 1k
    .ends
    .op
    .control
      run
      display x1.*        $ only X1's internal nodes
      print "x1.*"        $ their voltages (print needs the quotes)
      unlet x2.*          $ drop X2's internals
      display .*n1        $ x2.n1 is now gone
    .endc
    .end

        x1.n1               : voltage, real, 1 long        <- display x1.*
        x1.n2               : voltage, real, 1 long
    x1.n1 = 2.571429e+00                                   <- print "x1.*"
    x1.n2 = 2.142857e+00
        x1.n1               : voltage, real, 1 long        <- display .*n1 (only x1.n1 left)

On a real design the subcircuit-internal nodes number in the dozens — the two-stage balanced amplifier in `user_examples/two_balance.cir` exposes `x1.a`, `x1.b`, `x1.vp`, `x1.vcm`, `x2.…` and the per-device `@m.x1.m7[…]` currents. There, `display x1.*` lists just one instance’s nodes and `print "x1.*"` dumps them — regex turns “what’s inside X1?” into one line.

Note — two red herrings on hierarchical nodes. (1) A batch `.op` prints an operating-point device dump at the end of the run; that is the `.op` card’s own output, not anything `print`/regex did. (2) `print "x1.*"` silently skips zero-length vectors — e.g. the empty `@r.x1.r1[i]` placeholders that `.option savecurrents` leaves in an AC plot — so you get the real node voltages instead of a “vector not available” warning. Use `^x1\.` to match only names starting with `x1.` (excludes the `@…` device-current vectors entirely).
See also `setplot`, `print`, `plot`, `let`.

### `.disto` — small-signal distortion analysis batch + ui

Synopsis

    .disto  dec|oct|lin  n  fstart fstop  [ f2overf1 ]

Description

Computes steady-state harmonic (and, with `f2overf1`, intermodulation) distortion from the device small-signal series. Stimulus comes from sources carrying a `DISTOF1`/`DISTOF2` spec. Results land in two plots: `disto1` (2nd-order) and `disto2` (3rd-order); the node vectors are the distortion products.

disto.cir — run: `ngspice -b disto.cir`

    .model DMOD D(IS=1e-14)
    V1 in 0 DC 0.6 AC 1 DISTOF1 1
    R1 in a 1k
    D1 a 0 DMOD
    .disto dec 5 1k 100k
    .control
      run
      setplot disto1
      let hd2 = mag(a)
      print hd2[0]
      setplot disto2
      let hd3 = mag(a)
      print hd3[0]
    .endc
    .end

    hd2[0] = 1.044775e+00      (2nd-harmonic product at node a)
    hd3[0] = 2.018926e+00      (3rd-harmonic product)

Note. Only devices that implement the distortion model (diodes, BJTs, MOSFETs) contribute; a purely linear circuit yields zero. Results are raw product magnitudes, not a normalized THD — for THD from a large-signal run use `.four`.
See also `.four`, `.ac`, `setplot`.

### `echo` — print text ui only

Synopsis

    echo [ stuff … ]

Description

Prints its arguments, expanding shell variables (`$var`). It does not evaluate vectors or expressions — use `$&vec` for a vector’s value, or `print` for expressions.

echo.cir — run: `ngspice -b echo.cir`

    .control
      set name = world
      let x = 6*7
      echo "hello" $name       $ shell variable
      echo "x =" $&x           $ vector value
    .endc

    hello world
    x = 42

See also `print`, `set`.

### `fft` — FFT of a transient vector ui only

Synopsis

    fft  vector …

Description

After a `.tran` run, computes the FFT of each vector into a new spectrum plot (frequency resolution = 1/timespan). Use `mag()`/`db()` on the complex result. A windowed, more controllable alternative to `.four`; window via `set specwindow=…`.

Example — two-tone spectrum
fftex.cir — run: `ngspice -b fftex.cir`

    B1 s 0 V = sin(2*3.14159*1k*time) + 0.5*sin(2*3.14159*3k*time)
    R1 s 0 1k
    .tran 5u 10m
    .control
      run
      fft v(s)
      let m = mag(v(s))
      meas sp a1k MAX m FROM=0.9k TO=1.1k
      meas sp a3k MAX m FROM=2.9k TO=3.1k
    .endc
    .end

    FFT: Frequency resolution: 99.957 Hz, output length: 1005
    a1k =  9.95417e-01  at=  9.99570e+02
    a3k =  4.93659e-01  at=  2.99871e+03

Both tones are recovered at 1.0 / 3.0 kHz with a 2.01 : 1 amplitude ratio — matching the 1 : 0.5 input ✓.

Gotcha — `meas` needs a real vector. `meas sp … mag(v(s))` fails (“no such vector as mag(v(s))”): `meas` can’t take a function call inline. Materialize it first with `let m = mag(v(s))`, then measure `m`.
Example — `psd`: power spectral density
psd.cir — run: `ngspice -b psd.cir`

    B1 s 0 V = sin(2*3.14159*1k*time)
    R1 s 0 1k
    .tran 5u 10m
    .control
      run
      psd 1 v(s)             $ 1 = averaging length; result overwrites v(s)
      let p = db(v(s))
      meas sp pk MAX p
    .endc
    .end

    PSD: Frequency resolution: 100 Hz, output length: 1005
    pk                  =  -4.60992e+01 at=  1.00000e+03

`psd len vec…` is a close cousin of `fft` that returns the power spectral density (magnitude squared, optionally smoothed over `len` bins). The peak lands on the 1 kHz tone ✓. Use `fft` when you want the complex spectrum, `psd` when you want power.

See also `.four`, `spec`, `meas`.

### `fopen` / `fread` / `fclose` — read a file line by line ui only

Synopsis

    fopen  handle file [mode]     $ open; handle name in $handle-var
    fread  handle var [len]       $ read a line into $var; status in $len-var
    fclose handle

Description

Line-oriented file reading for `.control` scripts (parameter tables, external stimuli).

Batch limitation. On this build `fread` can block indefinitely under `ngspice -b` (no interactive input pump). Reliable interactively; for batch pipelines read the file with an external tool and pass values in via `.param`/`.csparam`.

### `foreach` — iterate over a word list ui only

Synopsis

    foreach  var  word1 word2 …
      … body (use $var) …
    end

Description

Runs the body once per word, with `var` set to each word in turn (words are literal text — values, node names, filenames). The most common way to repeat a run over a set of parameter values.

foreach.cir — run: `ngspice -b foreach.cir`

    .control
      foreach r 1k 2k 4k
        echo "R = $r"
      end
    .endc
    .end

    R = 1k
    R = 2k
    R = 4k

foreach_set.cir — iterate a `set` list variable; run: `ngspice -b foreach_set.cir`

    .control
      set rvals = ( 1k 2k 4k )
      foreach r $rvals        $ walk the list held in a variable, not literal words
        echo "R = $r"
      end
    .endc
    .end

    R = 1000
    R = 2000
    R = 4000

The word list can come from a `set` list variable (`$rvals`) rather than being typed inline — handy when the values are built elsewhere. Note the suffixes are evaluated to plain numbers (`1k`→`1000`) when stored in the variable.

See also `while`, `repeat`, `.dc`.

### `.four` / `fourier` — Fourier analysis of a transient batch + ui

Synopsis

    .four  freq  output_variable ...

Description

After a `.tran` run, computes the DC component, fundamental (at `freq`), and harmonics 2–9 of each output, plus total harmonic distortion (THD). Operates on the last period of the transient waveform.

Example — harmonics of a half-wave rectifier
four.cir — run: `ngspice -b four.cir`

    * Half-wave rectifier -> .four shows strong harmonics
    Vs in 0 SIN(0 1 1k)
    R1 in a 50
    D1 a out DMOD
    Rout out 0 1k
    .model DMOD D(IS=1e-14 N=1)
    .tran 2u 20m
    .four 1k v(out)
    .end

    Fourier analysis for v(out):
      No. Harmonics: 10, THD: 87.7379 %, Gridsize: 200, ...
     Harmonic  Frequency  Magnitude   Norm. Mag
     0         0          0.0703844   0
     1         1000       0.128272    1
     2         2000       0.0956183   0.745433
     3         3000       0.0548493   0.427601
     4         4000       0.0192777   0.150288

Half-wave rectification is strongly nonlinear → THD ≈ 88 %, with a large 2nd harmonic (74 % of the fundamental) — exactly the even-harmonic-rich spectrum expected.

Gotcha — sampling & period matter. `.four` resamples the last period onto a fixed grid; too coarse a `.tran` `tstep`, or a `tstop` that isn’t an integer number of periods of `freq`, inflates the reported THD with interpolation error. Use a small `tstep` and a `tstop` that is a whole multiple of 1/`freq`.
Example — `fourier`: the on-demand `.control` form
four_ctrl.cir — run: `ngspice -b four_ctrl.cir`

    Vs in 0 SIN(0 1 1k)
    R1 in a 50
    D1 a out DMOD
    Rout out 0 1k
    .model DMOD D(IS=1e-14 N=1)
    .tran 2u 20m
    .control
      run
      fourier 1k v(out)      $ same maths as .four, run when you choose
    .endc
    .end

      No. Harmonics: 10, THD: 87.7379 %, Gridsize: 200, ...

The bare command `fourier freq vec…` is the interactive twin of the `.four` card: identical result, but computed on demand inside `.control` (e.g. after an `alter`, or on a vector you just built with `let`) rather than automatically at end of run.

See also `.tran`, `fft`, `spec`.

### `.global` — global nodes batch + ui

Synopsis

    .global  node …

Description

Declares nodes that are the same net everywhere — reachable inside any subcircuit without being wired through its ports. The usual candidates are supplies and the substrate (`vdd`, `gnd`, `vss`). Ground node `0` is always global.

global.cir — run: `ngspice -b global.cir`

    .global vdd
    Vdd vdd 0 DC 5
    Vin in 0 DC 1
    X1 in out amp
    .subckt amp i o
      R1 i o 1k
      R2 o vdd 3k        $ vdd here is the global node, not a port
    .ends
    .tran 1u 5u
    .control
      run
      print v(out)[0]    $ 2.0
    .endc
    .end

    v(out)[0] = 2.000000e+00

The subckt reaches `vdd` without it being a port; v(out) settles where the two dividers balance: (1/1k + 5/3k)/(1/1k + 1/3k) = 2.0 V ✓.

See also `.subckt`.

### `goto` / `label` — control-flow jump ui only

Synopsis

    label name        goto name

Description

An unstructured jump within a `.control` block. Prefer the structured loops (`foreach`/`while`/`repeat`); `goto` is for the occasional escape or a small state machine.

goto.cir — run: `ngspice -b goto.cir`

    .control
      let i = 0
      label top
      echo "i =" $&i
      let i = i + 1
      if i < 3
        goto top
      end
    .endc

    i = 0
    i = 1
    i = 2

See also `while`, `foreach`.

### `.ic` / `.nodeset` — initial node conditions batch + ui

Synopsis

    .ic       v(node) = value …      $ forced initial value (with .tran uic)
    .nodeset  v(node) = value …      $ hint for the DC operating-point solver

Description

`.ic` forces a node to a value at the start of a transient run when the analysis uses `uic`. `.nodeset` only seeds the DC solver (to pick between multiple solutions or aid convergence) and is released once DC converges — it does not force a transient start value.

Example — pre-charged capacitor decays
ic.cir — run: `ngspice -b ic.cir`

    V1 in 0 DC 0
    R1 in out 1k
    C1 out 0 1u
    .ic v(out)=1
    .tran 10u 5m uic
    .control
      run
      print v(out)[0]                   $ 1.0 (the initial condition)
      meas tran vend FIND v(out) AT=5m  $ decays ~ e^-5
    .endc
    .end

    v(out)[0] = 9.999000e-01
    vend      = 6.73767e-03

C starts at 1 V and discharges through R (τ=RC=1 ms); at 5 ms = 5τ it is 1·e−5 = 6.7 mV ✓.

Gotcha — `.ic` needs `uic`. Without `uic` on the `.tran` line, ngspice computes a normal DC operating point and the `.ic` values are ignored (they only bias the initial guess). `.nodeset` never forces a transient start.
See also `.tran` (`uic`), `.op`.

### `.if` / `.elseif` / `.else` / `.endif` — conditional netlist batch + ui

Synopsis

    .if(expr)
      … cards …
    .elseif(expr)
      … cards …
    .else
      … cards …
    .endif

Description

Includes netlist cards conditionally at parse time, based on a parameter expression — corners, options, or variant topologies selected from one deck.

ifelse.cir — run: `ngspice -b ifelse.cir`

    .param mode=2
    .if (mode==1)
      R1 in 0 1k
    .else
      R1 in 0 4k
    .endif
    V1 in 0 DC 1
    .op
    .control
      run
      print @R1[resistance]     $ 4k (the else branch, since mode=2)
    .endc
    .end

    @r1[resistance] = 4.000000e+03

See also `.param`, `foreach`.

### `.include` / `.lib` — pull in other files batch + ui

Synopsis

    .include  filename
    .lib      filename  section

Description

`.include` textually inserts another file (parameters, subcircuits, models). `.lib file section` pulls only a named section — the standard way PDKs ship process corners.

include.cir + params.inc — run: `ngspice -b include.cir`

    * params.inc:   .param vval=5 rval=2k
    .include params.inc
    V1 in 0 DC {vval}
    R1 in out {rval}
    R2 out 0 {rval}
    .tran 1u 5u
    .control
      run
      print v(in)[0]      $ 5  (from the included vval)
      print v(out)[0]     $ 2.5
    .endc
    .end

    v(in)[0] = 5.000000e+00
    v(out)[0] = 2.500000e+00

See also `.param`, `.ic`, `.temp`. Related structural cards not yet detailed: `.global`, `.option`, `.csparam`, `.model`.

### `let` / `print` — vectors and expressions ui only

Synopsis

    let  name = expression      $ create/assign a vector
    print  expression …         $ evaluate and show

Description

`let` creates or overwrites a vector from an expression over existing vectors and constants; `print` evaluates any expression (arithmetic, node voltages `v(n)`, branch currents `i(vs)`, functions). Operators include `+ - * /`, `%` (mod), comparisons, and many functions (`sqrt`, `abs`, `db`, …).

vectors.cir — run: `ngspice -b vectors.cir`

    V1 in 0 DC 2
    R1 in out 1k
    R2 out 0 1k
    .op
    .control
      run
      let x = 2 + 3
      print x                  $ 5
      print v(out)             $ 1  (2 V divided by two equal Rs)
      print @R1[resistance]    $ 1000  (device parameter)
    .endc
    .end

    x = 5.000000e+00
    v(out) = 1.000000e+00
    @r1[resistance] = 1.000000e+03

See also `set`, `define`, `@dev[param]`.

### `linearize` — resample onto a uniform time grid ui only

Synopsis

    linearize [ vec … ]

Description

A transient runs on an adaptive timestep, so its samples are unevenly spaced. `linearize` interpolates the vectors onto a uniform grid (step = the `.tran` `tstep`). Required before operations that assume even spacing — notably `fft` — and handy for exporting clean columns with `wrdata`.

linearize.cir — run: `ngspice -b linearize.cir`

    V1 in 0 SIN(0 1 1k)
    R1 in out 1k
    C1 out 0 100n
    .tran 10u 1m
    .control
      run
      let n1 = length(v(out))
      echo "raw (adaptive) points:" $&n1
      linearize
      let n2 = length(v(out))
      echo "linearized points:" $&n2
    .endc
    .end

    raw (adaptive) points: 112
    linearized points: 101

The adaptive run used 112 unevenly-spaced points; `linearize` resamples to 101 uniform points (1 ms / 10 µs + 1) ✓.

See also `fft`, `wrdata`.

### `listing` — show the parsed circuit ui only

Synopsis

    listing [ logical | physical | deck | expand | runnable ]

Description

Prints the current circuit as ngspice sees it after parsing — useful to confirm parameter substitution, subcircuit expansion (`expand`), or the exact runnable deck (`runnable`).

listing.cir — run: `ngspice -b listing.cir`

    V1 in 0 DC 1
    R1 in out 1k
    R2 out 0 1k
    .op
    .control
      listing
    .endc
    .end

         3 : v1 in 0 dc 1
         4 : r1 in out 1k
         5 : r2 out 0 1k

See also `source`, `edit`.

### `load` — read a raw file back ui only

Synopsis

    load [ rawfile … ]

Description

Reads a raw file (from `-r` or `write`) into a new plot, so an earlier run’s data can be re-examined, compared, or post-processed without re-simulating.

load.cir — run: `ngspice -b load.cir`

    .control
      run
      write /tmp/saved.raw v(out)
      destroy all
      load /tmp/saved.raw       $ data is back — no re-run
      print v(out)[0]
    .endc

    v(out)[0] = 0.000000e+00

See also `write`, `setplot`.

### `.meas` / `meas` — extract figures of merit batch + ui

Synopsis

    .meas  analysis  result  FIND expr  AT=t | WHEN expr=val
    .meas  analysis  result  MAX|MIN|AVG|RMS|PP|INTEG  expr  [FROM=t TO=t]
    .meas  analysis  result  TRIG expr VAL=a RISE=n  TARG expr VAL=b RISE=n
    .meas  analysis  result  PARAM='expr'

Control-mode: same, without the dot (`meas tran …`) — except `PARAM='expr'`, which is dot-card only (see the table below). `analysis` is `tran`, `ac`, `dc`, or `sp`.
Description

ngspice’s measurement command — the analog of SPICE `.measure` / Eldo `.EXTRACT`. It reduces an analysis result to a named scalar you can print, reuse in a later `.meas`, or feed to a script: peak/trough (`MAX`/`MIN`), average/RMS/peak-to-peak/integral over a window, a value at a time or when a condition holds (`FIND … AT`/`WHEN`), and delays/rise-times (`TRIG … TARG`).

`.meas` vs. `meas` — same engine, different role

The dot-card and the control-mode command share one measurement engine: the grammar above (`TRIG/TARG`, `FIND/WHEN`, `MAX/MIN/PP/AVG/RMS/INTEG`) and the numbers are identical either way. They are not fully interchangeable, though:

| `.meas` (dot-card) | `meas` (in `.control`)
--- | --- | ---
when it runs | automatically at the end of its analysis | on demand, after you call `run`
result reusable? | prints only — not exposed as a vector (`print name` afterward fails) | stores a vector you can compute with (`meas … vpk MAX …` then `let d = vpk*2`)
can measure derived vectors (from `let`/`fft`) | no — only the analysis output | yes (e.g. `let m=mag(v(s))` then `meas sp … MAX m`)
`PARAM='expr'` type | yes | no — rejected (“no such function as ‘param=’”); use `let`
`.options autostop` (end run early once met) | yes — shortens the transient | no — the run has already finished
needs `.control`? | no — works in a plain batch deck | yes

Rule of thumb: if the number is the end product (a batch/regression figure of merit, or you want `autostop`) use `.meas`; if the number is an input to more work — a `let` expression, a second measurement, or measuring a vector you built yourself — use the control-mode `meas`. The Q example below relies on exactly that reuse.

Chaining works only within one mode. A measurement can reference an earlier result of the same kind: `.meas`→`.meas` via the `PARAM=` type (`.meas tran vpk2 param='vpk*2'`), and control `meas`→`meas` because each result is a stored vector (`let scaled = vpk*2`). But a `.meas` result cannot be used by a control-mode `meas` — it is print-only and never enters the interactive namespace (`let`/`$var`/`meas … WHEN =vpk` all fail with “vpk not available”). If you need a measured value inside a script, compute it with the control `meas` after `run`, not with `.meas`.
Example — series-RLC step (underdamped)
meas.cir — run: `ngspice -b meas.cir`

    V1 in 0 PULSE(0 1 0 1n 1n 1 2)
    R1 in a 20
    L1 a out 1m
    C1 out 0 1u
    .tran 1u 1m
    .control
      run
      meas tran vpeak  MAX  v(out)
      meas tran vfinal FIND v(out) AT=1m
      meas tran vavg   AVG  v(out) FROM=0.6m TO=1m
      meas tran vrms   RMS  v(out) FROM=0 TO=1m
      meas tran trise  TRIG v(out) VAL=0.1 RISE=1 TARG v(out) VAL=0.9 RISE=1
    .endc
    .end

    vpeak  =  1.35098e+00  at=  1.04838e-04
    vfinal =  1.00001e+00
    vavg   =  9.99799e-01  from=6.0e-04 to=1.0e-03
    vrms   =  9.97501e-01  from=0.0     to=1.0e-03
    trise  =  4.24622e-05  targ=5.74e-05 trig=1.50e-05

Damping ζ = (R/2)·√(C/L) = 0.316 → overshoot e−πζ/√(1−ζ²) ≈ 0.35, so the peak is 1.35 V ✓, reached at 105 µs (≈ half the ringing period), and the output settles to 1.0 V.

Example — extracting the Q of a resonator from its −3 dB bandwidth
q_resonator.cir — run: `ngspice -b q_resonator.cir`

    * parallel RLC tank: Q = R*sqrt(C/L) ; here R=316.23, L=1u, C=1n -> Q=10
    Iac in 0 AC 1
    R1 in 0 316.23
    L1 in 0 1u
    C1 in 0 1n
    .ac dec 2000 1e6 30e6
    .control
      run
      meas ac zmax MAX vm(in)              $ peak impedance = R, at fo
      let thr = zmax/sqrt(2)               $ -3 dB level
      meas ac flo WHEN vm(in)=thr RISE=1   $ lower skirt
      meas ac fhi WHEN vm(in)=thr FALL=1   $ upper skirt
      let Q = sqrt(flo*fhi)/(fhi-flo)      $ Q = fo / bandwidth
      print Q
    .endc
    .end

    zmax                =  3.16211e+02 at=  5.03567e+06
    flo                 =  4.78753e+06
    fhi                 =  5.29089e+06
    q = 9.998790e+00

A current source drives the tank, so `vm(in)` is its impedance: it peaks at R at the resonance fo, and the two frequencies where it falls to R/√2 bound the bandwidth. Combining `meas` (peak + both skirts) with `let` yields Q = fo/BW = 10, matching R·√(C/L) ✓. The `RISE=1`/`FALL=1` qualifiers pick the lower and upper crossings.

See also `.tran`, `print`, `.four`.

### `.model` — named device model batch + ui

Synopsis

    .model  name  type ( param=value … )

`type` is a device kind: `D`, `NPN`/`PNP`, `NMOS`/`PMOS`, `R`, `C`, `SW`, …
Description

Defines a named parameter set shared by every instance that references it. The instance card names the model in its model position (e.g. `D1 a k MYDIODE`).

model.cir — run: `ngspice -b model.cir`

    .model MYDIODE D(IS=1e-14 N=1.2 RS=5)
    V1 a 0 DC 0.7
    D1 a 0 MYDIODE
    .tran 1u 5u
    .control
      run
      print -i(v1)[0]        $ 61.7 uA
    .endc
    .end

    -i(v1)[0] = 6.171025e-05

Example — an NPN transistor model, recovering β
model_bjt.cir — run: `ngspice -b model_bjt.cir`

    * force 10uA base current, read back beta = Ic/Ib
    .model MYNPN NPN(IS=1e-16 BF=100 VAF=80)
    IB 0 b DC 10u
    Q1 c b 0 MYNPN
    VC c 0 DC 5
    .op
    .control
      run
      let beta = -i(vc)/10u      $ negate: current is into the VC source
      print beta
    .endc
    .end

    beta = 1.052822e+02

β comes out just above the model’s `BF=100`: the Early effect (`VAF=80`) lifts it to BF·(1+VCE/VAF) ≈ 106 at VCE=5 V ✓.

Example — an NMOS model, recovering the square-law current
model_mos.cir — run: `ngspice -b model_mos.cir`

    * LEVEL-1 NMOS in saturation, drain current from KP/VTO
    .model NMOS1 NMOS(LEVEL=1 VTO=0.7 KP=120u LAMBDA=0)
    M1 d g 0 0 NMOS1 W=10u L=1u    $ drain gate source bulk model
    VG g 0 DC 1.7
    VD d 0 DC 3
    .op
    .control
      run
      let id = -i(vd)               $ negate: current is into VD
      print id
    .endc
    .end

    id = 6.000000e-04

The `M` card names four nodes (drain, gate, source, bulk) then the model, with `W`/`L` per-instance. With VGS=1.7, VDS=3 (saturated) and `LAMBDA=0`, ID = ½·KP·(W/L)·(VGS−VTO)² = ½·120 µ·10·(1.0)² = 600 µA ✓.

See also `altermod` (change a model at runtime), OSDI.

### `.noise` — noise analysis batch + ui

Synopsis

    .noise  v(output [,ref])  input_source  dec|oct|lin  n  fstart  fstop  [ pts_per_summary ]

Description

Computes output-referred and input-referred noise spectral density vs. frequency (vectors `onoise_spectrum`, `inoise_spectrum`), and their integrals over the band (scalars `onoise_total`, `inoise_total`, in V and V/√Hz-integrated → V rms).

Example — the kT/C result
noise.cir — run: `ngspice -b noise.cir`

    * Output noise of RC low-pass -> integrates to sqrt(kT/C)
    V1 in 0 DC 0 AC 1
    R1 in out 1k
    C1 out 0 100n
    .noise v(out) V1 dec 100 0.1 100meg
    .control
      run
      print onoise_total
    .endc
    .end

    onoise_total = 2.035612e-07

The resistor’s thermal noise, low-pass filtered by C, integrates to the classic √(kT/C) = √(1.38e-23·300/100 nF) = 2.03e-7 V rms — independent of R ✓. A textbook result the tool reproduces to three digits.

Example — reading the spectral density directly
noise_spec.cir — run: `ngspice -b noise_spec.cir`

    * thermal floor of a 1k resistor = sqrt(4kTR) = 4.07 nV/rtHz, flat
    V1 in 0 DC 0 AC 1
    R1 in out 1k
    C1 out 0 1p
    .noise v(out) V1 dec 10 1 1k
    .control
      run
      setplot noise1              $ spectra live in noise1, totals in noise2
      print onoise_spectrum[0]
    .endc
    .end

    onoise_spectrum[0] = 4.071372e-09

√(4kTR) = √(4·1.38e-23·300·1 kΩ) = 4.07 nV/√Hz ✓.

Gotcha — two plots, and the density is already per √Hz. `.noise` makes noise1 (the `*_spectrum` vectors vs. frequency) and noise2 (the scalar `*_total`). After `run` the current plot is noise2, so `setplot noise1` first to reach a spectrum. And `onoise_spectrum` is stored in V/√Hz, not V²/Hz — don’t take a second square root.
See also `.ac`.

### `.op` — DC operating point batch + ui

Synopsis

    .op

Control-mode: `op`.
Description

Solves the quiescent DC bias: all capacitors open, all inductors shorted, sources at their DC value. Prints every node voltage and source current, and (in interactive mode) makes the small-signal device operating-point parameters available. It is the starting point of `.tran` (unless `uic`) and `.ac`.

Example — resistive divider
op.cir — run: `ngspice -b op.cir`

    * DC operating point of a resistive divider
    V1 in 0 DC 1
    R1 in mid 1k
    R2 mid 0 1k
    .op
    .end

    Node                                   Voltage
    in                               1.000000e+00
    mid                              5.000000e-01

v(mid) = 1 V · R2/(R1+R2) = 0.5 V ✓.

See also `.dc`, `.tf`, `.tran`.

### `.option` / `.options` — simulator options batch + ui

Synopsis

    .option  name[=value] …

Description

Sets tolerances, temperature, integration method, and output behavior. The most common:

option | default | effect
--- | --- | ---
`reltol` | 1e-3 | relative convergence/LTE tolerance
`abstol` / `vntol` | 1e-12 / 1e-6 | absolute current / voltage tolerance
`trtol` / `chgtol` | 7 / 1e-14 | transient LTE / charge tolerance (see the accuracy note)
`temp` / `tnom` | 27 / 27 | analysis / nominal temperature (°C)
`method` | trap | integration: `trap` or `gear`
`bypass` | 0 | device-eval bypass — leave off for precision analog
`savecurrents` | off | store branch currents (readable as `@dev[i]`)
`numdgt` | 6 | digits in printed output
`itl1` / `itl2` | 100 / 50 | Newton iteration limit for the DC operating point / the DC transfer curve
`itl4` | 10 | Newton iteration limit at one transient timepoint; exceeding it cuts the timestep
`maxiterfloor` | 100 | lower bound the solver forces on `itl1`/`itl2`/`itl4` — set to 0 to have them obeyed (see below)
`minres` | 0 (off) | parasitic reduction: short resistors below this value by merging their two nodes (ground and source terminals are protected)
`mincap` | 0 (off) | parasitic reduction: delete capacitors below this value
`gmin` | 1e-12 S | conductance added across every nonlinear device junction (the classic SPICE convergence aid)
`floatgmin` | 1e-12 S | conductance tying floating (fewer than two terminals) nodes to ground; `=0` disables
`rshunt` | off | resistance from every node to ground — a blunt instrument; see the gotcha below
`pstran` / `dptran` | off | last-resort pseudo-transient continuation for an operating point that Newton and the fallback methods cannot reach; see below

option.cir — run: `ngspice -b option.cir`

    .options savecurrents
    V1 in 0 DC 1
    R1 in mid 1k
    R2 mid 0 1k
    .op
    .control
      run
      print @R1[i]        $ (1 - 0.5)/1k = 0.5 mA
      print @R2[i]
    .endc
    .end

    @r1[i] = 5.000000e-04
    @r2[i] = 5.000000e-04

Iteration limits and the escalation ladder — `itl1`, `itl4`, `maxiterfloor`

When Newton–Raphson fails to converge, ngspice does not simply give up: it escalates through a ladder of progressively more robust (and more expensive) methods — dynamic gmin stepping, true gmin stepping, source stepping, and finally `optran`/pseudo-transient. The `itl` options decide how long it flails at Newton before moving on, and the whole value of a fallback ladder is reaching it early.

The catch: for decades the solver silently raised any of these limits to a floor of 100. That made `itl4` (default 10) and `itl2` (default 50) inoperative at their own documented defaults, and made a user-supplied `itl1` below 100 a no-op. On a small circuit this is invisible: every timepoint converges in two or three iterations, so the limit is never reached and it cannot matter — which is exactly why it went unnoticed, and why no regression test catches it: every circuit in the suite converges. It becomes visible only on a deck that fails to converge, where the solver then burns the full 100 iterations — ten times the budget `itl4` asked for — at every rejected timestep, before it will consider a method that might actually work.

`maxiterfloor` exposes that floor. It defaults to 100, so every existing deck behaves exactly as before; set `maxiterfloor=0` and `itl1`/`itl2`/`itl4` mean what the table says.

Example — making `itl1` real
iterlimit.cir — run: `ngspice -b iterlimit.cir`

    .option itl1=3            $ ask Newton to give up after 3 iterations
    .option maxiterfloor=0    $ ... and actually mean it
    .control
      set iter_verbose
      op
    .endc

      NIiter: iteration 1 of at most 3  (0.00 s)
      NIiter: iteration 2 of at most 3  (0.00 s)
      NIiter: iteration 3 of at most 3  (0.00 s)
      NIiter: iteration 4 of at most 3  (0.00 s)
    Note: Newton iteration failed, escalating to fallback OP methods
    Note: Starting dynamic gmin stepping
      NIiter: iteration 1 of at most 50  (0.00 s)

Newton is abandoned after 3 iterations and the ladder takes over. Delete the `maxiterfloor=0` line and the very same deck prints `iteration 1 of at most 100`: `itl1=3` is discarded.

`set iter_verbose` (a front-end variable, not an option) is what produces those lines. It reports every Newton iteration with its wall-clock cost, flushed immediately — without it a long solve is completely silent, and there is no way to tell a slow iteration from a hang, nor to measure what an iteration actually costs on a big circuit.

Gotcha — lifting the floor is not free. The floor was introduced as a robustness band-aid (“some convergence issues get resolved by increasing max iter”), and that is sometimes true: with 100 iterations allowed, a transient timepoint that would have been rejected at `itl4`=10 may instead converge at, say, iteration 30 and be accepted. So `maxiterfloor=0` does not only change speed — it can change which timesteps are accepted, and therefore the results. Use it when you want to fail fast and reach the fallback methods (large extracted decks, a DC point that will clearly never converge); leave it alone for ordinary work. The defect it fixes is not that the floor exists — it is that `itl1`/`itl2`/`itl4` were silently untruthful, with no way to opt out.
Reduction and floating-node options — `minres`, `mincap`, `floatgmin`

These are ordinary options — use them on any deck when you want the behaviour they describe. They earn their keep most often on a post-layout (extracted) netlist, because extraction produces things a hand-written circuit rarely has — millions of parasitic elements, resistors of 0 Ω, capacitors of 0 F, and nodes that nothing actually connects to — but nothing about them is specific to extracted decks. The three answer three different problems.

option | the problem it solves | when to reach for it
--- | --- | ---
`minres` | Size. Extraction emits near-zero resistors that carry no information but each add a node and a matrix row. `minres` shorts them by merging their two nodes. | The deck is bigger than it needs to be. Measure first: on many decks the count of sub-milliohm resistors is tiny and this changes nothing.
`mincap` | Size. Same idea for negligible capacitors, which are simply deleted. | As above. Both are reduction levers, not convergence levers.
`floatgmin` | Convergence — one specific cause. A node that fewer than two device terminals reach cannot carry current, so at DC it has no reference: its matrix row is empty and the matrix is rank-deficient. The solver reports a singular matrix — at an arbitrary node, not necessarily the guilty one. `floatgmin` ties exactly those degree-<2 nodes to ground. | On by default (1e-12 S). It handles the dangling-node case automatically; you touch it only to disable it (`=0`) or to see what it caught.

The mechanism, concretely. At DC a capacitor is an open circuit and a MOSFET gate draws no current, so a floating gate — a gate wired to nothing else — has no resistive path to anywhere. Its row in the conductance matrix is all zeros: nothing sets its voltage, the matrix loses rank, and the solver stops with singular matrix. `floatgmin` attaches a single conductance of 1e-12 S (a 1 TΩ resistor, the default) from that node to ground. That places one nonzero term on the node's diagonal, which is all the matrix needs: the row is no longer empty, the node acquires a DC reference and simply sits at 0 V. Because 1 TΩ passes essentially no current (a picoamp per volt), it changes nothing else in the circuit — it is a reference, not a load. The classic structures it rescues, both routine in extracted layout, are exactly this dangling gate and the MOS with only its bulk connected (drain, gate and source all going nowhere); commercial simulators prune such devices at elaboration, ngspice keeps them, so their nodes stay unreferenced until `floatgmin` supplies the path.

`floatgmin` is not a cure for “singular matrix”. It fixes exactly one cause — a node reachable by fewer than two device terminals — and on a deck whose only problem is dangling nodes it fixes it completely (the example below). But a singular matrix has other causes it cannot touch: an entire sub-network with no DC path to ground (for example a resistor island that only ever drives MOS gates, or a device-internal node behind a series resistor), two voltage sources in a loop, or plain ill-conditioning. Because the solver names an arbitrary node, you cannot tell which cause you have from the message alone. Treat `floatgmin` as the cheap first thing to try when a matrix goes singular and you do not yet know why — not as a guarantee. If it ties nodes (watch `set topo_verbose`) and the matrix is still singular, the deficiency is elsewhere and tying more nodes will not help; the honest next step is to find the sub-network that has no path to ground, or — if the circuit is solvable but merely hard to reach — to escalate to `pstran` below.
Example — a dangling gate and a bulk-only MOS
floatgmin.cir — run: `ngspice -b floatgmin.cir`

    * Floating nodes: a dangling MOS gate and a bulk-only MOS
    .model nm nmos level=1 vto=0.7 kp=100u
    V1 d 0 2
    R1 d dd 1k
    VG g 0 1.5
    M1 dd g 0 0 nm w=10u l=1u             $ working device, gate driven
    M2 dd floatg 0 0 nm w=10u l=1u        $ gate dangles -> node floatg floats
    M3 dead1 dead2 dead3 0 nm w=10u l=1u  $ only the bulk is connected
    .control
      set topo_verbose
      op
      print v(dd)
    .endc
    .end

      floating node: floatg
      floating node: dead1
      floating node: dead2
      floating node: dead3
    Topology reduction: 4 floating node(s) tied to ground through 1e-12 S (.option floatgmin).
    v(dd) = 1.680000e+00

The working device M1 solves normally. Add `.option floatgmin=0` to the same deck and every DC method fails in turn:

    Warning: singular matrix:  check node dead2
    Note: Newton iteration failed, escalating to fallback OP methods
    Warning: Dynamic gmin stepping failed
    Warning: True gmin stepping failed
    Warning: source stepping failed

`set topo_verbose` (a front-end variable, not an option) prints the offending node names and also writes them to `float_nodes.txt` — on a real extracted deck that file is how you find out which nets your extraction left dangling. Without it the reduction is silent.

Three conductances, three scopes. They are easy to confuse, and reaching for the wrong one is how a “convergence fix” silently corrupts an answer:

option | where it is applied | can it change a correct result?
--- | --- | ---
`gmin` | across every nonlinear device junction | negligibly — it is what makes SPICE converge at all
`floatgmin` | only on nodes that cannot carry current | no — no current can reach those nodes
`rshunt` | every node in the circuit | yes — it loads every high-impedance node you have

Gotcha — `floatgmin` is not `rshunt`. Both add a conductance to ground, and it is tempting to reach for `rshunt` when a big deck will not converge. Do not: `rshunt` shunts every node in the circuit, so it loads every genuinely high-impedance node you have — a bandgap reference, a sample-and-hold storage node, a comparator input — and quietly corrupts the answer. `floatgmin` is applied only to nodes that provably cannot carry current, so it cannot perturb a working circuit. If a singular-matrix warning names a node and moves to a different node when you change the shunt value, you have a structural problem, not a numerical one: find and fix the topology (`floatgmin` already handles the dangling-node case), do not turn up `rshunt`.
Reaching an operating point that Newton cannot — `pstran` / `dptran`

Not every hard operating point is a broken one. A circuit can be perfectly well-posed — a unique DC solution exists — and still defeat Newton–Raphson from a cold start: high-gain feedback loops (bandgaps, comparators, ring oscillators, latches) make the Newton map oscillate, and gmin- and source-stepping lose the path. For these, ngspice inherits the standard escalation ladder — Newton → gmin stepping → source stepping → `optran` — and `pstran` adds one more, final rung.

`pstran` is pseudo-transient continuation: it finds the DC point by pretending to run a transient and letting the circuit settle. It attaches an artificial capacitor `Cp` (`pstrancp`, default 1 nF) from every node to ground and steps forward in a fictitious “pseudo time” using backward Euler, with no truncation-error control — the trajectory is meaningless, only the resting state matters.

The mechanics are the useful part. At each pseudo step of size `dt`, the backward-Euler companion of each artificial capacitor puts a conductance g = Cp/dt on that node's matrix diagonal and a current source g·vprev (its voltage at the previous step) on the right-hand side. That added diagonal term is what buys the robustness: it makes each step's Jacobian diagonally dominant and well-conditioned, so Newton converges at that step even on a circuit where a cold-start Newton would oscillate or diverge. The step is solved, `vprev` is updated, and `dt` is grown aggressively (×2, then ×10 on repeated success). As `dt → ∞`, g = Cp/dt → 0: the artificial capacitor's conductance and its RHS current both vanish, the added terms disappear from the system, and what remains is the original circuit at steady state — the exact DC operating point. So it is a homotopy: it walks from an easy, heavily-damped problem (small `dt`, large `g`) to the real one (`g → 0`) in continuous steps, and each intermediate step is one Newton solve that actually converges. Physically it is the numerical form of bringing a bench supply up through a soft-start instead of slamming the rail on.

`dptran` is the damped variant: on top of the pseudo capacitor it also clamps how far any node voltage may move in a single Newton iteration (0.3 V), which stops a positive-feedback stage — a latch, a bandgap, a comparator — from overshooting past its solution and re-diverging. It trades more iterations for that safety; reach for it when plain `pstran` still will not settle.

option | default | effect
--- | --- | ---
`pstran` | off | enable pseudo-transient continuation as the last-resort OP method
`dptran` | off | damped pstran (implies `pstran`) — for positive-feedback circuits
`pstrancp` | 1n | artificial capacitance placed on every node
`pstrandtinit` | 1n | initial pseudo timestep
`pstrandtmax` | auto | final pseudo timestep (0 = auto, `pstrancp`/gmin)
`pstranmaxsteps` | 200 | maximum number of pseudo steps

pstran.cir — run: `ngspice -b pstran.cir`

    * pstran: a high-gain feedback ring that defeats Newton
    b1 n2 0 v = 0.9 - 0.89*tanh(200*(v(n1)-0.9))
    b2 n3 0 v = 0.9 - 0.89*tanh(200*(v(n2)-0.9))
    b3 n1 0 v = 0.9 - 0.89*tanh(200*(v(n3)-0.9))
    r1 n1 0 1meg
    .option pstran
    .control
      op
      print v(n1) v(n2) v(n3)
    .endc
    .end

    Note: Newton iteration failed, escalating to fallback OP methods
    Note: Starting dynamic gmin stepping        ... (fails)
    Note: Starting source stepping              ... (fails)
    Note: Starting pseudo-transient continuation (pstran)
    Note: Pseudo-transient continuation completed
    v(n1) = 9.000000e-01
    v(n2) = 9.000000e-01
    v(n3) = 9.000000e-01

Three high-gain `tanh` stages in a ring: Newton two-cycles on the steep map, gmin stepping cannot damp voltage-forced nodes, and source stepping loses the path near full value — yet the circuit has one honest solution (all nodes at 0.9 V), and `pstran` relaxes into it.

`pstran` is for hard, not for broken. It is a continuation method, so it can only reach a solution that exists. It cannot repair a genuinely singular (rank-deficient) matrix — a floating sub-network, a voltage-source loop — because there is no equilibrium to relax toward; pstran will grind through its step budget and fail, the same as every other method. That is the dividing line: `floatgmin` is for a matrix that is structurally broken (a node with no DC reference); `pstran` is for a matrix that is fine but whose solution Newton cannot find. Diagnosing which you have is the real work — the singular-matrix message alone will not tell you.
See also `.temp`, `@dev[param]`, accuracy note.

### `Other commands` — less-common, alias & developer reference

The remaining `cp_coms` commands, for completeness. Aliases point at their documented twin; developer/debug dumps are grouped at the end.

command | mode | what it does
--- | --- | ---
`setcs` | ui | like `set` but preserves the case of the value
`unset` | ui | remove a variable set by `set`
`undefine` | ui | remove a user function made with `define`
`newhelp` / `oldhelp` | ui | alternate front-ends to `help` (same content)
`trace` | batch+ui | print a node’s value at every timepoint (breakpoint family; remove with `delete`)
`iplot` | batch+ui | plot nodes incrementally as the run progresses (interactive display)
`bltplot` | ui | plot via the BLT widget toolkit (Tcl builds)
`use` | ui | load a compiled device library (like `codemodel`/`osdi`)
`deftype` | ui | redefine vector/plot type names (cf. `settype`)
`reshape`-kin: `shift` | ui | shift `argv` or a list variable one place left (scripting)
`circbyline` | ui | enter a netlist one line at a time (programmatic deck building)
`setcirc` / `remcirc` / `removecirc` | batch+ui | switch to / delete the current circuit when several decks are loaded
`remzerovec` | ui | drop zero-length vectors from the current plot
`inventory` | batch+ui | print a count of devices by type in the circuit
`wrs2p` | ui | write S-parameter data as a Touchstone `.s2p` file (needs `.sp` + `P` ports)
`aspice` / `rspice` / `jobs` | ui | run a SPICE job asynchronously / remotely, and report on such jobs
`optran` | batch+ui | configure the OP→TRAN ramp-up (the 6 optran flags)
`cdump` | ui | dump the parsed `.control` structures (debug)
`mdump` / `mrdump` | batch+ui | dump the solver matrix / right-hand-side to a file (debug)
`check_ifparm` | batch+ui | validate device `IFparm` descriptors (developer)

See also `set`, `stop` (breakpoints), `osdi`.

### `.param` / `.func` — parameters and functions batch + ui

Synopsis

    .param  name = value   [ name2 = expr … ]
    .func   name(args) = { expression }

Description

`.param` defines named constants (usable in any value via `{name}`); `.func` defines a parameter-time function. Both are resolved during parsing — they cannot see simulation results (for that, use `let`/`define` in `.control`).

structure.cir — run: `ngspice -b structure.cir`

    .param mult=3 rtop=2k
    .func scaled(x)={x*mult}
    .subckt divider a b rt=1k rb=1k
      R1 a mid {rt}
      R2 mid b {rb}
    .ends
    V1 in 0 DC {scaled(1)}
    X1 in 0 divider rt={rtop} rb=1k
    .tran 1u 5u
    .control
      run
      print v(in)[0]         $ scaled(1) = 3
      print v(x1.mid)[0]     $ 3 * 1k/(2k+1k) = 1
    .endc
    .end

    v(in)[0] = 3.000000e+00
    v(x1.mid)[0] = 1.000000e+00

Example 2 — random parameters for Monte-Carlo (`agauss`)
mc.cir — run: `ngspice -b mc.cir`

    .param rval=agauss(1k,100,3)   $ gaussian: nominal 1k, ±100 at 3σ
    V1 in 0 DC 1
    R1 in 0 {rval}
    .op
    .control
      setseed 7
      run
      print @R1[resistance]        $ one random draw
    .endc
    .end

    @r1[resistance] = 1.009399e+03

`agauss`/`gauss`/`unif` draw random parameter values (fixed with `setseed` for reproducibility) — the basis of a Monte-Carlo loop with `foreach` + `alter`.

Parse-time vs run-time. `.param`/`.func` are evaluated once at parse; they cannot reference node voltages or run results. Conversely `let`/`define` (in `.control`) work on simulation vectors and are not visible to element cards. Mixing the two is a common source of “parameter not found”.
See also `.subckt`, `let`, `define`.

### `.plot` / `plot` — a curve, ASCII or graphical batch + ui

Synopsis

    .plot  analysis  expr ...          $ batch: ASCII line plot
    plot   expr ... [ vs xexpr ]       $ control mode: graphical window
    plot   "regexp" ...                $ plot every matching vector

Regex selection

A double-quoted argument is a regular expression matched over the current plot (like `display`): `plot ".*#branch"` plots all branch currents. The quotes are required because bare metacharacters are expression operators; unquoted `vs`/limit keywords and expressions are untouched. (Applies equally to `gnuplot` and `hardcopy`, which share the plot engine.)

The `plot` vs `.plot` trap. They are not the same command. `.plot` (dot-card) renders an ASCII plot to the console and works in plain batch. `plot` (no dot, control mode) opens a graphical window and needs a display — in headless batch it produces no usable output. For a terminal/log or PDF pipeline, use `.plot` or `wrdata`+gnuplot, not `plot`.
Example — `.plot` ASCII output
dotplot.cir — run: `ngspice -b dotplot.cir`

    V1 in 0 SIN(0 1 1k)
    R1 in out 1k
    C1 out 0 100n
    .tran 20u 1m
    .plot tran v(out)

    Legend:  + = v(out)
    -----------------------------------------------------------------------
     2.000e-05  1.264e-02 .                        +                       .
     4.000e-05  4.518e-02 .                        .+                      .
     6.000e-05  9.294e-02 .                        . +                     .
     8.000e-05  1.532e-01 .                        .  +                    .
     1.000e-04  2.224e-01 .                        .    +                  .
     1.200e-04  2.974e-01 .                        .      +                .

Related — `asciiplot`, `hardcopy`, `gnuplot`

Three siblings render the same vectors different ways: `asciiplot vec…` draws the text plot shown above straight into the log (identical to a `.plot` card); `gnuplot file vec…` hands off to gnuplot; and `hardcopy file vec…` writes a PostScript file — the only one that works cleanly under `-b` for automated figure generation.

hardcopy.cir — run: `ngspice -b hardcopy.cir`

    V1 in 0 SIN(0 1 1k)
    R1 in out 1k
    C1 out 0 100n
    .tran 20u 1m
    .control
      run
      set hcopypscolor=1     $ colour instead of monochrome
      hardcopy plot.ps v(out)
    .endc
    .end

    plot.ps : %!PS-Adobe-3.0 EPSF-3.0   (a ~9 kB Encapsulated PostScript figure)

See also `.print`, `wrdata`, `gnuplot`.

### `print` / `.print` — values as text batch + ui

Synopsis

    print  expr ...                    $ control mode: dump values to stdout
    print  "regexp" ...                $ control mode: values of all matching vectors
    .print  analysis  expr ...         $ batch: a formatted table

Regex selection

Because arguments are expressions (where `*`, `(`, … are operators), a regular expression must be double-quoted: `print ".*#branch"` prints every branch current, `print "v.*"` every v-node — matched over the current plot (same regex as `display`). Unquoted expressions like `v(out)` or `vdb(a)-vdb(b)` are unaffected.

Description

Control-mode `print` writes the value of vectors/expressions to the console (a scalar prints on one line; a vector prints all points). The dot-card `.print tran|ac|dc …` produces a numbered table of the named vectors after the run — and is one of the output directives that make an analysis actually run in batch mode.

Example — `.print` table
dotprint.cir — run: `ngspice -b dotprint.cir`

    V1 in 0 SIN(0 1 1k)
    R1 in out 1k
    C1 out 0 100n
    .tran 100u 500u
    .print tran v(in) v(out)

    Index   time         v(in)          v(out)
    0       0.000000e+00 0.000000e+00   0.000000e+00
    1       5.000000e-08 3.141593e-04   1.570011e-07
    2       5.420140e-08 3.405574e-04   1.713021e-07
     …

Example — quoted regex over a hierarchical circuit
regex_hier.cir — run: `ngspice -b regex_hier.cir`

    * two instances X1, X2 of a stage -> internal nodes x1.n1, x2.n1, ...
    Vin in 0 DC 3
    X1 in  mid stage
    X2 mid out stage
    RL out 0 1k
    .subckt stage a z
      R1 a n1 1k
      R2 n1 n2 1k
      R3 n2 z 1k
    .ends
    .op
    .control
      run
      print "x1.*"        $ every node inside instance X1 (quotes required)
    .endc
    .end

    x1.n1 = 2.571429e+00
    x1.n2 = 2.142857e+00

One line dumps every node inside `X1`. On a real design this is the fast way to inspect a subcircuit — the two-stage amplifier in `user_examples/two_balance.cir` (see `.subckt`) exposes `x1.a`, `x1.b`, `x1.vp`, `x1.vcm`, `x2.…`, so `print "x1.*"` reads back that whole instance at once. Two gotchas — both covered in the `display` entry — apply here: the quotes are required (bare `*`/`(` are operators), and a batch `.op` also prints a separate operating-point device dump that is not from `print`. Empty `@dev[i]` vectors (AC `savecurrents`) are skipped automatically; use `"^x1\."` to match only nodes and exclude device-current vectors.

See also `.plot`, `wrdata`, `display`.

### `.pz` — pole–zero analysis batch + ui

Synopsis

    .pz  node1 node2 node3 node4  cur|vol  pz|pol|zer

Description

Finds the poles and/or zeros of a small-signal transfer function about the DC operating point. `node1 node2` are the input port, `node3 node4` the output port; `vol` (voltage transfer) or `cur` (transimpedance); `pz` = both, `pol` = poles only, `zer` = zeros only. Results appear as complex vectors `pole(k)`, `zero(k)` (rad/s).

Example — the single RC pole
pz.cir — run: `ngspice -b pz.cir`

    * Pole–zero of an RC low-pass: v(out)/v(in)
    V1 in 0 AC 1
    R1 in out 1k
    C1 out 0 100n
    .pz in 0 out 0 vol pz
    .control
      run
      print pole(1)
    .endc
    .end

    pole(1) = -1.00000e+04,0.000000e+00

A single real pole at −104 rad/s = −1/RC (= −1591 Hz), with no finite zero ✓. Poles are in rad/s; divide by 2π for Hz.

See also `.ac`, `.tf`.

### `repeat` / `if` / `else` — fixed count & branching ui only

Synopsis

    repeat [ n ]        if condition        break / continue
      … body …            … body …          (exit / skip a loop pass)
    end                 else
                          … body …
                        end

`repeat n` runs the body `n` times (forever if `n` is omitted — use `break` to leave).
repeat.cir — run: `ngspice -b repeat.cir`

    .control
      let n = 0
      repeat 4
        let n = n + 1
        if n % 2 = 0
          echo $&n "is even"
        else
          echo $&n "is odd"
        end
      end
    .endc
    .end

    1 is odd
    2 is even
    3 is odd
    4 is even

See also `while`, `foreach`.

### `reset` / `rehash` / `help` / `quit` — session commands batch + ui

Synopsis

    reset      $ re-setup the circuit to its just-parsed state
    rehash     $ rebuild the executable-command hash
    help [subject]     quit     bug     tutorial

Description

`reset` restores the circuit to its initial state, undoing `alter`s and prior runs; `help` browses the built-in docs; `quit` exits; `bug` shows the bug-report address; `tutorial` points at the tutorials.

trivial.cir — run: `ngspice -b trivial.cir`

    V1 in 0 DC 1
    R1 in 0 1k
    .op
    .control
      run
      print v(in)      $ 1.0
      reset
      run              $ fresh setup, run again
      print v(in)      $ 1.0
    .endc
    .end

    v(in) = 1.000000e+00
    v(in) = 1.000000e+00

See also `alter`, `source`.

### `reshape` / `transpose` / `cutout` / `cross` — vector surgery ui only

Synopsis

    reshape   vec …  [dim1,dim2,…]   $ change a vector's dimensions
    transpose var …                  $ transpose a multi-dimensional vector
    cutout    vec …                  $ keep only the current scale range
    cross     newvec index vec …     $ build a vector from one index across others

Description

Manipulate the shape/contents of data vectors — reshape a flat vector into a matrix, transpose it, trim to a window, or gather a cross-section. Mostly for post-processing multi-dimensional (e.g. swept) results.

See also `compose`, `let`.

### `.save` / `save` — choose what is stored batch + ui

Description

Without any `.save`, a `-r` run stores the default set: every node voltage and every voltage-source current. A `.save` (or control-mode `save`) replaces that with exactly the vectors you name, always alongside the `time`/frequency scale. Use it to shrink large raw files, or to store an expression such as a differential voltage.

Regex selection

`save` runs before the circuit, so a regex argument matches the circuit node names (not plot vectors): `save vd.*` saves every node starting with `vd`, `save .*out.*` every node containing `out`. The save spec forms `v(..)`/`i(..)`, `@dev[param]` and `all` are not treated as patterns. Bare regex is fine here (unlike `print`) because `save`’s arguments are names, not expressions.

Example A — default set vs. `.save`
default.cir (no .save) — run: `ngspice -b -r default.raw default.cir`

    No. Variables: 4
       0  time    time
       1  v(in)   voltage
       2  v(out)  voltage
       3  i(v1)   current

restrict.cir — same circuit with `.save v(out)` — same command

    * restrict the raw file
    .save v(out)
    V1 in 0 SIN(0 1 1k)
    R1 in out 1k
    C1 out 0 100n
    .tran 10u 2m
    .end

    No. Variables: 2
       0  time    time
       1  v(out)  voltage

Example 2 — store a computed expression (a differential voltage)
save_diff.cir — run: `ngspice -b -r out.raw save_diff.cir`

    V1 p 0 SIN(0 1 1k)
    V2 n 0 SIN(0 0.7 1k)
    R1 p 0 1k
    R2 n 0 1k
    .save v(p) v(n) vdiff=par('v(p)-v(n)')
    .tran 50u 1m
    .end

    No. Variables: 4
       1  v(p)      voltage
       2  v(n)      voltage
       3  v(vdiff)  voltage      ← the saved expression

`.save name=par('expr')` stores a computed vector straight into the raw file — the clean way to capture a differential output without post-processing.

Example — internal device parameters with `@device[param]`
save_at.cir — run: `ngspice -b save_at.cir`

    * @device[param] exposes a model's internal quantities
    .model NMOS1 NMOS(LEVEL=1 VTO=0.7 KP=120u LAMBDA=0)
    M1 d g 0 0 NMOS1 W=10u L=1u
    VG g 0 DC 1.7
    VD d 0 DC 3
    .options savecurrents      $ makes @m1[id] @m1[is] ... available too
    .op
    .control
      run
      print @m1[gm] @m1[vdsat] @m1[id]
    .endc
    .end

    @m1[gm]    = 1.200000e-03
    @m1[vdsat] = 1.000000e+00
    @m1[id]    = 6.000000e-04

Beyond node voltages and source currents, `@name[param]` reads a device's internal values — here gm = KP·(W/L)·(VGS−VTO) = 1.2 mA/V and Vdsat = 1.0 V ✓. Hierarchical instances use the full path, e.g. `@m.x1.x2.m1[vgs]`. Per-terminal branch currents (`@m1[id]`, `@m1[is]`, …) need `.options savecurrents`; you can `save` or `plot` any of these like an ordinary vector.

Gotcha — `.save` must not be the first line. The first line of a deck is always the title and is ignored. A deck that opens with `.save v(out)` silently saves everything because that line was treated as the title. (This exact mistake produced a wrong result while writing this entry — hence the leading comment line in the example above.)

### `.sens` — sensitivity analysis batch + ui

Synopsis

    .sens  output_variable                        $ DC sensitivity
    .sens  output_variable  ac  dec n fstart fstop  $ AC sensitivity

Description

Computes the sensitivity of an output to every circuit element and model parameter, at the DC operating point (or over frequency with the `ac` form). Results land in a Sensitivity Analysis plot: the bare element name (e.g. `r1`) is d(output)/d(element value), and `elem:param` (e.g. `r1:tc1`) breaks it down per model parameter.

Example — divider output sensitivities
sens.cir — run: `ngspice -b sens.cir`

    V1 in 0 DC 1
    R1 in out 1k
    R2 out 0 3k
    .sens v(out)
    .control
      run
      print r1 r2 v1     $ dV(out)/dR1, /dR2, /dV1
    .endc
    .end

    r1 = -1.87500e-04
    r2 =  6.249994e-05
    v1 =  7.500000e-01

For v(out) = V·R2/(R1+R2) = 0.75 V: dV/dR1 = −V·R2/(R1+R2)² = −1.875e-4, dV/dR2 = +V·R1/(R1+R2)² = +6.25e-5, and dV/dV1 = R2/(R1+R2) = 0.75 — all matched ✓.

Note — batch needs an output directive. Like `.pz`, `.sens` only runs in `-b` with a `.control` block or a `.print`/`.plot` line (see §2). The sensitivity plot does not contain the output vector itself, so `print v(out)` alongside the sensitivities fails — print them in separate statements.
See also `.tf`, `.op`, `.ac`.

### `set` / `$` / `$&` — shell variables vs. vector values ui only

Synopsis

    set name = value          $ define a shell variable
    $name                     $ expand a shell variable (text)
    $&vecname                 $ expand the numeric value of a vector

Description

`set` makes a shell variable (a string/flag, e.g. `set appendwrite`). `$name` substitutes its text. To drop a vector’s numeric value into a command line (e.g. an `echo` or a filename), use `$&vecname` — the `&` is what says “take the value of the vector”, not “look up a shell variable”.

vectors.cir (same deck) — the `set`/`$` lines

      set foo = hello
      echo "foo is" $foo       $ shell variable -> text
      let v = 7
      echo "v ampersand:" $&v  $ vector value -> text

    foo is hello
    v ampersand: 7

Gotcha — `$v` vs `$&v`. `$v` looks for a shell variable named `v`; if only the vector `v` exists, it expands to nothing (or errors). Use `$&v` for the vector’s value. This is the single most common `.control` scripting mistake.
Diagnostic variables

Some behaviour is controlled by `set` variables rather than `.option`, because it belongs to the front end. The two most useful when a large deck misbehaves — `set iter_verbose` (report every Newton iteration and its cost) and `set topo_verbose` (report what topology reduction tied or removed) — are described under `.option`, and are best placed in `.spiceinit` so they are active before the deck is even read.

See also `let`, `foreach` / `while`, `.spiceinit`.

### `setplot` / `setscale` / `destroy` — manage plots ui only

Synopsis

    setplot [ plotname ]     $ list plots / switch the current one
    setscale [ vecname ]     $ set the default scale of the current plot
    destroy [ plotname … ]   $ free a plot's data (or 'all')

Description

Each run/analysis makes a new plot (a named vector set: `tran1`, `ac1`, `sens1`, …) — so the plot list is your list of completed simulations. `setplot` with no argument lists them, marks the current, and shows each one’s analysis type and title (and interactively prompts you to pick one); with a name it switches. Cross-plot references use `plot.vector` (e.g. `ac1.inp`). `destroy` reclaims memory.

Listing plots vs. vectors

command | lists
--- | ---
`setplot` (no arg) | the plots — one per analysis run — with type/title, current marked
`display` (no arg) | the vectors in the current plot
`echo $plots` | plot names only, as a string — for scripting, not a listing

Example — several analyses, then list & switch
plotmgmt.cir — run: `ngspice -b plotmgmt.cir`

    V1 in 0 AC 1 SIN(0 1 1k)
    R1 in out 1k
    C1 out 0 100n
    .control
      op                 $ -> plot op1
      ac dec 10 1 1meg   $ -> plot ac1
      tran 20u 1m        $ -> plot tran1
      setplot            $ list all plots, marks the current
      setplot ac1        $ switch to the AC plot
      display            $ vectors in ac1
    .endc
    .end

    List of plots available:

    Current tran1   * … (Transient Analysis)
            ac1     * … (AC Analysis)
            op1     * … (Operating Point)
            const   Constant values (constants)

Three runs → three plots, each tagged with its analysis. A second `tran` would add `tran2`, so the list doubles as a run history. `echo $plots` gives the same names tersely (`const op1 ac1 tran1`) when you need them in a script.

See also `display`, `reset`.

### `setseed` — seed the random generator ui only

Synopsis

    setseed  integer

Description

Fixes the seed of the random generator used by `agauss`/`gauss`/`unif` and Monte-Carlo draws, so a randomized run is exactly reproducible — set the same seed before each run to repeat a sequence.

See also `mc_source`, `.option`.

### `settype` — set a vector's type/units ui only

Synopsis

    settype  type  vec | regexp …     $ type = voltage | current | time | frequency | impedance | …

Description

Relabels a computed vector’s physical type so plots and printouts carry the correct axis label and units (a derived impedance, power, gain, …). A vector argument containing regex metacharacters retypes every current-plot vector whose name matches — e.g. `settype impedance .*#branch` (same regex selection as `display`).

settype.cir — run: `ngspice -b settype.cir`

    .control
      run
      let z = v(out)/1m
      settype impedance z
      display z
    .endc

    z : impedance, real, 59 long

See also `let`, `display`.

### `shell` / `cd` / `getcwd` — OS shell & directory ui only

Synopsis

    shell [ command ]     cd [ dir ]     getcwd

Description

`shell` runs an OS command (or forks an interactive shell); `cd`/`getcwd` change and print the working directory. Handy for pre/post-processing around a run.

Example — backtick command substitution
backtick.cir — run: `ngspice -b backtick.cir`

    R1 1 0 1k
    V1 1 0 DC 5
    .op
    .control
      run
      set nwords = `echo one two three four five | wc -w`   $ capture OS stdout
      echo "the shell counted $nwords words"
      let total = $nwords * 2                                $ feed it into a let
      print total
    .endc
    .end

    the shell counted 5 words
    total = 1.000000e+01

Backticks ``…`` run a command and paste its standard output straight into the `.control` line — the ngspice analogue of shell command substitution. Combined with `set`/`let` it lets an external tool (`sed`, `awk`, `wc`, a helper script) feed values back into the simulation.

Note. `shell` can block a non-interactive `-b` run if the command waits for input or a terminal; prefer running such steps outside ngspice in batch pipelines.

### `snsave` / `snload` — save/restore simulation state ui only

Synopsis

    snsave file      snload file

Description

Writes (or reloads) a full simulator snapshot — node values and internal state — so a long run can be checkpointed and continued later, or a hard-won operating point reused as a starting condition.

See also `.ic`.

### `source` / `edit` — load a deck interactively ui only

Synopsis

    source  file        edit [ file ]

Description

`source` reads and loads another netlist file into the running session; `edit` opens the current (or a named) deck in `$EDITOR` and reloads it on exit. Interactive workflow aids; use `.include` for static composition.

See also `listing`, `.include`.

### `.sp` / `.pss` / `.hb` — RF & large-signal analyses batch + ui

Synopsis

    .sp   dec|lin n fstart fstop     $ S-parameters (needs P RF-port elements)
    .pss  fund tstop points …        $ periodic steady state (shooting method)
    .hb   fund nharm                 $ harmonic balance

Description

`.sp` computes scattering parameters and requires `P` RF-port elements (each with a reference impedance) on the ports of interest. `.pss` (periodic steady state) and `.hb` (harmonic balance) are large-signal RF analyses for finding a circuit’s steady periodic response directly.

Build note. On this ngspice-46 build, `.sp` is present (but errors without RF ports: “No RF Port is present”), while `pss` and `hb` report “no such command available” — they need a build compiled with those experimental analyses enabled.
See also `.ac`, `.tf`.

### `spec` — spectrum over a frequency range ui only

Synopsis

    spec  fstart  fstop  fstep  vector …

Description

Like `fft`, but computes the spectrum at the exact frequencies you name (`fstart`…`fstop` by `fstep`), rather than the FFT’s fixed 1/timespan bins. Run `linearize` first.

spec.cir — run: `ngspice -b spec.cir`

    B1 s 0 V = sin(2*3.14159*1k*time) + 0.5*sin(2*3.14159*3k*time)
    R1 s 0 1k
    .tran 5u 20m
    .control
      run
      linearize
      spec 0 5k 100 v(s)
      let m = mag(v(s))
      meas sp a1k MAX m FROM=0.9k TO=1.1k
      meas sp a3k MAX m FROM=2.9k TO=3.1k
    .endc
    .end

    a1k =  9.99901e-01  at=  1.00000e+03
    a3k =  4.99552e-01  at=  3.00000e+03

Both tones land in exact 1.000 / 3.000 kHz bins with the right 2:1 amplitude ratio ✓.

See also `fft`, `.four`, `linearize`.

### `.step` — not supported

ngspice has no `.step`. A deck with `.step param …` errors with “unimplemented dot command '.step'” — it is an LTspice/PSpice extension. To sweep a parameter across runs, use a control-mode loop with `alter`/`altermod`:
the ngspice way (no download — pattern)

    .control
      foreach rl 1k 2k 3k
        alter R2 = $rl
        run
        meas tran vout FIND v(out) AT=...
      end
    .endc

See also `foreach`, `alter`, `.dc` (built-in sweeps).

### `stop` / `resume` / `delete` — breakpoints batch + ui

Synopsis

    stop [ after n ] [ when expr ] …    $ set a breakpoint / condition
    resume                              $ continue a stopped run
    delete [ all | number … ]           $ remove breakpoints

Description

Pause a transient at a time, iteration, or condition to inspect state, then `resume`. Most useful interactively (or when driven from a script that examines vectors at each stop).

See also `.tran`.

### `strcmp` / `strstr` / `strslice` — string operations ui only

Synopsis

    strcmp   var s1 s2            $ var = C strcmp(s1,s2): 0 if equal
    strstr   var s1 s2            $ var = index of s2 in s1 (-1 if absent)
    strslice var s1 offset len    $ var = s1[offset .. offset+len)

Description

Each sets a shell variable from a string operation, for scripting file names, labels, and conditionals in `.control`.

strings.cir — run: `ngspice -b strings.cir`

    .control
      strcmp  c "abc" "abc"
      strcmp  d "abc" "abd"
      strstr  p "hello world" "world"
      strslice s "abcdef" 2 3
      echo "equal=" $c "differ=" $d "pos=" $p "slice=" $s
    .endc

    equal= 0 differ= -1 pos= 6 slice= cde

See also `set`, `echo`.

### `.subckt` / `.ends` / `X` — subcircuits batch + ui

Synopsis

    .subckt  name  node1 node2 …  [ param=default … ]
      … elements …
    .ends
    Xname  net1 net2 …  name  [ param=value … ]      $ instantiation

Description

A subcircuit is a reusable block with external ports and optional parameters. Instantiate it with an `X` card, wiring your nets to its ports and overriding parameters by name. Internal nodes are addressed hierarchically as `Xinstance.node` (e.g. `v(x1.mid)` above).

Example — parameters: one block, three corner frequencies
subckt_params.cir — run: `ngspice -b subckt_params.cir`

    * one parameterized RC low-pass, reused at three cutoffs
    V1 in 0 AC 1
    Xdef in def rclp                 $ takes the default  c=1n  -> ~159 kHz
    Xmid in mid rclp c=10n           $ override by name   -> ~15.9 kHz
    Xlo  in lo  rclp c=100n          $ override by name   -> ~1.59 kHz
    .subckt rclp a b r=1k c=1n       $ r,c are params with defaults
      R1 a b {r}                     $ {..} evaluates the param
      C1 b 0 {c}
    .ends
    .ac dec 100 100 1meg
    .control
      run
      meas ac fcdef WHEN vdb(def)=-3.0103
      meas ac fcmid WHEN vdb(mid)=-3.0103
      meas ac fclo  WHEN vdb(lo)=-3.0103
    .endc
    .end

    fcdef               =  1.59155e+05
    fcmid               =  1.59155e+04
    fclo                =  1.59155e+03

Parameters are declared as `name=default` on the `.subckt` line and used inside as `{name}`. Each `X` card overrides by name (`Xmid … c=10n`) or, like `Xdef`, inherits the default. The three cutoffs land exactly on 1/(2π·1 kΩ·C) for C = 1 n, 10 n, 100 n ✓ — one definition, three behaviours.

Gotcha — wrap parameter references in `{…}`. Writing `C1 b 0 c` makes ngspice parse the bare `c` as a model name and warn `can't find model 'c'`. The brace form `{c}` forces the token to be evaluated as the parameter. A literal number needs no braces; a parameter reference always does. (The `.subckt name … params: c=1n` keyword before the defaults is an accepted synonym.)
Example — a two-stage balanced amplifier (two instances chained), extracting its differential input capacitance
two_balance.cir — full runnable set (models + stimulus) in `user_examples/`; run: `ngspice -b two_balance.cir`

    * two instances of the same subcircuit, chained
    xa vcasc inp inn ib0 op   on   vdd gnd balance_simple
    xb vcasc op  on  ib1 outp outn vdd gnd balance_simple
    .include ngspice_stimuli2               $ models + sources + .control

    .subckt balance_simple vcasc inp inn ib outn outp vdd gnd
      M3 a inp vcm gnd nch w=10u l=0.18u m=1000    $ input diff pair
      M1 b inn vcm gnd nch w=10u l=0.18u m=1000
      M2 outn vcasc a gnd nch w=10u l=0.18u m=100  $ cascode devices
      M4 outp vcasc b gnd nch w=10u l=0.18u m=100
      M5 vcm ib gnd gnd nch w=10u l=0.6u m=100     $ tail current sink
      M6 outn vp  vdd vdd pch w=5u l=0.25u m=500   $ PMOS load
      M7 outp vp  vdd vdd pch w=5u l=0.25u m=500
      R1 vp outn 10meg   $  R2 vp outp 10meg  (common-mode feedback)
    .ends

    * --- ngspice_stimuli2 (excerpt): extract Cdiff from two AC runs ---
    .control
      ac dec 100 1k 11e6                       $ ac1: common-mode drive
      meas ac vinp_cm   find ac1.inp at=1e3
      alter vinp acphase 0
      alter vinn acphase 180                   $ ac2: differential drive
      ac dec 100 1k 11e6
      meas ac vinp_diff find ac2.inp at=1e3
      let cinp = 10p*(1/ac2.vinp_diff - 1/ac1.vinp_cm)/2
      echo "Cdiff = $&cinp"
    .endc

    Warning: command 'plot' is not available during batch simulation, ignored!
    vinp_cm   = 4.887917e-01
    vinp_diff = 4.803305e-01
    Cdiff  = 1.80193E-13

The block is instantiated twice (`xa`, `xb`) and chained; the script drives it common-mode then differentially and computes the differential input capacitance, Cdiff ≈ 0.18 pF. Note the batch warning: the interactive `plot` is ignored under `-b` (use `.plot` / gnuplot) — see § plot vs .plot. (User-contributed example; generic LEVEL-3 models, no foundry data.)

AC differential response — `vdb(outp,outn)` vs frequency (rendered from the saved raw)

Passband differential gain ≈ 3.93 dB, flat to ~1 MHz, rolling off by ~10 MHz — the amplifier bandwidth. (This is what the batch-ignored `plot` would have shown; here it is drawn from the saved data.)

See also `.param`, `.include`, `.ac`, `meas`.

### `.temp` — simulation temperature batch + ui

Synopsis

    .temp  value_in_celsius        $ or, in .control:  set temp = value

Description

Sets the analysis temperature (°C). Every temperature-dependent model (diode/BJT/MOS saturation currents, resistor `tc1/tc2`, …) is re-evaluated. In `.control` you can change `set temp` between runs to sweep temperature.

Example — diode current at 27 °C vs 100 °C
temp.cir — run: `ngspice -b temp.cir`

    .model DMOD D(IS=1e-14)
    V1 a 0 DC 0.65
    D1 a 0 DMOD
    .tran 1u 5u
    .control
      set temp = 27
      run
      print -i(v1)[0]        $ 0.82 mA
      set temp = 100
      run
      print -i(v1)[0]        $ 51 mA
    .endc
    .end

    -i(v1)[0] = 8.204764e-04
    -i(v1)[0] = 5.113547e-02

At a fixed 0.65 V the diode conducts 62× more at 100 °C — the strong temperature dependence of the saturation current ✓.

See also `altermod`, `.op`.

### `.tf` — DC small-signal transfer function batch + ui

Synopsis

    .tf  output_variable  input_source

Description

Computes, at the DC operating point, the small-signal ratio `output/input`, the resistance seen at the input source, and the resistance seen at the output. One command for gain and both port impedances.

Example — divider gain and impedances
tf.cir — run: `ngspice -b tf.cir`

    * DC small-signal transfer function
    V1 in 0 DC 1
    R1 in out 1k
    R2 out 0 3k
    .tf v(out) V1
    .end

    transfer_function          = 7.500000e-01
    output_impedance_at_v(out) = 7.500000e+02
    v1#input_impedance         = 4.000000e+03

Gain = R2/(R1+R2) = 0.75; Zout = R1‖R2 = 750 Ω; Zin = R1+R2 = 4 kΩ ✓.

See also `.op`, `.ac`.

### `.tran` — transient analysis batch + ui

Synopsis

    .tran  tstep  tstop  [ tstart  [ tmax ] ]  [ uic ]

As a control-mode command (inside `.control`), drop the dot: `tran tstep tstop …`.
Description

Computes the circuit response in the time domain from `tstart` (default 0) to `tstop`. `tstep` is the printing/initial step; the actual internal step is chosen adaptively by local-truncation-error control and never exceeds `tmax` (default `(tstop−tstart)/50`). Results are stored in the current plot as vectors indexed by the `time` scale.

Parameters

parameter | meaning
--- | ---
`tstep` | suggested/printing step size; also the initial step
`tstop` | end time
`tstart` | time at which output storage begins (analysis still starts at 0)
`tmax` | maximum internal timestep (caps the adaptive step)
`uic` | use initial conditions: skip the DC operating point; start from `.ic` / device `IC=` values instead

Example — RC low-pass step response (τ = R·C = 1 kΩ · 1 µF = 1 ms)
rc.cir — run: `ngspice -b rc.cir`

    * RC low-pass step response
    V1 in 0 PULSE(0 1 0 1n 1n 1 2)
    R1 in out 1k
    C1 out 0 1u
    .tran 0.1m 5m
    .control
      run
      meas tran v5ms  FIND v(out) AT=5m
      meas tran trise TRIG v(out) VAL=0.1 RISE=1 TARG v(out) VAL=0.9 RISE=1
    .endc
    .end

    ngspice -b rc.cir

    No. of Data Rows : 79
    v5ms                =  9.93289e-01
    trise               =  2.19596e-03   targ=  2.30186e-03   trig=  1.05894e-04

The output settles to 0.993 V at 5 ms (= 5τ, so 1 − e−5 ≈ 0.993 ✓), and the 10–90 % rise time is 2.196 ms ≈ 2.2τ, as expected for a single-pole RC.

Example 2 — `uic`: an LC tank rings from an initial condition
tran_uic.cir — run: `ngspice -b tran_uic.cir`

    L1 n 0 1m
    C1 n 0 100n ic=1        $ start the cap at 1 V
    .tran 1u 400u uic       $ no DC op; use the initial condition
    .control
      run
      meas tran period TRIG v(n) VAL=0 FALL=1 TARG v(n) VAL=0 FALL=2
    .endc
    .end

    period = 6.28842e-05

An LC tank has no DC operating point to settle to; `uic` starts it from `C1`’s `ic=1` and it oscillates at T = 2π√(LC) = 2π√(1 mH·100 nF) = 62.8 µs ✓.

Gotcha — `uic` skips the operating point. With `uic`, ngspice does not solve the DC bias first; every node not pinned by an `.ic` or a device `IC=` starts at 0 V. Use it for ring oscillators and switched circuits that have no valid DC solution, not as a convergence shortcut.
See also `.save` (restrict what is stored), `.ic`/`.nodeset`, control-mode `tran`, `meas`.

### `unlet` — delete vectors ui only

Synopsis

    unlet  vecname | regexp …

Description

Removes vectors from the current plot (frees memory, or drops intermediates before a `write`). The inverse of `let`. An argument containing regex metacharacters deletes every current-plot vector whose name matches — e.g. `unlet temp.*`, `unlet .*#branch` (same grep-style regex selection as `display`; the scale vector is protected).

See also `let`, `display`, `destroy`.

### `version` / `sysinfo` / `rusage` / `history` — session info batch + ui

Synopsis

    version [n]     sysinfo     rusage [resource]     history [-r] [n]

Description

`version` prints the ngspice version (and can assert a minimum); `sysinfo` reports CPU/memory; `rusage` prints resource counters (time, memory, iterations) — useful for benchmarking; `history` lists past commands.

info.cir — run: `ngspice -b info.cir`

    ** ngspice-46 : Circuit level simulation program  [ngspice-jit fork: behavioral foundry R/C JIT-compiled to native OSDI]

See also `.option`.

### `where` / `dump` / `state` / `status` — diagnostics batch + ui

Synopsis

    where     $ node/device with the largest residual (non-convergence)
    dump      $ the circuit's node/branch matrix
    state     $ current analysis state
    status    $ analyses in progress and breakpoints

Description

Convergence and state introspection. `where` names the node or device carrying the largest residual after a failed Newton step — the first thing to check on a “no convergence” error; `dump` prints the raw matrix; `state`/`status` report the analysis and any breakpoints.

where.cir — run: `ngspice -b where.cir`

    No unconverged node found.
    v(out) = 5.000000e-01

Title-line reminder. While writing this entry, a test deck put `V1 …` on line 1 — the title — so the source vanished and the divider’s resistors were (correctly) removed as dangling, giving v(out)=0. Always start a deck with a comment/title (see §1).
See also `.option` (tolerances), `stop`.

### `while` / `dowhile` — condition loops ui only

Synopsis

    while  condition          dowhile  condition
      … body …                  … body …
    end                       end

`while` tests before the body (may run zero times); `dowhile` tests after (runs at least once).
while.cir — run: `ngspice -b while.cir`

    .control
      let i = 0
      while i < 3
        echo "step" $&i
        let i = i + 1
      end
    .endc
    .end

    step 0
    step 1
    step 2

`$&i` prints the numeric value of the vector `i` (see the Vectors chapter). A bare `$i` would look for a shell variable named `i`, which does not exist here.

See also `foreach`, `repeat`.

### `wrdata` — ASCII column data ui only

Synopsis

    wrdata  file  expr ...

Description

Writes the scale (time/frequency) and each expression as whitespace-separated columns of plain text — the easiest format to hand to gnuplot, a spreadsheet, or a script. Control mode only (no dot-card form). A double-quoted argument is a regular expression over the current plot — `wrdata f.txt ".*#branch"` writes every branch current as columns (same rule as `print`).

Example
wrdata.cir — run: `ngspice -b wrdata.cir`

    V1 in 0 SIN(0 1 1k)
    R1 in out 1k
    C1 out 0 100n
    .tran 50u 1m
    .control
      run
      wrdata out.txt v(out)
    .endc
    .end

    # out.txt : two columns, time then value
    0.00000000e+00  0.00000000e+00
    1.00000000e-07  6.27690799e-07
    1.08405602e-07  6.84886513e-07
     …

Example — several columns, one shared scale, with a header
wrdata_multi.cir — run: `ngspice -b wrdata_multi.cir`

    V1 in 0 SIN(0 1 1k)
    R1 in out 1k
    C1 out 0 100n
    .tran 100u 1m
    .control
      run
      set wr_singlescale     $ one time column, not one per expression
      set wr_vecnames        $ prepend a header row of vector names
      wrdata multi.txt v(in) v(out)
    .endc
    .end

     time            v(in)           v(out)
     0.00000000e+00  0.00000000e+00  0.00000000e+00
     1.00000000e-07  6.28318489e-04  6.27690799e-07
     …

By default `wrdata` repeats the scale column before every expression (so N exprs give 2N columns). `set wr_singlescale` collapses that to one scale + N value columns; `set wr_vecnames` adds the name header — the two settings that make the file gnuplot- and spreadsheet-ready.

Example — `wrnodev`: snapshot node voltages as `.ic` lines
wrnodev.cir — run: `ngspice -b wrnodev.cir`

    V1 in 0 SIN(0 1 1k)
    R1 in out 1k
    C1 out 0 100n
    .tran 20u 1m
    .control
      run
      wrnodev nodes.ic       $ present node voltages -> .ic cards
    .endc
    .end

    # nodes.ic
    * Recorded at simulation time: 0.001
    .ic v(in) = -2.44929e-16
    .ic v(out) = -0.450639

Unlike `wrdata` (which writes a whole waveform), `wrnodev` captures just the present operating state of every node as ready-to-`.include` `.ic` cards — a convenient way to seed another run from where this one stopped.

See also `print`, `write` (binary raw), `gnuplot`.

### `write` vs. `-r` — two ways to make a raw file ui only

Description

`-r file` dumps the run’s default (or `.save`-restricted) set to file. `write file2 expr…`, inside `.control`, writes a chosen set to file2. They coexist only if the filenames differ. A double-quoted argument is a regular expression over the current plot — `write br.raw ".*#branch"` writes every branch current (same rule as `print`).

Example B — `write` to a different file than `-r`: both are produced
both.cir — run: `ngspice -b -r main.raw both.cir`

    * write to a different file than -r
    V1 in 0 SIN(0 1 1k)
    R1 in out 1k
    C1 out 0 100n
    .tran 10u 2m
    .control
      run
      write sel.raw v(out)
    .endc
    .end

    main.raw : No. Variables: 4   (time, v(in), v(out), i(v1) — the -r default set)
    sel.raw  : No. Variables: 2   (time, v(out) — exactly what write named)

Example — a human-readable (ASCII) raw file
write_ascii.cir — run: `ngspice -b write_ascii.cir`

    V1 in 0 SIN(0 1 1k)
    R1 in out 1k
    C1 out 0 100n
    .tran 100u 1m
    .control
      run
      set filetype=ascii      $ default is binary
      write out_ascii.raw v(out)
    .endc
    .end

    Title: * set filetype=ascii makes 'write' emit a human-readable rawfile.
    Plotname: Transient Analysis
    Flags: real
    No. Variables: 2
    No. Points: 63
    Variables:
    	0	time	time
    	1	v(out)	voltage
    Values:
     0	0.000000000000000e+00
    	0.000000000000000e+00
     …

Rawfiles are binary by default (compact, exact). `set filetype=ascii` before `write` emits the same structure as text — readable, diff-able, and loadable back with `load`. It applies to `-r` too.

Example — `appendwrite`: many runs, one file
appendwrite.cir — run: `ngspice -b appendwrite.cir`

    V1 in 0 SIN(0 1 1k)
    R1 in out 1k
    C1 out 0 100n
    .tran 20u 1m
    .control
      let rk = 1k
      while rk le 3k
        alter r1 rk
        run
        write sweep.raw v(out)
        set appendwrite      $ 2nd run onward: append, don't overwrite
        let rk = rk + 1k
      end
      destroy all            $ clear the in-memory run plots first
      load sweep.raw         $ ...so echo shows only what the FILE holds
      echo $plots
    .endc
    .end

    const tran1 tran2 tran3

Normally each `write` overwrites; `set appendwrite` makes subsequent writes append a new plot instead — the idiom for sweeping a parameter with `alter` and keeping every run in one raw file. Reload it and each run reappears as `tran1`, `tran2`, … (the `destroy all` is only so the echo isn’t cluttered by the plots still live from this session).

Gotcha — `write` to the same file as `-r` loses. If `write` targets the very file passed to `-r`, the `-r` dump wins: the file ends up with the full default set (4 variables), and `write`’s selection is discarded. Give `write` a different filename from `-r` (or don’t use `-r` at all).
Gotcha — bare `run` needs an analysis card. `run` executes a previously defined analysis (a `.tran`/`.ac`/… card, or a `tran` issued earlier in `.control`). With no analysis defined, `run` does nothing and no raw file is written.
See also `.tran`, `wrdata` (ASCII columns), `print`, `load` (read a raw file back).

## 4. OSDI — compiling behavioral foundry passives into native devices

Foundry PDKs express precision passives as behavioral equations — a resistor or capacitor whose value is a function of terminal voltage and temperature (high-field `tanh`, `vc1/vc2` polynomials, temperature written into the expression). Stock SPICE can’t model those directly, and the usual fallbacks hurt:

- A behavioral `C=f(v)` is realized as an auxiliary differentiator — numerically fragile, and on large designs it aborts with “Timestep too small”.
- A behavioral `R=f(v)` becomes a `B`-source `I = V/R(V)`; the foundry’s `abs(V)` puts a slope discontinuity right at the operating point, hurting the Newton Jacobian.

This build adds a parse-time pass that instead compiles the foundry’s own equation into a native OSDI device using the system `gcc` — no OpenVAF, no Verilog-A, no model edits. Resistors become a native current device with an analytic Jacobian; capacitors become a charge-conserving native charge device (so they are stable where the differentiator was not). Identical laws are compiled once and shared (structural dedup); `m` (mfactor) is first-class; and electrically inert caps (shorted `n1==n2`, or dangling on a floating node) are dropped, matching native topology reduction. It is on by default; `set osdi_res_bsource` forces the old behavioral path.

### `osdi` codegen — behavioral R/C → native batch + ui

How it engages

Automatic: any behavioral resistor (`R='…v(a,b)…'`) or capacitor (`C='…v(a,b)…'`) in the netlist is compiled at parse time. The run log reports what was generated, e.g. `osdi-res: N resistor(s) + M capacitor(s) -> K C-OSDI model(s)`.

Turning it off — `set osdi_res_bsource`

The codegen is the default; one boolean variable is the global opt-out. With `set osdi_res_bsource`, every behavioral `R=`/`C=` keeps its original B-source implementation instead — no compilation, no OSDI device. It governs resistors and capacitors together.

    * in .spiceinit (it is read before the deck is parsed):
    set osdi_res_bsource        $ keep all behavioral R/C as B-sources, skip OSDI codegen

Put it in `.spiceinit`, not in the deck’s `.control`: codegen runs at parse time, before an in-deck `.control` block executes, so setting it there would be too late. Verified — with the flag the `osdi-res:` line disappears and the circuit runs the behavioral network. Two more behaviors: even with codegen on, a device whose expression uses an unsupported function, or a system with no `gcc`, falls back to B-source automatically (warned once); and `set osdi_res_include="/path"` overrides the C header include directory used when compiling the generated model.

Example — generic voltage-dependent R and C (no foundry data)
osdi_selftest.cir — run: `ngspice -b osdi_selftest.cir`

    .param r0=1000 k=0.1 c0=1e-12 vc=0.05
    * voltage-dependent resistor  R(v) = r0*(1 + k*v^2)
    vr a 0 dc 1
    Rtest a 0 R='r0*(1 + k*v(a,0)*v(a,0))'
    * voltage-dependent capacitor  C(v) = c0*(1 + vc*v)
    vx n 0 dc 0 pulse(0 1 1n 1n 1n 20n 40n)
    Rin n x 1k
    Ctest x 0 c='c0*(1 + vc*v(x,0))'
    .control
      op
      print i(vr)
      tran 0.5n 40n
    .endc
    .end

    osdi-res: 1 resistor(s) + 1 capacitor(s) -> 2 C-OSDI model(s); 0 kept behavioral; 0 shorted/dangling cap(s) dropped
    i(vr) = -9.09091e-04

The two behavioral expressions are compiled into two native OSDI devices, and the DC check is exact: at v(a)=1 V, R = r0(1 + k·1²) = 1100 Ω, so i(vr) = −1/1100 = −9.09e-4 A ✓ (negative by source convention — see `.dc`).

Precision analog: leave `.option bypass` off. On very-high-gain sample-and-hold / ADC nodes, `.option bypass=1` lets a bounded ~1 mV error meander through the hold — a property of the bypass approximation, not of OSDI (native devices show it too). Bypass is off by default; keep it off for precision analog. Full analysis in `docs/osdi_bypass_wobble/` of the ngspice source tree.
Manual codegen — `osdigen` / `osdigenc`

Besides the automatic path, two `.control` commands emit a C-OSDI model on demand:

    osdigen   modelname n1 n2 expr    $ resistor  R(v) from a behavioral expression
    osdigenc  modelname n1 n2 qexpr   $ capacitor from a charge expression Q(v)

Use them to pre-generate and inspect a model (e.g. to check the emitted C, or build a library) rather than letting the parser compile netlist behaviorals silently. Same generator, same output devices.

See also the OSDI codegen docs in the source (`docs/osdi_codegen_overview/`, `osdi_res_codegen/`, `osdi_cap_codegen/`).

All four sections populated (§1 Syntax, §2 Concepts, §3 Command reference A–Z, §4 OSDI); every worked example verified against ngspice-46. Full `cp_coms` catalog coverage: all 118 interactive commands are documented — the common ones with tested examples, the niche/alias/developer remainder in the Other commands table.