Skip to content

Commit 25cf9e2

Browse files
measrainseytgilonpre-commit-ci[bot]
authored
feat: edit marginal costs and dispatch of biomass/biogas generators in CBA (#719)
* feat: set marginal costs for biomass and biogas generators * feat: remove volume limits on biomass and biogas dispatch * chore: remove checks and logs * chore: remove logger and update docs * refac: make simple refactoring and add comments * feat: revert `scripts/cba/solve_cba_network.py` back to `master` * feat: add slack to biomass/biogas dispatch `e_sum_min` * feat: increase slack to 40% * docs: add #719 to doc/release_notes * refac: use `n_msv.snapshots` instead of `msv_mp.index` Co-authored-by: Thomas Gilon <thomas.gilon@openenergytransition.org> * feat: directly set marginal price as marginal cost, instead of adding Co-authored-by: Thomas Gilon <thomas.gilon@openenergytransition.org> * chore: edit comment Co-authored-by: Thomas Gilon <thomas.gilon@openenergytransition.org> * feat: create config option `cba.biomass_biogas_slack` for slack instead of hardcoding * refac: filter by both carriers and volume limits * refac: remove setting marginal cost dataframe * chore: add type hints * feat: add `cba.biomass_biogas_slack` config option as rule input * fix: add back Generator type * docs: add #719 to doc/release_notes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * chore: add details in comments of configs * docs: change function description * chore: edit comment for slack in config/test/config.tyndp.yaml Co-authored-by: Thomas Gilon <thomas.gilon@openenergytransition.org> * doc: edit link in PR description Co-authored-by: Thomas Gilon <thomas.gilon@openenergytransition.org> * refac: change `has_volume_limit` to bool * feat: remove `e_sum` energy budget constraint on biomass/biogas slack * feat: remove `cba.biomass_biogas_slack` config option * refac: remove `get_components_with_volume_limits()` function * docs: edit PR description in doc/release_notes --------- Co-authored-by: Thomas Gilon <thomas.gilon@openenergytransition.org> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent a220946 commit 25cf9e2

3 files changed

Lines changed: 54 additions & 21 deletions

File tree

doc/release_notes.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
- Add `include_objective_constant` and `assign_all_duals` to solving config validator.
3030
- Add `gurobi-simplex` as solver option.
3131

32+
* Change dispatch of biomass and biogas generators in CBA by (a) setting the buses' marginal prices as the generators' marginal costs and (b) removing energy budget constraints ([#719](https://github.com/open-energy-transition/open-tyndp/pull/719)).
33+
3234
**Bugfixes and Compatibility**
3335

3436
* Rename bus for `t339` project (Tyrrhenian) from ITSI to ITVI ([#751](https://github.com/open-energy-transition/open-tyndp/pull/751)).

scripts/cba/prepare_rolling_horizon.py

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,8 @@ def disable_volume_limits(n: pypsa.Network):
135135
for c in n.components[{"Generator", "Link"}]:
136136
has_e_sum_min = isfinite(c.static.get("e_sum_min", []))
137137
if has_e_sum_min.any():
138-
c.static["has_volume_limit"] = 0
139-
c.static.loc[has_e_sum_min, "has_volume_limit"] = 1
138+
c.static["has_volume_limit"] = False
139+
c.static.loc[has_e_sum_min, "has_volume_limit"] = True
140140
c.static.loc[has_e_sum_min, "e_sum_min"] = -inf
141141
c.static.loc[has_e_sum_min, "e_sum_max"] = inf
142142

@@ -196,6 +196,52 @@ def apply_msv_to_network(
196196
n.c[c].dynamic["marginal_cost"].loc[:, s_i] = msv
197197

198198

199+
def apply_biomass_biogas_bus_marginal_prices(
200+
n: pypsa.Network,
201+
n_msv: pypsa.Network,
202+
carriers: list[str] = ["solid biomass", "biogas"],
203+
resample_method: str = "ffill",
204+
):
205+
"""
206+
Set biomass/biogas buses' marginal prices as the generators' marginal costs.
207+
208+
For each biomass/biogas generator in the rolling horizon network:
209+
use the marginal price time series of its attached bus from the MSV network and
210+
set the marginal price as the generator's marginal cost.
211+
212+
Parameters
213+
----------
214+
n : pypsa.Network
215+
Target network.
216+
n_msv : pypsa.Network
217+
Solved MSV network containing bus marginal prices.
218+
carriers : list[str], optional
219+
Generator carriers to apply bus marginal prices to. Defaults to ["solid biomass", "biogas"].
220+
resample_method : str, optional
221+
Method for resampling MSV bus marginal prices to target resolution. Default "ffill".
222+
"""
223+
if isinstance(carriers, str):
224+
carriers = [carriers]
225+
226+
# Get bus marginal prices from MSV network
227+
msv_mp = n_msv.buses_t.marginal_price
228+
229+
# Resample marginal prices if needed
230+
bus_mp = (
231+
msv_mp
232+
if n_msv.snapshots.equals(n.snapshots)
233+
else resample_msv_to_target(msv_mp, n.snapshots, method=resample_method)
234+
)
235+
236+
# Get index of generators with target carriers
237+
gen_index = n.generators.index[n.generators.carrier.isin(carriers)]
238+
239+
# Set marginal cost for each generator based on the marginal price of its bus
240+
for g in gen_index:
241+
bus = n.generators.at[g, "bus"]
242+
n.generators_t.marginal_cost[g] = bus_mp[bus].reindex(n.snapshots)
243+
244+
199245
def set_initial_state_from_pf(
200246
n: pypsa.Network,
201247
n_msv: pypsa.Network,
@@ -359,6 +405,9 @@ def fix_reservoir_soc_at_boundaries(
359405
# Apply marginal storage value to all non-cyclic carriers
360406
apply_msv_to_network(n, n_msv, cyclic_carriers, resample_method)
361407

408+
# Add bus marginal prices to the marginal costs of the biomass/biogas generators
409+
apply_biomass_biogas_bus_marginal_prices(n, n_msv, resample_method=resample_method)
410+
362411
# Fix reservoir state of charge at window boundaries from perfect foresight
363412
soc_boundary_carriers = snakemake.params.get("soc_boundary_carriers", [])
364413
cba_solving = snakemake.config.get("cba", {}).get("solving", {})

scripts/cba/solve_cba_network.py

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -191,24 +191,6 @@ def optimize_with_rolling_horizon(
191191
n.storage_units_t.state_of_charge.loc[snapshots[start - 1]]
192192
)
193193

194-
# Set per-window energy budgets for volume-limited components
195-
# (biomass, biogas) based on PF dispatch stored in generators_t.p
196-
for c_name in ["Generator", "Link"]:
197-
c = n.c[c_name]
198-
if "has_volume_limit" not in c.static.columns:
199-
continue
200-
vol_idx = c.static.index[c.static["has_volume_limit"] == 1]
201-
if vol_idx.empty:
202-
continue
203-
p_col = "p" if c_name == "Generator" else "p0"
204-
pf_p = c.dynamic[p_col]
205-
for comp in vol_idx:
206-
if comp not in pf_p.columns:
207-
continue
208-
window_energy = pf_p.loc[sns, comp].sum()
209-
c.static.loc[comp, "e_sum_min"] = window_energy
210-
c.static.loc[comp, "e_sum_max"] = window_energy
211-
212194
status, condition = n.optimize(sns, **kwargs) # type: ignore
213195

214196
# If solve is successful, dispose of Model object to release license before next rolling horizon
@@ -341,7 +323,7 @@ def solve_network(
341323
snakemake = mock_snakemake(
342324
"solve_cba_network",
343325
run="NT",
344-
cba_project="t4",
326+
cba_project="t16",
345327
planning_horizons="2030",
346328
configfiles=["config/config.tyndp.yaml"],
347329
)

0 commit comments

Comments
 (0)