# Device, Instance, and Model Base Classes

`devbase.h`/`devbase.cpp` define `Device`, `Model`, and `Instance` - the abstract base classes every device type (builtin or OSDI) plugs into the simulator through. They form a three-level hierarchy that mirrors the netlist itself: one `Device` per device *type* (`resistor`, an OSDI-loaded compact model, ...), one `Model` per `model` statement (or subcircuit definition) built from that type, and one `Instance` per instance placement built from that model.

## The elaboration/simulation pipeline

`Device` declares a fixed sequence of virtual methods, each with a harmless do-nothing default, that together form the whole elaboration-and-simulation pipeline a device type opts into by overriding only what it needs:

- **`setup()`** sets parameter defaults and (for instances) computes node collapsing, reporting whether the unknown count or matrix sparsity changed.
- **`setStaticTolerances()`** registers static (non-switch-branch) tolerances.
- **`collapseNodes()`** merges nodes that turn out to be electrically identical (e.g. a zero-resistance branch).
- **`populateStructures()`** allocates Jacobian matrix entries and state-vector slots.
- **`preAnalysis()`** runs computations that depend on other, already-set-up instances/models, immediately before an analysis starts.
- **`bind()`** stores pointers to where each Jacobian contribution will be written at load time, using `KluMatrixAccess` (defined in `klumatrix.h`, not documented separately here) so the same code path handles real and complex (AC) matrices.
- **`eval()`/`load()`/`evalAndLoad()`** evaluate an instance's equations and add the results into the linear system, driven by [`EvalSetup`/`LoadSetup`](elsetup.md).

`Device` fans each of these out across its `Model`s and their `Instance`s; concrete device types typically don't override `Device`'s versions directly but instead the finer-grained hooks provided by [the builtin device template framework](devbuiltin.md) or by the OSDI classes.

## Dirty-tracking flags

`DeviceFlags` (`IsValid`, `GeneratesDCIncremental`, `GeneratesAC`, `Bypassable` - the latter three mark, respectively, a device type that injects DC incremental excitation, one that injects AC small-signal excitation, and one whose instances may be bypassed during NR iteration), `ModelFlags` (`NeedsSetup`, `IsValid`), and `InstanceFlags` (`ParamsChanged`, `NeedsSetup`, `HasSetupHistory`, `IsValid`, `LimitingApplied`, `HasDeviceHistory`, `OutputConverged`, `Bypassed`) - all via [`FlagBase`](flags.md) - drive incremental re-elaboration: a parameter write sets `ParamsChanged`/`NeedsSetup`, and `setup()` only redoes work for models/instances that actually need it, rather than re-elaborating the whole circuit on every change. `force` parameters threaded through the pipeline let a caller override this when a full rebuild is genuinely required (e.g. after a model-level change that must propagate to every instance).

## `Model` and `Instance`

`Model` and `Instance` both inherit `Parameterized` ([Parameterized Objects](parameterized.md)), so every device type's parameters are gettable/settable by name or index uniformly, and both carry a reference to their `PTModel`/`PTInstance` parse-tree node ([Circuit Representation and Construction API](parseroutput.md)) for location/redefinition-checking purposes. `parameterIsFree(name)` checks whether a given parameter was left at its default (not given as an expression) by scanning the parsed parameter list - noted in the source as a linear search that could be made faster.

`Device::createModel()`/`Model::createInstance()` are the factory methods (pure virtual, one implementation per device family) that turn a parsed `PTModel`/`PTInstance` into a live `Model`/`Instance` owned by the `Circuit`.

## Hierarchical name translation

`Instance::translate(child)` prepends the instance's own name and a `:` separator to a child name (no-op for the toplevel instance, which has no parent) - the mechanism that flattens nested subcircuit instantiation into unique global names (`top:sub1:r1`). `translateNode()` does the same for node names, except global nodes (including ground) are left untranslated, since those are meant to be shared across the whole hierarchy. `translatePeer()` translates a name in the *parent's* naming scope instead of the instance's own - for referring to a sibling rather than a child.

## Hierarchy traversal: `HierarchicalIterator`

A hand-rolled forward iterator (the header notes coroutines would be more elegant, referencing [the coroutine `Generator`](generator.md) used elsewhere) that walks an instance's whole subhierarchy depth-first: `operator++` either descends into a hierarchical instance's children (if it has any and `stopDescent()` hasn't been called) or advances to the next peer, popping back up through the traversal stack when peers are exhausted. `stopDescent()` lets a caller skip descending into the current instance's children on the next increment - for hierarchy-aware code that wants to prune a subtree without walking it.

## Context and hierarchy building

`enterContext()`/`revertContext()` push/pop a [`ContextStack`](context.md) scope for evaluating a hierarchical instance's own parameter expressions and those of its descendants (the base class default just returns the current stack marker unchanged, since a non-hierarchical instance has no scope of its own - it evaluates in its parent's). `subhierarchyChanged()`, `propagateParameters()`, `deleteHierarchy()`, and `buildHierarchy()` are the hooks a hierarchical device (a subcircuit instance) implements to detect when a parameter change requires rebuilding its subinstances, propagate parameter values down to immediate children, and tear down/rebuild the subtree accordingly - the base class defaults implement the "not hierarchical" case (nothing to propagate, nothing to rebuild).

## The source, output variable, and noise APIs

Three optional feature surfaces a device type opts into by overriding virtuals that otherwise default to "not applicable":

- **Source API** (`sourceExcitation()`, `sourceResponse()`, `scaledUnityExcitation()`, `responseScalingFactor()`, `isSource()`, `isVoltageSource()`): lets voltage and current sources plug into the same equation-formulation and small-signal/AC excitation machinery uniformly - a voltage source returns the equation index carrying its constraint equation as its "excitation" target and its branch current as its "response" unknown; a current source returns the two node unknowns it drives as excitation and the node-pair voltage as response. The extensive doc comments in the header spell out the sign conventions in detail.
- **Output variable API** (`outvarCount()`, `outvarIndex()`, `outvarName()`, `outvarType()`, `getOutvar()`, `outvarOutputSource()`): exposes named, read-only internal quantities (e.g. a resistor's dissipated power) that can be `save`d like any other simulation output, independent of the instance's own settable parameters.
- **Noise API** (`noiseSourceCount()`, `noiseSourceName()`, `noiseSourceType()`, `loadNoise()`, ...): exposes the instance's individual noise sources (thermal, flicker, ...) for noise analysis to sum.

## `getInternalNode()`

A protected helper `Instance` subclasses use to create or retrieve an internal node by name, translating it through the instance's own hierarchical name first, then tagging it `InternalDeviceNode` (see [Nodes](node.md)) - internal device nodes aren't meaningful convergence-check targets on their own, and the residual-based convergence tests skip any node carrying that flag.

## Legacy SPICE tolerance helpers

`updatePotentialNodeSpiceTolerances()`/`updateFlowNodeSpiceTolerances()` are protected helpers that register a node's classic SPICE tolerance quantities (`vntol`/`fluxtol` for a potential node's unknown and `abstol`/`chgtol` for its equation, or the reverse for a flow node - see [Nodes](node.md)' `PotentialNode`/`FlowNode` flags) with `CommonData`, expressed as [`NatureTolerance`](natures.md) pairs tagged with the builtin `NatureRegistry::spiceVoltage`/`spiceCurrent`/`spiceFlux`/`spiceCharge` identities. This is how a device that only knows about the legacy SPICE tolerance options (rather than declaring its own Verilog-A nature) still participates in the same per-nature tolerance tracking every OSDI device's natures go through (see [Natures](natures.md)).
