# Analysis Core Base Class

`AnalysisCore` (`core.h`/`core.cpp`) is the base class an analysis's computational "core" is built on - the object that actually holds the numerical state (Jacobian, solution history, solver) and runs it, as opposed to `Analysis` ([Analysis Base Class](an.md)), which drives sweeps, parameters, and output files around it. An analysis such as operating point or transient owns one or more concrete cores (`OperatingPointCore`, `TransientCore`, ...) derived from `AnalysisCore`; some analyses combine cores that depend on each other (e.g. AC analysis uses an operating-point core to establish the bias point before its own small-signal core runs).

## Role and Relation to `Analysis`

An `AnalysisCore` is constructed with a reference back to an `OutputDescriptorResolver` (`parentResolver`), the `Circuit`, and `CommonData` ([Namespace and Types](common.md)). `parentResolver` is normally the owning `Analysis` itself (`Analysis` derives from `OutputDescriptorResolver`), which is how a core delegates resolving an output descriptor it doesn't recognize back up to the analysis (`resolveOutputDescriptor()`, invoked via `parentResolver.resolveOutputDescriptor(...)` inside a concrete core's `resolveOutputDescriptors()`). `Analysis` holds each of its cores as a plain member (not a pointer or virtual base handle) and forwards its own hook methods to them by calling the concrete core's methods directly - `AnalysisCore` establishes the common naming and signature convention every core's methods follow, but `Analysis` dispatches to the concrete type, not through `AnalysisCore&`. This is why most of `AnalysisCore`'s pipeline methods below are plain (non-`virtual`) member functions with harmless do-nothing default bodies: they document the shape a derived core is expected to redefine, while a genuinely polymorphic subset (state storage and the homotopy interface, see below) is declared `virtual` because generic code elsewhere - sweep and homotopy bookkeeping - operates on cores only through `AnalysisCore` references.

## Control Flow

A core participates in the same per-run and per-sweep-point sequence that `Analysis::coroutine()` drives (see [Analysis Base Class](an.md)). In order:

1. **`preMapping()`** runs before output descriptors exist, to check whether the core needs new sparsity-map entries or state-vector slots; it returns a pair `(ok, needMapping)`.
2. **`populateStructures()`** adds those entries (Jacobian sparsity, state-vector slots) if `preMapping()` said they were needed, and sets up any NR-solver forces that require extra off-diagonal matrix elements.
3. **`clearOutputDescriptors()`**, **`addOutputDescriptor()`**/**`addOutputDescriptor()`** (move overload), and the `add*` save handlers (see Save-Directive Helpers below) build up the core's list of output descriptors from `save` directives and from `addCoreOutputDescriptors()` (core-specific descriptors such as time or frequency, added regardless of what was saved) and `addDefaultOutputDescriptors()` (a fallback descriptor set used when no save directive produced anything).
4. **`rebuild()`** is called once the descriptor list is settled and before the core runs: it rebuilds Jacobian structures, binds device instances to their Jacobian entries, and calls the NR solver's `rebuild()` if the core uses one.
5. **`resolveOutputDescriptors()`** converts the accumulated output descriptors into concrete `Output::SourcesList` entries (`outputSources`), using the `add*OutputSource()` helpers for descriptor types the core itself understands, and delegating anything else to `parentResolver`.
6. **`initializeOutputs()`**/**`finalizeOutputs()`** open and close the core's output file(s) once per sweep run; **`deleteOutputs()`** removes them if the analysis failed and strict-output cleanup is requested.
7. **`run()`** (or, for coroutine-driven cores, a `coroutine()` returning a `CoreCoroutine`) executes the actual numerical work - e.g. an operating-point solve or one transient time step - and reports a `CoreState` (`Uninitilized`, `Aborted`, `Stopped`, `Finished`) back to the owning analysis.

## Data Structures

- **`CoreState`**/**`CoreCoroutine`**: the state enum a core's coroutine yields, and the [`Generator`](generator.md)-based coroutine type built on it.
- **`OutputDescriptorResolver`**: a one-method interface (`resolveOutputDescriptor()`, default fails) that both `AnalysisCore` (fanning unresolved descriptors up to the parent) and `Analysis` (implementing that fallback) participate in.
- **`CoreStateStorage`**: one saved slot - an `AnnotatedSolution` plus `coherent`/`valid` flags - used by sweep continuation and homotopy to stash and later restore a core's solution. `coherent` means the slot's contents still match the current circuit topology; it is cleared by `makeStateIncoherent()` whenever a core rebuild invalidates previously stored solutions, and set again the next time the slot is written.
- **`AnalysisCore::Error`**: a small enum (`NodeNotFound`, `InstanceNotFound`, `OutputSpec`, `OutputType`, `InstanceNotSource`) covering the failure modes of the save-directive and output-source helpers, paired with `errorId`/`errorId2` for the offending name(s) and formatted into a `Status` by `formatError()`.

## Parameters

`AnalysisCore` itself defines no parameter structure - a core's parameters (if it has any of its own, such as `OperatingPointParameters`) are a plain struct defined in the concrete core's header and passed into the core's constructor by reference, owned by the analysis. `AnalysisCore` only carries a reference to `CommonData` (`commons`, exposed via `commonData()`), the simulator-wide options and tolerance state shared by every analysis and core (see [Namespace and Types](common.md)).

## State Storage and the Homotopy Interface

These members are declared `virtual` in `AnalysisCore` (unlike the pipeline methods above) because sweep continuation and homotopy algorithms address a core generically:

- **`stateStorageSize()`**, **`allocateStateStorage(n)`**, **`deallocateStateStorage(n)`** manage the `coreStates` vector of `CoreStateStorage` slots; `allocateStateStorage()` returns the index of the first newly allocated slot and marks new slots incoherent and invalid.
- **`storeState(ndx, storeDetails)`**/**`restoreState(ndx)`** write/read a slot; the default implementations are no-ops (`return true`), left for a concrete core to override when it has state worth preserving across sweep points.
- **`makeStateIncoherent(ndx)`** marks a slot's contents stale without discarding them - used after a core rebuild, so a subsequent `restoreState()` can still recover variable names for remapping even though the numeric state no longer applies directly.
- **`runSolver(continuePrevious)`**, **`iterations()`**, **`iterationLimit(continuePrevious)`** are the homotopy-facing entry points: `runSolver()` returns `(converged, abort)`, and the other two report the last run's iteration count and the applicable iteration cap. The base defaults are inert (`(true, false)`, `0`, `0`); a core with an NR solver (operating point, transient, ...) overrides them.

## Save-Directive Helpers and Output Descriptors

Rather than have every concrete core reimplement parsing of common `save` directive shapes, `AnalysisCore` provides shared handlers that a core's own `resolveSave()`-style logic (invoked from the owning analysis's `an<suffix>.cpp`) can call directly:

| Helper | Save shape handled |
|---|---|
| `addAllUnknowns()` | all circuit unknowns (representative nodes), no arguments |
| `addAllNodes()` | all non-ground nodes, no arguments |
| `addNode()` | a single named node |
| `addFlow()` | an instance's branch current, `<inst>:flow(br)` |
| `addInstanceOutvar()` | one instance output variable, `<inst>.<outvar>` |
| `addAllTfZin()` | `tf`/`zin` descriptors for every independent source, no arguments |
| `addTf()` | transfer function relative to one named source |
| `addZin()` | input impedance at one named source |
| `addYin()` | input admittance at one named source |
| `addAllNoiseContribInst()` | per-instance noise contributions for every instance with noise sources, optionally with per-source detail |
| `addNoiseContribInst()` | noise contribution of one named instance, optionally one named noise source |

Each helper validates the `PTSave` argument shape (via `expectedSaveArgumentsError()` on mismatch), builds one or more `OutputDescriptor` values (see `output.h`), and adds them with `addOutputDescriptor()`, which silently ignores a descriptor whose name duplicates one already present. `savesCount` counts how many save directives (of any kind) produced at least one descriptor, which is what a core's `addDefaultOutputDescriptors()` checks before falling back to a default save set.

A parallel family of `add*OutputSource()` helpers - `addRealVarOutputSource()`, `addComplexVarOutputSource()` (both overloaded for a plain `Vector` and for a `VectorRepository`, with `stride`/`offset` for interleaved complex storage), and `addOutvarOutputSource()` - convert a resolved descriptor into an `Output::SourcesList` entry once the underlying node, unknown index, or instance output variable is looked up; each takes a `strict` flag controlling whether an unresolved name is a hard error or a silently-empty output source.

`getDiffNodePair()` and `getExcitation()` are lower-level lookups shared by several of the helpers above: the former turns a save argument (a single node name, or a two-element node-pair vector) into an `UnknownIndex` pair for a differential quantity; the latter resolves an instance name to an `Instance*` and checks it is actually an independent source, for `tf`/`zin`/`yin`-style saves.

No save-directive table specific to a single analysis belongs here - these are the generic building blocks; concrete analyses' recognized `save` names are documented per-analysis in their own docs, alongside the `an<suffix>.h`/`core<suffix>.h` pair that defines them.

## Related Documentation

- [Analysis Base Class](an.md) - the class that owns and drives a core.
- [Namespace and Types](common.md) - `CommonData`, `UnknownIndex`, and related shared types.
- [Coroutine Generator](generator.md) - the `Generator` template behind `CoreCoroutine`.
- [Progress Reporting](progress.md) - `ProgressTracker`, the base `AnalysisCore` inherits for reporting run progress.
