diff --git a/GridKit/Model/PhasorDynamics/Branch/Branch.hpp b/GridKit/Model/PhasorDynamics/Branch/Branch.hpp index b169cf4a7..44ca8ebf6 100644 --- a/GridKit/Model/PhasorDynamics/Branch/Branch.hpp +++ b/GridKit/Model/PhasorDynamics/Branch/Branch.hpp @@ -203,6 +203,8 @@ namespace GridKit RealT X_{0.0}; RealT G_{0.0}; RealT B_{0.0}; + RealT Gmag_{0.0}; + RealT Bmag_{0.0}; RealT tap_{1.0}; RealT phase_{0.0}; IdxT bus1_id_{0}; diff --git a/GridKit/Model/PhasorDynamics/Branch/BranchData.hpp b/GridKit/Model/PhasorDynamics/Branch/BranchData.hpp index 9a1c607cf..40fa61ba5 100644 --- a/GridKit/Model/PhasorDynamics/Branch/BranchData.hpp +++ b/GridKit/Model/PhasorDynamics/Branch/BranchData.hpp @@ -17,8 +17,10 @@ namespace GridKit { R, ///< Line series resistance X, ///< Line series reactance - G, ///< Total shunt conductance - B, ///< Total shunt susceptance + G, ///< Total line shunt conductance + B, ///< Total line shunt susceptance + Gmag, ///< Magnetizing shunt conductance at bus 1 (from bus, tapped side) + Bmag, ///< Magnetizing shunt susceptance at bus 1 (from bus, tapped side) tap, ///< Off-nominal tap magnitude on bus1 side phase, ///< Phase shift angle in radians }; diff --git a/GridKit/Model/PhasorDynamics/Branch/BranchImpl.hpp b/GridKit/Model/PhasorDynamics/Branch/BranchImpl.hpp index 6b3a51bba..498dd76f3 100644 --- a/GridKit/Model/PhasorDynamics/Branch/BranchImpl.hpp +++ b/GridKit/Model/PhasorDynamics/Branch/BranchImpl.hpp @@ -52,8 +52,8 @@ namespace GridKit * @param bus2 - pointer to bus-2 * @param R - line series resistance * @param X - line series reactance - * @param G - total shunt conductance - * @param B - total shunt susceptance + * @param G - total line shunt conductance + * @param B - total line shunt susceptance * @param tap - off-nominal tap magnitude on bus1 side * @param phase - phase shift angle in radians */ @@ -165,6 +165,8 @@ namespace GridKit check(std::isfinite(X_), "X must be finite"); check(std::isfinite(G_), "G must be finite"); check(std::isfinite(B_), "B must be finite"); + check(std::isfinite(Gmag_), "Gmag must be finite"); + check(std::isfinite(Bmag_), "Bmag must be finite"); check(std::isfinite(tap_), "tap must be finite"); check(std::isfinite(phase_), "phase must be finite"); check(R_ * R_ + X_ * X_ > RealT{0.0}, "R and X cannot both be zero"); @@ -335,6 +337,8 @@ namespace GridKit readRealParameter(data, Parameter::X, X_); readRealParameter(data, Parameter::G, G_); readRealParameter(data, Parameter::B, B_); + readRealParameter(data, Parameter::Gmag, Gmag_); + readRealParameter(data, Parameter::Bmag, Bmag_); readRealParameter(data, Parameter::tap, tap_); readRealParameter(data, Parameter::phase, phase_); @@ -478,11 +482,11 @@ namespace GridKit const RealT cos_ph = std::cos(phase_); const RealT sin_ph = std::sin(phase_); - const RealT g_diag = -(g_br + RealT{0.5} * G_); - const RealT b_diag = -(b_br + RealT{0.5} * B_); + const RealT g_diag = -g_br; + const RealT b_diag = -b_br; - g11_ = g_diag * inv_tap * inv_tap; - b11_ = b_diag * inv_tap * inv_tap; + g11_ = g_diag * inv_tap * inv_tap - RealT{0.5} * G_ - Gmag_; + b11_ = b_diag * inv_tap * inv_tap - RealT{0.5} * B_ - Bmag_; g12_ = (g_br * cos_ph - b_br * sin_ph) * inv_tap; b12_ = (b_br * cos_ph + g_br * sin_ph) * inv_tap; @@ -490,8 +494,8 @@ namespace GridKit g21_ = (g_br * cos_ph + b_br * sin_ph) * inv_tap; b21_ = (b_br * cos_ph - g_br * sin_ph) * inv_tap; - g22_ = g_diag; - b22_ = b_diag; + g22_ = g_diag - RealT{0.5} * G_; + b22_ = b_diag - RealT{0.5} * B_; } } // namespace PhasorDynamics diff --git a/GridKit/Model/PhasorDynamics/Branch/README.md b/GridKit/Model/PhasorDynamics/Branch/README.md index fcdea7692..97256c5aa 100644 --- a/GridKit/Model/PhasorDynamics/Branch/README.md +++ b/GridKit/Model/PhasorDynamics/Branch/README.md @@ -7,30 +7,24 @@ contributions are oriented entering the adjacent buses. Notes: - Setting $\tau = 1$ and $\theta = 0$ gives the ordinary symmetric transmission-line $\pi$ model. -- $G$ and $B$ are total branch shunt values split equally between terminals. +- The total line shunt $G + jB$ is split equally between the two terminals, + while the magnetizing shunt $G_\text{mag} + jB_\text{mag}$ is connected at + bus 1; both shunts are added outside the $\mathbf{M}$ transformation. - The branch has no solver-owned variables; it contributes current residuals directly to the connected buses. -## Circuit Diagram - -An ideal complex tap is placed on the bus-1 side of the branch equivalent. The -ordinary transmission-line $\pi$ model is recovered with $\tau = 1$ and -$\theta = 0$. - -![](../../../../docs/Figures/transformer-branch.png) - -Figure 1: Branch equivalent circuit - ## Model Parameters -Symbol | Units | Description | Typical Value | Note -------------|---------|--------------------------------------------------|---------------|------ -$R$ | [p.u.] | Branch series resistance | | -$X$ | [p.u.] | Branch series reactance | | -$G$ | [p.u.] | Total branch shunt conductance | 0 | -$B$ | [p.u.] | Total branch shunt susceptance | 0 | -$\tau$ | [p.u.] | Off-nominal tap magnitude on bus-1 side | 1 | Parameter name: `tap` -$\theta$ | [rad] | Phase-shift angle | 0 | Parameter name: `phase` +Symbol | Units | JSON | Description | Typical Value | Note +---------------------|--------|---------|-----------------------------------------|---------------|------ +$R$ | [p.u.] | `R` | Branch series resistance | | +$X$ | [p.u.] | `X` | Branch series reactance | | +$G$ | [p.u.] | `G` | Total line shunt conductance | 0.0 | +$B$ | [p.u.] | `B` | Total line shunt susceptance | 0.0 | +$G_\text{mag}$ | [p.u.] | `Gmag` | Magnetizing shunt conductance at bus 1 | 0.0 | +$B_\text{mag}$ | [p.u.] | `Bmag` | Magnetizing shunt susceptance at bus 1 | 0.0 | +$\tau$ | [p.u.] | `tap` | Off-nominal tap magnitude on bus-1 side | 1.0 | +$\theta$ | [rad] | `phase` | Phase-shift angle | 0.0 | ### Parameter Validation @@ -38,7 +32,8 @@ Invalid Branch parameter sets are rejected by the following checks: ```math \begin{aligned} - &R, X, G, B, \tau, \theta \in \mathbb{R}\ \text{and finite} \\ + &R, X, G, B, G_\text{mag}, B_\text{mag}, \tau, \theta + \in \mathbb{R}\ \text{and finite} \\ &R^2 + X^2 > 0 \\ &\tau > 0 \end{aligned} @@ -46,17 +41,17 @@ Invalid Branch parameter sets are rejected by the following checks: ### Model Derived Parameters -The series and shunt admittances are: +The series, magnetizing shunt, and line shunt admittances are: ```math \begin{aligned} - Y_{\mathrm{br}} &= \dfrac{1}{R + jX} \\ - Y_{\mathrm{sh}} &= G + jB + Y_{\mathrm{br}} &= \dfrac{1}{R + jX} \\ + Y_{\mathrm{mag}} &= G_\text{mag} + jB_\text{mag} \\ + Y_{\mathrm{sh}} &= G + jB \end{aligned} ``` -The nominal $\pi$-branch admittance matrix is the sum of the series and shunt -admittance contributions: +The series, magnetizing shunt, and line shunt admittance matrices are: ```math \begin{aligned} @@ -67,7 +62,16 @@ admittance contributions: Y_{\mathrm{br}} & -Y_{\mathrm{br}} \end{bmatrix} - + + \\ + \mathbf{Y}_\text{mag} + &= + \begin{bmatrix} + -Y_{\mathrm{mag}} & 0 \\ + 0 & 0 + \end{bmatrix} + \\ + \mathbf{Y}_\text{sh} + &= \dfrac{1}{2} \begin{bmatrix} -Y_{\mathrm{sh}} & 0 \\ @@ -85,8 +89,23 @@ The off-nominal transformer transformation uses bus 1 as the tap side: \begin{bmatrix} \tau^{-1} & 0 \\ 0 & e^{j\theta} - \end{bmatrix} \\ - \mathbf{Y} &= \mathbf{M}^{\dagger}\mathbf{Y}_0\mathbf{M} + \end{bmatrix} +\end{aligned} +``` + +The magnetizing and line shunts are added outside the transformation: + +```math +\begin{aligned} + \mathbf{Y} + &= + \mathbf{M}^{\dagger} + \mathbf{Y}_0 + \mathbf{M} + + + \mathbf{Y}_\text{mag} + + + \mathbf{Y}_\text{sh} \end{aligned} ``` diff --git a/GridKit/Model/PhasorDynamics/INPUT_FORMAT.md b/GridKit/Model/PhasorDynamics/INPUT_FORMAT.md index 96f57e852..ef7a497d6 100644 --- a/GridKit/Model/PhasorDynamics/INPUT_FORMAT.md +++ b/GridKit/Model/PhasorDynamics/INPUT_FORMAT.md @@ -139,7 +139,7 @@ are specified: Device class | Description | Ports | Initialization parameters | Variables available to monitor ---------------------|------------------------------------------------------|----------------------------------|---------------------------- | ------------------------- - `Branch` | algebraic pi model for a line or off-nominal transformer branch | `bus1`, `bus2` | `R`, `X`, `G`, `B`, `tap`, `phase` | `ir1`, `ii1`, `im1`, `p1`, `q1`, `ir2`, `ii2`, `im2`, `p2`, `q2` + `Branch` | algebraic pi model for a line or off-nominal transformer branch | `bus1`, `bus2` | `R`, `X`, `G`, `B`, `Gmag`, `Bmag`, `tap`, `phase` | `ir1`, `ii1`, `im1`, `p1`, `q1`, `ir2`, `ii2`, `im2`, `p2`, `q2` `Load` | a basic static impedence load model | `bus` | `R`, `X` | `p`, `q` `Genrou` | 6th order machine model | `bus`, `pmech`\*, `speed`\*, `efd`\* | `p0`, `q0`, `H`, `D`, `Ra`, `Tdop`, `Tdopp`, `Tqop`, `Tqopp`, `Xd`, `Xdp`, `Xdpp`, `Xq`, `Xqp`, `Xqpp`, `Xl`, `S10`, `S12`, `mva` | `ir`, `ii`, `p`, `q`, `delta`, `omega`, `speed` `Gensal` | 5th order salient-pole machine model | `bus`, `pmech`\*, `speed`\*, `efd`\* | `p0`, `q0`, `H`, `D`, `Ra`, `Tdop`, `Tdopp`, `Tqopp`, `Xd`, `Xdp`, `Xdpp`, `Xq`, `Xl`, `S10`, `S12`, `mva` | `ir`, `ii`, `p`, `q`, `delta`, `omega`, `speed`, `Eqp`, `psidp`, `psiqpp`, `psidpp`, `vd`, `vq`, `te`, `id`, `iq` diff --git a/tests/UnitTests/PhasorDynamics/BranchTests.hpp b/tests/UnitTests/PhasorDynamics/BranchTests.hpp index 6f88668d8..f6fa06a1a 100644 --- a/tests/UnitTests/PhasorDynamics/BranchTests.hpp +++ b/tests/UnitTests/PhasorDynamics/BranchTests.hpp @@ -92,13 +92,15 @@ namespace GridKit TestOutcome offNominalResidual() { - // Verifies tap and phase-shift current residual contributions. + // Verifies tap, phase-shift, line-shunt, and bus-1 magnetizing-shunt contributions. TestStatus success = true; RealT R{2.0}; RealT X{4.0}; - RealT G{0.2}; - RealT B{1.2}; + RealT G{0.4}; + RealT B{0.8}; + RealT Gmag{0.2}; + RealT Bmag{1.2}; RealT tap{1.25}; RealT phase{0.3}; @@ -107,10 +109,10 @@ namespace GridKit ScalarT Vr2{30.0}; ScalarT Vi2{40.0}; - const ScalarT Ir1{12.719793434963478}; - const ScalarT Ii1{-4.047960563981182}; - const ScalarT Ir2{13.821345956502421}; - const ScalarT Ii2{-21.182080826645354}; + const ScalarT Ir1{33.679793434963472}; + const ScalarT Ii1{-22.927960563981181}; + const ScalarT Ir2{2.821345956502423}; + const ScalarT Ii2{-19.182080826645358}; PhasorDynamics::Bus bus1(Vr1, Vi1); PhasorDynamics::Bus bus2(Vr2, Vi2); @@ -121,7 +123,20 @@ namespace GridKit bus2.initialize(); bus2.evaluateResidual(); - PhasorDynamics::Branch branch(&bus1, &bus2, R, X, G, B, tap, phase); + using Data = typename PhasorDynamics::Branch::ModelDataT; + using Parameter = typename Data::Parameters; + + Data data; + data.parameters[Parameter::R] = R; + data.parameters[Parameter::X] = X; + data.parameters[Parameter::G] = G; + data.parameters[Parameter::B] = B; + data.parameters[Parameter::Gmag] = Gmag; + data.parameters[Parameter::Bmag] = Bmag; + data.parameters[Parameter::tap] = tap; + data.parameters[Parameter::phase] = phase; + + PhasorDynamics::Branch branch(&bus1, &bus2, data); branch.allocate(); branch.evaluateResidual(); @@ -385,12 +400,12 @@ namespace GridKit const std::complex ybr{g, b}; const std::complex ysh{G, B}; const std::complex rotation{std::cos(phase), std::sin(phase)}; - const std::complex ydiag = -(ybr + RealT{0.5} * ysh); + const std::complex ydiag = -ybr; - const std::complex y11 = ydiag * inv_tap * inv_tap; + const std::complex y11 = ydiag * inv_tap * inv_tap - RealT{0.5} * ysh; const std::complex y12 = ybr * rotation * inv_tap; const std::complex y21 = ybr * std::conj(rotation) * inv_tap; - const std::complex y22 = ydiag; + const std::complex y22 = ydiag - RealT{0.5} * ysh; std::vector dependencies(4); dependencies[0] = {{0, y11.real()}, {1, -y11.imag()}, {2, y12.real()}, {3, -y12.imag()}}; diff --git a/tests/UnitTests/Utilities/CaseFormatTests.hpp b/tests/UnitTests/Utilities/CaseFormatTests.hpp index fd0f0d2de..aca5ca4cf 100644 --- a/tests/UnitTests/Utilities/CaseFormatTests.hpp +++ b/tests/UnitTests/Utilities/CaseFormatTests.hpp @@ -61,7 +61,7 @@ namespace GridKit { "number": 2, "class": "infinite_bus", "name": "Bus 2", "init": {"Vr":1.0, "Vi":0.0}, "params": {"kv": 115.0} } ], "devices": [ - { "class": "Branch", "ports": {"bus1":1, "bus2":2}, "id": "1", "params": {"R":0.0, "X":0.1, "G":0.0, "B":0.0, "tap":1.05, "phase":0.1} }, + { "class": "Branch", "ports": {"bus1":1, "bus2":2}, "id": "1", "params": {"R":0.0, "X":0.1, "G":0.0, "B":0.0, "Gmag":0.01, "Bmag":0.02, "tap":1.05, "phase":0.1} }, { "class": "Genrou", "ports": {"bus":1}, "id": "1", "params": {"p0":1.0, "q0":0.05013, "H":3.0, "D":0.0, "Ra":0.0, "Tdop":7.0, "Tdopp":0.04, "Tqopp":0.05, "Tqop":0.75, "Xd":2.1, "Xdp":0.2, "Xdpp":0.18, "Xq":0.5, "Xqp": 0.0, "Xqpp":0.18, "Xl":0.15, "S10":0.0, "S12":0.0}, "mon": ["delta", "omega"] }, { "class": "Gensal", "ports": {"bus":1}, "id": "2", "params": {"p0":1.0, "q0":0.05013, "H":3.0, "D":0.0, "Ra":0.0, "Tdop":7.0, "Tdopp":0.04, "Tqopp":0.05, @@ -114,6 +114,8 @@ namespace GridKit success *= std::get(result.branch[0].parameters[BranchParameters::X]) == 0.1; success *= std::get(result.branch[0].parameters[BranchParameters::G]) == 0.0; success *= std::get(result.branch[0].parameters[BranchParameters::B]) == 0.0; + success *= std::get(result.branch[0].parameters[BranchParameters::Gmag]) == 0.01; + success *= std::get(result.branch[0].parameters[BranchParameters::Bmag]) == 0.02; success *= std::get(result.branch[0].parameters[BranchParameters::tap]) == 1.05; success *= std::get(result.branch[0].parameters[BranchParameters::phase]) == 0.1; success *= result.branch[0].buses[BranchBuses::bus1] == 1;