Note

You can download this example as a Jupyter notebook or start it in interactive mode.

Security-Constrained Optimisation

In this example, the dispatch of generators is optimised using the security-constrained linear OPF, to guaranteed that no branches are overloaded by certain branch outages.

[1]:
import pypsa, os
import numpy as np
[2]:
network = pypsa.examples.scigrid_de(from_master=True)
WARNING:pypsa.io:Importing network from PyPSA version v0.17.1 while current version is v0.21.3. Read the release notes at https://pypsa.readthedocs.io/en/latest/release_notes.html to prepare your network for import.
INFO:pypsa.io:Imported network scigrid-de.nc has buses, generators, lines, loads, storage_units, transformers

There are some infeasibilities without line extensions.

[3]:
for line_name in ["316", "527", "602"]:
    network.lines.loc[line_name, "s_nom"] = 1200

now = network.snapshots[0]

Performing security-constrained linear OPF

[4]:
branch_outages = network.lines.index[:15]
network.sclopf(now, branch_outages=branch_outages, solver_name="cbc")
INFO:pypsa.opf:Building pyomo model using `kirchhoff` formulation
WARNING:pypsa.contingency:No type given for 1, assuming it is a line
WARNING:pypsa.contingency:No type given for 2, assuming it is a line
WARNING:pypsa.contingency:No type given for 3, assuming it is a line
WARNING:pypsa.contingency:No type given for 4, assuming it is a line
WARNING:pypsa.contingency:No type given for 5, assuming it is a line
WARNING:pypsa.contingency:No type given for 6, assuming it is a line
WARNING:pypsa.contingency:No type given for 7, assuming it is a line
WARNING:pypsa.contingency:No type given for 8, assuming it is a line
WARNING:pypsa.contingency:No type given for 9, assuming it is a line
WARNING:pypsa.contingency:No type given for 10, assuming it is a line
WARNING:pypsa.contingency:No type given for 11, assuming it is a line
WARNING:pypsa.contingency:No type given for 12, assuming it is a line
WARNING:pypsa.contingency:No type given for 13, assuming it is a line
WARNING:pypsa.contingency:No type given for 14, assuming it is a line
WARNING:pypsa.contingency:No type given for 15, assuming it is a line
INFO:pypsa.opf:Solving model using cbc
INFO:pypsa.opf:Optimization successful
# ==========================================================
# = Solver Results                                         =
# ==========================================================
# ----------------------------------------------------------
#   Problem Information
# ----------------------------------------------------------
Problem:
- Name: unknown
  Lower bound: 347887.0881
  Upper bound: 347887.0881
  Number of objectives: 1
  Number of constraints: 31362
  Number of variables: 2486
  Number of nonzeros: 441
  Sense: minimize
# ----------------------------------------------------------
#   Solver Information
# ----------------------------------------------------------
Solver:
- Status: ok
  User time: -1.0
  System time: 0.8
  Wallclock time: 0.77
  Termination condition: optimal
  Termination message: Model was solved to optimality (subject to tolerances), and an optimal solution is available.
  Statistics:
    Branch and bound:
      Number of bounded subproblems: None
      Number of created subproblems: None
    Black box:
      Number of iterations: 1242
  Error rc: 0
  Time: 0.7817301750183105
# ----------------------------------------------------------
#   Solution Information
# ----------------------------------------------------------
Solution:
- number of solutions: 0
  number of solutions displayed: 0

For the PF, set the P to the optimised P.

[5]:
network.generators_t.p_set = network.generators_t.p_set.reindex(
    columns=network.generators.index
)
network.generators_t.p_set.loc[now] = network.generators_t.p.loc[now]

network.storage_units_t.p_set = network.storage_units_t.p_set.reindex(
    columns=network.storage_units.index
)
network.storage_units_t.p_set.loc[now] = network.storage_units_t.p.loc[now]

Check no lines are overloaded with the linear contingency analysis

[6]:
p0_test = network.lpf_contingency(now, branch_outages=branch_outages)
p0_test
INFO:pypsa.pf:Performing linear load-flow on AC sub-network SubNetwork 0 for snapshot(s) DatetimeIndex(['2011-01-01'], dtype='datetime64[ns]', name='snapshot', freq=None)
WARNING:pypsa.contingency:No type given for 1, assuming it is a line
WARNING:pypsa.contingency:No type given for 2, assuming it is a line
WARNING:pypsa.contingency:No type given for 3, assuming it is a line
WARNING:pypsa.contingency:No type given for 4, assuming it is a line
WARNING:pypsa.contingency:No type given for 5, assuming it is a line
WARNING:pypsa.contingency:No type given for 6, assuming it is a line
WARNING:pypsa.contingency:No type given for 7, assuming it is a line
WARNING:pypsa.contingency:No type given for 8, assuming it is a line
WARNING:pypsa.contingency:No type given for 9, assuming it is a line
WARNING:pypsa.contingency:No type given for 10, assuming it is a line
WARNING:pypsa.contingency:No type given for 11, assuming it is a line
WARNING:pypsa.contingency:No type given for 12, assuming it is a line
WARNING:pypsa.contingency:No type given for 13, assuming it is a line
WARNING:pypsa.contingency:No type given for 14, assuming it is a line
WARNING:pypsa.contingency:No type given for 15, assuming it is a line
[6]:
base (Line, 1) (Line, 2) (Line, 3) (Line, 4) (Line, 5) (Line, 6) (Line, 7) (Line, 8) (Line, 9) (Line, 10) (Line, 11) (Line, 12) (Line, 13) (Line, 14) (Line, 15)
Line 1 -68.803346 0.000000 -68.803346 -68.803346 -68.803346 -68.803346 -68.803346 -68.803346 -68.803346 -68.803346 -68.803346 -68.803346 -68.803346 -68.803346 -68.803346 -68.803346
2 190.707265 190.707265 0.000000 212.790039 13.505556 -122.706611 190.839626 191.016597 190.975555 190.815136 191.158931 154.292808 190.026608 190.064355 191.158177 190.758686
3 325.824492 325.824492 334.145547 0.000000 383.397379 250.482728 325.812935 325.797484 325.801067 325.853064 325.946002 313.484603 327.596349 327.498089 325.945799 325.839946
4 -750.815233 -750.815233 -724.824605 -773.225246 0.000000 -487.570136 -750.782636 -750.739052 -750.749159 -750.921327 -751.264938 -708.345991 -756.662619 -756.338348 -751.264188 -750.871164
5 1069.888693 1069.888693 1045.960033 1054.623188 932.859980 0.000000 1067.815628 1065.043858 1065.686669 1069.894049 1069.913751 1156.214752 1134.263564 1130.693606 1069.913709 1069.893820
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
Transformer 404 3.995927 3.995927 3.998272 3.994991 3.992287 3.984332 3.995921 3.995912 3.995914 3.988667 3.966849 3.993605 3.996374 3.996349 3.966898 3.993758
413 94.603093 94.603093 94.650025 94.624896 94.833999 94.575138 94.606061 94.610031 94.609110 94.624524 94.688312 94.841071 94.467176 94.474713 94.688169 94.608894
421 52.977689 52.977689 53.170623 53.069458 53.942928 52.873377 52.990054 53.006585 53.002751 53.084124 53.401445 53.971017 52.411839 52.443219 53.400738 53.007013
450 82.518672 82.518672 82.460263 82.496275 82.250386 82.130707 82.517165 82.515150 82.515617 82.523641 82.539029 82.337610 82.595022 82.590788 82.538995 82.520604
458 83.475454 83.475454 83.415989 83.452658 83.202372 83.080710 83.473919 83.471867 83.472343 83.480511 83.496176 83.291125 83.553195 83.548884 83.496141 83.477420

948 rows × 16 columns

Check loading as per unit of s_nom in each contingency

[7]:
max_loading = (
    abs(p0_test.divide(network.passive_branches().s_nom, axis=0)).describe().loc["max"]
)
max_loading
[7]:
base          1.0
(Line, 1)     1.0
(Line, 2)     1.0
(Line, 3)     1.0
(Line, 4)     1.0
(Line, 5)     1.0
(Line, 6)     1.0
(Line, 7)     1.0
(Line, 8)     1.0
(Line, 9)     1.0
(Line, 10)    1.0
(Line, 11)    1.0
(Line, 12)    1.0
(Line, 13)    1.0
(Line, 14)    1.0
(Line, 15)    1.0
Name: max, dtype: float64
[8]:
np.allclose(max_loading, np.ones((len(max_loading))))
[8]:
True