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 PyPSA from older version of PyPSA than current version.
Please read the release notes at https://pypsa.org/doc/release_notes.html
carefully to prepare your network for import.
Currently used PyPSA version [0, 18, 1], imported network file PyPSA version [0, 17, 1].

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
/home/docs/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/scipy/sparse/linalg/dsolve/linsolve.py:318: SparseEfficiencyWarning: splu requires CSC matrix format
  warn('splu requires CSC matrix format', SparseEfficiencyWarning)
/home/docs/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/contingency.py:70: RuntimeWarning: divide by zero encountered in true_divide
  denominator = csr_matrix((1/(1-np.diag(branch_PTDF)),(r_[:num_branches],r_[:num_branches])))
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
WARNING:pyomo.solvers:Could not locate the 'cbc' executable, which is required for solver cbc
---------------------------------------------------------------------------
ApplicationError                          Traceback (most recent call last)
/tmp/ipykernel_792/3955788825.py in <module>
      1 branch_outages = network.lines.index[:15]
----> 2 network.sclopf(now,branch_outages=branch_outages, solver_name='cbc')

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/contingency.py in network_sclopf(network, snapshots, branch_outages, solver_name, pyomo, skip_pre, extra_functionality, solver_options, keep_files, formulation, ptdf_tolerance)
    368                  skip_pre=True, extra_functionality=_extra_functionality,
    369                  solver_options=solver_options, keep_files=keep_files,
--> 370                  formulation=formulation, **pyomo_kwargs)

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/components.py in lopf(self, snapshots, pyomo, solver_name, solver_options, solver_logfile, formulation, keep_files, extra_functionality, multi_investment_periods, **kwargs)
    646
    647         if pyomo:
--> 648             return network_lopf(self, **args)
    649         else:
    650             return network_lopf_lowmem(self, **args)

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/opf.py in network_lopf(network, snapshots, solver_name, solver_io, skip_pre, extra_functionality, multi_investment_periods, solver_logfile, solver_options, keep_files, formulation, ptdf_tolerance, free_memory, extra_postprocessing)
   1663                               solver_logfile=solver_logfile, solver_options=solver_options,
   1664                               keep_files=keep_files, free_memory=free_memory,
-> 1665                               extra_postprocessing=extra_postprocessing)

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/opf.py in network_lopf_solve(network, snapshots, formulation, solver_options, solver_logfile, keep_files, free_memory, extra_postprocessing)
   1563             network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, logfile=solver_logfile, options=solver_options)
   1564     else:
-> 1565         network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, logfile=solver_logfile, options=solver_options)
   1566
   1567     if logger.isEnabledFor(logging.INFO):

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pyomo/opt/base/solvers.py in solve(self, *args, **kwds)
    510         """ Solve the problem """
    511
--> 512         self.available(exception_flag=True)
    513         #
    514         # If the inputs are models, then validate that they have been

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pyomo/opt/solver/shellcmd.py in available(self, exception_flag)
    123             if exception_flag:
    124                 msg = "No executable found for solver '%s'"
--> 125                 raise ApplicationError(msg % self.name)
    126             return False
    127         return True

ApplicationError: No executable found for solver 'cbc'

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]', freq=None)
/home/docs/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/scipy/sparse/linalg/dsolve/linsolve.py:318: SparseEfficiencyWarning: splu requires CSC matrix format
  warn('splu requires CSC matrix format', SparseEfficiencyWarning)
/home/docs/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/contingency.py:70: RuntimeWarning: divide by zero encountered in true_divide
  denominator = csr_matrix((1/(1-np.diag(branch_PTDF)),(r_[:num_branches],r_[:num_branches])))
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)
name
Transformer 2 -38405.760474 -38405.760474 -37524.606180 -38651.314748 -38024.054191 -38332.574280 -38404.456639 -38404.837538 -38404.582759 -38405.995642 -38406.675222 -38467.252543 -38428.699550 -38427.275047 -38406.673695 -38405.860448
5 -3147.518795 -3147.518795 -3812.294807 -2693.431047 -3566.113045 -2974.740546 -3147.650764 -3147.612211 -3147.637999 -3147.250645 -3146.470125 -2982.706686 -3138.643080 -3139.194257 -3146.471875 -3147.399893
10 1134.736147 1134.736147 1108.369316 1133.614815 1128.000480 1228.312398 1062.398182 1083.530812 1069.395423 1134.809567 1135.015857 1005.662933 1876.792258 1830.711003 1135.015390 1134.762235
12 -751.239426 -751.239426 -743.548394 -750.788863 -748.611004 -774.731882 -628.209969 -664.151483 -640.110592 -751.252547 -751.288140 -717.041121 -1485.421235 -1439.828970 -751.288059 -751.242977
13 -75.637982 -75.637982 -59.843461 -74.036117 -65.999162 -75.343146 -75.823972 -75.769637 -75.805981 53.962071 -247.060201 -57.859489 -71.776136 -72.015954 -246.774034 56.902183
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
Line 855 322.269748 322.269748 327.492701 322.591660 324.632638 323.504120 322.252135 322.257280 322.253839 322.324971 322.477745 325.024802 322.674975 322.649811 322.477397 322.287290
856 529.037526 529.037526 537.642110 529.620629 533.213552 531.416054 529.010405 529.018328 529.013029 529.134182 529.401488 533.693253 529.682144 529.642114 529.400881 529.068149
857 685.362531 685.362531 740.292181 683.951778 684.639762 668.491429 685.035552 685.131075 685.067180 685.394175 685.489784 702.107908 691.160513 690.800462 685.489571 685.379613
858 157.849501 157.849501 160.286124 158.014623 159.032057 158.523046 157.841821 157.844065 157.842564 157.876872 157.952567 159.167897 158.032042 158.020707 157.952395 157.858173
859 768.482235 768.482235 786.076837 769.094771 773.938314 769.808634 768.411094 768.431877 768.417976 768.611853 768.971217 776.447560 769.959749 769.867997 768.970401 768.524086

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           52.006682
(Line, 1)     104.008569
(Line, 2)      52.006682
(Line, 3)      52.006682
(Line, 4)      52.006682
(Line, 5)      52.006682
(Line, 6)      52.006682
(Line, 7)      52.006682
(Line, 8)      52.006682
(Line, 9)      52.006682
(Line, 10)     52.006682
(Line, 11)     52.006682
(Line, 12)     52.006682
(Line, 13)     52.006682
(Line, 14)     52.006682
(Line, 15)     52.006682
Name: max, dtype: float64
[8]:
np.allclose(max_loading,np.ones((len(max_loading))))
[8]:
False