# AC Analysis

`anac.h`/`coreac.h` (with `lib/anac.cpp`/`lib/coreac.cpp`) implement AC analysis: the standard small-signal frequency sweep. It solves an operating point, linearizes the circuit into a complex admittance matrix that depends on frequency, and reports the complex phasor response of every unknown at each swept frequency.

## Class and Data Structures

`AC` is the typedef `SmallSignal<ACCore, AcData>` ([Small-Signal Analysis Template](ansmsig.md)), pairing an `OperatingPointCore` ([Operating Point Analysis](anop.md)) with `ACCore`. `AcData` (`anac.h`) supplies the `DataMixin` slot's storage:

- `acMatrix` - a `KluComplexMatrix` ([KLU Matrix](klumatrix.md)), the frequency-dependent complex system matrix, sharing the op core's real Jacobian sparsity pattern but factored independently at each frequency.
- `acSolution` - the complex phasor solution vector, one entry per unknown plus the bucket.

`ACCore` (`coreac.h`) is the concrete `AnalysisCore`. It keeps references to the op core's real Jacobian and solution/state vectors (`dcJacobian`, `dcSolution`, `dcStates`) in addition to its own `acMatrix`/`acSolution`. `ACParameters` embeds `OperatingPointParameters opParams` and the frequency-sweep fields (`from`, `to`, `step`, `mode`, `points`, `values`) consumed directly by a `ScalarSweep` ([Sweep Support](answeep.md)) - unlike a `sweep` clause, this sweep is a fixed part of the AC analysis itself, not a generic nested parameter sweep.

`SmallSignal<ACCore, AcData>` is specialized in `anac.cpp` for its constructor, `resolveSave()`, and `dump()`.

## Core Algorithm

Same governing equation as the rest of this section (comment block in `coreac.h`):

```
            d
   f(x(t)) + ---- q(x(t)) = 0
            dt
```

with `x(t)` the unknowns, `f(x)` the resistive residual, `q(x)` the reactive residual. AC analysis assumes `t=0`, solves the operating point `f(x0) = 0` via `opCore_`, then linearizes around `x0` by computing the resistive Jacobian `Jr = df/dx` (already available from the op core) and the reactive Jacobian `Jc = dq/dx` (evaluated once here with `evaluateReactiveJacobian = true`). At each swept frequency it forms and solves

```
   (Jr + omega Jc) X = U
```

for `omega = 2 pi f`. `U` comprises the AC excitations given by the `mag`/`phase` parameters of independent sources (`phase` in degrees; `mag` may be negative, equivalent to adding 180 degrees to `phase`). The resulting phasor vector `X` corresponds to sinusoidal responses: a signal `A cos(omega t + phi)` is represented by the phasor `A exp(j phi)`.

Per frequency point, `ACCore::coroutine()`:

1. Copies `Jr` into the real part of `acMatrix` and zeroes its imaginary part (the real part never changes across the sweep, since it comes straight from the already-evaluated op Jacobian, including shunt resistors).
2. Loads the imaginary part scaled by `omega` (`LoadSetup::reactiveJacobianFactor`) and the AC residual into `acSolution`, then negates it (it was loaded as a residual and needs to sit on the right-hand side).
3. Refactors `acMatrix` if it was already factored from the previous frequency point, falling back to a full factorization on refactor failure (or if not yet factored); optionally checks the reciprocal condition number (`rcondcheck`) against a singular-matrix threshold.
4. Solves for `X` and, if `solutioncheck` is set, verifies the solution is finite.
5. Writes an output point (if enabled), then advances the frequency sweeper.

Frequency values are drawn from a `ScalarSweep` set up directly from `ACParameters` (`from`/`to`/`step`/`mode`/`points`/`values` - see [Sweep Support](answeep.md) for the four supported shapes: stepped, `lin`, `dec`/`oct`, and explicit `values`). This is independent of, and can be combined with, a `sweep` clause attached to the analysis. See [Operating Point Analysis](anop.md) for how nodesets seed the initial op solve.

## Parameters

`ACParameters` (`coreac.h`), registered for introspection in `coreac.cpp`:

| Parameter | Type | Description |
|---|---|---|
| `from` | Real | Start frequency (step and `lin`/`dec`/`oct` sweeps). |
| `to` | Real | Stop frequency (step and `lin`/`dec`/`oct` sweeps). |
| `step` | Real | Step size for a stepped sweep. |
| `mode` | Id | `lin` (linearly spaced, `points` intervals), `dec` (points per decade), or `oct` (points per octave). |
| `points` | Int | Interval/point-per-decade/point-per-octave count for `mode`-based sweeps. |
| `values` | Value | Explicit vector or list of frequency values, instead of `from`/`to`/`step`/`mode`/`points`. |
| `write` | Int | Write the AC results to a file (default 1). |
| `writeop` | Int | Exposes `opParams.write`: write the operating-point results to a separate `<name>.op` file (default 0). |
| `nodeset` | Value | Exposes `opParams.nodeset`: stored-solution slot name to read, or a list of nodesets for the op solve. |
| `store` | String | Exposes `opParams.store`: stored-solution slot name to write after the op solve. |

Exactly one of `values`, `mode`, or `step` selects the sweep shape, mirroring `ScalarSweep::setup()` in [Sweep Support](answeep.md).

## Recognized Save Directives

Resolved in `SmallSignal<ACCore, AcData>::resolveSave()` (`anac.cpp`). Anything not in the first table falls through to `resolveOpSave()` (shared operating-point saves, written to the separate `.op` output file).

| Save directive | Arguments | Description |
|---|---|---|
| `default` | none | All circuit unknowns' complex phasor values (`addAllUnknowns()`); the fallback when no save directive produces any descriptor. |
| `full` | none | Complex phasor value of every non-ground node. |
| `dv` | one node name | Complex phasor voltage at the given node. |
| `di` | one instance name | Complex phasor branch current of the given instance (`<inst>:flow(br)`). |

Regardless of `save` directives, `ACCore::addCoreOutputDescriptors()` always adds a `frequency` descriptor (unless output is suppressed), so the swept frequency is always present in the output alongside whatever was saved.

| OP save directive (via `resolveOpSave()`) | Arguments | Description |
|---|---|---|
| `opdefault` | none | All circuit unknowns, operating-point values. |
| `opfull` | none | Every non-ground node, operating-point values. |
| `v` | one node name | Operating-point voltage at the given node. |
| `i` | one instance name | Operating-point branch current of the given instance. |
| `p` | instance name, outvar name | One instance output variable's operating-point value. |

## Related Documentation

- [Small-Signal Analysis Template](ansmsig.md) - the `SmallSignal<CoreClass, DataMixin>` template this analysis instantiates.
- [Operating Point Analysis](anop.md) - the dependency core providing the DC bias point and nodeset mechanism.
- [Analysis Core Base Class](core.md) - `AnalysisCore`, the save-directive helpers, and the core pipeline.
- [Sweep Support](answeep.md) - `ScalarSweep`, reused here directly for the frequency axis.
- [KLU Matrix](klumatrix.md) - `KluComplexMatrix`, the complex system matrix factored at each frequency.
- [DC Incremental Analysis](andcinc.md) - the single-operating-point counterpart of this analysis (no frequency dependence).
- [AC Small-Signal Transfer Function Analysis](anacxf.md) - the per-source transfer-function/impedance counterpart of this analysis.
