Key Concepts in PyPSA#

Individual learning outcomes#

  • Get overall understanding on the core building blocks used in PyPSA

  • Understand the key functional elements of a PyPSA model

PyPSA stands for Python for Power System Analysis.

PyPSA is an open source Python package for simulating and optimising modern energy systems that include features such as

  • conventional generators with unit commitment (ramp-up, ramp-down, start-up, shut-down),

  • time-varying wind and solar generation,

  • energy storage with efficiency losses and inflow/spillage for hydroelectricity

  • coupling to other energy sectors (electricity, transport, heat, industry),

  • conversion between energy carriers (e.g. electricity to hydrogen),

  • transmission networks (AC, DC, other fuels)

PyPSA can be used for a variety of problem types (e.g. electricity market modelling, long-term investment planning, transmission network expansion planning), and is designed to scale well with large networks and long time series.

Compared to building power system by hand in linopy, PyPSA does the following things for you:

  • manage data inputs in standardised format

  • build standardised optimisation problem

  • communicate with the solver(s)

  • retrieve and process optimisation results

  • manage data outputs in standardised format

The documentation describes it as follows:

PyPSA is an open-source Python framework for optimising and simulating modern power and energy systems that include features such as conventional generators with unit commitment, variable wind and solar generation, hydro-electricity, inter-temporal storage, coupling to other energy sectors, elastic demands, and linearised power flow with loss approximations in DC and AC networks. PyPSA is designed to scale well with large networks and long time series. It is made for researchers, planners and utilities with basic coding aptitude who need a fast, easy-to-use and transparent tool for power and energy system analysis.

Note

Documentation for PyPSA is available at https://docs.pypsa.org including hand-ons examples.

Basic Structure#

Component

Description

Network

Container for all components.

Bus

Node where components attach.

Carrier

Energy carrier or technology (e.g. electricity, hydrogen, gas, coal, oil, biomass, on-/offshore wind, solar). Can track properties such as specific carbon dioxide emissions or nice names and colors for plots.

Load

Energy consumer (e.g. electricity demand).

Generator

Generator (e.g. power plant, wind turbine, PV panel).

Line

Power distribution and transmission lines (overhead and cables).

Link

Links connect two buses with controllable energy flow, direction-control and losses. They can be used to model:

  • HVDC links
  • HVAC li’nes (neglecting KVL, only net transfer capacities (NTCs))
  • conversion between carriers (e.g. electricity to hydrogen in electrolysis)

StorageUnit

Storage with fixed nominal energy-to-power ratio.

GlobalConstraint

Constraints affecting many components at once, such as emission limits.

Store

Storage with separately extendable energy capacity.

Note

Links in the table lead to documentation for each component.

Warning

Per unit values of voltage and impedance are used internally for network calculations. It is assumed internally that the base power is 1 MW.

From structured data to optimisation#

The design principle of PyPSA is that each component is associated with a set of variables and constraints that will be added to the optimisation model based on the input data stored for the components.

For an hourly electricity market simulation, PyPSA will solve an optimisation problem that looks like this

(1)#\[\begin{equation} \min_{g_{i,s,t}; f_{\ell,t}; g_{i,r,t,\text{charge}}; g_{i,r,t,\text{discharge}}; e_{i,r,t}} \sum_s o_{s} g_{i,s,t} \end{equation}\]

such that

(2)#\[\begin{align} 0 & \leq g_{i,s,t} \leq \hat{g}_{i,s,t} G_{i,s} & \text{generation limits : generator} \\ -F_\ell &\leq f_{\ell,t} \leq F_\ell & \text{transmission limits : line} \\ d_{i,t} &= \sum_s g_{i,s,t} + \sum_r g_{i,r,t,\text{discharge}} - \sum_r g_{i,r,t,\text{charge}} - \sum_\ell K_{i\ell} f_{\ell,t} & \text{KCL : bus} \\ 0 &=\sum_\ell C_{\ell c} x_\ell f_{\ell,t} & \text{KVL : cycles} \\ 0 & \leq g_{i,r,t,\text{discharge}} \leq G_{i,r,\text{discharge}}& \text{discharge limits : storage unit} \\ 0 & \leq g_{i,r,t,\text{charge}} \leq G_{i,r,\text{charge}} & \text{charge limits : storage unit} \\ 0 & \leq e_{i,r,t} \leq E_{i,r} & \text{energy limits : storage unit} \\ e_{i,r,t} &= \eta^0_{i,r,t} e_{i,r,t-1} + \eta^1_{i,r,t}g_{i,r,t,\text{charge}} - \frac{1}{\eta^2_{i,r,t}} g_{i,r,t,\text{discharge}} & \text{consistency : storage unit} \\ e_{i,r,0} & = e_{i,r,|T|-1} & \text{cyclicity : storage unit} \end{align}\]

Decision variables:

  • \(g_{i,s,t}\) is the generator dispatch at bus \(i\), technology \(s\), time step \(t\),

  • \(f_{\ell,t}\) is the power flow in line \(\ell\),

  • \(g_{i,r,t,\text{dis-/charge}}\) denotes the charge and discharge of storage unit \(r\) at bus \(i\) and time step \(t\),

  • \(e_{i,r,t}\) is the state of charge of storage \(r\) at bus \(i\) and time step \(t\).

Parameters:

  • \(o_{i,s}\) is the marginal generation cost of technology \(s\) at bus \(i\),

  • \(x_\ell\) is the reactance of transmission line \(\ell\),

  • \(K_{i\ell}\) is the incidence matrix,

  • \(C_{\ell c}\) is the cycle matrix,

  • \(G_{i,s}\) is the nominal capacity of the generator of technology \(s\) at bus \(i\),

  • \(F_{\ell}\) is the rating of the transmission line \(\ell\),

  • \(E_{i,r}\) is the energy capacity of storage \(r\) at bus \(i\),

  • \(\eta^{0/1/2}_{i,r,t}\) denote the standing (0), charging (1), and discharging (2) efficiencies.

Note

For a full reference to the optimisation problem description, see https://docs.pypsa.org/latest/user-guide/optimization/overview/

Simple example#

We’ll build a strongly simplified model building using PyPSA components as building blocks

#import pandas as pd
import pypsa
#pypsa.options.params.optimize.include_objective_constant = True
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[1], line 2
      1 #import pandas as pd
----> 2 import pypsa
      3 #pypsa.options.params.optimize.include_objective_constant = True

File ~/miniconda3/envs/pypsa-zambia-workshops/lib/python3.13/site-packages/pypsa/__init__.py:21
     13 __copyright__ = (
     14     "Copyright 2015-2025 PyPSA Developers, see https://docs.pypsa.org/latest/contributing/contributors.html, "
     15     "MIT License"
     16 )
     19 from typing import NoReturn
---> 21 from pypsa import (
     22     clustering,
     23     common,
     24     components,
     25     costs,
     26     descriptors,
     27     examples,
     28     geo,
     29     optimization,
     30     plot,
     31     statistics,
     32 )
     33 from pypsa._options import (
     34     option_context,
     35     options,
     36 )
     37 from pypsa.collection import NetworkCollection

File ~/miniconda3/envs/pypsa-zambia-workshops/lib/python3.13/site-packages/pypsa/examples.py:19
     16 from platformdirs import user_cache_dir
     18 from pypsa._options import options
---> 19 from pypsa.networks import Network
     20 from pypsa.version import __version_base__
     22 logger = logging.getLogger(__name__)

File ~/miniconda3/envs/pypsa-zambia-workshops/lib/python3.13/site-packages/pypsa/networks.py:29
     25     from pathlib import Path
     27 import functools
---> 29 import linopy
     30 import numpy as np
     31 import pandas as pd

File ~/miniconda3/envs/pypsa-zambia-workshops/lib/python3.13/site-packages/linopy/__init__.py:21
     19 from linopy.expressions import LinearExpression, QuadraticExpression, merge
     20 from linopy.io import read_netcdf
---> 21 from linopy.model import Model, Variable, Variables, available_solvers
     22 from linopy.objective import Objective
     23 from linopy.remote import OetcHandler, RemoteHandler

File ~/miniconda3/envs/pypsa-zambia-workshops/lib/python3.13/site-packages/linopy/model.py:63
     61 from linopy.matrices import MatrixAccessor
     62 from linopy.objective import Objective
---> 63 from linopy.remote import OetcHandler, RemoteHandler
     64 from linopy.solver_capabilities import SolverFeature, solver_supports
     65 from linopy.solvers import (
     66     IO_APIS,
     67     available_solvers,
     68 )

File ~/miniconda3/envs/pypsa-zambia-workshops/lib/python3.13/site-packages/linopy/remote/__init__.py:11
      1 """
      2 Remote execution handlers for linopy models.
      3 
   (...)      8 - OetcHandler: Cloud-based execution via OET Cloud service
      9 """
---> 11 from linopy.remote.oetc import OetcCredentials, OetcHandler, OetcSettings
     12 from linopy.remote.ssh import RemoteHandler
     14 __all__ = [
     15     "RemoteHandler",
     16     "OetcHandler",
     17     "OetcSettings",
     18     "OetcCredentials",
     19 ]

File ~/miniconda3/envs/pypsa-zambia-workshops/lib/python3.13/site-packages/linopy/remote/oetc.py:13
     10 from enum import Enum
     12 import requests
---> 13 from google.cloud import storage
     14 from google.oauth2 import service_account
     15 from requests import RequestException

File ~/miniconda3/envs/pypsa-zambia-workshops/lib/python3.13/site-packages/google/cloud/storage/__init__.py:35
      1 # Copyright 2014 Google LLC
      2 #
      3 # Licensed under the Apache License, Version 2.0 (the "License");
   (...)     12 # See the License for the specific language governing permissions and
     13 # limitations under the License.
     15 """Shortcut methods for getting set up with Google Cloud Storage.
     16 
     17 You'll typically use these to get started with the API:
   (...)     31   machine).
     32 """
---> 35 from pkg_resources import get_distribution
     37 __version__ = get_distribution("google-cloud-storage").version
     39 from google.cloud.storage.batch import Batch

ModuleNotFoundError: No module named 'pkg_resources'

First, we create a new Network object which serves as the overall container for all components.

n = pypsa.Network()

The second component we need are buses. Buses are the fundamental nodes of the network, to which all other components like loads, generators and transmission lines attach. They enforce energy conservation for all elements feeding in and out of it (i.e. Kirchhoff’s Current Law).

Components can be added to the network n using the n.add() function. It takes the component name as a first argument, the name of the component as a second argument and possibly further parameters as keyword arguments. Let’s use this function, to add buses for each country to our network:

n.add("Bus", "Copperbelt", y=-15.42, x=28.2, v_nom=400) # v_nom is nominal valtage [kV]
n.add("Bus", "Lusaka", y=-13.0, x=28.0, v_nom=400)

For each class of components, the data describing the components is stored as specific type DataFrame defined in pandas package which will be covered in Python session. You can check what is inside each component using n.{component}, such as

n.buses
attribute v_nom type x y carrier unit v_mag_pu_set v_mag_pu_min v_mag_pu_max control generator sub_network
Bus
Copperbelt 400.0 28.2 -15.42 AC 1.0 0.0 inf PQ
Lusaka 400.0 28.0 -13.00 AC 1.0 0.0 inf PQ

You can look up on all the attributes available for buses here https://docs.pypsa.org/latest/user-guide/components/buses/, and similarly for all other components.

The n.add() function is very general. It lets you add any component to the network object n. For instance, in the next step we add Line component to connect both nodes:

n.add(
    "Line",
    "Lusaka-Copperbelt",
    bus0="Lusaka",
    bus1="Copperbelt",
    s_nom=1_500, # transmission capacity [MW]
    x=1,
    r=1,
)
n.explore()
INFO:pypsa.plot:Components rendered on the map: Bus, Line.
INFO:pypsa.plot:Components omitted as they are missing or not selected: Generator, Link, Load, StorageUnit, Transformer.
Make this Notebook Trusted to load map: File -> Trust Notebook

Information about carriers must be added to distinguish between different energy sources. To make visualization nicer, we can also specify nice names and plotting colors.

n.add(
    "Carrier",
    ["coal", "oil", "hydro"],
    #co2_emissions=emissions,
    nice_name=["Coal", "Oil", "Hydro"],
    color=["dimgrey", "olive", "royalblue"],
)

n.add("Carrier", "AC", nice_name="Electricity", color="crimson")

Add generators#

Generator is component which can feed in power. Generators convert energy from their carrier to the carrier of the bus to which they attach with user-specified parameters for energy availability and transformation efficiency. They can be used to represent renewable generators with variable potential, dispatchable conventional power plants, or supply of grid electricity or an external import.

Availability of renewable generators is usually defined with p_max_pu which is the maximum value of the capacity factor.

n.add(
    "Generator",
    "Hydro Generation",
    bus="Lusaka",
    carrier="hydro",
    p_nom=5000, # MW
    p_max_pu=0.4, # hydro availability which depends on the weather year
    marginal_cost=0, # default
)

For conventional generators, efficiencies and fuel costs can be specified:

fuel_cost = dict(
    coal=8,
    oil=48,
)
efficiency = dict(
    coal=0.33,
    oil=0.35,
)
n.add(
    "Generator",
    "Coal Generation",
    bus="Copperbelt",
    carrier="coal",
    efficiency=efficiency.get("coal", 1),
    p_nom=1750,
    marginal_cost=fuel_cost.get("coal", 0) / efficiency.get("coal", 1),
)
n.add(
    "Generator",
    "Oil Generation",
    bus="Copperbelt",
    carrier="oil",
    efficiency=efficiency.get("oil", 1),
    p_nom=1200,
    marginal_cost=fuel_cost.get("oil", 0) / efficiency.get("oil", 1),
)

As a result, the n.generators DataFrame looks like this:

n.generators
attribute bus control type p_nom p_nom_mod p_nom_extendable p_nom_min p_nom_max p_min_pu p_max_pu ... 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 weight p_nom_opt
Generator
Hydro Generation Lusaka PQ 5000.0 0.0 False 0.0 inf 0.0 0.4 ... 0 0 1 0 NaN NaN 1.0 1.0 1.0 0.0
Coal Generation Copperbelt PQ 1750.0 0.0 False 0.0 inf 0.0 1.0 ... 0 0 1 0 NaN NaN 1.0 1.0 1.0 0.0
Oil Generation Copperbelt PQ 1200.0 0.0 False 0.0 inf 0.0 1.0 ... 0 0 1 0 NaN NaN 1.0 1.0 1.0 0.0

3 rows × 34 columns

Add loads#

Next, we’re going to add the electricity demand. A positive value for p_set means consumption of power from the bus (in MW).

loads = {
    "Lusaka": 700,
    "Copperbelt": 3000,
}
n.add(
    "Load",
    "Lusaka electricity demand",
    bus="Lusaka",
    p_set=loads["Lusaka"],
    carrier="AC",
)
n.add(
    "Load",
    "Copperbelt electricity demand",
    bus="Copperbelt",
    p_set=loads["Copperbelt"],
    carrier="AC",
)
n_dry_year = n.copy()
n_wet_year = n.copy()
n.optimize(solver_name="highs", log_to_console=False)
WARNING:pypsa.consistency:The following generators have carriers which are not defined:
Index(['Hydro Generation', 'Coal Generation', 'Oil Generation'], dtype='object', name='Generator')
WARNING:pypsa.consistency:The following generators have carriers which are not defined:
Index(['Hydro Generation', 'Coal Generation', 'Oil Generation'], dtype='object', name='Generator')
INFO:linopy.model: Solve problem using Highs solver
INFO:linopy.model:Solver options:
 - log_to_console: False
INFO:linopy.io: Writing time: 0.01s
INFO:linopy.constants: Optimization successful: 
Status: ok
Termination condition: optimal
Solution: 4 primals, 10 duals
Objective: 4.12e+04
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 were not assigned to the network.
Running HiGHS 1.11.0 (git hash: n/a): Copyright (c) 2025 HiGHS under MIT licence terms
('ok', 'optimal')
loads
{'Lusaka': 700, 'Copperbelt': 3000}
n.statistics()
Optimal Capacity Installed Capacity Supply Withdrawal Energy Balance Transmission Capacity Factor Curtailment Capital Expenditure Operational Expenditure Revenue Market Value
Generator coal 1750.0 1750.0 1700.0 0.0 1700.0 0.0 0.971429 50.0 0.0 41212.12121 41212.12121 24.242424
hydro 5000.0 5000.0 2000.0 0.0 2000.0 0.0 0.400000 0.0 0.0 0.00000 48484.84848 24.242424
oil 1200.0 1200.0 0.0 0.0 0.0 0.0 0.000000 1200.0 0.0 0.00000 0.00000 0.000000
Line Electricity 1500.0 1500.0 1300.0 1300.0 0.0 1300.0 0.866667 0.0 0.0 0.00000 0.00000 NaN
Load Electricity 0.0 0.0 0.0 3700.0 -3700.0 0.0 NaN 0.0 0.0 0.00000 -89696.96970 NaN

Modify network#

Add a power transmission line with a specific transmission capacity s_nom [MW]

n.lines.loc["Lusaka-Copperbelt", "s_nom"] = 750
n.optimize()
WARNING:pypsa.consistency:The following generators have carriers which are not defined:
Index(['Hydro Generation', 'Coal Generation', 'Oil Generation'], dtype='object', name='Generator')
WARNING:pypsa.consistency:The following generators have carriers which are not defined:
Index(['Hydro Generation', 'Coal Generation', 'Oil Generation'], dtype='object', name='Generator')
INFO:linopy.model: Solve problem using Highs solver
INFO:linopy.io: Writing time: 0.01s
INFO:linopy.constants: Optimization successful: 
Status: ok
Termination condition: optimal
Solution: 4 primals, 10 duals
Objective: 1.11e+05
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 were not assigned to the network.
Running HiGHS 1.11.0 (git hash: n/a): Copyright (c) 2025 HiGHS under MIT licence terms
LP   linopy-problem-6txvbqhq has 10 rows; 4 cols; 13 nonzeros
Coefficient ranges:
  Matrix [1e+00, 1e+00]
  Cost   [2e+01, 1e+02]
  Bound  [0e+00, 0e+00]
  RHS    [7e+02, 3e+03]
Presolving model
1 rows, 2 cols, 2 nonzeros  0s
0 rows, 0 cols, 0 nonzeros  0s
Presolve : Reductions: rows 0(-10); columns 0(-4); elements 0(-13) - Reduced to empty
Solving the original LP from the solution after postsolve
Model name          : linopy-problem-6txvbqhq
Model status        : Optimal
Objective value     :  1.1099567100e+05
P-D objective error :  1.3110285785e-16
HiGHS run time      :          0.00
Writing the solution to /private/var/folders/qn/vpndfm21795ckkq89np1ckp40000gn/T/linopy-solve-15qbsnrh.sol
('ok', 'optimal')
n.statistics()
Optimal Capacity Installed Capacity Supply Withdrawal Energy Balance Transmission Capacity Factor Curtailment Capital Expenditure Operational Expenditure Revenue Market Value
Generator coal 1750.0 1750.0 1750.0 0.0 1750.0 0.0 1.000000 0.0 0.0 42424.24242 240000.00000 137.142857
hydro 5000.0 5000.0 1450.0 0.0 1450.0 0.0 0.290000 550.0 0.0 0.00000 0.00000 NaN
oil 1200.0 1200.0 500.0 0.0 500.0 0.0 0.416667 700.0 0.0 68571.42857 68571.42857 137.142857
Line Electricity 750.0 750.0 750.0 750.0 0.0 750.0 1.000000 0.0 0.0 0.00000 102857.14286 137.142857
Load Electricity 0.0 0.0 0.0 3700.0 -3700.0 0.0 NaN 0.0 0.0 0.00000 -411428.57143 NaN

Hydrological scenarios#

n_dry_year.generators.loc["Hydro Generation", "p_max_pu"] = 0.3
n_wet_year.generators.loc["Hydro Generation", "p_max_pu"] = 0.6
n_dry_year.optimize(solver_name="highs", log_to_console=False)
WARNING:pypsa.consistency:The following generators have carriers which are not defined:
Index(['Hydro Generation', 'Coal Generation', 'Oil Generation'], dtype='object', name='Generator')
WARNING:pypsa.consistency:The following generators have carriers which are not defined:
Index(['Hydro Generation', 'Coal Generation', 'Oil Generation'], dtype='object', name='Generator')
INFO:linopy.model: Solve problem using Highs solver
INFO:linopy.model:Solver options:
 - log_to_console: False
INFO:linopy.io: Writing time: 0.01s
INFO:linopy.constants: Optimization successful: 
Status: ok
Termination condition: optimal
Solution: 4 primals, 10 duals
Objective: 1.04e+05
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 were not assigned to the network.
Running HiGHS 1.11.0 (git hash: n/a): Copyright (c) 2025 HiGHS under MIT licence terms
('ok', 'optimal')
n_wet_year.optimize(solver_name="highs", log_to_console=False)
WARNING:pypsa.consistency:The following generators have carriers which are not defined:
Index(['Hydro Generation', 'Coal Generation', 'Oil Generation'], dtype='object', name='Generator')
WARNING:pypsa.consistency:The following generators have carriers which are not defined:
Index(['Hydro Generation', 'Coal Generation', 'Oil Generation'], dtype='object', name='Generator')
INFO:linopy.model: Solve problem using Highs solver
INFO:linopy.model:Solver options:
 - log_to_console: False
INFO:linopy.io: Writing time: 0.01s
INFO:linopy.constants: Optimization successful: 
Status: ok
Termination condition: optimal
Solution: 4 primals, 10 duals
Objective: 3.64e+04
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 were not assigned to the network.
Running HiGHS 1.11.0 (git hash: n/a): Copyright (c) 2025 HiGHS under MIT licence terms
('ok', 'optimal')
n.statistics()
Optimal Capacity Installed Capacity Supply Withdrawal Energy Balance Transmission Capacity Factor Curtailment Capital Expenditure Operational Expenditure Revenue Market Value
Generator coal 1750.0 1750.0 1750.0 0.0 1750.0 0.0 1.000000 0.0 0.0 42424.24242 240000.00000 137.142857
hydro 5000.0 5000.0 1450.0 0.0 1450.0 0.0 0.290000 550.0 0.0 0.00000 0.00000 NaN
oil 1200.0 1200.0 500.0 0.0 500.0 0.0 0.416667 700.0 0.0 68571.42857 68571.42857 137.142857
Line Electricity 750.0 750.0 750.0 750.0 0.0 750.0 1.000000 0.0 0.0 0.00000 102857.14286 137.142857
Load Electricity 0.0 0.0 0.0 3700.0 -3700.0 0.0 NaN 0.0 0.0 0.00000 -411428.57143 NaN
n_dry_year.statistics()
Optimal Capacity Installed Capacity Supply Withdrawal Energy Balance Transmission Capacity Factor Curtailment Capital Expenditure Operational Expenditure Revenue Market Value
Generator coal 1750.0 1750.0 1750.0 0.0 1750.0 0.0 1.000000 0.0 0.0 42424.24242 240000.00000 137.142857
hydro 5000.0 5000.0 1500.0 0.0 1500.0 0.0 0.300000 0.0 0.0 0.00000 205714.28571 137.142857
oil 1200.0 1200.0 450.0 0.0 450.0 0.0 0.375000 750.0 0.0 61714.28571 61714.28571 137.142857
Line Electricity 1500.0 1500.0 800.0 800.0 0.0 800.0 0.533333 0.0 0.0 0.00000 0.00000 NaN
Load Electricity 0.0 0.0 0.0 3700.0 -3700.0 0.0 NaN 0.0 0.0 0.00000 -507428.57143 NaN
n_wet_year.statistics()
Optimal Capacity Installed Capacity Supply Withdrawal Energy Balance Transmission Capacity Factor Curtailment Capital Expenditure Operational Expenditure Revenue Market Value
Generator coal 1750.0 1750.0 1500.0 0.0 1500.0 0.0 0.857143 250.0 0.0 36363.63636 36363.63636 24.242424
hydro 5000.0 5000.0 2200.0 0.0 2200.0 0.0 0.440000 800.0 0.0 0.00000 0.00000 NaN
oil 1200.0 1200.0 0.0 0.0 0.0 0.0 0.000000 1200.0 0.0 0.00000 0.00000 0.000000
Line Electricity 1500.0 1500.0 1500.0 1500.0 0.0 1500.0 1.000000 0.0 0.0 0.00000 36363.63636 24.242424
Load Electricity 0.0 0.0 0.0 3700.0 -3700.0 0.0 NaN 0.0 0.0 0.00000 -72727.27273 NaN

Comprehensive documentation is available for PyPSA

Example for modelling interconnection between South Africa and Mozambique

Key Dependencies#

  • pandas for storing data about network components and time series

  • numpy and scipy for linear algebra and sparse matrix calculations

  • matplotlib and cartopy for plotting on a map

  • networkx for network calculations

  • linopy for handling optimisation problems