Generic Branch Examples

Generic Branch Representation of a Three-winding Transformer

The Generic Branch component can be used to model three-winding transformers by decomposing them into three interconnected two-winding transformers. Each of these branches is characterized by its electrical parameters, such as resistance, reactance, and admittance.

import warnings

with warnings.catch_warnings():
    warnings.simplefilter("ignore", category=DeprecationWarning)
    # Suppress warning about pyarrow as future required dependency

import pandas as pd

from power_grid_model import (
    AttributeType,
    CalculationMethod,
    CalculationType,
    ComponentType,
    DatasetType,
    LoadGenType,
    PowerGridModel,
    initialize_array,
)
from power_grid_model.validation import assert_valid_input_data

# network
network = """

 5,Source -->|---Line, Br12----|
                               |
                            ------ 1, HV
                               |
                               0  Br8, T1
                               0
                               | 4, AUX
                 2,MV x--------x---------x 3, LV
                      |                  |
                      0  Br9,T2          0   Br10, T3
                      0                  0
                      |                  |
                    ----               -----> 7, Load
                      |
                      |
                      |  Line, Br11
                      |
                      |
                    ----- 11, Station1
                      |
                      ----> 6, Load

"""
# Voltage levels of the transformer
u_rated = {"HV": 200e3, "MV": 150e3, "LV": 30.2e3, "AUX": 200e3, "Station1": 150e3, "Source": 200e3}

# Injected power on the HV side
source_voltage = u_rated["Source"]  # 200 kV
source_power = 1e40  # High short-circuit power -> v_pu = 1.0

# Loads
load_MV = {"P": 30e6, "Q": 5e6}
load_LV = {"P": 1.5e6, "Q": 0.15e6}  # Self-consumption

# Assignment of node numbers
nodes = {"HV": 1, "MV": 2, "LV": 3, "AUX": 4, "Station1": 11, "Source": 14}
non = len(nodes)  # number of nodes

# Tap-changer
v_tap = 6.0e3

# Generate nodes
node_data = initialize_array(DatasetType.input, ComponentType.node, non)
node_data[AttributeType.id] = list(nodes.values())
node_data[AttributeType.u_rated] = list(u_rated.values())

# Slack
source_data = initialize_array(DatasetType.input, ComponentType.source, 1)
source_data[AttributeType.id] = [5]
source_data[AttributeType.node] = [nodes["Source"]]
source_data[AttributeType.status] = [1]
source_data[AttributeType.u_ref] = [1.0]
source_data[AttributeType.sk] = [source_power]

# Loads 110kV
load_data = initialize_array(DatasetType.input, ComponentType.sym_load, 2)
load_data[AttributeType.id] = [6, 7]
load_data[AttributeType.type] = [LoadGenType.const_power, LoadGenType.const_power]
load_data[AttributeType.node] = [nodes["Station1"], nodes["LV"]]
load_data[AttributeType.p_specified] = [load_MV["P"], load_LV["P"]]
load_data[AttributeType.q_specified] = [load_MV["Q"], load_LV["Q"]]
load_data[AttributeType.status] = [1, 1]

node_name = ["HV", "MV", "LV", "AUX", "Station1", "Source"]
branch_name = ["BHV", "BMV", "BLV", "BLine2", "BLine1"]

branch_data = initialize_array(DatasetType.input, ComponentType.generic_branch, 5)
branch_data[AttributeType.id] = [8, 9, 10, 12, 13]
branch_data[AttributeType.from_node] = [nodes["HV"], nodes["AUX"], nodes["AUX"], nodes["MV"], nodes["Source"]]
branch_data[AttributeType.to_node] = [nodes["AUX"], nodes["MV"], nodes["LV"], nodes["Station1"], nodes["HV"]]
branch_data[AttributeType.from_status] = [1, 1, 1, 1, 1]
branch_data[AttributeType.to_status] = [1, 1, 1, 1, 1]

#                    T1        T2        T3     Line2  Line 1
branch_data[AttributeType.r1] = [0.129059, 0.039528, 0.013811, 0.5, 0.5]
branch_data[AttributeType.x1] = [16.385859, -0.889632, 0.757320, 2.0, 2.0]
branch_data[AttributeType.g1] = [8.692e-7, 0.000002, 0.000038, 0.0, 0.0]
branch_data[AttributeType.b1] = [-2.336e-7, -4.152e-7, -0.00001, 0.0, 0.0]
branch_data[AttributeType.k] = [1.0, 1.0, 1.0, 1.0, 1.0]
branch_data[AttributeType.theta] = [0.0, 0.0, 0.0, 0.0, 0.0]
branch_data[AttributeType.sn] = [450e6, 450e6, 100e6, 100e6, 450e6]

input_data = {
    ComponentType.node: node_data,
    ComponentType.source: source_data,
    ComponentType.sym_load: load_data,
    ComponentType.generic_branch: branch_data,
}

assert_valid_input_data(input_data=input_data, calculation_type=CalculationType.power_flow)

model = PowerGridModel(input_data)
output_data = model.calculate_power_flow(
    symmetric=True, error_tolerance=1e-8, max_iterations=20, calculation_method=CalculationMethod.newton_raphson
)


def e3(x):
    return x / 1e3


def e6(x):
    return x / 1e6


def percent(x):
    return x * 100


def print_node_input(input_data):
    node_in = input_data[ComponentType.node]
    df = pd.DataFrame(node_in)
    df.insert(1, "Name", node_name)

    map_column(df, AttributeType.u_rated, rename_to="Voltage [kV]", unit_fn=e3, round_dec=0)

    print("\nnode data")
    print("---------")
    print(df.to_string(index=False))
    print("\n")


def print_branch_input(input_data):
    genb_in = input_data[ComponentType.generic_branch]
    df = pd.DataFrame(genb_in)
    df.insert(1, "Name", branch_name)

    map_column(df, AttributeType.r1, rename_to="R [Ohm]", round_dec=2)
    map_column(df, AttributeType.x1, rename_to="X [Ohm]", round_dec=2)
    map_column(df, AttributeType.g1, rename_to="G [S]", round_dec=2)
    map_column(df, AttributeType.b1, rename_to="B [S]", round_dec=2)
    map_column(df, AttributeType.sn, rename_to="S [MVA]", unit_fn=e6, round_dec=2)

    print("\ngeneric branch data")
    print("-------------------")
    print(df.to_string(index=False))


def print_load_input(input_data):
    sym_load_in = input_data[ComponentType.sym_load]
    df = pd.DataFrame(sym_load_in)
    df.insert(1, "Name", ["L1", "L2"])

    map_column(df, AttributeType.p_specified, rename_to="P [MW]", unit_fn=e6, round_dec=3)
    map_column(df, AttributeType.q_specified, rename_to="Q [MVar]", unit_fn=e6, round_dec=3)

    print("\nload data")
    print("---------")
    print(df.to_string(index=False))


def print_source_input(input_data):
    source_in = input_data[ComponentType.source]
    df = pd.DataFrame(source_in)
    df.insert(1, "Name", ["G"])

    map_column(df, AttributeType.u_ref, rename_to="Voltage [pu]", round_dec=3)

    print("\nsource data")
    print("-----------")
    print(df.to_string(index=False))
    print("\n")


def print_node_output(output_data):
    node_out = output_data[ComponentType.node]

    df = pd.DataFrame(node_out)
    df.insert(1, "Name", node_name)

    map_column(df, AttributeType.u_pu, rename_to="Voltage [pu]", round_dec=3)
    map_column(df, AttributeType.u, rename_to="Voltage [kV]", unit_fn=e3, round_dec=3)
    map_column(df, AttributeType.u_angle, rename_to="Angle [°]", round_dec=3)
    map_column(df, AttributeType.p, rename_to="P [MW]", unit_fn=e6, round_dec=2)
    map_column(df, AttributeType.q, rename_to="Q [MVAr]", unit_fn=e6, round_dec=2)

    df.drop([AttributeType.energized], axis=1, inplace=True)

    print("\nnode data")
    print("---------")
    print(df.to_string(index=False))


def print_branch_output(output_data, input_data):
    genb_out = output_data[ComponentType.generic_branch]
    genb_in = input_data[ComponentType.generic_branch]
    df = pd.DataFrame(genb_out)
    df.insert(1, "Name", branch_name)
    df.insert(2, "From Node", list(genb_in["from_node"]))
    df.insert(3, "To Node", list(genb_in["to_node"]))

    map_column(df, AttributeType.loading, rename_to="Loading [%]", unit_fn=percent, round_dec=1)
    map_column(df, AttributeType.p_from, rename_to="From P [MW]", unit_fn=e6, round_dec=2)
    map_column(df, AttributeType.q_from, rename_to="From Q [MVAr]", unit_fn=e6, round_dec=2)
    map_column(df, AttributeType.p_to, rename_to="To P [MW]", unit_fn=e6, round_dec=2)
    map_column(df, AttributeType.q_to, rename_to="To Q [MVAr]", unit_fn=e6, round_dec=2)

    df.drop(
        [AttributeType.energized, AttributeType.i_from, AttributeType.s_from, AttributeType.i_to, AttributeType.s_to],
        axis=1,
        inplace=True,
    )

    print("\ngeneric branch data")
    print("-------------------")
    print(df.to_string(index=False))


def map_column(df, col_name, rename_to=None, unit_fn=None, round_dec=None):
    col = df[col_name] if unit_fn is None else unit_fn(df[col_name])
    col = col if round_dec is None else col.round(round_dec)
    df[col_name] = col
    df.rename(columns={col_name: rename_to}, inplace=True)
    return df


def calculate_total_power(output_data):
    genb_out = output_data[ComponentType.generic_branch]
    df = pd.DataFrame(genb_out)

    # Sum up the active and reactive performance across all branches
    P_total = e6(df[AttributeType.p_from].sum()) + e6(df[AttributeType.p_to].sum())
    Q_total = e6(df[AttributeType.q_from].sum()) + e6(df[AttributeType.q_to].sum())

    print("\nTotal Power for all Branches")
    print("----------------------------")
    print(f"Total Active Power (P_total): {P_total:.2f} MW")
    print(f"Total Reactive Power (Q_total): {Q_total:.2f} MVAr")

    return P_total, Q_total


print("Network:")
print(network)

print("========================================")
print("   Input Data")
print("========================================")
print_node_input(input_data)
print_branch_input(input_data)
print_load_input(input_data)
print_source_input(input_data)

print("========================================")
print("   Output Data")
print("========================================")
print_node_output(output_data)
print_branch_output(output_data, input_data)

calculate_total_power(output_data)
Network:


 5,Source -->|---Line, Br12----|
                               |
                            ------ 1, HV
                               |
                               0  Br8, T1
                               0
                               | 4, AUX
                 2,MV x--------x---------x 3, LV
                      |                  |
                      0  Br9,T2          0   Br10, T3
                      0                  0
                      |                  |
                    ----               -----> 7, Load
                      |
                      |
                      |  Line, Br11
                      |
                      |
                    ----- 11, Station1
                      |
                      ----> 6, Load


========================================
   Input Data
========================================

node data
---------
 id     Name  Voltage [kV]
  1       HV         200.0
  2       MV         150.0
  3       LV          30.0
  4      AUX         200.0
 11 Station1         150.0
 14   Source         200.0



generic branch data
-------------------
 id   Name  from_node  to_node  from_status  to_status  R [Ohm]  X [Ohm]  G [S]  B [S]   k  theta  S [MVA]
  8    BHV          1        4            1          1     0.13    16.39    0.0   -0.0 1.0    0.0    450.0
  9    BMV          4        2            1          1     0.04    -0.89    0.0   -0.0 1.0    0.0    450.0
 10    BLV          4        3            1          1     0.01     0.76    0.0   -0.0 1.0    0.0    100.0
 12 BLine2          2       11            1          1     0.50     2.00    0.0    0.0 1.0    0.0    100.0
 13 BLine1         14        1            1          1     0.50     2.00    0.0    0.0 1.0    0.0    450.0

load data
---------
 id Name  node  status  type  P [MW]  Q [MVar]
  6   L1    11       1     0    30.0      5.00
  7   L2     3       1     0     1.5      0.15

source data
-----------
 id Name  node  status  Voltage [pu]  u_ref_angle           sk  rx_ratio  z01_ratio
  5    G    14       1           1.0          NaN 1.000000e+40       NaN        NaN


========================================
   Output Data
========================================

node data
---------
 id     Name  Voltage [pu]  Voltage [kV]  Angle [°]  P [MW]  Q [MVAr]
  1       HV         0.999       199.864     -0.002    0.00      0.00
  2       MV         0.997       149.571     -0.013    0.00     -0.00
  3       LV         0.997        30.104     -0.016   -1.50     -0.15
  4      AUX         0.997       199.398     -0.014   -0.00      0.00
 11 Station1         0.996       149.403     -0.016  -30.00     -5.00
 14   Source         1.000       200.000     -0.000   31.65      5.70

generic branch data
-------------------
 id   Name  From Node  To Node  Loading [%]  From P [MW]  From Q [MVAr]  To P [MW]  To Q [MVAr]
  8    BHV          1        4          7.1        31.64           5.65     -31.60        -5.22
  9    BMV          4        2          6.8        30.07           5.06     -30.02        -5.08
 10    BLV          4        3          1.5         1.53           0.16      -1.50        -0.15
 12 BLine2          2       11         30.4        30.02           5.08     -30.00        -5.00
 13 BLine1         14        1          7.1        31.65           5.70     -31.64        -5.65

Total Power for all Branches
----------------------------
Total Active Power (P_total): 0.15 MW
Total Reactive Power (Q_total): 0.55 MVAr
(np.float64(0.1524792857302799), np.float64(0.5505383338017147))

Generic Branch Representation for Line, Transformer and PST

In this example, the Generic Branch is used as a Phase Shifting Transformer (PST) to optimize the loading of cable L34. Depending on the shift angle \(\theta\), the loading of the cable can be adjusted. With a shift angle of \(-0.1\) rad, the cable loading is approximately \(81\)%, while at a shift angle of \(0.0\) rad, it drops to just \(53\)%.

# some basic imports
import warnings

with warnings.catch_warnings():
    warnings.simplefilter("ignore", category=DeprecationWarning)
    # Suppress warning about pyarrow as future required dependency


from power_grid_model import (
    AttributeType,
    CalculationMethod,
    CalculationType,
    ComponentType,
    DatasetType,
    LoadGenType,
    PowerGridModel,
    initialize_array,
)
from power_grid_model.validation import assert_valid_input_data

# network
network = """

                          (100km)                       (100km)            B5,230kV
             L 100MW <-|----L46-----------|   |-----------L45---------------|-> L 100 MW
                      B6,230kV            ----- B4                          |-
                                            |                                |
                                           L34  (cheap kabel, 40km)          |
                                            |                               L57  (100km)
                                          ----- B3                           |
                                            |                                |
                          (100km)          PST23           (100km)          |-
             L 200MW <-|----L28--------|    |    |----------L27-------------|-> L 200 MW
                      B8,230kV          --------- B2,230kV                 B7,230kV
                                            |
                                           T12 Transformer
                                            |
                                          -----  B1, 15.5kV
                                            ^
                                            |
                                            G (*Slack)

"""
# node
node = initialize_array(DatasetType.input, ComponentType.node, 8)
node[AttributeType.id] = [1, 2, 3, 4, 5, 6, 7, 8]
node[AttributeType.u_rated] = [15.5e3, 230e3, 230e3, 230e3, 230e3, 230e3, 230e3, 230e3]

node_name = ["B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8"]
branch_name = ["L27", "L28", "L45", "L46", "L57", "L34", "T12", "PST23"]

# load
sym_load = initialize_array(DatasetType.input, ComponentType.sym_load, 4)
sym_load[AttributeType.id] = [9, 10, 11, 12]
sym_load[AttributeType.node] = [5, 6, 7, 8]
sym_load[AttributeType.status] = [1, 1, 1, 1]
sym_load[AttributeType.type] = [LoadGenType.const_power]
sym_load[AttributeType.p_specified] = [100e6, 100e6, 200e6, 200e6]
sym_load[AttributeType.q_specified] = [0.0, 0.0, 0.0, 0.0]

# source
source = initialize_array(DatasetType.input, ComponentType.source, 1)
source[AttributeType.id] = [13]
source[AttributeType.node] = [1]
source[AttributeType.status] = [1]
source[AttributeType.u_ref] = [1.0]
source[AttributeType.sk] = [1e40]

# generic_branch
theta_pst = -0.1  # shift angle of PST
gb = initialize_array(DatasetType.input, ComponentType.generic_branch, 8)
#           L27 L28 L45 L46 L57 L34 T12  P23
gb[AttributeType.id] = [14, 15, 16, 17, 18, 19, 20, 21]
gb[AttributeType.from_node] = [2, 2, 4, 4, 5, 3, 1, 2]
gb[AttributeType.to_node] = [7, 8, 5, 6, 7, 4, 2, 3]
gb[AttributeType.from_status] = [1, 1, 1, 1, 1, 1, 1, 1]
gb[AttributeType.to_status] = [1, 1, 1, 1, 1, 1, 1, 1]
gb[AttributeType.r1] = [2.0, 2.0, 2.0, 2.0, 2.0, 4.6, 0.5, 0.0]
gb[AttributeType.x1] = [8.0, 8.0, 8.0, 8.0, 8.0, 3.4, 2.0, 4.0]
gb[AttributeType.g1] = [0.0, 0.0, 0.0, 0.0, 0.0, 4e-6, 0.0, 0.0]
gb[AttributeType.b1] = [0.0, 0.0, 0.0, 0.0, 0.0, 40e-4, 0.0, 0.0]
gb[AttributeType.k] = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
gb[AttributeType.theta] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, theta_pst]
gb[AttributeType.sn] = [500e6, 500e6, 500e6, 500e6, 250e6, 500e6, 1000e6, 1000e6]

# all
input_data = {
    ComponentType.node: node,
    ComponentType.generic_branch: gb,
    ComponentType.sym_load: sym_load,
    ComponentType.source: source,
}

assert_valid_input_data(input_data=input_data, calculation_type=CalculationType.power_flow)

model = PowerGridModel(input_data)

output_data = model.calculate_power_flow(
    symmetric=True, error_tolerance=1e-8, max_iterations=20, calculation_method=CalculationMethod.newton_raphson
)


pd.options.display.max_columns = None
pd.options.display.max_rows = None
pd.options.display.expand_frame_repr = False
pd.options.display.max_colwidth = None


def e3(x):
    return x / 1e3


def e6(x):
    return x / 1e6


def percent(x):
    return x * 100


def print_node_input(input_data):
    node_in = input_data[ComponentType.node]
    df = pd.DataFrame(node_in)
    df.insert(1, "Name", node_name)

    map_column(df, AttributeType.u_rated, rename_to="Voltage [kV]", unit_fn=e3, round_dec=0)

    print("\nnode data")
    print("---------")
    print(df.to_string(index=False))
    print("\n")


def print_branch_input(input_data):
    genb_in = input_data[ComponentType.generic_branch]
    df = pd.DataFrame(genb_in)
    df.insert(1, "Name", branch_name)

    map_column(df, AttributeType.r1, rename_to="R [Ohm]", round_dec=2)
    map_column(df, AttributeType.x1, rename_to="X [Ohm]", round_dec=2)
    map_column(df, AttributeType.g1, rename_to="G [S]", round_dec=2)
    map_column(df, AttributeType.b1, rename_to="B [S]", round_dec=2)
    map_column(df, AttributeType.sn, rename_to="S [MVA]", unit_fn=e6, round_dec=2)

    print("\ngeneric branch data")
    print("-------------------")
    print(df.to_string(index=False))


def print_load_input(input_data):
    sym_load_in = input_data[ComponentType.sym_load]
    df = pd.DataFrame(sym_load_in)
    df.insert(1, "Name", ["L1", "L2", "L3", "L4"])

    map_column(df, AttributeType.p_specified, rename_to="P [MW]", unit_fn=e6, round_dec=3)
    map_column(df, AttributeType.q_specified, rename_to="Q [MVar]", unit_fn=e6, round_dec=3)

    print("\nload data")
    print("---------")
    print(df.to_string(index=False))


def print_source_input(input_data):
    source_in = input_data[ComponentType.source]
    df = pd.DataFrame(source_in)
    df.insert(1, "Name", ["G"])

    map_column(df, AttributeType.u_ref, rename_to="Voltage [pu]", round_dec=3)

    print("\nsource data")
    print("-----------")
    print(df.to_string(index=False))
    print("\n")


def print_node_output(output_data):
    node_out = output_data[ComponentType.node]

    df = pd.DataFrame(node_out)
    df.insert(1, "Name", node_name)

    map_column(df, AttributeType.u_pu, rename_to="Voltage [pu]", round_dec=3)
    map_column(df, AttributeType.u, rename_to="Voltage [kV]", unit_fn=e3, round_dec=3)
    map_column(df, AttributeType.u_angle, rename_to="Angle [°]", round_dec=3)
    map_column(df, AttributeType.p, rename_to="P [MW]", unit_fn=e6, round_dec=2)
    map_column(df, AttributeType.q, rename_to="Q [MVAr]", unit_fn=e6, round_dec=2)

    df.drop([AttributeType.energized], axis=1, inplace=True)

    print("\nnode data")
    print("---------")
    print(df.to_string(index=False))


def print_branch_output(output_data, input_data):
    genb_out = output_data[ComponentType.generic_branch]
    genb_in = input_data[ComponentType.generic_branch]
    df = pd.DataFrame(genb_out)
    df.insert(1, "Name", branch_name)
    df.insert(2, "From Node", list(genb_in["from_node"]))
    df.insert(3, "To Node", list(genb_in["to_node"]))

    map_column(df, AttributeType.loading, rename_to="Loading [%]", unit_fn=percent, round_dec=1)
    map_column(df, AttributeType.p_from, rename_to="From P [MW]", unit_fn=e6, round_dec=2)
    map_column(df, AttributeType.q_from, rename_to="From Q [MVAr]", unit_fn=e6, round_dec=2)
    map_column(df, AttributeType.p_to, rename_to="To P [MW]", unit_fn=e6, round_dec=2)
    map_column(df, AttributeType.q_to, rename_to="To Q [MVAr]", unit_fn=e6, round_dec=2)

    df.drop(
        [AttributeType.energized, AttributeType.i_from, AttributeType.s_from, AttributeType.i_to, AttributeType.s_to],
        axis=1,
        inplace=True,
    )

    print("\ngeneric branch data")
    print("-------------------")
    print(df.to_string(index=False))


def map_column(df, col_name, rename_to=None, unit_fn=None, round_dec=None):
    col = df[col_name] if unit_fn is None else unit_fn(df[col_name])
    col = col if round_dec is None else col.round(round_dec)
    df[col_name] = col
    df.rename(columns={col_name: rename_to}, inplace=True)
    return df


def calculate_total_power(output_data):
    genb_out = output_data[ComponentType.generic_branch]
    df = pd.DataFrame(genb_out)

    #  Sum up the active and reactive performance across all branches
    P_total = e6(df[AttributeType.p_from].sum()) + e6(df[AttributeType.p_to].sum())
    Q_total = e6(df[AttributeType.q_from].sum()) + e6(df[AttributeType.q_to].sum())

    print("\nTotal Power for all Branches")
    print("----------------------------")
    print(f"Total Active Power (P_total): {P_total:.2f} MW")
    print(f"Total Reactive Power (Q_total): {Q_total:.2f} MVAr")

    return P_total, Q_total


print("Generic Branch Example")

print("Network:")
print(network)

print("========================================")
print("   Input Data")
print("========================================")
print_node_input(input_data)
print_branch_input(input_data)
print_load_input(input_data)
print_source_input(input_data)

print("========================================")
print("   Output Data")
print("========================================")
print_node_output(output_data)
print_branch_output(output_data, input_data)
calculate_total_power(output_data)
Generic Branch Example
Network:


                          (100km)                       (100km)            B5,230kV
             L 100MW <-|----L46-----------|   |-----------L45---------------|-> L 100 MW
                      B6,230kV            ----- B4                          |-
                                            |                                |
                                           L34  (cheap kabel, 40km)          |
                                            |                               L57  (100km)
                                          ----- B3                           |
                                            |                                |
                          (100km)          PST23           (100km)          |-
             L 200MW <-|----L28--------|    |    |----------L27-------------|-> L 200 MW
                      B8,230kV          --------- B2,230kV                 B7,230kV
                                            |
                                           T12 Transformer
                                            |
                                          -----  B1, 15.5kV
                                            ^
                                            |
                                            G (*Slack)


========================================
   Input Data
========================================

node data
---------
 id Name  Voltage [kV]
  1   B1          16.0
  2   B2         230.0
  3   B3         230.0
  4   B4         230.0
  5   B5         230.0
  6   B6         230.0
  7   B7         230.0
  8   B8         230.0



generic branch data
-------------------
 id  Name  from_node  to_node  from_status  to_status  R [Ohm]  X [Ohm]  G [S]  B [S]   k  theta  S [MVA]
 14   L27          2        7            1          1      2.0      8.0    0.0    0.0 1.0    0.0    500.0
 15   L28          2        8            1          1      2.0      8.0    0.0    0.0 1.0    0.0    500.0
 16   L45          4        5            1          1      2.0      8.0    0.0    0.0 1.0    0.0    500.0
 17   L46          4        6            1          1      2.0      8.0    0.0    0.0 1.0    0.0    500.0
 18   L57          5        7            1          1      2.0      8.0    0.0    0.0 1.0    0.0    250.0
 19   L34          3        4            1          1      4.6      3.4    0.0    0.0 1.0    0.0    500.0
 20   T12          1        2            1          1      0.5      2.0    0.0    0.0 1.0    0.0   1000.0
 21 PST23          2        3            1          1      0.0      4.0    0.0    0.0 1.0   -0.1   1000.0

load data
---------
 id Name  node  status  type  P [MW]  Q [MVar]
  9   L1     5       1     0   100.0       0.0
 10   L2     6       1     0   100.0       0.0
 11   L3     7       1     0   200.0       0.0
 12   L4     8       1     0   200.0       0.0

source data
-----------
 id Name  node  status  Voltage [pu]  u_ref_angle           sk  rx_ratio  z01_ratio
 13    G     1       1           1.0          NaN 1.000000e+40       NaN        NaN


========================================
   Output Data
========================================

node data
---------
 id Name  Voltage [pu]  Voltage [kV]  Angle [°]  P [MW]  Q [MVAr]
  1   B1         1.000        15.500     -0.000  619.35   -160.47
  2   B2         1.001       230.120     -0.025   -0.00     -0.00
  3   B3         1.017       233.948      0.050    0.00      0.00
  4   B4         0.997       229.217      0.019    0.00      0.00
  5   B5         0.992       228.208     -0.016 -100.00     -0.00
  6   B6         0.993       228.315      0.004 -100.00     -0.00
  7   B7         0.992       228.259     -0.036 -200.00      0.00
  8   B8         0.992       228.261     -0.055 -200.00      0.00

generic branch data
-------------------
 id  Name  From Node  To Node  Loading [%]  From P [MW]  From Q [MVAr]  To P [MW]  To Q [MVAr]
 14   L27          2        7         17.4        79.91          33.96     -79.63       -32.82
 15   L28          2        8         40.3       201.54           6.14    -200.00         0.00
 16   L45          4        5         44.8       222.88         -22.79    -220.97        30.43
 17   L46          4        6         20.1       100.38           1.53    -100.00        -0.00
 18   L57          5        7         49.9       120.97         -30.43    -120.37        32.82
 19   L34          3        4         80.9       334.03        -228.00    -323.26        21.25
 20   T12          1        2         64.0       619.35        -160.47    -615.48       175.94
 21 PST23          2        3         40.4       334.03        -216.05    -334.03       228.00

Total Power for all Branches
----------------------------
Total Active Power (P_total): 19.35 MW
Total Reactive Power (Q_total): -160.47 MVAr
(np.float64(19.347916682930418), np.float64(-160.46709070282753))

Using the Generic Branch with CIM/CGMES Data

The Generic Branch enables the direct modeling of electrical assets such as lines, transformers, and equivalent branches using data from CIM/CGMES network datasets. Unlike traditional approaches that rely on manufacturer-specific parameters for modeling transformers and other equipment, the Generic Branch allows the direct use of electrical equivalent circuit parameters as specified in CIM/CGMES.

This enhancement significantly simplifies the handling of grid models, especially when integrating external datasets into existing systems. It eliminates the need to convert equivalent circuit parameters into manufacturer-specific values, thereby improving data integrity and reducing potential sources of error.

This chapter provides a guide on how to use the Generic Branch to import data from CIM/CGMES network datasets and integrate it into a PGM model.

Note: The PhaseAngleClock of transformers is not used in this document because the generic branch in this context is only applied to symmetrically loaded phases. The rated power of transformers is only used to calculate the relative loading.

CIM:ACLineSegment

For ACLineSegments, the Generic Branch can be used directly. The equivalent circuit parameters are directly mapped to the Generic Branch. The length of the line is accounted for in the parameters \(r\), \(x\), \(b\) and \(g\):

r1 = cim:ACLineSegment.r
x1 = cim:ACLineSegment.x
b1 = cim:ACLineSegment.bch
g1 = cim:ACLineSegment.gch
k = 1.0
theta = 0.0

CIM:EquivalentBranch

EquivalentBranches which connect different voltage levels should be modeled as generic_branch.

The conversion factors for impedance \(z_{\text{conv}}\) is calculated as follows:

Note: We assume that the nominal voltage of terminal 1 is higher than the nominal voltage of terminal 2, u1 > u2

z_conv = (u2 * u2) / (u1 * u1)

r1 = cim:EquivalentBranch.r * z_conv
x1 = cim:EquivalentBranch.x * z_conv

k = 1.0
theta = 0.0

Two-winding Power Transformers

Two-winding PowerTransformers are characterized by only two PowerTransformerEnds. The Generic Branch can be used to model these transformers. On the high-voltage side, non-zero values for \(r\), \(x\), \(b\) and \(g\) are provided in the cim datasets, while on the low-voltage side, these values are set to zero.

end1 = [cim:PowerTransformerEnd]()
end2 = [cim:PowerTransformerEnd]()

ratedS1 = end1.ratedS
ratedS2 = end2.ratedS

rated_u1 = end1.ratedU
rated_u2 = end2.ratedU

Note: We asume rated_u1 > rated_u2

nom_u1 = end1.nominalVoltage
nom_u2 = end2.nominalVoltage


sn = max(ratedS1, ratedS2) * 1e6 # MVA -> VA

z_conv = (rated_u2 * rated_u2) / (rated_u1 * rated_u1)
y_conv = 1 / z_conv


# if r, x, g and b provided in the CIM file as RatioTapChangerTablePoint one has to consider the tap changer position
r1 = cim:PowerTransformerEnd.r * z_conv
x1 = cim:PowerTransformerEnd.x * z_conv
b1 = cim:PowerTransformerEnd.bch * y_conv
g1 = cim:PowerTransformerEnd.gch * y_conv

r, x, b, g = getRatioTapChangerTablePoint() # returns (0,0,0,0) if not provided in the CIM file
r1 = r1 + r1*r/100.0
x1 = x1 + x1*x/100.0
b1 = b1 + b1*b/100.0
g1 = g1 + g1*g/100.0


# Correction of the rated voltage depending on the tap changer position
regulate_side = get_side_from_tap_changer()
step_size = get_voltage_increment_from_tap_changer()
act_ratio = get_ratio()
if regulate_side == 0
  step = step_neutral = 0

# case 1: if a ratio is not given in the CIM file
if act_ratio is None:
  rated_u = rated_u1 if regulate_side == 1 else rated_u2
  corr_u = (step - step_neutral) * rated_u * (step_size  / 100)
  if regulate_side == 1:
    rated_u1 = rated_u1 + corr_u
  else:
    rated_u2 = rated_u2 + corr_u
else:
# case 2: if a ratio is given in the CIM file CIM:RatioTapChangerTable
  act_ratio = get_ratio()
  if regulate_side == 1:
    rated_u1 = rated_u1 * act_ratio
  else:
    rated_u2 = rated_u2 * act_ratio


k = (rated_u1 / rated_u2) / (nom_u1 / nom_u2)
theta = 0.0

Pseudo Function get_side_from_tap_changer()

Purpose: Returns the side of the tap changer. If the tap changer is on the high voltage side, the side is \(1\). If the tap changer is on the low voltage side, the side is \(2\). If there is no tap changer, the side is \(0\).

Pseudo Function get_ratio()

Purpose: Returns the ratio of the current step position of the tap changer from the CIM-File (CIM:RatioTapChangerTable). Returns None if the ratio is not given in the CIM file.

Pseudo Function get_voltage_increment_from_tap_changer()

Purpose: Returns the voltage increment of the tap changer from the CIM-File.

Three-winding Power Transformers

For Three-winding PowerTransformers, the three PowerTransformerEnds represent a star-equivalent circuit, with each leg in the star defined by its \(r\), \(x\), \(g\) and \(b\) values. This configuration requires three separate Generic Branches and an additional auxiliary node to connect them (see also Example for Three-Winding Transformer).

                  GB_HV
                    |
        GB_MV ------*------ GB_LV
  • GB_HV: Generic Branch for the high-voltage side

  • GB_MV: Generic Branch for the medium-voltage side

  • GB_LV: Generic Branch for the low-voltage side

  • *: Auxiliary node with voltage = HV nominal voltage

Each transformer end (or winding) must provide:

  • Nominal voltage

  • Rated power

  • Rated voltage

  • Parameters \(r\), \(x\), \(b\), \(g\)

  • Optional: Tap changer side, position, neutral position and voltage step increment.

The voltage on the auxiliary node is the nominal voltage of the high-voltage side. The tap changer’s position must be determined to adjust the ratio for the regulated side, while the ratios for the other sides are set to \(1.0\). For the generic branch on the regulated side, the ratio k should be calculated as described in section Two-Winding Power Transformers. The electrical parameters \(r\), \(x\), \(b\), and \(g\) can be used as given in the CIM-File without any further conversion. In contrast to Two-winding transformers, for Three-winding transformers the the parameters \(r\), \(x\), \(b\), and \(g\) are given for each TransformerEnd in the CIM-Dataset.

Phase Shift Transformers

Asymmetrical Phase Shifting Transformers

For a Three-Winding Transformer with phase shift, the configuration mirrors the star-equivalent model described in section Three-Winding Power Transformers. However, one leg must account for the phase shift.

                  GB_HV
                    |
        GB_MV --//--*------ GB_LV
  • GB_HV: Generic Branch for the high-voltage side

  • GB_MV: Generic Branch for the medium-voltage side

  • GB_LV: Generic Branch for the low-voltage side

  • *: Auxiliary node with voltage = HV nominal voltage

  • –//–: Phase shift angle

In the PI model of the Generic Branch, the controller (ratio + shift angle) is located on the input side, requiring adjustments to the phase shift angle if the controller is located on the output side (e.g., when the tap changer is on the high-voltage side of the transformer).

u1 o----   -----Z_ser------------o u2
        | |   .               .
        | |   .               .
        | |  Y_shunt        Y_shunt
        | |   .               .
        | |   .               .
   o----   ----------------------o
         k

\(k = \text{ratio} \cdot e^{j \cdot \theta}\)

If the tap changer is located on the MV side, the phase shift angle must be multiplied by \(-1.0\) and always converted to radians.

if regulate_side == 2:
  deg_to_rad = math.pi / 180.0
  theta = angle * deg_to_rad * -1.0
else:
  deg_to_rad = math.pi / 180.0
  theta = angle * deg_to_rad  

The phase shift depends on the tap changer position, as specified in the CIM dataset e.g. CIM:PhaseTapChangerTabular or should be calculated using the formulas from “Phase Shift Transformers Modelling” (ENTSOE_CGMES_v2.4_28May2014_PSTmodelling.pdf).

Symmetrical Phase Shifting Transformers

A Symmetrical Phase Shifting Transformer (PST) can also be modeled using a Generic Branch. In this case:

  • The nominal voltages on both sides are identical.

  • The tap changer influences the total shift of the transformer.

The ratio is set to \(1.0\), and the phase angle shift is either provided in the CIM file or calculated using the formulas from Phase Shift Transformers Modelling (ENTSOE_CGMES_v2.4_28May2014_PSTmodelling.pdf). Similar to the asymmetrical case, the angle is converted to radians and multiplied by \(-1.0\) depending on the tap changer’s location for proper modeling.