Updates

Typed updates change a module in place. Each update names a component by ComponentId and gives an absolute value with an explicit unit. You apply updates as a batch, and the batch is validated as a whole before anything changes.

case = parse("case9.m")
load = ComponentId("load", case.value.loads[1].component_id)   # "bus-5"

report = apply_updates!(case, [
    set_load_active_power(load, ActivePower(megawatts=91.5)),
    set_load_reactive_power(load, ReactivePower(megavars=31.0)),
])

report.connectivity_changed        # false
report.changes[1].component_id     # ComponentId("load", "bus-5")
report.changes[1].field            # "load_active_power"
case.value.loads[1].p_mw           # 91.5

The quantity types ActivePower, ReactivePower, and ApparentPower take exactly one unit keyword: ActivePower(watts=...) or ActivePower(megawatts=...), ReactivePower(vars=...) or ReactivePower(megavars=...), ApparentPower(volt_amperes=...) or ApparentPower(megavolt_amperes=...).

Update constructors

Operating point updates:

FunctionField changed
set_load_active_power, set_load_reactive_powera load's demand
set_generator_active_power, set_generator_reactive_powera generator's dispatch
set_generator_voltage_magnitudea generator's voltage setpoint, per unit
set_generator_in_service, set_branch_in_serviceservice status
set_transformer_tap_ratio, set_transformer_phase_shift_degreesa transformer branch
set_switch_closeda switch position

The network update is set_branch_thermal_rating. The power setters accept terminal= to address one terminal of a multiconductor element.

Applying a batch

apply_updates! validates every update before changing anything, so a batch with an unknown component or an invalid value throws PowerIOError and leaves the module as it was. On success the module's value is refreshed and case.value reflects the change; a BalancedNetwork you obtained before the call keeps the data from before it. The returned UpdateReport lists every UpdateChange and says whether a service status or switch change altered the energized topology.

Component ids for MATPOWER sources follow the reader's convention: loads and generators are "bus-N", branches are "F-T". Read the component_id field of the element rather than assuming a convention.

Aggregate demand at one bus

apply_bus_load_active_power sets the total active demand at one bus and spreads it over the loads there, which saves naming each load when a study moves demand by bus. The rule is "proportional_to_current_active_power", which keeps each load's present share, or "equal", which splits the total evenly. The module holds a calculation instance, bus_id is the source bus number, and the call reports the same UpdateReport as a batch of updates.

dc = to_dc_opf_instance(parse("case9.m"))
report = apply_bus_load_active_power(dc, 5, ActivePower(megawatts=125.0))
length(report)                            # one change per load moved
dc.value.network.loads[1].p_mw            # 125.0

apply_bus_load_active_power(dc, 5, ActivePower(megawatts=80.0); allocation="equal")

An unknown rule throws PowerIOError with code REQUEST.CAPI.ALLOCATION_UNKNOWN, a bus with no load gives VALIDATE.UPDATE.COMPONENT_UNKNOWN, and a module holding a bare network gives REQUEST.CAPI.TYPE_MISMATCH. Each leaves the module as it was.

PowerIO.ApparentPower — Type
ApparentPower(; volt_amperes) or ApparentPower(; megavolt_amperes)

Apparent power with an explicit unit.

source
PowerIO.NetworkUpdate — Type
NetworkUpdate

One change to network data. set_branch_thermal_rating is the network update in this release.

source
PowerIO.set_load_active_power — Function
set_load_active_power(id::ComponentId, power::ActivePower; terminal=nothing)

An update that sets a load's active power. terminal selects one terminal of a multiconductor load.

source
PowerIO.apply_updates! — Function
apply_updates!(m::PioModule, updates) -> UpdateReport

Validate the whole batch of OperatingPointUpdate and NetworkUpdate values, then apply it atomically. A failed batch throws PowerIOError and leaves the module unchanged. On success m.value is refreshed; values obtained before the call keep the pre-update data.

Calls sharing the module handle are synchronized through mutation and value refresh. Values already obtained from m retain their pre-update data and remain independently readable.

source
PowerIO.apply_bus_load_active_power — Function
apply_bus_load_active_power(m::PioModule, bus_id::Integer, total::ActivePower;
                            allocation="proportional_to_current_active_power") -> UpdateReport

Set the aggregate active demand at one bus to total, spreading it over the loads at that bus by the named rule. "proportional_to_current_active_power" keeps each load's share of the present demand; "equal" gives every load the same amount. Any other token throws PowerIOError with code REQUEST.CAPI.ALLOCATION_UNKNOWN.

m holds a calculation instance, such as one to_dc_opf_instance constructed. A module holding a bare network reports REQUEST.CAPI.TYPE_MISMATCH, and bus_id is the source bus number.

Like apply_updates! this validates before it changes anything, so an unknown bus or rule leaves the module untouched. On success m.value is refreshed and the returned UpdateReport lists one UpdateChange per load it moved.

source
PowerIO.UpdateReport — Type
UpdateReport

What apply_updates! changed: changes::Vector{UpdateChange} and connectivity_changed, true when a service status or switch change altered the energized topology. length(report) is the number of changes.

source
PowerIO.UpdateChange — Type
UpdateChange(component_id, field, terminal)

One change an applied batch made: the component, the field name such as "load_active_power" or "branch_thermal_rating", and the terminal when the change targeted one.

source