# Accounting

`acct.h`/`acct.cpp` define `Accounting`, the simulator's runtime statistics collector: call counts and wall-clock time for parsing, elaboration, device evaluation/load, linear solves, and Newton-Raphson iterations. One `Accounting` instance lives on `ParserTables` (reached via `tables().accounting()`) and is threaded by reference into the pieces of the simulator that do the work being measured, notably [KLU Matrix](klumatrix.md)'s `KluMatrixCore` (`setAccounting()`/`noAccounting()`) and the Newton-Raphson solver.

## `AcctData`

A plain struct of counters (`size_t`) and timers (`double`, seconds), one field per measured event: `parse`/`tparse`, `elab`/`telab` (full elaboration), `chgelab`/`tchgelab` (partial/change elaboration, see [Elaboration and Partial Elaboration](cirparams.md)), `evalload`/`tevalload` (device evaluate-and-load passes), `factor`/`refactor`/`solve` and their complex counterparts `cxfactor`/`cxrefactor`/`cxsolve` (each with a matching `t*` timer), `nrcall`/`nriter`/`tnr` (Newton-Raphson invocations, total iterations, total time), `accepted`/`rejected` (transient timepoints), `points` (unused by any current caller), and `bpinst`/`bpopport`/`bpbypassed` (bypass-related device evaluation counts - see below). All fields default-initialize to `0`.

`Accounting` holds five `AcctData` instances: `acctNew` (the live, continuously-accumulating totals - the only one any code currently writes to) and four snapshot slots (`acctPrevParse`, `acctPrevAnalysis`, `acctPrevSweepPoint`, `acctPrevPoint`) intended to hold earlier copies of `acctNew` for computing incremental (since-last-checkpoint) statistics. `dumpChange()` is declared for that purpose but has no definition in `acct.cpp` and is not called anywhere in the codebase - only `dumpTotal()` (cumulative stats) is wired up.

## Timing helpers

`Accounting` centralizes wall-clock measurement on `std::chrono::high_resolution_clock`:

- **`wclk()`** returns a timepoint (call it `t0` before an operation).
- **`wclkDelta(t0, update=false)`** returns the elapsed time since `t0` in seconds; with `update=true` it also resets `t0` to now, letting a caller chain multiple sub-measurements from a single starting timepoint by re-arming it after each one.
- **`timeDelta(begin, end)`** is the two-timepoint form used internally.
- **`resolution()`** reports the clock's tick period in seconds (`stats` prints this scaled to nanoseconds).

Every accounted operation follows the same pattern: capture `t0 = wclk()` before the work, increment the relevant `acctNew` counter (before or after depending on the call site), do the work, then add `wclkDelta(t0)` to the matching timer field. Accounting is opt-in per matrix (`KluMatrixCore::acct` is `nullptr` unless `setAccounting()` was called) and per call site - e.g. `factor()`/`refactor()`/`solve()`/`tsolve()`/`solveBlock()`/`tsolveBlock()` in `KluMatrixCore` all check `if (acct)` before touching counters, so an unaccounted matrix pays no bookkeeping cost.

## Device evaluation timing

`devEvalLoadTimes`/`devEvalLoadCalls` are parallel vectors, one slot per device plus a trailing "overhead" slot, sized to `devices.size()+1` and filled in by `Circuit`'s evaluate-and-load pass (each device's slot accumulates its own eval/load time and call count; the last slot catches time not attributed to any single device). `dumpDevTimes()` prints per-device average time (`t/n`), and, when a device tracks separate overhead time (`novh`/`tovh`, e.g. OSDI code-generation overhead), a breakdown of that too.

Per-device timing is normally gated by the master switch `constexpr bool devacct = false` at the top of `acct.h` - call sites wrap their `acctNew`/timer updates in `if constexpr(devacct)`, so with the switch off the compiler eliminates the accounting code entirely rather than paying a runtime branch per device evaluation.

## Bypass accounting

`bpinst`/`bpopport`/`bpbypassed` count, respectively, how many bypassable device instance evaluations occurred, how many of those had an opportunity to bypass (skip re-evaluation because inputs did not change enough), and how many were actually bypassed - accumulated from the Newton-Raphson core's per-iteration bypass bookkeeping. `dumpTotal()` only prints this block when `bpinst>0`, so circuits/analyses that never enable bypass do not show a block of zeros.

## Output: `dumpTotal()`/`dumpDevTimes()`

Both take an indent (spaces) and an output stream. `dumpTotal()` prints the full `acctNew` snapshot as labeled lines (parser invocations/time, elaborations/time, partial elaborations/time, eval-and-load calls/time, NR calls/iterations/time, the bypass block if applicable, real and complex factor/refactor/solve counts and times, accepted/rejected timepoints). `dumpDevTimes()` prints the per-device breakdown described above, needing a `Circuit&` to resolve device indices to names. Both are invoked together by the `info stats` control command (`simulator/cmd.cpp`), which also prints structural circuit stats (unknown count, initial nonzero count/sparsity) and the clock resolution alongside them.

## Where the fields are set

Besides the KLU matrix and Newton-Raphson call sites already mentioned, `acctNew.parse`/`tparse` are set in the parser, `elab`/`telab` and `chgelab`/`tchgelab` in `Circuit`'s full- and partial-elaboration paths, `evalload`/`tevalload` in `Circuit`'s evaluate-and-load entry point, and `accepted`/`rejected` in the transient analysis core when a timestep is accepted or rejected by local truncation error control.
