diff --git a/config/config.default.yaml b/config/config.default.yaml index fcc74981..75de9437 100644 --- a/config/config.default.yaml +++ b/config/config.default.yaml @@ -59,14 +59,9 @@ snapshots: start: "2013-01-01" end: "2014-01-01" inclusive: 'left' - -snapshot_opts: - average_every_nhours: - enable: false - hour: 2 - time_segmentation: - enable: false - hour: 4380 + resolution: false + segmentation: false + #representative: false # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#enable enable: @@ -81,21 +76,6 @@ enable: retrieve_natura_raster: true custom_busmap: false -enable_sector: - no_heat_district: false - land_transport: false - heating: false - waste_heat: false - biomass: false - biomass_transport: false - agriculture_machinery: false - industry: false - decentral: false - #wave_energy: false - #wave_energy_factor: - noH2network: false - carbon_budget: false - co2limit_sector: false # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#co2-budget co2_budget: @@ -107,16 +87,6 @@ co2_budget: 2045: 0.032 2050: 0.000 -co2_budget_opts: - from_descrete_value: - enable: true - from_beta_decay: - enable: false # TODO: move to own rule with sector-opts wildcard? - value: 1 - from_exp_decay: - enable: false - value: 1 - # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#electricity electricity: voltages: [220., 300., 380.] @@ -159,13 +129,6 @@ electricity: Onshore: [onwind] PV: [solar] - adjust_carrier: # This is the solar+c0.5 thing - enable: false - #solar: - # p_nom_max: - # capital_cost: - # marginal_cost: - autarky: enable: false by_country: false @@ -618,12 +581,9 @@ costs: battery: 0. battery inverter: 0. emission_prices: + enable: false co2: 0. - - enable: - emission_prices: false - monthly_prices: false - + co2_monthly_prices: false # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#clustering clustering: diff --git a/rules/build_electricity.smk b/rules/build_electricity.smk index b15796c9..bfbc1bd2 100644 --- a/rules/build_electricity.smk +++ b/rules/build_electricity.smk @@ -20,7 +20,11 @@ if config["enable"].get("prepare_links_p_nom", False): rule build_electricity_demand: params: - snapshots=config["snapshots"], + snapshots={ + "start":config["snapshots"]["start"], + "end":config["snapshots"]["end"], + "inclusive":config["snapshots"]["inclusive"], + }, countries=config["countries"], load=config["load"], input: @@ -61,7 +65,11 @@ rule build_powerplants: rule base_network: params: countries=config["countries"], - snapshots=config["snapshots"], + snapshots={ + "start":config["snapshots"]["start"], + "end":config["snapshots"]["end"], + "inclusive":config["snapshots"]["inclusive"], + }, lines=config["lines"], links=config["links"], transformers=config["transformers"], @@ -144,7 +152,11 @@ if config["enable"].get("build_cutout", False): rule build_cutout: params: - snapshots=config["snapshots"], + snapshots={ + "start":config["snapshots"]["start"], + "end":config["snapshots"]["end"], + "inclusive":config["snapshots"]["inclusive"], + }, cutouts=config["atlite"]["cutouts"], input: regions_onshore=RESOURCES + "regions_onshore.geojson", @@ -470,6 +482,10 @@ rule add_extra_components: rule prepare_network: params: + snapshots={ + "resolution":config["snapshots"].get("resolution", False), + "segmentation":config["snapshots"].get("segmentation", False), + }, links=config["links"], lines=config["lines"], co2base=config["electricity"]["co2base"], @@ -479,7 +495,6 @@ rule prepare_network: gaslimit=config["electricity"].get("gaslimit"), max_hours=config["electricity"]["max_hours"], costs=config["costs"], - snapshot_opts=config.get("snapshot_opts", {}), autarky=config["electricity"].get("autarky", {}), input: RESOURCES + "networks/elec_s{simpl}_{clusters}_ec.nc", diff --git a/rules/build_sector.smk b/rules/build_sector.smk index 1bee1ed0..56a6b532 100644 --- a/rules/build_sector.smk +++ b/rules/build_sector.smk @@ -141,7 +141,11 @@ if not (config["sector"]["gas_network"] or config["sector"]["H2_retrofit"]): rule build_heat_demands: params: - snapshots=config["snapshots"], + snapshots={ + "start":config["snapshots"]["start"], + "end":config["snapshots"]["end"], + "inclusive":config["snapshots"]["inclusive"], + }, input: pop_layout=RESOURCES + "pop_layout_{scope}.nc", regions_onshore=RESOURCES + "regions_onshore_elec_s{simpl}_{clusters}.geojson", @@ -163,7 +167,11 @@ rule build_heat_demands: rule build_temperature_profiles: params: - snapshots=config["snapshots"], + snapshots={ + "start":config["snapshots"]["start"], + "end":config["snapshots"]["end"], + "inclusive":config["snapshots"]["inclusive"], + }, input: pop_layout=RESOURCES + "pop_layout_{scope}.nc", regions_onshore=RESOURCES + "regions_onshore_elec_s{simpl}_{clusters}.geojson", @@ -215,7 +223,11 @@ rule build_cop_profiles: rule build_solar_thermal_profiles: params: - snapshots=config["snapshots"], + snapshots={ + "start":config["snapshots"]["start"], + "end":config["snapshots"]["end"], + "inclusive":config["snapshots"]["inclusive"], + }, solar_thermal=config["solar_thermal"], input: pop_layout=RESOURCES + "pop_layout_{scope}.nc", @@ -677,7 +689,11 @@ rule build_shipping_demand: rule build_transport_demand: params: - snapshots=config["snapshots"], + snapshots={ + "start":config["snapshots"]["start"], + "end":config["snapshots"]["end"], + "inclusive":config["snapshots"]["inclusive"], + }, sector=config["sector"], input: clustered_pop_layout=RESOURCES + "pop_layout_elec_s{simpl}_{clusters}.csv", @@ -705,7 +721,6 @@ rule build_transport_demand: rule prepare_sector_network: params: - enable_sector=config.get("enable_sector", {}), co2_budget=config["co2_budget"], conventional_carriers=config["existing_capacities"]["conventional_carriers"], foresight=config["foresight"], @@ -718,7 +733,6 @@ rule prepare_sector_network: countries=config["countries"], emissions_scope=config["energy"]["emissions"], eurostat_report_year=config["energy"]["eurostat_report_year"], - snapshot_opts=config.get("snapshot_opts", {}), RDIR=RDIR, input: **build_retro_cost_output, diff --git a/rules/postprocess.smk b/rules/postprocess.smk index cf0038a3..42f0722e 100644 --- a/rules/postprocess.smk +++ b/rules/postprocess.smk @@ -55,7 +55,11 @@ rule make_summary: params: foresight=config["foresight"], costs=config["costs"], - snapshots=config["snapshots"], + snapshots={ + "start":config["snapshots"]["start"], + "end":config["snapshots"]["end"], + "inclusive":config["snapshots"]["inclusive"], + }, scenario=config["scenario"], RDIR=RDIR, input: diff --git a/rules/validate.smk b/rules/validate.smk index cfb8c959..ece9bebd 100644 --- a/rules/validate.smk +++ b/rules/validate.smk @@ -17,7 +17,11 @@ rule build_electricity_production: The data is used for validation of the optimization results. """ params: - snapshots=config["snapshots"], + snapshots={ + "start":config["snapshots"]["start"], + "end":config["snapshots"]["end"], + "inclusive":config["snapshots"]["inclusive"], + }, countries=config["countries"], output: RESOURCES + "historical_electricity_production.csv", @@ -35,7 +39,11 @@ rule build_cross_border_flows: The data is used for validation of the optimization results. """ params: - snapshots=config["snapshots"], + snapshots={ + "start":config["snapshots"]["start"], + "end":config["snapshots"]["end"], + "inclusive":config["snapshots"]["inclusive"], + }, countries=config["countries"], input: network=RESOURCES + "networks/base.nc", @@ -55,7 +63,11 @@ rule build_electricity_prices: The data is used for validation of the optimization results. """ params: - snapshots=config["snapshots"], + snapshots={ + "start":config["snapshots"]["start"], + "end":config["snapshots"]["end"], + "inclusive":config["snapshots"]["inclusive"], + }, countries=config["countries"], output: RESOURCES + "historical_electricity_prices.csv", diff --git a/scripts/prepare_network.py b/scripts/prepare_network.py index 2a2d73f8..f89db024 100755 --- a/scripts/prepare_network.py +++ b/scripts/prepare_network.py @@ -312,24 +312,16 @@ if __name__ == "__main__": set_line_s_max_pu(n, snakemake.params.lines["s_max_pu"]) # temporal averaging - nhours_opts_config = snakemake.params.snapshot_opts.get("average_every_nhours", {}) - nhours_enable_config = nhours_opts_config.get("enable", None) - nhours_config = str(nhours_opts_config.get("hour", None)) + "h" + nhours_config = snakemake.params.snapshots.get("resolution",False) nhours_wildcard = get_opt(opts, r"^\d+h$") - if nhours_wildcard is not None or ( - nhours_enable_config and nhours_config is not None - ): + if nhours_wildcard is not None or isinstance(nhours_config, str): nhours = nhours_wildcard or nhours_config n = average_every_nhours(n, nhours) # segments with package tsam - time_seg_opts_config = snakemake.params.snapshot_opts.get("time_segmentation", {}) - time_seg_enable_config = nhours_opts_config.get("enable", None) - time_seg_config = str(nhours_opts_config.get("hour", None)) + "seg" + time_seg_config = snakemake.params.snapshots.get("segmentation",False) time_seg_wildcard = get_opt(opts, r"^\d+seg$") - if time_seg_wildcard is not None or ( - time_seg_enable_config and time_seg_config is not None - ): + if time_seg_wildcard is not None or isinstance(time_seg_config, str): time_seg = time_seg_wildcard or time_seg_config solver_name = snakemake.config["solving"]["solver"]["name"] n = apply_time_segmentation(n, time_seg, solver_name) @@ -383,7 +375,7 @@ if __name__ == "__main__": sel = c.df.carrier.str.contains(carrier) c.df.loc[sel, attr] *= factor - Ept_config = snakemake.params.costs.get("enable", {}).get("monthly_prices", False) + Ept_config = snakemake.params.costs["emission_prices"].get("co2_monthly_prices", False) for o in opts: if "Ept" in o or Ept_config: logger.info( @@ -392,15 +384,15 @@ if __name__ == "__main__": add_dynamic_emission_prices(n) Ept_config = True - Ep_config = snakemake.params.costs.get("enable", {}).get("emission_prices", False) + Ep_config = snakemake.params.costs["emission_prices"].get("enable", False) Ep_wildcard, co2_wildcard = find_opt(opts, "Ep") if (Ep_wildcard or Ep_config) and not Ept_config: if co2_wildcard is not None: - logger.info("Setting emission prices according to wildcard value.") + logger.info("Setting CO2 prices according to wildcard value.") add_emission_prices(n, dict(co2=co2_wildcard)) else: - logger.info("Setting emission prices according to config value.") - add_emission_prices(n, snakemake.params.costs["emission_prices"]) + logger.info("Setting CO2 prices according to config value.") + add_emission_prices(n, dict(co2=snakemake.params.costs["emission_prices"]["co2"])) ll_type, factor = snakemake.wildcards.ll[0], snakemake.wildcards.ll[1:] set_transmission_limit(n, ll_type, factor, costs, Nyears) diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index a2ffc3da..11406bff 100644 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -17,7 +17,7 @@ import numpy as np import pandas as pd import pypsa import xarray as xr -from _helpers import generate_periodic_profiles, get_opt, update_config_with_sector_opts +from _helpers import generate_periodic_profiles, update_config_with_sector_opts from add_electricity import calculate_annuity, sanitize_carriers from build_energy_totals import build_co2_totals, build_eea_co2, build_eurostat_co2 from networkx.algorithms import complement @@ -161,11 +161,11 @@ spatial = SimpleNamespace() def emission_sectors_from_opts(opts): sectors = ["electricity"] - if "T" in opts or opts_config.get("land_transport", False): + if "T" in opts: sectors += ["rail non-elec", "road non-elec"] - if "H" in opts or opts_config.get("heating", False): + if "H" in opts: sectors += ["residential non-elec", "services non-elec"] - if "I" in opts or opts_config.get("industry", False): + if "I" in opts: sectors += [ "industrial non-elec", "industrial processes", @@ -174,14 +174,7 @@ def emission_sectors_from_opts(opts): "domestic navigation", "international navigation", ] - - heat_and_industry = opts_config.get("industry", False) and opts_config.get( - "heating", False - ) - - if ("I" in opts and "H" in opts and "A" in opts) or ( - heat_and_industry and opts_config.get("agriculture_machinery", False) - ): + if "A" in opts: sectors += ["agriculture"] return sectors @@ -3263,45 +3256,27 @@ def set_temporal_aggregation(n, opts, solver_name): """ Aggregate network temporally. """ - # temporal averaging - nhours_opts_config = snakemake.params.snapshot_opts.get("average_every_nhours", {}) - nhours_enable_config = nhours_opts_config.get("enable", None) - nhours_config = str(nhours_opts_config.get("hour", None)) + "H" - nhours_wildcard = get_opt(opts, r"^\d+h$") - if nhours_wildcard is not None or ( - nhours_enable_config and nhours_config is not None - ): - nhours = nhours_wildcard or nhours_config - n = average_every_nhours(n, nhours) - return n - - # representative snapshots - snapshots_opts_config = snakemake.params.snapshot_opts.get("set_snapshots", {}) - snapshots_enable_config = snapshots_opts_config.get("enable", None) - snapshots_config = snapshots_opts_config.get("hour", None) - snapshots_wildcard = get_opt(opts, r"(^\d+)sn$") - if snapshots_wildcard is not None or ( - snapshots_enable_config and snapshots_config is not None - ): - sn = int(snapshots_wildcard[:-2]) or snapshots_config - logger.info(f"Use every {sn} snapshot as representative") - n.set_snapshots(n.snapshots[::sn]) - n.snapshot_weightings *= sn - return n - - # segments with package tsam - time_seg_opts_config = snakemake.params.snapshot_opts.get("time_segmentation", {}) - time_seg_enable_config = nhours_opts_config.get("enable", None) - time_seg_config = nhours_opts_config.get("hour", None) - time_seg_wildcard = get_opt(opts, r"^(\d+)seg$") - if time_seg_wildcard is not None or ( - time_seg_enable_config and time_seg_config is not None - ): - segments = int(time_seg_wildcard[:-3]) or time_seg_config - logger.info(f"Use temporal segmentation with {segments} segments") - n = apply_time_segmentation(n, segments, solver_name=solver_name) - return n - + for o in opts: + # temporal averaging + m = re.match(r"^\d+h$", o, re.IGNORECASE) + if m is not None: + n = average_every_nhours(n, m.group(0)) + break + # representative snapshots + m = re.match(r"(^\d+)sn$", o, re.IGNORECASE) + if m is not None: + sn = int(m[1]) + logger.info(f"Use every {sn} snapshot as representative") + n.set_snapshots(n.snapshots[::sn]) + n.snapshot_weightings *= sn + break + # segments with package tsam + m = re.match(r"^(\d+)seg$", o, re.IGNORECASE) + if m is not None: + segments = int(m[1]) + logger.info(f"Use temporal segmentation with {segments} segments") + n = apply_time_segmentation(n, segments, solver_name=solver_name) + break return n @@ -3328,12 +3303,6 @@ if __name__ == "__main__": opts = snakemake.wildcards.sector_opts.split("-") - opts_config = snakemake.params.enable_sector - - heat_and_industry = opts_config.get("industry", False) and opts_config.get( - "heating", False - ) - investment_year = int(snakemake.wildcards.planning_horizons[-4:]) n = pypsa.Network(snakemake.input.network) @@ -3371,57 +3340,51 @@ if __name__ == "__main__": # TODO merge with opts cost adjustment below for o in opts: - if o[:4] == "wave": # TODO: add config wildcard options or depreciated? + if o[:4] == "wave": wave_cost_factor = float(o[4:].replace("p", ".").replace("m", "-")) logger.info( f"Including wave generators with cost factor of {wave_cost_factor}" ) add_wave(n, wave_cost_factor) - if o[:4] == "dist": # TODO: add config wildcard options + if o[:4] == "dist": options["electricity_distribution_grid"] = True options["electricity_distribution_grid_cost_factor"] = float( o[4:].replace("p", ".").replace("m", "-") ) - for o in opts: - if o == "biomasstransport" or opts_config.get("biomass_transport", False): + if o == "biomasstransport": options["biomass_transport"] = True - break - if "nodistrict" in opts or opts_config.get("no_heat_district", False): + if "nodistrict" in opts: options["district_heating"]["progress"] = 0.0 - if "T" in opts or opts_config.get("land_transport", False): + if "T" in opts: add_land_transport(n, costs) - if "H" in opts or opts_config.get("heating", False): + if "H" in opts: add_heat(n, costs) - if "B" in opts or opts_config.get("biomass", False): + if "B" in opts: add_biomass(n, costs) if options["ammonia"]: add_ammonia(n, costs) - if "I" in opts or opts_config.get("industry", False): + if "I" in opts: add_industry(n, costs) - if ("I" in opts and "H" in opts) or ( - heat_and_industry and opts_config.get("waste_heat", False) - ): + if "I" in opts and "H" in opts: add_waste_heat(n) - if ("I" in opts and "H" in opts and "A" in opts) or ( - heat_and_industry and opts_config.get("agriculture_machinery", False) - ): # requires H and I + if "A" in opts: # requires H and I add_agriculture(n, costs) if options["dac"]: add_dac(n, costs) - if "decentral" in opts or opts_config.get("decentral", False): + if "decentral" in opts: decentral(n) - if "noH2network" in opts or opts_config.get("noH2network", False): + if "noH2network" in opts: remove_h2_network(n) if options["co2network"]: @@ -3436,7 +3399,7 @@ if __name__ == "__main__": limit_type = "config" limit = get(snakemake.params.co2_budget, investment_year) for o in opts: - if "cb" not in o or opts_config.get("carbon_budget", False) is False: + if "cb" not in o: continue limit_type = "carbon budget" fn = "results/" + snakemake.params.RDIR + "csvs/carbon_budget_distribution.csv" @@ -3456,7 +3419,7 @@ if __name__ == "__main__": limit = co2_cap.loc[investment_year] break for o in opts: - if "Co2L" not in o or opts_config.get("co2limit_sector", False) is False: + if "Co2L" not in o: continue limit_type = "wildcard" limit = o[o.find("Co2L") + 4 :] @@ -3465,7 +3428,7 @@ if __name__ == "__main__": logger.info(f"Add CO2 limit from {limit_type}") add_co2limit(n, nyears, limit) - for o in opts: # TODO: add config wildcard options or depreciated? + for o in opts: if not o[:10] == "linemaxext": continue maxext = float(o[10:]) * 1e3