DC Power Flow Reference

Sparlectra._DcBranchTermType
_DcBranchTerm

One in-service branch's contribution to the DC power-flow matrices: branch_idx/from/to identify it, b is its DC series susceptance (1/(x_pu*tap)), and shift_rad is its phase-shift angle in radians (0 for a plain line or an off-nominal-tap-only transformer). Pf = b*(theta[from] - theta[to]) - b*shift_rad reproduces MATPOWER's Bf*theta + Pfinj per branch without assembling a separate sparse Bf matrix.

source
Sparlectra.assemble_dc_bbusMethod
assemble_dc_bbus(net::Net) -> (Bbus, Pbusinj, terms::Vector{_DcBranchTerm})

DC power-flow susceptance matrix and phase-shift injection vector, ported from MATPOWER's makeBdc.m (BSD-3-Clause; algorithm only, no code copied). Series reactance only — resistance, shunt conductance/susceptance, and line charging are all ignored, per the lossless DC model. Tap magnitude divides the series susceptance; phase shift enters only as an injection term, never as part of Bbus itself (MATPOWER's DC convention).

Reads Branch.tap_ratio/Branch.phase_shift_deg — the branch's live effective tap this network was last stamped with — gated by Branch.ratio != 0.0 marking a transformer (mirrors calcBranchRatio's "line vs. transformer" convention in src/branch.jl). Only in-service (status == 1) branches contribute.

Returns the full n×n sparse Bbus (before any slack elimination — reduce with reduce_susceptance_to_nonslack using value_of = x -> -x, since that helper expects positive off-diagonal branch weights and Bbus's off-diagonals are the standard-convention -b; see solve_dc_powerflow's docstring for why), the n-length bus-level shift injection Pbusinj (per-unit), and terms, the per-branch data needed to compute branch flows after theta is solved.

source
Sparlectra.solve_dc_powerflowMethod
solve_dc_powerflow(net::Net, slack_idx::Int; angle_reference_rad=0.0, performance_profile=nothing)
    -> (theta::Vector{Float64}, Pf::Vector{Float64}, Pt::Vector{Float64}, slack_p_mw::Float64, terms::Vector{_DcBranchTerm})

Solve Bbus * theta = Pbus - Pbusinj for bus angles (radians), with the slack bus fixed. Pbus[k] = (node._pƩGen - node._pƩLoad)/baseMVA — MATPOWER's Pbus, built from specified generation/load, independent of any AC solve (shunts are deliberately excluded: the lossless DC model has no shunt term).

The reduced linear system is built assuming a zero slack angle (matching MATPOWER's dcpf.m convention and reusing reduce_susceptance_to_nonslack and _solve_dc_angle_system unmodified, shared with the rectangular solver's DC-angle start projection); angle_reference_rad is then added as a uniform shift to every bus angle afterward. This is exact, not approximate: Bbus's rows sum to zero by construction (a weighted graph Laplacian), so Bbus*(theta .+ c) == Bbus*theta for any constant c — fixing the slack angle to a nonzero reference is mathematically equivalent to solving with a zero reference and shifting the whole result afterward.

Note on reduce_susceptance_to_nonslack's value_of: the helper reconstructs the reduced Laplacian purely from Bsrc's off-diagonal entries (its own diagonal is ignored), and expects those off-diagonal entries to already be the positive per-branch weight — exactly what imag(Ybus) off-diagonals are for the legacy AC-start heuristic. Bbus (from assemble_dc_bbus) is a real MATPOWER-style Laplacian with negative off-diagonals (-b, matching makeBdc's actual Bbus, which must stay that way since it's a documented public return value), so value_of = x -> -x is passed here to flip it back to the positive convention the helper expects — value_of = identity would silently double-negate every angle and flow.

Pf[l] = b_l*(theta[from_l] - theta[to_l]) - b_l*shift_rad_l, in MW after scaling by baseMVA; Pt = -Pf (lossless, not an independent unknown). slack_p_mw is the slack bus's injection implied by the solved flows (its net outflow across incident branches), not read from Pbus at the slack bus (which is not part of the reduced solve).

source
Sparlectra.dc_pf_statusMethod
dc_pf_status(net::Net) -> Any

Retrieve the most recent DC power-flow status for a network (set by rundcpf!), or nothing if rundcpf! has not run on this network (or it has been garbage-collected). Kept in a registry separate from rectangular_pf_status so a DC result is never mistaken for an AC one by AC-only status readers.

source
Sparlectra.DcPowerFlowReportType
DcPowerFlowReport

Structured container for DC power-flow results (see rundcpf!).

Deliberately not ACPFlowReport: the DC model has no voltage magnitude solution (always 1.0 pu by definition), no reactive power, no losses (exactly zero, lossless model), and no iteration/tolerance concept (a direct linear solve, not a Newton iteration) — so those columns are simply absent rather than filled with placeholder values.

Fields

  • metadata: Run/case metadata (case, baseMVA, converged, elapsed_s, iterations, solver, timestamp, plus seed_ac_start/ac_converged/ ac_iterations/ac_elapsed_s when rundcpf! was called with seed_ac_start=true).
  • nodes: Per-bus angle and specified generation/load.
  • branches: Per-branch directional MW flow (p_to_MW == -p_from_MW, lossless).
source
Sparlectra._dc_seed_rectangular_angles!Method
_dc_seed_rectangular_angles!(net, pf_cfg; verbose=0, performance_profile=nothing)

Run the standalone DC power flow and write its bus angles into net as a Newton-Raphson start seed, without disturbing Slack/PV regulated voltage magnitudes. The DC model has no voltage-magnitude solution — it writes vm_pu=1.0 for every bus — which is correct for a standalone DC report but would erase Slack/PV setpoints ahead of an AC seed (initialVrect's non-flatstart path reads node._vm_pu directly, with no separate setpoint fallback). Snapshots those setpoints before the DC solve and restores them immediately after, so only the angles end up DC-seeded. Shared by rundcpf!'s seed_ac_start=true path and the config-driven power_flow.start_mode.dc_seed_unconditional path in execution.jl.

source
Sparlectra.buildDcPowerFlowReportMethod
buildDcPowerFlowReport(net::Net; ct=0.0, converged=true) -> DcPowerFlowReport

Builds a structured report from a net already solved by rundcpf! (or _run_dc_powerflow!). Reuses the same bus-name/power-component helpers as buildACPFlowReport so DC and AC reports stay visually consistent, but only includes the columns the DC model actually defines.

source
Sparlectra.printDcPowerFlowResultsMethod
printDcPowerFlowResults(net::Net, ct::Float64; toFile=false, path="")

Compact text summary of a solved DC power flow, printed to stdout (or a result_<net.name>.txt file when toFile=true, mirroring printACPFlowResults's file-output convention). Omits the Q/tap- controller/wrong-branch sections that don't apply to the DC model.

source
Sparlectra.rundcpf!Method
rundcpf!(net::Net; angle_reference_deg=0.0, verbose=0, performance_profile=nothing,
         seed_ac_start=false, ac_kwargs=NamedTuple()) -> DcPowerFlowReport

Solve the standalone, MATPOWER-rundcpf-equivalent DC power flow and write the bus angles (voltage magnitude implicitly 1.0 pu everywhere, by DC-model definition) and lossless branch flows back into net. The run is flagged via dc_pf_status — a status registry kept separate from rectangular_pf_status, so AC reporting/status code never mistakes a DC solution for an AC one. Respects power_flow.islands (each AC island is solved independently, matpower_like reference bus). Ported from MATPOWER's rundcpf/makeBdc (BSD-3-Clause; algorithm only, no code copied).

Keyword arguments

  • angle_reference_deg: uniform angle offset added to every bus after the slack-referenced solve (see solve_dc_powerflow for why this is exact, not approximate). The slack bus itself is fixed at this reference.
  • seed_ac_start: if true, after the DC solve succeeds, immediately runs the AC rectangular Newton-Raphson solve (runpf_rectangular!) seeded from the just-computed DC angles (opt_flatstart=false, so the solver picks up net's current _va_deg/_vm_pu as its starting point — the DC step already wrote these). Slack/PV voltage-magnitude setpoints (which the DC write would otherwise flatten to 1.0 pu along with every other bus) are snapshotted before the DC solve and restored onto net right before the AC call, so only the angles are DC-seeded. The AC call also defaults to damp=1.0 (full Newton steps, matching what run_sparlectra's config-driven path uses — runpf_rectangular!'s own bare keyword default, damp=0.2, is deliberately conservative for unmediated direct calls and would waste a good start on needlessly small steps). On return, net holds the AC-converged solution (not the DC one) when this option is used; the returned DcPowerFlowReport still reflects the DC step's own result (a useful before/after comparison point), with the AC outcome recorded in report.metadata.ac_converged/ac_iterations/ac_elapsed_s.
  • ac_kwargs: extra keyword arguments forwarded to runpf_rectangular! when seed_ac_start=true (e.g. (tol = 1e-9, maxiter = 40)). opt_flatstart is always forced to false and cannot be overridden this way, since that is the entire point of seeding; damp defaults to 1.0 as described above but can be overridden here.
  • verbose, performance_profile: forwarded to both the DC solve and (when seed_ac_start=true) the AC solve.

Does not support outer-loop controllers (tap/PST/shunt/... control): the static effect of a phase-shifting transformer's current phase_shift_deg is correctly represented in the DC model (it enters the B′/injection math), but no outer loop adjusts it — mirrors the existing power_flow.solver=:apslf restriction.

source