diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index bb1951c0..d8c04382 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ 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/.gitignore b/.gitignore index d103c00c..8db27659 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ - +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: GPL-3.0-only .snakemake _build diff --git a/.gitignore.save b/.gitignore.save index 7fe1b259..d81ba932 100644 --- a/.gitignore.save +++ b/.gitignore.save @@ -1,4 +1,6 @@ - +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: GPL-3.0-only .snakemake _build diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..57a88091 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,46 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: GPL-3.0-only +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,WEGE,Wege,Eletricity'] # 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 diff --git a/.readthedocs.yml b/.readthedocs.yml index e52632c7..7857e5c8 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,5 +1,12 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: GPL-3.0-only + version: 2 +sphinx: + configuration: docs/conf.py + build: os: ubuntu-22.04 tools: diff --git a/.syncignore b/.syncignore index def939b6..b7aeef40 100644 --- a/.syncignore +++ b/.syncignore @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: GPL-3.0-only + .snakemake .git .pytest_cache diff --git a/README.md b/README.md index 3bfde94d..855eb52d 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 1f75cdd3..94b36e70 100644 --- a/Snakefile +++ b/Snakefile @@ -1,32 +1,39 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: GPL-3.0-only + 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 +50,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 a73f541f..ee7f99b4 100644 --- a/config.yaml +++ b/config.yaml @@ -1,29 +1,33 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: GPL-3.0-only + 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 be901185..0ff5b067 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: GPL-3.0-only + # Makefile for Sphinx documentation # diff --git a/docs/addnew.rst b/docs/addnew.rst index 5f07627b..205299ba 100644 --- a/docs/addnew.rst +++ b/docs/addnew.rst @@ -1,3 +1,8 @@ +.. + SPDX-FileCopyrightText: Contributors to technology-data + + SPDX-License-Identifier: GPL-3.0-only + .. _addnew: ########################################## diff --git a/docs/conf.py b/docs/conf.py index df0fe1ca..8c539fec 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: GPL-3.0-only + # -*- coding: utf-8 -*- # # PyPSA documentation build configuration file, created by @@ -13,18 +17,19 @@ # serve to show the default. import os -import shlex 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 # 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 +37,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 +49,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 +78,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 +91,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 +131,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 +143,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 +175,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 +299,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 +312,32 @@ # (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} +if sphinx.version_info[0] < 8: + intersphinx_mapping = {"http://docs.python.org/": None} +else: + intersphinx_mapping = {"python": ("https://docs.python.org/", None)} diff --git a/docs/index.rst b/docs/index.rst index 12a89e49..af2eb6d6 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,5 +1,11 @@ +.. + SPDX-FileCopyrightText: Contributors to technology-data + + SPDX-License-Identifier: GPL-3.0-only + +#################### 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 7176ee3e..9f016782 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -1,3 +1,8 @@ +.. + SPDX-FileCopyrightText: Contributors to technology-data + + SPDX-License-Identifier: GPL-3.0-only + .. _installation: ########################################## diff --git a/docs/release_notes.rst b/docs/release_notes.rst index 34649f0f..5513edce 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -1,3 +1,9 @@ + +.. + SPDX-FileCopyrightText: Contributors to technology-data + + SPDX-License-Identifier: GPL-3.0-only + ########################################## Release Notes ########################################## @@ -27,6 +33,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) ====================================== diff --git a/docs/requirements.txt b/docs/requirements.txt index 9edc46ad..65d36d80 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,8 @@ -sphinx -sphinx-book-theme -sphinxcontrib-bibtex -myst-parser \ No newline at end of file +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: GPL-3.0-only + +sphinx==8.1.3 +sphinx-book-theme==1.1.3 +sphinxcontrib-bibtex==2.6.3 +myst-parser==4.0.0 \ No newline at end of file diff --git a/docs/structure.rst b/docs/structure.rst index f3606665..a97f602d 100644 --- a/docs/structure.rst +++ b/docs/structure.rst @@ -1,3 +1,8 @@ +.. + SPDX-FileCopyrightText: Contributors to technology-data + + SPDX-License-Identifier: GPL-3.0-only + .. _structure: ########################################## diff --git a/environment.yaml b/environment.yaml index ae4dede7..7ffdbb93 100644 --- a/environment.yaml +++ b/environment.yaml @@ -1,21 +1,29 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: GPL-3.0-only + 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 - - 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 779dfb1c..7c6b1d7f 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,137 @@ """ 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 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 +150,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 +171,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 328910f1..8b1929d7 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/outputs/costs_2020.csv b/outputs/costs_2020.csv index 150f2133..10845458 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 10db78ee..7268d567 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 79c426e4..ba2dcc43 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 58605705..9618c8dd 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 76087893..dd23f57f 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 d5c481e5..164897ea 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 1d9a8931..e6ff04a3 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 diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 00000000..6ffa5c34 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,53 @@ +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: GPL-3.0-only + +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 diff --git a/scripts/_helpers.py b/scripts/_helpers.py index 9279ce4e..9772ca2d 100644 --- a/scripts/_helpers.py +++ b/scripts/_helpers.py @@ -1,5 +1,9 @@ -from pathlib import Path +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: GPL-3.0-only + import re +from pathlib import Path class Dict(dict): @@ -7,17 +11,15 @@ 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): """ - 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 +180,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 c1a533ee..77a54e0b 100644 --- a/scripts/compile_cost_assumptions.py +++ b/scripts/compile_cost_assumptions.py @@ -1,7 +1,10 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: GPL-3.0-only + +# 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 @@ -25,247 +28,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 +291,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 +352,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 +525,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 +551,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 +580,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 +606,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 +620,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 +649,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 +715,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 +867,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 +1029,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 +1126,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 +1227,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 +1284,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 +1300,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 +1313,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 +1334,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 +1354,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 Input", "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 +1459,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 +1470,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]) @@ -1232,13 +1507,13 @@ 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' + 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 +1526,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 +1592,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 +1605,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 +1690,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 +1786,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 +1804,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 +1868,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 +2030,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 +2042,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 subtracted 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 + # 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 + ) + 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 Maintenance, 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 +2255,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 +2347,521 @@ 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 + 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 +2884,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 +2904,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 between 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 +2976,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 +3010,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 +3130,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 + # 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(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 +3159,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,78 +3245,88 @@ 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], - }]) - # not concat if df year is 2021 or 2030 (otherwhise duplicate) + 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 (otherwise duplicate) if data_year == 2021 or data_year == 2030: continue else: 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,12 +3345,12 @@ 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 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) @@ -2631,45 +3367,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) + + # add heavy-duty assumptions, cost year is 2022 + 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 +3418,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 +3469,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 +3479,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 +3488,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 +3516,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 4828195c..31b1ddbf 100755 --- a/scripts/convert_pdf_EWG_to_dataframe.py +++ b/scripts/convert_pdf_EWG_to_dataframe.py @@ -1,5 +1,8 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: GPL-3.0-only + +# 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 +10,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 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] +) 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 +64,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 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] 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 +117,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 3e2af73c..bf9fcc68 100644 --- a/scripts/convert_pdf_fraunhofer_to_dataframe.py +++ b/scripts/convert_pdf_fraunhofer_to_dataframe.py @@ -1,78 +1,95 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: GPL-3.0-only + +# 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) -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 (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 + 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") diff --git a/scripts/retrieve_data_from_dea.py b/scripts/retrieve_data_from_dea.py index 3ddf3b92..c1fccf49 100644 --- a/scripts/retrieve_data_from_dea.py +++ b/scripts/retrieve_data_from_dea.py @@ -1,5 +1,8 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- +# SPDX-FileCopyrightText: Contributors to technology-data +# +# SPDX-License-Identifier: GPL-3.0-only + +# coding: utf-8 """ Created on Mon May 4 18:48:11 2020 @@ -8,9 +11,10 @@ @author: bw0928 """ -import requests -import urllib.request import time +import urllib.request + +import requests from bs4 import BeautifulSoup # %% @@ -24,31 +28,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 +56,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) - - - - -