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
ERROR 1: PROJ: proj_create_from_database: Open of /home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/share/proj failed
[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.26.2. 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.optimize.optimize_security_constrained(
now, branch_outages=branch_outages, solver_name="cbc"
)
WARNING:pypsa.components:The following transformers have zero r, which could break the linear load flow:
Index(['2', '5', '10', '12', '13', '15', '18', '20', '22', '24', '26', '30',
'32', '37', '42', '46', '52', '56', '61', '68', '69', '74', '78', '86',
'87', '94', '95', '96', '99', '100', '104', '105', '106', '107', '117',
'120', '123', '124', '125', '128', '129', '138', '143', '156', '157',
'159', '160', '165', '184', '191', '195', '201', '220', '231', '232',
'233', '236', '247', '248', '250', '251', '252', '261', '263', '264',
'267', '272', '279', '281', '282', '292', '303', '307', '308', '312',
'315', '317', '322', '332', '334', '336', '338', '351', '353', '360',
'362', '382', '384', '385', '391', '403', '404', '413', '421', '450',
'458'],
dtype='object', name='Transformer')
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.26.2/lib/python3.11/site-packages/xarray/core/utils.py:494: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
warnings.warn(
INFO:linopy.model: Solve problem using Cbc solver
INFO:linopy.io: Writing time: 0.37s
INFO:linopy.constants: Optimization successful:
Status: ok
Termination condition: optimal
Solution: 2485 primals, 34397 duals
Objective: 3.48e+05
Solver model: not available
Solver message: Optimal - objective value 347887.09255214
Welcome to the CBC MILP Solver
Version: 2.10.10
Build Date: Apr 19 2023
command line - cbc -printingOptions all -import /tmp/linopy-problem-h42h_9a3.lp -solve -solu /tmp/linopy-solve-r01tpvlf.sol (default strategy 1)
Option for printingOptions changed from normal to all
Presolve 14006 (-20391) rows, 1061 (-1424) columns and 30479 (-28022) elements
Perturbing problem by 0.001% of 34203.746 - largest nonzero change 0.00099776614 ( 0.0015440604%) - largest zero change 0.00099619036
0 Obj 24484.924 Primal inf 6732711.7 (3096) Dual inf 1209.4693 (1)
295 Obj 1778.5517 Primal inf 2.845691e+08 (3036)
535 Obj 1786.9359 Primal inf 1.0295285e+08 (3113)
718 Obj 1789.6726 Primal inf 1.1192701e+08 (2610)
919 Obj 2821.1915 Primal inf 2.660011e+11 (4795)
946 Obj 54785.758 Primal inf 1.4277873e+10 (3189)
1091 Obj 271983.1 Primal inf 9024153.9 (978)
1152 Obj 342158.37 Primal inf 233.98947 (28)
1185 Obj 347890.68
1185 Obj 347887.1 Dual inf 8.6477973e-05 (1)
1186 Obj 347887.09
Optimal - objective value 347887.09
After Postsolve, objective 347887.09, infeasibilities - dual 50.795647 (2), primal 4.2010642e-07 (2)
Presolved model was optimal, full model needs cleaning up
0 Obj 347887.09 Dual inf 0.50795627 (2)
End of values pass after 2 iterations
2 Obj 347887.09
Optimal - objective value 347887.09
Optimal objective 347887.0926 - 1188 iterations time 0.672, Presolve 0.07
Total time (CPU seconds): 0.90 (Wallclock seconds): 0.95
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, StorageUnit-fix-p_dispatch-lower, StorageUnit-fix-p_dispatch-upper, StorageUnit-fix-p_store-lower, StorageUnit-fix-p_store-upper, StorageUnit-fix-state_of_charge-lower, StorageUnit-fix-state_of_charge-upper, Kirchhoff-Voltage-Law, StorageUnit-energy_balance, Transformer-fix-s-lower-security, Transformer-fix-s-upper-security, Line-fix-s-lower-security, Line-fix-s-upper-security were not assigned to the network.
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) | ||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Transformer | 2 | -398.673557 | -398.673557 | -418.812170 | -359.776332 | -494.531593 | -456.944271 | -398.465081 | -398.186341 | -398.250985 | -398.719310 | -398.867087 | -390.267047 | -407.034895 | -406.571211 | -398.866764 | -398.697283 |
5 | 883.055800 | 883.055800 | 898.249129 | 811.125660 | 988.177524 | 745.490073 | 883.034699 | 883.006486 | 883.013029 | 883.107970 | 883.277664 | 860.524527 | 886.291015 | 886.111604 | 883.277294 | 883.084018 | |
10 | -227.642977 | -227.642977 | -227.040368 | -227.465351 | -225.951447 | -302.148218 | -239.209405 | -254.674181 | -251.087689 | -227.628693 | -227.583800 | -209.997527 | 42.837910 | 27.838187 | -227.583898 | -227.636786 | |
12 | -1211.646080 | -1211.646080 | -1211.821857 | -1211.717451 | -1212.306156 | -1192.941429 | -1191.974372 | -1165.672511 | -1171.772271 | -1211.648632 | -1211.656386 | -1216.321290 | -1479.256769 | -1464.416214 | -1211.656369 | -1211.646922 | |
13 | 41.861950 | 41.861950 | 41.500970 | 41.608206 | 39.441350 | 41.627203 | 41.832212 | 41.792450 | 41.801671 | 67.075992 | 5.594734 | 39.431473 | 43.269601 | 43.191538 | 5.655277 | 73.316136 | |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
Line | 855 | 65.129873 | 65.129873 | 65.010504 | 65.078881 | 64.536480 | 64.147069 | 65.127057 | 65.123292 | 65.124165 | 65.140617 | 65.173879 | 64.753233 | 65.277580 | 65.269389 | 65.173805 | 65.134037 |
856 | 93.141666 | 93.141666 | 92.945010 | 93.049299 | 92.092939 | 91.247887 | 93.137330 | 93.131532 | 93.132876 | 93.160471 | 93.218668 | 92.505187 | 93.376630 | 93.363600 | 93.218540 | 93.148934 | |
857 | 359.231588 | 359.231588 | 357.976182 | 359.455060 | 359.413098 | 372.664329 | 359.179306 | 359.109403 | 359.125615 | 359.237745 | 359.258511 | 356.942347 | 361.344964 | 361.227765 | 359.258466 | 359.235642 | |
858 | 33.680485 | 33.680485 | 33.624796 | 33.654329 | 33.383509 | 33.144210 | 33.679257 | 33.677615 | 33.677996 | 33.685810 | 33.702290 | 33.500249 | 33.747022 | 33.743332 | 33.702254 | 33.682543 | |
859 | 182.371439 | 182.371439 | 181.969317 | 182.274409 | 181.001252 | 181.315362 | 182.360064 | 182.344855 | 182.348382 | 182.396656 | 182.474891 | 181.282508 | 182.909996 | 182.880130 | 182.474718 | 182.381371 |
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