diff --git a/.flake8 b/.flake8 deleted file mode 100644 index d9472f59..00000000 --- a/.flake8 +++ /dev/null @@ -1,4 +0,0 @@ -[flake8] -exclude = meta/migrations/ -ignore = E265, W503 -max-line-length = 100 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..624ae6f2 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,44 @@ +name: "CodeQL" + +on: + push: + branches: + - dev + - master + - 'release/**' + pull_request: + branches: [ "dev" ] + schedule: + - cron: "23 2 * * 2" + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ python ] + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + queries: +security-and-quality + + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..3e5f7315 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,38 @@ +name: Black + +on: + push: + branches: + - master + - dev + - 'release/**' + pull_request: + +jobs: + run-linters: + name: Run linters + runs-on: ubuntu-latest + + steps: + - name: Check out Git repository + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: 3.9 + + - name: Install Python dependencies + run: pip install black flake8 + + - name: Run linters + uses: samuelmeuli/lint-action@v1 + with: + github_token: ${{ secrets.github_token }} + # Enable linters + black: true + flake8: false + # Mark the following line true if you want linters to attempt to autocorrect your code + auto_fix: false + git_name: "Greene Lab Linter" + git_email: "csgreene@upenn.edu" diff --git a/.github/workflows/packaging.yml b/.github/workflows/packaging.yml new file mode 100644 index 00000000..a09540cf --- /dev/null +++ b/.github/workflows/packaging.yml @@ -0,0 +1,40 @@ +name: packaging + +on: + # Make sure packaging process is not broken + push: + branches: + - master + - dev + - 'release/**' + pull_request: + # Make a package for release + release: + types: [published] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + max-parallel: 4 + matrix: + python-version: [3.9] + + steps: + - uses: actions/checkout@v1 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools setuptools_scm twine wheel build + - name: Create packages + run: python -m build . + - name: Run twine check + run: twine check dist/* + - uses: actions/upload-artifact@v4 + with: + name: tox-gh-actions-dist + path: dist diff --git a/.github/workflows/tox_checks.yml b/.github/workflows/tox_checks.yml new file mode 100644 index 00000000..5553b0ac --- /dev/null +++ b/.github/workflows/tox_checks.yml @@ -0,0 +1,57 @@ +# NB: this name is used in the status badge +name: tox checks + +on: + push: + branches: + - master + - dev + - 'release/**' + pull_request: + + workflow_dispatch: + schedule: + - cron: "0 5 * * 6" # 5:00 UTC every Saturday + +jobs: + lint: + name: ${{ matrix.toxenv }} + runs-on: ubuntu-latest + + strategy: + matrix: + toxenv: + - clean + - check + - docs + + steps: + - name: Update package list + run: sudo apt update + - name: Install LaTeX + run: sudo apt install dvipng rubber texlive-latex-extra + - name: Git clone + uses: actions/checkout@v2 + + - name: Set up Python ${{ env.default_python || '3.9' }} + uses: actions/setup-python@v5 + with: + python-version: "${{ env.default_python || '3.9' }}" + + - name: Pip cache + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ matrix.toxenv }}-${{ hashFiles('tox.ini', 'setup.py') }} + restore-keys: | + ${{ runner.os }}-pip-${{ matrix.toxenv }}- + ${{ runner.os }}-pip- + + - name: Install dependencies + run: | + python -m pip install -U pip + python -m pip install -U setuptools wheel build + python -m pip install -U tox + + - name: Run ${{ matrix.toxenv }} + run: python -m tox -e ${{ matrix.toxenv }} diff --git a/.github/workflows/tox_pytests.yml b/.github/workflows/tox_pytests.yml new file mode 100644 index 00000000..a28cb482 --- /dev/null +++ b/.github/workflows/tox_pytests.yml @@ -0,0 +1,44 @@ +name: tox pytests + +on: + push: + branches: + - master + - dev + - 'release/**' + pull_request: + + workflow_dispatch: + schedule: + - cron: "0 5 * * 6" # 5:00 UTC every Saturday + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.9, "3.10", "3.11"] + + steps: + - uses: actions/checkout@v1 + - name: Install cbc + run: sudo apt install coinor-cbc + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox tox-gh-actions coverage coveralls + - name: Test with tox + run: tox + + - name: Check test coverage + run: coverage report -m --fail-under=${{ matrix.vcs == 'bzr' && 84 || 85 }} + + - name: Report to coveralls + run: coveralls + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_SERVICE_NAME: github diff --git a/.gitignore b/.gitignore index 135b2adb..179b09a5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ __pycache__/ # Distribution / packaging .Python env/ +.venv/ +.vscode/ venv/ build/ develop-eggs/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4d8e2957..00000000 --- a/.travis.yml +++ /dev/null @@ -1,55 +0,0 @@ -language: python - -matrix: - include: - - python: 3.6 - - python: 3.7 - dist: xenial - - -# To install packages which are not on pip -#before_install: -# - sudo apt-get install ... -#addons: -# apt: -# update: true - -# command to install dependencies -install: - - pip install -r tests/test_requirements.txt - - pip install -e . - - pip install pytest-cov - - pip install coveralls - -# commands to run tests -script: - - flake8 - - pylint */*.py - - pytest --cov=src/oemof/thermal tests/ -#jobs: -# include: -# - stage: "Tests" # naming the Tests stage -# name: "Linting Tests" # names the first Tests stage job -# script: flake8 -# script: pylint -# for later stages -# - script: ./integration-tests -# name: "Integration Tests" # names the second Tests stage job -# - stage: deploy -# name: "Deploy to GCP" -# script: ./deploy - -# blocklist -#branches: -# except: -# - branch_name1 -# - branch_name2 - -# safelist -#branches: -# only: -# - branch_name1 -# - branch_name2 - -after_success: - - coveralls diff --git a/LICENSE b/LICENSE index 6a1dd2bb..88ee8e0f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 oemof developing group +Copyright (c) oemof developing group Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/VERSION b/VERSION deleted file mode 100644 index 14402fdf..00000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -__version__ = "0.0.7.dev" diff --git a/docs/_pics/README.rst b/docs/_pics/README.rst deleted file mode 100644 index 507d2531..00000000 --- a/docs/_pics/README.rst +++ /dev/null @@ -1,12 +0,0 @@ -Handling figures and images ----------------------------- - -Requirements towards formats of images have to be met with regard to chosen download: - -- **pdf**: png, jpeg, pdf -- **html**: please check your browser compatibility -- **epub**: png, jpeg, gif, svg - -In order to match the compatibility of every download source, png should be preferred. Please ensure a resolution of 300 - 600 dpi as a guide value. - -If your original image is a vector graphics (e.g. svg), it is advised to upload the original file to keep it possible to make adaptions at a later point in time. diff --git a/docs/compression_heat_pumps_and_chillers.rst b/docs/compression_heat_pumps_and_chillers.rst index 5d466511..ea9e1a06 100644 --- a/docs/compression_heat_pumps_and_chillers.rst +++ b/docs/compression_heat_pumps_and_chillers.rst @@ -12,7 +12,7 @@ _____ This module was developed to provide COP calculations based on temperatures for energy system optimizations with oemof.solph. -A time series of pre-calculated COPs can be used as input for a transformer +A time series of pre-calculated COPs can be used as input for a Converter (an oemof.solph component) in an energy system optimization. Discover more possibilities to use this module with our examples: https://github.com/oemof/oemof-thermal/tree/dev/examples @@ -166,7 +166,7 @@ Do NOT use this function to determine the input for `calc_cops()`! References __________ -.. [1] VDE ETG Energietechnik, VDE-Studie "Potenziale für Strom im Wärmemarkt bis 2050 - Wärmeversorgung in flexiblen Energieversorgungssystemen mit hohen Anteilen an erneuerbaren Energien". 2015. (http://www.energiedialog2050.de/BASE/DOWNLOADS/VDE_ST_ETG_Warmemarkt_RZ-web.pdf) +.. [1] VDE ETG Energietechnik, VDE-Studie "Potenziale für Strom im Wärmemarkt bis 2050 - Wärmeversorgung in flexiblen Energieversorgungssystemen mit hohen Anteilen an erneuerbaren Energien". 2015. .. [2] C. Arpagaus, Hochtemperatur-Wärmepumpen - Marktübersicht, Stand der Technik und Anwendungsbeispiele. Berlin, Offenbach: VDE-Verlag, 2019. diff --git a/docs/concentrating_solar_power.rst b/docs/concentrating_solar_power.rst index 8557963c..28eba0b3 100644 --- a/docs/concentrating_solar_power.rst +++ b/docs/concentrating_solar_power.rst @@ -16,8 +16,8 @@ system optimizations with oemof.solph. In https://github.com/oemof/oemof-thermal/tree/dev/examples you can find an example on how to use the modul to calculate a CSP power plant. -A time series of pre-calculated heat flows can be used as input for a source -(an oemof.solph component), and a transformer (an oemof.solph component) can be +A time series of pre-calculated heat flows can be used as input for a Source +(an oemof.solph component), and a Converter (an oemof.solph component) can be used to hold electrical power consumption and further thermal losses of the collector in an energy system optimization. In addition, you will find an example which compares this precalculation with @@ -175,7 +175,7 @@ calculated with a fix efficiency. The results of this precalculation can be used in an oemof energy system model as output of a source component. To model the behaviour of a collector, it can be -complemented with a transformer, which holds the electrical consumption of pumps +complemented with a Converter, which holds the electrical consumption of pumps and peripheral heat losses (see the the example csp_plant_collector.py). ParabolicTroughCollector facade @@ -193,7 +193,7 @@ class of the facade module for all parameters which have to be provided. See example_csp_facade.py for an application example. It models the same system as the csp_plant_example.py, but uses the ParabolicTroughCollector facade -instead of separate source and transformer. +instead of separate Source and Converter. .. code-block:: python diff --git a/docs/conf.py b/docs/conf.py index ae363705..0dfaf096 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,12 +20,12 @@ # 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('..')) +sys.path.insert(0, os.path.abspath("..")) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' rst_prolog = """ .. role:: py(code) @@ -38,119 +38,118 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.autosummary', - 'sphinx.ext.viewcode', - 'sphinx.ext.imgmath', - 'sphinx.ext.napoleon' + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinx.ext.viewcode", + "sphinx.ext.imgmath", + "sphinx.ext.napoleon", ] numpydoc_show_class_members = False # -autoclass_content = 'both' +autoclass_content = "both" # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -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" # General information about the project. -project = u'oemof.thermal' -copyright = u'2014-2019, oemof developer group' -author = u'oemof developer group' +project = "oemof.thermal" +copyright = "2014-2019, oemof developer group" +author = "oemof developer group" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '' +version = "" # The full version, including alpha/beta/rc tags. -release = '' +release = "" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # 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', 'whatsnew/*'] +exclude_patterns = ["_build", "whatsnew/*"] # 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 # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -#html_theme = 'bizstyle' +# html_theme = 'bizstyle' html_theme = "sphinx_rtd_theme" -html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # 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 # documentation. -#html_theme_options = { -# "sidebarwidth": "25em", -# "documentwidth":"50em", -# "pagewidth": "75em", -# } +# html_theme_options = { +# "sidebarwidth": "25em", +# "documentwidth":"50em", +# "pagewidth": "75em", +# } # 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, @@ -160,51 +159,51 @@ # 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 # Output file base name for HTML help builder. -htmlhelp_basename = 'oemof_doc' +htmlhelp_basename = "oemof_doc" # -- Options for LaTeX output --------------------------------------------- @@ -212,10 +211,8 @@ 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': '', } @@ -224,29 +221,34 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'oemof_thermal.tex', u'oemof.thermal documentation', - u'oemof developer group', 'manual'), + ( + "index", + "oemof_thermal.tex", + "oemof.thermal documentation", + "oemof developer group", + "manual", + ), ] # 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 --------------------------------------- @@ -254,12 +256,17 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'oemof', u'oemof.thermal documentation', - [u'oemof developer group'], 1) + ( + "index", + "oemof", + "oemof.thermal documentation", + ["oemof developer group"], + 1, + ) ] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------- @@ -268,89 +275,95 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'oemof.thermal', u'oemof.thermal documentation', - u'Author', 'oemof developer group', 'One line description of project.', - 'Miscellaneous'), + ( + "index", + "oemof.thermal", + "oemof.thermal documentation", + "Author", + "oemof developer group", + "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 # -- Options for Epub output ---------------------------------------------- # Bibliographic Dublin Core info. -epub_title = u'oemof.thermal' -epub_author = u'oemof developer group' -epub_publisher = u'oemof developer group' -epub_copyright = u'2019, oemof developer group' +epub_title = "oemof.thermal" +epub_author = "oemof developer group" +epub_publisher = "oemof developer group" +epub_copyright = "2019, oemof developer group" # The basename for the epub file. It defaults to the project name. -#epub_basename = u'pahesmf' +# epub_basename = u'pahesmf' # The HTML theme for the epub output. Since the default themes are not optimized # for small screen space, using the same theme for HTML and epub output is # usually not wise. This defaults to 'epub', a theme designed to save visual # space. -#epub_theme = 'epub' +# epub_theme = 'epub' # The language of the text. It defaults to the language option # or en if the language is not set. -#epub_language = '' +# epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. -#epub_scheme = '' +# epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. -#epub_identifier = '' +# epub_identifier = '' # A unique identification for the text. -#epub_uid = '' +# epub_uid = '' # A tuple containing the cover image and cover page html template filenames. -#epub_cover = () +# epub_cover = () # A sequence of (type, uri, title) tuples for the guide element of content.opf. -#epub_guide = () +# epub_guide = () # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. -#epub_pre_files = [] +# epub_pre_files = [] # HTML files shat should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. -#epub_post_files = [] +# epub_post_files = [] # A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] +epub_exclude_files = ["search.html"] # The depth of the table of contents in toc.ncx. -#epub_tocdepth = 3 +# epub_tocdepth = 3 # Allow duplicate toc entries. -#epub_tocdup = True +# epub_tocdup = True # Choose between 'default' and 'includehidden'. -#epub_tocscope = 'default' +# epub_tocscope = 'default' # Fix unsupported image types using the PIL. -#epub_fix_images = False +# epub_fix_images = False # Scale large images. -#epub_max_image_width = 0 +# epub_max_image_width = 0 # How to display URL addresses: 'footnote', 'no', or 'inline'. -#epub_show_urls = 'inline' +# epub_show_urls = 'inline' # If false, no index is generated. -#epub_use_index = True +# epub_use_index = True diff --git a/docs/examples.rst b/docs/examples.rst index 3e9c07c0..afedd88e 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -23,7 +23,7 @@ __________________________ An example provides an "how to" on the use of the 'calc_cops' function to get the coefficients of performance (COP) of an exemplary air-source heat pump (ASHP). It also shows how to use the -pre-calculated COPs in a solph.Transformer. +pre-calculated COPs in a solph.Converter. Furthermore, the maximal possible heat output of the heat pump is pre-calculated and varies with the temperature levels of the heat reservoirs. In the example the ambient air is used as low temperature heat reservoir. @@ -32,7 +32,7 @@ In addition to that, the example provides a manual on using the 'calc_cops' func to get the COPs of a heat pump, by plotting the temperature dependency of the COP, and COPs of an exemplary ground-source heat pump (GSHP) using the soil temperature as low temperature heat reservoir. -The Examples can be found `here `_. +The Examples can be found `at compression_heatpumps_and_chiller `_. **Absorption Chiller** @@ -40,7 +40,7 @@ The first example shows the behaviour of the coefficient of performance and heat for different cooling water temperatures based on the characteristic equation method. The second example underlines the dependence of the temperature of the cooling water on the cooling capacity. -The Examples can be found `here `_. +The Examples can be found `at absorption_heatpumps_and_chiller `_. **Concentrating solar power (CSP)** @@ -50,7 +50,7 @@ The collector's efficiency and irradiance can be calculated with two different l An application is presented which models a csp plant to meet an electrical demand. The plant itself consists of a parabolic trough collector field, a turbine, and a storage. -The Examples can be found `here `_. +The Examples can be found `at concentrating_solar_power `_. **Solar thermal collector** @@ -58,7 +58,7 @@ In these examples the functionality of the solar thermal collector is shown. Once with a fixed collector size (aperture area), once with a fixed collector size using the facade and another time with a collector size to be invested. It also provides plots which can be called by the flat_plate_collector_example.py. -The Examples can be found `here `_. +The Examples can be found `at solar_thermal_collector `_. **Stratified thermal storage** @@ -68,12 +68,12 @@ to specify a storage in a model that optimizes operation with oemof-solph. Furth Furthermore the examples show how to invest into nominal_storage_capacity and capacity (charging/discharging power) with a fixed ratio and independently with no fixed ratio. -The Examples can be found `here `_. +The Examples can be found `at stratified_thermal_storage `_. **Cogeneration** We further provide an example on different emission allocation methods in cogeneration. -This Example can be found `here `_. +This Example can be found `at cogeneration `_. List of available models diff --git a/docs/getting_started.rst b/docs/getting_started.rst index 4a52c41d..8ec41f2c 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -87,5 +87,5 @@ enhancements. Or you can contribute a new approach that helps to model thermal e systems. If you want to contribute, fork the project at github, develop your features on a new branch and finally open a pull request to merge your contribution to oemof.thermal. -As oemof.thermal is part of the oemof developer group we use the same developer rules, described -`here `_. +As oemof.thermal is part of the oemof developer group we use the same developer rules, +described in the `oemof documentation `_. diff --git a/docs/requirements.txt b/docs/requirements.txt index d6e1198b..46ae74e9 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1 +1,3 @@ -e . +sphinx>=1.3 +sphinx-rtd-theme diff --git a/docs/solar_thermal_collector.rst b/docs/solar_thermal_collector.rst index 19ec2a10..9bd68988 100644 --- a/docs/solar_thermal_collector.rst +++ b/docs/solar_thermal_collector.rst @@ -17,8 +17,8 @@ In https://github.com/oemof/oemof-thermal/tree/dev/examples you can find an example, how to use the modul to calculate a system with flat plate collector, storage and backup to provide a given heat demand. -The time series of the pre-calculated heat is output of a source (an oemof.solph -component) representing the collector, and a transformer (an oemof.solph component) +The time series of the pre-calculated heat is output of a Source (an oemof.solph +component) representing the collector, and a Converter (an oemof.solph component) is used to hold electrical power consumption and further thermal losses of the collector in an energy system optimization. In addition, you will find a plot, which compares this precalculation with a calculation with a constant efficiency. @@ -138,7 +138,7 @@ function in comparison to the heat calculated with a fix efficiency. The results of this precalculation can be used in an oemof energy system model as output of a source component. To model the behaviour of a collector, it can be -complemented with a transformer, which holds the electrical consumption of pumps +complemented with a Converter, which holds the electrical consumption of pumps and peripheral heat losses (see the the examples flat_plate_collector_example.py and flat_plate_collector_example_investment.py). @@ -157,7 +157,7 @@ class of the facade module for all parameters which have to be provided. See flat_plate_collector_example_facade.py for an application example. It models the same system as the flat_plate_collector_example.py, but uses the SolarThermalCollector facade -instead of separate source and transformer. +instead of separate Source and Converter. .. code-block:: python diff --git a/docs/validation_compression_heat_pumps_and_chillers.rst b/docs/validation_compression_heat_pumps_and_chillers.rst index fa570b3e..4f05a6b2 100644 --- a/docs/validation_compression_heat_pumps_and_chillers.rst +++ b/docs/validation_compression_heat_pumps_and_chillers.rst @@ -11,7 +11,7 @@ The validation of the compression heat pump and chiller has been conducted withi `_. Monitored data of the two components in combination with PV without storage has been provided by Universidad Politécnica de Madrid (UPM). An open access publication containing further description of the data and experiments done -in [2] is planned for November 2020. +in [2]_ is planned for November 2020. Both, heat pump and chiller, are working with air to air technology. The set of data contains amongst others external and internal temperatures of the components and a calculated Coefficient of performance (COP) / Energy Efficiency Ratio (EER) value. The code used for the validation can @@ -54,7 +54,7 @@ temperature hub as well as the relation between residuals and monitored coeffici Results of the chiller ______________________ -Typical EER of chillers used for cooling are around 4 to 5 [1]. By adhering to these reference values we conclude +Typical EER of chillers used for cooling are around 4 to 5 [1]_. By adhering to these reference values we conclude that EERs with quality grades ranging from 0.25 to 0.4 give fitting results. The RMSE for the validated quality grades presents the standard deviation of the residuals. Among the range of quality diff --git a/docs/validation_concentrating_solar_power.rst b/docs/validation_concentrating_solar_power.rst deleted file mode 100644 index 277b4cf0..00000000 --- a/docs/validation_concentrating_solar_power.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. _validation_csp_label: - -~~~~~~~~~~~~~~~~~~~~~~~~~ -Concentrating solar power -~~~~~~~~~~~~~~~~~~~~~~~~~ - diff --git a/docs/validation_solar_thermal_collector.rst b/docs/validation_solar_thermal_collector.rst deleted file mode 100644 index b153a1a6..00000000 --- a/docs/validation_solar_thermal_collector.rst +++ /dev/null @@ -1,8 +0,0 @@ -.. _validation_solar_thermal_collector_label: - -~~~~~~~~~~~~~~~~~~~~~~~ -Solar thermal collector -~~~~~~~~~~~~~~~~~~~~~~~ - - - diff --git a/docs/whats_new/v0-0-4.rst b/docs/whats_new/v0-0-4.rst index 40c8322a..8ff88a00 100644 --- a/docs/whats_new/v0-0-4.rst +++ b/docs/whats_new/v0-0-4.rst @@ -1,5 +1,5 @@ v0.0.4 (October 14, 2020) -============= +========================= New features ------------ diff --git a/docs/whats_new/v0-0-7.rst b/docs/whats_new/v0-0-7.rst index cca984b7..9f474fe0 100644 --- a/docs/whats_new/v0-0-7.rst +++ b/docs/whats_new/v0-0-7.rst @@ -1,5 +1,5 @@ v0.0.7.dev (???) -============= +================ API Changes ----------- diff --git a/examples/absorption_heatpump_and_chiller/absorption_chiller.py b/examples/absorption_heatpump_and_chiller/absorption_chiller.py index 89b68867..01ceb14d 100644 --- a/examples/absorption_heatpump_and_chiller/absorption_chiller.py +++ b/examples/absorption_heatpump_and_chiller/absorption_chiller.py @@ -1,29 +1,34 @@ - # import absorption_heatpumps_and_chillers as abs_hp_chiller -import oemof.thermal.absorption_heatpumps_and_chillers as abs_hp_chiller -import oemof.solph as solph -import pandas as pd import os + import matplotlib.pyplot as plt +import oemof.solph as solph +import pandas as pd + +import oemof.thermal.absorption_heatpumps_and_chillers as abs_hp_chiller + def absorption_chiller_example(): - solver = 'cbc' - debug = False + solver = "cbc" number_of_time_steps = 48 solver_verbose = True - date_time_index = pd.date_range('1/1/2012', periods=number_of_time_steps, - freq='H') + date_time_index = pd.date_range( + "1/1/2012", periods=number_of_time_steps, freq="H" + ) energysystem = solph.EnergySystem(timeindex=date_time_index) # Read data file - filename_data = os.path.join(os.path.dirname(__file__), 'data/AC_example.csv') + filename_data = os.path.join( + os.path.dirname(__file__), "data/AC_example.csv" + ) data = pd.read_csv(filename_data) - filename_charpara = os.path.join(os.path.dirname(__file__), - 'data/characteristic_parameters.csv') + filename_charpara = os.path.join( + os.path.dirname(__file__), "data/characteristic_parameters.csv" + ) charpara = pd.read_csv(filename_charpara) - chiller_name = 'Kuehn' + chiller_name = "Kuehn" # Buses with three different temperature levels b_th_high = solph.Bus(label="hot") @@ -31,22 +36,31 @@ def absorption_chiller_example(): b_th_low = solph.Bus(label="chilled") energysystem.add(b_th_high, b_th_low) - energysystem.add(solph.components.Source( - label='driving_heat', - outputs={b_th_high: solph.Flow(variable_costs=0)})) - energysystem.add(solph.components.Source( - label='cooling_shortage', - outputs={b_th_low: solph.Flow(variable_costs=20)})) + energysystem.add( + solph.components.Source( + label="driving_heat", + outputs={b_th_high: solph.Flow(variable_costs=0)}, + ) + ) + energysystem.add( + solph.components.Source( + label="cooling_shortage", + outputs={b_th_low: solph.Flow(variable_costs=20)}, + ) + ) # energysystem.add(solph.components.Sink( # label='dry_cooling_tower', # inputs={b_th_medium: solph.Flow(variable_costs=0)})) - energysystem.add(solph.components.Sink( - label='cooling_demand', - inputs={b_th_low: solph.Flow(fix=1, nominal_value=35)})) + energysystem.add( + solph.components.Sink( + label="cooling_demand", + inputs={b_th_low: solph.Flow(fix=1, nominal_value=35)}, + ) + ) # Mean cooling water temperature in degC (dry cooling tower) temp_difference = 4 - t_cooling = [t + temp_difference for t in data['air_temperature']] + t_cooling = [t + temp_difference for t in data["air_temperature"]] n = len(t_cooling) # Pre-Calculations @@ -54,42 +68,51 @@ def absorption_chiller_example(): t_hot=[85], t_cool=t_cooling, t_chill=[15] * n, - coef_a=charpara[(charpara['name'] == chiller_name)]['a'].values[0], - coef_e=charpara[(charpara['name'] == chiller_name)]['e'].values[0], - method='kuehn_and_ziegler') + coef_a=charpara[(charpara["name"] == chiller_name)]["a"].values[0], + coef_e=charpara[(charpara["name"] == chiller_name)]["e"].values[0], + method="kuehn_and_ziegler", + ) Q_dots_evap = abs_hp_chiller.calc_heat_flux( ddts=ddt, - coef_s=charpara[(charpara['name'] == chiller_name)]['s_E'].values[0], - coef_r=charpara[(charpara['name'] == chiller_name)]['r_E'].values[0], - method='kuehn_and_ziegler') + coef_s=charpara[(charpara["name"] == chiller_name)]["s_E"].values[0], + coef_r=charpara[(charpara["name"] == chiller_name)]["r_E"].values[0], + method="kuehn_and_ziegler", + ) Q_dots_gen = abs_hp_chiller.calc_heat_flux( ddts=ddt, - coef_s=charpara[(charpara['name'] == chiller_name)]['s_G'].values[0], - coef_r=charpara[(charpara['name'] == chiller_name)]['r_G'].values[0], - method='kuehn_and_ziegler') + coef_s=charpara[(charpara["name"] == chiller_name)]["s_G"].values[0], + coef_r=charpara[(charpara["name"] == chiller_name)]["r_G"].values[0], + method="kuehn_and_ziegler", + ) COPs = [Qevap / Qgen for Qgen, Qevap in zip(Q_dots_gen, Q_dots_evap)] nominal_Q_dots_evap = 10 actual_value = [Q_e / nominal_Q_dots_evap for Q_e in Q_dots_evap] # Absorption Chiller - energysystem.add(solph.components.Transformer( - label="AC", - inputs={b_th_high: solph.Flow()}, - outputs={b_th_low: solph.Flow(nominal_value=nominal_Q_dots_evap, - max=actual_value, - variable_costs=5)}, - conversion_factors={b_th_low: COPs})) + energysystem.add( + solph.components.Converter( + label="AC", + inputs={b_th_high: solph.Flow()}, + outputs={ + b_th_low: solph.Flow( + nominal_value=nominal_Q_dots_evap, + max=actual_value, + variable_costs=5, + ) + }, + conversion_factors={b_th_low: COPs}, + ) + ) model = solph.Model(energysystem) - model.solve(solver=solver, solve_kwargs={'tee': solver_verbose}) + model.solve(solver=solver, solve_kwargs={"tee": solver_verbose}) - energysystem.results['main'] = solph.processing.results(model) - energysystem.results['meta'] = solph.processing.meta_results(model) + energysystem.results["main"] = solph.processing.results(model) + energysystem.results["meta"] = solph.processing.meta_results(model) energysystem.dump(dpath=None, filename=None) - # **************************************************************************** # ********** PART 2 - Processing the results ********************************* # **************************************************************************** @@ -97,44 +120,42 @@ def absorption_chiller_example(): energysystem = solph.EnergySystem() energysystem.restore(dpath=None, filename=None) - results = energysystem.results['main'] + results = energysystem.results["main"] - high_temp_bus = solph.views.node(results, 'hot') - low_temp_bus = solph.views.node(results, 'chilled') + high_temp_bus = solph.views.node(results, "hot") + low_temp_bus = solph.views.node(results, "chilled") string_results = solph.views.convert_keys_to_strings( - energysystem.results['main']) - AC_output = string_results[ - 'AC', 'chilled']['sequences'].values - demand_cooling = string_results[ - 'chilled', 'cooling_demand']['sequences'].values - ASHP_input = string_results[ - 'hot', 'AC']['sequences'].values - - - fig2, axs = plt.subplots(3, 1, figsize=(8, 5), sharex=True) - axs[0].plot(AC_output, label='cooling output') - axs[0].plot(demand_cooling, linestyle='--', label='cooling demand') - axs[1].plot(COPs, linestyle='-.') - axs[2].plot(data['air_temperature']) - axs[0].set_title('Cooling capacity and demand') - axs[1].set_title('Coefficient of Performance') - axs[2].set_title('Air Temperature') + energysystem.results["main"] + ) + AC_output = string_results["AC", "chilled"]["sequences"].values + demand_cooling = string_results["chilled", "cooling_demand"][ + "sequences" + ].values + + _, axs = plt.subplots(3, 1, figsize=(8, 5), sharex=True) + axs[0].plot(AC_output, label="cooling output") + axs[0].plot(demand_cooling, linestyle="--", label="cooling demand") + axs[1].plot(COPs, linestyle="-.") + axs[2].plot(data["air_temperature"]) + axs[0].set_title("Cooling capacity and demand") + axs[1].set_title("Coefficient of Performance") + axs[2].set_title("Air Temperature") axs[0].legend() axs[0].grid() axs[1].grid() axs[2].grid() - axs[0].set_ylabel('Cooling capacity in kW') - axs[1].set_ylabel('COP') - axs[2].set_ylabel('Temperature in $°$C') - axs[2].set_xlabel('Time in h') + axs[0].set_ylabel("Cooling capacity in kW") + axs[1].set_ylabel("COP") + axs[2].set_ylabel("Temperature in $°$C") + axs[2].set_xlabel("Time in h") plt.tight_layout() plt.show() - print('********* Main results *********') - print(high_temp_bus['sequences'].sum(axis=0)) - print(low_temp_bus['sequences'].sum(axis=0)) + print("********* Main results *********") + print(high_temp_bus["sequences"].sum(axis=0)) + print(low_temp_bus["sequences"].sum(axis=0)) if __name__ == "__main__": diff --git a/examples/absorption_heatpump_and_chiller/cooling_cap_dependence_on_cooling_water_temp.py b/examples/absorption_heatpump_and_chiller/cooling_cap_dependence_on_cooling_water_temp.py index 11e76dda..bece0f81 100644 --- a/examples/absorption_heatpump_and_chiller/cooling_cap_dependence_on_cooling_water_temp.py +++ b/examples/absorption_heatpump_and_chiller/cooling_cap_dependence_on_cooling_water_temp.py @@ -1,15 +1,17 @@ +import os -import oemof.thermal.absorption_heatpumps_and_chillers as abs_hp_chiller import matplotlib.pyplot as plt -import os import pandas as pd +import oemof.thermal.absorption_heatpumps_and_chillers as abs_hp_chiller + def cooling_cap_example(): - filename = os.path.join(os.path.dirname(__file__), - 'data/characteristic_parameters.csv') + filename = os.path.join( + os.path.dirname(__file__), "data/characteristic_parameters.csv" + ) charpara = pd.read_csv(filename) - chiller_name = 'Kuehn' + chiller_name = "Kuehn" t_cooling = [23, 25, 27, 29, 31, 33, 35, 36, 37, 38, 39, 40] @@ -17,67 +19,79 @@ def cooling_cap_example(): t_hot=[75], t_cool=t_cooling, t_chill=[15], - coef_a=charpara[(charpara['name'] == chiller_name)]['a'].values[0], - coef_e=charpara[(charpara['name'] == chiller_name)]['e'].values[0], - method='kuehn_and_ziegler') + coef_a=charpara[(charpara["name"] == chiller_name)]["a"].values[0], + coef_e=charpara[(charpara["name"] == chiller_name)]["e"].values[0], + method="kuehn_and_ziegler", + ) Q_dots_evap_75 = abs_hp_chiller.calc_heat_flux( ddts=ddt_75, - coef_s=charpara[(charpara['name'] == chiller_name)]['s_E'].values[0], - coef_r=charpara[(charpara['name'] == chiller_name)]['r_E'].values[0], - method='kuehn_and_ziegler') + coef_s=charpara[(charpara["name"] == chiller_name)]["s_E"].values[0], + coef_r=charpara[(charpara["name"] == chiller_name)]["r_E"].values[0], + method="kuehn_and_ziegler", + ) Q_dots_gen_75 = abs_hp_chiller.calc_heat_flux( ddts=ddt_75, - coef_s=charpara[(charpara['name'] == chiller_name)]['s_G'].values[0], - coef_r=charpara[(charpara['name'] == chiller_name)]['r_G'].values[0], - method='kuehn_and_ziegler') - COPs_75 = [Qevap / Qgen for Qgen, Qevap in zip(Q_dots_gen_75, Q_dots_evap_75)] + coef_s=charpara[(charpara["name"] == chiller_name)]["s_G"].values[0], + coef_r=charpara[(charpara["name"] == chiller_name)]["r_G"].values[0], + method="kuehn_and_ziegler", + ) + COPs_75 = [ + Qevap / Qgen for Qgen, Qevap in zip(Q_dots_gen_75, Q_dots_evap_75) + ] ddt_80 = abs_hp_chiller.calc_characteristic_temp( t_hot=[80], t_cool=t_cooling, t_chill=[15], - coef_a=charpara[(charpara['name'] == chiller_name)]['a'].values[0], - coef_e=charpara[(charpara['name'] == chiller_name)]['e'].values[0], - method='kuehn_and_ziegler') + coef_a=charpara[(charpara["name"] == chiller_name)]["a"].values[0], + coef_e=charpara[(charpara["name"] == chiller_name)]["e"].values[0], + method="kuehn_and_ziegler", + ) Q_dots_evap_80 = abs_hp_chiller.calc_heat_flux( ddts=ddt_80, - coef_s=charpara[(charpara['name'] == chiller_name)]['s_E'].values[0], - coef_r=charpara[(charpara['name'] == chiller_name)]['r_E'].values[0], - method='kuehn_and_ziegler') - + coef_s=charpara[(charpara["name"] == chiller_name)]["s_E"].values[0], + coef_r=charpara[(charpara["name"] == chiller_name)]["r_E"].values[0], + method="kuehn_and_ziegler", + ) fig1 = plt.figure(figsize=(8, 6)) fig1.set_size_inches(8, 6, forward=True) ax1 = fig1.add_subplot(111) - ax1.grid(axis='y') + ax1.grid(axis="y") - line1 = ax1.plot(t_cooling, - Q_dots_evap_80, - linestyle='dotted', - marker='d', - color='black', - label='Cooling capacity ($80°$C driving heat)') - line2 = ax1.plot(t_cooling, - Q_dots_evap_75, - linestyle='dashed', - marker='d', - color='black', - label='Cooling capacity ($75°$C driving heat)') - plt.ylabel('Cooling capacity in kW') + ax1.plot( + t_cooling, + Q_dots_evap_80, + linestyle="dotted", + marker="d", + color="black", + label="Cooling capacity ($80°$C driving heat)", + ) + ax1.plot( + t_cooling, + Q_dots_evap_75, + linestyle="dashed", + marker="d", + color="black", + label="Cooling capacity ($75°$C driving heat)", + ) + plt.ylabel("Cooling capacity in kW") ax2 = fig1.add_subplot(111, sharex=ax1, frameon=False) - line3 = ax2.plot(t_cooling, - COPs_75, - linestyle='-', - marker='o', - color='black', - label='COP ($75°$C driving heat)') + ax2.plot( + t_cooling, + COPs_75, + linestyle="-", + marker="o", + color="black", + label="COP ($75°$C driving heat)", + ) ax2.yaxis.tick_right() - ax2.yaxis.set_label_position('right') - plt.ylabel('COP') - plt.xlabel('Cooling water temperature in $°$C') - plt.title('Chiller performance at varying cooling water temperatures') - ax2.legend(loc='upper right') - ax1.legend(loc='lower left') + ax2.yaxis.set_label_position("right") + plt.ylabel("COP") + plt.xlabel("Cooling water temperature in $°$C") + plt.title("Chiller performance at varying cooling water temperatures") + ax2.legend(loc="upper right") + ax1.legend(loc="lower left") ax2.set_ylim(0.4, 0.8) ax1.set_ylim(0, 24) diff --git a/examples/check_examples.py b/examples/check_examples.py index 13dc1bcc..0c3d49d5 100644 --- a/examples/check_examples.py +++ b/examples/check_examples.py @@ -1,31 +1,60 @@ - -from absorption_heatpump_and_chiller.absorption_chiller import absorption_chiller_exaple -from absorption_heatpump_and_chiller.cooling_cap_dependence_on_cooling_water_temp import cooling_cap_example - -from cogeneration.emission_allocation_methods import emission_allocation_example - -from compression_heatpump_and_chiller.airsource_heatpump_const_max_output import airource_hp_const_example -from compression_heatpump_and_chiller.airsource_heatpump_variable_max_output import airource_hp_variable_example -from compression_heatpump_and_chiller.chiller_cop import chiller_cop_const_example -from compression_heatpump_and_chiller.chiller_cop_as_TimeSeries import chiller_cop_timeseries_example -from compression_heatpump_and_chiller.groundsource_heatpump import groundsource_hp_example - +from absorption_heatpump_and_chiller.absorption_chiller import ( + absorption_chiller_exaple, +) +from absorption_heatpump_and_chiller.cooling_cap_dependence_on_cooling_water_temp import ( + cooling_cap_example, +) +from cogeneration.emission_allocation_methods import ( + emission_allocation_example, +) +from compression_heatpump_and_chiller.airsource_heatpump_const_max_output import ( + airource_hp_const_example, +) +from compression_heatpump_and_chiller.airsource_heatpump_variable_max_output import ( + airource_hp_variable_example, +) +from compression_heatpump_and_chiller.chiller_cop import ( + chiller_cop_const_example, +) +from compression_heatpump_and_chiller.chiller_cop_as_TimeSeries import ( + chiller_cop_timeseries_example, +) +from compression_heatpump_and_chiller.groundsource_heatpump import ( + groundsource_hp_example, +) from concentrating_solar_power.csp_collector_plot import csp_collector_example -from concentrating_solar_power.csp_collector_plot_andasol import csp_andasol_example +from concentrating_solar_power.csp_collector_plot_andasol import ( + csp_andasol_example, +) from concentrating_solar_power.csp_facade import csp_facade_example from concentrating_solar_power.csp_plant import csp_plant_example - -from solar_thermal_collector.flat_plate_collector import flat_plate_collector_example -from solar_thermal_collector.flat_plate_collector_facade import flat_plate_collector_facade_example -from solar_thermal_collector.flat_plate_collector_investment import flat_plate_collector_investment_example - -from stratified_thermal_storage.operation_generic_storage import operation_example -from stratified_thermal_storage.operation_facade import operation_facade_example -from stratified_thermal_storage.investment_fixed_ratio_facade import fixed_ratio_invest_facade_example -from stratified_thermal_storage.investment_fixed_ratio_generic_storage import fixed_ratio_invest_example -from stratified_thermal_storage.investment_independent_facade import invest_independent_facade_example -from stratified_thermal_storage.investment_independent_generic_storage import invest_independent_example - +from solar_thermal_collector.flat_plate_collector import ( + flat_plate_collector_example, +) +from solar_thermal_collector.flat_plate_collector_facade import ( + flat_plate_collector_facade_example, +) +from solar_thermal_collector.flat_plate_collector_investment import ( + flat_plate_collector_investment_example, +) +from stratified_thermal_storage.investment_fixed_ratio_facade import ( + fixed_ratio_invest_facade_example, +) +from stratified_thermal_storage.investment_fixed_ratio_generic_storage import ( + fixed_ratio_invest_example, +) +from stratified_thermal_storage.investment_independent_facade import ( + invest_independent_facade_example, +) +from stratified_thermal_storage.investment_independent_generic_storage import ( + invest_independent_example, +) +from stratified_thermal_storage.operation_facade import ( + operation_facade_example, +) +from stratified_thermal_storage.operation_generic_storage import ( + operation_example, +) # absorption_chiller_example() cooling_cap_example() diff --git a/examples/cogeneration/emission_allocation_methods.py b/examples/cogeneration/emission_allocation_methods.py index 1e1d55a2..3f7fd332 100644 --- a/examples/cogeneration/emission_allocation_methods.py +++ b/examples/cogeneration/emission_allocation_methods.py @@ -5,45 +5,47 @@ """ -import pandas as pd import matplotlib.pyplot as plt +import pandas as pd from oemof.thermal.cogeneration import allocate_emissions + def emission_allocation_example(): emissions_dict = {} - for method in ['iea', 'efficiency', 'finnish']: + for method in ["iea", "efficiency", "finnish"]: emissions_dict[method] = allocate_emissions( total_emissions=200, # Arbitrary units. Assume [kgCO2]. eta_el=0.3, eta_th=0.5, method=method, eta_el_ref=0.525, - eta_th_ref=0.82 + eta_th_ref=0.82, ) - df = pd.DataFrame(emissions_dict, index=['el', 'th']).T + df = pd.DataFrame(emissions_dict, index=["el", "th"]).T print(df) # Example plot fig, ax = plt.subplots() - df.loc[:, 'el'] *= -1 - bars = df.plot.barh(stacked=True, ax=ax) + df.loc[:, "el"] *= -1 + df.plot.barh(stacked=True, ax=ax) - for i, (el, th) in enumerate(zip(df['el'], df['th'])): - ax.text(el, i, round(-el), ha='left') - ax.text(th, i, round(th), ha='right') + for i, (el, th) in enumerate(zip(df["el"], df["th"])): + ax.text(el, i, round(-el), ha="left") + ax.text(th, i, round(th), ha="right") ax.axvline(0) - ax.set_ylabel('Method') - ax.set_xlabel('Allocated emissions [kgCO2]') - plt.title('allocated to electricity', loc='left') - plt.title('allocated to heat', loc='right') + ax.set_ylabel("Method") + ax.set_xlabel("Allocated emissions [kgCO2]") + plt.title("allocated to electricity", loc="left") + plt.title("allocated to heat", loc="right") fig.tight_layout() plt.show() + if __name__ == "__main__": emission_allocation_example() diff --git a/examples/compression_heatpump_and_chiller/airsource_heatpump_const_max_output.py b/examples/compression_heatpump_and_chiller/airsource_heatpump_const_max_output.py index 65838ac1..5cd5a59f 100644 --- a/examples/compression_heatpump_and_chiller/airsource_heatpump_const_max_output.py +++ b/examples/compression_heatpump_and_chiller/airsource_heatpump_const_max_output.py @@ -1,80 +1,98 @@ """ Example on how to use the 'calc_cops' function to get the COPs of an exemplary air-source heat pump (ASHP) and use the -pre-calculated COPs in a solph.Transformer. +pre-calculated COPs in a solph.Converter. We use the ambient air as low temperature heat reservoir. """ import os -import oemof.thermal.compression_heatpumps_and_chillers as cmpr_hp_chiller -import oemof.solph as solph -import pandas as pd + import matplotlib.pyplot as plt import numpy as np +import oemof.solph as solph +import pandas as pd + +import oemof.thermal.compression_heatpumps_and_chillers as cmpr_hp_chiller + def airource_hp_const_example(): # Set paths - data_path = os.path.join(os.path.dirname(__file__), 'data/ASHP_example.csv') + data_path = os.path.join( + os.path.dirname(__file__), "data/ASHP_example.csv" + ) # Read input data data = pd.read_csv(data_path) # Set up an energy system model - solver = 'cbc' + solver = "cbc" number_of_time_steps = 24 solver_verbose = False - date_time_index = pd.date_range('1/1/2012', periods=number_of_time_steps, - freq='H') + date_time_index = pd.date_range( + "1/1/2012", periods=number_of_time_steps, freq="H" + ) energysystem = solph.EnergySystem(timeindex=date_time_index) - b_el = solph.Bus(label="electricity") b_heat = solph.Bus(label="heat") energysystem.add(b_el, b_heat) - energysystem.add(solph.components.Source( - label='grid_el', - outputs={b_el: solph.Flow(variable_costs=10)})) - - energysystem.add(solph.components.Source( - label='backup_heating', - outputs={b_heat: solph.Flow(variable_costs=10)})) - - energysystem.add(solph.components.Sink( - label='demand', - inputs={b_heat: solph.Flow(fix=data['demand_heat'], - nominal_value=1)})) + energysystem.add( + solph.components.Source( + label="grid_el", outputs={b_el: solph.Flow(variable_costs=10)} + ) + ) + + energysystem.add( + solph.components.Source( + label="backup_heating", + outputs={b_heat: solph.Flow(variable_costs=10)}, + ) + ) + + energysystem.add( + solph.components.Sink( + label="demand", + inputs={ + b_heat: solph.Flow(fix=data["demand_heat"], nominal_value=1) + }, + ) + ) temp_threshold_icing = 2 # Precalculation of COPs cops_ASHP = cmpr_hp_chiller.calc_cops( temp_high=[40], - temp_low=data['ambient_temperature'], + temp_low=data["ambient_temperature"], quality_grade=0.4, - mode='heat_pump', + mode="heat_pump", temp_threshold_icing=temp_threshold_icing, - factor_icing=0.8) + factor_icing=0.8, + ) # Air-Source Heat Pump - energysystem.add(solph.components.Transformer( - label="ASHP", - inputs={b_el: solph.Flow()}, - outputs={b_heat: solph.Flow(nominal_value=25, variable_costs=5)}, - conversion_factors={b_heat: cops_ASHP})) + energysystem.add( + solph.components.Converter( + label="ASHP", + inputs={b_el: solph.Flow()}, + outputs={b_heat: solph.Flow(nominal_value=25, variable_costs=5)}, + conversion_factors={b_heat: cops_ASHP}, + ) + ) # Create and solve the optimization model model = solph.Model(energysystem) - model.solve(solver=solver, solve_kwargs={'tee': solver_verbose}) + model.solve(solver=solver, solve_kwargs={"tee": solver_verbose}) # Get results - energysystem.results['main'] = solph.processing.results(model) - energysystem.results['meta'] = solph.processing.meta_results(model) + energysystem.results["main"] = solph.processing.results(model) + energysystem.results["meta"] = solph.processing.meta_results(model) energysystem.dump(dpath=None, filename=None) @@ -82,64 +100,69 @@ def airource_hp_const_example(): energysystem = solph.EnergySystem() energysystem.restore(dpath=None, filename=None) - results = energysystem.results['main'] - - electricity_bus = solph.views.node(results, 'electricity') - heat_bus = solph.views.node(results, 'heat') - string_results = solph.views.convert_keys_to_strings( - energysystem.results['main']) - ASHP_output = string_results[ - 'ASHP', 'heat']['sequences'].values - demand_h = string_results[ - 'heat', 'demand']['sequences'].values - ASHP_input = string_results[ - 'electricity', 'ASHP']['sequences'].values + energysystem.results["main"] + ) + ASHP_output = string_results["ASHP", "heat"]["sequences"].values + demand_h = string_results["heat", "demand"]["sequences"].values + ASHP_input = string_results["electricity", "ASHP"]["sequences"].values # Example plot - fig, axs = plt.subplots(3, 1, figsize=(8, 5), sharex=True) - axs[0].plot(ASHP_output, label='heat output') - axs[0].plot(demand_h, linestyle='--', label='heat demand') - axs[1].plot(cops_ASHP, linestyle='-.') - axs[2].plot([-1, number_of_time_steps], - [temp_threshold_icing, temp_threshold_icing], - linestyle='--', - color='red', - label='threshold temperature') - axs[2].text(x=number_of_time_steps - 1, - y=temp_threshold_icing, - s='threshold temperature', - ha='right', - va='center', - color='red', - fontsize=10, - bbox=dict(facecolor='white', edgecolor='white', alpha=1.0)) - axs[2].plot(data['ambient_temperature'], label='ambient temperature') - axs[0].set_title('Heat Output and Demand') - axs[1].set_title('Coefficient of Performance') - axs[2].set_title('Source Temperature (Ambient)') + _, axs = plt.subplots(3, 1, figsize=(8, 5), sharex=True) + axs[0].plot(ASHP_output, label="heat output") + axs[0].plot(demand_h, linestyle="--", label="heat demand") + axs[1].plot(cops_ASHP, linestyle="-.") + axs[2].plot( + [-1, number_of_time_steps], + [temp_threshold_icing, temp_threshold_icing], + linestyle="--", + color="red", + label="threshold temperature", + ) + axs[2].text( + x=number_of_time_steps - 1, + y=temp_threshold_icing, + s="threshold temperature", + ha="right", + va="center", + color="red", + fontsize=10, + bbox=dict(facecolor="white", edgecolor="white", alpha=1.0), + ) + axs[2].plot(data["ambient_temperature"], label="ambient temperature") + axs[0].set_title("Heat Output and Demand") + axs[1].set_title("Coefficient of Performance") + axs[2].set_title("Source Temperature (Ambient)") axs[0].legend() axs[0].grid() axs[1].grid() axs[2].grid() axs[0].set_xlim(0, number_of_time_steps) - axs[0].set_ylabel('Heat flow in kW') - axs[1].set_ylabel('COP') - axs[2].set_ylabel('Temperature in $°$C') - axs[2].set_xlabel('Time in h') + axs[0].set_ylabel("Heat flow in kW") + axs[1].set_ylabel("COP") + axs[2].set_ylabel("Temperature in $°$C") + axs[2].set_xlabel("Time in h") plt.tight_layout() plt.show() - print('********* Main results *********') - print("Total electricity consumption: {:2.1f}".format( - ASHP_input.sum(axis=0)[0])) - print("Total heat output: {:2.1f}".format( - ASHP_output.sum(axis=0)[0])) - print("Average Coefficient of Performance (COP): {:2.2f}".format( - np.mean(cops_ASHP))) - print("Seasonal Performance Factor (SPF): {:2.2f}".format( - ASHP_output.sum(axis=0)[0] / ASHP_input.sum(axis=0)[0])) + print("********* Main results *********") + print( + "Total electricity consumption: {:2.1f}".format( + ASHP_input.sum(axis=0)[0] + ) + ) + print("Total heat output: {:2.1f}".format(ASHP_output.sum(axis=0)[0])) + print( + "Average Coefficient of Performance (COP): {:2.2f}".format( + np.mean(cops_ASHP) + ) + ) + print( + "Seasonal Performance Factor (SPF): {:2.2f}".format( + ASHP_output.sum(axis=0)[0] / ASHP_input.sum(axis=0)[0] + ) + ) if __name__ == "__main__": diff --git a/examples/compression_heatpump_and_chiller/airsource_heatpump_variable_max_output.py b/examples/compression_heatpump_and_chiller/airsource_heatpump_variable_max_output.py index c2eb04e2..510df8d4 100644 --- a/examples/compression_heatpump_and_chiller/airsource_heatpump_variable_max_output.py +++ b/examples/compression_heatpump_and_chiller/airsource_heatpump_variable_max_output.py @@ -1,7 +1,7 @@ """ Example on how to use the 'calc_cops' function to get the COPs of an exemplary air-source heat pump (ASHP) and use the -pre-calculated COPs in a solph.Transformer. +pre-calculated COPs in a solph.Converter. Furthermore, the maximal possible heat output of the heat pump is pre-calculated and varies with the temperature levels of the heat reservoirs. @@ -9,27 +9,32 @@ """ import os -import oemof.thermal.compression_heatpumps_and_chillers as cmpr_hp_chiller -import oemof.solph as solph -import pandas as pd + import matplotlib.pyplot as plt import numpy as np +import oemof.solph as solph +import pandas as pd + +import oemof.thermal.compression_heatpumps_and_chillers as cmpr_hp_chiller def airource_hp_variable_example(): # Set paths - data_path = os.path.join(os.path.dirname(__file__), 'data/ASHP_example.csv') + data_path = os.path.join( + os.path.dirname(__file__), "data/ASHP_example.csv" + ) # Read input data data = pd.read_csv(data_path) # Set up an energy system model - solver = 'cbc' + solver = "cbc" number_of_time_steps = 24 solver_verbose = False - date_time_index = pd.date_range('1/1/2012', periods=number_of_time_steps, - freq='H') + date_time_index = pd.date_range( + "1/1/2012", periods=number_of_time_steps, freq="H" + ) energysystem = solph.EnergySystem(timeindex=date_time_index) @@ -39,55 +44,70 @@ def airource_hp_variable_example(): energysystem.add(b_el, b_heat) - energysystem.add(solph.components.Source( - label='el_grid', - outputs={b_el: solph.Flow(variable_costs=10)})) - - energysystem.add(solph.components.Source( - label='backup_heating', - outputs={b_heat: solph.Flow(variable_costs=10)})) - - energysystem.add(solph.components.Sink( - label='demand', - inputs={b_heat: solph.Flow(fix=data['demand_heat'], - nominal_value=1)})) + energysystem.add( + solph.components.Source( + label="el_grid", outputs={b_el: solph.Flow(variable_costs=10)} + ) + ) + + energysystem.add( + solph.components.Source( + label="backup_heating", + outputs={b_heat: solph.Flow(variable_costs=10)}, + ) + ) + + energysystem.add( + solph.components.Sink( + label="demand", + inputs={ + b_heat: solph.Flow(fix=data["demand_heat"], nominal_value=1) + }, + ) + ) temp_threshold_icing = 2 # Precalculation of COPs cops_ASHP = cmpr_hp_chiller.calc_cops( temp_high=[40], - temp_low=data['ambient_temperature'], + temp_low=data["ambient_temperature"], quality_grade=0.4, - mode='heat_pump', + mode="heat_pump", temp_threshold_icing=temp_threshold_icing, - factor_icing=0.8) + factor_icing=0.8, + ) # Define operation condition for nominal output - nominal_conditions = {'nominal_Q_hot': 25, - 'nominal_el_consumption': 7} - + nominal_conditions = {"nominal_Q_hot": 25, "nominal_el_consumption": 7} - max_Q_dot_heating = cmpr_hp_chiller.calc_max_Q_dot_heat(nominal_conditions, - cops_ASHP) + max_Q_dot_heating = cmpr_hp_chiller.calc_max_Q_dot_heat( + nominal_conditions, cops_ASHP + ) # Air-Source Heat Pump - energysystem.add(solph.components.Transformer( - label="ASHP", - inputs={b_el: solph.Flow()}, - outputs={b_heat: solph.Flow( - nominal_value=nominal_conditions['nominal_Q_hot'], - max=max_Q_dot_heating, - variable_costs=5)}, - conversion_factors={b_heat: cops_ASHP})) + energysystem.add( + solph.components.Converter( + label="ASHP", + inputs={b_el: solph.Flow()}, + outputs={ + b_heat: solph.Flow( + nominal_value=nominal_conditions["nominal_Q_hot"], + max=max_Q_dot_heating, + variable_costs=5, + ) + }, + conversion_factors={b_heat: cops_ASHP}, + ) + ) # Create and solve the optimization model model = solph.Model(energysystem) - model.solve(solver=solver, solve_kwargs={'tee': solver_verbose}) + model.solve(solver=solver, solve_kwargs={"tee": solver_verbose}) # Get results - energysystem.results['main'] = solph.processing.results(model) - energysystem.results['meta'] = solph.processing.meta_results(model) + energysystem.results["main"] = solph.processing.results(model) + energysystem.results["meta"] = solph.processing.meta_results(model) energysystem.dump(dpath=None, filename=None) @@ -95,74 +115,80 @@ def airource_hp_variable_example(): energysystem = solph.EnergySystem() energysystem.restore(dpath=None, filename=None) - results = energysystem.results['main'] - - electricity_bus = solph.views.node(results, 'electricity') - heat_bus = solph.views.node(results, 'heat') - string_results = solph.views.convert_keys_to_strings( - energysystem.results['main']) - ASHP_output = string_results[ - 'ASHP', 'heat']['sequences'].values - demand_h = string_results[ - 'heat', 'demand']['sequences'].values - ASHP_input = string_results[ - 'electricity', 'ASHP']['sequences'].values + energysystem.results["main"] + ) + ASHP_output = string_results["ASHP", "heat"]["sequences"].values + demand_h = string_results["heat", "demand"]["sequences"].values + ASHP_input = string_results["electricity", "ASHP"]["sequences"].values # Absolute values of maximal heating capacity - max_Q_dot_heating_abs = [nominal_conditions['nominal_Q_hot'] * max_heating for - max_heating in max_Q_dot_heating] + max_Q_dot_heating_abs = [ + nominal_conditions["nominal_Q_hot"] * max_heating + for max_heating in max_Q_dot_heating + ] # Example plot - fig, axs = plt.subplots(3, 1, figsize=(8, 5), sharex=True) - axs[0].plot(max_Q_dot_heating_abs, - linestyle='-.', - label='max heat output') - axs[0].plot(ASHP_output, label='actual heat output') - axs[0].plot(demand_h, linestyle='--', label='heat demand') - axs[1].plot(cops_ASHP, linestyle='-.') - axs[2].plot([-1, number_of_time_steps], - [temp_threshold_icing, temp_threshold_icing], - linestyle='--', - color='red', - label='threshold temperature') - axs[2].text(x=number_of_time_steps - 1, - y=temp_threshold_icing, - s='threshold temperature', - ha='right', - va='center', - color='red', - fontsize=10, - bbox=dict(facecolor='white', edgecolor='white', alpha=0.9)) - axs[2].plot(data['ambient_temperature']) - axs[0].set_title('Heat Output and Demand') - axs[1].set_title('Coefficient of Performance') - axs[2].set_title('Source Temperature (Ambient)') + _, axs = plt.subplots(3, 1, figsize=(8, 5), sharex=True) + axs[0].plot(max_Q_dot_heating_abs, linestyle="-.", label="max heat output") + axs[0].plot(ASHP_output, label="actual heat output") + axs[0].plot(demand_h, linestyle="--", label="heat demand") + axs[1].plot(cops_ASHP, linestyle="-.") + axs[2].plot( + [-1, number_of_time_steps], + [temp_threshold_icing, temp_threshold_icing], + linestyle="--", + color="red", + label="threshold temperature", + ) + axs[2].text( + x=number_of_time_steps - 1, + y=temp_threshold_icing, + s="threshold temperature", + ha="right", + va="center", + color="red", + fontsize=10, + bbox=dict(facecolor="white", edgecolor="white", alpha=0.9), + ) + axs[2].plot(data["ambient_temperature"]) + axs[0].set_title("Heat Output and Demand") + axs[1].set_title("Coefficient of Performance") + axs[2].set_title("Source Temperature (Ambient)") axs[0].legend() axs[0].grid() axs[1].grid() axs[2].grid() axs[0].set_xlim(0, number_of_time_steps) - axs[0].set_ylabel('Heat flow in kW') - axs[1].set_ylabel('COP') - axs[2].set_ylabel('Temperature in $°$C') - axs[2].set_xlabel('Time in h') + axs[0].set_ylabel("Heat flow in kW") + axs[1].set_ylabel("COP") + axs[2].set_ylabel("Temperature in $°$C") + axs[2].set_xlabel("Time in h") plt.tight_layout() plt.show() # print('********* Main results *********') # print(electricity_bus['sequences'].sum(axis=0)) # print(heat_bus['sequences'].sum(axis=0)) - print('********* Main results *********') - print("Total electricity consumption: {:2.1f}".format( - ASHP_input.sum(axis=0)[0])) - print("Total heat output: {:2.1f}".format( - ASHP_output.sum(axis=0)[0])) - print("Average Coefficient of Performance (COP): {:2.2f}".format( - np.mean(cops_ASHP))) - print("Seasonal Performance Factor (SPF): {:2.2f}".format( - ASHP_output.sum(axis=0)[0] / ASHP_input.sum(axis=0)[0])) + print("********* Main results *********") + print( + "Total electricity consumption: {:2.1f}".format( + ASHP_input.sum(axis=0)[0] + ) + ) + print("Total heat output: {:2.1f}".format(ASHP_output.sum(axis=0)[0])) + print( + "Average Coefficient of Performance (COP): {:2.2f}".format( + np.mean(cops_ASHP) + ) + ) + print( + "Seasonal Performance Factor (SPF): {:2.2f}".format( + ASHP_output.sum(axis=0)[0] / ASHP_input.sum(axis=0)[0] + ) + ) + if __name__ == "__main__": airource_hp_variable_example() diff --git a/examples/compression_heatpump_and_chiller/chiller_cop.py b/examples/compression_heatpump_and_chiller/chiller_cop.py index 008a6372..c35e1071 100644 --- a/examples/compression_heatpump_and_chiller/chiller_cop.py +++ b/examples/compression_heatpump_and_chiller/chiller_cop.py @@ -4,24 +4,48 @@ We use the ambient air as heat sink (high temperature reservoir). The input is a list to show how the function can be applied on several time steps. The -output is a list as well and may serve as input (conversion_factor) for a -oemof.solph.transformer. +output is a list as well and may serve as input (conversion_factor) for an +oemof.solph.Converter. """ import oemof.thermal.compression_heatpumps_and_chillers as cmpr_hp_chiller + def chiller_cop_const_example(): # Ambient temperatures in degC for a single day (24h) - temp_ambient = [24, 24, 24, 25, 25, 25, - 26, 27, 28, 29, 31, 32, - 35, 34, 27, 26, 25, 24, - 24, 24, 24, 24, 24, 23] - - cops_chiller = cmpr_hp_chiller.calc_cops(temp_high=temp_ambient, - temp_low=[18], - quality_grade=0.3, - mode='chiller') - + temp_ambient = [ + 24, + 24, + 24, + 25, + 25, + 25, + 26, + 27, + 28, + 29, + 31, + 32, + 35, + 34, + 27, + 26, + 25, + 24, + 24, + 24, + 24, + 24, + 24, + 23, + ] + + cops_chiller = cmpr_hp_chiller.calc_cops( + temp_high=temp_ambient, + temp_low=[18], + quality_grade=0.3, + mode="chiller", + ) print("") print("Coefficients of Performance (COP):") diff --git a/examples/compression_heatpump_and_chiller/chiller_cop_as_TimeSeries.py b/examples/compression_heatpump_and_chiller/chiller_cop_as_TimeSeries.py index f1a5612c..8d313aea 100644 --- a/examples/compression_heatpump_and_chiller/chiller_cop_as_TimeSeries.py +++ b/examples/compression_heatpump_and_chiller/chiller_cop_as_TimeSeries.py @@ -4,32 +4,57 @@ We use the ambient air as heat sink (high temperature reservoir). The input is a list to show how the function can be applied on several time steps. The -output is a list as well and may serve as input (conversion_factor) for a -oemof.solph.transformer. +output is a list as well and may serve as input (conversion_factor) for an +oemof.solph.Converter. """ -import oemof.thermal.compression_heatpumps_and_chillers as cmpr_hp_chiller import pandas as pd +import oemof.thermal.compression_heatpumps_and_chillers as cmpr_hp_chiller + + def chiller_cop_timeseries_example(): # Ambient temperatures in degC for a single day (24h) - temp_ambient = [24, 24, 24, 25, 25, 25, - 26, 27, 28, 29, 31, 32, - 35, 34, 27, 26, 25, 24, - 24, 24, 24, 24, 24, 23] + temp_ambient = [ + 24, + 24, + 24, + 25, + 25, + 25, + 26, + 27, + 28, + 29, + 31, + 32, + 35, + 34, + 27, + 26, + 25, + 24, + 24, + 24, + 24, + 24, + 24, + 23, + ] - timestamps = pd.date_range('20200101', periods=24, freq='H') + timestamps = pd.date_range("20200101", periods=24, freq="H") # Convert temp_ambient to pandas DataFrame df_temp_ambient = pd.DataFrame(temp_ambient, timestamps) # Convert temp_ambient to pandas Series series_temp_ambient = pd.Series(temp_ambient, timestamps) - cops_chiller = cmpr_hp_chiller.calc_cops(temp_high=series_temp_ambient, - temp_low=[18], - quality_grade=0.3, - mode='chiller') - + cops_chiller = cmpr_hp_chiller.calc_cops( + temp_high=series_temp_ambient, + temp_low=[18], + quality_grade=0.3, + mode="chiller", + ) print("") print("Coefficients of Performance (COP):") @@ -39,5 +64,6 @@ def chiller_cop_timeseries_example(): t += 1 print("") + if __name__ == "__main__": chiller_cop_timeseries_example() diff --git a/examples/compression_heatpump_and_chiller/cop_dependence_on_temperature_difference.py b/examples/compression_heatpump_and_chiller/cop_dependence_on_temperature_difference.py index 98cd7ff1..c26c6160 100644 --- a/examples/compression_heatpump_and_chiller/cop_dependence_on_temperature_difference.py +++ b/examples/compression_heatpump_and_chiller/cop_dependence_on_temperature_difference.py @@ -5,9 +5,10 @@ This example plots the temperature dependency of the COP. """ -import oemof.thermal.compression_heatpumps_and_chillers as cmpr_hp_chiller -import pandas as pd import matplotlib.pyplot as plt +import pandas as pd + +import oemof.thermal.compression_heatpumps_and_chillers as cmpr_hp_chiller def cop_temperature_example(): @@ -27,113 +28,145 @@ def cop_temperature_example(): # COPs of ax exemplary heat pump for domestic hot water # (sink temperature below 100 degC) - cops['temperature_difference'] = temp_difference + cops["temperature_difference"] = temp_difference list_temp_low = [temp_high - temp_diff for temp_diff in temp_difference] cops[temp_high] = cmpr_hp_chiller.calc_cops( temp_high=[temp_high], temp_low=list_temp_low, quality_grade=0.35, - mode='heat_pump') + mode="heat_pump", + ) # COPs of ax exemplary hight temperature heat pump for industrial applications # (sink temperature above 100 degC and higher quality grade) - cops['temperature_difference_industrial'] = temp_difference_industrial - temp_l_ind = [temp_high_industrial - temp_diff for - temp_diff in temp_difference_industrial] + cops["temperature_difference_industrial"] = temp_difference_industrial + temp_l_ind = [ + temp_high_industrial - temp_diff + for temp_diff in temp_difference_industrial + ] cops[temp_high_industrial] = cmpr_hp_chiller.calc_cops( temp_high=[temp_high_industrial], temp_low=temp_l_ind, quality_grade=0.45, - mode='heat_pump') + mode="heat_pump", + ) # COPs for varying quality grades cops_q_grade = pd.DataFrame() - cops_q_grade['temperature_diff'] = temperature_diff_q_grade - list_temp_low_q_grade = [temp_high - temp_diff_q for - temp_diff_q in temperature_diff_q_grade] + cops_q_grade["temperature_diff"] = temperature_diff_q_grade + list_temp_low_q_grade = [ + temp_high - temp_diff_q for temp_diff_q in temperature_diff_q_grade + ] for q in quality_grades: - list_temp_low = [temp_high - temp_diff_q for - temp_diff_q in temperature_diff_q_grade] cops_q_grade[q] = cmpr_hp_chiller.calc_cops( temp_high=[temp_high], temp_low=list_temp_low_q_grade, quality_grade=q, - mode='heat_pump') + mode="heat_pump", + ) # Example plot fig1 = plt.figure(figsize=(8, 6)) fig1.set_size_inches(8, 6, forward=True) axs = fig1.add_subplot(111) - plt.plot(cops['temperature_difference_industrial'], - cops[temp_high_industrial], - linestyle='-', - color='red', - label='COP of a high temperature heat pump') - plt.plot(cops['temperature_difference'], - cops[temp_high], - linestyle='-', - color='blue', - label='COP of a heat pump for domestic hot water') + plt.plot( + cops["temperature_difference_industrial"], + cops[temp_high_industrial], + linestyle="-", + color="red", + label="COP of a high temperature heat pump", + ) + plt.plot( + cops["temperature_difference"], + cops[temp_high], + linestyle="-", + color="blue", + label="COP of a heat pump for domestic hot water", + ) for q in quality_grades: - plt.plot(cops_q_grade['temperature_diff'], - cops_q_grade[q], - linestyle='dotted', - color='grey') + plt.plot( + cops_q_grade["temperature_diff"], + cops_q_grade[q], + linestyle="dotted", + color="grey", + ) axs.set_ylim(0, 12) axs.set_xlim(0, 145) - plt.title('COP Dependence on Temperature Difference') - plt.xlabel('Temperature Difference in K') - plt.ylabel('Coefficient of Performance (COP)') - plt.legend(loc='upper right') + plt.title("COP Dependence on Temperature Difference") + plt.xlabel("Temperature Difference in K") + plt.ylabel("Coefficient of Performance (COP)") + plt.legend(loc="upper right") bbox_props = dict(boxstyle="round", fc="w", ec="1.0", alpha=0.9) - textcol = '0.3' + textcol = "0.3" textsize = 10 - axs.text(47, 7.2, "100%", - ha="center", - va="center", - size=textsize, - rotation=-60, - color=textcol, - bbox=bbox_props) - axs.text(57, 5.85, "(Carnot)", - ha="center", - va="center", - size=textsize, - rotation=-52, - color=textcol, - bbox=bbox_props) - axs.text(30, 9.1, "quality grade", - ha="center", - va="center", - size=textsize, - rotation=-76, - color=textcol, - bbox=bbox_props) - axs.text(39, 6.8, "80%", - ha="center", - va="center", - size=textsize, - rotation=-70, - color=textcol, - bbox=bbox_props) - axs.text(12, 5.7, "20%", - ha="center", - va="center", - size=textsize, - rotation=-70, - color=textcol, - bbox=bbox_props) - axs.annotate('quality grade 45%,\nT_sink 110 degC', - xy=(97, 1.8), - xytext=(95, 4), - arrowprops=dict(arrowstyle="->", - connectionstyle="arc3")) - axs.annotate('quality grade 35%,\nT_sink 65 degC', - xy=(40, 3), - xytext=(4, 0.6), - arrowprops=dict(arrowstyle="->", - connectionstyle="arc3")) + axs.text( + 47, + 7.2, + "100%", + ha="center", + va="center", + size=textsize, + rotation=-60, + color=textcol, + bbox=bbox_props, + ) + axs.text( + 57, + 5.85, + "(Carnot)", + ha="center", + va="center", + size=textsize, + rotation=-52, + color=textcol, + bbox=bbox_props, + ) + axs.text( + 30, + 9.1, + "quality grade", + ha="center", + va="center", + size=textsize, + rotation=-76, + color=textcol, + bbox=bbox_props, + ) + axs.text( + 39, + 6.8, + "80%", + ha="center", + va="center", + size=textsize, + rotation=-70, + color=textcol, + bbox=bbox_props, + ) + axs.text( + 12, + 5.7, + "20%", + ha="center", + va="center", + size=textsize, + rotation=-70, + color=textcol, + bbox=bbox_props, + ) + axs.annotate( + "quality grade 45%,\nT_sink 110 degC", + xy=(97, 1.8), + xytext=(95, 4), + arrowprops=dict(arrowstyle="->", connectionstyle="arc3"), + ) + axs.annotate( + "quality grade 35%,\nT_sink 65 degC", + xy=(40, 3), + xytext=(4, 0.6), + arrowprops=dict(arrowstyle="->", connectionstyle="arc3"), + ) # plt.savefig('cop_dependence_on_temp_difference.png', dpi=300) plt.show() diff --git a/examples/compression_heatpump_and_chiller/groundsource_heatpump.py b/examples/compression_heatpump_and_chiller/groundsource_heatpump.py index f9f7fe00..a3205349 100644 --- a/examples/compression_heatpump_and_chiller/groundsource_heatpump.py +++ b/examples/compression_heatpump_and_chiller/groundsource_heatpump.py @@ -6,27 +6,31 @@ """ import os -import oemof.thermal.compression_heatpumps_and_chillers as cmpr_hp_chiller + +import matplotlib.pyplot as plt import oemof.solph as solph import pandas as pd -import matplotlib.pyplot as plt +import oemof.thermal.compression_heatpumps_and_chillers as cmpr_hp_chiller def groundsource_hp_example(): # Set paths - data_path = os.path.join(os.path.dirname(__file__), 'data/GSHP_example.csv') + data_path = os.path.join( + os.path.dirname(__file__), "data/GSHP_example.csv" + ) # Read input data data = pd.read_csv(data_path) # Set up an energy system model - solver = 'cbc' + solver = "cbc" number_of_time_steps = 24 solver_verbose = False - date_time_index = pd.date_range('1/1/2012', periods=number_of_time_steps, - freq='H') + date_time_index = pd.date_range( + "1/1/2012", periods=number_of_time_steps, freq="H" + ) energysystem = solph.EnergySystem(timeindex=date_time_index) @@ -38,47 +42,66 @@ def groundsource_hp_example(): energysystem.add(b_el, b_th_high, b_th_low) - energysystem.add(solph.components.Source( - label='el_grid', - outputs={b_el: solph.Flow(variable_costs=10)})) + energysystem.add( + solph.components.Source( + label="el_grid", outputs={b_el: solph.Flow(variable_costs=10)} + ) + ) - energysystem.add(solph.components.Source( - label='backup_heating', - outputs={b_th_high: solph.Flow(variable_costs=10)})) + energysystem.add( + solph.components.Source( + label="backup_heating", + outputs={b_th_high: solph.Flow(variable_costs=10)}, + ) + ) # Borehole that acts as heat source for the heat pump with # limited extraction heat flow rate - energysystem.add(solph.components.Source( - label='ambient', - outputs={b_th_low: solph.Flow(nominal_value=30)})) - - energysystem.add(solph.components.Sink( - label='demand', - inputs={b_th_high: solph.Flow(fix=data['demand_heat'], - nominal_value=1)})) + energysystem.add( + solph.components.Source( + label="ambient", outputs={b_th_low: solph.Flow(nominal_value=30)} + ) + ) + + energysystem.add( + solph.components.Sink( + label="demand", + inputs={ + b_th_high: solph.Flow(fix=data["demand_heat"], nominal_value=1) + }, + ) + ) # Precalculation of COPs cops_GSHP = cmpr_hp_chiller.calc_cops( temp_high=[40], - temp_low=data['ground_temperature'], + temp_low=data["ground_temperature"], quality_grade=0.4, - mode='heat_pump') + mode="heat_pump", + ) # Ground-Source Heat Pump - energysystem.add(solph.components.Transformer( - label="GSHP", - inputs={b_el: solph.Flow(), b_th_low: solph.Flow()}, - outputs={b_th_high: solph.Flow(nominal_value=25, variable_costs=5)}, - conversion_factors={b_el: [1 / cop for cop in cops_GSHP], - b_th_low: [(cop - 1) / cop for cop in cops_GSHP]})) + energysystem.add( + solph.components.Converter( + label="GSHP", + inputs={b_el: solph.Flow(), b_th_low: solph.Flow()}, + outputs={ + b_th_high: solph.Flow(nominal_value=25, variable_costs=5) + }, + conversion_factors={ + b_el: [1 / cop for cop in cops_GSHP], + b_th_low: [(cop - 1) / cop for cop in cops_GSHP], + }, + ) + ) # Create and solve the optimization model model = solph.Model(energysystem) - model.solve(solver=solver, solve_kwargs={'tee': solver_verbose}) + model.solve(solver=solver, solve_kwargs={"tee": solver_verbose}) # Get results - energysystem.results['main'] = solph.processing.results(model) - energysystem.results['meta'] = solph.processing.meta_results(model) + energysystem.results["main"] = solph.processing.results(model) + energysystem.results["meta"] = solph.processing.meta_results(model) energysystem.dump(dpath=None, filename=None) @@ -86,48 +109,45 @@ def groundsource_hp_example(): energysystem = solph.EnergySystem() energysystem.restore(dpath=None, filename=None) - results = energysystem.results['main'] + results = energysystem.results["main"] - electricity_bus = solph.views.node(results, 'electricity') - heat_bus = solph.views.node(results, 'heat') + electricity_bus = solph.views.node(results, "electricity") + heat_bus = solph.views.node(results, "heat") string_results = solph.views.convert_keys_to_strings( - energysystem.results['main']) - GSHP_output = string_results[ - 'GSHP', 'heat']['sequences'].values - demand_h = string_results[ - 'heat', 'demand']['sequences'].values - GSHP_input = string_results[ - 'electricity', 'GSHP']['sequences'].values - env_heat = string_results[ - 'ambient', 'heat_low_temp']['sequences'].values + energysystem.results["main"] + ) + GSHP_output = string_results["GSHP", "heat"]["sequences"].values + demand_h = string_results["heat", "demand"]["sequences"].values + env_heat = string_results["ambient", "heat_low_temp"]["sequences"].values # Example plot - fig2, axs = plt.subplots(3, 1, figsize=(8, 5), sharex=True) - axs[0].plot(GSHP_output, label='heat output') - axs[0].plot(demand_h, linestyle='--', label='heat demand') - axs[0].plot(env_heat, label='heat from environment') - axs[1].plot(cops_GSHP, linestyle='-.') - axs[2].plot(data['ground_temperature']) - axs[0].set_title('Heat Output and Demand') - axs[1].set_title('Coefficient of Performance') - axs[2].set_title('Ground Temperature') + _, axs = plt.subplots(3, 1, figsize=(8, 5), sharex=True) + axs[0].plot(GSHP_output, label="heat output") + axs[0].plot(demand_h, linestyle="--", label="heat demand") + axs[0].plot(env_heat, label="heat from environment") + axs[1].plot(cops_GSHP, linestyle="-.") + axs[2].plot(data["ground_temperature"]) + axs[0].set_title("Heat Output and Demand") + axs[1].set_title("Coefficient of Performance") + axs[2].set_title("Ground Temperature") axs[0].legend() axs[0].grid() axs[1].grid() axs[2].grid() - axs[0].set_ylabel('Heat flow in kW') - axs[1].set_ylabel('COP') - axs[2].set_ylabel('Temperature in degC') - axs[2].set_xlabel('Time in h') + axs[0].set_ylabel("Heat flow in kW") + axs[1].set_ylabel("COP") + axs[2].set_ylabel("Temperature in degC") + axs[2].set_xlabel("Time in h") plt.tight_layout() plt.show() - print('********* Main results *********') - print(electricity_bus['sequences'].sum(axis=0)) - print(heat_bus['sequences'].sum(axis=0)) + print("********* Main results *********") + print(electricity_bus["sequences"].sum(axis=0)) + print(heat_bus["sequences"].sum(axis=0)) print("heat from environment:", env_heat.sum()) + if __name__ == "__main__": groundsource_hp_example() diff --git a/examples/concentrating_solar_power/csp_collector_plot.py b/examples/concentrating_solar_power/csp_collector_plot.py index 45e96007..02673e34 100644 --- a/examples/concentrating_solar_power/csp_collector_plot.py +++ b/examples/concentrating_solar_power/csp_collector_plot.py @@ -5,6 +5,7 @@ The collectors efficiency and irradiance are calculated depending on the default loss method. """ + import os import matplotlib.pyplot as plt @@ -16,8 +17,8 @@ def csp_collector_example(): # Set paths base_path = os.path.dirname(os.path.abspath(os.path.join(__file__))) - data_path = os.path.join(base_path, 'data') - results_path = os.path.join(base_path, 'results') + data_path = os.path.join(base_path, "data") + results_path = os.path.join(base_path, "results") if not os.path.exists(results_path): os.mkdir(results_path) @@ -25,23 +26,27 @@ def csp_collector_example(): # Precalculation # Read input data - dataframe = pd.read_csv(os.path.join(data_path, 'data_Muscat_22_8.csv')) - dataframe['Datum'] = pd.to_datetime(dataframe['Datum']) - dataframe.set_index('Datum', inplace=True) - dataframe.index = dataframe.index.tz_localize(tz='Asia/Muscat') - - df_temp_amb_series = pd.read_csv(os.path.join(data_path, 'data_Muscat_22_8_midday.csv')) - df_temp_amb_series['Datum'] = pd.to_datetime(df_temp_amb_series['Datum']) - df_temp_amb_series.set_index('Datum', inplace=True) - df_temp_amb_series.index = df_temp_amb_series.index.tz_localize(tz='Asia/Muscat') - - temp_amb_series = pd.read_csv(os.path.join(data_path, 'temp_ambience.csv'))['t_amb'] + dataframe = pd.read_csv(os.path.join(data_path, "data_Muscat_22_8.csv")) + dataframe["Datum"] = pd.to_datetime(dataframe["Datum"]) + dataframe.set_index("Datum", inplace=True) + dataframe.index = dataframe.index.tz_localize(tz="Asia/Muscat") + + df_temp_amb_series = pd.read_csv( + os.path.join(data_path, "data_Muscat_22_8_midday.csv") + ) + df_temp_amb_series["Datum"] = pd.to_datetime(df_temp_amb_series["Datum"]) + df_temp_amb_series.set_index("Datum", inplace=True) + df_temp_amb_series.index = df_temp_amb_series.index.tz_localize( + tz="Asia/Muscat" + ) + + temp_amb_series = pd.read_csv( + os.path.join(data_path, "temp_ambience.csv") + )["t_amb"] # Parameters for the precalculation - periods = 24 latitude = 23.614328 longitude = 58.545284 - timezone = 'Asia/Muscat' collector_tilt = 10 collector_azimuth = 180 cleanliness = 0.9 @@ -55,26 +60,39 @@ def csp_collector_example(): # Plot showing the difference between a constant efficiency without considering # cleaniness for the heat of the collector during a day - data_precalc = csp_precalc(latitude, longitude, - collector_tilt, collector_azimuth, cleanliness, - eta_0, c_1, c_2, - temp_collector_inlet, temp_collector_outlet, - dataframe['t_amb'], - a_1, a_2, - E_dir_hor=dataframe['E_dir_hor']) - - heat_calc = data_precalc['collector_heat'] - irradiance_on_collector = (data_precalc['collector_irradiance'] - / (cleanliness**1.5)) + data_precalc = csp_precalc( + latitude, + longitude, + collector_tilt, + collector_azimuth, + cleanliness, + eta_0, + c_1, + c_2, + temp_collector_inlet, + temp_collector_outlet, + dataframe["t_amb"], + a_1, + a_2, + E_dir_hor=dataframe["E_dir_hor"], + ) + + heat_calc = data_precalc["collector_heat"] + irradiance_on_collector = data_precalc["collector_irradiance"] / ( + cleanliness**1.5 + ) heat_compare = irradiance_on_collector * eta_0 t = list(range(1, 25)) # Example plot fig, ax = plt.subplots() - ax.plot(t, heat_calc, label='CSP precalculation') - ax.plot(t, heat_compare, label='constant efficiency') - ax.set(xlabel='time [h]', ylabel='Q_coll [W/m2]', - title='Heat of the collector') + ax.plot(t, heat_calc, label="CSP precalculation") + ax.plot(t, heat_compare, label="constant efficiency") + ax.set( + xlabel="time [h]", + ylabel="Q_coll [W/m2]", + title="Heat of the collector", + ) ax.grid() ax.legend() @@ -83,25 +101,39 @@ def csp_collector_example(): # day df_result = pd.DataFrame() for i in range(len(temp_amb_series)): - df_temp_amb_series['t_amb'] = temp_amb_series[i] + df_temp_amb_series["t_amb"] = temp_amb_series[i] data_precalc_temp_amb = csp_precalc( - latitude, longitude, - collector_tilt, collector_azimuth, cleanliness, - eta_0, c_1, c_2, - temp_collector_inlet, temp_collector_outlet, - df_temp_amb_series['t_amb'], - a_1, a_2, - E_dir_hor=df_temp_amb_series['E_dir_hor']) + latitude, + longitude, + collector_tilt, + collector_azimuth, + cleanliness, + eta_0, + c_1, + c_2, + temp_collector_inlet, + temp_collector_outlet, + df_temp_amb_series["t_amb"], + a_1, + a_2, + E_dir_hor=df_temp_amb_series["E_dir_hor"], + ) df_result = df_result.append(data_precalc_temp_amb, ignore_index=True) - fig, ax = plt.subplots() - ax.plot(temp_amb_series, df_result['eta_c'], - label='efficiency depending on ambient temperature') - ax.plot(temp_amb_series, [eta_0] * 24, label='constant efficiency') + _, ax = plt.subplots() + ax.plot( + temp_amb_series, + df_result["eta_c"], + label="efficiency depending on ambient temperature", + ) + ax.plot(temp_amb_series, [eta_0] * 24, label="constant efficiency") ax.set_ylim(0, 1) - ax.set(xlabel='ambient temperature [°C]', ylabel='eta_collector', - title='collectors efficiency') + ax.set( + xlabel="ambient temperature [°C]", + ylabel="eta_collector", + title="collectors efficiency", + ) ax.grid() ax.legend() plt.show() diff --git a/examples/concentrating_solar_power/csp_collector_plot_andasol.py b/examples/concentrating_solar_power/csp_collector_plot_andasol.py index cbc1c158..b28b0b8c 100644 --- a/examples/concentrating_solar_power/csp_collector_plot_andasol.py +++ b/examples/concentrating_solar_power/csp_collector_plot_andasol.py @@ -5,6 +5,7 @@ The collectors efficiency and irradiance are calculated depending on the loss method 'Andasol'. """ + import os import matplotlib.pyplot as plt @@ -16,8 +17,8 @@ def csp_andasol_example(): # Set paths base_path = os.path.dirname(os.path.abspath(os.path.join(__file__))) - data_path = os.path.join(base_path, 'data') - results_path = os.path.join(base_path, 'results') + data_path = os.path.join(base_path, "data") + results_path = os.path.join(base_path, "results") if not os.path.exists(results_path): os.mkdir(results_path) @@ -25,23 +26,27 @@ def csp_andasol_example(): # Precalculation # Read input data - dataframe = pd.read_csv(os.path.join(data_path, 'data_Muscat_22_8.csv')) - dataframe['Datum'] = pd.to_datetime(dataframe['Datum']) - dataframe.set_index('Datum', inplace=True) - dataframe.index = dataframe.index.tz_localize(tz='Asia/Muscat') - - df_temp_amb_series = pd.read_csv(os.path.join(data_path, 'data_Muscat_22_8_midday.csv')) - df_temp_amb_series['Datum'] = pd.to_datetime(df_temp_amb_series['Datum']) - df_temp_amb_series.set_index('Datum', inplace=True) - df_temp_amb_series.index = df_temp_amb_series.index.tz_localize(tz='Asia/Muscat') - - temp_amb_series = pd.read_csv(os.path.join(data_path, 'temp_ambience.csv'))['t_amb'] + dataframe = pd.read_csv(os.path.join(data_path, "data_Muscat_22_8.csv")) + dataframe["Datum"] = pd.to_datetime(dataframe["Datum"]) + dataframe.set_index("Datum", inplace=True) + dataframe.index = dataframe.index.tz_localize(tz="Asia/Muscat") + + df_temp_amb_series = pd.read_csv( + os.path.join(data_path, "data_Muscat_22_8_midday.csv") + ) + df_temp_amb_series["Datum"] = pd.to_datetime(df_temp_amb_series["Datum"]) + df_temp_amb_series.set_index("Datum", inplace=True) + df_temp_amb_series.index = df_temp_amb_series.index.tz_localize( + tz="Asia/Muscat" + ) + + temp_amb_series = pd.read_csv( + os.path.join(data_path, "temp_ambience.csv") + )["t_amb"] # Parameters for the precalculation - periods = 24 latitude = 23.614328 longitude = 58.545284 - timezone = 'Asia/Muscat' collector_tilt = 10 collector_azimuth = 180 cleanliness = 0.9 @@ -60,27 +65,44 @@ def csp_andasol_example(): # Plot showing the difference between a constant efficiency without considering # cleaniness for the heat of the collector during a day - data_precalc = csp_precalc(latitude, longitude, - collector_tilt, collector_azimuth, cleanliness, - eta_0, c_1, c_2, - temp_collector_inlet, temp_collector_outlet, - dataframe['t_amb'], - a_1, a_2, a_3, a_4, a_5, a_6, - loss_method='Andasol', - E_dir_hor=dataframe['E_dir_hor']) - - heat_calc = data_precalc['collector_heat'] - irradiance_on_collector = (data_precalc['collector_irradiance'] - / (cleanliness**1.5)) + data_precalc = csp_precalc( + latitude, + longitude, + collector_tilt, + collector_azimuth, + cleanliness, + eta_0, + c_1, + c_2, + temp_collector_inlet, + temp_collector_outlet, + dataframe["t_amb"], + a_1, + a_2, + a_3, + a_4, + a_5, + a_6, + loss_method="Andasol", + E_dir_hor=dataframe["E_dir_hor"], + ) + + heat_calc = data_precalc["collector_heat"] + irradiance_on_collector = data_precalc["collector_irradiance"] / ( + cleanliness**1.5 + ) heat_compare = irradiance_on_collector * eta_0 t = list(range(1, 25)) # Example plot - fig, ax = plt.subplots() - ax.plot(t, heat_calc, label='CSP precalculation') - ax.plot(t, heat_compare, label='constant efficiency') - ax.set(xlabel='time (h)', ylabel='Q_coll [W/m2]', - title='Heat of the collector') + _, ax = plt.subplots() + ax.plot(t, heat_calc, label="CSP precalculation") + ax.plot(t, heat_compare, label="constant efficiency") + ax.set( + xlabel="time (h)", + ylabel="Q_coll [W/m2]", + title="Heat of the collector", + ) ax.grid() ax.legend() @@ -89,29 +111,48 @@ def csp_andasol_example(): # day df_result = pd.DataFrame() for i in range(len(temp_amb_series)): - df_temp_amb_series['t_amb'] = temp_amb_series[i] + df_temp_amb_series["t_amb"] = temp_amb_series[i] data_precalc_temp_amb = csp_precalc( - latitude, longitude, - collector_tilt, collector_azimuth, cleanliness, - eta_0, c_1, c_2, - temp_collector_inlet, temp_collector_outlet, - df_temp_amb_series['t_amb'], - a_1, a_2, a_3, a_4, a_5, a_6, - loss_method='Andasol', - E_dir_hor=df_temp_amb_series['E_dir_hor']) + latitude, + longitude, + collector_tilt, + collector_azimuth, + cleanliness, + eta_0, + c_1, + c_2, + temp_collector_inlet, + temp_collector_outlet, + df_temp_amb_series["t_amb"], + a_1, + a_2, + a_3, + a_4, + a_5, + a_6, + loss_method="Andasol", + E_dir_hor=df_temp_amb_series["E_dir_hor"], + ) df_result = df_result.append(data_precalc_temp_amb, ignore_index=True) - fig, ax = plt.subplots() - ax.plot(temp_amb_series, df_result['eta_c'], - label='efficiency depending on ambient temperature') - ax.plot(temp_amb_series, [eta_0] * 24, label='constant efficiency') + _, ax = plt.subplots() + ax.plot( + temp_amb_series, + df_result["eta_c"], + label="efficiency depending on ambient temperature", + ) + ax.plot(temp_amb_series, [eta_0] * 24, label="constant efficiency") ax.set_ylim(0, 1) - ax.set(xlabel='ambient temperature [°C]', ylabel='eta_collector', - title='collectors efficiency') + ax.set( + xlabel="ambient temperature [°C]", + ylabel="eta_collector", + title="collectors efficiency", + ) ax.grid() ax.legend() plt.show() + if __name__ == "__main__": csp_andasol_example() diff --git a/examples/concentrating_solar_power/csp_facade.py b/examples/concentrating_solar_power/csp_facade.py index bfd8a6fc..fe95a6ed 100644 --- a/examples/concentrating_solar_power/csp_facade.py +++ b/examples/concentrating_solar_power/csp_facade.py @@ -8,26 +8,27 @@ SPDX-License-Identifier: MIT """ + import os -import pandas as pd -import matplotlib.pyplot as plt +import matplotlib.pyplot as plt +import pandas as pd +from oemof.tools import economics -from oemof.thermal import facades from oemof import solph -from oemof.tools import economics +from oemof.thermal import facades + def csp_facade_example(): # Set paths base_path = os.path.dirname(os.path.abspath(os.path.join(__file__))) - results_path = os.path.join(base_path, 'results') - data_path = os.path.join(base_path, 'data') + results_path = os.path.join(base_path, "results") + data_path = os.path.join(base_path, "data") if not os.path.exists(results_path): os.mkdir(results_path) - # Set up an energy system model periods = 50 latitude = 23.614328 @@ -44,14 +45,15 @@ def csp_facade_example(): temp_collector_outlet = 500 # Read input data - input_data = pd.read_csv(os.path.join(data_path, 'data_csp_plant.csv')).head(periods) - input_data['Datum'] = pd.to_datetime(input_data['Datum']) - input_data.set_index('Datum', inplace=True) - input_data.index = input_data.index.tz_localize(tz='Asia/Muscat') + input_data = pd.read_csv( + os.path.join(data_path, "data_csp_plant.csv") + ).head(periods) + input_data["Datum"] = pd.to_datetime(input_data["Datum"]) + input_data.set_index("Datum", inplace=True) + input_data.index = input_data.index.tz_localize(tz="Asia/Muscat") date_time_index = input_data.index - date_time_index.freq = 'H' - + date_time_index.freq = "H" # oemof_system @@ -67,19 +69,19 @@ def csp_facade_example(): size_collector = 1000 # Busses - bth = solph.Bus(label='thermal') - bel = solph.Bus(label='electricity') + bth = solph.Bus(label="thermal") + bel = solph.Bus(label="electricity") # Collector collector = facades.ParabolicTroughCollector( - label='solar_collector', + label="solar_collector", heat_bus=bth, electrical_bus=bel, electrical_consumption=elec_consumption, additional_losses=additional_losses, aperture_area=size_collector, - loss_method='Janotte', - irradiance_method='horizontal', + loss_method="Janotte", + irradiance_method="horizontal", latitude=latitude, longitude=longitude, collector_tilt=collector_tilt, @@ -92,72 +94,92 @@ def csp_facade_example(): c_2=c_2, temp_collector_inlet=temp_collector_inlet, temp_collector_outlet=temp_collector_outlet, - temp_amb=input_data['t_amb'], - irradiance=input_data['E_dir_hor']) + temp_amb=input_data["t_amb"], + irradiance=input_data["E_dir_hor"], + ) # Sources and sinks el_grid = solph.components.Source( - label='grid', - outputs={bel: solph.Flow(variable_costs=costs_electricity)}) + label="grid", + outputs={bel: solph.Flow(variable_costs=costs_electricity)}, + ) backup = solph.components.Source( - label='backup', - outputs={bth: solph.Flow(variable_costs=backup_costs)}) + label="backup", outputs={bth: solph.Flow(variable_costs=backup_costs)} + ) consumer = solph.components.Sink( - label='demand', - inputs={bel: solph.Flow( - fix=input_data['ES_load_actual_entsoe_power_statistics'], - nominal_value=1)}) - - excess = solph.components.Sink( - label='excess', - inputs={bth: solph.Flow()}) - - # Transformer and storages - turbine = solph.components.Transformer( - label='turbine', + label="demand", + inputs={ + bel: solph.Flow( + fix=input_data["ES_load_actual_entsoe_power_statistics"], + nominal_value=1, + ) + }, + ) + + excess = solph.components.Sink(label="excess", inputs={bth: solph.Flow()}) + + # Converter and storages + turbine = solph.components.Converter( + label="turbine", inputs={bth: solph.Flow()}, outputs={bel: solph.Flow()}, - conversion_factors={bel: conversion_factor_turbine}) + conversion_factors={bel: conversion_factor_turbine}, + ) storage = solph.components.GenericStorage( - label='storage_th', + label="storage_th", inputs={bth: solph.Flow()}, outputs={bth: solph.Flow()}, loss_rate=cap_loss, inflow_conversion_factor=conversion_storage, outflow_conversion_factor=conversion_storage, - investment=solph.Investment(ep_costs=costs_storage)) + investment=solph.Investment(ep_costs=costs_storage), + ) # Build the system and solve the problem energysystem = solph.EnergySystem(timeindex=date_time_index) - energysystem.add(bth, bel, el_grid, backup, excess, consumer, storage, turbine, - collector) + energysystem.add( + bth, + bel, + el_grid, + backup, + excess, + consumer, + storage, + turbine, + collector, + ) # Create and solve the optimization model model = solph.Model(energysystem) - model.solve(solver='cbc', solve_kwargs={'tee': True}) + model.solve(solver="cbc", solve_kwargs={"tee": True}) # Get results results = solph.processing.results(model) - collector_inflow = solph.views.node( - results, 'solar_collector-inflow')['sequences'] - thermal_bus = solph.views.node(results, 'thermal')['sequences'] - electricity_bus = solph.views.node(results, 'electricity')['sequences'] + collector_inflow = solph.views.node(results, "solar_collector-inflow")[ + "sequences" + ] + thermal_bus = solph.views.node(results, "thermal")["sequences"] + electricity_bus = solph.views.node(results, "electricity")["sequences"] df = pd.DataFrame() df = df.append(collector_inflow) - df = df.join(thermal_bus, lsuffix='_1') - df = df.join(electricity_bus, lsuffix='_1') - df.to_csv(os.path.join(results_path, 'facade_results.csv')) + df = df.join(thermal_bus, lsuffix="_1") + df = df.join(electricity_bus, lsuffix="_1") + df.to_csv(os.path.join(results_path, "facade_results.csv")) # Example plot - fig, ax = plt.subplots() - ax.plot(list(range(periods)), thermal_bus[(('solar_collector', 'thermal'), 'flow')][:-1]) - ax.set(xlabel='time [h]', ylabel='Q_coll [W]', - title='Heat of the collector') + _, ax = plt.subplots() + ax.plot( + list(range(periods)), + thermal_bus[(("solar_collector", "thermal"), "flow")][:-1], + ) + ax.set( + xlabel="time [h]", ylabel="Q_coll [W]", title="Heat of the collector" + ) ax.grid() plt.show() diff --git a/examples/concentrating_solar_power/csp_plant.py b/examples/concentrating_solar_power/csp_plant.py index 3b3383e4..af31c8d1 100644 --- a/examples/concentrating_solar_power/csp_plant.py +++ b/examples/concentrating_solar_power/csp_plant.py @@ -8,21 +8,20 @@ """ import os +import matplotlib.pyplot as plt import pandas as pd +from oemof.tools import economics from oemof import solph -from oemof.tools import economics from oemof.thermal.concentrating_solar_power import csp_precalc -import matplotlib.pyplot as plt - def csp_plant_example(): # Set paths base_path = os.path.dirname(os.path.abspath(os.path.join(__file__))) - results_path = os.path.join(base_path, 'results') - data_path = os.path.join(base_path, 'data') + results_path = os.path.join(base_path, "results") + data_path = os.path.join(base_path, "data") if not os.path.exists(results_path): os.mkdir(results_path) @@ -47,28 +46,42 @@ def csp_plant_example(): # It is necessary, to set a timeindex, so the pvlib is able to process the data # Here, the given column 'Datum' is convertet to a datetime and this is used as # index: - dataframe = pd.read_csv(os.path.join(data_path, 'data_csp_plant.csv')).head(periods) - dataframe['Datum'] = pd.to_datetime(dataframe['Datum']) - dataframe.set_index('Datum', inplace=True) + dataframe = pd.read_csv( + os.path.join(data_path, "data_csp_plant.csv") + ).head(periods) + dataframe["Datum"] = pd.to_datetime(dataframe["Datum"]) + dataframe.set_index("Datum", inplace=True) # For some pandas version, it is necessary to set the frequence of the df: - dataframe = dataframe.asfreq('H') + dataframe = dataframe.asfreq("H") # The time of the input data is given locally, so it has to be adapted to the # correct timezone: - dataframe.index = dataframe.index.tz_localize(tz='Asia/Muscat') + dataframe.index = dataframe.index.tz_localize(tz="Asia/Muscat") # Precalculation - data_precalc = csp_precalc(latitude, longitude, - collector_tilt, collector_azimuth, cleanliness, - eta_0, c_1, c_2, - temp_collector_inlet, temp_collector_outlet, - dataframe['t_amb'], - a_1, a_2, - E_dir_hor=dataframe['E_dir_hor']) - - data_precalc['ES_load_actual_entsoe_power_statistics'] = list( - dataframe['ES_load_actual_entsoe_power_statistics'].iloc[:periods]) - - data_precalc.to_csv(os.path.join(results_path, 'results_csp_plant_precalc.csv')) + data_precalc = csp_precalc( + latitude, + longitude, + collector_tilt, + collector_azimuth, + cleanliness, + eta_0, + c_1, + c_2, + temp_collector_inlet, + temp_collector_outlet, + dataframe["t_amb"], + a_1, + a_2, + E_dir_hor=dataframe["E_dir_hor"], + ) + + data_precalc["ES_load_actual_entsoe_power_statistics"] = list( + dataframe["ES_load_actual_entsoe_power_statistics"].iloc[:periods] + ) + + data_precalc.to_csv( + os.path.join(results_path, "results_csp_plant_precalc.csv") + ) # Regular oemof_system @@ -84,90 +97,121 @@ def csp_plant_example(): size_collector = 1000 # Busses - bth = solph.Bus(label='thermal') - bel = solph.Bus(label='electricity') - bcol = solph.Bus(label='solar') + bth = solph.Bus(label="thermal") + bel = solph.Bus(label="electricity") + bcol = solph.Bus(label="solar") # Sources and sinks col_heat = solph.components.Source( - label='collector_heat', - outputs={bcol: solph.Flow( - fix=data_precalc['collector_heat'], - nominal_value=size_collector)}) + label="collector_heat", + outputs={ + bcol: solph.Flow( + fix=data_precalc["collector_heat"], + nominal_value=size_collector, + ) + }, + ) el_grid = solph.components.Source( - label='grid', - outputs={bel: solph.Flow(variable_costs=costs_electricity)}) + label="grid", + outputs={bel: solph.Flow(variable_costs=costs_electricity)}, + ) backup = solph.components.Source( - label='backup', - outputs={bth: solph.Flow(variable_costs=backup_costs)}) + label="backup", outputs={bth: solph.Flow(variable_costs=backup_costs)} + ) consumer = solph.components.Sink( - label='demand', - inputs={bel: solph.Flow( - fix=data_precalc['ES_load_actual_entsoe_power_statistics'], - nominal_value=1)}) + label="demand", + inputs={ + bel: solph.Flow( + fix=data_precalc["ES_load_actual_entsoe_power_statistics"], + nominal_value=1, + ) + }, + ) ambience_sol = solph.components.Sink( - label='ambience_sol', - inputs={bcol: solph.Flow()}) + label="ambience_sol", inputs={bcol: solph.Flow()} + ) - # Transformer and storages - collector = solph.components.Transformer( - label='collector', - inputs={ - bcol: solph.Flow(), - bel: solph.Flow()}, + # Converter and storages + collector = solph.components.Converter( + label="collector", + inputs={bcol: solph.Flow(), bel: solph.Flow()}, outputs={bth: solph.Flow()}, conversion_factors={ bcol: 1, bel: elec_consumption * (1 - additional_losses), - bth: 1 - additional_losses}) + bth: 1 - additional_losses, + }, + ) - turbine = solph.components.Transformer( - label='turbine', + turbine = solph.components.Converter( + label="turbine", inputs={bth: solph.Flow()}, outputs={bel: solph.Flow()}, - conversion_factors={bel: conversion_factor_turbine}) + conversion_factors={bel: conversion_factor_turbine}, + ) storage = solph.components.GenericStorage( - label='storage_th', + label="storage_th", inputs={bth: solph.Flow()}, outputs={bth: solph.Flow()}, loss_rate=cap_loss, inflow_conversion_factor=conversion_storage, outflow_conversion_factor=conversion_storage, - investment=solph.Investment(ep_costs=costs_storage)) + investment=solph.Investment(ep_costs=costs_storage), + ) # Build the system and solve the problem dataframe.index.freq = pd.infer_freq(dataframe.index) date_time_index = dataframe.index energysystem = solph.EnergySystem(timeindex=date_time_index) - energysystem.add(bth, bcol, bel, col_heat, el_grid, backup, consumer, - ambience_sol, collector, turbine, storage) + energysystem.add( + bth, + bcol, + bel, + col_heat, + el_grid, + backup, + consumer, + ambience_sol, + collector, + turbine, + storage, + ) # Create and solve the optimization model model = solph.Model(energysystem) - model.solve(solver='cbc', solve_kwargs={'tee': True}) + model.solve(solver="cbc", solve_kwargs={"tee": True}) # Get results results = solph.processing.results(model) - electricity_bus = solph.views.node(results, 'electricity')['sequences'] - thermal_bus = solph.views.node(results, 'thermal')['sequences'] - solar_bus = solph.views.node(results, 'solar')['sequences'] + electricity_bus = solph.views.node(results, "electricity")["sequences"] + thermal_bus = solph.views.node(results, "thermal")["sequences"] + solar_bus = solph.views.node(results, "solar")["sequences"] df = pd.merge( - pd.merge(electricity_bus, thermal_bus, left_index=True, right_index=True), - solar_bus, left_index=True, right_index=True) - df.to_csv(os.path.join(results_path, 'csp_plant_results.csv')) + pd.merge( + electricity_bus, thermal_bus, left_index=True, right_index=True + ), + solar_bus, + left_index=True, + right_index=True, + ) + df.to_csv(os.path.join(results_path, "csp_plant_results.csv")) # Example plot - fig, ax = plt.subplots() - ax.plot(list(range(periods)), thermal_bus[(('collector', 'thermal'), 'flow')][:-1]) - ax.set(xlabel='time [h]', ylabel='Q_coll [W]', - title='Heat of the collector') + _, ax = plt.subplots() + ax.plot( + list(range(periods)), + thermal_bus[(("collector", "thermal"), "flow")][:-1], + ) + ax.set( + xlabel="time [h]", ylabel="Q_coll [W]", title="Heat of the collector" + ) ax.grid() plt.show() diff --git a/examples/solar_thermal_collector/_plots.py b/examples/solar_thermal_collector/_plots.py index 94816002..a2d206d0 100644 --- a/examples/solar_thermal_collector/_plots.py +++ b/examples/solar_thermal_collector/_plots.py @@ -3,33 +3,35 @@ """ import os + import matplotlib.pyplot as plt + def plot_collector_heat(data_precalc, periods, eta_0): - ''' + """ Plot showing the difference between a constant efficiency and the efficiency depending on the ambient temperature for the same irradiance and hour of the day. - ''' + """ - heat_calc = data_precalc['collectors_heat'] - irradiance_on_collector = data_precalc['col_ira'] + heat_calc = data_precalc["collectors_heat"] + irradiance_on_collector = data_precalc["col_ira"] heat_compare = irradiance_on_collector * eta_0 t = list(range(1, periods + 1)) - fig, ax = plt.subplots() - ax.plot(t, heat_calc, label='Solar thermal precalculation') - ax.plot(t, heat_compare, label='constant efficiency') + _, ax = plt.subplots() + ax.plot(t, heat_calc, label="Solar thermal precalculation") + ax.plot(t, heat_compare, label="constant efficiency") ax.set( - xlabel='time in h', - ylabel='Q_coll in W/m2', - title='Heat of the collector', + xlabel="time in h", + ylabel="Q_coll in W/m2", + title="Heat of the collector", ) ax.grid() ax.legend() base_path = os.path.dirname(os.path.abspath(os.path.join(__file__))) - plt.savefig(os.path.join(base_path, 'results/compare_precalculations.png')) + plt.savefig(os.path.join(base_path, "results/compare_precalculations.png")) plt.show() return diff --git a/examples/solar_thermal_collector/flat_plate_collector.py b/examples/solar_thermal_collector/flat_plate_collector.py index 7342cbec..95a28575 100644 --- a/examples/solar_thermal_collector/flat_plate_collector.py +++ b/examples/solar_thermal_collector/flat_plate_collector.py @@ -11,18 +11,19 @@ import os import pandas as pd +from oemof.tools import economics from oemof import solph -from oemof.tools import economics from oemof.thermal.solar_thermal_collector import flat_plate_precalc from ._plots import plot_collector_heat + def flat_plate_collector_example(): # Set paths base_path = os.path.dirname(os.path.abspath(os.path.join(__file__))) - data_path = os.path.join(base_path, 'data') - results_path = os.path.join(base_path, 'results') + data_path = os.path.join(base_path, "data") + results_path = os.path.join(base_path, "results") if not os.path.exists(results_path): os.mkdir(results_path) @@ -39,17 +40,18 @@ def flat_plate_collector_example(): delta_temp_n = 10 # Read input data - input_data = pd.read_csv(os.path.join(data_path, 'data_flat_collector.csv')).head(periods) - input_data['Datum'] = pd.to_datetime(input_data['Datum']) - input_data.set_index('Datum', inplace=True) - input_data.index = input_data.index.tz_localize(tz='Europe/Berlin') - input_data = input_data.asfreq('H') + input_data = pd.read_csv( + os.path.join(data_path, "data_flat_collector.csv") + ).head(periods) + input_data["Datum"] = pd.to_datetime(input_data["Datum"]) + input_data.set_index("Datum", inplace=True) + input_data.index = input_data.index.tz_localize(tz="Europe/Berlin") + input_data = input_data.asfreq("H") demand_df = pd.read_csv( - os.path.join(base_path, 'data', 'heat_demand.csv'), - sep=',' + os.path.join(base_path, "data", "heat_demand.csv"), sep="," ) - demand = list(demand_df['heat_demand'].iloc[:periods]) + demand = list(demand_df["heat_demand"].iloc[:periods]) # Precalculation # - calculate global irradiance on the collector area @@ -65,17 +67,15 @@ def flat_plate_collector_example(): a_2, temp_collector_inlet, delta_temp_n, - irradiance_global=input_data['global_horizontal_W_m2'], - irradiance_diffuse=input_data['diffuse_horizontal_W_m2'], - temp_amb=input_data['temp_amb'], + irradiance_global=input_data["global_horizontal_W_m2"], + irradiance_diffuse=input_data["diffuse_horizontal_W_m2"], + temp_amb=input_data["temp_amb"], ) precalc_data.to_csv( - os.path.join(results_path, 'flat_plate_precalcs.csv'), - sep=';' + os.path.join(results_path, "flat_plate_precalcs.csv"), sep=";" ) - # regular oemof system # # Parameters for the energy system @@ -89,17 +89,17 @@ def flat_plate_collector_example(): size_collector = 10 # m2 # busses - bth = solph.Bus(label='thermal') - bel = solph.Bus(label='electricity') - bcol = solph.Bus(label='solar') + bth = solph.Bus(label="thermal") + bel = solph.Bus(label="electricity") + bcol = solph.Bus(label="solar") # source for collector heat. # - actual_value is the precalculated collector heat - collector_heat = solph.components.Source( - label='collector_heat', + label="collector_heat", outputs={ bcol: solph.Flow( - fix=precalc_data['collectors_heat'], + fix=precalc_data["collectors_heat"], nominal_value=size_collector, ) }, @@ -107,36 +107,37 @@ def flat_plate_collector_example(): # sources and sinks el_grid = solph.components.Source( - label='grid', outputs={bel: solph.Flow(variable_costs=costs_electricity)} + label="grid", + outputs={bel: solph.Flow(variable_costs=costs_electricity)}, ) backup = solph.components.Source( - label='backup', outputs={bth: solph.Flow(variable_costs=backup_costs)} + label="backup", outputs={bth: solph.Flow(variable_costs=backup_costs)} ) consumer = solph.components.Sink( - label='demand', + label="demand", inputs={bth: solph.Flow(fix=demand, nominal_value=1)}, ) collector_excess_heat = solph.components.Sink( - label='collector_excess_heat', inputs={bcol: solph.Flow()} + label="collector_excess_heat", inputs={bcol: solph.Flow()} ) - # transformer and storage - collector = solph.components.Transformer( - label='collector', + # converter and storage + collector = solph.components.Converter( + label="collector", inputs={bcol: solph.Flow(), bel: solph.Flow()}, outputs={bth: solph.Flow()}, conversion_factors={ bcol: 1, bel: elec_consumption * (1 - peripheral_losses), - bth: 1 - peripheral_losses + bth: 1 - peripheral_losses, }, ) storage = solph.components.GenericStorage( - label='storage', + label="storage", inputs={bth: solph.Flow()}, outputs={bth: solph.Flow()}, loss_rate=storage_loss_rate, @@ -164,18 +165,23 @@ def flat_plate_collector_example(): # Create and solve the optimization model model = solph.Model(energysystem) - model.solve(solver='cbc', solve_kwargs={'tee': True}) + model.solve(solver="cbc", solve_kwargs={"tee": True}) # Get results results = solph.processing.results(model) - electricity_bus = solph.views.node(results, 'electricity')['sequences'] - thermal_bus = solph.views.node(results, 'thermal')['sequences'] - solar_bus = solph.views.node(results, 'solar')['sequences'] + electricity_bus = solph.views.node(results, "electricity")["sequences"] + thermal_bus = solph.views.node(results, "thermal")["sequences"] + solar_bus = solph.views.node(results, "solar")["sequences"] df = pd.merge( - pd.merge(electricity_bus, thermal_bus, left_index=True, right_index=True), - solar_bus, left_index=True, right_index=True) - df.to_csv(os.path.join(results_path, 'flat_plate_results.csv')) + pd.merge( + electricity_bus, thermal_bus, left_index=True, right_index=True + ), + solar_bus, + left_index=True, + right_index=True, + ) + df.to_csv(os.path.join(results_path, "flat_plate_results.csv")) # Example plot plot_collector_heat(precalc_data, periods, eta_0) diff --git a/examples/solar_thermal_collector/flat_plate_collector_facade.py b/examples/solar_thermal_collector/flat_plate_collector_facade.py index e1c6e25a..4950f9dd 100644 --- a/examples/solar_thermal_collector/flat_plate_collector_facade.py +++ b/examples/solar_thermal_collector/flat_plate_collector_facade.py @@ -9,17 +9,20 @@ SPDX-License-Identifier: GPL-3.0-or-later """ import os -import pandas as pd + import matplotlib.pyplot as plt -from oemof.thermal import facades -from oemof import solph +import pandas as pd from oemof.tools import economics +from oemof import solph +from oemof.thermal import facades + + def flat_plate_collector_facade_example(): # Set paths base_path = os.path.dirname(os.path.abspath(os.path.join(__file__))) - data_path = os.path.join(base_path, 'data') - results_path = os.path.join(base_path, 'results') + data_path = os.path.join(base_path, "data") + results_path = os.path.join(base_path, "results") if not os.path.exists(results_path): os.mkdir(results_path) @@ -45,28 +48,28 @@ def flat_plate_collector_facade_example(): size_collector = 10 # m2 # Read input data - input_data = pd.read_csv(os.path.join(data_path, 'data_flat_collector.csv')).head(periods) - input_data['Datum'] = pd.to_datetime(input_data['Datum']) - input_data.set_index('Datum', inplace=True) - input_data.index = input_data.index.tz_localize(tz='Europe/Berlin') - input_data = input_data.asfreq('H') + input_data = pd.read_csv( + os.path.join(data_path, "data_flat_collector.csv") + ).head(periods) + input_data["Datum"] = pd.to_datetime(input_data["Datum"]) + input_data.set_index("Datum", inplace=True) + input_data.index = input_data.index.tz_localize(tz="Europe/Berlin") + input_data = input_data.asfreq("H") demand_df = pd.read_csv( - os.path.join(data_path, 'heat_demand.csv'), - sep=',' + os.path.join(data_path, "heat_demand.csv"), sep="," ) - demand = list(demand_df['heat_demand'].iloc[:periods]) - + demand = list(demand_df["heat_demand"].iloc[:periods]) # Set up an energy system model # Busses - bth = solph.Bus(label='thermal') - bel = solph.Bus(label='electricity') + bth = solph.Bus(label="thermal") + bel = solph.Bus(label="electricity") # Collector collector = facades.SolarThermalCollector( - label='solar_collector', + label="solar_collector", heat_out_bus=bth, electricity_in_bus=bel, electrical_consumption=elec_consumption, @@ -81,31 +84,33 @@ def flat_plate_collector_facade_example(): a_2=a_2, temp_collector_inlet=temp_collector_inlet, delta_temp_n=delta_temp_n, - irradiance_global=input_data['global_horizontal_W_m2'], - irradiance_diffuse=input_data['diffuse_horizontal_W_m2'], - temp_amb=input_data['temp_amb']) + irradiance_global=input_data["global_horizontal_W_m2"], + irradiance_diffuse=input_data["diffuse_horizontal_W_m2"], + temp_amb=input_data["temp_amb"], + ) # Sources and sinks el_grid = solph.components.Source( - label='grid', outputs={bel: solph.Flow(variable_costs=costs_electricity)} + label="grid", + outputs={bel: solph.Flow(variable_costs=costs_electricity)}, ) backup = solph.components.Source( - label='backup', outputs={bth: solph.Flow(variable_costs=backup_costs)} + label="backup", outputs={bth: solph.Flow(variable_costs=backup_costs)} ) consumer = solph.components.Sink( - label='demand', + label="demand", inputs={bth: solph.Flow(fix=demand, nominal_value=1)}, ) collector_excess_heat = solph.components.Sink( - label='collector_excess_heat', inputs={bth: solph.Flow()} + label="collector_excess_heat", inputs={bth: solph.Flow()} ) # Storage storage = solph.components.GenericStorage( - label='storage', + label="storage", inputs={bth: solph.Flow()}, outputs={bth: solph.Flow()}, loss_rate=storage_loss_rate, @@ -131,31 +136,31 @@ def flat_plate_collector_facade_example(): # Create and solve the optimization model model = solph.Model(energysystem) - model.solve(solver='cbc', solve_kwargs={'tee': True}) + model.solve(solver="cbc", solve_kwargs={"tee": True}) # Get results results = solph.processing.results(model) - collector_inflow = solph.views.node( - results, 'solar_collector-inflow')['sequences'] - thermal_bus = solph.views.node(results, 'thermal')['sequences'] - electricity_bus = solph.views.node(results, 'electricity')['sequences'] + collector_inflow = solph.views.node(results, "solar_collector-inflow")[ + "sequences" + ] + thermal_bus = solph.views.node(results, "thermal")["sequences"] + electricity_bus = solph.views.node(results, "electricity")["sequences"] df = pd.DataFrame() df = df.append(collector_inflow) - df = df.join(thermal_bus, lsuffix='_1') - df = df.join(electricity_bus, lsuffix='_1') - df.to_csv(os.path.join(results_path, 'facade_results.csv')) + df = df.join(thermal_bus, lsuffix="_1") + df = df.join(electricity_bus, lsuffix="_1") + df.to_csv(os.path.join(results_path, "facade_results.csv")) # Example plot heat_calc = collector_inflow t = list(range(1, periods + 1)) - fig, ax = plt.subplots() + _, ax = plt.subplots() ax.plot(t, heat_calc[:-1]) ax.set( - xlabel='time in h', - ylabel='Q_coll in W', - title='Heat of the collector') + xlabel="time in h", ylabel="Q_coll in W", title="Heat of the collector" + ) ax.grid() plt.show() diff --git a/examples/solar_thermal_collector/flat_plate_collector_investment.py b/examples/solar_thermal_collector/flat_plate_collector_investment.py index 7c35ac64..a231cbf7 100644 --- a/examples/solar_thermal_collector/flat_plate_collector_investment.py +++ b/examples/solar_thermal_collector/flat_plate_collector_investment.py @@ -11,18 +11,19 @@ import os import pandas as pd +from oemof.tools import economics from oemof import solph -from oemof.tools import economics from oemof.thermal.solar_thermal_collector import flat_plate_precalc from ._plots import plot_collector_heat + def flat_plate_collector_investment_example(): # Set paths base_path = os.path.dirname(os.path.abspath(os.path.join(__file__))) - data_path = os.path.join(base_path, 'data') - results_path = os.path.join(base_path, 'results') + data_path = os.path.join(base_path, "data") + results_path = os.path.join(base_path, "results") if not os.path.exists(results_path): os.mkdir(results_path) @@ -39,17 +40,18 @@ def flat_plate_collector_investment_example(): delta_temp_n = 10 # Read input data - input_data = pd.read_csv(os.path.join(data_path, 'data_flat_collector.csv')).head(periods) - input_data['Datum'] = pd.to_datetime(input_data['Datum']) - input_data.set_index('Datum', inplace=True) - input_data.index = input_data.index.tz_localize(tz='Europe/Berlin') - input_data = input_data.asfreq('H') + input_data = pd.read_csv( + os.path.join(data_path, "data_flat_collector.csv") + ).head(periods) + input_data["Datum"] = pd.to_datetime(input_data["Datum"]) + input_data.set_index("Datum", inplace=True) + input_data.index = input_data.index.tz_localize(tz="Europe/Berlin") + input_data = input_data.asfreq("H") demand_df = pd.read_csv( - os.path.join(data_path, 'heat_demand.csv'), - sep=',' + os.path.join(data_path, "heat_demand.csv"), sep="," ) - demand = list(demand_df['heat_demand'].iloc[:periods]) + demand = list(demand_df["heat_demand"].iloc[:periods]) # Precalculation # - calculate global irradiance on the collector area @@ -65,17 +67,15 @@ def flat_plate_collector_investment_example(): a_2, temp_collector_inlet, delta_temp_n, - irradiance_global=input_data['global_horizontal_W_m2'], - irradiance_diffuse=input_data['diffuse_horizontal_W_m2'], - temp_amb=input_data['temp_amb'], + irradiance_global=input_data["global_horizontal_W_m2"], + irradiance_diffuse=input_data["diffuse_horizontal_W_m2"], + temp_amb=input_data["temp_amb"], ) precalc_data.to_csv( - os.path.join(results_path, 'flate_plate_precalcs.csv'), - sep=';' + os.path.join(results_path, "flate_plate_precalcs.csv"), sep=";" ) - # Regular oemof system # Parameters for the energy system @@ -89,17 +89,17 @@ def flat_plate_collector_investment_example(): conversion_storage = 0.98 # Busses - bth = solph.Bus(label='thermal') - bel = solph.Bus(label='electricity') - bcol = solph.Bus(label='solar') + bth = solph.Bus(label="thermal") + bel = solph.Bus(label="electricity") + bcol = solph.Bus(label="solar") # Source for collector heat. # - actual_value is the precalculated collector heat - collector_heat = solph.components.Source( - label='collector_heat', + label="collector_heat", outputs={ bcol: solph.Flow( - fix=precalc_data['collectors_heat'], + fix=precalc_data["collectors_heat"], investment=solph.Investment(ep_costs=costs_collector), ) }, @@ -107,36 +107,37 @@ def flat_plate_collector_investment_example(): # Sources and sinks el_grid = solph.components.Source( - label='grid', outputs={bel: solph.Flow(variable_costs=costs_electricity)} + label="grid", + outputs={bel: solph.Flow(variable_costs=costs_electricity)}, ) backup = solph.components.Source( - label='backup', outputs={bth: solph.Flow(variable_costs=backup_costs)} + label="backup", outputs={bth: solph.Flow(variable_costs=backup_costs)} ) consumer = solph.components.Sink( - label='demand', + label="demand", inputs={bth: solph.Flow(fix=demand, nominal_value=1)}, ) collector_excess_heat = solph.components.Sink( - label='collector_excess_heat', inputs={bcol: solph.Flow()} + label="collector_excess_heat", inputs={bcol: solph.Flow()} ) - # Transformer and storage - collector = solph.components.Transformer( - label='collector', + # Converter and storage + collector = solph.components.Converter( + label="collector", inputs={bcol: solph.Flow(), bel: solph.Flow()}, outputs={bth: solph.Flow()}, conversion_factors={ bcol: 1, bel: elec_consumption * (1 - peripheral_losses), - bth: 1 - peripheral_losses + bth: 1 - peripheral_losses, }, ) storage = solph.components.GenericStorage( - label='storage', + label="storage", inputs={bth: solph.Flow()}, outputs={bth: solph.Flow()}, loss_rate=storage_loss_rate, @@ -164,18 +165,23 @@ def flat_plate_collector_investment_example(): # Create and solve the optimization model model = solph.Model(energysystem) - model.solve(solver='cbc', solve_kwargs={'tee': True}) + model.solve(solver="cbc", solve_kwargs={"tee": True}) # Get results results = solph.processing.results(model) - electricity_bus = solph.views.node(results, 'electricity')['sequences'] - thermal_bus = solph.views.node(results, 'thermal')['sequences'] - solar_bus = solph.views.node(results, 'solar')['sequences'] + electricity_bus = solph.views.node(results, "electricity")["sequences"] + thermal_bus = solph.views.node(results, "thermal")["sequences"] + solar_bus = solph.views.node(results, "solar")["sequences"] df = pd.merge( - pd.merge(electricity_bus, thermal_bus, left_index=True, right_index=True), - solar_bus, left_index=True, right_index=True) - df.to_csv(os.path.join(results_path, 'flat_plate_results.csv')) + pd.merge( + electricity_bus, thermal_bus, left_index=True, right_index=True + ), + solar_bus, + left_index=True, + right_index=True, + ) + df.to_csv(os.path.join(results_path, "flat_plate_results.csv")) # Example plot plot_collector_heat(precalc_data, periods, eta_0) diff --git a/examples/stratified_thermal_storage/investment_fixed_ratio_facade.py b/examples/stratified_thermal_storage/investment_fixed_ratio_facade.py index e6f605f8..e22d6211 100644 --- a/examples/stratified_thermal_storage/investment_fixed_ratio_facade.py +++ b/examples/stratified_thermal_storage/investment_fixed_ratio_facade.py @@ -3,37 +3,43 @@ """ import os -import pandas as pd + import numpy as np +import pandas as pd +from oemof.solph import Bus +from oemof.solph import EnergySystem +from oemof.solph import Flow +from oemof.solph import Model +from oemof.solph import processing +from oemof.solph.components import Sink +from oemof.solph.components import Source -from oemof.thermal.stratified_thermal_storage import calculate_storage_u_value from oemof.thermal import facades - -from oemof.solph import (processing, Bus, Flow, - Model, EnergySystem) -from oemof.solph.components import Source, Sink +from oemof.thermal.stratified_thermal_storage import calculate_storage_u_value def fixed_ratio_invest_facade_example(): # Set paths data_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), - 'data/stratified_thermal_storage.csv') + "data/stratified_thermal_storage.csv", + ) # Read input data - input_data = pd.read_csv(data_path, index_col=0, header=0)['var_value'] + input_data = pd.read_csv(data_path, index_col=0, header=0)["var_value"] # Precalculation u_value = calculate_storage_u_value( - input_data['s_iso'], - input_data['lamb_iso'], - input_data['alpha_inside'], - input_data['alpha_outside']) + input_data["s_iso"], + input_data["lamb_iso"], + input_data["alpha_inside"], + input_data["alpha_outside"], + ) # Set up an energy system model - solver = 'cbc' + solver = "cbc" periods = 100 - datetimeindex = pd.date_range('1/1/2019', periods=periods, freq='H') + datetimeindex = pd.date_range("1/1/2019", periods=periods, freq="H") demand_timeseries = np.zeros(periods) demand_timeseries[-5:] = 1 heat_feedin_timeseries = np.zeros(periods) @@ -41,70 +47,79 @@ def fixed_ratio_invest_facade_example(): energysystem = EnergySystem(timeindex=datetimeindex) - bus_heat = Bus(label='bus_heat') + bus_heat = Bus(label="bus_heat") heat_source = Source( - label='heat_source', - outputs={bus_heat: Flow( - nominal_value=1, - fix=heat_feedin_timeseries)}) + label="heat_source", + outputs={bus_heat: Flow(nominal_value=1, fix=heat_feedin_timeseries)}, + ) shortage = Source( - label='shortage', - outputs={bus_heat: Flow(variable_costs=1e6)}) + label="shortage", outputs={bus_heat: Flow(variable_costs=1e6)} + ) - excess = Sink( - label='excess', - inputs={bus_heat: Flow()}) + excess = Sink(label="excess", inputs={bus_heat: Flow()}) heat_demand = Sink( - label='heat_demand', - inputs={bus_heat: Flow( - nominal_value=1, - fix=demand_timeseries)}) + label="heat_demand", + inputs={bus_heat: Flow(nominal_value=1, fix=demand_timeseries)}, + ) thermal_storage = facades.StratifiedThermalStorage( - label='thermal_storage', + label="thermal_storage", bus=bus_heat, - diameter=input_data['diameter'], # TODO: setting to zero should give an error - temp_h=input_data['temp_h'], - temp_c=input_data['temp_c'], - temp_env=input_data['temp_env'], + diameter=input_data[ + "diameter" + ], # TODO: setting to zero should give an error + temp_h=input_data["temp_h"], + temp_c=input_data["temp_c"], + temp_env=input_data["temp_env"], u_value=u_value, expandable=True, capacity_cost=0, storage_capacity_cost=400, minimum_storage_capacity=1, # TODO: setting to zero should give an error! invest_relation_input_capacity=1 / 6, - min_storage_level=input_data['min_storage_level'], - max_storage_level=input_data['max_storage_level'], - efficiency=input_data['efficiency'], - marginal_cost=0.0001 + min_storage_level=input_data["min_storage_level"], + max_storage_level=input_data["max_storage_level"], + efficiency=input_data["efficiency"], + marginal_cost=0.0001, ) - energysystem.add(bus_heat, heat_source, shortage, excess, heat_demand, thermal_storage) + energysystem.add( + bus_heat, heat_source, shortage, excess, heat_demand, thermal_storage + ) # Create and solve the optimization model optimization_model = Model(energysystem) - optimization_model.solve(solver=solver, - solve_kwargs={'tee': False, 'keepfiles': False}) + optimization_model.solve( + solver=solver, solve_kwargs={"tee": False, "keepfiles": False} + ) # Get results results = processing.results(optimization_model) - string_results = processing.convert_keys_to_strings(results) - sequences = {k: v['sequences'] for k, v in string_results.items()} - df = pd.concat(sequences, axis=1) - # Print storage sizing - built_storage_capacity = results[thermal_storage, None]['scalars']['invest'] - initial_storage_capacity = results[thermal_storage, None]['scalars']['init_content'] - maximum_heat_flow_charging = results[bus_heat, thermal_storage]['scalars']['invest'] - - dash = '-' * 50 + built_storage_capacity = results[thermal_storage, None]["scalars"][ + "invest" + ] + maximum_heat_flow_charging = results[bus_heat, thermal_storage]["scalars"][ + "invest" + ] + + dash = "-" * 50 print(dash) - print('{:>32s}{:>15.3f}'.format('Invested capacity [MW]', maximum_heat_flow_charging)) - print('{:>32s}{:>15.3f}'.format('Invested storage capacity [MWh]', built_storage_capacity)) + print( + "{:>32s}{:>15.3f}".format( + "Invested capacity [MW]", maximum_heat_flow_charging + ) + ) + print( + "{:>32s}{:>15.3f}".format( + "Invested storage capacity [MWh]", built_storage_capacity + ) + ) print(dash) + if __name__ == "__main__": fixed_ratio_invest_facade_example() diff --git a/examples/stratified_thermal_storage/investment_fixed_ratio_generic_storage.py b/examples/stratified_thermal_storage/investment_fixed_ratio_generic_storage.py index 3d0e37ad..f6c5f0c1 100644 --- a/examples/stratified_thermal_storage/investment_fixed_ratio_generic_storage.py +++ b/examples/stratified_thermal_storage/investment_fixed_ratio_generic_storage.py @@ -6,68 +6,75 @@ """ import os -import pandas as pd -import numpy as np -from oemof.thermal.stratified_thermal_storage import (calculate_storage_u_value, - calculate_losses) -from oemof.solph import (Bus, Flow, - Investment, Model, EnergySystem) -from oemof.solph.components import GenericStorage, Source, Sink +import numpy as np +import pandas as pd +from oemof.solph import Bus +from oemof.solph import EnergySystem +from oemof.solph import Flow +from oemof.solph import Investment +from oemof.solph import Model from oemof.solph import processing +from oemof.solph.components import GenericStorage +from oemof.solph.components import Sink +from oemof.solph.components import Source + +from oemof.thermal.stratified_thermal_storage import calculate_losses +from oemof.thermal.stratified_thermal_storage import calculate_storage_u_value def fixed_ratio_invest_example(): # Set paths data_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), - 'data/stratified_thermal_storage.csv') + "data/stratified_thermal_storage.csv", + ) # Read input data - input_data = pd.read_csv(data_path, index_col=0, header=0)['var_value'] + input_data = pd.read_csv(data_path, index_col=0, header=0)["var_value"] # Precalculation u_value = calculate_storage_u_value( - input_data['s_iso'], - input_data['lamb_iso'], - input_data['alpha_inside'], - input_data['alpha_outside']) + input_data["s_iso"], + input_data["lamb_iso"], + input_data["alpha_inside"], + input_data["alpha_outside"], + ) loss_rate, fixed_losses_relative, fixed_losses_absolute = calculate_losses( u_value, - input_data['diameter'], - input_data['temp_h'], - input_data['temp_c'], - input_data['temp_env']) - + input_data["diameter"], + input_data["temp_h"], + input_data["temp_c"], + input_data["temp_env"], + ) def print_parameters(): parameter = { - 'EQ-cost [Eur/]': 0, - 'U-value [W/(m2*K)]': u_value, - 'Loss rate [-]': loss_rate, - 'Fixed relative losses [-]': fixed_losses_relative, - 'Fixed absolute losses [MWh]': fixed_losses_absolute, + "EQ-cost [Eur/]": 0, + "U-value [W/(m2*K)]": u_value, + "Loss rate [-]": loss_rate, + "Fixed relative losses [-]": fixed_losses_relative, + "Fixed absolute losses [MWh]": fixed_losses_absolute, } - dash = '-' * 50 + dash = "-" * 50 print(dash) - print('{:>32s}{:>15s}'.format('Parameter name', 'Value')) + print("{:>32s}{:>15s}".format("Parameter name", "Value")) print(dash) for name, param in parameter.items(): - print('{:>32s}{:>15.5f}'.format(name, param)) + print("{:>32s}{:>15.5f}".format(name, param)) print(dash) - print_parameters() # Set up an energy system model - solver = 'cbc' + solver = "cbc" periods = 100 - datetimeindex = pd.date_range('1/1/2019', periods=periods, freq='H') + datetimeindex = pd.date_range("1/1/2019", periods=periods, freq="H") demand_timeseries = np.zeros(periods) demand_timeseries[-5:] = 1 heat_feedin_timeseries = np.zeros(periods) @@ -75,69 +82,78 @@ def print_parameters(): energysystem = EnergySystem(timeindex=datetimeindex) - bus_heat = Bus(label='bus_heat') + bus_heat = Bus(label="bus_heat") heat_source = Source( - label='heat_source', - outputs={bus_heat: Flow( - nominal_value=1, - fix=heat_feedin_timeseries)}) + label="heat_source", + outputs={bus_heat: Flow(nominal_value=1, fix=heat_feedin_timeseries)}, + ) shortage = Source( - label='shortage', - outputs={bus_heat: Flow(variable_costs=1e6)}) + label="shortage", outputs={bus_heat: Flow(variable_costs=1e6)} + ) - excess = Sink( - label='excess', - inputs={bus_heat: Flow()}) + excess = Sink(label="excess", inputs={bus_heat: Flow()}) heat_demand = Sink( - label='heat_demand', - inputs={bus_heat: Flow( - nominal_value=1, - fix=demand_timeseries)}) + label="heat_demand", + inputs={bus_heat: Flow(nominal_value=1, fix=demand_timeseries)}, + ) thermal_storage = GenericStorage( - label='thermal_storage', - inputs={bus_heat: Flow( - investment=Investment())}, - outputs={bus_heat: Flow( - investment=Investment(), - variable_costs=0.0001)}, - min_storage_level=input_data['min_storage_level'], - max_storage_level=input_data['max_storage_level'], + label="thermal_storage", + inputs={bus_heat: Flow(investment=Investment())}, + outputs={ + bus_heat: Flow(investment=Investment(), variable_costs=0.0001) + }, + min_storage_level=input_data["min_storage_level"], + max_storage_level=input_data["max_storage_level"], loss_rate=loss_rate, fixed_losses_relative=fixed_losses_relative, fixed_losses_absolute=fixed_losses_absolute, - inflow_conversion_factor=input_data['inflow_conversion_factor'], - outflow_conversion_factor=input_data['outflow_conversion_factor'], + inflow_conversion_factor=input_data["inflow_conversion_factor"], + outflow_conversion_factor=input_data["outflow_conversion_factor"], invest_relation_input_output=1, invest_relation_input_capacity=1 / 6, - investment=Investment(ep_costs=400, minimum=1) + investment=Investment(ep_costs=400, minimum=1), ) - energysystem.add(bus_heat, heat_source, shortage, excess, heat_demand, thermal_storage) + energysystem.add( + bus_heat, heat_source, shortage, excess, heat_demand, thermal_storage + ) # Create and solve the optimization model optimization_model = Model(energysystem) - optimization_model.solve(solver=solver, - solve_kwargs={'tee': False, 'keepfiles': False}) + optimization_model.solve( + solver=solver, solve_kwargs={"tee": False, "keepfiles": False} + ) # Get results results = processing.results(optimization_model) string_results = processing.convert_keys_to_strings(results) - sequences = {k: v['sequences'] for k, v in string_results.items()} + sequences = {k: v["sequences"] for k, v in string_results.items()} df = pd.concat(sequences, axis=1) # Print storage sizing - built_storage_capacity = results[thermal_storage, None]['scalars']['invest'] - initial_storage_capacity = results[thermal_storage, None]['scalars']['init_content'] - maximum_heat_flow_charging = results[bus_heat, thermal_storage]['scalars']['invest'] - - dash = '-' * 50 + built_storage_capacity = results[thermal_storage, None]["scalars"][ + "invest" + ] + maximum_heat_flow_charging = results[bus_heat, thermal_storage]["scalars"][ + "invest" + ] + + dash = "-" * 50 print(dash) - print('{:>32s}{:>15.3f}'.format('Invested capacity [MW]', maximum_heat_flow_charging)) - print('{:>32s}{:>15.3f}'.format('Invested storage capacity [MWh]', built_storage_capacity)) + print( + "{:>32s}{:>15.3f}".format( + "Invested capacity [MW]", maximum_heat_flow_charging + ) + ) + print( + "{:>32s}{:>15.3f}".format( + "Invested storage capacity [MWh]", built_storage_capacity + ) + ) print(dash) diff --git a/examples/stratified_thermal_storage/investment_independent_facade.py b/examples/stratified_thermal_storage/investment_independent_facade.py index fd6df21e..f82c986a 100644 --- a/examples/stratified_thermal_storage/investment_independent_facade.py +++ b/examples/stratified_thermal_storage/investment_independent_facade.py @@ -6,37 +6,43 @@ """ import os -import pandas as pd + import numpy as np +import pandas as pd +from oemof.solph import Bus +from oemof.solph import EnergySystem +from oemof.solph import Flow +from oemof.solph import Model +from oemof.solph import processing +from oemof.solph.components import Sink +from oemof.solph.components import Source -from oemof.thermal.stratified_thermal_storage import calculate_storage_u_value from oemof.thermal import facades - -from oemof.solph import (processing, Bus, Flow, - Model, EnergySystem) -from oemof.solph.components import Source, Sink +from oemof.thermal.stratified_thermal_storage import calculate_storage_u_value def invest_independent_facade_example(): # Set paths data_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), - 'data/stratified_thermal_storage.csv') + "data/stratified_thermal_storage.csv", + ) # Read input data - input_data = pd.read_csv(data_path, index_col=0, header=0)['var_value'] + input_data = pd.read_csv(data_path, index_col=0, header=0)["var_value"] # Precalculation u_value = calculate_storage_u_value( - input_data['s_iso'], - input_data['lamb_iso'], - input_data['alpha_inside'], - input_data['alpha_outside']) + input_data["s_iso"], + input_data["lamb_iso"], + input_data["alpha_inside"], + input_data["alpha_outside"], + ) # Set up an energy system model - solver = 'cbc' + solver = "cbc" periods = 100 - datetimeindex = pd.date_range('1/1/2019', periods=periods, freq='H') + datetimeindex = pd.date_range("1/1/2019", periods=periods, freq="H") demand_timeseries = np.zeros(periods) demand_timeseries[-5:] = 1 heat_feedin_timeseries = np.zeros(periods) @@ -44,68 +50,74 @@ def invest_independent_facade_example(): energysystem = EnergySystem(timeindex=datetimeindex) - bus_heat = Bus(label='bus_heat') + bus_heat = Bus(label="bus_heat") heat_source = Source( - label='heat_source', - outputs={bus_heat: Flow( - nominal_value=1, - fix=heat_feedin_timeseries)}) + label="heat_source", + outputs={bus_heat: Flow(nominal_value=1, fix=heat_feedin_timeseries)}, + ) shortage = Source( - label='shortage', - outputs={bus_heat: Flow(variable_costs=1e6)}) + label="shortage", outputs={bus_heat: Flow(variable_costs=1e6)} + ) - excess = Sink( - label='excess', - inputs={bus_heat: Flow()}) + excess = Sink(label="excess", inputs={bus_heat: Flow()}) heat_demand = Sink( - label='heat_demand', - inputs={bus_heat: Flow( - nominal_value=1, - fix=demand_timeseries)}) + label="heat_demand", + inputs={bus_heat: Flow(nominal_value=1, fix=demand_timeseries)}, + ) thermal_storage = facades.StratifiedThermalStorage( - label='thermal_storage', + label="thermal_storage", bus=bus_heat, - diameter=input_data['diameter'], # TODO: setting to zero should give an error - temp_h=input_data['temp_h'], - temp_c=input_data['temp_c'], - temp_env=input_data['temp_env'], + diameter=input_data[ + "diameter" + ], # TODO: setting to zero should give an error + temp_h=input_data["temp_h"], + temp_c=input_data["temp_c"], + temp_env=input_data["temp_env"], u_value=u_value, expandable=True, capacity_cost=50, storage_capacity_cost=400, minimum_storage_capacity=1, # TODO: setting to zero should give an error! - min_storage_level=input_data['min_storage_level'], - max_storage_level=input_data['max_storage_level'], - efficiency=input_data['efficiency'], - marginal_cost=0.0001 + min_storage_level=input_data["min_storage_level"], + max_storage_level=input_data["max_storage_level"], + efficiency=input_data["efficiency"], + marginal_cost=0.0001, ) - energysystem.add(bus_heat, heat_source, shortage, excess, heat_demand, thermal_storage) + energysystem.add( + bus_heat, heat_source, shortage, excess, heat_demand, thermal_storage + ) # Create and solve the optimization model optimization_model = Model(energysystem) - optimization_model.solve(solver=solver, - solve_kwargs={'tee': False, 'keepfiles': False}) + optimization_model.solve( + solver=solver, solve_kwargs={"tee": False, "keepfiles": False} + ) # Get results results = processing.results(optimization_model) string_results = processing.convert_keys_to_strings(results) - sequences = {k: v['sequences'] for k, v in string_results.items()} + sequences = {k: v["sequences"] for k, v in string_results.items()} df = pd.concat(sequences, axis=1) # Print storage sizing - built_storage_capacity = results[thermal_storage, None]['scalars']['invest'] - initial_storage_capacity = results[thermal_storage, None]['scalars']['init_content'] - built_capacity = results[bus_heat, thermal_storage]['scalars']['invest'] + built_storage_capacity = results[thermal_storage, None]["scalars"][ + "invest" + ] + built_capacity = results[bus_heat, thermal_storage]["scalars"]["invest"] - dash = '-' * 50 + dash = "-" * 50 print(dash) - print('{:>32s}{:>15.3f}'.format('Invested capacity [MW]', built_capacity)) - print('{:>32s}{:>15.3f}'.format('Invested storage capacity [MWh]', built_storage_capacity)) + print("{:>32s}{:>15.3f}".format("Invested capacity [MW]", built_capacity)) + print( + "{:>32s}{:>15.3f}".format( + "Invested storage capacity [MWh]", built_storage_capacity + ) + ) print(dash) diff --git a/examples/stratified_thermal_storage/investment_independent_generic_storage.py b/examples/stratified_thermal_storage/investment_independent_generic_storage.py index 92e36454..467883b9 100644 --- a/examples/stratified_thermal_storage/investment_independent_generic_storage.py +++ b/examples/stratified_thermal_storage/investment_independent_generic_storage.py @@ -7,67 +7,75 @@ """ import os -import pandas as pd + import numpy as np +import pandas as pd +from oemof.solph import Bus +from oemof.solph import EnergySystem +from oemof.solph import Flow +from oemof.solph import Investment +from oemof.solph import Model +from oemof.solph import processing +from oemof.solph.components import GenericStorage +from oemof.solph.components import Sink +from oemof.solph.components import Source -from oemof.thermal.stratified_thermal_storage import (calculate_storage_u_value, - calculate_losses) -from oemof.solph import (processing, Bus, Flow, - Investment, Model, EnergySystem) -from oemof.solph.components import GenericStorage, Source, Sink +from oemof.thermal.stratified_thermal_storage import calculate_losses +from oemof.thermal.stratified_thermal_storage import calculate_storage_u_value def invest_independent_example(): # Set paths data_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), - 'data/stratified_thermal_storage.csv') + "data/stratified_thermal_storage.csv", + ) # Read input data - input_data = pd.read_csv(data_path, index_col=0, header=0)['var_value'] + input_data = pd.read_csv(data_path, index_col=0, header=0)["var_value"] # Precalculation u_value = calculate_storage_u_value( - input_data['s_iso'], - input_data['lamb_iso'], - input_data['alpha_inside'], - input_data['alpha_outside']) + input_data["s_iso"], + input_data["lamb_iso"], + input_data["alpha_inside"], + input_data["alpha_outside"], + ) loss_rate, fixed_losses_relative, fixed_losses_absolute = calculate_losses( u_value, - input_data['diameter'], - input_data['temp_h'], - input_data['temp_c'], - input_data['temp_env']) - + input_data["diameter"], + input_data["temp_h"], + input_data["temp_c"], + input_data["temp_env"], + ) def print_parameters(): parameter = { - 'EQ-cost [Eur/]': 0, - 'U-value [W/(m2*K)]': u_value, - 'Loss rate [-]': loss_rate, - 'Fixed relative losses [-]': fixed_losses_relative, - 'Fixed absolute losses [MWh]': fixed_losses_absolute, + "EQ-cost [Eur/]": 0, + "U-value [W/(m2*K)]": u_value, + "Loss rate [-]": loss_rate, + "Fixed relative losses [-]": fixed_losses_relative, + "Fixed absolute losses [MWh]": fixed_losses_absolute, } - dash = '-' * 50 + dash = "-" * 50 print(dash) - print('{:>32s}{:>15s}'.format('Parameter name', 'Value')) + print("{:>32s}{:>15s}".format("Parameter name", "Value")) print(dash) for name, param in parameter.items(): - print('{:>32s}{:>15.5f}'.format(name, param)) + print("{:>32s}{:>15.5f}".format(name, param)) print(dash) - print_parameters() # Set up an energy system model - solver = 'cbc' + solver = "cbc" periods = 100 - datetimeindex = pd.date_range('1/1/2019', periods=periods, freq='H') + datetimeindex = pd.date_range("1/1/2019", periods=periods, freq="H") demand_timeseries = np.zeros(periods) demand_timeseries[-5:] = 1 heat_feedin_timeseries = np.zeros(periods) @@ -75,69 +83,76 @@ def print_parameters(): energysystem = EnergySystem(timeindex=datetimeindex) - bus_heat = Bus(label='bus_heat') + bus_heat = Bus(label="bus_heat") heat_source = Source( - label='heat_source', - outputs={bus_heat: Flow( - nominal_value=1, - fix=heat_feedin_timeseries)}) + label="heat_source", + outputs={bus_heat: Flow(nominal_value=1, fix=heat_feedin_timeseries)}, + ) shortage = Source( - label='shortage', - outputs={bus_heat: Flow(variable_costs=1e6)}) + label="shortage", outputs={bus_heat: Flow(variable_costs=1e6)} + ) - excess = Sink( - label='excess', - inputs={bus_heat: Flow()}) + excess = Sink(label="excess", inputs={bus_heat: Flow()}) heat_demand = Sink( - label='heat_demand', - inputs={bus_heat: Flow( - nominal_value=1, - fix=demand_timeseries)}) + label="heat_demand", + inputs={bus_heat: Flow(nominal_value=1, fix=demand_timeseries)}, + ) thermal_storage = GenericStorage( - label='thermal_storage', - inputs={bus_heat: Flow( - investment=Investment(ep_costs=50))}, - outputs={bus_heat: Flow( - investment=Investment(), - variable_costs=0.0001)}, - min_storage_level=input_data['min_storage_level'], - max_storage_level=input_data['max_storage_level'], + label="thermal_storage", + inputs={bus_heat: Flow(investment=Investment(ep_costs=50))}, + outputs={ + bus_heat: Flow(investment=Investment(), variable_costs=0.0001) + }, + min_storage_level=input_data["min_storage_level"], + max_storage_level=input_data["max_storage_level"], loss_rate=loss_rate, fixed_losses_relative=fixed_losses_relative, fixed_losses_absolute=fixed_losses_absolute, - inflow_conversion_factor=input_data['inflow_conversion_factor'], - outflow_conversion_factor=input_data['outflow_conversion_factor'], + inflow_conversion_factor=input_data["inflow_conversion_factor"], + outflow_conversion_factor=input_data["outflow_conversion_factor"], invest_relation_input_output=1, - investment=Investment(ep_costs=400, minimum=1) + investment=Investment(ep_costs=400, minimum=1), ) - energysystem.add(bus_heat, heat_source, shortage, excess, heat_demand, thermal_storage) + energysystem.add( + bus_heat, heat_source, shortage, excess, heat_demand, thermal_storage + ) # Create and solve the optimization model optimization_model = Model(energysystem) - optimization_model.solve(solver=solver, - solve_kwargs={'tee': False, 'keepfiles': False}) + optimization_model.solve( + solver=solver, solve_kwargs={"tee": False, "keepfiles": False} + ) # Get results results = processing.results(optimization_model) - string_results = processing.convert_keys_to_strings(results) - sequences = {k: v['sequences'] for k, v in string_results.items()} - df = pd.concat(sequences, axis=1) # Print storage sizing - built_storage_capacity = results[thermal_storage, None]['scalars']['invest'] - initial_storage_capacity = results[thermal_storage, None]['scalars']['init_content'] - maximum_heat_flow_charging = results[bus_heat, thermal_storage]['scalars']['invest'] - - dash = '-' * 50 + built_storage_capacity = results[thermal_storage, None]["scalars"][ + "invest" + ] + maximum_heat_flow_charging = results[bus_heat, thermal_storage]["scalars"][ + "invest" + ] + + dash = "-" * 50 print(dash) - print('{:>32s}{:>15.3f}'.format('Invested capacity [MW]', maximum_heat_flow_charging)) - print('{:>32s}{:>15.3f}'.format('Invested storage capacity [MWh]', built_storage_capacity)) + print( + "{:>32s}{:>15.3f}".format( + "Invested capacity [MW]", maximum_heat_flow_charging + ) + ) + print( + "{:>32s}{:>15.3f}".format( + "Invested storage capacity [MWh]", built_storage_capacity + ) + ) print(dash) + if __name__ == "__main__": invest_independent_example() diff --git a/examples/stratified_thermal_storage/model_validation.py b/examples/stratified_thermal_storage/model_validation.py index 5cac21e3..476da7b8 100644 --- a/examples/stratified_thermal_storage/model_validation.py +++ b/examples/stratified_thermal_storage/model_validation.py @@ -1,92 +1,92 @@ import os + import matplotlib.pyplot as plt -import pandas as pd import numpy as np import oemof.solph as solph +import pandas as pd +from oemof.solph import Bus +from oemof.solph import EnergySystem +from oemof.solph import Flow +from oemof.solph import Model +from oemof.solph.components import Sink +from oemof.solph.components import Source + +from oemof.thermal import facades from oemof.thermal.stratified_thermal_storage import ( # noqa - calculate_storage_u_value, + calculate_capacities, +) +from oemof.thermal.stratified_thermal_storage import ( calculate_storage_dimensions, - calculate_capacities ) -from oemof.thermal import facades +from oemof.thermal.stratified_thermal_storage import calculate_storage_u_value -from oemof.solph.components import ( - Source, - Sink, -) -from oemof.solph import ( - Bus, - Flow, - Model, - EnergySystem, -) def model_validation(): data_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), - './data/validation_data.csv') - - input_data = pd.read_csv(data_path, index_col=0, header=0)['var_value'] + "./data/validation_data.csv", + ) + input_data = pd.read_csv(data_path, index_col=0, header=0)["var_value"] def run_storage_model(initial_storage_level, temp_h, temp_c): data_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), - './data/validation_data.csv') + "./data/validation_data.csv", + ) - input_data = pd.read_csv(data_path, index_col=0, header=0)['var_value'] + input_data = pd.read_csv(data_path, index_col=0, header=0)["var_value"] u_value = calculate_storage_u_value( - input_data['s_iso'], - input_data['lamb_iso'], - input_data['alpha_inside'], - input_data['alpha_outside']) + input_data["s_iso"], + input_data["lamb_iso"], + input_data["alpha_inside"], + input_data["alpha_outside"], + ) # Set up an energy system model periods = 10 - datetimeindex = pd.date_range('1/1/2019', periods=periods, freq='H') + datetimeindex = pd.date_range("1/1/2019", periods=periods, freq="H") demand_timeseries = np.zeros(periods) heat_feedin_timeseries = np.zeros(periods) energysystem = EnergySystem(timeindex=datetimeindex) - bus_heat = Bus(label='bus_heat') + bus_heat = Bus(label="bus_heat") heat_source = Source( - label='heat_source', - outputs={bus_heat: Flow( - nominal_value=1, - fix=heat_feedin_timeseries)}) + label="heat_source", + outputs={ + bus_heat: Flow(nominal_value=1, fix=heat_feedin_timeseries) + }, + ) shortage = Source( - label='shortage', - outputs={bus_heat: Flow(variable_costs=1e6)}) + label="shortage", outputs={bus_heat: Flow(variable_costs=1e6)} + ) - excess = Sink( - label='excess', - inputs={bus_heat: Flow()}) + excess = Sink(label="excess", inputs={bus_heat: Flow()}) heat_demand = Sink( - label='heat_demand', - inputs={bus_heat: Flow( - nominal_value=1, - fix=demand_timeseries)}) + label="heat_demand", + inputs={bus_heat: Flow(nominal_value=1, fix=demand_timeseries)}, + ) thermal_storage = facades.StratifiedThermalStorage( - label='thermal_storage', + label="thermal_storage", bus=bus_heat, - diameter=input_data['diameter'], - height=input_data['height'], + diameter=input_data["diameter"], + height=input_data["height"], temp_h=temp_h, temp_c=temp_c, - temp_env=input_data['temp_env'], + temp_env=input_data["temp_env"], u_value=u_value, # W/(m2*K) - min_storage_level=input_data['min_storage_level'], - max_storage_level=input_data['max_storage_level'], + min_storage_level=input_data["min_storage_level"], + max_storage_level=input_data["max_storage_level"], initial_storage_level=initial_storage_level, - capacity=input_data['maximum_heat_flow_charging'], + capacity=input_data["maximum_heat_flow_charging"], efficiency=1, - marginal_cost=0.0001 + marginal_cost=0.0001, ) energysystem.add( @@ -95,77 +95,86 @@ def run_storage_model(initial_storage_level, temp_h, temp_c): shortage, excess, heat_demand, - thermal_storage) + thermal_storage, + ) # create and solve the optimization model optimization_model = Model(energysystem) - optimization_model.write('storage_model_facades.lp', - io_options={'symbolic_solver_labels': True}) - optimization_model.solve(solver='cbc', solve_kwargs={'tee': False}) + optimization_model.write( + "storage_model_facades.lp", + io_options={"symbolic_solver_labels": True}, + ) + optimization_model.solve(solver="cbc", solve_kwargs={"tee": False}) - energysystem.results['main'] = solph.processing.results(optimization_model) - string_results = solph.views.convert_keys_to_strings(energysystem.results['main']) + energysystem.results["main"] = solph.processing.results( + optimization_model + ) + string_results = solph.views.convert_keys_to_strings( + energysystem.results["main"] + ) # Get time series of level (state of charge) of the thermal energy storage - TES_soc = (string_results['thermal_storage', 'None']['sequences']) + TES_soc = string_results["thermal_storage", "None"]["sequences"] # Save results to csv file TES_soc.to_csv("./data/storage_soc_calculated.csv") return + initial_storage_level = input_data["initial_storage_level"] - initial_storage_level = input_data['initial_storage_level'] - - run_storage_model(initial_storage_level=input_data['initial_storage_level'], - temp_h=input_data['temp_h'], - temp_c=input_data['temp_c']) + run_storage_model( + initial_storage_level=input_data["initial_storage_level"], + temp_h=input_data["temp_h"], + temp_c=input_data["temp_c"], + ) volume, surface = calculate_storage_dimensions( - input_data['height'], - input_data['diameter']) + input_data["height"], input_data["diameter"] + ) # Max capacity in MWh nominal_storage_capacity = calculate_capacities( - volume, - input_data['temp_h'], - input_data['temp_c']) + volume, input_data["temp_h"], input_data["temp_c"] + ) # Get measurement data - filename = './data/storage_soc_measured.csv' + filename = "./data/storage_soc_measured.csv" level_meas = pd.read_csv(filename, header=0) # Get simulation results (hourly values) - filename = './data/storage_soc_calculated.csv' + filename = "./data/storage_soc_calculated.csv" TES_soc_df = pd.read_csv(filename, header=0) # Convert to list - TES_soc_hourly = TES_soc_df['storage_content'].values + TES_soc_hourly = TES_soc_df["storage_content"].values # Convert simulation data to relative values in % - TES_soc_relative = [soc / nominal_storage_capacity * 100 for soc in - TES_soc_hourly] + TES_soc_relative = [ + soc / nominal_storage_capacity * 100 for soc in TES_soc_hourly + ] end_step = 7 # Make list with time steps for x-axes in plot t_meas = np.arange(0, (len(level_meas) / 4), 0.25) - plt.style.use('ggplot') - fig, ax = plt.subplots() + plt.style.use("ggplot") + _, ax = plt.subplots() # Plot horizontal line (initial level) - init_level = level_meas['level'][0] - plt.plot([init_level] * end_step, '--', color='gray') + init_level = level_meas["level"][0] + plt.plot([init_level] * end_step, "--", color="gray") # Plot simulation data TES_soc_relative_list = [initial_storage_level * 100] [TES_soc_relative_list.append(TES_soc_relative[i]) for i in range(10)] - plt.plot(TES_soc_relative_list[:end_step], - label="storage level (simulation)") + plt.plot( + TES_soc_relative_list[:end_step], label="storage level (simulation)" + ) # Plot measurement data - plt.plot(t_meas, level_meas['level'], label="storage level (measurement)") + plt.plot(t_meas, level_meas["level"], label="storage level (measurement)") plt.legend() ax.set_xlabel("Time in h") diff --git a/examples/stratified_thermal_storage/operation_facade.py b/examples/stratified_thermal_storage/operation_facade.py index 9faff86a..e3c55e43 100644 --- a/examples/stratified_thermal_storage/operation_facade.py +++ b/examples/stratified_thermal_storage/operation_facade.py @@ -4,56 +4,61 @@ """ import os -import pandas as pd + import numpy as np +import pandas as pd +from oemof.solph import Bus # noqa +from oemof.solph import EnergySystem +from oemof.solph import Flow +from oemof.solph.components import Sink +from oemof.solph.components import Source -from oemof.solph import Bus, Flow, Model, EnergySystem # noqa -from oemof.solph.components import Sink, Source from oemof.thermal import facades from oemof.thermal.stratified_thermal_storage import ( # noqa calculate_storage_u_value, ) + def operation_facade_example(): # Set paths data_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), - 'data', 'stratified_thermal_storage.csv') + "data", + "stratified_thermal_storage.csv", + ) # Read input data - input_data = pd.read_csv(data_path, index_col=0, header=0)['var_value'] + input_data = pd.read_csv(data_path, index_col=0, header=0)["var_value"] # Precalculation u_value = calculate_storage_u_value( - input_data['s_iso'], - input_data['lamb_iso'], - input_data['alpha_inside'], - input_data['alpha_outside']) - + input_data["s_iso"], + input_data["lamb_iso"], + input_data["alpha_inside"], + input_data["alpha_outside"], + ) def print_parameters(): parameter = { - 'U-value [W/(m2*K)]': u_value, + "U-value [W/(m2*K)]": u_value, } - dash = '-' * 50 + dash = "-" * 50 print(dash) - print('{:>32s}{:>15s}'.format('Parameter name', 'Value')) + print("{:>32s}{:>15s}".format("Parameter name", "Value")) print(dash) for name, param in parameter.items(): - print('{:>32s}{:>15.5f}'.format(name, param)) + print("{:>32s}{:>15.5f}".format(name, param)) print(dash) - print_parameters() # Set up an energy system model - solver = 'cbc' periods = 100 - datetimeindex = pd.date_range('1/1/2019', periods=periods, freq='H') + datetimeindex = pd.date_range("1/1/2019", periods=periods, freq="H") demand_timeseries = np.zeros(periods) demand_timeseries[-5:] = 1 heat_feedin_timeseries = np.zeros(periods) @@ -61,48 +66,43 @@ def print_parameters(): energysystem = EnergySystem(timeindex=datetimeindex) - bus_heat = Bus(label='bus_heat') + bus_heat = Bus(label="bus_heat") heat_source = Source( - label='heat_source', - outputs={bus_heat: Flow( - nominal_value=1, - fix=heat_feedin_timeseries)}) + label="heat_source", + outputs={bus_heat: Flow(nominal_value=1, fix=heat_feedin_timeseries)}, + ) shortage = Source( - label='shortage', - outputs={bus_heat: Flow(variable_costs=1e6)}) + label="shortage", outputs={bus_heat: Flow(variable_costs=1e6)} + ) - excess = Sink( - label='excess', - inputs={bus_heat: Flow()}) + excess = Sink(label="excess", inputs={bus_heat: Flow()}) heat_demand = Sink( - label='heat_demand', - inputs={bus_heat: Flow( - nominal_value=1, - fix=demand_timeseries)}) + label="heat_demand", + inputs={bus_heat: Flow(nominal_value=1, fix=demand_timeseries)}, + ) thermal_storage = facades.StratifiedThermalStorage( - label='thermal_storage', + label="thermal_storage", bus=bus_heat, - diameter=input_data['diameter'], - height=input_data['height'], - temp_h=input_data['temp_h'], - temp_c=input_data['temp_c'], - temp_env=input_data['temp_env'], + diameter=input_data["diameter"], + height=input_data["height"], + temp_h=input_data["temp_h"], + temp_c=input_data["temp_c"], + temp_env=input_data["temp_env"], u_value=u_value, - min_storage_level=input_data['min_storage_level'], - max_storage_level=input_data['max_storage_level'], - capacity=input_data['maximum_heat_flow_charging'], - efficiency=input_data['efficiency'], - marginal_cost=0.0001 + min_storage_level=input_data["min_storage_level"], + max_storage_level=input_data["max_storage_level"], + capacity=input_data["maximum_heat_flow_charging"], + efficiency=input_data["efficiency"], + marginal_cost=0.0001, ) - energysystem.add(bus_heat, heat_source, shortage, excess, heat_demand, thermal_storage) - - # Create and solve the optimization model - optimization_model = Model(energysystem) + energysystem.add( + bus_heat, heat_source, shortage, excess, heat_demand, thermal_storage + ) if __name__ == "__main__": diff --git a/examples/stratified_thermal_storage/operation_generic_storage.py b/examples/stratified_thermal_storage/operation_generic_storage.py index fdebecc6..3a8c986b 100644 --- a/examples/stratified_thermal_storage/operation_generic_storage.py +++ b/examples/stratified_thermal_storage/operation_generic_storage.py @@ -4,82 +4,90 @@ """ import os -import pandas as pd -import numpy as np -import matplotlib.pyplot as plt +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd +from oemof.solph import Bus +from oemof.solph import EnergySystem +from oemof.solph import Flow +from oemof.solph import Model +from oemof.solph import processing +from oemof.solph.components import GenericStorage +from oemof.solph.components import Sink +from oemof.solph.components import Source + +from oemof.thermal.stratified_thermal_storage import calculate_capacities +from oemof.thermal.stratified_thermal_storage import calculate_losses from oemof.thermal.stratified_thermal_storage import ( - calculate_storage_u_value, calculate_storage_dimensions, - calculate_capacities, - calculate_losses, ) -from oemof.solph import processing, Bus, Flow, Model, EnergySystem -from oemof.solph.components import GenericStorage, Source, Sink +from oemof.thermal.stratified_thermal_storage import calculate_storage_u_value + def operation_example(): # Set paths data_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), - 'data', 'stratified_thermal_storage.csv') + "data", + "stratified_thermal_storage.csv", + ) # Read input data - input_data = pd.read_csv(data_path, index_col=0, header=0)['var_value'] + input_data = pd.read_csv(data_path, index_col=0, header=0)["var_value"] # Precalculation u_value = calculate_storage_u_value( - input_data['s_iso'], - input_data['lamb_iso'], - input_data['alpha_inside'], - input_data['alpha_outside']) + input_data["s_iso"], + input_data["lamb_iso"], + input_data["alpha_inside"], + input_data["alpha_outside"], + ) volume, surface = calculate_storage_dimensions( - input_data['height'], - input_data['diameter'] + input_data["height"], input_data["diameter"] ) nominal_storage_capacity = calculate_capacities( - volume, - input_data['temp_h'], - input_data['temp_c']) + volume, input_data["temp_h"], input_data["temp_c"] + ) loss_rate, fixed_losses_relative, fixed_losses_absolute = calculate_losses( u_value, - input_data['diameter'], - input_data['temp_h'], - input_data['temp_c'], - input_data['temp_env']) - + input_data["diameter"], + input_data["temp_h"], + input_data["temp_c"], + input_data["temp_env"], + ) def print_parameters(): parameter = { - 'U-value [W/(m2*K)]': u_value, - 'Volume [m3]': volume, - 'Surface [m2]': surface, - 'Nominal storage capacity [MWh]': nominal_storage_capacity, - 'Loss rate [-]': loss_rate, - 'Fixed relative losses [-]': fixed_losses_relative, - 'Fixed absolute losses [MWh]': fixed_losses_absolute, + "U-value [W/(m2*K)]": u_value, + "Volume [m3]": volume, + "Surface [m2]": surface, + "Nominal storage capacity [MWh]": nominal_storage_capacity, + "Loss rate [-]": loss_rate, + "Fixed relative losses [-]": fixed_losses_relative, + "Fixed absolute losses [MWh]": fixed_losses_absolute, } - dash = '-' * 50 + dash = "-" * 50 print(dash) - print('{:>32s}{:>15s}'.format('Parameter name', 'Value')) + print("{:>32s}{:>15s}".format("Parameter name", "Value")) print(dash) for name, param in parameter.items(): - print('{:>32s}{:>15.5f}'.format(name, param)) + print("{:>32s}{:>15.5f}".format(name, param)) print(dash) - print_parameters() # Set up an energy system model - solver = 'cbc' + solver = "cbc" periods = 100 - datetimeindex = pd.date_range('1/1/2019', periods=periods, freq='H') + datetimeindex = pd.date_range("1/1/2019", periods=periods, freq="H") demand_timeseries = np.zeros(periods) demand_timeseries[-5:] = 1 heat_feedin_timeseries = np.zeros(periods) @@ -87,81 +95,94 @@ def print_parameters(): energysystem = EnergySystem(timeindex=datetimeindex) - bus_heat = Bus(label='bus_heat') + bus_heat = Bus(label="bus_heat") heat_source = Source( - label='heat_source', - outputs={bus_heat: Flow( - nominal_value=1, - fix=heat_feedin_timeseries)}) + label="heat_source", + outputs={bus_heat: Flow(nominal_value=1, fix=heat_feedin_timeseries)}, + ) shortage = Source( - label='shortage', - outputs={bus_heat: Flow(variable_costs=1e6)}) + label="shortage", outputs={bus_heat: Flow(variable_costs=1e6)} + ) - excess = Sink( - label='excess', - inputs={bus_heat: Flow()}) + excess = Sink(label="excess", inputs={bus_heat: Flow()}) heat_demand = Sink( - label='heat_demand', - inputs={bus_heat: Flow( - nominal_value=1, - fix=demand_timeseries)}) + label="heat_demand", + inputs={bus_heat: Flow(nominal_value=1, fix=demand_timeseries)}, + ) thermal_storage = GenericStorage( - label='thermal_storage', - inputs={bus_heat: Flow( - nominal_value=input_data['maximum_heat_flow_charging'])}, - outputs={bus_heat: Flow( - nominal_value=input_data['maximum_heat_flow_discharging'], - variable_costs=0.0001 - )}, + label="thermal_storage", + inputs={ + bus_heat: Flow( + nominal_value=input_data["maximum_heat_flow_charging"] + ) + }, + outputs={ + bus_heat: Flow( + nominal_value=input_data["maximum_heat_flow_discharging"], + variable_costs=0.0001, + ) + }, nominal_storage_capacity=nominal_storage_capacity, - min_storage_level=input_data['min_storage_level'], - max_storage_level=input_data['max_storage_level'], + min_storage_level=input_data["min_storage_level"], + max_storage_level=input_data["max_storage_level"], loss_rate=loss_rate, fixed_losses_relative=fixed_losses_relative, fixed_losses_absolute=fixed_losses_absolute, - inflow_conversion_factor=input_data['inflow_conversion_factor'], - outflow_conversion_factor=input_data['outflow_conversion_factor'], + inflow_conversion_factor=input_data["inflow_conversion_factor"], + outflow_conversion_factor=input_data["outflow_conversion_factor"], ) - energysystem.add(bus_heat, heat_source, shortage, excess, heat_demand, thermal_storage) + energysystem.add( + bus_heat, heat_source, shortage, excess, heat_demand, thermal_storage + ) # Create and solve the optimization model optimization_model = Model(energysystem) - optimization_model.solve(solver=solver, - solve_kwargs={'tee': False, 'keepfiles': False}) + optimization_model.solve( + solver=solver, solve_kwargs={"tee": False, "keepfiles": False} + ) # Get results results = processing.results(optimization_model) string_results = processing.convert_keys_to_strings(results) - sequences = {k: v['sequences'] for k, v in string_results.items()} + sequences = {k: v["sequences"] for k, v in string_results.items()} df = pd.concat(sequences, axis=1) # Example plot fig, (ax1, ax2) = plt.subplots(2, 1) - df[[('shortage', 'bus_heat', 'flow'), - ('heat_source', 'bus_heat', 'flow'), - ('thermal_storage', 'bus_heat', 'flow')]].plot.area(ax=ax1, stacked=True, color=['y', 'b', 'k']) + df[ + [ + ("shortage", "bus_heat", "flow"), + ("heat_source", "bus_heat", "flow"), + ("thermal_storage", "bus_heat", "flow"), + ] + ].plot.area(ax=ax1, stacked=True, color=["y", "b", "k"]) - (-df[('bus_heat', 'thermal_storage', 'flow')]).plot.area(ax=ax1, color='g', ylim=(-2, 2)) + (-df[("bus_heat", "thermal_storage", "flow")]).plot.area( + ax=ax1, color="g", ylim=(-2, 2) + ) - df[('bus_heat', 'heat_demand', 'flow')].plot(ax=ax1, linestyle='-', marker='o', color='r') + df[("bus_heat", "heat_demand", "flow")].plot( + ax=ax1, linestyle="-", marker="o", color="r" + ) - df[('thermal_storage', 'None', 'storage_content')].plot.area(ax=ax2) + df[("thermal_storage", "None", "storage_content")].plot.area(ax=ax2) - ax1.set_title('Heat flow to and from heat bus') + ax1.set_title("Heat flow to and from heat bus") ax1.set_ylim(-2, 2) - ax1.legend(loc='center left', bbox_to_anchor=(1.0, 0.5)) + ax1.legend(loc="center left", bbox_to_anchor=(1.0, 0.5)) - ax2.set_title('Storage content') - ax2.set_xlabel('Timesteps') - ax2.legend(loc='center left', bbox_to_anchor=(1.0, 0.5)) + ax2.set_title("Storage content") + ax2.set_xlabel("Timesteps") + ax2.legend(loc="center left", bbox_to_anchor=(1.0, 0.5)) plt.tight_layout() plt.show() + if __name__ == "__main__": operation_example() diff --git a/examples/stratified_thermal_storage/plots.py b/examples/stratified_thermal_storage/plots.py index 8c6c31c2..99ac2673 100644 --- a/examples/stratified_thermal_storage/plots.py +++ b/examples/stratified_thermal_storage/plots.py @@ -6,134 +6,157 @@ """ import os -import pandas as pd -import matplotlib.pyplot as plt +import matplotlib.pyplot as plt +import pandas as pd +from oemof.solph import Bus +from oemof.solph import EnergySystem +from oemof.solph import Flow +from oemof.solph import Model +from oemof.solph import processing +from oemof.solph import views +from oemof.solph.components import GenericStorage +from oemof.thermal.stratified_thermal_storage import calculate_capacities +from oemof.thermal.stratified_thermal_storage import calculate_losses from oemof.thermal.stratified_thermal_storage import ( - calculate_storage_u_value, calculate_storage_dimensions, - calculate_capacities, - calculate_losses, ) -from oemof.solph import processing, views, Bus, Flow, Model, EnergySystem -from oemof.solph.components import GenericStorage +from oemof.thermal.stratified_thermal_storage import calculate_storage_u_value + def plots(): # Set paths data_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), - 'data', 'stratified_thermal_storage.csv') + "data", + "stratified_thermal_storage.csv", + ) - input_data = pd.read_csv(data_path, index_col=0, header=0)['var_value'] + input_data = pd.read_csv(data_path, index_col=0, header=0)["var_value"] u_value = calculate_storage_u_value( - input_data['s_iso'], - input_data['lamb_iso'], - input_data['alpha_inside'], - input_data['alpha_outside'] + input_data["s_iso"], + input_data["lamb_iso"], + input_data["alpha_inside"], + input_data["alpha_outside"], ) volume, surface = calculate_storage_dimensions( - input_data['height'], - input_data['diameter'] + input_data["height"], input_data["diameter"] ) nominal_storage_capacity = calculate_capacities( - volume, - input_data['temp_h'], - input_data['temp_c'] + volume, input_data["temp_h"], input_data["temp_c"] ) loss_rate, fixed_losses_relative, fixed_losses_absolute = calculate_losses( u_value, - input_data['diameter'], - input_data['temp_h'], - input_data['temp_c'], - input_data['temp_env'], + input_data["diameter"], + input_data["temp_h"], + input_data["temp_c"], + input_data["temp_env"], ) - def print_results(): parameter = { - 'U-value [W/(m2*K)]': u_value, - 'Volume [m3]': volume, - 'Surface [m2]': surface, - 'Nominal storage capacity [MWh]': nominal_storage_capacity, - 'Loss rate [-]': loss_rate, - 'Fixed relative losses [-]': fixed_losses_relative, - 'Fixed absolute losses [MWh]': fixed_losses_absolute, + "U-value [W/(m2*K)]": u_value, + "Volume [m3]": volume, + "Surface [m2]": surface, + "Nominal storage capacity [MWh]": nominal_storage_capacity, + "Loss rate [-]": loss_rate, + "Fixed relative losses [-]": fixed_losses_relative, + "Fixed absolute losses [MWh]": fixed_losses_absolute, } - dash = '-' * 50 + dash = "-" * 50 print(dash) - print('{:>32s}{:>15s}'.format('Parameter name', 'Value')) + print("{:>32s}{:>15s}".format("Parameter name", "Value")) print(dash) for name, param in parameter.items(): - print('{:>32s}{:>15.5f}'.format(name, param)) + print("{:>32s}{:>15.5f}".format(name, param)) print(dash) - print_results() # Set up an energy system model - solver = 'cbc' + solver = "cbc" periods = 800 - datetimeindex = pd.date_range('1/1/2019', periods=periods, freq='H') + datetimeindex = pd.date_range("1/1/2019", periods=periods, freq="H") energysystem = EnergySystem(timeindex=datetimeindex) storage_list = [] - bus_simple_thermal_storage = Bus(label='bus_simple_thermal_storage', balanced=False) + bus_simple_thermal_storage = Bus( + label="bus_simple_thermal_storage", balanced=False + ) energysystem.add(bus_simple_thermal_storage) - storage_list.append(GenericStorage( - label='simple_thermal_storage', - inputs={bus_simple_thermal_storage: Flow( - nominal_value=input_data['maximum_heat_flow_charging'], - variable_costs=0.0001)}, - outputs={bus_simple_thermal_storage: Flow( - nominal_value=input_data['maximum_heat_flow_discharging'], - variable_costs=0.0001)}, - nominal_storage_capacity=nominal_storage_capacity, - min_storage_level=input_data['min_storage_level'], - max_storage_level=input_data['max_storage_level'], - initial_storage_level=27 / nominal_storage_capacity, - loss_rate=0.001, - inflow_conversion_factor=1., - outflow_conversion_factor=1., - balanced=False - )) + storage_list.append( + GenericStorage( + label="simple_thermal_storage", + inputs={ + bus_simple_thermal_storage: Flow( + nominal_value=input_data["maximum_heat_flow_charging"], + variable_costs=0.0001, + ) + }, + outputs={ + bus_simple_thermal_storage: Flow( + nominal_value=input_data["maximum_heat_flow_discharging"], + variable_costs=0.0001, + ) + }, + nominal_storage_capacity=nominal_storage_capacity, + min_storage_level=input_data["min_storage_level"], + max_storage_level=input_data["max_storage_level"], + initial_storage_level=27 / nominal_storage_capacity, + loss_rate=0.001, + inflow_conversion_factor=1.0, + outflow_conversion_factor=1.0, + balanced=False, + ) + ) for i, nominal_storage_capacity in enumerate([30, 65, 90]): - bus_i = Bus(label=f'bus_{i}', balanced=False) + bus_i = Bus(label=f"bus_{i}", balanced=False) energysystem.add(bus_i) - storage_list.append(GenericStorage( - label=f'stratified_thermal_storage_{nominal_storage_capacity}_MWh', - inputs={bus_i: Flow( - nominal_value=input_data['maximum_heat_flow_charging'], - variable_costs=0.0001)}, - outputs={bus_i: Flow( - nominal_value=input_data['maximum_heat_flow_discharging'], - variable_costs=0.0001)}, - nominal_storage_capacity=nominal_storage_capacity, - min_storage_level=input_data['min_storage_level'], - max_storage_level=input_data['max_storage_level'], - initial_storage_level=27 / nominal_storage_capacity, - loss_rate=loss_rate, - fixed_losses_relative=fixed_losses_relative, - fixed_losses_absolute=fixed_losses_absolute, - inflow_conversion_factor=1., - outflow_conversion_factor=1., - balanced=False - )) + storage_list.append( + GenericStorage( + label=f"stratified_thermal_storage_{nominal_storage_capacity}_MWh", + inputs={ + bus_i: Flow( + nominal_value=input_data["maximum_heat_flow_charging"], + variable_costs=0.0001, + ) + }, + outputs={ + bus_i: Flow( + nominal_value=input_data[ + "maximum_heat_flow_discharging" + ], + variable_costs=0.0001, + ) + }, + nominal_storage_capacity=nominal_storage_capacity, + min_storage_level=input_data["min_storage_level"], + max_storage_level=input_data["max_storage_level"], + initial_storage_level=27 / nominal_storage_capacity, + loss_rate=loss_rate, + fixed_losses_relative=fixed_losses_relative, + fixed_losses_absolute=fixed_losses_absolute, + inflow_conversion_factor=1.0, + outflow_conversion_factor=1.0, + balanced=False, + ) + ) energysystem.add(*storage_list) @@ -144,16 +167,19 @@ def print_results(): # Get results results = processing.results(optimization_model) - storage_content = views.node_weight_by_type(results, GenericStorage)\ - .reset_index(drop=True) + storage_content = views.node_weight_by_type( + results, GenericStorage + ).reset_index(drop=True) - storage_content.columns = storage_content.columns\ - .set_levels([k.label for k in storage_content.columns.levels[0]], level=0) + storage_content.columns = storage_content.columns.set_levels( + [k.label for k in storage_content.columns.levels[0]], level=0 + ) - losses = - storage_content.iloc[1:, :].reset_index(drop=True)\ - + storage_content.iloc[:-1, :].reset_index(drop=True) + losses = -storage_content.iloc[1:, :].reset_index( + drop=True + ) + storage_content.iloc[:-1, :].reset_index(drop=True) - losses.columns = losses.columns.set_levels(['losses'], level=1) + losses.columns = losses.columns.set_levels(["losses"], level=1) storage_content = storage_content.iloc[:-1, :] @@ -162,32 +188,31 @@ def print_results(): storage_df = storage_df.reindex(sorted(storage_df.columns), axis=1) # Plot storage_content vs. time - fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5)) + _, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5)) storage_content.plot(ax=ax1) - ax1.set_title('Storage content') - ax1.set_xlabel('Timesteps') - ax1.set_ylabel('Storage content [MWh]') + ax1.set_title("Storage content") + ax1.set_xlabel("Timesteps") + ax1.set_ylabel("Storage content [MWh]") ax1.grid(alpha=0.3) ax1.get_legend().remove() # Plot losses vs storage content for storage_label in (storage.label for storage in storage_list): ax2.scatter( - storage_df[(storage_label, 'storage_content')], - storage_df[(storage_label, 'losses')], + storage_df[(storage_label, "storage_content")], + storage_df[(storage_label, "losses")], label=storage_label, - s=1 + s=1, ) ax2.set_xlim(0, 27.2) ax2.set_ylim(0, 0.035) - ax2.set_title('Losses vs. storage content') - ax2.set_xlabel('Storage content [MWh]') - ax2.set_ylabel('Losses [MW]') + ax2.set_title("Losses vs. storage content") + ax2.set_xlabel("Storage content [MWh]") + ax2.set_ylabel("Losses [MW]") ax2.grid(alpha=0.3) - ax2.legend(markerscale=8, loc='center left', bbox_to_anchor=(1.0, 0.5)) + ax2.legend(markerscale=8, loc="center left", bbox_to_anchor=(1.0, 0.5)) plt.tight_layout() - plt.savefig('compare_storage_models.svg') - + plt.savefig("compare_storage_models.svg") if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..1dc9ac71 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,57 @@ +[build-system] +requires = ["flit_core >=3.2,<4"] +build-backend = "flit_core.buildapi" + +[tool.flit.sdist] +include = [ + "LICENSE", + "README.rst", + "docs/", + "examples/", + "src/", + "tests/", + ".pylintrc", + "tox.ini", +] + +[project] +name = "oemof.thermal" +dynamic = ["version"] +description = "Thermal energy components for the open energy modelling framework." +readme = "README.rst" +authors = [ + {name = "oemof developer group", email = "contact@oemof.org"}, +] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Operating System :: Unix", + "Operating System :: POSIX", + "Operating System :: Microsoft :: Windows", + "Operating System :: MacOS", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Topic :: Utilities", +] + +requires-python = ">=3.9" +dependencies = [ + 'oemof.solph', + 'matplotlib', + 'pvlib <= 0.9.0', + 'numpy >= 1.16.5', + 'pandas >= 0.18.0' +] +license = {text = "MIT"} + +[tool.black] +line-length = 79 +target-version = ['py39', 'py310', 'py311'] + +[tool.isort] +profile = "black" +force_single_line = true +line_length = 79 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 11e9ec40..00000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[metadata] -description-file = README.rst \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 7032ca2f..00000000 --- a/setup.py +++ /dev/null @@ -1,29 +0,0 @@ -#! /usr/bin/env python -# -*- encoding: utf-8 -*- - -from setuptools import find_packages, setup -import os - - -def read(fname): - return open(os.path.join(os.path.dirname(__file__), fname)).read() - - -setup(name='oemof.thermal', - version='0.0.7.dev', - author='oemof developer group', - author_email='contact@oemof.org', - description=( - 'Thermal energy components for ' - 'the open energy modelling framework.' - ), - url='https://github.com/oemof/oemof-thermal', - long_description=read('README.rst'), - long_description_content_type="text/x-rst", - packages=["oemof"] + ["oemof." + p for p in find_packages("src/oemof")], - package_dir={"": "src"}, - install_requires=['oemof.solph', - 'matplotlib', - 'pvlib', - 'numpy >= 1.16.5', - 'pandas >= 0.18.0']) diff --git a/src/oemof/thermal/__init__.py b/src/oemof/thermal/__init__.py index e6537d98..0f76c415 100644 --- a/src/oemof/thermal/__init__.py +++ b/src/oemof/thermal/__init__.py @@ -1,13 +1,13 @@ -__version__ = '0.0.7.dev' -__project__ = 'oemof.thermal' +__version__ = "0.0.7.dev0" +__project__ = "oemof.thermal" from . import absorption_heatpumps_and_chillers -from . import compression_heatpumps_and_chillers -from . import facades -from . import stratified_thermal_storage from . import cogeneration +from . import compression_heatpumps_and_chillers from . import concentrating_solar_power +from . import facades from . import solar_thermal_collector +from . import stratified_thermal_storage __all__ = [ "absorption_heatpumps_and_chillers", diff --git a/src/oemof/thermal/absorption_heatpumps_and_chillers.py b/src/oemof/thermal/absorption_heatpumps_and_chillers.py index f051f1ed..1ab63934 100644 --- a/src/oemof/thermal/absorption_heatpumps_and_chillers.py +++ b/src/oemof/thermal/absorption_heatpumps_and_chillers.py @@ -3,9 +3,9 @@ """ This module is designed to hold functions for calculating absorption chillers. -This file is part of project oemof (github.com/oemof/oemof-thermal). It's copyrighted -by the contributors recorded in the version control history of the file, -available from its original location: +This file is part of project oemof (github.com/oemof/oemof-thermal). It's +copyrighted by the contributors recorded in the version control history of the +file, available from its original location: oemof-thermal/src/oemof/thermal/absorption_heatpumps_and_chillers.py SPDX-License-Identifier: MIT @@ -13,7 +13,6 @@ def calc_characteristic_temp(t_hot, t_cool, t_chill, coef_a, coef_e, method): - r""" Calculates the characteristic temperature difference. @@ -24,7 +23,8 @@ def calc_characteristic_temp(t_hot, t_cool, t_chill, coef_a, coef_e, method): Parameters ---------- t_hot : numeric - External arithmetic mean fluid temperature of hot water at heat exchanger (generator) [K] + External arithmetic mean fluid temperature of hot water at heat + exchanger (generator) [K] t_cool : numeric External arithmetic mean fluid temperature of cooling water at @@ -73,14 +73,11 @@ def calc_characteristic_temp(t_hot, t_cool, t_chill, coef_a, coef_e, method): """ if not isinstance(t_hot, (list)): - raise TypeError("Argument 't_hot' is not of " - "type list!") + raise TypeError("Argument 't_hot' is not of " "type list!") if not isinstance(t_cool, (list)): - raise TypeError("Argument 't_cool' is not of " - "type list!") + raise TypeError("Argument 't_cool' is not of " "type list!") if not isinstance(t_chill, (list)): - raise TypeError("Argument 't_chill' is not of " - "type list!") + raise TypeError("Argument 't_chill' is not of " "type list!") lengths = [len(t_hot), len(t_cool), len(t_chill)] length = max(lengths) @@ -91,7 +88,9 @@ def calc_characteristic_temp(t_hot, t_cool, t_chill, coef_a, coef_e, method): elif len(t_hot) == length: list_t_g = t_hot else: - raise ValueError("Length of argument 't_hot' does not to match requirements") + raise ValueError( + "Length of argument 't_hot' does not to match requirements" + ) # External mean temperature at absorber/condenser (ac) if len(t_cool) == 1: @@ -99,7 +98,9 @@ def calc_characteristic_temp(t_hot, t_cool, t_chill, coef_a, coef_e, method): elif len(t_cool) == length: list_t_ac = t_cool else: - raise ValueError("Length of argument 't_cool' does not to match requirements") + raise ValueError( + "Length of argument 't_cool' does not to match requirements" + ) # External mean temperature at evaporator (e) if len(t_chill) == 1: @@ -107,50 +108,58 @@ def calc_characteristic_temp(t_hot, t_cool, t_chill, coef_a, coef_e, method): elif len(t_chill) == length: list_t_e = t_chill else: - raise ValueError("Length of argument 't_chill' does not to match requirements") - - if method == 'kuehn_and_ziegler': - ddts = [t_g - coef_a * t_ac + coef_e * t_e for - t_g, t_ac, t_e in zip(list_t_g, list_t_ac, list_t_e)] + raise ValueError( + "Length of argument 't_chill' does not to match requirements" + ) + + if method == "kuehn_and_ziegler": + ddts = [ + t_g - coef_a * t_ac + coef_e * t_e + for t_g, t_ac, t_e in zip(list_t_g, list_t_ac, list_t_e) + ] else: - raise ValueError("Unrecognized input for argument 'method'. " - "Possible options: 'kuehn_and_ziegler'.") + raise ValueError( + "Unrecognized input for argument 'method'. " + "Possible options: 'kuehn_and_ziegler'." + ) return ddts def calc_heat_flux(ddts, coef_s, coef_r, method): r""" - Calculates the heat flux at external heat exchanger. + Calculates the heat flux at external heat exchanger. - .. calc_heat_flux-equations: + .. calc_heat_flux-equations: - :math:`\dot{Q} = s \cdot \Delta\Delta T + r` + :math:`\dot{Q} = s \cdot \Delta\Delta T + r` - Parameters - ---------- - ddt : numeric - Characteristic temperature difference [K] + Parameters + ---------- + ddt : numeric + Characteristic temperature difference [K] - coeff_s : numeric - Characteristic parameter [-] + coeff_s : numeric + Characteristic parameter [-] - coeff_r : numeric - Characteristic parameter [-] + coeff_r : numeric + Characteristic parameter [-] - method : string - Method to calculate characteristic temperature difference + method : string + Method to calculate characteristic temperature difference - Returns - ------- - Q_dots : numeric - Heat flux [W] + Returns + ------- + Q_dots : numeric + Heat flux [W] - """ - if method == 'kuehn_and_ziegler': + """ + if method == "kuehn_and_ziegler": Q_dots = [coef_s * ddt + coef_r for ddt in ddts] else: - raise ValueError("Unrecognized input for argument 'method'. " - "Possible options: 'kuehn_and_ziegler'.") + raise ValueError( + "Unrecognized input for argument 'method'. " + "Possible options: 'kuehn_and_ziegler'." + ) return Q_dots @@ -190,8 +199,8 @@ def define_AC_specs(Q_dots_evap, Q_dots_gen): """ AC_specs = { - 'COPs': [Q_e / Q_g for Q_e, Q_g in zip(Q_dots_evap, Q_dots_gen)], - 'Q_chill_max': [Q_e / max(Q_dots_evap) for Q_e in Q_dots_evap], - 'Q_chill_nominal': max(Q_dots_evap) + "COPs": [Q_e / Q_g for Q_e, Q_g in zip(Q_dots_evap, Q_dots_gen)], + "Q_chill_max": [Q_e / max(Q_dots_evap) for Q_e in Q_dots_evap], + "Q_chill_nominal": max(Q_dots_evap), } return AC_specs diff --git a/src/oemof/thermal/cogeneration.py b/src/oemof/thermal/cogeneration.py index 2174a951..029f56ae 100644 --- a/src/oemof/thermal/cogeneration.py +++ b/src/oemof/thermal/cogeneration.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 """ -This module is designed to hold functions for pre- and postprocessing for combined heat -and power plants. +This module is designed to hold functions for pre- and postprocessing for +combined heat and power plants. -This file is part of project oemof (github.com/oemof/oemof-thermal). It's copyrighted -by the contributors recorded in the version control history of the file, -available from its original location: oemof-thermal/src/oemof/thermal/chp.py +This file is part of project oemof (github.com/oemof/oemof-thermal). It's +copyrighted by the contributors recorded in the version control history of the +file, available from its original location: +oemof-thermal/src/oemof/thermal/chp.py SPDX-License-Identifier: MIT """ @@ -14,8 +15,8 @@ def allocate_emissions(total_emissions, eta_el, eta_th, method, **kwargs): r""" - Function to allocate emissions caused in cogeneration to the products electrical energy - and heat according to specified method. + Function to allocate emissions caused in cogeneration to the products + electrical energy and heat according to specified method. .. allocate_emissions-equations: @@ -39,17 +40,19 @@ def allocate_emissions(total_emissions, eta_el, eta_th, method, **kwargs): with - :math:`PEE = 1 - \frac{1}{\frac{\eta_{th}}{\eta_{th,ref}}+\frac{\eta_{el}}{\eta_{el,ref}}}`. + :math:`PEE = 1 - \frac{1}{\frac{\eta_{th}}{\eta_{th,ref}}+\frac{\eta_{el}} {\eta_{el,ref}}}`. Reference: Mauch, W., Corradini, R., Wiesmeyer, K., Schwentzek, M. (2010). - Allokationsmethoden für spezifische CO2-Emissionen von Strom und Waerme aus KWK-Anlagen. + Allokationsmethoden für spezifische CO2-Emissionen von Strom und Waerme + aus KWK-Anlagen. Energiewirtschaftliche Tagesfragen, 55(9), 12–14. Parameters ---------- total_emissions : numeric - Total absolute emissions to be allocated to electricity and heat [in CO2 equivalents]. + Total absolute emissions to be allocated to electricity and heat + [in CO2 equivalents]. eta_el : numeric Electrical efficiency of the cogeneration [-]. @@ -58,43 +61,65 @@ def allocate_emissions(total_emissions, eta_el, eta_th, method, **kwargs): Thermal efficiency of the cogeneration [-]. method : str - Specification of method to use. Choose from ['iea', finnish', 'efficiency']. + Specification of method to use. Choose from + ['iea', finnish', 'efficiency']. **kwargs - For the finnish method, `eta_el_ref` and `eta_th_ref` have to be passed. + For the finnish method, `eta_el_ref` and `eta_th_ref` have to be + passed. Returns ------- allocated_emissions_electricity : numeric - total emissions allocated to electricity according to specified `method` - [in CO2 equivalents]. + total emissions allocated to electricity according to specified + `method` [in CO2 equivalents]. allocated_emissions_heat : numeric total emissions allocated to heat according to specified `method` [in CO2 equivalents]. """ - if method == 'iea': - allocated_emissions_electricity = total_emissions * eta_el * 1 / (eta_el + eta_th) - allocated_emissions_heat = total_emissions * eta_th * 1 / (eta_el + eta_th) - - elif method == 'efficiency': - allocated_emissions_electricity = total_emissions * eta_th * 1 / (eta_el + eta_th) - allocated_emissions_heat = total_emissions * eta_el * 1 / (eta_el + eta_th) - - elif method == 'finnish': - if kwargs is not None and kwargs.keys() >= {'eta_el_ref', 'eta_th_ref'}: - eta_el_ref = kwargs.get('eta_el_ref') - eta_th_ref = kwargs.get('eta_th_ref') + if method == "iea": + allocated_emissions_electricity = ( + total_emissions * eta_el * 1 / (eta_el + eta_th) + ) + allocated_emissions_heat = ( + total_emissions * eta_th * 1 / (eta_el + eta_th) + ) + + elif method == "efficiency": + allocated_emissions_electricity = ( + total_emissions * eta_th * 1 / (eta_el + eta_th) + ) + allocated_emissions_heat = ( + total_emissions * eta_el * 1 / (eta_el + eta_th) + ) + + elif method == "finnish": + if kwargs is not None and kwargs.keys() >= { + "eta_el_ref", + "eta_th_ref", + }: + eta_el_ref = kwargs.get("eta_el_ref") + eta_th_ref = kwargs.get("eta_th_ref") else: - raise ValueError('Must specify eta_el_ref, eta_th_ref when using finnish method.') + raise ValueError( + "Must specify eta_el_ref, eta_th_ref" + " when using finnish method." + ) pee = 1 - 1 / ((eta_el / eta_el_ref) + (eta_th / eta_th_ref)) - allocated_emissions_electricity = total_emissions * (1 - pee) * (eta_el / eta_el_ref) - allocated_emissions_heat = total_emissions * (1 - pee) * (eta_th / eta_th_ref) + allocated_emissions_electricity = ( + total_emissions * (1 - pee) * (eta_el / eta_el_ref) + ) + allocated_emissions_heat = ( + total_emissions * (1 - pee) * (eta_th / eta_th_ref) + ) else: - raise ValueError(f"Method '{method}' is not available. " - "Please choose from ['iea', finnish', 'efficiency']") + raise ValueError( + f"Method '{method}' is not available. " + "Please choose from ['iea', finnish', 'efficiency']" + ) return allocated_emissions_electricity, allocated_emissions_heat diff --git a/src/oemof/thermal/compression_heatpumps_and_chillers.py b/src/oemof/thermal/compression_heatpumps_and_chillers.py index 06f455e9..5f0ff373 100644 --- a/src/oemof/thermal/compression_heatpumps_and_chillers.py +++ b/src/oemof/thermal/compression_heatpumps_and_chillers.py @@ -4,9 +4,9 @@ This module provides functions to calculate compression heat pumps and compression chillers. -This file is part of project oemof (github.com/oemof/oemof-thermal). It's copyrighted -by the contributors recorded in the version control history of the file, -available from its original location: +This file is part of project oemof (github.com/oemof/oemof-thermal). It's +copyrighted by the contributors recorded in the version control history of the +file, available from its original location: oemof-thermal/src/oemof/thermal/compression_heatpumps_and_chillers.py SPDX-License-Identifier: MIT @@ -14,9 +14,14 @@ import pandas as pd -def calc_cops(mode, temp_high, temp_low, quality_grade, temp_threshold_icing=2, - factor_icing=None): - +def calc_cops( + mode, + temp_high, + temp_low, + quality_grade, + temp_threshold_icing=2, + factor_icing=None, +): r""" Calculates the Coefficient of Performance (COP) of heat pumps and chillers based on the Carnot efficiency (ideal process) and a scale-down factor. @@ -51,7 +56,7 @@ def calc_cops(mode, temp_high, temp_low, quality_grade, temp_threshold_icing=2, quality_grade : numerical value Factor that scales down the efficiency of the real heat pump (or chiller) process from the ideal process (Carnot efficiency), where - a factor of 1 means teh real process is equal to the ideal one. + a factor of 1 means teh real process is equal to the ideal one. factor_icing: numerical value Sets the relative COP drop caused by icing, where 1 stands for no efficiency-drop. @@ -70,17 +75,22 @@ def calc_cops(mode, temp_high, temp_low, quality_grade, temp_threshold_icing=2, """ # Check if input arguments have proper type and length if not isinstance(temp_low, (list, pd.Series)): - raise TypeError("Argument 'temp_low' is not of type list or pd.Series!") + raise TypeError( + "Argument 'temp_low' is not of type list or pd.Series!" + ) if not isinstance(temp_high, (list, pd.Series)): - raise TypeError("Argument 'temp_high' is not of " - "type list or pd.Series!") + raise TypeError( + "Argument 'temp_high' is not of " "type list or pd.Series!" + ) if len(temp_high) != len(temp_low): if (len(temp_high) != 1) and ((len(temp_low) != 1)): - raise IndexError("Arguments 'temp_low' and 'temp_high' " - "have to be of same length or one has " - "to be of length 1 !") + raise IndexError( + "Arguments 'temp_low' and 'temp_high' " + "have to be of same length or one has " + "to be of length 1 !" + ) # if factor_icing is not None and consider_icing is False: # raise ValueError('Argument factor_icing can not be used without ' @@ -95,21 +105,25 @@ def calc_cops(mode, temp_high, temp_low, quality_grade, temp_threshold_icing=2, length = max([len(temp_high), len(temp_low)]) if len(temp_high) == 1: list_temp_high_K = [temp_high[0] + 273.15] * length - elif len(temp_high) == length: + else: # len(temp_high) == length: list_temp_high_K = [t + 273.15 for t in temp_high] if len(temp_low) == 1: list_temp_low_K = [temp_low[0] + 273.15] * length - elif len(temp_low) == length: + else: # len(temp_low) == length: list_temp_low_K = [t + 273.15 for t in temp_low] # Calculate COPs depending on selected mode (without icing). if factor_icing is None: if mode == "heat_pump": - cops = [quality_grade * t_h / (t_h - t_l) for - t_h, t_l in zip(list_temp_high_K, list_temp_low_K)] + cops = [ + quality_grade * t_h / (t_h - t_l) + for t_h, t_l in zip(list_temp_high_K, list_temp_low_K) + ] elif mode == "chiller": - cops = [quality_grade * t_l / (t_h - t_l) for - t_h, t_l in zip(list_temp_high_K, list_temp_low_K)] + cops = [ + quality_grade * t_l / (t_h - t_l) + for t_h, t_l in zip(list_temp_high_K, list_temp_low_K) + ] # Calculate COPs of a heat pump and lower COP when icing occurs. elif factor_icing is not None: @@ -122,8 +136,9 @@ def calc_cops(mode, temp_high, temp_low, quality_grade, temp_threshold_icing=2, if t_l >= temp_threshold_icing + 273.15: cops = cops + [quality_grade * t_h / (t_h - t_l)] elif mode == "chiller": - raise ValueError("Argument 'factor_icing' has " - "to be None for mode='chiller'!") + raise ValueError( + "Argument 'factor_icing' has " "to be None for mode='chiller'!" + ) return cops @@ -165,50 +180,54 @@ def calc_max_Q_dot_chill(nominal_conditions, cops): if not isinstance(cops, list): raise TypeError("Argument 'cops' is not of type list!") - nominal_cop = (nominal_conditions['nominal_Q_chill'] / nominal_conditions[ - 'nominal_el_consumption']) + nominal_cop = ( + nominal_conditions["nominal_Q_chill"] + / nominal_conditions["nominal_el_consumption"] + ) max_Q_chill = [actual_cop / nominal_cop for actual_cop in cops] return max_Q_chill def calc_max_Q_dot_heat(nominal_conditions, cops): r""" - Calculates the maximal heating capacity (relative value) of a - heat pump. - - Note - ---- - This function assumes the heating capacity of a heat pump can exceed - the rated nominal capacity (e.g., from the technical specification - sheet). That means: The value of :py:obj:`max_Q_hot` can be greater - than 1. - Make sure your actual heat pump is capable of doing so. - If not, use 1 for the maximal heating capacity. + Calculates the maximal heating capacity (relative value) of a + heat pump. + + Note + ---- + This function assumes the heating capacity of a heat pump can exceed + the rated nominal capacity (e.g., from the technical specification + sheet). That means: The value of :py:obj:`max_Q_hot` can be greater + than 1. + Make sure your actual heat pump is capable of doing so. + If not, use 1 for the maximal heating capacity. .. calc_max_Q_dot_heat-equations: :math:`\dot{Q}_\mathrm{hot, max} = \frac{COP_\mathrm{actual}}{COP_\mathrm{nominal}}` - Parameters - ---------- - nominal_conditions : dict - Dictionary describing one operating point (e.g., operation - under STC) of the heat pump by its - heating capacity, its electricity consumption and its COP - ('nominal_Q_hot', 'nominal_el_consumption' and 'nominal_cop') - cops : list of numerical values - Actual COP - - Returns - ------- - max_Q_hot : list of numerical values - Maximal heating capacity (relative value). Value is equal or - greater than 0 and can be greater than 1. - - """ - nominal_cop = (nominal_conditions['nominal_Q_hot'] / nominal_conditions[ - 'nominal_el_consumption']) + Parameters + ---------- + nominal_conditions : dict + Dictionary describing one operating point (e.g., operation + under STC) of the heat pump by its + heating capacity, its electricity consumption and its COP + ('nominal_Q_hot', 'nominal_el_consumption' and 'nominal_cop') + cops : list of numerical values + Actual COP + + Returns + ------- + max_Q_hot : list of numerical values + Maximal heating capacity (relative value). Value is equal or + greater than 0 and can be greater than 1. + + """ + nominal_cop = ( + nominal_conditions["nominal_Q_hot"] + / nominal_conditions["nominal_el_consumption"] + ) max_Q_hot = [actual_cop / nominal_cop for actual_cop in cops] return max_Q_hot @@ -244,9 +263,11 @@ def calc_chiller_quality_grade(nominal_conditions): Quality grade """ - t_h = nominal_conditions['t_high_nominal'] + 273.15 - t_l = nominal_conditions['t_low_nominal'] + 273.15 - nominal_cop = (nominal_conditions['nominal_Q_chill'] / nominal_conditions[ - 'nominal_el_consumption']) + t_h = nominal_conditions["t_high_nominal"] + 273.15 + t_l = nominal_conditions["t_low_nominal"] + 273.15 + nominal_cop = ( + nominal_conditions["nominal_Q_chill"] + / nominal_conditions["nominal_el_consumption"] + ) q_grade = nominal_cop / (t_l / (t_h - t_l)) return q_grade diff --git a/src/oemof/thermal/concentrating_solar_power.py b/src/oemof/thermal/concentrating_solar_power.py index 4f3eed54..c9b1ad94 100644 --- a/src/oemof/thermal/concentrating_solar_power.py +++ b/src/oemof/thermal/concentrating_solar_power.py @@ -3,28 +3,44 @@ """ This module is designed to hold functions which are necessary for the CSP. -This file is part of project oemof (github.com/oemof/oemof-thermal). It's copyrighted -by the contributors recorded in the version control history of the file, -available from its original location: +This file is part of project oemof (github.com/oemof/oemof-thermal). It's +copyrighted by the contributors recorded in the version control history of the +file, available from its original location: oemof-thermal/src/oemof/thermal/concentrating_solar_power.py SPDX-License-Identifier: MIT """ -import pvlib -import pandas as pd -import numpy as np import warnings +import numpy as np +import pandas as pd +import pvlib + -def csp_precalc(lat, long, collector_tilt, collector_azimuth, cleanliness, - eta_0, c_1, c_2, - temp_collector_inlet, temp_collector_outlet, temp_amb, - a_1, a_2, a_3=0, a_4=0, a_5=0, a_6=0, - loss_method='Janotte', - irradiance_method='horizontal', - **kwargs): +def csp_precalc( + lat, + long, + collector_tilt, + collector_azimuth, + cleanliness, + eta_0, + c_1, + c_2, + temp_collector_inlet, + temp_collector_outlet, + temp_amb, + a_1, + a_2, + a_3=0, + a_4=0, + a_5=0, + a_6=0, + loss_method="Janotte", + irradiance_method="horizontal", + **kwargs, +): r""" Calculates collectors efficiency and irradiance according to [1] and the heat of the thermal collector. For the calculation of irradiance pvlib [2] @@ -133,84 +149,109 @@ def csp_precalc(lat, long, collector_tilt, collector_azimuth, cleanliness, https://doi.org/10.21105/joss.00884 """ - if loss_method not in ['Janotte', 'Andasol']: - raise ValueError( - "loss_method should be 'Janotte' or 'Andasol'") + if loss_method not in ["Janotte", "Andasol"]: + raise ValueError("loss_method should be 'Janotte' or 'Andasol'") - if irradiance_method not in ['normal', 'horizontal']: + if irradiance_method not in ["normal", "horizontal"]: raise ValueError( - "irradiance_method should be 'normal' or 'horizontal'") + "irradiance_method should be 'normal' or 'horizontal'" + ) - required_dict = {'horizontal': 'E_dir_hor', 'normal': 'dni'} + required_dict = {"horizontal": "E_dir_hor", "normal": "dni"} irradiance_required = required_dict[irradiance_method] if irradiance_required not in kwargs: raise AttributeError( - f"'{irradiance_required}' necessary for {irradiance_method} is not provided") + f"'{irradiance_required}' necessary for {irradiance_method} is not provided" + ) - if loss_method == 'Andasol' and (c_2 != 0): - warnings.warn( - "Parameter c_2 is not used for loss method 'Andasol'") + if loss_method == "Andasol" and (c_2 != 0): + warnings.warn("Parameter c_2 is not used for loss method 'Andasol'") - if loss_method == 'Andasol' and (a_3 == 0 or a_4 == 0 or a_5 == 0 or a_6 == 0): + if loss_method == "Andasol" and ( + a_3 == 0 or a_4 == 0 or a_5 == 0 or a_6 == 0 + ): warnings.warn( - "Parameters a_3 to a_6 are required for loss method 'Andasol'") + "Parameters a_3 to a_6 are required for loss method 'Andasol'" + ) irradiance = kwargs.get(irradiance_required) if not temp_amb.index.equals(irradiance.index): - raise IndexError(f"Index of temp_amb and {irradiance_required} have to be the same.") + raise IndexError( + f"Index of temp_amb and {irradiance_required} have to be the same." + ) # Creation of a df with 2 columns - data = pd.DataFrame({'irradiance': irradiance, - 't_amb': temp_amb}) + data = pd.DataFrame({"irradiance": irradiance, "t_amb": temp_amb}) # Calculation of geometrical position of collector with the pvlib solarposition = pvlib.solarposition.get_solarposition( - time=data.index, - latitude=lat, - longitude=long) + time=data.index, latitude=lat, longitude=long + ) # Calculation of the tracking data with the pvlib tracking_data = pvlib.tracking.singleaxis( - solarposition['apparent_zenith'], solarposition['azimuth'], - axis_tilt=collector_tilt, axis_azimuth=collector_azimuth) + solarposition["apparent_zenith"], + solarposition["azimuth"], + axis_tilt=collector_tilt, + axis_azimuth=collector_azimuth, + ) # Calculation of the irradiance which hits the collectors surface irradiance_on_collector = calc_irradiance( - tracking_data['surface_tilt'], tracking_data['surface_azimuth'], - solarposition['apparent_zenith'], solarposition['azimuth'], - data['irradiance'], irradiance_method) + tracking_data["surface_tilt"], + tracking_data["surface_azimuth"], + solarposition["apparent_zenith"], + solarposition["azimuth"], + data["irradiance"], + irradiance_method, + ) # Calculation of the irradiance which reaches the collector after all # losses (cleanliness) collector_irradiance = calc_collector_irradiance( - irradiance_on_collector, cleanliness) + irradiance_on_collector, cleanliness + ) # Calculation of the incidence angle modifier iam = calc_iam( - a_1, a_2, a_3, a_4, a_5, a_6, tracking_data['aoi'], loss_method) + a_1, a_2, a_3, a_4, a_5, a_6, tracking_data["aoi"], loss_method + ) # Calculation of the collectors efficiency eta_c = calc_eta_c( - eta_0, c_1, c_2, iam, temp_collector_inlet, temp_collector_outlet, - data['t_amb'], collector_irradiance, loss_method) + eta_0, + c_1, + c_2, + iam, + temp_collector_inlet, + temp_collector_outlet, + data["t_amb"], + collector_irradiance, + loss_method, + ) # Calculation of the collectors heat - collector_heat = calc_heat_coll( - eta_c, collector_irradiance) + collector_heat = calc_heat_coll(eta_c, collector_irradiance) # Writing the results in the output df - data['collector_irradiance'] = collector_irradiance - data['eta_c'] = eta_c - data['collector_heat'] = collector_heat + data["collector_irradiance"] = collector_irradiance + data["eta_c"] = eta_c + data["collector_heat"] = collector_heat return data -def calc_irradiance(surface_tilt, surface_azimuth, apparent_zenith, azimuth, - irradiance, irradiance_method): +def calc_irradiance( + surface_tilt, + surface_azimuth, + apparent_zenith, + azimuth, + irradiance, + irradiance_method, +): r""" Parameters @@ -240,16 +281,17 @@ def calc_irradiance(surface_tilt, surface_azimuth, apparent_zenith, azimuth, Irradiance which hits collectors surface. """ - if irradiance_method == 'horizontal': + if irradiance_method == "horizontal": poa_horizontal_ratio = pvlib.irradiance.poa_horizontal_ratio( - surface_tilt, surface_azimuth, apparent_zenith, azimuth) + surface_tilt, surface_azimuth, apparent_zenith, azimuth + ) poa_horizontal_ratio[poa_horizontal_ratio < 0] = 0 irradiance_on_collector = irradiance * poa_horizontal_ratio - elif irradiance_method == 'normal': + elif irradiance_method == "normal": irradiance_on_collector = pvlib.irradiance.beam_component( - surface_tilt, surface_azimuth, apparent_zenith, azimuth, - irradiance) + surface_tilt, surface_azimuth, apparent_zenith, azimuth, irradiance + ) return irradiance_on_collector @@ -318,18 +360,33 @@ def calc_iam(a_1, a_2, a_3, a_4, a_5, a_6, aoi, loss_method): Incidence angle modifier: series of numeric """ - if loss_method == 'Janotte': + if loss_method == "Janotte": iam = 1 - a_1 * abs(aoi) - a_2 * aoi**2 - if loss_method == 'Andasol': - iam = (1 - a_1 * abs(aoi) - a_2 * aoi**2 - a_3 * aoi**3 - a_4 * aoi**4 - - a_5 * aoi**5 - a_6 * aoi**6) + if loss_method == "Andasol": + iam = ( + 1 + - a_1 * abs(aoi) + - a_2 * aoi**2 + - a_3 * aoi**3 + - a_4 * aoi**4 + - a_5 * aoi**5 + - a_6 * aoi**6 + ) return iam -def calc_eta_c(eta_0, c_1, c_2, iam, - temp_collector_inlet, temp_collector_outlet, temp_amb, - collector_irradiance, loss_method): +def calc_eta_c( + eta_0, + c_1, + c_2, + iam, + temp_collector_inlet, + temp_collector_outlet, + temp_amb, + collector_irradiance, + loss_method, +): r""" Calculates collectors efficiency depending on the loss method @@ -380,12 +437,17 @@ def calc_eta_c(eta_0, c_1, c_2, iam, """ - if loss_method == 'Janotte': - delta_temp = (temp_collector_inlet + temp_collector_outlet) / 2 - temp_amb - eta_c = eta_0 * iam - c_1 * delta_temp / collector_irradiance - c_2\ - * delta_temp ** 2 / collector_irradiance - - if loss_method == 'Andasol': + if loss_method == "Janotte": + delta_temp = ( + temp_collector_inlet + temp_collector_outlet + ) / 2 - temp_amb + eta_c = ( + eta_0 * iam + - c_1 * delta_temp / collector_irradiance + - c_2 * delta_temp**2 / collector_irradiance + ) + + if loss_method == "Andasol": eta_c = eta_0 * iam - c_1 / collector_irradiance eta_c[eta_c < 0] = 0 diff --git a/src/oemof/thermal/facades.py b/src/oemof/thermal/facades.py index 6d0600cc..f540dbc0 100644 --- a/src/oemof/thermal/facades.py +++ b/src/oemof/thermal/facades.py @@ -2,17 +2,20 @@ """ Adapted from `oemof.tabular's facades -`_ +`_ Facade's are classes providing a simplified view on more complex classes. -More specifically, the :class:`Facade` s in this module inherit from `oemof.solph`'s generic -classes to serve as more concrete and energy specific interface. - -The concept of the facades has been derived from oemof.tabular. The idea is to be able to -instantiate a :class:`Facade` using only keyword arguments. Under the hood the :class:`Facade` then -uses these arguments to construct an `oemof.solph` component and sets it up to be easily used in an -:class:`EnergySystem`. Usually, a subset of the attributes of the parent class remains while another -part can be addressed by more specific or simpler attributes. +More specifically, the :class:`Facade` s in this module inherit from +`oemof.solph`'s generic classes to serve as more concrete and energy specific +interface. + +The concept of the facades has been derived from oemof.tabular. The idea is to +be able to instantiate a :class:`Facade` using only keyword arguments. Under +the hood the :class:`Facade` then uses these arguments to construct an +`oemof.solph` component and sets it up to be easily used in an +:class:`EnergySystem`. Usually, a subset of the attributes of the parent class +remains while another part can be addressed by more specific or simpler +attributes. **Note** The mathematical notation is as follows: @@ -26,29 +29,23 @@ import warnings from collections import deque -from oemof.thermal.stratified_thermal_storage import calculate_storage_dimensions,\ - calculate_capacities, calculate_losses -from oemof.thermal.concentrating_solar_power import csp_precalc -from oemof.thermal.solar_thermal_collector import flat_plate_precalc from oemof.network.energy_system import EnergySystem from oemof.network.network import Node - +from oemof.solph import Flow +from oemof.solph import Investment +from oemof.solph import sequence +from oemof.solph.components import Converter +from oemof.solph.components import GenericStorage +from oemof.solph.components import Source from oemof.tools.debugging import SuspiciousUsageWarning -try: - from oemof.solph import ( - Flow, - Investment, - sequence, - ) - from oemof.solph.components import ( - GenericStorage, - Transformer, - Source, - ) -except ImportError: # solph <= v0.4 - from oemof.solph import Flow, Investment, Transformer, Source - from oemof.solph.components import GenericStorage - from oemof.solph.plumbing import sequence + +from oemof.thermal.concentrating_solar_power import csp_precalc +from oemof.thermal.solar_thermal_collector import flat_plate_precalc +from oemof.thermal.stratified_thermal_storage import calculate_capacities +from oemof.thermal.stratified_thermal_storage import calculate_losses +from oemof.thermal.stratified_thermal_storage import ( + calculate_storage_dimensions, +) def add_subnodes(n, **kwargs): @@ -66,9 +63,8 @@ class Facade(Node): raises an error if he doesn't find them. """ - def __init__(self, *args, **kwargs): - """ - """ + def __init__(self, label, **kwargs): + """ """ self.mapped_type = type(self) @@ -76,7 +72,7 @@ def __init__(self, *args, **kwargs): required = kwargs.pop("_facade_requires_", []) - super().__init__(*args, **kwargs) + super().__init__(label=label) self.subnodes = [] EnergySystem.signals[EnergySystem.add].connect( @@ -95,7 +91,7 @@ def __init__(self, *args, **kwargs): ) def _nominal_value(self): - """ Returns None if self.expandable ist True otherwise it returns + """Returns None if self.expandable ist True otherwise it returns the capacity """ if self.expandable is True: @@ -147,7 +143,7 @@ def update(self): class StratifiedThermalStorage(GenericStorage, Facade): - r""" Stratified thermal storage unit. + r"""Stratified thermal storage unit. Parameters ---------- @@ -193,9 +189,9 @@ class StratifiedThermalStorage(GenericStorage, Facade): more information on possible parameters) - The attribute :attr:`nominal_storage_capacity` of the base class :class:`GenericStorage` - should not be passed because it is determined internally from :attr:`height` - and :attr:`parameter`. + The attribute :attr:`nominal_storage_capacity` of the base class + :class:`GenericStorage` should not be passed because it is determined + internally from :attr:`height` and :attr:`parameter`. Examples --------- @@ -218,34 +214,43 @@ class StratifiedThermalStorage(GenericStorage, Facade): """ def __init__( - self, - label=None, - inputs=None, - outputs=None, - nominal_storage_capacity=None, - initial_storage_level=None, - investment=None, - invest_relation_input_output=None, - invest_relation_input_capacity=None, - invest_relation_output_capacity=None, - min_storage_level=0.0, - max_storage_level=1.0, - balanced=True, - loss_rate=0, - fixed_losses_relative=0, - fixed_losses_absolute=0, - inflow_conversion_factor=1, - outflow_conversion_factor=1, - custom_attributes=None, - **kwargs + self, + label=None, + inputs=None, + outputs=None, + nominal_storage_capacity=None, + initial_storage_level=None, + investment=None, + invest_relation_input_output=None, + invest_relation_input_capacity=None, + invest_relation_output_capacity=None, + min_storage_level=0.0, + max_storage_level=1.0, + balanced=True, + loss_rate=0, + fixed_losses_relative=0, + fixed_losses_absolute=0, + inflow_conversion_factor=1, + outflow_conversion_factor=1, + custom_attributes=None, + **kwargs, ): + kwargs.update( + { + "_facade_requires_": [ + "bus", + "diameter", + "temp_h", + "temp_c", + "temp_env", + "u_value", + ] + } + ) Facade.__init__( self, - _facade_requires_=[ - "bus", "diameter", - "temp_h", "temp_c", "temp_env", - "u_value"], - **kwargs + label=label, + **kwargs, ) with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=SuspiciousUsageWarning) @@ -275,8 +280,8 @@ def __init__( self.height = kwargs.get("height") self.water_properties = { - 'heat_capacity': kwargs.get("heat_capacity"), - 'density': kwargs.get("density") + "heat_capacity": kwargs.get("heat_capacity"), + "density": kwargs.get("density"), } self.capacity = kwargs.get("capacity") @@ -316,7 +321,11 @@ def __init__( self.temp_h, self.temp_c, self.temp_env, - **{key: value for key, value in self.water_properties.items() if value is not None} + **{ + key: value + for key, value in self.water_properties.items() + if value is not None + }, ) self.loss_rate = losses[0] @@ -328,8 +337,7 @@ def __init__( self.build_solph_components() def build_solph_components(self): - """ - """ + """ """ self.inflow_conversion_factor = sequence(self.efficiency) self.outflow_conversion_factor = sequence(self.efficiency) @@ -361,24 +369,30 @@ def build_solph_components(self): maximum=self.capacity_potential, existing=self.capacity, ), - **self.input_parameters + **self.input_parameters, ) # set investment, but no costs (as relation input / output = 1) fo = Flow( investment=Investment(), variable_costs=self.marginal_cost, - **self.output_parameters + **self.output_parameters, ) # required for correct grouping in oemof.solph.components self._invest_group = True else: - self.volume = calculate_storage_dimensions(self.height, self.diameter)[0] + self.volume = calculate_storage_dimensions( + self.height, self.diameter + )[0] self.nominal_storage_capacity = calculate_capacities( self.volume, self.temp_h, self.temp_c, - **{key: value for key, value in self.water_properties.items() if value is not None} + **{ + key: value + for key, value in self.water_properties.items() + if value is not None + }, ) fi = Flow( @@ -387,7 +401,7 @@ def build_solph_components(self): fo = Flow( nominal_value=self._nominal_value(), variable_costs=self.marginal_cost, - **self.output_parameters + **self.output_parameters, ) self.inputs.update({self.bus: fi}) @@ -397,17 +411,19 @@ def build_solph_components(self): self._set_flows() -class ParabolicTroughCollector(Transformer, Facade): - r""" Parabolic trough collector unit +class ParabolicTroughCollector(Converter, Facade): + r"""Parabolic trough collector unit Parameters ---------- heat_bus: oemof.solph.Bus An oemof bus instance in which absorbs the collectors heat. electrical_bus: oemof.solph.Bus - An oemof bus instance which provides electrical energy to the collector. + An oemof bus instance which provides electrical energy to the + collector. electrical_consumption: numeric - Specifies how much electrical energy is used per provided thermal energy. + Specifies how much electrical energy is used per provided thermal + energy. additional_losses: numeric Specifies how much thermal energy is lost in peripheral parts like pipes and pumps. @@ -450,19 +466,11 @@ class ParabolicTroughCollector(Transformer, Facade): ... ) """ - def __init__(self, *args, **kwargs): - - kwargs.update( - { - "_facade_requires_": [ - "longitude" - ] - } - ) - Facade.__init__(self, *args, **kwargs) - Transformer.__init__(self) + def __init__(self, **kwargs): - self.label = kwargs.get("label") + kwargs.update({"_facade_requires_": ["longitude"]}) + Facade.__init__(self, **kwargs) + Converter.__init__(self, label=kwargs.get("label")) self.heat_bus = kwargs.get("heat_bus") @@ -518,36 +526,57 @@ def __init__(self, *args, **kwargs): if self.irradiance_method == "horizontal": heat = csp_precalc( - self.latitude, self.longitude, - self.collector_tilt, self.collector_azimuth, self.cleanliness, - self.eta_0, self.c_1, self.c_2, - self.temp_collector_inlet, self.temp_collector_outlet, + self.latitude, + self.longitude, + self.collector_tilt, + self.collector_azimuth, + self.cleanliness, + self.eta_0, + self.c_1, + self.c_2, + self.temp_collector_inlet, + self.temp_collector_outlet, self.temp_amb, - self.a_1, self.a_2, self.a_3, self.a_4, self.a_5, self.a_6, + self.a_1, + self.a_2, + self.a_3, + self.a_4, + self.a_5, + self.a_6, loss_method=self.loss_method, irradiance_method=self.irradiance_method, - E_dir_hor=self.irradiance + E_dir_hor=self.irradiance, ) if self.irradiance_method == "normal": heat = csp_precalc( - self.latitude, self.longitude, - self.collector_tilt, self.collector_azimuth, self.cleanliness, - self.eta_0, self.c_1, self.c_2, - self.temp_collector_inlet, self.temp_collector_outlet, + self.latitude, + self.longitude, + self.collector_tilt, + self.collector_azimuth, + self.cleanliness, + self.eta_0, + self.c_1, + self.c_2, + self.temp_collector_inlet, + self.temp_collector_outlet, self.temp_amb, - self.a_1, self.a_2, self.a_3, self.a_4, self.a_5, self.a_6, + self.a_1, + self.a_2, + self.a_3, + self.a_4, + self.a_5, + self.a_6, loss_method=self.loss_method, irradiance_method=self.irradiance_method, - dni=self.irradiance + dni=self.irradiance, ) - self.collectors_heat = heat['collector_heat'] + self.collectors_heat = heat["collector_heat"] self.build_solph_components() def build_solph_components(self): - """ - """ + """ """ if self.expandable: raise NotImplementedError( @@ -557,49 +586,49 @@ def build_solph_components(self): inflow = Source( label=self.label + "-inflow", outputs={ - self: Flow(nominal_value=self.aperture_area, - max=self.collectors_heat) + self: Flow( + nominal_value=self.aperture_area, max=self.collectors_heat + ) }, ) self.conversion_factors.update( { - self.electrical_bus: sequence(self.electrical_consumption - * (1 - self.additional_losses)), + self.electrical_bus: sequence( + self.electrical_consumption * (1 - self.additional_losses) + ), self.heat_bus: sequence(1 - self.additional_losses), - inflow: sequence(1) + inflow: sequence(1), } ) - self.inputs.update( - {self.electrical_bus: Flow()} - ) - self.outputs.update( - {self.heat_bus: Flow()} - ) + self.inputs.update({self.electrical_bus: Flow()}) + self.outputs.update({self.heat_bus: Flow()}) self.subnodes = (inflow,) -class SolarThermalCollector(Transformer, Facade): - r""" Solar thermal collector unit +class SolarThermalCollector(Converter, Facade): + r"""Solar thermal collector unit Parameters: ----------- heat_out_bus: oemof.solph.Bus An oemof bus instance which absorbs the collectors heat. electrical_in_bus: oemof.solph.Bus - An oemof bus instance which provides electrical energy to the collector. + An oemof bus instance which provides electrical energy to the + collector. electrical_consumption: numeric - Specifies how much electrical energy is used per provided thermal energy. + Specifies how much electrical energy is used per provided thermal + energy. peripheral_losses: numeric Specifies how much thermal energy is lost in peripheral parts like pipes and pumps as percentage of provided thermal energy. aperture_area: numeric Specifies the size of the collector as surface area. - See the API of flat_plate_precalc in oemof.thermal.solar_thermal_collector for - the other parameters. + See the API of flat_plate_precalc in oemof.thermal.solar_thermal_collector + for the other parameters. Example: ---------- @@ -629,19 +658,11 @@ class SolarThermalCollector(Transformer, Facade): ) """ - def __init__(self, *args, **kwargs): + def __init__(self, **kwargs): - kwargs.update( - { - "_facade_requires_": [ - "longitude" - ] - } - ) - Facade.__init__(self, *args, **kwargs) - Transformer.__init__(self) - - self.label = kwargs.get("label") + kwargs.update({"_facade_requires_": ["longitude"]}) + Facade.__init__(self, **kwargs) + Converter.__init__(self, label=kwargs.get("label")) self.heat_out_bus = kwargs.get("heat_out_bus") @@ -694,49 +715,41 @@ def __init__(self, *args, **kwargs): self.temp_amb, ) - self.collectors_eta_c = data['eta_c'] + self.collectors_eta_c = data["eta_c"] - self.collectors_heat = data['collectors_heat'] + self.collectors_heat = data["collectors_heat"] self.build_solph_components() def build_solph_components(self): - """ - """ + """ """ if self.expandable: raise NotImplementedError( - "Investment for solar thermal collector facade has not been implemented yet." + "Investment for solar thermal collector" + " facade has not been implemented yet." ) inflow = Source( label=self.label + "-inflow", outputs={ - self: Flow(nominal_value=self.aperture_area, - max=self.collectors_heat) + self: Flow( + nominal_value=self.aperture_area, max=self.collectors_heat + ) }, ) self.conversion_factors.update( { - self.electricity_in_bus: sequence(self.electrical_consumption - * (1 - self.peripheral_losses)), + self.electricity_in_bus: sequence( + self.electrical_consumption * (1 - self.peripheral_losses) + ), self.heat_out_bus: sequence(1 - self.peripheral_losses), - inflow: sequence(1) + inflow: sequence(1), } ) - self.inputs.update( - { - self.electricity_in_bus: Flow( - ) - } - ) - self.outputs.update( - { - self.heat_out_bus: Flow( - ) - } - ) + self.inputs.update({self.electricity_in_bus: Flow()}) + self.outputs.update({self.heat_out_bus: Flow()}) self.subnodes = (inflow,) diff --git a/src/oemof/thermal/solar_thermal_collector.py b/src/oemof/thermal/solar_thermal_collector.py index 81e5b5c3..814889d0 100644 --- a/src/oemof/thermal/solar_thermal_collector.py +++ b/src/oemof/thermal/solar_thermal_collector.py @@ -1,19 +1,20 @@ # -*- coding: utf-8 """ -This module is designed to hold functions for calculating a solar thermal collector. +This module is designed to hold functions for calculating a solar thermal +collector. -This file is part of project oemof (github.com/oemof/oemof-thermal). It's copyrighted -by the contributors recorded in the version control history of the file, -available from its original location: +This file is part of project oemof (github.com/oemof/oemof-thermal). It's +copyrighted by the contributors recorded in the version control history of the +file, available from its original location: oemof-thermal/src/oemof/thermal/solar_thermal_collector.py SPDX-License-Identifier: MIT """ -import pvlib import pandas as pd +import pvlib def flat_plate_precalc( @@ -28,7 +29,7 @@ def flat_plate_precalc( delta_temp_n, irradiance_global, irradiance_diffuse, - temp_amb + temp_amb, ): r""" Calculates collectors heat, efficiency and irradiance @@ -50,7 +51,8 @@ def flat_plate_precalc( The tilt of the collector. collector_azimuth: numeric - The azimuth of the collector. Azimuth according to pvlib in decimal degrees East of North. + The azimuth of the collector. Azimuth according to pvlib in decimal + degrees East of North. eta_0: numeric Optical efficiency of the collector. @@ -86,9 +88,9 @@ def flat_plate_precalc( # Creation of a df with 3 columns data = pd.DataFrame( { - 'ghi': irradiance_global, - 'dhi': irradiance_diffuse, - 'temp_amb': temp_amb + "ghi": irradiance_global, + "dhi": irradiance_diffuse, + "temp_amb": temp_amb, } ) @@ -105,20 +107,20 @@ def flat_plate_precalc( ) dni = pvlib.irradiance.dni( - ghi=data['ghi'], dhi=data['dhi'], zenith=solposition['apparent_zenith'] + ghi=data["ghi"], dhi=data["dhi"], zenith=solposition["apparent_zenith"] ) total_irradiation = pvlib.irradiance.get_total_irradiance( surface_tilt=collector_tilt, surface_azimuth=collector_azimuth, - solar_zenith=solposition['apparent_zenith'], - solar_azimuth=solposition['azimuth'], + solar_zenith=solposition["apparent_zenith"], + solar_azimuth=solposition["azimuth"], dni=dni.fillna(0), # fill NaN values with '0' - ghi=data['ghi'], - dhi=data['dhi'], + ghi=data["ghi"], + dhi=data["dhi"], ) - data['col_ira'] = total_irradiation['poa_global'] + data["col_ira"] = total_irradiation["poa_global"] eta_c = calc_eta_c_flate_plate( eta_0, @@ -126,11 +128,11 @@ def flat_plate_precalc( a_2, temp_collector_inlet, delta_temp_n, - data['temp_amb'], - total_irradiation['poa_global'], + data["temp_amb"], + total_irradiation["poa_global"], ) - data['eta_c'] = eta_c - collectors_heat = eta_c * total_irradiation['poa_global'] + data["eta_c"] = eta_c + collectors_heat = eta_c * total_irradiation["poa_global"] data["collectors_heat"] = collectors_heat return data diff --git a/src/oemof/thermal/stratified_thermal_storage.py b/src/oemof/thermal/stratified_thermal_storage.py index 4ca46e03..318c2f3e 100644 --- a/src/oemof/thermal/stratified_thermal_storage.py +++ b/src/oemof/thermal/stratified_thermal_storage.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 """ -This module is designed to hold functions for calculating stratified thermal storages. +This module is designed to hold functions for calculating stratified thermal +storages. -This file is part of project oemof (github.com/oemof/oemof-thermal). It's copyrighted -by the contributors recorded in the version control history of the file, -available from its original location: +This file is part of project oemof (github.com/oemof/oemof-thermal). It's +copyrighted by the contributors recorded in the version control history of the +file, available from its original location: oemof-thermal/src/oemof/thermal/stratified_thermal_storage.py SPDX-License-Identifier: MIT @@ -32,17 +33,21 @@ def calculate_storage_u_value(s_iso, lamb_iso, alpha_inside, alpha_outside): Thermal conductivity of isolation layer [W/(m*K)] alpha_inside : numeric - Heat transfer coefficient at the inner surface of the storage [W/(m2*K)] + Heat transfer coefficient at the inner surface of the storage + [W/(m2*K)] alpha_outside : numeric - Heat transfer coefficient at the outer surface of the storage [W/(m2*K)] + Heat transfer coefficient at the outer surface of the storage + [W/(m2*K)] Returns ------- u_value : numeric Thermal transmittance (U-value) [W/(m2*K)] """ - denominator = 1 / alpha_inside + s_iso * 1e-3 / lamb_iso + 1 / alpha_outside + denominator = ( + 1 / alpha_inside + s_iso * 1e-3 / lamb_iso + 1 / alpha_outside + ) u_value = 1 / denominator return u_value @@ -74,8 +79,8 @@ def calculate_storage_dimensions(height, diameter): surface : numeric Total surface of storage [m2] """ - volume = 0.25 * np.pi * diameter ** 2 * height - surface = np.pi * diameter * height + 0.5 * np.pi * diameter ** 2 + volume = 0.25 * np.pi * diameter**2 * height + surface = np.pi * diameter * height + 0.5 * np.pi * diameter**2 return volume, surface @@ -118,7 +123,9 @@ def calculate_capacities( Maximum amount of stored thermal energy [MWh] """ - nominal_storage_capacity = volume * heat_capacity * density * (temp_h - temp_c) + nominal_storage_capacity = ( + volume * heat_capacity * density * (temp_h - temp_c) + ) nominal_storage_capacity *= 1 / 3600 # J to Wh nominal_storage_capacity *= 1e-6 # Wh to MWh @@ -192,19 +199,31 @@ def calculate_losses( nominal storage capacity between two consecutive timesteps [MWh] """ loss_rate = ( - 4 * u_value * 1 / (diameter * density * heat_capacity) * time_increment + 4 + * u_value + * 1 + / (diameter * density * heat_capacity) + * time_increment * 3600 # Ws to Wh ) fixed_losses_relative = ( - 4 * u_value * (temp_c - temp_env) - * 1 / ((diameter * density * heat_capacity) * (temp_h - temp_c)) + 4 + * u_value + * (temp_c - temp_env) + * 1 + / ((diameter * density * heat_capacity) * (temp_h - temp_c)) * time_increment * 3600 # Ws to Wh ) fixed_losses_absolute = ( - 0.25 * u_value * np.pi * diameter ** 2 * (temp_h + temp_c - 2 * temp_env) * time_increment + 0.25 + * u_value + * np.pi + * diameter**2 + * (temp_h + temp_c - 2 * temp_env) + * time_increment ) fixed_losses_absolute *= 1e-6 # Wh to MWh diff --git a/tests/lp_files/csp_collector.lp b/tests/lp_files/csp_collector.lp index 3406863a..bb763fa4 100644 --- a/tests/lp_files/csp_collector.lp +++ b/tests/lp_files/csp_collector.lp @@ -1,6 +1,6 @@ \* Source Pyomo model name=Model *\ -min +min objective: +0 ONE_VAR_CONSTANT @@ -30,47 +30,45 @@ c_e_BusBlock_balance(bus_heat_2)_: +1 flow(solar_collector_bus_heat_2) = 0 -c_e_TransformerBlock_relation(solar_collector_bus_el_bus_heat_0)_: -+0.80000000000000004 flow(bus_el_solar_collector_0) --0.040000000000000008 flow(solar_collector_bus_heat_0) -= 0 - -c_e_TransformerBlock_relation(solar_collector_bus_el_bus_heat_1)_: -+0.80000000000000004 flow(bus_el_solar_collector_1) --0.040000000000000008 flow(solar_collector_bus_heat_1) +c_e_ConverterBlock_relation(solar_collector_solar_collector_inflow_bus_heat_0)_: +-1 flow(solar_collector_bus_heat_0) ++0.8 flow(solar_collector_inflow_solar_collector_0) = 0 -c_e_TransformerBlock_relation(solar_collector_bus_el_bus_heat_2)_: -+0.80000000000000004 flow(bus_el_solar_collector_2) --0.040000000000000008 flow(solar_collector_bus_heat_2) +c_e_ConverterBlock_relation(solar_collector_bus_el_bus_heat_0)_: ++0.8 flow(bus_el_solar_collector_0) +-0.04000000000000001 flow(solar_collector_bus_heat_0) = 0 -c_e_TransformerBlock_relation(solar_collector_solar_collector_inflow_bus_heat_0)_: --1 flow(solar_collector_bus_heat_0) -+0.80000000000000004 flow(solar_collector_inflow_solar_collector_0) +c_e_ConverterBlock_relation(solar_collector_solar_collector_inflow_bus_heat_1)_: +-1 flow(solar_collector_bus_heat_1) ++0.8 flow(solar_collector_inflow_solar_collector_1) = 0 -c_e_TransformerBlock_relation(solar_collector_solar_collector_inflow_bus_heat_1)_: --1 flow(solar_collector_bus_heat_1) -+0.80000000000000004 flow(solar_collector_inflow_solar_collector_1) +c_e_ConverterBlock_relation(solar_collector_bus_el_bus_heat_1)_: ++0.8 flow(bus_el_solar_collector_1) +-0.04000000000000001 flow(solar_collector_bus_heat_1) = 0 -c_e_TransformerBlock_relation(solar_collector_solar_collector_inflow_bus_heat_2)_: +c_e_ConverterBlock_relation(solar_collector_solar_collector_inflow_bus_heat_2)_: -1 flow(solar_collector_bus_heat_2) -+0.80000000000000004 flow(solar_collector_inflow_solar_collector_2) ++0.8 flow(solar_collector_inflow_solar_collector_2) = 0 -c_e_ONE_VAR_CONSTANT: -ONE_VAR_CONSTANT = 1.0 +c_e_ConverterBlock_relation(solar_collector_bus_el_bus_heat_2)_: ++0.8 flow(bus_el_solar_collector_2) +-0.04000000000000001 flow(solar_collector_bus_heat_2) += 0 bounds + 1 <= ONE_VAR_CONSTANT <= 1 0 <= flow(bus_el_solar_collector_0) <= +inf 0 <= flow(bus_el_solar_collector_1) <= +inf 0 <= flow(bus_el_solar_collector_2) <= +inf 0 <= flow(solar_collector_bus_heat_0) <= +inf 0 <= flow(solar_collector_bus_heat_1) <= +inf 0 <= flow(solar_collector_bus_heat_2) <= +inf - 0 <= flow(solar_collector_inflow_solar_collector_0) <= 0 - 0 <= flow(solar_collector_inflow_solar_collector_1) <= 75650.812223268294 - 0 <= flow(solar_collector_inflow_solar_collector_2) <= 0 + 0 <= flow(solar_collector_inflow_solar_collector_0) <= 0.0 + 0 <= flow(solar_collector_inflow_solar_collector_1) <= 75650.8122232683 + 0 <= flow(solar_collector_inflow_solar_collector_2) <= 0.0 end diff --git a/tests/lp_files/solar_thermal_collector.lp b/tests/lp_files/solar_thermal_collector.lp index 2259355a..bac524f9 100644 --- a/tests/lp_files/solar_thermal_collector.lp +++ b/tests/lp_files/solar_thermal_collector.lp @@ -1,6 +1,6 @@ \* Source Pyomo model name=Model *\ -min +min objective: +0 ONE_VAR_CONSTANT @@ -30,47 +30,45 @@ c_e_BusBlock_balance(bus_heat_2)_: +1 flow(solar_collector_bus_heat_2) = 0 -c_e_TransformerBlock_relation(solar_collector_bus_el_bus_heat_0)_: -+0.94999999999999996 flow(bus_el_solar_collector_0) --0.019 flow(solar_collector_bus_heat_0) -= 0 - -c_e_TransformerBlock_relation(solar_collector_bus_el_bus_heat_1)_: -+0.94999999999999996 flow(bus_el_solar_collector_1) --0.019 flow(solar_collector_bus_heat_1) +c_e_ConverterBlock_relation(solar_collector_solar_collector_inflow_bus_heat_0)_: +-1 flow(solar_collector_bus_heat_0) ++0.95 flow(solar_collector_inflow_solar_collector_0) = 0 -c_e_TransformerBlock_relation(solar_collector_bus_el_bus_heat_2)_: -+0.94999999999999996 flow(bus_el_solar_collector_2) --0.019 flow(solar_collector_bus_heat_2) +c_e_ConverterBlock_relation(solar_collector_bus_el_bus_heat_0)_: ++0.95 flow(bus_el_solar_collector_0) +-0.019 flow(solar_collector_bus_heat_0) = 0 -c_e_TransformerBlock_relation(solar_collector_solar_collector_inflow_bus_heat_0)_: --1 flow(solar_collector_bus_heat_0) -+0.94999999999999996 flow(solar_collector_inflow_solar_collector_0) +c_e_ConverterBlock_relation(solar_collector_solar_collector_inflow_bus_heat_1)_: +-1 flow(solar_collector_bus_heat_1) ++0.95 flow(solar_collector_inflow_solar_collector_1) = 0 -c_e_TransformerBlock_relation(solar_collector_solar_collector_inflow_bus_heat_1)_: --1 flow(solar_collector_bus_heat_1) -+0.94999999999999996 flow(solar_collector_inflow_solar_collector_1) +c_e_ConverterBlock_relation(solar_collector_bus_el_bus_heat_1)_: ++0.95 flow(bus_el_solar_collector_1) +-0.019 flow(solar_collector_bus_heat_1) = 0 -c_e_TransformerBlock_relation(solar_collector_solar_collector_inflow_bus_heat_2)_: +c_e_ConverterBlock_relation(solar_collector_solar_collector_inflow_bus_heat_2)_: -1 flow(solar_collector_bus_heat_2) -+0.94999999999999996 flow(solar_collector_inflow_solar_collector_2) ++0.95 flow(solar_collector_inflow_solar_collector_2) = 0 -c_e_ONE_VAR_CONSTANT: -ONE_VAR_CONSTANT = 1.0 +c_e_ConverterBlock_relation(solar_collector_bus_el_bus_heat_2)_: ++0.95 flow(bus_el_solar_collector_2) +-0.019 flow(solar_collector_bus_heat_2) += 0 bounds + 1 <= ONE_VAR_CONSTANT <= 1 0 <= flow(bus_el_solar_collector_0) <= +inf 0 <= flow(bus_el_solar_collector_1) <= +inf 0 <= flow(bus_el_solar_collector_2) <= +inf 0 <= flow(solar_collector_bus_heat_0) <= +inf 0 <= flow(solar_collector_bus_heat_1) <= +inf 0 <= flow(solar_collector_bus_heat_2) <= +inf - 0 <= flow(solar_collector_inflow_solar_collector_0) <= 0 + 0 <= flow(solar_collector_inflow_solar_collector_0) <= 0.0 0 <= flow(solar_collector_inflow_solar_collector_1) <= 14566.653922252568 - 0 <= flow(solar_collector_inflow_solar_collector_2) <= 35868.680625337023 + 0 <= flow(solar_collector_inflow_solar_collector_2) <= 35868.68062533702 end diff --git a/tests/lp_files/stratified_thermal_storage.lp b/tests/lp_files/stratified_thermal_storage.lp index 578f9b26..b6bcd583 100644 --- a/tests/lp_files/stratified_thermal_storage.lp +++ b/tests/lp_files/stratified_thermal_storage.lp @@ -23,35 +23,50 @@ c_e_BusBlock_balance(bus_heat_2)_: +1 flow(thermal_storage_bus_heat_2) = 0 -c_e_GenericStorageBlock_balance(thermal_storage_0)_: --0.99982340904089895 GenericStorageBlock_storage_content(thermal_storage_0) -+1 GenericStorageBlock_storage_content(thermal_storage_1) --1 flow(bus_heat_thermal_storage_0) -+1 flow(thermal_storage_bus_heat_0) +c_e_GenericStorageBlock_losses(thermal_storage_0)_: ++0.00017659095910105282 GenericStorageBlock_storage_content(thermal_storage_0) +-1 GenericStorageBlock_storage_losses(thermal_storage_0) = -0.028863382504856223 -c_e_GenericStorageBlock_balance(thermal_storage_1)_: --0.99982340904089895 GenericStorageBlock_storage_content(thermal_storage_1) -+1 GenericStorageBlock_storage_content(thermal_storage_2) --1 flow(bus_heat_thermal_storage_1) -+1 flow(thermal_storage_bus_heat_1) +c_e_GenericStorageBlock_losses(thermal_storage_1)_: ++0.00017659095910105282 GenericStorageBlock_storage_content(thermal_storage_1) +-1 GenericStorageBlock_storage_losses(thermal_storage_1) = -0.028863382504856223 -c_e_GenericStorageBlock_balance(thermal_storage_2)_: --0.99982340904089895 GenericStorageBlock_storage_content(thermal_storage_2) -+1 GenericStorageBlock_storage_content(thermal_storage_3) --1 flow(bus_heat_thermal_storage_2) -+1 flow(thermal_storage_bus_heat_2) +c_e_GenericStorageBlock_losses(thermal_storage_2)_: ++0.00017659095910105282 GenericStorageBlock_storage_content(thermal_storage_2) +-1 GenericStorageBlock_storage_losses(thermal_storage_2) = -0.028863382504856223 +c_e_GenericStorageBlock_balance(thermal_storage_0)_: ++1 flow(bus_heat_thermal_storage_0) +-1 flow(thermal_storage_bus_heat_0) ++1 GenericStorageBlock_storage_content(thermal_storage_0) +-1 GenericStorageBlock_storage_content(thermal_storage_1) +-1 GenericStorageBlock_storage_losses(thermal_storage_0) += 0 + +c_e_GenericStorageBlock_balance(thermal_storage_1)_: ++1 flow(bus_heat_thermal_storage_1) +-1 flow(thermal_storage_bus_heat_1) ++1 GenericStorageBlock_storage_content(thermal_storage_1) +-1 GenericStorageBlock_storage_content(thermal_storage_2) +-1 GenericStorageBlock_storage_losses(thermal_storage_1) += 0 + +c_e_GenericStorageBlock_balance(thermal_storage_2)_: ++1 flow(bus_heat_thermal_storage_2) +-1 flow(thermal_storage_bus_heat_2) ++1 GenericStorageBlock_storage_content(thermal_storage_2) +-1 GenericStorageBlock_storage_content(thermal_storage_3) +-1 GenericStorageBlock_storage_losses(thermal_storage_2) += 0 + c_e_GenericStorageBlock_balanced_cstr(thermal_storage)_: -1 GenericStorageBlock_storage_content(thermal_storage_0) +1 GenericStorageBlock_storage_content(thermal_storage_3) = 0 -c_e_ONE_VAR_CONSTANT: -ONE_VAR_CONSTANT = 1.0 - bounds 0 <= flow(bus_heat_thermal_storage_0) <= 2 0 <= flow(bus_heat_thermal_storage_1) <= 2 @@ -59,8 +74,11 @@ bounds 0 <= flow(thermal_storage_bus_heat_0) <= 2 0 <= flow(thermal_storage_bus_heat_1) <= 2 0 <= flow(thermal_storage_bus_heat_2) <= 2 - 91.06370721026974 <= GenericStorageBlock_storage_content(thermal_storage_0) <= 2.3349668515453779 - 91.06370721026974 <= GenericStorageBlock_storage_content(thermal_storage_1) <= 2.3349668515453779 - 91.06370721026974 <= GenericStorageBlock_storage_content(thermal_storage_2) <= 2.3349668515453779 - 91.06370721026974 <= GenericStorageBlock_storage_content(thermal_storage_3) <= 2.3349668515453779 + 91.06370721026974 <= GenericStorageBlock_storage_content(thermal_storage_0) <= 2.334966851545378 + 91.06370721026974 <= GenericStorageBlock_storage_content(thermal_storage_1) <= 2.334966851545378 + 91.06370721026974 <= GenericStorageBlock_storage_content(thermal_storage_2) <= 2.334966851545378 + 91.06370721026974 <= GenericStorageBlock_storage_content(thermal_storage_3) <= 2.334966851545378 + -inf <= GenericStorageBlock_storage_losses(thermal_storage_0) <= +inf + -inf <= GenericStorageBlock_storage_losses(thermal_storage_1) <= +inf + -inf <= GenericStorageBlock_storage_losses(thermal_storage_2) <= +inf end diff --git a/tests/lp_files/stratified_thermal_storage_invest_option_1.lp b/tests/lp_files/stratified_thermal_storage_invest_option_1.lp index 3226de26..446699f5 100644 --- a/tests/lp_files/stratified_thermal_storage_invest_option_1.lp +++ b/tests/lp_files/stratified_thermal_storage_invest_option_1.lp @@ -1,11 +1,11 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+400 GenericInvestmentStorageBlock_invest(thermal_storage) +0.0001 flow(thermal_storage_bus_heat_0) +0.0001 flow(thermal_storage_bus_heat_1) +0.0001 flow(thermal_storage_bus_heat_2) ++400 GenericInvestmentStorageBlock_invest(thermal_storage_0) s.t. @@ -24,113 +24,125 @@ c_e_BusBlock_balance(bus_heat_2)_: +1 flow(thermal_storage_bus_heat_2) = 0 -c_u_InvestmentFlowBlock_max(bus_heat_thermal_storage_0)_: --1 InvestmentFlowBlock_invest(bus_heat_thermal_storage) -+1 flow(bus_heat_thermal_storage_0) +c_e_InvestmentFlowBlock_total_rule(thermal_storage_bus_heat_0)_: ++1 InvestmentFlowBlock_total(thermal_storage_bus_heat_0) +-1 InvestmentFlowBlock_invest(thermal_storage_bus_heat_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(bus_heat_thermal_storage_0)_: ++1 InvestmentFlowBlock_total(bus_heat_thermal_storage_0) +-1 InvestmentFlowBlock_invest(bus_heat_thermal_storage_0) += 0 + +c_u_InvestmentFlowBlock_max(thermal_storage_bus_heat_0_0)_: ++1 flow(thermal_storage_bus_heat_0) +-1 InvestmentFlowBlock_total(thermal_storage_bus_heat_0) <= 0 -c_u_InvestmentFlowBlock_max(bus_heat_thermal_storage_1)_: --1 InvestmentFlowBlock_invest(bus_heat_thermal_storage) -+1 flow(bus_heat_thermal_storage_1) +c_u_InvestmentFlowBlock_max(thermal_storage_bus_heat_0_1)_: ++1 flow(thermal_storage_bus_heat_1) +-1 InvestmentFlowBlock_total(thermal_storage_bus_heat_0) <= 0 -c_u_InvestmentFlowBlock_max(bus_heat_thermal_storage_2)_: --1 InvestmentFlowBlock_invest(bus_heat_thermal_storage) -+1 flow(bus_heat_thermal_storage_2) +c_u_InvestmentFlowBlock_max(thermal_storage_bus_heat_0_2)_: ++1 flow(thermal_storage_bus_heat_2) +-1 InvestmentFlowBlock_total(thermal_storage_bus_heat_0) <= 0 -c_u_InvestmentFlowBlock_max(thermal_storage_bus_heat_0)_: --1 InvestmentFlowBlock_invest(thermal_storage_bus_heat) -+1 flow(thermal_storage_bus_heat_0) +c_u_InvestmentFlowBlock_max(bus_heat_thermal_storage_0_0)_: ++1 flow(bus_heat_thermal_storage_0) +-1 InvestmentFlowBlock_total(bus_heat_thermal_storage_0) <= 0 -c_u_InvestmentFlowBlock_max(thermal_storage_bus_heat_1)_: --1 InvestmentFlowBlock_invest(thermal_storage_bus_heat) -+1 flow(thermal_storage_bus_heat_1) +c_u_InvestmentFlowBlock_max(bus_heat_thermal_storage_0_1)_: ++1 flow(bus_heat_thermal_storage_1) +-1 InvestmentFlowBlock_total(bus_heat_thermal_storage_0) <= 0 -c_u_InvestmentFlowBlock_max(thermal_storage_bus_heat_2)_: --1 InvestmentFlowBlock_invest(thermal_storage_bus_heat) -+1 flow(thermal_storage_bus_heat_2) +c_u_InvestmentFlowBlock_max(bus_heat_thermal_storage_0_2)_: ++1 flow(bus_heat_thermal_storage_2) +-1 InvestmentFlowBlock_total(bus_heat_thermal_storage_0) <= 0 +c_e_GenericInvestmentStorageBlock_total_storage_rule(thermal_storage_0)_: +-1 GenericInvestmentStorageBlock_invest(thermal_storage_0) ++1 GenericInvestmentStorageBlock_total(thermal_storage_0) += 0 + c_u_GenericInvestmentStorageBlock_init_content_limit(thermal_storage)_: +-1 GenericInvestmentStorageBlock_invest(thermal_storage_0) +1 GenericInvestmentStorageBlock_init_content(thermal_storage) --1 GenericInvestmentStorageBlock_invest(thermal_storage) <= 0 c_e_GenericInvestmentStorageBlock_balance_first(thermal_storage)_: -+0.99982340904089895 GenericInvestmentStorageBlock_init_content(thermal_storage) --0.00025227279871584872 GenericInvestmentStorageBlock_invest(thermal_storage) --1 GenericInvestmentStorageBlock_storage_content(thermal_storage_0) -+1 flow(bus_heat_thermal_storage_0) --1 flow(thermal_storage_bus_heat_0) -= 0.0053014376029327757 +-1 flow(bus_heat_thermal_storage_0) ++1 flow(thermal_storage_bus_heat_0) ++0.0002522727987158487 GenericInvestmentStorageBlock_invest(thermal_storage_0) +-0.999823409040899 GenericInvestmentStorageBlock_init_content(thermal_storage) ++1 GenericInvestmentStorageBlock_storage_content(thermal_storage_0) += -0.005301437602932776 -c_e_GenericInvestmentStorageBlock_balance(thermal_storage_1)_: --0.00025227279871584872 GenericInvestmentStorageBlock_invest(thermal_storage) -+0.99982340904089895 GenericInvestmentStorageBlock_storage_content(thermal_storage_0) --1 GenericInvestmentStorageBlock_storage_content(thermal_storage_1) -+1 flow(bus_heat_thermal_storage_1) --1 flow(thermal_storage_bus_heat_1) -= 0.0053014376029327757 +c_e_GenericInvestmentStorageBlock_balance(thermal_storage_0_1)_: +-1 flow(bus_heat_thermal_storage_1) ++1 flow(thermal_storage_bus_heat_1) ++0.0002522727987158487 GenericInvestmentStorageBlock_total(thermal_storage_0) +-0.999823409040899 GenericInvestmentStorageBlock_storage_content(thermal_storage_0) ++1 GenericInvestmentStorageBlock_storage_content(thermal_storage_1) += -0.005301437602932776 -c_e_GenericInvestmentStorageBlock_balance(thermal_storage_2)_: --0.00025227279871584872 GenericInvestmentStorageBlock_invest(thermal_storage) -+0.99982340904089895 GenericInvestmentStorageBlock_storage_content(thermal_storage_1) --1 GenericInvestmentStorageBlock_storage_content(thermal_storage_2) -+1 flow(bus_heat_thermal_storage_2) --1 flow(thermal_storage_bus_heat_2) -= 0.0053014376029327757 +c_e_GenericInvestmentStorageBlock_balance(thermal_storage_0_2)_: +-1 flow(bus_heat_thermal_storage_2) ++1 flow(thermal_storage_bus_heat_2) ++0.0002522727987158487 GenericInvestmentStorageBlock_total(thermal_storage_0) +-0.999823409040899 GenericInvestmentStorageBlock_storage_content(thermal_storage_1) ++1 GenericInvestmentStorageBlock_storage_content(thermal_storage_2) += -0.005301437602932776 c_e_GenericInvestmentStorageBlock_balanced_cstr(thermal_storage)_: -1 GenericInvestmentStorageBlock_init_content(thermal_storage) +1 GenericInvestmentStorageBlock_storage_content(thermal_storage_2) = 0 -c_e_GenericInvestmentStorageBlock_power_coupled(thermal_storage)_: --1 InvestmentFlowBlock_invest(bus_heat_thermal_storage) -+1 InvestmentFlowBlock_invest(thermal_storage_bus_heat) +c_e_GenericInvestmentStorageBlock_power_coupled(thermal_storage_0)_: ++1 InvestmentFlowBlock_total(thermal_storage_bus_heat_0) +-1 InvestmentFlowBlock_total(bus_heat_thermal_storage_0) = 0 -c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(thermal_storage)_: --0.16666666666666666 GenericInvestmentStorageBlock_invest(thermal_storage) -+1 InvestmentFlowBlock_invest(bus_heat_thermal_storage) +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(thermal_storage_0)_: ++1 InvestmentFlowBlock_total(bus_heat_thermal_storage_0) +-0.16666666666666666 GenericInvestmentStorageBlock_total(thermal_storage_0) = 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(thermal_storage_0)_: --0.025000000000000001 GenericInvestmentStorageBlock_invest(thermal_storage) +c_u_GenericInvestmentStorageBlock_max_storage_content(thermal_storage_0_0)_: +-0.025 GenericInvestmentStorageBlock_total(thermal_storage_0) +1 GenericInvestmentStorageBlock_storage_content(thermal_storage_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(thermal_storage_1)_: --0.025000000000000001 GenericInvestmentStorageBlock_invest(thermal_storage) +c_u_GenericInvestmentStorageBlock_max_storage_content(thermal_storage_0_1)_: +-0.025 GenericInvestmentStorageBlock_total(thermal_storage_0) +1 GenericInvestmentStorageBlock_storage_content(thermal_storage_1) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(thermal_storage_2)_: --0.025000000000000001 GenericInvestmentStorageBlock_invest(thermal_storage) +c_u_GenericInvestmentStorageBlock_max_storage_content(thermal_storage_0_2)_: +-0.025 GenericInvestmentStorageBlock_total(thermal_storage_0) +1 GenericInvestmentStorageBlock_storage_content(thermal_storage_2) <= 0 -c_u_GenericInvestmentStorageBlock_min_storage_content(thermal_storage_0)_: -+0.97499999999999998 GenericInvestmentStorageBlock_invest(thermal_storage) +c_u_GenericInvestmentStorageBlock_min_storage_content(thermal_storage_0_0)_: ++0.975 GenericInvestmentStorageBlock_total(thermal_storage_0) -1 GenericInvestmentStorageBlock_storage_content(thermal_storage_0) <= 0 -c_u_GenericInvestmentStorageBlock_min_storage_content(thermal_storage_1)_: -+0.97499999999999998 GenericInvestmentStorageBlock_invest(thermal_storage) +c_u_GenericInvestmentStorageBlock_min_storage_content(thermal_storage_0_1)_: ++0.975 GenericInvestmentStorageBlock_total(thermal_storage_0) -1 GenericInvestmentStorageBlock_storage_content(thermal_storage_1) <= 0 -c_u_GenericInvestmentStorageBlock_min_storage_content(thermal_storage_2)_: -+0.97499999999999998 GenericInvestmentStorageBlock_invest(thermal_storage) +c_u_GenericInvestmentStorageBlock_min_storage_content(thermal_storage_0_2)_: ++0.975 GenericInvestmentStorageBlock_total(thermal_storage_0) -1 GenericInvestmentStorageBlock_storage_content(thermal_storage_2) <= 0 -c_e_ONE_VAR_CONSTANT: -ONE_VAR_CONSTANT = 1.0 - bounds 0 <= flow(bus_heat_thermal_storage_0) <= +inf 0 <= flow(bus_heat_thermal_storage_1) <= +inf @@ -138,11 +150,14 @@ bounds 0 <= flow(thermal_storage_bus_heat_0) <= +inf 0 <= flow(thermal_storage_bus_heat_1) <= +inf 0 <= flow(thermal_storage_bus_heat_2) <= +inf - 0 <= InvestmentFlowBlock_invest(bus_heat_thermal_storage) <= +inf - 0 <= InvestmentFlowBlock_invest(thermal_storage_bus_heat) <= +inf + 1 <= GenericInvestmentStorageBlock_invest(thermal_storage_0) <= +inf + 0 <= InvestmentFlowBlock_total(thermal_storage_bus_heat_0) <= +inf + 0 <= InvestmentFlowBlock_total(bus_heat_thermal_storage_0) <= +inf + 0 <= InvestmentFlowBlock_invest(thermal_storage_bus_heat_0) <= +inf + 0 <= InvestmentFlowBlock_invest(bus_heat_thermal_storage_0) <= +inf + 0 <= GenericInvestmentStorageBlock_total(thermal_storage_0) <= +inf + 0 <= GenericInvestmentStorageBlock_init_content(thermal_storage) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(thermal_storage_0) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(thermal_storage_1) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(thermal_storage_2) <= +inf - 1 <= GenericInvestmentStorageBlock_invest(thermal_storage) <= +inf - 0 <= GenericInvestmentStorageBlock_init_content(thermal_storage) <= +inf -end \ No newline at end of file +end diff --git a/tests/lp_files/stratified_thermal_storage_invest_option_2.lp b/tests/lp_files/stratified_thermal_storage_invest_option_2.lp index fc20e009..f26ee90b 100644 --- a/tests/lp_files/stratified_thermal_storage_invest_option_2.lp +++ b/tests/lp_files/stratified_thermal_storage_invest_option_2.lp @@ -1,12 +1,12 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+400 GenericInvestmentStorageBlock_invest(thermal_storage) -+50 InvestmentFlowBlock_invest(bus_heat_thermal_storage) ++50 InvestmentFlowBlock_invest(bus_heat_thermal_storage_0) +0.0001 flow(thermal_storage_bus_heat_0) +0.0001 flow(thermal_storage_bus_heat_1) +0.0001 flow(thermal_storage_bus_heat_2) ++400 GenericInvestmentStorageBlock_invest(thermal_storage_0) s.t. @@ -25,120 +25,135 @@ c_e_BusBlock_balance(bus_heat_2)_: +1 flow(thermal_storage_bus_heat_2) = 0 -c_u_InvestmentFlowBlock_max(bus_heat_thermal_storage_0)_: --1 InvestmentFlowBlock_invest(bus_heat_thermal_storage) -+1 flow(bus_heat_thermal_storage_0) +c_e_InvestmentFlowBlock_total_rule(thermal_storage_bus_heat_0)_: +-1 InvestmentFlowBlock_invest(thermal_storage_bus_heat_0) ++1 InvestmentFlowBlock_total(thermal_storage_bus_heat_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(bus_heat_thermal_storage_0)_: +-1 InvestmentFlowBlock_invest(bus_heat_thermal_storage_0) ++1 InvestmentFlowBlock_total(bus_heat_thermal_storage_0) += 0 + +c_u_InvestmentFlowBlock_max(thermal_storage_bus_heat_0_0)_: ++1 flow(thermal_storage_bus_heat_0) +-1 InvestmentFlowBlock_total(thermal_storage_bus_heat_0) <= 0 -c_u_InvestmentFlowBlock_max(bus_heat_thermal_storage_1)_: --1 InvestmentFlowBlock_invest(bus_heat_thermal_storage) -+1 flow(bus_heat_thermal_storage_1) +c_u_InvestmentFlowBlock_max(thermal_storage_bus_heat_0_1)_: ++1 flow(thermal_storage_bus_heat_1) +-1 InvestmentFlowBlock_total(thermal_storage_bus_heat_0) <= 0 -c_u_InvestmentFlowBlock_max(bus_heat_thermal_storage_2)_: --1 InvestmentFlowBlock_invest(bus_heat_thermal_storage) -+1 flow(bus_heat_thermal_storage_2) +c_u_InvestmentFlowBlock_max(thermal_storage_bus_heat_0_2)_: ++1 flow(thermal_storage_bus_heat_2) +-1 InvestmentFlowBlock_total(thermal_storage_bus_heat_0) <= 0 -c_u_InvestmentFlowBlock_max(thermal_storage_bus_heat_0)_: --1 InvestmentFlowBlock_invest(thermal_storage_bus_heat) -+1 flow(thermal_storage_bus_heat_0) +c_u_InvestmentFlowBlock_max(bus_heat_thermal_storage_0_0)_: ++1 flow(bus_heat_thermal_storage_0) +-1 InvestmentFlowBlock_total(bus_heat_thermal_storage_0) <= 0 -c_u_InvestmentFlowBlock_max(thermal_storage_bus_heat_1)_: --1 InvestmentFlowBlock_invest(thermal_storage_bus_heat) -+1 flow(thermal_storage_bus_heat_1) +c_u_InvestmentFlowBlock_max(bus_heat_thermal_storage_0_1)_: ++1 flow(bus_heat_thermal_storage_1) +-1 InvestmentFlowBlock_total(bus_heat_thermal_storage_0) <= 0 -c_u_InvestmentFlowBlock_max(thermal_storage_bus_heat_2)_: --1 InvestmentFlowBlock_invest(thermal_storage_bus_heat) -+1 flow(thermal_storage_bus_heat_2) +c_u_InvestmentFlowBlock_max(bus_heat_thermal_storage_0_2)_: ++1 flow(bus_heat_thermal_storage_2) +-1 InvestmentFlowBlock_total(bus_heat_thermal_storage_0) <= 0 +c_e_GenericInvestmentStorageBlock_total_storage_rule(thermal_storage_0)_: +-1 GenericInvestmentStorageBlock_invest(thermal_storage_0) ++1 GenericInvestmentStorageBlock_total(thermal_storage_0) += 0 + c_u_GenericInvestmentStorageBlock_init_content_limit(thermal_storage)_: +-1 GenericInvestmentStorageBlock_invest(thermal_storage_0) +1 GenericInvestmentStorageBlock_init_content(thermal_storage) --1 GenericInvestmentStorageBlock_invest(thermal_storage) <= 0 c_e_GenericInvestmentStorageBlock_balance_first(thermal_storage)_: -+0.99982340904089895 GenericInvestmentStorageBlock_init_content(thermal_storage) --0.00025227279871584872 GenericInvestmentStorageBlock_invest(thermal_storage) --1 GenericInvestmentStorageBlock_storage_content(thermal_storage_0) -+1 flow(bus_heat_thermal_storage_0) --1 flow(thermal_storage_bus_heat_0) -= 0.0053014376029327757 +-1 flow(bus_heat_thermal_storage_0) ++1 flow(thermal_storage_bus_heat_0) ++0.0002522727987158487 GenericInvestmentStorageBlock_invest(thermal_storage_0) +-0.999823409040899 GenericInvestmentStorageBlock_init_content(thermal_storage) ++1 GenericInvestmentStorageBlock_storage_content(thermal_storage_0) += -0.005301437602932776 -c_e_GenericInvestmentStorageBlock_balance(thermal_storage_1)_: --0.00025227279871584872 GenericInvestmentStorageBlock_invest(thermal_storage) -+0.99982340904089895 GenericInvestmentStorageBlock_storage_content(thermal_storage_0) --1 GenericInvestmentStorageBlock_storage_content(thermal_storage_1) -+1 flow(bus_heat_thermal_storage_1) --1 flow(thermal_storage_bus_heat_1) -= 0.0053014376029327757 +c_e_GenericInvestmentStorageBlock_balance(thermal_storage_0_1)_: +-1 flow(bus_heat_thermal_storage_1) ++1 flow(thermal_storage_bus_heat_1) ++0.0002522727987158487 GenericInvestmentStorageBlock_total(thermal_storage_0) +-0.999823409040899 GenericInvestmentStorageBlock_storage_content(thermal_storage_0) ++1 GenericInvestmentStorageBlock_storage_content(thermal_storage_1) += -0.005301437602932776 -c_e_GenericInvestmentStorageBlock_balance(thermal_storage_2)_: --0.00025227279871584872 GenericInvestmentStorageBlock_invest(thermal_storage) -+0.99982340904089895 GenericInvestmentStorageBlock_storage_content(thermal_storage_1) --1 GenericInvestmentStorageBlock_storage_content(thermal_storage_2) -+1 flow(bus_heat_thermal_storage_2) --1 flow(thermal_storage_bus_heat_2) -= 0.0053014376029327757 +c_e_GenericInvestmentStorageBlock_balance(thermal_storage_0_2)_: +-1 flow(bus_heat_thermal_storage_2) ++1 flow(thermal_storage_bus_heat_2) ++0.0002522727987158487 GenericInvestmentStorageBlock_total(thermal_storage_0) +-0.999823409040899 GenericInvestmentStorageBlock_storage_content(thermal_storage_1) ++1 GenericInvestmentStorageBlock_storage_content(thermal_storage_2) += -0.005301437602932776 c_e_GenericInvestmentStorageBlock_balanced_cstr(thermal_storage)_: -1 GenericInvestmentStorageBlock_init_content(thermal_storage) +1 GenericInvestmentStorageBlock_storage_content(thermal_storage_2) = 0 -c_e_GenericInvestmentStorageBlock_power_coupled(thermal_storage)_: --1 InvestmentFlowBlock_invest(bus_heat_thermal_storage) -+1 InvestmentFlowBlock_invest(thermal_storage_bus_heat) +c_e_GenericInvestmentStorageBlock_power_coupled(thermal_storage_0)_: ++1 InvestmentFlowBlock_total(thermal_storage_bus_heat_0) +-1 InvestmentFlowBlock_total(bus_heat_thermal_storage_0) = 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(thermal_storage_0)_: --0.025000000000000001 GenericInvestmentStorageBlock_invest(thermal_storage) +c_u_GenericInvestmentStorageBlock_max_storage_content(thermal_storage_0_0)_: +-0.025 GenericInvestmentStorageBlock_total(thermal_storage_0) +1 GenericInvestmentStorageBlock_storage_content(thermal_storage_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(thermal_storage_1)_: --0.025000000000000001 GenericInvestmentStorageBlock_invest(thermal_storage) +c_u_GenericInvestmentStorageBlock_max_storage_content(thermal_storage_0_1)_: +-0.025 GenericInvestmentStorageBlock_total(thermal_storage_0) +1 GenericInvestmentStorageBlock_storage_content(thermal_storage_1) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(thermal_storage_2)_: --0.025000000000000001 GenericInvestmentStorageBlock_invest(thermal_storage) +c_u_GenericInvestmentStorageBlock_max_storage_content(thermal_storage_0_2)_: +-0.025 GenericInvestmentStorageBlock_total(thermal_storage_0) +1 GenericInvestmentStorageBlock_storage_content(thermal_storage_2) <= 0 -c_u_GenericInvestmentStorageBlock_min_storage_content(thermal_storage_0)_: -+0.97499999999999998 GenericInvestmentStorageBlock_invest(thermal_storage) +c_u_GenericInvestmentStorageBlock_min_storage_content(thermal_storage_0_0)_: ++0.975 GenericInvestmentStorageBlock_total(thermal_storage_0) -1 GenericInvestmentStorageBlock_storage_content(thermal_storage_0) <= 0 -c_u_GenericInvestmentStorageBlock_min_storage_content(thermal_storage_1)_: -+0.97499999999999998 GenericInvestmentStorageBlock_invest(thermal_storage) +c_u_GenericInvestmentStorageBlock_min_storage_content(thermal_storage_0_1)_: ++0.975 GenericInvestmentStorageBlock_total(thermal_storage_0) -1 GenericInvestmentStorageBlock_storage_content(thermal_storage_1) <= 0 -c_u_GenericInvestmentStorageBlock_min_storage_content(thermal_storage_2)_: -+0.97499999999999998 GenericInvestmentStorageBlock_invest(thermal_storage) +c_u_GenericInvestmentStorageBlock_min_storage_content(thermal_storage_0_2)_: ++0.975 GenericInvestmentStorageBlock_total(thermal_storage_0) -1 GenericInvestmentStorageBlock_storage_content(thermal_storage_2) <= 0 -c_e_ONE_VAR_CONSTANT: -ONE_VAR_CONSTANT = 1.0 - bounds + 0 <= InvestmentFlowBlock_invest(thermal_storage_bus_heat_0) <= +inf + 0 <= InvestmentFlowBlock_invest(bus_heat_thermal_storage_0) <= +inf 0 <= flow(bus_heat_thermal_storage_0) <= +inf 0 <= flow(bus_heat_thermal_storage_1) <= +inf 0 <= flow(bus_heat_thermal_storage_2) <= +inf 0 <= flow(thermal_storage_bus_heat_0) <= +inf 0 <= flow(thermal_storage_bus_heat_1) <= +inf 0 <= flow(thermal_storage_bus_heat_2) <= +inf - 0 <= InvestmentFlowBlock_invest(bus_heat_thermal_storage) <= +inf - 0 <= InvestmentFlowBlock_invest(thermal_storage_bus_heat) <= +inf + 1 <= GenericInvestmentStorageBlock_invest(thermal_storage_0) <= +inf + 0 <= InvestmentFlowBlock_total(thermal_storage_bus_heat_0) <= +inf + 0 <= InvestmentFlowBlock_total(bus_heat_thermal_storage_0) <= +inf + 0 <= GenericInvestmentStorageBlock_total(thermal_storage_0) <= +inf + 0 <= GenericInvestmentStorageBlock_init_content(thermal_storage) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(thermal_storage_0) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(thermal_storage_1) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(thermal_storage_2) <= +inf - 1 <= GenericInvestmentStorageBlock_invest(thermal_storage) <= +inf - 0 <= GenericInvestmentStorageBlock_init_content(thermal_storage) <= +inf -end \ No newline at end of file +end diff --git a/tests/test_constraints.py b/tests/test_constraints.py index ed893f82..fa7b7ba2 100644 --- a/tests/test_constraints.py +++ b/tests/test_constraints.py @@ -13,21 +13,19 @@ import logging import os import re -from difflib import unified_diff +import oemof.solph as solph import pandas as pd - -from oemof.thermal import facades -from oemof.network.network import Node from oemof.solph import helpers -import oemof.solph as solph +from pyomo.repn.tests.lp_diff import lp_diff +from oemof.thermal import facades logging.disable(logging.INFO) def chop_trailing_whitespace(lines): - return [re.sub(r'\s*$', '', line) for line in lines] + return [re.sub(r"\s*$", "", line) for line in lines] def remove(pattern, lines): @@ -38,69 +36,68 @@ def remove(pattern, lines): def normalize_to_positive_results(lines): negative_result_indices = [ - n for n, line in enumerate(lines) - if re.match("^= -", line)] + n for n, line in enumerate(lines) if re.match("^= -", line) + ] equation_start_indices = [ - [n for n in reversed(range(0, nri)) - if re.match('.*:$', lines[n])][0] + 1 - for nri in negative_result_indices] - for (start, end) in zip( - equation_start_indices, - negative_result_indices): + [n for n in reversed(range(0, nri)) if re.match(".*:$", lines[n])][0] + + 1 + for nri in negative_result_indices + ] + for start, end in zip(equation_start_indices, negative_result_indices): for n in range(start, end): lines[n] = ( - '-' - if lines[n] and lines[n][0] == '+' - else '+' - if lines[n] - else lines[n]) + lines[n][1:] - lines[end] = '= ' + lines[end][3:] + "-" + if lines[n] and lines[n][0] == "+" + else "+" if lines[n] else lines[n] + ) + lines[n][1:] + lines[end] = "= " + lines[end][3:] return lines -def compare_lp_files(lp_file_1, lp_file_2, ignored=None): - lines_1 = remove(ignored, chop_trailing_whitespace(lp_file_1.readlines())) - lines_2 = remove(ignored, chop_trailing_whitespace(lp_file_2.readlines())) +def compare_lp_files(lp_file_1, lp_file_2): + r"""Compare lp-files to check constraints generated within solph.""" + exp = lp_file_1.read() + gen = lp_file_2.read() - lines_1 = normalize_to_positive_results(lines_1) - lines_2 = normalize_to_positive_results(lines_2) + # lp_diff returns two arrays of strings with cleaned lp syntax + # It automatically prints the diff + exp_diff, gen_diff = lp_diff(exp, gen) - if not lines_1 == lines_2: - raise AssertionError( - "Failed matching lp_file_1 with lp_file_2:\n" - + "\n".join( - unified_diff( - lines_1, - lines_2, - fromfile=os.path.relpath( - lp_file_1.name), - tofile=os.path.basename( - lp_file_2.name), - lineterm="" - ) - )) + # sometimes, 0.0 is printed, sometimes 0, harmonise that + exp_diff = [line + " ".replace(" 0.0 ", " 0 ") for line in exp_diff] + gen_diff = [line + " ".replace(" 0.0 ", " 0 ") for line in gen_diff] + + assert len(exp_diff) == len(gen_diff) + + # Created the LP files do not have a reproducible + # order of the lines. Thus, we sort the lines. + for exp, gen in zip(sorted(exp_diff), sorted(gen_diff)): + assert exp == gen, "Failed matching expected with generated lp file." class TestConstraints: @classmethod def setup_class(cls): - cls.objective_pattern = re.compile(r'^objective.*(?=s\.t\.)', - re.DOTALL | re.MULTILINE) + cls.objective_pattern = re.compile( + r"^objective.*(?=s\.t\.)", re.DOTALL | re.MULTILINE + ) - cls.date_time_index = pd.date_range('1/1/2012', periods=3, freq='H') + cls.date_time_index = pd.date_range("1/1/2012", periods=3, freq="H") - cls.tmpdir = helpers.extend_basic_path('tmp') + cls.tmpdir = helpers.extend_basic_path("tmp") logging.info(cls.tmpdir) @classmethod def setup_method(self): - self.energysystem = solph.EnergySystem(groupings=solph.GROUPINGS, - timeindex=self.date_time_index) + self.energysystem = solph.EnergySystem( + groupings=solph.GROUPINGS, timeindex=self.date_time_index + ) def get_om(self): - return solph.Model(self.energysystem, - timeindex=self.energysystem.timeindex) + return solph.Model( + self.energysystem, timeindex=self.energysystem.timeindex + ) def compare_to_reference_lp(self, ref_filename, my_om=None): if my_om is None: @@ -108,184 +105,206 @@ def compare_to_reference_lp(self, ref_filename, my_om=None): else: om = my_om - tmp_filename = ref_filename.replace('.lp', '') + '_tmp.lp' + tmp_filename = ref_filename.replace(".lp", "") + "_tmp.lp" new_filepath = os.path.join(self.tmpdir, tmp_filename) - om.write(new_filepath, io_options={'symbolic_solver_labels': True}) + om.write(new_filepath, io_options={"symbolic_solver_labels": True}) - ref_filepath = os.path.join(os.path.dirname(__file__), 'lp_files', ref_filename) + ref_filepath = os.path.join( + os.path.dirname(__file__), "lp_files", ref_filename + ) with open(new_filepath) as new_file: with open(ref_filepath) as ref_file: compare_lp_files(new_file, ref_file) def test_stratified_thermal_storage_facade(self): - """Constraint test of a StratifiedThermalStorage without investment. - """ - bus_heat = solph.Bus(label='bus_heat') + """Constraint test of a StratifiedThermalStorage without investment.""" + bus_heat = solph.Bus(label="bus_heat") self.energysystem.add(bus_heat) - self.energysystem.add(facades.StratifiedThermalStorage( - label='thermal_storage', - bus=bus_heat, - diameter=10, - height=30, - temp_h=95, - temp_c=60, - temp_env=10, - u_value=0.5, - min_storage_level=0.975, - max_storage_level=0.025, - capacity=2, - efficiency=1, - marginal_cost=0.0001 - )) - - self.compare_to_reference_lp('stratified_thermal_storage.lp') + self.energysystem.add( + facades.StratifiedThermalStorage( + label="thermal_storage", + bus=bus_heat, + diameter=10, + height=30, + temp_h=95, + temp_c=60, + temp_env=10, + u_value=0.5, + min_storage_level=0.975, + max_storage_level=0.025, + capacity=2, + efficiency=1, + marginal_cost=0.0001, + ) + ) + + self.compare_to_reference_lp("stratified_thermal_storage.lp") def test_stratified_thermal_storage_invest_option_1_facade(self): """ Constraint test of a StratifiedThermalStorage with investment. Ratio between capacity and storage_capacity is fixed. """ - bus_heat = solph.Bus(label='bus_heat') + bus_heat = solph.Bus(label="bus_heat") self.energysystem.add(bus_heat) - self.energysystem.add(facades.StratifiedThermalStorage( - label='thermal_storage', - bus=bus_heat, - diameter=10, - temp_h=95, - temp_c=60, - temp_env=10, - u_value=0.5, - expandable=True, - capacity_cost=0, - storage_capacity_cost=400, - minimum_storage_capacity=1, - invest_relation_input_capacity=1 / 6, - min_storage_level=0.975, - max_storage_level=0.025, - efficiency=1, - marginal_cost=0.0001 - )) - - self.compare_to_reference_lp('stratified_thermal_storage_invest_option_1.lp') + self.energysystem.add( + facades.StratifiedThermalStorage( + label="thermal_storage", + bus=bus_heat, + diameter=10, + temp_h=95, + temp_c=60, + temp_env=10, + u_value=0.5, + expandable=True, + capacity_cost=0, + storage_capacity_cost=400, + minimum_storage_capacity=1, + invest_relation_input_capacity=1 / 6, + min_storage_level=0.975, + max_storage_level=0.025, + efficiency=1, + marginal_cost=0.0001, + ) + ) + + self.compare_to_reference_lp( + "stratified_thermal_storage_invest_option_1.lp" + ) def test_stratified_thermal_storage_invest_option_2_facade(self): """ Constraint test of a StratifiedThermalStorage with investment. Ratio between capacity and storage_capacity is left open. """ - bus_heat = solph.Bus(label='bus_heat') + bus_heat = solph.Bus(label="bus_heat") self.energysystem.add(bus_heat) - self.energysystem.add(facades.StratifiedThermalStorage( - label='thermal_storage', - bus=bus_heat, - diameter=10, - temp_h=95, - temp_c=60, - temp_env=10, - u_value=0.5, - expandable=True, - capacity_cost=50, - storage_capacity_cost=400, - minimum_storage_capacity=1, - min_storage_level=0.975, - max_storage_level=0.025, - efficiency=1, - marginal_cost=0.0001 - )) - - self.compare_to_reference_lp('stratified_thermal_storage_invest_option_2.lp') + self.energysystem.add( + facades.StratifiedThermalStorage( + label="thermal_storage", + bus=bus_heat, + diameter=10, + temp_h=95, + temp_c=60, + temp_env=10, + u_value=0.5, + expandable=True, + capacity_cost=50, + storage_capacity_cost=400, + minimum_storage_capacity=1, + min_storage_level=0.975, + max_storage_level=0.025, + efficiency=1, + marginal_cost=0.0001, + ) + ) + + self.compare_to_reference_lp( + "stratified_thermal_storage_invest_option_2.lp" + ) def test_csp_collector_facade(self): - """Constraint test of a csp collector. - """ - bus_heat = solph.Bus(label='bus_heat') - bus_el = solph.Bus(label='bus_el') + """Constraint test of a csp collector.""" + bus_heat = solph.Bus(label="bus_heat") + bus_el = solph.Bus(label="bus_el") self.energysystem.add(bus_heat, bus_el) d = { - 'Datum': [ - '01.02.2003 09:00', '01.02.2003 10:00', '01.02.2003 11:00'], - 'E_dir_hor': [43.1, 152.7, 76.9], - 't_amb': [22.2, 23.2, 24.1]} + "Datum": [ + "01.02.2003 09:00", + "01.02.2003 10:00", + "01.02.2003 11:00", + ], + "E_dir_hor": [43.1, 152.7, 76.9], + "t_amb": [22.2, 23.2, 24.1], + } input_data = pd.DataFrame(data=d) - input_data['Datum'] = pd.to_datetime(input_data['Datum']) - input_data.set_index('Datum', inplace=True) - input_data.index = input_data.index.tz_localize(tz='Asia/Muscat') - - self.energysystem.add(facades.ParabolicTroughCollector( - label='solar_collector', - heat_bus=bus_heat, - electrical_bus=bus_el, - electrical_consumption=0.05, - additional_losses=0.2, - aperture_area=1000, - loss_method='Janotte', - irradiance_method='horizontal', - latitude=23.614328, - longitude=58.545284, - collector_tilt=10, - collector_azimuth=180, - cleanliness=0.9, - a_1=-0.00159, - a_2=0.0000977, - eta_0=0.816, - c_1=0.0622, - c_2=0.00023, - temp_collector_inlet=435, - temp_collector_outlet=500, - temp_amb=input_data['t_amb'], - irradiance=input_data['E_dir_hor'], - )) - - self.compare_to_reference_lp('csp_collector.lp') + input_data["Datum"] = pd.to_datetime(input_data["Datum"]) + input_data.set_index("Datum", inplace=True) + input_data.index = input_data.index.tz_localize(tz="Asia/Muscat") + + self.energysystem.add( + facades.ParabolicTroughCollector( + label="solar_collector", + heat_bus=bus_heat, + electrical_bus=bus_el, + electrical_consumption=0.05, + additional_losses=0.2, + aperture_area=1000, + loss_method="Janotte", + irradiance_method="horizontal", + latitude=23.614328, + longitude=58.545284, + collector_tilt=10, + collector_azimuth=180, + cleanliness=0.9, + a_1=-0.00159, + a_2=0.0000977, + eta_0=0.816, + c_1=0.0622, + c_2=0.00023, + temp_collector_inlet=435, + temp_collector_outlet=500, + temp_amb=input_data["t_amb"], + irradiance=input_data["E_dir_hor"], + ) + ) + + self.compare_to_reference_lp("csp_collector.lp") def test_solar_thermal_collector_facade(self): """ Constraint test of a solar thermal collector. """ - bus_heat = solph.Bus(label='bus_heat') - bus_el = solph.Bus(label='bus_el') + bus_heat = solph.Bus(label="bus_heat") + bus_el = solph.Bus(label="bus_el") self.energysystem.add(bus_heat, bus_el) d = { - 'Datum': [ - '01.02.2003 09:00', '01.02.2003 10:00', '01.02.2003 11:00'], - 'global_horizontal_W_m2': [47, 132, 131], - 'diffuse_horizontal_W_m2': [37.57155865, 69.72163199, 98.85021832], - 'temp_amb': [4, 6, 8]} + "Datum": [ + "01.02.2003 09:00", + "01.02.2003 10:00", + "01.02.2003 11:00", + ], + "global_horizontal_W_m2": [47, 132, 131], + "diffuse_horizontal_W_m2": [37.57155865, 69.72163199, 98.85021832], + "temp_amb": [4, 6, 8], + } input_data = pd.DataFrame(data=d) - input_data['Datum'] = pd.to_datetime(input_data['Datum']) - input_data.set_index('Datum', inplace=True) - input_data.index = input_data.index.tz_localize(tz='Europe/Berlin') - - self.energysystem.add(facades.SolarThermalCollector( - label='solar_collector', - heat_out_bus=bus_heat, - electricity_in_bus=bus_el, - electrical_consumption=0.02, - peripheral_losses=0.05, - aperture_area=1000, - latitude=52.2443, - longitude=10.5594, - collector_tilt=10, - collector_azimuth=20, - eta_0=0.73, - a_1=1.7, - a_2=0.016, - temp_collector_inlet=20, - delta_temp_n=10, - irradiance_global=input_data['global_horizontal_W_m2'], - irradiance_diffuse=input_data['diffuse_horizontal_W_m2'], - temp_amb=input_data['temp_amb'], - )) - - self.compare_to_reference_lp('solar_thermal_collector.lp') + input_data["Datum"] = pd.to_datetime(input_data["Datum"]) + input_data.set_index("Datum", inplace=True) + input_data.index = input_data.index.tz_localize(tz="Europe/Berlin") + + self.energysystem.add( + facades.SolarThermalCollector( + label="solar_collector", + heat_out_bus=bus_heat, + electricity_in_bus=bus_el, + electrical_consumption=0.02, + peripheral_losses=0.05, + aperture_area=1000, + latitude=52.2443, + longitude=10.5594, + collector_tilt=10, + collector_azimuth=20, + eta_0=0.73, + a_1=1.7, + a_2=0.016, + temp_collector_inlet=20, + delta_temp_n=10, + irradiance_global=input_data["global_horizontal_W_m2"], + irradiance_diffuse=input_data["diffuse_horizontal_W_m2"], + temp_amb=input_data["temp_amb"], + ) + ) + + self.compare_to_reference_lp("solar_thermal_collector.lp") diff --git a/tests/test_functions.py b/tests/test_functions.py index 0e316652..47a799c8 100644 --- a/tests/test_functions.py +++ b/tests/test_functions.py @@ -1,67 +1,66 @@ import os + import numpy as np import pandas as pd import pytest from pytest import approx +import oemof.thermal.absorption_heatpumps_and_chillers as ac import oemof.thermal.compression_heatpumps_and_chillers as cmpr_hp_chllr import oemof.thermal.concentrating_solar_power as csp -import oemof.thermal.absorption_heatpumps_and_chillers as ac from oemof.thermal.cogeneration import allocate_emissions -from oemof.thermal.stratified_thermal_storage import (calculate_storage_u_value, - calculate_storage_dimensions, - calculate_capacities, - calculate_losses) -from oemof.thermal.solar_thermal_collector import (flat_plate_precalc, - calc_eta_c_flate_plate) +from oemof.thermal.solar_thermal_collector import calc_eta_c_flate_plate +from oemof.thermal.solar_thermal_collector import flat_plate_precalc +from oemof.thermal.stratified_thermal_storage import calculate_capacities +from oemof.thermal.stratified_thermal_storage import calculate_losses +from oemof.thermal.stratified_thermal_storage import ( + calculate_storage_dimensions, +) +from oemof.thermal.stratified_thermal_storage import calculate_storage_u_value def test_cop_calculation_hp(): cops_HP = cmpr_hp_chllr.calc_cops( - temp_high=[40], - temp_low=[12], - quality_grade=0.4, - mode='heat_pump') + temp_high=[40], temp_low=[12], quality_grade=0.4, mode="heat_pump" + ) assert cops_HP == [4.473571428571428] def test_calc_cops_with_Series_01(): - ambient_temp_each_hour = {'01:00': 12, '02:00': 12, '03:00': 12} + ambient_temp_each_hour = {"01:00": 12, "02:00": 12, "03:00": 12} temp_l_series = pd.Series(ambient_temp_each_hour) cops_HP = cmpr_hp_chllr.calc_cops( temp_high=[40], temp_low=temp_l_series, quality_grade=0.4, - mode='heat_pump') + mode="heat_pump", + ) assert cops_HP == [4.473571428571428, 4.473571428571428, 4.473571428571428] def test_calc_cops_with_Series_02(): - set_temp_each_hour = {'01:00': 40, '02:00': 40, '03:00': 40} + set_temp_each_hour = {"01:00": 40, "02:00": 40, "03:00": 40} temp_h_series = pd.Series(set_temp_each_hour) cops_HP = cmpr_hp_chllr.calc_cops( temp_high=temp_h_series, temp_low=[12], quality_grade=0.4, - mode='heat_pump') + mode="heat_pump", + ) assert cops_HP == [4.473571428571428, 4.473571428571428, 4.473571428571428] def test_cop_calculation_hp_list_input_01(): cops_HP = cmpr_hp_chllr.calc_cops( - temp_high=[40, 40], - temp_low=[12], - quality_grade=0.4, - mode='heat_pump') + temp_high=[40, 40], temp_low=[12], quality_grade=0.4, mode="heat_pump" + ) assert cops_HP == [4.473571428571428, 4.473571428571428] def test_cop_calculation_hp_list_input_02(): cops_HP = cmpr_hp_chllr.calc_cops( - temp_high=[40], - temp_low=[12, 12], - quality_grade=0.4, - mode='heat_pump') + temp_high=[40], temp_low=[12, 12], quality_grade=0.4, mode="heat_pump" + ) assert cops_HP == [4.473571428571428, 4.473571428571428] @@ -70,9 +69,10 @@ def test_cop_calculation_airsource_hp_with_icing_01(): temp_high=[40], temp_low=[1.3], quality_grade=0.5, - mode='heat_pump', + mode="heat_pump", temp_threshold_icing=2, - factor_icing=0.8) + factor_icing=0.8, + ) assert cops_ASHP == [3.236692506459949] @@ -81,18 +81,17 @@ def test_cop_calculation_airsource_hp_with_icing_02(): temp_high=[40], temp_low=[2.3], quality_grade=0.5, - mode='heat_pump', + mode="heat_pump", temp_threshold_icing=2, - factor_icing=0.8) + factor_icing=0.8, + ) assert cops_ASHP == [4.15318302387268] def test_cop_calculation_chiller(): cops_chiller = cmpr_hp_chllr.calc_cops( - temp_high=[35], - temp_low=[17], - quality_grade=0.45, - mode='chiller') + temp_high=[35], temp_low=[17], quality_grade=0.45, mode="chiller" + ) assert cops_chiller == [7.25375] @@ -103,9 +102,10 @@ def test_raised_exception_01(): temp_high=[40], temp_low=12, # ERROR - temp_low has to be a list! quality_grade=0.4, - mode='heat_pump', + mode="heat_pump", temp_threshold_icing=2, - factor_icing=0.8) + factor_icing=0.8, + ) def test_raised_exception_02(): @@ -115,9 +115,10 @@ def test_raised_exception_02(): temp_high=40, # ERROR - temp_high has to be a list! temp_low=[12], quality_grade=0.4, - mode='heat_pump', + mode="heat_pump", temp_threshold_icing=2, - factor_icing=0.8) + factor_icing=0.8, + ) def test_raised_exception_03(): @@ -129,79 +130,84 @@ def test_raised_exception_03(): temp_low=[12, 10], # ERROR - len(temp_low) has # to be 1 or equal to len(temp_high) quality_grade=0.4, - mode='heat_pump', + mode="heat_pump", temp_threshold_icing=2, - factor_icing=0.8) + factor_icing=0.8, + ) def test_raised_exception_04(): - """Test if an exception is raised if ... """ + """Test if an exception is raised if ...""" with pytest.raises(ValueError): cmpr_hp_chllr.calc_cops( temp_high=[39], temp_low=[17], quality_grade=0.4, - mode='chiller', + mode="chiller", temp_threshold_icing=2, - factor_icing=0.8) + factor_icing=0.8, + ) def test_raised_exception_05(): - """Test if an exception is raised if ... """ + """Test if an exception is raised if ...""" with pytest.raises(ValueError): cmpr_hp_chllr.calc_cops( temp_high=[39], temp_low=[17], quality_grade=0.4, - mode='chiller', + mode="chiller", temp_threshold_icing=2, - factor_icing=0.8) + factor_icing=0.8, + ) def test_calc_max_Q_dot_chill(): - nominal_conditions = { - 'nominal_Q_chill': 20, - 'nominal_el_consumption': 5} + nominal_conditions = {"nominal_Q_chill": 20, "nominal_el_consumption": 5} actual_cop = [4.5] - max_Q_chill = cmpr_hp_chllr.calc_max_Q_dot_chill(nominal_conditions, - cops=actual_cop) + max_Q_chill = cmpr_hp_chllr.calc_max_Q_dot_chill( + nominal_conditions, cops=actual_cop + ) assert max_Q_chill == [1.125] def test_raised_exceptions_05(): with pytest.raises(TypeError): actual_cop = 4.5 # ERROR - has to be of type list! - nom_cond = {'nominal_Q_chill': 20, 'nominal_el_consumption': 5} - cmpr_hp_chllr.calc_max_Q_dot_chill(nominal_conditions=nom_cond, - cops=actual_cop) + nom_cond = {"nominal_Q_chill": 20, "nominal_el_consumption": 5} + cmpr_hp_chllr.calc_max_Q_dot_chill( + nominal_conditions=nom_cond, cops=actual_cop + ) def test_calc_max_Q_dot_heat(): - nom_cond = { - 'nominal_Q_hot': 20, - 'nominal_el_consumption': 5} + nom_cond = {"nominal_Q_hot": 20, "nominal_el_consumption": 5} actual_cop = [4.5] - max_Q_hot = cmpr_hp_chllr.calc_max_Q_dot_heat(nominal_conditions=nom_cond, - cops=actual_cop) + max_Q_hot = cmpr_hp_chllr.calc_max_Q_dot_heat( + nominal_conditions=nom_cond, cops=actual_cop + ) assert max_Q_hot == [1.125] def test_calc_chiller_quality_grade(): nom_cond = { - 'nominal_Q_chill': 20, - 'nominal_el_consumption': 5, - 't_high_nominal': 35, - 't_low_nominal': 7} - q_grade = cmpr_hp_chllr.calc_chiller_quality_grade(nominal_conditions=nom_cond) + "nominal_Q_chill": 20, + "nominal_el_consumption": 5, + "t_high_nominal": 35, + "t_low_nominal": 7, + } + q_grade = cmpr_hp_chllr.calc_chiller_quality_grade( + nominal_conditions=nom_cond + ) assert q_grade == 0.39978582902016785 def test_calculate_storage_u_value(): params = { - 's_iso': 50, # mm - 'lamb_iso': 0.05, # W/(m*K) - 'alpha_inside': 1, # W/(m2*K) - 'alpha_outside': 1 # W/(m2*K) + "s_iso": 50, # mm + "lamb_iso": 0.05, # W/(m*K) + "alpha_inside": 1, # W/(m2*K) + "alpha_outside": 1, # W/(m2*K) } u_value = calculate_storage_u_value(**params) @@ -210,8 +216,8 @@ def test_calculate_storage_u_value(): def test_calculate_storage_dimensions(): params = { - 'height': 10, # m - 'diameter': 10, # m + "height": 10, # m + "diameter": 10, # m } volume, surface = calculate_storage_dimensions(**params) @@ -220,9 +226,9 @@ def test_calculate_storage_dimensions(): def test_calculate_capacities(): params = { - 'volume': 1000, # m3 - 'temp_h': 100, # deg C - 'temp_c': 50, # deg C + "volume": 1000, # m3 + "temp_h": 100, # deg C + "temp_c": 50, # deg C } nominal_storage_capacity = calculate_capacities(**params) @@ -231,69 +237,70 @@ def test_calculate_capacities(): def test_calculate_losses(): params = { - 'u_value': 1, # W/(m2*K) - 'diameter': 10, # m - 'temp_h': 100, # deg C - 'temp_c': 50, # deg C - 'temp_env': 10, # deg C + "u_value": 1, # W/(m2*K) + "diameter": 10, # m + "temp_h": 100, # deg C + "temp_c": 50, # deg C + "temp_env": 10, # deg C } - loss_rate, fixed_losses_relative, fixed_losses_absolute = calculate_losses(**params) - assert loss_rate == 0.0003531819182021882\ - and fixed_losses_relative == 0.00028254553456175054\ + loss_rate, fixed_losses_relative, fixed_losses_absolute = calculate_losses( + **params + ) + assert ( + loss_rate == 0.0003531819182021882 + and fixed_losses_relative == 0.00028254553456175054 and fixed_losses_absolute == 0.010210176124166827 + ) def test_allocate_emissions(): emissions_dict = {} - for method in ['iea', 'efficiency', 'finnish']: + for method in ["iea", "efficiency", "finnish"]: emissions_dict[method] = allocate_emissions( total_emissions=200, eta_el=0.3, eta_th=0.5, method=method, eta_el_ref=0.525, - eta_th_ref=0.82 + eta_th_ref=0.82, ) result = { - 'iea': (75.0, 125.0), - 'efficiency': (125.0, 75.0), - 'finnish': (96.7551622418879, 103.24483775811208)} + "iea": (75.0, 125.0), + "efficiency": (125.0, 75.0), + "finnish": (96.7551622418879, 103.24483775811208), + } assert emissions_dict == result def test_allocate_emission_series(): emissions_dict = {} - for method in ['iea', 'efficiency', 'finnish']: + for method in ["iea", "efficiency", "finnish"]: emissions_dict[method] = allocate_emissions( total_emissions=pd.Series([200, 200]), eta_el=pd.Series([0.3, 0.3]), eta_th=pd.Series([0.5, 0.5]), method=method, eta_el_ref=pd.Series([0.525, 0.525]), - eta_th_ref=pd.Series([0.82, 0.82]) + eta_th_ref=pd.Series([0.82, 0.82]), ) default = { - 'iea': ( - pd.Series([75.0, 75.0]), - pd.Series([125.0, 125.0]) - ), - 'efficiency': ( - pd.Series([125.0, 125.0]), - pd.Series([75.0, 75.0]) - ), - 'finnish': ( + "iea": (pd.Series([75.0, 75.0]), pd.Series([125.0, 125.0])), + "efficiency": (pd.Series([125.0, 125.0]), pd.Series([75.0, 75.0])), + "finnish": ( pd.Series([96.7551622418879, 96.7551622418879]), - pd.Series([103.24483775811208, 103.24483775811208]) - )} + pd.Series([103.24483775811208, 103.24483775811208]), + ), + } for key in default: for em_result, em_default in zip(emissions_dict[key], default[key]): - assert em_result.equals(em_default),\ - f"Result \n{em_result} does not match default \n{em_default}" + assert em_result.equals( + em_default + ), f"Result \n{em_result} does not match default \n{em_default}" def test_calculation_of_collector_irradiance(): @@ -301,26 +308,35 @@ def test_calculation_of_collector_irradiance(): res = csp.calc_collector_irradiance(s, 0.9) result = pd.Series( [8.5381496824546242, 17.0762993649092484, 25.614449047363873], - index=[1, 2, 3]) + index=[1, 2, 3], + ) assert res.values == approx(result.values) def test_calculation_iam_for_single_value(): - res = csp.calc_iam(-0.00159, 0.0000977, 0, 0, 0, 0, 50, 'Janotte') + res = csp.calc_iam(-0.00159, 0.0000977, 0, 0, 0, 0, 50, "Janotte") assert res == 0.8352499999999999 def test_calculation_iam_andasol(): - res = csp.calc_iam(-8.65e-4, 8.87e-4, -5.425e-5, 1.665e-6, -2.309e-8, - 1.197e-10, 50, 'Andasol') + res = csp.calc_iam( + -8.65e-4, + 8.87e-4, + -5.425e-5, + 1.665e-6, + -2.309e-8, + 1.197e-10, + 50, + "Andasol", + ) assert res == 0.5460625000000001 def test_calculation_iam_for_a_series(): s = pd.Series([10, 20, 30], index=[1, 2, 3]) - res = csp.calc_iam(-0.00159, 0.0000977, 0, 0, 0, 0, s, 'Janotte') + res = csp.calc_iam(-0.00159, 0.0000977, 0, 0, 0, 0, s, "Janotte") result = pd.Series([1.00613, 0.99272, 0.95977], index=[1, 2, 3]) assert res.eq(result).all() @@ -332,19 +348,30 @@ def test_csp_different_timeindex(): E_dir_hor = pd.Series([30, 40], index=[1, 2]) t_amb = pd.Series([30, 40], index=[2, 3]) with pytest.raises(IndexError): - csp.csp_precalc(20, 60, - 10, 180, 0.9, - 0.78, 0.816, 0.0622, - 235, 300, t_amb, - -8.65e-4, 8.87e-4, - loss_method='Janotte', - E_dir_hor=E_dir_hor) + csp.csp_precalc( + 20, + 60, + 10, + 180, + 0.9, + 0.78, + 0.816, + 0.0622, + 235, + 300, + t_amb, + -8.65e-4, + 8.87e-4, + loss_method="Janotte", + E_dir_hor=E_dir_hor, + ) def test_csp_wrong_loss_method(): with pytest.raises(ValueError): - df = pd.DataFrame(data={'date': [1, 2], 'E_dir_hor': [ - 30, 40], 't_amb': [30, 40]}) + df = pd.DataFrame( + data={"date": [1, 2], "E_dir_hor": [30, 40], "t_amb": [30, 40]} + ) latitude = 23.614328 longitude = 58.545284 collector_tilt = 10 @@ -357,107 +384,133 @@ def test_csp_wrong_loss_method(): c_2 = 0.0622 temp_collector_inlet = 235 temp_collector_outlet = 300 - csp.csp_precalc(latitude, longitude, - collector_tilt, collector_azimuth, cleanliness, - eta_0, c_1, c_2, - temp_collector_inlet, temp_collector_outlet, df['t_amb'], - a_1, a_2, a_3=0, a_4=0, a_5=0, a_6=0, - loss_method='quatsch') + csp.csp_precalc( + latitude, + longitude, + collector_tilt, + collector_azimuth, + cleanliness, + eta_0, + c_1, + c_2, + temp_collector_inlet, + temp_collector_outlet, + df["t_amb"], + a_1, + a_2, + a_3=0, + a_4=0, + a_5=0, + a_6=0, + loss_method="quatsch", + ) def test_eta_janotte(): s = pd.Series([50], index=[1]) - res = csp.calc_eta_c(0.816, 0.0622, 0.00023, 0.95, 235, 300, 30, s, - 'Janotte') + res = csp.calc_eta_c( + 0.816, 0.0622, 0.00023, 0.95, 235, 300, 30, s, "Janotte" + ) result = pd.Series([0.22028124999999987], index=[1]) assert res.eq(result).all() def test_eta_andasol(): s = pd.Series([100], index=[1]) - res = csp.calc_eta_c(0.816, 64, 0.00023, 0.95, 235, 300, 30, s, - 'Andasol') + res = csp.calc_eta_c(0.816, 64, 0.00023, 0.95, 235, 300, 30, s, "Andasol") result = pd.Series([0.13519999999999988], index=[1]) assert res.eq(result).all() def test_flat_plate_precalc(): d = { - 'Datum': [ - '01.01.2003 12:00', '01.01.2003 13:00'], - 'global_horizontal_W_m2': [112, 129], - 'diffuse_horizontal_W_m2': [100.3921648, 93.95959036], - 'temp_amb': [9, 10]} + "Datum": ["01.01.2003 12:00", "01.01.2003 13:00"], + "global_horizontal_W_m2": [112, 129], + "diffuse_horizontal_W_m2": [100.3921648, 93.95959036], + "temp_amb": [9, 10], + } input_data = pd.DataFrame(data=d) - input_data['Datum'] = pd.to_datetime(input_data['Datum']) - input_data.set_index('Datum', inplace=True) - input_data.index = input_data.index.tz_localize(tz='Europe/Berlin') + input_data["Datum"] = pd.to_datetime(input_data["Datum"]) + input_data.set_index("Datum", inplace=True) + input_data.index = input_data.index.tz_localize(tz="Europe/Berlin") params = { - 'lat': 52.2443, - 'long': 10.5594, - 'collector_tilt': 10, - 'collector_azimuth': 20, - 'eta_0': 0.73, - 'a_1': 1.7, - 'a_2': 0.016, - 'temp_collector_inlet': 20, - 'delta_temp_n': 10, - 'irradiance_global': input_data['global_horizontal_W_m2'], - 'irradiance_diffuse': input_data['diffuse_horizontal_W_m2'], - 'temp_amb': input_data['temp_amb'] + "lat": 52.2443, + "long": 10.5594, + "collector_tilt": 10, + "collector_azimuth": 20, + "eta_0": 0.73, + "a_1": 1.7, + "a_2": 0.016, + "temp_collector_inlet": 20, + "delta_temp_n": 10, + "irradiance_global": input_data["global_horizontal_W_m2"], + "irradiance_diffuse": input_data["diffuse_horizontal_W_m2"], + "temp_amb": input_data["temp_amb"], } # Save return value from flat_plate_precalc(...) as data data = flat_plate_precalc(**params) # Data frame containing separately calculated results - results = pd.DataFrame({'eta_c': [0.32003169094533845, 0.34375125091055275], - 'collectors_heat': [33.37642124, 35.95493984]}) + results = pd.DataFrame( + { + "eta_c": [0.32003169094533845, 0.34375125091055275], + "collectors_heat": [33.37642124, 35.95493984], + } + ) - assert data['eta_c'].values == approx(results['eta_c'].values) and \ - data['collectors_heat'].values == approx(results['collectors_heat'].values) + assert data["eta_c"].values == approx(results["eta_c"].values) and data[ + "collectors_heat" + ].values == approx(results["collectors_heat"].values) def test_calc_eta_c_flate_plate(): - temp_amb = pd.DataFrame({'date': ['1970-01-01 00:00:00.000000001+01:00'], - 'temp_amb': [9]}) - temp_amb.set_index('date', inplace=True) - - collector_irradiance = pd.DataFrame({'date': ['1970-01-01 00:00:00.000000001+01:00'], - 'poa_global': 99.84226497618872}) - collector_irradiance.set_index('date', inplace=True) + temp_amb = pd.DataFrame( + {"date": ["1970-01-01 00:00:00.000000001+01:00"], "temp_amb": [9]} + ) + temp_amb.set_index("date", inplace=True) + + collector_irradiance = pd.DataFrame( + { + "date": ["1970-01-01 00:00:00.000000001+01:00"], + "poa_global": 99.84226497618872, + } + ) + collector_irradiance.set_index("date", inplace=True) params = { - 'eta_0': 0.73, - 'a_1': 1.7, - 'a_2': 0.016, - 'temp_collector_inlet': 20, - 'delta_temp_n': 10, - 'temp_amb': temp_amb['temp_amb'], - 'collector_irradiance': collector_irradiance['poa_global'] + "eta_0": 0.73, + "a_1": 1.7, + "a_2": 0.016, + "temp_collector_inlet": 20, + "delta_temp_n": 10, + "temp_amb": temp_amb["temp_amb"], + "collector_irradiance": collector_irradiance["poa_global"], } data = calc_eta_c_flate_plate(**params) - assert data.values == approx(0.30176452266786186) # Adjust this value + assert data.values == approx(0.30176452266786186) # Adjust this value def test_calc_characteristic_temp_Kuehn(): """Test characteristic temperature calculation for chiller 'Kuehn'.""" filename_charpara = os.path.join( os.path.dirname(__file__), - '../examples/absorption_heatpump_and_chiller/' - 'data/characteristic_parameters.csv') + "../examples/absorption_heatpump_and_chiller/" + "data/characteristic_parameters.csv", + ) charpara = pd.read_csv(filename_charpara) - chiller_name = 'Kuehn' + chiller_name = "Kuehn" ddt = ac.calc_characteristic_temp( t_hot=[85, 85], t_cool=[30], t_chill=[15], - coef_a=charpara[(charpara['name'] == chiller_name)]['a'].values[0], - coef_e=charpara[(charpara['name'] == chiller_name)]['e'].values[0], - method='kuehn_and_ziegler') + coef_a=charpara[(charpara["name"] == chiller_name)]["a"].values[0], + coef_e=charpara[(charpara["name"] == chiller_name)]["e"].values[0], + method="kuehn_and_ziegler", + ) assert ddt == [37, 37] @@ -465,19 +518,21 @@ def test_calc_characteristic_temp_Braod_01(): """Test characteristic temperature calculation for chiller 'Broad_01'.""" filename_charpara = os.path.join( os.path.dirname(__file__), - '../examples/absorption_heatpump_and_chiller/' - 'data/characteristic_parameters.csv') + "../examples/absorption_heatpump_and_chiller/" + "data/characteristic_parameters.csv", + ) charpara = pd.read_csv(filename_charpara) - chiller_name = 'Broad_01' + chiller_name = "Broad_01" n = 2 ddt = ac.calc_characteristic_temp( t_hot=[85] * n, t_cool=[30], t_chill=[15], - coef_a=charpara[(charpara['name'] == chiller_name)]['a'].values[0], - coef_e=charpara[(charpara['name'] == chiller_name)]['e'].values[0], - method='kuehn_and_ziegler') + coef_a=charpara[(charpara["name"] == chiller_name)]["a"].values[0], + coef_e=charpara[(charpara["name"] == chiller_name)]["e"].values[0], + method="kuehn_and_ziegler", + ) assert ddt == [61.45, 61.45] @@ -485,19 +540,21 @@ def test_calc_characteristic_temp_Braod_02(): """Test characteristic temperature calculation for chiller 'Broad_02'.""" filename_charpara = os.path.join( os.path.dirname(__file__), - '../examples/absorption_heatpump_and_chiller/' - 'data/characteristic_parameters.csv') + "../examples/absorption_heatpump_and_chiller/" + "data/characteristic_parameters.csv", + ) charpara = pd.read_csv(filename_charpara) - chiller_name = 'Broad_02' + chiller_name = "Broad_02" n = 2 ddt = ac.calc_characteristic_temp( t_hot=[85] * n, t_cool=[30], t_chill=[15], - coef_a=charpara[(charpara['name'] == chiller_name)]['a'].values[0], - coef_e=charpara[(charpara['name'] == chiller_name)]['e'].values[0], - method='kuehn_and_ziegler') + coef_a=charpara[(charpara["name"] == chiller_name)]["a"].values[0], + coef_e=charpara[(charpara["name"] == chiller_name)]["e"].values[0], + method="kuehn_and_ziegler", + ) assert ddt == [87.625, 87.625] @@ -505,19 +562,21 @@ def test_calc_characteristic_temp_Rotartica(): """Test characteristic temperature calculation for chiller 'Rotartica'.""" filename_charpara = os.path.join( os.path.dirname(__file__), - '../examples/absorption_heatpump_and_chiller/' - 'data/characteristic_parameters.csv') + "../examples/absorption_heatpump_and_chiller/" + "data/characteristic_parameters.csv", + ) charpara = pd.read_csv(filename_charpara) - chiller_name = 'Rotartica' + chiller_name = "Rotartica" n = 2 ddt = ac.calc_characteristic_temp( t_hot=[85] * n, t_cool=[30], t_chill=[15], - coef_a=charpara[(charpara['name'] == chiller_name)]['a'].values[0], - coef_e=charpara[(charpara['name'] == chiller_name)]['e'].values[0], - method='kuehn_and_ziegler') + coef_a=charpara[(charpara["name"] == chiller_name)]["a"].values[0], + coef_e=charpara[(charpara["name"] == chiller_name)]["e"].values[0], + method="kuehn_and_ziegler", + ) assert ddt == [approx(32.125), approx(32.125)] @@ -525,19 +584,21 @@ def test_calc_characteristic_temp_Safarik(): """Test characteristic temperature calculation for chiller 'Safarik'.""" filename_charpara = os.path.join( os.path.dirname(__file__), - '../examples/absorption_heatpump_and_chiller/' - 'data/characteristic_parameters.csv') + "../examples/absorption_heatpump_and_chiller/" + "data/characteristic_parameters.csv", + ) charpara = pd.read_csv(filename_charpara) - chiller_name = 'Safarik' + chiller_name = "Safarik" n = 2 ddt = ac.calc_characteristic_temp( t_hot=[85] * n, t_cool=[30], t_chill=[15], - coef_a=charpara[(charpara['name'] == chiller_name)]['a'].values[0], - coef_e=charpara[(charpara['name'] == chiller_name)]['e'].values[0], - method='kuehn_and_ziegler') + coef_a=charpara[(charpara["name"] == chiller_name)]["a"].values[0], + coef_e=charpara[(charpara["name"] == chiller_name)]["e"].values[0], + method="kuehn_and_ziegler", + ) assert ddt == [approx(54.64), approx(54.64)] @@ -550,27 +611,24 @@ def test_calc_characteristic_temp_input_list_single_entry(): t_chill=[15], coef_a=2.5, coef_e=1.8, - method='kuehn_and_ziegler') + method="kuehn_and_ziegler", + ) assert ddt == [37] def test_calc_heat_flux_evaporator(): """Test calculation of cooling capacity for chiller 'Broad_01'.""" Q_dots_evap = ac.calc_heat_flux( - ddts=[50], - coef_s=24.121, - coef_r=-553.194, - method='kuehn_and_ziegler') + ddts=[50], coef_s=24.121, coef_r=-553.194, method="kuehn_and_ziegler" + ) assert Q_dots_evap == [652.856] def test_calc_heat_flux_generator(): """Test calculation of driving heat for chiller 'Broad_02'.""" Q_dots_gen = ac.calc_heat_flux( - ddts=[110], - coef_s=10.807, - coef_r=-603.85, - method='kuehn_and_ziegler') + ddts=[110], coef_s=10.807, coef_r=-603.85, method="kuehn_and_ziegler" + ) assert Q_dots_gen == [584.92] @@ -583,7 +641,8 @@ def test_raised_exception_argument_type_01(): t_chill=[15], coef_a=2.5, coef_e=1.8, - method='kuehn_and_ziegler') + method="kuehn_and_ziegler", + ) def test_raised_exception_argument_type_02(): @@ -595,7 +654,8 @@ def test_raised_exception_argument_type_02(): t_chill=[15], coef_a=2.5, coef_e=1.8, - method='kuehn_and_ziegler') + method="kuehn_and_ziegler", + ) def test_raised_exception_argument_type_03(): @@ -607,7 +667,8 @@ def test_raised_exception_argument_type_03(): t_chill=15, coef_a=2.5, coef_e=1.8, - method='kuehn_and_ziegler') + method="kuehn_and_ziegler", + ) def test_raised_exception_argument_length_01(): @@ -619,7 +680,8 @@ def test_raised_exception_argument_length_01(): t_chill=[15] * 2, coef_a=2.5, coef_e=1.8, - method='kuehn_and_ziegler') + method="kuehn_and_ziegler", + ) def test_raised_exception_argument_length_02(): @@ -631,7 +693,8 @@ def test_raised_exception_argument_length_02(): t_chill=[15] * 3, coef_a=2.5, coef_e=1.8, - method='kuehn_and_ziegler') + method="kuehn_and_ziegler", + ) def test_raised_exception_method_selection_01(): @@ -643,14 +706,13 @@ def test_raised_exception_method_selection_01(): t_chill=[15], coef_a=2.5, coef_e=1.8, - method='shaken_not_stirred') + method="shaken_not_stirred", + ) def test_raised_exception_method_selection_02(): """Test if an exception is raised if unknown method name is passed.""" with pytest.raises(ValueError): ac.calc_heat_flux( - ddts=25, - coef_s=0.42, - coef_r=0.9, - method='shaken_not_stirred') + ddts=25, coef_s=0.42, coef_r=0.9, method="shaken_not_stirred" + ) diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..eb4c1743 --- /dev/null +++ b/tox.ini @@ -0,0 +1,143 @@ +[tox] +envlist = + clean, + check, + docs, + py39, + py310, + py311, + py3-nocov, + report + +[gh-actions] +python = + 3.9: py39 + 3.10: py310 + 3.11: py311 + +[testenv] +basepython = + docs: {env:TOXPYTHON:python3} + {bootstrap,clean,check,report,codecov,coveralls}: {env:TOXPYTHON:python3} +setenv = + PYTHONPATH={toxinidir}/tests + PYTHONUNBUFFERED=yes +passenv = + * +deps = + py + pytest +commands = + {posargs:pytest -vv --ignore=src} + +ignore_basepython_conflict = True + +[testenv:bootstrap] +deps = + jinja2 + matrix +skip_install = true +commands = + python ci/bootstrap.py --no-env + +[testenv:check] +deps = + docutils + check-manifest + flake8 + twine + pygments + isort +skip_install = true +commands = + python -m build . + twine check dist/oemof* + check-manifest {toxinidir} + flake8 src tests + isort --check-only --profile black --diff src tests + + +[testenv:docs] +usedevelop = true +deps = + -r{toxinidir}/docs/requirements.txt +commands = + sphinx-build {posargs:-E} -W -b html docs dist/docs + sphinx-build -b linkcheck docs dist/docs + +[testenv:coveralls] +deps = + coveralls +skip_install = true +commands = + coveralls [] + +[testenv:codecov] +deps = + codecov +skip_install = true +commands = + codecov [] + +[testenv:report] +deps = coverage +skip_install = true +commands = + coverage report + coverage html + +[testenv:clean] +commands = coverage erase +skip_install = true +deps = coverage + +[testenv:py310] +basepython = {env:TOXPYTHON:python3.10} +setenv = + {[testenv]setenv} +usedevelop = true +commands = + {posargs:pytest --cov --cov-report=term-missing -vv} +deps = + {[testenv]deps} + pytest-cov + +[testenv:py311] +basepython = {env:TOXPYTHON:python3.11} +setenv = + {[testenv]setenv} +usedevelop = true +commands = + {posargs:pytest --cov --cov-report=term-missing -vv} +deps = + {[testenv]deps} + pytest-cov + + +[testenv:py39] +basepython = {env:TOXPYTHON:python3.9} +setenv = + {[testenv]setenv} +usedevelop = true +commands = + {posargs:pytest --cov --cov-report=term-missing -vv} +deps = + {[testenv]deps} + pytest-cov + +[testenv:py38] +basepython = {env:TOXPYTHON:python3.8} +setenv = + {[testenv]setenv} +usedevelop = true +commands = + {posargs:pytest --cov --cov-report=term-missing -vv} +deps = + {[testenv]deps} + pytest-cov + +[testenv:py3-nocov] +basepython = {env:TOXPYTHON:python3} + +[flake8] +extend-ignore = E501