Note
You can download this example as a Jupyter notebook or start it in interactive mode.
Redispatch Example with SciGRID network#
In this example, we compare a 2-stage market with an initial market clearing in two bidding zones with flow-based market coupling and a subsequent redispatch market (incl. curtailment) to an idealised nodal pricing scheme.
[1]:
import pypsa
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
from pypsa.descriptors import get_switchable_as_dense as as_dense
[2]:
solver = "cbc"
Load example network#
[3]:
o = pypsa.examples.scigrid_de(from_master=True)
o.lines.s_max_pu = 0.7
o.lines.loc[["316", "527", "602"], "s_nom"] = 1715
o.set_snapshots([o.snapshots[12]])
WARNING:pypsa.io:Importing network from PyPSA version v0.17.1 while current version is v0.25.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
[4]:
n = o.copy() # for redispatch model
m = o.copy() # for market model
[5]:
o.plot();
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/cartopy/mpl/style.py:76: UserWarning: facecolor will have no effect as it has been defined as "never".
warnings.warn('facecolor will have no effect as it has been '
Solve original nodal market model o
#
First, let us solve a nodal market using the original model o
:
[6]:
o.optimize(solver_name=solver)
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')
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.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Generator' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'StorageUnit' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'StorageUnit' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Line' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Line' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Transformer' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Transformer' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
INFO:linopy.model: Solve problem using Cbc solver
INFO:linopy.io: Writing time: 0.1s
INFO:linopy.constants: Optimization successful:
Status: ok
Termination condition: optimal
Solution: 2485 primals, 5957 duals
Objective: 3.01e+05
Solver model: not available
Solver message: Optimal - objective value 301209.38232509
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 were not assigned to the network.
Welcome to the CBC MILP Solver
Version: 2.10.10
Build Date: Apr 19 2023
command line - cbc -printingOptions all -import /tmp/linopy-problem-dhoinxht.lp -solve -solu /tmp/linopy-solve-056rle5l.sol (default strategy 1)
Option for printingOptions changed from normal to all
Presolve 624 (-5333) rows, 1084 (-1401) columns and 4132 (-7068) elements
Perturbing problem by 0.001% of 2348.6084 - largest nonzero change 0.00098451421 ( 0.0082413801%) - largest zero change 0.0009841746
0 Obj -11.650259 Primal inf 1535674.9 (576)
87 Obj -11.650259 Primal inf 945623.69 (549)
159 Obj -11.042033 Primal inf 879161.53 (513)
246 Obj -10.219206 Primal inf 929980.63 (476)
312 Obj -9.2945766 Primal inf 2833069.1 (474)
378 Obj -7.7263114 Primal inf 1012247.1 (406)
446 Obj 3997.2864 Primal inf 581252.41 (327)
519 Obj 3999.2068 Primal inf 176373.76 (223)
590 Obj 4035.3797 Primal inf 124091.16 (158)
677 Obj 154547.14 Primal inf 11838.651 (90)
764 Obj 298698.41 Primal inf 670.78849 (23)
791 Obj 301212.56
Optimal - objective value 301209.38
After Postsolve, objective 301209.38, infeasibilities - dual 24.116221 (1), primal 6.0436272e-07 (1)
Presolved model was optimal, full model needs cleaning up
0 Obj 301209.38 Dual inf 0.24116211 (1)
End of values pass after 1 iterations
1 Obj 301209.38
Optimal - objective value 301209.38
Optimal objective 301209.3823 - 792 iterations time 0.122, Presolve 0.02
Total time (CPU seconds): 0.17 (Wallclock seconds): 0.13
[6]:
('ok', 'optimal')
Costs are 301 k€.
Build market model m
with two bidding zones#
For this example, we split the German transmission network into two market zones at latitude 51 degrees.
You can build any other market zones by providing an alternative mapping from bus to zone.
[7]:
zones = (n.buses.y > 51).map(lambda x: "North" if x else "South")
Next, we assign this mapping to the market model m
.
We re-assign the buses of all generators and loads, and remove all transmission lines within each bidding zone.
Here, we assume that the bidding zones are coupled through the transmission lines that connect them.
[8]:
for c in m.iterate_components(m.one_port_components):
c.df.bus = c.df.bus.map(zones)
for c in m.iterate_components(m.branch_components):
c.df.bus0 = c.df.bus0.map(zones)
c.df.bus1 = c.df.bus1.map(zones)
internal = c.df.bus0 == c.df.bus1
m.mremove(c.name, c.df.loc[internal].index)
m.mremove("Bus", m.buses.index)
m.madd("Bus", ["North", "South"]);
Now, we can solve the coupled market with two bidding zones.
[9]:
m.optimize(solver_name=solver)
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Generator' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'StorageUnit' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'StorageUnit' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Line' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Line' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
INFO:linopy.model: Solve problem using Cbc solver
INFO:linopy.io: Writing time: 0.07s
INFO:linopy.constants: Optimization successful:
Status: ok
Termination condition: optimal
Solution: 1561 primals, 3185 duals
Objective: 2.14e+05
Solver model: not available
Solver message: Optimal - objective value 213988.68595810
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, 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 were not assigned to the network.
Welcome to the CBC MILP Solver
Version: 2.10.10
Build Date: Apr 19 2023
command line - cbc -printingOptions all -import /tmp/linopy-problem-yygpog5j.lp -solve -solu /tmp/linopy-solve-1vrjmlvs.sol (default strategy 1)
Option for printingOptions changed from normal to all
Presolve 40 (-3145) rows, 410 (-1151) columns and 487 (-4342) elements
Perturbing problem by 0.001% of 212.59539 - largest nonzero change 0.00017578427 ( 0.0036987348%) - largest zero change 0.00015445146
0 Obj 0 Primal inf 11285.222 (1)
48 Obj 184184.9 Primal inf 1700.1029 (24)
86 Obj 213988.73
Optimal - objective value 213988.69
After Postsolve, objective 213988.69, infeasibilities - dual 0 (0), primal 0 (0)
Optimal objective 213988.686 - 86 iterations time 0.012, Presolve 0.00
Total time (CPU seconds): 0.05 (Wallclock seconds): 0.03
[9]:
('ok', 'optimal')
Costs are 214 k€, which is much lower than the 301 k€ of the nodal market.
This is because network restrictions apart from the North/South division are not taken into account yet.
We can look at the market clearing prices of each zone:
[10]:
m.buses_t.marginal_price
[10]:
Bus | North | South |
---|---|---|
snapshot | ||
2011-01-01 12:00:00 | 8.0 | 25.0 |
Build redispatch model n
#
Next, based on the market outcome with two bidding zones m
, we build a secondary redispatch market n
that rectifies transmission constraints through curtailment and ramping up/down thermal generators.
First, we fix the dispatch of generators to the results from the market simulation. (For simplicity, this example disregards storage units.)
[11]:
p = m.generators_t.p / m.generators.p_nom
n.generators_t.p_min_pu = p
n.generators_t.p_max_pu = p
Then, we add generators bidding into redispatch market using the following assumptions:
All generators can reduce their dispatch to zero. This includes also curtailment of renewables.
All generators can increase their dispatch to their available/nominal capacity.
No changes to the marginal costs, i.e. reducing dispatch lowers costs.
With these settings, the 2-stage market should result in the same cost as the nodal market.
[12]:
g_up = n.generators.copy()
g_down = n.generators.copy()
g_up.index = g_up.index.map(lambda x: x + " ramp up")
g_down.index = g_down.index.map(lambda x: x + " ramp down")
up = (
as_dense(m, "Generator", "p_max_pu") * m.generators.p_nom - m.generators_t.p
).clip(0) / m.generators.p_nom
down = -m.generators_t.p / m.generators.p_nom
up.columns = up.columns.map(lambda x: x + " ramp up")
down.columns = down.columns.map(lambda x: x + " ramp down")
n.madd("Generator", g_up.index, p_max_pu=up, **g_up.drop("p_max_pu", axis=1))
n.madd(
"Generator",
g_down.index,
p_min_pu=down,
p_max_pu=0,
**g_down.drop(["p_max_pu", "p_min_pu"], axis=1)
);
Now, let’s solve the redispatch market:
[13]:
n.optimize(solver_name=solver)
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')
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.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Generator' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'StorageUnit' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'StorageUnit' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Line' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Line' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Transformer' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Transformer' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Generator' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'StorageUnit' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'StorageUnit' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Line' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Line' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Transformer' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
INFO:linopy.model: Solve problem using Cbc solver
INFO:linopy.io: Writing time: 0.13s
INFO:linopy.constants: Optimization successful:
Status: ok
Termination condition: optimal
Solution: 5331 primals, 11649 duals
Objective: 3.01e+05
Solver model: not available
Solver message: Optimal - objective value 301209.38114435
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 were not assigned to the network.
Welcome to the CBC MILP Solver
Version: 2.10.10
Build Date: Apr 19 2023
command line - cbc -printingOptions all -import /tmp/linopy-problem-3ghpk48a.lp -solve -solu /tmp/linopy-solve-hway703v.sol (default strategy 1)
Option for printingOptions changed from normal to all
Presolve 628 (-11021) rows, 1314 (-4017) columns and 4381 (-15357) elements
Perturbing problem by 0.001% of 2381.906 - largest nonzero change 0.00099500838 ( 0.0099911826%) - largest zero change 0.00099445828
0 Obj 195154.34 Primal inf 1555075 (578) Dual inf 7306.4734 (158)
87 Obj -12.490546 Primal inf 778573.92 (550)
174 Obj -12.104513 Primal inf 753788.21 (517)
251 Obj -11.683694 Primal inf 577993.61 (465)
314 Obj -10.037542 Primal inf 936730.05 (461)
374 Obj -8.555758 Primal inf 452428.67 (389)
442 Obj -6.9053077 Primal inf 972343.59 (385)
520 Obj 3999.4702 Primal inf 689345.3 (325)
607 Obj 7019.4097 Primal inf 3492997.9 (269)
675 Obj 21990.748 Primal inf 1790002.5 (236)
741 Obj 54141.974 Primal inf 86357.709 (129)
828 Obj 257622.6 Primal inf 5736.9649 (48)
879 Obj 301211.41
879 Obj 301209.38 Dual inf 1.4139822e-05 (3)
882 Obj 301209.38
Optimal - objective value 301209.38
After Postsolve, objective 301209.38, infeasibilities - dual 1496.6983 (101), primal 2.2690555e-05 (96)
Presolved model was optimal, full model needs cleaning up
0 Obj 301209.38 Primal inf 5.2775232e-07 (4) Dual inf 4.0000001e+08 (105)
End of values pass after 106 iterations
106 Obj 301209.38
Optimal - objective value 301209.38
Optimal objective 301209.3811 - 988 iterations time 0.132, Presolve 0.03
Total time (CPU seconds): 0.23 (Wallclock seconds): 0.18
[13]:
('ok', 'optimal')
And, as expected, the costs are the same as for the nodal market: 301 k€.
Now, we can plot both the market results of the 2 bidding zone market and the redispatch results:
[14]:
fig, axs = plt.subplots(
1, 3, figsize=(20, 10), subplot_kw={"projection": ccrs.AlbersEqualArea()}
)
market = (
n.generators_t.p[m.generators.index]
.T.squeeze()
.groupby(n.generators.bus)
.sum()
.div(2e4)
)
n.plot(ax=axs[0], bus_sizes=market, title="2 bidding zones market simulation")
redispatch_up = (
n.generators_t.p.filter(like="ramp up")
.T.squeeze()
.groupby(n.generators.bus)
.sum()
.div(2e4)
)
n.plot(
ax=axs[1], bus_sizes=redispatch_up, bus_colors="blue", title="Redispatch: ramp up"
)
redispatch_down = (
n.generators_t.p.filter(like="ramp down")
.T.squeeze()
.groupby(n.generators.bus)
.sum()
.div(-2e4)
)
n.plot(
ax=axs[2],
bus_sizes=redispatch_down,
bus_colors="red",
title="Redispatch: ramp down / curtail",
);
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/cartopy/mpl/style.py:76: UserWarning: facecolor will have no effect as it has been defined as "never".
warnings.warn('facecolor will have no effect as it has been '
We can also read out the final dispatch of each generator:
[15]:
grouper = n.generators.index.str.split(" ramp", expand=True).get_level_values(0)
n.generators_t.p.groupby(grouper, axis=1).sum().squeeze()
[15]:
1 Gas 0.000000
1 Hard Coal 0.000000
1 Solar 11.326192
1 Wind Onshore 1.754375
100_220kV Solar 14.913326
...
98 Wind Onshore 71.451646
99_220kV Gas 0.000000
99_220kV Hard Coal 0.000000
99_220kV Solar 8.246606
99_220kV Wind Onshore 3.432939
Name: 2011-01-01 12:00:00, Length: 1423, dtype: float64
Changing bidding strategies in redispatch market#
We can also formulate other bidding strategies or compensation mechanisms for the redispatch market.
For example, that ramping up a generator is twice as expensive.
[16]:
n.generators.loc[n.generators.index.str.contains("ramp up"), "marginal_cost"] *= 2
Or that generators need to be compensated for curtailing them or ramping them down at 50% of their marginal cost.
[17]:
n.generators.loc[n.generators.index.str.contains("ramp down"), "marginal_cost"] *= -0.5
In this way, the outcome should be more expensive than the ideal nodal market:
[18]:
n.optimize(solver_name=solver)
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')
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.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Generator' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'StorageUnit' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'StorageUnit' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Line' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Line' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Transformer' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Transformer' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Generator' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'StorageUnit' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'StorageUnit' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Line' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Line' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
/home/docs/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.25.2/lib/python3.11/site-packages/linopy/expressions.py:176: FutureWarning: the `pandas.MultiIndex` object(s) passed as 'Transformer' coordinate(s) or data variable(s) will no longer be implicitly promoted and wrapped into multiple indexed coordinates in the future (i.e., one coordinate for each multi-index level + one dimension coordinate). If you want to keep this behavior, you need to first wrap it explicitly using `mindex_coords = xarray.Coordinates.from_pandas_multiindex(mindex_obj, 'dim')` and pass it as coordinates, e.g., `xarray.Dataset(coords=mindex_coords)`, `dataset.assign_coords(mindex_coords)` or `dataarray.assign_coords(mindex_coords)`.
ds = self.data.assign_coords({group_dim: idx})
INFO:linopy.model: Solve problem using Cbc solver
INFO:linopy.io: Writing time: 0.13s
INFO:linopy.constants: Optimization successful:
Status: ok
Termination condition: optimal
Solution: 5331 primals, 11649 duals
Objective: 4.79e+05
Solver model: not available
Solver message: Optimal - objective value 479003.12190570
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 were not assigned to the network.
Welcome to the CBC MILP Solver
Version: 2.10.10
Build Date: Apr 19 2023
command line - cbc -printingOptions all -import /tmp/linopy-problem-i7515cw_.lp -solve -solu /tmp/linopy-solve-7o1d_f0y.sol (default strategy 1)
Option for printingOptions changed from normal to all
Presolve 628 (-11021) rows, 1314 (-4017) columns and 4381 (-15357) elements
Perturbing problem by 0.001% of 4763.8119 - largest nonzero change 0.00084818851 ( 0.0022743704%) - largest zero change 0.00084787745
0 Obj 223385.84 Primal inf 1555075 (578)
87 Obj 223385.84 Primal inf 793226.96 (547)
174 Obj 223386.36 Primal inf 718003.82 (506)
247 Obj 223387.56 Primal inf 878616.93 (497)
334 Obj 223391.27 Primal inf 583206.79 (440)
392 Obj 223392.84 Primal inf 1080167.2 (426)
469 Obj 230850.89 Primal inf 635598.46 (336)
549 Obj 231304.79 Primal inf 257946 (190)
621 Obj 231308.23 Primal inf 78507.611 (155)
708 Obj 274417.48 Primal inf 10238.144 (76) Dual inf 2.8229491e-18 (1)
795 Obj 478996.39 Primal inf 0.19170344 (2)
797 Obj 479006.15
797 Obj 479003.13 Dual inf 0.00033720888 (9)
806 Obj 479003.12
Optimal - objective value 479003.12
After Postsolve, objective 479003.12, infeasibilities - dual 2777.845 (90), primal 1.9590642e-05 (84)
Presolved model was optimal, full model needs cleaning up
0 Obj 479003.12 Primal inf 5.2775226e-07 (4) Dual inf 4.0000003e+08 (94)
End of values pass after 94 iterations
94 Obj 479003.12
Optimal - objective value 479003.12
Optimal objective 479003.1219 - 900 iterations time 0.122, Presolve 0.03
Total time (CPU seconds): 0.23 (Wallclock seconds): 0.17
[18]:
('ok', 'optimal')
Costs are now 502 k€ compared to 301 k€.