Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

BMOPF field mapping

This page says what each BMOPF field becomes in PowerIO, in which unit, and which constraint or objective term it enters. It is the authority for the BMOPF converter, so if you find a field that reads or writes differently from what is written here, one of the two has a defect.

Schema versions come from the dsopt-schema repository. 0.1.0 is the version the IEEE PES Task Force on Benchmarking Multiconductor OPF accepts, and 0.2.0 is the proposal that adds the element classes 0.1.0 has no table for. PowerIO reads both and writes 0.2.0 by default; to write 0.1.0, pass BmopfEmitOptions::with_schema_version(BmopfSchemaVersion::Bmopf010).

Conventions

Every BMOPF quantity is SI and absolute: volts, amperes, watts, vars, volt-amperes, ohms, siemens, metres, radians, hertz, and a cost rate in currency per kilowatt-hour. PowerIO stores the same units with no scaling, so a mapping below gives a unit only where the two names differ.

A matrix element A_k_j is row k, column j, counting from one, and lands in row k - 1, column j - 1 of the corresponding ConductorMatrix. These matrices are symmetric, so the reader fills an unstated transpose cell from its mirror; a stated cell wins over its mirror. A per terminal array has one entry per name in the element’s own terminal map, in that order, and PowerIO keeps that order.

An absent constraint field means no constraint and reads as None; an absent parameter field is zero. A field with no typed slot lands in the element’s extras, and the reader reports it under READ.BMOPF.RETAINED_SOURCE_ONLY. Fields with no typed slot lists every one.

Document level

BMOPFPowerIONote
namePioModule value name
meta.$schemaresolved by BmopfSchemaVersion::from_schema_idAbsent raises READ.BMOPF.SCHEMA_ABSENT; a value naming no version raises READ.BMOPF.SCHEMA_UNKNOWN. Both parse, and both versions are accepted.
meta.schema_versionexplicit schema versionThe reader checks agreement with meta.$schema. Fresh proposal output pins a retrieval URL and records proposal status and schema digest in provenance.
meta.frequencyMulticonductorNetwork::base_frequency, HzAbsent defaults to 60 with READ.BMOPF.VALUE_DEFAULTED.
meta.* (the rest)MulticonductorNetwork::extras["bmopf_meta"]Re-emitted, except the three the writer owns.
terminal_conventionsMulticonductorNetwork::extras["bmopf_terminal_conventions"]Re-emitted verbatim; authored from the terminal names when the source states none.
extrasMulticonductorNetwork::extras["bmopf_extras"]Re-emitted verbatim, minus the tables the reader types out of it.

bus

DistBus, in MulticonductorNetwork::buses().

BMOPFPowerIONote
terminal_namesterminalsOrdered; fixes every per-terminal order on this bus.
perfectly_grounded_terminalsgrounded
v_min, v_maxv_min_phase, v_max_phase and scalar v_min, v_maxUnequal bounds remain ordered phase vectors through IR and bindings. A scalar edit explicitly overrides the vector; balanced lowering rejects unequal phase bounds.
vpn_min, vpn_maxvpn_min, vpn_maxPer phase terminal, kept as arrays.
vpp_min, vpp_maxvpp_min, vpp_maxPer ordered phase pair.
vpos_min, vpos_maxvpos_min, vpos_maxScalars.
vneg_max, vzero_maxvneg_max, vzero_maxMagnitude caps; the lower bound is always zero.
vn_maxvn_maxNeutral to ground cap.
longitude, latitudelocation and MulticonductorNetwork::geoThe BMOPFTools coordinate fields, outside both schema versions. Read into the coordinate space; written back only with BmopfEmitOptions::sideload_coordinates.

line and linecode

DistLine and DistLineCode.

BMOPFPowerIONote
line.bus_from, line.bus_tobus_from, bus_to
line.terminal_map_from, line.terminal_map_toterminal_map_from, terminal_map_toPosition i of the from map fixes matrix index i.
line.linecodelinecode
line.lengthlength, m
line.R_series_i_j, line.X_series_i_ja synthesized DistLineCode named after the line, ohm per metreThe inline branch states absolute ohms, so the reader divides by length to store per metre and keeps the line’s own length. READ.BMOPF.VALUE_INFERRED names the synthesis.
line.G_from_i_j, line.B_from_i_j, line.G_to_i_j, line.B_to_i_jthe same synthesized line code’s g_from, b_from, g_to, b_to
line.i_max, line.s_maxi_max, s_maxPer conductor; override the line code’s.
linecode.R_series_i_j, linecode.X_series_i_jr_series, x_series, ohm per metre
linecode.G_from_i_j, linecode.B_from_i_j, linecode.G_to_i_j, linecode.B_to_i_jg_from, b_from, g_to, b_to, siemens per metreHalf the total shunt at each end.
linecode.i_max, linecode.s_maxi_max, s_maxPer conductor, applied at both ends.
linecode.sourcesource
linecode.line_geometry, linecode.derivationextras0.2.0 fields; retained, not typed.

A line has exactly one impedance source, and the oneOf in both schema versions enforces it: either a linecode with a length, or inline R_series_1_1 and X_series_1_1 with no linecode.

switch

DistSwitch.

BMOPFPowerIONote
bus_from, bus_to, terminal_map_from, terminal_map_tothe same names
open_switchopen
i_maxi_maxPer conductor. A switch has no shunt, so both ends carry the same magnitude and one array bounds both.

load

DistLoad. Each array has one entry per branch of the load: a phase to neutral branch for WYE, the terminal pair for SINGLE_PHASE, or a line to line branch for DELTA.

BMOPFPowerIONote
bus, terminal_map, configurationthe same namesconfiguration reads case-insensitively; an unrecognized value reads as WYE with READ.BMOPF.VALUE_UNSUPPORTED.
p_nom, q_nomp_nom, q_nom, W and var
modelthe DistLoadVoltageModel variantCONSTANT_POWER, CONSTANT_CURRENT, CONSTANT_IMPEDANCE, ZIP, EXPONENTIAL.
v_nomthe variant’s v_nom
alpha_z, alpha_i, alpha_p, beta_z, beta_i, beta_pthe Zip variant’s fieldsThe three active fractions sum to one, and so do the three reactive.
gamma_p, gamma_qthe Exponential variant’s fields

generator

DistGenerator. Arrays are per phase conductor; WYE is the only configuration the specification supports.

BMOPFPowerIONote
bus, terminal_map, configurationthe same names
p_min, p_max, q_min, q_maxthe same names, W and varWhen a bound pair is equal the dispatch is pinned, and the reader states the same values as p_nom and q_nom so a power flow target has a setpoint.
s_maxs_max, VABounds the sum of squares of that phase’s active and reactive power.
i_maxi_max, APer phase, with an optional trailing entry bounding the neutral return current.
costcost, currency per kWhKept exactly as stated: one entry per phase. A bare scalar reads as a one-entry statement.

voltage_source

VoltageSource. Both versions permit exactly one.

BMOPFPowerIONote
bus, terminal_mapthe same names
v_magnitudev_magnitude, VPer terminal, phase to ground; a grounded terminal states zero.
v_anglev_angle, radPer terminal.
costextras["cost"]A 0.2.0 field; retained and re-emitted, not typed.
p_min, p_max, q_min, q_maxextrasThe same standing as cost.

shunt and capacitor

DistShunt is raw admittance, grounding impedance included; DistCapacitor is a bank with a nameplate rating.

BMOPFPowerIONote
shunt.bus, shunt.terminal_mapthe same names
shunt.G_i_j, shunt.B_i_jg, b, total siemens in conductor order
capacitor.bus, capacitor.terminal_map, capacitor.configurationthe same names
capacitor.q_ratedq_rated, varThe whole bank, not one element.
capacitor.v_nomv_nom, VLine to line for the three-phase configurations; across the element terminals for SINGLE_PHASE.

transformer

DistTransformer with windings: Vec<DistWinding>. The BMOPF subtype is kept in DistTransformer::extras["bmopf_subtype"], because the winding list alone does not pin down every subtype; a centre tap unit, for example, reads as two secondary windings.

BMOPFPowerIONote
bus_from, bus_towindings[0].bus, windings[1].bus
terminal_map_from, terminal_map_tothe corresponding terminal_mapcenter_tap expands the three to-side terminals into two windings.
v_nom_from, v_nom_towindings[k].v_ref, VFor center_tap, v_nom_to is the per leg voltage.
s_ratingwindings[k].s_rating, VA
r_series_from, r_series_towindings[k].r_pct, percent of that winding’s own baseThe base is n_phases * v_ref^2 / s_rating.
x_series_from, x_series_toxsc_pct, percentThe short-circuit test measures the series sum referred to one side, which is what this field is.
r_series, x_series (three-phase legacy)windings[0].r_pct and xsc_pctThe lumped wye-side spelling; the delta winding is lossless in this model.
tap_ratio (0.2.0), tap (retained under 0.1.0)windings[0].tap / windings[1].tapThe multiplier on the nameplate turns ratio.
tap_ratio_min, tap_ratio_maxextrasBounds have no typed winding slot.
r_neutral_from, x_neutral_fromwindings[0].r_neutral, x_neutral, ohmThe winding’s own neutral to earth branch.
r_neutral_to, x_neutral_towindings[1].r_neutral, x_neutral
g_no_load, b_no_loadextrasThe magnetising branch has no typed slot.
i_max_from, i_max_toextrasPer winding conductor of that side, in that side’s own amperes.
n_winding.windings[]one DistWinding eachbus, terminal_map, v_nom, configuration, r_winding, delta_roll, i_max.
n_winding.x_scxsc_pct, ordered 12, 13, ..., 1n, 23, ..., (n-1)nKeyed i_j with i < j in BMOPF, all referred to winding 1.
single_phase_autotransformer, open_delta_regulatorwindings plus extras["bmopf_subtype"]The regulator ratio, its bounds, the ANSI type and the open delta connection ride in extras.

Under schema 0.1.0, the nine fields that have no subtype slot (tap, tap_min, tap_max, the four winding neutral fields, and the two no-load fields) are written to extras.transformer.<subtype>.<name> and folded back on read, with each move reported under EMIT.BMOPF.RETAINED_SOURCE_ONLY. Under 0.2.0 the subtypes declare all nine, so nothing moves; only the three tap names change, to tap_ratio, tap_ratio_min, and tap_ratio_max.

OpenDSS Xscarray populates every winding-pair reactance in this order. The reader applies scalar XHL/XHT/XLT updates at edit boundaries; regenerated four-or-more-winding records use the complete Xscarray.

ibr and control_profile

DistIbr and DistControlProfile. Both are typed under either version. Because 0.1.0 has no top-level table for them, that version writes them under extras; on read they come from either place, and the top-level copy wins.

BMOPFPowerIONote
bus, terminal_mapthe same names
topologytopology: SINGLE_PHASE, THREE_LEG, FOUR_LEG
prime_moverprime_mover: PV, BATTERY, GENERIC, STATCOM, DSTATCOM
s_maxs_max, VA per phase
i_maxi_max, A per conductor
p_availp_avail, W
p_min, p_max, q_min, q_maxthe same names, per phase
control_profilecontrol_profile, an id
voltage_aggregationvoltage_aggregation: PER_PHASE, AVERAGE
costextras["cost"]Retained and re-emitted, not typed.
dc_bus, dc_terminal_map, dc_control, dc_v_set, dc_p_ref, dc_droop, dc_deadband, dc_link_coupled, p_dc_min, p_dc_maxextrasThe DC coupling fields; retained, not typed.
r_filter, x_filter, b_filter_shunt, grid_forming, v_ref_internalextrasRetained, not typed.
control_profile.power_factor.pfPowerFactorControl
control_profile.volt_var.*VoltVarControl: voltage_reference, breakpoints, q_limits, q_unit, q_ref, and the two active-power thresholds
control_profile.volt_watt.*VoltWattControl: voltage_reference, breakpoints, p_limits, p_unit, p_ref

Fields with no typed slot

The classes below read into MulticonductorNetwork::untyped() by class and name, with their properties kept as text, and are written back to the table the target version declares: the top level under 0.2.0, extras under 0.1.0. Reading each one reports READ.BMOPF.RETAINED_SOURCE_ONLY.

dc_bus, dc_branch, dc_grounding, dc_load, dc_source, time_series, wire_data, line_geometry, and a per-element time_series reference map.

They pass through and re-emit unchanged. Nothing reads their values into a calculation, so if a case’s behaviour depends on them, the instance PowerIO builds from it does not represent that case. The reader says so per class rather than leaving you to discover it.

The calculation

powerio::to_mc_ac_opf_instance builds a McAcOpfInstance through McAcOpfInstance::from_network. The instance shares the network rather than copying it: it selects which of the network’s stated limits are active constraints and which objective terms are summed, and reads the numbers from the network.

Objective. There is one term, ObjectiveTerm::ActivePowerDispatchCost, which is the specification’s default objective: the sum over every dispatchable element and every phase of that phase’s cost rate against the active power the element injects into the network. A positive cost minimises the element’s injection and a negative cost maximises it, and that holds the same way for a generator, the voltage source, and an IBR. The rate comes from the element’s own per phase cost array: a generator reads it from DistGenerator::cost, and a voltage source or IBR from its extras["cost"]. ObjectiveTerm::NetworkGeneratorCost is the term for a balanced network and is not part of a BMOPF instance.

Constraints. MulticonductorActiveConstraints selects three families, each ConstraintSelection::All by default, which matches the specification’s rule that constraints are active for the elements present.

FamilyThe BMOPF fields it activates
terminal_voltage_boundsbus.v_min, v_max, vpn_min, vpn_max, vpp_min, vpp_max, vpos_min, vpos_max, vneg_max, vzero_max, vn_max
conductor_limitsline.i_max, line.s_max, linecode.i_max, linecode.s_max, switch.i_max, transformer.i_max_from, transformer.i_max_to
generator_capabilitygenerator.p_min, p_max, q_min, q_max, s_max, i_max, and the same bounds on an IBR

A family selects by stable element identity, so a study that relaxes one line’s thermal limit refers to that line instead of restating the bound. ConstraintSelection::None relaxes a whole family, and Only lists the elements whose limits stay active.

Equipment behaviour has no selection family, because there is no limit to relax: a closed switch equates its two ends conductor by conductor, an open switch carries no current, the ideal winding pair relates its two coil voltages by the turns ratio and balances ampere-turns, and the voltage source fixes its terminal voltage with a free current. Those hold in every instance built from a network that includes them.

The solution

Solved values stay in McAcOpfSolution and are not written back into the network, so the network still describes the case and the solution describes one result of it.

McAcOpfSolutionUnit and order
terminal_voltage_magnitudeV, resolved terminal order
terminal_voltage_anglerad, resolved terminal order
terminal_current_magnitudeA, resolved terminal order, when the solver reports it
terminal_active_powerW, resolved terminal order, when the solver reports it
source_active_injectionW, per source terminal
generator_active_powerW, generator table order with each generator’s terminal map order
objectivethe optimised objective value
termination, residuals, producerhow the solve ended, its residuals, and what produced it

Checked against what

The reference data is the two published example networks, vendored at tests/data/dist/bmopf/. powerio-dist/tests/bmopf.rs checks that each one parses, that writing the result validates against the schema of the version written, that a second write is identical to the first, and that parsing the written document reproduces the model. The equations above were checked against the specification pages of math-and-data-model-specifications rather than inferred from the data.

BMOPFTools.jl is publicly available and is identified as the generator of example_ieee13.json. The PowerIO 0.11 compatibility change exercises the typed module API, explicit legacy schema version, retained diagnostics, transformer core-shunt locations and nominal n-winding imports. Its numerical suite compares power-flow voltages and transformer admittance with OpenDSS, including independently prepared BMOPF cases and native OpenDSS conversions.

PowerIO’s structural tests additionally check triangular matrix completion, conductor order, regulator fields, proposal provenance and rejected malformed records. These checks distinguish data preservation from the equations a particular solver supports; retaining a regulator or n-winding record does not imply that PowerIO’s own matrix compiler implements it.

Explicit terminal-coil no-load admittance

transformer.<subtype>.<id>.no_load_shunt holds {winding, g, b} in transformer extras and IR version 2. The one-based winding index fixes its physical location, and g + j b is siemens per coil at the terminal voltage. It cannot coexist with the existing from-side g_no_load and b_no_load fields.

OpenDSS and PMD exciting-branch percentages map to winding 2 with a negative magnetizing susceptance. Conversion uses the actual tapped WYE phase-to-neutral or DELTA phase-to-phase coil voltage and divides total transformer VA by the phase count. A winding-2 shunt converts back to those percentages; other locations report that this target parameterization cannot represent them. Legacy BMOPF output preserves the object under extras.transformer and reports the relocation. Nonzero core shunts reject the limited PowerIO passive transformer matrix profile before execution.

The independent check evals/validation/validate_bmopf_core_shunts.py compares six transformer topologies with OpenDSS Yprim through an intermediate PowerIO IR document. BMOPFTools’ 0.11 adapter uses an equivalent bus-shunt matrix and retains the original coil object in provenance. Successful parsing alone does not establish support for a transformer calculation.