Transformer Example

In this notebook we will present examples of transformers in power-grid-model.

Both two-winding transformer and three-winding transformer are covered. We will do one-time power flow calculation and one-time state estimation for both types of tranformers.

This notebook serves as an example of how to use the Python API. For detailed API documentation, refer to Python API reference and Native Data Interface.

Transformer (Two-winding Transformer)

Transformer is described as a pi model in power-grid-model, and it belongs to the branch component type which connects two nodes with possibly different voltage levels.

Example Network

We use a simple network with 2 nodes, 1 source, 1 load and 1 transformer. As shown below:

   source_1 --- node_2 --- transformer_3 --- node_4 --- load_5
# some basic imports
import numpy as np
import pandas as pd

from power_grid_model import (
    AttributeType,
    BranchSide,
    CalculationMethod,
    CalculationType,
    ComponentType,
    DatasetType,
    LoadGenType,
    MeasuredTerminalType,
    PowerGridModel,
    TapChangingStrategy,
    initialize_array,
)

Input Dataset

We create an input dataset by using the helper function initialize_array.

Please refer to Components for detailed explanation of all component types and their input/output attributes.

# node
node = initialize_array(DatasetType.input, ComponentType.node, 2)
node[AttributeType.id] = np.array([2, 4])
node[AttributeType.u_rated] = [1e4, 4e2]

# load
sym_load = initialize_array(DatasetType.input, ComponentType.sym_load, 1)
sym_load[AttributeType.id] = [5]
sym_load[AttributeType.node] = [4]
sym_load[AttributeType.status] = [1]
sym_load[AttributeType.type] = [LoadGenType.const_power]
sym_load[AttributeType.p_specified] = [1e3]
sym_load[AttributeType.q_specified] = [5e3]

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

# transformer
transformer = initialize_array(DatasetType.input, ComponentType.transformer, 1)
transformer[AttributeType.id] = [3]
transformer[AttributeType.from_node] = [2]
transformer[AttributeType.to_node] = [4]
transformer[AttributeType.from_status] = [1]
transformer[AttributeType.to_status] = [1]
transformer[AttributeType.u1] = [1e4]
transformer[AttributeType.u2] = [4e2]
transformer[AttributeType.sn] = [1e5]
transformer[AttributeType.uk] = [0.1]
transformer[AttributeType.pk] = [1e3]
transformer[AttributeType.i0] = [1.0e-6]
transformer[AttributeType.p0] = [0.1]
transformer[AttributeType.winding_from] = [2]
transformer[AttributeType.winding_to] = [1]
transformer[AttributeType.clock] = [5]
transformer[AttributeType.tap_side] = [0]
transformer[AttributeType.tap_pos] = [3]
transformer[AttributeType.tap_min] = [-11]
transformer[AttributeType.tap_max] = [9]
transformer[AttributeType.tap_size] = [100]

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

We can print the input dataset by converting the numpy array to dataframe.

print(pd.DataFrame(input_data[ComponentType.transformer]))
   id  from_node  to_node  from_status  to_status       u1     u2        sn  \
0   3          2        4            1          1  10000.0  400.0  100000.0   

    uk      pk  ...  tap_nom  tap_size  uk_min  uk_max  pk_min  pk_max  \
0  0.1  1000.0  ...     -128     100.0     NaN     NaN     NaN     NaN   

   r_grounding_from  x_grounding_from  r_grounding_to  x_grounding_to  
0               NaN               NaN             NaN             NaN  

[1 rows x 31 columns]

One-time Power Flow Calculation

You can call the method calculate_power_flow to do a one-time calculation based on the current network data in the model.

For detailed explanation of the arguments, batch calculations and asymmetric calculations, we refer to the Power Flow Example and Asymmetric Calculation Example.

# validation (optional)
from power_grid_model.validation import assert_valid_input_data

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

# construction
model = PowerGridModel(input_data)

# one-time power flow calculation
output_data = model.calculate_power_flow(
    symmetric=True, error_tolerance=1e-8, max_iterations=20, calculation_method=CalculationMethod.newton_raphson
)

# result dataset
print("------node result------")
print(pd.DataFrame(output_data[ComponentType.node]))
------node result------
   id  energized      u_pu            u       u_angle            p  \
0   2          1  0.999999  9999.994897 -4.976260e-08  1002.882212   
1   4          1  0.965618   386.247005 -2.618522e+00 -1000.000000   

             q  
0  5027.744841  
1 -5000.000000  

One-time State Estimation

Below we present a simple example of state estimation for a network with a two-winding transformer.

NOTE: In power-grid-model, two-winding transformers belong to branch component type, therefore the measured_terminal_type of power sensors should be assigned to MeasuredTerminalType.branch_from/_to.

For detailed explanation of the arguments, batch calculations and asymmetric calculations, we refer to the State Estimation Example and Asymmetric Calculation Example.

# voltage sensor
sym_voltage_sensor = initialize_array(DatasetType.input, ComponentType.sym_voltage_sensor, 2)
sym_voltage_sensor[AttributeType.id] = [6, 7]
sym_voltage_sensor[AttributeType.measured_object] = [2, 4]
sym_voltage_sensor[AttributeType.u_sigma] = [1.0, 1.0]
sym_voltage_sensor[AttributeType.u_measured] = [1e5, 4e2]

# power sensor
sym_power_sensor = initialize_array(DatasetType.input, ComponentType.sym_power_sensor, 2)
sym_power_sensor[AttributeType.id] = [8, 9]
sym_power_sensor[AttributeType.measured_object] = [3, 3]
sym_power_sensor[AttributeType.measured_terminal_type] = [
    MeasuredTerminalType.branch_from,
    MeasuredTerminalType.branch_to,
]
sym_power_sensor[AttributeType.power_sigma] = [1.0, 1.0]
sym_power_sensor[AttributeType.p_measured] = [2e6, 1e5]
sym_power_sensor[AttributeType.q_measured] = [2e7, 1e6]

# use components from former input dataset cell.
input_data2 = {
    ComponentType.node: node,
    ComponentType.transformer: transformer,
    ComponentType.sym_load: sym_load,
    ComponentType.source: source,
    ComponentType.sym_voltage_sensor: sym_voltage_sensor,
    ComponentType.sym_power_sensor: sym_power_sensor,
}

# validation (optional)
from power_grid_model.validation import assert_valid_input_data

assert_valid_input_data(input_data=input_data2, calculation_type=CalculationType.state_estimation)

# construction
model2 = PowerGridModel(input_data2)

# one-time state estimation
output_data2 = model2.calculate_state_estimation(
    symmetric=True, error_tolerance=1e-8, max_iterations=20, calculation_method=CalculationMethod.iterative_linear
)

# result dataset
print("------node result------")
print(pd.DataFrame(output_data2[ComponentType.node]))
------node result------
   id  energized      u_pu             u   u_angle              p  \
0   2          1  9.987392  99873.919714  0.000000  470658.914282   
1   4          1  9.209290   3683.716125 -2.617974 -446912.927660   

              q  
0  4.700696e+06  
1 -4.464515e+06  

Three-Winding Transformer

Three-winding transformer is described as 3 transformers of pi model each connected together in star configuration, and it belongs to the branch3 component type which connects three nodes with possibly different voltage levels.

Example Network

We use a simple network with 3 nodes, 1 source, 1 load and 1 three-winding transformer. As shown below:

   source_1 --- node_2 --- three_winding_transformer_3 --- node_4 --- load_5
                                        |
                                        -------------------node_6 --- load_7
                                    

Input Dataset

We use the helper function initialize_array to create an input dataset.

Please refer to Components for detailed explanation of all component types and their input/output attributes.

# node
node = initialize_array(DatasetType.input, ComponentType.node, 3)
node[AttributeType.id] = np.array([2, 4, 6])
node[AttributeType.u_rated] = [1e4, 1e2, 1e2]

# load
sym_load = initialize_array(DatasetType.input, ComponentType.sym_load, 2)
sym_load[AttributeType.id] = [5, 7]
sym_load[AttributeType.node] = [4, 6]
sym_load[AttributeType.status] = [1]
sym_load[AttributeType.type] = [LoadGenType.const_power]
sym_load[AttributeType.p_specified] = [1e3, 1e3]
sym_load[AttributeType.q_specified] = [5e3, 5e3]

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

# three-winding transformer
three_winding_transformer = initialize_array(DatasetType.input, ComponentType.three_winding_transformer, 1)
three_winding_transformer[AttributeType.id] = [3]
three_winding_transformer[AttributeType.node_1] = [2]
three_winding_transformer[AttributeType.node_2] = [4]
three_winding_transformer[AttributeType.node_3] = [6]
three_winding_transformer[AttributeType.status_1] = [1]
three_winding_transformer[AttributeType.status_2] = [1]
three_winding_transformer[AttributeType.status_3] = [1]
three_winding_transformer[AttributeType.u1] = [1e4]
three_winding_transformer[AttributeType.u2] = [1e2]
three_winding_transformer[AttributeType.u3] = [1e2]
three_winding_transformer[AttributeType.sn_1] = [1e5]
three_winding_transformer[AttributeType.sn_2] = [1e5]
three_winding_transformer[AttributeType.sn_3] = [1e5]
three_winding_transformer[AttributeType.uk_12] = [0.09]
three_winding_transformer[AttributeType.uk_13] = [0.06]
three_winding_transformer[AttributeType.uk_23] = [0.06]
three_winding_transformer[AttributeType.pk_12] = [1e3]
three_winding_transformer[AttributeType.pk_13] = [1e3]
three_winding_transformer[AttributeType.pk_23] = [1e3]
three_winding_transformer[AttributeType.i0] = [0]
three_winding_transformer[AttributeType.p0] = [0]
three_winding_transformer[AttributeType.winding_1] = [2]
three_winding_transformer[AttributeType.winding_2] = [1]
three_winding_transformer[AttributeType.winding_3] = [1]
three_winding_transformer[AttributeType.clock_12] = [5]
three_winding_transformer[AttributeType.clock_13] = [-7]  # supports periodic input
three_winding_transformer[AttributeType.tap_side] = [0]
three_winding_transformer[AttributeType.tap_pos] = [0]
three_winding_transformer[AttributeType.tap_min] = [-10]
three_winding_transformer[AttributeType.tap_max] = [10]
three_winding_transformer[AttributeType.tap_nom] = [0]
three_winding_transformer[AttributeType.tap_size] = [1380]

# all
input_data3 = {
    ComponentType.node: node,
    ComponentType.three_winding_transformer: three_winding_transformer,
    ComponentType.sym_load: sym_load,
    ComponentType.source: source,
}

One-time power flow calculation

# validation (optional)
from power_grid_model.validation import assert_valid_input_data

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

# construction
model3 = PowerGridModel(input_data3)

# one-time power flow calculation
output_data3 = model3.calculate_power_flow(
    symmetric=True, error_tolerance=1e-8, max_iterations=20, calculation_method=CalculationMethod.newton_raphson
)

# result dataset
print("------node result------")
print(pd.DataFrame(output_data3[ComponentType.node]))
------node result------
   id  energized      u_pu            u       u_angle            p  \
0   2          1  0.999999  9999.989788 -9.966674e-08  2007.896574   
1   4          1  0.993097    99.309655 -2.618590e+00 -1000.000000   
2   6          1  0.994637    99.463733 -2.618281e+00 -1000.000000   

              q  
0  10062.592544  
1  -5000.000000  
2  -5000.000000  

One-time State Estimation

Below we present a simple example of state estimation for a network with a three-winding transformer.

NOTE: In power-grid-model, three-winding transformers belong to branch3 component type, therefore the measured_terminal_type of power sensors should be assigned to MeasuredTerminalType.branch3_1/_2/_3.

For detailed explanation of the arguments, batch calculations and asymmetric calculations, we refer to the State Estimation Example and Asymmetric Calculation Example.

# voltage sensor
sym_voltage_sensor = initialize_array(DatasetType.input, ComponentType.sym_voltage_sensor, 3)
sym_voltage_sensor[AttributeType.id] = [8, 9, 10]
sym_voltage_sensor[AttributeType.measured_object] = [2, 4, 6]
sym_voltage_sensor[AttributeType.u_sigma] = [1.0, 1.0, 1.0]
sym_voltage_sensor[AttributeType.u_measured] = [1e4, 1e2, 1e2]

# power sensor
sym_power_sensor = initialize_array(DatasetType.input, ComponentType.sym_power_sensor, 3)
sym_power_sensor[AttributeType.id] = [11, 12, 13]
sym_power_sensor[AttributeType.measured_object] = [3, 3, 3]
sym_power_sensor[AttributeType.measured_terminal_type] = [
    MeasuredTerminalType.branch3_1,
    MeasuredTerminalType.branch3_2,
    MeasuredTerminalType.branch3_3,
]
sym_power_sensor[AttributeType.power_sigma] = [1.0, 1.0, 1.0]
sym_power_sensor[AttributeType.p_measured] = [2e3, 1e3, 1e3]
sym_power_sensor[AttributeType.q_measured] = [1e4, 5e3, 5e3]

# use components from the one-time power flow calculation with three-winding transformer
input_data4 = {
    ComponentType.node: node,
    ComponentType.three_winding_transformer: three_winding_transformer,
    ComponentType.sym_load: sym_load,
    ComponentType.source: source,
    ComponentType.sym_voltage_sensor: sym_voltage_sensor,
    ComponentType.sym_power_sensor: sym_power_sensor,
}

# validation (optional)
from power_grid_model.validation import assert_valid_input_data

assert_valid_input_data(input_data=input_data4, calculation_type=CalculationType.state_estimation)

# construction
model4 = PowerGridModel(input_data4)

# one-time state estimation
output_data4 = model4.calculate_state_estimation(
    symmetric=True, error_tolerance=1e-8, max_iterations=20, calculation_method=CalculationMethod.iterative_linear
)

# result dataset
print("------three-winding transformer result------")
print(pd.DataFrame(output_data4[ComponentType.three_winding_transformer]))
------three-winding transformer result------
   id  energized  loading_1  loading_2  loading_3   loading         p_1  \
0   3          1   0.033993   0.016958   0.016967  0.033993  667.156598   

          q_1       i_1          s_1         p_2          q_2       i_2  \
0  3333.23678  0.196261  3399.347785 -333.400686 -1662.691395  9.813068   

           s_2         p_3          q_3       i_3          s_3  
0  1695.788516 -332.889246 -1663.677083  9.813068  1696.654557  

Power flow calculations with automatic tap changing

Example Network

We use a simple network with 3 nodes, 1 source, 1 load, 1 line, 1 transformer and 1 transformer tap regulator. As shown below:

                   (tap_side)  (control side)
source_1 --- node_2 --- transformer_3 --- node_4 --- line_5 --- node_6 --- load_7
                        |                    |
        transformer_tap_regulator_8 <--------/ (control voltage)

Input Dataset

We create an input dataset by using the helper function initialize_array.

Please refer to Components for detailed explanation of all component types and their input/output attributes.

# node
node = initialize_array(DatasetType.input, ComponentType.node, 3)
node[AttributeType.id] = [2, 4, 6]
node[AttributeType.u_rated] = [1e4, 4e2, 4e2]

# load
sym_load = initialize_array(DatasetType.input, ComponentType.sym_load, 1)
sym_load[AttributeType.id] = [7]
sym_load[AttributeType.node] = [6]
sym_load[AttributeType.status] = [1]
sym_load[AttributeType.type] = [LoadGenType.const_power]
sym_load[AttributeType.p_specified] = [1e3]
sym_load[AttributeType.q_specified] = [5e3]

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

# line
line = initialize_array(DatasetType.input, ComponentType.line, 1)
line[AttributeType.id] = [5]
line[AttributeType.from_node] = [4]
line[AttributeType.to_node] = [6]
line[AttributeType.from_status] = [1]
line[AttributeType.to_status] = [1]
line[AttributeType.r1] = [10.0]
line[AttributeType.x1] = [0.0]
line[AttributeType.c1] = [0.0]
line[AttributeType.tan1] = [0.0]

# transformer
transformer = initialize_array(DatasetType.input, ComponentType.transformer, 1)
transformer[AttributeType.id] = [3]
transformer[AttributeType.from_node] = [2]
transformer[AttributeType.to_node] = [4]
transformer[AttributeType.from_status] = [1]
transformer[AttributeType.to_status] = [1]
transformer[AttributeType.u1] = [1e4]
transformer[AttributeType.u2] = [4e2]
transformer[AttributeType.sn] = [1e5]
transformer[AttributeType.uk] = [0.1]
transformer[AttributeType.pk] = [1e3]
transformer[AttributeType.i0] = [1.0e-6]
transformer[AttributeType.p0] = [0.1]
transformer[AttributeType.winding_from] = [2]
transformer[AttributeType.winding_to] = [1]
transformer[AttributeType.clock] = [5]
transformer[AttributeType.tap_side] = [0]
transformer[AttributeType.tap_pos] = [3]
transformer[AttributeType.tap_min] = [-11]
transformer[AttributeType.tap_max] = [9]
transformer[AttributeType.tap_size] = [100]

# transformer tap regulator
transformer_tap_regulator = initialize_array(DatasetType.input, ComponentType.transformer_tap_regulator, 1)
transformer_tap_regulator[AttributeType.id] = [8]
transformer_tap_regulator[AttributeType.regulated_object] = [3]
transformer_tap_regulator[AttributeType.status] = [1]
transformer_tap_regulator[AttributeType.control_side] = [BranchSide.to_side]
transformer_tap_regulator[AttributeType.u_set] = [400.0]
transformer_tap_regulator[AttributeType.u_band] = [20.0]
transformer_tap_regulator[AttributeType.line_drop_compensation_r] = [0.0]
transformer_tap_regulator[AttributeType.line_drop_compensation_x] = [0.0]

# all
input_data5 = {
    ComponentType.node: node,
    ComponentType.line: line,
    ComponentType.transformer: transformer,
    ComponentType.sym_load: sym_load,
    ComponentType.source: source,
    ComponentType.transformer_tap_regulator: transformer_tap_regulator,
}

One-time Power Flow Calculation

As before, you can call the method calculate_power_flow to do a one-time calculation based on the current network data in the model.

For a detailed explanation of the arguments, batch calculations and asymmetric calculations, we refer to the Power Flow Example and Asymmetric Calculation Example.

# construction
model5 = PowerGridModel(input_data5)

# one-time power flow calculation without automatic tap changing
output_data5 = model5.calculate_power_flow()

# Both load-side nodes node_4 and node_6 have a voltage below 400 V
print("------node result------")
display(pd.DataFrame(output_data5[ComponentType.node])[[AttributeType.id, AttributeType.u]])
------node result------
id u
0 2 9999.994637
1 4 386.141058
2 6 322.704292

Any voltage tap position strategy

In the regular one-time power flow calculation in the example above, the voltage at the low-voltage side of the transformer is lower than 390 V.

To get a voltage at the control side of the transformer in the acceptable voltage band as provided to the transformer tap regulator, run the power flow calculation with any the tap changing strategy set to any valid tap.

# one-time power flow calculation with automatic tap changing
output_data6 = model5.calculate_power_flow(tap_changing_strategy=TapChangingStrategy.any_valid_tap)

# the node at the control side of the transformer now has a voltage within the specified voltage band
print("------node result------")
display(pd.DataFrame(output_data6[ComponentType.node])[[AttributeType.id, AttributeType.u]])

print("\n------tap regulator result------")
display(pd.DataFrame(output_data6[ComponentType.transformer_tap_regulator]))
------node result------
id u
0 2 9999.994657
1 4 393.881868
2 6 334.529465
------tap regulator result------
id energized tap_pos
0 8 1 1

You could also opt for fast_any_tap that takes advantage of binary search instead of linear search internally.

# one-time power flow calculation with automatic tap changing
output_data6f = model5.calculate_power_flow(tap_changing_strategy=TapChangingStrategy.fast_any_tap)

# the node at the control side of the transformer now has a voltage within the specified voltage band
print("------node result------")
display(pd.DataFrame(output_data6f[ComponentType.node])[[AttributeType.id, AttributeType.u]])

print("\n------tap regulator result------")
print("\n----------fast_any_tap----------")
display(pd.DataFrame(output_data6f[ComponentType.transformer_tap_regulator]))
------node result------
id u
0 2 9999.994675
1 4 401.932237
2 6 346.203709
------tap regulator result------

----------fast_any_tap----------
id energized tap_pos
0 8 1 -1

NOTE: the tap positions obtained using the any_valid_tap and fast_any_tap strategy may depend on the initial tap position of the transformers.

transformer_update = initialize_array(DatasetType.update, ComponentType.transformer, (2, 1))
transformer_update[AttributeType.id] = 3
transformer_update[AttributeType.tap_pos] = [[0], [1]]

update_data = {ComponentType.transformer: transformer_update}

print("------transformer batch update------")
display(pd.DataFrame(update_data[ComponentType.transformer][:, 0]))

# power flow batch calculation with automatic tap changing
output_data = model5.calculate_power_flow(
    update_data=update_data, tap_changing_strategy=TapChangingStrategy.fast_any_tap
)

print("------node_4 batch result------")
display(pd.DataFrame(output_data[ComponentType.node][:, 1])[[AttributeType.id, AttributeType.u]])  # only output node 1

print("\n------tap regulator batch result------")
display(pd.DataFrame(output_data[ComponentType.transformer_tap_regulator][:, 0]))
------transformer batch update------
id from_status to_status tap_pos
0 3 -128 -128 0
1 3 -128 -128 1
------node_4 batch result------
id u
0 4 397.867176
1 4 393.881868
------tap regulator batch result------
id energized tap_pos
0 8 1 0
1 8 1 1

Maximum voltage tap position strategy

To specifically optimize for the higher end of the voltage band, the max_voltage_tap tap changing strategy can be provided.

# one-time power flow calculation with automatic tap changing
output_data5 = model5.calculate_power_flow(tap_changing_strategy=TapChangingStrategy.max_voltage_tap)

print("------node result------")
display(pd.DataFrame(output_data5[ComponentType.node])[[AttributeType.id, AttributeType.u]])

print("\n------tap regulator result------")
display(pd.DataFrame(output_data5[ComponentType.transformer_tap_regulator]))
------node result------
id u
0 2 9999.994683
1 4 406.079525
2 6 352.021534
------tap regulator result------
id energized tap_pos
0 8 1 -2

Minimum voltage tap position strategy

To specifically optimize for the lower end of the voltage band, the min_voltage_tap tap changing strategy can be provided.

# one-time power flow calculation with automatic tap changing
output_data5 = model5.calculate_power_flow(tap_changing_strategy=TapChangingStrategy.min_voltage_tap)

print("------node result------")
display(pd.DataFrame(output_data5[ComponentType.node])[[AttributeType.id, AttributeType.u]])

print("\n------tap regulator result------")
display(pd.DataFrame(output_data5[ComponentType.transformer_tap_regulator]))
------node result------
id u
0 2 9999.994657
1 4 393.881868
2 6 334.529465
------tap regulator result------
id energized tap_pos
0 8 1 1

Automatic tap changing with line drop compensation

In the above examples, the voltage at the low-voltage side of the transformer is controlled. However, the load is still connected to a node with a voltage lower than 390 V.

To regulate the node that the load is connected to, the voltage needs to be compensated for the voltage drop over the line.

                   (tap_side)  (control side)
source_1 --- node_2 --- transformer_3 --- node_4 --- line_5 --- node_6 --- load_7
                        |                 |                         :
                        regulator_8 <-----/ <......................./ (line drop compensation)
                                     (control voltage) 
# use the same input data
input_data6 = {component: component_data.copy() for component, component_data in input_data5.items()}

# set the regulator's line drop compensation
input_data6[ComponentType.transformer_tap_regulator][AttributeType.line_drop_compensation_r] = [10.0]

# construction
model6 = PowerGridModel(input_data=input_data6)

The result of the power flow calculation with automatic tap changing contains a voltage at the node that the load is connected to that is within the specified voltage band.

# one-time power flow calculation with automatic tap changing
output_data6 = model6.calculate_power_flow(tap_changing_strategy=TapChangingStrategy.any_valid_tap)

print("------node result------")
display(pd.DataFrame(output_data6[ComponentType.node])[[AttributeType.id, AttributeType.u]])

print("\n------tap regulator result------")
display(pd.DataFrame(output_data6[ComponentType.transformer_tap_regulator]))
------node result------
id u
0 2 9999.994731
1 4 437.642686
2 6 393.354002
------tap regulator result------
id energized tap_pos
0 8 1 -9