Power Flow Module

This module provides functions for AC, DC and AC/DC power flow analysis.

functions are found in pyflow_acdc.ACDC_PF

Running the Power Flow

For a simple power flow, the function power_flow() can be used. This function will automatically detect the type of power flow to run (AC, DC or AC/DC) and will run the appropriate power flow.

power_flow(grid, tol_lim=1e-10, maxIter=100, Droop_PF=True)[source]

Run power flow on grid, dispatching on its AC/DC content.

Picks the AC-only, DC-only, or sequential AC/DC solver based on the grid’s ACmode/DCmode. Results are written back onto the grid in place.

Parameters:
  • grid (Grid) – Network to solve (mutated in place).

  • tol_lim (float, optional) – Convergence tolerance on the mismatch (per unit on grid.S_base). The solver uses effective_tol = tol_lim * grid.tol_scaler, where tol_scaler is 1 until change_S_base() changes S_base away from S_base_ref (MW-normalized stopping). For hybrid grids, acdc_sequential() is called with internal_tol=tol_lim and outer tol_lim * PF_SEQ_TOL_FACTOR (four orders looser).

  • maxIter (int, optional) – Maximum Newton iterations.

  • Droop_PF (bool, optional) – Passed to the DC and hybrid solvers. If True, include droop-controlled DC nodes in the solve.

Returns:

(elapsed_seconds, final_tolerance).

Return type:

tuple

Examples

>>> time, tol = pyf.power_flow(grid)

AC Power Flow

The AC power flow solution is based on [1], to solve the AC power flow Newton Raphson method is used. The equations involving \(P_{slack}\) and \(Q_{slack}\) of each separate AC grid ( \(\Gamma\) = number of AC grids) are not included. In addition, the Q equations of PV nodes are also not included.

The number of unknown variables for \(V_i\) and \(\theta_i\) are (\(|\mathcal{N}_{ac}| - \Gamma - PV\)) and (\(|\mathcal{N}_{ac}| - \Gamma\) ) respectively. These unknown variables are arranged into vectors \(\boldsymbol{\theta}\), \(|V|\), and the composite vector \(\boldsymbol{x}\).

The active power equations at each node i are:

\[P_i = \sum P_{g_i} + \sum \gamma_{rg_i}P_{rg_i} - P_{l_i} + \sum P_{cn_i}\]
\[P_i(x) = \sum_{k=1}^{\mathcal{N}_{ac}} |V_i||V_k|[G_{ik} \cos(\theta_i-\theta_k) + B_{ik} \sin(\theta_i-\theta_k)]\]

The reactive power equations at each node i are:

\[Q_i = \sum Q_{g_i} + \sum Q_{rg_i} -Q_{l_i}+\sum Q_{cn_{i}}\]
\[Q_i(x) = \sum_{k=1}^{\mathcal{N}_{ac}} |V_i||V_k|[G_{ik} \sin(\theta_i-\theta_k) - B_{ik} \cos(\theta_i-\theta_k)]\]

These equations are combined into the function \(\boldsymbol{f(x)} = 0\):

\[\begin{split}\boldsymbol{f(x)} = \begin{bmatrix} P_1(\boldsymbol{x})-P_1 \\ \vdots \\ P_{|\mathcal{N}_{ac}|-\Gamma}(\boldsymbol{x})-P_{|\mathcal{N}_{ac}|-\Gamma} \\ Q_1(\boldsymbol{x})-Q_1 \\ \vdots \\ Q_{|\mathcal{N}_{ac}|-\Gamma-PV}(\boldsymbol{x})-Q_{|\mathcal{N}_{ac}|-\Gamma-PV} \end{bmatrix}\end{split}\]

The Jacobian matrix \(\boldsymbol{J}\) contains the first-order partial derivatives:

\[\begin{split}\boldsymbol{J} = \begin{bmatrix} \boldsymbol{J_{11}} & \boldsymbol{J_{12}} \\ \boldsymbol{J_{21}} & \boldsymbol{J_{22}} \end{bmatrix}\end{split}\]

Where:

\[\begin{split}[i,i] \text{ are the diagonal elements} \\ [i,k] \text{ are the off-diagonal elements}\end{split}\]

\(J_{11}\) (size \(|\mathcal{N}_{ac}|-\Gamma \times |\mathcal{N}_{ac}|-\Gamma\)) contains \(\partial P(x)/\partial \theta\):

\[\begin{split}\boldsymbol{J_{11}}[i,i] &= -Q_i-V_i^2 \cdot B_{ii} \\ \boldsymbol{J_{11}}[i,k] &= V_i \cdot V_k \cdot (G_{ik} \cdot \sin(\theta_i-\theta_k)-B_{ik} \cdot \cos(\theta_i-\theta_k))\end{split}\]

\(J_{12}\) (size \(|\mathcal{N}_{ac}|-\Gamma \times |\mathcal{N}_{ac}|-\Gamma-PV\)) contains \(\partial P(x)/\partial V\):

\[\begin{split}\boldsymbol{J_{12}}[i,i] &= \frac{P_i}{V_i}+ G_{ii} \cdot V_i \\ \boldsymbol{J_{12}}[i,k] &= V_i \cdot (G_{ik} \cdot \cos(\theta_i-\theta_k)+B_{ik} \cdot \sin(\theta_i-\theta_k))\end{split}\]

\(J_{21}\) (size \(|\mathcal{N}_{ac}|-\Gamma-PV \times |\mathcal{N}_{ac}|-\Gamma\)) contains \(\partial Q(x)/\partial \theta\):

\[\begin{split}\boldsymbol{J_{21}}[i,i] &= P_i-V_i^2 \cdot G_{ii} \\ \boldsymbol{J_{21}}[i,k] &= -V_i \cdot V_k \cdot (G_{ik} \cdot \cos(\theta_i-\theta_k)+B_{ik} \cdot \sin(\theta_i-\theta_k))\end{split}\]

\(J_{22}\) (size \(|\mathcal{N}_{ac}|-\Gamma-PV \times |\mathcal{N}_{ac}|-\Gamma-PV\)) contains \(\partial Q(x)/\partial V\):

\[\begin{split}\boldsymbol{J_{22}}[i,i] &= \frac{Q_i}{V_i}-B_{ii} \cdot V_i \\ \boldsymbol{J_{22}}[i,k] &= V_i \cdot (G_{ik} \cdot \sin(\theta_i-\theta_k)-B_{ik} \cdot \cos(\theta_i-\theta_k))\end{split}\]

The Newton-Raphson iteration is then:

\[\begin{split}\begin{bmatrix} \boldsymbol{J_{11}} & \boldsymbol{J_{12}} \\ \boldsymbol{J_{21}} & \boldsymbol{J_{22}} \end{bmatrix} \begin{bmatrix} \boldsymbol{\Delta \theta} \\ \boldsymbol{\Delta |V|} \end{bmatrix} = \begin{bmatrix} \boldsymbol{\Delta P(x)} \\ \boldsymbol{\Delta Q(x)} \end{bmatrix}\end{split}\]

Running the AC Power Flow

ac_power_flow(grid, tol_lim=1e-10, maxIter=100)[source]

Solve the AC-side Newton-Raphson power flow.

Builds Ybus, solves the AC network, and writes bus voltages and line flows back onto grid.

Parameters:

tol_lim (float, optional) – Per-unit convergence tolerance. Scaled by grid.tol_scaler when S_base differs from S_base_ref (see power_flow()).

Returns:

(elapsed_seconds, final_tolerance).

Return type:

tuple

Examples

>>> time, tol = pyf.ac_power_flow(grid)

DC Power Flow

It is important to note that ‘DC power flow’ here specifically refers to the flow in DC grids and not to the linearized power flow that is often used as a simplification of AC grids.

The DC power flow solution is based on [2], to solve the DC power flow Newton Raphson method is used.

(1)\[P_{cn_d} - P_{l_d} = U_d \sum_{\substack{f=1; f \neq d}}^{n_{dc}} \left( \left( U_d - U_f \right) \cdot p_e \left(\frac{1}{R_{df}} \right) \right), \left\{ R_{df} \neq 0 \right\}\]

Similar to the AC power flow, the DC power flow is solved by Newton-Raphson method by defining a vector \(\boldsymbol{y}\):

\[\begin{split}\boldsymbol{y} = \begin{bmatrix} U_{1} \\ U_{2} \\ \vdots \\ U_{|\mathcal{N}_{dc}|-s_{DC}} \end{bmatrix}\end{split}\]
\[\begin{split}P_d &= P_{cn_d} - P_{l_d} \\ P_d(y) &= U_d \sum_{\substack{f=1; f \neq d}}^{n_{dc}} \left( \left( U_d - V_f \right) \cdot p_e \left(\frac{1}{R_{df}} \right) \right), \left\{ R_{df} \neq 0 \right\}\end{split}\]
\[\begin{split}\boldsymbol{f(y)} = \begin{bmatrix} P_1(\boldsymbol{y})-P_1 \\ \vdots \\ P_{|\mathcal{N}_{dc}|-s_{DC}}(\boldsymbol{y})-P_{|\mathcal{N}_{dc}|-s_{DC}} \\ \end{bmatrix} = 0\end{split}\]

With \(J_{DC}\), the Jacobian matrix of \(\boldsymbol{f(y)}\) considered as:

\[\begin{split}J_{DC} \cdot \frac{\Delta U_{DC}}{U_{DC}} &= \Delta P_{DC} \\ J_{DC} &= U_{DC}\frac{\delta P_{DC}}{\delta U_{DC}}\end{split}\]

\(J_{DC}\) is a matrix of \(|\mathcal{N}_{dc}|-s_{DC}\) (\(s_{DC}\) is the number of DC slack buses)

\[\begin{split}[d,d] \text{ are the diagonal elements} \\ [d,f] \text{ are the off-diagonal elements}\end{split}\]
\[\begin{split}\boldsymbol{J_{DC}}{[d,d]} &= P_{DC_d}+ U_{d}^2 \cdot \sum^n_{f=1; d\neq f}(\frac{1}{R_{df}} \cdot p_{e}) , \left\{ R_{df} \neq 0 \right\} \\ \boldsymbol{J_{DC}}{[d,f]} &= - p_{e}\cdot \frac{1}{R_{df}} \cdot U_d\cdot V_f , \left\{ R_{df} \neq 0 \right\}\end{split}\]

In contrast to the AC Newton-Raphson, in the DC Newton-Raphson, the power target of the droop nodes changes each iteration.

\[P = -P_{l_d}+P_{conv_0}+ (1-U_d) \kappa\]

Where \(P_{conv_0}\) is the target power in pu of the converter, \(U_i\) is the voltage of the DC bus in pu and \(\kappa\) the droop coefficient in \(P_{pu}/V_{pu}\). For the DC bus that are under droop control the Jacobian is also modified as follows:

\[\boldsymbol{J_{DC}}{[d,d]} = P_{DC_d}+ \kappa \cdot U_d+U_{d}^2 \cdot \sum^n_{f=1; f\neq d}(\frac{1}{R_{df}} \cdot p_{e}) , \left\{ R_{df} \neq 0 \right\}\]

Running the DC Power Flow

dc_power_flow(grid, tol_lim=1e-10, maxIter=100, Droop_PF=True)[source]

Solve the DC-side power flow.

Writes DC bus voltages and line flows back onto grid.

Parameters:
  • tol_lim (float, optional) – Per-unit convergence tolerance. Scaled by grid.tol_scaler when S_base differs from S_base_ref (see power_flow()).

  • Droop_PF (bool, optional) – If True, include droop-controlled nodes in the solve.

Returns:

(elapsed_seconds, final_tolerance).

Return type:

tuple

Examples

>>> time, tol = pyf.dc_power_flow(grid)

Sequential AC/DC Power Flow

Sequential AC/DC power flow is a method that solves the AC and DC power flows sequentially. It is a three-section process:

  1. AC power flow: Solves the AC power flow equations for the AC grid.

  2. Converter power flow: Solves the converter power flow equations.

  3. DC power flow: Solves the DC power flow equations for the DC grid.

The sequential solver will compare the \(P_{conv}\) of converters in the AC grid until convergence is reached.

Sequential AC/DC Power Flow

Sequential AC/DC Power Flow

Sequential AC/DC Power Flow

Sequential AC/DC Power Flow

Running the Sequential AC/DC Power Flow

acdc_sequential(grid, tol_lim=0.0001, maxIter=100, internal_tol=1e-08, change_slack2Droop=False, QLimit=False, Droop_PF=True)[source]

Solve a coupled AC/DC system by sequential iteration.

Alternates AC power flow, DC power flow, and converter solves until the outer AC/DC interface mismatch converges. Results are written back onto grid in place.

Parameters:
  • tol_lim (float, optional) – Outer (interface) convergence tolerance (per unit). Scaled by grid.tol_scaler when S_base differs from S_base_ref.

  • internal_tol (float, optional) – Inner AC/DC solve tolerance (per unit). Scaled the same way. Defaults to PF_OUTER_TOLERANCE / PF_SEQ_TOL_FACTOR. Converter solves use internal_tol / PF_SEQ_TOL_FACTOR (CONV_TOLERANCE at defaults).

  • change_slack2Droop (bool, optional) – Convert slack-controlled DC nodes to droop control during the solve.

  • QLimit (bool, optional) – Enforce converter reactive-power limits.

  • Droop_PF (bool, optional) – Include droop-controlled nodes in the DC solve.

Returns:

(elapsed_seconds, final_tolerance, tolerance_tracker) where tolerance_tracker is a dict of per-iteration convergence detail.

Return type:

tuple

Examples

>>> time, tol, ps_iterations = pyf.acdc_sequential(grid)

References