Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
- Added cmake-format hooks, including in pre-commit.
- Added off-nominal tap ratio and phase shift support to the PhasorDynamics `Branch` model.
- Added portable Vector class to GridKit
- Added `closed` parameter to `Branch` for declaring out-of-service lines in case files.

## v0.1

Expand Down
7 changes: 2 additions & 5 deletions GridKit/Model/PhasorDynamics/Branch/Branch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,6 @@ namespace GridKit
void setDerivedParams();
void terminalCurrent1(ScalarT& Ir, ScalarT& Ii);
void terminalCurrent2(ScalarT& Ir, ScalarT& Ii);
bool readRealParameter(const ModelDataT& data,
typename ModelDataT::Parameters parameter,
RealT& target);

static __attribute__((always_inline)) inline void addAdmittanceContribution(RealT G,
RealT B,
Expand Down Expand Up @@ -201,6 +198,8 @@ namespace GridKit
RealT B_{0.0};
RealT tap_{1.0};
RealT phase_{0.0};
bool closed_{true};
RealT in_service_factor_{1.0};
IdxT bus1_id_{0};
IdxT bus2_id_{0};

Expand All @@ -213,8 +212,6 @@ namespace GridKit
RealT g22_{0.0};
RealT b22_{0.0};

int parameter_error_count_{0};

/// Variable monitor
std::unique_ptr<MonitorT> monitor_;
};
Expand Down
1 change: 1 addition & 0 deletions GridKit/Model/PhasorDynamics/Branch/BranchData.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace GridKit
B, ///< Total shunt susceptance
tap, ///< Off-nominal tap magnitude on bus1 side
phase, ///< Phase shift angle in radians
closed ///< In-service flag (true = closed, default true)
};

/// Ports for a branch
Expand Down
97 changes: 45 additions & 52 deletions GridKit/Model/PhasorDynamics/Branch/BranchImpl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@
*/

#include <cmath>
#include <variant>

#include <magic_enum/magic_enum.hpp>

#include <GridKit/Model/PhasorDynamics/Branch/Branch.hpp>
#include <GridKit/Model/PhasorDynamics/Branch/BranchData.hpp>
Expand Down Expand Up @@ -147,7 +144,7 @@ namespace GridKit
template <typename scalar_type, typename index_type>
int Branch<scalar_type, index_type>::verify() const
{
int ret = parameter_error_count_;
int ret = 0;

auto check = [&](bool condition, const char* message)
{
Expand Down Expand Up @@ -310,51 +307,46 @@ namespace GridKit
template <typename scalar_type, typename index_type>
void Branch<scalar_type, index_type>::initializeParameters(const ModelDataT& data)
{
readRealParameter(data, ModelDataT::Parameters::R, R_);
readRealParameter(data, ModelDataT::Parameters::X, X_);
readRealParameter(data, ModelDataT::Parameters::G, G_);
readRealParameter(data, ModelDataT::Parameters::B, B_);
readRealParameter(data, ModelDataT::Parameters::tap, tap_);
readRealParameter(data, ModelDataT::Parameters::phase, phase_);
using P = typename ModelDataT::Parameters;

if (data.ports.contains(ModelDataT::Ports::bus1))
if (data.parameters.contains(P::R))
{
bus1_id_ = data.ports.at(ModelDataT::Ports::bus1);
R_ = std::get<RealT>(data.parameters.at(P::R));
}

if (data.ports.contains(ModelDataT::Ports::bus2))
if (data.parameters.contains(P::X))
{
bus2_id_ = data.ports.at(ModelDataT::Ports::bus2);
X_ = std::get<RealT>(data.parameters.at(P::X));
}
}

template <typename scalar_type, typename index_type>
bool Branch<scalar_type, index_type>::readRealParameter(const ModelDataT& data,
typename ModelDataT::Parameters parameter,
RealT& target)
{
if (!data.parameters.contains(parameter))
if (data.parameters.contains(P::G))
{
return false;
G_ = std::get<RealT>(data.parameters.at(P::G));
}

const auto& value = data.parameters.at(parameter);
if (const auto* real_value = std::get_if<RealT>(&value))
if (data.parameters.contains(P::B))
{
target = *real_value;
return true;
B_ = std::get<RealT>(data.parameters.at(P::B));
}
if (data.parameters.contains(P::tap))
{
tap_ = std::get<RealT>(data.parameters.at(P::tap));
}
if (data.parameters.contains(P::phase))
{
phase_ = std::get<RealT>(data.parameters.at(P::phase));
}
if (data.parameters.contains(P::closed))
{
closed_ = std::get<bool>(data.parameters.at(P::closed));
}

if (const auto* integer_value = std::get_if<IdxT>(&value))
if (data.ports.contains(ModelDataT::Ports::bus1))
{
target = static_cast<RealT>(*integer_value);
return true;
bus1_id_ = data.ports.at(ModelDataT::Ports::bus1);
}

Log::error() << "Branch: parameter " << magic_enum::enum_name(parameter)
<< " must be numeric\n";
parameter_error_count_ += 1;
return false;
if (data.ports.contains(ModelDataT::Ports::bus2))
{
bus2_id_ = data.ports.at(ModelDataT::Ports::bus2);
}
}

template <typename scalar_type, typename index_type>
Expand Down Expand Up @@ -436,14 +428,15 @@ namespace GridKit
template <typename scalar_type, typename index_type>
void Branch<scalar_type, index_type>::setDerivedParams()
{
g11_ = RealT{0.0};
b11_ = RealT{0.0};
g12_ = RealT{0.0};
b12_ = RealT{0.0};
g21_ = RealT{0.0};
b21_ = RealT{0.0};
g22_ = RealT{0.0};
b22_ = RealT{0.0};
g11_ = RealT{0.0};
b11_ = RealT{0.0};
g12_ = RealT{0.0};
b12_ = RealT{0.0};
g21_ = RealT{0.0};
b21_ = RealT{0.0};
g22_ = RealT{0.0};
b22_ = RealT{0.0};
in_service_factor_ = closed_ ? RealT{1.0} : RealT{0.0};

const RealT denom = R_ * R_ + X_ * X_;
if (denom == RealT{0.0} || tap_ == RealT{0.0})
Expand All @@ -460,17 +453,17 @@ namespace GridKit
const RealT g_diag = -(g_br + RealT{0.5} * G_);
const RealT b_diag = -(b_br + RealT{0.5} * B_);

g11_ = g_diag * inv_tap * inv_tap;
b11_ = b_diag * inv_tap * inv_tap;
g11_ = in_service_factor_ * g_diag * inv_tap * inv_tap;
b11_ = in_service_factor_ * b_diag * inv_tap * inv_tap;

g12_ = (g_br * cos_ph - b_br * sin_ph) * inv_tap;
b12_ = (b_br * cos_ph + g_br * sin_ph) * inv_tap;
g12_ = in_service_factor_ * (g_br * cos_ph - b_br * sin_ph) * inv_tap;
b12_ = in_service_factor_ * (b_br * cos_ph + g_br * sin_ph) * inv_tap;

g21_ = (g_br * cos_ph + b_br * sin_ph) * inv_tap;
b21_ = (b_br * cos_ph - g_br * sin_ph) * inv_tap;
g21_ = in_service_factor_ * (g_br * cos_ph + b_br * sin_ph) * inv_tap;
b21_ = in_service_factor_ * (b_br * cos_ph - g_br * sin_ph) * inv_tap;

g22_ = g_diag;
b22_ = b_diag;
g22_ = in_service_factor_ * g_diag;
b22_ = in_service_factor_ * b_diag;
}

} // namespace PhasorDynamics
Expand Down
83 changes: 63 additions & 20 deletions GridKit/Model/PhasorDynamics/Branch/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ Notes:
- $G$ and $B$ are total branch shunt values split equally between terminals.
- The branch has no solver-owned variables; it contributes current residuals
directly to the connected buses.
- `closed=false` is static case-import status. It removes the branch admittance
contribution but does not insert fake admittance; islanded or underconstrained
topology can still make the system singular.
- Sparse automatic differentiation may materialize zero-valued structural
entries for an open branch; the mathematical Jacobian contribution is zero.

## Circuit Diagram

Expand All @@ -25,14 +30,15 @@ $\theta = 0$.

## 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 branch shunt conductance | 0 |
$B$ | [p.u.] | `B` | Total branch shunt susceptance | 0 |
$\tau$ | [p.u.] | `tap` | Off-nominal tap magnitude on bus-1 side | 1 |
$\theta$ | [rad] | `phase` | Phase-shift angle | 0 |
$c_{\mathrm{br}}$ | [-] | `closed` | Static branch closed status | `true` | JSON boolean

### Parameter Validation

Expand All @@ -41,17 +47,23 @@ 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} \\
&c_{\mathrm{br}} \in \{\mathrm{true}, \mathrm{false}\} \\
&R^2 + X^2 > 0 \\
&\tau > 0
\end{aligned}
```

### Model Derived Parameters

The series and shunt admittances are:
The closed-status factor and branch admittances are:

```math
\begin{aligned}
s_{\mathrm{br}} &=
\begin{cases}
1, & c_{\mathrm{br}} = \mathrm{true} \\
0, & c_{\mathrm{br}} = \mathrm{false}
\end{cases} \\
Y_{\mathrm{br}} &= \dfrac{1}{R + jX} \\
Y_{\mathrm{sh}} &= G + jB
\end{aligned}
Expand Down Expand Up @@ -88,11 +100,47 @@ The off-nominal transformer transformation uses bus 1 as the tap side:
\tau^{-1} & 0 \\
0 & e^{j\theta}
\end{bmatrix} \\
\mathbf{Y} &= \mathbf{M}^{\dagger}\mathbf{Y}_0\mathbf{M}
\mathbf{Y} &= s_{\mathrm{br}}\mathbf{M}^{\dagger}\mathbf{Y}_0\mathbf{M}
\end{aligned}
```

For the equations below, write each entry as $Y_{mn}=G_{mn}+jB_{mn}$.
For each entry $Y_{mn}=G_{mn}+jB_{mn}$, the real-valued contribution from
terminal $n$ to current at terminal $m$ is:

```math
\begin{aligned}
\begin{bmatrix}
I_{rm} \\
I_{im}
\end{bmatrix}_{n}
=
\begin{bmatrix}
G_{mn} & -B_{mn} \\
B_{mn} & G_{mn}
\end{bmatrix}
\begin{bmatrix}
V_{rn} \\
V_{in}
\end{bmatrix}
\end{aligned}
```

The voltage derivative for the same block is:

```math
\begin{aligned}
\frac{\partial [I_{rm}, I_{im}]^T}
{\partial [V_{rn}, V_{in}]}
=
\begin{bmatrix}
G_{mn} & -B_{mn} \\
B_{mn} & G_{mn}
\end{bmatrix}
\end{aligned}
```

When `closed=false`, $s_{\mathrm{br}}=0$, so $\mathbf{Y}=0$ and every current
block and voltage derivative block is zero.

## Model Variables

Expand Down Expand Up @@ -129,24 +177,19 @@ None.

### Algebraic Equations

The branch current relation is $0 = -\mathbf{I} + \mathbf{Y}\mathbf{V}$.

```math
\begin{aligned}
I_{r1} &= G_{11} V_{r1} - B_{11} V_{i1}
0 &= -I_{r1} + G_{11} V_{r1} - B_{11} V_{i1}
+ G_{12} V_{r2} - B_{12} V_{i2} \\
I_{i1} &= B_{11} V_{r1} + G_{11} V_{i1}
0 &= -I_{i1} + B_{11} V_{r1} + G_{11} V_{i1}
+ B_{12} V_{r2} + G_{12} V_{i2} \\
I_{r2} &= G_{21} V_{r1} - B_{21} V_{i1}
0 &= -I_{r2} + G_{21} V_{r1} - B_{21} V_{i1}
+ G_{22} V_{r2} - B_{22} V_{i2} \\
I_{i2} &= B_{21} V_{r1} + G_{21} V_{i1}
0 &= -I_{i2} + B_{21} V_{r1} + G_{21} V_{i1}
+ B_{22} V_{r2} + G_{22} V_{i2}
\end{aligned}
```

These current contributions are added to the connected bus residuals with
positive sign because branch current is oriented entering the bus.

## Initialization

The Branch model has no internal state to initialize. During construction or
Expand Down
6 changes: 1 addition & 5 deletions GridKit/Model/PhasorDynamics/ComponentDataJSONParser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,10 @@ namespace GridKit
{
c.parameters[key.value()] = raw_parameter.value().template get<bool>();
}
else if (raw_parameter.value().is_number_float())
else if (raw_parameter.value().is_number())
{
c.parameters[key.value()] = raw_parameter.value().template get<RealT>();
}
else if (raw_parameter.value().is_number_integer())
{
c.parameters[key.value()] = raw_parameter.value().template get<IdxT>();
}
else
{
Log::error() << "\n\tInvalid initial parameter value type: "
Expand Down
9 changes: 5 additions & 4 deletions GridKit/Model/PhasorDynamics/INPUT_FORMAT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`, `tap`, `phase`, `closed` | `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`
Expand All @@ -155,9 +155,10 @@ Ports marked with \* are optional and, if missing, will be assumed to be
connected to a constant value. This list is subject to change.


For `Branch`, `tap` and `phase` are optional parameters. If omitted, `tap`
defaults to `1.0` and `phase` defaults to `0.0` radians. Bus `bus1` is the tap
side for off-nominal transformer branches.
For `Branch`, `tap`, `phase`, and `closed` are optional parameters. If omitted,
`tap` defaults to `1.0`, `phase` defaults to `0.0` radians, and `closed`
defaults to `true`. `closed` must be a JSON boolean; numeric status values are
rejected. Bus `bus1` is the tap side for off-nominal transformer branches.

## Example File for a 2-Bus System

Expand Down
Loading
Loading