From da27fea66e5962c55624c417ddfb0c1c001cd041 Mon Sep 17 00:00:00 2001 From: Fabrizio Finozzi Date: Fri, 10 Jan 2025 11:17:46 +0100 Subject: [PATCH 01/15] config: add pre-commit yaml --- .pre-commit-config.yaml | 50 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..b57a5a8 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: CC0-1.0 +exclude: "^LICENSES" + +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: check-merge-conflict + - id: check-added-large-files + args: ["--maxkb=2000"] + +# Run ruff to lint and format +- repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.8.6 + hooks: + # Run the linter. + - id: ruff + args: [--fix] + # Run the formatter. + - id: ruff-format + + # Find common spelling mistakes in comments and docstrings +- repo: https://github.com/codespell-project/codespell + rev: v2.3.0 + hooks: + - id: codespell + args: ['--ignore-regex="(\b[A-Z]+\b)"', '--ignore-words-list=fom,appartment,bage,ore,setis,tabacco,berfore,vor,pris'] # Ignore capital case words, e.g. country codes + types_or: [python, rst, markdown] + files: ^(scripts|doc)/ + + # YAML formatting +- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks + rev: v2.14.0 + hooks: + - id: pretty-format-yaml + exclude: pinned\.yaml$ + args: [--autofix, --indent, "2", --preserve-quotes] + + # Format Snakemake rule / workflow files +- repo: https://github.com/snakemake/snakefmt + rev: v0.10.2 + hooks: + - id: snakefmt + + # Check for FSFE REUSE compliance (licensing) +- repo: https://github.com/fsfe/reuse-tool + rev: v5.0.2 + hooks: + - id: reuse From cf7999c96bebb4d4d162a1433a241a3fe84a560c Mon Sep 17 00:00:00 2001 From: Fabrizio Finozzi Date: Fri, 10 Jan 2025 11:20:13 +0100 Subject: [PATCH 02/15] docs: modify release notes --- docs/release_notes.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/release_notes.rst b/docs/release_notes.rst index 34649f0..7185769 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -27,6 +27,8 @@ Release Notes * added energy to power ratio for central water pit storage and central/decentral water tank storage +* add pre-commit + Technology-Data 0.9.2 (30 August 2024) ====================================== From 6073fc9fb2c48c1c698f6440cecce22beec48d11 Mon Sep 17 00:00:00 2001 From: Fabrizio Finozzi Date: Fri, 10 Jan 2025 11:42:22 +0100 Subject: [PATCH 03/15] config: modify environment.yaml --- environment.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/environment.yaml b/environment.yaml index ae4dede..5e27d81 100644 --- a/environment.yaml +++ b/environment.yaml @@ -16,6 +16,10 @@ dependencies: - openpyxl>=3.1.2 - packaging +# Development dependencies + - pre-commit + - ruff + - pip: - tabula-py - currencyconverter From 3dd3e2e6b2b9d63c674fa21c04a6dbb228666c07 Mon Sep 17 00:00:00 2001 From: Fabrizio Finozzi Date: Fri, 10 Jan 2025 11:44:36 +0100 Subject: [PATCH 04/15] config: add ruff.toml --- ruff.toml | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 ruff.toml diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..2d75d20 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,53 @@ +# SPDX-FileCopyrightText: : 2021-2024 The PyPSA-Eur Authors +# +# SPDX-License-Identifier: CC0-1.0 + +extend-include = ['*.ipynb'] + +[lint] +select = [ + 'F', # pyflakes + 'E', # pycodestyle: Error + 'W', # pycodestyle: Warning + 'I', # isort + 'D', # pydocstyle + 'UP', # pyupgrade + # 'ANN', # flake-8 annotations + 'TID', # flake8-tidy-imports + # 'NPY', # numpy + # 'RUF', # ruff +] + +ignore = [ + 'ANN401', # Dynamically typed expressions are forbidden + 'E712', # comparison to False should be 'if cond is False:' or 'if not cond:' + 'E741', # ambiguous variable names + 'D203', # 1 blank line required before class docstring + 'D212', # Multi-line docstring summary should start at the second line + 'D401', # First line should be in imperative mood + ] + + +[lint.per-file-ignores] +# pydocstyle ignores, which could be enabled in future when existing +# issues are fixed +"!**/{xxx.py}" = [ + 'E501', # line too long + 'D100', # Missing docstring in public module + 'D101', # Missing docstring in public class + 'D102', # Missing docstring in public method + 'D103', # Missing docstring in public function + 'D104', # Missing docstring in public package + 'D105', # Missing docstring in magic method + 'D107', # Missing docstring in __init__ + 'D200', # One-line docstring should fit on one line with quotes + 'D202', # No blank lines allowed after function docstring + 'D205', # 1 blank line required between summary line and description + 'D400', # First line should end with a period + 'D404', # First word of the docstring should not be "This + 'D413', # Missing blank line after last section + 'D415', # First line should end with a period, question mark, or exclamation point + 'D417', # Missing argument descriptions in the docstring + # Include once available + # https://github.com/astral-sh/ruff/issues/2310 + ] \ No newline at end of file From a863756b383415e12aab345c85863271100e1e52 Mon Sep 17 00:00:00 2001 From: Fabrizio Finozzi Date: Fri, 10 Jan 2025 12:55:43 +0100 Subject: [PATCH 05/15] code: initial pre-commit changes --- .github/ISSUE_TEMPLATE/bug_report.md | 4 + .github/ISSUE_TEMPLATE/config.yml | 10 +- .github/ISSUE_TEMPLATE/feature_request.md | 4 + .github/dependabot.yml | 4 + .github/pull_request_template.md | 4 + .github/workflows/ci.yaml | 4 + .gitignore | 4 +- .gitignore.save | 4 +- .readthedocs.yml | 4 + .syncignore | 4 + Snakefile | 58 +- config.yaml | 28 +- docs/Makefile | 4 + docs/conf.py | 183 +- environment.yaml | 36 +- latex_tables/tables_in_csv.py | 254 +- latex_tables/tables_in_latex.py | 771 ++-- ruff.toml | 4 +- scripts/_helpers.py | 10 +- scripts/compile_cost_assumptions.py | 3479 ++++++++++------- scripts/convert_pdf_EWG_to_dataframe.py | 125 +- .../convert_pdf_fraunhofer_to_dataframe.py | 32 +- scripts/retrieve_data_from_dea.py | 30 +- 23 files changed, 3017 insertions(+), 2043 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 9c99ce1..b743f83 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: MIT + --- name: Bug report about: Create a report if something doesn't work quite right. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index bb1951c..d7f7900 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,9 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: MIT + blank_issues_enabled: false contact_links: - - name: PyPSA Mailing List - url: https://groups.google.com/forum/#!forum/pypsa - about: Please ask and answer general usage questions here. \ No newline at end of file +- name: PyPSA Mailing List + url: https://groups.google.com/forum/#!forum/pypsa + about: Please ask and answer general usage questions here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 303989a..80bd8f1 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: MIT + --- name: Feature request about: Suggest an idea for this project diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f8f779b..52cab81 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: MIT + # dependabot # Ref: https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file # ------------------------------------------------------------------------------ diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 4b90def..6439393 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: MIT + Closes # (if applicable). ## Changes proposed in this Pull Request diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 11b8733..1749805 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: MIT + name: CI on: [push] diff --git a/.gitignore b/.gitignore index d103c00..8a85749 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ - +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: MIT .snakemake _build diff --git a/.gitignore.save b/.gitignore.save index 7fe1b25..be4a247 100644 --- a/.gitignore.save +++ b/.gitignore.save @@ -1,4 +1,6 @@ - +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: MIT .snakemake _build diff --git a/.readthedocs.yml b/.readthedocs.yml index e52632c..202883e 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: MIT + version: 2 build: diff --git a/.syncignore b/.syncignore index def939b..cbe0dac 100644 --- a/.syncignore +++ b/.syncignore @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: MIT + .snakemake .git .pytest_cache diff --git a/Snakefile b/Snakefile index 1f75cdd..e893fdb 100644 --- a/Snakefile +++ b/Snakefile @@ -4,29 +4,32 @@ configfile: "config.yaml" rule compile_cost_assumptions: input: - inflation_rate = "inputs/prc_hicp_aind__custom_9928419_spreadsheet.xlsx", - pypsa_costs = "inputs/costs_PyPSA.csv", - fraunhofer_costs = "inputs/Fraunhofer_ISE_costs.csv", - fraunhofer_energy_prices = "inputs/Fraunhofer_ISE_energy_prices.csv", - fraunhofer_vehicles_costs = "inputs/Fraunhofer_ISE_vehicles_costs.csv", - EWG_costs = "inputs/EWG_costs.csv", - dea_transport = "inputs/energy_transport_data_sheet_dec_2017.xlsx", - dea_vehicles = "inputs/data_sheets_for_commercial_freight_and_passenger_transport_0.xlsx", - dea_renewable_fuels = "inputs/data_sheets_for_renewable_fuels.xlsx", - dea_storage = "inputs/technology_data_catalogue_for_energy_storage.xlsx", - dea_generation = "inputs/technology_data_for_el_and_dh.xlsx", - dea_heating = "inputs/technologydatafor_heating_installations_marts_2018.xlsx", - dea_industrial = "inputs/technology_data_for_industrial_process_heat.xlsx", - dea_ship = "inputs/data_sheets_for_maritime_commercial_freight_and_passenger_transport.xlsx", - dea_ccts = "inputs/technology_data_for_carbon_capture_transport_storage.xlsx", - pnnl_energy_storage = "inputs/pnnl-energy-storage-database.xlsx", - manual_input = "inputs/manual_input.csv" + inflation_rate="inputs/prc_hicp_aind__custom_9928419_spreadsheet.xlsx", + pypsa_costs="inputs/costs_PyPSA.csv", + fraunhofer_costs="inputs/Fraunhofer_ISE_costs.csv", + fraunhofer_energy_prices="inputs/Fraunhofer_ISE_energy_prices.csv", + fraunhofer_vehicles_costs="inputs/Fraunhofer_ISE_vehicles_costs.csv", + EWG_costs="inputs/EWG_costs.csv", + dea_transport="inputs/energy_transport_data_sheet_dec_2017.xlsx", + dea_vehicles="inputs/data_sheets_for_commercial_freight_and_passenger_transport_0.xlsx", + dea_renewable_fuels="inputs/data_sheets_for_renewable_fuels.xlsx", + dea_storage="inputs/technology_data_catalogue_for_energy_storage.xlsx", + dea_generation="inputs/technology_data_for_el_and_dh.xlsx", + dea_heating="inputs/technologydatafor_heating_installations_marts_2018.xlsx", + dea_industrial="inputs/technology_data_for_industrial_process_heat.xlsx", + dea_ship="inputs/data_sheets_for_maritime_commercial_freight_and_passenger_transport.xlsx", + dea_ccts="inputs/technology_data_for_carbon_capture_transport_storage.xlsx", + pnnl_energy_storage="inputs/pnnl-energy-storage-database.xlsx", + manual_input="inputs/manual_input.csv", output: - expand("outputs/costs_{year}.csv", year = config["years"]) + expand("outputs/costs_{year}.csv", year=config["years"]), threads: 1 - resources: mem=500 - conda: "environment.yaml" - script: "scripts/compile_cost_assumptions.py" + resources: + mem=500, + conda: + "environment.yaml" + script: + "scripts/compile_cost_assumptions.py" # rule convert_fraunhofer: @@ -43,10 +46,13 @@ rule compile_cost_assumptions: rule convert_EWG: input: - EWG = "docu/EWG_LUT_100RE_All_Sectors_Global_Report_2019.pdf" + EWG="docu/EWG_LUT_100RE_All_Sectors_Global_Report_2019.pdf", output: - costs = "inputs/EWG_costs.csv", + costs="inputs/EWG_costs.csv", threads: 1 - resources: mem=500 - conda: "environment.yaml" - script: "scripts/convert_pdf_EWG_to_dataframe.py" + resources: + mem=500, + conda: + "environment.yaml" + script: + "scripts/convert_pdf_EWG_to_dataframe.py" diff --git a/config.yaml b/config.yaml index a73f541..58932e5 100644 --- a/config.yaml +++ b/config.yaml @@ -1,29 +1,33 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: MIT + version: 0.9.2 # considered years for output data -years : [2020, 2025, 2030, 2035, 2040, 2045, 2050] +years: [2020, 2025, 2030, 2035, 2040, 2045, 2050] -expectation : "" # tech data uncertainty, possible options [None, "optimist", "pessimist"] +expectation: "" # tech data uncertainty, possible options [None, "optimist", "pessimist"] #year for EUR outputs -eur_year : 2020 +eur_year: 2020 # add solar from different source -solar_utility_from_vartiaien : false -solar_rooftop_from_etip : false +solar_utility_from_vartiaien: false +solar_rooftop_from_etip: false energy_storage_database: - h2_from_budischak: false # add fuel cell/electrolysis efficiencies from Budischak (DEA assumptions very conservative) - ewg_home_battery: true # add home battery data derived from DEA data and EWG study - pnnl_energy_storage: - add_data: true # add storage data mainly from PNNL - approx_beyond_2030: ["same_as_2030"] # ["geometric_series"] or ["same_as_2030"] + h2_from_budischak: false # add fuel cell/electrolysis efficiencies from Budischak (DEA assumptions very conservative) + ewg_home_battery: true # add home battery data derived from DEA data and EWG study + pnnl_energy_storage: + add_data: true # add storage data mainly from PNNL + approx_beyond_2030: ["same_as_2030"] # ["geometric_series"] or ["same_as_2030"] # remove grid connection costs from DEA for offwind because they are calculated # separately in pypsa-eur -offwind_no_gridcosts : true +offwind_no_gridcosts: true desalination: - salinity: 35 # in PSU (Practical Salinity Unit) = kg/m^3 + salinity: 35 # in PSU (Practical Salinity Unit) = kg/m^3 ndigits: 4 diff --git a/docs/Makefile b/docs/Makefile index be90118..024c9cc 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: MIT + # Makefile for Sphinx documentation # diff --git a/docs/conf.py b/docs/conf.py index df0fe1c..18331ef 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # PyPSA documentation build configuration file, created by # sphinx-quickstart on Tue Jan 5 10:04:42 2016. @@ -13,18 +12,17 @@ # serve to show the default. import os -import shlex import sys # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('../scripts')) +sys.path.insert(0, os.path.abspath("../scripts")) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom @@ -32,11 +30,11 @@ extensions = [ #'sphinx.ext.autodoc', #'sphinx.ext.autosummary', - 'sphinx.ext.intersphinx', - 'sphinx.ext.todo', - 'sphinx.ext.mathjax', - 'sphinx.ext.napoleon', - 'sphinx.ext.graphviz', + "sphinx.ext.intersphinx", + "sphinx.ext.todo", + "sphinx.ext.mathjax", + "sphinx.ext.napoleon", + "sphinx.ext.graphviz", #'sphinxcontrib.programoutput', # for python output #'sphinx.ext.pngmath', #'sphinxcontrib.tikz', @@ -44,28 +42,28 @@ #'sphinx.ext.imgconverter', # for SVG conversion ] -autodoc_default_flags = ['members'] +autodoc_default_flags = ["members"] autosummary_generate = True # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # -- Project information ----------------------------------------------------- -project = u'technology-data' -copyright = u'2020-2024, Marta Victoria (Aarhus University), Kun Zhu (Aarhus University), Elisabeth Zeyen (TUB, KIT), Tom Brown (TUB, KIT)' -author = u'2020-2024 Marta Victoria (Aarhus University), Kun Zhu (Aarhus University), Elisabeth Zeyen (TUB, KIT), Tom Brown (TUB, KIT)' +project = "technology-data" +copyright = "2020-2024, Marta Victoria (Aarhus University), Kun Zhu (Aarhus University), Elisabeth Zeyen (TUB, KIT), Tom Brown (TUB, KIT)" +author = "2020-2024 Marta Victoria (Aarhus University), Kun Zhu (Aarhus University), Elisabeth Zeyen (TUB, KIT), Tom Brown (TUB, KIT)" # The version info for the project you're documenting, acts as replacement for @@ -73,9 +71,9 @@ # built documents. # # The short X.Y version. -version = u'0.9' +version = "0.9" # The full version, including alpha/beta/rc tags. -release = u'0.9.2' +release = "0.9.2" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -86,37 +84,37 @@ # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +# keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True @@ -126,7 +124,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'sphinx_book_theme' +html_theme = "sphinx_book_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -138,23 +136,23 @@ } # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -170,117 +168,123 @@ # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. -#html_extra_path = [] +# html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' -#html_search_language = 'en' +# html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value -#html_search_options = {'type': 'default'} +# html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. -#html_search_scorer = 'scorer.js' +# html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'technologydatadoc' +htmlhelp_basename = "technologydatadoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', - -# Latex figure (float) alignment -#'figure_align': 'htbp', + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + #'preamble': '', + # Latex figure (float) alignment + #'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'technologydata.tex', u'Technology-data Documentation', - u'author', 'manual'), + ( + master_doc, + "technologydata.tex", + "Technology-data Documentation", + "author", + "manual", + ), ] -#Added for rinoh http://www.mos6581.org/rinohtype/quickstart.html -rinoh_documents = [(master_doc, # top-level file (index.rst) - 'technology-data', # output (target.pdf) - 'Technology-data Documentation', # document title - 'author')] # document author +# Added for rinoh http://www.mos6581.org/rinohtype/quickstart.html +rinoh_documents = [ + ( + master_doc, # top-level file (index.rst) + "technology-data", # output (target.pdf) + "Technology-data Documentation", # document title + "author", + ) +] # document author # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output --------------------------------------- @@ -288,12 +292,11 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'technology-data', u'Technology-data Documentation', - [author], 1) + (master_doc, "technology-data", "Technology-data Documentation", [author], 1) ] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------- @@ -302,23 +305,29 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'technology-data', u'Technology-data Documentation', - author, 'technology-data', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "technology-data", + "Technology-data Documentation", + author, + "technology-data", + "One line description of project.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'https://docs.python.org/': None} +intersphinx_mapping = {"https://docs.python.org/": None} diff --git a/environment.yaml b/environment.yaml index 5e27d81..1f6b2a6 100644 --- a/environment.yaml +++ b/environment.yaml @@ -1,25 +1,25 @@ name: technology-data channels: - - conda-forge - - bioconda +- conda-forge +- bioconda dependencies: - - python>=3.8 - - pip +- python>=3.8 +- pip # for running the package - - snakemake-minimal>=8.5 - - pandas>=2.1 - - pypsa - - numpy - - beautifulsoup4 - - xlrd - - scipy - - openpyxl>=3.1.2 - - packaging +- snakemake-minimal>=8.5 +- pandas>=2.1 +- pypsa +- numpy +- beautifulsoup4 +- xlrd +- scipy +- openpyxl>=3.1.2 +- packaging # Development dependencies - - pre-commit - - ruff +- pre-commit +- ruff - - pip: - - tabula-py - - currencyconverter +- pip: + - tabula-py + - currencyconverter diff --git a/latex_tables/tables_in_csv.py b/latex_tables/tables_in_csv.py index 779dfb1..953d2bf 100644 --- a/latex_tables/tables_in_csv.py +++ b/latex_tables/tables_in_csv.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Created on Thu Dec 12 16:10:15 2019 @@ -6,116 +5,138 @@ """ import pandas as pd -import numpy as np - """ Latex table including FOM, efficiencies and lifetimes """ -#write latex table +# write latex table idx = pd.IndexSlice path_out = "latex_tables/costs_2030_nice_names.csv" -costs = pd.read_csv('outputs/costs_2030.csv', - index_col=list(range(2))).sort_index() +costs = pd.read_csv("outputs/costs_2030.csv", index_col=list(range(2))).sort_index() # %% -technologies=['onwind', 'offwind', 'solar-utility', 'solar-rooftop', 'OCGT', - 'hydro', 'ror', 'PHS', - 'central gas CHP', 'central solid biomass CHP', - 'HVDC overhead', 'HVDC inverter pair', - 'battery storage', - 'battery inverter', 'electrolysis', 'fuel cell', - 'hydrogen storage underground', 'hydrogen storage tank type 1', - 'DAC', 'methanation', 'helmeth', - 'central gas boiler', 'decentral gas boiler', - 'central resistive heater', 'decentral resistive heater', - 'central water tank storage', 'decentral water tank storage', - 'water tank charger', - 'decentral air-sourced heat pump', - 'central air-sourced heat pump', - 'decentral ground-sourced heat pump', - 'Gasnetz', 'H2 pipeline', - 'SMR', 'biogas upgrading', - 'micro CHP', - 'decentral solar thermal', 'central solar thermal', - 'electricity distribution grid', 'electricity grid connection', - 'gas storage', 'gas storage charger', 'gas storage discharger', - ] +technologies = [ + "onwind", + "offwind", + "solar-utility", + "solar-rooftop", + "OCGT", + "hydro", + "ror", + "PHS", + "central gas CHP", + "central solid biomass CHP", + "HVDC overhead", + "HVDC inverter pair", + "battery storage", + "battery inverter", + "electrolysis", + "fuel cell", + "hydrogen storage underground", + "hydrogen storage tank type 1", + "DAC", + "methanation", + "helmeth", + "central gas boiler", + "decentral gas boiler", + "central resistive heater", + "decentral resistive heater", + "central water tank storage", + "decentral water tank storage", + "water tank charger", + "decentral air-sourced heat pump", + "central air-sourced heat pump", + "decentral ground-sourced heat pump", + "Gasnetz", + "H2 pipeline", + "SMR", + "biogas upgrading", + "micro CHP", + "decentral solar thermal", + "central solar thermal", + "electricity distribution grid", + "electricity grid connection", + "gas storage", + "gas storage charger", + "gas storage discharger", +] -name={'onwind' : 'Onshore Wind', - 'offwind' : 'Offshore Wind', - 'solar-utility' : 'Solar PV (utility-scale)', - 'solar-rooftop' : 'Solar PV (rooftop)', - 'OCGT': 'OCGT', - 'CCGT': 'CCGT', - 'coal': 'Coal power plant', - 'lignite': 'Lignite', - 'nuclear': 'Nuclear', - 'hydro':'Reservoir hydro', - 'ror':'Run of river', - 'PHS':'PHS', - 'battery inverter': 'Battery inverter', - 'battery storage': 'Battery storage', - 'hydrogen storage underground': 'H$_2$ storage underground', - 'hydrogen storage tank type 1': 'H$_2$ storage tank', - 'electrolysis': 'Electrolysis', - 'fuel cell': 'Fuel cell', - 'methanation': 'Methanation', - 'DAC': 'DAC (direct-air capture)', - 'central gas boiler': 'Gas boiler central', - 'decentral gas boiler': 'Gas boiler decentral', - 'central resistive heater':'Resistive heater central', - 'decentral resistive heater':'Resistive heater decentral', - 'central gas CHP': 'Gas CHP', - 'central coal CHP': 'Coal CHP', - 'biomass CHP':'Biomass CHP', - 'biomass EOP':'Biomass power plant', - 'biomass HOP':'Biomass central heat plant', - 'central water tank storage': 'Water tank storage central', - 'decentral water tank storage': 'Water tank storage decentral', - 'water tank charger': 'Water tank charger/discharger', - 'HVDC overhead':'HVDC overhead', - 'HVDC inverter pair':'HVDC inverter pair', - 'decentral air-sourced heat pump': 'Air-sourced heat pump decentral', - 'central air-sourced heat pump': 'Air-sourced heat pump central', - 'central ground-sourced heat pump': 'Ground-sourced heat pump central', - 'decentral air-sourced heat pump': 'Air-sourced heat pump decentral', - 'decentral ground-sourced heat pump': 'Ground-sourced heat pump decentral', - 'Gasnetz': 'Gas pipeline', - 'micro CHP': 'Micro CHP', - 'central solid biomass CHP': 'Solid biomass CHP central', - 'helmeth': 'Helmeth (Power to SNG, KIT project)', - 'H2 pipeline': 'H2 pipeline', - 'SMR': 'Steam Methane Reforming (SMR)', - 'biogas upgrading': 'Biogas upgrading', - 'decentral solar thermal': 'Solar thermal central', - 'central solar thermal': 'Solar thermal decentral', - 'electricity distribution grid': 'Electricity distribution grid', - 'electricity grid connection': 'Electricity grid connection', - 'gas storage': 'Gas storage (underground cavern)', - 'gas storage charger': 'Gas storage injection', - 'gas storage discharger': 'Gas storage withdrawl', - } +name = { + "onwind": "Onshore Wind", + "offwind": "Offshore Wind", + "solar-utility": "Solar PV (utility-scale)", + "solar-rooftop": "Solar PV (rooftop)", + "OCGT": "OCGT", + "CCGT": "CCGT", + "coal": "Coal power plant", + "lignite": "Lignite", + "nuclear": "Nuclear", + "hydro": "Reservoir hydro", + "ror": "Run of river", + "PHS": "PHS", + "battery inverter": "Battery inverter", + "battery storage": "Battery storage", + "hydrogen storage underground": "H$_2$ storage underground", + "hydrogen storage tank type 1": "H$_2$ storage tank", + "electrolysis": "Electrolysis", + "fuel cell": "Fuel cell", + "methanation": "Methanation", + "DAC": "DAC (direct-air capture)", + "central gas boiler": "Gas boiler central", + "decentral gas boiler": "Gas boiler decentral", + "central resistive heater": "Resistive heater central", + "decentral resistive heater": "Resistive heater decentral", + "central gas CHP": "Gas CHP", + "central coal CHP": "Coal CHP", + "biomass CHP": "Biomass CHP", + "biomass EOP": "Biomass power plant", + "biomass HOP": "Biomass central heat plant", + "central water tank storage": "Water tank storage central", + "decentral water tank storage": "Water tank storage decentral", + "water tank charger": "Water tank charger/discharger", + "HVDC overhead": "HVDC overhead", + "HVDC inverter pair": "HVDC inverter pair", + "decentral air-sourced heat pump": "Air-sourced heat pump decentral", + "central air-sourced heat pump": "Air-sourced heat pump central", + "central ground-sourced heat pump": "Ground-sourced heat pump central", + "decentral air-sourced heat pump": "Air-sourced heat pump decentral", + "decentral ground-sourced heat pump": "Ground-sourced heat pump decentral", + "Gasnetz": "Gas pipeline", + "micro CHP": "Micro CHP", + "central solid biomass CHP": "Solid biomass CHP central", + "helmeth": "Helmeth (Power to SNG, KIT project)", + "H2 pipeline": "H2 pipeline", + "SMR": "Steam Methane Reforming (SMR)", + "biogas upgrading": "Biogas upgrading", + "decentral solar thermal": "Solar thermal central", + "central solar thermal": "Solar thermal decentral", + "electricity distribution grid": "Electricity distribution grid", + "electricity grid connection": "Electricity grid connection", + "gas storage": "Gas storage (underground cavern)", + "gas storage charger": "Gas storage injection", + "gas storage discharger": "Gas storage withdrawl", +} -dic_ref = {'Technology Data for Energy Plants for Electricity and District heating generation':'DEA_2019', - 'Impact of weighted average cost of capital, capital expenditure, and other parameters on future utility‐scale PV levelised cost of electricity': 'Vartiainen_2019', - 'European PV Technology and Innovation Platform' : 'Vartiainen_2017', - 'Lazard’s Levelized Cost of Energy Analysis - Version 13.0': 'Lazard_2019', - #'budischak2013':'Budischak_2013', - #'NREL http://www.nrel.gov/docs/fy09osti/45873.pdf; budischak2013': 'Steward_2009b, Budischak_2013', - 'Schaber thesis':'Schaber_2013', - 'Hagspiel':'Hagspiel_2014', - 'Fasihi':'Fasihi_2017', - 'HP' : ' ', - 'DIW DataDoc http://hdl.handle.net/10419/80348' : 'Schroeder_2013', - 888 : 'water tank charger', - 'BP 2019':'BP_2019', - 'https://www.eia.gov/environment/emissions/co2_vol_mass.php' : 'EIA_emission_coefficients', - 'DIW': 'Schroeder_2013', - 'IEA2011b' : 'BP_2019', - 'Is a 100% renewable European power system feasible by 2050?': 'Zappa_2019, JRC_biomass', - 'Entwicklung der spezifischen Kohlendioxid-Emissionen des deutschen Strommix in den Jahren 1990 - 2018': 'German_Environment_Agency', +dic_ref = { + "Technology Data for Energy Plants for Electricity and District heating generation": "DEA_2019", + "Impact of weighted average cost of capital, capital expenditure, and other parameters on future utility‐scale PV levelised cost of electricity": "Vartiainen_2019", + "European PV Technology and Innovation Platform": "Vartiainen_2017", + "Lazard’s Levelized Cost of Energy Analysis - Version 13.0": "Lazard_2019", + #'budischak2013':'Budischak_2013', + #'NREL http://www.nrel.gov/docs/fy09osti/45873.pdf; budischak2013': 'Steward_2009b, Budischak_2013', + "Schaber thesis": "Schaber_2013", + "Hagspiel": "Hagspiel_2014", + "Fasihi": "Fasihi_2017", + "HP": " ", + "DIW DataDoc http://hdl.handle.net/10419/80348": "Schroeder_2013", + 888: "water tank charger", + "BP 2019": "BP_2019", + "https://www.eia.gov/environment/emissions/co2_vol_mass.php": "EIA_emission_coefficients", + "DIW": "Schroeder_2013", + "IEA2011b": "BP_2019", + "Is a 100% renewable European power system feasible by 2050?": "Zappa_2019, JRC_biomass", + "Entwicklung der spezifischen Kohlendioxid-Emissionen des deutschen Strommix in den Jahren 1990 - 2018": "German_Environment_Agency", } # Solar thermal collector decentral & 270 & m$^{2}$ & 1.3 & 20 & variable & \cite{Henning20141003} \\ @@ -130,16 +151,17 @@ relevant.loc["DAC", ("value", "investment")] = 210.50 quantities = ["FOM", "VOM", "efficiency", "investment", "lifetime"] table = relevant["value"][quantities] -table["investment"] = (table["investment"].astype(str) + " " - + relevant[("unit", "investment")]) +table["investment"] = ( + table["investment"].astype(str) + " " + relevant[("unit", "investment")] +) table["VOM"] = table["VOM"].astype(str) + " " + relevant[("unit", "VOM")] table.fillna(" ", inplace=True) -table["source"] = relevant["source"][quantities].apply(lambda row: - row.dropna().unique(), - axis=1) -table["source"] = table.source.apply(lambda x: ', '.join(x)) +table["source"] = relevant["source"][quantities].apply( + lambda row: row.dropna().unique(), axis=1 +) +table["source"] = table.source.apply(lambda x: ", ".join(x)) table.source.str.contains("DEA") # shorten source names table.loc[table.source.str.contains("DEA"), "source"] = "DEA" @@ -150,14 +172,20 @@ table.loc[table.source.str.contains("Fraunhofer"), "source"] = "FA ISE" table.rename(index=name, inplace=True) -table.replace({'_th': '$_{th}$', - "_el": "$_{el}$", - "_e": "$_{el}$", - "kWel": "kW$_{el}$", - "kWGas": "kW$_{Gas}$"}, regex=True, inplace=True) +table.replace( + { + "_th": "$_{th}$", + "_el": "$_{el}$", + "_e": "$_{el}$", + "kWel": "kW$_{el}$", + "kWGas": "kW$_{Gas}$", + }, + regex=True, + inplace=True, +) # add costs for gas distribution -table.loc['Gas distribution grid'] = table.loc['Electricity distribution grid'] +table.loc["Gas distribution grid"] = table.loc["Electricity distribution grid"] # save table as csv -table.sort_index().to_csv(path_out) \ No newline at end of file +table.sort_index().to_csv(path_out) diff --git a/latex_tables/tables_in_latex.py b/latex_tables/tables_in_latex.py index 328910f..8b1929d 100644 --- a/latex_tables/tables_in_latex.py +++ b/latex_tables/tables_in_latex.py @@ -1,233 +1,275 @@ -# -*- coding: utf-8 -*- """ Created on Thu Dec 12 16:10:15 2019 @author: Marta """ -#%% -import pandas as pd -import numpy as np + +# %% import os +import numpy as np +import pandas as pd + """ Latex table including FOM, efficiencies and lifetimes """ -#write latex table +# write latex table # read 2020 costs idx = pd.IndexSlice root_path = os.getcwd() -costs = pd.read_csv(os.path.join(root_path, 'outputs', 'costs_2060.csv'),index_col=list(range(2))).sort_index() +costs = pd.read_csv( + os.path.join(root_path, "outputs", "costs_2060.csv"), index_col=list(range(2)) +).sort_index() -filename='table_inputs.tex' +filename = "table_inputs.tex" -file = open(filename, 'w') +file = open(filename, "w") -technologies=['onwind', 'offwind', 'solar-utility', 'solar-rooftop', 'OCGT', - 'CCGT', 'coal', 'lignite', 'nuclear', 'hydro', 'ror', 'PHS', - 'central gas CHP', - 'biomass CHP', - #'central coal CHP', - #'biomass HOP', - #'biomass EOP', - 'HVDC overhead', 'HVDC inverter pair', - 'battery storage', - 'battery inverter', - 'home battery storage', - 'home battery inverter', - 'electrolysis', - 'fuel cell', - 'hydrogen storage underground', - 'hydrogen storage tank type 1', - 'direct air capture', - 'methanation', - 'central gas boiler', - 'decentral gas boiler', - 'central resistive heater', - 'decentral resistive heater', - 'central water tank storage', - 'decentral water tank storage', - 'water tank charger', - 'decentral air-sourced heat pump', - 'central air-sourced heat pump', - 'decentral ground-sourced heat pump', - 'biomass CHP capture', - 'Fischer-Tropsch', - 'SMR', - 'SMR CC', - 'BioSNG', - 'BtL', - 'biogas plus hydrogen', - 'industrial heat pump medium temperature', - 'industrial heat pump high temperature', - 'electric boiler steam', - 'gas boiler steam', - 'solid biomass boiler steam', - 'methanolisation', - 'Compressed-Air-Adiabatic-bicharger', - 'Compressed-Air-Adiabatic-store', 'Concrete-charger', - 'Concrete-discharger', 'Concrete-store', 'Gravity-Brick-bicharger', - 'Gravity-Brick-store', 'Gravity-Water-Aboveground-bicharger', - 'Gravity-Water-Aboveground-store', - 'Gravity-Water-Underground-bicharger', - 'Gravity-Water-Underground-store', 'HighT-Molten-Salt-charger', - 'HighT-Molten-Salt-discharger', 'HighT-Molten-Salt-store', - 'Hydrogen-charger', 'Hydrogen-discharger', 'Hydrogen-store', - 'Lead-Acid-bicharger', 'Lead-Acid-store', 'Liquid-Air-charger', - 'Liquid-Air-discharger', 'Liquid-Air-store', - 'Lithium-Ion-LFP-bicharger', 'Lithium-Ion-LFP-store', - 'Lithium-Ion-NMC-bicharger', 'Lithium-Ion-NMC-store', - 'LowT-Molten-Salt-charger', 'LowT-Molten-Salt-discharger', - 'LowT-Molten-Salt-store', 'Ni-Zn-bicharger', 'Ni-Zn-store', - 'Pumped-Heat-charger', 'Pumped-Heat-discharger', - 'Pumped-Heat-store', 'Pumped-Storage-Hydro-bicharger', - 'Pumped-Storage-Hydro-store', 'Sand-charger', 'Sand-discharger', - 'Sand-store', 'Vanadium-Redox-Flow-bicharger', - 'Vanadium-Redox-Flow-store', 'Zn-Air-bicharger', 'Zn-Air-store', - 'Zn-Br-Flow-bicharger', 'Zn-Br-Flow-store', - 'Zn-Br-Nonflow-bicharger', 'Zn-Br-Nonflow-store' - ] +technologies = [ + "onwind", + "offwind", + "solar-utility", + "solar-rooftop", + "OCGT", + "CCGT", + "coal", + "lignite", + "nuclear", + "hydro", + "ror", + "PHS", + "central gas CHP", + "biomass CHP", + #'central coal CHP', + #'biomass HOP', + #'biomass EOP', + "HVDC overhead", + "HVDC inverter pair", + "battery storage", + "battery inverter", + "home battery storage", + "home battery inverter", + "electrolysis", + "fuel cell", + "hydrogen storage underground", + "hydrogen storage tank type 1", + "direct air capture", + "methanation", + "central gas boiler", + "decentral gas boiler", + "central resistive heater", + "decentral resistive heater", + "central water tank storage", + "decentral water tank storage", + "water tank charger", + "decentral air-sourced heat pump", + "central air-sourced heat pump", + "decentral ground-sourced heat pump", + "biomass CHP capture", + "Fischer-Tropsch", + "SMR", + "SMR CC", + "BioSNG", + "BtL", + "biogas plus hydrogen", + "industrial heat pump medium temperature", + "industrial heat pump high temperature", + "electric boiler steam", + "gas boiler steam", + "solid biomass boiler steam", + "methanolisation", + "Compressed-Air-Adiabatic-bicharger", + "Compressed-Air-Adiabatic-store", + "Concrete-charger", + "Concrete-discharger", + "Concrete-store", + "Gravity-Brick-bicharger", + "Gravity-Brick-store", + "Gravity-Water-Aboveground-bicharger", + "Gravity-Water-Aboveground-store", + "Gravity-Water-Underground-bicharger", + "Gravity-Water-Underground-store", + "HighT-Molten-Salt-charger", + "HighT-Molten-Salt-discharger", + "HighT-Molten-Salt-store", + "Hydrogen-charger", + "Hydrogen-discharger", + "Hydrogen-store", + "Lead-Acid-bicharger", + "Lead-Acid-store", + "Liquid-Air-charger", + "Liquid-Air-discharger", + "Liquid-Air-store", + "Lithium-Ion-LFP-bicharger", + "Lithium-Ion-LFP-store", + "Lithium-Ion-NMC-bicharger", + "Lithium-Ion-NMC-store", + "LowT-Molten-Salt-charger", + "LowT-Molten-Salt-discharger", + "LowT-Molten-Salt-store", + "Ni-Zn-bicharger", + "Ni-Zn-store", + "Pumped-Heat-charger", + "Pumped-Heat-discharger", + "Pumped-Heat-store", + "Pumped-Storage-Hydro-bicharger", + "Pumped-Storage-Hydro-store", + "Sand-charger", + "Sand-discharger", + "Sand-store", + "Vanadium-Redox-Flow-bicharger", + "Vanadium-Redox-Flow-store", + "Zn-Air-bicharger", + "Zn-Air-store", + "Zn-Br-Flow-bicharger", + "Zn-Br-Flow-store", + "Zn-Br-Nonflow-bicharger", + "Zn-Br-Nonflow-store", +] -name={'onwind' : 'Onshore Wind', - 'offwind' : 'Offshore Wind', - 'solar-utility' : 'Solar PV (utility-scale)', - 'solar-rooftop' : 'Solar PV (rooftop)', - 'OCGT': 'OCGT', - 'CCGT': 'CCGT', - 'coal': 'Coal power plant', - 'lignite': 'Lignite', - 'nuclear': 'Nuclear', - 'hydro':'Reservoir hydro', - 'ror':'Run of river', - 'PHS':'PHS', - 'battery inverter': 'Battery inverter', - 'battery storage': 'Battery storage', - 'home battery inverter': 'Home battery inverter', - 'home battery storage': 'Home battery storage', - 'hydrogen storage underground': 'H$_2$ storage underground', - 'hydrogen storage tank type 1': 'H$_2$ storage tank', - 'electrolysis': 'Electrolysis', - 'fuel cell': 'Fuel cell', - 'methanation': 'Methanation', - 'direct air capture': 'direct air capture', - 'central gas boiler': 'Central gas boiler', - 'decentral gas boiler': 'Domestic gas boiler', - 'central resistive heater':'Central resistive heater', - 'decentral resistive heater':'Domestic resistive heater', - 'central gas CHP':' Gas CHP', - 'central coal CHP':' Coal CHP', - 'biomass CHP':'Biomass CHP', - 'biomass EOP':'Biomass power plant', - 'biomass HOP':'Biomass central heat plant', - 'central water tank storage': 'Central water tank storage', - 'decentral water tank storage': 'Domestic water tank storage', - 'water tank charger': 'Water tank charger/discharger', - 'HVDC overhead':'HVDC overhead', - 'HVDC inverter pair':'HVDC inverter pair', - #'central heat pump': 'Central heat pump', - #'decentral heat pump': 'Decentral heat pump', - #'central ground-sourced heat pump': 'Central ground-sourced heat pump', - 'central air-sourced heat pump': 'Central air-sourced heat pump', - 'decentral air-sourced heat pump': 'Domestic air-sourced heat pump', - 'decentral ground-sourced heat pump': 'Domestic ground-sourced heat pump', - 'biomass CHP capture':'CO$_2$ capture in CHP', - 'Fischer-Tropsch':'Fischer-Tropsch', - 'SMR': 'Steam Methane Reforming', - 'SMR CC': 'Steam Methane Reforming with CC', - 'BioSNG': 'BioSNG', - 'BtL': 'BtL', - 'biogas plus hydrogen': 'biogas plus hydrogen', - 'industrial heat pump medium temperature': 'industrial heat pump medium temperature', - 'industrial heat pump high temperature': 'industrial heat pump high temperature', - 'electric boiler steam': 'electric boiler steam', - 'gas boiler steam': 'gas boiler steam', - 'solid biomass boiler steam': 'solid biomass boiler steam', - 'methanolisation': 'methanolisation', - 'Compressed-Air-Adiabatic-bicharger': 'Compressed-Air-Adiabatic-bicharger', - 'Compressed-Air-Adiabatic-store': 'Compressed-Air-Adiabatic-store', - 'Concrete-charger': 'Concrete-charger', - 'Concrete-discharger': 'Concrete-discharger', - 'Concrete-store': 'Concrete-store', - 'Gravity-Brick-bicharger': 'Gravity-Brick-bicharger', - 'Gravity-Brick-store': 'Gravity-Brick-store', - 'Gravity-Water-Aboveground-bicharger': 'Gravity-Water-Aboveground-bicharger', - 'Gravity-Water-Aboveground-store': 'Gravity-Water-Aboveground-store', - 'Gravity-Water-Underground-bicharger': 'Gravity-Water-Underground-bicharger', - 'Gravity-Water-Underground-store': 'Gravity-Water-Underground-store', - 'HighT-Molten-Salt-charger': 'HighT-Molten-Salt-charger', - 'HighT-Molten-Salt-discharger': 'HighT-Molten-Salt-discharger', - 'HighT-Molten-Salt-store': 'HighT-Molten-Salt-store', - 'Hydrogen-charger': 'Hydrogen-charger', - 'Hydrogen-discharger': 'Hydrogen-discharger', - 'Hydrogen-store': 'Hydrogen-store', - 'Lead-Acid-bicharger': 'Lead-Acid-bicharger', - 'Lead-Acid-store': 'Lead-Acid-store', - 'Liquid-Air-charger': 'Liquid-Air-charger', - 'Liquid-Air-discharger': 'Liquid-Air-discharger', - 'Liquid-Air-store': 'Liquid-Air-store', - 'Lithium-Ion-LFP-bicharger': 'Lithium-Ion-LFP-bicharger', - 'Lithium-Ion-LFP-store': 'Lithium-Ion-LFP-store', - 'Lithium-Ion-NMC-bicharger': 'Lithium-Ion-NMC-bicharger', - 'Lithium-Ion-NMC-store': 'Lithium-Ion-NMC-store', - 'LowT-Molten-Salt-charger': 'LowT-Molten-Salt-charger', - 'LowT-Molten-Salt-discharger': 'LowT-Molten-Salt-discharger', - 'LowT-Molten-Salt-store': 'LowT-Molten-Salt-store', - 'Ni-Zn-bicharger': 'Ni-Zn-bicharger', - 'Ni-Zn-store': 'Ni-Zn-store', - 'Pumped-Heat-charger': 'Pumped-Heat-charger', - 'Pumped-Heat-discharger': 'Pumped-Heat-discharger', - 'Pumped-Heat-store': 'Pumped-Heat-store', - 'Pumped-Storage-Hydro-bicharger': 'Pumped-Storage-Hydro-bicharger', - 'Pumped-Storage-Hydro-store': 'Pumped-Storage-Hydro-store', - 'Sand-charger': 'Sand-charger', - 'Sand-discharger': 'Sand-discharger', - 'Sand-store': 'Sand-store', - 'Vanadium-Redox-Flow-bicharger': 'Vanadium-Redox-Flow-bicharger', - 'Vanadium-Redox-Flow-store': 'Vanadium-Redox-Flow-store', - 'Zn-Air-bicharger': 'Zn-Air-bicharger', - 'Zn-Air-store': 'Zn-Air-store', - 'Zn-Br-Flow-bicharger': 'Zn-Br-Flow-bicharger', - 'Zn-Br-Flow-store': 'Zn-Br-Flow-store', - 'Zn-Br-Nonflow-bicharger': 'Zn-Br-Nonflow-bicharger', - 'Zn-Br-Nonflow-store': 'Zn-Br-Nonflow-store', - } +name = { + "onwind": "Onshore Wind", + "offwind": "Offshore Wind", + "solar-utility": "Solar PV (utility-scale)", + "solar-rooftop": "Solar PV (rooftop)", + "OCGT": "OCGT", + "CCGT": "CCGT", + "coal": "Coal power plant", + "lignite": "Lignite", + "nuclear": "Nuclear", + "hydro": "Reservoir hydro", + "ror": "Run of river", + "PHS": "PHS", + "battery inverter": "Battery inverter", + "battery storage": "Battery storage", + "home battery inverter": "Home battery inverter", + "home battery storage": "Home battery storage", + "hydrogen storage underground": "H$_2$ storage underground", + "hydrogen storage tank type 1": "H$_2$ storage tank", + "electrolysis": "Electrolysis", + "fuel cell": "Fuel cell", + "methanation": "Methanation", + "direct air capture": "direct air capture", + "central gas boiler": "Central gas boiler", + "decentral gas boiler": "Domestic gas boiler", + "central resistive heater": "Central resistive heater", + "decentral resistive heater": "Domestic resistive heater", + "central gas CHP": " Gas CHP", + "central coal CHP": " Coal CHP", + "biomass CHP": "Biomass CHP", + "biomass EOP": "Biomass power plant", + "biomass HOP": "Biomass central heat plant", + "central water tank storage": "Central water tank storage", + "decentral water tank storage": "Domestic water tank storage", + "water tank charger": "Water tank charger/discharger", + "HVDC overhead": "HVDC overhead", + "HVDC inverter pair": "HVDC inverter pair", + #'central heat pump': 'Central heat pump', + #'decentral heat pump': 'Decentral heat pump', + #'central ground-sourced heat pump': 'Central ground-sourced heat pump', + "central air-sourced heat pump": "Central air-sourced heat pump", + "decentral air-sourced heat pump": "Domestic air-sourced heat pump", + "decentral ground-sourced heat pump": "Domestic ground-sourced heat pump", + "biomass CHP capture": "CO$_2$ capture in CHP", + "Fischer-Tropsch": "Fischer-Tropsch", + "SMR": "Steam Methane Reforming", + "SMR CC": "Steam Methane Reforming with CC", + "BioSNG": "BioSNG", + "BtL": "BtL", + "biogas plus hydrogen": "biogas plus hydrogen", + "industrial heat pump medium temperature": "industrial heat pump medium temperature", + "industrial heat pump high temperature": "industrial heat pump high temperature", + "electric boiler steam": "electric boiler steam", + "gas boiler steam": "gas boiler steam", + "solid biomass boiler steam": "solid biomass boiler steam", + "methanolisation": "methanolisation", + "Compressed-Air-Adiabatic-bicharger": "Compressed-Air-Adiabatic-bicharger", + "Compressed-Air-Adiabatic-store": "Compressed-Air-Adiabatic-store", + "Concrete-charger": "Concrete-charger", + "Concrete-discharger": "Concrete-discharger", + "Concrete-store": "Concrete-store", + "Gravity-Brick-bicharger": "Gravity-Brick-bicharger", + "Gravity-Brick-store": "Gravity-Brick-store", + "Gravity-Water-Aboveground-bicharger": "Gravity-Water-Aboveground-bicharger", + "Gravity-Water-Aboveground-store": "Gravity-Water-Aboveground-store", + "Gravity-Water-Underground-bicharger": "Gravity-Water-Underground-bicharger", + "Gravity-Water-Underground-store": "Gravity-Water-Underground-store", + "HighT-Molten-Salt-charger": "HighT-Molten-Salt-charger", + "HighT-Molten-Salt-discharger": "HighT-Molten-Salt-discharger", + "HighT-Molten-Salt-store": "HighT-Molten-Salt-store", + "Hydrogen-charger": "Hydrogen-charger", + "Hydrogen-discharger": "Hydrogen-discharger", + "Hydrogen-store": "Hydrogen-store", + "Lead-Acid-bicharger": "Lead-Acid-bicharger", + "Lead-Acid-store": "Lead-Acid-store", + "Liquid-Air-charger": "Liquid-Air-charger", + "Liquid-Air-discharger": "Liquid-Air-discharger", + "Liquid-Air-store": "Liquid-Air-store", + "Lithium-Ion-LFP-bicharger": "Lithium-Ion-LFP-bicharger", + "Lithium-Ion-LFP-store": "Lithium-Ion-LFP-store", + "Lithium-Ion-NMC-bicharger": "Lithium-Ion-NMC-bicharger", + "Lithium-Ion-NMC-store": "Lithium-Ion-NMC-store", + "LowT-Molten-Salt-charger": "LowT-Molten-Salt-charger", + "LowT-Molten-Salt-discharger": "LowT-Molten-Salt-discharger", + "LowT-Molten-Salt-store": "LowT-Molten-Salt-store", + "Ni-Zn-bicharger": "Ni-Zn-bicharger", + "Ni-Zn-store": "Ni-Zn-store", + "Pumped-Heat-charger": "Pumped-Heat-charger", + "Pumped-Heat-discharger": "Pumped-Heat-discharger", + "Pumped-Heat-store": "Pumped-Heat-store", + "Pumped-Storage-Hydro-bicharger": "Pumped-Storage-Hydro-bicharger", + "Pumped-Storage-Hydro-store": "Pumped-Storage-Hydro-store", + "Sand-charger": "Sand-charger", + "Sand-discharger": "Sand-discharger", + "Sand-store": "Sand-store", + "Vanadium-Redox-Flow-bicharger": "Vanadium-Redox-Flow-bicharger", + "Vanadium-Redox-Flow-store": "Vanadium-Redox-Flow-store", + "Zn-Air-bicharger": "Zn-Air-bicharger", + "Zn-Air-store": "Zn-Air-store", + "Zn-Br-Flow-bicharger": "Zn-Br-Flow-bicharger", + "Zn-Br-Flow-store": "Zn-Br-Flow-store", + "Zn-Br-Nonflow-bicharger": "Zn-Br-Nonflow-bicharger", + "Zn-Br-Nonflow-store": "Zn-Br-Nonflow-store", +} -dic_ref = {'Technology Data for Energy Plants for Electricity and District heating generation':'DEA_2019', - 'Impact of weighted average cost of capital, capital expenditure, and other parameters on future utility‐scale PV levelised cost of electricity': 'Vartiainen_2019', - 'European PV Technology and Innovation Platform' : 'Vartiainen_2017', - 'Lazard’s Levelized Cost of Energy Analysis - Version 13.0': 'Lazard_2019', - 'budischak2013':'Budischak_2013, DEA_2019', - #'NREL http://www.nrel.gov/docs/fy09osti/45873.pdf; - 'IWES Interaktion':'Gerhardt_2015, DEA_2019', - 'Schaber thesis':'Schaber_2013', - 'Hagspiel et al. (2014): doi:10.1016/j.energy.2014.01.025 ': 'Hagspiel_2014', - 'Hagspiel':'Hagspiel_2014', - #'Fasihi':'Fasihi_2017', - 'Fasihi et al 2017, table 1, https://www.mdpi.com/2071-1050/9/2/306':'Fasihi_2017', - 'HP' : ' ', - 'DIW DataDoc http://hdl.handle.net/10419/80348' : 'Schroeder_2013', - 888 : 'water tank charger', - 'BP 2019':'BP_2019', - 'https://www.eia.gov/environment/emissions/co2_vol_mass.php' : 'EIA_emission_coefficients', - 'DIW': 'Schroeder_2013', - 'IEA2011b' : 'BP_2019', - 'Is a 100% renewable European power system feasible by 2050?': 'Zappa_2019, JRC_biomass', - 'Entwicklung der spezifischen Kohlendioxid-Emissionen des deutschen Strommix in den Jahren 1990 - 2018': 'German_Environment_Agency', - 'IEA WEM2017 97USD/boe = http://www.iea.org/media/weowebsite/2017/WEM_Documentation_WEO2017.pdf':'IEA_WEO2017', - 'Danish Energy Agency': 'DEA_2019', - 'Danish Energy Agency, technology_data_for_el_and_dh.xlsx':'DEA_2019', - 'Danish Energy Agency, technology_data_for_el_and_dh_-_0009.xlsx':'DEA_2019', - 'Danish Energy Agency, technology_data_catalogue_for_energy_storage.xlsx':'DEA_2019', - 'Danish Energy Agency, technology_data_catalogue_for_energy_storage.xlsx, Note K.':'DEA_2019', - 'Danish Energy Agency, data_sheets_for_renewable_fuels.xlsx':'DEA_2019', - 'Danish Energy Agency, technology_data_for_industrial_process_heat_0002.xlsx':'DEA_2019', - 'Danish Energy Agency, technologydatafor_heating_installations_marts_2018.xlsx':'DEA_2019', - 'Lazard s Levelized Cost of Energy Analysis - Version 13.0':'Lazard_2019', - 'Global Energy System based on 100% Renewable Energy, Energywatchgroup/LTU University, 2019, Danish Energy Agency, technology_data_catalogue_for_energy_storage.xlsx' :'Ram_2019, DEA_2019', - 'Global Energy System based on 100% Renewable Energy, Energywatchgroup/LTU University, 2019, Danish Energy Agency, technology_data_catalogue_for_energy_storage.xlsx, Note K.' :'Ram_2019, DEA_2019', - 'TODO':'govUK', - 'Viswanathan_2022': 'Viswanathan_2022', - 'Georgiou_2018': 'Georgiou_2018', +dic_ref = { + "Technology Data for Energy Plants for Electricity and District heating generation": "DEA_2019", + "Impact of weighted average cost of capital, capital expenditure, and other parameters on future utility‐scale PV levelised cost of electricity": "Vartiainen_2019", + "European PV Technology and Innovation Platform": "Vartiainen_2017", + "Lazard’s Levelized Cost of Energy Analysis - Version 13.0": "Lazard_2019", + "budischak2013": "Budischak_2013, DEA_2019", + #'NREL http://www.nrel.gov/docs/fy09osti/45873.pdf; + "IWES Interaktion": "Gerhardt_2015, DEA_2019", + "Schaber thesis": "Schaber_2013", + "Hagspiel et al. (2014): doi:10.1016/j.energy.2014.01.025 ": "Hagspiel_2014", + "Hagspiel": "Hagspiel_2014", + #'Fasihi':'Fasihi_2017', + "Fasihi et al 2017, table 1, https://www.mdpi.com/2071-1050/9/2/306": "Fasihi_2017", + "HP": " ", + "DIW DataDoc http://hdl.handle.net/10419/80348": "Schroeder_2013", + 888: "water tank charger", + "BP 2019": "BP_2019", + "https://www.eia.gov/environment/emissions/co2_vol_mass.php": "EIA_emission_coefficients", + "DIW": "Schroeder_2013", + "IEA2011b": "BP_2019", + "Is a 100% renewable European power system feasible by 2050?": "Zappa_2019, JRC_biomass", + "Entwicklung der spezifischen Kohlendioxid-Emissionen des deutschen Strommix in den Jahren 1990 - 2018": "German_Environment_Agency", + "IEA WEM2017 97USD/boe = http://www.iea.org/media/weowebsite/2017/WEM_Documentation_WEO2017.pdf": "IEA_WEO2017", + "Danish Energy Agency": "DEA_2019", + "Danish Energy Agency, technology_data_for_el_and_dh.xlsx": "DEA_2019", + "Danish Energy Agency, technology_data_for_el_and_dh_-_0009.xlsx": "DEA_2019", + "Danish Energy Agency, technology_data_catalogue_for_energy_storage.xlsx": "DEA_2019", + "Danish Energy Agency, technology_data_catalogue_for_energy_storage.xlsx, Note K.": "DEA_2019", + "Danish Energy Agency, data_sheets_for_renewable_fuels.xlsx": "DEA_2019", + "Danish Energy Agency, technology_data_for_industrial_process_heat_0002.xlsx": "DEA_2019", + "Danish Energy Agency, technologydatafor_heating_installations_marts_2018.xlsx": "DEA_2019", + "Lazard s Levelized Cost of Energy Analysis - Version 13.0": "Lazard_2019", + "Global Energy System based on 100% Renewable Energy, Energywatchgroup/LTU University, 2019, Danish Energy Agency, technology_data_catalogue_for_energy_storage.xlsx": "Ram_2019, DEA_2019", + "Global Energy System based on 100% Renewable Energy, Energywatchgroup/LTU University, 2019, Danish Energy Agency, technology_data_catalogue_for_energy_storage.xlsx, Note K.": "Ram_2019, DEA_2019", + "TODO": "govUK", + "Viswanathan_2022": "Viswanathan_2022", + "Georgiou_2018": "Georgiou_2018", } # Solar thermal collector decentral & 270 & m$^{2}$ & 1.3 & 20 & variable & \cite{Henning20141003} \\ @@ -237,151 +279,224 @@ # Gas distribution network\tnote{f} & 387 & kW\th & 2 & 40 & 1 & based on \cite{bnetza2017} \\ for technology in technologies: - if idx[technology,'FOM'] in costs.index: - FOM = str(round(costs.loc[idx[technology,'FOM'],'value'],1)) + if idx[technology, "FOM"] in costs.index: + FOM = str(round(costs.loc[idx[technology, "FOM"], "value"], 1)) else: - FOM= ' ' - if idx[technology,'lifetime'] in costs.index: - lifetime = str(int(costs.loc[idx[technology,'lifetime'],'value'])) + FOM = " " + if idx[technology, "lifetime"] in costs.index: + lifetime = str(int(costs.loc[idx[technology, "lifetime"], "value"])) else: - lifetime= ' ' - if idx[technology,'investment'] in costs.index: - investment = str(int(costs.loc[idx[technology,'investment'],'value']/1000)) + lifetime = " " + if idx[technology, "investment"] in costs.index: + investment = str(int(costs.loc[idx[technology, "investment"], "value"] / 1000)) else: - investment= ' ' - if idx[technology,'efficiency'] in costs.index and technology not in ['onwind', - 'offwind', 'central gas CHP', 'biomass CHP', 'battery storage', - 'home battery storage', 'central coal CHP' - 'hydrogen storage underground', 'hydrogen storage tank type 1', - 'central water tank storage', 'decentral water tank storage', - 'decentral air-sourced heat pump', 'central ground-sourced heat pump', - 'decentral ground-sourced heat pump']: - - efficiency = str(round(costs.loc[idx[technology,'efficiency'],'value'],2)) + investment = " " + if idx[technology, "efficiency"] in costs.index and technology not in [ + "onwind", + "offwind", + "central gas CHP", + "biomass CHP", + "battery storage", + "home battery storage", + "central coal CHP" "hydrogen storage underground", + "hydrogen storage tank type 1", + "central water tank storage", + "decentral water tank storage", + "decentral air-sourced heat pump", + "central ground-sourced heat pump", + "decentral ground-sourced heat pump", + ]: + efficiency = str(round(costs.loc[idx[technology, "efficiency"], "value"], 2)) + else: + efficiency = " " + if technology not in [ + "water tank charger", + "hydro", + "ror", + "PHS", + "electrolysis", + "fuel cell", + "decentral water tank storage", + ]: + source = costs.loc[idx[technology, "lifetime"], "source"] + elif technology == "decentral water tank storage": + source = costs.loc[idx[technology, "investment"], "source"] else: - efficiency= ' ' - if technology not in ['water tank charger', 'hydro', 'ror', 'PHS', - 'electrolysis', 'fuel cell', 'decentral water tank storage']: - source = costs.loc[idx[technology,'lifetime'],'source'] - elif technology == 'decentral water tank storage': - source = costs.loc[idx[technology,'investment'],'source'] + source = costs.loc[idx[technology, "efficiency"], "source"] + if technology == "water tank charger": + file.write( + " " + + name[technology] + + " & " + + investment + + " & " + + FOM + + " & " + + lifetime + + " & " + + efficiency + + " & " + + " \\" + + " " + ) else: - source = costs.loc[idx[technology,'efficiency'],'source'] - if technology == 'water tank charger': - file.write(' ' + name[technology] - + ' & ' + investment - + ' & ' + FOM - + ' & ' + lifetime - + ' & ' + efficiency - + ' & ' + ' \\' + ' ') - else: - file.write(' ' + name[technology] - + ' & ' + investment - + ' & ' + FOM - + ' & ' + lifetime - + ' & ' + efficiency - + ' & ' + ' \\' + 'cite{' + dic_ref[source.split(sep=",")[0]] + '} ') + file.write( + " " + + name[technology] + + " & " + + investment + + " & " + + FOM + + " & " + + lifetime + + " & " + + efficiency + + " & " + + " \\" + + "cite{" + + dic_ref[source.split(sep=",")[0]] + + "} " + ) - file.write('\\') - file.write('\\') + file.write("\\") + file.write("\\") file.close() -#%% +# %% """ Table including costs as a function of years """ -years=np.arange(2020,2055,5) -filename='table_costs.tex' -file = open(filename, 'w') -technologies=[t for t in technologies if t not in ['water tank charger']] -dic_units={'EUR/kWel':'\EUR/kW$_{el}$', - 'EUR/kWth':'\EUR/kW$_{th}$', - 'EUR/kWH2':'\EUR/kW$_{H_2}$', - 'EUR/kW_CH4':'\EUR/kW$_{CH4}$', - 'EUR/kWCH4':'\EUR/kW$_{CH4}$', - 'EUR/kWhth':'\EUR/kWh$_{th}$', - 'EUR/(tCO2/a)': '\EUR/(tCO$_2$/a)', - 'EUR/(tCO2/h)' :'\EUR/(tCO$_2$/h)', - 'EUR/m3':'\EUR/m$^3$', - 'EUR/MW/km':'\EUR/MWkm', - 'EUR/MW':'\EUR/MW', - 'USD/kWel':'USD/kW$_{el}$', - 'USD/kWh':'USD/kWh', - 'EUR/kWh': '\EUR/kWh', - 'EUR/kW': '\EUR/kW', - 'EUR/kW_e':'\EUR/kW$_{el}$', - 'EUR/kW_th - heat output':'\EUR/kW$_{th}$', - 'EUR/kW_th': '\EUR/kW$_{th}$', - 'EUR/kWhCapacity': '\EUR/kWh', - 'EUR/kW_th excluding drive energy': '\EUR/kW$_{th}$', - 'EUR/kW_FT/year':'\EUR/kW$_{FT}$/a', - 'EUR/kW_MeOH':'\EUR/kW$_{MeOH}$' - } - +years = np.arange(2020, 2055, 5) +filename = "table_costs.tex" +file = open(filename, "w") +technologies = [t for t in technologies if t not in ["water tank charger"]] +dic_units = { + "EUR/kWel": r"\EUR/kW$_{el}$", + "EUR/kWth": r"\EUR/kW$_{th}$", + "EUR/kWH2": r"\EUR/kW$_{H_2}$", + "EUR/kW_CH4": r"\EUR/kW$_{CH4}$", + "EUR/kWCH4": r"\EUR/kW$_{CH4}$", + "EUR/kWhth": r"\EUR/kWh$_{th}$", + "EUR/(tCO2/a)": r"\EUR/(tCO$_2$/a)", + "EUR/(tCO2/h)": r"\EUR/(tCO$_2$/h)", + "EUR/m3": r"\EUR/m$^3$", + "EUR/MW/km": r"\EUR/MWkm", + "EUR/MW": r"\EUR/MW", + "USD/kWel": "USD/kW$_{el}$", + "USD/kWh": "USD/kWh", + "EUR/kWh": r"\EUR/kWh", + "EUR/kW": r"\EUR/kW", + "EUR/kW_e": r"\EUR/kW$_{el}$", + "EUR/kW_th - heat output": r"\EUR/kW$_{th}$", + "EUR/kW_th": r"\EUR/kW$_{th}$", + "EUR/kWhCapacity": r"\EUR/kWh", + "EUR/kW_th excluding drive energy": r"\EUR/kW$_{th}$", + "EUR/kW_FT/year": r"\EUR/kW$_{FT}$/a", + "EUR/kW_MeOH": r"\EUR/kW$_{MeOH}$", +} for technology in technologies: - file.write(' ' +name[technology] + ' & ') - file.write(dic_units[costs.loc[idx[technology,'investment'],'unit']]+ ' & ' ) + file.write(" " + name[technology] + " & ") + file.write(dic_units[costs.loc[idx[technology, "investment"], "unit"]] + " & ") for year in years: - costs_year = pd.read_csv('../outputs/costs_' + str(year) +'.csv',index_col=list(range(2))).sort_index() - if technology in ['hydrogen storage underground', 'central water tank storage']: - file.write(str(round(costs_year.loc[idx[technology,'investment'],'value'],1))+ ' & ' ) + costs_year = pd.read_csv( + "../outputs/costs_" + str(year) + ".csv", index_col=list(range(2)) + ).sort_index() + if technology in ["hydrogen storage underground", "central water tank storage"]: + file.write( + str(round(costs_year.loc[idx[technology, "investment"], "value"], 1)) + + " & " + ) else: - file.write(str(int(costs_year.loc[idx[technology,'investment'],'value']))+ ' & ' ) + file.write( + str(int(costs_year.loc[idx[technology, "investment"], "value"])) + " & " + ) - if technology not in ['water tank charger', 'hydro', 'ror', 'PHS', 'decentral water tank storage']: - # water tank charger has no lifetime, hydro reference for lifetime - # is IEA2011, but for cost is DIW - source = costs.loc[idx[technology,'lifetime'],'source'] - elif technology == 'decentral water tank storage': - source = costs.loc[idx[technology,'investment'],'source'] + if technology not in [ + "water tank charger", + "hydro", + "ror", + "PHS", + "decentral water tank storage", + ]: + # water tank charger has no lifetime, hydro reference for lifetime + # is IEA2011, but for cost is DIW + source = costs.loc[idx[technology, "lifetime"], "source"] + elif technology == "decentral water tank storage": + source = costs.loc[idx[technology, "investment"], "source"] else: - source = costs.loc[idx[technology,'efficiency'],'source'] - if technology == 'water tank charger': - file.write( ' \\' + ' ') + source = costs.loc[idx[technology, "efficiency"], "source"] + if technology == "water tank charger": + file.write(" \\" + " ") else: - file.write( ' \\' + 'cite{' + dic_ref[source]+ '} ') - file.write('\\') - file.write('\\') + file.write(" \\" + "cite{" + dic_ref[source] + "} ") + file.write("\\") + file.write("\\") file.close() -#%% +# %% """ Table including fuel characteristics """ -filename='table_fuels.tex' -file = open(filename, 'w') -for fuel in [ 'coal', 'lignite', 'gas', 'oil','nuclear', 'solid biomass']: - if idx[fuel,'fuel'] in costs.index: - cost = str(round(costs.loc[idx[fuel,'fuel'],'value'],1)) - source1 = costs.loc[idx[fuel,'fuel'],'source'] +filename = "table_fuels.tex" +file = open(filename, "w") +for fuel in ["coal", "lignite", "gas", "oil", "nuclear", "solid biomass"]: + if idx[fuel, "fuel"] in costs.index: + cost = str(round(costs.loc[idx[fuel, "fuel"], "value"], 1)) + source1 = costs.loc[idx[fuel, "fuel"], "source"] else: - cost = ' ' + cost = " " - if idx[fuel,'CO2 intensity'] in costs.index: - emissions = str(round(costs.loc[idx[fuel,'CO2 intensity'],'value'],3)) - source2 = costs.loc[idx[fuel,'CO2 intensity'],'source'] + if idx[fuel, "CO2 intensity"] in costs.index: + emissions = str(round(costs.loc[idx[fuel, "CO2 intensity"], "value"], 3)) + source2 = costs.loc[idx[fuel, "CO2 intensity"], "source"] else: - emissions = ' ' - if fuel not in ['nuclear', 'solid biomass','gas', 'oil','digestible biomass','biogas'] : - file.write(' ' + fuel - + ' & ' + cost - + ' & ' + - ' \\' + 'cite{' + dic_ref[source1]+ '} ' - + ' & ' + emissions - + ' & ' + - ' \\' + 'cite{' + dic_ref[source2]+ '} ') + emissions = " " + if fuel not in [ + "nuclear", + "solid biomass", + "gas", + "oil", + "digestible biomass", + "biogas", + ]: + file.write( + " " + + fuel + + " & " + + cost + + " & " + + " \\" + + "cite{" + + dic_ref[source1] + + "} " + + " & " + + emissions + + " & " + + " \\" + + "cite{" + + dic_ref[source2] + + "} " + ) else: - file.write(' ' + fuel - + ' & ' + cost - + ' & ' + - ' \\' + 'cite{' + dic_ref[source1]+ '} ' - + ' & ' + str(0) - + ' & ' + - ' ') - file.write('\\') - file.write('\\') + file.write( + " " + + fuel + + " & " + + cost + + " & " + + " \\" + + "cite{" + + dic_ref[source1] + + "} " + + " & " + + str(0) + + " & " + + " " + ) + file.write("\\") + file.write("\\") file.close() diff --git a/ruff.toml b/ruff.toml index 2d75d20..ea06ac5 100644 --- a/ruff.toml +++ b/ruff.toml @@ -1,6 +1,6 @@ -# SPDX-FileCopyrightText: : 2021-2024 The PyPSA-Eur Authors +# SPDX-FileCopyrightText: Contributors to technology-data # -# SPDX-License-Identifier: CC0-1.0 +# SPDX-License-Identifier: MIT extend-include = ['*.ipynb'] diff --git a/scripts/_helpers.py b/scripts/_helpers.py index 9279ce4..38d73a7 100644 --- a/scripts/_helpers.py +++ b/scripts/_helpers.py @@ -1,5 +1,5 @@ -from pathlib import Path import re +from pathlib import Path class Dict(dict): @@ -12,12 +12,10 @@ class Dict(dict): def __setattr__(self, name, value): """ - setattr is called when the syntax a.b = 2 is used to set a value. + Setattr is called when the syntax a.b = 2 is used to set a value. """ if hasattr(Dict, name): - raise AttributeError( - "'Dict' object attribute " "'{0}' is read-only".format(name) - ) + raise AttributeError("'Dict' object attribute " f"'{name}' is read-only") self[name] = value def __getattr__(self, item): @@ -178,4 +176,4 @@ def make_accessable(*ios): finally: if user_in_script_dir: os.chdir(script_dir) - return snakemake \ No newline at end of file + return snakemake diff --git a/scripts/compile_cost_assumptions.py b/scripts/compile_cost_assumptions.py index c1a533e..10e09a7 100644 --- a/scripts/compile_cost_assumptions.py +++ b/scripts/compile_cost_assumptions.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- """ Script creates cost csv for choosen years from different source (source_dict). The data is standardized for uniform: @@ -25,247 +24,254 @@ @author: Marta, Lisa """ -import pandas as pd import numpy as np +import pandas as pd + try: - pd.set_option('future.no_silent_downcasting', True) + pd.set_option("future.no_silent_downcasting", True) except Exception: pass # ---------- sources ------------------------------------------------------- source_dict = { - 'DEA': 'Danish Energy Agency', - # solar utility - 'Vartiaien': 'Impact of weighted average cost of capital, capital expenditure, and other parameters on future utility‐scale PV levelised cost of electricity', - # solar rooftop - 'ETIP': 'European PV Technology and Innovation Platform', - # fuel cost - 'zappa': 'Is a 100% renewable European power system feasible by 2050?', - # co2 intensity - "co2" :'Entwicklung der spezifischen Kohlendioxid-Emissionen des deutschen Strommix in den Jahren 1990 - 2018', - # gas pipeline costs - "ISE": "WEGE ZU EINEM KLIMANEUTRALEN ENERGIESYSEM, Anhang zur Studie, Fraunhofer-Institut für Solare Energiesysteme ISE, Freiburg", - # Water desalination costs - "Caldera2016": "Caldera et al 2016: Local cost of seawater RO desalination based on solar PV and windenergy: A global estimate. (https://doi.org/10.1016/j.desal.2016.02.004)", - "Caldera2017": "Caldera et al 2017: Learning Curve for Seawater Reverse Osmosis Desalination Plants: Capital Cost Trend of the Past, Present, and Future (https://doi.org/10.1002/2017WR021402)", - # home battery storage and inverter investment costs - "EWG": "Global Energy System based on 100% Renewable Energy, Energywatchgroup/LTU University, 2019", - "HyNOW" : "Zech et.al. DBFZ Report Nr. 19. Hy-NOW - Evaluierung der Verfahren und Technologien für die Bereitstellung von Wasserstoff auf Basis von Biomasse, DBFZ, 2014", - # efficiencies + lifetime SMR / SMR + CC - "IEA": "IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050", - # SMR capture rate - "Timmerberg": "Hydrogen and hydrogen-derived fuels through methane decomposition of natural gas – GHG emissions and costs Timmerberg et al. (2020), https://doi.org/10.1016/j.ecmx.2020.100043", - # geothermal (enhanced geothermal systems) - "Aghahosseini2020": "Aghahosseini, Breyer 2020: From hot rock to useful energy: A global estimate of enhanced geothermal systems potential, https://www.sciencedirect.com/science/article/pii/S0306261920312551", - # review of existing deep geothermal projects - "Breede2015": "Breede et al. 2015: Overcoming challenges in the classification of deep geothermal potential, https://eprints.gla.ac.uk/169585/", - # Study of deep geothermal systems in the Northern Upper Rhine Graben - "Frey2022": "Frey et al. 2022: Techno-Economic Assessment of Geothermal Resources in the Variscan Basement of the Northern Upper Rhine Graben", - # vehicles - "vehicles" : "PATHS TO A CLIMATE-NEUTRAL ENERGY SYSTEM The German energy transformation in its social context. https://www.ise.fraunhofer.de/en/publications/studies/paths-to-a-climate-neutral-energy-system.html" - } + "DEA": "Danish Energy Agency", + # solar utility + "Vartiaien": "Impact of weighted average cost of capital, capital expenditure, and other parameters on future utility‐scale PV levelised cost of electricity", + # solar rooftop + "ETIP": "European PV Technology and Innovation Platform", + # fuel cost + "zappa": "Is a 100% renewable European power system feasible by 2050?", + # co2 intensity + "co2": "Entwicklung der spezifischen Kohlendioxid-Emissionen des deutschen Strommix in den Jahren 1990 - 2018", + # gas pipeline costs + "ISE": "WEGE ZU EINEM KLIMANEUTRALEN ENERGIESYSEM, Anhang zur Studie, Fraunhofer-Institut für Solare Energiesysteme ISE, Freiburg", + # Water desalination costs + "Caldera2016": "Caldera et al 2016: Local cost of seawater RO desalination based on solar PV and windenergy: A global estimate. (https://doi.org/10.1016/j.desal.2016.02.004)", + "Caldera2017": "Caldera et al 2017: Learning Curve for Seawater Reverse Osmosis Desalination Plants: Capital Cost Trend of the Past, Present, and Future (https://doi.org/10.1002/2017WR021402)", + # home battery storage and inverter investment costs + "EWG": "Global Energy System based on 100% Renewable Energy, Energywatchgroup/LTU University, 2019", + "HyNOW": "Zech et.al. DBFZ Report Nr. 19. Hy-NOW - Evaluierung der Verfahren und Technologien für die Bereitstellung von Wasserstoff auf Basis von Biomasse, DBFZ, 2014", + # efficiencies + lifetime SMR / SMR + CC + "IEA": "IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050", + # SMR capture rate + "Timmerberg": "Hydrogen and hydrogen-derived fuels through methane decomposition of natural gas – GHG emissions and costs Timmerberg et al. (2020), https://doi.org/10.1016/j.ecmx.2020.100043", + # geothermal (enhanced geothermal systems) + "Aghahosseini2020": "Aghahosseini, Breyer 2020: From hot rock to useful energy: A global estimate of enhanced geothermal systems potential, https://www.sciencedirect.com/science/article/pii/S0306261920312551", + # review of existing deep geothermal projects + "Breede2015": "Breede et al. 2015: Overcoming challenges in the classification of deep geothermal potential, https://eprints.gla.ac.uk/169585/", + # Study of deep geothermal systems in the Northern Upper Rhine Graben + "Frey2022": "Frey et al. 2022: Techno-Economic Assessment of Geothermal Resources in the Variscan Basement of the Northern Upper Rhine Graben", + # vehicles + "vehicles": "PATHS TO A CLIMATE-NEUTRAL ENERGY SYSTEM The German energy transformation in its social context. https://www.ise.fraunhofer.de/en/publications/studies/paths-to-a-climate-neutral-energy-system.html", +} # [DEA-sheet-names] -sheet_names = {'onwind': '20 Onshore turbines', - 'offwind': '21 Offshore turbines', - 'solar-utility': '22 Utility-scale PV', - 'solar-utility single-axis tracking': '22 Utility-scale PV tracker', - 'solar-rooftop residential': '22 Rooftop PV residential', - 'solar-rooftop commercial': '22 Rooftop PV commercial', - 'OCGT': '52 OCGT - Natural gas', - 'CCGT': '05 Gas turb. CC, steam extract.', - 'oil': '50 Diesel engine farm', - 'biomass CHP': '09c Straw, Large, 40 degree', - 'biomass EOP': '09c Straw, Large, 40 degree', - 'biomass HOP': '09c Straw HOP', - 'central coal CHP': '01 Coal CHP', - 'central gas CHP': '04 Gas turb. simple cycle, L', - 'central gas CHP CC': '04 Gas turb. simple cycle, L', - 'central solid biomass CHP': '09a Wood Chips, Large 50 degree', - 'central solid biomass CHP CC': '09a Wood Chips, Large 50 degree', - 'central solid biomass CHP powerboost CC': '09a Wood Chips, Large 50 degree', - # 'solid biomass power': '09a Wood Chips extract. plant', - # 'solid biomass power CC': '09a Wood Chips extract. plant', - 'central air-sourced heat pump': '40 Comp. hp, airsource 3 MW', - 'central geothermal-sourced heat pump': '45.1.a Geothermal DH, 1200m, E', - 'central geothermal heat source': '45.1.a Geothermal DH, 1200m, E', - 'central excess-heat-sourced heat pump': '40 Comp. hp, excess heat 10 MW', - 'central water-sourced heat pump': '40 Comp. hp, seawater 20 MW', - 'central ground-sourced heat pump': '40 Absorption heat pump, DH', - 'central resistive heater': '41 Electric Boilers', - 'central gas boiler': '44 Natural Gas DH Only', - 'decentral gas boiler': '202 Natural gas boiler', - 'direct firing gas': '312.a Direct firing Natural Gas', - 'direct firing gas CC': '312.a Direct firing Natural Gas', - 'direct firing solid fuels': '312.b Direct firing Sold Fuels', - 'direct firing solid fuels CC': '312.b Direct firing Sold Fuels', - 'decentral ground-sourced heat pump': '207.7 Ground source existing', - 'decentral air-sourced heat pump': '207.3 Air to water existing', - # 'decentral resistive heater': '216 Electric heating', - 'central water pit storage': '140 PTES seasonal', - 'central water tank storage': '141 Large hot water tank', - 'decentral water tank storage': '142 Small scale hot water tank', - 'fuel cell': '12 LT-PEMFC CHP', - 'hydrogen storage underground': '151c Hydrogen Storage - Caverns', - 'hydrogen storage tank type 1 including compressor': '151a Hydrogen Storage - Tanks', - 'micro CHP': '219 LT-PEMFC mCHP - natural gas', - 'biogas' : '81 Biogas, Basic plant, small', - 'biogas CC' : '81 Biogas, Basic plant, small', - 'biogas upgrading': '82 Upgrading 3,000 Nm3 per h', - 'battery': '180 Lithium Ion Battery', - 'industrial heat pump medium temperature': '302.a High temp. hp Up to 125 C', - 'industrial heat pump high temperature': '302.b High temp. hp Up to 150', - 'electric boiler steam': '310.1 Electric boiler steam ', - 'gas boiler steam': '311.1c Steam boiler Gas', - 'solid biomass boiler steam': '311.1e Steam boiler Wood', - 'solid biomass boiler steam CC': '311.1e Steam boiler Wood', - 'biomass boiler': '204 Biomass boiler, automatic', - 'electrolysis': '86 AEC 100 MW', - 'direct air capture': '403.a Direct air capture', - 'biomass CHP capture': '401.a Post comb - small CHP', - 'cement capture': '401.c Post comb - Cement kiln', - 'BioSNG': '84 Gasif. CFB, Bio-SNG', - 'BtL': '85 Gasif. Ent. Flow FT, liq fu ', - 'biomass-to-methanol': '97 Methanol from biomass gasif.', - 'biogas plus hydrogen': '99 SNG from methan. of biogas', - 'methanolisation': '98 Methanol from hydrogen', - 'Fischer-Tropsch': '102 Hydrogen to Jet', - 'central hydrogen CHP': '12 LT-PEMFC CHP', - 'Haber-Bosch': '103 Hydrogen to Ammonia', - 'air separation unit': '103 Hydrogen to Ammonia', - 'waste CHP': '08 WtE CHP, Large, 50 degree', - 'waste CHP CC': '08 WtE CHP, Large, 50 degree', - # 'electricity distribution rural': '101 2 el distri Rural', - # 'electricity distribution urban': '101 4 el distri city', - # 'gas distribution rural': '102 7 gas Rural', - # 'gas distribution urban': '102 9 gas City', - # 'DH distribution rural': '103_12 DH_Distribu Rural', - # 'DH distribution urban': '103_14 DH_Distribu City', - # 'DH distribution low T': '103_16 DH_Distr New area LTDH', - # 'gas pipeline': '102 6 gas Main distri line', - # "DH main transmission": "103_11 DH transmission", - 'biochar pyrolysis': '105 Slow pyrolysis, Straw', - #'biomethanation': '106 Biomethanation of biogas', - 'electrolysis small': '86 AEC 10 MW', - } +sheet_names = { + "onwind": "20 Onshore turbines", + "offwind": "21 Offshore turbines", + "solar-utility": "22 Utility-scale PV", + "solar-utility single-axis tracking": "22 Utility-scale PV tracker", + "solar-rooftop residential": "22 Rooftop PV residential", + "solar-rooftop commercial": "22 Rooftop PV commercial", + "OCGT": "52 OCGT - Natural gas", + "CCGT": "05 Gas turb. CC, steam extract.", + "oil": "50 Diesel engine farm", + "biomass CHP": "09c Straw, Large, 40 degree", + "biomass EOP": "09c Straw, Large, 40 degree", + "biomass HOP": "09c Straw HOP", + "central coal CHP": "01 Coal CHP", + "central gas CHP": "04 Gas turb. simple cycle, L", + "central gas CHP CC": "04 Gas turb. simple cycle, L", + "central solid biomass CHP": "09a Wood Chips, Large 50 degree", + "central solid biomass CHP CC": "09a Wood Chips, Large 50 degree", + "central solid biomass CHP powerboost CC": "09a Wood Chips, Large 50 degree", + # 'solid biomass power': '09a Wood Chips extract. plant', + # 'solid biomass power CC': '09a Wood Chips extract. plant', + "central air-sourced heat pump": "40 Comp. hp, airsource 3 MW", + "central geothermal-sourced heat pump": "45.1.a Geothermal DH, 1200m, E", + "central geothermal heat source": "45.1.a Geothermal DH, 1200m, E", + "central excess-heat-sourced heat pump": "40 Comp. hp, excess heat 10 MW", + "central water-sourced heat pump": "40 Comp. hp, seawater 20 MW", + "central ground-sourced heat pump": "40 Absorption heat pump, DH", + "central resistive heater": "41 Electric Boilers", + "central gas boiler": "44 Natural Gas DH Only", + "decentral gas boiler": "202 Natural gas boiler", + "direct firing gas": "312.a Direct firing Natural Gas", + "direct firing gas CC": "312.a Direct firing Natural Gas", + "direct firing solid fuels": "312.b Direct firing Sold Fuels", + "direct firing solid fuels CC": "312.b Direct firing Sold Fuels", + "decentral ground-sourced heat pump": "207.7 Ground source existing", + "decentral air-sourced heat pump": "207.3 Air to water existing", + # 'decentral resistive heater': '216 Electric heating', + "central water pit storage": "140 PTES seasonal", + "central water tank storage": "141 Large hot water tank", + "decentral water tank storage": "142 Small scale hot water tank", + "fuel cell": "12 LT-PEMFC CHP", + "hydrogen storage underground": "151c Hydrogen Storage - Caverns", + "hydrogen storage tank type 1 including compressor": "151a Hydrogen Storage - Tanks", + "micro CHP": "219 LT-PEMFC mCHP - natural gas", + "biogas": "81 Biogas, Basic plant, small", + "biogas CC": "81 Biogas, Basic plant, small", + "biogas upgrading": "82 Upgrading 3,000 Nm3 per h", + "battery": "180 Lithium Ion Battery", + "industrial heat pump medium temperature": "302.a High temp. hp Up to 125 C", + "industrial heat pump high temperature": "302.b High temp. hp Up to 150", + "electric boiler steam": "310.1 Electric boiler steam ", + "gas boiler steam": "311.1c Steam boiler Gas", + "solid biomass boiler steam": "311.1e Steam boiler Wood", + "solid biomass boiler steam CC": "311.1e Steam boiler Wood", + "biomass boiler": "204 Biomass boiler, automatic", + "electrolysis": "86 AEC 100 MW", + "direct air capture": "403.a Direct air capture", + "biomass CHP capture": "401.a Post comb - small CHP", + "cement capture": "401.c Post comb - Cement kiln", + "BioSNG": "84 Gasif. CFB, Bio-SNG", + "BtL": "85 Gasif. Ent. Flow FT, liq fu ", + "biomass-to-methanol": "97 Methanol from biomass gasif.", + "biogas plus hydrogen": "99 SNG from methan. of biogas", + "methanolisation": "98 Methanol from hydrogen", + "Fischer-Tropsch": "102 Hydrogen to Jet", + "central hydrogen CHP": "12 LT-PEMFC CHP", + "Haber-Bosch": "103 Hydrogen to Ammonia", + "air separation unit": "103 Hydrogen to Ammonia", + "waste CHP": "08 WtE CHP, Large, 50 degree", + "waste CHP CC": "08 WtE CHP, Large, 50 degree", + # 'electricity distribution rural': '101 2 el distri Rural', + # 'electricity distribution urban': '101 4 el distri city', + # 'gas distribution rural': '102 7 gas Rural', + # 'gas distribution urban': '102 9 gas City', + # 'DH distribution rural': '103_12 DH_Distribu Rural', + # 'DH distribution urban': '103_14 DH_Distribu City', + # 'DH distribution low T': '103_16 DH_Distr New area LTDH', + # 'gas pipeline': '102 6 gas Main distri line', + # "DH main transmission": "103_11 DH transmission", + "biochar pyrolysis": "105 Slow pyrolysis, Straw", + #'biomethanation': '106 Biomethanation of biogas', + "electrolysis small": "86 AEC 10 MW", +} # [DEA-sheet-names] -uncrtnty_lookup = {'onwind': 'J:K', - 'offwind': 'J:K', - 'solar-utility': 'J:K', - 'solar-utility single-axis tracking': 'J:K', - 'solar-rooftop residential': 'J:K', - 'solar-rooftop commercial': 'J:K', - 'OCGT': 'I:J', - 'CCGT': 'I:J', - 'oil': 'I:J', - 'biomass CHP': 'I:J', - 'biomass EOP': 'I:J', - 'biomass HOP': 'I:J', - 'central coal CHP': '', - 'central gas CHP': 'I:J', - 'central gas CHP CC': 'I:J', - 'central hydrogen CHP': 'I:J', - 'central solid biomass CHP': 'I:J', - 'central solid biomass CHP CC': 'I:J', - 'central solid biomass CHP powerboost CC': 'I:J', - # 'solid biomass power': 'J:K', - # 'solid biomass power CC': 'J:K', - 'solar': '', - 'central air-sourced heat pump': 'J:K', - 'central geothermal-sourced heat pump': 'H:K', - 'central geothermal heat source': 'H:K', - 'central excess-heat-sourced heat pump': 'H:K', - 'central water-sourced heat pump': 'H:K', - 'central ground-sourced heat pump': 'I:J', - 'central resistive heater': 'I:J', - 'central gas boiler': 'I:J', - 'decentral gas boiler': 'I:J', - 'direct firing gas': 'H:I', - 'direct firing gas CC': 'H:I', - 'direct firing solid fuels': 'H:I', - 'direct firing solid fuels CC': 'H:I', - 'decentral ground-sourced heat pump': 'I:J', - 'decentral air-sourced heat pump': 'I:J', - 'central water pit storage': 'J:K', - 'central water tank storage': 'J:K', - 'decentral water tank storage': 'J:K', - 'fuel cell': 'I:J', - 'hydrogen storage underground': 'J:K', - 'hydrogen storage tank type 1 including compressor': 'J:K', - 'micro CHP': 'I:J', - 'biogas': 'I:J', - 'biogas CC': 'I:J', - 'biogas upgrading': 'I:J', - 'electrolysis': 'I:J', - 'battery': 'L,N', - 'direct air capture': 'I:J', - 'cement capture': 'I:J', - 'biomass CHP capture': 'I:J', - 'BioSNG': 'I:J', - 'BtL': 'J:K', - 'biomass-to-methanol': 'J:K', - 'biogas plus hydrogen': 'J:K', - 'industrial heat pump medium temperature': 'H:I', - 'industrial heat pump high temperature': 'H:I', - 'electric boiler steam': 'H:I', - 'gas boiler steam': 'H:I', - 'solid biomass boiler steam': 'H:I', - 'solid biomass boiler steam CC': 'H:I', - 'biomass boiler': 'I:J', - 'Fischer-Tropsch': 'I:J', - 'Haber-Bosch': 'I:J', - 'air separation unit': 'I:J', - 'methanolisation': 'J:K', - 'waste CHP': 'I:J', - 'waste CHP CC': 'I:J', - 'biochar pyrolysis': 'J:K', - 'biomethanation': 'J:K', - 'electrolysis small': 'I:J', - } +uncrtnty_lookup = { + "onwind": "J:K", + "offwind": "J:K", + "solar-utility": "J:K", + "solar-utility single-axis tracking": "J:K", + "solar-rooftop residential": "J:K", + "solar-rooftop commercial": "J:K", + "OCGT": "I:J", + "CCGT": "I:J", + "oil": "I:J", + "biomass CHP": "I:J", + "biomass EOP": "I:J", + "biomass HOP": "I:J", + "central coal CHP": "", + "central gas CHP": "I:J", + "central gas CHP CC": "I:J", + "central hydrogen CHP": "I:J", + "central solid biomass CHP": "I:J", + "central solid biomass CHP CC": "I:J", + "central solid biomass CHP powerboost CC": "I:J", + # 'solid biomass power': 'J:K', + # 'solid biomass power CC': 'J:K', + "solar": "", + "central air-sourced heat pump": "J:K", + "central geothermal-sourced heat pump": "H:K", + "central geothermal heat source": "H:K", + "central excess-heat-sourced heat pump": "H:K", + "central water-sourced heat pump": "H:K", + "central ground-sourced heat pump": "I:J", + "central resistive heater": "I:J", + "central gas boiler": "I:J", + "decentral gas boiler": "I:J", + "direct firing gas": "H:I", + "direct firing gas CC": "H:I", + "direct firing solid fuels": "H:I", + "direct firing solid fuels CC": "H:I", + "decentral ground-sourced heat pump": "I:J", + "decentral air-sourced heat pump": "I:J", + "central water pit storage": "J:K", + "central water tank storage": "J:K", + "decentral water tank storage": "J:K", + "fuel cell": "I:J", + "hydrogen storage underground": "J:K", + "hydrogen storage tank type 1 including compressor": "J:K", + "micro CHP": "I:J", + "biogas": "I:J", + "biogas CC": "I:J", + "biogas upgrading": "I:J", + "electrolysis": "I:J", + "battery": "L,N", + "direct air capture": "I:J", + "cement capture": "I:J", + "biomass CHP capture": "I:J", + "BioSNG": "I:J", + "BtL": "J:K", + "biomass-to-methanol": "J:K", + "biogas plus hydrogen": "J:K", + "industrial heat pump medium temperature": "H:I", + "industrial heat pump high temperature": "H:I", + "electric boiler steam": "H:I", + "gas boiler steam": "H:I", + "solid biomass boiler steam": "H:I", + "solid biomass boiler steam CC": "H:I", + "biomass boiler": "I:J", + "Fischer-Tropsch": "I:J", + "Haber-Bosch": "I:J", + "air separation unit": "I:J", + "methanolisation": "J:K", + "waste CHP": "I:J", + "waste CHP CC": "I:J", + "biochar pyrolysis": "J:K", + "biomethanation": "J:K", + "electrolysis small": "I:J", +} # since February 2022 DEA uses a new format for the technology data # all excel sheets of updated technologies have a different layout and are # given in EUR_2020 money (instead of EUR_2015) -cost_year_2020 = ['solar-utility', - 'solar-utility single-axis tracking', - 'solar-rooftop residential', - 'solar-rooftop commercial', - 'offwind', - 'electrolysis', - 'biogas', - 'biogas CC', - 'biogas upgrading', - 'direct air capture', - 'biomass CHP capture', - 'cement capture', - 'BioSNG', - 'BtL', - 'biomass-to-methanol', - 'biogas plus hydrogen', - 'methanolisation', - 'Fischer-Tropsch', - 'biochar pyrolysis', - 'biomethanation', - 'electrolysis small', - ] - -cost_year_2019 = ['direct firing gas', - 'direct firing gas CC', - 'direct firing solid fuels', - 'direct firing solid fuels CC', - 'industrial heat pump medium temperature', - 'industrial heat pump high temperature', - 'electric boiler steam', - 'gas boiler steam', - 'solid biomass boiler steam', - 'solid biomass boiler steam CC', - ] +cost_year_2020 = [ + "solar-utility", + "solar-utility single-axis tracking", + "solar-rooftop residential", + "solar-rooftop commercial", + "offwind", + "electrolysis", + "biogas", + "biogas CC", + "biogas upgrading", + "direct air capture", + "biomass CHP capture", + "cement capture", + "BioSNG", + "BtL", + "biomass-to-methanol", + "biogas plus hydrogen", + "methanolisation", + "Fischer-Tropsch", + "biochar pyrolysis", + "biomethanation", + "electrolysis small", +] + +cost_year_2019 = [ + "direct firing gas", + "direct firing gas CC", + "direct firing solid fuels", + "direct firing solid fuels CC", + "industrial heat pump medium temperature", + "industrial heat pump high temperature", + "electric boiler steam", + "gas boiler steam", + "solid biomass boiler steam", + "solid biomass boiler steam CC", +] # -------- FUNCTIONS --------------------------------------------------- + def get_excel_sheets(excel_files): - """" + """ + " read all excel sheets and return them as a dictionary (data_in) """ @@ -281,54 +287,60 @@ def get_excel_sheets(excel_files): def get_sheet_location(tech, sheet_names, data_in): """ - looks up in which excel file technology is saved + Looks up in which excel file technology is saved """ for key in data_in: if sheet_names[tech] in data_in[key]: return key print("******* warning *************") - print("tech ", tech, " with sheet name ", sheet_names[tech], - " not found in excel sheets.") + print( + "tech ", + tech, + " with sheet name ", + sheet_names[tech], + " not found in excel sheets.", + ) print("****************************") return None + # + def get_dea_maritime_data(fn, data): """ Get technology data for shipping from DEA. """ - sheet_names = ['Container feeder, diesel', - 'Container feeder, methanol', - 'Container feeder, ammonia', - 'Container, diesel', - 'Container, methanol', - 'Container, ammonia', - 'Tank&bulk, diesel', - 'Tank&bulk, methanol', - 'Tankbulk, ammonia', - ] - excel = pd.read_excel(fn, - sheet_name=sheet_names, - index_col=[0,1], - usecols="A:F", - na_values="N/A") - - wished_index = ["Typical ship lifetime (years)", - "Upfront ship cost (mill. €)", - "Fixed O&M (€/year)", - "Variable O&M (€/nm)", - ] - - + sheet_names = [ + "Container feeder, diesel", + "Container feeder, methanol", + "Container feeder, ammonia", + "Container, diesel", + "Container, methanol", + "Container, ammonia", + "Tank&bulk, diesel", + "Tank&bulk, methanol", + "Tankbulk, ammonia", + ] + excel = pd.read_excel( + fn, sheet_name=sheet_names, index_col=[0, 1], usecols="A:F", na_values="N/A" + ) + + wished_index = [ + "Typical ship lifetime (years)", + "Upfront ship cost (mill. €)", + "Fixed O&M (€/year)", + "Variable O&M (€/nm)", + ] + for sheet in excel.keys(): df = excel[sheet] - df = df.iloc[1:,:].set_axis(df.iloc[0], axis=1) - + df = df.iloc[1:, :].set_axis(df.iloc[0], axis=1) + assert "Typical operational speed" in df.index.get_level_values(1)[22] # in unit GJ/nm efficiency = df.iloc[22] - + df = df[df.index.get_level_values(1).isin(wished_index)] df = df.droplevel(level=0) df.loc["efficiency (GJ/nm)"] = efficiency @@ -336,157 +348,171 @@ def get_dea_maritime_data(fn, data): df = df.astype(float) df = df.interpolate(axis=1, limit_direction="both") df = df[years] - + # dropna df = df.dropna(how="all", axis=0) # add column for units - df["unit"] = (df.rename(index=lambda x: - x[x.rfind("(")+1: x.rfind(")")]).index.values) + df["unit"] = df.rename( + index=lambda x: x[x.rfind("(") + 1 : x.rfind(")")] + ).index.values df["unit"] = df.unit.str.replace("€", "EUR") # remove units from index - df.index = df.index.str.replace(r" \(.*\)","", regex=True) - + df.index = df.index.str.replace(r" \(.*\)", "", regex=True) + # convert million Euro -> Euro - df_i = df[df.unit == 'mill. EUR'].index + df_i = df[df.unit == "mill. EUR"].index df.loc[df_i, years] *= 1e6 df.loc[df_i, "unit"] = "EUR" - + # convert FOM in % of investment/year - if 'Fixed O&M' in df.index: - df.loc['Fixed O&M', years] /= (df.loc['Upfront ship cost', years] - * 100) - df.loc['Fixed O&M', "unit"] = "%/year" - + if "Fixed O&M" in df.index: + df.loc["Fixed O&M", years] /= df.loc["Upfront ship cost", years] * 100 + df.loc["Fixed O&M", "unit"] = "%/year" + # convert nm in km # 1 Nautical Mile (nm) = 1.852 Kilometers (km) - df_i = df[df.unit.str.contains('/nm')].index + df_i = df[df.unit.str.contains("/nm")].index df.loc[df_i, years] /= 1.852 df.loc[df_i, "unit"] = df.loc[df_i, "unit"].str.replace("/nm", "/km") - + # 1 GJ = 1/3600 * 1e9 Wh = 1/3600 * 1e3 MWh - df_i = df[df.unit.str.contains('GJ')].index - df.loc[df_i, years] *= 1e3/3600 + df_i = df[df.unit.str.contains("GJ")].index + df.loc[df_i, years] *= 1e3 / 3600 df.loc[df_i, "unit"] = df.loc[df_i, "unit"].str.replace("GJ", "MWh") - + # add source + cost year df["source"] = f"Danish Energy Agency, {fn}" # cost year is 2023 p.10 df["currency_year"] = 2023 # add sheet name - df['further description'] = sheet - + df["further description"] = sheet + # FOM, VOM,efficiency, lifetime, investment - rename = {'Typical ship lifetime': "lifetime", - 'Upfront ship cost': "investment", - 'Fixed O&M': "FOM", - 'Variable O&M': "VOM", - } - + rename = { + "Typical ship lifetime": "lifetime", + "Upfront ship cost": "investment", + "Fixed O&M": "FOM", + "Variable O&M": "VOM", + } + df = df.rename(index=rename) - + df = pd.concat([df], keys=[sheet], names=["technology", "parameter"]) - + data = pd.concat([data, df]) - + return data - - - + + def get_dea_vehicle_data(fn, data): """ Get heavy-duty vehicle data from DEA. """ - sheet_names = ['Diesel L1', 'Diesel L2', 'Diesel L3', - 'Diesel B1', 'Diesel B2', - 'BEV L1', 'BEV L2', 'BEV L3', - 'BEV B1', 'BEV B2', - 'FCV L1', 'FCV L2', 'FCV L3', - 'FCV B1', 'FCV B2'] - excel = pd.read_excel(fn, - sheet_name=sheet_names, - index_col=0, - usecols="A:F", - na_values="no data") - - wished_index = ["Typical vehicle lifetime (years)", - "Upfront vehicle cost (€)", - "Fixed maintenance cost (€/year)", - "Variable maintenance cost (€/km)", - "Motor size (kW)", - ] - + sheet_names = [ + "Diesel L1", + "Diesel L2", + "Diesel L3", + "Diesel B1", + "Diesel B2", + "BEV L1", + "BEV L2", + "BEV L3", + "BEV B1", + "BEV B2", + "FCV L1", + "FCV L2", + "FCV L3", + "FCV B1", + "FCV B2", + ] + excel = pd.read_excel( + fn, sheet_name=sheet_names, index_col=0, usecols="A:F", na_values="no data" + ) + + wished_index = [ + "Typical vehicle lifetime (years)", + "Upfront vehicle cost (€)", + "Fixed maintenance cost (€/year)", + "Variable maintenance cost (€/km)", + "Motor size (kW)", + ] + # clarify DEA names - types = {"L1": "Truck Solo max 26 tons", - "L2": "Truck Trailer max 56 tons", - "L3": "Truck Semi-Trailer max 50 tons", - "B1": "Bus city", - "B2": "Coach"} - + types = { + "L1": "Truck Solo max 26 tons", + "L2": "Truck Trailer max 56 tons", + "L3": "Truck Semi-Trailer max 50 tons", + "B1": "Bus city", + "B2": "Coach", + } + for sheet in excel.keys(): df = excel[sheet] tech = sheet.split()[0] + " " + types.get(sheet.split()[1], "") - df = df.iloc[1:,:].set_axis(df.iloc[0], axis=1) - # "Fuel energy - typical load (MJ/km)" + df = df.iloc[1:, :].set_axis(df.iloc[0], axis=1) + # "Fuel energy - typical load (MJ/km)" # represents efficiency for average weight vehicle carries during normal # operation, currently assuming mean between urban, regional and long haul - assert df.index[27] == 'Fuel energy - typical load (MJ/km)' - efficiency = df.iloc[28:31].mean() + assert df.index[27] == "Fuel energy - typical load (MJ/km)" + efficiency = df.iloc[28:31].mean() df = df[df.index.isin(wished_index)] df.loc["efficiency (MJ/km)"] = efficiency df = df.reindex(columns=pd.Index(years).union(df.columns)) df = df.interpolate(axis=1, limit_direction="both") df = df[years] - + # add column for units - df["unit"] = (df.rename(index=lambda x: - x[x.rfind("(")+1: x.rfind(")")]).index.values) + df["unit"] = df.rename( + index=lambda x: x[x.rfind("(") + 1 : x.rfind(")")] + ).index.values df["unit"] = df.unit.str.replace("€", "EUR") # remove units from index - df.index = df.index.str.replace(r" \(.*\)","", regex=True) - + df.index = df.index.str.replace(r" \(.*\)", "", regex=True) + # convert MJ in kWh -> 1 kWh = 3.6 MJ - df_i = df.index[df.unit=="MJ/km"] + df_i = df.index[df.unit == "MJ/km"] df.loc[df_i, years] /= 3.6 - df.loc[df_i, "unit"] = "kWh/km" - + df.loc[df_i, "unit"] = "kWh/km" + # convert FOM in % of investment/year - df.loc["Fixed maintenance cost", years] /= (df.loc["Upfront vehicle cost", years] - * 100) + df.loc["Fixed maintenance cost", years] /= ( + df.loc["Upfront vehicle cost", years] * 100 + ) df.loc["Fixed maintenance cost", "unit"] = "%/year" - + # clarify costs are per vehicle df.loc["Upfront vehicle cost", "unit"] += "/vehicle" - + # add source + cost year df["source"] = f"Danish Energy Agency, {fn}" # cost year is 2022 p.12 df["currency_year"] = 2022 # add sheet name - df['further description'] = sheet - + df["further description"] = sheet + # FOM, VOM,efficiency, lifetime, investment - rename = {'Typical vehicle lifetime': "lifetime", - 'Upfront vehicle cost': "investment", - 'Fixed maintenance cost': "FOM", - 'Variable maintenance cost': "VOM", - } - + rename = { + "Typical vehicle lifetime": "lifetime", + "Upfront vehicle cost": "investment", + "Fixed maintenance cost": "FOM", + "Variable maintenance cost": "VOM", + } + df = df.rename(index=rename) - - to_keep = ['Motor size', 'lifetime', "FOM", "VOM", "efficiency", - "investment"] + + to_keep = ["Motor size", "lifetime", "FOM", "VOM", "efficiency", "investment"] df = df[df.index.isin(to_keep)] - + df = pd.concat([df], keys=[tech], names=["technology", "parameter"]) - + data = pd.concat([data, df]) - + return data - + def get_data_DEA(tech, data_in, expectation=None): """ - interpolate cost for a given technology from DEA database sheet + Interpolate cost for a given technology from DEA database sheet uncertainty can be "optimist", "pessimist" or None|"" """ @@ -495,14 +521,24 @@ def get_data_DEA(tech, data_in, expectation=None): print("excel file not found for tech ", tech) return None - if tech=="battery": + if tech == "battery": usecols = "B:J" - elif tech in ['direct air capture', 'cement capture', 'biomass CHP capture']: + elif tech in ["direct air capture", "cement capture", "biomass CHP capture"]: usecols = "A:F" - elif tech in ['industrial heat pump medium temperature', 'industrial heat pump high temperature', - 'electric boiler steam', "gas boiler steam", "solid biomass boiler steam", "solid biomass boiler steam CC", "direct firing gas", "direct firing gas CC", "direct firing solid fuels", "direct firing solid fuels CC"]: + elif tech in [ + "industrial heat pump medium temperature", + "industrial heat pump high temperature", + "electric boiler steam", + "gas boiler steam", + "solid biomass boiler steam", + "solid biomass boiler steam CC", + "direct firing gas", + "direct firing gas CC", + "direct firing solid fuels", + "direct firing solid fuels CC", + ]: usecols = "A:E" - elif tech in ['Fischer-Tropsch', 'Haber-Bosch', 'air separation unit']: + elif tech in ["Fischer-Tropsch", "Haber-Bosch", "air separation unit"]: usecols = "B:F" elif tech in ["central water-sourced heat pump"]: usecols = "B,I,K" @@ -511,23 +547,27 @@ def get_data_DEA(tech, data_in, expectation=None): usecols += f",{uncrtnty_lookup[tech]}" - - if ((tech in cost_year_2019) or (tech in cost_year_2020) or ("renewable_fuels" in excel_file)): + if ( + (tech in cost_year_2019) + or (tech in cost_year_2020) + or ("renewable_fuels" in excel_file) + ): skiprows = [0] else: - skiprows = [0,1] - - excel = pd.read_excel(excel_file, - sheet_name=sheet_names[tech], - index_col=0, - usecols=usecols, - skiprows=skiprows, - na_values="N.A") + skiprows = [0, 1] + + excel = pd.read_excel( + excel_file, + sheet_name=sheet_names[tech], + index_col=0, + usecols=usecols, + skiprows=skiprows, + na_values="N.A", + ) # print(excel) excel.dropna(axis=1, how="all", inplace=True) - excel.index = excel.index.fillna(" ") excel.index = excel.index.astype(str) excel.dropna(axis=0, how="all", inplace=True) @@ -536,9 +576,14 @@ def get_data_DEA(tech, data_in, expectation=None): if tech in ["central water-sourced heat pump"]: # use only upper uncertainty range for systems without existing water intake # convert "Uncertainty (2025)"" to "2025", "Uncertainty (2050)"" to "2050" (and so on if more years are added) - this_years = excel.loc[:,excel.iloc[1,:]=="Lower"].iloc[0,:].str.slice(-5,-1).astype(int) + this_years = ( + excel.loc[:, excel.iloc[1, :] == "Lower"] + .iloc[0, :] + .str.slice(-5, -1) + .astype(int) + ) # get values in upper uncertainty range - excel = excel.loc[:,excel.iloc[1,:]=="Upper"] + excel = excel.loc[:, excel.iloc[1, :] == "Upper"] # rename columns to years constructed above excel.columns = this_years # add missing years @@ -557,8 +602,11 @@ def get_data_DEA(tech, data_in, expectation=None): # Extrapolation for missing values (not native in pandas) # Currently, this is only first column (2020), since DEA data is available for 2025 and 2050 if excel.iloc[:, 0].isnull().all(): - excel.iloc[:, 0] = excel.iloc[:, 1] + (excel.iloc[:, 1] - excel.iloc[:, 2]) / (excel.columns[2] - excel.columns[1]) * (excel.columns[1] - excel.columns[0]) - + excel.iloc[:, 0] = excel.iloc[:, 1] + ( + excel.iloc[:, 1] - excel.iloc[:, 2] + ) / (excel.columns[2] - excel.columns[1]) * ( + excel.columns[1] - excel.columns[0] + ) if 2020 not in excel.columns: selection = excel[excel.isin([2020])].dropna(how="all").index @@ -568,16 +616,27 @@ def get_data_DEA(tech, data_in, expectation=None): uncertainty_columns = ["2050-optimist", "2050-pessimist"] if uncrtnty_lookup[tech]: # hydrogen storage sheets have reverse order of lower/upper estimates - if tech in ["hydrogen storage tank type 1 including compressor", "hydrogen storage cavern"]: + if tech in [ + "hydrogen storage tank type 1 including compressor", + "hydrogen storage cavern", + ]: uncertainty_columns.reverse() - excel.rename(columns={excel.columns[-2]: uncertainty_columns[0], - excel.columns[-1]: uncertainty_columns[1] - }, inplace=True) + excel.rename( + columns={ + excel.columns[-2]: uncertainty_columns[0], + excel.columns[-1]: uncertainty_columns[1], + }, + inplace=True, + ) else: for col in uncertainty_columns: - excel.loc[:,col] = excel.loc[:,2050] + excel.loc[:, col] = excel.loc[:, 2050] - swap_patterns = ["technical life", "efficiency", "Hydrogen output, at LHV"] # cases where bigger is better + swap_patterns = [ + "technical life", + "efficiency", + "Hydrogen output, at LHV", + ] # cases where bigger is better swap = [any(term in idx.lower() for term in swap_patterns) for idx in excel.index] tmp = excel.loc[swap, "2050-pessimist"] excel.loc[swap, "2050-pessimist"] = excel.loc[swap, "2050-optimist"] @@ -586,53 +645,65 @@ def get_data_DEA(tech, data_in, expectation=None): if expectation: # drop duplicates excel = excel[~excel.index.duplicated()] - excel.loc[:,2050] = excel.loc[:,f"2050-{expectation}"].combine_first(excel.loc[:,2050]) + excel.loc[:, 2050] = excel.loc[:, f"2050-{expectation}"].combine_first( + excel.loc[:, 2050] + ) excel.drop(columns=uncertainty_columns, inplace=True) # fix for battery with different excel sheet format if tech == "battery": - excel.rename(columns={"Technology":2040}, inplace=True) + excel.rename(columns={"Technology": 2040}, inplace=True) if expectation: - excel = excel.loc[:,[2020,2050]] - - parameters = ["efficiency", "investment", "Fixed O&M", - "Variable O&M", "production capacity for one unit", - "Output capacity expansion cost", - "Hydrogen Output", - "Hydrogen (% total input_e (MWh / MWh))", - "Hydrogen [% total input_e", - " - hereof recoverable for district heating (%-points of heat loss)", - "Cb coefficient", - "Cv coefficient", - "Distribution network costs", "Technical life", - "Energy storage expansion cost", - 'Output capacity expansion cost (M€2015/MW)', - 'Heat input', 'Heat input', 'Electricity input', 'Eletricity input', 'Heat out', - 'capture rate', - "FT Liquids Output, MWh/MWh Total Input", - " - hereof recoverable for district heating [%-points of heat loss]", - " - hereof recoverable for district heating (%-points of heat loss)", - "Bio SNG Output [% of fuel input]", - "Methanol Output", - "District heat Output", - "Electricity Output", - "Total O&M", - "Biochar Output", # biochar pyrolysis - "Pyrolysis oil Output", # biochar pyrolysis - "Pyrolysis gas Output", # biochar pyrolysis - "Heat Output", # biochar pyrolysis - "Specific energy content [GJ/ton] biochar", # biochar pyrolysis - 'Electricity Consumption', - 'Feedstock Consumption', # biochar pyrolysis - 'Methane Output', - 'CO2 Consumption', - 'Hydrogen Consumption', - ' - of which is equipment excluding heat pump', - ' - of which is heat pump including its installation', - 'Input capacity', - 'Output capacity', - 'Energy storage capacity'] + excel = excel.loc[:, [2020, 2050]] + + parameters = [ + "efficiency", + "investment", + "Fixed O&M", + "Variable O&M", + "production capacity for one unit", + "Output capacity expansion cost", + "Hydrogen Output", + "Hydrogen (% total input_e (MWh / MWh))", + "Hydrogen [% total input_e", + " - hereof recoverable for district heating (%-points of heat loss)", + "Cb coefficient", + "Cv coefficient", + "Distribution network costs", + "Technical life", + "Energy storage expansion cost", + "Output capacity expansion cost (M€2015/MW)", + "Heat input", + "Heat input", + "Electricity input", + "Eletricity input", + "Heat out", + "capture rate", + "FT Liquids Output, MWh/MWh Total Input", + " - hereof recoverable for district heating [%-points of heat loss]", + " - hereof recoverable for district heating (%-points of heat loss)", + "Bio SNG Output [% of fuel input]", + "Methanol Output", + "District heat Output", + "Electricity Output", + "Total O&M", + "Biochar Output", # biochar pyrolysis + "Pyrolysis oil Output", # biochar pyrolysis + "Pyrolysis gas Output", # biochar pyrolysis + "Heat Output", # biochar pyrolysis + "Specific energy content [GJ/ton] biochar", # biochar pyrolysis + "Electricity Consumption", + "Feedstock Consumption", # biochar pyrolysis + "Methane Output", + "CO2 Consumption", + "Hydrogen Consumption", + " - of which is equipment excluding heat pump", + " - of which is heat pump including its installation", + "Input capacity", + "Output capacity", + "Energy storage capacity", + ] df = pd.DataFrame() for para in parameters: @@ -640,88 +711,151 @@ def get_data_DEA(tech, data_in, expectation=None): attr = excel[[para in index for index in excel.index]] if len(attr) != 0: df = pd.concat([df, attr]) - df.index = df.index.str.replace('€', 'EUR') + df.index = df.index.str.replace("€", "EUR") df = df.reindex(columns=df.columns[df.columns.isin(years)]) - df = df[~df.index.duplicated(keep='first')] + df = df[~df.index.duplicated(keep="first")] # replace missing data df.replace("-", np.nan, inplace=True) # average data in format "lower_value-upper_value" - df = df.apply(lambda row: row.apply(lambda x: (float(x.split("-")[0]) - + float(x.split("-")[1])) - / 2 if isinstance(x, str) and "-" in x else x), - axis=1) + df = df.apply( + lambda row: row.apply( + lambda x: (float(x.split("-")[0]) + float(x.split("-")[1])) / 2 + if isinstance(x, str) and "-" in x + else x + ), + axis=1, + ) # remove symbols "~", ">", "<" and " " for sym in ["~", ">", "<", " "]: - df = df.apply(lambda col: col.apply(lambda x: x.replace(sym, "") - if isinstance(x, str) else x)) - + df = df.apply( + lambda col: col.apply( + lambda x: x.replace(sym, "") if isinstance(x, str) else x + ) + ) df = df.astype(float) - df = df.mask(df.apply(pd.to_numeric, errors='coerce').isnull(), df.astype(str).apply(lambda x: x.str.strip())) + df = df.mask( + df.apply(pd.to_numeric, errors="coerce").isnull(), + df.astype(str).apply(lambda x: x.str.strip()), + ) # print(df) ## Modify data loaded from DEA on a per-technology case - if (tech == "offwind") and snakemake.config['offwind_no_gridcosts']: - df.loc['Nominal investment (*total) [MEUR/MW_e, 2020]'] -= excel.loc['Nominal investment (installation: grid connection) [M€/MW_e, 2020]'] + if (tech == "offwind") and snakemake.config["offwind_no_gridcosts"]: + df.loc["Nominal investment (*total) [MEUR/MW_e, 2020]"] -= excel.loc[ + "Nominal investment (installation: grid connection) [M€/MW_e, 2020]" + ] # Exlucde indirect costs for centralised system with additional piping. - if tech.startswith('industrial heat pump'): - df = df.drop('Indirect investments cost (MEUR per MW)') + if tech.startswith("industrial heat pump"): + df = df.drop("Indirect investments cost (MEUR per MW)") - if tech == 'biogas plus hydrogen': + if tech == "biogas plus hydrogen": df.drop(df.loc[df.index.str.contains("GJ SNG")].index, inplace=True) - if tech == 'BtL': + if tech == "BtL": df.drop(df.loc[df.index.str.contains("1,000 t FT Liquids")].index, inplace=True) if tech == "biomass-to-methanol": df.drop(df.loc[df.index.str.contains("1,000 t Methanol")].index, inplace=True) - if tech == 'methanolisation': + if tech == "methanolisation": df.drop(df.loc[df.index.str.contains("1,000 t Methanol")].index, inplace=True) - if tech == 'Fischer-Tropsch': + if tech == "Fischer-Tropsch": df.drop(df.loc[df.index.str.contains("l FT Liquids")].index, inplace=True) - if tech == 'biomass boiler': - df.drop(df.loc[df.index.str.contains("Possible additional")].index, inplace=True) + if tech == "biomass boiler": + df.drop( + df.loc[df.index.str.contains("Possible additional")].index, inplace=True + ) df.drop(df.loc[df.index.str.contains("Total efficiency")].index, inplace=True) if tech == "Haber-Bosch": - df.drop(df.loc[df.index.str.contains("Specific investment mark-up factor optional ASU")].index, inplace=True) - df.drop(df.loc[df.index.str.contains("Specific investment (MEUR /TPD Ammonia output", regex=False)].index, inplace=True) - df.drop(df.loc[df.index.str.contains("Fixed O&M (MEUR /TPD Ammonia", regex=False)].index, inplace=True) - df.drop(df.loc[df.index.str.contains("Variable O&M (EUR /t Ammonia)", regex=False)].index, inplace=True) + df.drop( + df.loc[ + df.index.str.contains("Specific investment mark-up factor optional ASU") + ].index, + inplace=True, + ) + df.drop( + df.loc[ + df.index.str.contains( + "Specific investment (MEUR /TPD Ammonia output", regex=False + ) + ].index, + inplace=True, + ) + df.drop( + df.loc[ + df.index.str.contains("Fixed O&M (MEUR /TPD Ammonia", regex=False) + ].index, + inplace=True, + ) + df.drop( + df.loc[ + df.index.str.contains("Variable O&M (EUR /t Ammonia)", regex=False) + ].index, + inplace=True, + ) if tech == "air separation unit": - divisor = ((df.loc["Specific investment mark-up factor optional ASU"] - 1.0) - / excel.loc["N2 Consumption, [t/t] Ammonia"]).astype(float) - + divisor = ( + (df.loc["Specific investment mark-up factor optional ASU"] - 1.0) + / excel.loc["N2 Consumption, [t/t] Ammonia"] + ).astype(float) + # Calculate ASU cost separate to HB facility in terms of t N2 output - df.loc[[ - "Specific investment [MEUR /TPD Ammonia output]", - "Fixed O&M [kEUR /TPD Ammonia]", - "Variable O&M [EUR /t Ammonia]" - ]] *= divisor + df.loc[ + [ + "Specific investment [MEUR /TPD Ammonia output]", + "Fixed O&M [kEUR /TPD Ammonia]", + "Variable O&M [EUR /t Ammonia]", + ] + ] *= divisor # Convert output to hourly generation - df.loc[[ - "Specific investment [MEUR /TPD Ammonia output]", - "Fixed O&M [kEUR /TPD Ammonia]", - ]] *= 24 + df.loc[ + [ + "Specific investment [MEUR /TPD Ammonia output]", + "Fixed O&M [kEUR /TPD Ammonia]", + ] + ] *= 24 # Rename costs for correct units df.index = df.index.str.replace("MEUR /TPD Ammonia output", "MEUR/t_N2/h") df.index = df.index.str.replace("kEUR /TPD Ammonia", "kEUR/t_N2/h/year") df.index = df.index.str.replace("EUR /t Ammonia", "EUR/t_N2") - df.drop(df.loc[df.index.str.contains("Specific investment mark-up factor optional ASU")].index, inplace=True) - df.drop(df.loc[df.index.str.contains("Specific investment [MEUR /MW Ammonia output]", regex=False)].index, inplace=True) - df.drop(df.loc[df.index.str.contains("Fixed O&M [kEUR/MW Ammonia/year]", regex=False)].index, inplace=True) - df.drop(df.loc[df.index.str.contains("Variable O&M [EUR/MWh Ammonia]", regex=False)].index, inplace=True) - + df.drop( + df.loc[ + df.index.str.contains("Specific investment mark-up factor optional ASU") + ].index, + inplace=True, + ) + df.drop( + df.loc[ + df.index.str.contains( + "Specific investment [MEUR /MW Ammonia output]", regex=False + ) + ].index, + inplace=True, + ) + df.drop( + df.loc[ + df.index.str.contains("Fixed O&M [kEUR/MW Ammonia/year]", regex=False) + ].index, + inplace=True, + ) + df.drop( + df.loc[ + df.index.str.contains("Variable O&M [EUR/MWh Ammonia]", regex=False) + ].index, + inplace=True, + ) + if "solid biomass power" in tech: df.index = df.index.str.replace("EUR/MWeh", "EUR/MWh") @@ -729,116 +863,161 @@ def get_data_DEA(tech, data_in, expectation=None): df = biochar_pyrolysis_harmonise_dea(df) elif tech == "central geothermal-sourced heat pump": - df.loc["Nominal investment (MEUR per MW)"] = df.loc[" - of which is heat pump including its installation"] + df.loc["Nominal investment (MEUR per MW)"] = df.loc[ + " - of which is heat pump including its installation" + ] elif tech == "central geothermal heat source": - df.loc["Nominal investment (MEUR per MW)"] = df.loc[" - of which is equipment excluding heat pump"] + df.loc["Nominal investment (MEUR per MW)"] = df.loc[ + " - of which is equipment excluding heat pump" + ] df_final = pd.DataFrame(index=df.index, columns=years) # [RTD-interpolation-example] for index in df_final.index: - values = np.interp(x=years, xp=df.columns.values.astype(float), fp=df.loc[index, :].values.astype(float)) + values = np.interp( + x=years, + xp=df.columns.values.astype(float), + fp=df.loc[index, :].values.astype(float), + ) df_final.loc[index, :] = values # if year-specific data is missing and not fixed by interpolation fill forward with same values df_final = df_final.ffill(axis=1) - df_final["source"] = source_dict["DEA"] + ", " + excel_file.replace("inputs/","") - if tech in cost_year_2020 and (not ("for_carbon_capture_transport_storage" in excel_file)) and (not ("renewable_fuels" in excel_file)): + df_final["source"] = source_dict["DEA"] + ", " + excel_file.replace("inputs/", "") + if ( + tech in cost_year_2020 + and ("for_carbon_capture_transport_storage" not in excel_file) + and ("renewable_fuels" not in excel_file) + ): for attr in ["investment", "Fixed O&M"]: - to_drop = df[df.index.str.contains(attr) & - ~df.index.str.contains("\(\*total\)")].index + to_drop = df[ + df.index.str.contains(attr) & ~df.index.str.contains(r"\(\*total\)") + ].index df_final.drop(to_drop, inplace=True) - df_final["unit"] = (df_final.rename(index=lambda x: - x[x.rfind("[")+1: x.rfind("]")]).index.values) + df_final["unit"] = df_final.rename( + index=lambda x: x[x.rfind("[") + 1 : x.rfind("]")] + ).index.values else: - df_final.index = df_final.index.str.replace("\[", "(", regex=True).str.replace("\]", ")", regex=True) - df_final["unit"] = (df_final.rename(index=lambda x: - x[x.rfind("(")+1: x.rfind(")")]).index.values) - df_final.index = df_final.index.str.replace(r" \(.*\)","", regex=True) - + df_final.index = df_final.index.str.replace(r"\[", "(", regex=True).str.replace( + r"\]", ")", regex=True + ) + df_final["unit"] = df_final.rename( + index=lambda x: x[x.rfind("(") + 1 : x.rfind(")")] + ).index.values + df_final.index = df_final.index.str.replace(r" \(.*\)", "", regex=True) return df_final + def add_desalinsation_data(costs): """ - add technology data for sea water desalination (SWRO) and water storage. + Add technology data for sea water desalination (SWRO) and water storage. """ # Interpolate cost based on historic costs/cost projection to fitting year - cs = [2070,1917,1603,1282,1025] # in USD/(m^3/d) - ys = [2015,2022,2030,2040,2050] + cs = [2070, 1917, 1603, 1282, 1025] # in USD/(m^3/d) + ys = [2015, 2022, 2030, 2040, 2050] c = np.interp(year, ys, cs) - c *= 24 # in USD/(m^3/h) - c /= 1.17 # in EUR/(m^3/h) + c *= 24 # in USD/(m^3/h) + c /= 1.17 # in EUR/(m^3/h) tech = "seawater desalination" - costs.loc[(tech, 'investment'), 'value'] = c - costs.loc[(tech, 'investment'), 'unit'] = "EUR/(m^3-H2O/h)" - costs.loc[(tech, 'investment'), 'source'] = source_dict['Caldera2017'] + ", Table 4." - costs.loc[(tech, 'investment'), 'currency_year'] = 2015 - - costs.loc[(tech, 'FOM'), 'value'] = 4. - costs.loc[(tech, 'FOM'), 'unit'] = "%/year" - costs.loc[(tech, 'FOM'), 'source'] = source_dict['Caldera2016'] + ", Table 1." - - costs.loc[(tech, 'lifetime'), 'value'] = 30 - costs.loc[(tech, 'lifetime'), 'unit'] = "years" - costs.loc[(tech, 'lifetime'), 'source'] = source_dict['Caldera2016'] + ", Table 1." - - salinity = snakemake.config['desalination']['salinity'] - costs.loc[(tech, 'electricity-input'), 'value'] = (0.0003*salinity**2+0.0018*salinity+2.6043) - costs.loc[(tech, 'electricity-input'), 'unit'] = "kWh/m^3-H2O" - costs.loc[(tech, 'electricity-input'), 'source'] = source_dict['Caldera2016'] + ", Fig. 4." + costs.loc[(tech, "investment"), "value"] = c + costs.loc[(tech, "investment"), "unit"] = "EUR/(m^3-H2O/h)" + costs.loc[(tech, "investment"), "source"] = ( + source_dict["Caldera2017"] + ", Table 4." + ) + costs.loc[(tech, "investment"), "currency_year"] = 2015 + + costs.loc[(tech, "FOM"), "value"] = 4.0 + costs.loc[(tech, "FOM"), "unit"] = "%/year" + costs.loc[(tech, "FOM"), "source"] = source_dict["Caldera2016"] + ", Table 1." + + costs.loc[(tech, "lifetime"), "value"] = 30 + costs.loc[(tech, "lifetime"), "unit"] = "years" + costs.loc[(tech, "lifetime"), "source"] = source_dict["Caldera2016"] + ", Table 1." + + salinity = snakemake.config["desalination"]["salinity"] + costs.loc[(tech, "electricity-input"), "value"] = ( + 0.0003 * salinity**2 + 0.0018 * salinity + 2.6043 + ) + costs.loc[(tech, "electricity-input"), "unit"] = "kWh/m^3-H2O" + costs.loc[(tech, "electricity-input"), "source"] = ( + source_dict["Caldera2016"] + ", Fig. 4." + ) tech = "clean water tank storage" - costs.loc[(tech, 'investment'), 'value'] = 65 - costs.loc[(tech, 'investment'), 'unit'] = "EUR/m^3-H2O" - costs.loc[(tech, 'investment'), 'source'] = source_dict['Caldera2016'] + ", Table 1." - costs.loc[(tech, 'investment'), 'currency_year'] = 2013 + costs.loc[(tech, "investment"), "value"] = 65 + costs.loc[(tech, "investment"), "unit"] = "EUR/m^3-H2O" + costs.loc[(tech, "investment"), "source"] = ( + source_dict["Caldera2016"] + ", Table 1." + ) + costs.loc[(tech, "investment"), "currency_year"] = 2013 - costs.loc[(tech, 'FOM'), 'value'] = 2 - costs.loc[(tech, 'FOM'), 'unit'] = "%/year" - costs.loc[(tech, 'FOM'), 'source'] = source_dict['Caldera2016'] + ", Table 1." + costs.loc[(tech, "FOM"), "value"] = 2 + costs.loc[(tech, "FOM"), "unit"] = "%/year" + costs.loc[(tech, "FOM"), "source"] = source_dict["Caldera2016"] + ", Table 1." - costs.loc[(tech, 'lifetime'), 'value'] = 30 - costs.loc[(tech, 'lifetime'), 'unit'] = "years" - costs.loc[(tech, 'lifetime'), 'source'] = source_dict['Caldera2016'] + ", Table 1." + costs.loc[(tech, "lifetime"), "value"] = 30 + costs.loc[(tech, "lifetime"), "unit"] = "years" + costs.loc[(tech, "lifetime"), "source"] = source_dict["Caldera2016"] + ", Table 1." return costs def add_co2_intensity(costs): - """" + """ + " add CO2 intensity for the carriers """ TJ_to_MWh = 277.78 - costs.loc[('gas', 'CO2 intensity'), 'value'] = 55827 / 1e3 / TJ_to_MWh # Erdgas - costs.loc[('coal', 'CO2 intensity'), 'value'] = 93369 / 1e3 / TJ_to_MWh # Steinkohle - costs.loc[('lignite', 'CO2 intensity'), 'value'] = 113031 / 1e3 / TJ_to_MWh # Rohbraunkohle Rheinland - costs.loc[('oil', 'CO2 intensity'), 'value'] = 74020 / 1e3 / TJ_to_MWh # Heizöl, leicht - costs.loc[('methanol', 'CO2 intensity'), 'value'] = 0.2482 # t_CO2/MWh_th, based on stochiometric composition. - costs.loc[('solid biomass', 'CO2 intensity'), 'value'] = 0.3 - - oil_specific_energy = 44 #GJ/t - CO2_CH2_mass_ratio = 44/14 #kg/kg (1 mol per mol) - CO2_C_mass_ratio = 44/12 #kg/kg - methane_specific_energy = 50 #GJ/t - CO2_CH4_mass_ratio = 44/16 #kg/kg (1 mol per mol) - biomass_specific_energy = 18 #GJ/t LHV + costs.loc[("gas", "CO2 intensity"), "value"] = 55827 / 1e3 / TJ_to_MWh # Erdgas + costs.loc[("coal", "CO2 intensity"), "value"] = ( + 93369 / 1e3 / TJ_to_MWh + ) # Steinkohle + costs.loc[("lignite", "CO2 intensity"), "value"] = ( + 113031 / 1e3 / TJ_to_MWh + ) # Rohbraunkohle Rheinland + costs.loc[("oil", "CO2 intensity"), "value"] = ( + 74020 / 1e3 / TJ_to_MWh + ) # Heizöl, leicht + costs.loc[("methanol", "CO2 intensity"), "value"] = ( + 0.2482 # t_CO2/MWh_th, based on stochiometric composition. + ) + costs.loc[("solid biomass", "CO2 intensity"), "value"] = 0.3 + + oil_specific_energy = 44 # GJ/t + CO2_CH2_mass_ratio = 44 / 14 # kg/kg (1 mol per mol) + CO2_C_mass_ratio = 44 / 12 # kg/kg + methane_specific_energy = 50 # GJ/t + CO2_CH4_mass_ratio = 44 / 16 # kg/kg (1 mol per mol) + biomass_specific_energy = 18 # GJ/t LHV biomass_carbon_content = 0.5 - costs.loc[('oil', 'CO2 intensity'), 'value'] = (1/oil_specific_energy) * 3.6 * CO2_CH2_mass_ratio #tCO2/MWh - costs.loc[('gas', 'CO2 intensity'), 'value'] = (1/methane_specific_energy) * 3.6 * CO2_CH4_mass_ratio #tCO2/MWh - costs.loc[('solid biomass', 'CO2 intensity'), 'value'] = biomass_carbon_content * (1/biomass_specific_energy) * 3.6 * CO2_C_mass_ratio #tCO2/MWh - - costs.loc[('oil', 'CO2 intensity'), 'source'] = "Stoichiometric calculation with 44 GJ/t diesel and -CH2- approximation of diesel" - costs.loc[('gas', 'CO2 intensity'), 'source'] = "Stoichiometric calculation with 50 GJ/t CH4" - costs.loc[('solid biomass', 'CO2 intensity'), 'source'] = "Stoichiometric calculation with 18 GJ/t_DM LHV and 50% C-content for solid biomass" - costs.loc[('coal', 'CO2 intensity'), 'source'] = source_dict["co2"] - costs.loc[('lignite', 'CO2 intensity'), 'source'] = source_dict["co2"] - + costs.loc[("oil", "CO2 intensity"), "value"] = ( + (1 / oil_specific_energy) * 3.6 * CO2_CH2_mass_ratio + ) # tCO2/MWh + costs.loc[("gas", "CO2 intensity"), "value"] = ( + (1 / methane_specific_energy) * 3.6 * CO2_CH4_mass_ratio + ) # tCO2/MWh + costs.loc[("solid biomass", "CO2 intensity"), "value"] = ( + biomass_carbon_content * (1 / biomass_specific_energy) * 3.6 * CO2_C_mass_ratio + ) # tCO2/MWh + + costs.loc[("oil", "CO2 intensity"), "source"] = ( + "Stoichiometric calculation with 44 GJ/t diesel and -CH2- approximation of diesel" + ) + costs.loc[("gas", "CO2 intensity"), "source"] = ( + "Stoichiometric calculation with 50 GJ/t CH4" + ) + costs.loc[("solid biomass", "CO2 intensity"), "source"] = ( + "Stoichiometric calculation with 18 GJ/t_DM LHV and 50% C-content for solid biomass" + ) + costs.loc[("coal", "CO2 intensity"), "source"] = source_dict["co2"] + costs.loc[("lignite", "CO2 intensity"), "source"] = source_dict["co2"] costs.loc[pd.IndexSlice[:, "CO2 intensity"], "unit"] = "tCO2/MWh_th" @@ -846,84 +1025,92 @@ def add_co2_intensity(costs): def add_solar_from_other(costs): - """" + """ + " add solar from other sources than DEA (since the lifetime assumed in DEA is very optimistic) """ # solar utility from Vartiaian 2019 - data = np.interp(x=years, xp=[2020, 2030, 2040, 2050], - fp=[431, 275, 204, 164]) + data = np.interp(x=years, xp=[2020, 2030, 2040, 2050], fp=[431, 275, 204, 164]) # the paper says 'In this report, all results are given in real 2019 # money.' - data = data / (1 + snakemake.config['rate_inflation'])**(2019 - snakemake.config['eur_year']) + data = data / (1 + snakemake.config["rate_inflation"]) ** ( + 2019 - snakemake.config["eur_year"] + ) solar_uti = pd.Series(data=data, index=years) # solar rooftop from ETIP 2019 data = np.interp(x=years, xp=[2020, 2030, 2050], fp=[1150, 800, 550]) # using 2016 money in page 10 - data = data / (1 + snakemake.config['rate_inflation'])**(2016 - snakemake.config['eur_year']) + data = data / (1 + snakemake.config["rate_inflation"]) ** ( + 2016 - snakemake.config["eur_year"] + ) solar_roof = pd.Series(data=data, index=years) # solar utility from Vartiaian 2019 - if snakemake.config['solar_utility_from_vartiaien']: - costs.loc[('solar-utility', 'investment'), 'value'] = solar_uti[year] - costs.loc[('solar-utility', 'investment'), 'source'] = source_dict['Vartiaien'] - costs.loc[('solar-utility', 'investment'), 'currency_year'] = 2019 + if snakemake.config["solar_utility_from_vartiaien"]: + costs.loc[("solar-utility", "investment"), "value"] = solar_uti[year] + costs.loc[("solar-utility", "investment"), "source"] = source_dict["Vartiaien"] + costs.loc[("solar-utility", "investment"), "currency_year"] = 2019 - costs.loc[('solar-utility', 'lifetime'), 'value'] = 30 - costs.loc[('solar-utility', 'lifetime'), 'source'] = source_dict['Vartiaien'] - costs.loc[('solar-utility', 'lifetime'), 'currency_year'] = 2019 + costs.loc[("solar-utility", "lifetime"), "value"] = 30 + costs.loc[("solar-utility", "lifetime"), "source"] = source_dict["Vartiaien"] + costs.loc[("solar-utility", "lifetime"), "currency_year"] = 2019 - if snakemake.config['solar_rooftop_from_etip']: + if snakemake.config["solar_rooftop_from_etip"]: # solar rooftop from ETIP 2019 - costs.loc[('solar-rooftop', 'investment'), 'value'] = solar_roof[year] - costs.loc[('solar-rooftop', 'investment'), 'source'] = source_dict['ETIP'] - costs.loc[('solar-rooftop', 'investment'), 'currency_year'] = 2019 + costs.loc[("solar-rooftop", "investment"), "value"] = solar_roof[year] + costs.loc[("solar-rooftop", "investment"), "source"] = source_dict["ETIP"] + costs.loc[("solar-rooftop", "investment"), "currency_year"] = 2019 - costs.loc[('solar-rooftop', 'lifetime'), 'value'] = 30 - costs.loc[('solar-rooftop', 'lifetime'), 'source'] = source_dict['ETIP'] - costs.loc[('solar-rooftop', 'lifetime'), 'currency_year'] = 2019 + costs.loc[("solar-rooftop", "lifetime"), "value"] = 30 + costs.loc[("solar-rooftop", "lifetime"), "source"] = source_dict["ETIP"] + costs.loc[("solar-rooftop", "lifetime"), "currency_year"] = 2019 # lifetime&efficiency for solar - costs.loc[('solar', 'lifetime'), 'value'] = costs.loc[( - ['solar-rooftop', 'solar-utility'], 'lifetime'), 'value'].mean() - costs.loc[('solar', 'lifetime'), 'unit'] = 'years' - costs.loc[('solar', 'lifetime'), 'currency_year'] = 2019 - costs.loc[('solar', 'lifetime'), - 'source'] = 'Assuming 50% rooftop, 50% utility' + costs.loc[("solar", "lifetime"), "value"] = costs.loc[ + (["solar-rooftop", "solar-utility"], "lifetime"), "value" + ].mean() + costs.loc[("solar", "lifetime"), "unit"] = "years" + costs.loc[("solar", "lifetime"), "currency_year"] = 2019 + costs.loc[("solar", "lifetime"), "source"] = "Assuming 50% rooftop, 50% utility" # costs.loc[('solar', 'efficiency'), 'value'] = 1 # costs.loc[('solar', 'efficiency'), 'unit'] = 'per unit' return costs + # [add-h2-from-other] def add_h2_from_other(costs): """ - assume higher efficiency for electrolysis(0.8) and fuel cell(0.58) + Assume higher efficiency for electrolysis(0.8) and fuel cell(0.58) """ - costs.loc[('electrolysis', 'efficiency'), 'value'] = 0.8 - costs.loc[('fuel cell', 'efficiency'), 'value'] = 0.58 - costs.loc[('electrolysis', 'efficiency'), 'source'] = 'budischak2013' - costs.loc[('electrolysis', 'efficiency'), 'currency_year'] = 2013 - costs.loc[('fuel cell', 'efficiency'), 'source'] = 'budischak2013' - costs.loc[('fuel cell', 'efficiency'), 'currency_year'] = 2013 + costs.loc[("electrolysis", "efficiency"), "value"] = 0.8 + costs.loc[("fuel cell", "efficiency"), "value"] = 0.58 + costs.loc[("electrolysis", "efficiency"), "source"] = "budischak2013" + costs.loc[("electrolysis", "efficiency"), "currency_year"] = 2013 + costs.loc[("fuel cell", "efficiency"), "source"] = "budischak2013" + costs.loc[("fuel cell", "efficiency"), "currency_year"] = 2013 return costs + # [unify-diw-inflation] def unify_diw(costs): - """" + """ + " add currency year for the DIW costs from 2010 """ - costs.loc[('PHS', 'investment'), 'currency_year'] = 2010 - costs.loc[('ror', 'investment'), 'currency_year'] = 2010 - costs.loc[('hydro', 'investment'), 'currency_year'] = 2010 + costs.loc[("PHS", "investment"), "currency_year"] = 2010 + costs.loc[("ror", "investment"), "currency_year"] = 2010 + costs.loc[("hydro", "investment"), "currency_year"] = 2010 return costs -def biochar_pyrolysis_harmonise_dea (df): + +def biochar_pyrolysis_harmonise_dea(df): # data for 2020 not available if 2020 in df.columns: df.drop(columns=2020, inplace=True) @@ -935,70 +1122,99 @@ def biochar_pyrolysis_harmonise_dea (df): # all pyrolysis product except char are combusted for heat df_sum = pd.concat( - (df.iloc[df.index.str.contains("Pyrolysis oil Output")], - df.iloc[df.index.str.contains("Pyrolysis gas Output")], - df.iloc[df.index.str.contains("Heat Output")]), axis=0).sum(axis=0, skipna=False) + ( + df.iloc[df.index.str.contains("Pyrolysis oil Output")], + df.iloc[df.index.str.contains("Pyrolysis gas Output")], + df.iloc[df.index.str.contains("Heat Output")], + ), + axis=0, + ).sum(axis=0, skipna=False) df.iloc[df.index.str.contains("Heat Output")] = df_sum * 100 - to_drop = df[df.index.str.contains("Pyrolysis oil Output") | - df.index.str.contains("Pyrolysis gas Output") | df.index.str.contains( - "Electricity Consumption") | - df.index.str.contains("Feedstock Consumption")].index + to_drop = df[ + df.index.str.contains("Pyrolysis oil Output") + | df.index.str.contains("Pyrolysis gas Output") + | df.index.str.contains("Electricity Consumption") + | df.index.str.contains("Feedstock Consumption") + ].index df.drop(to_drop, inplace=True) # normalizing costs to biochar output - df_divid = pd.concat((df.iloc[df.index.str.contains("Biochar Output")], - df.iloc[df.index.str.contains("Heat Output")]), axis=0).sum(axis=0, skipna=False) + df_divid = pd.concat( + ( + df.iloc[df.index.str.contains("Biochar Output")], + df.iloc[df.index.str.contains("Heat Output")], + ), + axis=0, + ).sum(axis=0, skipna=False) biochar_totoutput = df.iloc[df.index.str.contains("Biochar Output")] / df_divid idx3 = df.index.str.contains("EUR") - df.loc[idx3] = df.loc[idx3].values.astype(float) / biochar_totoutput.values.astype(float) + df.loc[idx3] = df.loc[idx3].values.astype(float) / biochar_totoutput.values.astype( + float + ) df.index = df.index.str.replace(" output from pyrolysis process", "", regex=True) - #rename units - df.rename(index={df.loc[df.index.str.contains('Specific investment')].index[0]: - df.loc[df.index.str.contains("Specific investment")].index.str.replace( - "MW", "MW_biochar")[0], - df.loc[df.index.str.contains('Fixed O&M')].index[0]: - df.loc[df.index.str.contains("Fixed O&M")].index.str.replace( - "MW", "MW_biochar")[0], - df.loc[df.index.str.contains("Variable O&M")].index[0]: - df.loc[df.index.str.contains("Variable O&M")].index.str.replace( - "MWh", "MWh_biochar")[0]}, inplace=True) - - df_div = df.iloc[df.index.str.contains("Specific energy content")].astype(float) / 3.6 - df.iloc[df.index.str.contains("Specific energy content")] = df.iloc[df.index.str.contains( - "Biochar Output")].astype(float) / df_div.values.astype(float) + # rename units + df.rename( + index={ + df.loc[df.index.str.contains("Specific investment")].index[0]: df.loc[ + df.index.str.contains("Specific investment") + ].index.str.replace("MW", "MW_biochar")[0], + df.loc[df.index.str.contains("Fixed O&M")].index[0]: df.loc[ + df.index.str.contains("Fixed O&M") + ].index.str.replace("MW", "MW_biochar")[0], + df.loc[df.index.str.contains("Variable O&M")].index[0]: df.loc[ + df.index.str.contains("Variable O&M") + ].index.str.replace("MWh", "MWh_biochar")[0], + }, + inplace=True, + ) + + df_div = ( + df.iloc[df.index.str.contains("Specific energy content")].astype(float) / 3.6 + ) + df.iloc[df.index.str.contains("Specific energy content")] = df.iloc[ + df.index.str.contains("Biochar Output") + ].astype(float) / df_div.values.astype(float) df.rename( - index={df.loc[df.index.str.contains("Specific energy content")].index.values[ - 0]: 'yield biochar [ton biochar/MWh_feedstock]', - df.loc[df.index.str.contains("Biochar Output")].index.values[ - 0]: 'efficiency biochar [MWh_biochar/MWh_feedstock]', - df.loc[df.index.str.contains("Heat Output")].index.values[ - 0]: 'efficiency heat [% MWh_feedstock]'}, inplace=True) + index={ + df.loc[df.index.str.contains("Specific energy content")].index.values[ + 0 + ]: "yield biochar [ton biochar/MWh_feedstock]", + df.loc[df.index.str.contains("Biochar Output")].index.values[ + 0 + ]: "efficiency biochar [MWh_biochar/MWh_feedstock]", + df.loc[df.index.str.contains("Heat Output")].index.values[ + 0 + ]: "efficiency heat [% MWh_feedstock]", + }, + inplace=True, + ) - #df = df.astype(float) - #df = df.mask(df.apply(pd.to_numeric, errors='coerce').isna(), df.astype(str).apply(lambda x: x.str.strip())) + # df = df.astype(float) + # df = df.mask(df.apply(pd.to_numeric, errors='coerce').isna(), df.astype(str).apply(lambda x: x.str.strip())) return df def get_data_from_DEA(data_in, expectation=None): """ - saves technology data from DEA in dictionary d_by_tech + Saves technology data from DEA in dictionary d_by_tech """ d_by_tech = {} for tech, dea_tech in sheet_names.items(): - print(f'{tech} in PyPSA corresponds to {dea_tech} in DEA database.') + print(f"{tech} in PyPSA corresponds to {dea_tech} in DEA database.") df = get_data_DEA(tech, data_in, expectation).fillna(0) d_by_tech[tech] = df return d_by_tech + def adjust_for_inflation(inflation_rate, costs, techs, ref_year, col): """ - adjust the investment costs for the specified techs for inflation. + Adjust the investment costs for the specified techs for inflation. techs: str or list One or more techs in costs index for which the inflation adjustment is done. @@ -1007,48 +1223,55 @@ def adjust_for_inflation(inflation_rate, costs, techs, ref_year, col): costs: pd.Dataframe Dataframe containing the costs data with multiindex on technology and one index key 'investment'. """ - + def get_factor(inflation_rate, ref_year, eur_year): - if (pd.isna(ref_year)) or (ref_year<1900): return np.nan - if ref_year == eur_year: return 1 + if (pd.isna(ref_year)) or (ref_year < 1900): + return np.nan + if ref_year == eur_year: + return 1 mean = inflation_rate.mean() - if ref_year< eur_year: - new_index = np.arange(ref_year+1, eur_year+1) - df = 1 + inflation_rate.reindex(new_index).fillna(mean) + if ref_year < eur_year: + new_index = np.arange(ref_year + 1, eur_year + 1) + df = 1 + inflation_rate.reindex(new_index).fillna(mean) return df.cumprod().loc[eur_year] else: - new_index = np.arange(eur_year+1, ref_year+1) + new_index = np.arange(eur_year + 1, ref_year + 1) df = 1 + inflation_rate.reindex(new_index).fillna(mean) - return 1/df.cumprod().loc[ref_year] - - inflation = costs.currency_year.apply(lambda x: get_factor(inflation_rate, x, snakemake.config['eur_year'])) + return 1 / df.cumprod().loc[ref_year] - paras = ["investment", "VOM", "fuel"] - filter_i = costs.index.get_level_values(0).isin(techs) & costs.index.get_level_values(1).isin(paras) - costs.loc[filter_i, col] = costs.loc[filter_i, col].mul(inflation.loc[filter_i], axis=0) + inflation = costs.currency_year.apply( + lambda x: get_factor(inflation_rate, x, snakemake.config["eur_year"]) + ) + paras = ["investment", "VOM", "fuel"] + filter_i = costs.index.get_level_values(0).isin( + techs + ) & costs.index.get_level_values(1).isin(paras) + costs.loc[filter_i, col] = costs.loc[filter_i, col].mul( + inflation.loc[filter_i], axis=0 + ) return costs def clean_up_units(tech_data, value_column="", source=""): """ - converts units of a pd.Dataframe tech_data to match: + Converts units of a pd.Dataframe tech_data to match: power: Mega Watt (MW) energy: Mega-Watt-hour (MWh) currency: Euro (EUR) clarifies if MW_th or MW_e """ - from currency_converter import CurrencyConverter from datetime import date - from currency_converter import ECB_URL + + from currency_converter import ECB_URL, CurrencyConverter # Currency conversion REPLACEMENTS = [ - ('€', 'EUR'), - ('$', 'USD'), - ('₤', 'GBP'), + ("€", "EUR"), + ("$", "USD"), + ("₤", "GBP"), ] # Download the full history, this will be up to date. Current value is: # https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.zip @@ -1057,7 +1280,9 @@ def clean_up_units(tech_data, value_column="", source=""): for old, new in REPLACEMENTS: tech_data.unit = tech_data.unit.str.replace(old, new, regex=False) - tech_data.loc[tech_data.unit.str.contains(new), value_column] *= c.convert(1, new, "EUR", date=date(2020, 1, 1)) + tech_data.loc[tech_data.unit.str.contains(new), value_column] *= c.convert( + 1, new, "EUR", date=date(2020, 1, 1) + ) tech_data.unit = tech_data.unit.str.replace(new, "EUR") tech_data.unit = tech_data.unit.str.replace(" per ", "/") @@ -1071,7 +1296,7 @@ def clean_up_units(tech_data, value_column="", source=""): tech_data.loc[tech_data.unit.str.contains("mio EUR"), value_column] *= 1e6 tech_data.unit = tech_data.unit.str.replace("mio EUR", "EUR") - + tech_data.loc[tech_data.unit.str.contains("mill. EUR"), value_column] *= 1e6 tech_data.unit = tech_data.unit.str.replace("mill. EUR", "EUR") @@ -1084,7 +1309,10 @@ def clean_up_units(tech_data, value_column="", source=""): tech_data.loc[tech_data.unit.str.contains("/kW"), value_column] *= 1e3 - tech_data.loc[tech_data.unit.str.contains("kW") & ~tech_data.unit.str.contains("/kW"), value_column] /= 1e3 + tech_data.loc[ + tech_data.unit.str.contains("kW") & ~tech_data.unit.str.contains("/kW"), + value_column, + ] /= 1e3 tech_data.unit = tech_data.unit.str.replace("kW", "MW") tech_data.loc[tech_data.unit.str.contains("/GWh"), value_column] /= 1e3 @@ -1102,7 +1330,9 @@ def clean_up_units(tech_data, value_column="", source=""): tech_data.unit = tech_data.unit.str.replace("EUR-2015", "EUR") tech_data.unit = tech_data.unit.str.replace("MWe", "MW_e") tech_data.unit = tech_data.unit.str.replace("EUR/MW of total input_e", "EUR/MW_e") - tech_data.unit = tech_data.unit.str.replace("MWh/MWh\)", "MWh_H2/MWh_e", regex=True) + tech_data.unit = tech_data.unit.str.replace( + r"MWh/MWh\)", "MWh_H2/MWh_e", regex=True + ) tech_data.unit = tech_data.unit.str.replace("MWth", "MW_th") tech_data.unit = tech_data.unit.str.replace("MWheat", "MW_th") tech_data.unit = tech_data.unit.str.replace("MWhth", "MWh_th") @@ -1120,62 +1350,94 @@ def clean_up_units(tech_data, value_column="", source=""): tech_data.unit = tech_data.unit.str.replace("MW SNG", "MW_CH4") tech_data.unit = tech_data.unit.str.replace("EUR/MWh of total input", "EUR/MWh_e") tech_data.unit = tech_data.unit.str.replace("EUR/MWeh", "EUR/MWh_e") - tech_data.unit = tech_data.unit.str.replace("% -points of heat loss", "MWh_th/MWh_el") - tech_data.unit = tech_data.unit.str.replace("FT Liquids Output, MWh/MWh Total Inpu", "MWh_FT/MWh_H2") + tech_data.unit = tech_data.unit.str.replace( + "% -points of heat loss", "MWh_th/MWh_el" + ) + tech_data.unit = tech_data.unit.str.replace( + "FT Liquids Output, MWh/MWh Total Inpu", "MWh_FT/MWh_H2" + ) # biomass-to-methanol-specific if isinstance(tech_data.index, pd.MultiIndex): - tech_data.loc[tech_data.index.get_level_values(1)=="Methanol Output,", "unit"] = "MWh_MeOH/MWh_th" - tech_data.loc[tech_data.index.get_level_values(1)=='District heat Output,', "unit"] = "MWh_th/MWh_th" - tech_data.loc[tech_data.index.get_level_values(1)=='Electricity Output,', "unit"] = "MWh_e/MWh_th" - + tech_data.loc[ + tech_data.index.get_level_values(1) == "Methanol Output,", "unit" + ] = "MWh_MeOH/MWh_th" + tech_data.loc[ + tech_data.index.get_level_values(1) == "District heat Output,", "unit" + ] = "MWh_th/MWh_th" + tech_data.loc[ + tech_data.index.get_level_values(1) == "Electricity Output,", "unit" + ] = "MWh_e/MWh_th" + # Ammonia-specific - tech_data.unit = tech_data.unit.str.replace("MW Ammonia output", "MW_NH3") #specific investment - tech_data.unit = tech_data.unit.str.replace("MW Ammonia", "MW_NH3") #fom - tech_data.unit = tech_data.unit.str.replace("MWh Ammonia", "MWh_NH3") #vom - tech_data.loc[tech_data.unit=='EUR/MW/y', "unit"] = 'EUR/MW/year' + tech_data.unit = tech_data.unit.str.replace( + "MW Ammonia output", "MW_NH3" + ) # specific investment + tech_data.unit = tech_data.unit.str.replace("MW Ammonia", "MW_NH3") # fom + tech_data.unit = tech_data.unit.str.replace("MWh Ammonia", "MWh_NH3") # vom + tech_data.loc[tech_data.unit == "EUR/MW/y", "unit"] = "EUR/MW/year" # convert per unit costs to MW cost_per_unit = tech_data.unit.str.contains("/unit") - tech_data.loc[cost_per_unit, value_column] = tech_data.loc[cost_per_unit, value_column].apply( - lambda x: (x / tech_data.loc[(x.name[0], - "Heat production capacity for one unit")][value_column]).iloc[0,:], - axis=1) - tech_data.loc[cost_per_unit, "unit"] = tech_data.loc[cost_per_unit, - "unit"].str.replace("/unit", "/MW_th") + tech_data.loc[cost_per_unit, value_column] = tech_data.loc[ + cost_per_unit, value_column + ].apply( + lambda x: ( + x + / tech_data.loc[(x.name[0], "Heat production capacity for one unit")][ + value_column + ] + ).iloc[0, :], + axis=1, + ) + tech_data.loc[cost_per_unit, "unit"] = tech_data.loc[ + cost_per_unit, "unit" + ].str.replace("/unit", "/MW_th") if source == "dea": # clarify MW -> MW_th # see on p.278 of docu: "However, the primary purpose of the heat pumps in the # technology catalogue is heating. In this chapter the unit MW is referring to # the heat output (also MJ/s) unless otherwise noted" - techs_mwth = ['central air-sourced heat pump', 'central geothermal-sourced heat pump', - 'central gas boiler', 'central resistive heater', 'decentral air-sourced heat pump', - 'decentral gas boiler', 'decentral ground-sourced heat pump' ] - tech_data.loc[techs_mwth, "unit"] = (tech_data.loc[techs_mwth, "unit"] - .replace({"EUR/MW": "EUR/MW_th", - "EUR/MW/year": "EUR/MW_th/year", - 'EUR/MWh':'EUR/MWh_th', - "MW": "MW_th"})) + techs_mwth = [ + "central air-sourced heat pump", + "central geothermal-sourced heat pump", + "central gas boiler", + "central resistive heater", + "decentral air-sourced heat pump", + "decentral gas boiler", + "decentral ground-sourced heat pump", + ] + tech_data.loc[techs_mwth, "unit"] = tech_data.loc[techs_mwth, "unit"].replace( + { + "EUR/MW": "EUR/MW_th", + "EUR/MW/year": "EUR/MW_th/year", + "EUR/MWh": "EUR/MWh_th", + "MW": "MW_th", + } + ) # clarify MW -> MW_e - techs_e = ['fuel cell'] - tech_data.loc[techs_e, "unit"] = (tech_data.loc[techs_e, "unit"] - .replace({"EUR/MW": "EUR/MW_e", - "EUR/MW/year": "EUR/MW_e/year", - 'EUR/MWh':'EUR/MWh_e', - "MW": "MW_e"})) + techs_e = ["fuel cell"] + tech_data.loc[techs_e, "unit"] = tech_data.loc[techs_e, "unit"].replace( + { + "EUR/MW": "EUR/MW_e", + "EUR/MW/year": "EUR/MW_e/year", + "EUR/MWh": "EUR/MWh_e", + "MW": "MW_e", + } + ) if "methanolisation" in tech_data.index: tech_data = tech_data.sort_index() - tech_data.loc[('methanolisation', 'Variable O&M'), "unit"] = "EUR/MWh_MeOH" - - tech_data.unit = tech_data.unit.str.replace("\)", "") + tech_data.loc[("methanolisation", "Variable O&M"), "unit"] = "EUR/MWh_MeOH" + + tech_data.unit = tech_data.unit.str.replace(r"\)", "") return tech_data def set_specify_assumptions(tech_data): """ - for following technologies more specific investment and efficiency + For following technologies more specific investment and efficiency assumptions are taken: - central resistive heater (investment costs for large > 10 MW @@ -1193,7 +1455,7 @@ def set_specify_assumptions(tech_data): # for central resistive heater there are investment costs for small (1-5MW) # and large (>10 MW) generators, assume the costs for large generators - to_drop = [("central resistive heater", 'Nominal investment, 400/690 V; 1-5 MW')] + to_drop = [("central resistive heater", "Nominal investment, 400/690 V; 1-5 MW")] # for decentral gas boilers total and heat efficiency given, the values are # the same, drop one of the rows to avoid duplicates @@ -1204,24 +1466,33 @@ def set_specify_assumptions(tech_data): # not connected yet those costs are added as an extra row since the # lifetime of the branchpipe is assumed to be 50 years (see comment K in # excel sheet) - boiler_connect = tech_data.loc[[("decentral gas boiler", - "Possible additional specific investment"), - ("decentral gas boiler", - "Technical lifetime")]] + boiler_connect = tech_data.loc[ + [ + ("decentral gas boiler", "Possible additional specific investment"), + ("decentral gas boiler", "Technical lifetime"), + ] + ] boiler_connect.loc[("decentral gas boiler", "Technical lifetime"), years] = 50 - boiler_connect.rename(index={"decentral gas boiler": - "decentral gas boiler connection"}, inplace=True) + boiler_connect.rename( + index={"decentral gas boiler": "decentral gas boiler connection"}, inplace=True + ) tech_data = pd.concat([tech_data, boiler_connect]) to_drop.append(("decentral gas boiler", "Possible additional specific investment")) # biogas upgrading investment costs should include grid injection costs index = tech_data.loc["biogas upgrading"].index.str.contains("investment") - name = 'investment (upgrading, methane redution and grid injection)' - inv = tech_data.loc["biogas upgrading"].loc[index].groupby(["unit", "source"]).sum().reset_index() - new = pd.concat([tech_data.loc["biogas upgrading"].loc[~index], - inv]).rename({0:name}) - new.index = pd.MultiIndex.from_product([["biogas upgrading"], - new.index.to_list()]) + name = "investment (upgrading, methane redution and grid injection)" + inv = ( + tech_data.loc["biogas upgrading"] + .loc[index] + .groupby(["unit", "source"]) + .sum() + .reset_index() + ) + new = pd.concat([tech_data.loc["biogas upgrading"].loc[~index], inv]).rename( + {0: name} + ) + new.index = pd.MultiIndex.from_product([["biogas upgrading"], new.index.to_list()]) tech_data.drop("biogas upgrading", level=0, inplace=True) tech_data = pd.concat([tech_data, new]) @@ -1234,11 +1505,11 @@ def set_specify_assumptions(tech_data): # efficiencies are lower (conservative approach) those are assumed # furthermore the total efficiency is assumed which includes auxilary electricity # consumption - name = 'Heat efficiency, annual average, net, radiators' + name = "Heat efficiency, annual average, net, radiators" techs_radiator = tech_data.xs(name, level=1).index for tech in techs_radiator: df = tech_data.loc[tech] - df = df[(~df.index.str.contains("efficiency")) | (df.index==name)] + df = df[(~df.index.str.contains("efficiency")) | (df.index == name)] df.rename(index={name: name + ", existing one family house"}, inplace=True) df.index = pd.MultiIndex.from_product([[tech], df.index.to_list()]) tech_data.drop(tech, level=0, inplace=True) @@ -1251,44 +1522,64 @@ def set_specify_assumptions(tech_data): def set_round_trip_efficiency(tech_data): """ - get round trip efficiency for hydrogen and battery storage + Get round trip efficiency for hydrogen and battery storage assume for battery sqrt(DC efficiency) and split into inverter + storage rename investment rows for easier sorting """ # hydrogen storage - to_drop = [("hydrogen storage tank type 1 including compressor", ' - Charge efficiency')] - to_drop.append(("hydrogen storage tank type 1 including compressor", ' - Discharge efficiency')) - to_drop.append(("hydrogen storage underground", ' - Charge efficiency')) - to_drop.append(("hydrogen storage underground", ' - Discharge efficiency')) - tech_data.loc[("hydrogen storage underground", "Round trip efficiency"), years] *= 100 - tech_data.loc[("hydrogen storage tank type 1 including compressor", "Round trip efficiency"), years] *= 100 - - + to_drop = [ + ("hydrogen storage tank type 1 including compressor", " - Charge efficiency") + ] + to_drop.append( + ("hydrogen storage tank type 1 including compressor", " - Discharge efficiency") + ) + to_drop.append(("hydrogen storage underground", " - Charge efficiency")) + to_drop.append(("hydrogen storage underground", " - Discharge efficiency")) + tech_data.loc[("hydrogen storage underground", "Round trip efficiency"), years] *= ( + 100 + ) + tech_data.loc[ + ("hydrogen storage tank type 1 including compressor", "Round trip efficiency"), + years, + ] *= 100 # battery split into inverter and storage, assume for efficiency sqr(round trip DC) df = tech_data.loc["battery"] - inverter = df.loc[['Round trip efficiency DC', - 'Output capacity expansion cost', - 'Technical lifetime', 'Fixed O&M']] + inverter = df.loc[ + [ + "Round trip efficiency DC", + "Output capacity expansion cost", + "Technical lifetime", + "Fixed O&M", + ] + ] - inverter.rename(index ={'Output capacity expansion cost': - 'Output capacity expansion cost investment'}, - inplace=True) + inverter.rename( + index={ + "Output capacity expansion cost": "Output capacity expansion cost investment" + }, + inplace=True, + ) # Manual correction based on footnote. - inverter.loc['Technical lifetime', years] = 10. - inverter.loc['Technical lifetime', 'source'] += ', Note K.' - - inverter.index = pd.MultiIndex.from_product([["battery inverter"], - inverter.index.to_list()]) - - storage = df.reindex(index=['Technical lifetime', - 'Energy storage expansion cost']) - storage.rename(index={'Energy storage expansion cost': - 'Energy storage expansion cost investment'}, inplace=True) - storage.index = pd.MultiIndex.from_product([["battery storage"], - storage.index.to_list()]) + inverter.loc["Technical lifetime", years] = 10.0 + inverter.loc["Technical lifetime", "source"] += ", Note K." + + inverter.index = pd.MultiIndex.from_product( + [["battery inverter"], inverter.index.to_list()] + ) + + storage = df.reindex(index=["Technical lifetime", "Energy storage expansion cost"]) + storage.rename( + index={ + "Energy storage expansion cost": "Energy storage expansion cost investment" + }, + inplace=True, + ) + storage.index = pd.MultiIndex.from_product( + [["battery storage"], storage.index.to_list()] + ) tech_data.drop("battery", level=0, inplace=True) tech_data = pd.concat([tech_data, inverter, storage]) @@ -1297,7 +1588,7 @@ def set_round_trip_efficiency(tech_data): def order_data(tech_data): """ - check if the units of different variables are conform + Check if the units of different variables are conform -> print warning if not return a pd.Dataframe 'data' in pypsa tech data syntax (investment, FOM, VOM, efficiency) @@ -1310,64 +1601,83 @@ def order_data(tech_data): df = tech_data.loc[tech] # --- investment ---- - investment = df[(df.index.str.contains("investment") | - df.index.str.contains("Distribution network costs")) - & ((df.unit == "EUR/MW") | - (df.unit == "EUR/MW_e") | - (df.unit == "EUR/MW_th - heat output") | - (df.unit == "EUR/MW_th excluding drive energy") | - (df.unit == "EUR/MW_th") | - (df.unit == "EUR/MW_MeOH") | - (df.unit == "EUR/MW_FT/year") | - (df.unit == "EUR/MW_NH3") | - (df.unit == "EUR/MWhCapacity") | - (df.unit == "EUR/MWh") | - (df.unit == "EUR/MW_CH4") | - (df.unit == "EUR/MWh/year") | - (df.unit == "EUR/MW_e, 2020") | - (df.unit == "EUR/MW input") | - (df.unit == 'EUR/MW-methanol') | - (df.unit == "EUR/t_N2/h") | # air separation unit - (df.unit == 'EUR/MW_biochar')) - ].copy() + investment = df[ + ( + df.index.str.contains("investment") + | df.index.str.contains("Distribution network costs") + ) + & ( + (df.unit == "EUR/MW") + | (df.unit == "EUR/MW_e") + | (df.unit == "EUR/MW_th - heat output") + | (df.unit == "EUR/MW_th excluding drive energy") + | (df.unit == "EUR/MW_th") + | (df.unit == "EUR/MW_MeOH") + | (df.unit == "EUR/MW_FT/year") + | (df.unit == "EUR/MW_NH3") + | (df.unit == "EUR/MWhCapacity") + | (df.unit == "EUR/MWh") + | (df.unit == "EUR/MW_CH4") + | (df.unit == "EUR/MWh/year") + | (df.unit == "EUR/MW_e, 2020") + | (df.unit == "EUR/MW input") + | (df.unit == "EUR/MW-methanol") + | (df.unit == "EUR/t_N2/h") # air separation unit + | (df.unit == "EUR/MW_biochar") + ) + ].copy() if len(investment) != 1: switch = True - print("check investment: ", tech, " ", - df[df.index.str.contains("investment")].unit) + print( + "check investment: ", + tech, + " ", + df[df.index.str.contains("investment")].unit, + ) else: investment["parameter"] = "investment" clean_df[tech] = investment # ---- FOM ---------------- if len(investment): - fixed = df[(df.index.str.contains("Fixed O&M") | - df.index.str.contains("Total O&M")) & - ((df.unit == investment.unit.iloc[0] + "/year") | - (df.unit == "EUR/MW/km/year") | - (df.unit == "EUR/MW/year") | - (df.unit == "EUR/MW_e/y, 2020") | - (df.unit == "EUR/MW_e/y") | - (df.unit == "EUR/MW_FT/year") | - (df.unit == "EUR/MWh_FT") | - (df.unit == "EUR/MW_MeOH/year") | - (df.unit == "EUR/MW_CH4/year") | - (df.unit == 'EUR/MW_biochar/year') | - (df.unit == '% of specific investment/year') | - (df.unit == investment.unit.str.split(" ").iloc[0][0] + "/year"))].copy() + fixed = df[ + ( + df.index.str.contains("Fixed O&M") + | df.index.str.contains("Total O&M") + ) + & ( + (df.unit == investment.unit.iloc[0] + "/year") + | (df.unit == "EUR/MW/km/year") + | (df.unit == "EUR/MW/year") + | (df.unit == "EUR/MW_e/y, 2020") + | (df.unit == "EUR/MW_e/y") + | (df.unit == "EUR/MW_FT/year") + | (df.unit == "EUR/MWh_FT") + | (df.unit == "EUR/MW_MeOH/year") + | (df.unit == "EUR/MW_CH4/year") + | (df.unit == "EUR/MW_biochar/year") + | (df.unit == "% of specific investment/year") + | (df.unit == investment.unit.str.split(" ").iloc[0][0] + "/year") + ) + ].copy() if (len(fixed) != 1) and (len(df[df.index.str.contains("Fixed O&M")]) != 0): switch = True - print("check FOM: ", tech, " ", - df[df.index.str.contains("Fixed O&M")].unit) + print( + "check FOM: ", + tech, + " ", + df[df.index.str.contains("Fixed O&M")].unit, + ) if len(fixed) == 1: fixed["parameter"] = "fixed" clean_df[tech] = pd.concat([clean_df[tech], fixed]) fom = pd.DataFrame(columns=fixed.columns) - if not any(fixed.unit.str.contains('% of specific investment/year')): - investment[investment==0] = float('nan') + if not any(fixed.unit.str.contains("% of specific investment/year")): + investment[investment == 0] = float("nan") investment = investment.ffill(axis=1).fillna(0) - fom[years] = fixed[years]/investment[years].values*100 + fom[years] = fixed[years] / investment[years].values * 100 else: fom[years] = fixed[years] fom["parameter"] = "FOM" @@ -1376,71 +1686,91 @@ def order_data(tech_data): clean_df[tech] = pd.concat([clean_df[tech], fom]) # ---- VOM ----- - vom = df[df.index.str.contains("Variable O&M") & ((df.unit == "EUR/MWh") | - (df.unit == "EUR/MWh_e") | - (df.unit == "EUR/MWh_th") | - (df.unit == "EUR/MWh_FT") | - (df.unit == "EUR/MWh_NH3") | - (df.unit == "EUR/MWh_MeOH") | - (df.unit == "EUR/MWh/year") | - (df.unit == "EUR/MWh/km") | - (df.unit == "EUR/MWh") | - (df.unit == "EUR/MWhoutput") | - (df.unit == "EUR/MWh_CH4") | - (df.unit == 'EUR/MWh_biochar')| - (tech == "biogas upgrading"))].copy() + vom = df[ + df.index.str.contains("Variable O&M") + & ( + (df.unit == "EUR/MWh") + | (df.unit == "EUR/MWh_e") + | (df.unit == "EUR/MWh_th") + | (df.unit == "EUR/MWh_FT") + | (df.unit == "EUR/MWh_NH3") + | (df.unit == "EUR/MWh_MeOH") + | (df.unit == "EUR/MWh/year") + | (df.unit == "EUR/MWh/km") + | (df.unit == "EUR/MWh") + | (df.unit == "EUR/MWhoutput") + | (df.unit == "EUR/MWh_CH4") + | (df.unit == "EUR/MWh_biochar") + | (tech == "biogas upgrading") + ) + ].copy() if len(vom) == 1: vom.loc[:, "parameter"] = "VOM" clean_df[tech] = pd.concat([clean_df[tech], vom]) - elif len(vom)!=1 and len(df[df.index.str.contains("Variable O&M")])!=0: + elif len(vom) != 1 and len(df[df.index.str.contains("Variable O&M")]) != 0: switch = True - print("check VOM: ", tech, " ", - df[df.index.str.contains("Variable O&M")].unit) + print( + "check VOM: ", tech, " ", df[df.index.str.contains("Variable O&M")].unit + ) # ----- lifetime -------- - lifetime = df[df.index.str.contains("Technical life") & (df.unit=="years")].copy() - if len(lifetime)!=1: - switch = True - print("check lifetime: ", tech, " ", - df[df.index.str.contains("Technical life")].unit) + lifetime = df[ + df.index.str.contains("Technical life") & (df.unit == "years") + ].copy() + if len(lifetime) != 1: + switch = True + print( + "check lifetime: ", + tech, + " ", + df[df.index.str.contains("Technical life")].unit, + ) else: lifetime["parameter"] = "lifetime" clean_df[tech] = pd.concat([clean_df[tech], lifetime]) - # ----- efficiencies ------ - efficiency = df[((df.index.str.contains("efficiency")) | - (df.index.str.contains("Hydrogen output, at LHV")) | - (df.index.str.contains("Hydrogen Output")) | - (df.index.str.contains("FT Liquids Output, MWh/MWh Total Input")) | - (df.index.str.contains("Methanol Output")) | - (df.index.str.contains("District heat Output")) | - (df.index.str.contains("Electricity Output")) | - (df.index.str.contains("hereof recoverable for district heating")) | - (df.index.str.contains("Bio SNG")) | - (df.index.str.contains("biochar")) | - (df.index == ("Hydrogen"))) - & ((df.unit == "%") | (df.unit == "% total size") | - (df.unit == "% of fuel input") | - (df.unit == "MWh_H2/MWh_e") | - (df.unit == "%-points of heat loss") | - (df.unit == "MWh_MeOH/MWh_th") | - (df.unit == "MWh_e/MWh_th") | - (df.unit == "MWh_th/MWh_th") | - (df.unit == 'MWh/MWh Total Input') | - df.unit.str.contains("MWh_FT/MWh_H2") | - df.unit.str.contains("MWh_biochar/MWh_feedstock") | - df.unit.str.contains("ton biochar/MWh_feedstock") | - df.unit.str.contains("MWh_CH4/MWh_H2") | - df.unit.str.contains("% MWh_feedstock"))].copy() - - if tech == 'Fischer-Tropsch': + efficiency = df[ + ( + (df.index.str.contains("efficiency")) + | (df.index.str.contains("Hydrogen output, at LHV")) + | (df.index.str.contains("Hydrogen Output")) + | (df.index.str.contains("FT Liquids Output, MWh/MWh Total Input")) + | (df.index.str.contains("Methanol Output")) + | (df.index.str.contains("District heat Output")) + | (df.index.str.contains("Electricity Output")) + | (df.index.str.contains("hereof recoverable for district heating")) + | (df.index.str.contains("Bio SNG")) + | (df.index.str.contains("biochar")) + | (df.index == ("Hydrogen")) + ) + & ( + (df.unit == "%") + | (df.unit == "% total size") + | (df.unit == "% of fuel input") + | (df.unit == "MWh_H2/MWh_e") + | (df.unit == "%-points of heat loss") + | (df.unit == "MWh_MeOH/MWh_th") + | (df.unit == "MWh_e/MWh_th") + | (df.unit == "MWh_th/MWh_th") + | (df.unit == "MWh/MWh Total Input") + | df.unit.str.contains("MWh_FT/MWh_H2") + | df.unit.str.contains("MWh_biochar/MWh_feedstock") + | df.unit.str.contains("ton biochar/MWh_feedstock") + | df.unit.str.contains("MWh_CH4/MWh_H2") + | df.unit.str.contains("% MWh_feedstock") + ) + ].copy() + + if tech == "Fischer-Tropsch": efficiency[years] *= 100 - # take annual average instead of name plate efficiency, unless central air-sourced heat pump - if any(efficiency.index.str.contains("annual average")) and tech != "central air-sourced heat pump": + if ( + any(efficiency.index.str.contains("annual average")) + and tech != "central air-sourced heat pump" + ): efficiency = efficiency[efficiency.index.str.contains("annual average")] elif any(efficiency.index.str.contains("name plate")): efficiency = efficiency[efficiency.index.str.contains("name plate")] @@ -1452,13 +1782,16 @@ def order_data(tech_data): efficiency_heat = efficiency[with_heat_recovery].copy() efficiency_heat["parameter"] = "efficiency-heat" clean_df[tech] = pd.concat([clean_df[tech], efficiency_heat]) - efficiency_h2 = efficiency[efficiency.index.str.contains("Hydrogen Output")].copy() + efficiency_h2 = efficiency[ + efficiency.index.str.contains("Hydrogen Output") + ].copy() efficiency_h2["parameter"] = "efficiency" clean_df[tech] = pd.concat([clean_df[tech], efficiency_h2]) # check if electric and heat efficiencies are given - if (any(["Electric" in ind for ind in efficiency.index]) and - any(["Heat" in ind for ind in efficiency.index])): + if any(["Electric" in ind for ind in efficiency.index]) and any( + ["Heat" in ind for ind in efficiency.index] + ): efficiency_heat = efficiency[efficiency.index.str.contains("Heat")].copy() efficiency_heat["parameter"] = "efficiency-heat" clean_df[tech] = pd.concat([clean_df[tech], efficiency_heat]) @@ -1467,34 +1800,50 @@ def order_data(tech_data): clean_df[tech] = pd.concat([clean_df[tech], efficiency]) elif tech == "biomass-to-methanol": - efficiency_heat = efficiency[efficiency.index.str.contains("District heat")].copy() + efficiency_heat = efficiency[ + efficiency.index.str.contains("District heat") + ].copy() efficiency_heat["parameter"] = "efficiency-heat" - efficiency_heat.loc[:,years] *= 100 # in % + efficiency_heat.loc[:, years] *= 100 # in % clean_df[tech] = pd.concat([clean_df[tech], efficiency_heat]) - efficiency_elec = efficiency[efficiency.index.str.contains("Electric")].copy() + efficiency_elec = efficiency[ + efficiency.index.str.contains("Electric") + ].copy() efficiency_elec["parameter"] = "efficiency-electricity" clean_df[tech] = pd.concat([clean_df[tech], efficiency_elec]) - efficiency_meoh = efficiency[efficiency.index.str.contains("Methanol")].copy() + efficiency_meoh = efficiency[ + efficiency.index.str.contains("Methanol") + ].copy() efficiency_meoh["parameter"] = "efficiency" - efficiency_meoh.loc[:,years] *= 100 # in % + efficiency_meoh.loc[:, years] *= 100 # in % clean_df[tech] = pd.concat([clean_df[tech], efficiency_meoh]) elif tech == "biochar pyrolysis": - efficiency_biochar = efficiency[efficiency.index.str.contains("efficiency biochar")].copy() + efficiency_biochar = efficiency[ + efficiency.index.str.contains("efficiency biochar") + ].copy() efficiency_biochar["parameter"] = "efficiency-biochar" clean_df[tech] = pd.concat([clean_df[tech], efficiency_biochar]) - efficiency_biochar_mass = efficiency[efficiency.index.str.contains("yield biochar")].copy() + efficiency_biochar_mass = efficiency[ + efficiency.index.str.contains("yield biochar") + ].copy() efficiency_biochar_mass["parameter"] = "yield-biochar" clean_df[tech] = pd.concat([clean_df[tech], efficiency_biochar_mass]) - efficiency_heat = efficiency[efficiency.index.str.contains("efficiency heat")].copy() + efficiency_heat = efficiency[ + efficiency.index.str.contains("efficiency heat") + ].copy() efficiency_heat["parameter"] = "efficiency-heat" clean_df[tech] = pd.concat([clean_df[tech], efficiency_heat]) elif len(efficiency) != 1: switch = True if not any(efficiency.index.str.contains("Round trip")): - print("check efficiency: ", tech, " ", - df[df.index.str.contains("efficiency")].unit) + print( + "check efficiency: ", + tech, + " ", + df[df.index.str.contains("efficiency")].unit, + ) else: efficiency["parameter"] = "efficiency" clean_df[tech] = pd.concat([clean_df[tech], efficiency]) @@ -1515,87 +1864,161 @@ def order_data(tech_data): print("---------------------------------------") # concat data - data = (pd.concat(clean_df).reset_index().rename(columns={"level_0":"technology", - "level_1": "further description"}) - .set_index(["technology", "parameter"])) + data = ( + pd.concat(clean_df) + .reset_index() + .rename(columns={"level_0": "technology", "level_1": "further description"}) + .set_index(["technology", "parameter"]) + ) # add central water tank charger/ discharger - charger_tank = tech_data.loc[("central water tank storage", " - Charge efficiency")].copy() + charger_tank = tech_data.loc[ + ("central water tank storage", " - Charge efficiency") + ].copy() charger_tank["further description"] = "Charger efficiency" - charger_tank.rename(index={" - Charge efficiency": "efficiency"}, - level=1, inplace=True) - charger_tank.rename(index={'central water tank storage': "central water tank charger"}, - level=0, inplace=True) + charger_tank.rename( + index={" - Charge efficiency": "efficiency"}, level=1, inplace=True + ) + charger_tank.rename( + index={"central water tank storage": "central water tank charger"}, + level=0, + inplace=True, + ) data = pd.concat([data, charger_tank], sort=True) - charger_tank.rename(index={"central water tank charger": "central water tank discharger"}, - level=0, inplace=True) + charger_tank.rename( + index={"central water tank charger": "central water tank discharger"}, + level=0, + inplace=True, + ) charger_tank["further description"] = "Discharger efficiency" data = pd.concat([data, charger_tank], sort=True) # add decentral water tank charger/ discharger - charger_tank = tech_data.loc[("decentral water tank storage", " - Charge efficiency")].copy() + charger_tank = tech_data.loc[ + ("decentral water tank storage", " - Charge efficiency") + ].copy() charger_tank["further description"] = "Charger efficiency" - charger_tank.rename(index={" - Charge efficiency": "efficiency"}, - level=1, inplace=True) - charger_tank.rename(index={'decentral water tank storage': "decentral water tank charger"}, - level=0, inplace=True) + charger_tank.rename( + index={" - Charge efficiency": "efficiency"}, level=1, inplace=True + ) + charger_tank.rename( + index={"decentral water tank storage": "decentral water tank charger"}, + level=0, + inplace=True, + ) data = pd.concat([data, charger_tank], sort=True) - charger_tank.rename(index={"decentral water tank charger": "decentral water tank discharger"}, - level=0, inplace=True) + charger_tank.rename( + index={"decentral water tank charger": "decentral water tank discharger"}, + level=0, + inplace=True, + ) charger_tank["further description"] = "Discharger efficiency" data = pd.concat([data, charger_tank], sort=True) # add water pit charger/ discharger - charger_pit = tech_data.loc[("central water pit storage", " - Charge efficiency")].copy() + charger_pit = tech_data.loc[ + ("central water pit storage", " - Charge efficiency") + ].copy() charger_pit["further description"] = "Charger efficiency" - charger_pit.rename(index={" - Charge efficiency": "efficiency"}, - level=1, inplace=True) - charger_pit.rename(index={'central water pit storage': "central water pit charger"}, - level=0, inplace=True) + charger_pit.rename( + index={" - Charge efficiency": "efficiency"}, level=1, inplace=True + ) + charger_pit.rename( + index={"central water pit storage": "central water pit charger"}, + level=0, + inplace=True, + ) data = pd.concat([data, charger_pit], sort=True) - charger_pit.rename(index={"central water pit charger": "central water pit discharger"}, - level=0, inplace=True) + charger_pit.rename( + index={"central water pit charger": "central water pit discharger"}, + level=0, + inplace=True, + ) charger_pit["further description"] = "Discharger efficiency" data = pd.concat([data, charger_pit], sort=True) - # add energy to power ratio for central water tank storage - power_ratio_tank = tech_data.loc[("central water tank storage", "Input capacity for one unit")].copy().squeeze() - storage_capacity_tank = tech_data.loc[("central water tank storage", "Energy storage capacity for one unit")].copy().squeeze() + power_ratio_tank = ( + tech_data.loc[("central water tank storage", "Input capacity for one unit")] + .copy() + .squeeze() + ) + storage_capacity_tank = ( + tech_data.loc[ + ("central water tank storage", "Energy storage capacity for one unit") + ] + .copy() + .squeeze() + ) power_ratio_tank[years] = storage_capacity_tank[years].div(power_ratio_tank[years]) - power_ratio_tank["further description"] = "Ratio between energy storage and input capacity" + power_ratio_tank["further description"] = ( + "Ratio between energy storage and input capacity" + ) power_ratio_tank["unit"] = "h" power_ratio_tank = power_ratio_tank.to_frame().T - power_ratio_tank.rename(index={"Input capacity for one unit": "energy to power ratio"}, - level=1, inplace=True) + power_ratio_tank.rename( + index={"Input capacity for one unit": "energy to power ratio"}, + level=1, + inplace=True, + ) data = pd.concat([data, power_ratio_tank], sort=True) # add energy to power ratio for decentral water tank storage - power_ratio_tank = tech_data.loc[("decentral water tank storage", "Input capacity for one unit")].copy().squeeze() - storage_capacity_tank = tech_data.loc[("decentral water tank storage", "Energy storage capacity for one unit")].copy().squeeze() + power_ratio_tank = ( + tech_data.loc[("decentral water tank storage", "Input capacity for one unit")] + .copy() + .squeeze() + ) + storage_capacity_tank = ( + tech_data.loc[ + ("decentral water tank storage", "Energy storage capacity for one unit") + ] + .copy() + .squeeze() + ) power_ratio_tank[years] = storage_capacity_tank[years].div(power_ratio_tank[years]) - power_ratio_tank["further description"] = "Ratio between energy storage and input capacity" + power_ratio_tank["further description"] = ( + "Ratio between energy storage and input capacity" + ) power_ratio_tank["unit"] = "h" power_ratio_tank = power_ratio_tank.to_frame().T - power_ratio_tank.rename(index={"Input capacity for one unit": "energy to power ratio"}, - level=1, inplace=True) + power_ratio_tank.rename( + index={"Input capacity for one unit": "energy to power ratio"}, + level=1, + inplace=True, + ) data = pd.concat([data, power_ratio_tank], sort=True) # add energy to power ratio for water pit storage - power_ratio_pit = tech_data.loc[("central water pit storage", "Input capacity for one unit")].copy().squeeze() - storage_capacity_pit = tech_data.loc[("central water pit storage", "Energy storage capacity for one unit")].copy().squeeze() + power_ratio_pit = ( + tech_data.loc[("central water pit storage", "Input capacity for one unit")] + .copy() + .squeeze() + ) + storage_capacity_pit = ( + tech_data.loc[ + ("central water pit storage", "Energy storage capacity for one unit") + ] + .copy() + .squeeze() + ) power_ratio_pit[years] = storage_capacity_pit[years].div(power_ratio_pit[years]) - power_ratio_pit["further description"] = "Ratio between energy storage and input capacity" + power_ratio_pit["further description"] = ( + "Ratio between energy storage and input capacity" + ) power_ratio_pit["unit"] = "h" power_ratio_pit = power_ratio_pit.to_frame().T - power_ratio_pit.rename(index={"Input capacity for one unit": "energy to power ratio"}, - level=1, inplace=True) + power_ratio_pit.rename( + index={"Input capacity for one unit": "energy to power ratio"}, + level=1, + inplace=True, + ) data = pd.concat([data, power_ratio_pit], sort=True) return data @@ -1603,7 +2026,7 @@ def order_data(tech_data): def add_description(data): """ - add as a column to the tech data the excel sheet name, + Add as a column to the tech data the excel sheet name, add comment for offwind connection costs """ # add excel sheet names to data frame @@ -1615,167 +2038,210 @@ def add_description(data): data["further description"] = sheets + ": " + data["further description"] # add comment for offwind investment - if snakemake.config['offwind_no_gridcosts']: - data.loc[("offwind", "investment"), - "further description"] += " grid connection costs substracted from investment costs" + if snakemake.config["offwind_no_gridcosts"]: + data.loc[("offwind", "investment"), "further description"] += ( + " grid connection costs substracted from investment costs" + ) return data def convert_units(data): """ - convert investment and efficiency units to be align with old pypsa + Convert investment and efficiency units to be align with old pypsa assumptions """ # convert efficiency from % -> per unit - data.loc[data.index.get_level_values(1).isin(["efficiency", "efficiency-heat"]) - , years] /= 100 - data.loc[data.index.get_level_values(1).isin(["efficiency", "efficiency-heat"]) - , "unit"] = "per unit" + data.loc[ + data.index.get_level_values(1).isin(["efficiency", "efficiency-heat"]), years + ] /= 100 + data.loc[ + data.index.get_level_values(1).isin(["efficiency", "efficiency-heat"]), "unit" + ] = "per unit" # convert MW -> kW - to_convert = (data.index.get_level_values(1).isin(["fixed", "investment"]) & - data.unit.str.contains("/MW")) + to_convert = data.index.get_level_values(1).isin( + ["fixed", "investment"] + ) & data.unit.str.contains("/MW") data.loc[to_convert, years] /= 1e3 - data.loc[to_convert, "unit"] = (data.loc[to_convert, "unit"].str - .replace("/MW","/kW")) + data.loc[to_convert, "unit"] = data.loc[to_convert, "unit"].str.replace( + "/MW", "/kW" + ) return data def add_gas_storage(data): """ - add gas storage tech data, different methodolgy than other sheets and + Add gas storage tech data, different methodolgy than other sheets and therefore added later """ - gas_storage = pd.read_excel(snakemake.input.dea_storage, - sheet_name="150 Underground Storage of Gas", - index_col=1) + gas_storage = pd.read_excel( + snakemake.input.dea_storage, + sheet_name="150 Underground Storage of Gas", + index_col=1, + ) gas_storage.dropna(axis=1, how="all", inplace=True) # establishment of one cavern ~ 100*1e6 Nm3 = 1.1 TWh - investment = gas_storage.loc['Total cost, 100 mio Nm3 active volume'].iloc[0] + investment = gas_storage.loc["Total cost, 100 mio Nm3 active volume"].iloc[0] # convert million EUR/1.1 TWh -> EUR/kWh - investment /= (1.1 * 1e3) + investment /= 1.1 * 1e3 data.loc[("gas storage", "investment"), years] = investment data.loc[("gas storage", "investment"), "source"] = source_dict["DEA"] - data.loc[("gas storage", "investment"), "further description"] = "150 Underground Storage of Gas, Establishment of one cavern (units converted)" + data.loc[("gas storage", "investment"), "further description"] = ( + "150 Underground Storage of Gas, Establishment of one cavern (units converted)" + ) data.loc[("gas storage", "investment"), "unit"] = "EUR/kWh" data.loc[("gas storage", "investment"), "currency_year"] = 2015 - + data.loc[("gas storage", "lifetime"), years] = 100 data.loc[("gas storage", "lifetime"), "source"] = "TODO no source" - data.loc[("gas storage", "lifetime"), "further description"] = "estimation: most underground storage are already build, they do have a long lifetime" + data.loc[("gas storage", "lifetime"), "further description"] = ( + "estimation: most underground storage are already build, they do have a long lifetime" + ) data.loc[("gas storage", "lifetime"), "unit"] = "years" - # process equipment, injection (2200MW) withdrawl (6600MW) # assuming half of investment costs for injection, half for withdrawl - investment_charge = gas_storage.loc["Total investment cost"].iloc[0,0]/2/2200*1e3 - investment_discharge = gas_storage.loc["Total investment cost"].iloc[0,0]/2/6600*1e3 + investment_charge = ( + gas_storage.loc["Total investment cost"].iloc[0, 0] / 2 / 2200 * 1e3 + ) + investment_discharge = ( + gas_storage.loc["Total investment cost"].iloc[0, 0] / 2 / 6600 * 1e3 + ) data.loc[("gas storage charger", "investment"), years] = investment_charge data.loc[("gas storage discharger", "investment"), years] = investment_discharge - + data.loc[("gas storage charger", "investment"), "source"] = source_dict["DEA"] - data.loc[("gas storage charger", "investment"), "further description"] = "150 Underground Storage of Gas, Process equipment (units converted)" + data.loc[("gas storage charger", "investment"), "further description"] = ( + "150 Underground Storage of Gas, Process equipment (units converted)" + ) data.loc[("gas storage charger", "investment"), "unit"] = "EUR/kW" data.loc[("gas storage charger", "investment"), "currency_year"] = 2015 - data.loc[("gas storage discharger", "investment"), "source"] = source_dict["DEA"] - data.loc[("gas storage discharger", "investment"), "further description"] = "150 Underground Storage of Gas, Process equipment (units converted)" + data.loc[("gas storage discharger", "investment"), "further description"] = ( + "150 Underground Storage of Gas, Process equipment (units converted)" + ) data.loc[("gas storage discharger", "investment"), "unit"] = "EUR/kW" data.loc[("gas storage charger", "investment"), "currency_year"] = 2015 # operation + maintenance 400-500 million m³ = 4.4-5.5 TWh - FOM = gas_storage.loc["Total, incl. administration"].iloc[0] /(5.5*investment*1e3)*100 + FOM = ( + gas_storage.loc["Total, incl. administration"].iloc[0] + / (5.5 * investment * 1e3) + * 100 + ) data.loc[("gas storage", "FOM"), years] = FOM data.loc[("gas storage", "FOM"), "source"] = source_dict["DEA"] - data.loc[("gas storage", "FOM"), "further description"] = "150 Underground Storage of Gas, Operation and Maintenace, salt cavern (units converted)" + data.loc[("gas storage", "FOM"), "further description"] = ( + "150 Underground Storage of Gas, Operation and Maintenace, salt cavern (units converted)" + ) data.loc[("gas storage", "FOM"), "unit"] = "%" return data -def add_carbon_capture(data, tech_data): - - for tech in ['cement capture', 'biomass CHP capture']: - data.loc[(tech,"capture_rate"), years] = tech_data.loc[(tech,'Ax) CO2 capture rate, net'), years].values[0]/100 - data.loc[(tech,"capture_rate"), 'unit'] = 'per unit' - - - for tech in ['direct air capture', 'cement capture', 'biomass CHP capture']: - data.loc[(tech,"investment"), years] = tech_data.loc[(tech,'Specific investment'), years].values[0]*1e6 - data.loc[(tech,"investment"), 'unit'] = 'EUR/(tCO2/h)' +def add_carbon_capture(data, tech_data): + for tech in ["cement capture", "biomass CHP capture"]: + data.loc[(tech, "capture_rate"), years] = ( + tech_data.loc[(tech, "Ax) CO2 capture rate, net"), years].values[0] / 100 + ) + data.loc[(tech, "capture_rate"), "unit"] = "per unit" - data.loc[(tech,"FOM"), years] = tech_data.loc[(tech,'Fixed O&M'), years].values[0]/tech_data.loc[(tech,'Specific investment'), years].values[0]*100 - data.loc[(tech,"FOM"), 'unit'] = '%/year' + for tech in ["direct air capture", "cement capture", "biomass CHP capture"]: + data.loc[(tech, "investment"), years] = ( + tech_data.loc[(tech, "Specific investment"), years].values[0] * 1e6 + ) + data.loc[(tech, "investment"), "unit"] = "EUR/(tCO2/h)" - name_list = [('C2) Eletricity input ',"electricity-input"), - ('C1) Heat input ',"heat-input"), - ('C1) Heat out ','heat-output'), - ('CO₂ compression and dehydration - Electricity input',"compression-electricity-input"), - ('CO₂ compression and dehydration - Heat out',"compression-heat-output")] + data.loc[(tech, "FOM"), years] = ( + tech_data.loc[(tech, "Fixed O&M"), years].values[0] + / tech_data.loc[(tech, "Specific investment"), years].values[0] + * 100 + ) + data.loc[(tech, "FOM"), "unit"] = "%/year" + + name_list = [ + ("C2) Eletricity input ", "electricity-input"), + ("C1) Heat input ", "heat-input"), + ("C1) Heat out ", "heat-output"), + ( + "CO₂ compression and dehydration - Electricity input", + "compression-electricity-input", + ), + ("CO₂ compression and dehydration - Heat out", "compression-heat-output"), + ] for dea_name, our_name in name_list: - data.loc[(tech,our_name), years] = tech_data.loc[(tech,dea_name), years].values[0] - data.loc[(tech,our_name), 'unit'] = 'MWh/tCO2' + data.loc[(tech, our_name), years] = tech_data.loc[ + (tech, dea_name), years + ].values[0] + data.loc[(tech, our_name), "unit"] = "MWh/tCO2" - data.loc[tech,'source'] = data.loc[(tech,'lifetime'),'source'] - data.loc[tech,'further description'] = sheet_names[tech] + data.loc[tech, "source"] = data.loc[(tech, "lifetime"), "source"] + data.loc[tech, "further description"] = sheet_names[tech] return data + def rename_pypsa_old(costs_pypsa): """ - renames old technology names to new ones to compare + Renames old technology names to new ones to compare converts units from water tanks to compare """ - to_drop = ['retrofitting I', 'retrofitting II'] + to_drop = ["retrofitting I", "retrofitting II"] costs_pypsa.drop(to_drop, level=0, inplace=True) # rename to new names - costs_pypsa.rename({'central CHP': 'central gas CHP'}, inplace=True) - costs_pypsa.rename({'hydrogen underground storage': 'hydrogen storage underground'}, - inplace=True) + costs_pypsa.rename({"central CHP": "central gas CHP"}, inplace=True) + costs_pypsa.rename( + {"hydrogen underground storage": "hydrogen storage underground"}, inplace=True + ) - #convert EUR/m^3 to EUR/kWh for 40 K diff and 1.17 kWh/m^3/K - costs_pypsa.loc[('decentral water tank storage','investment'), - 'value'] /= 1.17*40 - costs_pypsa.loc[('decentral water tank storage','investment'),'unit'] = 'EUR/kWh' + # convert EUR/m^3 to EUR/kWh for 40 K diff and 1.17 kWh/m^3/K + costs_pypsa.loc[("decentral water tank storage", "investment"), "value"] /= ( + 1.17 * 40 + ) + costs_pypsa.loc[("decentral water tank storage", "investment"), "unit"] = "EUR/kWh" return costs_pypsa -def add_manual_input(data): - df = pd.read_csv(snakemake.input['manual_input'], quotechar='"',sep=',', keep_default_na=False) +def add_manual_input(data): + df = pd.read_csv( + snakemake.input["manual_input"], quotechar='"', sep=",", keep_default_na=False + ) df = df.rename(columns={"further_description": "further description"}) - l = [] - for tech in df['technology'].unique(): - c0 = df[df['technology'] == tech] - for param in c0['parameter'].unique(): - - c = df.query('technology == @tech and parameter == @param') - - s = pd.Series(index=snakemake.config['years'], - data=np.interp(snakemake.config['years'], c['year'], c['value']), - name=param) - s['parameter'] = param - s['technology'] = tech + for tech in df["technology"].unique(): + c0 = df[df["technology"] == tech] + for param in c0["parameter"].unique(): + c = df.query("technology == @tech and parameter == @param") + + s = pd.Series( + index=snakemake.config["years"], + data=np.interp(snakemake.config["years"], c["year"], c["value"]), + name=param, + ) + s["parameter"] = param + s["technology"] = tech try: - s["currency_year"] = int(c["currency_year"].values[0]) + s["currency_year"] = int(c["currency_year"].values[0]) except ValueError: s["currency_year"] = np.nan - for col in ['unit','source','further description']: + for col in ["unit", "source", "further description"]: s[col] = "; and\n".join(c[col].unique().astype(str)) - s = s.rename({"further_description":"further description"}) # match column name between manual_input and original TD workflow + s = s.rename( + {"further_description": "further description"} + ) # match column name between manual_input and original TD workflow l.append(s) - new_df = pd.DataFrame(l).set_index(['technology','parameter']) + new_df = pd.DataFrame(l).set_index(["technology", "parameter"]) data.index.set_names(["technology", "parameter"], inplace=True) # overwrite DEA data with manual input data = new_df.combine_first(data) @@ -1785,69 +2251,88 @@ def add_manual_input(data): def rename_ISE(costs_ISE): """ - rename ISE costs to fit to tech data + Rename ISE costs to fit to tech data """ - costs_ISE.rename(index = {"Investition": "investment", - "Lebensdauer": "lifetime", - "M/O-Kosten": "FOM"}, - columns = {"Einheit": "unit", - "2020": 2020, - "2025": 2025, - "2030": 2030, - "2035": 2035, - "2040": 2040, - "2045": 2045, - "2050": 2050}, inplace=True) + costs_ISE.rename( + index={ + "Investition": "investment", + "Lebensdauer": "lifetime", + "M/O-Kosten": "FOM", + }, + columns={ + "Einheit": "unit", + "2020": 2020, + "2025": 2025, + "2030": 2030, + "2035": 2035, + "2040": 2040, + "2045": 2045, + "2050": 2050, + }, + inplace=True, + ) costs_ISE.index.names = ["technology", "parameter"] costs_ISE["unit"] = costs_ISE.unit.replace({"a": "years", "% Invest": "%"}) costs_ISE["source"] = source_dict["ISE"] - costs_ISE['further description'] = costs_ISE.reset_index()["technology"].values + costs_ISE["further description"] = costs_ISE.reset_index()["technology"].values # could not find specific currency year in report, assume year of publication - costs_ISE['currency_year'] = 2020 + costs_ISE["currency_year"] = 2020 return costs_ISE def rename_ISE_vehicles(costs_vehicles): """ - rename ISE_vehicles costs to fit to tech data + Rename ISE_vehicles costs to fit to tech data """ - costs_vehicles.rename(index = {"Investition": "investment", - "Lebensdauer": "lifetime", - "M/O-Kosten": "FOM", - "Wirkungsgrad*" : "efficiency", - "PKW Batterie-Elektromotor" : "Battery electric (passenger cars)", - "LKW Batterie-Elektromotor" : "Battery electric (trucks)", - "LKW H2- Brennstoffzelle": "Hydrogen fuel cell (trucks)", - "PKW H2- Brennstoffzelle": "Hydrogen fuel cell (passenger cars)", - "LKW ICE- Fl�ssigtreibstoff": "Liquid fuels ICE (trucks)", - "PKW ICE- Fl�ssigtreibstoff": "Liquid fuels ICE (passenger cars)", - "LKW Ladeinfrastruktur Brennstoffzellen Fahrzeuge * LKW": "Charging infrastructure fuel cell vehicles trucks", - "PKW Ladeinfrastruktur Brennstoffzellen Fahrzeuge * PKW": "Charging infrastructure fuel cell vehicles passenger cars", - "PKW Ladeinfrastruktur schnell (reine) Batteriefahrzeuge*" : "Charging infrastructure fast (purely) battery electric vehicles passenger cars", - "Ladeinfrastruktur langsam (reine) Batteriefahrzeuge*" : "Charging infrastructure slow (purely) battery electric vehicles passenger cars"}, - columns = {"Einheit": "unit", - "2020": 2020, - "2025": 2025, - "2030": 2030, - "2035": 2035, - "2040": 2040, - "2045": 2045, - "2050": 2050}, inplace=True) + costs_vehicles.rename( + index={ + "Investition": "investment", + "Lebensdauer": "lifetime", + "M/O-Kosten": "FOM", + "Wirkungsgrad*": "efficiency", + "PKW Batterie-Elektromotor": "Battery electric (passenger cars)", + "LKW Batterie-Elektromotor": "Battery electric (trucks)", + "LKW H2- Brennstoffzelle": "Hydrogen fuel cell (trucks)", + "PKW H2- Brennstoffzelle": "Hydrogen fuel cell (passenger cars)", + "LKW ICE- Fl�ssigtreibstoff": "Liquid fuels ICE (trucks)", + "PKW ICE- Fl�ssigtreibstoff": "Liquid fuels ICE (passenger cars)", + "LKW Ladeinfrastruktur Brennstoffzellen Fahrzeuge * LKW": "Charging infrastructure fuel cell vehicles trucks", + "PKW Ladeinfrastruktur Brennstoffzellen Fahrzeuge * PKW": "Charging infrastructure fuel cell vehicles passenger cars", + "PKW Ladeinfrastruktur schnell (reine) Batteriefahrzeuge*": "Charging infrastructure fast (purely) battery electric vehicles passenger cars", + "Ladeinfrastruktur langsam (reine) Batteriefahrzeuge*": "Charging infrastructure slow (purely) battery electric vehicles passenger cars", + }, + columns={ + "Einheit": "unit", + "2020": 2020, + "2025": 2025, + "2030": 2030, + "2035": 2035, + "2040": 2040, + "2045": 2045, + "2050": 2050, + }, + inplace=True, + ) costs_vehicles.index.names = ["technology", "parameter"] - costs_vehicles["unit"] = costs_vehicles.unit.replace({"a": "years", "% Invest": "%"}) + costs_vehicles["unit"] = costs_vehicles.unit.replace( + {"a": "years", "% Invest": "%"} + ) costs_vehicles["source"] = source_dict["vehicles"] # could not find specific currency year in report, assume year of publication costs_vehicles["currency_year"] = 2020 - costs_vehicles['further description'] = costs_vehicles.reset_index()["technology"].values + costs_vehicles["further description"] = costs_vehicles.reset_index()[ + "technology" + ].values return costs_vehicles -def carbon_flow(costs,year): + +def carbon_flow(costs, year): # NB: This requires some digits of accuracy; rounding to two digits creates carbon inbalances when scaling up - c_in_char = 0 # Carbon ending up in char: zero avoids inbalace -> assumed to be circulated back and eventually end up in one of the other output streams - medium_out = '' - CH4_specific_energy = 50 #GJ/t methane + c_in_char = 0 # Carbon ending up in char: zero avoids inbalace -> assumed to be circulated back and eventually end up in one of the other output streams + medium_out = "" + CH4_specific_energy = 50 # GJ/t methane btlcost_data = np.interp(x=years, xp=[2020, 2050], fp=[3500, 2000]) btl_cost = pd.Series(data=btlcost_data, index=years) @@ -1858,393 +2343,522 @@ def carbon_flow(costs,year): btleta_data = np.interp(x=years, xp=[2020, 2050], fp=[0.35, 0.45]) btl_eta = pd.Series(data=btleta_data, index=years) - #Adding pelletizing cost to biomass boiler - costs.loc[('biomass boiler', 'pelletizing cost'), 'value'] = 9 - costs.loc[('biomass boiler', 'pelletizing cost'), 'unit'] = "EUR/MWh_pellets" - costs.loc[('biomass boiler', 'pelletizing cost'), 'currency_year'] = 2019 - costs.loc[('biomass boiler', 'pelletizing cost'), 'source'] = "Assumption based on doi:10.1016/j.rser.2019.109506" - + # Adding pelletizing cost to biomass boiler + costs.loc[("biomass boiler", "pelletizing cost"), "value"] = 9 + costs.loc[("biomass boiler", "pelletizing cost"), "unit"] = "EUR/MWh_pellets" + costs.loc[("biomass boiler", "pelletizing cost"), "currency_year"] = 2019 + costs.loc[("biomass boiler", "pelletizing cost"), "source"] = ( + "Assumption based on doi:10.1016/j.rser.2019.109506" + ) - for tech in ['Fischer-Tropsch', 'methanolisation', 'BtL', 'biomass-to-methanol', 'BioSNG', 'biogas', - 'biogas CC', 'digestible biomass to hydrogen', - 'solid biomass to hydrogen', 'electrobiofuels']: + for tech in [ + "Fischer-Tropsch", + "methanolisation", + "BtL", + "biomass-to-methanol", + "BioSNG", + "biogas", + "biogas CC", + "digestible biomass to hydrogen", + "solid biomass to hydrogen", + "electrobiofuels", + ]: inv_cost = 0 eta = 0 lifetime = 0 FOM = 0 VOM = 0 currency_year = np.nan - source = 'TODO' + source = "TODO" co2_capture_rate = 0.90 - if not (tech, "capture rate") in costs.index: - costs.loc[(tech, 'capture rate'), 'value'] = co2_capture_rate - costs.loc[(tech, 'capture rate'), 'unit'] = "per unit" - costs.loc[(tech, 'capture rate'), 'source'] = "Assumption based on doi:10.1016/j.biombioe.2015.01.006" - + if (tech, "capture rate") not in costs.index: + costs.loc[(tech, "capture rate"), "value"] = co2_capture_rate + costs.loc[(tech, "capture rate"), "unit"] = "per unit" + costs.loc[(tech, "capture rate"), "source"] = ( + "Assumption based on doi:10.1016/j.biombioe.2015.01.006" + ) - if tech == 'BtL': + if tech == "BtL": inv_cost = btl_cost[year] - medium_out = 'oil' + medium_out = "oil" eta = btl_eta[year] source = "doi:10.1016/j.enpol.2017.05.013" currency_year = 2017 - if tech == 'biomass-to-methanol': - medium_out = 'methanol' + if tech == "biomass-to-methanol": + medium_out = "methanol" - elif tech == 'BioSNG': - medium_out = 'gas' + elif tech == "BioSNG": + medium_out = "gas" lifetime = 25 - elif tech in ['biogas', 'biogas CC']: + elif tech in ["biogas", "biogas CC"]: eta = 1 source = "Assuming input biomass is already given in biogas output" - AD_CO2_share = 0.4 #volumetric share in biogas (rest is CH4) + AD_CO2_share = 0.4 # volumetric share in biogas (rest is CH4) - - elif tech == 'biogas plus hydrogen': - #NB: this falls between power to gas and biogas and should be used with care, due to possible minor + elif tech == "biogas plus hydrogen": + # NB: this falls between power to gas and biogas and should be used with care, due to possible minor # differences in resource use etc. which may tweak results in favour of one tech or another eta = 1.6 H2_in = 0.46 heat_out = 0.19 source = "Calculated from data in Danish Energy Agency, data_sheets_for_renewable_fuels.xlsx" - costs.loc[(tech, 'hydrogen input'), 'value'] = H2_in - costs.loc[(tech, 'hydrogen input'), 'unit'] = "MWh_H2/MWh_CH4" - costs.loc[(tech, 'hydrogen input'), 'source'] = source + costs.loc[(tech, "hydrogen input"), "value"] = H2_in + costs.loc[(tech, "hydrogen input"), "unit"] = "MWh_H2/MWh_CH4" + costs.loc[(tech, "hydrogen input"), "source"] = source - costs.loc[(tech, 'heat output'), 'value'] = heat_out - costs.loc[(tech, 'heat output'), 'unit'] = "MWh_th/MWh_CH4" - costs.loc[(tech, 'heat output'), 'source'] = source - currency_year = costs.loc[('biogas plus hydrogen', 'VOM'), "currency_year"] + costs.loc[(tech, "heat output"), "value"] = heat_out + costs.loc[(tech, "heat output"), "unit"] = "MWh_th/MWh_CH4" + costs.loc[(tech, "heat output"), "source"] = source + currency_year = costs.loc[("biogas plus hydrogen", "VOM"), "currency_year"] - #TODO: this needs to be refined based on e.g. stoichiometry: - AD_CO2_share = 0.1 #volumetric share in biogas (rest is CH4). + # TODO: this needs to be refined based on e.g. stoichiometry: + AD_CO2_share = 0.1 # volumetric share in biogas (rest is CH4). - elif tech == 'digestible biomass to hydrogen': + elif tech == "digestible biomass to hydrogen": inv_cost = bmH2_cost[year] eta = 0.39 FOM = 4.25 currency_year = 2014 - source = 'Zech et.al. DBFZ Report Nr. 19. Hy-NOW - Evaluierung der Verfahren und Technologien für die Bereitstellung von Wasserstoff auf Basis von Biomasse, DBFZ, 2014' #source_dict('HyNOW') + source = "Zech et.al. DBFZ Report Nr. 19. Hy-NOW - Evaluierung der Verfahren und Technologien für die Bereitstellung von Wasserstoff auf Basis von Biomasse, DBFZ, 2014" # source_dict('HyNOW') - elif tech == 'solid biomass to hydrogen': + elif tech == "solid biomass to hydrogen": inv_cost = bmH2_cost[year] eta = 0.56 FOM = 4.25 currency_year = 2014 - source = 'Zech et.al. DBFZ Report Nr. 19. Hy-NOW - Evaluierung der Verfahren und Technologien für die Bereitstellung von Wasserstoff auf Basis von Biomasse, DBFZ, 2014' #source_dict('HyNOW') + source = "Zech et.al. DBFZ Report Nr. 19. Hy-NOW - Evaluierung der Verfahren und Technologien für die Bereitstellung von Wasserstoff auf Basis von Biomasse, DBFZ, 2014" # source_dict('HyNOW') if eta > 0: - costs.loc[(tech, 'efficiency'), 'value'] = eta - costs.loc[(tech, 'efficiency'), 'unit'] = "per unit" - costs.loc[(tech, 'efficiency'), 'source'] = source - - if tech in ['BioSNG', 'BtL', 'biomass-to-methanol']: - input_CO2_intensity = costs.loc[('solid biomass', 'CO2 intensity'), 'value'] - - costs.loc[(tech, 'C in fuel'), 'value'] = costs.loc[(tech, 'efficiency'), 'value'] \ - * costs.loc[(medium_out, 'CO2 intensity'), 'value'] \ - / input_CO2_intensity - costs.loc[(tech, 'C stored'), 'value'] = 1 - costs.loc[(tech, 'C in fuel'), 'value'] - c_in_char - costs.loc[(tech, 'CO2 stored'), 'value'] = input_CO2_intensity * costs.loc[(tech, 'C stored'), 'value'] - - costs.loc[(tech, 'C in fuel'), 'unit'] = "per unit" - costs.loc[(tech, 'C stored'), 'unit'] = "per unit" - costs.loc[(tech, 'CO2 stored'), 'unit'] = "tCO2/MWh_th" - - costs.loc[(tech, 'C in fuel'), 'source'] = "Stoichiometric calculation, doi:10.1016/j.apenergy.2022.120016" - costs.loc[(tech, 'C stored'), 'source'] = "Stoichiometric calculation, doi:10.1016/j.apenergy.2022.120016" - costs.loc[(tech, 'CO2 stored'), 'source'] = "Stoichiometric calculation, doi:10.1016/j.apenergy.2022.120016" - - elif tech in ['electrobiofuels']: - - input_CO2_intensity = costs.loc[('solid biomass', 'CO2 intensity'), 'value'] - oil_CO2_intensity = costs.loc[('oil', 'CO2 intensity'), 'value'] - - costs.loc[('electrobiofuels', 'C in fuel'), 'value'] = (costs.loc[('BtL', 'C in fuel'), 'value'] - + costs.loc[('BtL', 'C stored'), 'value'] - * costs.loc[('Fischer-Tropsch', 'capture rate'), 'value']) - costs.loc[('electrobiofuels', 'C in fuel'), 'unit'] = 'per unit' - costs.loc[('electrobiofuels', 'C in fuel'), 'source'] = 'Stoichiometric calculation' - - costs.loc[('electrobiofuels', 'efficiency-biomass'), 'value'] = costs.loc[('electrobiofuels', 'C in fuel'), 'value'] \ - * input_CO2_intensity / oil_CO2_intensity - costs.loc[('electrobiofuels', 'efficiency-biomass'), 'unit'] = 'per unit' - costs.loc[('electrobiofuels', 'efficiency-biomass'), 'source'] = 'Stoichiometric calculation' - - - efuel_scale_factor = costs.loc[('BtL', 'C stored'), 'value']* costs.loc[('Fischer-Tropsch', 'capture rate'), 'value'] - - costs.loc[('electrobiofuels', 'efficiency-hydrogen'), 'value'] = costs.loc[('Fischer-Tropsch', 'efficiency'), 'value']\ - / efuel_scale_factor - costs.loc[('electrobiofuels', 'efficiency-hydrogen'), 'unit'] = 'per unit' - costs.loc[('electrobiofuels', 'efficiency-hydrogen'), 'source'] = 'Stoichiometric calculation' - - costs.loc[('electrobiofuels', 'efficiency-tot'), 'value'] = (1 / - (1 / costs.loc[('electrobiofuels', 'efficiency-hydrogen'), 'value'] + - 1 / costs.loc[('electrobiofuels', 'efficiency-biomass'), 'value'])) - costs.loc[('electrobiofuels', 'efficiency-tot'), 'unit'] = 'per unit' - costs.loc[('electrobiofuels', 'efficiency-tot'), 'source'] = 'Stoichiometric calculation' - - inv_cost = btl_cost[year] + costs.loc[('Fischer-Tropsch', 'investment'), 'value'] * efuel_scale_factor - VOM = costs.loc[('BtL', 'VOM'), 'value'] + costs.loc[('Fischer-Tropsch', 'VOM'), 'value'] * efuel_scale_factor - FOM = costs.loc[('BtL', 'FOM'), 'value'] - medium_out = 'oil' - currency_year = costs.loc[('Fischer-Tropsch', 'investment'), "currency_year"] + costs.loc[(tech, "efficiency"), "value"] = eta + costs.loc[(tech, "efficiency"), "unit"] = "per unit" + costs.loc[(tech, "efficiency"), "source"] = source + + if tech in ["BioSNG", "BtL", "biomass-to-methanol"]: + input_CO2_intensity = costs.loc[("solid biomass", "CO2 intensity"), "value"] + + costs.loc[(tech, "C in fuel"), "value"] = ( + costs.loc[(tech, "efficiency"), "value"] + * costs.loc[(medium_out, "CO2 intensity"), "value"] + / input_CO2_intensity + ) + costs.loc[(tech, "C stored"), "value"] = ( + 1 - costs.loc[(tech, "C in fuel"), "value"] - c_in_char + ) + costs.loc[(tech, "CO2 stored"), "value"] = ( + input_CO2_intensity * costs.loc[(tech, "C stored"), "value"] + ) + + costs.loc[(tech, "C in fuel"), "unit"] = "per unit" + costs.loc[(tech, "C stored"), "unit"] = "per unit" + costs.loc[(tech, "CO2 stored"), "unit"] = "tCO2/MWh_th" + + costs.loc[(tech, "C in fuel"), "source"] = ( + "Stoichiometric calculation, doi:10.1016/j.apenergy.2022.120016" + ) + costs.loc[(tech, "C stored"), "source"] = ( + "Stoichiometric calculation, doi:10.1016/j.apenergy.2022.120016" + ) + costs.loc[(tech, "CO2 stored"), "source"] = ( + "Stoichiometric calculation, doi:10.1016/j.apenergy.2022.120016" + ) + + elif tech in ["electrobiofuels"]: + input_CO2_intensity = costs.loc[("solid biomass", "CO2 intensity"), "value"] + oil_CO2_intensity = costs.loc[("oil", "CO2 intensity"), "value"] + + costs.loc[("electrobiofuels", "C in fuel"), "value"] = ( + costs.loc[("BtL", "C in fuel"), "value"] + + costs.loc[("BtL", "C stored"), "value"] + * costs.loc[("Fischer-Tropsch", "capture rate"), "value"] + ) + costs.loc[("electrobiofuels", "C in fuel"), "unit"] = "per unit" + costs.loc[("electrobiofuels", "C in fuel"), "source"] = ( + "Stoichiometric calculation" + ) + + costs.loc[("electrobiofuels", "efficiency-biomass"), "value"] = ( + costs.loc[("electrobiofuels", "C in fuel"), "value"] + * input_CO2_intensity + / oil_CO2_intensity + ) + costs.loc[("electrobiofuels", "efficiency-biomass"), "unit"] = "per unit" + costs.loc[("electrobiofuels", "efficiency-biomass"), "source"] = ( + "Stoichiometric calculation" + ) + + efuel_scale_factor = ( + costs.loc[("BtL", "C stored"), "value"] + * costs.loc[("Fischer-Tropsch", "capture rate"), "value"] + ) + + costs.loc[("electrobiofuels", "efficiency-hydrogen"), "value"] = ( + costs.loc[("Fischer-Tropsch", "efficiency"), "value"] + / efuel_scale_factor + ) + costs.loc[("electrobiofuels", "efficiency-hydrogen"), "unit"] = "per unit" + costs.loc[("electrobiofuels", "efficiency-hydrogen"), "source"] = ( + "Stoichiometric calculation" + ) + + costs.loc[("electrobiofuels", "efficiency-tot"), "value"] = 1 / ( + 1 / costs.loc[("electrobiofuels", "efficiency-hydrogen"), "value"] + + 1 / costs.loc[("electrobiofuels", "efficiency-biomass"), "value"] + ) + costs.loc[("electrobiofuels", "efficiency-tot"), "unit"] = "per unit" + costs.loc[("electrobiofuels", "efficiency-tot"), "source"] = ( + "Stoichiometric calculation" + ) + + inv_cost = ( + btl_cost[year] + + costs.loc[("Fischer-Tropsch", "investment"), "value"] + * efuel_scale_factor + ) + VOM = ( + costs.loc[("BtL", "VOM"), "value"] + + costs.loc[("Fischer-Tropsch", "VOM"), "value"] * efuel_scale_factor + ) + FOM = costs.loc[("BtL", "FOM"), "value"] + medium_out = "oil" + currency_year = costs.loc[ + ("Fischer-Tropsch", "investment"), "currency_year" + ] source = "combination of BtL and electrofuels" - elif tech in ['biogas', 'biogas CC', 'biogas plus hydrogen']: - CH4_density = 0.657 #kg/Nm3 - CO2_density = 1.98 #kg/Nm3 - CH4_vol_energy_density = CH4_specific_energy * CH4_density / (1000 * 3.6) #MJ/Nm3 -> MWh/Nm3 + elif tech in ["biogas", "biogas CC", "biogas plus hydrogen"]: + CH4_density = 0.657 # kg/Nm3 + CO2_density = 1.98 # kg/Nm3 + CH4_vol_energy_density = ( + CH4_specific_energy * CH4_density / (1000 * 3.6) + ) # MJ/Nm3 -> MWh/Nm3 CO2_weight_share = AD_CO2_share * CO2_density - costs.loc[(tech, 'CO2 stored'), 'value'] = CO2_weight_share / CH4_vol_energy_density / 1000 #tCO2/MWh,in (NB: assuming the input is already given in the biogas potential and cost - costs.loc[(tech, 'CO2 stored'), 'unit'] = "tCO2/MWh_th" - costs.loc[(tech, 'CO2 stored'), 'source'] = "Stoichiometric calculation, doi:10.1016/j.apenergy.2022.120016" + costs.loc[(tech, "CO2 stored"), "value"] = ( + CO2_weight_share / CH4_vol_energy_density / 1000 + ) # tCO2/MWh,in (NB: assuming the input is already given in the biogas potential and cost + costs.loc[(tech, "CO2 stored"), "unit"] = "tCO2/MWh_th" + costs.loc[(tech, "CO2 stored"), "source"] = ( + "Stoichiometric calculation, doi:10.1016/j.apenergy.2022.120016" + ) if inv_cost > 0: - costs.loc[(tech, 'investment'), 'value'] = inv_cost - costs.loc[(tech, 'investment'), 'unit'] = "EUR/kW_th" - costs.loc[(tech, 'investment'), 'source'] = source - costs.loc[(tech, 'investment'), 'currency_year'] = currency_year + costs.loc[(tech, "investment"), "value"] = inv_cost + costs.loc[(tech, "investment"), "unit"] = "EUR/kW_th" + costs.loc[(tech, "investment"), "source"] = source + costs.loc[(tech, "investment"), "currency_year"] = currency_year if lifetime > 0: - costs.loc[(tech, 'lifetime'), 'value'] = lifetime - costs.loc[(tech, 'lifetime'), 'unit'] = "years" - costs.loc[(tech, 'lifetime'), 'source'] = source + costs.loc[(tech, "lifetime"), "value"] = lifetime + costs.loc[(tech, "lifetime"), "unit"] = "years" + costs.loc[(tech, "lifetime"), "source"] = source if FOM > 0: - costs.loc[(tech, 'FOM'), 'value'] = FOM - costs.loc[(tech, 'FOM'), 'unit'] = "%/year" - costs.loc[(tech, 'FOM'), 'source'] = source + costs.loc[(tech, "FOM"), "value"] = FOM + costs.loc[(tech, "FOM"), "unit"] = "%/year" + costs.loc[(tech, "FOM"), "source"] = source if VOM > 0: - costs.loc[(tech, 'VOM'), 'value'] = VOM - costs.loc[(tech, 'VOM'), 'unit'] = "EUR/MWh_th" - costs.loc[(tech, 'VOM'), 'source'] = source - costs.loc[(tech, 'VOM'), 'currency_year'] = currency_year + costs.loc[(tech, "VOM"), "value"] = VOM + costs.loc[(tech, "VOM"), "unit"] = "EUR/MWh_th" + costs.loc[(tech, "VOM"), "source"] = source + costs.loc[(tech, "VOM"), "currency_year"] = currency_year return costs -def energy_penalty(costs): +def energy_penalty(costs): # Energy penalty for biomass carbon capture # Need to take steam production for CC into account, assumed with the main feedstock, # e.g. the input biomass is used also for steam, and the efficiency for el and heat is scaled down accordingly - for tech in ['central solid biomass CHP CC', 'waste CHP CC', 'solid biomass boiler steam CC', 'direct firing solid fuels CC', 'direct firing gas CC', 'biogas CC']: - - if 'powerboost' in tech: - boiler = 'electric boiler steam' - feedstock = 'solid biomass' - co2_capture = costs.loc[(feedstock, 'CO2 intensity'), 'value'] - elif 'gas' in tech: - boiler = 'gas boiler steam' - feedstock = 'gas' - co2_capture = costs.loc[(feedstock, 'CO2 intensity'), 'value'] - elif 'biogas' in tech: - boiler = 'gas boiler steam' - co2_capture = costs.loc[(tech, 'CO2 stored'), 'value'] + for tech in [ + "central solid biomass CHP CC", + "waste CHP CC", + "solid biomass boiler steam CC", + "direct firing solid fuels CC", + "direct firing gas CC", + "biogas CC", + ]: + if "powerboost" in tech: + boiler = "electric boiler steam" + feedstock = "solid biomass" + co2_capture = costs.loc[(feedstock, "CO2 intensity"), "value"] + elif "gas" in tech: + boiler = "gas boiler steam" + feedstock = "gas" + co2_capture = costs.loc[(feedstock, "CO2 intensity"), "value"] + elif "biogas" in tech: + boiler = "gas boiler steam" + co2_capture = costs.loc[(tech, "CO2 stored"), "value"] else: - boiler = 'solid biomass boiler steam' - feedstock = 'solid biomass' - co2_capture = costs.loc[(feedstock, 'CO2 intensity'), 'value'] - - #Scaling biomass input to account for heat demand of carbon capture - scalingFactor = 1 / (1 + co2_capture * costs.loc[ - ('biomass CHP capture', 'heat-input'), 'value'] - / costs.loc[(boiler, 'efficiency'), 'value']) + boiler = "solid biomass boiler steam" + feedstock = "solid biomass" + co2_capture = costs.loc[(feedstock, "CO2 intensity"), "value"] + + # Scaling biomass input to account for heat demand of carbon capture + scalingFactor = 1 / ( + 1 + + co2_capture + * costs.loc[("biomass CHP capture", "heat-input"), "value"] + / costs.loc[(boiler, "efficiency"), "value"] + ) - eta_steam = (1 - scalingFactor) * costs.loc[(boiler, 'efficiency'), 'value'] - eta_old = costs.loc[(tech, 'efficiency'), 'value'] + eta_steam = (1 - scalingFactor) * costs.loc[(boiler, "efficiency"), "value"] + eta_old = costs.loc[(tech, "efficiency"), "value"] - temp = costs.loc[(tech, 'efficiency'), 'value'] - eta_main = costs.loc[(tech, 'efficiency'), 'value'] * scalingFactor + temp = costs.loc[(tech, "efficiency"), "value"] + eta_main = costs.loc[(tech, "efficiency"), "value"] * scalingFactor # Adapting investment share of tech due to steam boiler addition. Investment per MW_el. - costs.loc[(tech, 'investment'), 'value'] = costs.loc[(tech, 'investment'), 'value'] * eta_old / eta_main \ - + costs.loc[(boiler, 'investment'), 'value'] * eta_steam / eta_main - costs.loc[(tech, 'investment'), 'source'] = 'Combination of ' + tech + ' and ' + boiler - costs.loc[(tech, 'investment'), 'further description'] = '' + costs.loc[(tech, "investment"), "value"] = ( + costs.loc[(tech, "investment"), "value"] * eta_old / eta_main + + costs.loc[(boiler, "investment"), "value"] * eta_steam / eta_main + ) + costs.loc[(tech, "investment"), "source"] = ( + "Combination of " + tech + " and " + boiler + ) + costs.loc[(tech, "investment"), "further description"] = "" - if costs.loc[(tech, 'VOM'), 'value']: + if costs.loc[(tech, "VOM"), "value"]: break else: - costs.loc[(tech, 'VOM'), 'value'] = 0. - - costs.loc[(tech, 'VOM'), 'value'] = costs.loc[(tech, 'VOM'), 'value'] * eta_old / eta_main \ - + costs.loc[(boiler, 'VOM'), 'value'] * eta_steam / eta_main - costs.loc[(tech, 'VOM'), 'source'] = 'Combination of ' + tech + ' and ' + boiler - costs.loc[(tech, 'VOM'), 'further description'] = '' - - costs.loc[(tech, 'efficiency'), 'value'] = eta_main - costs.loc[(tech, 'efficiency'), 'source'] = 'Combination of ' + tech + ' and ' + boiler - costs.loc[(tech, 'efficiency'), 'further description'] = '' - - if 'CHP' in tech: - costs.loc[(tech, 'efficiency-heat'), 'value'] = \ - costs.loc[(tech, 'efficiency-heat'), 'value'] * scalingFactor \ - + costs.loc[('solid biomass', 'CO2 intensity'), 'value'] * \ - (costs.loc[('biomass CHP capture', 'heat-output'), 'value'] + - costs.loc[('biomass CHP capture', 'compression-heat-output'), 'value']) - costs.loc[(tech, 'efficiency-heat'), 'source'] = 'Combination of ' + tech + ' and ' + boiler - costs.loc[(tech, 'efficiency-heat'), 'further description'] = '' - - if 'biogas CC' in tech: - costs.loc[(tech, 'VOM'), 'value'] = 0 - costs.loc[(tech, 'VOM'), 'unit'] = 'EUR/MWh' - - costs.loc[(tech, 'VOM'), 'value'] = costs.loc[(tech, 'VOM'), 'value'] * eta_old / eta_main \ - + costs.loc[(boiler, 'VOM'), 'value'] * eta_steam / eta_main - costs.loc[(tech, 'VOM'), 'source'] = 'Combination of ' + tech + ' and ' + boiler - costs.loc[(tech, 'VOM'), 'further description'] = '' + costs.loc[(tech, "VOM"), "value"] = 0.0 + + costs.loc[(tech, "VOM"), "value"] = ( + costs.loc[(tech, "VOM"), "value"] * eta_old / eta_main + + costs.loc[(boiler, "VOM"), "value"] * eta_steam / eta_main + ) + costs.loc[(tech, "VOM"), "source"] = "Combination of " + tech + " and " + boiler + costs.loc[(tech, "VOM"), "further description"] = "" + + costs.loc[(tech, "efficiency"), "value"] = eta_main + costs.loc[(tech, "efficiency"), "source"] = ( + "Combination of " + tech + " and " + boiler + ) + costs.loc[(tech, "efficiency"), "further description"] = "" + + if "CHP" in tech: + costs.loc[(tech, "efficiency-heat"), "value"] = costs.loc[ + (tech, "efficiency-heat"), "value" + ] * scalingFactor + costs.loc[ + ("solid biomass", "CO2 intensity"), "value" + ] * ( + costs.loc[("biomass CHP capture", "heat-output"), "value"] + + costs.loc[("biomass CHP capture", "compression-heat-output"), "value"] + ) + costs.loc[(tech, "efficiency-heat"), "source"] = ( + "Combination of " + tech + " and " + boiler + ) + costs.loc[(tech, "efficiency-heat"), "further description"] = "" + + if "biogas CC" in tech: + costs.loc[(tech, "VOM"), "value"] = 0 + costs.loc[(tech, "VOM"), "unit"] = "EUR/MWh" + + costs.loc[(tech, "VOM"), "value"] = ( + costs.loc[(tech, "VOM"), "value"] * eta_old / eta_main + + costs.loc[(boiler, "VOM"), "value"] * eta_steam / eta_main + ) + costs.loc[(tech, "VOM"), "source"] = "Combination of " + tech + " and " + boiler + costs.loc[(tech, "VOM"), "further description"] = "" return costs + def add_egs_data(data): """ Adds data of enhanced geothermal systems. Data taken from Aghahosseini, Breyer 2020: From hot rock to useful energy... - - """ - parameters = ["CO2 intensity", "lifetime", "efficiency residential heat", "efficiency electricity", "FOM"] + + """ + parameters = [ + "CO2 intensity", + "lifetime", + "efficiency residential heat", + "efficiency electricity", + "FOM", + ] techs = ["geothermal"] - multi_i = pd.MultiIndex.from_product([techs, parameters], names=["technology", "parameter"]) + multi_i = pd.MultiIndex.from_product( + [techs, parameters], names=["technology", "parameter"] + ) geoth_df = pd.DataFrame(index=multi_i, columns=data.columns) years = [col for col in data.columns if isinstance(col, int)] # lifetime - geoth_df.loc[("geothermal", "lifetime"), years] = 30 #years + geoth_df.loc[("geothermal", "lifetime"), years] = 30 # years geoth_df.loc[("geothermal", "lifetime"), "unit"] = "years" geoth_df.loc[("geothermal", "lifetime"), "source"] = source_dict["Aghahosseini2020"] # co2 emissions - geoth_df.loc[("geothermal", "CO2 intensity"), years] = 0.12 # tCO2/MWh_el + geoth_df.loc[("geothermal", "CO2 intensity"), years] = 0.12 # tCO2/MWh_el geoth_df.loc[("geothermal", "CO2 intensity"), "unit"] = "tCO2/MWh_el" - geoth_df.loc[("geothermal", "CO2 intensity"), "source"] = source_dict["Aghahosseini2020"] - geoth_df.loc[("geothermal", "CO2 intensity"), "further description"] = "Likely to be improved; Average of 85 percent of global egs power plant capacity" + geoth_df.loc[("geothermal", "CO2 intensity"), "source"] = source_dict[ + "Aghahosseini2020" + ] + geoth_df.loc[("geothermal", "CO2 intensity"), "further description"] = ( + "Likely to be improved; Average of 85 percent of global egs power plant capacity" + ) # efficiency for heat generation using organic rankine cycle geoth_df.loc[("geothermal", "efficiency residential heat"), years] = 0.8 geoth_df.loc[("geothermal", "efficiency residential heat"), "unit"] = "per unit" - geoth_df.loc[("geothermal", "efficiency residential heat"), "source"] = "{}; {}".format(source_dict["Aghahosseini2020"], source_dict["Breede2015"]) - geoth_df.loc[("geothermal", "efficiency residential heat"), "further description"] = "This is a rough estimate, depends on local conditions" + geoth_df.loc[("geothermal", "efficiency residential heat"), "source"] = ( + "{}; {}".format(source_dict["Aghahosseini2020"], source_dict["Breede2015"]) + ) + geoth_df.loc[ + ("geothermal", "efficiency residential heat"), "further description" + ] = "This is a rough estimate, depends on local conditions" # efficiency for electricity generation using organic rankine cycle geoth_df.loc[("geothermal", "efficiency electricity"), years] = 0.1 geoth_df.loc[("geothermal", "efficiency electricity"), "unit"] = "per unit" - geoth_df.loc[("geothermal", "efficiency electricity"), "source"] = "{}; {}".format(source_dict["Aghahosseini2020"], source_dict["Breede2015"]) - geoth_df.loc[("geothermal", "efficiency electricity"), "further description"] = "This is a rough estimate, depends on local conditions" + geoth_df.loc[("geothermal", "efficiency electricity"), "source"] = "{}; {}".format( + source_dict["Aghahosseini2020"], source_dict["Breede2015"] + ) + geoth_df.loc[("geothermal", "efficiency electricity"), "further description"] = ( + "This is a rough estimate, depends on local conditions" + ) # relative additional capital cost of using residual heat for district heating (25 percent) geoth_df.loc[("geothermal", "district heating cost"), years] = 0.25 geoth_df.loc[("geothermal", "district heating cost"), "unit"] = "%" - geoth_df.loc[("geothermal", "district heating cost"), "source"] = "{}".format(source_dict["Frey2022"]) - geoth_df.loc[("geothermal", "district heating cost"), "further description"] = "If capital cost of electric generation from EGS is 100%, district heating adds additional 25%" + geoth_df.loc[("geothermal", "district heating cost"), "source"] = "{}".format( + source_dict["Frey2022"] + ) + geoth_df.loc[("geothermal", "district heating cost"), "further description"] = ( + "If capital cost of electric generation from EGS is 100%, district heating adds additional 25%" + ) # fixed operational costs - geoth_df.loc[("geothermal", "FOM"), years] = 2. + geoth_df.loc[("geothermal", "FOM"), years] = 2.0 geoth_df.loc[("geothermal", "FOM"), "unit"] = "%/year" - geoth_df.loc[("geothermal", "FOM"), "source"] = source_dict["Aghahosseini2020"] - geoth_df.loc[("geothermal", "FOM"), "further description"] = "Both for flash, binary and ORC plants. See Supplemental Material for details" - - geoth_df = geoth_df.dropna(axis=1, how='all') - + geoth_df.loc[("geothermal", "FOM"), "source"] = source_dict["Aghahosseini2020"] + geoth_df.loc[("geothermal", "FOM"), "further description"] = ( + "Both for flash, binary and ORC plants. See Supplemental Material for details" + ) + + geoth_df = geoth_df.dropna(axis=1, how="all") + return pd.concat([data, geoth_df]) -def annuity(n,r=0.07): +def annuity(n, r=0.07): """ Calculate the annuity factor for an asset with lifetime n years and discount rate of r """ if isinstance(r, pd.Series): - return pd.Series(1/n, index=r.index).where(r == 0, r/(1. - 1./(1.+r)**n)) + return pd.Series(1 / n, index=r.index).where( + r == 0, r / (1.0 - 1.0 / (1.0 + r) ** n) + ) elif r > 0: - return r/(1. - 1./(1.+r)**n) + return r / (1.0 - 1.0 / (1.0 + r) ** n) else: - return 1/n + return 1 / n + def add_home_battery_costs(costs): """ - adds investment costs for home battery storage and inverter. + Adds investment costs for home battery storage and inverter. Since home battery costs are not part of the DEA cataloque, utility-scale costs are multiplied by a factor determined by data from the EWG study """ # get DEA assumptions for utility scale - home_battery = (data.loc[["battery storage", "battery inverter"]] - .rename(index=lambda x: "home " + x, level=0)) + home_battery = data.loc[["battery storage", "battery inverter"]].rename( + index=lambda x: "home " + x, level=0 + ) # get EWG cost assumptions - costs_ewg = pd.read_csv(snakemake.input.EWG_costs, - index_col=list(range(2))).sort_index() + costs_ewg = pd.read_csv( + snakemake.input.EWG_costs, index_col=list(range(2)) + ).sort_index() v = costs_ewg.unstack()[[str(year) for year in years]].swaplevel(axis=1) - def annuity(n,r=0.07): + def annuity(n, r=0.07): """ Calculate the annuity factor for an asset with lifetime n years and discount rate of r """ if isinstance(r, pd.Series): - return pd.Series(1/n, index=r.index).where(r == 0, r/(1. - 1./(1.+r)**n)) + return pd.Series(1 / n, index=r.index).where( + r == 0, r / (1.0 - 1.0 / (1.0 + r) ** n) + ) elif r > 0: - return r/(1. - 1./(1.+r)**n) + return r / (1.0 - 1.0 / (1.0 + r) ** n) else: - return 1/n + return 1 / n # annualise EWG cost assumptions - fixed = (annuity(v["lifetime"])+v["FOM"]/100.) * v["investment"] + fixed = (annuity(v["lifetime"]) + v["FOM"] / 100.0) * v["investment"] # battery storage index in EWG -------------- battery_store_i = [ - 'Battery PV prosumer - commercial storage', - 'Battery PV prosumer - industrial storage', - 'Battery PV prosumer - residential storage', - 'Battery storage'] + "Battery PV prosumer - commercial storage", + "Battery PV prosumer - industrial storage", + "Battery PV prosumer - residential storage", + "Battery storage", + ] battery_store_ewg = fixed.loc[battery_store_i].T def get_factor(df, cols, utility_col): - """get factor by which costs are increasing for home installations""" - return (df[cols].div(df[utility_col], axis=0).mean(axis=1) - .rename(index=lambda x: float(x))) + """Get factor by which costs are increasing for home installations""" + return ( + df[cols] + .div(df[utility_col], axis=0) + .mean(axis=1) + .rename(index=lambda x: float(x)) + ) # take mean of cost increase for commercial and residential storage compared to utility-scale - home_cols = ['Battery PV prosumer - commercial storage', - 'Battery PV prosumer - residential storage'] + home_cols = [ + "Battery PV prosumer - commercial storage", + "Battery PV prosumer - residential storage", + ] factor = get_factor(battery_store_ewg, home_cols, "Battery storage") - home_cost = (home_battery.loc[("home battery storage", "investment"), years] * factor).values + home_cost = ( + home_battery.loc[("home battery storage", "investment"), years] * factor + ).values home_battery.loc[("home battery storage", "investment"), years] = home_cost # battery inverter index in EWG ----------------------- battery_inverter_i = [ - 'Battery PV prosumer - commercial interface', - 'Battery PV prosumer - industrial interface PHES', - 'Battery PV prosumer - residential interface', - 'Battery interface'] + "Battery PV prosumer - commercial interface", + "Battery PV prosumer - industrial interface PHES", + "Battery PV prosumer - residential interface", + "Battery interface", + ] battery_inverter_ewg = fixed.loc[battery_inverter_i].T - home_cols = ['Battery PV prosumer - commercial interface', - 'Battery PV prosumer - residential interface'] + home_cols = [ + "Battery PV prosumer - commercial interface", + "Battery PV prosumer - residential interface", + ] factor = get_factor(battery_inverter_ewg, home_cols, "Battery interface") - home_cost = (home_battery.loc[("home battery inverter", "investment"), years] * factor).values + home_cost = ( + home_battery.loc[("home battery inverter", "investment"), years] * factor + ).values home_battery.loc[("home battery inverter", "investment"), years] = home_cost # adjust source - home_battery["source"] = home_battery["source"].apply(lambda x: source_dict["EWG"] + ", " + x) + home_battery["source"] = home_battery["source"].apply( + lambda x: source_dict["EWG"] + ", " + x + ) return pd.concat([costs, home_battery]) def add_SMR_data(data): - """Add steam methane reforming (SMR) technology data. + """ + Add steam methane reforming (SMR) technology data. investment cost : Currently no cost reduction for investment costs of SMR CC assumed. @@ -2267,12 +2881,14 @@ def add_SMR_data(data): """ parameters = ["FOM", "investment", "lifetime", "efficiency"] techs = ["SMR", "SMR CC"] - multi_i = pd.MultiIndex.from_product([techs, parameters], names=["technology", "parameter"]) + multi_i = pd.MultiIndex.from_product( + [techs, parameters], names=["technology", "parameter"] + ) SMR_df = pd.DataFrame(index=multi_i, columns=data.columns) # efficiencies per unit in LHV (stays constant 2019 to 2050) - SMR_df.loc[("SMR", "efficiency"), years] = 0.76 - SMR_df.loc[("SMR CC", "efficiency"), years] = 0.69 + SMR_df.loc[("SMR", "efficiency"), years] = 0.76 + SMR_df.loc[("SMR CC", "efficiency"), years] = 0.69 SMR_df.loc[(techs, "efficiency"), "source"] = source_dict["IEA"] SMR_df.loc[(techs, "efficiency"), "unit"] = "per unit (in LHV)" @@ -2285,53 +2901,71 @@ def add_SMR_data(data): SMR_df.loc[(techs, "FOM"), years] = 5 SMR_df.loc[(techs, "FOM"), "source"] = source_dict["DEA"] SMR_df.loc[(techs, "FOM"), "unit"] = "%/year" - SMR_df.loc[(techs, "FOM"), "further description"] = "Technology data for renewable fuels, in pdf on table 3 p.311" + SMR_df.loc[(techs, "FOM"), "further description"] = ( + "Technology data for renewable fuels, in pdf on table 3 p.311" + ) # investment # investment given in unit EUR/kg H_2/h -> convert to EUR/MW_CH4 # lower heating value (LHV) of H2 - LHV_H2 = 33.33 # unit kWh/kg - SMR = 12500 / LHV_H2 * 1e3 * 1/SMR_df.loc[("SMR", "efficiency"), years] - SMR_CCS = 14500 / LHV_H2 * 1e3 * 1/SMR_df.loc[("SMR", "efficiency"), years] + LHV_H2 = 33.33 # unit kWh/kg + SMR = 12500 / LHV_H2 * 1e3 * 1 / SMR_df.loc[("SMR", "efficiency"), years] + SMR_CCS = 14500 / LHV_H2 * 1e3 * 1 / SMR_df.loc[("SMR", "efficiency"), years] SMR_df.loc[("SMR", "investment"), years] = SMR SMR_df.loc[("SMR CC", "investment"), years] = SMR_CCS SMR_df.loc[(techs, "investment"), "source"] = source_dict["DEA"] SMR_df.loc[(techs, "investment"), "unit"] = "EUR/MW_CH4" SMR_df.loc[(techs, "investment"), "currency_year"] = 2015 - SMR_df.loc[(techs, "investment"), "further description"] = "Technology data for renewable fuels, in pdf on table 3 p.311" + SMR_df.loc[(techs, "investment"), "further description"] = ( + "Technology data for renewable fuels, in pdf on table 3 p.311" + ) # carbon capture rate SMR_df.loc[("SMR CC", "capture_rate"), years] = 0.9 SMR_df.loc[("SMR CC", "capture_rate"), "source"] = source_dict["IEA"] SMR_df.loc[("SMR CC", "capture_rate"), "unit"] = "EUR/MW_CH4" - SMR_df.loc[("SMR CC", "capture_rate"), "further description"] = "wide range: capture rates betwen 54%-90%" - - SMR_df = SMR_df.dropna(axis=1, how='all') - + SMR_df.loc[("SMR CC", "capture_rate"), "further description"] = ( + "wide range: capture rates betwen 54%-90%" + ) + + SMR_df = SMR_df.dropna(axis=1, how="all") + return pd.concat([data, SMR_df]) def add_mean_solar_rooftop(data): # take mean of rooftop commercial and residential - rooftop = (data.loc[data.index.get_level_values(0) - .str.contains("solar-rooftop")][years] - .astype(float).groupby(level=1).mean()) + rooftop = ( + data.loc[data.index.get_level_values(0).str.contains("solar-rooftop")][years] + .astype(float) + .groupby(level=1) + .mean() + ) for col in data.columns[~data.columns.isin(years)]: rooftop[col] = data.loc["solar-rooftop residential"][col] # set multi index rooftop = pd.concat([rooftop], keys=["solar-rooftop"]) rooftop["source"] = "Calculated. See 'further description'." - rooftop["further description"] = "Mixed investment costs based on average of 50% 'solar-rooftop commercial' and 50% 'solar-rooftop residential'" + rooftop["further description"] = ( + "Mixed investment costs based on average of 50% 'solar-rooftop commercial' and 50% 'solar-rooftop residential'" + ) # add to data rooftop.index.names = data.index.names data = pd.concat([data, rooftop]) # add solar assuming 50% utility and 50% rooftop - solar = (data.loc[["solar-rooftop", "solar-utility"]][years]).astype(float).groupby(level=1).mean() + solar = ( + (data.loc[["solar-rooftop", "solar-utility"]][years]) + .astype(float) + .groupby(level=1) + .mean() + ) for col in data.columns[~data.columns.isin(years)]: - solar[col] = data.loc["solar-rooftop residential"][col] + solar[col] = data.loc["solar-rooftop residential"][col] solar["source"] = "Calculated. See 'further description'." - solar["further description"] = "Mixed investment costs based on average of 50% 'solar-rooftop' and 50% 'solar-utility'" + solar["further description"] = ( + "Mixed investment costs based on average of 50% 'solar-rooftop' and 50% 'solar-utility'" + ) # set multi index solar = pd.concat([solar], keys=["solar"]) solar.index.names = data.index.names @@ -2339,8 +2973,9 @@ def add_mean_solar_rooftop(data): def add_energy_storage_database(costs, data_year): - """Add energy storage database compiled - + """ + Add energy storage database compiled + Learning rate drop. For example, the nominal DC SB learning rate for RFBs is set at 4.5%, 1.5% for lead-acid batteries, compared to 10% for Li-ion batteries, corresponding to cost drops of 17%, 6%, and 35%, respectively. For the rest of the categories for battery-based systems, the learning @@ -2372,79 +3007,117 @@ def add_energy_storage_database(costs, data_year): "reference": str, "ref_size_MW": float, "EP_ratio_h": float, - }, + }, ) df = df.drop(columns=["ref_size_MW", "EP_ratio_h"]) df = df.fillna(df.dtypes.replace({"float64": 0.0, "O": "NULL"})) - df.loc[:,"unit"] = df.unit.str.replace("NULL", "per unit") + df.loc[:, "unit"] = df.unit.str.replace("NULL", "per unit") - # b) Change data to PyPSA format (aggregation of components, units, currency, etc.) + # b) Change data to PyPSA format (aggregation of components, units, currency, etc.) df = clean_up_units(df, "value") # base clean up # rewrite technology to be charger, store, discharger, bidirectional-charger - df.loc[:,"carrier"] = df.carrier.str.replace("NULL", "") - df.loc[:,"carrier"] = df["carrier"].apply(lambda x: x.split('-')) + df.loc[:, "carrier"] = df.carrier.str.replace("NULL", "") + df.loc[:, "carrier"] = df["carrier"].apply(lambda x: x.split("-")) carrier_list_len = df["carrier"].apply(lambda x: len(x)) carrier_str_len = df["carrier"].apply(lambda x: len(x[0])) - carrier_first_item = df["carrier"].apply(lambda x: x[0]) - carrier_last_item = df["carrier"].apply(lambda x: x[-1]) - bicharger_filter = (carrier_list_len == 3) + carrier_first_item = df["carrier"].apply(lambda x: x[0]) + carrier_last_item = df["carrier"].apply(lambda x: x[-1]) + bicharger_filter = carrier_list_len == 3 charger_filter = (carrier_list_len == 2) & (carrier_first_item == "elec") discharger_filter = (carrier_list_len == 2) & (carrier_last_item == "elec") store_filter = (carrier_list_len == 1) & (carrier_str_len > 0) - reference_filter = (carrier_list_len == 1) & (carrier_first_item == "reference_value") + reference_filter = (carrier_list_len == 1) & ( + carrier_first_item == "reference_value" + ) df = df[~reference_filter] # remove reference values - df.loc[bicharger_filter,"technology_type"] = "bicharger" - df.loc[charger_filter,"technology_type"] = "charger" - df.loc[discharger_filter,"technology_type"] = "discharger" - df.loc[store_filter,"technology_type"] = "store" - df.loc[df.unit=="EUR/MWh-year", "technology_type"] = "store" - # Some investment inputs need to be distributed between charger and discharger + df.loc[bicharger_filter, "technology_type"] = "bicharger" + df.loc[charger_filter, "technology_type"] = "charger" + df.loc[discharger_filter, "technology_type"] = "discharger" + df.loc[store_filter, "technology_type"] = "store" + df.loc[df.unit == "EUR/MWh-year", "technology_type"] = "store" + # Some investment inputs need to be distributed between charger and discharger for tech in df.technology.unique(): - nan_filter = (df.technology==tech) & (carrier_str_len==0) & (df.parameter=="investment") - store_filter = nan_filter & (df.unit=="EUR/MWh") + nan_filter = ( + (df.technology == tech) + & (carrier_str_len == 0) + & (df.parameter == "investment") + ) + store_filter = nan_filter & (df.unit == "EUR/MWh") if not df.loc[store_filter].empty: - df.loc[store_filter, "technology_type"] = "store" # value will be aggregated later in the groupby + df.loc[store_filter, "technology_type"] = ( + "store" # value will be aggregated later in the groupby + ) # charger and discharger with 50% distribution e.g. in case of Hydrogen - power_filter = nan_filter & (df.unit=="EUR/MW") + power_filter = nan_filter & (df.unit == "EUR/MW") if not df.loc[power_filter].empty: - agg = df.loc[power_filter].groupby(["technology", "year"]).sum(numeric_only=True) - charger_investment_filter = charger_filter & (df.technology==tech) & (df.parameter=="investment") - discharger_investment_filter = discharger_filter & (df.technology==tech) & (df.parameter=="investment") - df.loc[charger_investment_filter & df.year==2021, "value"] += agg.loc[(tech, 2021)]/2 - df.loc[charger_investment_filter & df.year==2030, "value"] += agg.loc[(tech, 2030)]/2 - df.loc[discharger_investment_filter & df.year==2021, "value"] += agg.loc[(tech, 2021)]/2 - df.loc[discharger_investment_filter & df.year==2030, "value"] += agg.loc[(tech, 2030)]/2 - df.loc[:,"technology"] = df["technology"] + "-" + df["technology_type"] + agg = ( + df.loc[power_filter] + .groupby(["technology", "year"]) + .sum(numeric_only=True) + ) + charger_investment_filter = ( + charger_filter + & (df.technology == tech) + & (df.parameter == "investment") + ) + discharger_investment_filter = ( + discharger_filter + & (df.technology == tech) + & (df.parameter == "investment") + ) + df.loc[charger_investment_filter & df.year == 2021, "value"] += ( + agg.loc[(tech, 2021)] / 2 + ) + df.loc[charger_investment_filter & df.year == 2030, "value"] += ( + agg.loc[(tech, 2030)] / 2 + ) + df.loc[discharger_investment_filter & df.year == 2021, "value"] += ( + agg.loc[(tech, 2021)] / 2 + ) + df.loc[discharger_investment_filter & df.year == 2030, "value"] += ( + agg.loc[(tech, 2030)] / 2 + ) + df.loc[:, "technology"] = df["technology"] + "-" + df["technology_type"] # aggregate technology_type and unit - df = df.groupby(["technology", "unit", "year"]).agg({ - 'technology': 'first', - 'year': 'first', - 'parameter': 'first', - 'value': 'sum', - 'unit': 'first', - 'type': 'first', - 'carrier': 'first', - 'technology_type': 'first', - 'source': 'first', - 'note': 'first', - 'reference': 'first', - }).reset_index(drop=True) + df = ( + df.groupby(["technology", "unit", "year"]) + .agg( + { + "technology": "first", + "year": "first", + "parameter": "first", + "value": "sum", + "unit": "first", + "type": "first", + "carrier": "first", + "technology_type": "first", + "source": "first", + "note": "first", + "reference": "first", + } + ) + .reset_index(drop=True) + ) # calculate %/year FOM on aggregated values for tech in df.technology.unique(): for year in df.year.unique(): df_tech = df.loc[(df.technology == tech) & (df.year == year)].copy() - a = df_tech.loc[df_tech.unit=="EUR/MW-year", "value"].values - b = df_tech.loc[df_tech.unit=="EUR/MW", "value"].values - df.loc[df_tech.loc[df_tech.unit=="EUR/MW-year"].index, "value"] = a / b * 100 # EUR/MW-year / EUR/MW = %/year - c = df_tech.loc[df_tech.unit=="EUR/MWh-year", "value"].values - d = df_tech.loc[df_tech.unit=="EUR/MWh", "value"].values - df.loc[df_tech.loc[df_tech.unit=="EUR/MWh-year"].index, "value"] = c / d * 100 # EUR/MWh-year / EUR/MWh = %/year - - df.loc[:,"unit"] = df.unit.str.replace("EUR/MW-year", "%/year") - df.loc[:,"unit"] = df.unit.str.replace("EUR/MWh-year", "%/year") + a = df_tech.loc[df_tech.unit == "EUR/MW-year", "value"].values + b = df_tech.loc[df_tech.unit == "EUR/MW", "value"].values + df.loc[df_tech.loc[df_tech.unit == "EUR/MW-year"].index, "value"] = ( + a / b * 100 + ) # EUR/MW-year / EUR/MW = %/year + c = df_tech.loc[df_tech.unit == "EUR/MWh-year", "value"].values + d = df_tech.loc[df_tech.unit == "EUR/MWh", "value"].values + df.loc[df_tech.loc[df_tech.unit == "EUR/MWh-year"].index, "value"] = ( + c / d * 100 + ) # EUR/MWh-year / EUR/MWh = %/year + + df.loc[:, "unit"] = df.unit.str.replace("EUR/MW-year", "%/year") + df.loc[:, "unit"] = df.unit.str.replace("EUR/MWh-year", "%/year") # c) Linear Inter/Extrapolation # data available for 2021 and 2030, but value for given "year" passed by function needs to be calculated @@ -2454,25 +3127,27 @@ def add_energy_storage_database(costs, data_year): y = df.loc[filter, "value"] if y.empty: continue # nothing to interpolate - elif y.iloc[0]==y.iloc[1] or param=="efficiency" or param=="lifetime": + elif y.iloc[0] == y.iloc[1] or param == "efficiency" or param == "lifetime": ynew = y.iloc[1] # assume new value is the same as 2030 - elif y.iloc[0]!=y.iloc[1]: - x = df.loc[filter, "year"] # both values 2021+2030 - first_segment_diff = y.iloc[0]-y.iloc[1] + elif y.iloc[0] != y.iloc[1]: + x = df.loc[filter, "year"] # both values 2021+2030 + first_segment_diff = y.iloc[0] - y.iloc[1] endp_first_segment = y.iloc[1] - + # Below we create linear segments between 2021-2030 # While the first segment is known, the others are defined by the initial segments with a accumulating quadratic descreasing gradient other_segments_points = [2034, 2039, 2044, 2049, 2054, 2059] - - def geometric_series(nominator, denominator=1, number_of_terms=1, start=1): + + def geometric_series( + nominator, denominator=1, number_of_terms=1, start=1 + ): """ A geometric series is a series with a constant ratio between successive terms. When moving to infinity the geometric series converges to a limit. https://en.wikipedia.org/wiki/Series_(mathematics) Example: - -------- + ------- nominator = 1 denominator = 2 number_of_terms = 3 @@ -2481,37 +3156,85 @@ def geometric_series(nominator, denominator=1, number_of_terms=1, start=1): If moving to infinity the result converges to 2 """ - return sum([nominator/denominator**i for i in range(start, start+number_of_terms)]) - - if tech=="Hydrogen-discharger" or tech=="Pumped-Heat-store": - x1 = pd.concat([x,pd.DataFrame(other_segments_points)], ignore_index=True) + return sum( + [ + nominator / denominator**i + for i in range(start, start + number_of_terms) + ] + ) + + if tech == "Hydrogen-discharger" or tech == "Pumped-Heat-store": + x1 = pd.concat( + [x, pd.DataFrame(other_segments_points)], ignore_index=True + ) y1 = y factor = 5 - for i in range(len(other_segments_points)): # -1 because of segments - cost_at_year = endp_first_segment - geometric_series(nominator=first_segment_diff, denominator=factor, number_of_terms=i+1) - y1 = pd.concat([y1, pd.DataFrame([cost_at_year])], ignore_index=True) - f = interpolate.interp1d(x1.squeeze(), y1.squeeze(), kind='linear', fill_value="extrapolate") - elif tech=="Hydrogen-charger": - x2 = pd.concat([x,pd.DataFrame(other_segments_points)], ignore_index=True) + for i in range( + len(other_segments_points) + ): # -1 because of segments + cost_at_year = endp_first_segment - geometric_series( + nominator=first_segment_diff, + denominator=factor, + number_of_terms=i + 1, + ) + y1 = pd.concat( + [y1, pd.DataFrame([cost_at_year])], ignore_index=True + ) + f = interpolate.interp1d( + x1.squeeze(), + y1.squeeze(), + kind="linear", + fill_value="extrapolate", + ) + elif tech == "Hydrogen-charger": + x2 = pd.concat( + [x, pd.DataFrame(other_segments_points)], ignore_index=True + ) y2 = y factor = 6.5 for i in range(len(other_segments_points)): - cost_at_year = endp_first_segment - geometric_series(nominator=first_segment_diff, denominator=factor, number_of_terms=i+1) - y2 = pd.concat([y2, pd.DataFrame([cost_at_year])], ignore_index=True) - f = interpolate.interp1d(x2.squeeze(), y2.squeeze(), kind='linear', fill_value="extrapolate") + cost_at_year = endp_first_segment - geometric_series( + nominator=first_segment_diff, + denominator=factor, + number_of_terms=i + 1, + ) + y2 = pd.concat( + [y2, pd.DataFrame([cost_at_year])], ignore_index=True + ) + f = interpolate.interp1d( + x2.squeeze(), + y2.squeeze(), + kind="linear", + fill_value="extrapolate", + ) else: - x3 = pd.concat([x,pd.DataFrame(other_segments_points)], ignore_index=True) + x3 = pd.concat( + [x, pd.DataFrame(other_segments_points)], ignore_index=True + ) y3 = y factor = 2 for i in range(len(other_segments_points)): - cost_at_year = endp_first_segment - geometric_series(nominator=first_segment_diff, denominator=factor, number_of_terms=i+1) - y3 = pd.concat([y3, pd.DataFrame([cost_at_year])], ignore_index=True) - f = interpolate.interp1d(x3.squeeze(), y3.squeeze(), kind='linear', fill_value="extrapolate") - - option = snakemake.config['energy_storage_database']['pnnl_energy_storage'] - if option.get('approx_beyond_2030') == ["geometric_series"]: + cost_at_year = endp_first_segment - geometric_series( + nominator=first_segment_diff, + denominator=factor, + number_of_terms=i + 1, + ) + y3 = pd.concat( + [y3, pd.DataFrame([cost_at_year])], ignore_index=True + ) + f = interpolate.interp1d( + x3.squeeze(), + y3.squeeze(), + kind="linear", + fill_value="extrapolate", + ) + + option = snakemake.config["energy_storage_database"][ + "pnnl_energy_storage" + ] + if option.get("approx_beyond_2030") == ["geometric_series"]: ynew = f(data_year) - if option.get('approx_beyond_2030') == ["same_as_2030"]: + if option.get("approx_beyond_2030") == ["same_as_2030"]: if data_year <= 2030: # apply linear interpolation ynew = f(data_year) @@ -2519,19 +3242,25 @@ def geometric_series(nominator, denominator=1, number_of_terms=1, start=1): # apply same value as 2030 ynew = y.iloc[1] # assume new value is the same as 2030 - df_new = pd.DataFrame([{ - "technology": tech, - "year": data_year, - "parameter": param, - "value": ynew, - "unit": df.loc[filter, "unit"].unique().item(), - "source": df.loc[filter, "source"].unique().item(), - 'carrier': df.loc[filter, "carrier"].iloc[1], - 'technology_type': df.loc[filter, "technology_type"].unique().item(), - 'type': df.loc[filter, "type"].unique().item(), - 'note': df.loc[filter, "note"].iloc[1], - 'reference': df.loc[filter, "reference"].iloc[1], - }]) + df_new = pd.DataFrame( + [ + { + "technology": tech, + "year": data_year, + "parameter": param, + "value": ynew, + "unit": df.loc[filter, "unit"].unique().item(), + "source": df.loc[filter, "source"].unique().item(), + "carrier": df.loc[filter, "carrier"].iloc[1], + "technology_type": df.loc[filter, "technology_type"] + .unique() + .item(), + "type": df.loc[filter, "type"].unique().item(), + "note": df.loc[filter, "note"].iloc[1], + "reference": df.loc[filter, "reference"].iloc[1], + } + ] + ) # not concat if df year is 2021 or 2030 (otherwhise duplicate) if data_year == 2021 or data_year == 2030: continue @@ -2539,58 +3268,62 @@ def geometric_series(nominator, denominator=1, number_of_terms=1, start=1): df = pd.concat([df, df_new], ignore_index=True) # d) Combine metadata and add to cost database - df.loc[:,"source"] = df["source"] + ", " + df["reference"] + df.loc[:, "source"] = df["source"] + ", " + df["reference"] for i in df.index: - df.loc[i,"further description"] = str( + df.loc[i, "further description"] = str( { - "carrier": df.loc[i,"carrier"], - "technology_type": [df.loc[i,"technology_type"]], - "type": [df.loc[i,"type"]], - "note": [df.loc[i,"note"]], + "carrier": df.loc[i, "carrier"], + "technology_type": [df.loc[i, "technology_type"]], + "type": [df.loc[i, "type"]], + "note": [df.loc[i, "note"]], } ) # keep only relevant columns - df = df.loc[df.year == data_year,["technology", "parameter", "value", "unit", "source", "further description"]] + df = df.loc[ + df.year == data_year, + ["technology", "parameter", "value", "unit", "source", "further description"], + ] tech = df.technology.unique() - df = df.set_index(['technology', 'parameter']) + df = df.set_index(["technology", "parameter"]) return pd.concat([costs, df]), tech def prepare_inflation_rate(fn): - """read in annual inflation rate from Eurostat + """ + Read in annual inflation rate from Eurostat https://ec.europa.eu/eurostat/api/dissemination/sdmx/2.1/dataflow/ESTAT/prc_hicp_aind/1.0?references=descendants&detail=referencepartial&format=sdmx_2.1_generic&compressed=true """ - inflation_rate = pd.read_excel(fn, - sheet_name="Sheet 1", index_col=0, - header=[8]) - inflation_rate = (inflation_rate.loc["European Union - 27 countries (from 2020)"] - .dropna()).loc["2001"::] + inflation_rate = pd.read_excel(fn, sheet_name="Sheet 1", index_col=0, header=[8]) + inflation_rate = ( + inflation_rate.loc["European Union - 27 countries (from 2020)"].dropna() + ).loc["2001"::] inflation_rate.rename(index=lambda x: int(x), inplace=True) inflation_rate = inflation_rate.astype(float) - + inflation_rate /= 100 - + return inflation_rate - + + # %% ************************************************************************* # ---------- MAIN ------------------------------------------------------------ if __name__ == "__main__": - if 'snakemake' not in globals(): - import os + if "snakemake" not in globals(): from _helpers import mock_snakemake - #os.chdir(os.path.join(os.getcwd(), "scripts")) + + # os.chdir(os.path.join(os.getcwd(), "scripts")) snakemake = mock_snakemake("compile_cost_assumptions") - years = snakemake.config['years'] + years = snakemake.config["years"] inflation_rate = prepare_inflation_rate(snakemake.input.inflation_rate) - + # p.77 Figure 51 share of vehicle-km driven by truck # (1) DEA data # (a)-------- get data from DEA excel sheets ---------------------------------- # read excel sheet names of all excel files - excel_files = [v for k,v in snakemake.input.items() if "dea" in k] + excel_files = [v for k, v in snakemake.input.items() if "dea" in k] data_in = get_excel_sheets(excel_files) # create dictionary with raw data from DEA sheets d_by_tech = get_data_from_DEA(data_in, expectation=snakemake.config["expectation"]) @@ -2609,7 +3342,7 @@ def prepare_inflation_rate(fn): tech_data = set_round_trip_efficiency(tech_data) # drop all rows which only contains zeros - tech_data = tech_data.loc[(tech_data[years]!=0).sum(axis=1)!=0] + tech_data = tech_data.loc[(tech_data[years] != 0).sum(axis=1) != 0] # (c) ----- get tech data in pypsa syntax ----------------------------------- # make categories: investment, FOM, VOM, efficiency, c_b, c_v @@ -2631,45 +3364,48 @@ def prepare_inflation_rate(fn): data.at[x, "currency_year"] = 2019 else: data.at[x, "currency_year"] = 2015 - + # add heavy duty assumptions, cost year is 2022 - data = get_dea_vehicle_data(snakemake.input.dea_vehicles, data) + data = get_dea_vehicle_data(snakemake.input.dea_vehicles, data) # add shipping data data = get_dea_maritime_data(snakemake.input.dea_ship, data) # %% (2) -- get data from other sources which need formatting ----------------- # (a) ---------- get old pypsa costs --------------------------------------- - costs_pypsa = pd.read_csv(snakemake.input.pypsa_costs, - index_col=[0,2]).sort_index() + costs_pypsa = pd.read_csv( + snakemake.input.pypsa_costs, index_col=[0, 2] + ).sort_index() # rename some techs and convert units costs_pypsa = rename_pypsa_old(costs_pypsa) # (b1) ------- add vehicle costs from Fraunhofer vehicle study ------------------------ - costs_vehicles = pd.read_csv(snakemake.input.fraunhofer_vehicles_costs, - engine="python", - index_col=[0,1], - encoding="ISO-8859-1") + costs_vehicles = pd.read_csv( + snakemake.input.fraunhofer_vehicles_costs, + engine="python", + index_col=[0, 1], + encoding="ISO-8859-1", + ) # rename + reorder to fit to other data costs_vehicles = rename_ISE_vehicles(costs_vehicles) - if 'NT' in costs_vehicles.index: - costs_vehicles.drop(['NT'], axis=0, inplace=True, level=0) + if "NT" in costs_vehicles.index: + costs_vehicles.drop(["NT"], axis=0, inplace=True, level=0) costs_vehicles = convert_units(costs_vehicles) # add costs for vehicles data = pd.concat([data, costs_vehicles], sort=True) - # (b) ------- add costs from Fraunhofer ISE study -------------------------- - costs_ISE = pd.read_csv(snakemake.input.fraunhofer_costs, - engine="python", - index_col=[0,1], - encoding = "ISO-8859-1") + costs_ISE = pd.read_csv( + snakemake.input.fraunhofer_costs, + engine="python", + index_col=[0, 1], + encoding="ISO-8859-1", + ) # rename + reorder to fit to other data - costs_ISE = rename_ISE(costs_ISE) + costs_ISE = rename_ISE(costs_ISE) # add costs for gas pipelines data = pd.concat([data, costs_ISE.loc[["Gasnetz"]]], sort=True) - data = add_manual_input(data) # add costs for home batteries @@ -2679,35 +3415,46 @@ def prepare_inflation_rate(fn): data = add_SMR_data(data) # add solar rooftop costs by taking the mean of commercial and residential data = add_mean_solar_rooftop(data) - + data.index.names = ["technology", "parameter"] # %% (3) ------ add additional sources and save cost as csv ------------------ # [RTD-target-multiindex-df] for year in years: - costs = (data[[year, "unit", "source", "further description", - "currency_year"]] - .rename(columns={year: "value"})) + costs = data[ + [year, "unit", "source", "further description", "currency_year"] + ].rename(columns={year: "value"}) costs["value"] = costs["value"].astype(float) # biomass is differentiated by biomass CHP and HOP - costs.loc[('solid biomass', 'fuel'), 'value'] = 12 - costs.loc[('solid biomass', 'fuel'), 'unit'] = 'EUR/MWh_th' - costs.loc[('solid biomass', 'fuel'), 'source'] = "JRC ENSPRESO ca avg for MINBIOWOOW1 (secondary forest residue wood chips), ENS_Ref for 2040" - costs.loc[('solid biomass', 'fuel'), 'currency_year'] = 2010 - - costs.loc[('digestible biomass', 'fuel'), 'value'] = 15 - costs.loc[('digestible biomass', 'fuel'), 'unit'] = 'EUR/MWh_th' - costs.loc[('digestible biomass', 'fuel'), 'source'] = "JRC ENSPRESO ca avg for MINBIOAGRW1, ENS_Ref for 2040" - costs.loc[('digestible biomass', 'fuel'), 'currency_year'] = 2010 - + costs.loc[("solid biomass", "fuel"), "value"] = 12 + costs.loc[("solid biomass", "fuel"), "unit"] = "EUR/MWh_th" + costs.loc[("solid biomass", "fuel"), "source"] = ( + "JRC ENSPRESO ca avg for MINBIOWOOW1 (secondary forest residue wood chips), ENS_Ref for 2040" + ) + costs.loc[("solid biomass", "fuel"), "currency_year"] = 2010 + + costs.loc[("digestible biomass", "fuel"), "value"] = 15 + costs.loc[("digestible biomass", "fuel"), "unit"] = "EUR/MWh_th" + costs.loc[("digestible biomass", "fuel"), "source"] = ( + "JRC ENSPRESO ca avg for MINBIOAGRW1, ENS_Ref for 2040" + ) + costs.loc[("digestible biomass", "fuel"), "currency_year"] = 2010 + # add solar data from other source than DEA - if any([snakemake.config['solar_utility_from_vartiaien'], snakemake.config['solar_rooftop_from_etip']]): + if any( + [ + snakemake.config["solar_utility_from_vartiaien"], + snakemake.config["solar_rooftop_from_etip"], + ] + ): costs = add_solar_from_other(costs) # add desalination and clean water tank storage costs = add_desalinsation_data(costs) # add energy storage database - if snakemake.config['energy_storage_database']['pnnl_energy_storage'].get("add_data", True): + if snakemake.config["energy_storage_database"]["pnnl_energy_storage"].get( + "add_data", True + ): costs, tech = add_energy_storage_database(costs, year) costs.loc[tech, "currency_year"] = 2020 @@ -2719,7 +3466,7 @@ def prepare_inflation_rate(fn): costs = add_co2_intensity(costs) # carbon balances - costs = carbon_flow(costs,year) + costs = carbon_flow(costs, year) # energy penalty of carbon capture costs = energy_penalty(costs) @@ -2729,7 +3476,7 @@ def prepare_inflation_rate(fn): # missing technologies missing = costs_pypsa.index.levels[0].difference(costs.index.levels[0]) - if (len(missing) & (year==years[0])): + if len(missing) & (year == years[0]): print("************************************************************") print("warning, in new cost assumptions the following components: ") for i in range(len(missing)): @@ -2738,21 +3485,23 @@ def prepare_inflation_rate(fn): print("************************************************************") to_add = costs_pypsa.loc[missing].drop("year", axis=1) - to_add.loc[:,"further description"] = " from old pypsa cost assumptions" + to_add.loc[:, "further description"] = " from old pypsa cost assumptions" # TODO check currency year from old pypsa cost assumptions to_add["currency_year"] = 2015 costs_tot = pd.concat([costs, to_add], sort=False) # single components missing comp_missing = costs_pypsa.index.difference(costs_tot.index) - if (year==years[0]): - print("single parameters of technologies are missing, using old PyPSA assumptions: ") + if year == years[0]: + print( + "single parameters of technologies are missing, using old PyPSA assumptions: " + ) print(comp_missing) print("old c_v and c_b values are assumed where given") to_add = costs_pypsa.loc[comp_missing].drop("year", axis=1) to_add.loc[:, "further description"] = " from old pypsa cost assumptions" # more data on geothermal is added downstream, so old assumptions are redundant - to_add = to_add.drop("geothermal") + to_add = to_add.drop("geothermal") # TODO check currency year from old pypsa cost assumptions to_add["currency_year"] = 2015 costs_tot = pd.concat([costs_tot, to_add], sort=False) @@ -2764,11 +3513,13 @@ def prepare_inflation_rate(fn): # adjust for inflation techs = costs_tot.index.get_level_values(0).unique() costs_tot["currency_year"] = costs_tot.currency_year.astype(float) - costs_tot = adjust_for_inflation(inflation_rate, costs_tot, techs, - costs_tot.currency_year, ["value"]) + costs_tot = adjust_for_inflation( + inflation_rate, costs_tot, techs, costs_tot.currency_year, ["value"] + ) # format and sort costs_tot.sort_index(inplace=True) - costs_tot.loc[:,'value'] = round(costs_tot.value.astype(float), - snakemake.config.get("ndigits", 2)) - costs_tot.to_csv([v for v in snakemake.output if str(year) in v][0]) \ No newline at end of file + costs_tot.loc[:, "value"] = round( + costs_tot.value.astype(float), snakemake.config.get("ndigits", 2) + ) + costs_tot.to_csv([v for v in snakemake.output if str(year) in v][0]) diff --git a/scripts/convert_pdf_EWG_to_dataframe.py b/scripts/convert_pdf_EWG_to_dataframe.py index 4828195..0e03526 100755 --- a/scripts/convert_pdf_EWG_to_dataframe.py +++ b/scripts/convert_pdf_EWG_to_dataframe.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- """ script to convert the technology data assumptions of the Study "Global Energy System based on 100% Renewable Energy" of Energywatchgroup/LTU University @@ -7,38 +6,53 @@ (see also pdf in folder docu) into a .csv format """ +import numpy as np import pandas as pd from tabula import read_pdf -import numpy as np # Detect running outside of snakemake and mock snakemake for testing -if 'snakemake' not in globals(): +if "snakemake" not in globals(): from vresutils.snakemake import MockSnakemake + snakemake = MockSnakemake() - snakemake.input = dict(EWG = "docu/EWG_LUT_100RE_All_Sectors_Global_Report_2019.pdf") - snakemake.output = dict(costs = "inputs/EWG_costs.csv") + snakemake.input = dict(EWG="docu/EWG_LUT_100RE_All_Sectors_Global_Report_2019.pdf") + snakemake.output = dict(costs="inputs/EWG_costs.csv") -df_list = read_pdf(snakemake.input["EWG"], - pages="305-309", - multiple_tables=True) -#%% +df_list = read_pdf(snakemake.input["EWG"], pages="305-309", multiple_tables=True) +# %% # wished columns -wished_columns = ['Technologies', 'Type', 'Units', - '2015', '2020', '2025', '2030', - '2035', '2040', '2045', '2050', 'Ref'] +wished_columns = [ + "Technologies", + "Type", + "Units", + "2015", + "2020", + "2025", + "2030", + "2035", + "2040", + "2045", + "2050", + "Ref", +] # clean data frame split_units = df_list[0]["Units 2015"].fillna(" ").str.split(" ", expand=True) # check where split is too long -to_be_merged = split_units[split_units[2].apply(lambda x: x!=None)].index -split_units.loc[to_be_merged, 0] = split_units.loc[to_be_merged, 0] + " " + split_units.loc[to_be_merged, 1] +to_be_merged = split_units[split_units[2].apply(lambda x: x != None)].index +split_units.loc[to_be_merged, 0] = ( + split_units.loc[to_be_merged, 0] + " " + split_units.loc[to_be_merged, 1] +) split_units.loc[to_be_merged, 1] = split_units.loc[to_be_merged, 2] -df_list[0] = pd.concat([df_list[0].drop("Units 2015", axis=1), - split_units.iloc[:, 0:2]], axis=1) +df_list[0] = pd.concat( + [df_list[0].drop("Units 2015", axis=1), split_units.iloc[:, 0:2]], axis=1 +) # renmae columns -df_list[0] = df_list[0].rename(columns={'Unnamed: 0': "Type", - 0:"Units", - 1:"2015"}).reindex(wished_columns, axis=1) +df_list[0] = ( + df_list[0] + .rename(columns={"Unnamed: 0": "Type", 0: "Units", 1: "2015"}) + .reindex(wished_columns, axis=1) +) for page in range(1, len(df_list)): df_list[page] = df_list[page].T.reset_index().T if len(df_list[page].columns) != len(wished_columns): @@ -46,32 +60,48 @@ df_list[page] = df_list[page].iloc[:, :12] df_list[page].columns = wished_columns -df_list[4] = pd.concat([df_list[4].iloc[:, :2], df_list[4].iloc[:,2:].shift(axis=1)], axis=1) +df_list[4] = pd.concat( + [df_list[4].iloc[:, :2], df_list[4].iloc[:, 2:].shift(axis=1)], axis=1 +) for sign in [" €", " kWh"]: split_units = df_list[4]["Type"].fillna(" ").str.split(sign, expand=True) # check where split is too long - to_be_merged = split_units[split_units[1].apply(lambda x: x!=None)].index + to_be_merged = split_units[split_units[1].apply(lambda x: x != None)].index df_list[4].loc[to_be_merged, "Type"] = split_units.loc[to_be_merged, 0] df_list[4].loc[to_be_merged, "Units"] = sign + split_units.loc[to_be_merged, 1] clean_df = pd.concat([df_list[page] for page in range(len(df_list))]) clean_df.reset_index(drop=True, inplace=True) -#%% +# %% # fill missing rows with tech name for row in range(len(clean_df)): - if not str(clean_df.loc[row, "Technologies"])=="nan": - for end in range(row+1, row+5): - if not (any(clean_df.loc[row:end, "Technologies"].isna()) - or any([exclude in str(clean_df.loc[end, "Technologies"]) for exclude in ["Residential", "Battery", "DH"]])): - clean_df.loc[row, "Technologies"] += " " + clean_df.loc[end, "Technologies"] + if not str(clean_df.loc[row, "Technologies"]) == "nan": + for end in range(row + 1, row + 5): + if not ( + any(clean_df.loc[row:end, "Technologies"].isna()) + or any( + [ + exclude in str(clean_df.loc[end, "Technologies"]) + for exclude in ["Residential", "Battery", "DH"] + ] + ) + ): + clean_df.loc[row, "Technologies"] += ( + " " + clean_df.loc[end, "Technologies"] + ) else: - if any([exclude in str(clean_df.loc[end, "Technologies"]) for exclude in ["Residential", "Battery", "DH"]]): + if any( + [ + exclude in str(clean_df.loc[end, "Technologies"]) + for exclude in ["Residential", "Battery", "DH"] + ] + ): end -= 1 - clean_df.loc[row+1: end, "Technologies"] = np.nan + clean_df.loc[row + 1 : end, "Technologies"] = np.nan break # convert to float -years = ['2015', '2020', '2025', '2030', '2035', '2040', '2045', '2050'] +years = ["2015", "2020", "2025", "2030", "2035", "2040", "2045", "2050"] clean_df[years] = clean_df[years].applymap(lambda x: str(x).replace(",", ".")) clean_df[years] = clean_df[years].apply(lambda x: pd.to_numeric(x, errors="coerce")) @@ -83,28 +113,33 @@ clean_df[years] = clean_df[years].fillna(axis=1, method="ffill") -rename_types = {"Lifetime": "lifetime", - 'Lifetime years': "lifetime", - "Opex var": "VOM", - '[120Opex var': "VOM", - '[123,Opex fix': "FOM", - '[125,Opex fix': "FOM", - '[137Opex fix': "FOM", - "Opex fix": "FOM", - "Capex": "investment"} -clean_df.rename(index=rename_types,level=1, inplace=True) +rename_types = { + "Lifetime": "lifetime", + "Lifetime years": "lifetime", + "Opex var": "VOM", + "[120Opex var": "VOM", + "[123,Opex fix": "FOM", + "[125,Opex fix": "FOM", + "[137Opex fix": "FOM", + "Opex fix": "FOM", + "Capex": "investment", +} +clean_df.rename(index=rename_types, level=1, inplace=True) aggregate = {year: sum for year in years} -aggregate['Units']= 'first' -aggregate['Ref']= 'first' +aggregate["Units"] = "first" +aggregate["Ref"] = "first" final_df = clean_df.groupby(clean_df.index).agg(aggregate) final_df.index = pd.MultiIndex.from_tuples(final_df.index) -fom = (final_df[years].xs("FOM", level=1).div(final_df[years].xs("investment", level=1))*100).dropna(how="all", axis=0) +fom = ( + final_df[years].xs("FOM", level=1).div(final_df[years].xs("investment", level=1)) + * 100 +).dropna(how="all", axis=0) fom.index = pd.MultiIndex.from_product([fom.index, ["FOM"]]) final_df.loc[fom.index, years] = fom final_df.loc[fom.index, "Units"] = "%/year" final_df.index.rename(["technology", "parameter"], inplace=True) -final_df.rename(columns={"Units": "unit", "Ref":"source"}, inplace=True) +final_df.rename(columns={"Units": "unit", "Ref": "source"}, inplace=True) final_df["unit"] = final_df["unit"].apply(lambda x: str(x).replace("€", "EUR")) -round(final_df, ndigits=2).to_csv(snakemake.output["costs"], encoding='iso-8859-15') +round(final_df, ndigits=2).to_csv(snakemake.output["costs"], encoding="iso-8859-15") diff --git a/scripts/convert_pdf_fraunhofer_to_dataframe.py b/scripts/convert_pdf_fraunhofer_to_dataframe.py index 3e2af73..4c94163 100644 --- a/scripts/convert_pdf_fraunhofer_to_dataframe.py +++ b/scripts/convert_pdf_fraunhofer_to_dataframe.py @@ -1,53 +1,49 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- """ script to convert the technology data assumptions of the Fraunhofer ISE Study "Wege zu einem klimaneutralen Energiesystem" (see pdf in folder docu) into a .csv format """ +import numpy as np import pandas as pd from tabula import read_pdf -import numpy as np -df_list = read_pdf(snakemake.input.fraunhofer, - pages="3-15", - multiple_tables=True) +df_list = read_pdf(snakemake.input.fraunhofer, pages="3-15", multiple_tables=True) print(df_list) clean_df = [] j = 0 for i in range(len(df_list)): print(i) - if len(df_list[i])==1: + if len(df_list[i]) == 1: print("table is dropped ", i) - if 'Komponente' in df_list[i].iloc[0].unique(): + if "Komponente" in df_list[i].iloc[0].unique(): print("table is added ", i) clean_df.append(df_list[i]) j += 1 print(j) continue else: - clean_df[j-1] = clean_df[j-1].append(df_list[i]) + clean_df[j - 1] = clean_df[j - 1].append(df_list[i]) print("table is appended ", i) for i in range(len(clean_df)): - clean_df[i].columns = clean_df[i].iloc[0,:] - clean_df[i] = clean_df[i].iloc[1:,:] + clean_df[i].columns = clean_df[i].iloc[0, :] + clean_df[i] = clean_df[i].iloc[1:, :] clean_df[i].reset_index(drop=True, inplace=True) clean_df[i].dropna(axis=1, how="all", inplace=True) -columns = ["Komponente", "Größe", "Einheit", 2020, 2025, 2030, 2035, 2040, - 2045, 2050] +columns = ["Komponente", "Größe", "Einheit", 2020, 2025, 2030, 2035, 2040, 2045, 2050] for table in range(len(clean_df)): print(table) test = clean_df[table]["Komponente"] - counter = len(test)-2 + counter = len(test) - 2 for i in range(counter, -1, -1): - if (type(test.iloc[i]) == str) and (type(test.iloc[i-1]) == str): - test.iloc[i-1] = test.iloc[i-1] +" "+ test.iloc[i] + if (type(test.iloc[i]) == str) and (type(test.iloc[i - 1]) == str): + test.iloc[i - 1] = test.iloc[i - 1] + " " + test.iloc[i] test.iloc[i] = np.nan test.fillna(method="ffill", inplace=True) clean_df[table]["Komponente"] = test @@ -55,7 +51,7 @@ new = {} for i in range(len(test)): a = clean_df[table].loc[i].dropna() - if len(a)==len(columns): + if len(a) == len(columns): new[i] = a new[i].index = columns else: @@ -68,11 +64,11 @@ total = pd.concat(clean_df) total.Einheit = total.Einheit.str.replace("€", "EUR") -total.to_csv(snakemake.output.costs, encoding='iso-8859-15') +total.to_csv(snakemake.output.costs, encoding="iso-8859-15") energiepreise = read_pdf(snakemake.input.fraunhofer, pages="15") energiepreise.dropna(axis=1, how="all", inplace=True) energiepreise.dropna(axis=0, how="all", inplace=True) energiepreise = energiepreise.rename(columns={"Unnamed: 1": "Fuel"}).set_index("Fuel") energiepreise["unit"] = "Eur/MWh" -energiepreise.to_csv(snakemake.output.energy_prices, encoding='iso-8859-15') +energiepreise.to_csv(snakemake.output.energy_prices, encoding="iso-8859-15") diff --git a/scripts/retrieve_data_from_dea.py b/scripts/retrieve_data_from_dea.py index 3ddf3b9..f798cb5 100644 --- a/scripts/retrieve_data_from_dea.py +++ b/scripts/retrieve_data_from_dea.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- """ Created on Mon May 4 18:48:11 2020 @@ -8,9 +7,10 @@ @author: bw0928 """ -import requests -import urllib.request import time +import urllib.request + +import requests from bs4 import BeautifulSoup # %% @@ -24,31 +24,27 @@ soup = BeautifulSoup(response.text, "html.parser") # %% -search = "https://ens.dk/en/our-services/projections-and-models/technology-data/" -links = soup.findAll('a', {"href" : lambda href: href and search in href}) +search = "https://ens.dk/en/our-services/projections-and-models/technology-data/" +links = soup.findAll("a", {"href": lambda href: href and search in href}) for i in range(len(links)): - one_a_tag = links[i] link_to_site = one_a_tag["href"] response2 = requests.get(link_to_site) soup2 = BeautifulSoup(response2.text, "html.parser") - data = soup2.findAll('a', {"href" : lambda href: href and ".xlsx" in href}) - docu = soup2.findAll('a', {"href" : lambda href: href and ".pdf" in href}) + data = soup2.findAll("a", {"href": lambda href: href and ".xlsx" in href}) + docu = soup2.findAll("a", {"href": lambda href: href and ".pdf" in href}) # get the data for j in range(len(data)): - link_to_data = data[j]["href"] download_url = prefix + link_to_data - urllib.request.urlretrieve(download_url, path_out + - link_to_data.split("/")[-1]) + urllib.request.urlretrieve(download_url, path_out + link_to_data.split("/")[-1]) time.sleep(1) # get the documentation if docu: for j in range(len(docu)): - link_to_docu = docu[j]["href"] if prefix not in link_to_docu: @@ -56,11 +52,7 @@ else: download_url = link_to_docu - urllib.request.urlretrieve(download_url, path_out + "/docu/" + - link_to_docu.split("/")[-1]) + urllib.request.urlretrieve( + download_url, path_out + "/docu/" + link_to_docu.split("/")[-1] + ) time.sleep(1) - - - - - From 7ae8ed1e5c1b993d49c0b21a4376d60f12dcabc2 Mon Sep 17 00:00:00 2001 From: Fabrizio Finozzi Date: Fri, 10 Jan 2025 13:02:19 +0100 Subject: [PATCH 06/15] remove license --- .github/ISSUE_TEMPLATE/bug_report.md | 4 ---- .github/ISSUE_TEMPLATE/config.yml | 4 ---- .github/ISSUE_TEMPLATE/feature_request.md | 4 ---- .github/dependabot.yml | 4 ---- .github/pull_request_template.md | 4 ---- .github/workflows/ci.yaml | 4 ---- 6 files changed, 24 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b743f83..9c99ce1 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,7 +1,3 @@ -# SPDX-FileCopyrightText: Contributors to technology-data -# -# SPDX-License-Identifier: MIT - --- name: Bug report about: Create a report if something doesn't work quite right. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index d7f7900..d8c0438 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,7 +1,3 @@ -# SPDX-FileCopyrightText: Contributors to technology-data -# -# SPDX-License-Identifier: MIT - blank_issues_enabled: false contact_links: - name: PyPSA Mailing List diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 80bd8f1..303989a 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,7 +1,3 @@ -# SPDX-FileCopyrightText: Contributors to technology-data -# -# SPDX-License-Identifier: MIT - --- name: Feature request about: Suggest an idea for this project diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 52cab81..f8f779b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,7 +1,3 @@ -# SPDX-FileCopyrightText: Contributors to technology-data -# -# SPDX-License-Identifier: MIT - # dependabot # Ref: https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file # ------------------------------------------------------------------------------ diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 6439393..4b90def 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,7 +1,3 @@ -# SPDX-FileCopyrightText: Contributors to technology-data -# -# SPDX-License-Identifier: MIT - Closes # (if applicable). ## Changes proposed in this Pull Request diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1749805..11b8733 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,7 +1,3 @@ -# SPDX-FileCopyrightText: Contributors to technology-data -# -# SPDX-License-Identifier: MIT - name: CI on: [push] From b3ddc1ec7d58bbae48081fb6ae2dd88085306705 Mon Sep 17 00:00:00 2001 From: Fabrizio Finozzi Date: Fri, 10 Jan 2025 13:12:39 +0100 Subject: [PATCH 07/15] config: new licence --- .pre-commit-config.yaml | 4 +++- .reuse/dep5 | 24 ++++++++++++++++++++++++ README.md | 5 +++++ Snakefile | 4 ++++ environment.yaml | 4 ++++ 5 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 .reuse/dep5 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b57a5a8..310cd71 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,6 @@ -# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: MIT exclude: "^LICENSES" repos: diff --git a/.reuse/dep5 b/.reuse/dep5 new file mode 100644 index 0000000..aaee7f5 --- /dev/null +++ b/.reuse/dep5 @@ -0,0 +1,24 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: technology-data +Upstream-Contact: Tom Brown +Source: https://github.com/pypsa/technology-data + +Files: latex_tables/* +Copyright: 2019-2024 The technology-data Authors +License: CC-BY-4.0 + +Files: docu/* +Copyright: 2019-2024 The technology-data Authors +License: CC-BY-4.0 + +Files: inputs/* +Copyright: 2017-2024 The technology-data Authors +License: CC-BY-4.0 + +Files: outputs/* +Copyright: 2017-2024 The technology-data Authors +License: CC-BY-4.0 + +Files: .github/* +Copyright: 2019-2024 The technology-data Authors +License: CC0-1.0 diff --git a/README.md b/README.md index 3bfde94..4c3b979 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ + + ![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/pypsa/technology-data?include_prereleases) [![Documentation](https://readthedocs.org/projects/technology-data/badge/?version=latest)](https://technology-data.readthedocs.io/en/latest/?badge=latest) ![Licence](https://img.shields.io/github/license/pypsa/technology-data) diff --git a/Snakefile b/Snakefile index e893fdb..0e57311 100644 --- a/Snakefile +++ b/Snakefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: MIT + configfile: "config.yaml" diff --git a/environment.yaml b/environment.yaml index 1f6b2a6..521fc89 100644 --- a/environment.yaml +++ b/environment.yaml @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: CC0-1.0 + name: technology-data channels: - conda-forge From bf91aa1a9b26080ba7d8fa46f35a75acd2c6a7ad Mon Sep 17 00:00:00 2001 From: Fabrizio Finozzi Date: Fri, 10 Jan 2025 13:15:32 +0100 Subject: [PATCH 08/15] config: modify license --- .gitignore | 2 +- .gitignore.save | 2 +- .pre-commit-config.yaml | 2 +- .readthedocs.yml | 2 +- .syncignore | 2 +- Snakefile | 2 -- config.yaml | 2 +- ruff.toml | 2 +- 8 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 8a85749..30b0efb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: Contributors to technology-data # -# SPDX-License-Identifier: MIT +# SPDX-License-Identifier: CC0-1.0 .snakemake _build diff --git a/.gitignore.save b/.gitignore.save index be4a247..95cb5d8 100644 --- a/.gitignore.save +++ b/.gitignore.save @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: Contributors to technology-data # -# SPDX-License-Identifier: MIT +# SPDX-License-Identifier: CC0-1.0 .snakemake _build diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 310cd71..61d71c9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: Contributors to technology-data # -# SPDX-License-Identifier: MIT +# SPDX-License-Identifier: CC0-1.0 exclude: "^LICENSES" repos: diff --git a/.readthedocs.yml b/.readthedocs.yml index 202883e..755253c 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: Contributors to technology-data # -# SPDX-License-Identifier: MIT +# SPDX-License-Identifier: CC0-1.0 version: 2 diff --git a/.syncignore b/.syncignore index cbe0dac..28480a9 100644 --- a/.syncignore +++ b/.syncignore @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: Contributors to technology-data # -# SPDX-License-Identifier: MIT +# SPDX-License-Identifier: CC0-1.0 .snakemake .git diff --git a/Snakefile b/Snakefile index 0e57311..39cff78 100644 --- a/Snakefile +++ b/Snakefile @@ -2,10 +2,8 @@ # # SPDX-License-Identifier: MIT - configfile: "config.yaml" - rule compile_cost_assumptions: input: inflation_rate="inputs/prc_hicp_aind__custom_9928419_spreadsheet.xlsx", diff --git a/config.yaml b/config.yaml index 58932e5..9b75f0e 100644 --- a/config.yaml +++ b/config.yaml @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: Contributors to technology-data # -# SPDX-License-Identifier: MIT +# SPDX-License-Identifier: CC0-1.0 version: 0.9.2 diff --git a/ruff.toml b/ruff.toml index ea06ac5..55eb75c 100644 --- a/ruff.toml +++ b/ruff.toml @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: Contributors to technology-data # -# SPDX-License-Identifier: MIT +# SPDX-License-Identifier: CC0-1.0 extend-include = ['*.ipynb'] From 1802efb4f846e3add7db5d9544b5213e0ff763dd Mon Sep 17 00:00:00 2001 From: Fabrizio Finozzi Date: Fri, 10 Jan 2025 13:17:07 +0100 Subject: [PATCH 09/15] change license for Makefile --- docs/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Makefile b/docs/Makefile index 024c9cc..9fac4e0 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: Contributors to technology-data # -# SPDX-License-Identifier: MIT +# SPDX-License-Identifier: CC0-1.0 # Makefile for Sphinx documentation # From 0c4d5db07bfe74cafda7c88431f2ed1ce0c0d7d0 Mon Sep 17 00:00:00 2001 From: Fabrizio Finozzi Date: Fri, 10 Jan 2025 14:33:01 +0100 Subject: [PATCH 10/15] code: add license --- .reuse/dep5 | 4 ++++ docs/Makefile | 2 +- docs/conf.py | 5 +++++ docs/index.rst | 8 +++++++- docs/installation.rst | 5 +++++ docs/release_notes.rst | 6 ++++++ docs/requirements.txt | 4 ++++ docs/structure.rst | 5 +++++ scripts/_helpers.py | 5 ++++- scripts/compile_cost_assumptions.py | 6 +++++- scripts/convert_pdf_EWG_to_dataframe.py | 6 +++++- scripts/convert_pdf_fraunhofer_to_dataframe.py | 6 +++++- scripts/retrieve_data_from_dea.py | 6 +++++- 13 files changed, 61 insertions(+), 7 deletions(-) diff --git a/.reuse/dep5 b/.reuse/dep5 index aaee7f5..11c76ae 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -11,6 +11,10 @@ Files: docu/* Copyright: 2019-2024 The technology-data Authors License: CC-BY-4.0 +Files: docs/*.csv +Copyright: 2019-2024 The technology-data Authors +License: CC-BY-4.0 + Files: inputs/* Copyright: 2017-2024 The technology-data Authors License: CC-BY-4.0 diff --git a/docs/Makefile b/docs/Makefile index 9fac4e0..024c9cc 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: Contributors to technology-data # -# SPDX-License-Identifier: CC0-1.0 +# SPDX-License-Identifier: MIT # Makefile for Sphinx documentation # diff --git a/docs/conf.py b/docs/conf.py index 18331ef..6a423d3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,3 +1,8 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: MIT + +# -*- coding: utf-8 -*- # # PyPSA documentation build configuration file, created by # sphinx-quickstart on Tue Jan 5 10:04:42 2016. diff --git a/docs/index.rst b/docs/index.rst index 12a89e4..43a9242 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,5 +1,11 @@ +.. + SPDX-FileCopyrightText: Contributors to technology-data + + SPDX-License-Identifier: CC-BY-4.0 + +#################### Technology-data base -===================================================================================== +#################### .. image:: https://img.shields.io/github/v/release/pypsa/technology-data?include_prereleases :alt: GitHub release (latest by date including pre-releases) diff --git a/docs/installation.rst b/docs/installation.rst index 7176ee3..01d2e3d 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -1,3 +1,8 @@ +.. + SPDX-FileCopyrightText: Contributors to technology-data + + SPDX-License-Identifier: CC-BY-4.0 + .. _installation: ########################################## diff --git a/docs/release_notes.rst b/docs/release_notes.rst index 7185769..902f65d 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -1,3 +1,9 @@ + +.. + SPDX-FileCopyrightText: Contributors to technology-data + + SPDX-License-Identifier: CC-BY-4.0 + ########################################## Release Notes ########################################## diff --git a/docs/requirements.txt b/docs/requirements.txt index 9edc46a..d7d6bdb 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: CC0-1.0 + sphinx sphinx-book-theme sphinxcontrib-bibtex diff --git a/docs/structure.rst b/docs/structure.rst index f360666..5cf272e 100644 --- a/docs/structure.rst +++ b/docs/structure.rst @@ -1,3 +1,8 @@ +.. + SPDX-FileCopyrightText: Contributors to technology-data + + SPDX-License-Identifier: CC-BY-4.0 + .. _structure: ########################################## diff --git a/scripts/_helpers.py b/scripts/_helpers.py index 38d73a7..8bfe7ac 100644 --- a/scripts/_helpers.py +++ b/scripts/_helpers.py @@ -1,7 +1,10 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: MIT + import re from pathlib import Path - class Dict(dict): """ Dict is a subclass of dict, which allows you to get AND SET items in the diff --git a/scripts/compile_cost_assumptions.py b/scripts/compile_cost_assumptions.py index 10e09a7..27cc5b5 100644 --- a/scripts/compile_cost_assumptions.py +++ b/scripts/compile_cost_assumptions.py @@ -1,4 +1,8 @@ -#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: MIT + +# coding: utf-8 """ Script creates cost csv for choosen years from different source (source_dict). The data is standardized for uniform: diff --git a/scripts/convert_pdf_EWG_to_dataframe.py b/scripts/convert_pdf_EWG_to_dataframe.py index 0e03526..cfabcc6 100755 --- a/scripts/convert_pdf_EWG_to_dataframe.py +++ b/scripts/convert_pdf_EWG_to_dataframe.py @@ -1,4 +1,8 @@ -#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: MIT + +# coding: utf-8 """ script to convert the technology data assumptions of the Study "Global Energy System based on 100% Renewable Energy" of Energywatchgroup/LTU University diff --git a/scripts/convert_pdf_fraunhofer_to_dataframe.py b/scripts/convert_pdf_fraunhofer_to_dataframe.py index 4c94163..b686a26 100644 --- a/scripts/convert_pdf_fraunhofer_to_dataframe.py +++ b/scripts/convert_pdf_fraunhofer_to_dataframe.py @@ -1,4 +1,8 @@ -#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: MIT + +# coding: utf-8 """ script to convert the technology data assumptions of the Fraunhofer ISE Study "Wege zu einem klimaneutralen Energiesystem" (see pdf in folder docu) into a diff --git a/scripts/retrieve_data_from_dea.py b/scripts/retrieve_data_from_dea.py index f798cb5..fea7910 100644 --- a/scripts/retrieve_data_from_dea.py +++ b/scripts/retrieve_data_from_dea.py @@ -1,4 +1,8 @@ -#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: MIT + +# coding: utf-8 """ Created on Mon May 4 18:48:11 2020 From 611ebf6507e8e15e754a2eb47ca1b5b24214c723 Mon Sep 17 00:00:00 2001 From: Fabrizio Finozzi Date: Fri, 10 Jan 2025 14:46:15 +0100 Subject: [PATCH 11/15] code : add licenses --- .reuse/dep5 | 28 ---- LICENSES/CC-BY-4.0.txt | 156 ++++++++++++++++++ LICENSES/CC0-1.0.txt | 121 ++++++++++++++ LICENSES/MIT.txt | 18 ++ REUSE.toml | 40 +++++ Snakefile | 2 + docs/addnew.rst | 5 + latex_tables/tables_in_csv.py | 1 - scripts/_helpers.py | 3 +- scripts/compile_cost_assumptions.py | 19 +-- scripts/convert_pdf_EWG_to_dataframe.py | 4 +- .../convert_pdf_fraunhofer_to_dataframe.py | 2 +- 12 files changed, 356 insertions(+), 43 deletions(-) delete mode 100644 .reuse/dep5 create mode 100644 LICENSES/CC-BY-4.0.txt create mode 100644 LICENSES/CC0-1.0.txt create mode 100644 LICENSES/MIT.txt create mode 100644 REUSE.toml diff --git a/.reuse/dep5 b/.reuse/dep5 deleted file mode 100644 index 11c76ae..0000000 --- a/.reuse/dep5 +++ /dev/null @@ -1,28 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: technology-data -Upstream-Contact: Tom Brown -Source: https://github.com/pypsa/technology-data - -Files: latex_tables/* -Copyright: 2019-2024 The technology-data Authors -License: CC-BY-4.0 - -Files: docu/* -Copyright: 2019-2024 The technology-data Authors -License: CC-BY-4.0 - -Files: docs/*.csv -Copyright: 2019-2024 The technology-data Authors -License: CC-BY-4.0 - -Files: inputs/* -Copyright: 2017-2024 The technology-data Authors -License: CC-BY-4.0 - -Files: outputs/* -Copyright: 2017-2024 The technology-data Authors -License: CC-BY-4.0 - -Files: .github/* -Copyright: 2019-2024 The technology-data Authors -License: CC0-1.0 diff --git a/LICENSES/CC-BY-4.0.txt b/LICENSES/CC-BY-4.0.txt new file mode 100644 index 0000000..13ca539 --- /dev/null +++ b/LICENSES/CC-BY-4.0.txt @@ -0,0 +1,156 @@ +Creative Commons Attribution 4.0 International + + Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. + +Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. More considerations for licensors. + +Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public. + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. + +Section 1 – Definitions. + + a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. + + d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. + + g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights under this Public License. + + i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. + +Section 2 – Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: + + A. reproduce and Share the Licensed Material, in whole or in part; and + + B. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. + + 3. Term. The term of this Public License is specified in Section 6(a). + + 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. + + 5. Downstream recipients. + + A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. + + B. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. + + 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). + +b. Other rights. + + 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this Public License. + + 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. + +Section 3 – License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified form), You must: + + A. retain the following if it is supplied by the Licensor with the Licensed Material: + + i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of warranties; + + v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; + + B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and + + C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. + + 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. + +Section 4 – Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; + + b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. +For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. + +Section 5 – Disclaimer of Warranties and Limitation of Liability. + + a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You. + + b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You. + + c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. + +Section 6 – Term and Termination. + + a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or + + 2. upon express reinstatement by the Licensor. + + c. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. + + d. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. + + e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. + +Section 7 – Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. + +Section 8 – Interpretation. + + a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. + + c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. + + d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. + +Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/LICENSES/CC0-1.0.txt b/LICENSES/CC0-1.0.txt new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/LICENSES/CC0-1.0.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt new file mode 100644 index 0000000..fc2cf8e --- /dev/null +++ b/LICENSES/MIT.txt @@ -0,0 +1,18 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/REUSE.toml b/REUSE.toml new file mode 100644 index 0000000..aa11c6c --- /dev/null +++ b/REUSE.toml @@ -0,0 +1,40 @@ +version = 1 +SPDX-PackageName = "technology-data" +SPDX-PackageSupplier = "Tom Brown " +SPDX-PackageDownloadLocation = "https://github.com/pypsa/technology-data" + +[[annotations]] +path = "latex_tables/**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2019-2024 The technology-data Authors" +SPDX-License-Identifier = "CC-BY-4.0" + +[[annotations]] +path = "docu/**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2019-2024 The technology-data Authors" +SPDX-License-Identifier = "CC-BY-4.0" + +[[annotations]] +path = "docs/**.csv" +precedence = "aggregate" +SPDX-FileCopyrightText = "2019-2024 The technology-data Authors" +SPDX-License-Identifier = "CC-BY-4.0" + +[[annotations]] +path = "inputs/**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2017-2024 The technology-data Authors" +SPDX-License-Identifier = "CC-BY-4.0" + +[[annotations]] +path = "outputs/**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2017-2024 The technology-data Authors" +SPDX-License-Identifier = "CC-BY-4.0" + +[[annotations]] +path = ".github/**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2019-2024 The technology-data Authors" +SPDX-License-Identifier = "CC0-1.0" diff --git a/Snakefile b/Snakefile index 39cff78..0e57311 100644 --- a/Snakefile +++ b/Snakefile @@ -2,8 +2,10 @@ # # SPDX-License-Identifier: MIT + configfile: "config.yaml" + rule compile_cost_assumptions: input: inflation_rate="inputs/prc_hicp_aind__custom_9928419_spreadsheet.xlsx", diff --git a/docs/addnew.rst b/docs/addnew.rst index 5f07627..a0733ae 100644 --- a/docs/addnew.rst +++ b/docs/addnew.rst @@ -1,3 +1,8 @@ +.. + SPDX-FileCopyrightText: Contributors to technology-data + + SPDX-License-Identifier: CC-BY-4.0 + .. _addnew: ########################################## diff --git a/latex_tables/tables_in_csv.py b/latex_tables/tables_in_csv.py index 953d2bf..7c6b1d7 100644 --- a/latex_tables/tables_in_csv.py +++ b/latex_tables/tables_in_csv.py @@ -100,7 +100,6 @@ "decentral air-sourced heat pump": "Air-sourced heat pump decentral", "central air-sourced heat pump": "Air-sourced heat pump central", "central ground-sourced heat pump": "Ground-sourced heat pump central", - "decentral air-sourced heat pump": "Air-sourced heat pump decentral", "decentral ground-sourced heat pump": "Ground-sourced heat pump decentral", "Gasnetz": "Gas pipeline", "micro CHP": "Micro CHP", diff --git a/scripts/_helpers.py b/scripts/_helpers.py index 8bfe7ac..66f55dd 100644 --- a/scripts/_helpers.py +++ b/scripts/_helpers.py @@ -5,12 +5,13 @@ import re from pathlib import Path + class Dict(dict): """ Dict is a subclass of dict, which allows you to get AND SET items in the dict using the attribute syntax! - Stripped down from addict https://github.com/mewwts/addict/ used in from pypsa.decriptor import Dict. + Stripped down from addict https://github.com/mewwts/addict/ used in from pypsa.descriptor import Dict. """ def __setattr__(self, name, value): diff --git a/scripts/compile_cost_assumptions.py b/scripts/compile_cost_assumptions.py index 27cc5b5..b8a182a 100644 --- a/scripts/compile_cost_assumptions.py +++ b/scripts/compile_cost_assumptions.py @@ -4,7 +4,7 @@ # coding: utf-8 """ -Script creates cost csv for choosen years from different source (source_dict). +Script creates cost csv for chosen years from different source (source_dict). The data is standardized for uniform: - cost years (depending on the rate of inflation ) - technology names @@ -1507,7 +1507,7 @@ def set_specify_assumptions(tech_data): # in the DEA they do differ between heating the floor area or heating with # radiators, since most households heat with radiators and there # efficiencies are lower (conservative approach) those are assumed - # furthermore the total efficiency is assumed which includes auxilary electricity + # furthermore the total efficiency is assumed which includes auxiliary electricity # consumption name = "Heat efficiency, annual average, net, radiators" techs_radiator = tech_data.xs(name, level=1).index @@ -2044,7 +2044,7 @@ def add_description(data): # add comment for offwind investment if snakemake.config["offwind_no_gridcosts"]: data.loc[("offwind", "investment"), "further description"] += ( - " grid connection costs substracted from investment costs" + " grid connection costs subtracted from investment costs" ) return data @@ -2107,8 +2107,8 @@ def add_gas_storage(data): ) data.loc[("gas storage", "lifetime"), "unit"] = "years" - # process equipment, injection (2200MW) withdrawl (6600MW) - # assuming half of investment costs for injection, half for withdrawl + # process equipment, injection (2200MW) withdrawal (6600MW) + # assuming half of investment costs for injection, half for withdrawal investment_charge = ( gas_storage.loc["Total investment cost"].iloc[0, 0] / 2 / 2200 * 1e3 ) @@ -2141,7 +2141,7 @@ def add_gas_storage(data): data.loc[("gas storage", "FOM"), years] = FOM data.loc[("gas storage", "FOM"), "source"] = source_dict["DEA"] data.loc[("gas storage", "FOM"), "further description"] = ( - "150 Underground Storage of Gas, Operation and Maintenace, salt cavern (units converted)" + "150 Underground Storage of Gas, Operation and Maintenance, salt cavern (units converted)" ) data.loc[("gas storage", "FOM"), "unit"] = "%" @@ -2614,7 +2614,6 @@ def energy_penalty(costs): eta_steam = (1 - scalingFactor) * costs.loc[(boiler, "efficiency"), "value"] eta_old = costs.loc[(tech, "efficiency"), "value"] - temp = costs.loc[(tech, "efficiency"), "value"] eta_main = costs.loc[(tech, "efficiency"), "value"] * scalingFactor # Adapting investment share of tech due to steam boiler addition. Investment per MW_el. @@ -2930,7 +2929,7 @@ def add_SMR_data(data): SMR_df.loc[("SMR CC", "capture_rate"), "source"] = source_dict["IEA"] SMR_df.loc[("SMR CC", "capture_rate"), "unit"] = "EUR/MW_CH4" SMR_df.loc[("SMR CC", "capture_rate"), "further description"] = ( - "wide range: capture rates betwen 54%-90%" + "wide range: capture rates between 54%-90%" ) SMR_df = SMR_df.dropna(axis=1, how="all") @@ -3139,7 +3138,7 @@ def add_energy_storage_database(costs, data_year): endp_first_segment = y.iloc[1] # Below we create linear segments between 2021-2030 - # While the first segment is known, the others are defined by the initial segments with a accumulating quadratic descreasing gradient + # While the first segment is known, the others are defined by the initial segments with a accumulating quadratic decreasing gradient other_segments_points = [2034, 2039, 2044, 2049, 2054, 2059] def geometric_series( @@ -3265,7 +3264,7 @@ def geometric_series( } ] ) - # not concat if df year is 2021 or 2030 (otherwhise duplicate) + # not concat if df year is 2021 or 2030 (otherwise duplicate) if data_year == 2021 or data_year == 2030: continue else: diff --git a/scripts/convert_pdf_EWG_to_dataframe.py b/scripts/convert_pdf_EWG_to_dataframe.py index cfabcc6..07d0c52 100755 --- a/scripts/convert_pdf_EWG_to_dataframe.py +++ b/scripts/convert_pdf_EWG_to_dataframe.py @@ -42,7 +42,7 @@ # clean data frame split_units = df_list[0]["Units 2015"].fillna(" ").str.split(" ", expand=True) # check where split is too long -to_be_merged = split_units[split_units[2].apply(lambda x: x != None)].index +to_be_merged = split_units[split_units[2].apply(lambda x: x is not None)].index split_units.loc[to_be_merged, 0] = ( split_units.loc[to_be_merged, 0] + " " + split_units.loc[to_be_merged, 1] ) @@ -70,7 +70,7 @@ for sign in [" €", " kWh"]: split_units = df_list[4]["Type"].fillna(" ").str.split(sign, expand=True) # check where split is too long - to_be_merged = split_units[split_units[1].apply(lambda x: x != None)].index + to_be_merged = split_units[split_units[1].apply(lambda x: x is not None)].index df_list[4].loc[to_be_merged, "Type"] = split_units.loc[to_be_merged, 0] df_list[4].loc[to_be_merged, "Units"] = sign + split_units.loc[to_be_merged, 1] diff --git a/scripts/convert_pdf_fraunhofer_to_dataframe.py b/scripts/convert_pdf_fraunhofer_to_dataframe.py index b686a26..1787fd2 100644 --- a/scripts/convert_pdf_fraunhofer_to_dataframe.py +++ b/scripts/convert_pdf_fraunhofer_to_dataframe.py @@ -46,7 +46,7 @@ counter = len(test) - 2 for i in range(counter, -1, -1): - if (type(test.iloc[i]) == str) and (type(test.iloc[i - 1]) == str): + if (isinstance(test.iloc[i], str)) and (isinstance(test.iloc[i - 1], str)): test.iloc[i - 1] = test.iloc[i - 1] + " " + test.iloc[i] test.iloc[i] = np.nan test.fillna(method="ffill", inplace=True) From a4f3c75cfe818390fa963fcdb24742bc2f12ef0e Mon Sep 17 00:00:00 2001 From: Fabrizio Finozzi Date: Fri, 10 Jan 2025 15:15:55 +0100 Subject: [PATCH 12/15] code: changes driven by linter --- .pre-commit-config.yaml | 2 +- scripts/compile_cost_assumptions.py | 6 +- .../convert_pdf_fraunhofer_to_dataframe.py | 123 ++++++++++-------- 3 files changed, 74 insertions(+), 57 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 61d71c9..5178ddf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,7 +27,7 @@ repos: rev: v2.3.0 hooks: - id: codespell - args: ['--ignore-regex="(\b[A-Z]+\b)"', '--ignore-words-list=fom,appartment,bage,ore,setis,tabacco,berfore,vor,pris'] # Ignore capital case words, e.g. country codes + args: ['--ignore-regex="(\b[A-Z]+\b)"', '--ignore-words-list=fom,appartment,bage,ore,setis,tabacco,berfore,vor,pris,WEGE,Wege,Eletricity'] # Ignore capital case words, e.g. country codes types_or: [python, rst, markdown] files: ^(scripts|doc)/ diff --git a/scripts/compile_cost_assumptions.py b/scripts/compile_cost_assumptions.py index b8a182a..b703040 100644 --- a/scripts/compile_cost_assumptions.py +++ b/scripts/compile_cost_assumptions.py @@ -1358,7 +1358,7 @@ def clean_up_units(tech_data, value_column="", source=""): "% -points of heat loss", "MWh_th/MWh_el" ) tech_data.unit = tech_data.unit.str.replace( - "FT Liquids Output, MWh/MWh Total Inpu", "MWh_FT/MWh_H2" + "FT Liquids Output, MWh/MWh Total Input", "MWh_FT/MWh_H2" ) # biomass-to-methanol-specific if isinstance(tech_data.index, pd.MultiIndex): @@ -3350,7 +3350,7 @@ def prepare_inflation_rate(fn): # (c) ----- get tech data in pypsa syntax ----------------------------------- # make categories: investment, FOM, VOM, efficiency, c_b, c_v data = order_data(tech_data) - # add excel sheet names and further description + # add Excel sheet names and further description data = add_description(data) # convert efficiency from %-> per unit and investment from MW->kW to compare data = convert_units(data) @@ -3368,7 +3368,7 @@ def prepare_inflation_rate(fn): else: data.at[x, "currency_year"] = 2015 - # add heavy duty assumptions, cost year is 2022 + # add heavy-duty assumptions, cost year is 2022 data = get_dea_vehicle_data(snakemake.input.dea_vehicles, data) # add shipping data diff --git a/scripts/convert_pdf_fraunhofer_to_dataframe.py b/scripts/convert_pdf_fraunhofer_to_dataframe.py index 1787fd2..78b08cd 100644 --- a/scripts/convert_pdf_fraunhofer_to_dataframe.py +++ b/scripts/convert_pdf_fraunhofer_to_dataframe.py @@ -13,66 +13,83 @@ import pandas as pd from tabula import read_pdf -df_list = read_pdf(snakemake.input.fraunhofer, pages="3-15", multiple_tables=True) -print(df_list) -clean_df = [] -j = 0 -for i in range(len(df_list)): - print(i) - if len(df_list[i]) == 1: - print("table is dropped ", i) - if "Komponente" in df_list[i].iloc[0].unique(): - print("table is added ", i) - clean_df.append(df_list[i]) - j += 1 - print(j) - continue - else: - clean_df[j - 1] = clean_df[j - 1].append(df_list[i]) - print("table is appended ", i) +if __name__ == "__main__": + if "snakemake" not in globals(): + from _helpers import mock_snakemake + snakemake = mock_snakemake("convert_pdf_fraunhofer_to_dataframe") -for i in range(len(clean_df)): - clean_df[i].columns = clean_df[i].iloc[0, :] - clean_df[i] = clean_df[i].iloc[1:, :] - clean_df[i].reset_index(drop=True, inplace=True) - clean_df[i].dropna(axis=1, how="all", inplace=True) + df_list = read_pdf(snakemake.input.fraunhofer, pages="3-15", multiple_tables=True) + print(df_list) + clean_df = [] + j = 0 + for i in range(len(df_list)): + print(i) + if len(df_list[i]) == 1: + print("table is dropped ", i) + if "Komponente" in df_list[i].iloc[0].unique(): + print("table is added ", i) + clean_df.append(df_list[i]) + j += 1 + print(j) + continue + else: + clean_df[j - 1] = clean_df[j - 1].append(df_list[i]) + print("table is appended ", i) + for i in range(len(clean_df)): + clean_df[i].columns = clean_df[i].iloc[0, :] + clean_df[i] = clean_df[i].iloc[1:, :] + clean_df[i].reset_index(drop=True, inplace=True) + clean_df[i].dropna(axis=1, how="all", inplace=True) -columns = ["Komponente", "Größe", "Einheit", 2020, 2025, 2030, 2035, 2040, 2045, 2050] -for table in range(len(clean_df)): - print(table) - test = clean_df[table]["Komponente"] + columns = [ + "Komponente", + "Größe", + "Einheit", + 2020, + 2025, + 2030, + 2035, + 2040, + 2045, + 2050, + ] + for table in range(len(clean_df)): + print(table) + test = clean_df[table]["Komponente"] - counter = len(test) - 2 - for i in range(counter, -1, -1): - if (isinstance(test.iloc[i], str)) and (isinstance(test.iloc[i - 1], str)): - test.iloc[i - 1] = test.iloc[i - 1] + " " + test.iloc[i] - test.iloc[i] = np.nan - test.fillna(method="ffill", inplace=True) - clean_df[table]["Komponente"] = test + counter = len(test) - 2 + for i in range(counter, -1, -1): + if (isinstance(test.iloc[i], str)) and (isinstance(test.iloc[i - 1], str)): + test.iloc[i - 1] = test.iloc[i - 1] + " " + test.iloc[i] + test.iloc[i] = np.nan + test.fillna(method="ffill", inplace=True) + clean_df[table]["Komponente"] = test - new = {} - for i in range(len(test)): - a = clean_df[table].loc[i].dropna() - if len(a) == len(columns): - new[i] = a - new[i].index = columns - else: - print(a) + new = {} + for i in range(len(test)): + a = clean_df[table].loc[i].dropna() + if len(a) == len(columns): + new[i] = a + new[i].index = columns + else: + print(a) - clean_df[table] = pd.concat(new, axis=1).T - clean_df[table].set_index(["Komponente", "Größe"], inplace=True) - clean_df[table].dropna(how="all", inplace=True) + clean_df[table] = pd.concat(new, axis=1).T + clean_df[table].set_index(["Komponente", "Größe"], inplace=True) + clean_df[table].dropna(how="all", inplace=True) -total = pd.concat(clean_df) + total = pd.concat(clean_df) -total.Einheit = total.Einheit.str.replace("€", "EUR") -total.to_csv(snakemake.output.costs, encoding="iso-8859-15") + total.Einheit = total.Einheit.str.replace("€", "EUR") + total.to_csv(snakemake.output.costs, encoding="iso-8859-15") -energiepreise = read_pdf(snakemake.input.fraunhofer, pages="15") -energiepreise.dropna(axis=1, how="all", inplace=True) -energiepreise.dropna(axis=0, how="all", inplace=True) -energiepreise = energiepreise.rename(columns={"Unnamed: 1": "Fuel"}).set_index("Fuel") -energiepreise["unit"] = "Eur/MWh" -energiepreise.to_csv(snakemake.output.energy_prices, encoding="iso-8859-15") + energiepreise = read_pdf(snakemake.input.fraunhofer, pages="15") + energiepreise.dropna(axis=1, how="all", inplace=True) + energiepreise.dropna(axis=0, how="all", inplace=True) + energiepreise = energiepreise.rename(columns={"Unnamed: 1": "Fuel"}).set_index( + "Fuel" + ) + energiepreise["unit"] = "Eur/MWh" + energiepreise.to_csv(snakemake.output.energy_prices, encoding="iso-8859-15") From c3b2d38adf3cc5cdfe9da582146d1b52209ceed7 Mon Sep 17 00:00:00 2001 From: Fabrizio Finozzi Date: Fri, 10 Jan 2025 15:31:09 +0100 Subject: [PATCH 13/15] data: update outputs --- outputs/costs_2020.csv | 6 +++--- outputs/costs_2025.csv | 6 +++--- outputs/costs_2030.csv | 6 +++--- outputs/costs_2035.csv | 6 +++--- outputs/costs_2040.csv | 6 +++--- outputs/costs_2045.csv | 6 +++--- outputs/costs_2050.csv | 6 +++--- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/outputs/costs_2020.csv b/outputs/costs_2020.csv index 150f213..1084545 100644 --- a/outputs/costs_2020.csv +++ b/outputs/costs_2020.csv @@ -477,7 +477,7 @@ SMR,efficiency,0.76,per unit (in LHV),"IEA Global average levelised cost of hydr SMR,investment,522201.0492,EUR/MW_CH4,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311",2015.0 SMR,lifetime,30.0,years,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, SMR CC,FOM,5.0,%/year,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311", -SMR CC,capture_rate,0.9,EUR/MW_CH4,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",wide range: capture rates betwen 54%-90%, +SMR CC,capture_rate,0.9,EUR/MW_CH4,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",wide range: capture rates between 54%-90%, SMR CC,efficiency,0.69,per unit (in LHV),"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, SMR CC,investment,605753.2171,EUR/MW_CH4,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311",2015.0 SMR CC,lifetime,30.0,years,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, @@ -900,7 +900,7 @@ gas boiler steam,VOM,1.1077,EUR/MWh,"Danish Energy Agency, technology_data_for_i gas boiler steam,efficiency,0.92,per unit,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx","311.1c Steam boiler Gas: Total efficiency, net, annual average",2019.0 gas boiler steam,investment,54.9273,EUR/kW,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx",311.1c Steam boiler Gas: Nominal investment,2019.0 gas boiler steam,lifetime,25.0,years,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx",311.1c Steam boiler Gas: Technical lifetime,2019.0 -gas storage,FOM,3.5919,%,Danish Energy Agency,"150 Underground Storage of Gas, Operation and Maintenace, salt cavern (units converted)",2015.0 +gas storage,FOM,3.5919,%,Danish Energy Agency,"150 Underground Storage of Gas, Operation and Maintenance, salt cavern (units converted)",2015.0 gas storage,investment,0.0348,EUR/kWh,Danish Energy Agency,"150 Underground Storage of Gas, Establishment of one cavern (units converted)",2015.0 gas storage,lifetime,100.0,years,TODO no source,"estimation: most underground storage are already build, they do have a long lifetime",2015.0 gas storage charger,investment,15.1737,EUR/kW,Danish Energy Agency,"150 Underground Storage of Gas, Process equipment (units converted)",2015.0 @@ -1006,7 +1006,7 @@ nuclear,investment,8594.1354,EUR/kW_e,"Lazard's levelized cost of energy analysi nuclear,lifetime,40.0,years,"Lazard's levelized cost of energy analysis - version 16.0 (2023): https://www.lazard.com/media/typdgxmm/lazards-lcoeplus-april-2023.pdf , pg. 49 (Levelized Cost of Energy - Key Assumptions), accessed: 2023-12-14.",,2023.0 offwind,FOM,2.5093,%/year,"Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Fixed O&M [EUR/MW_e/y, 2020]",2020.0 offwind,VOM,0.0212,EUR/MWhel,RES costs made up to fix curtailment order, from old pypsa cost assumptions,2015.0 -offwind,investment,1992.6105,"EUR/kW_e, 2020","Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Nominal investment [MEUR/MW_e, 2020] grid connection costs substracted from investment costs",2020.0 +offwind,investment,1992.6105,"EUR/kW_e, 2020","Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Nominal investment [MEUR/MW_e, 2020] grid connection costs subtracted from investment costs",2020.0 offwind,lifetime,27.0,years,"Danish Energy Agency, technology_data_for_el_and_dh.xlsx",21 Offshore turbines: Technical lifetime [years],2020.0 offwind-ac-connection-submarine,investment,2841.3251,EUR/MW/km,DEA https://ens.dk/en/our-services/projections-and-models/technology-data, from old pypsa cost assumptions,2015.0 offwind-ac-connection-underground,investment,1420.1334,EUR/MW/km,DEA https://ens.dk/en/our-services/projections-and-models/technology-data, from old pypsa cost assumptions,2015.0 diff --git a/outputs/costs_2025.csv b/outputs/costs_2025.csv index 10db78e..7268d56 100644 --- a/outputs/costs_2025.csv +++ b/outputs/costs_2025.csv @@ -477,7 +477,7 @@ SMR,efficiency,0.76,per unit (in LHV),"IEA Global average levelised cost of hydr SMR,investment,522201.0492,EUR/MW_CH4,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311",2015.0 SMR,lifetime,30.0,years,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, SMR CC,FOM,5.0,%/year,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311", -SMR CC,capture_rate,0.9,EUR/MW_CH4,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",wide range: capture rates betwen 54%-90%, +SMR CC,capture_rate,0.9,EUR/MW_CH4,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",wide range: capture rates between 54%-90%, SMR CC,efficiency,0.69,per unit (in LHV),"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, SMR CC,investment,605753.2171,EUR/MW_CH4,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311",2015.0 SMR CC,lifetime,30.0,years,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, @@ -900,7 +900,7 @@ gas boiler steam,VOM,1.0574,EUR/MWh,"Danish Energy Agency, technology_data_for_i gas boiler steam,efficiency,0.925,per unit,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx","311.1c Steam boiler Gas: Total efficiency, net, annual average",2019.0 gas boiler steam,investment,50.35,EUR/kW,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx",311.1c Steam boiler Gas: Nominal investment,2019.0 gas boiler steam,lifetime,25.0,years,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx",311.1c Steam boiler Gas: Technical lifetime,2019.0 -gas storage,FOM,3.5919,%,Danish Energy Agency,"150 Underground Storage of Gas, Operation and Maintenace, salt cavern (units converted)",2015.0 +gas storage,FOM,3.5919,%,Danish Energy Agency,"150 Underground Storage of Gas, Operation and Maintenance, salt cavern (units converted)",2015.0 gas storage,investment,0.0348,EUR/kWh,Danish Energy Agency,"150 Underground Storage of Gas, Establishment of one cavern (units converted)",2015.0 gas storage,lifetime,100.0,years,TODO no source,"estimation: most underground storage are already build, they do have a long lifetime",2015.0 gas storage charger,investment,15.1737,EUR/kW,Danish Energy Agency,"150 Underground Storage of Gas, Process equipment (units converted)",2015.0 @@ -1006,7 +1006,7 @@ nuclear,investment,8594.1354,EUR/kW_e,"Lazard's levelized cost of energy analysi nuclear,lifetime,40.0,years,"Lazard's levelized cost of energy analysis - version 16.0 (2023): https://www.lazard.com/media/typdgxmm/lazards-lcoeplus-april-2023.pdf , pg. 49 (Levelized Cost of Energy - Key Assumptions), accessed: 2023-12-14.",,2023.0 offwind,FOM,2.3741,%/year,"Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Fixed O&M [EUR/MW_e/y, 2020]",2020.0 offwind,VOM,0.0212,EUR/MWhel,RES costs made up to fix curtailment order, from old pypsa cost assumptions,2015.0 -offwind,investment,1769.1171,"EUR/kW_e, 2020","Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Nominal investment [MEUR/MW_e, 2020] grid connection costs substracted from investment costs",2020.0 +offwind,investment,1769.1171,"EUR/kW_e, 2020","Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Nominal investment [MEUR/MW_e, 2020] grid connection costs subtracted from investment costs",2020.0 offwind,lifetime,30.0,years,"Danish Energy Agency, technology_data_for_el_and_dh.xlsx",21 Offshore turbines: Technical lifetime [years],2020.0 offwind-ac-connection-submarine,investment,2841.3251,EUR/MW/km,DEA https://ens.dk/en/our-services/projections-and-models/technology-data, from old pypsa cost assumptions,2015.0 offwind-ac-connection-underground,investment,1420.1334,EUR/MW/km,DEA https://ens.dk/en/our-services/projections-and-models/technology-data, from old pypsa cost assumptions,2015.0 diff --git a/outputs/costs_2030.csv b/outputs/costs_2030.csv index 79c426e..ba2dcc4 100644 --- a/outputs/costs_2030.csv +++ b/outputs/costs_2030.csv @@ -477,7 +477,7 @@ SMR,efficiency,0.76,per unit (in LHV),"IEA Global average levelised cost of hydr SMR,investment,522201.0492,EUR/MW_CH4,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311",2015.0 SMR,lifetime,30.0,years,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, SMR CC,FOM,5.0,%/year,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311", -SMR CC,capture_rate,0.9,EUR/MW_CH4,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",wide range: capture rates betwen 54%-90%, +SMR CC,capture_rate,0.9,EUR/MW_CH4,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",wide range: capture rates between 54%-90%, SMR CC,efficiency,0.69,per unit (in LHV),"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, SMR CC,investment,605753.2171,EUR/MW_CH4,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311",2015.0 SMR CC,lifetime,30.0,years,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, @@ -900,7 +900,7 @@ gas boiler steam,VOM,1.007,EUR/MWh,"Danish Energy Agency, technology_data_for_in gas boiler steam,efficiency,0.93,per unit,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx","311.1c Steam boiler Gas: Total efficiency, net, annual average",2019.0 gas boiler steam,investment,45.7727,EUR/kW,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx",311.1c Steam boiler Gas: Nominal investment,2019.0 gas boiler steam,lifetime,25.0,years,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx",311.1c Steam boiler Gas: Technical lifetime,2019.0 -gas storage,FOM,3.5919,%,Danish Energy Agency,"150 Underground Storage of Gas, Operation and Maintenace, salt cavern (units converted)",2015.0 +gas storage,FOM,3.5919,%,Danish Energy Agency,"150 Underground Storage of Gas, Operation and Maintenance, salt cavern (units converted)",2015.0 gas storage,investment,0.0348,EUR/kWh,Danish Energy Agency,"150 Underground Storage of Gas, Establishment of one cavern (units converted)",2015.0 gas storage,lifetime,100.0,years,TODO no source,"estimation: most underground storage are already build, they do have a long lifetime",2015.0 gas storage charger,investment,15.1737,EUR/kW,Danish Energy Agency,"150 Underground Storage of Gas, Process equipment (units converted)",2015.0 @@ -1006,7 +1006,7 @@ nuclear,investment,8594.1354,EUR/kW_e,"Lazard's levelized cost of energy analysi nuclear,lifetime,40.0,years,"Lazard's levelized cost of energy analysis - version 16.0 (2023): https://www.lazard.com/media/typdgxmm/lazards-lcoeplus-april-2023.pdf , pg. 49 (Levelized Cost of Energy - Key Assumptions), accessed: 2023-12-14.",,2023.0 offwind,FOM,2.3185,%/year,"Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Fixed O&M [EUR/MW_e/y, 2020]",2020.0 offwind,VOM,0.0212,EUR/MWhel,RES costs made up to fix curtailment order, from old pypsa cost assumptions,2015.0 -offwind,investment,1682.1226,"EUR/kW_e, 2020","Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Nominal investment [MEUR/MW_e, 2020] grid connection costs substracted from investment costs",2020.0 +offwind,investment,1682.1226,"EUR/kW_e, 2020","Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Nominal investment [MEUR/MW_e, 2020] grid connection costs subtracted from investment costs",2020.0 offwind,lifetime,30.0,years,"Danish Energy Agency, technology_data_for_el_and_dh.xlsx",21 Offshore turbines: Technical lifetime [years],2020.0 offwind-ac-connection-submarine,investment,2841.3251,EUR/MW/km,DEA https://ens.dk/en/our-services/projections-and-models/technology-data, from old pypsa cost assumptions,2015.0 offwind-ac-connection-underground,investment,1420.1334,EUR/MW/km,DEA https://ens.dk/en/our-services/projections-and-models/technology-data, from old pypsa cost assumptions,2015.0 diff --git a/outputs/costs_2035.csv b/outputs/costs_2035.csv index 5860570..9618c8d 100644 --- a/outputs/costs_2035.csv +++ b/outputs/costs_2035.csv @@ -477,7 +477,7 @@ SMR,efficiency,0.76,per unit (in LHV),"IEA Global average levelised cost of hydr SMR,investment,522201.0492,EUR/MW_CH4,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311",2015.0 SMR,lifetime,30.0,years,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, SMR CC,FOM,5.0,%/year,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311", -SMR CC,capture_rate,0.9,EUR/MW_CH4,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",wide range: capture rates betwen 54%-90%, +SMR CC,capture_rate,0.9,EUR/MW_CH4,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",wide range: capture rates between 54%-90%, SMR CC,efficiency,0.69,per unit (in LHV),"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, SMR CC,investment,605753.2171,EUR/MW_CH4,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311",2015.0 SMR CC,lifetime,30.0,years,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, @@ -900,7 +900,7 @@ gas boiler steam,VOM,1.007,EUR/MWh,"Danish Energy Agency, technology_data_for_in gas boiler steam,efficiency,0.93,per unit,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx","311.1c Steam boiler Gas: Total efficiency, net, annual average",2019.0 gas boiler steam,investment,45.7727,EUR/kW,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx",311.1c Steam boiler Gas: Nominal investment,2019.0 gas boiler steam,lifetime,25.0,years,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx",311.1c Steam boiler Gas: Technical lifetime,2019.0 -gas storage,FOM,3.5919,%,Danish Energy Agency,"150 Underground Storage of Gas, Operation and Maintenace, salt cavern (units converted)",2015.0 +gas storage,FOM,3.5919,%,Danish Energy Agency,"150 Underground Storage of Gas, Operation and Maintenance, salt cavern (units converted)",2015.0 gas storage,investment,0.0348,EUR/kWh,Danish Energy Agency,"150 Underground Storage of Gas, Establishment of one cavern (units converted)",2015.0 gas storage,lifetime,100.0,years,TODO no source,"estimation: most underground storage are already build, they do have a long lifetime",2015.0 gas storage charger,investment,15.1737,EUR/kW,Danish Energy Agency,"150 Underground Storage of Gas, Process equipment (units converted)",2015.0 @@ -1006,7 +1006,7 @@ nuclear,investment,8594.1354,EUR/kW_e,"Lazard's levelized cost of energy analysi nuclear,lifetime,40.0,years,"Lazard's levelized cost of energy analysis - version 16.0 (2023): https://www.lazard.com/media/typdgxmm/lazards-lcoeplus-april-2023.pdf , pg. 49 (Levelized Cost of Energy - Key Assumptions), accessed: 2023-12-14.",,2023.0 offwind,FOM,2.25,%/year,"Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Fixed O&M [EUR/MW_e/y, 2020]",2020.0 offwind,VOM,0.0212,EUR/MWhel,RES costs made up to fix curtailment order, from old pypsa cost assumptions,2015.0 -offwind,investment,1622.2443,"EUR/kW_e, 2020","Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Nominal investment [MEUR/MW_e, 2020] grid connection costs substracted from investment costs",2020.0 +offwind,investment,1622.2443,"EUR/kW_e, 2020","Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Nominal investment [MEUR/MW_e, 2020] grid connection costs subtracted from investment costs",2020.0 offwind,lifetime,30.0,years,"Danish Energy Agency, technology_data_for_el_and_dh.xlsx",21 Offshore turbines: Technical lifetime [years],2020.0 offwind-ac-connection-submarine,investment,2841.3251,EUR/MW/km,DEA https://ens.dk/en/our-services/projections-and-models/technology-data, from old pypsa cost assumptions,2015.0 offwind-ac-connection-underground,investment,1420.1334,EUR/MW/km,DEA https://ens.dk/en/our-services/projections-and-models/technology-data, from old pypsa cost assumptions,2015.0 diff --git a/outputs/costs_2040.csv b/outputs/costs_2040.csv index 7608789..dd23f57 100644 --- a/outputs/costs_2040.csv +++ b/outputs/costs_2040.csv @@ -477,7 +477,7 @@ SMR,efficiency,0.76,per unit (in LHV),"IEA Global average levelised cost of hydr SMR,investment,522201.0492,EUR/MW_CH4,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311",2015.0 SMR,lifetime,30.0,years,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, SMR CC,FOM,5.0,%/year,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311", -SMR CC,capture_rate,0.9,EUR/MW_CH4,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",wide range: capture rates betwen 54%-90%, +SMR CC,capture_rate,0.9,EUR/MW_CH4,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",wide range: capture rates between 54%-90%, SMR CC,efficiency,0.69,per unit (in LHV),"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, SMR CC,investment,605753.2171,EUR/MW_CH4,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311",2015.0 SMR CC,lifetime,30.0,years,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, @@ -900,7 +900,7 @@ gas boiler steam,VOM,1.007,EUR/MWh,"Danish Energy Agency, technology_data_for_in gas boiler steam,efficiency,0.93,per unit,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx","311.1c Steam boiler Gas: Total efficiency, net, annual average",2019.0 gas boiler steam,investment,45.7727,EUR/kW,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx",311.1c Steam boiler Gas: Nominal investment,2019.0 gas boiler steam,lifetime,25.0,years,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx",311.1c Steam boiler Gas: Technical lifetime,2019.0 -gas storage,FOM,3.5919,%,Danish Energy Agency,"150 Underground Storage of Gas, Operation and Maintenace, salt cavern (units converted)",2015.0 +gas storage,FOM,3.5919,%,Danish Energy Agency,"150 Underground Storage of Gas, Operation and Maintenance, salt cavern (units converted)",2015.0 gas storage,investment,0.0348,EUR/kWh,Danish Energy Agency,"150 Underground Storage of Gas, Establishment of one cavern (units converted)",2015.0 gas storage,lifetime,100.0,years,TODO no source,"estimation: most underground storage are already build, they do have a long lifetime",2015.0 gas storage charger,investment,15.1737,EUR/kW,Danish Energy Agency,"150 Underground Storage of Gas, Process equipment (units converted)",2015.0 @@ -1006,7 +1006,7 @@ nuclear,investment,8594.1354,EUR/kW_e,"Lazard's levelized cost of energy analysi nuclear,lifetime,40.0,years,"Lazard's levelized cost of energy analysis - version 16.0 (2023): https://www.lazard.com/media/typdgxmm/lazards-lcoeplus-april-2023.pdf , pg. 49 (Levelized Cost of Energy - Key Assumptions), accessed: 2023-12-14.",,2023.0 offwind,FOM,2.1762,%/year,"Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Fixed O&M [EUR/MW_e/y, 2020]",2020.0 offwind,VOM,0.0212,EUR/MWhel,RES costs made up to fix curtailment order, from old pypsa cost assumptions,2015.0 -offwind,investment,1562.3661,"EUR/kW_e, 2020","Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Nominal investment [MEUR/MW_e, 2020] grid connection costs substracted from investment costs",2020.0 +offwind,investment,1562.3661,"EUR/kW_e, 2020","Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Nominal investment [MEUR/MW_e, 2020] grid connection costs subtracted from investment costs",2020.0 offwind,lifetime,30.0,years,"Danish Energy Agency, technology_data_for_el_and_dh.xlsx",21 Offshore turbines: Technical lifetime [years],2020.0 offwind-ac-connection-submarine,investment,2841.3251,EUR/MW/km,DEA https://ens.dk/en/our-services/projections-and-models/technology-data, from old pypsa cost assumptions,2015.0 offwind-ac-connection-underground,investment,1420.1334,EUR/MW/km,DEA https://ens.dk/en/our-services/projections-and-models/technology-data, from old pypsa cost assumptions,2015.0 diff --git a/outputs/costs_2045.csv b/outputs/costs_2045.csv index d5c481e..164897e 100644 --- a/outputs/costs_2045.csv +++ b/outputs/costs_2045.csv @@ -477,7 +477,7 @@ SMR,efficiency,0.76,per unit (in LHV),"IEA Global average levelised cost of hydr SMR,investment,522201.0492,EUR/MW_CH4,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311",2015.0 SMR,lifetime,30.0,years,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, SMR CC,FOM,5.0,%/year,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311", -SMR CC,capture_rate,0.9,EUR/MW_CH4,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",wide range: capture rates betwen 54%-90%, +SMR CC,capture_rate,0.9,EUR/MW_CH4,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",wide range: capture rates between 54%-90%, SMR CC,efficiency,0.69,per unit (in LHV),"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, SMR CC,investment,605753.2171,EUR/MW_CH4,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311",2015.0 SMR CC,lifetime,30.0,years,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, @@ -900,7 +900,7 @@ gas boiler steam,VOM,1.007,EUR/MWh,"Danish Energy Agency, technology_data_for_in gas boiler steam,efficiency,0.935,per unit,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx","311.1c Steam boiler Gas: Total efficiency, net, annual average",2019.0 gas boiler steam,investment,45.7727,EUR/kW,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx",311.1c Steam boiler Gas: Nominal investment,2019.0 gas boiler steam,lifetime,25.0,years,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx",311.1c Steam boiler Gas: Technical lifetime,2019.0 -gas storage,FOM,3.5919,%,Danish Energy Agency,"150 Underground Storage of Gas, Operation and Maintenace, salt cavern (units converted)",2015.0 +gas storage,FOM,3.5919,%,Danish Energy Agency,"150 Underground Storage of Gas, Operation and Maintenance, salt cavern (units converted)",2015.0 gas storage,investment,0.0348,EUR/kWh,Danish Energy Agency,"150 Underground Storage of Gas, Establishment of one cavern (units converted)",2015.0 gas storage,lifetime,100.0,years,TODO no source,"estimation: most underground storage are already build, they do have a long lifetime",2015.0 gas storage charger,investment,15.1737,EUR/kW,Danish Energy Agency,"150 Underground Storage of Gas, Process equipment (units converted)",2015.0 @@ -1006,7 +1006,7 @@ nuclear,investment,8594.1354,EUR/kW_e,"Lazard's levelized cost of energy analysi nuclear,lifetime,40.0,years,"Lazard's levelized cost of energy analysis - version 16.0 (2023): https://www.lazard.com/media/typdgxmm/lazards-lcoeplus-april-2023.pdf , pg. 49 (Levelized Cost of Energy - Key Assumptions), accessed: 2023-12-14.",,2023.0 offwind,FOM,2.1709,%/year,"Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Fixed O&M [EUR/MW_e/y, 2020]",2020.0 offwind,VOM,0.0212,EUR/MWhel,RES costs made up to fix curtailment order, from old pypsa cost assumptions,2015.0 -offwind,investment,1543.1486,"EUR/kW_e, 2020","Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Nominal investment [MEUR/MW_e, 2020] grid connection costs substracted from investment costs",2020.0 +offwind,investment,1543.1486,"EUR/kW_e, 2020","Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Nominal investment [MEUR/MW_e, 2020] grid connection costs subtracted from investment costs",2020.0 offwind,lifetime,30.0,years,"Danish Energy Agency, technology_data_for_el_and_dh.xlsx",21 Offshore turbines: Technical lifetime [years],2020.0 offwind-ac-connection-submarine,investment,2841.3251,EUR/MW/km,DEA https://ens.dk/en/our-services/projections-and-models/technology-data, from old pypsa cost assumptions,2015.0 offwind-ac-connection-underground,investment,1420.1334,EUR/MW/km,DEA https://ens.dk/en/our-services/projections-and-models/technology-data, from old pypsa cost assumptions,2015.0 diff --git a/outputs/costs_2050.csv b/outputs/costs_2050.csv index 1d9a893..e6ff04a 100644 --- a/outputs/costs_2050.csv +++ b/outputs/costs_2050.csv @@ -477,7 +477,7 @@ SMR,efficiency,0.76,per unit (in LHV),"IEA Global average levelised cost of hydr SMR,investment,522201.0492,EUR/MW_CH4,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311",2015.0 SMR,lifetime,30.0,years,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, SMR CC,FOM,5.0,%/year,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311", -SMR CC,capture_rate,0.9,EUR/MW_CH4,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",wide range: capture rates betwen 54%-90%, +SMR CC,capture_rate,0.9,EUR/MW_CH4,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",wide range: capture rates between 54%-90%, SMR CC,efficiency,0.69,per unit (in LHV),"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, SMR CC,investment,605753.2171,EUR/MW_CH4,Danish Energy Agency,"Technology data for renewable fuels, in pdf on table 3 p.311",2015.0 SMR CC,lifetime,30.0,years,"IEA Global average levelised cost of hydrogen production by energy source and technology, 2019 and 2050 (2020), https://www.iea.org/data-and-statistics/charts/global-average-levelised-cost-of-hydrogen-production-by-energy-source-and-technology-2019-and-2050",, @@ -900,7 +900,7 @@ gas boiler steam,VOM,1.007,EUR/MWh,"Danish Energy Agency, technology_data_for_in gas boiler steam,efficiency,0.94,per unit,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx","311.1c Steam boiler Gas: Total efficiency, net, annual average",2019.0 gas boiler steam,investment,45.7727,EUR/kW,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx",311.1c Steam boiler Gas: Nominal investment,2019.0 gas boiler steam,lifetime,25.0,years,"Danish Energy Agency, technology_data_for_industrial_process_heat.xlsx",311.1c Steam boiler Gas: Technical lifetime,2019.0 -gas storage,FOM,3.5919,%,Danish Energy Agency,"150 Underground Storage of Gas, Operation and Maintenace, salt cavern (units converted)",2015.0 +gas storage,FOM,3.5919,%,Danish Energy Agency,"150 Underground Storage of Gas, Operation and Maintenance, salt cavern (units converted)",2015.0 gas storage,investment,0.0348,EUR/kWh,Danish Energy Agency,"150 Underground Storage of Gas, Establishment of one cavern (units converted)",2015.0 gas storage,lifetime,100.0,years,TODO no source,"estimation: most underground storage are already build, they do have a long lifetime",2015.0 gas storage charger,investment,15.1737,EUR/kW,Danish Energy Agency,"150 Underground Storage of Gas, Process equipment (units converted)",2015.0 @@ -1006,7 +1006,7 @@ nuclear,investment,8594.1354,EUR/kW_e,"Lazard's levelized cost of energy analysi nuclear,lifetime,40.0,years,"Lazard's levelized cost of energy analysis - version 16.0 (2023): https://www.lazard.com/media/typdgxmm/lazards-lcoeplus-april-2023.pdf , pg. 49 (Levelized Cost of Energy - Key Assumptions), accessed: 2023-12-14.",,2023.0 offwind,FOM,2.1655,%/year,"Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Fixed O&M [EUR/MW_e/y, 2020]",2020.0 offwind,VOM,0.0212,EUR/MWhel,RES costs made up to fix curtailment order, from old pypsa cost assumptions,2015.0 -offwind,investment,1523.9311,"EUR/kW_e, 2020","Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Nominal investment [MEUR/MW_e, 2020] grid connection costs substracted from investment costs",2020.0 +offwind,investment,1523.9311,"EUR/kW_e, 2020","Danish Energy Agency, technology_data_for_el_and_dh.xlsx","21 Offshore turbines: Nominal investment [MEUR/MW_e, 2020] grid connection costs subtracted from investment costs",2020.0 offwind,lifetime,30.0,years,"Danish Energy Agency, technology_data_for_el_and_dh.xlsx",21 Offshore turbines: Technical lifetime [years],2020.0 offwind-ac-connection-submarine,investment,2841.3251,EUR/MW/km,DEA https://ens.dk/en/our-services/projections-and-models/technology-data, from old pypsa cost assumptions,2015.0 offwind-ac-connection-underground,investment,1420.1334,EUR/MW/km,DEA https://ens.dk/en/our-services/projections-and-models/technology-data, from old pypsa cost assumptions,2015.0 From e9a40d69476351e6a07983c53e2bf83ca37fda9a Mon Sep 17 00:00:00 2001 From: Fabrizio Finozzi Date: Fri, 10 Jan 2025 15:57:07 +0100 Subject: [PATCH 14/15] change conf.py --- docs/conf.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 6a423d3..efecd06 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,6 +18,7 @@ import os import sys +import sphinx # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -335,4 +336,7 @@ # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {"https://docs.python.org/": None} +if sphinx.version_info[0] < 8: + intersphinx_mapping = {"http://docs.python.org/": None} +else: + intersphinx_mapping = {'python': ('https://docs.python.org/', None)} From 8da83cd81d2cac4d73cdb61f0e727f9195c5ecb1 Mon Sep 17 00:00:00 2001 From: Fabrizio Finozzi Date: Fri, 10 Jan 2025 15:58:00 +0100 Subject: [PATCH 15/15] change conf.py - 2 --- docs/conf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index efecd06..e984f40 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,6 +18,7 @@ import os import sys + import sphinx # If extensions (or modules to document with autodoc) are in another directory, @@ -339,4 +340,4 @@ if sphinx.version_info[0] < 8: intersphinx_mapping = {"http://docs.python.org/": None} else: - intersphinx_mapping = {'python': ('https://docs.python.org/', None)} + intersphinx_mapping = {"python": ("https://docs.python.org/", None)}