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 foundEach 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.diagnosticsBinding 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:
| Reason | What the action named |
|---|---|
:no_such_bus | a bus the network does not state |
:no_such_branch | a branch the network does not state |
:ambiguous_branch | several branches, with no circuit to tell them apart |
:no_such_transformer_3w | a three winding transformer the network does not state |
:ambiguous_transformer_3w | several three winding transformers |
:no_such_machine | a machine id the bus does not state |
:no_such_shunt | a shunt the bus does not state |
:no_such_load | a load the bus does not state |
:unrecognized | a 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 subsystemReference
PowerIO.ContingencySet — Type
ContingencySetThe 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.
PowerIO.SubsystemSet — Type
SubsystemSetThe 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.
PowerIO.MonitoredSet — Type
MonitoredSetThe 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.
PowerIO.ContingencyResolution — Type
ContingencyResolutionOne 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: oneContingencyCaseResultper 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.
PowerIO.ContingencyCaseResult — Type
ContingencyCaseResultWhat 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.
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.
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.
PowerIO.resolve_contingencies — Function
resolve_contingencies(net::BalancedNetwork, cases::ContingencySet) -> ContingencyResolution
resolve_contingencies(net::BalancedNetwork, text::AbstractString) -> ContingencyResolutionBind 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.
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.
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".