Bus admittance matrix
64-bitThe admittances that link every pair of buses.
y = 1 ÷ (r + jx); Yff = (y + jb ÷ 2) ÷ |a|²; Yft = −y ÷ conj(a); Ytt = y + jb ÷ 2
The series admittance is r ÷ (r² + x²) and −x ÷ (r² + x²), computed once per branch. Tap phase is converted from degrees with π ÷ 180. Entries are summed into dense arrays in branch order.
/** Bus admittance matrix as real parts G, B (n×n) for the given system; also the per-branch four-element admittances. */
export function buildYbus(sys) {
const n = sys.buses.length, idx = new Map(sys.buses.map((b, i) => [b.id, i]));
const G = Array.from({ length: n }, () => new Array(n).fill(0)), B = Array.from({ length: n }, () => new Array(n).fill(0));
const br = [];
sys.buses.forEach((b, i) => { G[i][i] += b.Gs || 0; B[i][i] += b.Bs || 0; });
for (const l of sys.branches) {
const f = idx.get(l.from), t = idx.get(l.to);
if (f == null || t == null) throw new Error(`branch ${l.id}: unknown bus`);
const den = l.r * l.r + l.x * l.x; if (!(den > 0)) throw new Error(`branch ${l.id}: zero impedance`);
const y = [l.r / den, -l.x / den], bc = (l.b || 0) / 2, tap = l.tap || 1, ph = (l.shift || 0) * DEG;
const a = [tap * Math.cos(ph), tap * Math.sin(ph)], a2 = tap * tap;
const yff = [y[0] / a2, (y[1] + bc) / a2], ytt = [y[0], y[1] + bc];
const yA = [(y[0] * a[0] - y[1] * a[1]) / a2, (y[0] * a[1] + y[1] * a[0]) / a2]; // y·a/|a|² = y / conj(a)
const yB = [(y[0] * a[0] + y[1] * a[1]) / a2, (y[1] * a[0] - y[0] * a[1]) / a2]; // y·conj(a)/|a|² = y / a
const Yft = [-yA[0], -yA[1]], Ytf = [-yB[0], -yB[1]];
G[f][f] += yff[0]; B[f][f] += yff[1]; G[t][t] += ytt[0]; B[t][t] += ytt[1];
G[f][t] += Yft[0]; B[f][t] += Yft[1]; G[t][f] += Ytf[0]; B[t][f] += Ytf[1];
br.push({ id: l.id, f, t, Yff: yff, Yft, Ytf, Ytt: ytt, rating: l.rating });
}
return { G, B, branches: br, idx };
}