Open-source project
PyPSA/PyPSA avatar
PyPSA/PyPSA

PyPSA: a power system model where the optimisation problem is the product

PyPSA: Python for Power System Analysis

2,180 stars713 forksPythonMIT

At a glance

What is it?
An open-source Python framework for dispatch, network-constrained dispatch, capacity expansion and sector coupling, built on pandas, linopy and a solver you choose.
Who is it for?
PyPSA is at its best when the question is a least-cost question: cheapest dispatch under network limits, cheapest investment path over a decade, cheapest portfolio of near-equivalent designs. The network object with one table per component type is the thing you will either love or fight, and the documentation at docs.pypsa.org, not the README, is where the component attribute list actually lives.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 8, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Six lines that build a network and solve it

The usage block in the README is short enough to memorise, and it teaches the whole model in one pass:

py
import pypsa

# create a new network
n = pypsa.Network()
n.add("Bus", "mybus")
n.add("Load", "myload", bus="mybus", p_set=100)
n.add("Generator", "mygen", bus="mybus", p_nom=100, marginal_cost=20)

Three things are worth pausing on. A network is built by adding typed components, not by writing a config file or an XML document, and each `add` call takes a component class, a name and keyword attributes. A load is a sink with a setpoint, a generator is a source with a rated capacity and a marginal cost, and both point at a bus by name. The third observation is that nothing here is electrical engineering yet. A single bus with a load and a generator is a dispatch problem, and dispatch is the easiest thing PyPSA can do.

Then the operations follow in the same declarative order. You can pull in a bundled example, solve, inspect and plot:

py
# load an example network
n = pypsa.examples.ac_dc_meshed()

# run the optimisation
n.optimize()

# plot results
n.generators_t.p.plot()
n.plot()

# get statistics
n.statistics()
n.statistics.energy_balance()

The naming convention is consistent throughout and worth learning once. Components live in plural attributes such as `n.generators`, time-varying results in `n.generators_t`, so the underscore-t suffix always means per-snapshot data. `n.optimize()` with no arguments is the whole solve, and `n.statistics()` returns the balance tables you would otherwise assemble by hand.

Nine capabilities, and the distinction that matters most

The feature list is organised by optimisation problem rather than by module, which is the right frame if you are deciding whether this tool fits. Economic dispatch covers unit commitment, renewables availability, short-duration and seasonal storage with hydro inflow and spillage dynamics, elastic demand, load shedding and energy carrier conversion, under either perfect foresight or rolling horizon resolution.

Linear optimal power flow extends that with network constraints in meshed AC and DC grids, using a linearised representation of the flow equations with optional loss approximations. Security-constrained LOPF adds line outage contingencies so that reliability under N-1 conditions is part of the objective rather than a check afterwards. Static power flow, listed separately, is the non-optimising counterpart and computes both full non-linear and linearised load flows with Newton-Raphson.

The remaining four are long-horizon. Capacity expansion planning makes least-cost investment decisions in generation, storage, conversion and transmission, across either a single investment period or several. Pathway planning co-optimises those periods to plan a transition with perfect foresight. Stochastic optimisation is a two-stage formulation with investments as first-stage decisions and dispatch as recourse. Modelling-to-Generate-Alternatives explores near-optimal decision spaces, which is what you want when the answer is too sensitive to defend.

Sector coupling sits apart from that ladder because it changes the model rather than the objective: multiple carriers such as electricity, heat and hydrogen, with conversion between them, covering heat pumps, electrolysers, battery electric vehicles, direct air capture and synthetic fuels.

The distinction that matters most in practice is dispatch versus power flow. If your question is about operations, linear dispatch is fast and reliable. If it is about the network, the linearisation is an approximation you have to accept.

The solver is a separate choice, and linopy builds the problem

PyPSA does not include an optimisation solver. It builds a linear program and hands it to something else, which is a deliberate separation and the single most important architectural fact for a new user. In the dependencies list, `linopy`, a PyPSA project of its own, prepares the optimisation problem, and the packaging metadata additionally requires `highspy`, the Python binding for HiGHS, so a working solver ships by default.

The optional dependency groups show how far this goes. There is a `gurobipy` extra for anyone who has a Gurobi licence and wants the commercial solver, alongside groups for hdf5, cartopy, excel reading, cloudpath, dotenv and `tsam`. Declaring Gurobi as optional rather than required tells you the solver choice is genuinely open, and that the default path is free.

It also tells you PyPSA is not a lightweight package. The required list runs from numpy, scipy and pandas through pyarrow, xarray, netcdf4, linopy, matplotlib, plotly, pydeck, seaborn, geopandas, shapely and networkx, with `rapidfuzz`, `validators` and `platformdirs` for more mundane jobs. A large time series in pandas is the intended data model, and if your instinct is to hold results in a database you will spend your time converting.

What the packaging metadata says that the README does not

This is where two legitimate sources disagree, and both are useful. The README's installation section is three one-liners and stops there:

bash
pip install pypsa
bash
conda install -c conda-forge pypsa
bash
uv add pypsa

Not one of those lines mentions a Python version. The packaging file does, and it is strict about it: `requires-python = ">=3.11"`, with classifiers for 3.11, 3.12, 3.13 and 3.14. It also pins `pandas>=3.0`, which is a much higher floor than most people assume when they see a scientific Python tool, and requires `linopy>=0.9.0` and `networkx>=2`.

So the honest reading is that the install instructions are incomplete on their own rather than wrong, and the packaging metadata is the authoritative statement of what you need. If you hit an import error on an older interpreter, the version floor is where to look.

A smaller inconsistency sits in the naming. The README heading calls the project Python for Power System Analysis, while the packaging description says Python for Power Systems Analysis. The README also spells out the pronunciation, pipes-ah, which is the kind of detail that saves a conference talk. Neither difference affects anything except a search for the package.

Version numbers come from git tags, not from a file

The packaging file declares `dynamic = ["version"]` and its build requirements include `setuptools_scm`, which means no version string is written anywhere in the repository. The version is derived from the git history at build time. The published history shows v1.2.3 on 2026-06-12, v1.2.4 on 2026-06-27 and v1.3.0 on 2026-08-19, with the repository pushed on 2026-09-28.

This has two consequences that are easy to trip over. A checkout of the default branch reports whatever the tags in that clone say, so a local version string can differ from the released one. And `pip install pypsa` gives you the newest tag, not the newest commit, which means the code you read on the default branch can be ahead of the code you installed. Both facts are the flip side of a sane setup, and both are reasons to check the version rather than assume.

The rest of the repository infrastructure is unusually complete for a research library. There is a `test/` directory with a codecov badge, a `.pre-commit-config.yaml` and a Ruff badge, a `CITATION.cff` file and a Zenodo DOI badge for archiving releases, `.readthedocs.yml` with a documentation status badge, and a `mkdocs.yml` at the root for the docs build. A `REUSE.toml` and a `LICENSES/` directory carry the REUSE compliance badge, matching the SPDX headers at the top of both the README and the packaging file.

Who maintains it and what that means for your plans

The maintenance note is specific in a way few research libraries are. Leadership sits with the Department of Digital Transformation in Energy Systems at the Technical University of Berlin, currently supported by the German Research Foundation under grant number 528775426. Earlier versions were developed at the Karlsruhe Institute of Technology with Helmholtz Association funding, and at FIAS with German Federal Ministry funding.

That history explains something about the design. A framework that has been rewritten more than once, under different funders and institutions, tends to keep its data model conservative and push new capability into separate projects. The related searches around this name point at PyPSA-Eur, PyPSA-Earth, PyPSA-USA and PyPSA-GB, which is the family of prebuilt models built on top of the core. Treat PyPSA itself as the engine and those as the datasets.

For planning purposes, the useful distinction is between the library and the documentation. The README is a good summary of scope and an honest statement of intent, with the target audience described as researchers, planners and utilities with basic coding aptitude who want a fast and transparent tool. The component-by-component reference is on docs.pypsa.org, and every real modelling decision, from unit commitment options to sector-coupled component attributes, lives there rather than in this repository.

The last push of 2026-09-28 against releases through August 2026 is a normal cadence for an actively maintained research framework, and the 159 open issues in the tracker are consistent with that rather than a sign of neglect.

Editorial conclusion

PyPSA is at its best when the question is a least-cost question: cheapest dispatch under network limits, cheapest investment path over a decade, cheapest portfolio of near-equivalent designs. The network object with one table per component type is the thing you will either love or fight, and the documentation at docs.pypsa.org, not the README, is where the component attribute list actually lives. Two details decide how you install it. The packaging metadata requires Python 3.11 or newer and pins `pandas>=3.0`, a floor the README's install section never mentions, so a machine on an older pandas will not simply upgrade its way out of it. And the version number is derived from git tags by setuptools_scm rather than written into the file, so `pip install pypsa` and the checkout you are reading may differ. Start with the six-line bus, load and generator example above and call `n.optimize()` on it before touching a real network.

Frequently asked questions

Is PyPSA free?

Yes. The project is MIT licensed, with the LICENSE file at the repository root and SPDX headers declaring the same identifier. A free default solver ships with it through the `highspy` dependency, so the commercial Gurobi binding is an optional extra rather than a requirement.

What Python version does PyPSA need?

The packaging metadata requires Python 3.11 or newer and declares support for 3.11 through 3.14. It also pins `pandas>=3.0`, which is a higher floor than the README's install commands suggest, so check that before choosing an interpreter.

Which optimisation solver does PyPSA use?

PyPSA builds the problem and delegates the solve. `linopy` prepares the optimisation model, and `highspy` ships as a required dependency so HiGHS is available by default. A `gurobipy` extra exists for users with a Gurobi licence who want the commercial solver instead.

How do I start modelling a network from nothing?

Create `pypsa.Network()` and add components by type and name, pointing each at a bus: `n.add("Bus", "mybus")`, then a `Load` with `p_set` and a `Generator` with `p_nom` and `marginal_cost`. Call `n.optimize()` to solve, and `n.statistics()` for the balance tables. `pypsa.examples.ac_dc_meshed()` gives you a populated network to inspect instead.

Official sources

  1. License: MIT
  2. Project website
  3. PyPSA/PyPSA on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/pypsa-pypsa.svg)](https://hysenlabs.com/projects/pypsa-pypsa)