Skip to content

Commit 54222a7

Browse files
m-bossartjd-lara
authored andcommitted
Refactor arc admittance column merging to use immutable sparse matrix re
1 parent 1b0eb46 commit 54222a7

1 file changed

Lines changed: 64 additions & 20 deletions

File tree

src/Ybus.jl

Lines changed: 64 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,7 +1374,7 @@ function _resolve_arc_admittance(
13741374
arc_remove_ixs = indexin(removed_arcs, get_arc_axis(new_y_ft))
13751375
arc_keep_ixs = setdiff(collect(1:length(get_arc_axis(new_y_ft))), arc_remove_ixs)
13761376
# Remap arc endpoint labels to surviving bus numbers. Column data was already merged
1377-
# in _merge_arc_admittance_bus_columns! before this call; only the axis labels need
1377+
# in _merge_arc_admittance_bus_columns before this call; only the axis labels need
13781378
# updating so downstream reductions can match arcs by their new bus numbers.
13791379
if !isempty(merged_bus_pairs)
13801380
for k in eachindex(arc_ax)
@@ -1445,30 +1445,73 @@ function _resolve_arc_admittance(
14451445
return existing_ft, existing_tf
14461446
end
14471447

1448-
# Transfer arc admittance contributions from the removed bus column to the surviving bus
1449-
# column before the removed bus column is sliced out. Without this, to-bus admittance
1450-
# entries for arcs that terminate at the removed bus are silently dropped.
1451-
function _merge_arc_admittance_bus_columns!(
1448+
# Add every removed bus's column into its surviving bus's column on one arc×bus admittance
1449+
# matrix, in a single O(nnz) relabel-and-sum pass. Each stored entry is emitted at its own
1450+
# column and, when that column is a removed bus, also at its survivor's column; `sparse` sums
1451+
# the collisions. Removed columns keep their own entries, since the bus-removal slice later
1452+
# drops them.
1453+
function _merge_arc_admittance_columns(
1454+
M::SparseArrays.SparseMatrixCSC{T, Int},
1455+
bus_lookup::Dict{Int, Int},
1456+
merged_bus_pairs::Dict{Int, Int},
1457+
) where {T}
1458+
ncols = size(M, 2)
1459+
col_survivor = collect(1:ncols)
1460+
for (removed_bus, surviving_bus) in merged_bus_pairs
1461+
col_survivor[bus_lookup[removed_bus]] = bus_lookup[surviving_bus]
1462+
end
1463+
rows = SparseArrays.rowvals(M)
1464+
vals = SparseArrays.nonzeros(M)
1465+
cap = 2 * length(vals)
1466+
I = Vector{Int}(undef, cap)
1467+
J = Vector{Int}(undef, cap)
1468+
V = Vector{T}(undef, cap)
1469+
n = 0
1470+
for col in 1:ncols
1471+
s = col_survivor[col]
1472+
for k in SparseArrays.nzrange(M, col)
1473+
r = rows[k]
1474+
v = vals[k]
1475+
n += 1
1476+
I[n] = r
1477+
J[n] = col
1478+
V[n] = v
1479+
if s != col
1480+
n += 1
1481+
I[n] = r
1482+
J[n] = s
1483+
V[n] = v
1484+
end
1485+
end
1486+
end
1487+
return SparseArrays.sparse(
1488+
resize!(I, n), resize!(J, n), resize!(V, n), size(M, 1), ncols)
1489+
end
1490+
1491+
# Merge the removed-bus columns into their survivors on both arc admittance matrices, returning
1492+
# new matrices. This transfer keeps the to-bus admittance entries for arcs terminating at a
1493+
# removed bus, which the later bus-removal slice would otherwise drop.
1494+
function _merge_arc_admittance_bus_columns(
14521495
yft::ArcAdmittanceMatrix,
14531496
ytf::ArcAdmittanceMatrix,
14541497
bus_lookup::Dict{Int, Int},
14551498
merged_bus_pairs::Dict{Int, Int},
14561499
)
1457-
for (removed_bus, surviving_bus) in merged_bus_pairs
1458-
i = bus_lookup[surviving_bus]
1459-
j = bus_lookup[removed_bus]
1460-
yft.data[:, i] += yft.data[:, j]
1461-
ytf.data[:, i] += ytf.data[:, j]
1462-
end
1463-
return
1500+
new_yft = ArcAdmittanceMatrix(
1501+
_merge_arc_admittance_columns(yft.data, bus_lookup, merged_bus_pairs),
1502+
yft.axes, yft.lookup, yft.network_reduction_data, yft.direction)
1503+
new_ytf = ArcAdmittanceMatrix(
1504+
_merge_arc_admittance_columns(ytf.data, bus_lookup, merged_bus_pairs),
1505+
ytf.axes, ytf.lookup, ytf.network_reduction_data, ytf.direction)
1506+
return new_yft, new_ytf
14641507
end
14651508

1466-
_merge_arc_admittance_bus_columns!(
1509+
_merge_arc_admittance_bus_columns(
14671510
::Nothing,
14681511
::Nothing,
14691512
::Dict{Int, Int},
14701513
::Dict{Int, Int},
1471-
) = nothing
1514+
) = (nothing, nothing)
14721515

14731516
function _accumulate_csc_row_into!(
14741517
M::SparseArrays.SparseMatrixCSC,
@@ -1655,14 +1698,15 @@ function _apply_reduction(ybus::Ybus, nr_new::NetworkReductionData)
16551698
nr = get_network_reduction_data(ybus)
16561699

16571700
# A pure-merge reduction (only ZIBR today) folds removed buses into survivors purely by
1658-
# index relabeling, so the merge and the bus-removal slice are fused into one O(nnz)
1659-
# rebuild below instead of in-place CSC structural inserts. Arc-admittance columns are
1660-
# still merged here (column adds on the arc×bus matrix, not the hot square-matrix path).
1701+
# index relabeling, so its merge and bus-removal slice are fused into one O(nnz) rebuild
1702+
# below; any other reduction merges the square Ybus in place via _merge_ybus_buses!. In
1703+
# either case the arc-admittance bus columns are merged in _merge_arc_admittance_bus_columns.
16611704
fast_merge = _is_pure_merge_reduction(nr_new)
1705+
yft_merged, ytf_merged = ybus.arc_admittance_from_to, ybus.arc_admittance_to_from
16621706
if !isempty(nr_new.merged_bus_pairs)
16631707
fast_merge ||
16641708
_merge_ybus_buses!(data, adjacency_data, bus_lookup, nr_new.merged_bus_pairs)
1665-
_merge_arc_admittance_bus_columns!(
1709+
yft_merged, ytf_merged = _merge_arc_admittance_bus_columns(
16661710
ybus.arc_admittance_from_to,
16671711
ybus.arc_admittance_to_from,
16681712
bus_lookup,
@@ -1674,8 +1718,8 @@ function _apply_reduction(ybus::Ybus, nr_new::NetworkReductionData)
16741718
new_y_ft, new_y_tf = _add_series_branches_to_ybus!(
16751719
ybus.data,
16761720
get_bus_lookup(ybus),
1677-
ybus.arc_admittance_from_to,
1678-
ybus.arc_admittance_to_from,
1721+
yft_merged,
1722+
ytf_merged,
16791723
nr_new.series_branch_map,
16801724
nr,
16811725
)

0 commit comments

Comments
 (0)