Contingency analysis

PSS/E states a contingency study in three text files beside the case. PowerIO reads each one into its own value type, binds a study to a network, and writes every file back.

Vocabulary

A contingency set is the content of a .con file: named cases, each a list of outage actions such as opening a branch or removing a machine. A case may also be stated as an automatic specification, a rule that names a group of elements instead of listing them.

A subsystem is PSS/E's name for a bus selection stated in a .sub file through bus, area, zone, owner, and kV range selectors. Automatic specifications in a .con file and statements in a .mon file name subsystems.

A monitored set is the content of a .mon file: the elements whose loading and voltage a study reports.

Reading the files

parse recognizes all three formats from the file name and content, so a parsed module carries the typed value and the reader's notes:

using PowerIO

cases = parse("study.con")          # PioModule{ContingencySet}
groups = parse("groups.sub")        # PioModule{SubsystemSet}
monitored = parse("monitored.mon")  # PioModule{MonitoredSet}

length(cases.value)                 # how many cases
cases.value.cases[1]                # the first case name
groups.value.names                  # the subsystem names, in the file's order
monitored.value.statement_count     # how many statements the reader kept
cases.diagnostics                   # what the reader found

Each value writes itself back. set.text is the writer's canonical form, which normalizes spacing and quoting; emit(cases, "psse-con") reproduces the bytes the reader kept when the module still holds its source.

emit(cases, "psse-con", "copy.con")

Text in memory reads through the type's own constructor, which carries the reader's notes on the value itself:

cases = ContingencySet(read("study.con", String))
groups = SubsystemSet(read("groups.sub", String))
monitored = MonitoredSet(read("monitored.mon", String))
cases.diagnostics

Binding a set to a network

resolve_contingencies binds every action of every case to the elements of one balanced network. It reports rather than refuses: an action naming no element of the network is kept with its reason, its case counts as unresolved, and the actions of that case that did bind stay listed.

net = parse("case.raw").value
resolution = resolve_contingencies(net, cases)

resolution.cases                    # 20
resolution.resolved                 # 17
resolution.unresolved               # 3
resolution.unrecognized_statements  # 1

case = resolution[1]
case.name                           # "BR_1_2_C1"
case.components                     # [ContingencyComponent("branch", "1-2", 1, true)]
case.unresolved                     # UnresolvedAction[]

A component names the table its row indexes, 1-based, and states the element's own identity as local_id, or nothing when the network states none for that row. An unresolved action carries the .con line the writer produces for it and one of these reasons:

ReasonWhat the action named
:no_such_busa bus the network does not state
:no_such_brancha branch the network does not state
:ambiguous_branchseveral branches, with no circuit to tell them apart
:no_such_transformer_3wa three winding transformer the network does not state
:ambiguous_transformer_3wseveral three winding transformers
:no_such_machinea machine id the bus does not state
:no_such_shunta shunt the bus does not state
:no_such_loada load the bus does not state
:unrecognizeda statement the reader kept as text

A second method takes .con text directly, so a study reads and binds in one step:

resolution = resolve_contingencies(net, read("study.con", String))

Expanding automatic specifications

expand_contingencies turns every automatic specification into explicit cases over one network and one subsystem set. A specification naming a subsystem the subsystem set does not state stays in the returned set. The returned notes are the contingency reader's, then the subsystem reader's, then one note per specification that expanded into nothing.

expanded, notes = expand_contingencies(net, cases, groups)
expanded.cases        # ["EXPLICIT", "L_101_102_1", "L_102_103_1", ...]
write("expanded.con", expanded.text)
[d.code for d in notes]

The text method takes and returns text:

text, notes = expand_contingencies(net, read("study.con", String),
                                   read("groups.sub", String))

Selecting the buses of a subsystem

select_subsystem_buses evaluates one named subsystem against a network and gives its bus numbers in ascending order. The name is matched without case and without surrounding whitespace, the way a .con or .mon statement names a subsystem. A name the subsystem set does not state throws a PowerIOError with code "BIND.CAPI.INDEX_OUT_OF_RANGE".

select_subsystem_buses(net, groups, "A1")        # [101, 102, 103]
select_subsystem_buses(net, groups, " a1 ")      # the same subsystem

Reference

PowerIO.ContingencySet — Type
ContingencySet

The cases of one PSS/E .con file. set.cases are the case names in the file's own order, set.text writes the set back as .con text, set.diagnostics are the reader's notes, and length(set) is the case count. resolve_contingencies binds a set to a network and expand_contingencies turns its automatic specifications into explicit cases.

source
PowerIO.SubsystemSet — Type
SubsystemSet

The bus selections of one PSS/E .sub file. set.names are the subsystem names in the file's own order, set.text writes the set back as .sub text, set.diagnostics are the reader's notes, and length(set) is the subsystem count. select_subsystem_buses evaluates one named subsystem against a network.

source
PowerIO.MonitoredSet — Type
MonitoredSet

The statements of one PSS/E .mon file. set.statement_count is how many statements the reader kept, set.text writes the set back as .mon text, and set.diagnostics are the reader's notes.

source
PowerIO.ContingencyResolution — Type
ContingencyResolution

One contingency set bound to one balanced network, decoded in full.

  • cases: how many cases the set states.
  • resolved: how many cases had every action bind.
  • unresolved: how many cases held at least one action that did not bind.
  • unrecognized_statements: how many actions the reader kept as text, counted over every case.
  • case_results: one ContingencyCaseResult per case, in the set's own order.
  • diagnostics: the notes of the contingency set that was bound.

length, 1-based getindex, and iteration run over case_results.

source
PowerIO.ContingencyCaseResult — Type
ContingencyCaseResult

What binding one case to a network produced: its name, whether every action bound (resolved), the components the actions bound to in action order, and the unresolved actions that bound to nothing.

source
PowerIO.ContingencyComponent — Type
ContingencyComponent(component_type, local_id, row, in_service)

One network element a contingency case bound to. component_type names the table row indexes, 1-based; local_id is the element's own identity, or nothing when the network states none for that row; in_service is the element's flag before the case is applied.

source
PowerIO.UnresolvedAction — Type
UnresolvedAction(action, reason)

One action of a case that bound to no element. action is the .con line the writer produces for it, without its line ending. reason is one of :no_such_bus, :no_such_branch, :ambiguous_branch, :ambiguous_transformer_3w, :no_such_machine, :no_such_shunt, :no_such_load, :no_such_transformer_3w, and :unrecognized.

source
PowerIO.resolve_contingencies — Function
resolve_contingencies(net::BalancedNetwork, cases::ContingencySet) -> ContingencyResolution
resolve_contingencies(net::BalancedNetwork, text::AbstractString) -> ContingencyResolution

Bind every case of a contingency set to the elements of one balanced network. Binding reports rather than refuses: an action naming no element of the network is kept with its reason and its case counts as unresolved, while the actions of that case that did bind stay listed. The text form reads .con text first, so its result carries the reader's notes.

source
PowerIO.expand_contingencies — Function
expand_contingencies(net, cases::ContingencySet, subsystems::SubsystemSet) -> (ContingencySet, Vector{Diagnostic})
expand_contingencies(net, con_text::AbstractString, sub_text::AbstractString) -> (String, Vector{Diagnostic})

Turn every automatic specification of a contingency set into explicit cases over one balanced network and one subsystem set. A specification naming a subsystem the subsystem set does not state stays in the returned set. The returned notes are the contingency reader's, then the subsystem reader's, then one note per specification that expanded into nothing. The text form returns the expanded .con text.

source
PowerIO.select_subsystem_buses — Function
select_subsystem_buses(net::BalancedNetwork, subsystems::SubsystemSet, name) -> Vector{Int}
select_subsystem_buses(net::BalancedNetwork, sub_text::AbstractString, name) -> Vector{Int}

The bus numbers of one balanced network that the named subsystem selects, in ascending order. The name is matched without case and without surrounding whitespace, the way a .con or .mon statement names a subsystem. A name the subsystem set does not state throws PowerIOError with code "BIND.CAPI.INDEX_OUT_OF_RANGE".

source