Contingency Reference

Public API

Sparlectra.ContingencyCaseType
ContingencyCase

One contingency to evaluate: the outage of a single network element.

Fields

  • name::String: display name of the case (defaults to the element name).
  • kind::Symbol: :branch (a line/transformer outage) or :gen (a generator outage).
  • element::String: for :branch, the branch component name as reported by getCompName(branch.comp), resolved against net.branchVec; for :gen, the generator prosumer's component name, resolved against net.prosumpsVec. Both can share a component name (parallel circuits, several units at one bus), so generateN1Branches / generateN1Generators disambiguate duplicates as "<name>#<index>" and the resolver verifies the index carries that name before use.
  • weight::Float64: a per-case importance/probability weight (default 1.0), carried through to the ContingencyResult so a weighted ranking can read it. Set it via the 4-argument constructor, or attach outage rates in bulk with applyContingencyWeights / readContingencyWeightsCSV. A weight of 0 is a pure ranking weight, NOT a skip switch: the case is still solved and reported, and only a weighted ranking neutralizes it. To leave an outage out of the batch entirely, drop it at generation time with the generateN1Branches screening filters.
source
Sparlectra.ContingencyResultType
ContingencyResult

Outcome of one ContingencyCase evaluated by runContingencies!.

Fields

  • name::String: the case name.
  • weight::Float64: the originating case's importance/probability weight (default 1.0), carried through unchanged for weighted ranking.
  • converged::Bool: whether the post-outage power flow converged.
  • iterations::Int: Newton iterations of the contingency solve (0 when the solve never ran; the sum across ladder stages that were attempted).
  • start_used::Symbol: the start-value ladder stage that converged (:warm, :apslf, :dc, or :flat), :none for a failed case.
  • max_vm_pu::Float64 / min_vm_pu::Float64: voltage envelope over the non-isolated buses of the solved case (NaN when not solved).
  • max_branch_loading_pct::Float64: maximum loading over all rated branches (|S| at either end against sn_MVA); NaN when the case did not solve or no branch carries a finite rating.
  • severity::Float64: a rankable severity, weight · max(0, max loading - 100) for a converged case (0 when nothing is overloaded), and NaN for a failed case so the failures sort to the TOP. This is what printContingencyResults ranks by, giving weight its consumer.
  • overloads::Vector{OverloadRecord}: the branches loaded above 100 percent, each with its loading and the delta to base, worst first.
  • voltage_violations::Vector{String}: bus names outside the [vm_min_pu, vm_max_pu] band.
  • island_count::Int: AC islands of the post-outage topology (0 when the net could not be evaluated).
  • shed_load_mw::Float64: load disconnected by islanding (the total load in reference-less islands); 0.0 when the case solves or does not island.
  • error::Union{Nothing,String}: nothing on success; otherwise the failure in one line ("islanded without reference", the solver status, or the exception message). Failures are REPORTED, never thrown.
source
Sparlectra.OverloadRecordType
OverloadRecord

One overloaded branch under a contingency, collected per case in ContingencyResult.overloads so a reporting consumer reads the numbers directly instead of parsing a string.

Fields

  • name::String: the branch component name.
  • loading_pct::Float64: post-contingency loading, 100 · s_MVA / sn_MVA.
  • loading_base_pct::Float64: the same branch's loading in the SOLVED base case (before the outage); NaN when the base did not converge or the branch was unrated at base. The reference that makes the post-outage value readable.
  • delta_pct::Float64: loading_pct - loading_base_pct (a jump from 40 % to 105 % is a very different event than 98 % to 105 %); NaN if the base is unknown.
  • s_MVA::Float64: post-contingency apparent power, max(|S_from|, |S_to|).
  • sn_MVA::Float64: the branch rating the loading is measured against.
source
Sparlectra.applyContingencyWeightsMethod
applyContingencyWeights(cases, weights; default = 1.0) -> Vector{ContingencyCase}

Return a copy of cases with each case's weight set from weights (an AbstractDict mapping case name to a non-negative number), looked up by case.name. Cases whose name is absent from weights get default. Useful to attach per-branch outage rates read with readContingencyWeightsCSV before calling runContingencies!; ContingencyCase is immutable, so this builds new cases rather than mutating in place.

source
Sparlectra.buildContingencyReportMethod
buildContingencyReport(results; top = 10) -> ContingencyReport

Summarize a runContingencies! batch: count cases by outcome, the total and worst load shed, the worst single branch loading across all cases, and the top branches overloaded by the most contingencies (ties broken by name).

source
Sparlectra.generateContingenciesFromFOR001Method
generateContingenciesFromFOR001(net::Net) -> Vector{ContingencyCase}

Map the FOR001 contingency branch names imported from MATPOWER metadata (mpc.for001_contingencies, stored on net.for001Contingencies) to ContingencyCases. Names that do not resolve to a branch are still listed; runContingencies! reports them as failed cases with an actionable error instead of dropping them silently.

source
Sparlectra.generateN1BranchesMethod
generateN1Branches(net::Net; include_transformers = true,
                   min_vn_kV = 0.0, min_sn_MVA = 0.0, name_pattern = nothing)
    -> Vector{ContingencyCase}

Generate one branch-outage ContingencyCase per in-service branch of net (aggregate status == 1; partially open branches are skipped, their outage is already half-effective). With include_transformers = false only AC-line branches are listed; a branch counts as a transformer when its component type is Trafo (net-builder path) or its winding ratio is nonzero (the classical MATPOWER indicator: line rows carry ratio = 0).

Optional screening filters keep only the outages worth simulating on a large grid (all default to "no filter"):

  • min_vn_kV: keep a branch only if the HIGHER of its two endpoint voltages is at least this value (so a step-down transformer touching the EHV level is kept). Screens N-1 down to the transmission grid.
  • min_sn_MVA: keep a branch only if it carries a finite sn_MVA rating of at least this value. Unrated branches are dropped when this filter is active.
  • name_pattern: keep a branch only if its component name matches this Regex or contains this substring.
source
Sparlectra.generateN1GeneratorsMethod
generateN1Generators(net::Net; min_pg_MW = 0.0, name_pattern = nothing)
    -> Vector{ContingencyCase}

Generate one generator-outage ContingencyCase (kind = :gen) per in-service generator prosumer of net (isGenerator, i.e. an injection: a generator, external-grid feed-in, or synchronous machine). A generator outage removes ONLY that one unit's injection; the topology is unchanged, so the lost active power must be picked up by the slack (or, with distributed_slack_enabled = true on runContingencies!, shared over the surviving participants). Removing a bus's last voltage-regulating source demotes it to PQ, and removing the island's only reference is reported as islanded without reference with the stranded generation named.

Optional screening filters (default to "no filter"):

  • min_pg_MW: keep a generator only if |Pg| is at least this value (skips tiny or zero-output units).
  • name_pattern: keep names matching a Regex or containing a substring.

Generator component names are not unique (several units can share one name or bus); duplicates are disambiguated as "<name>#<prosumerIndex>".

source
Sparlectra.printContingencyResultsMethod
printContingencyResults([io::IO], results::Vector{ContingencyResult}; max_rows = 50)

Print the contingency results as a fixed-width table (style of printShortCircuitResult): case, convergence, iterations, voltage envelope, worst loading, violation counts, shed load, weight, severity, and the error line for failed cases. Rows beyond max_rows are summarized in one line.

By default (sort_by = :severity) the rows are ranked so the failed cases come first and the converged cases follow by descending severity (weight · max(0, loading - 100)); this keeps the worst contingency on the first page of a long list. Pass sort_by = :none to keep the input order (the order writeContingencyResultsCSV always uses).

source
Sparlectra.readContingencyWeightsCSVMethod
readContingencyWeightsCSV(path) -> Dict{String,Float64}

Read per-case outage weights from a two-column CSV mapping case name to weight. The delimiter is auto-detected as ; or , (semicolon wins when both appear). A header line naming the columns (e.g. name;weight) is recognized and skipped; blank lines and lines beginning with # are ignored. Feed the result to applyContingencyWeights. Throws an ArgumentError on a malformed row or a negative / non-finite weight.

source
Sparlectra.runContingencies!Method
runContingencies!(net::Net, cases::Vector{ContingencyCase};
                  vm_min_pu = 0.9, vm_max_pu = 1.1,
                  maxIte = 30, tol = 1e-8,
                  rescue_ladder = [:warm],
                  parallel_enabled = nothing, parallel_max_tasks = nothing,
                  parallel_min_work_items = nothing,
                  kwargs...) -> Vector{ContingencyResult}

Evaluate branch-outage contingencies. The base net is NEVER mutated: a solved template copy is created first (the warm start), and each case works on its own deepcopy of that template, removes its branch via removeBranch!, solves, and evaluates the [vm_min_pu, vm_max_pu] band plus branch loadings against sn_MVA ratings. Cases are returned in input order; failures (no convergence, islanding without reference, unknown element) are REPORTED in the result, never thrown.

rescue_ladder is the per-case start-value ladder (#331): an ordered, duplicate-free subset of (:warm, :apslf, :dc, :flat), default [:warm] (the pre-#331 single warm solve). Each stage is ONE bounded runpf! on the case-local net with a distinct start recipe, tried in order until one converges; the winning stage is reported as start_used:

  • :warm: the template (base-case) voltages;
  • :apslf: an APSLF start (the dropped with a warning when the extension is not loaded);
  • :dc: flat magnitudes with DC-projected start angles;
  • :flat: flatstart = true.

The full solver rescue ladder (:settled_qlimits, :autodamp) is NOT used per case, only for the base case (below).

When the base case does not converge, it is retried through the solver rescue ladder (runpf! with rescue = true) before falling back to a flat template with a warning. Remaining kwargs... are forwarded to the :warm/:flat/:dc contingency solves (the :apslf config path does not forward them).

retry_flat_start is DEPRECATED (kept one minor cycle): retry_flat_start = true now just appends :flat to the ladder.

The batch fans out over Julia threads in runtime.parallel.max_tasks chunks (gated by runtime.parallel.*; the three parallel_* keywords override the active configuration). Parallel and serial runs produce identical results.

screening_mode / screening_margin_pct (scenario task D5): :off (default, bit-identical to the pre-screening behavior) solves every case fully and returns Vector{ContingencyResult}; :flag estimates every non-islanding outage first on the base Jacobian factorization and fully solves only flagged cases; :only keeps the estimates. With screening active the return type is Vector{ScenarioResult} (screened marks rows whose metric columns carry estimates). The contingency.screening.* configuration (default :flag) drives the service path, not this programmatic default.

source
Sparlectra.writeContingencyResultsCSVMethod
writeContingencyResultsCSV(path::AbstractString, results::Vector{ContingencyResult})

Write the contingency results as a semicolon-separated CSV (one row per case, always in input order): name, weight, converged, iterations, the start-value ladder stage that converged, voltage envelope, worst loading, severity, the overloaded branches (each name@loading%, the per-branch delta to base lives in the structured overloads field), voltage-violation list, island count, shed load in MW, and the error text. Returns path.

source
Sparlectra.UndoLogType
UndoLog

The reversible record of one apply!: the field writes and structural deletions in application order, plus the flag that derived state (bus types, Q-limit tables, node injection sums, isolated-node registry) must be recomputed after the replay.

source
Sparlectra.apply!Method
apply!(net, ops, index) -> UndoLog

Apply the patch operations to the working copy net, addressing components through the ScenarioIndex, and record every change. Derived state (node injection sums, bus types, Q-limit tables, the isolated-node registry) is refreshed once after the patches, exactly as the importers do after construction.

source
Sparlectra.restore!Method
restore!(net, undo) -> Nothing

Replay the undo log in reverse: reinsert every deleted element at its recorded position and write back every recorded field value, then refresh the derived state once. After this the working copy equals the base bitwise on every field a patch can touch (the step-2 acceptance test checks exactly that with the SCF reflection comparison).

source
Sparlectra.validate_scenariosMethod
validate_scenarios(set, index, net) -> ScenarioSet

The net-aware validation of step 2 on top of the structural one: a tap_pos patch is rejected at load time when the transformer carries an ACTIVE tap controller (the message names it), and accepted only where a ratio tap changer exists. Tap-step scenarios on unregulated transformers are a first-class case (maintainer decision 1 of the scenario task).

source
Sparlectra.ScenarioResultType
ScenarioResult

Outcome of one scenario under an engine with screening (design decision D8): the full ContingencyResult surface (all its fields forward) plus screened (true when the full run was skipped and the metric columns carry the screening estimates) and screening_estimate (the estimate tuple: estimated worst loading, voltage envelope, and the screening-step mismatch before and after; nothing when no estimate was computed).

source
Sparlectra.runScenarios!Method
runScenarios!(net, set::ScenarioSet; index::ScenarioIndex, kwargs...) -> Vector{ContingencyResult}
runScenarios!(net, scenarios::Vector{Scenario}; index::ScenarioIndex, kwargs...)

Evaluate a scenario set on net (design decision D9): validate against the index and the net (active tap controllers reject a tap_pos patch), expand the N-1 modes through the existing generators, and run every scenario on the engine. A scenario that is a single status-0 outage reproduces runContingencies! exactly; general patch scenarios (setpoint changes, load scalings, multi-op outages) run the same ladder and metrics on the patched working copy. net is never mutated. The keyword surface matches runContingencies! (vm_min_pu, vm_max_pu, maxIte, tol, rescue_ladder, the parallel_* overrides); remaining keywords reach the per-scenario power-flow solves. Results are returned in scenario order; failures are reported in the result, never thrown.

Screening (design decision D5): with screening_mode = :flag every non-islanding single outage is estimated first with one Woodbury-corrected Newton step on the base factorization, and only scenarios whose estimate comes within screening_margin_pct of a limit (or whose screening step did not reduce the mismatch) get the full solve; :only reports the estimates without full runs where an estimate exists. The programmatic default is :off (every scenario fully solved, bit-identical to the pre-screening engine); the service path applies the contingency.screening.* configuration, whose default is :off as well (screening is a deliberate opt-in). With screening active the return type is Vector{ScenarioResult} (the ContingencyResult surface plus screened and screening_estimate).

source
Sparlectra.writeContingencyResultsCSVMethod
writeContingencyResultsCSV(path, results::Vector{ScenarioResult})

The ContingencyResult CSV with the two screening columns appended (D8): screened and the compact estimate max_loading_pct|vmin_pu|vmax_pu (empty when no estimate was computed). With screening :off the engine returns plain ContingencyResult rows and the classic writer keeps the historical byte-identical format.

source
Sparlectra.PatchOpType
PatchOp

One patch operation of a scenario (design decision D1): op is :status, :set or :scale, target names the component class, id the SCF component id. :status carries value (0 or 1); :set carries field (one of p, q, vm_pu, tap_pos, b_pu, angle_deg) and value; :scale carries factor and applies it to p and q together unless field names one of them. No structural operations exist by design.

source
Sparlectra.ScenarioIndexType
ScenarioIndex

The id map of one typed case: SCF component id to component class and the INTERNAL index the built network uses (branch vector position for branches, prosumer position for appliances, shunt position for shunts), so a patch resolves without name lookups.

source
Sparlectra.ScenarioIndexMethod
ScenarioIndex(case::SCFCase) -> ScenarioIndex

Build the id map of case: lines and links map to :branch and :link, generic branches to :transformer, sources to :external_grid, symgen to :generator, symload to :load, shunts to :shunt. Internal indices come from the recorded build-order fields of the extra block.

source
Sparlectra.ScenarioSetType
ScenarioSet

An ordered vector of scenarios plus the selection mode (:explicit, :n1_branches, :n1_generators, :n1_all) and the exclusions the N-1 expansion honors. The N-1 modes expand to explicit scenarios at load time (expand_scenarios), so the engine sees one kind of input.

source
Sparlectra.expand_scenariosMethod
expand_scenarios(set, net, index) -> Vector{Scenario}

Expand the N-1 modes to explicit scenarios through the EXISTING generators (generateN1Branches, generateN1Generators), honoring exclusions by case element name; :explicit returns the stored scenarios unchanged. Every expanded scenario carries exactly one status patch, which is what makes N-1 the special case of the model.

source
Sparlectra.scenario_set_from_contingenciesMethod
scenario_set_from_contingencies(raw, case) -> ScenarioSet

Map a legacy sparlectra.contingencies block onto the scenario model (design decision D3): mode and exclusions carry over, and every explicit case becomes one scenario with a status patch per outage component id.

source
Sparlectra.scf_case_scenariosMethod
scf_case_scenarios(case) -> Union{Nothing,ScenarioSet}

The scenario set a typed case carries: the scenarios block when present, otherwise the legacy contingencies block mapped onto the scenario model, otherwise nothing.

source
Sparlectra.validate_scenariosMethod
validate_scenarios(set, index) -> ScenarioSet

Load-time validation of design decision D1: every operation must name a known id whose class admits the operation and field; a scenario needs a non-empty op list and a unique name. Errors carry the scenario name and the op index. Returns set unchanged on success.

source

Internals

Sparlectra._validate_contingency_ladderMethod
_validate_contingency_ladder(ladder; context) -> Vector{Symbol}

Validate a contingency start-value ladder: a non-empty, duplicate-free ordered subset of (:warm, :apslf, :dc, :flat). Throws an ArgumentError naming context otherwise. Shared by the contingency.rescue_ladder config section and the runContingencies! keyword so both reject the same inputs.

source
Sparlectra.ScenarioEngineType
ScenarioEngine

The scenario batch engine (design decision D4): the solved base template (never mutated after construction), the base branch loadings every result reports its deltas against, the resolved solve parameters, the optional ScenarioIndex that patch scenarios address components through, and, with screening_mode other than :off, the screening state (D5): the base Jacobian factorization plus everything one Woodbury-corrected Newton step needs. A distributed-slack batch screens on the AUGMENTED system (step 4b): lambda as the extra state, a generator outage zeroes the lost unit's participation factor and renormalizes the rest (a rank-1 column update the generic correction covers), and a participating unit whose estimated output leaves its P band flags the scenario. screen === nothing with screening requested means the state could not be built (base not converged, residual inconsistent, voltage-dependent injections, no surviving participant); every scenario then gets the full run.

source
Sparlectra.ScenarioEngineMethod
ScenarioEngine(net; vm_min_pu, vm_max_pu, maxIte, tol, ladder, index, pf_kwargs)

Build the engine template from net: solve the base case (with the full solver rescue ladder as fallback, then a flat template with a warning, exactly the old runContingencies! preamble), clear the solver status and Q-limit logs the workers must not inherit, and record the base branch loadings. net itself is never mutated.

source
Sparlectra.ScenarioWorkerType
ScenarioWorker

One reusable working copy of the engine template. After every evaluation the worker is reset to the template state (patch undo plus solver-state reset), so the next scenario starts from exactly the state a fresh deepcopy would have provided.

source
Sparlectra._reset_scenario_worker!Method
_reset_scenario_worker!(work, template) -> Nothing

Reset a working copy to the template state after an evaluation. The patch itself is already undone (undo log replay, prosumer reinsertion); what remains is everything a SOLVE mutates: node voltages and types, branch status/flow/loss fields, prosumer write-backs, the isolated-node registry, slack rows, Q-limit tables and logs, loss accumulators, and the solver status. The step-3 acceptance test proves the reset with the SCF reflection comparison: after evaluations with solves, the worker equals the template bitwise on every compared field. Transformer tap CONTROLLER state is not reset here: scenario nets with active tap controllers are rejected at validation time (step 2), so no engine path moves a tap.

source
Sparlectra.evaluate!Method
evaluate!(engine, worker, item) -> ContingencyResult

Evaluate one work item on the worker's net and reset the worker afterwards. An outage item reproduces the pre-engine contingency semantics exactly: a branch outage opens both terminals (equivalent to the old structural removal for the Y-bus, isolation marking, and metrics, all of which treat a fully open branch as absent), and a generator outage runs _remove_contingency_generator! with the deleted prosumer reinserted afterwards. A patch item applies its operations through apply! with the documented scenario semantics.

source