add option for additional national carbon budget constraints
This commit is contained in:
parent
7884392326
commit
9b9090c76c
@ -84,6 +84,22 @@ co2_budget:
|
|||||||
2045: 0.032
|
2045: 0.032
|
||||||
2050: 0.000
|
2050: 0.000
|
||||||
|
|
||||||
|
co2_budget_national:
|
||||||
|
2030:
|
||||||
|
'DE': 0.350
|
||||||
|
'AT': 0.450
|
||||||
|
'BE': 0.450
|
||||||
|
'CH': 0.450
|
||||||
|
'CZ': 0.450
|
||||||
|
'DK': 0.450
|
||||||
|
'FR': 0.450
|
||||||
|
'GB': 0.450
|
||||||
|
'LU': 0.450
|
||||||
|
'NL': 0.450
|
||||||
|
'NO': 0.450
|
||||||
|
'PL': 0.450
|
||||||
|
'SE': 0.450
|
||||||
|
|
||||||
# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#electricity
|
# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#electricity
|
||||||
electricity:
|
electricity:
|
||||||
voltages: [220., 300., 380.]
|
voltages: [220., 300., 380.]
|
||||||
@ -454,6 +470,7 @@ sector:
|
|||||||
hydrogen_turbine: false
|
hydrogen_turbine: false
|
||||||
SMR: true
|
SMR: true
|
||||||
SMR_cc: true
|
SMR_cc: true
|
||||||
|
co2_budget_national: false
|
||||||
regional_co2_sequestration_potential:
|
regional_co2_sequestration_potential:
|
||||||
enable: false
|
enable: false
|
||||||
attribute: 'conservative estimate Mt'
|
attribute: 'conservative estimate Mt'
|
||||||
|
@ -88,11 +88,13 @@ rule solve_sector_network_myopic:
|
|||||||
co2_sequestration_potential=config["sector"].get(
|
co2_sequestration_potential=config["sector"].get(
|
||||||
"co2_sequestration_potential", 200
|
"co2_sequestration_potential", 200
|
||||||
),
|
),
|
||||||
|
countries=config["countries"],
|
||||||
input:
|
input:
|
||||||
network=RESULTS
|
network=RESULTS
|
||||||
+ "prenetworks-brownfield/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc",
|
+ "prenetworks-brownfield/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc",
|
||||||
costs="data/costs_{planning_horizons}.csv",
|
costs="data/costs_{planning_horizons}.csv",
|
||||||
config=RESULTS + "config.yaml",
|
config=RESULTS + "config.yaml",
|
||||||
|
co2_totals_name=RESOURCES + "co2_totals.csv",
|
||||||
output:
|
output:
|
||||||
RESULTS
|
RESULTS
|
||||||
+ "postnetworks/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc",
|
+ "postnetworks/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc",
|
||||||
|
@ -41,6 +41,8 @@ logger = logging.getLogger(__name__)
|
|||||||
pypsa.pf.logger.setLevel(logging.WARNING)
|
pypsa.pf.logger.setLevel(logging.WARNING)
|
||||||
from pypsa.descriptors import get_switchable_as_dense as get_as_dense
|
from pypsa.descriptors import get_switchable_as_dense as get_as_dense
|
||||||
|
|
||||||
|
from prepare_sector_network import emission_sectors_from_opts
|
||||||
|
|
||||||
|
|
||||||
def add_land_use_constraint(n, planning_horizons, config):
|
def add_land_use_constraint(n, planning_horizons, config):
|
||||||
if "m" in snakemake.wildcards.clusters:
|
if "m" in snakemake.wildcards.clusters:
|
||||||
@ -762,6 +764,92 @@ def add_pipe_retrofit_constraint(n):
|
|||||||
n.model.add_constraints(lhs == rhs, name="Link-pipe_retrofit")
|
n.model.add_constraints(lhs == rhs, name="Link-pipe_retrofit")
|
||||||
|
|
||||||
|
|
||||||
|
def add_co2limit_country(n, config, limit_countries, nyears=1.0):
|
||||||
|
"""
|
||||||
|
Add a set of emissions limit constraints for specified countries.
|
||||||
|
|
||||||
|
The countries and emissions limits are specified in the config file entry 'co2_budget_country_{investment_year}'.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
n : pypsa.Network
|
||||||
|
config : dict
|
||||||
|
limit_countries : dict
|
||||||
|
nyears: float, optional
|
||||||
|
Used to scale the emissions constraint to the number of snapshots of the base network.
|
||||||
|
"""
|
||||||
|
logger.info(f"Adding CO2 budget limit for each country as per unit of 1990 levels")
|
||||||
|
|
||||||
|
# TODO: n.config (submodule) vs snakemake.config (main module, overwrite/overwritten config)?
|
||||||
|
# countries = config.countries
|
||||||
|
# print(config)
|
||||||
|
countries = ['AT', 'BE', 'CH', 'CZ', 'DE', 'DK', 'FR', 'GB', 'LU', 'NL', 'NO', 'PL', 'SE']
|
||||||
|
|
||||||
|
# TODO: import function from prepare_sector_network? Move to common place?
|
||||||
|
sectors = emission_sectors_from_opts(opts)
|
||||||
|
|
||||||
|
# convert Mt to tCO2
|
||||||
|
co2_totals = 1e6 * pd.read_csv(snakemake.input.co2_totals_name, index_col=0)
|
||||||
|
|
||||||
|
co2_limit_countries = co2_totals.loc[countries, sectors].sum(axis=1)
|
||||||
|
co2_limit_countries = co2_limit_countries.loc[co2_limit_countries.index.isin(limit_countries.keys())]
|
||||||
|
|
||||||
|
co2_limit_countries *= co2_limit_countries.index.map(limit_countries) * nyears
|
||||||
|
|
||||||
|
p = n.model["Link-p"] # dimension: (time, component)
|
||||||
|
|
||||||
|
# NB: Most country-specific links retain their locational information in bus1 (except for DAC, where it is in bus2)
|
||||||
|
country = n.links.bus1.map(n.buses.location).map(n.buses.country)
|
||||||
|
country_DAC = (
|
||||||
|
n.links[n.links.carrier == "DAC"]
|
||||||
|
.bus2.map(n.buses.location)
|
||||||
|
.map(n.buses.country)
|
||||||
|
)
|
||||||
|
country[country_DAC.index] = country_DAC
|
||||||
|
|
||||||
|
lhs = []
|
||||||
|
for port in [col[3:] for col in n.links if col.startswith("bus")]:
|
||||||
|
if port == str(0):
|
||||||
|
efficiency = (
|
||||||
|
n.links["efficiency"].apply(lambda x: 1.0).rename("efficiency0")
|
||||||
|
)
|
||||||
|
elif port == str(1):
|
||||||
|
efficiency = n.links["efficiency"].rename("efficiency1")
|
||||||
|
else:
|
||||||
|
efficiency = n.links[f"efficiency{port}"]
|
||||||
|
mask = n.links[f"bus{port}"].map(n.buses.carrier).eq("co2")
|
||||||
|
|
||||||
|
idx = n.links[mask].index
|
||||||
|
|
||||||
|
grouping = country.loc[idx]
|
||||||
|
|
||||||
|
if not grouping.isnull().all():
|
||||||
|
expr = (
|
||||||
|
(p.loc[:, idx] * efficiency[idx])
|
||||||
|
.groupby(grouping, axis=1)
|
||||||
|
.sum()
|
||||||
|
.sum(dims="snapshot")
|
||||||
|
)
|
||||||
|
lhs.append(expr)
|
||||||
|
|
||||||
|
lhs = sum(lhs) # dimension: (country)
|
||||||
|
lhs = lhs.rename({list(lhs.dims.keys())[0]: "country"})
|
||||||
|
rhs = pd.Series(co2_limit_countries) # dimension: (country)
|
||||||
|
|
||||||
|
for ct in lhs.indexes["country"]:
|
||||||
|
n.model.add_constraints(
|
||||||
|
lhs.loc[ct] <= rhs[ct],
|
||||||
|
name=f"GlobalConstraint-co2_limit_per_country{ct}",
|
||||||
|
)
|
||||||
|
n.add(
|
||||||
|
"GlobalConstraint",
|
||||||
|
f"co2_limit_per_country{ct}",
|
||||||
|
constant=rhs[ct],
|
||||||
|
sense="<=",
|
||||||
|
type="",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def extra_functionality(n, snapshots):
|
def extra_functionality(n, snapshots):
|
||||||
"""
|
"""
|
||||||
Collects supplementary constraints which will be passed to
|
Collects supplementary constraints which will be passed to
|
||||||
@ -792,6 +880,17 @@ def extra_functionality(n, snapshots):
|
|||||||
add_carbon_budget_constraint(n, snapshots)
|
add_carbon_budget_constraint(n, snapshots)
|
||||||
add_retrofit_gas_boiler_constraint(n, snapshots)
|
add_retrofit_gas_boiler_constraint(n, snapshots)
|
||||||
|
|
||||||
|
if n.config["sector"]["co2_budget_national"]:
|
||||||
|
# prepare co2 constraint
|
||||||
|
nhours = n.snapshot_weightings.generators.sum()
|
||||||
|
nyears = nhours / 8760
|
||||||
|
investment_year = int(snakemake.wildcards.planning_horizons[-4:])
|
||||||
|
limit_countries = snakemake.config["co2_budget_national"][investment_year]
|
||||||
|
|
||||||
|
# add co2 constraint for each country
|
||||||
|
logger.info(f"Add CO2 limit for each country")
|
||||||
|
add_co2limit_country(n, config, limit_countries, nyears)
|
||||||
|
|
||||||
|
|
||||||
def solve_network(n, config, solving, opts="", **kwargs):
|
def solve_network(n, config, solving, opts="", **kwargs):
|
||||||
set_of_options = solving["solver"]["options"]
|
set_of_options = solving["solver"]["options"]
|
||||||
|
Loading…
Reference in New Issue
Block a user