# Sweep Support

`answeep.h`/`answeep.cpp` implement the generic mechanism by which any analysis can be repeated over one or more swept parameters, options, or circuit variables - the machinery behind a `sweep` clause attached to an analysis (`op`, `dc`, `ac`, `tran`, ...). It defines `ScalarSweep`, which enumerates the values of a single sweep, and `ParameterSweeper`, which drives a (possibly nested) collection of `ScalarSweep`s and applies their values to the circuit.

## Data Structures

`SweepSettings` is the parameter structure for one `sweep` clause: it names the sweep (the name under which its values appear as a vector in the raw output file), identifies what is being swept (an instance parameter, a model parameter, a simulator option, or a circuit variable), and specifies the range or value list. It is populated through the `Introspection`/`IStruct` machinery ([Parameterized Objects](parameterized.md)) from the parsed `PTSweep` parameter list, so its fields double as the recognized keywords of a `sweep` clause. See the Parameters table below.

`ScalarSweep` knows how to enumerate the values of a single sweep once `SweepSettings` has been resolved into concrete numbers. It supports four `SweepType`s:

- **Stepped** - `setupSteppedSweep(from, to, step)`, a fixed linear step; the point count is derived from the range and step (rounding so that `to` is not necessarily hit exactly).
- **Lin** - `setupLinearSweep(from, to, points)`, `points` linearly spaced intervals between `from` and `to` (`points+1` values).
- **Log** - `setupLogSweep(from, to, factor, pointsPerFactor)`, logarithmically spaced values; `factor` is 10 for decade-based (`dec`) and 2 for octave-based (`oct`) sweeps.
- **Value** - `setupValueSweep(values)`, an explicit vector or list of values given directly.

`ScalarSweep::setup(settings)` is a template that picks one of the above based on which of `SweepSettings::values`, `mode`, or `step` was given (exactly one is allowed); it is instantiated for `SweepSettings` but written generically so other settings-like structures could reuse it. Internally a `ScalarSweep` just tracks a 0-based position `at_` and the value count `end`; `at()`/`count()`/`advance()`/`reset()` step through positions and `compute(v)` evaluates the value at the current position from the sweep's `from`/`to`/`step`/`vals`/`factor` fields.

`ParameterSweeper` owns and drives a whole `sweep` clause list (`std::vector<PTSweep>`, one nested level per entry - the last entry is the innermost sweep). For each entry it keeps: a resolved `SweepSettings`, a `ScalarSweep`, which `ParameterFamily` it belongs to (`Instance`, `Model`, `Option`, or `Variable`), and, once bound, a pointer to the target `Parameterized` object (`Instance`/`Model`/the simulator options struct, or `nullptr` for a circuit variable, which is written through the circuit directly) plus its `ParameterIndex`. `ParameterSweeper` inherits `ProgressTracker` ([Progress Reporting](progress.md)) and reports progress as the product of all nested sweep counts.

## Control Flow

The header documents the intended call order directly:

```
bind()
storeState()
reset()
repeat
  write() sweep
  invoke analysis
  advance()
write() stored state
```

- **`setup()`** evaluates each `PTSweep`'s parameter expressions into a `SweepSettings` (via `RpnEvaluationNetlistContext`, since bounds/step can themselves be expressions depending on circuit variables), determines each sweep's `ParameterFamily` from which of `variable`/`option`/`model`/`instance` was given (exactly one, or `Instance` by default), and configures the corresponding `ScalarSweep`. It computes the total point count (the product of all nested sweep counts) for progress reporting.
- **`bind(circuit, opt)`** resolves each sweep's target into an actual object: an `Instance` or `Model` looked up by name (or the toplevel instance if no instance/model name was given), a simulator option index into the `SimulatorOptions` struct passed in, or a circuit variable looked up via `Circuit::getVariable()`. Binding also validates that the swept parameter exists and, for instance/model parameters, that it is currently free (`parameterIsFree()` - not already bound to an expression), since a swept parameter's value is meant to be under the sweeper's exclusive control during the sweep. Binding must be redone whenever circuit hierarchy changes (e.g. a subcircuit rebuild driven by an outer sweep point), since instance/model pointers can be invalidated.
- **`storeState()`** reads and saves the pre-sweep value of every swept target, so the circuit can be restored to its original state once the sweep completes.
- **`reset()`** rewinds every nested `ScalarSweep` to its first point and resets progress.
- **`advance()`** steps the innermost sweep; if it is exhausted, it is reset and the next sweep out is advanced instead (odometer-style), recursing outward until a sweep advances without exhausting or all sweeps have wrapped (whole sweep done). It returns `(finished, index of the sweep level that actually advanced)` - non-wrapping resets don't count as an advance for this purpose. The returned index tells the caller which nesting level's stored continuation state (if any) needs to be updated, since only that level and everything nested inside it changed.
- **`write(types, what)`** applies either the current sweep values (`WriteValues::Sweep`) or the previously stored pre-sweep values (`WriteValues::StoredState`) to the circuit, restricted to the `ParameterFamily` bitmask given in `types` (`ParameterSweeper::ParameterFamily` is a `DEFINE_FLAG_OPERATORS` bitmask, see [Flags](flags.md), so a caller can, for instance, write only `Option` family sweeps before circuit elaboration and `Instance`/`Model` family sweeps after). It returns `(ok, changed)`, where `changed` reports whether any instance or model parameter actually changed value - the caller uses this to decide whether re-elaboration is needed.
- **`update(advancedSweepIndex)`** re-evaluates the `SweepSettings` (and reconfigures the `ScalarSweep`) of every sweep nested inside the one that just advanced, since an outer sweep's new value may be referenced by an inner sweep's `from`/`to`/`step`/`values` expressions (see "Nesting" below). Sweeps outer than or equal to `advancedSweepIndex` keep their already-computed settings.

`sweepName(ndx)`, `valueIndex(ndx)`, and `compute(ndx, v)` expose per-level sweep identity/position/value to callers such as the output layer.

## Nesting and Expression Dependence

Multiple `sweep` clauses on one analysis nest, with the last one in `ptSweeps` as the innermost loop (it advances fastest, matching the `advance()` odometer order). Because a sweep's own bounds can be expressions over circuit variables, and outer sweeps write their value into the circuit before inner sweeps are set up, a change made by an outer sweep point is automatically visible to any inner sweep whose settings expressions reference it - `Analysis` calls `update()` (via `Analysis::updateSweeper()`) after every `advance()`, passing the index of the sweep level that advanced, so only sweeps nested inside it get their settings and `ScalarSweep` recomputed.

## Applying Sweep Values to the Circuit

`ParameterSweeper` never writes to the circuit on its own initiative; `Analysis::run()` (`lib/an.cpp`) drives it explicitly on each sweep point:

1. `circuit.elaborateChanges()` is called with the sweeper and `ParameterSweeper::WriteValues::Sweep`, which internally calls `write()` to push the current sweep-point values into the bound instance/model parameters, options struct, or circuit variables, then re-elaborates the circuit if anything changed (this is where `changed` from `write()` matters: no-op sweep points skip re-elaboration).
2. If elaboration reports the hierarchy changed (a swept parameter drove a subcircuit rebuild), the sweeper is re-bound, since the old `Instance`/`Model` pointers may no longer be valid.
3. Once the whole sweep is done, `write(..., WriteValues::StoredState)` restores the pre-sweep values saved by `storeState()`.

A sweep-swept simulator option overrides any expression given for that option outside the analysis, on the reasoning that an explicit sweep is a stronger statement of intent than a default expression.

## Interaction with the Analysis Base Class

Every [`Analysis`](an.md) instance owns a `ParameterSweeper sweeper` member, constructed from `ptAnalysis.sweeps()` (the parsed `sweep` clauses attached to that analysis). `Analysis::run()` performs the full `setup()`/`bind()`/`storeState()`/`reset()` sequence once, then loops: apply the current sweep point, run one full pass of the analysis's core coroutine, advance the sweeper, and repeat until `advance()` reports the whole sweep finished. Analyses that support state continuation across sweep points (see `Analysis::storeState()`/`restoreState()`) consult `sweeper.continuation(level)` and `sweeper.innermostSweepPosition()` to decide whether the previous NR solution can seed the next point instead of starting cold - `ParameterSweeper::continuation(i)` simply reflects each sweep's `continuation` setting. `sweeper.progress()` feeds the debug log and the CLI progress reporter (`sweeper.install()`), and each `ScalarSweep`'s current value is exposed as an output vector through `OutputSource`'s `ParameterSweeper*` constructor (`sweeperPtr->compute()`/`valueIndex()`), which is how swept values end up as named vectors in the raw output file alongside the analysis's own results.

If an analysis has no `sweep` clauses at all, `Analysis::run()` skips the sweeper loop entirely and runs the core coroutine once.

## Parameters

Fields of `SweepSettings`, as read from a `sweep` clause:

| Field | Type | Description |
|---|---|---|
| `name` | Id | Name of the sweep; appears as the vector name holding swept values in the raw output file. |
| `instance` | Id | Name of the instance whose parameter is swept (toplevel instance parameter if `model` is also absent). |
| `model` | Id | Name of the model whose parameter is swept. |
| `parameter` | Id | Name of the instance or model parameter to sweep. |
| `option` | Id | Name of the simulator option to sweep. |
| `variable` | Id | Name of the circuit variable to sweep. |
| `component` | Int | Vector component to sweep; not supported yet (must be left unset). |
| `from` | Real | Starting value (stepped/lin/log sweeps). |
| `to` | Real | End value (stepped/lin/log sweeps). |
| `step` | Real | Step size for a stepped sweep (selects stepped mode when given, without `mode`). |
| `mode` | Id | Sweep mode when using a point count: `lin` (linear), `dec` (points per decade), or `oct` (points per octave). |
| `points` | Int | Number of intervals (`lin`) or points per decade/octave (`dec`/`oct`) when `mode` is given. |
| `values` | Value | Explicit vector or list of sweep values, used instead of `from`/`to`/`step`/`mode`/`points`. |
| `continuation` | Int | Enables continuation mode (reuse of the previous point's converged state as the initial guess), 1 by default. |

Exactly one of `instance`, `model`, `option`, or `variable` may be given per sweep clause; exactly one of `values`, `mode`, or `step` may be given to specify the sweep shape.

## Related Documentation

- [Analysis Base Class](an.md)
- [Parameterized Objects](parameterized.md)
- [Flags](flags.md)
- [Progress Reporting](progress.md)
