# Native voltage/temperature-dependent capacitor

This fork models voltage-dependent capacitors (foundry MIM caps written as
`C = f(v)`) as a **native, charge-conserving device**, instead of letting ngspice
expand them into a behavioral differentiator. It is the capacitor counterpart of
the [native poly resistor](../native_polyR/), and it is what the voltage-
dependent-cap support was originally built for: **convergence**, not speed.

## The problem: the behavioral `C=f(v)` differentiator

ngspice realizes a behavioral voltage-dependent capacitor by *differentiating* —
it rewrites `C n1 n2 C={f(v)}` into an internal network (a large VCCS, a tiny
reference capacitor, a sense branch, and a `B`-source) that measures a current
and rescales it. That construction has two failure modes on real designs:

- **Gigaamp internal currents + femtosecond RC.** The expansion used a `1e9`
  VCCS transconductance with a `1e-9` resistor, so the internal node carries
  currents ~`1e9 x Vcap` — **gigaamps**, a pure numerical artifact — and forms a
  `1e-9 x 1e-6 = 1e-15 s` (femtosecond) RC that forces femtosecond timesteps.
  Result: **"Timestep too small"**, often right at `t = 0`.
- **Conditioning collapse at scale.** Across thousands of such caps in coupled
  feedback the matrix conditioning degrades; the run aborts with the same
  message (immediately under a fast direct solver, or after tens of ns
  otherwise).

A single behavioral cap converges; a large mesh of them in feedback does not.

## The native device

The capacitor device carries the voltage dependence directly, with `vc1`/`vc2`
coefficients alongside the existing `tc1`/`tc2` (model **and** instance level):

```
C(V) = C0 * (1 + vc1*V + vc2*V^2)          (x tc1/tc2 temperature factor)
```

realized **charge-conservingly**: the device stores the integrated charge

```
Q(V) = C0 * (V + vc1*V^2/2 + vc2*V^3/3)
```

as its integration state and stamps the differential capacitance `dQ/dV` into
the Jacobian. No auxiliary nodes or branches, no differentiator — so no gigaamp
currents and no femtosecond RC. This is how commercial simulators
(Eldo / HSPICE / Spectre) do it: `Q = integral C dV` as the state, `I = dQ/dt`,
charge-conserving with physically-scaled LTE.

### Companion-model note (matters for large `vc`)

For a nonlinear `Q(V)` the Newton companion current is `I = geq*V + ceq` with
`geq = ag0 * dQ/dV`, so the constant must be `ceq = I - geq*V` (`I = dQ/dt`). The
generic integrator helper returns `I - ag0*Q`, which assumes `Q = C*V` (true only
for a *linear* cap) and otherwise leaks a spurious current `~ag0*(dQ/dV*V - Q)`
that grows with `|V|` and with `1/dt`. The device recomputes `ceq = I - geq*V`;
this is identical for a linear cap, is negligible at typical MIM coefficients
(`vc ~ 1e-4/V`), and becomes essential for large coefficients.

## Dangling compensation capacitors (topology reduction)

A separate but related failure: opamp compensation caps often hang on a
degree-1 node with no DC reference. As the timestep shrinks such a node becomes
ill-conditioned and triggers a spurious "Timestep too small". A generic
topology-reduction pass (`CKTtopologyReduce`, `set no_topo_reduce` to disable)
removes dangling passive leaves from the matrix and pins the floating node with a
unit diagonal — the Eldo "KEEPDANGLING off" behavior. It counts node degree over
*every* device type, so a live node can never be pruned.

## Evidence

- **Single cap, identical terminal current, ~16 orders of magnitude cleaner
  internally.** For one voltage-dependent cap the terminal current is the same
  either way, but the behavioral realization's internal sense/VCCS branch
  currents reach ~`1e4`-`1e9 A` while the charge formulation's internal current
  is ~`1e-12 A` — the behavioral internal currents are pure numerical artifact.
- **A large design that aborts behaviorally runs natively.** The behavioral
  `C=f(v)` form aborts "Timestep too small"; the native charge device clears that
  wall, runs substantially further, and is ~2x faster at that stage (fewer nodes
  and no fragile differentiator). Topology reduction then removes the remaining
  dangling-cap aborts.

## Using it

Convert a behavioral model library once (see [`tools/tsmc2ngspice`](../../tools/tsmc2ngspice/)),
which emits `.model … C (… vc1 vc2 tc1 tc2)` cards plus value-only cap instances;
then run the design unchanged. Or write the native form by hand:

```
.model cmod C tnom=25 vc1=... vc2=... tc1=... tc2=...
c1 n1 n2 cmod 'C0'        ; C0 = base capacitance (no v(), no temper)
```

## Validation

`cap_native_selftest.cir` (generic, non-PDK coefficients) drives a native cap and
the equivalent behavioral `C=f(v)` with a sine and compares terminal currents:
they agree to integration-tolerance level (~1e-4 relative), and the native
current is zero at the sine peaks (`dV/dt = 0`), as a capacitor must be. Run:

```
ngspice -b cap_native_selftest.cir
```

## Note

This documentation and the self-test use only generic coefficients. Foundry
model files and PDK coefficient values are proprietary and are **not** part of
this repository.
