# Elaboration and Partial Elaboration

`cirparams.cpp` implements [`Circuit`](circuit.md)'s incremental re-elaboration pipeline - `elaborateChanges()` and the hierarchy-wide propagation routine it relies on, `propagateDownHierarchy()` - plus the small per-instance/per-model parameter get/set API that feeds it. Full, from-scratch elaboration (`elaborate()`, called once before an analysis run starts) is implemented in `circuit.cpp` and documented in [Circuit](circuit.md). This file covers what runs instead once a circuit is already elaborated and something changes underneath it - a swept variable, an altered instance parameter, a changed option, a new analysis - so only the affected part of the pipeline is redone rather than rebuilding everything from the parsed netlist.

## Full vs. incremental elaboration

`Circuit::needsElaboration()` (declared in `circuit.h`) is true whenever the `Elaborated` flag is clear or any of `VariablesChanged`/`HierarchyAffectingOptionsChanged`/`ParametrizationAffectingOptionsChanged`/`MappingAffectingOptionsChanged`/`TolerancesAffectingOptionsChanged`/`HierarchyParametersChanged` is set (see [Circuit's `CircuitFlags`](circuit.md)). The first elaboration of a circuit always goes through `elaborate()`. Every later change - a new sweep point, a `var`/`alter` command, an `options` command, entering a new analysis - goes through `elaborateChanges()` instead, which reads back exactly which of those flags are set and does only the corresponding work.

Two overloads exist:

- The full one, used from inside an analysis's sweep loop, takes a `ParameterSweeper*` (writes swept values before evaluating anything), the `Analysis*` being run (so analysis parameter expressions and sparsity requests are updated too), an optional external `IStruct<SimulatorOptions>*`/`PTParameterMap*` pair (for analyses that carry their own options, e.g. a nested `mc` or `sweep`), and a `DeviceRequests*` forwarded to `setup()` (see [Elaboration and Setup Support](elsetup.md)).
- A simplified overload (defined inline in `circuit.h`) is for use outside of any analysis, e.g. from the command interpreter's `elaborate`/`options` commands: it builds a throwaway `CommonData`, has no sweeper or analysis, and - because the full overload only applies `optionsMap` when `VariablesChanged` is set - applies `optionsMap` up front to a clean, default-constructed `IStruct<SimulatorOptions>` before delegating, so every option not mentioned in `optionsMap` reverts to its default rather than being left as whatever it was before.

## `elaborateChanges()` control flow

The function runs a fixed sequence of stages, each guarded by whichever flags make it necessary; `s.extend()`/early-return on failure leaves the circuit only partially updated, but the caller can still retry once the underlying error is fixed since flags are only cleared on full success at the end.

1. **Sweep write-back (variables)**: if a `ParameterSweeper` was given, `sweeper->write(Variable, what, s)` writes the current (or stored) sweep point into circuit variables. `what` selects between writing the live sweep value and restoring a previously stored one (see [Sweep Support](answeep.md)).
2. **`variablesChanged`** is snapshotted from `Flags::VariablesChanged` right after that write.
3. If a sweeper and an analysis are present and variables changed, `an->updateSweeper(s)` lets any *inner* sweep (nested `sweep` clauses) pick up new bounds that depend on the outer variables.
4. **Options target**: `optPtr` is the caller-supplied `options` struct if given, otherwise the circuit's own `simOptions`.
5. If `variablesChanged`: `updateGlobalContext()` re-anchors `paramEvaluator_`'s context stack on `variables` (see [Circuit Variables Management](cirvariables.md)); expression-valued entries of `optionsMap` are applied to `optPtr`; if an analysis is present, `an->updateParameterExpressions()` re-evaluates its expression-valued parameters and `analysisParametersChanged` records whether anything actually changed (this later forces the analysis's core to rebuild).
6. **Sweep write-back (options)**: `sweeper->write(Option, what, s)` writes swept option values to wherever scalar sweeps are bound.
7. If an external `options` struct was supplied, `setOptions(*options)` copies it into the circuit (see [Options and Options Management](options.md)); this is what actually sets the four `*OptionsChanged` flags for anything that differs.
8. The four `*OptionsChanged` flags (`hierarchyAffectingOptionsChanged`, `parametrizationAffectingOptionsChanged`, `mappingAffectingOptionsChanged`, `tolerancesAffectingOptionsChanged`) are snapshotted here, after option writes are done.
9. **Sweep write-back (instance/model parameters)**: `sweeper->write(Instance|Model, what, s)`; if anything changed, `HierarchyParametersChanged` is set explicitly (the sweeper writes parameters directly, bypassing the setters in the next section that would otherwise set this flag).
10. `hierarchyParametersChanged` is snapshotted from `Flags::HierarchyParametersChanged`.
11. **Hierarchy propagation**: `propagateDownHierarchy()` (below) runs if `variablesChanged || hierarchyParametersChanged || hierarchyAffectingOptionsChanged || parametrizationAffectingOptionsChanged`, and reports `hierarchyChanged`.
12. If `hierarchyChanged`: `buildEntityLists()` and `nodeOrdering()` rebuild the device/model/instance lists and the sorted node order (see [Circuit](circuit.md)).
13. `commons.fromOptions(simOptions.core())` refreshes the `CommonData` gmin/gshunt fields `setup()` reads.
14. **Setup**: `setup(commons, mappingAffectingOptionsChanged, devReq, s)` runs if `hierarchyChanged || variablesChanged || hierarchyParametersChanged || parametrizationAffectingOptionsChanged || mappingAffectingOptionsChanged`, and reports `unknownsChanged`/`sparsityChanged`.
15. If an analysis is present, `an->preMapping(s)` reports `analysisNeedsSparsity` - whether the analysis needs extra sparsity-map entries or state-vector slots beyond the devices' own.
16. **Unknown mapping**: `mapUnknowns()` runs if `hierarchyChanged || unknownsChanged`.
17. **Sparsity/states**: `buildSparsityAndStates()` runs if unknowns were remapped or the analysis needs sparsity (`mapUnknownsNeeded || analysisNeedsSparsity`).
18. If sparsity was needed, `an->populateStructures(s)` lets the analysis claim its extra entries/states.
19. **Enumeration**: `enumerateSystem()` runs whenever step 17 ran.
20. If `unknownsChanged || tolerancesAffectingOptionsChanged`: `setStaticTolerances(commons, s)` and `commons.enumerateNatures(s)` rebuild the static tolerance tables.
21. All six change flags are cleared and `Elaborated` is set - the circuit is consistent again.
22. Return value is `(ok, hierarchyChanged, analysisBindingNeeded)`, where `analysisBindingNeeded = mapUnknownsNeeded || buildNeeded || analysisParametersChanged` tells the caller (in `an.cpp`) whether the analysis must re-bind to the (possibly relocated) sparse matrix and state vector before continuing.

Whether a core actually needs to rebuild in response to `analysisParametersChanged`/`hierarchyChanged` is decided by the caller in `an.cpp`, not here - this function only reports the raw signals.

## `propagateDownHierarchy()`

This is the routine that walks every toplevel instance's subhierarchy, pushes and pops the parameter contexts (see [Context](context.md)) needed to re-evaluate parameter expressions, and rebuilds exactly the subtrees whose active conditional blocks changed. It is the only place `elaborateChanges()` calls into instance-level hierarchy logic.

Two flags force work across the *whole* hierarchy regardless of per-instance `ParamsChanged` state:

- `forceHierarchyChangeCheck` = `HierarchyAffectingOptionsChanged` - check every instance for a hierarchy change, not just ones with changed parameters (currently no option is classified this way, see [Options and Options Management](options.md)).
- `forceParameterPropagation` = `VariablesChanged || ParametrizationAffectingOptionsChanged`, further forced on if any toplevel instance's own `ParamsChanged` flag is set.

The walk has four phases, all operating on `paramEvaluator_`'s context stack and using `Instance::enterContext()`/`Instance::revertContext()` markers so any failure can unwind the stack back to `initialContextMarker` before returning:

1. **Toplevel scan**: for each toplevel instance (`toplevelInstances_[i]`, `i = 0..n-1`), enter its stored global context (`toplevelContext_[i]`), recomputing it only if propagation is forced; if forced (or a hierarchy-affecting option changed), call `subhierarchyChanged()` on it. The context of toplevel instance 0 (the default toplevel instance) is left on the stack afterward because it is visible from everywhere in the hierarchy - see the parameter lookup order documented at the top of `circuit.h` (instance context, then toplevel instance context, then default toplevel context, then variables, then constants); every other toplevel instance's context is reverted immediately. The scan stops at the first toplevel instance whose subhierarchy changed, recording its index as `changeAt` (`changeAt = n` if none changed).
2. **Delete affected toplevel subtrees**: for `i = n-1` down to `changeAt`, re-enter that toplevel instance's context and call `deleteHierarchy()` on it, tearing down everything that will be rebuilt in phase 4.
3. **Propagate through unaffected toplevel instances** (`i = 0..changeAt-1`, i.e. instances whose *toplevel* subhierarchy did not change): walk the instance's whole subtree with `Instance::HierarchicalIterator` (`hierarchyBegin()`/`hierarchyEnd()`, declared in `devbase.h`). For every visited instance whose `ParamsChanged` flag is set (or propagation is forced): enter its context, call `subhierarchyChanged()`. If it changed, `deleteHierarchy()` + `buildHierarchy()` rebuild that instance's subtree and the iterator calls `stopDescent()` to skip over the just-rebuilt children (they don't need a separate visit); otherwise `propagateParameters()` re-evaluates expression-valued parameters of its immediate children and sets their `ParamsChanged` flag if anything changed, so the iterator picks them up on the next step. `hierarchyChanged` accumulates true if any instance anywhere reported a subhierarchy change.
4. **Rebuild affected toplevel subtrees**: for `i = changeAt..n-1`, re-enter the (already-loaded) toplevel context and call `buildHierarchy()` to instantiate the subtree from scratch via a fresh `InstantiationData`.

The function returns `(ok, hierarchyChanged)`. See [Hierarchical Devices](hierdevice.md) for what `subhierarchyChanged()`/`propagateParameters()`/`buildHierarchy()`/`deleteHierarchy()` actually do on a `HierarchicalInstance` - in short, a subcircuit instance's active `@if`/`@elseif`/`@else` blocks are recomputed from current parameter values, and a change in the *set* of active blocks is what triggers a rebuild; non-hierarchical instances never report a hierarchy change.

## Instance/model parameter API

`setInstanceParameter()`/`setModelParameter()` (single parameter, by `Id`) and `setInstanceParameters()`/`setModelParameters()` (a whole `PTParameters` group) are thin wrappers - `singleSetterHelper<T>()`/`groupSetterHelper<T>()` templates, specialized for `Instance` and `Model` via `findInstance()`/`findModel()` - around `Instance::setParameter()`/`setParameters()`. The group setters tag the write with an `RpnEvaluationNetlistContext` (`MCData::CtxType::Instance` or `CtxType::Model`, see [RPN Evaluation Context](rpnevalctx.md)) naming the target object, for diagnostics. Whenever the underlying call reports the value actually changed, the setter sets `Circuit::Flags::HierarchyParametersChanged` - this is the flag that makes `elaborateChanges()` invoke `propagateDownHierarchy()` on the next call, and it must be set manually by any code that mutates instance/model parameters without going through these setters (see the comment on `HierarchyParametersChanged` in `circuit.h`). `instanceParameter()`/`modelParameter()` (via `getterHelper<T>()`) are the read-only counterparts and do not touch any flag.

## Relation to other Circuit internals docs

- [Circuit](circuit.md) documents `elaborate()` (full build) and the `CircuitFlags` this file's functions read and clear.
- [Circuit Variables Management](cirvariables.md) documents `updateGlobalContext()` and the `VariablesChanged` flag `elaborateChanges()` checks first.
- [Options and Options Management](options.md) documents the four `*OptionsChanged` flags and the option-category maps that decide which of them a given option write sets.
- [Sweep Support](answeep.md) documents `ParameterSweeper`/`ScalarSweep`, whose `write()` calls appear at three points in `elaborateChanges()`.
- [Hierarchical Devices](hierdevice.md) documents the `HierarchicalInstance` overrides of `enterContext()`/`subhierarchyChanged()`/`propagateParameters()`/`buildHierarchy()`/`deleteHierarchy()` that `propagateDownHierarchy()` drives.
