# Parasitic-network reduction (`.option minres` / `.option mincap`)

Post-layout RC extraction of a modern block can produce **millions of resistors
and capacitors** — for the ADC used throughout this project, ~5 M R and ~4.5 M C.
Most of those elements are electrically negligible (femto/atto-farad coupling
caps, micro-/milli-ohm interconnect stubs) but they dominate matrix size and
solve time. This feature removes the negligible ones at **parse time**, before
any device is instantiated, so the simulator never pays for them.

Two brutal-but-effective simplifications, each selectable and off by default:

```
.option minres=<value>    short every plain resistor whose value < <value>
                          (its two terminals are merged into one node)
.option mincap=<value>    delete every plain capacitor whose value < <value>
```

Example — a 5 mΩ / 1 fF cleanup:

```
.option minres=5m mincap=1f
```

Both default to `0` (feature off); a deck without these options is untouched and
pays zero cost.

---

## What each option does

### `minres` — short small resistors (node merge)

A resistor below the threshold is *not* deleted-and-left-floating: its two
terminals are **merged into a single node** (a genuine short), so current still
flows through where the resistor was. This is what actually shrinks the matrix —
a parasitic R network is a mesh of tiny series resistors, and shorting the
sub-threshold ones collapses whole chains of nodes together.

- Merges are **transitive**: if `Ra` shorts `a–b` and `Rb` shorts `b–c`, then
  `a`, `b`, `c` all become one node.
- Correctness is exact, not approximate. Shorting `Rt` (2→0) so that a 1 kΩ
  `R1` sees ground directly gives `i(V1) = −1.00000000000e−03` **exactly**,
  versus `−9.99999999e−04` with the 1 µΩ resistor left in series.

#### Pinned nets: ground and sources are protected

A node that a source drives must never be merged into a *different* driven net —
doing so would short a source (e.g. a 1 µΩ parasitic between `VDD` and ground, or
between `VDD` and `VSS`, would collapse the supply and give
`Fatal error: ... is a shorted VSRC`). So the reduction treats as **pinned**:

- ground (node `0`), and
- both terminal nodes of every source: independent `V`/`I` and controlled
  `E`/`F`/`G`/`H`/`B` (their driven output nodes).

Rules for a sub-threshold resistor:

- **plain ↔ plain** → shorted; the first-listed node survives.
- **plain ↔ pinned** (ground or a supply) → shorted; the **pinned net survives**,
  so the plain node *joins* ground / the supply and keeps its driven value.
- **pinned ↔ pinned** (two different driven nets) → **refused**: the resistor is
  left in place and counted in the summary. This is what stops a supply from
  being shorted to ground or to another supply.

Because pinned nets always win, power/supply nets keep their names and stay
connected to their sources through the reduction — they behave exactly like
ground.

### `mincap` — drop small capacitors

A capacitor below the threshold is simply removed from the netlist. No node
merge is needed (a cap does not carry DC current); the two nodes keep whatever
other connections they have.

---

## Safety — what is *not* touched

The reduction only ever acts on **plain, two-terminal, numeric** R and C cards:

- **Model-based** resistors/caps (`R1 a b rmodel …`) are left alone — the value
  field is a model name, not a number, so the card is skipped.
- **Behavioral / voltage-dependent** elements (`R1 a b R='1u*(1+v(a,b))'`) are
  left alone — the expression does not evaluate to a static constant at parse
  time, so the card is skipped.
- A constant *expression* that does reduce to a small number (e.g. `'1u+0'`) is
  treated as that number and may be shorted/dropped — this is correct, its value
  really is below the threshold.

Because the pass runs after subcircuit expansion, `.param`/`agauss` substitution,
every instance is judged on its final numeric value.

---

## How it works

The pass (`src/spicelib/parser/inpparred.c`, called from `inp_spsource` in
`src/frontend/inp.c`, right after subckt expansion + `agauss` and before the OSDI
code-generation pass) runs on the flattened deck:

1. Read the `minres` / `mincap` thresholds from the `.option` cards.
2. Walk the deck. For each sub-threshold resistor, **union** its two node names
   (union-find, with `0` forced to be the class root) and comment the card out.
   For each sub-threshold capacitor, comment the card out.
3. The resulting *name → representative-name* map is consulted by
   **`INPtermInsert`** (`inpsymt.c`) when every later device resolves its
   terminal names to `CKTnode`s. Any device whose terminal is on a merged class
   is transparently pointed at the single representative node.

Doing the merge in `INPtermInsert` — the one chokepoint every device's terminals
pass through — means node positions are handled by each device's own parser;
there is **no fragile text rewriting** of arbitrary device cards.

The `minres` / `mincap` options are registered in the options table
(`cktsopt.c`, `optdefs.h`) so the `.option` card parses without an
"unknown option" error; the numeric effect is applied entirely by the parse-time
pass.

Before the resistor pass, a pre-scan pins ground and every source terminal
(`pin_source_nodes`); the union step refuses any merge between two pinned nets.

A reduction summary is printed — drop counts against the totals (so you can
see the fraction removed) plus the **number of nodes eliminated** (which is what
actually shrinks the matrix / solve cost):

```
Parasitic reduction:
    resistors: shorted 41732 of 5001220 (0.8%) < 0.005 ohm; 41720 node(s) eliminated
    kept 12 sub-threshold resistor(s) that would short two driven nets (source/ground)
    capacitors: removed 88061 of 4498330 (2.0%) < 1e-15 F
```

("nodes eliminated" counts only merges that joined two *distinct* nets; a
sub-threshold resistor whose ends were already the same net is still dropped but
removes no node.)

---

## Caveats

- **Aggressive thresholds can float a node.** If a node's *only* connections are
  a removed small cap and shorted small resistors, it may lose every DC path.
  In practice the resistor short merges such a node into its neighbour, so this
  is rare; `gmin` and the existing topology reduction cover most residual cases.
  Start with conservative thresholds (well below any element you care about) and
  raise them only as needed.
- The threshold is compared against the **element value as written**, not an
  effective impedance at any frequency.
- Merged nodes disappear from the output — `v(<merged-node>)` is no longer a
  distinct vector (it is now the representative/ground node).

---

## Files

- `src/spicelib/parser/inpparred.c` — the pass + union-find + alias lookup.
- `src/spicelib/parser/inpsymt.c` — `INPtermInsert` consults the alias map.
- `src/frontend/inp.c` — call site (`INPparasiticReduce`).
- `src/spicelib/analysis/cktsopt.c`, `src/include/ngspice/optdefs.h` — option
  registration.
