diff --git a/doc/design/carbon-management.md b/doc/design/carbon-management.md index 294cbe813a..38315c6442 100644 --- a/doc/design/carbon-management.md +++ b/doc/design/carbon-management.md @@ -86,6 +86,10 @@ years. Storage sites can be resolved regionally from a geological database of potentials, with a choice between offshore formations only, which are larger and far from settlements, and onshore formations as well. +Regional geological potentials are divided by `years_of_storage` to obtain +the modeled annual potential. A fixed-capacity, negative-dispatch Generator +enforces each region's potential and charges the sequestration cost. + !!! warning "The sequestration limit shapes the result" The limit is a deliberate choice: it keeps sequestration for hard-to-abate emissions and prevents it from becoming a backstop that diff --git a/doc/release_notes.md b/doc/release_notes.md index 6a2b9a305c..cbd3a755a8 100644 --- a/doc/release_notes.md +++ b/doc/release_notes.md @@ -6,8 +6,12 @@ + +* Fix: Regional CO2 sequestration allowances no longer accumulate across myopic planning horizons. Sequestration Stores are replaced by fixed-capacity Generators with energy-sum limits and infinite lifetimes, preventing brownfield carry-over from duplicating regional potentials. Sequestration costs are charged on Generator dispatch instead of the connecting Links. This can change sequestration locations and CO2 pipeline expansion ([#2369](https://github.com/PyPSA/pypsa-eur/pull/2369), fixes [#2368](https://github.com/PyPSA/pypsa-eur/issues/2368)). + * Fix: `lines: types` must now define a line type for the 380 kV level; the config validation fails early with a clear message instead of the workflow failing later ([#2166](https://github.com/PyPSA/pypsa-eur/issues/2166)). + * Feature: New switches `industry: solid_biomass_for_industry_cc`, `industry: gas_for_industry_cc` and `industry: process_emissions_cc` to disable the carbon capture options for solid biomass for industry, gas for industry and industrial process emissions. All default to `true`, which matches the previous behaviour ([#2357](https://github.com/PyPSA/pypsa-eur/pull/2357)). * Feature: New option `sector: electrolysis` to allow hydrogen electrolysis to be configurable ([#2358](https://github.com/PyPSA/pypsa-eur/pull/2358)). diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index 41964ff937..529af39098 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -660,31 +660,6 @@ def add_co2_tracking( carrier="co2 expansion", unit="t_co2", ) - n.add( - "Link", - sequestration_buses, - bus0=spatial.co2.dense, - bus1=sequestration_buses, - carrier="co2 sequestered", - marginal_cost=options["co2_sequestration_cost"], - efficiency=1.0, - p_nom=np.inf, - p_nom_extendable=False, - ) - - else: - n.add( - "Link", - sequestration_buses, - bus0=spatial.co2.nodes, - bus1=sequestration_buses, - carrier="co2 sequestered", - marginal_cost=options["co2_sequestration_cost"], - efficiency=1.0, - p_nom=np.inf, - p_nom_extendable=False, - ) - if options["regional_co2_sequestration_potential"]["enable"]: if sequestration_potential_file is None: raise ValueError( @@ -713,14 +688,33 @@ def add_co2_tracking( else: e_nom_max = np.inf + p_nom = ( + e_nom_max if options["regional_co2_sequestration_potential"]["enable"] else 1e7 + ) n.add( - "Store", + "Link", + sequestration_buses, + bus0=spatial.co2.dense if co2_liquefaction else spatial.co2.nodes, + bus1=sequestration_buses, + carrier="co2 sequestered", + efficiency=1.0, + p_nom=p_nom, + p_nom_extendable=False, + lifetime=np.inf, + ) + + n.add( + "Generator", sequestration_buses, - e_nom_extendable=True, - e_nom_max=e_nom_max, - marginal_cost=-0.1, bus=sequestration_buses, - lifetime=options["co2_sequestration_lifetime"], + p_nom=p_nom, + p_nom_extendable=False, + p_min_pu=-1.0, + p_max_pu=0, + e_sum_min=-e_nom_max, + e_sum_max=0, + marginal_cost=-options["co2_sequestration_cost"], + lifetime=np.inf, # Lifetime inf means assets from previous horizons get dropped in add_brownfield carrier="co2 sequestered", ) diff --git a/scripts/solve_network.py b/scripts/solve_network.py index 31f8712331..d17d8216b8 100644 --- a/scripts/solve_network.py +++ b/scripts/solve_network.py @@ -554,7 +554,7 @@ def prepare_network( if limit_max_growth is not None and limit_max_growth["enable"]: add_max_growth(n, limit_max_growth) - if n.stores.carrier.eq("co2 sequestered").any(): + if n.generators.carrier.eq("co2 sequestered").any(): limit_dict = co2_sequestration_potential add_co2_sequestration_limit( n, limit_dict=limit_dict, planning_horizons=planning_horizons