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.