Transformers¶
This example illustrates the use of transformers with non-trivial phase shift and tap ratio. The example is a copy of the pandapower minimal example.
In [2]:
Copied!
import numpy as np
import pandas as pd
import pypsa
n = pypsa.Network()
n.add("Bus", "MV bus", v_nom=20, v_mag_pu_set=1.02)
n.add("Bus", "LV1 bus", v_nom=0.4)
n.add("Bus", "LV2 bus", v_nom=0.4)
n.add(
"Transformer",
"MV-LV trafo",
type="0.4 MVA 20/0.4 kV",
bus0="MV bus",
bus1="LV1 bus",
)
n.add(
"Line", "LV cable", type="NAYY 4x50 SE", bus0="LV1 bus", bus1="LV2 bus", length=0.1
)
n.add("Generator", "External Grid", bus="MV bus", control="Slack", marginal_cost=10)
n.add("Load", "LV load", bus="LV2 bus", p_set=0.1, q_set=0.05);
import numpy as np
import pandas as pd
import pypsa
n = pypsa.Network()
n.add("Bus", "MV bus", v_nom=20, v_mag_pu_set=1.02)
n.add("Bus", "LV1 bus", v_nom=0.4)
n.add("Bus", "LV2 bus", v_nom=0.4)
n.add(
"Transformer",
"MV-LV trafo",
type="0.4 MVA 20/0.4 kV",
bus0="MV bus",
bus1="LV1 bus",
)
n.add(
"Line", "LV cable", type="NAYY 4x50 SE", bus0="LV1 bus", bus1="LV2 bus", length=0.1
)
n.add("Generator", "External Grid", bus="MV bus", control="Slack", marginal_cost=10)
n.add("Load", "LV load", bus="LV2 bus", p_set=0.1, q_set=0.05);
In [3]:
Copied!
def run_power_flow(n: pypsa.Network) -> pd.DataFrame:
n.lpf()
n.pf(use_seed=True)
return pd.DataFrame(
{
"Voltage Angles": n.buses_t.v_ang.loc["now"] * 180.0 / np.pi,
"Voltage Magnitude": n.buses_t.v_mag_pu.loc["now"],
}
)
def run_power_flow(n: pypsa.Network) -> pd.DataFrame:
n.lpf()
n.pf(use_seed=True)
return pd.DataFrame(
{
"Voltage Angles": n.buses_t.v_ang.loc["now"] * 180.0 / np.pi,
"Voltage Magnitude": n.buses_t.v_mag_pu.loc["now"],
}
)
In [4]:
Copied!
run_power_flow(n)
run_power_flow(n)
INFO:pypsa.network.power_flow:Performing linear load-flow on AC sub-network <pypsa.SubNetwork object at 0x72f8600faf90> for snapshot(s) Index(['now'], dtype='object', name='snapshot')
INFO:pypsa.network.power_flow:Performing non-linear load-flow on AC sub-network <pypsa.SubNetwork object at 0x72f80bf70e10> for snapshots Index(['now'], dtype='object', name='snapshot')
Out[4]:
| Voltage Angles | Voltage Magnitude | |
|---|---|---|
| name | ||
| MV bus | 0.000000 | 1.020000 |
| LV1 bus | -150.760126 | 1.008843 |
| LV2 bus | -149.884141 | 0.964431 |
In [5]:
Copied!
n.transformers.tap_position = 2
run_power_flow(n)
n.transformers.tap_position = 2
run_power_flow(n)
INFO:pypsa.network.power_flow:Performing linear load-flow on AC sub-network <pypsa.SubNetwork object at 0x72f82c359310> for snapshot(s) Index(['now'], dtype='object', name='snapshot')
INFO:pypsa.network.power_flow:Performing non-linear load-flow on AC sub-network <pypsa.SubNetwork object at 0x72f80c31ec40> for snapshots Index(['now'], dtype='object', name='snapshot')
Out[5]:
| Voltage Angles | Voltage Magnitude | |
|---|---|---|
| name | ||
| MV bus | 0.000000 | 1.020000 |
| LV1 bus | -150.843911 | 0.959655 |
| LV2 bus | -149.870837 | 0.912713 |
In [6]:
Copied!
n.transformers.tap_position = -2
run_power_flow(n)
n.transformers.tap_position = -2
run_power_flow(n)
INFO:pypsa.network.power_flow:Performing linear load-flow on AC sub-network <pypsa.SubNetwork object at 0x72f8600b5810> for snapshot(s) Index(['now'], dtype='object', name='snapshot')
INFO:pypsa.network.power_flow:Performing non-linear load-flow on AC sub-network <pypsa.SubNetwork object at 0x72f80b514830> for snapshots Index(['now'], dtype='object', name='snapshot')
Out[6]:
| Voltage Angles | Voltage Magnitude | |
|---|---|---|
| name | ||
| MV bus | 0.000000 | 1.020000 |
| LV1 bus | -150.681666 | 1.063133 |
| LV2 bus | -149.896634 | 1.021202 |
Now play with tap changer on LV side
In [7]:
Copied!
new_trafo_lv_tap = n.transformer_types.loc[["0.4 MVA 20/0.4 kV"]]
new_trafo_lv_tap.index = ["New trafo"]
new_trafo_lv_tap.tap_side = 1
new_trafo_lv_tap.T
new_trafo_lv_tap = n.transformer_types.loc[["0.4 MVA 20/0.4 kV"]]
new_trafo_lv_tap.index = ["New trafo"]
new_trafo_lv_tap.tap_side = 1
new_trafo_lv_tap.T
Out[7]:
| New trafo | |
|---|---|
| f_nom | 50.0 |
| s_nom | 0.4 |
| v_nom_0 | 20.0 |
| v_nom_1 | 0.4 |
| vsc | 6.0 |
| vscr | 1.425 |
| pfe | 1.35 |
| i0 | 0.3375 |
| phase_shift | 150.0 |
| tap_side | 1 |
| tap_neutral | 0 |
| tap_min | -2 |
| tap_max | 2 |
| tap_step | 2.5 |
| references | pandapower;Oswald - Transformatoren - Vorlesun... |
In [8]:
Copied!
n.add("TransformerType", "New trafo", **new_trafo_lv_tap.iloc[0].to_dict())
n.transformers.type = "New trafo"
n.transformers.tap_position = 2
run_power_flow(n)
n.add("TransformerType", "New trafo", **new_trafo_lv_tap.iloc[0].to_dict())
n.transformers.type = "New trafo"
n.transformers.tap_position = 2
run_power_flow(n)
INFO:pypsa.network.power_flow:Performing linear load-flow on AC sub-network <pypsa.SubNetwork object at 0x72f80bf4d8c0> for snapshot(s) Index(['now'], dtype='object', name='snapshot')
INFO:pypsa.network.power_flow:Performing non-linear load-flow on AC sub-network <pypsa.SubNetwork object at 0x72f80bf4d8c0> for snapshots Index(['now'], dtype='object', name='snapshot')
Out[8]:
| Voltage Angles | Voltage Magnitude | |
|---|---|---|
| name | ||
| MV bus | 0.000000 | 1.020000 |
| LV1 bus | -150.755820 | 1.059317 |
| LV2 bus | -149.964875 | 1.017221 |
In [9]:
Copied!
n.transformers.T
n.transformers.T
Out[9]:
| name | MV-LV trafo |
|---|---|
| bus0 | MV bus |
| bus1 | LV1 bus |
| type | New trafo |
| model | t |
| x | 0.058283 |
| r | 0.01425 |
| g | 0.003375 |
| b | -0.0 |
| s_nom | 0.4 |
| s_nom_mod | 0.0 |
| s_nom_extendable | False |
| s_nom_min | 0.0 |
| s_nom_max | inf |
| s_nom_set | NaN |
| s_max_pu | 1.0 |
| capital_cost | 0.0 |
| overnight_cost | NaN |
| discount_rate | NaN |
| fom_cost | 0.0 |
| num_parallel | 1.0 |
| tap_ratio | 1.05 |
| tap_side | 1 |
| tap_position | 2 |
| phase_shift | 150.0 |
| active | True |
| build_year | 0 |
| lifetime | inf |
| v_ang_min | -inf |
| v_ang_max | inf |
| sub_network | 0 |
| x_pu | 0.145712 |
| r_pu | 0.035618 |
| g_pu | 0.00135 |
| b_pu | -0.0 |
| x_pu_eff | 0.152994 |
| r_pu_eff | 0.037406 |
| s_nom_opt | 0.0 |
In [10]:
Copied!
n.transformers.tap_position = -2
run_power_flow(n)
n.transformers.tap_position = -2
run_power_flow(n)
INFO:pypsa.network.power_flow:Performing linear load-flow on AC sub-network <pypsa.SubNetwork object at 0x72f80b5f9050> for snapshot(s) Index(['now'], dtype='object', name='snapshot')
INFO:pypsa.network.power_flow:Performing non-linear load-flow on AC sub-network <pypsa.SubNetwork object at 0x72f80c3a3150> for snapshots Index(['now'], dtype='object', name='snapshot')
Out[10]:
| Voltage Angles | Voltage Magnitude | |
|---|---|---|
| name | ||
| MV bus | 0.000000 | 1.020000 |
| LV1 bus | -150.765232 | 0.958366 |
| LV2 bus | -149.789394 | 0.911353 |
Finally, let's double-check that the phase shift is also there in the linear optimal power flow (optimisation) solution.
In [11]:
Copied!
n.generators.p_nom = 1
n.lines.s_nom = 1
n.optimize()
pd.DataFrame(
{
"Voltage Angles": n.buses_t.v_ang.loc["now"] * 180.0 / np.pi,
"Voltage Magnitude": n.buses_t.v_mag_pu.loc["now"],
}
)
n.generators.p_nom = 1
n.lines.s_nom = 1
n.optimize()
pd.DataFrame(
{
"Voltage Angles": n.buses_t.v_ang.loc["now"] * 180.0 / np.pi,
"Voltage Magnitude": n.buses_t.v_mag_pu.loc["now"],
}
)
/tmp/ipykernel_8431/1803171228.py:3: FutureWarning: The default value of `include_objective_constant` will change from True to False in version 2.0. Set `include_objective_constant` explicitly to suppress this warning. Using False improves LP numerical conditioning by not including the objective constant as a variable. n.optimize() WARNING:pypsa.consistency:The following buses have carriers which are not defined. Run n.sanitize() to add them. Components with undefined carriers: Index(['MV bus', 'LV1 bus', 'LV2 bus'], dtype='object', name='name')
WARNING:pypsa.consistency:Encountered nan's in varying data 'p' for columns ['External Grid'] of component 'Generator'.
WARNING:pypsa.consistency:The following lines have carriers which are not defined. Run n.sanitize() to add them. Components with undefined carriers: Index(['LV cable'], dtype='object', name='name')
WARNING:pypsa.consistency:The following sub_networks have carriers which are not defined. Run n.sanitize() to add them. Components with undefined carriers: Index(['0'], dtype='object', name='name')
INFO:linopy.model: Solve problem using Highs solver
INFO:linopy.model:Solver options: - log_to_console: False
INFO:linopy.io: Writing time: 0.04s
INFO:linopy.constants: Optimization successful: Status: ok Termination condition: optimal Solution: 3 primals, 9 duals Objective: 1.00e+00 Solver model: available Solver message: Optimal
INFO:pypsa.optimization.optimize:The shadow-prices of the constraints Generator-fix-p-lower, Generator-fix-p-upper, Line-fix-s-lower, Line-fix-s-upper, Transformer-fix-s-lower, Transformer-fix-s-upper were not assigned to the network.
Out[11]:
| Voltage Angles | Voltage Magnitude | |
|---|---|---|
| name | ||
| MV bus | 0.627810 | 1.020000 |
| LV1 bus | -0.165294 | 0.958366 |
| LV2 bus | -0.462516 | 0.911353 |