Build sparse DC susceptance matrices used by reduction and DC power flow.
Two flavors:
build_b_for_reduction
Models the full-model bus susceptance matrix used by the Kron
reduction, mirroring Initiation.m / BuildYMat.m:
- branch susceptance ``b = 1/x`` (tap ratio ignored)
- branch shunts (``mpc.branch[:, 4] / 2``) added to **both diagonal
entries**
- bus shunts ``mpc.bus[:, BS] / baseMVA`` added to the diagonal
build_b_for_dcpf
Models the DC power flow B' matrix used by both LoadRedistribution.m
and the standalone DC PF:
- branch susceptance ``b = 1 / (x * tap)`` (``tap = 0`` is rewritten
to ``1`` per MATPOWER convention)
- **no** branch or bus shunts
- phase shifters return an injection vector added to the RHS
build_b_for_reduction
build_b_for_reduction(case: PowerCase) -> sp.csc_matrix
Build the symmetric bus susceptance matrix used by the Kron reduction.
Parameters:
| Name |
Type |
Description |
Default |
case
|
PowerCase
|
The full input case. Buses must already be sorted by bus ID
(see :func:simplenet.preprocess.preprocess).
|
required
|
Returns:
| Type |
Description |
csc_matrix
|
N_bus x N_bus sparse CSC matrix. Buses are 0-indexed in
the order of case.bus rows.
|
Source code in src/simplenet/ymatrix.py
| def build_b_for_reduction(case: PowerCase) -> sp.csc_matrix:
"""Build the symmetric bus susceptance matrix used by the Kron reduction.
Parameters
----------
case
The full input case. Buses must already be sorted by bus ID
(see :func:`simplenet.preprocess.preprocess`).
Returns
-------
sp.csc_matrix
``N_bus x N_bus`` sparse CSC matrix. Buses are 0-indexed in
the order of ``case.bus`` rows.
"""
n = case.n_bus()
_, id_to_idx = _internal_bus_indices(case)
rows: list[np.ndarray] = []
cols: list[np.ndarray] = []
data: list[np.ndarray] = []
if case.n_branch():
f_idx, t_idx = _branch_endpoint_indices(case, id_to_idx)
x = case.branch[:, BR_X]
with np.errstate(divide="raise", invalid="raise"):
line_b = 1.0 / x
shunt_half = case.branch[:, BR_B] / 2.0
rows.append(f_idx)
cols.append(t_idx)
data.append(-line_b)
rows.append(t_idx)
cols.append(f_idx)
data.append(-line_b)
diag_contrib = line_b + shunt_half
rows.append(f_idx)
cols.append(f_idx)
data.append(diag_contrib)
rows.append(t_idx)
cols.append(t_idx)
data.append(diag_contrib)
bus_shunt = case.bus[:, BS] / case.base_mva
rows.append(np.arange(n))
cols.append(np.arange(n))
data.append(bus_shunt)
row_arr = np.concatenate(rows) if rows else np.zeros(0, dtype=np.int64)
col_arr = np.concatenate(cols) if cols else np.zeros(0, dtype=np.int64)
val_arr = np.concatenate(data) if data else np.zeros(0)
return sp.csc_matrix((val_arr, (row_arr, col_arr)), shape=(n, n))
|
build_b_for_dcpf
build_b_for_dcpf(case: PowerCase) -> tuple[sp.csc_matrix, np.ndarray]
Build the DC power flow matrix and phase-shifter injection vector.
Parameters:
| Name |
Type |
Description |
Default |
case
|
PowerCase
|
|
required
|
Returns:
| Name | Type |
Description |
B |
csc_matrix
|
N_bus x N_bus sparse CSC bus susceptance matrix used for
the DC power flow equation B theta = P_net.
|
P_shift |
ndarray
|
Per-bus injection contribution from phase-shifting
transformers, in per-unit on baseMVA.
|
Source code in src/simplenet/ymatrix.py
| def build_b_for_dcpf(
case: PowerCase,
) -> tuple[sp.csc_matrix, np.ndarray]:
"""Build the DC power flow matrix and phase-shifter injection vector.
Parameters
----------
case
The input case.
Returns
-------
B : sp.csc_matrix
``N_bus x N_bus`` sparse CSC bus susceptance matrix used for
the DC power flow equation ``B theta = P_net``.
P_shift : np.ndarray
Per-bus injection contribution from phase-shifting
transformers, in per-unit on ``baseMVA``.
"""
n = case.n_bus()
_, id_to_idx = _internal_bus_indices(case)
rows_l: list[np.ndarray] = []
cols_l: list[np.ndarray] = []
data_l: list[np.ndarray] = []
p_shift = np.zeros(n)
if case.n_branch():
f_idx, t_idx = _branch_endpoint_indices(case, id_to_idx)
x = case.branch[:, BR_X]
tap = case.branch[:, TAP].copy()
tap[tap == 0] = 1.0
b = 1.0 / (x * tap)
rows_l.append(f_idx)
cols_l.append(t_idx)
data_l.append(-b)
rows_l.append(t_idx)
cols_l.append(f_idx)
data_l.append(-b)
rows_l.append(f_idx)
cols_l.append(f_idx)
data_l.append(b)
rows_l.append(t_idx)
cols_l.append(t_idx)
data_l.append(b)
shift_rad = case.branch[:, SHIFT] * np.pi / 180.0
if np.any(shift_rad):
inj = shift_rad * b
np.add.at(p_shift, f_idx, -inj)
np.add.at(p_shift, t_idx, inj)
if not rows_l:
return sp.csc_matrix((n, n)), p_shift
row_arr = np.concatenate(rows_l)
col_arr = np.concatenate(cols_l)
val_arr = np.concatenate(data_l)
return sp.csc_matrix((val_arr, (row_arr, col_arr)), shape=(n, n)), p_shift
|