From 9d997fbd790321dee611cd0dbb49683e6ce1cd53 Mon Sep 17 00:00:00 2001 From: Fabian Date: Tue, 28 Jun 2022 10:14:26 +0200 Subject: [PATCH 1/5] generalize conventional attr handling through config --- Snakefile | 3 ++- config.default.yaml | 2 +- ...{nuclear_eafs.csv => nuclear_p_max_pu.csv} | 0 doc/configuration.rst | 13 ++++++++++- doc/release_notes.rst | 6 ++--- scripts/add_electricity.py | 23 +++++++++++-------- 6 files changed, 31 insertions(+), 16 deletions(-) rename data/{nuclear_eafs.csv => nuclear_p_max_pu.csv} (100%) diff --git a/Snakefile b/Snakefile index af2e6e90..22b6107d 100644 --- a/Snakefile +++ b/Snakefile @@ -217,7 +217,8 @@ rule add_electricity: load='resources/load.csv', nuts3_shapes='resources/nuts3_shapes.geojson', **{f"profile_{tech}": f"resources/profile_{tech}.nc" - for tech in config['renewable']} + for tech in config['renewable']}, + **{"conventional_{carrier}_{attrs}": fn for carrier in config.get('conventional', {None: {}}).values() for fn in carrier.values() if str(fn).startswith("data/")}, output: "networks/elec.nc" log: "logs/add_electricity.log" benchmark: "benchmarks/add_electricity" diff --git a/config.default.yaml b/config.default.yaml index 177c5e74..a67562c6 100755 --- a/config.default.yaml +++ b/config.default.yaml @@ -187,7 +187,7 @@ renewable: conventional: nuclear: - energy_availability_factors: "data/nuclear-eafs.csv" # float of file name + p_max_pu: "data/nuclear_p_max_pu.csv" # float of file name lines: types: diff --git a/data/nuclear_eafs.csv b/data/nuclear_p_max_pu.csv similarity index 100% rename from data/nuclear_eafs.csv rename to data/nuclear_p_max_pu.csv diff --git a/doc/configuration.rst b/doc/configuration.rst index 67d25228..c332ea7d 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -171,7 +171,7 @@ Define and specify the ``atlite.Cutout`` used for calculating renewable potentia .. literalinclude:: ../config.default.yaml :language: yaml :start-at: hydro: - :end-before: lines: + :end-before: conventional: .. csv-table:: :header-rows: 1 @@ -180,6 +180,17 @@ Define and specify the ``atlite.Cutout`` used for calculating renewable potentia .. _lines_cf: +``conventional`` +============= + +Define additional generator attribute for conventional carrier types. If a scalar value is given it is applied to all generators. However if a string starting with "data/" is given, the value is interpreted as a path to a csv file with country specific values. Then, the values are read in and applied to all generators of the given carrier in the given country. Note that the value(s) overwrite the existing values in the corresponding section of the ``generators`` dataframe. + +.. literalinclude:: ../config.default.yaml + :language: yaml + :start-at: conventional: + :end-before: lines: + + ``lines`` ============= diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 57c0a2f0..3addf3ab 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -53,11 +53,11 @@ Upcoming Release * Add function to add global constraint on use of gas in :mod:`prepare_network`. This can be activated by including the keyword ``CH4L`` in the ``{opts}`` wildcard which enforces the limit set in ``electricity: gaslimit:`` given in MWh thermal. Alternatively, it is possible to append a number in the `{opts}` wildcard, e.g. `CH4L200` which limits the gas use to 200 TWh thermal. -* Add configuration option to implement Energy Availability Factors (EAFs) for conventional generation technologies. - * A new section ``conventional`` was added to the config file. This section contains configurations for conventional carriers. -* Implement country-specific EAFs for nuclear power plants based on IAEA 2018-2020 reported country averages. These are specified ``data/nuclear_eafs.csv`` and translate to static ``p_max_pu`` values. +* Add configuration option to implement arbitrary generator attributes for conventional generation technologies. + +* Implement country-specific Energy Availability Factors (EAFs) for nuclear power plants based on IAEA 2018-2020 reported country averages. These are specified ``data/nuclear_p_max_pu.csv`` and translate to static ``p_max_pu`` values. * The powerplants that have been shut down before 2021 are filtered out. diff --git a/scripts/add_electricity.py b/scripts/add_electricity.py index ea95fd94..88c2ce66 100755 --- a/scripts/add_electricity.py +++ b/scripts/add_electricity.py @@ -331,17 +331,20 @@ def attach_conventional_generators(n, costs, ppl, conventional_carriers, extenda # Generators with technology affected idx = n.generators.query("carrier == @carrier").index - factors = conventional_config[carrier].get("energy_availability_factors") - if isinstance(factors, float): - # Single value affecting all generators of technology k indiscriminantely of country - n.generators.loc[idx, "p_max_pu"] = factors - elif isinstance(factors, str): - factors = pd.read_csv(factors, index_col=0)['factor'] - # Values affecting generators of technology k country-specific - # First map generator buses to countries; then map countries to p_max_pu - bus_factors = n.buses.country.map(factors) - n.generators.p_max_pu.update(n.generators.loc[idx].bus.map(bus_factors).dropna()) + for key in list(set(conventional_carriers[carrier]) & set(n.generators)): + + values = conventional_config[carrier][key] + + if isinstance(values, str) and str(values).startswith("data/"): + # Values affecting generators of technology k country-specific + # First map generator buses to countries; then map countries to p_max_pu + values = pd.read_csv(values, index_col=0).iloc[:, 0] + bus_values = n.buses.country.map(values) + n.generators[key].update(n.generators.loc[idx].bus.map(bus_values).dropna()) + else: + # Single value affecting all generators of technology k indiscriminantely of country + n.generators.loc[idx, key] = values From 917f41ef21d97635a1ff1aa8ebca2d7ca0a1255d Mon Sep 17 00:00:00 2001 From: Fabian Hofmann Date: Tue, 28 Jun 2022 13:11:08 +0200 Subject: [PATCH 2/5] Update Snakefile Co-authored-by: Fabian Neumann --- Snakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Snakefile b/Snakefile index 22b6107d..c786efdc 100644 --- a/Snakefile +++ b/Snakefile @@ -218,7 +218,7 @@ rule add_electricity: nuts3_shapes='resources/nuts3_shapes.geojson', **{f"profile_{tech}": f"resources/profile_{tech}.nc" for tech in config['renewable']}, - **{"conventional_{carrier}_{attrs}": fn for carrier in config.get('conventional', {None: {}}).values() for fn in carrier.values() if str(fn).startswith("data/")}, + **{f"conventional_{carrier}_{attrs}": fn for carrier in config.get('conventional', {None: {}}).values() for attrs, fn in carrier.items() if str(fn).startswith("data/")}, output: "networks/elec.nc" log: "logs/add_electricity.log" benchmark: "benchmarks/add_electricity" From 522f218eed4cacd8d2d37ea3b034ca6b38e60c52 Mon Sep 17 00:00:00 2001 From: Fabian Date: Tue, 28 Jun 2022 13:15:37 +0200 Subject: [PATCH 3/5] Snakefile: rename attrs to attr in add_electricity input function --- Snakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Snakefile b/Snakefile index c786efdc..9e8d6236 100644 --- a/Snakefile +++ b/Snakefile @@ -218,7 +218,7 @@ rule add_electricity: nuts3_shapes='resources/nuts3_shapes.geojson', **{f"profile_{tech}": f"resources/profile_{tech}.nc" for tech in config['renewable']}, - **{f"conventional_{carrier}_{attrs}": fn for carrier in config.get('conventional', {None: {}}).values() for attrs, fn in carrier.items() if str(fn).startswith("data/")}, + **{f"conventional_{carrier}_{attr}": fn for carrier in config.get('conventional', {None: {}}).values() for attr, fn in carrier.items() if str(fn).startswith("data/")}, output: "networks/elec.nc" log: "logs/add_electricity.log" benchmark: "benchmarks/add_electricity" From 82013fd0816c380e148e4b214408254d84f8c946 Mon Sep 17 00:00:00 2001 From: Fabian Hofmann Date: Tue, 28 Jun 2022 13:16:55 +0200 Subject: [PATCH 4/5] Update scripts/add_electricity.py Co-authored-by: Martha Frysztacki --- scripts/add_electricity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/add_electricity.py b/scripts/add_electricity.py index 88c2ce66..d244a381 100755 --- a/scripts/add_electricity.py +++ b/scripts/add_electricity.py @@ -332,7 +332,7 @@ def attach_conventional_generators(n, costs, ppl, conventional_carriers, extenda # Generators with technology affected idx = n.generators.query("carrier == @carrier").index - for key in list(set(conventional_carriers[carrier]) & set(n.generators)): + for key in list(set(conventional_config[carrier]) & set(n.generators)): values = conventional_config[carrier][key] From 67ac464b6a0b66e012b1dcaae9748c0c5722e0a6 Mon Sep 17 00:00:00 2001 From: Fabian Date: Tue, 28 Jun 2022 16:33:46 +0200 Subject: [PATCH 5/5] add_electricity: use conventional_inputs from snakemake.input for attach_conventional_generators --- Snakefile | 2 +- scripts/add_electricity.py | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Snakefile b/Snakefile index a8d98057..576d1fbb 100644 --- a/Snakefile +++ b/Snakefile @@ -229,7 +229,7 @@ rule add_electricity: nuts3_shapes='resources/nuts3_shapes.geojson', **{f"profile_{tech}": f"resources/profile_{tech}.nc" for tech in config['renewable']}, - **{f"conventional_{carrier}_{attr}": fn for carrier in config.get('conventional', {None: {}}).values() for attr, fn in carrier.items() if str(fn).startswith("data/")}, + **{f"conventional_{carrier}_{attr}": fn for carrier, d in config.get('conventional', {None: {}}).items() for attr, fn in d.items() if str(fn).startswith("data/")}, output: "networks/elec.nc" log: "logs/add_electricity.log" benchmark: "benchmarks/add_electricity" diff --git a/scripts/add_electricity.py b/scripts/add_electricity.py index d244a381..342b12e9 100755 --- a/scripts/add_electricity.py +++ b/scripts/add_electricity.py @@ -302,7 +302,7 @@ def attach_wind_and_solar(n, costs, input_profiles, technologies, extendable_car p_max_pu=ds['profile'].transpose('time', 'bus').to_pandas()) -def attach_conventional_generators(n, costs, ppl, conventional_carriers, extendable_carriers, conventional_config): +def attach_conventional_generators(n, costs, ppl, conventional_carriers, extendable_carriers, conventional_config, conventional_inputs): carriers = set(conventional_carriers) | set(extendable_carriers['Generator']) _add_missing_carriers_from_costs(n, costs, carriers) @@ -332,19 +332,19 @@ def attach_conventional_generators(n, costs, ppl, conventional_carriers, extenda # Generators with technology affected idx = n.generators.query("carrier == @carrier").index - for key in list(set(conventional_config[carrier]) & set(n.generators)): + for attr in list(set(conventional_config[carrier]) & set(n.generators)): - values = conventional_config[carrier][key] + values = conventional_config[carrier][attr] - if isinstance(values, str) and str(values).startswith("data/"): + if f"conventional_{carrier}_{attr}" in conventional_inputs: # Values affecting generators of technology k country-specific # First map generator buses to countries; then map countries to p_max_pu values = pd.read_csv(values, index_col=0).iloc[:, 0] bus_values = n.buses.country.map(values) - n.generators[key].update(n.generators.loc[idx].bus.map(bus_values).dropna()) + n.generators[attr].update(n.generators.loc[idx].bus.map(bus_values).dropna()) else: # Single value affecting all generators of technology k indiscriminantely of country - n.generators.loc[idx, key] = values + n.generators.loc[idx, attr] = values @@ -603,7 +603,8 @@ if __name__ == "__main__": update_transmission_costs(n, costs, snakemake.config['lines']['length_factor']) - attach_conventional_generators(n, costs, ppl, conventional_carriers, extendable_carriers, snakemake.config.get("conventional", {})) + conventional_inputs = {k: v for k, v in snakemake.input.items() if k.startswith("conventional_")} + attach_conventional_generators(n, costs, ppl, conventional_carriers, extendable_carriers, snakemake.config.get("conventional", {}), conventional_inputs) attach_wind_and_solar(n, costs, snakemake.input, renewable_carriers, extendable_carriers, snakemake.config['lines']['length_factor'])