Note

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

Meshed AC-DC example

This example has a 3-node AC network coupled via AC-DC converters to a 3-node DC network. There is also a single point-to-point DC using the Link component.

The data files for this example are in the examples folder of the github repository: https://github.com/PyPSA/PyPSA.

[1]:
import pypsa
import numpy as np
import pandas as pd
import os
import matplotlib.pyplot as plt
import cartopy.crs as ccrs

%matplotlib inline
plt.rc("figure", figsize=(8, 8))
[2]:
network = pypsa.examples.ac_dc_meshed(from_master=True)
WARNING:pypsa.io:
Importing PyPSA from older version of PyPSA than current version.
Please read the release notes at https://pypsa.readthedocs.io/en/latest/release_notes.html
carefully to prepare your network for import.
Currently used PyPSA version [0, 19, 3], imported network file PyPSA version [0, 17, 1].

INFO:pypsa.io:Imported network ac-dc-meshed.nc has buses, carriers, generators, global_constraints, lines, links, loads
[3]:
# get current type (AC or DC) of the lines from the buses
lines_current_type = network.lines.bus0.map(network.buses.carrier)
lines_current_type
[3]:
Line
0    AC
1    AC
2    DC
3    DC
4    DC
5    AC
6    AC
Name: bus0, dtype: object
[4]:
network.plot(
    line_colors=lines_current_type.map(lambda ct: "r" if ct == "DC" else "b"),
    title="Mixed AC (blue) - DC (red) network - DC (cyan)",
    color_geomap=True,
    jitter=0.3,
)
plt.tight_layout()
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.19.3/lib/python3.10/site-packages/cartopy/io/__init__.py:241: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/50m_physical/ne_50m_land.zip
  warnings.warn(f'Downloading: {url}', DownloadWarning)
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.19.3/lib/python3.10/site-packages/cartopy/io/__init__.py:241: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/50m_physical/ne_50m_ocean.zip
  warnings.warn(f'Downloading: {url}', DownloadWarning)
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.19.3/lib/python3.10/site-packages/cartopy/io/__init__.py:241: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/50m_cultural/ne_50m_admin_0_boundary_lines_land.zip
  warnings.warn(f'Downloading: {url}', DownloadWarning)
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.19.3/lib/python3.10/site-packages/cartopy/io/__init__.py:241: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/50m_physical/ne_50m_coastline.zip
  warnings.warn(f'Downloading: {url}', DownloadWarning)
../_images/examples_ac-dc-lopf_4_1.png
[5]:
network.links.loc["Norwich Converter", "p_nom_extendable"] = False

We inspect the topology of the network. Therefore use the function determine_network_topology and inspect the subnetworks in network.sub_networks.

[6]:
network.determine_network_topology()
network.sub_networks["n_branches"] = [
    len(sn.branches()) for sn in network.sub_networks.obj
]
network.sub_networks["n_buses"] = [len(sn.buses()) for sn in network.sub_networks.obj]

network.sub_networks
[6]:
attribute carrier slack_bus obj n_branches n_buses
SubNetwork
0 AC Manchester SubNetwork 0 3 3
1 DC Norwich DC SubNetwork 1 3 3
2 AC Frankfurt SubNetwork 2 1 2
3 AC Norway SubNetwork 3 0 1

The network covers 10 time steps. These are given by the snapshots attribute.

[7]:
network.snapshots
[7]:
DatetimeIndex(['2015-01-01 00:00:00', '2015-01-01 01:00:00',
               '2015-01-01 02:00:00', '2015-01-01 03:00:00',
               '2015-01-01 04:00:00', '2015-01-01 05:00:00',
               '2015-01-01 06:00:00', '2015-01-01 07:00:00',
               '2015-01-01 08:00:00', '2015-01-01 09:00:00'],
              dtype='datetime64[ns]', name='snapshot', freq=None)

There are 6 generators in the network, 3 wind and 3 gas. All are attached to buses:

[8]:
network.generators
[8]:
bus capital_cost efficiency marginal_cost p_nom p_nom_extendable p_nom_min carrier control type ... shut_down_cost min_up_time min_down_time up_time_before down_time_before ramp_limit_up ramp_limit_down ramp_limit_start_up ramp_limit_shut_down p_nom_opt
Generator
Manchester Wind Manchester 2793.651603 1.000000 0.110000 80.0 True 100.0 wind Slack ... 0.0 0 0 1 0 NaN NaN 1.0 1.0 0.0
Manchester Gas Manchester 196.615168 0.350026 4.532368 50000.0 True 0.0 gas PQ ... 0.0 0 0 1 0 NaN NaN 1.0 1.0 0.0
Norway Wind Norway 2184.374796 1.000000 0.090000 100.0 True 100.0 wind Slack ... 0.0 0 0 1 0 NaN NaN 1.0 1.0 0.0
Norway Gas Norway 158.251250 0.356836 5.892845 20000.0 True 0.0 gas PQ ... 0.0 0 0 1 0 NaN NaN 1.0 1.0 0.0
Frankfurt Wind Frankfurt 2129.456122 1.000000 0.100000 110.0 True 100.0 wind Slack ... 0.0 0 0 1 0 NaN NaN 1.0 1.0 0.0
Frankfurt Gas Frankfurt 102.676953 0.351666 4.086322 80000.0 True 0.0 gas PQ ... 0.0 0 0 1 0 NaN NaN 1.0 1.0 0.0

6 rows × 30 columns

We see that the generators have different capital and marginal costs. All of them have a p_nom_extendable set to True, meaning that capacities can be extended in the optimization.

The wind generators have a per unit limit for each time step, given by the weather potentials at the site.

[9]:
network.generators_t.p_max_pu.plot.area(subplots=True)
plt.tight_layout()
../_images/examples_ac-dc-lopf_13_0.png

Alright now we know how the network looks like, where the generators and lines are. Now, let’s perform a optimization of the operation and capacities.

[10]:
network.lopf();
INFO:pypsa.opf:Performed preliminary steps
INFO:pypsa.opf:Building pyomo model using `kirchhoff` formulation
INFO:pypsa.opf:Solving model using glpk
INFO:pypsa.opf:Optimization successful
# ==========================================================
# = Solver Results                                         =
# ==========================================================
# ----------------------------------------------------------
#   Problem Information
# ----------------------------------------------------------
Problem:
- Name: unknown
  Lower bound: -3474094.13081624
  Upper bound: -3474094.13081624
  Number of objectives: 1
  Number of constraints: 452
  Number of variables: 187
  Number of nonzeros: 971
  Sense: minimize
# ----------------------------------------------------------
#   Solver Information
# ----------------------------------------------------------
Solver:
- Status: ok
  Termination condition: optimal
  Statistics:
    Branch and bound:
      Number of bounded subproblems: 0
      Number of created subproblems: 0
  Error rc: 0
  Time: 0.011372089385986328
# ----------------------------------------------------------
#   Solution Information
# ----------------------------------------------------------
Solution:
- number of solutions: 0
  number of solutions displayed: 0
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.19.3/lib/python3.10/site-packages/pypsa/opf.py:1971: FutureWarning: Using the level keyword in DataFrame and Series aggregations is deprecated and will be removed in a future version. Use groupby instead. df.sum(level=1) should use df.groupby(level=1).sum().
  pd.concat(

The objective is given by:

[11]:
network.objective
[11]:
-3474094.13081624

Why is this number negative? It considers the starting point of the optimization, thus the existent capacities given by network.generators.p_nom are taken into account.

The real system cost are given by

[12]:
network.objective + network.objective_constant
[12]:
18440973.387462903

The optimal capacities are given by p_nom_opt for generators, links and storages and s_nom_opt for lines.

Let’s look how the optimal capacities for the generators look like.

[13]:
network.generators.p_nom_opt.div(1e3).plot.bar(ylabel="GW", figsize=(8, 3))
plt.tight_layout()
../_images/examples_ac-dc-lopf_21_0.png

Their production is again given as a time-series in network.generators_t.

[14]:
network.generators_t.p.div(1e3).plot.area(subplots=True, ylabel="GW")
plt.tight_layout()
../_images/examples_ac-dc-lopf_23_0.png

What are the Locational Marginal Prices in the network. From the optimization these are given for each bus and snapshot.

[15]:
network.buses_t.marginal_price.mean(1).plot.area(figsize=(8, 3), ylabel="Euro per MWh")
plt.tight_layout()
../_images/examples_ac-dc-lopf_25_0.png

We can inspect futher quantities as the active power of AC-DC converters and HVDC link.

[16]:
network.links_t.p0
[16]:
Link Norwich Converter Norway Converter Bremen Converter DC link
snapshot
2015-01-01 00:00:00 -316.117092 674.584826 -358.467734 -252.722217
2015-01-01 01:00:00 93.671942 -116.727192 23.055250 -96.601322
2015-01-01 02:00:00 -285.234363 581.970687 -296.736325 317.997991
2015-01-01 03:00:00 -127.723339 272.557949 -144.834610 -276.046800
2015-01-01 04:00:00 37.371395 -79.749487 42.378092 -38.002664
2015-01-01 05:00:00 231.585916 -494.197717 262.611801 -162.836280
2015-01-01 06:00:00 900.000000 -257.520720 -642.479280 317.997991
2015-01-01 07:00:00 123.677319 971.918845 -1095.596165 -86.858742
2015-01-01 08:00:00 244.715828 850.880337 -1095.596165 317.997991
2015-01-01 09:00:00 441.610034 -86.851803 -354.758231 294.728014
[17]:
network.lines_t.p0
[17]:
Line 0 1 2 3 4 5 6
snapshot
2015-01-01 00:00:00 68.553227 -49.027273 0.000000 -316.117092 358.467734 -148.372745 -534.340860
2015-01-01 01:00:00 -486.507301 749.994027 -31.623657 62.048285 -54.678907 393.715939 -823.210906
2015-01-01 02:00:00 -287.678472 414.148821 10.156889 -275.077474 306.893213 280.906831 173.898185
2015-01-01 03:00:00 -52.491649 227.450897 0.000000 -127.723339 144.834610 -197.785304 -743.788495
2015-01-01 04:00:00 -120.055592 248.573165 0.000000 37.371395 -42.378092 -6.958088 -883.817538
2015-01-01 05:00:00 -620.461143 1172.121810 0.000000 231.585916 -262.611801 148.559628 -1030.848768
2015-01-01 06:00:00 -661.294714 1378.422245 -632.371427 267.628573 10.107853 -53.448436 319.386680
2015-01-01 07:00:00 -383.768641 540.906725 -469.925715 -346.248396 625.670450 393.715939 600.728881
2015-01-01 08:00:00 -778.280943 1444.069207 -522.116240 -277.400413 573.479924 229.294311 501.346379
2015-01-01 09:00:00 -528.891061 836.250807 -325.313399 116.296635 29.444832 393.715939 -248.566847

…or the active power injection per bus.

[18]:
network.buses_t.p
[18]:
Bus London Norwich Norwich DC Manchester Bremen Bremen DC Frankfurt Norway Norway DC
snapshot
2015-01-01 00:00:00 216.925973 -99.345473 -316.117092 -117.580500 -534.340860 -358.467734 534.340860 3.979039e-12 674.584826
2015-01-01 01:00:00 -880.223240 -356.278088 93.671942 1236.501328 -823.210906 23.055250 823.210906 4.263256e-14 -116.727192
2015-01-01 02:00:00 -568.585303 -133.241990 -285.234363 701.827293 173.898185 -296.736325 -173.898185 -1.136868e-13 581.970687
2015-01-01 03:00:00 145.293655 -425.236201 -127.723339 279.942546 -743.788495 -144.834610 743.788495 0.000000e+00 272.557949
2015-01-01 04:00:00 -113.097504 -255.531253 37.371395 368.628757 -883.817538 42.378092 883.817538 -1.421085e-14 -79.749487
2015-01-01 05:00:00 -769.020772 -1023.562182 231.585916 1792.582954 -1030.848768 262.611801 1030.848768 -5.684342e-14 -494.197717
2015-01-01 06:00:00 -607.846278 -1431.870681 900.000000 2039.716959 319.386680 -642.479280 -319.386680 -5.684342e-14 -257.520720
2015-01-01 07:00:00 -777.484580 -147.190786 123.677319 924.675366 600.728881 -1095.596165 -600.728881 5.002221e-12 971.918845
2015-01-01 08:00:00 -1007.575254 -1214.774896 244.715828 2222.350150 501.346379 -1095.596165 -501.346379 0.000000e+00 850.880337
2015-01-01 09:00:00 -922.607000 -442.534868 441.610034 1365.141868 -248.566847 -354.758231 248.566847 -2.273737e-13 -86.851803