From c8f601edf8ca9ba6236241efd25ce6652ee997fc Mon Sep 17 00:00:00 2001 From: Fabian Date: Fri, 20 Jan 2023 10:28:01 +0100 Subject: [PATCH 01/18] solve.py: start converting extra_funcionalities --- scripts/solve_network.py | 179 ++++++++++++++++++--------------------- 1 file changed, 81 insertions(+), 98 deletions(-) diff --git a/scripts/solve_network.py b/scripts/solve_network.py index 93442a41..51aedb79 100644 --- a/scripts/solve_network.py +++ b/scripts/solve_network.py @@ -4,8 +4,10 @@ import pypsa import numpy as np import pandas as pd +import xarray as xr from pypsa.linopt import get_var, linexpr, define_constraints +from linopy import merge from pypsa.linopf import network_lopf, ilopf @@ -107,110 +109,79 @@ def prepare_network(n, solve_opts=None): def add_battery_constraints(n): - - chargers_b = n.links.carrier.str.contains("battery charger") - chargers = n.links.index[chargers_b & n.links.p_nom_extendable] - dischargers = chargers.str.replace("charger", "discharger") - - if chargers.empty or ('Link', 'p_nom') not in n.variables.index: + """ + Add constraints to ensure that the ratio between the charger and + discharger. + 1 * charger_size - efficiency * discharger_size = 0 + """ + nodes = n.buses.index[n.buses.carrier == "battery"] + if nodes.empty: return - - link_p_nom = get_var(n, "Link", "p_nom") - - lhs = linexpr((1,link_p_nom[chargers]), - (-n.links.loc[dischargers, "efficiency"].values, - link_p_nom[dischargers].values)) - - define_constraints(n, lhs, "=", 0, 'Link', 'charger_ratio') + link_p_nom = n.model["Link-p_nom"] + eff = n.links.efficiency[nodes + " discharger"].values + lhs = link_p_nom.loc[nodes + ' charger'] - link_p_nom.loc[nodes + ' discharger'] * eff + n.model.add_constraints(lhs == 0, name="Link-charger_ratio") def add_chp_constraints(n): - electric_bool = (n.links.index.str.contains("urban central") - & n.links.index.str.contains("CHP") - & n.links.index.str.contains("electric")) - heat_bool = (n.links.index.str.contains("urban central") - & n.links.index.str.contains("CHP") - & n.links.index.str.contains("heat")) + electric = (n.links.index.str.contains("urban central") + & n.links.index.str.contains("CHP") + & n.links.index.str.contains("electric")) + heat = (n.links.index.str.contains("urban central") + & n.links.index.str.contains("CHP") + & n.links.index.str.contains("heat")) - electric = n.links.index[electric_bool] - heat = n.links.index[heat_bool] + electric_ext = n.links[electric].query("p_nom_extendable").index + heat_ext = n.links[heat].query("p_nom_extendable").index - electric_ext = n.links.index[electric_bool & n.links.p_nom_extendable] - heat_ext = n.links.index[heat_bool & n.links.p_nom_extendable] + electric_fix = n.links[electric].query("~p_nom_extendable").index + heat_fix = n.links[heat].query("~p_nom_extendable").index - electric_fix = n.links.index[electric_bool & ~n.links.p_nom_extendable] - heat_fix = n.links.index[heat_bool & ~n.links.p_nom_extendable] - - link_p = get_var(n, "Link", "p") + p = n.model["Link-p"] # dimension: [time, link] + # output ratio between heat and electricity and top_iso_fuel_line for extendable if not electric_ext.empty: + p_nom = n.model["Link-p_nom"] - link_p_nom = get_var(n, "Link", "p_nom") + lhs = (p_nom.loc[electric_ext] * (n.links.p_nom_ratio * n.links.efficiency)[electric_ext].values - + p_nom.loc[heat_ext] * n.links.efficiency[heat_ext].values) + n.model.add_constraints(lhs == 0, name='chplink-fix_p_nom_ratio') - #ratio of output heat to electricity set by p_nom_ratio - lhs = linexpr((n.links.loc[electric_ext, "efficiency"] - *n.links.loc[electric_ext, "p_nom_ratio"], - link_p_nom[electric_ext]), - (-n.links.loc[heat_ext, "efficiency"].values, - link_p_nom[heat_ext].values)) + rename = {"Link-ext": "Link"} + lhs = p.loc[:, electric_ext] + p.loc[:, heat_ext] - p_nom.rename(rename).loc[electric_ext] + n.model.add_constraints(lhs <= 0, name='chplink-top_iso_fuel_line_ext') - define_constraints(n, lhs, "=", 0, 'chplink', 'fix_p_nom_ratio') - - #top_iso_fuel_line for extendable - lhs = linexpr((1,link_p[heat_ext]), - (1,link_p[electric_ext].values), - (-1,link_p_nom[electric_ext].values)) - - define_constraints(n, lhs, "<=", 0, 'chplink', 'top_iso_fuel_line_ext') + # top_iso_fuel_line for fixed if not electric_fix.empty: + lhs = p.loc[:, electric_fix] + p.loc[:, heat_fix] + rhs = n.links.p_nom[electric_fix] + n.model.add_constraints(lhs <= rhs, name='chplink-top_iso_fuel_line_fix') - #top_iso_fuel_line for fixed - lhs = linexpr((1,link_p[heat_fix]), - (1,link_p[electric_fix].values)) - - rhs = n.links.loc[electric_fix, "p_nom"].values - - define_constraints(n, lhs, "<=", rhs, 'chplink', 'top_iso_fuel_line_fix') - + # back-pressure if not electric.empty: + lhs = (p.loc[:, heat] * (n.links.efficiency[heat] * n.links.c_b[electric].values) - + p.loc[:, electric] * n.links.efficiency[electric]) + n.model.add_constraints(lhs <= rhs, name='chplink-backpressure') - #backpressure - lhs = linexpr((n.links.loc[electric, "c_b"].values - *n.links.loc[heat, "efficiency"], - link_p[heat]), - (-n.links.loc[electric, "efficiency"].values, - link_p[electric].values)) - - define_constraints(n, lhs, "<=", 0, 'chplink', 'backpressure') - -def basename(x): - return x.split("-2")[0] def add_pipe_retrofit_constraint(n): """Add constraint for retrofitting existing CH4 pipelines to H2 pipelines.""" - gas_pipes_i = n.links.query("carrier == 'gas pipeline' and p_nom_extendable").index h2_retrofitted_i = n.links.query("carrier == 'H2 pipeline retrofitted' and p_nom_extendable").index - if h2_retrofitted_i.empty or gas_pipes_i.empty: return + if h2_retrofitted_i.empty or gas_pipes_i.empty: + return - link_p_nom = get_var(n, "Link", "p_nom") + p_nom = n.model["Link-p_nom"] CH4_per_H2 = 1 / n.config["sector"]["H2_retrofit_capacity_per_CH4"] - fr = "H2 pipeline retrofitted" - to = "gas pipeline" + lhs = p_nom.loc[gas_pipes_i] + CH4_per_H2 * p_nom.loc[h2_retrofitted_i] + rhs = n.links.p_nom[gas_pipes_i].rename_axis("Link-ext") - pipe_capacity = n.links.loc[gas_pipes_i, 'p_nom'].rename(basename) + n.model.add_constraints(lhs == rhs, name='Link-pipe_retrofit') - lhs = linexpr( - (CH4_per_H2, link_p_nom.loc[h2_retrofitted_i].rename(index=lambda x: x.replace(fr, to))), - (1, link_p_nom.loc[gas_pipes_i]) - ) - - lhs.rename(basename, inplace=True) - define_constraints(n, lhs, "=", pipe_capacity, 'Link', 'pipe_retrofit') def add_co2_sequestration_limit(n, sns): @@ -243,37 +214,48 @@ def add_co2_sequestration_limit(n, sns): def extra_functionality(n, snapshots): add_battery_constraints(n) add_pipe_retrofit_constraint(n) - add_co2_sequestration_limit(n, snapshots) + # add_co2_sequestration_limit(n, snapshots) -def solve_network(n, config, opts='', **kwargs): - solver_options = config['solving']['solver'].copy() - solver_name = solver_options.pop('name') - cf_solving = config['solving']['options'] - track_iterations = cf_solving.get('track_iterations', False) - min_iterations = cf_solving.get('min_iterations', 4) - max_iterations = cf_solving.get('max_iterations', 6) - keep_shadowprices = cf_solving.get('keep_shadowprices', True) +def solve_network(n, config, opts="", **kwargs): + solver_options = config["solving"]["solver"].copy() + solver_name = solver_options.pop("name") + cf_solving = config["solving"]["options"] + track_iterations = cf_solving.get("track_iterations", False) + min_iterations = cf_solving.get("min_iterations", 4) + max_iterations = cf_solving.get("max_iterations", 6) # add to network for extra_functionality n.config = config n.opts = opts - if cf_solving.get('skip_iterations', False): - network_lopf(n, solver_name=solver_name, solver_options=solver_options, - extra_functionality=extra_functionality, - keep_shadowprices=keep_shadowprices, **kwargs) + skip_iterations = cf_solving.get("skip_iterations", False) + if not n.lines.s_nom_extendable.any(): + skip_iterations = True + logger.info("No expandable lines found. Skipping iterative solving.") + + if skip_iterations: + n.optimize( + solver_name=solver_name, + solver_options=solver_options, + extra_functionality=extra_functionality, + **kwargs, + ) else: - ilopf(n, solver_name=solver_name, solver_options=solver_options, - track_iterations=track_iterations, - min_iterations=min_iterations, - max_iterations=max_iterations, - extra_functionality=extra_functionality, - keep_shadowprices=keep_shadowprices, - **kwargs) + n.optimize.optimize_transmission_expansion_iteratively( + solver_name=solver_name, + solver_options=solver_options, + track_iterations=track_iterations, + min_iterations=min_iterations, + max_iterations=max_iterations, + extra_functionality=extra_functionality, + **kwargs, + ) + return n + if __name__ == "__main__": if 'snakemake' not in globals(): from helper import mock_snakemake @@ -281,10 +263,10 @@ if __name__ == "__main__": 'solve_network', simpl='', opts="", - clusters="37", + clusters="45", lv=1.0, - sector_opts='168H-T-H-B-I-A-solar+p3-dist1', - planning_horizons="2030", + sector_opts='Co2L0-3H-T-H-B-I-A-solar+p3-dist1', + planning_horizons="2050", ) logging.basicConfig(filename=snakemake.log.python, @@ -306,6 +288,7 @@ if __name__ == "__main__": n = pypsa.Network(snakemake.input.network, override_component_attrs=overrides) n = prepare_network(n, solve_opts) + n.snapshots = n.snapshots[:20] n = solve_network(n, config=snakemake.config, opts=opts, solver_dir=tmpdir, From c25091ce34bec2a6122f7b323e9acb9007634cda Mon Sep 17 00:00:00 2001 From: Fabian Date: Wed, 8 Feb 2023 13:30:10 +0100 Subject: [PATCH 02/18] solve_network.py: switch to linopy interface --- scripts/solve_network.py | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/scripts/solve_network.py b/scripts/solve_network.py index 51aedb79..3f2470cb 100644 --- a/scripts/solve_network.py +++ b/scripts/solve_network.py @@ -1,18 +1,9 @@ """Solve network.""" import pypsa - import numpy as np -import pandas as pd -import xarray as xr - -from pypsa.linopt import get_var, linexpr, define_constraints -from linopy import merge - -from pypsa.linopf import network_lopf, ilopf from vresutils.benchmark import memory_logger - from helper import override_component_attrs, update_config_with_sector_opts import logging @@ -73,15 +64,14 @@ def prepare_network(n, solve_opts=None): df.where(df>solve_opts['clip_p_max_pu'], other=0., inplace=True) if solve_opts.get('load_shedding'): + # intersect between macroeconomic and surveybased willingness to pay + # http://journal.frontiersin.org/article/10.3389/fenrg.2015.00055/full n.add("Carrier", "Load") n.madd("Generator", n.buses.index, " load", bus=n.buses.index, carrier='load', sign=1e-3, # Adjust sign to measure p and p_nom in kW instead of MW marginal_cost=1e2, # Eur/kWh - # intersect between macroeconomic and surveybased - # willingness to pay - # http://journal.frontiersin.org/article/10.3389/fenrg.2015.00055/full p_nom=1e9 # kW ) @@ -188,27 +178,17 @@ def add_co2_sequestration_limit(n, sns): co2_stores = n.stores.loc[n.stores.carrier=='co2 stored'].index - if co2_stores.empty or ('Store', 'e') not in n.variables.index: + if co2_stores.empty or 'Store-e' not in n.model.variables: return - vars_final_co2_stored = get_var(n, 'Store', 'e').loc[sns[-1], co2_stores] - - lhs = linexpr((1, vars_final_co2_stored)).sum() - limit = n.config["sector"].get("co2_sequestration_potential", 200) * 1e6 for o in opts: if not "seq" in o: continue limit = float(o[o.find("seq")+3:]) * 1e6 break - name = 'co2_sequestration_limit' - sense = "<=" - - n.add("GlobalConstraint", name, sense=sense, constant=limit, - type=np.nan, carrier_attribute=np.nan) - - define_constraints(n, lhs, sense, limit, 'GlobalConstraint', - 'mu', axes=pd.Index([name]), spec=name) + n.add("GlobalConstraint", 'co2_sequestration_limit', sense="<=", constant=limit, + type=np.nan, carrier_attribute="co2 stored") def extra_functionality(n, snapshots): @@ -288,7 +268,6 @@ if __name__ == "__main__": n = pypsa.Network(snakemake.input.network, override_component_attrs=overrides) n = prepare_network(n, solve_opts) - n.snapshots = n.snapshots[:20] n = solve_network(n, config=snakemake.config, opts=opts, solver_dir=tmpdir, From 6c57b81a4a7744e6b5907b4b3641a87864152da9 Mon Sep 17 00:00:00 2001 From: Fabian Date: Wed, 16 Nov 2022 14:48:09 +0100 Subject: [PATCH 03/18] config: move solver options in dedicated section test configs: reduce to relative changes CI: use snakemake --configfile options --- .github/workflows/ci.yaml | 6 +- .gitignore | 1 + config.default.yaml | 31 +- scripts/solve_network.py | 5 +- test/config.myopic.yaml | 534 +---------------------------------- test/config.overnight.yaml | 563 +------------------------------------ 6 files changed, 28 insertions(+), 1112 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index da984fd3..e718368f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -103,7 +103,5 @@ jobs: run: | conda activate pypsa-eur conda list - cp test/config.overnight.yaml config.yaml - snakemake -call - cp test/config.myopic.yaml config.yaml - snakemake -call + snakemake -call --configfile test/config.overnight.yaml + snakemake -call --configfile test/config.myopic.yaml diff --git a/.gitignore b/.gitignore index a7049efe..b59afca3 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ gurobi.log /data/Industrial_Database.csv /data/retro/tabula-calculator-calcsetbuilding.csv /data/nuts* +data/gas_network/scigrid-gas/ *.org diff --git a/config.default.yaml b/config.default.yaml index e165e895..05a55c99 100644 --- a/config.default.yaml +++ b/config.default.yaml @@ -386,22 +386,23 @@ solving: solver: name: gurobi - threads: 4 - method: 2 # barrier - crossover: 0 - BarConvTol: 1.e-6 - Seed: 123 - AggFill: 0 - PreDual: 0 - GURO_PAR_BARDENSETHRESH: 200 - #FeasibilityTol: 1.e-6 + options: + threads: 4 + method: 2 # barrier + crossover: 0 + BarConvTol: 1.e-6 + Seed: 123 + AggFill: 0 + PreDual: 0 + GURO_PAR_BARDENSETHRESH: 200 + #FeasibilityTol: 1.e-6 - #name: cplex - #threads: 4 - #lpmethod: 4 # barrier - #solutiontype: 2 # non basic solution, ie no crossover - #barrier_convergetol: 1.e-5 - #feasopt_tolerance: 1.e-6 + #name: cplex + #threads: 4 + #lpmethod: 4 # barrier + #solutiontype: 2 # non basic solution, ie no crossover + #barrier_convergetol: 1.e-5 + #feasopt_tolerance: 1.e-6 mem: 30000 #memory in MB; 20 GB enough for 50+B+I+H2; 100 GB for 181+B+I+H2 diff --git a/scripts/solve_network.py b/scripts/solve_network.py index 93442a41..63b073bd 100644 --- a/scripts/solve_network.py +++ b/scripts/solve_network.py @@ -247,8 +247,8 @@ def extra_functionality(n, snapshots): def solve_network(n, config, opts='', **kwargs): - solver_options = config['solving']['solver'].copy() - solver_name = solver_options.pop('name') + solver_options = config['solving']['solver']['options'] + solver_name = config['solving']['solver']['name'] cf_solving = config['solving']['options'] track_iterations = cf_solving.get('track_iterations', False) min_iterations = cf_solving.get('min_iterations', 4) @@ -290,6 +290,7 @@ if __name__ == "__main__": logging.basicConfig(filename=snakemake.log.python, level=snakemake.config['logging_level']) + print(snakemake.config) update_config_with_sector_opts(snakemake.config, snakemake.wildcards.sector_opts) tmpdir = snakemake.config['solving'].get('tmpdir') diff --git a/test/config.myopic.yaml b/test/config.myopic.yaml index d0a6a918..865204e4 100644 --- a/test/config.myopic.yaml +++ b/test/config.myopic.yaml @@ -1,62 +1,17 @@ -version: 0.6.0 - -logging_level: INFO - -retrieve_sector_databundle: true - -results_dir: results/ -summary_dir: results -costs_dir: ../technology-data/outputs/ run: test-myopic # use this to keep track of runs with different settings foresight: myopic # options are overnight, myopic, perfect (perfect is not yet implemented) -# if you use myopic or perfect foresight, set the investment years in "planning_horizons" below - scenario: - simpl: # only relevant for PyPSA-Eur - - '' lv: # allowed transmission line volume expansion, can be any float >= 1.0 (today) or "opt" - 1.5 clusters: # number of nodes in Europe, any integer between 37 (1 node per country-zone) and several hundred - 5 - opts: # only relevant for PyPSA-Eur - - '' sector_opts: # this is where the main scenario settings are - 191H-T-H-B-I-A-solar+p3-dist1 - # to really understand the options here, look in scripts/prepare_sector_network.py - # Co2Lx specifies the CO2 target in x% of the 1990 values; default will give default (5%); - # Co2L0p25 will give 25% CO2 emissions; Co2Lm0p05 will give 5% negative emissions - # xH is the temporal resolution; 3H is 3-hourly, i.e. one snapshot every 3 hours - # single letters are sectors: T for land transport, H for building heating, - # B for biomass supply, I for industry, shipping and aviation, - # A for agriculture, forestry and fishing - # solar+c0.5 reduces the capital cost of solar to 50\% of reference value - # solar+p3 multiplies the available installable potential by factor 3 - # co2 stored+e2 multiplies the potential of CO2 sequestration by a factor 2 - # dist{n} includes distribution grids with investment cost of n times cost in data/costs.csv - # for myopic/perfect foresight cb states the carbon budget in GtCO2 (cumulative - # emissions throughout the transition path in the timeframe determined by the - # planning_horizons), be:beta decay; ex:exponential decay - # cb40ex0 distributes a carbon budget of 40 GtCO2 following an exponential - # decay with initial growth rate 0 planning_horizons: # investment years for myopic and perfect; or costs year for overnight - 2030 - 2040 - 2050 - # for example, set to [2020, 2030, 2040, 2050] for myopic foresight -# CO2 budget as a fraction of 1990 emissions -# this is over-ridden if CO2Lx is set in sector_opts -# this is also over-ridden if cb is set in sector_opts -co2_budget: - 2020: 0.7011648746 - 2025: 0.5241935484 - 2030: 0.2970430108 - 2035: 0.1500896057 - 2040: 0.0712365591 - 2045: 0.0322580645 - 2050: 0 - -# snapshots are originally set in PyPSA-Eur/config.yaml but used again by PyPSA-Eur-Sec snapshots: # arguments to pd.date_range start: "2013-03-01" @@ -66,112 +21,10 @@ snapshots: atlite: cutout: ../pypsa-eur/cutouts/be-03-2013-era5.nc -# this information is NOT used but needed as an argument for -# pypsa-eur/scripts/add_electricity.py/load_costs in make_summary.py -electricity: - max_hours: - battery: 6 - H2: 168 - -# regulate what components with which carriers are kept from PyPSA-Eur; -# some technologies are removed because they are implemented differently -# (e.g. battery or H2 storage) or have different year-dependent costs -# in PyPSA-Eur-Sec -pypsa_eur: - Bus: - - AC - Link: - - DC - Generator: - - onwind - - offwind-ac - - offwind-dc - - solar - - ror - StorageUnit: - - PHS - - hydro - Store: [] - - -energy: - energy_totals_year: 2011 - base_emissions_year: 1990 - eurostat_report_year: 2016 - emissions: CO2 # "CO2" or "All greenhouse gases - (CO2 equivalent)" - -biomass: - year: 2030 - scenario: ENS_Med - classes: - solid biomass: - - Agricultural waste - - Fuelwood residues - - Secondary Forestry residues - woodchips - - Sawdust - - Residues from landscape care - - Municipal waste - not included: - - Sugar from sugar beet - - Rape seed - - "Sunflower, soya seed " - - Bioethanol barley, wheat, grain maize, oats, other cereals and rye - - Miscanthus, switchgrass, RCG - - Willow - - Poplar - - FuelwoodRW - - C&P_RW - biogas: - - Manure solid, liquid - - Sludge - - -solar_thermal: - clearsky_model: simple # should be "simple" or "enhanced"? - orientation: - slope: 45. - azimuth: 180. - -# only relevant for foresight = myopic or perfect existing_capacities: grouping_years: [1980, 1985, 1990, 1995, 2000, 2005, 2010, 2015, 2019] - threshold_capacity: 10 - conventional_carriers: - - lignite - - coal - - oil - - uranium - sector: - district_heating: - potential: 0.6 # maximum fraction of urban demand which can be supplied by district heating - # increase of today's district heating demand to potential maximum district heating share - # progress = 0 means today's district heating share, progress = 1 means maximum fraction of urban demand is supplied by district heating - progress: - 2020: 0.0 - 2030: 0.3 - 2040: 0.6 - 2050: 1.0 - district_heating_loss: 0.15 - bev_dsm_restriction_value: 0.75 #Set to 0 for no restriction on BEV DSM - bev_dsm_restriction_time: 7 #Time at which SOC of BEV has to be dsm_restriction_value - transport_heating_deadband_upper: 20. - transport_heating_deadband_lower: 15. - ICE_lower_degree_factor: 0.375 #in per cent increase in fuel consumption per degree above deadband - ICE_upper_degree_factor: 1.6 - EV_lower_degree_factor: 0.98 - EV_upper_degree_factor: 0.63 - bev_dsm: true #turns on EV battery - bev_availability: 0.5 #How many cars do smart charging - bev_energy: 0.05 #average battery size in MWh - bev_charge_efficiency: 0.9 #BEV (dis-)charging efficiency - bev_plug_to_wheel_efficiency: 0.2 #kWh/km from EPA https://www.fueleconomy.gov/feg/ for Tesla Model S - bev_charge_rate: 0.011 #3-phase charger with 11 kW - bev_avail_max: 0.95 - bev_avail_mean: 0.8 - v2g: true #allows feed-in to grid from EV battery - #what is not EV or FCEV is oil-fuelled ICE land_transport_fuel_cell_share: 2020: 0 2030: 0.05 @@ -182,13 +35,6 @@ sector: 2030: 0.25 2040: 0.6 2050: 0.85 - transport_fuel_cell_efficiency: 0.5 - transport_internal_combustion_efficiency: 0.3 - agriculture_machinery_electric_share: 0 - agriculture_machinery_fuel_efficiency: 0.7 # fuel oil per use - agriculture_machinery_electric_efficiency: 0.3 # electricity per use - shipping_average_efficiency: 0.4 #For conversion of fuel oil to propulsion in 2011 - shipping_hydrogen_liquefaction: false # whether to consider liquefaction costs for shipping H2 demands shipping_hydrogen_share: 2020: 0 2025: 0 @@ -197,14 +43,7 @@ sector: 2040: 0.3 2045: 0.6 2050: 1 - time_dep_hp_cop: true #time dependent heat pump coefficient of performance - heat_pump_sink_T: 55. # Celsius, based on DTU / large area radiators; used in build_cop_profiles.py - # conservatively high to cover hot water and space heating in poorly-insulated buildings - reduce_space_heat_exogenously: true # reduces space heat demand by a given factor (applied before losses in DH) - # this can represent e.g. building renovation, building demolition, or if - # the factor is negative: increasing floor area, increased thermal comfort, population growth - reduce_space_heat_exogenously_factor: # 0.29 # per unit reduction in space heat demand - # the default factors are determined by the LTS scenario from http://tool.european-calculator.eu/app/buildings/building-types-area/?levers=1ddd4444421213bdbbbddd44444ffffff11f411111221111211l212221 + reduce_space_heat_exogenously_factor: 2020: 0.10 # this results in a space heat demand reduction of 10% 2025: 0.09 # first heat demand increases compared to 2020 because of larger floor area per capita 2030: 0.09 @@ -212,63 +51,10 @@ sector: 2040: 0.16 2045: 0.21 2050: 0.29 - retrofitting : # co-optimises building renovation to reduce space heat demand - retro_endogen: false # co-optimise space heat savings - cost_factor: 1.0 # weight costs for building renovation - interest_rate: 0.04 # for investment in building components - annualise_cost: true # annualise the investment costs - tax_weighting: false # weight costs depending on taxes in countries - construction_index: true # weight costs depending on labour/material costs per country - tes: true - tes_tau: # 180 day time constant for centralised, 3 day for decentralised - decentral: 3 - central: 180 - boilers: true - oil_boilers: false - chp: true - micro_chp: false - solar_thermal: true - solar_cf_correction: 0.788457 # = >>> 1/1.2683 - marginal_cost_storage: 0. #1e-4 - methanation: true - helmeth: true - dac: true co2_vent: true - SMR: true - co2_sequestration_potential: 200 #MtCO2/a sequestration potential for Europe - co2_sequestration_cost: 10 #EUR/tCO2 for sequestration of CO2 - co2_network: false - cc_fraction: 0.9 # default fraction of CO2 captured with post-combustion capture - hydrogen_underground_storage: true - hydrogen_underground_storage_locations: - # - onshore # more than 50 km from sea - - nearshore # within 50 km of sea - # - offshore - use_fischer_tropsch_waste_heat: true - use_fuel_cell_waste_heat: true - electricity_distribution_grid: true - electricity_distribution_grid_cost_factor: 1.0 #multiplies cost in data/costs.csv - electricity_grid_connection: true # only applies to onshore wind and utility PV - H2_network: true - gas_network: false - H2_retrofit: false # if set to True existing gas pipes can be retrofitted to H2 pipes - # according to hydrogen backbone strategy (April, 2020) p.15 - # https://gasforclimate2050.eu/wp-content/uploads/2020/07/2020_European-Hydrogen-Backbone_Report.pdf - # 60% of original natural gas capacity could be used in cost-optimal case as H2 capacity - H2_retrofit_capacity_per_CH4: 0.6 # ratio for H2 capacity per original CH4 capacity of retrofitted pipelines - gas_network_connectivity_upgrade: 1 # https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.connectivity.edge_augmentation.k_edge_augmentation.html#networkx.algorithms.connectivity.edge_augmentation.k_edge_augmentation - gas_distribution_grid: true - gas_distribution_grid_cost_factor: 1.0 #multiplies cost in data/costs.csv - biomass_transport: false # biomass transport between nodes - conventional_generation: # generator : carrier - OCGT: gas - biomass_boiler: false - biomass_to_liquid: false - biosng: false - industry: - St_primary_fraction: # 0.3 # fraction of steel produced via primary route versus secondary route (scrap+EAF); today fraction is 0.6 + St_primary_fraction: # fraction of steel produced via primary route versus secondary route (scrap+EAF); today fraction is 0.6 2020: 0.6 2025: 0.55 2030: 0.5 @@ -276,7 +62,7 @@ industry: 2040: 0.4 2045: 0.35 2050: 0.3 - DRI_fraction: # 1 # fraction of the primary route converted to DRI + EAF + DRI_fraction: # fraction of the primary route converted to DRI + EAF 2020: 0 2025: 0 2030: 0.05 @@ -284,9 +70,7 @@ industry: 2040: 0.4 2045: 0.7 2050: 1 - H2_DRI: 1.7 #H2 consumption in Direct Reduced Iron (DRI), MWh_H2,LHV/ton_Steel from 51kgH2/tSt in Vogl et al (2018) doi:10.1016/j.jclepro.2018.08.279 - elec_DRI: 0.322 #electricity consumption in Direct Reduced Iron (DRI) shaft, MWh/tSt HYBRIT brochure https://ssabwebsitecdn.azureedge.net/-/media/hybrit/files/hybrit_brochure.pdf - Al_primary_fraction: # 0.2 # fraction of aluminium produced via the primary route versus scrap; today fraction is 0.4 + Al_primary_fraction: # fraction of aluminium produced via the primary route versus scrap; today fraction is 0.4 2020: 0.4 2025: 0.375 2030: 0.35 @@ -294,318 +78,10 @@ industry: 2040: 0.3 2045: 0.25 2050: 0.2 - MWh_CH4_per_tNH3_SMR: 10.8 # 2012's demand from https://ec.europa.eu/docsroom/documents/4165/attachments/1/translations/en/renditions/pdf - MWh_elec_per_tNH3_SMR: 0.7 # same source, assuming 94-6% split methane-elec of total energy demand 11.5 MWh/tNH3 - MWh_H2_per_tNH3_electrolysis: 6.5 # from https://doi.org/10.1016/j.joule.2018.04.017, around 0.197 tH2/tHN3 (>3/17 since some H2 lost and used for energy) - MWh_elec_per_tNH3_electrolysis: 1.17 # from https://doi.org/10.1016/j.joule.2018.04.017 Table 13 (air separation and HB) - NH3_process_emissions: 24.5 # in MtCO2/a from SMR for H2 production for NH3 from UNFCCC for 2015 for EU28 - petrochemical_process_emissions: 25.5 # in MtCO2/a for petrochemical and other from UNFCCC for 2015 for EU28 - HVC_primary_fraction: 1. # fraction of today's HVC produced via primary route - HVC_mechanical_recycling_fraction: 0. # fraction of today's HVC produced via mechanical recycling - HVC_chemical_recycling_fraction: 0. # fraction of today's HVC produced via chemical recycling - HVC_production_today: 52. # MtHVC/a from DECHEMA (2017), Figure 16, page 107; includes ethylene, propylene and BTX - MWh_elec_per_tHVC_mechanical_recycling: 0.547 # from SI of https://doi.org/10.1016/j.resconrec.2020.105010, Table S5, for HDPE, PP, PS, PET. LDPE would be 0.756. - MWh_elec_per_tHVC_chemical_recycling: 6.9 # Material Economics (2019), page 125; based on pyrolysis and electric steam cracking - chlorine_production_today: 9.58 # MtCl/a from DECHEMA (2017), Table 7, page 43 - MWh_elec_per_tCl: 3.6 # DECHEMA (2017), Table 6, page 43 - MWh_H2_per_tCl: -0.9372 # DECHEMA (2017), page 43; negative since hydrogen produced in chloralkali process - methanol_production_today: 1.5 # MtMeOH/a from DECHEMA (2017), page 62 - MWh_elec_per_tMeOH: 0.167 # DECHEMA (2017), Table 14, page 65 - MWh_CH4_per_tMeOH: 10.25 # DECHEMA (2017), Table 14, page 65 - hotmaps_locate_missing: false - reference_year: 2015 - # references: - # DECHEMA (2017): https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf - # Material Economics (2019): https://materialeconomics.com/latest-updates/industrial-transformation-2050 - -costs: - year: 2030 - lifetime: 25 #default lifetime - # From a Lion Hirth paper, also reflects average of Noothout et al 2016 - discountrate: 0.07 - # [EUR/USD] ECB: https://www.ecb.europa.eu/stats/exchange/eurofxref/html/eurofxref-graph-usd.en.html # noqa: E501 - USD2013_to_EUR2013: 0.7532 - - # Marginal and capital costs can be overwritten - # capital_cost: - # onwind: 500 - marginal_cost: - solar: 0.01 - onwind: 0.015 - offwind: 0.015 - hydro: 0. - H2: 0. - battery: 0. - - emission_prices: # only used with the option Ep (emission prices) - co2: 0. - - lines: - length_factor: 1.25 #to estimate offwind connection costs - solving: - #tmpdir: "path/to/tmp" - options: - formulation: kirchhoff - clip_p_max_pu: 1.e-2 - load_shedding: false - noisy_costs: true - skip_iterations: true - track_iterations: false - min_iterations: 4 - max_iterations: 6 - keep_shadowprices: - - Bus - - Line - - Link - - Transformer - - GlobalConstraint - - Generator - - Store - - StorageUnit - solver: name: cbc - # threads: 4 - # method: 2 # barrier - # crossover: 0 - # BarConvTol: 1.e-6 - # Seed: 123 - # AggFill: 0 - # PreDual: 0 - # GURO_PAR_BARDENSETHRESH: 200 - #FeasibilityTol: 1.e-6 - - #name: cplex - #threads: 4 - #lpmethod: 4 # barrier - #solutiontype: 2 # non basic solution, ie no crossover - #barrier_convergetol: 1.e-5 - #feasopt_tolerance: 1.e-6 + options: [] mem: 4000 #memory in MB; 20 GB enough for 50+B+I+H2; 100 GB for 181+B+I+H2 - -plotting: - map: - boundaries: [-11, 30, 34, 71] - color_geomap: - ocean: white - land: whitesmoke - costs_max: 1000 - costs_threshold: 1 - energy_max: 20000 - energy_min: -20000 - energy_threshold: 50 - vre_techs: - - onwind - - offwind-ac - - offwind-dc - - solar - - ror - renewable_storage_techs: - - PHS - - hydro - conv_techs: - - OCGT - - CCGT - - Nuclear - - Coal - storage_techs: - - hydro+PHS - - battery - - H2 - load_carriers: - - AC load - AC_carriers: - - AC line - - AC transformer - link_carriers: - - DC line - - Converter AC-DC - heat_links: - - heat pump - - resistive heater - - CHP heat - - CHP electric - - gas boiler - - central heat pump - - central resistive heater - - central CHP heat - - central CHP electric - - central gas boiler - heat_generators: - - gas boiler - - central gas boiler - - solar thermal collector - - central solar thermal collector - tech_colors: - # wind - onwind: "#235ebc" - onshore wind: "#235ebc" - offwind: "#6895dd" - offshore wind: "#6895dd" - offwind-ac: "#6895dd" - offshore wind (AC): "#6895dd" - offwind-dc: "#74c6f2" - offshore wind (DC): "#74c6f2" - # water - hydro: '#298c81' - hydro reservoir: '#298c81' - ror: '#3dbfb0' - run of river: '#3dbfb0' - hydroelectricity: '#298c81' - PHS: '#51dbcc' - wave: '#a7d4cf' - # solar - solar: "#f9d002" - solar PV: "#f9d002" - solar thermal: '#ffbf2b' - solar rooftop: '#ffea80' - # gas - OCGT: '#e0986c' - OCGT marginal: '#e0986c' - OCGT-heat: '#e0986c' - gas boiler: '#db6a25' - gas boilers: '#db6a25' - gas boiler marginal: '#db6a25' - gas: '#e05b09' - fossil gas: '#e05b09' - natural gas: '#e05b09' - CCGT: '#a85522' - CCGT marginal: '#a85522' - gas for industry co2 to atmosphere: '#692e0a' - gas for industry co2 to stored: '#8a3400' - gas for industry: '#853403' - gas for industry CC: '#692e0a' - gas pipeline: '#ebbca0' - gas pipeline new: '#a87c62' - # oil - oil: '#c9c9c9' - oil boiler: '#adadad' - agriculture machinery oil: '#949494' - shipping oil: "#808080" - land transport oil: '#afafaf' - # nuclear - Nuclear: '#ff8c00' - Nuclear marginal: '#ff8c00' - nuclear: '#ff8c00' - uranium: '#ff8c00' - # coal - Coal: '#545454' - coal: '#545454' - Coal marginal: '#545454' - solid: '#545454' - Lignite: '#826837' - lignite: '#826837' - Lignite marginal: '#826837' - # biomass - biogas: '#e3d37d' - biomass: '#baa741' - solid biomass: '#baa741' - solid biomass transport: '#baa741' - solid biomass for industry: '#7a6d26' - solid biomass for industry CC: '#47411c' - solid biomass for industry co2 from atmosphere: '#736412' - solid biomass for industry co2 to stored: '#47411c' - # power transmission - lines: '#6c9459' - transmission lines: '#6c9459' - electricity distribution grid: '#97ad8c' - # electricity demand - Electric load: '#110d63' - electric demand: '#110d63' - electricity: '#110d63' - industry electricity: '#2d2a66' - industry new electricity: '#2d2a66' - agriculture electricity: '#494778' - # battery + EVs - battery: '#ace37f' - battery storage: '#ace37f' - home battery: '#80c944' - home battery storage: '#80c944' - BEV charger: '#baf238' - V2G: '#e5ffa8' - land transport EV: '#baf238' - Li ion: '#baf238' - # hot water storage - water tanks: '#e69487' - hot water storage: '#e69487' - hot water charging: '#e69487' - hot water discharging: '#e69487' - # heat demand - Heat load: '#cc1f1f' - heat: '#cc1f1f' - heat demand: '#cc1f1f' - rural heat: '#ff5c5c' - central heat: '#cc1f1f' - decentral heat: '#750606' - low-temperature heat for industry: '#8f2727' - process heat: '#ff0000' - agriculture heat: '#d9a5a5' - # heat supply - heat pumps: '#2fb537' - heat pump: '#2fb537' - air heat pump: '#36eb41' - ground heat pump: '#2fb537' - Ambient: '#98eb9d' - CHP: '#8a5751' - CHP CC: '#634643' - CHP heat: '#8a5751' - CHP electric: '#8a5751' - district heating: '#e8beac' - resistive heater: '#d8f9b8' - retrofitting: '#8487e8' - building retrofitting: '#8487e8' - # hydrogen - H2 for industry: "#f073da" - H2 for shipping: "#ebaee0" - H2: '#bf13a0' - hydrogen: '#bf13a0' - SMR: '#870c71' - SMR CC: '#4f1745' - H2 liquefaction: '#d647bd' - hydrogen storage: '#bf13a0' - H2 storage: '#bf13a0' - land transport fuel cell: '#6b3161' - H2 pipeline: '#f081dc' - H2 pipeline retrofitted: '#ba99b5' - H2 Fuel Cell: '#c251ae' - H2 Electrolysis: '#ff29d9' - # syngas - Sabatier: '#9850ad' - methanation: '#c44ce6' - methane: '#c44ce6' - helmeth: '#e899ff' - # synfuels - Fischer-Tropsch: '#25c49a' - liquid: '#25c49a' - kerosene for aviation: '#a1ffe6' - naphtha for industry: '#57ebc4' - # co2 - CC: '#f29dae' - CCS: '#f29dae' - CO2 sequestration: '#f29dae' - DAC: '#ff5270' - co2 stored: '#f2385a' - co2: '#f29dae' - co2 vent: '#ffd4dc' - CO2 pipeline: '#f5627f' - # emissions - process emissions CC: '#000000' - process emissions: '#222222' - process emissions to stored: '#444444' - process emissions to atmosphere: '#888888' - oil emissions: '#aaaaaa' - shipping oil emissions: "#555555" - land transport oil emissions: '#777777' - agriculture machinery oil emissions: '#333333' - # other - shipping: '#03a2ff' - power-to-heat: '#2fb537' - power-to-gas: '#c44ce6' - power-to-H2: '#ff29d9' - power-to-liquid: '#25c49a' - gas-to-power/heat: '#ee8340' - waste: '#e3d37d' - other: '#000000' diff --git a/test/config.overnight.yaml b/test/config.overnight.yaml index 1dc314dd..21446811 100644 --- a/test/config.overnight.yaml +++ b/test/config.overnight.yaml @@ -1,60 +1,16 @@ -version: 0.6.0 - -logging_level: INFO - -retrieve_sector_databundle: true - -results_dir: results/ -summary_dir: results -costs_dir: ../technology-data/outputs/ run: test-overnight # use this to keep track of runs with different settings -foresight: overnight # options are overnight, myopic, perfect (perfect is not yet implemented) -# if you use myopic or perfect foresight, set the investment years in "planning_horizons" below scenario: - simpl: # only relevant for PyPSA-Eur - - '' lv: # allowed transmission line volume expansion, can be any float >= 1.0 (today) or "opt" - 1.5 clusters: # number of nodes in Europe, any integer between 37 (1 node per country-zone) and several hundred - 5 - opts: # only relevant for PyPSA-Eur - - '' sector_opts: # this is where the main scenario settings are - CO2L0-191H-T-H-B-I-A-solar+p3-dist1 - # to really understand the options here, look in scripts/prepare_sector_network.py - # Co2Lx specifies the CO2 target in x% of the 1990 values; default will give default (5%); - # Co2L0p25 will give 25% CO2 emissions; Co2Lm0p05 will give 5% negative emissions - # xH is the temporal resolution; 3H is 3-hourly, i.e. one snapshot every 3 hours - # single letters are sectors: T for land transport, H for building heating, - # B for biomass supply, I for industry, shipping and aviation, - # A for agriculture, forestry and fishing - # solar+c0.5 reduces the capital cost of solar to 50\% of reference value - # solar+p3 multiplies the available installable potential by factor 3 - # co2 stored+e2 multiplies the potential of CO2 sequestration by a factor 2 - # dist{n} includes distribution grids with investment cost of n times cost in data/costs.csv - # for myopic/perfect foresight cb states the carbon budget in GtCO2 (cumulative - # emissions throughout the transition path in the timeframe determined by the - # planning_horizons), be:beta decay; ex:exponential decay - # cb40ex0 distributes a carbon budget of 40 GtCO2 following an exponential - # decay with initial growth rate 0 planning_horizons: # investment years for myopic and perfect; or costs year for overnight - 2030 # for example, set to [2020, 2030, 2040, 2050] for myopic foresight -# CO2 budget as a fraction of 1990 emissions -# this is over-ridden if CO2Lx is set in sector_opts -# this is also over-ridden if cb is set in sector_opts -co2_budget: - 2020: 0.7011648746 - 2025: 0.5241935484 - 2030: 0.2970430108 - 2035: 0.1500896057 - 2040: 0.0712365591 - 2045: 0.0322580645 - 2050: 0 - -# snapshots are originally set in PyPSA-Eur/config.yaml but used again by PyPSA-Eur-Sec snapshots: # arguments to pd.date_range start: "2013-03-01" @@ -64,546 +20,29 @@ snapshots: atlite: cutout: ../pypsa-eur/cutouts/be-03-2013-era5.nc -# this information is NOT used but needed as an argument for -# pypsa-eur/scripts/add_electricity.py/load_costs in make_summary.py -electricity: - max_hours: - battery: 6 - H2: 168 - -# regulate what components with which carriers are kept from PyPSA-Eur; -# some technologies are removed because they are implemented differently -# (e.g. battery or H2 storage) or have different year-dependent costs -# in PyPSA-Eur-Sec -pypsa_eur: - Bus: - - AC - Link: - - DC - Generator: - - onwind - - offwind-ac - - offwind-dc - - solar - - ror - StorageUnit: - - PHS - - hydro - Store: [] - - -energy: - energy_totals_year: 2011 - base_emissions_year: 1990 - eurostat_report_year: 2016 - emissions: CO2 # "CO2" or "All greenhouse gases - (CO2 equivalent)" - -biomass: - year: 2030 - scenario: ENS_Med - classes: - solid biomass: - - Agricultural waste - - Fuelwood residues - - Secondary Forestry residues - woodchips - - Sawdust - - Residues from landscape care - - Municipal waste - not included: - - Sugar from sugar beet - - Rape seed - - "Sunflower, soya seed " - - Bioethanol barley, wheat, grain maize, oats, other cereals and rye - - Miscanthus, switchgrass, RCG - - Willow - - Poplar - - FuelwoodRW - - C&P_RW - biogas: - - Manure solid, liquid - - Sludge - - -solar_thermal: - clearsky_model: simple # should be "simple" or "enhanced"? - orientation: - slope: 45. - azimuth: 180. - -# only relevant for foresight = myopic or perfect -existing_capacities: - grouping_years: [1980, 1985, 1990, 1995, 2000, 2005, 2010, 2015, 2019] - threshold_capacity: 10 - conventional_carriers: - - lignite - - coal - - oil - - uranium - sector: district_heating: - potential: 0.6 # maximum fraction of urban demand which can be supplied by district heating - # increase of today's district heating demand to potential maximum district heating share - # progress = 0 means today's district heating share, progress = 1 means maximum fraction of urban demand is supplied by district heating progress: 1 - # 2020: 0.0 - # 2030: 0.3 - # 2040: 0.6 - # 2050: 1.0 - district_heating_loss: 0.15 - bev_dsm_restriction_value: 0.75 #Set to 0 for no restriction on BEV DSM - bev_dsm_restriction_time: 7 #Time at which SOC of BEV has to be dsm_restriction_value - transport_heating_deadband_upper: 20. - transport_heating_deadband_lower: 15. - ICE_lower_degree_factor: 0.375 #in per cent increase in fuel consumption per degree above deadband - ICE_upper_degree_factor: 1.6 - EV_lower_degree_factor: 0.98 - EV_upper_degree_factor: 0.63 - bev_dsm: true #turns on EV battery - bev_availability: 0.5 #How many cars do smart charging - bev_energy: 0.05 #average battery size in MWh - bev_charge_efficiency: 0.9 #BEV (dis-)charging efficiency - bev_plug_to_wheel_efficiency: 0.2 #kWh/km from EPA https://www.fueleconomy.gov/feg/ for Tesla Model S - bev_charge_rate: 0.011 #3-phase charger with 11 kW - bev_avail_max: 0.95 - bev_avail_mean: 0.8 - v2g: true #allows feed-in to grid from EV battery - #what is not EV or FCEV is oil-fuelled ICE land_transport_fuel_cell_share: 0.15 # 1 means all FCEVs - # 2020: 0 - # 2030: 0.05 - # 2040: 0.1 - # 2050: 0.15 land_transport_electric_share: 0.85 # 1 means all EVs - # 2020: 0 - # 2030: 0.25 - # 2040: 0.6 - # 2050: 0.85 - transport_fuel_cell_efficiency: 0.5 - transport_internal_combustion_efficiency: 0.3 - agriculture_machinery_electric_share: 0 - agriculture_machinery_fuel_efficiency: 0.7 # fuel oil per use - agriculture_machinery_electric_efficiency: 0.3 # electricity per use - shipping_average_efficiency: 0.4 #For conversion of fuel oil to propulsion in 2011 - shipping_hydrogen_liquefaction: false # whether to consider liquefaction costs for shipping H2 demands shipping_hydrogen_share: 1 # 1 means all hydrogen FC - # 2020: 0 - # 2025: 0 - # 2030: 0.05 - # 2035: 0.15 - # 2040: 0.3 - # 2045: 0.6 - # 2050: 1 - time_dep_hp_cop: true #time dependent heat pump coefficient of performance - heat_pump_sink_T: 55. # Celsius, based on DTU / large area radiators; used in build_cop_profiles.py - # conservatively high to cover hot water and space heating in poorly-insulated buildings - reduce_space_heat_exogenously: true # reduces space heat demand by a given factor (applied before losses in DH) - # this can represent e.g. building renovation, building demolition, or if - # the factor is negative: increasing floor area, increased thermal comfort, population growth reduce_space_heat_exogenously_factor: 0.29 # per unit reduction in space heat demand - # the default factors are determined by the LTS scenario from http://tool.european-calculator.eu/app/buildings/building-types-area/?levers=1ddd4444421213bdbbbddd44444ffffff11f411111221111211l212221 - # 2020: 0.10 # this results in a space heat demand reduction of 10% - # 2025: 0.09 # first heat demand increases compared to 2020 because of larger floor area per capita - # 2030: 0.09 - # 2035: 0.11 - # 2040: 0.16 - # 2045: 0.21 - # 2050: 0.29 - retrofitting : # co-optimises building renovation to reduce space heat demand - retro_endogen: false # co-optimise space heat savings - cost_factor: 1.0 # weight costs for building renovation - interest_rate: 0.04 # for investment in building components - annualise_cost: true # annualise the investment costs - tax_weighting: false # weight costs depending on taxes in countries - construction_index: true # weight costs depending on labour/material costs per country - tes: true - tes_tau: # 180 day time constant for centralised, 3 day for decentralised - decentral: 3 - central: 180 - boilers: true - oil_boilers: false - chp: true - micro_chp: false - solar_thermal: true - solar_cf_correction: 0.788457 # = >>> 1/1.2683 - marginal_cost_storage: 0. #1e-4 - methanation: true - helmeth: true - dac: true co2_vent: true - SMR: true - co2_sequestration_potential: 200 #MtCO2/a sequestration potential for Europe - co2_sequestration_cost: 10 #EUR/tCO2 for sequestration of CO2 - co2_network: false - cc_fraction: 0.9 # default fraction of CO2 captured with post-combustion capture - hydrogen_underground_storage: true - hydrogen_underground_storage_locations: - # - onshore # more than 50 km from sea - - nearshore # within 50 km of sea - # - offshore - use_fischer_tropsch_waste_heat: true - use_fuel_cell_waste_heat: true - electricity_distribution_grid: true - electricity_distribution_grid_cost_factor: 1.0 #multiplies cost in data/costs.csv - electricity_grid_connection: true # only applies to onshore wind and utility PV - H2_network: true gas_network: true H2_retrofit: true # if set to True existing gas pipes can be retrofitted to H2 pipes - # according to hydrogen backbone strategy (April, 2020) p.15 - # https://gasforclimate2050.eu/wp-content/uploads/2020/07/2020_European-Hydrogen-Backbone_Report.pdf - # 60% of original natural gas capacity could be used in cost-optimal case as H2 capacity - H2_retrofit_capacity_per_CH4: 0.6 # ratio for H2 capacity per original CH4 capacity of retrofitted pipelines - gas_network_connectivity_upgrade: 1 # https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.connectivity.edge_augmentation.k_edge_augmentation.html#networkx.algorithms.connectivity.edge_augmentation.k_edge_augmentation - gas_distribution_grid: true - gas_distribution_grid_cost_factor: 1.0 #multiplies cost in data/costs.csv - biomass_transport: false # biomass transport between nodes - conventional_generation: # generator : carrier - OCGT: gas biomass_boiler: false - biomass_to_liquid: false - biosng: false industry: St_primary_fraction: 0.3 # fraction of steel produced via primary route versus secondary route (scrap+EAF); today fraction is 0.6 - # 2020: 0.6 - # 2025: 0.55 - # 2030: 0.5 - # 2035: 0.45 - # 2040: 0.4 - # 2045: 0.35 - # 2050: 0.3 DRI_fraction: 1 # fraction of the primary route converted to DRI + EAF - # 2020: 0 - # 2025: 0 - # 2030: 0.05 - # 2035: 0.2 - # 2040: 0.4 - # 2045: 0.7 - # 2050: 1 - H2_DRI: 1.7 #H2 consumption in Direct Reduced Iron (DRI), MWh_H2,LHV/ton_Steel from 51kgH2/tSt in Vogl et al (2018) doi:10.1016/j.jclepro.2018.08.279 - elec_DRI: 0.322 #electricity consumption in Direct Reduced Iron (DRI) shaft, MWh/tSt HYBRIT brochure https://ssabwebsitecdn.azureedge.net/-/media/hybrit/files/hybrit_brochure.pdf Al_primary_fraction: 0.2 # fraction of aluminium produced via the primary route versus scrap; today fraction is 0.4 - # 2020: 0.4 - # 2025: 0.375 - # 2030: 0.35 - # 2035: 0.325 - # 2040: 0.3 - # 2045: 0.25 - # 2050: 0.2 - MWh_CH4_per_tNH3_SMR: 10.8 # 2012's demand from https://ec.europa.eu/docsroom/documents/4165/attachments/1/translations/en/renditions/pdf - MWh_elec_per_tNH3_SMR: 0.7 # same source, assuming 94-6% split methane-elec of total energy demand 11.5 MWh/tNH3 - MWh_H2_per_tNH3_electrolysis: 6.5 # from https://doi.org/10.1016/j.joule.2018.04.017, around 0.197 tH2/tHN3 (>3/17 since some H2 lost and used for energy) - MWh_elec_per_tNH3_electrolysis: 1.17 # from https://doi.org/10.1016/j.joule.2018.04.017 Table 13 (air separation and HB) - NH3_process_emissions: 24.5 # in MtCO2/a from SMR for H2 production for NH3 from UNFCCC for 2015 for EU28 - petrochemical_process_emissions: 25.5 # in MtCO2/a for petrochemical and other from UNFCCC for 2015 for EU28 - HVC_primary_fraction: 1. # fraction of today's HVC produced via primary route - HVC_mechanical_recycling_fraction: 0. # fraction of today's HVC produced via mechanical recycling - HVC_chemical_recycling_fraction: 0. # fraction of today's HVC produced via chemical recycling - HVC_production_today: 52. # MtHVC/a from DECHEMA (2017), Figure 16, page 107; includes ethylene, propylene and BTX - MWh_elec_per_tHVC_mechanical_recycling: 0.547 # from SI of https://doi.org/10.1016/j.resconrec.2020.105010, Table S5, for HDPE, PP, PS, PET. LDPE would be 0.756. - MWh_elec_per_tHVC_chemical_recycling: 6.9 # Material Economics (2019), page 125; based on pyrolysis and electric steam cracking - chlorine_production_today: 9.58 # MtCl/a from DECHEMA (2017), Table 7, page 43 - MWh_elec_per_tCl: 3.6 # DECHEMA (2017), Table 6, page 43 - MWh_H2_per_tCl: -0.9372 # DECHEMA (2017), page 43; negative since hydrogen produced in chloralkali process - methanol_production_today: 1.5 # MtMeOH/a from DECHEMA (2017), page 62 - MWh_elec_per_tMeOH: 0.167 # DECHEMA (2017), Table 14, page 65 - MWh_CH4_per_tMeOH: 10.25 # DECHEMA (2017), Table 14, page 65 - hotmaps_locate_missing: false - reference_year: 2015 - # references: - # DECHEMA (2017): https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf - # Material Economics (2019): https://materialeconomics.com/latest-updates/industrial-transformation-2050 - -costs: - year: 2030 - lifetime: 25 #default lifetime - # From a Lion Hirth paper, also reflects average of Noothout et al 2016 - discountrate: 0.07 - # [EUR/USD] ECB: https://www.ecb.europa.eu/stats/exchange/eurofxref/html/eurofxref-graph-usd.en.html # noqa: E501 - USD2013_to_EUR2013: 0.7532 - - # Marginal and capital costs can be overwritten - # capital_cost: - # onwind: 500 - marginal_cost: - solar: 0.01 - onwind: 0.015 - offwind: 0.015 - hydro: 0. - H2: 0. - battery: 0. - - emission_prices: # only used with the option Ep (emission prices) - co2: 0. - - lines: - length_factor: 1.25 #to estimate offwind connection costs - solving: - #tmpdir: "path/to/tmp" - options: - formulation: kirchhoff - clip_p_max_pu: 1.e-2 - load_shedding: false - noisy_costs: true - skip_iterations: true - track_iterations: false - min_iterations: 4 - max_iterations: 6 - keep_shadowprices: - - Bus - - Line - - Link - - Transformer - - GlobalConstraint - - Generator - - Store - - StorageUnit - solver: name: cbc - # threads: 4 - # method: 2 # barrier - # crossover: 0 - # BarConvTol: 1.e-6 - # Seed: 123 - # AggFill: 0 - # PreDual: 0 - # GURO_PAR_BARDENSETHRESH: 200 - #FeasibilityTol: 1.e-6 - - #name: cplex - #threads: 4 - #lpmethod: 4 # barrier - #solutiontype: 2 # non basic solution, ie no crossover - #barrier_convergetol: 1.e-5 - #feasopt_tolerance: 1.e-6 + options: [] mem: 4000 #memory in MB; 20 GB enough for 50+B+I+H2; 100 GB for 181+B+I+H2 -plotting: - map: - boundaries: [-11, 30, 34, 71] - color_geomap: - ocean: white - land: whitesmoke - costs_max: 1000 - costs_threshold: 1 - energy_max: 20000 - energy_min: -20000 - energy_threshold: 50 - vre_techs: - - onwind - - offwind-ac - - offwind-dc - - solar - - ror - renewable_storage_techs: - - PHS - - hydro - conv_techs: - - OCGT - - CCGT - - Nuclear - - Coal - storage_techs: - - hydro+PHS - - battery - - H2 - load_carriers: - - AC load - AC_carriers: - - AC line - - AC transformer - link_carriers: - - DC line - - Converter AC-DC - heat_links: - - heat pump - - resistive heater - - CHP heat - - CHP electric - - gas boiler - - central heat pump - - central resistive heater - - central CHP heat - - central CHP electric - - central gas boiler - heat_generators: - - gas boiler - - central gas boiler - - solar thermal collector - - central solar thermal collector - tech_colors: - # wind - onwind: "#235ebc" - onshore wind: "#235ebc" - offwind: "#6895dd" - offshore wind: "#6895dd" - offwind-ac: "#6895dd" - offshore wind (AC): "#6895dd" - offwind-dc: "#74c6f2" - offshore wind (DC): "#74c6f2" - # water - hydro: '#298c81' - hydro reservoir: '#298c81' - ror: '#3dbfb0' - run of river: '#3dbfb0' - hydroelectricity: '#298c81' - PHS: '#51dbcc' - wave: '#a7d4cf' - # solar - solar: "#f9d002" - solar PV: "#f9d002" - solar thermal: '#ffbf2b' - solar rooftop: '#ffea80' - # gas - OCGT: '#e0986c' - OCGT marginal: '#e0986c' - OCGT-heat: '#e0986c' - gas boiler: '#db6a25' - gas boilers: '#db6a25' - gas boiler marginal: '#db6a25' - gas: '#e05b09' - fossil gas: '#e05b09' - natural gas: '#e05b09' - CCGT: '#a85522' - CCGT marginal: '#a85522' - gas for industry co2 to atmosphere: '#692e0a' - gas for industry co2 to stored: '#8a3400' - gas for industry: '#853403' - gas for industry CC: '#692e0a' - gas pipeline: '#ebbca0' - gas pipeline new: '#a87c62' - # oil - oil: '#c9c9c9' - oil boiler: '#adadad' - agriculture machinery oil: '#949494' - shipping oil: "#808080" - land transport oil: '#afafaf' - # nuclear - Nuclear: '#ff8c00' - Nuclear marginal: '#ff8c00' - nuclear: '#ff8c00' - uranium: '#ff8c00' - # coal - Coal: '#545454' - coal: '#545454' - Coal marginal: '#545454' - solid: '#545454' - Lignite: '#826837' - lignite: '#826837' - Lignite marginal: '#826837' - # biomass - biogas: '#e3d37d' - biomass: '#baa741' - solid biomass: '#baa741' - solid biomass transport: '#baa741' - solid biomass for industry: '#7a6d26' - solid biomass for industry CC: '#47411c' - solid biomass for industry co2 from atmosphere: '#736412' - solid biomass for industry co2 to stored: '#47411c' - # power transmission - lines: '#6c9459' - transmission lines: '#6c9459' - electricity distribution grid: '#97ad8c' - # electricity demand - Electric load: '#110d63' - electric demand: '#110d63' - electricity: '#110d63' - industry electricity: '#2d2a66' - industry new electricity: '#2d2a66' - agriculture electricity: '#494778' - # battery + EVs - battery: '#ace37f' - battery storage: '#ace37f' - home battery: '#80c944' - home battery storage: '#80c944' - BEV charger: '#baf238' - V2G: '#e5ffa8' - land transport EV: '#baf238' - Li ion: '#baf238' - # hot water storage - water tanks: '#e69487' - hot water storage: '#e69487' - hot water charging: '#e69487' - hot water discharging: '#e69487' - # heat demand - Heat load: '#cc1f1f' - heat: '#cc1f1f' - heat demand: '#cc1f1f' - rural heat: '#ff5c5c' - central heat: '#cc1f1f' - decentral heat: '#750606' - low-temperature heat for industry: '#8f2727' - process heat: '#ff0000' - agriculture heat: '#d9a5a5' - # heat supply - heat pumps: '#2fb537' - heat pump: '#2fb537' - air heat pump: '#36eb41' - ground heat pump: '#2fb537' - Ambient: '#98eb9d' - CHP: '#8a5751' - CHP CC: '#634643' - CHP heat: '#8a5751' - CHP electric: '#8a5751' - district heating: '#e8beac' - resistive heater: '#d8f9b8' - retrofitting: '#8487e8' - building retrofitting: '#8487e8' - # hydrogen - H2 for industry: "#f073da" - H2 for shipping: "#ebaee0" - H2: '#bf13a0' - hydrogen: '#bf13a0' - SMR: '#870c71' - SMR CC: '#4f1745' - H2 liquefaction: '#d647bd' - hydrogen storage: '#bf13a0' - H2 storage: '#bf13a0' - land transport fuel cell: '#6b3161' - H2 pipeline: '#f081dc' - H2 pipeline retrofitted: '#ba99b5' - H2 Fuel Cell: '#c251ae' - H2 Electrolysis: '#ff29d9' - # syngas - Sabatier: '#9850ad' - methanation: '#c44ce6' - methane: '#c44ce6' - helmeth: '#e899ff' - # synfuels - Fischer-Tropsch: '#25c49a' - liquid: '#25c49a' - kerosene for aviation: '#a1ffe6' - naphtha for industry: '#57ebc4' - # co2 - CC: '#f29dae' - CCS: '#f29dae' - CO2 sequestration: '#f29dae' - DAC: '#ff5270' - co2 stored: '#f2385a' - co2: '#f29dae' - co2 vent: '#ffd4dc' - CO2 pipeline: '#f5627f' - # emissions - process emissions CC: '#000000' - process emissions: '#222222' - process emissions to stored: '#444444' - process emissions to atmosphere: '#888888' - oil emissions: '#aaaaaa' - shipping oil emissions: "#555555" - land transport oil emissions: '#777777' - agriculture machinery oil emissions: '#333333' - # other - shipping: '#03a2ff' - power-to-heat: '#2fb537' - power-to-gas: '#c44ce6' - power-to-H2: '#ff29d9' - power-to-liquid: '#25c49a' - gas-to-power/heat: '#ee8340' - waste: '#e3d37d' - other: '#000000' From 69dabc367f84370061cd1edee382b36d15b4d047 Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 17 Nov 2022 16:36:52 +0100 Subject: [PATCH 04/18] ci: fix test configs --- scripts/solve_network.py | 1 - test/config.myopic.yaml | 52 -------------------------------------- test/config.overnight.yaml | 11 -------- 3 files changed, 64 deletions(-) diff --git a/scripts/solve_network.py b/scripts/solve_network.py index 63b073bd..b2201478 100644 --- a/scripts/solve_network.py +++ b/scripts/solve_network.py @@ -290,7 +290,6 @@ if __name__ == "__main__": logging.basicConfig(filename=snakemake.log.python, level=snakemake.config['logging_level']) - print(snakemake.config) update_config_with_sector_opts(snakemake.config, snakemake.wildcards.sector_opts) tmpdir = snakemake.config['solving'].get('tmpdir') diff --git a/test/config.myopic.yaml b/test/config.myopic.yaml index 865204e4..64a4bdcb 100644 --- a/test/config.myopic.yaml +++ b/test/config.myopic.yaml @@ -25,60 +25,8 @@ existing_capacities: grouping_years: [1980, 1985, 1990, 1995, 2000, 2005, 2010, 2015, 2019] sector: - land_transport_fuel_cell_share: - 2020: 0 - 2030: 0.05 - 2040: 0.1 - 2050: 0.15 - land_transport_electric_share: - 2020: 0 - 2030: 0.25 - 2040: 0.6 - 2050: 0.85 - shipping_hydrogen_share: - 2020: 0 - 2025: 0 - 2030: 0.05 - 2035: 0.15 - 2040: 0.3 - 2045: 0.6 - 2050: 1 - reduce_space_heat_exogenously_factor: - 2020: 0.10 # this results in a space heat demand reduction of 10% - 2025: 0.09 # first heat demand increases compared to 2020 because of larger floor area per capita - 2030: 0.09 - 2035: 0.11 - 2040: 0.16 - 2045: 0.21 - 2050: 0.29 co2_vent: true -industry: - St_primary_fraction: # fraction of steel produced via primary route versus secondary route (scrap+EAF); today fraction is 0.6 - 2020: 0.6 - 2025: 0.55 - 2030: 0.5 - 2035: 0.45 - 2040: 0.4 - 2045: 0.35 - 2050: 0.3 - DRI_fraction: # fraction of the primary route converted to DRI + EAF - 2020: 0 - 2025: 0 - 2030: 0.05 - 2035: 0.2 - 2040: 0.4 - 2045: 0.7 - 2050: 1 - Al_primary_fraction: # fraction of aluminium produced via the primary route versus scrap; today fraction is 0.4 - 2020: 0.4 - 2025: 0.375 - 2030: 0.35 - 2035: 0.325 - 2040: 0.3 - 2045: 0.25 - 2050: 0.2 - solving: solver: name: cbc diff --git a/test/config.overnight.yaml b/test/config.overnight.yaml index 21446811..47cad3e2 100644 --- a/test/config.overnight.yaml +++ b/test/config.overnight.yaml @@ -22,23 +22,12 @@ atlite: sector: - district_heating: - progress: 1 - land_transport_fuel_cell_share: 0.15 # 1 means all FCEVs - land_transport_electric_share: 0.85 # 1 means all EVs - shipping_hydrogen_share: 1 # 1 means all hydrogen FC - reduce_space_heat_exogenously_factor: 0.29 # per unit reduction in space heat demand co2_vent: true gas_network: true H2_retrofit: true # if set to True existing gas pipes can be retrofitted to H2 pipes biomass_boiler: false -industry: - St_primary_fraction: 0.3 # fraction of steel produced via primary route versus secondary route (scrap+EAF); today fraction is 0.6 - DRI_fraction: 1 # fraction of the primary route converted to DRI + EAF - Al_primary_fraction: 0.2 # fraction of aluminium produced via the primary route versus scrap; today fraction is 0.4 - solving: solver: name: cbc From 2bb723ef04bc5dcafd12221ba69f94a5d300f265 Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 17 Nov 2022 17:16:10 +0100 Subject: [PATCH 05/18] CI: fix empty dict in configs --- test/config.myopic.yaml | 2 +- test/config.overnight.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/config.myopic.yaml b/test/config.myopic.yaml index 64a4bdcb..4477c343 100644 --- a/test/config.myopic.yaml +++ b/test/config.myopic.yaml @@ -30,6 +30,6 @@ sector: solving: solver: name: cbc - options: [] + options: {} mem: 4000 #memory in MB; 20 GB enough for 50+B+I+H2; 100 GB for 181+B+I+H2 diff --git a/test/config.overnight.yaml b/test/config.overnight.yaml index 47cad3e2..b1f59012 100644 --- a/test/config.overnight.yaml +++ b/test/config.overnight.yaml @@ -31,7 +31,7 @@ sector: solving: solver: name: cbc - options: [] + options: {} mem: 4000 #memory in MB; 20 GB enough for 50+B+I+H2; 100 GB for 181+B+I+H2 From 098e3bb3ddac1e5d9ad534ae423ce12e58bc0fa4 Mon Sep 17 00:00:00 2001 From: Fabian Date: Fri, 18 Nov 2022 08:49:13 +0100 Subject: [PATCH 06/18] ci: format configs --- test/config.myopic.yaml | 2 +- test/config.overnight.yaml | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/test/config.myopic.yaml b/test/config.myopic.yaml index 4477c343..e50bbd77 100644 --- a/test/config.myopic.yaml +++ b/test/config.myopic.yaml @@ -1,4 +1,4 @@ -run: test-myopic # use this to keep track of runs with different settings +run: test-myopic # use this to keep track of runs with different settings foresight: myopic # options are overnight, myopic, perfect (perfect is not yet implemented) scenario: lv: # allowed transmission line volume expansion, can be any float >= 1.0 (today) or "opt" diff --git a/test/config.overnight.yaml b/test/config.overnight.yaml index b1f59012..7dbf8848 100644 --- a/test/config.overnight.yaml +++ b/test/config.overnight.yaml @@ -1,4 +1,4 @@ -run: test-overnight # use this to keep track of runs with different settings +run: test-overnight # use this to keep track of runs with different settings scenario: lv: # allowed transmission line volume expansion, can be any float >= 1.0 (today) or "opt" @@ -20,18 +20,15 @@ snapshots: atlite: cutout: ../pypsa-eur/cutouts/be-03-2013-era5.nc - sector: co2_vent: true gas_network: true - H2_retrofit: true # if set to True existing gas pipes can be retrofitted to H2 pipes + H2_retrofit: true # if set to True existing gas pipes can be retrofitted to H2 pipes biomass_boiler: false - solving: solver: name: cbc options: {} mem: 4000 #memory in MB; 20 GB enough for 50+B+I+H2; 100 GB for 181+B+I+H2 - From 6d3959f36404f996e10132aea74e5f96354342aa Mon Sep 17 00:00:00 2001 From: Fabian Date: Fri, 18 Nov 2022 09:29:15 +0100 Subject: [PATCH 07/18] ci: format configs II --- test/config.myopic.yaml | 2 +- test/config.overnight.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/config.myopic.yaml b/test/config.myopic.yaml index e50bbd77..9de3fa8a 100644 --- a/test/config.myopic.yaml +++ b/test/config.myopic.yaml @@ -30,6 +30,6 @@ sector: solving: solver: name: cbc - options: {} + options: null mem: 4000 #memory in MB; 20 GB enough for 50+B+I+H2; 100 GB for 181+B+I+H2 diff --git a/test/config.overnight.yaml b/test/config.overnight.yaml index 7dbf8848..f0770345 100644 --- a/test/config.overnight.yaml +++ b/test/config.overnight.yaml @@ -29,6 +29,6 @@ sector: solving: solver: name: cbc - options: {} + options: null mem: 4000 #memory in MB; 20 GB enough for 50+B+I+H2; 100 GB for 181+B+I+H2 From faa95c500c6ca7fe61ec17e2e3556c0f7b7e68f5 Mon Sep 17 00:00:00 2001 From: Fabian Date: Fri, 18 Nov 2022 11:22:06 +0100 Subject: [PATCH 08/18] config: modularize solver_options --- config.default.yaml | 43 +++++++++++++++++++++++++++++++------- scripts/plot_network.py | 4 ++++ scripts/solve_network.py | 3 ++- test/config.myopic.yaml | 2 +- test/config.overnight.yaml | 2 +- 5 files changed, 43 insertions(+), 11 deletions(-) diff --git a/config.default.yaml b/config.default.yaml index 05a55c99..c6f8734b 100644 --- a/config.default.yaml +++ b/config.default.yaml @@ -386,7 +386,10 @@ solving: solver: name: gurobi - options: + options: gurobi-default + + solver_options: + gurobi-default: threads: 4 method: 2 # barrier crossover: 0 @@ -395,14 +398,38 @@ solving: AggFill: 0 PreDual: 0 GURO_PAR_BARDENSETHRESH: 200 - #FeasibilityTol: 1.e-6 + seed: 10 # Consistent seed for all plattforms + gurobi-numeric-focus: + name: gurobi + NumericFocus: 3 # Favour numeric stability over speed + method: 2 # barrier + crossover: 0 # do not use crossover + BarHomogeneous: 1 # Use homogeneous barrier if standard does not converge + BarConvTol: 1.e-5 + FeasibilityTol: 1.e-4 + OptimalityTol: 1.e-4 + ObjScale: -0.5 + threads: 8 + Seed: 123 + gurobi-fallback: # Use gurobi defaults + name: gurobi + crossover: 0 + method: 2 # barrier + BarHomogeneous: 1 # Use homogeneous barrier if standard does not converge + BarConvTol: 1.e-5 + FeasibilityTol: 1.e-5 + OptimalityTol: 1.e-5 + Seed: 123 + threads: 8 + cplex-default: + threads: 4 + lpmethod: 4 # barrier + solutiontype: 2 # non basic solution, ie no crossover + barrier_convergetol: 1.e-5 + feasopt_tolerance: 1.e-6 + + cbc-default: {} # Used in CI - #name: cplex - #threads: 4 - #lpmethod: 4 # barrier - #solutiontype: 2 # non basic solution, ie no crossover - #barrier_convergetol: 1.e-5 - #feasopt_tolerance: 1.e-6 mem: 30000 #memory in MB; 20 GB enough for 50+B+I+H2; 100 GB for 181+B+I+H2 diff --git a/scripts/plot_network.py b/scripts/plot_network.py index 53c52187..fdc131c9 100644 --- a/scripts/plot_network.py +++ b/scripts/plot_network.py @@ -62,6 +62,10 @@ def plot_map(network, components=["links", "stores", "storage_units", "generator for comp in components: df_c = getattr(n, comp) + + if df_c.empty: + continue + df_c["nice_group"] = df_c.carrier.map(rename_techs_tyndp) attr = "e_nom_opt" if comp == "stores" else "p_nom_opt" diff --git a/scripts/solve_network.py b/scripts/solve_network.py index b2201478..cad726d2 100644 --- a/scripts/solve_network.py +++ b/scripts/solve_network.py @@ -247,7 +247,8 @@ def extra_functionality(n, snapshots): def solve_network(n, config, opts='', **kwargs): - solver_options = config['solving']['solver']['options'] + options = config['solving']['solver']['options'] + solver_options = config['solving']["solver_options"][options] if options else None solver_name = config['solving']['solver']['name'] cf_solving = config['solving']['options'] track_iterations = cf_solving.get('track_iterations', False) diff --git a/test/config.myopic.yaml b/test/config.myopic.yaml index 9de3fa8a..f643154d 100644 --- a/test/config.myopic.yaml +++ b/test/config.myopic.yaml @@ -30,6 +30,6 @@ sector: solving: solver: name: cbc - options: null + options: cbc-default mem: 4000 #memory in MB; 20 GB enough for 50+B+I+H2; 100 GB for 181+B+I+H2 diff --git a/test/config.overnight.yaml b/test/config.overnight.yaml index f0770345..ee79bb24 100644 --- a/test/config.overnight.yaml +++ b/test/config.overnight.yaml @@ -29,6 +29,6 @@ sector: solving: solver: name: cbc - options: null + options: cbc-default mem: 4000 #memory in MB; 20 GB enough for 50+B+I+H2; 100 GB for 181+B+I+H2 From f4d0415cf0fb4563d3c90305f7a24ad76f716937 Mon Sep 17 00:00:00 2001 From: Fabian Date: Wed, 8 Feb 2023 18:19:31 +0100 Subject: [PATCH 09/18] solve_network: activate co2 seq constraint Snakefile: retrieve data bundle from pypsaeur function --- Snakefile | 6 +++--- scripts/solve_network.py | 22 ++++++++++++---------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Snakefile b/Snakefile index 90fcfb56..58fe149f 100644 --- a/Snakefile +++ b/Snakefile @@ -252,9 +252,9 @@ rule build_biomass_potentials: enspreso_biomass=HTTP.remote("https://cidportal.jrc.ec.europa.eu/ftp/jrc-opendata/ENSPRESO/ENSPRESO_BIOMASS.xlsx", keep_local=True), nuts2="data/nuts/NUTS_RG_10M_2013_4326_LEVL_2.geojson", # https://gisco-services.ec.europa.eu/distribution/v2/nuts/download/#nuts21 regions_onshore=pypsaeur("resources/regions_onshore_elec_s{simpl}_{clusters}.geojson"), - nuts3_population="../pypsa-eur/data/bundle/nama_10r_3popgdp.tsv.gz", - swiss_cantons="../pypsa-eur/data/bundle/ch_cantons.csv", - swiss_population="../pypsa-eur/data/bundle/je-e-21.03.02.xls", + nuts3_population=pypsaeur("data/bundle/nama_10r_3popgdp.tsv.gz"), + swiss_cantons=pypsaeur("data/bundle/ch_cantons.csv"), + swiss_population=pypsaeur("data/bundle/je-e-21.03.02.xls"), country_shapes=pypsaeur('resources/country_shapes.geojson') output: biomass_potentials_all='resources/biomass_potentials_all_s{simpl}_{clusters}.csv', diff --git a/scripts/solve_network.py b/scripts/solve_network.py index e1cfc3d5..f3e51c84 100644 --- a/scripts/solve_network.py +++ b/scripts/solve_network.py @@ -100,16 +100,18 @@ def prepare_network(n, solve_opts=None): def add_battery_constraints(n): """ - Add constraints to ensure that the ratio between the charger and - discharger. - 1 * charger_size - efficiency * discharger_size = 0 + Add constraint ensuring that charger = discharger: + 1 * charger_size - efficiency * discharger_size = 0 """ - nodes = n.buses.index[n.buses.carrier == "battery"] - if nodes.empty: - return - link_p_nom = n.model["Link-p_nom"] - eff = n.links.efficiency[nodes + " discharger"].values - lhs = link_p_nom.loc[nodes + ' charger'] - link_p_nom.loc[nodes + ' discharger'] * eff + discharger_bool = n.links.index.str.contains("battery discharger") + charger_bool = n.links.index.str.contains("battery charger") + + dischargers_ext= n.links[discharger_bool].query("p_nom_extendable").index + chargers_ext= n.links[charger_bool].query("p_nom_extendable").index + + eff = n.links.efficiency[dischargers_ext].values + lhs = n.model["Link-p_nom"].loc[chargers_ext] - n.model["Link-p_nom"].loc[dischargers_ext] * eff + n.model.add_constraints(lhs == 0, name="Link-charger_ratio") @@ -194,7 +196,7 @@ def add_co2_sequestration_limit(n, sns): def extra_functionality(n, snapshots): add_battery_constraints(n) add_pipe_retrofit_constraint(n) - # add_co2_sequestration_limit(n, snapshots) + add_co2_sequestration_limit(n, snapshots) def solve_network(n, config, opts="", **kwargs): From 3f98ccb9e266e1b5abe29d3d880fd5fbfd25172a Mon Sep 17 00:00:00 2001 From: Fabian Date: Wed, 8 Feb 2023 22:58:56 +0100 Subject: [PATCH 10/18] update technology data version --- config.default.yaml | 2 +- scripts/prepare_sector_network.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config.default.yaml b/config.default.yaml index 8a66a451..98ed4277 100644 --- a/config.default.yaml +++ b/config.default.yaml @@ -339,7 +339,7 @@ industry: costs: year: 2030 - version: v0.4.0 + version: v0.5.0 lifetime: 25 #default lifetime # From a Lion Hirth paper, also reflects average of Noothout et al 2016 discountrate: 0.07 diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index 89d774ca..a4e9f790 100644 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -997,7 +997,7 @@ def add_storage_and_grids(n, costs): ) # hydrogen stored overground (where not already underground) - h2_capital_cost = costs.at["hydrogen storage tank incl. compressor", "fixed"] + h2_capital_cost = costs.at["hydrogen storage tank type 1 including compressor", "fixed"] nodes_overground = h2_caverns.index.symmetric_difference(nodes) n.madd("Store", From 98e3c7ab2c68e42e71b140e8f682f0caff2d148a Mon Sep 17 00:00:00 2001 From: Fabian Date: Wed, 8 Feb 2023 22:59:36 +0100 Subject: [PATCH 11/18] solve_network: fix kwargs in solve_network --- scripts/solve_network.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/scripts/solve_network.py b/scripts/solve_network.py index f3e51c84..3995859c 100644 --- a/scripts/solve_network.py +++ b/scripts/solve_network.py @@ -200,8 +200,8 @@ def extra_functionality(n, snapshots): def solve_network(n, config, opts="", **kwargs): - options = config['solving']['solver']['options'] - solver_options = config['solving']["solver_options"][options] if options else None + set_of_options = config['solving']['solver']['options'] + solver_options = config['solving']["solver_options"][set_of_options] if set_of_options else {} solver_name = config['solving']['solver']['name'] cf_solving = config["solving"]["options"] track_iterations = cf_solving.get("track_iterations", False) @@ -220,18 +220,18 @@ def solve_network(n, config, opts="", **kwargs): if skip_iterations: n.optimize( solver_name=solver_name, - solver_options=solver_options, extra_functionality=extra_functionality, + **solver_options, **kwargs, ) else: n.optimize.optimize_transmission_expansion_iteratively( solver_name=solver_name, - solver_options=solver_options, track_iterations=track_iterations, min_iterations=min_iterations, max_iterations=max_iterations, extra_functionality=extra_functionality, + **solver_options, **kwargs, ) @@ -272,9 +272,7 @@ if __name__ == "__main__": n = prepare_network(n, solve_opts) - n = solve_network(n, config=snakemake.config, opts=opts, - solver_dir=tmpdir, - solver_logfile=snakemake.log.solver) + n = solve_network(n, config=snakemake.config, opts=opts, log_fn=snakemake.log.solver) if "lv_limit" in n.global_constraints.index: n.line_volume_limit = n.global_constraints.at["lv_limit", "constant"] From 46e3b169597a82d8faa524df5a9298dc865d3e31 Mon Sep 17 00:00:00 2001 From: Fabian Date: Wed, 8 Feb 2023 23:11:25 +0100 Subject: [PATCH 12/18] prepare sector: harmonize logger infos --- scripts/prepare_sector_network.py | 33 +++++++++++++++++-------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index a4e9f790..59267fae 100644 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -452,7 +452,9 @@ def remove_elec_base_techs(n): for c in n.iterate_components(snakemake.config["pypsa_eur"]): to_keep = snakemake.config["pypsa_eur"][c.name] to_remove = pd.Index(c.df.carrier.unique()).symmetric_difference(to_keep) - print("Removing", c.list_name, "with carrier", to_remove) + if to_remove.empty: + continue + logger.info(f"Removing {c.list_name} with carrier {list(to_remove)}") names = c.df.index[c.df.carrier.isin(to_remove)] n.mremove(c.name, names) n.carriers.drop(to_remove, inplace=True, errors="ignore") @@ -463,8 +465,10 @@ def remove_non_electric_buses(n): """ remove buses from pypsa-eur with carriers which are not AC buses """ - print("drop buses from PyPSA-Eur with carrier: ", n.buses[~n.buses.carrier.isin(["AC", "DC"])].carrier.unique()) - n.buses = n.buses[n.buses.carrier.isin(["AC", "DC"])] + to_drop = list(n.buses.query("carrier not in ['AC', 'DC']").carrier.unique()) + if to_drop: + logger.info(f"Drop buses from PyPSA-Eur with carrier: {to_drop}") + n.buses = n.buses[n.buses.carrier.isin(["AC", "DC"])] def patch_electricity_network(n): @@ -785,12 +789,12 @@ def insert_electricity_distribution_grid(n, costs): # TODO pop_layout? # TODO options? - print("Inserting electricity distribution grid with investment cost factor of", - options['electricity_distribution_grid_cost_factor']) + cost_factor = options['electricity_distribution_grid_cost_factor'] + + logger.info(f"Inserting electricity distribution grid with investment cost factor of {cost_factor:.2f}") nodes = pop_layout.index - cost_factor = options['electricity_distribution_grid_cost_factor'] n.madd("Bus", nodes + " low voltage", @@ -905,7 +909,7 @@ def insert_gas_distribution_costs(n, costs): f_costs = options['gas_distribution_grid_cost_factor'] - print("Inserting gas distribution grid with investment cost factor of", f_costs) + logger.info(f"Inserting gas distribution grid with investment cost factor of {f_costs}") capital_cost = costs.loc['electricity distribution grid']["fixed"] * f_costs @@ -1451,7 +1455,7 @@ def add_heat(n, costs): # exogenously reduce space heat demand if options["reduce_space_heat_exogenously"]: dE = get(options["reduce_space_heat_exogenously_factor"], investment_year) - print(f"assumed space heat reduction of {dE*100} %") + logger.info(f"assumed space heat reduction of {dE:.2%}") for sector in sectors: heat_demand[sector + " space"] = (1 - dE) * heat_demand[sector + " space"] @@ -1811,10 +1815,9 @@ def create_nodes_for_heat_sector(): diff = (urban_fraction * central_fraction) - dist_fraction_node progress = get(options["district_heating"]["progress"], investment_year) dist_fraction_node += diff * progress - print( - "The current district heating share compared to the maximum", - f"possible is increased by a progress factor of\n{progress}", - f"resulting in a district heating share of\n{dist_fraction_node}" + logger.info( + "Increase district heating share by a progress factor of {progress:.2%} " + f"resulting in new average share of {dist_fraction_node.mean():.2%}" ) return nodes, dist_fraction_node, urban_fraction @@ -2586,7 +2589,7 @@ def maybe_adjust_costs_and_potentials(n, opts): else: sel = c.df.carrier.str.contains(carrier) c.df.loc[sel,attr] *= factor - print("changing", attr , "for", carrier, "by factor", factor) + logger.info(f"changing {attr} for {carrier} by factor {factor}") # TODO this should rather be a config no wildcard @@ -2831,7 +2834,7 @@ if __name__ == "__main__": for o in opts: if o[:4] == "wave": wave_cost_factor = float(o[4:].replace("p", ".").replace("m", "-")) - print("Including wave generators with cost factor of", wave_cost_factor) + logger.info(f"Including wave generators with cost factor of {wave_cost_factor}") add_wave(n, wave_cost_factor) if o[:4] == "dist": options['electricity_distribution_grid'] = True @@ -2897,7 +2900,7 @@ if __name__ == "__main__": limit = o[o.find("Co2L")+4:] limit = float(limit.replace("p", ".").replace("m", "-")) break - print("Add CO2 limit from", limit_type) + logger.info(f"Add CO2 limit from {limit_type}") add_co2limit(n, Nyears, limit) for o in opts: From d36629a836f4cea23606946b4e25a28ebcc2b14e Mon Sep 17 00:00:00 2001 From: Fabian Date: Wed, 8 Feb 2023 23:58:24 +0100 Subject: [PATCH 13/18] add_brownfield: remove basename import --- scripts/add_brownfield.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/add_brownfield.py b/scripts/add_brownfield.py index 87cb8947..8a8f0af3 100644 --- a/scripts/add_brownfield.py +++ b/scripts/add_brownfield.py @@ -12,7 +12,6 @@ import numpy as np from add_existing_baseyear import add_build_year_to_new_assets from helper import override_component_attrs, update_config_with_sector_opts -from solve_network import basename def add_brownfield(n, n_p, year): @@ -99,7 +98,7 @@ def add_brownfield(n, n_p, year): pipe_capacity = n.links.loc[gas_pipes_i, 'p_nom'] # already retrofitted capacity from gas -> H2 already_retrofitted = (n.links.loc[h2_retrofitted_fixed_i, 'p_nom'] - .rename(lambda x: basename(x).replace(fr, to)).groupby(level=0).sum()) + .rename(lambda x: x.split("-2")[0].replace(fr, to)).groupby(level=0).sum()) remaining_capacity = pipe_capacity - CH4_per_H2 * already_retrofitted.reindex(index=pipe_capacity.index).fillna(0) n.links.loc[gas_pipes_i, "p_nom"] = remaining_capacity else: From a0c1d321cc7966ec48854a42c8bbeb6d8b61b111 Mon Sep 17 00:00:00 2001 From: Fabian Date: Wed, 8 Feb 2023 23:58:56 +0100 Subject: [PATCH 14/18] plot_network: fix empty h2 retrofit for myopic --- scripts/plot_network.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/scripts/plot_network.py b/scripts/plot_network.py index fdc131c9..df72bce1 100644 --- a/scripts/plot_network.py +++ b/scripts/plot_network.py @@ -244,7 +244,7 @@ def group_pipes(df, drop_direction=False): axis=1 ) # group pipe lines connecting the same buses and rename them for plotting - pipe_capacity = df["p_nom_opt"].groupby(level=0).sum() + pipe_capacity = df.groupby(level=0).agg({"p_nom_opt": sum, "bus0": "first", "bus1": "first"}) return pipe_capacity @@ -280,13 +280,16 @@ def plot_h2_map(network, regions): # drop all links which are not H2 pipelines n.links.drop(n.links.index[~n.links.carrier.str.contains("H2 pipeline")], inplace=True) - h2_new = n.links.loc[n.links.carrier=="H2 pipeline"] - h2_retro = n.links.loc[n.links.carrier=='H2 pipeline retrofitted'] + h2_new = n.links[n.links.carrier=="H2 pipeline"] + h2_retro = n.links[n.links.carrier=='H2 pipeline retrofitted'] if snakemake.config['foresight'] == 'myopic': # sum capacitiy for pipelines from different investment periods h2_new = group_pipes(h2_new) - h2_retro = group_pipes(h2_retro, drop_direction=True).reindex(h2_new.index).fillna(0) + + if not h2_retro.empty: + h2_retro = group_pipes(h2_retro, drop_direction=True).reindex(h2_new.index).fillna(0) + if not h2_retro.empty: @@ -314,7 +317,7 @@ def plot_h2_map(network, regions): else: - h2_total = h2_new + h2_total = h2_new.p_nom_opt link_widths_total = h2_total / linewidth_factor From a6d359c9b5d8ba2a695da56355a96fe2fe4868e4 Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 9 Feb 2023 17:24:08 +0100 Subject: [PATCH 15/18] update gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index b59afca3..ea7b1a68 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,10 @@ gurobi.log /data/retro/tabula-calculator-calcsetbuilding.csv /data/nuts* data/gas_network/scigrid-gas/ +data/costs_*.csv + +dask-worker-space + *.org From 38a58b62f71ce2199c25cfd231b0ff8dd4f002ca Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 9 Feb 2023 17:53:27 +0100 Subject: [PATCH 16/18] prepare sector: fix logger percentage info --- scripts/prepare_sector_network.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index 59267fae..0173ff57 100644 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -1285,7 +1285,7 @@ def add_land_transport(n, costs): total_share = fuel_cell_share + electric_share + ice_share if total_share != 1: - logger.warning(f"Total land transport shares sum up to {total_share*100}%, corresponding to increased or decreased demand assumptions.") + logger.warning(f"Total land transport shares sum up to {total_share:.2%}, corresponding to increased or decreased demand assumptions.") logger.info(f"FCEV share: {fuel_cell_share*100}%") logger.info(f"EV share: {electric_share*100}%") @@ -1816,7 +1816,7 @@ def create_nodes_for_heat_sector(): progress = get(options["district_heating"]["progress"], investment_year) dist_fraction_node += diff * progress logger.info( - "Increase district heating share by a progress factor of {progress:.2%} " + f"Increase district heating share by a progress factor of {progress:.2%} " f"resulting in new average share of {dist_fraction_node.mean():.2%}" ) @@ -2155,7 +2155,7 @@ def add_industry(n, costs): total_share = shipping_hydrogen_share + shipping_methanol_share + shipping_oil_share if total_share != 1: - logger.warning(f"Total shipping shares sum up to {total_share*100}%, corresponding to increased or decreased demand assumptions.") + logger.warning(f"Total shipping shares sum up to {total_share:.2%}, corresponding to increased or decreased demand assumptions.") domestic_navigation = pop_weighted_energy_totals.loc[nodes, "total domestic navigation"].squeeze() international_navigation = pd.read_csv(snakemake.input.shipping_demand, index_col=0).squeeze() @@ -2511,7 +2511,7 @@ def add_agriculture(n, costs): total_share = electric_share + oil_share if total_share != 1: - logger.warning(f"Total agriculture machinery shares sum up to {total_share*100}%, corresponding to increased or decreased demand assumptions.") + logger.warning(f"Total agriculture machinery shares sum up to {total_share:.2%}, corresponding to increased or decreased demand assumptions.") machinery_nodal_energy = pop_weighted_energy_totals.loc[nodes, "total agriculture machinery"] From 7d5d48ac26d551179a2eaf7fe69c0bc394ee0d46 Mon Sep 17 00:00:00 2001 From: Fabian Date: Fri, 10 Feb 2023 18:58:30 +0100 Subject: [PATCH 17/18] solve network: fix sequestration constraint --- scripts/prepare_sector_network.py | 2 ++ scripts/solve_network.py | 44 +++++++++++++++---------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index 0173ff57..98e62c32 100644 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -524,6 +524,8 @@ def add_co2_tracking(n, options): bus=spatial.co2.nodes ) + n.add("Carrier", "co2 stored") + if options['co2_vent']: n.madd("Link", diff --git a/scripts/solve_network.py b/scripts/solve_network.py index 3995859c..5ee5d6da 100644 --- a/scripts/solve_network.py +++ b/scripts/solve_network.py @@ -57,7 +57,22 @@ def _add_land_use_constraint_m(n): n.generators.p_nom_max.clip(lower=0, inplace=True) -def prepare_network(n, solve_opts=None): +def add_co2_sequestration_limit(n, limit=200): + """Add a global constraint on the amount of Mt CO2 that can be sequestered.""" + n.carriers.loc["co2 stored", "co2_absorptions"] = -1 + n.carriers.co2_absorptions = n.carriers.co2_absorptions.fillna(0) + + limit = limit * 1e6 + for o in opts: + if not "seq" in o: continue + limit = float(o[o.find("seq")+3:]) * 1e6 + break + + n.add("GlobalConstraint", 'co2_sequestration_limit', sense="<=", constant=limit, + type="primary_energy", carrier_attribute="co2_absorptions") + + +def prepare_network(n, solve_opts=None, config=None): if 'clip_p_max_pu' in solve_opts: for df in (n.generators_t.p_max_pu, n.generators_t.p_min_pu, n.storage_units_t.inflow): @@ -95,6 +110,10 @@ def prepare_network(n, solve_opts=None): if snakemake.config['foresight'] == 'myopic': add_land_use_constraint(n) + if n.stores.carrier.eq('co2 stored').any(): + limit = config["sector"].get("co2_sequestration_potential", 200) + add_co2_sequestration_limit(n, limit=limit) + return n @@ -111,7 +130,7 @@ def add_battery_constraints(n): eff = n.links.efficiency[dischargers_ext].values lhs = n.model["Link-p_nom"].loc[chargers_ext] - n.model["Link-p_nom"].loc[dischargers_ext] * eff - + n.model.add_constraints(lhs == 0, name="Link-charger_ratio") @@ -175,28 +194,9 @@ def add_pipe_retrofit_constraint(n): n.model.add_constraints(lhs == rhs, name='Link-pipe_retrofit') - -def add_co2_sequestration_limit(n, sns): - - co2_stores = n.stores.loc[n.stores.carrier=='co2 stored'].index - - if co2_stores.empty or 'Store-e' not in n.model.variables: - return - - limit = n.config["sector"].get("co2_sequestration_potential", 200) * 1e6 - for o in opts: - if not "seq" in o: continue - limit = float(o[o.find("seq")+3:]) * 1e6 - break - - n.add("GlobalConstraint", 'co2_sequestration_limit', sense="<=", constant=limit, - type=np.nan, carrier_attribute="co2 stored") - - def extra_functionality(n, snapshots): add_battery_constraints(n) add_pipe_retrofit_constraint(n) - add_co2_sequestration_limit(n, snapshots) def solve_network(n, config, opts="", **kwargs): @@ -270,7 +270,7 @@ if __name__ == "__main__": overrides = override_component_attrs(snakemake.input.overrides) n = pypsa.Network(snakemake.input.network, override_component_attrs=overrides) - n = prepare_network(n, solve_opts) + n = prepare_network(n, solve_opts, config=snakemake.config) n = solve_network(n, config=snakemake.config, opts=opts, log_fn=snakemake.log.solver) From 7e870444249bebbe2d8d527337b8007a84f01739 Mon Sep 17 00:00:00 2001 From: Fabian Date: Tue, 21 Feb 2023 21:58:42 +0100 Subject: [PATCH 18/18] solve_network: warn if network is not optimal --- scripts/solve_network.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/solve_network.py b/scripts/solve_network.py index 5ee5d6da..7e42838e 100644 --- a/scripts/solve_network.py +++ b/scripts/solve_network.py @@ -218,14 +218,14 @@ def solve_network(n, config, opts="", **kwargs): logger.info("No expandable lines found. Skipping iterative solving.") if skip_iterations: - n.optimize( + status, condition = n.optimize( solver_name=solver_name, extra_functionality=extra_functionality, **solver_options, **kwargs, ) else: - n.optimize.optimize_transmission_expansion_iteratively( + status, condition = n.optimize.optimize_transmission_expansion_iteratively( solver_name=solver_name, track_iterations=track_iterations, min_iterations=min_iterations, @@ -235,6 +235,9 @@ def solve_network(n, config, opts="", **kwargs): **kwargs, ) + if status != "ok": + logger.warning(f"Solving status '{status}' with termination condition '{condition}'") + return n @@ -246,7 +249,7 @@ if __name__ == "__main__": 'solve_network', simpl='', opts="", - clusters="45", + clusters="5", lv=1.0, sector_opts='Co2L0-3H-T-H-B-I-A-solar+p3-dist1', planning_horizons="2050",