Constraining the total capacity per bus and carrier

Note

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

Constraining the total capacity per bus and carrier#

In this small example, we limit the nominal capacity of generators of the same production carrier at the same bus.

Therefore, we introduce a column nom_min_{carrier} and nom_max_{carrier} in the buses dataframe. These are then used as lower and upper bounds of generators of the same carrier at the same bus.

We start with importing a small example network.

[1]:
import pandas as pd

import pypsa
ERROR 1: PROJ: proj_create_from_database: Open of /home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/latest/share/proj failed
[2]:
n = pypsa.examples.ac_dc_meshed(from_master=True)
n.links_t.p_set = pd.DataFrame(
    index=n.snapshots
)  # remove forced fixed values in optimization
WARNING:pypsa.io:Importing network from PyPSA version v0.17.1 while current version is v0.28.0. 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 ac-dc-meshed.nc has buses, carriers, generators, global_constraints, lines, links, loads

Now add a second wind generator at bus ‘Frankfurt’ and limit the combined capacity.

[3]:
n.add(
    "Generator",
    "Frankfurt Wind 2",
    bus="Frankfurt",
    capital_cost=120,
    carrier="wind",
    p_nom_extendable=True,
)

n.buses.loc[["Frankfurt", "Manchester"], "nom_min_wind"] = 2000
n.buses.loc[["Frankfurt"], "nom_max_wind"] = 2200

We are running the lopf and check whether the constraint is fulfilled.

[4]:
n.optimize()
WARNING:pypsa.components:The following lines have zero x, which could break the linear load flow:
Index(['2', '3', '4'], dtype='object', name='Line')
WARNING:pypsa.components:The following lines have zero r, which could break the linear load flow:
Index(['0', '1', '5', '6'], dtype='object', name='Line')
WARNING:pypsa.components:The following lines have zero x, which could break the linear load flow:
Index(['2', '3', '4'], dtype='object', name='Line')
WARNING:pypsa.components:The following lines have zero r, which could break the linear load flow:
Index(['0', '1', '5', '6'], dtype='object', name='Line')
INFO:linopy.model: Solve problem using Highs solver
INFO:linopy.io: Writing time: 0.07s
INFO:linopy.solvers:Log file at /tmp/highs.log.
INFO:linopy.constants: Optimization successful:
Status: ok
Termination condition: optimal
Solution: 199 primals, 492 duals
Objective: -1.38e+07
Solver model: available
Solver message: optimal

INFO:pypsa.optimization.optimize:The shadow-prices of the constraints Generator-ext-p-lower, Generator-ext-p-upper, Line-ext-s-lower, Line-ext-s-upper, Link-ext-p-lower, Link-ext-p-upper, Kirchhoff-Voltage-Law were not assigned to the network.
Running HiGHS 1.7.0 (git hash: 27ccfaa): Copyright (c) 2024 HiGHS under MIT licence terms
Coefficient ranges:
  Matrix [1e-02, 1e+00]
  Cost   [9e-03, 3e+03]
  Bound  [2e+07, 2e+07]
  RHS    [9e-01, 2e+03]
Presolving model
403 rows, 198 cols, 964 nonzeros  0s
402 rows, 198 cols, 962 nonzeros  0s
Presolve : Reductions: rows 402(-90); columns 198(-1); elements 962(-91)
Solving the presolved LP
Using EKK dual simplex solver - serial
  Iteration        Objective     Infeasibilities num(sum)
          0    -1.5896414189e+07 Pr: 61(162995); Du: 0(5.47058e-11) 0s
        217    -1.3793078250e+07 Pr: 0(0) 0s
Solving the original LP from the solution after postsolve
Model   status      : Optimal
Simplex   iterations: 217
Objective value     : -1.3793078250e+07
HiGHS run time      :          0.00
[4]:
('ok', 'optimal')
[5]:
n.generators[["p_nom_opt"]]
[5]:
p_nom_opt
Generator
Manchester Wind 2000.000000
Manchester Gas -0.000000
Norway Wind 895.372552
Norway Gas 91.001539
Frankfurt Wind 100.000000
Frankfurt Gas 884.092697
Frankfurt Wind 2 2100.000000

Looks good! The generators of carrier ‘wind’ at bus ‘Frankfurt’ are just the limit of 2200 MW.