Welcome to oemof’s documentation!¶
Contents:
Getting started¶
Oemof stands for “Open Energy System Modelling Framework” and provides a free, open source and clearly documented toolbox to analyse energy supply systems. It is developed in Python and designed as a framework with a modular structure containing several packages which communicate through well defined interfaces.
With oemof we provide base packages for energy system modelling and optimisation.
Everybody is welcome to use and/or develop oemof. Read our Why should I contribute? section.
Contribution is already possible on a low level by simply fixing typos in oemof’s documentation or rephrasing sections which are unclear. If you want to support us that way please fork the oemof repository to your own github account and make changes as described in the github guidelines: https://guides.github.com/activities/hello-world/
Documentation¶
Full documentation can be found at readthedocs. Use the project site of readthedocs to choose the version of the documentation. Go to the download page to download different versions and formats (pdf, html, epub) of the documentation.
To get the latest news visit and follow our website.
Installing oemof¶
If you have a working Python3 environment, use pypi to install the latest oemof version. Python >= 3.5 is recommended. Lower versions may work but are not tested.
pip install oemof
For more details have a look at Installation and setup. There is also a YouTube tutorial on how to install oemof under Windows.
The packages feedinlib, demandlib and oemof.db have to be installed separately. See section Using oemof for more details about all oemof packages.
If you want to use the latest features, you might want to install the developer version. See Developing oemof for more information. The developer version is not recommended for productive use.
Structure of the oemof cosmos¶
Oemof packages are organised in different levels. The basic oemof interfaces are defined by the core libraries (network). The next level contains libraries that depend on the core libraries but do not provide interfaces to other oemof libraries (solph, outputlib). The third level are libraries that do not depend on any oemof interface and therefore can be used as stand-alone application (demandlib, feedinlib). Together with some other recommended projects (pvlib, windpowerlib) the oemof cosmos provides a wealth of tools to model energy systems. If you want to become part of it, feel free to join us.
Examples¶
The linkage of specific modules of the various packages is called an application (app) and depicts for example a concrete energy system model. You can find a large variety of helpful examples in oemof’s example repository on github to download or clone. The examples show optimisations of different energy systems and are supposed to help new users to understand the framework’s structure. There is some elaboration on the examples in the respective repository.
You are welcome to contribute your own examples via a pull request or by sending us an e-mail (see here for contact information).
Got further questions on using oemof?¶
If you have questions regarding the use of oemof you can visit the forum at: https://forum.openmod-initiative.org/tags/c/qa/oemof and open a new thread if your questions hasn’t been already answered.
Join the developers!¶
A warm welcome to all who want to join the developers and contribute to oemof. Information on the details and how to approach us can be found in the documentation .
Keep in touch¶
You can become a watcher at our github site, but this will bring you quite a few mails and might be more interesting for developers. If you just want to get the latest news you can follow our news-blog at oemof.org.
Citing oemof¶
The core ideas of oemof are described in DOI:10.1016/j.esr.2018.07.001 (preprint at arXiv:1808.0807). To allow citing specific versions of oemof, we use the zenodo project to get a DOI for each version. Select the version you want to cite.
License¶
Copyright (c) 2019 oemof developer 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 in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
About oemof¶
This overview has been developed to make oemof easy to use and develop. It describes general ideas behind and structures of oemof and its modules.
The idea of an open framework¶
The Open Energy System Modelling Framework has been developed for the modelling and analysis of energy supply systems considering power and heat as well as prospectively mobility.
Oemof has been implemented in Python and uses several Python packages for scientific applications (e.g. mathematical optimisation, network analysis, data analysis), optionally in combination with a PostgreSQL/PostGIS database. It offers a toolbox of various features needed to build energy system models in high temporal and spatial resolution. For instance, the wind energy feed-in in a model region can be modelled based on weather data, the CO2-minimal operation of biomass power plants can be calculated or the future energy supply of Europe can be simulated.
The framework consists of different libraries. For the communication between these libraries different interfaces are provided. The oemof libraries and their modules are used to build what we call an ‘application’ (app) which depicts a concrete energy system model or a subprocess of this model. Generally, applications can be developed highly individually by the use of one or more libraries depending on the scope and purpose. The following image illustrates the typical application building process.
It gets clear that applications can be build flexibly using different libraries. Furthermore, single components of applications can be substituted easily if different functionalities are needed. This allows for individual application development and provides all degrees of freedom to the developer which is particularly relevant in environments such as scientific work groups that often work spatially distributed.
Among other applications, the apps ‘renpassG!S’ and ‘reegis’ are currently developed based on the framework. ‘renpassG!S’ enables the simulation of a future European energy system with a high spatial and temporal resolution. Different expansion pathways of conventional power plants, renewable energies and net infrastructure can be considered. The app ‘reegis’ provides a simulation of a regional heat and power supply system. Another application is ‘HESYSOPT’ which has been desined to simulate combined heat and power systems with MILP on the component level. These three examples show that the modular approach of the framework allows applications with very different objectives.
Application Examples¶
Some applications are publicly available and continuously developed. Examples and a screenshot gallery can be found on oemof’s official homepage.
Why are we developing oemof?¶
Energy system models often do not have publicly accessible source code and freely available data and are poorly documented. The missing transparency slows down the scientific discussion on model quality with regard to certain problems such as grid extension or cross-border interaction between national energy systems. Besides, energy system models are often developed for a certain application and cannot (or only with great effort) be adjusted to other requirements.
The Center for Sustainable Energy Systems (ZNES) Flensburg together with the Reiner Lemoine Institute (RLI) in Berlin and the Otto-von-Guericke-University of Magdeburg (OVGU) are developing the Open Energy System Modelling Framework (oemof) to address these problems by offering a free, open and clearly documented framework for energy system modelling. This transparent approach allows a sound scientific discourse on the underlying models and data. In this way the assessment of quality and significance of undertaken analyses is improved. Moreover, the modular composition of the framework supports the adjustment to a large number of application purposes.
The open source approach allows a collaborative development of the framework that offers several advantages:
- Synergies - By developing collaboratively synergies between the participating institutes can be utilized.
- Debugging - Through the input of a larger group of users and developers bugs are identified and fixed at an earlier stage.
- Advancement - The oemof-based application profits from further development of the framework.
Why should I contribute?¶
- You do not want to start at the very beginning. - You are not the first one, who wants to set up a energy system model. So why not start with existing code?
- You want your code to be more stable. - If other people use your code, they may find bugs or will have ideas to improve it.
- Tired of ‘write-only-code’. - Developing as part of a framework encourages you to document sufficiently, so that after years you may still understand your own code.
- You want to talk to other people when you are deadlocked. - People are even more willing to help, if they are interested in what you are doing because they can use it afterwards.
- You want your code to be seen and used. We try to make oemof more and more visible to the modelling community. Together it will be easier to increase the awareness of this framework and therefore for your contribution.
We know, sometimes it is difficult to start on an existing concept. It will take some time to understand it and you will need extra time to document your own stuff. But once you understand the libraries you will get lots of interesting features, always with the option to fit them to your own needs.
If you first want to try out the collaborative process of software development you can start with a contribution on a low level. Fixing typos in the documentation or rephrasing sentences which are unclear would help us on the one hand and brings you nearer to the collaboration process on the other hand.
For any kind of contribution, please fork the oemof repository to your own github account and make changes as described in the github guidelines: https://guides.github.com/activities/hello-world/
Just contact us if you have any questions!
Join oemof with your own approach or project¶
Oemof is designed as a framework and there is a lot of space for own ideas or own libraries. No matter if you want a heuristic solver library or different linear solver libraries. You may want to add tools to analyse the results or something we never heard of. You want to add a GUI or your application to be linked to. We think, that working together in one framework will increase the probability that somebody will use and test your code (see Why should I contribute?).
Interested? Together we can talk about how to transfer your ideas into oemof or even integrate your code. Maybe we just link to your project and try to adept the API for a better fit in the future.
Also consider joining our developer meetings which take place every 6 months (usually May and December). Just contact us!
Installation and setup¶
Following you find guidelines for the installation process for different operation systems.
Linux¶
Having Python 3 installed¶
As oemof is designed as a Python package it is mandatory to have Python 3 installed. Python >= 3.5 is recommended. Lower versions may work but are not tested. It is highly recommended to use a virtual environment. See this tutorial for more help or see the sections below. If you already have a Python 3 environment you can install oemof using pip:
pip install oemof
To use pip you have to install the pypi package. Normally pypi is part of your virtual environment.
Using Linux repositories to install Python¶
Most Linux distributions will have Python 3 in their repository. Use the specific software management to install it. If you are using Ubuntu/Debian try executing the following code in your terminal:
sudo apt-get install python3
You can also download different versions of Python via https://www.python.org/downloads/.
Using Virtualenv (community driven)¶
Skip the steps you have already done. Check your architecture first (32/64 bit).
Install virtualenv using the package management of your Linux distribution, pip install or install it from source (see virtualenv documentation)
Open terminal to create and activate a virtual environment by typing:
virtualenv -p /usr/bin/python3 your_env_name source your_env_name/bin/activateIn terminal type:
pip install oemof
Install a Solver if you want to use solph and execute the solph examples (See Run the installation_test file ) to check if the installation of the solver and oemof was successful
Warning: If you have an older version of virtualenv you should update pip pip install --upgrade pip
.
Using Anaconda¶
Skip the steps you have already done. Check your architecture first (32/64 bit).
Download latest Anaconda (Linux) for Python 3.x (64 or 32 bit)
Install Anaconda
Open terminal to create and activate a virtual environment by typing:
conda create -n yourenvname python=3.x source activate yourenvnameIn terminal type:
pip install oemof
Install a Solver if you want to use solph and execute the solph examples (See Run the installation_test file ) to check if the installation of the solver and oemof was successful
Solver¶
In order to use solph you need to install a solver. There are various commercial and open-source solvers that can be used with oemof.
There are two common OpenSource solvers available (CBC, GLPK), while oemof recommends CBC (Coin-or branch and cut). But sometimes its worth comparing the results of different solvers.
To install the solvers have a look at the package repository of your Linux distribution or search for precompiled packages. GLPK and CBC ares available at Debian, Feodora, Ubuntu and others.
Check the solver installation by executing the test_installation example (see Run the installation_test file ).
Other commercial solvers like Gurobi or Cplex can be used as well. Have a look at the pyomo documentation to learn about which solvers are supported.
Windows¶
If you are new to Python check out the YouTube tutorial on how to install oemof under Windows. It will guide you step by step through the installation process, starting with the installation of Python using WinPython, all the way to executing your first oemof example.
Having Python 3 installed¶
As oemof is designed as a Phyton-module it is mandatory to have Python 3 installed. Python >= 3.5 is recommended. Lower versions may work but are not tested. If you already have a working Python 3 environment you can install oemof by using pip. Run the following code in the command window of your python environment:
pip install oemof
If pip is not part of your python environment, you have to install the pypi package.
Using WinPython (community driven)¶
Skip the steps you have already done. Check your architecture first (32/64 bit)
- Download latest WinPython for Python 3.x (64 or 32 bit)
- Install WinPython
- Open the ‘WinPython Command Prompt’ and type:
pip install oemof
- Install a Windows Solver if you want to use solph and execute the solph examples (See Run the installation_test file ) to check if the installation of the solver and oemof was successful
Using Anaconda¶
Skip the steps you have already done. Check your architecture first (32/64 bit)
Download latest Anaconda for Python 3.x (64 or 32 bit)
Install Anaconda
Open ‘Anaconda Prompt’ to create and activate a virtual environment by typing:
conda create -n yourenvname python=3.x activate yourenvnameIn ‘Anaconda Prompt’ type:
pip install oemof
Install a Windows Solver if you want to use solph and execute the solph examples (See Run the installation_test file ) to check if the installation of the solver and oemof was successful
Windows Solver¶
In order to use solph you need to install a solver. There are various commercial and open-source solvers that can be used with oemof.
You do not have to install both solvers. Oemof recommends the CBC (Coin-or branch and cut) solver. But sometimes its worth comparing the results of different solvers (e.g. GLPK).
- Downloaded CBC from here (64 or 32 bit)
- Download GLPK from here (64/32 bit)
- Unpacked CBC/GLPK to any folder (e.g. C:/Users/Somebody/my_programs)
- Add the path of the executable files of both solvers to the PATH variable using this tutorial
- Restart Windows
Check the solver installation by executing the test_installation example (see Run the installation_test file ).
Other commercial solvers like Gurobi or Cplex can be used as well. Have a look at the pyomo documentation to learn about which solvers are supported.
Mac OSX¶
Installation guidelines for Mac OS are still incomplete and not tested. As we do not have Mac users we could not test the following approaches, but they should work. If you are a Mac user please help us to improve this installation guide. Have look at the installation guide of Linux or Windows to get an idea what to do.
You can download python here: https://www.python.org/downloads/mac-osx/. For information on the installation process and on how to install python packages see here: https://docs.python.org/3/using/mac.html.
Virtualenv: http://sourabhbajaj.com/mac-setup/Python/README.html
Anaconda: https://www.continuum.io/downloads#osx
You have to install a solver if you want to use solph and execute the solph examples (See Run the installation_test file ) to check if the installation of the solver and oemof was successful.
CBC-solver: https://projects.coin-or.org/Cbc
GLPK-solver: http://arnab-deka.com/posts/2010/02/installing-glpk-on-a-mac/
Run the installation_test file¶
Test the installation and the installed solver:
To test the whether the installation was successful simply run
oemof_installation_test
in your virtual environment. If the installation was successful, you will get:
*********
Solver installed with oemof:
glpk: working
cplex: not working
cbc: working
gurobi: working
*********
oemof successfully installed.
as an output.
Using oemof¶
Oemof is a framework and even though it is in an early stage it already provides useful tools to model energy systems. To model an energy system you have to write your own application in which you combine the oemof libraries for you specific task. The example section shows how an oemof application may look like.
Current oemof libraries
oemof-network¶
The oemof-network library is part of the oemof installation. By now it can be used to define energy systems as a network with components and buses. Every component should be connected to one or more buses. After definition, a component has to explicitely be added to its energy system. Allowed components are sources, sinks and transformer.
The code of the example above:
from oemof.network import *
from oemof.energy_system import *
# create the energy system
es = EnergySystem()
# create bus 1
bus_1 = Bus(label="bus_1")
# create bus 2
bus_2 = Bus(label="bus_2")
# add bus 1 and bus 2 to energy system
es.add(bus_1, bus_2)
# create and add sink 1 to energy system
es.add(Sink(label='sink_1', inputs={bus_1: []}))
# create and add sink 2 to energy system
es.add(Sink(label='sink_2', inputs={bus_2: []}))
# create and add source to energy system
es.add(Source(label='source', outputs={bus_1: []}))
# create and add transformer to energy system
es.add(Transformer(label='transformer', inputs={bus_1: []}, outputs={bus_2: []}))
The network class is aimed to be very generic and might have some network analyse tools in the future. By now the network library is mainly used as the base for the solph library.
oemof-solph¶
The oemof-solph library is part of the oemof installation. Solph is designed to create and solve linear or mixed-integer linear optimization problems. It is based on optimization modelling language pyomo.
To use solph at least one linear solver has to be installed on your system. See the pyomo installation guide to learn which solvers are supported. Solph is tested with the open source solver cbc and the gurobi solver (free for academic use). The open glpk solver recently showed some odd behaviour.
The formulation of the energy system is based on the oemof-network library but contains additional components such as storages. Furthermore the network class are enhanced with additional parameters such as efficiencies, bounds, cost and more. See the API documentation for more details. Try the examples to learn how to build a linear energy system.
oemof-outputlib¶
The oemof-outputlib library is part of the oemof installation. It collects the results of an optimisation in a dictionary holding scalar variables and pandas DataFrame for time dependend output. This makes it easy to process or plot the results using the capabilities of the pandas library.
The following code collects the results in a pandas DataFrame and selects the data for a specific component, in this case ‘heat’.
results = outputlib.processing.results(om)
heat = outputlib.views.node(results, 'heat')
To visualize results, either use pandas own visualization functionality, matplotlib or the plot library of your choice. Some existing plot methods can be found in a separate repository oemof_visio which can be helpful when looking for a quick way to create a plot.
feedinlib¶
The feedinlib library is not part of the oemof installation and has to be installed separately using pypi. It serves as an interface between Open Data weather data and libraries to calculate feedin timeseries for fluctuating renewable energy sources.
It is currently under revision (see here for further information). To begin with it will provide an interface to the pvlib and windpowerlib and functions to download MERRA2 weather data and open_FRED weather data. See documentation of the feedinlib for a full description of the library.
demandlib¶
The demandlib library is not part of the oemof installation and has to be installed separately using pypi. At the current state the demandlib can be used to create load profiles for elctricity and heat knowing the annual demand. See the documentation of the demandlib for examples and a full description of the library.
Developing oemof¶
Oemof is developed collaboratively and therefore driven by its community. While advancing as a user of oemof, you may find situations that demand solutions that are not readily available. In this case, your solution may be of help to other users as well. Contributing to the development of oemof is good for two reasons: Your code may help others and you increase the quality of your code through the review of other developers. Read also these arguments on why you should contribute.
A first step to get involved with development can be contributing a component that is not part of the current version that you defined for your energy system. We have a module oemof.solph.custom that is dedicated to collect custom components created by users. Feel free to start a pull request and contribute.
Another way to join the developers and learn how to contribute is to help improve the documentation. If you find that some part could be more clear or if you even find a mistake, please consider fixing it and creating a pull request.
New developments that provide new functionality may enter oemof at different locations. Please feel free to discuss contributions by creating a pull request or an issue.
In the following you find important notes for developing oemof and elements within the framework. On whatever level you may want to start, we highly encourage you to contribute to the further development of oemof. If you want to collaborate see description below or contact us.
Install the developer version¶
To avoid problems make sure you have fully uninstalled previous versions of oemof. It is highly recommended to use a virtual environment. See this virtualenv tutorial for more help. Afterwards you have to clone the repository. See the github documentation to learn how to clone a repository. Now you can install the cloned repository using pip:
pip install -e /path/to/the/repository
- Newly added required packages (via PyPi) can be installed by performing a manual
- upgrade of oemof. In that case run:
pip install --upgrade -e /path/to/the/repository
Contribute to the documentation¶
If you want to contribute by improving the documentation (typos, grammar, comprehensibility), please use the developer version of the dev branch at readthedocs.org. Every fixed typo helps.
Contribute to new components¶
You can develop a new component according to your needs. Therefore you can use the module oemof.solph.custom which collects custom components created by users and lowers the entry barrier for contributing.
Your code should fit to the Issue-Management and the docstring should be complete and hold the equations used in the constraints. But there are several steps you do not necessarily need to fulfill when contributing to oemof.solph.custom: you do not need to meet the Naming Conventions. Also compatiblity to the results-API must not be guaranteed. Further you do not need to test your components or adapt the documentation. These steps are all necessary once your custom component becomes a constant part of oemof (oemof.solph.components) and are described here: Generally the following steps are required when changing, adding or removing code. But in the first step have a look at existing custom components created by other users in oemof.solph.custom and easily create your own if you need.
Collaboration with pull requests¶
To collaborate use the pull request functionality of github as described here: https://guides.github.com/activities/hello-world/
How to create a pull request¶
- Fork the oemof repository to your own github account.
- Change, add or remove code.
- Commit your changes.
- Create a pull request and describe what you will do and why. Please use the pull request template we offer. It will be shown to you when you click on “New pull request”.
- Wait for approval.
Generally the following steps are required when changing, adding or removing code¶
- Read the Issue-Management and Naming Conventions and follow them
- Add new tests according to what you have done
- Add/change the documentation (new feature, API changes …)
- Add a whatsnew entry and your name to Contributors
- Check if all Tests still work.
Tests¶
Run the following test before pushing a successful merge.
nosetests -w "/path/to/oemof" --with-doctest
Issue-Management¶
A good way for communication with the developer group are issues. If you find a bug, want to contribute an enhancement or have a question on a specific problem in development you want to discuss, please create an issue:
- describing your point accurately
- using the list of category tags
- addressing other developers
If you want to address other developers you can use @name-of-developer, or use e.g. @oemof-solph to address a team. Here you can find an overview over existing teams on different subjects and their members.
Look at the existing issues to get an idea on the usage of issues.
Style guidelines¶
We mostly follow standard guidelines instead of developing own rules. So if anything is not defined in this section, search for a PEP rule and follow it.
Docstrings¶
We decided to use the style of the numpydoc docstrings. See the following link for an example.
Code commenting¶
Code comments are block and inline comments in the source code. They can help to understand the code and should be utilized “as much as necessary, as little as possible”. When writing comments follow the PEP 0008 style guide: https://www.python.org/dev/peps/pep-0008/#comments.
PEP8 (Python Style Guide)¶
- We adhere to PEP8 for any code produced in the framework.
- We use pylint to check your code. Pylint is integrated in many IDEs and Editors. Check here or ask the maintainer of your IDE or Editor
- Some IDEs have pep8 checkers, which are very helpful, especially for python beginners.
Quoted strings¶
As there is no recommendation in the PEP rules we use double quotes for strings read by humans such as logging/error messages and single quotes for internal strings such as keys and column names. However one can deviate from this rules if the string contains a double or single quote to avoid escape characters. According to PEP 257 and numpydoc we use three double quotes for docstrings.
logging.info("We use double quotes for messages")
my_dictionary.get('key_string')
logging.warning('Use three " to quote docstrings!' # exception to avoid escape characters
Naming Conventions¶
- We use plural in the code for modules if there is possibly more than one child class (e.g. import transformers AND NOT transformer). If there are arrays in the code that contain multiple elements they have to be named in plural (e.g. transformers = [T1, T2,…]).
- Please, follow the naming conventions of pylint
- Use talking names
- Variables/Objects: Name it after the data they describe (power_line, wind_speed)
- Functions/Method: Name it after what they do: use verbs (get_wind_speed, set_parameter)
Using git¶
Branching model¶
So far we adhere mostly to the git branching model by Vincent Driessen.
Differences are:
- instead of the name
origin/develop
we call the branchorigin/dev
. - feature branches are named like
features/*
- release branches are named like
releases/*
Commit message¶
Use this nice little commit tutorial to learn how to write a nice commit message.
Documentation¶
The general implementation-independent documentation such as installation guide, flow charts, and mathematical models is done via ReStructuredText (rst). The files can be found in the folder /oemof/doc. For further information on restructured text see: http://docutils.sourceforge.net/rst.html.
How to become a member of oemof¶
And last but not least, here you will find all information about how to become a member of the oemof organisation and of developer teams.
What’s New¶
These are new features and improvements of note in each release
Releases
- v0.3.2 (November 29, 2019)
- v0.3.1 (June 11, 2019)
- v0.3.0 (June 5, 2019)
- v0.2.3 (November 21, 2018)
- v0.2.2 (July 1, 2018)
- v0.2.1 (March 19, 2018)
- v0.2.0 (January 12, 2018)
- v0.1.4 (March 28, 2017)
- v0.1.2 (March 27, 2017)
- v0.1.1 (November 2, 2016)
- v0.1.0 (November 1, 2016)
- v0.0.7 (May 4, 2016)
- v0.0.6 (April 29, 2016)
- v0.0.5 (April 1, 2016)
- v0.0.4 (March 03, 2016)
- v0.0.3 (January 29, 2016)
- v0.0.2 (December 22, 2015)
- v0.0.1 (November 25, 2015)
v0.3.2 (November 29, 2019)¶
New features¶
- Allow generic limits for integral over weighted flows. (This is a generalised version of <solph.constraints.emission_limit>.)
- Allow time-dependent weights for integrated weighted limit.
New components¶
- Custom component:
SinkDSM
. Demand Side Management component that allows to represent flexibile demand. How the component is used is shown in SinkDSM (custom).
Documentation¶
- Revision of the outputlib documentation.
Other changes¶
- The license hase been changed from GPLv3 to the MIT license
- The BaseModel has been revised (test, docstring, warnings, internal naming) (PR #605)
- Style revision to meet pep8 and other pep rules.
Contributors¶
- Guido Plessmann
- Johannes Röder
- Julian Endres
- Patrik Schönfeldt
- Uwe Krien
v0.3.1 (June 11, 2019)¶
Other changes¶
- The API of the GenericStorage changed. Due to the open structure of solph the old parameters are still accepted. Therefore users may not notice that the default value is used instead of the given value after an update from v0.2.x to v0.3.x. With this version an error is raised. We work on a structure to avoid such problems in the future.
Contributors¶
- Patrik Schönfeldt
- Stephan Günther
- Uwe Krien
v0.3.0 (June 5, 2019)¶
API changes¶
- The
param_results
function does not exist anymore. It has been renamed toparameter_as_dict
(Issue #537). - The storage API has been revised. Please check the API documentation for all details.
- The OffsetTransformer is now a regular oemof.solph component. It has been tested and the documentation has been improved. So it has been move from custom to components. Use oemof.solph.components.OffsetTransformer ( Issue #522).
New features¶
- Now it is possible to model just one time step. This is important for time step based models and all other models with an outer loop (Issue #519).
- The storage can be used unbalanced now, which means that the level at the end could be different to the level at the beginning of the modeled time period. See the storage documentation for more details.
- NonConvexFlow <oemof.solph.blocks.NonConvexFlow> can now have activity_costs, maximum_startups, and maximum_shutdowns. This helps, to model e.g. terms of maintannace contracts for small CHP plants.
- Namedtuples and tuples as labels work now without problems. This makes it much easier to find objects and results in large energy systems (Issue #507).
- Groups are now fully lazy. This means that groups are only computed when they are accessed. Previously, whenever nodes where added to an energy system, groups where computed for all but the most recently added node. This node was then only grouped upon addition of another node or upon access of the groups property.
- There is now an explicit Edge <oemof.network.Edge> class. This means that an energy system now consists of Buses <oemof.network.Bus>, Components <oemof.network.Component> and Edges <oemof.network.Edge>. For implementation reasons, Edges <oemof.network.Edge> are still Nodes <oemof.network.Node>. If you know a bit of graph theory and this seems strange to you, think of these Edges <oemof.network.Edge> as classical graph theoretic edges, reified as nodes with an in- and outdegree of one.
- Energy systems <oemof.energy_system.EnergySystem> now support blinker signals. The first supported signal gets emitted, whenever a node <oemof.network.node> is added <oemof.energy_system.EnergySystem.add> to an energy system <oemof.energy_system.EnergySystem>. (blinker)
Documentation¶
- The template for docstrings with equations (docstring of block classes) has been improved.
- A lot of improvements on the documentation
Bug fixes¶
- The timeincrement attribute of the model is not set to one anymore. In earlier versions the timeincrement was set to one by default. This was a problem if a wrong time index was passed. In that case the timeincrement was set to one without a warning. Now an error is raised if no timeincrement or valid time index is found (Issue #549).
Testing¶
- Automatic test coverage control was implemented. Now a PR will not be accepted if it decreases the test coverage.
- Test coverage was increased to over 90%. A badge was added to the oemof github page that shows the actual test coverage.
- Test coverage on the
groupings
andnetwork
modules has significantly increased. These modules where historically very weakly tested and are now approaching 90% and 95% respectively with more tests being planned.
Contributors¶
(alphabetical order)
- ajimenezUCLA
- FranziPl
- Johannes Röder
- Jakob Wolf
- Jann Launer
- Lluis Millet
- Patrik Schönfeldt
- Simon Hilpert
- Stephan Günther
- Uwe Krien
v0.2.3 (November 21, 2018)¶
Bug fixes¶
- Some functions did not work with tuples as labels. It has been fixed for the ExtractionTurbineCHP, the graph module and the parameter_as_dict function. (Issue #507)
Contributors¶
- Simon Hilpert
- Stephan Günther
- Uwe Krien
v0.2.2 (July 1, 2018)¶
API changes¶
- The storage API has been revised, even though it is still possible to use the old API. In that case a warning is raised (Issue #491).
- The newly introduced parm_results are not results and therefore renamed to parameter_as_dict. The old name is still valid but raises a warning.
New features¶
- We added a new attribute existing to the solph.options.Investement class. It will now be possible to run investment optimization based on already installed capacity of a component. Take a look on Using the investment mode for usage of this option. (Issue #489).
- Investement variables for the capacity and the flows are now decoupled to enable more flexibility. It is possible to couple the flows to the capacity, the flows to itself or to not couple anything. The newly added attributes invest_relation_input_output, invest_relation_input_capacity and invest_relation_output_capacity replace the existing attributes nominal_input_capacity_ratio and nominal_input_capacity_ratio for the investment mode. In case of the dispatch mode one should use the nominal_value of the Flow classes. The attributes nominal_input_capacity_ratio and nominal_input_capacity_ratio will be removed in v0.3.0. Please adapt your application to avoid problems in the future (Issue #480).
- We now have experimental support for deserializing an energy system from a
tabular data package. Since
we have to extend the datapackage format a bit, the specification is not yet
finalized and documentation as well as tests range from sparse to
nonexistent. But anyone who wishes to help with the code is welcome to check
it out in the
datapackage
module.
New components¶
Documentation¶
- The documentation of the storage storage component has been improved.
- The documentation of the Extraction Turbine has been improved.
Known issues¶
- It is not possible to model one time step with oemof.solph. You have to model at least two timesteps (Issue #306). Please leave a comment if this bug affects you.
Bug fixes¶
- Fix file extension check to dump a graph correctly as .graphml-file
- The full constraint set of the ExtractionTurbineCHP class was only build for one object. If more than one object was present the input/output constraint was missing. This lead to wrong results.
- In the solph constraints module the emission constraint did not include the timeincrement from the model which has now be fixed.
- The parameter_as_dict (former: param_results) do work with the views functions now (Issue #494).
Testing¶
- The test coverage has been increased (>80%). oemof has experimental areas to test new functions. These functions are marked as experimental and will not be tested. Therefore the real coverage is even higher.
Other changes¶
Contributors¶
- Fabian Büllesbach
- Guido Plessmann
- Simon Hilpert
- Stephan Günther
- Uwe Krien
v0.2.1 (March 19, 2018)¶
API changes¶
- The function create_nx_graph only takes an energysystem as argument, not a solph model. As it is not a major release you can still pass a Model but you should adapt your application as soon as possible. (Issue #439)
New features¶
- It is now possible determine minimum up and downtimes for nonconvex flows. Check the oemof_examples repository for an exemplary usage.
- Startup and shutdown costs can now be defined time-dependent.
- The graph module has been revised. (Issue #439)
- You can now store a graph to disc as .graphml file to open it in yEd with labels.
- You can add weight to edges.
- Labels are attached to the nodes.
- Two functions get_node_by_name and filter_nodes have been added that allow to get specified nodes or nodes of one kind from the results dictionary. (Issue #426)
- A new function param_results() collects all parameters of nodes and flows in a dictionary similar to the results dictionary. (Issue #445)
- In outputlib.views.node(), an option for multiindex dataframe has been added.
Documentation¶
- Some small fixes and corrected typos.
Known issues¶
- It is not possible to model one time step with oemof.solph. You have to model at least two timesteps (Issue #306). Please leave a comment if this bug affects you.
Bug fixes¶
- Shutdown costs for nonconvex flows are now accounted within the objective which was not the case before due to a naming error.
- Console script oemof_test_installation has been fixed. (Issue #452)
- Adapt solph to API change in the Pyomo package.
- Deserializing a
Node
leads to an object which was no longer serializable. This is fixed now.Node
instances should be able to be dumped and restored an arbitraty amount of times.- Adding timesteps to index of constraint for component el-line fixes an issue with pyomo.
Testing¶
- New console script test_oemof has been added (experimental). (Issue #453)
Other changes¶
- Internal change: Unnecessary list extensions while creating a model are avoided, which leads to a decrease in runtime. (Issue #438)
- The negative/positive gradient attributes are dictionaries. In the constructor they moved from sequences to a new dictionaries argument. (Issue #437)
- License agreement was adapted according to the reuse project (Issue #442)
- Code of conduct was added. (Issue #440)
- Version of required packages is now limited to the most actual version (Issue #464)
Contributors¶
- Cord Kaldemeyer
- Jann Launer
- Simon Hilpert
- Stephan Günther
- Uwe Krien
v0.2.0 (January 12, 2018)¶
API changes¶
- The NodesFromCSV has been removed from the code base. An alternative excel (spreadsheet) reader is provided in the newly created excel example in the oemof_examples repository.
- LinearTransformer and LinearN1Transformer classes are now combined within one
Transformer class. The new class has n inputs and n outputs. Please note that
the definition of the conversion factors (for N1) has changed. See the new
docstring of
Transformer
class to avoid errors (Issue #351). - The
oemof.solph.network.Storage
class has been renamed and moved tooemof.solph.components.GenericStorage
. - As the example section has been moved to a new repository the oemof_example command was removed. Use oemof_installation_test to check your installation and the installed solvers.
- OperationalModel has been renamed to Model. The
es
parameter was renamed toenergysystem
parameter. Nodes
are no longer automatically added to the most recently createdenergy system
. You can still restore the old automatic registration by manually assigning anenergy system
toNode.registry
. On the other hand you can still explicitlyadd
nodes
to anenergy system
. This option has been made a bit more feature rich by the way, but see below for more on this. Also check the oemof_examples repository for more information on the usage.- The fixed_costs attribute of the
nodes
has been removed. See (Issue #407) for more information. - The classes
DataFramePlot
andResultsDataFrame
have been removed due to the redesign of the outputlib module.
New features¶
- A new oemof_examples repository with some new examples was created.
- A new outputlib module has been created to provide a convenient data structure for optimization results and enable quick analyses. All decision variables of a Node are now collected automatically which enables an easier development of custom components. See the revised oemof-outputlib documentation for more details or have a look at the oemof_examples repository for information on the usage. Keep your eyes open, some new functions will come soon that make the processing of the results easier. See the actual pull request or issues for detailed information.
- The transformer class can now be used with n inputs and n outputs (
Transformer
) - A new module with useful additional constraints were created with these constraints global emission or investment limits can be set. Furthermore it is possible to connect investment variables. Please add your own additional constraints or let us know what is needed in the future.
- A module to create a networkx graph from your energy system or your optimisation model was added. You can use networkx to plot and analyse graphs. See Graph in the documentation for more information.
- It’s now possible to modify a
node's
inputs
andoutputs
by inserting and removingnodes
to and from the correspoding dictionaries.Outputs
where already working previously, but due to an implementation quirk,inputs
did not behave as expected. This is now fixed. - One can now explicitly
add
nodes
to anenergy system
in bulk using * and ** syntax. For the latter case, thevalues
of the dictionary passed in will be added. - New components can now be added to the custom.py module. Components in this module are indicated as in a testing state. Use them with care. This lowers the entry barriers to test new components within the solph structure and find other testers.
New components¶
- The nodes
ElectricalLine
andElectricalBus
can be used for Linear Optimal Powerflow calculation based on angle formulations. These components have been added to the solph.custom module. Though it should work correctly, it is in a preliminary stage. Please check your results. Feedback is welcome! - The custom component
Link
can now be used to model a bidirectional connection within one component. Check out the example in the oemof_examples repository. - The component
GenericCHP
can be used to model different CHP types such as extraction or back-pressure turbines and motoric plants. The component uses a mixed-integer linear formulation and can be adapted to different technical layouts with a high level of detail. Check out the example in the oemof_examples repository. - The component
GenericCAES
can be used to model different concepts of compressed air energy storage. Technical concepts such as diabatic or adiabatic layouts can be modelled at a high level of detail. The component uses a mixed-integer linear formulation. - The custom component
GenericOffsetTransformer
can be used to model components with load ranges such as heat pumps and also uses a mixed-integer linear formulation.
Documentation¶
- Large parts of the documentation have been proofread and improved since the last developer meeting in Flensburg.
- The solph documentation has got an extra section with all existing components (Solph components).
- The developer documentation has been developed to lower the barriers for new developers. Furthermore, a template for pull request was created.
Known issues¶
- It is not possible to model one time step with oemof.solph. You have to model at least two timesteps (Issue #306). Please leave a comment if this bug affects you.
Bug fixes¶
- LP-file tests are now invariant against sign changes in equations, because the equations are now normalized to always have non-negative right hand sides.
Testing¶
- All known and newly created components are now tested within an independent testing environment which can be found in /tests/.
- Other testing routines have been streamlined and reviewed and example tests have been integrated in the nosetest environment.
Other changes¶
- The plot functionalities have been removed completely from the outputlib as they are less a necessary part but more an optional tool . Basic plotting examples that show how to quickly create plots from optimization results can now be found in the oemof_examples repository. You can still find the “old” standard plots within the oemof_visio repository as they are now maintained separately.
- A user forum has been created to answer use questions.
Contributors¶
- Cord Kaldemeyer
- Jens-Olaf Delfs
- Stephan Günther
- Simon Hilpert
- Uwe Krien
v0.1.4 (March 28, 2017)¶
Bug fixes¶
- fix examples (issue #298)
Documentation¶
- Adapt installation guide.
Contributors¶
- Uwe Krien
- Stephan Günther
v0.1.2 (March 27, 2017)¶
New features¶
- Revise examples - clearer naming, cleaner code, all examples work with cbc solver (issue #238, issue #247).
- Add option to choose solver when executing the examples (issue #247).
- Add new transformer class: VariableFractionTransformer (child class of LinearTransformer). This class represents transformers with a variable fraction between its output flows. In contrast to the LinearTransformer by now it is restricted to two output flows.(issue #248)
- Add new transformer class: N1Transformer (counterpart of LinearTransformer). This class allows to have multiple inputflows that are converted into one output flow e.g. heat pumps or mixing-components.
- Allow to set addtional flow attributes inside NodesFromCSV in solph inputlib
- Add economics module to calculate investment annuities (more to come in future versions)
- Add module to store input data in multiple csv files and merge by preprocessing
- Allow to slice all information around busses via a new method of the ResultsDataFrame
- Add the option to save formatted balances around busses as single csv files via a new method of the ResultsDataFrame
Documentation¶
- Improve the installation guide.
Bug fixes¶
- Allow conversion factors as a sequence in the CSV reader
Other changes¶
- Speed up constraint-building process by removing unnecessary method call
- Clean up the code according to pep8 and pylint
Contributors¶
- Cord Kaldemeyer
- Guido Plessmann
- Uwe Krien
- Simon Hilpert
- Stephan Günther
v0.1.1 (November 2, 2016)¶
Hot fix release to make examples executable.
Bug fixes¶
- Fix copy of default logging.ini (issue #235)
- Add matplotlib to requirements to make examples executable after installation (issue #236)
Contributors¶
- Guido Plessmann
- Uwe Krien
v0.1.0 (November 1, 2016)¶
The framework provides the basis for a great range of different energy system model types, ranging from LP bottom-up (power and heat) economic dispatch models with optional investment to MILP operational unit commitment models.
With v0.1.0 we refactored oemof (not backward compatible!) to bring the implementation in line with the general concept. Hence, the API of components has changed significantly and we introduced the new ‘Flow’ component. Besides an extensive grouping functionality for automatic creation of constraints based on component input data the documentation has been revised.
We provide examples to show the broad range of possible applications and the frameworks flexibility.
API changes¶
- The demandlib is no longer part of the oemof package. It has its own package now: (demandlib)
New features¶
Solph’s
EnergySystem
now automatically uses solph’sGROUPINGS
in addition to any user supplied ones. See the API documentation for more information.The
groupings
introduced in version 0.0.5 now have more features, more documentation and should generally be pretty usable:
- They moved to their own module:
oemof.groupings
and deprecated constructs ensuring compatibility with prior versions have been removed.- It’s possible to assign a node to multiple groups from one
Grouping
by returning a list of group keys fromkey
.- If you use a non callable object as the
key
parameter toGroupings
, the constructor will not make an attempt to call them, but use the object directly as a key.- There’s now a
filter
parameter, enabling a more concise way of filtering group contents than usingvalue
.
Documentation¶
- Complete revision of the documentation. We hope it is now more intuitive and easier to understand.
Testing¶
- Create a structure to use examples as system tests (issue #160)
Bug fixes¶
- Fix relative path of logger (issue #201)
- More path fixes regarding installation via pip
Other changes¶
- Travis CI will now check PR’s automatically
- Examples executable from command-line (issue #227)
Contributors¶
- Stephan Günther
- Simon Hilpert
- Uwe Krien
- Guido Pleßmann
- Cord Kaldemeyer
v0.0.7 (May 4, 2016)¶
Bug fixes¶
- Exclude non working pyomo version
v0.0.6 (April 29, 2016)¶
New features¶
- It is now possible to choose whether or not the heat load profile generated with the BDEW heat load profile method should only include space heating or space heating and warm water combined. (Issue #130)
- Add possibility to change the order of the columns of a DataFrame subset. This is useful to change the order of stacked plots. (Issue #148)
Documentation¶
Testing¶
- Fix constraint tests (Issue #137)
Bug fixes¶
- Use of wrong columns in generation of SF vector in BDEW heat load profile generation (Issue #129)
- Use of wrong temperature vector in generation of h vector in BDEW heat load profile generation.
Other changes¶
Contributors¶
- Uwe Krien
- Stephan Günther
- Simon Hilpert
- Cord Kaldemeyer
- Birgit Schachler
v0.0.5 (April 1, 2016)¶
New features¶
- There’s now a
flexible transformer
with two inputs and one output. (Issue #116) - You now have the option create special groups of entities in your energy system. The feature is not yet fully implemented, but simple use cases are usable already. (Issue #60)
Documentation¶
- The documentation of the
electrical demand
class has been cleaned up. - The API documentation now has its own section so it doesn’t clutter up the main navigation sidebar so much anymore.
Testing¶
- There’s now a dedicated module/suite testing solph constraints.
- This suite now has proper fixtures (i.e.
setup()
/teardown()
methods) making them (hopefully) independent of the order in which they are run (which, previously, they where not).
Bug fixes¶
- Searching for oemof’s configuration directory is now done in a platform independent manner. (Issue #122)
- Weeks no longer have more than seven days. (Issue #126)
Other changes¶
Contributors¶
- Uwe Krien
- Stephan Günther
- Guido Pleßmann
v0.0.4 (March 03, 2016)¶
New features¶
Documentation¶
- Update developer notes
Testing¶
- Described testing procedures in developer notes
- New constraint tests for heating buses
Bug fixes¶
- Use of pyomo fast build
- Broken result-DataFrame in outputlib
- Dumping of EnergySystem
Other changes¶
- PEP8
Contributors¶
- Cord Kaldemeyer
- Uwe Krien
- Simon Hilpert
- Stephan Günther
- Clemens Wingenbach
- Elisa Papdis
- Martin Soethe
- Guido Plessmann
v0.0.3 (January 29, 2016)¶
New features¶
- Added a class to convert the results dictionary to a multiindex DataFrame (issue #36)
- Added a basic plot library (issue #36)
- Add logging functionalities (issue #28)
- Add entities_from_csv functionality for creating of entities from csv-files
- Add a time-depended upper bound for the output of a component (issue #65)
- Add fast_build functionlity for pyomo models in solph module (issue #68)
- The package is no longer named oemof_base but is now just called oemof.
- The results dictionary stored in the energy system now contains an attribute for the objective function and for objects which have special result attributes, those are now accessible under the object keys, too. (issue #58)
Documentation¶
- Added the Readme.rst as “Getting started” to the documentation.
- Fixed installation description (issue #38)
- Improved the developer notes.
Testing¶
Bug fixes¶
- Fix contraints in solph
- Fix pep8
Other changes¶
Contributors¶
- Cord Kaldemeyer
- Uwe Krien
- Clemens Wingenbach
- Simon Hilpert
- Stephan Günther
v0.0.2 (December 22, 2015)¶
New features¶
- Adding a definition of a default oemof logger (issue #28)
- Revise the EnergySystem class according to the oemof developing meeting (issue #25)
- Add a dump and restore method to the EnergySystem class to dump/restore its attributes (issue #31)
- Functionality for minimum up- and downtime constraints (oemof.solph.linear_mixed_integer_constraints module)
- Add relax option to simulation class for calculation of linear relaxed mixed integer problems
- Instances of
EnergySystem
now keep track ofEntities
via theentities
attribute. (issue #20)- There’s now a standard way of working with the results obtained via a call to
OptimizationModel#results
. See its documentation, the documentation ofEnergySystem#optimize
and finally the discussion at issue #33 for more information.- New class
VariableEfficiencyCHP
to model combined heat and power units with variable electrical efficiency.- New methods for
VariableEfficiencyCHP
inside the solph-module:MILP-constraint
Linear-constraint
Documentation¶
Testing¶
Bug fixes¶
- now the api-docs can be read on readthedocs.org
- a storage automically calculates its maximum output/input if the capacity and the c-rate is given (issue #27)
- Fix error in accessing dual variables in oemof.solph.postprocessing
Other changes¶
Contributors¶
- Uwe Krien
- Simon Hilpert
- Cord Kaldemeyer
- Guido Pleßmann
- Stephan Günther
v0.0.1 (November 25, 2015)¶
First release by the oemof developing group.
oemof-network¶
The modeling of energy supply systems and its variety of components has a clearly structured approach within the oemof framework. Thus, energy supply systems with different levels of complexity can be based on equal basic module blocks. Those form an universal basic structure.
A node is either a bus or a component. A bus is always connected with one or several components. Likewise components are always connected with one or several buses. Based on their characteristics components are divided into several sub types.
Transformers have any number of inputs and outputs, e.g. a CHP takes from a bus of type ‘gas’ and feeds into a bus of type ‘electricity’ and a bus of type ‘heat’. With additional information like parameters and transfer functions input and output can be specified. Using the example of a gas turbine, the resource consumption (input) is related to the provided end energy (output) by means of an conversion factor. Components of type transformer can also be used to model transmission lines.
A sink has only an input but no output. With sink consumers like households can be modeled. But also for modelling excess energy you would use a sink.
A source has exactly one output but no input. Thus for example, wind energy and photovoltaic plants can be modeled.
Components and buses can be combined to an energy system. Components and buses are nodes, connected among each other through edges which are the inputs and outputs of the components. Such a model can be interpreted mathematically as bipartite graph as buses are solely connected to components and vice versa. Thereby the in- and outputs of the components are the directed edges of the graph. The components and buses themselves are the nodes of the graph.
oemof-network is part of oemofs core and contains the base classes that are used in oemof-solph. You do not need to define your energy system on the network level as all components can be found in oemof-solph, too. You may want to inherit from oemof-network components if you want to create new components.
Graph¶
In the graph module you will find a function to create a networkx graph from an energy system or solph model. The networkx package provides many features to analyse, draw and export graphs. See the networkx documentation for more details. See the API-doc of graph
for all details and an example. The graph module can be used with energy systems of solph as well.
oemof-solph¶
Solph is an oemof-package, designed to create and solve linear or mixed-integer linear optimization problems. The packages is based on pyomo. To create an energy system model the oemof-network is used and extended by components such as storages. To get started with solph, checkout the examples in the Solph Examples section.
How can I use solph?¶
To use solph you have to install oemof and at least one solver, which can be used together with pyomo. See pyomo installation guide. You can test it by executing one of the existing examples. Be aware that the examples require the CBC solver but you can change the solver name in the example files to your solver.
Once the example work you are close to your first energy model.
Set up an energy system¶
In most cases an EnergySystem object is defined when we start to build up an energy system model. The EnergySystem object will be the main container for the model.
To define an EnergySystem we need a Datetime index to define the time range and increment of our model. An easy way to this is to use the pandas time_range function. The following code example defines the year 2011 in hourly steps. See pandas date_range guide for more information.
import pandas as pd
my_index = pd.date_range('1/1/2011', periods=8760, freq='H')
This index can be used to define the EnergySystem:
import oemof.solph as solph
my_energysystem = solph.EnergySystem(timeindex=my_index)
Now you can start to add the components of the network.
Add components to the energy system¶
After defining an instance of the EnergySystem class you have to add all nodes you define in the following to your EnergySystem.
Basically, there are two types of nodes - components and buses. Every Component has to be connected with one or more buses. The connection between a component and a bus is the flow.
All solph components can be used to set up an energy system model but you should read the documentation of each component to learn about usage and restrictions. For example it is not possible to combine every component with every flow. Furthermore, you can add your own components in your application (see below) but we would be pleased to integrate them into solph if they are of general interest. To do so please use the module oemof.solph.custom as described here: http://oemof.readthedocs.io/en/latest/developing_oemof.html#contribute-to-new-components
An example of a simple energy system shows the usage of the nodes for real world representations:
The figure shows a simple energy system using the four basic network classes and the Bus class. If you remove the transmission line (transport 1 and transport 2) you get two systems but they are still one energy system in terms of solph and will be optimised at once.
There are different ways to add components to an energy system. The following line adds a bus object to the energy system defined above.
my_energysystem.add(solph.Bus())
It is also possible to assign the bus to a variable and add it afterwards. In that case it is easy to add as many objects as you like.
my_bus1 = solph.Bus()
my_bus2 = solph.Bus()
my_energysystem.add(my_bus1, my_bus2)
Therefore it is also possible to add lists or dictionaries with components but you have to dissolve them.
# add a list
my_energysystem.add(*my_list)
# add a dictionary
my_energysystem.add(*my_dictionary.values())
Bus¶
All flows into and out of a bus are balanced. Therefore an instance of the Bus class represents a grid or network without losses. To define an instance of a Bus only a unique label is necessary. If you do not set a label a random label is used but this makes it difficult to get the results later on.
To make it easier to connect the bus to a component you can optionally assign a variable for later use.
solph.Bus(label='natural_gas')
electricity_bus = solph.Bus(label='electricity')
Note
See the Bus
class for all parameters and the mathematical background.
Flow¶
The flow class has to be used to connect. An instance of the Flow class is normally used in combination with the definition of a component.
A Flow can be limited by upper and lower bounds (constant or time-dependent) or by summarised limits.
For all parameters see the API documentation of the Flow
class or the examples of the nodes below. A basic flow can be defined without any parameter.
solph.Flow()
Oemof has different types of flows but you should be aware that you cannot connect every flow type with every component.
Note
See the Flow
class for all parameters and the mathematical background.
Components¶
Components are divided in three categories. Basic components (solph.network), additional components (solph.components) and custom components (solph.custom). The custom section was created to lower the entry barrier for new components. Be aware that these components are in an experimental state. Let us know if you have used and tested these components. This is the first step to move them to the components section.
See Solph components for a list of all components.
Optimise your energy system¶
The typical optimisation of an energy system in solph is the dispatch optimisation, which means that the use of the sources is optimised to satisfy the demand at least costs. Therefore, variable cost can be defined for all components. The cost for gas should be defined in the gas source while the variable costs of the gas power plant are caused by operating material. You can deviate from this scheme but you should keep it consistent to make it understandable for others.
Costs do not have to be monetary costs but could be emissions or other variable units.
Furthermore, it is possible to optimise the capacity of different components (see Using the investment mode).
# set up a simple least cost optimisation
om = solph.Model(my_energysystem)
# solve the energy model using the CBC solver
om.solve(solver='cbc', solve_kwargs={'tee': True})
If you want to analyse the lp-file to see all equations and bounds you can write the file to you disc. In that case you should reduce the timesteps to 3. This will increase the readability of the file.
# set up a simple least cost optimisation
om = solph.Model(my_energysystem)
# write the lp file for debugging or other reasons
om.write('path/my_model.lp', io_options={'symbolic_solver_labels': True})
Analysing your results¶
If you want to analyse your results, you should first dump your EnergySystem instance, otherwise you have to run the simulation again.
my_energysystem.results = processing.results(om)
my_energysystem.dump('my_path', 'my_dump.oemof')
If you need the meta results of the solver you can do the following:
my_energysystem.results['main'] = processing.results(om)
my_energysystem.results['meta'] = processing.meta_results(om)
my_energysystem.dump('my_path', 'my_dump.oemof')
To restore the dump you can simply create an EnergySystem instance and restore your dump into it.
import oemof.solph as solph
my_energysystem = solph.EnergySystem()
my_energysystem.restore('my_path', 'my_dump.oemof')
results = my_energysystem.results
# If you use meta results do the following instead of the previous line.
results = my_energysystem.results['main']
meta = my_energysystem.results['meta']
If you call dump/restore without any parameters, the dump will be stored as ‘es_dump.oemof’ into the ‘.oemof/dumps/’ folder created in your HOME directory.
See oemof-outputlib to learn how to process, plot and analyse the results.
Solph components¶
Sink (basic)¶
A sink is normally used to define the demand within an energy model but it can also be used to detect excesses.
The example shows the electricity demand of the electricity_bus defined above. The ‘my_demand_series’ should be sequence of normalised values while the ‘nominal_value’ is the maximum demand the normalised sequence is multiplied with. The parameter ‘fixed=True’ means that the actual_value can not be changed by the solver.
solph.Sink(label='electricity_demand', inputs={electricity_bus: solph.Flow(
actual_value=my_demand_series, fixed=True, nominal_value=nominal_demand)})
In contrast to the demand sink the excess sink has normally less restrictions but is open to take the whole excess.
solph.Sink(label='electricity_excess', inputs={electricity_bus: solph.Flow()})
Note
The Sink class is only a plug and provides no additional constraints or variables.
Source (basic)¶
A source can represent a pv-system, a wind power plant, an import of natural gas or a slack variable to avoid creating an in-feasible model.
While a wind power plant will have an hourly feed-in depending on the weather conditions the natural_gas import might be restricted by maximum value (nominal_value) and an annual limit (summed_max). As we do have to pay for imported gas we should set variable costs. Comparable to the demand series an actual_value in combination with ‘fixed=True’ is used to define the normalised output of a wind power plan. The nominal_value sets the installed capacity.
solph.Source(
label='import_natural_gas',
outputs={my_energysystem.groups['natural_gas']: solph.Flow(
nominal_value=1000, summed_max=1000000, variable_costs=50)})
solph.Source(label='wind', outputs={electricity_bus: solph.Flow(
actual_value=wind_power_feedin_series, nominal_value=1000000, fixed=True)})
Note
The Source class is only a plug and provides no additional constraints or variables.
Transformer (basic)¶
An instance of the Transformer class can represent a node with multiple input and output flows such as a power plant, a transport line or any kind of a transforming process as electrolysis, a cooling device or a heat pump. The efficiency has to be constant within one time step to get a linear transformation. You can define a different efficiency for every time step (e.g. the thermal powerplant efficiency according to the ambient temperature) but this series has to be predefined and cannot be changed within the optimisation.
A condensing power plant can be defined by a transformer with one input (fuel) and one output (electricity).
b_gas = solph.Bus(label='natural_gas')
b_el = solph.Bus(label='electricity')
solph.Transformer(
label="pp_gas",
inputs={bgas: solph.Flow()},
outputs={b_el: solph.Flow(nominal_value=10e10)},
conversion_factors={electricity_bus: 0.58})
A CHP power plant would be defined in the same manner but with two outputs:
b_gas = solph.Bus(label='natural_gas')
b_el = solph.Bus(label='electricity')
b_th = solph.Bus(label='heat')
solph.Transformer(
label='pp_chp',
inputs={b_gas: Flow()},
outputs={b_el: Flow(nominal_value=30),
b_th: Flow(nominal_value=40)},
conversion_factors={b_el: 0.3, b_th: 0.4})
A CHP power plant with 70% coal and 30% natural gas can be defined with two inputs and two outputs:
b_gas = solph.Bus(label='natural_gas')
b_coal = solph.Bus(label='hard_coal')
b_el = solph.Bus(label='electricity')
b_th = solph.Bus(label='heat')
solph.Transformer(
label='pp_chp',
inputs={b_gas: Flow(), b_coal: Flow()},
outputs={b_el: Flow(nominal_value=30),
b_th: Flow(nominal_value=40)},
conversion_factors={b_el: 0.3, b_th: 0.4,
b_coal: 0.7, b_gas: 0.3})
A heat pump would be defined in the same manner. New buses are defined to make the code cleaner:
b_el = solph.Bus(label='electricity')
b_th_low = solph.Bus(label='low_temp_heat')
b_th_high = solph.Bus(label='high_temp_heat')
# The cop (coefficient of performance) of the heat pump can be defined as
# a scalar or a sequence.
cop = 3
solph.Transformer(
label='heat_pump',
inputs={b_el: Flow(), b_th_low: Flow()},
outputs={b_th_high: Flow()},
conversion_factors={b_el: 1/cop,
b_th_low: (cop-1)/cop})
If the low-temperature reservoir is nearly infinite (ambient air heat pump) the low temperature bus is not needed and, therefore, a Transformer with one input is sufficient.
Note
See the Transformer
class for all parameters and the mathematical background.
ExtractionTurbineCHP (component)¶
The ExtractionTurbineCHP
inherits from the
Transformer (basic) class. Like the name indicates,
the application example for the component is a flexible combined heat and power
(chp) plant. Of course, an instance of this class can represent also another
component with one input and two output flows and a flexible ratio between
these flows, leading to the following constraints:
where
is defined as:
where the first equation is the result of the relation between the input flow and the two output flows, the second equation stems from how the two output flows relate to each other, and the symbols used are defined as follows (with Variables (V) and Parameters (P)):
symbol attribute type explanation flow[i, n, t]
V fuel input flow flow[n, main_output, t]
V electric power flow[n, tapped_output, t]
V thermal output main_flow_loss_index[n, t]
P power loss index conversion_factor_full_condensation [n, t]
P electric efficiency without heat extraction conversion_factors[main_output][n, t]
P electric efficiency with max heat extraction conversion_factors[tapped_output][n, t]
P thermal efficiency with maximal heat extraction
These constraints are applied in addition those of a standard
Transformer
. The constraints limit the range of
the possible operation points, like the following picture shows. For a certain
flow of fuel, there is a line of operation points, whose slope is defined by
. The second constrain limits the decrease of electrical power.
For now ExtractionTurbineCHP
instances are
restricted to one input and two output flows. The class allows the definition
of a different efficiency for every time step but the corresponding series has
to be predefined as a parameter for the optimisation. In contrast to the
Transformer
, a main flow and a tapped flow is
defined. For the main flow you can define a conversion factor if the second
flow is zero (conversion_factor_single_flow).
solph.ExtractionTurbineCHP(
label='variable_chp_gas',
inputs={b_gas: solph.Flow(nominal_value=10e10)},
outputs={b_el: solph.Flow(), b_th: solph.Flow()},
conversion_factors={b_el: 0.3, b_th: 0.5},
conversion_factor_full_condensation={b_el: 0.5})
The key of the parameter ‘conversion_factor_full_condensation’ will indicate the main flow. In the example above, the flow to the Bus ‘b_el’ is the main flow and the flow to the Bus ‘b_th’ is the tapped flow. The following plot shows how the variable chp (right) schedules it’s electrical and thermal power production in contrast to a fixed chp (left). The plot is the output of an example in the oemof example repository.
Note
See the ExtractionTurbineCHP
class for all parameters and the mathematical background.
GenericCAES (custom)¶
Compressed Air Energy Storage (CAES). The following constraints describe the CAES:
Table: Symbols and attribute names of variables and parameters
Variables (V) and Parameters (P)¶ symbol attribute type explanation cmp_st[n,t]
V Status of compression cmp_p[n,t]
V Compression power cmp_p_max[n,t]
V Max. compression power cmp_q_out_sum[n,t]
V Summed heat flow in compression cmp_q_waste[n,t]
V Waste heat flow from compression exp_st[n,t]
V Status of expansion (binary) exp_p[n,t]
V Expansion power exp_p_max[n,t]
V Max. expansion power exp_q_in_sum[n,t]
V Summed heat flow in expansion exp_q_fuel_in[n,t]
V Heat (external) flow into expansion exp_q_add_in[n,t]
V Additional heat flow into expansion cav_level[n,t]
V Filling level if CAE cav_e_in[n,t]
V Exergy flow into CAS cav_e_out[n,t]
V Exergy flow from CAS tes_level[n,t]
V Filling level of Thermal Energy Storage (TES) tes_e_in[n,t]
V Heat flow into TES tes_e_out[n,t]
V Heat flow from TES cmp_p_max_b[n,t]
P Specific y-intersection cmp_q_out_b[n,t]
P Specific y-intersection exp_p_max_b[n,t]
P Specific y-intersection exp_q_in_b[n,t]
P Specific y-intersection cav_e_in_b[n,t]
P Specific y-intersection cav_e_out_b[n,t]
P Specific y-intersection cmp_p_max_m[n,t]
P Specific slope cmp_q_out_m[n,t]
P Specific slope exp_p_max_m[n,t]
P Specific slope exp_q_in_m[n,t]
P Specific slope cav_e_in_m[n,t]
P Specific slope cav_e_out_m[n,t]
P Specific slope cmp_p_min[n,t]
P Min. compression power cmp_q_tes_share[n,t]
P Ratio between waste heat flow and heat flow into TES exp_q_tes_share[n,t]
P Ratio between external heat flow into expansion and heat flows from TES and additional source m.timeincrement[n,t]
P Time interval length tes_level_max[n,t]
P Max. filling level of TES cav_level_max[n,t]
P Max. filling level of TES cav_eta_tmp[n,t]
P Temporal efficiency (loss factor to take intertemporal losses into account) flow[list(n.electrical_input.keys())[0], n, t]
P Electr. power input into compression flow[n, list(n.electrical_output.keys())[0], t]
P Electr. power output of expansion flow[list(n.fuel_input.keys())[0], n, t]
P Heat input (external) into Expansion
Note
See the GenericCAES
class for all parameters and the mathematical background.
GenericCHP (component)¶
With the GenericCHP class it is possible to model different types of CHP plants (combined cycle extraction turbines, back pressure turbines and motoric CHP), which use different ranges of operation, as shown in the figure below.
Combined cycle extraction turbines: The minimal and maximal electric power without district heating (red dots in the figure) define maximum load and minimum load of the plant. Beta defines electrical power loss through heat extraction. The minimal thermal condenser load to cooling water and the share of flue gas losses at maximal heat extraction determine the right boundary of the operation range.
solph.components.GenericCHP(
label='combined_cycle_extraction_turbine',
fuel_input={bgas: solph.Flow(
H_L_FG_share_max=[0.19 for p in range(0, periods)])},
electrical_output={bel: solph.Flow(
P_max_woDH=[200 for p in range(0, periods)],
P_min_woDH=[80 for p in range(0, periods)],
Eta_el_max_woDH=[0.53 for p in range(0, periods)],
Eta_el_min_woDH=[0.43 for p in range(0, periods)])},
heat_output={bth: solph.Flow(
Q_CW_min=[30 for p in range(0, periods)])},
Beta=[0.19 for p in range(0, periods)],
back_pressure=False)
For modeling a back pressure CHP, the attribute back_pressure has to be set to True. The ratio of power and heat production in a back pressure plant is fixed, therefore the operation range is just a line (see figure). Again, the P_min_woDH and P_max_woDH, the efficiencies at these points and the share of flue gas losses at maximal heat extraction have to be specified. In this case “without district heating” is not to be taken literally since an operation without heat production is not possible. It is advised to set Beta to zero, so the minimal and maximal electric power without district heating are the same as in the operation point (see figure). The minimal thermal condenser load to cooling water has to be zero, because there is no condenser besides the district heating unit.
solph.components.GenericCHP(
label='back_pressure_turbine',
fuel_input={bgas: solph.Flow(
H_L_FG_share_max=[0.19 for p in range(0, periods)])},
electrical_output={bel: solph.Flow(
P_max_woDH=[200 for p in range(0, periods)],
P_min_woDH=[80 for p in range(0, periods)],
Eta_el_max_woDH=[0.53 for p in range(0, periods)],
Eta_el_min_woDH=[0.43 for p in range(0, periods)])},
heat_output={bth: solph.Flow(
Q_CW_min=[0 for p in range(0, periods)])},
Beta=[0 for p in range(0, periods)],
back_pressure=True)
A motoric chp has no condenser, so Q_CW_min is zero. Electrical power does not depend on the amount of heat used so Beta is zero. The minimal and maximal electric power (without district heating) and the efficiencies at these points are needed, whereas the use of electrical power without using thermal energy is not possible. With Beta=0 there is no difference between these points and the electrical output in the operation range. As a consequence of the functionality of a motoric CHP, share of flue gas losses at maximal heat extraction but also at minimal heat extraction have to be specified.
solph.components.GenericCHP(
label='motoric_chp',
fuel_input={bgas: solph.Flow(
H_L_FG_share_max=[0.18 for p in range(0, periods)],
H_L_FG_share_min=[0.41 for p in range(0, periods)])},
electrical_output={bel: solph.Flow(
P_max_woDH=[200 for p in range(0, periods)],
P_min_woDH=[100 for p in range(0, periods)],
Eta_el_max_woDH=[0.44 for p in range(0, periods)],
Eta_el_min_woDH=[0.40 for p in range(0, periods)])},
heat_output={bth: solph.Flow(
Q_CW_min=[0 for p in range(0, periods)])},
Beta=[0 for p in range(0, periods)],
back_pressure=False)
Modeling different types of plants means telling the component to use different constraints. Constraint 1 to 9 are active in all three cases. Constraint 10 depends on the attribute back_pressure. If true, the constraint is an equality, if not it is a less or equal. Constraint 11 is only needed for modeling motoric CHP which is done by setting the attribute H_L_FG_share_min.
where
depends on the CHP being back pressure or not.
The coefficients
and
can be determined given the efficiencies maximal/minimal load:
If is given, e.g. for a motoric CHP:
The symbols used are defined as follows (with Variables (V) and Parameters (P)):
math. symbol attribute type explanation H_F[n,t]
V input of enthalpy through fuel input P[n,t]
V provided electric power P_woDH[n,t]
V electric power without district heating P_min_woDH[n,t]
P min. electric power without district heating P_max_woDH[n,t]
P max. electric power without district heating Q[n,t]
V provided heat Q_CW_min[n,t]
P minimal therm. condenser load to cooling water H_L_FG_min[n,t]
V flue gas enthalpy loss at min heat extraction H_L_FG_max[n,t]
V flue gas enthalpy loss at max heat extraction H_L_FG_share_min[n,t]
P share of flue gas loss at min heat extraction H_L_FG_share_max[n,t]
P share of flue gas loss at max heat extraction Y[n,t]
V status variable on/off n.alphas[0][n,t]
P coefficient describing efficiency n.alphas[1][n,t]
P coefficient describing efficiency Beta[n,t]
P power loss index Eta_el_min_woDH[n,t]
P el. eff. at min. fuel flow w/o distr. heating Eta_el_max_woDH[n,t]
P el. eff. at max. fuel flow w/o distr. heating
Note
See the GenericCHP
class for all parameters and the mathematical background.
GenericStorage (component)¶
In contrast to the three classes above the storage class is a pure solph class and is not inherited from the oemof-network module.
The nominal_storage_capacity
of the storage signifies the storage capacity. You can either set it to the net capacity or to the gross capacity and limit it using the min/max attribute.
To limit the input and output flows, you can define the nominal_storage_capacity
in the Flow objects.
Furthermore, an efficiency for loading, unloading and a capacity loss per time increment can be defined.
solph.GenericStorage(
label='storage',
inputs={b_el: solph.Flow(nominal_value=9, variable_costs=10)},
outputs={b_el: solph.Flow(nominal_value=25, variable_costs=10)},
loss_rate=0.001, nominal_storage_capacity=50,
inflow_conversion_factor=0.98, outflow_conversion_factor=0.8)
For initialising the state of charge before the first time step (time step zero) the parameter initial_storage_level
(default value: None
) can be set by a numeric value as fraction of the storage capacity.
Additionally the parameter balanced
(default value: True
) sets the relation of the state of charge of time step zero and the last time step.
If balanced=True
, the state of charge in the last time step is equal to initial value in time step zero.
Use balanced=False
with caution as energy might be added to or taken from the energy system due to different states of charge in time step zero and the last time step.
Generally, with these two parameters four configurations are possible, which might result in different solutions of the same optimization model:
initial_storage_level=None
,balanced=True
(default setting): The state of charge in time step zero is a result of the optimization. The state of charge of the last time step is equal to time step zero. Thus, the storage is not violating the energy conservation by adding or taking energy from the system due to different states of charge at the beginning and at the end of the optimization period.initial_storage_level=0.5
,balanced=True
: The state of charge in time step zero is fixed to 0.5 (50 % charged). The state of charge in the last time step is also constrained by 0.5 due to the coupling parameterbalanced
set toTrue
.initial_storage_level=None
,balanced=False
: Both, the state of charge in time step zero and the last time step are a result of the optimization and not coupled.initial_storage_level=0.5
,balanced=False
: The state of charge in time step zero is constrained by a given value. The state of charge of the last time step is a result of the optimization.
The following code block shows an example of the storage parametrization for the second configuration:
solph.GenericStorage(
label='storage',
inputs={b_el: solph.Flow(nominal_value=9, variable_costs=10)},
outputs={b_el: solph.Flow(nominal_value=25, variable_costs=10)},
loss_rate=0.001, nominal_storage_capacity=50,
initial_storage_level=0.5, balanced=True,
inflow_conversion_factor=0.98, outflow_conversion_factor=0.8)
For more information see the definition of the GenericStorage
class or check the example repository.
Using an investment object with the GenericStorage component¶
Based on the GenericStorage object the GenericInvestmentStorageBlock adds two main investment possibilities.
- Invest into the flow parameters e.g. a turbine or a pump
- Invest into capacity of the storage e.g. a basin or a battery cell
Investment in this context refers to the value of the variable for the ‘nominal_value’ (installed capacity) in the investment mode.
As an addition to other flow-investments, the storage class implements the possibility to couple or decouple the flows with the capacity of the storage. Three parameters are responsible for connecting the flows and the capacity of the storage:
- ‘ invest_relation_input_capacity ‘ fixes the input flow investment to the capacity investment. A ratio of ‘1’ means that the storage can be filled within one time-period.
- ‘ invest_relation_output_capacity ‘ fixes the output flow investment to the capacity investment. A ratio of ‘1’ means that the storage can be emptied within one period.
- ‘ invest_relation_input_output ‘ fixes the input flow investment to the output flow investment. For values <1, the input will be smaller and for values >1 the input flow will be larger.
You should not set all 3 parameters at the same time, since it will lead to overdetermination.
The following example pictures a Pumped Hydroelectric Energy Storage (PHES). Both flows and the storage itself (representing: pump, turbine, basin) are free in their investment. You can set the parameters to None or delete them as None is the default value.
solph.GenericStorage(
label='PHES',
inputs={b_el: solph.Flow(investment= solph.Investment(ep_costs=500))},
outputs={b_el: solph.Flow(investment= solph.Investment(ep_costs=500)},
loss_rate=0.001,
inflow_conversion_factor=0.98, outflow_conversion_factor=0.8),
investment = solph.Investment(ep_costs=40))
The following example describes a battery with flows coupled to the capacity of the storage.
solph.GenericStorage(
label='battery',
inputs={b_el: solph.Flow()},
outputs={b_el: solph.Flow()},
loss_rate=0.001,
inflow_conversion_factor=0.98,
outflow_conversion_factor=0.8,
invest_relation_input_capacity = 1/6,
invest_relation_output_capacity = 1/6,
investment = solph.Investment(ep_costs=400))
Note
See the GenericStorage
class for all parameters and the mathematical background.
OffsetTransformer (component)¶
The OffsetTransformer object makes it possible to create a Transformer with different efficiencies in part load condition. For this object it is necessary to define the inflow as a nonconvex flow and to set a minimum load. The following example illustrates how to define an OffsetTransformer for given information for the output:
eta_min = 0.5 # efficiency at minimal operation point
eta_max = 0.8 # efficiency at nominal operation point
P_out_min = 20 # absolute minimal output power
P_out_max = 100 # absolute nominal output power
# calculate limits of input power flow
P_in_min = P_out_min / eta_min
P_in_max = P_out_max / eta_max
# calculate coefficients of input-output line equation
c1 = (P_out_max-P_out_min)/(P_in_max-P_in_min)
c0 = P_out_max - c1*P_in_max
# define OffsetTransformer
solph.custom.OffsetTransformer(
label='boiler',
inputs={bfuel: solph.Flow(
nominal_value=P_in_max,
max=1,
min=P_in_min/P_in_max,
nonconvex=solph.NonConvex())},
outputs={bth: solph.Flow()},
coefficients = [c0, c1])
This example represents a boiler, which is supplied by fuel and generates heat. It is assumed that the nominal thermal power of the boiler (output power) is 100 (kW) and the efficiency at nominal power is 80 %. The boiler cannot operate under 20 % of nominal power, in this case 20 (kW) and the efficiency at that part load is 50 %. Note that the nonconvex flow has to be defined for the input flow. By using the OffsetTransformer a linear relation of in- and output power with a power dependent efficiency is generated. The following figures illustrate the relations:
Now, it becomes clear, why this object has been named OffsetTransformer. The
linear equation of in- and outflow does not hit the origin, but is offset. By multiplying
the Offset with the binary status variable of the nonconvex flow, the origin (0, 0) becomes
part of the solution space and the boiler is allowed to switch off:
Variables (V) and Parameters (P)¶ symbol attribute type explanation flow[n, o, t]
V Power of output flow[i, n, t]
V Power of input status[i, n, t]
V binary status variable of nonconvex input flow coefficients[1][n, t]
P linear coefficient 1 (slope) coefficients[0][n, t]
P linear coefficient 0 (y-intersection)
The following figures shows the efficiency dependent on the output power, which results in a nonlinear relation:
The parameters and
can be given by scalars or by series in order to define a different efficiency equation for every timestep.
Note
See the OffsetTransformer
class for all parameters and the mathematical background.
ElectricalLine (custom)¶
Electrical line.
Note
See the ElectricalLine
class for all parameters and the mathematical background.
SinkDSM (custom)¶
SinkDSM
can used to represent flexibility in a demand time series.
Elasticity of the demand is described by upper (capacity_up
) and lower (capacity_down
) bounds where within the demand is allowed to vary.
Upwards shifted demand is then balanced with downwards shifted demand.
At the moment, SinkDSM
provides two method how the Demand-Side Management (DSM) flexibility is represented in constraints
- “delay”: Implementation of the DSM modeling method proposed by Zerrahn & Schill (2015): On the representation of demand-side management in power system models,
in: Energy (84), pp. 840-845, 10.1016/j.energy.2015.03.037. Details:
SinkDSMDelayBlock
- “interval”: Is a fairly simple approach. Within a defined windows of time steps, demand can be shifted within the defined bounds of elasticity.
The window sequentially moves forwards. Details:
SinkDSMIntervalBlock
Cost can be associated to either demand up shifts or demand down shifts.
This small example of PV, grid and SinkDSM shows how to use the component
# Create some data
pv_day = [(-(1 / 6 * x ** 2) + 6) / 6 for x in range(-6, 7)]
pv_ts = [0] * 6 + pv_day + [0] * 6
data_dict = {"demand_el": [3] * len(pv_ts),
"pv": pv_ts,
"Cap_up": [0.5] * len(pv_ts),
"Cap_do": [0.5] * len(pv_ts)}
data = pd.DataFrame.from_dict(data_dict)
# Do timestamp stuff
datetimeindex = pd.date_range(start='1/1/2013', periods=len(data.index), freq='H')
data['timestamp'] = datetimeindex
data.set_index('timestamp', inplace=True)
# Create Energy System
es = solph.EnergySystem(timeindex=datetimeindex)
Node.registry = es
# Create bus representing electricity grid
b_elec = solph.Bus(label='Electricity bus')
# Create a back supply
grid = solph.Source(label='Grid',
outputs={
b_elec: solph.Flow(
nominal_value=10000,
variable_costs=50)}
)
# PV supply from time series
s_wind = solph.Source(label='wind',
outputs={
b_elec: solph.Flow(
actual_value=data['pv'],
fixed=True,
nominal_value=3.5)}
)
# Create DSM Sink
demand_dsm = solph.custom.SinkDSM(label='DSM',
inputs={b_elec: solph.Flow()},
capacity_up=data['Cap_up'],
capacity_down=data['Cap_do'],
delay_time=6,
demand=data['demand_el'],
method="delay",
cost_dsm_down=5)
Yielding the following results
Note
- This component is a candidate component. It’s implemented as a custom component for users that like to use and test the component at early stage. Please report issues to improve the component.
- See the
SinkDSM
class for all parameters and the mathematical background.
Using the investment mode¶
As described in Optimise your energy system the typical way to optimise an energy system is the dispatch optimisation based on marginal costs. Solph also provides a combined dispatch and investment optimisation. Based on investment costs you can compare the usage of existing components against building up new capacity. The annual savings by building up new capacity must therefore compensate the annuity of the investment costs (the time period does not have to be one year but depends on your Datetime index).
See the API of the Investment
class to see all possible parameters.
Basically an instance of the investment class can be added to a Flow or a Storage. All parameters that usually refer to the nominal_value/capacity will now refer to the investment variables and existing capacity. It is also possible to set a maximum limit for the capacity that can be build. If existing capacity is considered for a component with investment mode enabled, the ep_costs still apply only to the newly built capacity.
The investment object can be used in Flows and some components. See the Solph components section for detailed information of each component.
For example if you want to find out what would be the optimal capacity of a wind power plant to decrease the costs of an existing energy system, you can define this model and add an investment source. The wind_power_time_series has to be a normalised feed-in time series of you wind power plant. The maximum value might be caused by limited space for wind turbines.
solph.Source(label='new_wind_pp', outputs={electricity: solph.Flow(
actual_value=wind_power_time_series, fixed=True,
investment=solph.Investment(ep_costs=epc, maximum=50000))})
Let’s slightly alter the case and consider for already existing wind power capacity of 20,000 kW. We’re still expecting the total wind power capacity, thus we allow for 30,000 kW of new installations and formulate as follows.
solph.Source(label='new_wind_pp', outputs={electricity: solph.Flow(
actual_value=wind_power_time_series, fixed=True,
investment=solph.Investment(ep_costs=epc,
maximum=30000,
existing=20000))})
The periodical costs (ep_costs) are typically calculated as follows:
capex = 1000 # investment cost
lifetime = 20 # life expectancy
wacc = 0.05 # weighted average of capital cost
epc = capex * (wacc * (1 + wacc) ** lifetime) / ((1 + wacc) ** lifetime - 1)
This also implemented in annuity()
. The code above
would look like this:
from oemof.tools import economics
epc = economics.annuity(1000, 20, 0.05)
Note
At the moment the investment class is not compatible with the MIP classes NonConvex
.
Mixed Integer (Linear) Problems¶
Solph also allows you to model components with respect to more technical details
such as a minimal power production. Therefore, the class
NonConvex
exists in the
options
module.
Note that the usage of this class is currently not compatible with the
Investment
class.
If you want to use the functionality of the options-module, the only thing you have to do is to invoke a class instance inside your Flow() - declaration:
b_gas = solph.Bus(label='natural_gas')
b_el = solph.Bus(label='electricity')
b_th = solph.Bus(label='heat')
solph.Transformer(
label='pp_chp',
inputs={b_gas: Flow()},
outputs={b_el: Flow(nominal_value=30,
min=0.5,
nonconvex=NonConvex()),
b_th: Flow(nominal_value=40)},
conversion_factors={b_el: 0.3, b_th: 0.4})
The NonConvex() object of the electrical output of the created LinearTransformer will create
a ‘status’ variable for the flow.
This will be used to model for example minimal/maximal power production constraints if the
attributes min/max of the flow are set. It will also be used to include start up constraints and costs
if corresponding attributes of the class are provided. For more
information see the API of the NonConvex
class and its corresponding
block class NonConvex
.
Note
The usage of this class can sometimes be tricky as there are many interdenpendencies. So check out the examples and do not hesitate to ask the developers if your model does not work as expected.
Adding additional constraints¶
You can add additional constraints to your Model
. See flexible_modelling in the example repository to learn how to do it.
Some predefined additional constraints can be found in the
constraints
module.
- Emission limit for the model ->
emission_limit()
- Coupling of two variables e.g. investment variables) with a factor ->
equate_variables()
- Overall investment limit ->
investment_limit()
The Grouping module (Sets)¶
To construct constraints,
variables and objective expressions inside the blocks
and the models
modules, so called groups are used. Consequently,
certain constraints are created for all elements of a specific group. Thus,
mathematically the groups depict sets of elements inside the model.
The grouping is handled by the solph grouping module groupings
which is based on the oemof core groupings
functionality. You
do not need to understand how the underlying functionality works. Instead, checkout
how the solph grouping module is used to create groups.
The simplest form is a function that looks at every node of the energy system and returns a key for the group depending e.g. on node attributes:
def constraint_grouping(node):
if isinstance(node, Bus) and node.balanced:
return blocks.Bus
if isinstance(node, Transformer):
return blocks.Transformer
GROUPINGS = [constraint_grouping]
This function can be passed in a list to groupings
of
oemof.solph.network.EnergySystem
. So that we end up with two groups,
one with all Transformers and one with all Buses that are balanced. These
groups are simply stored in a dictionary. There are some advanced functionalities
to group two connected nodes with their connecting flow and others
(see for example: FlowsWithNodes
).
Using the Excel (csv) reader¶
Alternatively to a manual creation of energy system component objects as describe above, can also be created from a excel sheet (libreoffice, gnumeric…).
The idea is to create different sheets within one spreadsheet file for different components. Afterwards you can loop over the rows with the attributes in the columns. The name of the columns may differ from the name of the attribute. You may even create two sheets for the GenericStorage class with attributes such as C-rate for batteries or capacity of turbine for a PHES.
Once you have create your specific excel reader you can lower the entry barrier for other users. It is some sort of a GUI in form of platform independent spreadsheet software and to make data and models exchangeable in one archive.
See the example repository for an excel reader example.
Solph Examples¶
See the example repository for various examples. The repository has sections for each major release.
oemof-outputlib¶
For version 0.2.0, the outputlib has been refactored. Tools for plotting optimization results that were part of the outputlib in earlier versions are no longer part of this module as the requirements to plotting functions greatly depend on individial requirements.
Basic functions for plotting of optimisation results are now found in a separate repository oemof_visio.
The main purpose of the outputlib is to collect and organise results. It gives back the results as a python dictionary holding pandas Series for scalar values and pandas DataFrames for all nodes and flows between them. This way we can make use of the full power of the pandas package available to process the results.
See the pandas documentation to learn how to visualise, read or write or how to access parts of the DataFrame to process them.
The documentation of the outputlib consists of three parts:
The first step is the processing of the results (Collecting results). This is followed by basic examples of the general analysis of the results (General approach) and finally the use of functionalities already included in the outputlib for providing a quick access to your results (Easy access). Especially for larger energy systems the general approach will help you to write your own results processing functions.
Collecting results¶
Collecting results can be done with the help of the processing module:
results = outputlib.processing.results(om)
The scalars and sequences describe nodes (with keys like (node, None)) and flows between nodes (with keys like (node_1, node_2)). You can directly extract the data in the dictionary by using these keys, where “node” is the name of the object you want to address. Processing the results is the prerequisite for the examples in the following sections.
General approach¶
As stated above, after processing you will get a dictionary with all result data. If you want to access your results directly via labels, you can continue with Easy access. For a systematic analysis list comprehensions are the easiest way of filtering and analysing your results.
The keys of the results dictionary are tuples containing two nodes. Since flows have a starting node and an ending node, you get a list of all flows by filtering the results using the following expression:
flows = [x for x in results.keys() if x[1] is not None]
On the same way you can get a list of all nodes by applying:
nodes = [x for x in results.keys() if x[1] is None]
Probably you will just get storages as nodes, if you have some in your energy system. Note, that just nodes containing decision variables are listed, e.g. a Source or a Transformer object does not have decision variables. These are in the flows from or to the nodes.
All items within the results dictionary are dictionaries and have two items with ‘scalars’ and ‘sequences’ as keys:
for flow in flows:
print(flow)
print(results[flow]['scalars'])
print(results[flow]['sequences'])
There many options of filtering the flows and nodes as you prefer. The following will give you all flows which are outputs of transformer:
flows_from_transformer = [x for x in flows if isinstance(
x[0], solph.Transformer)]
You can filter your flows, if the label of in- or output contains a given string, e.g.:
flows_to_elec = [x for x in results.keys() if 'elec' in x[1].label]
Getting all labels of the starting node of your investment flows:
flows_invest = [x[0].label for x in flows if hasattr(
results[x]['scalars'], 'invest')]
Easy access¶
The outputlib provides some functions which will help you to access your results directly via labels, which is helpful especially for small energy systems. So, if you want to address objects by their label, you can convert the results dictionary such that the keys are changed to strings given by the labels:
views.convert_keys_to_strings(results)
print(results[('wind', 'bus_electricity')]['sequences']
Another option is to access data belonging to a grouping by the name of the grouping (note also this section on groupings. Given the label of an object, e.g. ‘wind’ you can access the grouping by its label and use this to extract data from the results dictionary.
node_wind = energysystem.groups['wind']
print(results[(node_wind, bus_electricity)])
However, in many situations it might be convenient to use the views module to collect information on a specific node. You can request all data related to a specific node by using either the node’s variable name or its label:
data_wind = outputlib.views.node(results, 'wind')
A function for collecting and printing meta results, i.e. information on the objective function, the problem and the solver, is provided as well:
meta_results = outputlib.processing.meta_results(om)
pp.pprint(meta_results)
oemof-tools¶
The oemof tools package contains little helpers to create your own application. You can use a configuration file in the ini-format to define computer specific parameters such as paths, addresses etc.. Furthermore a logging module helps you creating log files for your application.
Logger¶
The main purpose of this function is to provide a logger with well set default values but with the opportunity to change the most important parameters if you know what you want after a while. This is what most new users (or users who do not want to care about loggers) need. If you are an advanced user with your own ideas it might be easier to copy the whole function to your application and adapt it to your own wishes.
define_logging(logpath=None, logfile='oemof.log', file_format=None,
screen_format=None, file_datefmt=None, screen_datefmt=None,
screen_level=logging.INFO, file_level=logging.DEBUG,
log_version=True, log_path=True, timed_rotating=None):
By default down to INFO all messages are written on the screen and down to DEBUG all messages are written in the file. The file is placed in $HOME/.oemof/log_files as oemof.log. But you can easily pass your own path and your own filename. You can also change the logging level (screen/file) by changing the screen_level or the file_level to logging.DEBUG, logging.INFO, logging.WARNING…. . You can stop the logger from logging the oemof version or commit with log_version=False and the path of the file with log_path=False. Furthermore, you can change the format on the screen and in the file according to the python logging documentation. You can also change the used time format according to this documentation.
file_format = "%(asctime)s - %(levelname)s - %(module)s - %(message)s"
file_datefmt = "%x - %X"
screen_format = "%(asctime)s-%(levelname)s-%(message)s"
screen_datefmt = "%H:%M:%S"
You can also change the behaviour of the file handling (TimedRotatingFileHandler) by passing a dictionary with your own options (timed_rotating).
See the API-doc of define_logging()
for all details.
API¶
oemof¶
oemof package¶
Subpackages¶
oemof.outputlib package¶
Modules for providing a convenient data structure for solph results.
Information about the possible usage is provided within the examples.
This file is part of project oemof (github.com/oemof/oemof). It’s copyrighted by the contributors recorded in the version control history of the file, available from its original location oemof/oemof/outputlib/processing.py
SPDX-License-Identifier: MIT
-
oemof.outputlib.processing.
convert_keys_to_strings
(result, keep_none_type=False)[source]¶ Convert the dictionary keys to strings.
All (tuple) keys of the result object e.g. results[(pp1, bus1)] are converted into strings that represent the object labels e.g. results[(‘pp1’,’bus1’)].
-
oemof.outputlib.processing.
create_dataframe
(om)[source]¶ Create a result dataframe with all optimization data.
Results from Pyomo are written into pandas DataFrame where separate columns are created for the variable index e.g. for tuples of the flows and components or the timesteps.
-
oemof.outputlib.processing.
get_timestep
(x)[source]¶ Get the timestep from oemof tuples.
The timestep from tuples (n, n, int), (n, n), (n, int) and (n,) is fetched as the last element. For time-independent data (scalars) zero ist returned.
-
oemof.outputlib.processing.
get_tuple
(x)[source]¶ Get oemof tuple within iterable or create it.
Tuples from Pyomo are of type (n, n, int), (n, n) and (n, int). For single nodes n a tuple with one object (n,) is created.
-
oemof.outputlib.processing.
meta_results
(om, undefined=False)[source]¶ Fetch some meta data from the Solver. Feel free to add more keys.
Valid keys of the resulting dictionary are: ‘objective’, ‘problem’, ‘solver’.
- om : oemof.solph.Model
- A solved Model.
- undefined : bool
- By default (False) only defined keys can be found in the dictionary. Set to True to get also the undefined keys.
Returns: Return type: dict
-
oemof.outputlib.processing.
parameter_as_dict
(system, exclude_none=True)[source]¶ Create a result dictionary containing node parameters.
Results are written into a dictionary of pandas objects where a Series holds all scalar values and a dataframe all sequences for nodes and flows. The dictionary is keyed by flows (n, n) and nodes (n, None), e.g. parameter[(n, n)][‘sequences’] or parameter[(n, n)][‘scalars’].
Parameters: - system (energy_system.EnergySystem) – A populated energy system.
- exclude_none (bool) – If True, all scalars and sequences containing None values are excluded
Returns: dict
Return type: Parameters for all nodes and flows
-
oemof.outputlib.processing.
remove_timestep
(x)[source]¶ Remove the timestep from oemof tuples.
The timestep is removed from tuples of type (n, n, int) and (n, int).
-
oemof.outputlib.processing.
results
(om)[source]¶ Create a result dictionary from the result DataFrame.
Results from Pyomo are written into a dictionary of pandas objects where a Series holds all scalar values and a dataframe all sequences for nodes and flows. The dictionary is keyed by the nodes e.g. results[idx][‘scalars’] and flows e.g. results[n, n][‘sequences’].
Modules for providing convenient views for solph results.
Information about the possible usage is provided within the examples.
This file is part of project oemof (github.com/oemof/oemof). It’s copyrighted by the contributors recorded in the version control history of the file, available from its original location oemof/oemof/outputlib/views.py
SPDX-License-Identifier: MIT
-
class
oemof.outputlib.views.
NodeOption
[source]¶ Bases:
str
,enum.Enum
An enumeration.
-
All
= 'all'¶
-
HasInputs
= 'has_inputs'¶
-
HasOnlyInputs
= 'has_only_inputs'¶
-
HasOnlyOutputs
= 'has_only_outputs'¶
-
HasOutputs
= 'has_outputs'¶
-
-
oemof.outputlib.views.
convert_to_multiindex
(group, index_names=None, droplevel=None)[source]¶ Convert dict to pandas DataFrame with multiindex
Parameters: - group (dict) – Sequences of the oemof.solph.Model.results dictionary
- index_names (arraylike) – Array with names of the MultiIndex
- droplevel (arraylike) – List containing levels to be dropped from the dataframe
-
oemof.outputlib.views.
filter_nodes
(results, option=<NodeOption.All: 'all'>, exclude_busses=False)[source]¶ Get set of nodes from results-dict for given node option.
This function filters nodes from results for special needs. At the moment, the following options are available:
NodeOption.All
/’all’
:- Returns all nodes
NodeOption.HasOutputs
/’has_outputs’
:- Returns nodes with an output flow (eg. Transformer, Source)
NodeOption.HasInputs
/’has_inputs’
:- Returns nodes with an input flow (eg. Transformer, Sink)
NodeOption.HasOnlyOutputs
/’has_only_outputs’
:- Returns nodes having only output flows (eg. Source)
NodeOption.HasOnlyInputs
/’has_only_inputs’
:- Returns nodes having only input flows (eg. Sink)
Additionally, busses can be excluded by setting exclude_busses to
True
.Parameters: - results (dict) –
- option (NodeOption) –
- exclude_busses (bool) – If set, all bus nodes are excluded from the resulting node set.
Returns: A set of Nodes.
Return type: set
-
oemof.outputlib.views.
get_node_by_name
(results, *names)[source]¶ Searches results for nodes
Names are looked up in nodes from results and either returned single node (in case only one name is given) or as list of nodes. If name is not found, None is returned.
-
oemof.outputlib.views.
net_storage_flow
(results, node_type)[source]¶ Calculates the net storage flow for storage models that have one input edge and one output edge both with flows within the domain of non-negative reals.
- results: dict
- A result dictionary from a solved oemof.solph.Model object
- node_type: oemof.solph class
- Specifies the type for which (storage) type net flows are calculated
Returns: - pandas.DataFrame object with multiindex colums. Names of levels of columns
- are (from, to, net_flow.)
Examples
import oemof.solph as solph from oemof.outputlib import views
# solve oemof solph model ‘m’ # Then collect node weights views.net_storage_flow(m.results(), node_type=solph.GenericStorage)
-
oemof.outputlib.views.
node
(results, node, multiindex=False, keep_none_type=False)[source]¶ Obtain results for a single node e.g. a Bus or Component.
Either a node or its label string can be passed. Results are written into a dictionary which is keyed by ‘scalars’ and ‘sequences’ holding respective data in a pandas Series and DataFrame.
-
oemof.outputlib.views.
node_input_by_type
(results, node_type, droplevel=None)[source]¶ Gets all inputs for all nodes of the type node_type and returns a dataframe.
- results: dict
- A result dictionary from a solved oemof.solph.Model object
- node_type: oemof.solph class
- Specifies the type of the node for that inputs are selected
import oemof.solph as solph from oemof.outputlib import views
# solve oemof solph model ‘m’ # Then collect node weights views.node_input_by_type(m.results(), node_type=solph.Sink)
-
oemof.outputlib.views.
node_output_by_type
(results, node_type, droplevel=None)[source]¶ Gets all outputs for all nodes of the type node_type and returns a dataframe.
- results: dict
- A result dictionary from a solved oemof.solph.Model object
- node_type: oemof.solph class
- Specifies the type of the node for that outputs are selected
import oemof.solph as solph from oemof.outputlib import views
# solve oemof solph model ‘m’ # Then collect node weights views.node_output_by_type(m.results(), node_type=solph.Transformer)
-
oemof.outputlib.views.
node_weight_by_type
(results, node_type)[source]¶ Extracts node weights (if exist) of all components of the specified node_type.
Node weight are endogenous optimzation variables associated with the node and not the edge between two node, foxample the variable representing the storage level.
Parameters: - results (dict) – A result dictionary from a solved oemof.solph.Model object
- node_type (oemof.solph class) – Specifies the type for which node weights should be collected
Example
from oemof.outputlib import views
# solve oemof model ‘m’ # Then collect node weights views.node_weight_by_type(m.results(), node_type=solph.GenericStorage)
oemof.solph package¶
Creating sets, variables, constraints and parts of the objective function for the specified groups.
This file is part of project oemof (github.com/oemof/oemof). It’s copyrighted by the contributors recorded in the version control history of the file, available from its original location oemof/oemof/solph/blocks.py
SPDX-License-Identifier: MIT
-
class
oemof.solph.blocks.
Bus
(*args, **kwargs)[source]¶ Bases:
pyomo.core.base.block.SimpleBlock
Block for all balanced buses.
The following constraints are build:
- Bus balance
om.Bus.balance[i, o, t]
- Bus balance
-
class
oemof.solph.blocks.
Flow
(*args, **kwargs)[source]¶ Bases:
pyomo.core.base.block.SimpleBlock
Flow block with definitions for standard flows.
The following variables are created:
- negative_gradient :
- Difference of a flow in consecutive timesteps if flow is reduced indexed by NEGATIVE_GRADIENT_FLOWS, TIMESTEPS.
- positive_gradient :
- Difference of a flow in consecutive timesteps if flow is increased indexed by NEGATIVE_GRADIENT_FLOWS, TIMESTEPS.
The following sets are created: (-> see basic sets at
Model
)- SUMMED_MAX_FLOWS
- A set of flows with the attribute
summed_max
being not None. - SUMMED_MIN_FLOWS
- A set of flows with the attribute
summed_min
being not None. - NEGATIVE_GRADIENT_FLOWS
- A set of flows with the attribute
negative_gradient
being not None. - POSITIVE_GRADIENT_FLOWS
- A set of flows with the attribute
positive_gradient
being not None - INTEGER_FLOWS
- A set of flows wher the attribute
integer
is True (forces flow to only take integer values)
The following constraints are build:
- Flow max sum
om.Flow.summed_max[i, o]
- Flow min sum
om.Flow.summed_min[i, o]
- Negative gradient constraint
om.Flow.negative_gradient_constr[i, o]
: - Positive gradient constraint
om.Flow.positive_gradient_constr[i, o]
:
The following parts of the objective function are created:
- If
variable_costs
are set by the user:
The expression can be accessed by
om.Flow.variable_costs
and their value after optimization byom.Flow.variable_costs()
.
-
class
oemof.solph.blocks.
InvestmentFlow
(*args, **kwargs)[source]¶ Bases:
pyomo.core.base.block.SimpleBlock
Block for all flows with
investment
being not None.The following sets are created: (-> see basic sets at
Model
)- FLOWS
- A set of flows with the attribute
invest
of typeoptions.Investment
. - FIXED_FLOWS
- A set of flow with the attribute
fixed
set to True - SUMMED_MAX_FLOWS
- A subset of set FLOWS with flows with the attribute
summed_max
being not None. - SUMMED_MIN_FLOWS
- A subset of set FLOWS with flows with the attribute
summed_min
being not None. - MIN_FLOWS
- A subset of FLOWS with flows having set a value of not None in the first timestep.
The following variables are created:
- invest
om.InvestmentFlow.invest[i, o]
- Value of the investment variable i.e. equivalent to the nominal value of the flows after optimization (indexed by FLOWS)
The following constraints are build:
- Actual value constraint for fixed invest
- flows
om.InvestmentFlow.fixed[i, o, t]
- flows
- Lower bound (min) constraint for invest flows
om.InvestmentFlow.min[i, o, t]
- Upper bound (max) constraint for invest flows
om.InvestmentFlow.max[i, o, t]
- Flow max sum for invest flow
om.InvestmentFlow.summed_max[i, o]
- Flow min sum for invest flow
om.InvestmentFlow.summed_min[i, o]
The following parts of the objective function are created:
- Equivalent periodical costs (epc) expression
om.InvestmentFlow.investment_costs
:
The expression can be accessed by
om.InvestmentFlow.variable_costs
and their value after optimization byom.InvestmentFlow.variable_costs()
. This works similar for investment costs with*.investment_costs
etc.
-
class
oemof.solph.blocks.
NonConvexFlow
(*args, **kwargs)[source]¶ Bases:
pyomo.core.base.block.SimpleBlock
- The following sets are created: (-> see basic sets at
Model
)- A set of flows with the attribute
nonconvex
of type options.NonConvex
.- MIN_FLOWS
- A subset of set NONCONVEX_FLOWS with the attribute
min
being not None in the first timestep. - ACTIVITYCOSTFLOWS
- A subset of set NONCONVEX_FLOWS with the attribute
activity_costs
being not None. - STARTUPFLOWS
A subset of set NONCONVEX_FLOWS with the attribute
maximum_startups
orstartup_costs
being not None.- MAXSTARTUPFLOWS
- A subset of set STARTUPFLOWS with the attribute
maximum_startups
being not None. - SHUTDOWNFLOWS
- A subset of set NONCONVEX_FLOWS with the attribute
maximum_shutdowns
orshutdown_costs
being not None. - MAXSHUTDOWNFLOWS
- A subset of set SHUTDOWNFLOWS with the attribute
maximum_shutdowns
being not None. - MINUPTIMEFLOWS
- A subset of set NONCONVEX_FLOWS with the attribute
minimum_uptime
being not None. - MINDOWNTIMEFLOWS
- A subset of set NONCONVEX_FLOWS with the attribute
minimum_downtime
being not None.
The following variables are created:
- Status variable (binary)
om.NonConvexFlow.status
: - Variable indicating if flow is >= 0 indexed by FLOWS
- Startup variable (binary)
om.NonConvexFlow.startup
: - Variable indicating startup of flow (component) indexed by STARTUPFLOWS
- Shutdown variable (binary)
om.NonConvexFlow.shutdown
: - Variable indicating shutdown of flow (component) indexed by SHUTDOWNFLOWS
The following constraints are created:
- Minimum flow constraint
om.NonConvexFlow.min[i,o,t]
- Maximum flow constraint
om.NonConvexFlow.max[i,o,t]
- Startup constraint
om.NonConvexFlow.startup_constr[i,o,t]
Maximum startups constraint
om.NonConvexFlow.max_startup_constr[i,o,t]
- Shutdown constraint
om.NonConvexFlow.shutdown_constr[i,o,t]
Maximum shutdowns constraint
om.NonConvexFlow.max_startup_constr[i,o,t]
- Minimum uptime constraint
om.NonConvexFlow.uptime_constr[i,o,t]
- Minimum downtime constraint
om.NonConvexFlow.downtime_constr[i,o,t]
The following parts of the objective function are created:
- If
nonconvex.startup_costs
is set by the user: - If
nonconvex.shutdown_costs
is set by the user: - If
nonconvex.activity_costs
is set by the user:
-
class
oemof.solph.blocks.
Transformer
(*args, **kwargs)[source]¶ Bases:
pyomo.core.base.block.SimpleBlock
Block for the linear relation of nodes with type
Transformer
The following sets are created: (-> see basic sets at
Model
)- TRANSFORMERS
- A set with all
Transformer
objects.
The following constraints are created:
- Linear relation
om.Transformer.relation[i,o,t]
This module is designed to hold components with their classes and associated individual constraints (blocks) and groupings. Therefore this module holds the class definition and the block directly located by each other.
This file is part of project oemof (github.com/oemof/oemof). It’s copyrighted by the contributors recorded in the version control history of the file, available from its original location oemof/oemof/solph/components.py
SPDX-License-Identifier: MIT
-
class
oemof.solph.components.
ExtractionTurbineCHP
(conversion_factor_full_condensation, *args, **kwargs)[source]¶ Bases:
oemof.solph.network.Transformer
A CHP with an extraction turbine in a linear model. For more options see the
GenericCHP
class.One main output flow has to be defined and is tapped by the remaining flow. The conversion factors have to be defined for the maximum tapped flow ( full CHP mode) and for no tapped flow (full condensing mode). Even though it is possible to limit the variability of the tapped flow, so that the full condensing mode will never be reached.
Parameters: - conversion_factors (dict) – Dictionary containing conversion factors for conversion of inflow to specified outflow. Keys are output bus objects. The dictionary values can either be a scalar or a sequence with length of time horizon for simulation.
- conversion_factor_full_condensation (dict) – The efficiency of the main flow if there is no tapped flow. Only one key is allowed. Use one of the keys of the conversion factors. The key indicates the main flow. The other output flow is the tapped flow.
Note
- The following sets, variables, constraints and objective parts are created
Examples
>>> from oemof import solph >>> bel = solph.Bus(label='electricityBus') >>> bth = solph.Bus(label='heatBus') >>> bgas = solph.Bus(label='commodityBus') >>> et_chp = solph.components.ExtractionTurbineCHP( ... label='variable_chp_gas', ... inputs={bgas: solph.Flow(nominal_value=10e10)}, ... outputs={bel: solph.Flow(), bth: solph.Flow()}, ... conversion_factors={bel: 0.3, bth: 0.5}, ... conversion_factor_full_condensation={bel: 0.5})
-
class
oemof.solph.components.
ExtractionTurbineCHPBlock
(*args, **kwargs)[source]¶ Bases:
pyomo.core.base.block.SimpleBlock
Block for the linear relation of nodes with type
ExtractionTurbineCHP
The following two constraints are created:
where
is defined as:
where the first equation is the result of the relation between the input flow and the two output flows, the second equation stems from how the two output flows relate to each other, and the symbols used are defined as follows (with Variables (V) and Parameters (P)):
symbol attribute type explanation flow[i, n, t]
V fuel input flow flow[n, main_output, t]
V electric power flow[n, tapped_output, t]
V thermal output main_flow_loss_index[n, t]
P power loss index conversion_factor_full_condensation [n, t]
P electric efficiency without heat extraction conversion_factors[main_output][n, t]
P electric efficiency with max heat extraction conversion_factors[tapped_output][n, t]
P thermal efficiency with maximal heat extraction -
CONSTRAINT_GROUP
= True¶
-
-
class
oemof.solph.components.
GenericCHP
(*args, **kwargs)[source]¶ Bases:
oemof.network.Transformer
Component GenericCHP to model combined heat and power plants.
Can be used to model (combined cycle) extraction or back-pressure turbines and used a mixed-integer linear formulation. Thus, it induces more computational effort than the ExtractionTurbineCHP for the benefit of higher accuracy.
The full set of equations is described in: Mollenhauer, E., Christidis, A. & Tsatsaronis, G. Evaluation of an energy- and exergy-based generic modeling approach of combined heat and power plants Int J Energy Environ Eng (2016) 7: 167. https://doi.org/10.1007/s40095-016-0204-6
For a general understanding of (MI)LP CHP representation, see: Fabricio I. Salgado, P. Short - Term Operation Planning on Cogeneration Systems: A Survey Electric Power Systems Research (2007) Electric Power Systems Research Volume 78, Issue 5, May 2008, Pages 835-848 https://doi.org/10.1016/j.epsr.2007.06.001
Note
An adaption for the flow parameter H_L_FG_share_max has been made to set the flue gas losses at maximum heat extraction H_L_FG_max as share of the fuel flow H_F e.g. for combined cycle extraction turbines. The flow parameter H_L_FG_share_min can be used to set the flue gas losses at minimum heat extraction H_L_FG_min as share of the fuel flow H_F e.g. for motoric CHPs. The boolean component parameter back_pressure can be set to model back-pressure characteristics.
Also have a look at the examples on how to use it.
Parameters: - fuel_input (dict) – Dictionary with key-value-pair of oemof.Bus and oemof.Flow object for the fuel input.
- electrical_output (dict) – Dictionary with key-value-pair of oemof.Bus and oemof.Flow object for the electrical output. Related parameters like P_max_woDH are passed as attributes of the oemof.Flow object.
- heat_output (dict) – Dictionary with key-value-pair of oemof.Bus and oemof.Flow object for the heat output. Related parameters like Q_CW_min are passed as attributes of the oemof.Flow object.
- Beta (list of numerical values) – Beta values in same dimension as all other parameters (length of optimization period).
- back_pressure (boolean) – Flag to use back-pressure characteristics. Set to True and Q_CW_min to zero for back-pressure turbines. See paper above for more information.
Note
- The following sets, variables, constraints and objective parts are created
Examples
>>> from oemof import solph >>> bel = solph.Bus(label='electricityBus') >>> bth = solph.Bus(label='heatBus') >>> bgas = solph.Bus(label='commodityBus') >>> ccet = solph.components.GenericCHP( ... label='combined_cycle_extraction_turbine', ... fuel_input={bgas: solph.Flow( ... H_L_FG_share_max=[0.183])}, ... electrical_output={bel: solph.Flow( ... P_max_woDH=[155.946], ... P_min_woDH=[68.787], ... Eta_el_max_woDH=[0.525], ... Eta_el_min_woDH=[0.444])}, ... heat_output={bth: solph.Flow( ... Q_CW_min=[10.552])}, ... Beta=[0.122], back_pressure=False) >>> type(ccet) <class 'oemof.solph.components.GenericCHP'>
-
alphas
¶ Compute or return the _alphas attribute.
-
class
oemof.solph.components.
GenericCHPBlock
(*args, **kwargs)[source]¶ Bases:
pyomo.core.base.block.SimpleBlock
Block for the relation of the
nodes with type class:.GenericCHP.
The following constraints are created:
where
depends on the CHP being back pressure or not.
The coefficients
and
can be determined given the efficiencies maximal/minimal load:
For the attribute
being not None, e.g. for a motoric CHP, the following is created:
Constraint:The symbols used are defined as follows (with Variables (V) and Parameters (P)):
math. symbol attribute type explanation H_F[n,t]
V input of enthalpy through fuel input P[n,t]
V provided electric power P_woDH[n,t]
V electric power without district heating P_min_woDH[n,t]
P min. electric power without district heating P_max_woDH[n,t]
P max. electric power without district heating Q[n,t]
V provided heat Q_CW_min[n,t]
P minimal therm. condenser load to cooling water H_L_FG_min[n,t]
V flue gas enthalpy loss at min heat extraction H_L_FG_max[n,t]
V flue gas enthalpy loss at max heat extraction H_L_FG_share_min[n,t]
P share of flue gas loss at min heat extraction H_L_FG_share_max[n,t]
P share of flue gas loss at max heat extraction Y[n,t]
V status variable on/off n.alphas[0][n,t]
P coefficient describing efficiency n.alphas[1][n,t]
P coefficient describing efficiency Beta[n,t]
P power loss index Eta_el_min_woDH[n,t]
P el. eff. at min. fuel flow w/o distr. heating Eta_el_max_woDH[n,t]
P el. eff. at max. fuel flow w/o distr. heating -
CONSTRAINT_GROUP
= True¶
-
-
class
oemof.solph.components.
GenericInvestmentStorageBlock
(*args, **kwargs)[source]¶ Bases:
pyomo.core.base.block.SimpleBlock
Storage with an
Investment
object.The following sets are created: (-> see basic sets at
Model
)- INVESTSTORAGES
- A set with all storages containing an Investment object.
- INVEST_REL_CAP_IN
- A set with all storages containing an Investment object with coupled investment of input power and storage capacity
- INVEST_REL_CAP_OUT
- A set with all storages containing an Investment object with coupled investment of output power and storage capacity
- INVEST_REL_IN_OUT
- A set with all storages containing an Investment object with coupled investment of input and output power
- INITIAL_STORAGE_LEVEL
- A subset of the set INVESTSTORAGES where elements of the set have an initial_storage_level attribute.
- MIN_INVESTSTORAGES
- A subset of INVESTSTORAGES where elements of the set have an min_storage_level attribute greater than zero for at least one time step.
The following variables are created:
- capacity
om.InvestmentStorage.capacity[n, t]
- Level of the storage (indexed by STORAGES and TIMESTEPS)
- invest
om.InvestmentStorage.invest[n, t]
- Nominal capacity of the storage (indexed by STORAGES)
The following constraints are build:
- Storage balance
- Same as for
GenericStorageBlock
. - Initial capacity of
network.Storage
- Connect the invest variables of the storage and the input flow.
- Connect the invest variables of the storage and the output flow.
- Connect the invest variables of the input and the output flow.
- Maximal capacity
om.InvestmentStorage.max_capacity[n, t]
- Minimal capacity
om.InvestmentStorage.min_capacity[n, t]
The following parts of the objective function are created:
- Equivalent periodical costs (investment costs):
The expression can be accessed by
om.InvestStorages.investment_costs
and their value after optimization byom.InvestStorages.investment_costs()
.The symbols are the same as in:class:.GenericStorageBlock.
-
CONSTRAINT_GROUP
= True¶
-
class
oemof.solph.components.
GenericStorage
(*args, max_storage_level=1, min_storage_level=0, **kwargs)[source]¶ Bases:
oemof.network.Transformer
Component GenericStorage to model with basic characteristics of storages.
Parameters: - nominal_storage_capacity (numeric) – Absolute nominal capacity of the storage
- invest_relation_input_capacity (numeric or None) –
Ratio between the investment variable of the input Flow and the investment variable of the storage.
- invest_relation_output_capacity (numeric or None) –
Ratio between the investment variable of the output Flow and the investment variable of the storage.
- invest_relation_input_output (numeric or None) –
Ratio between the investment variable of the output Flow and the investment variable of the input flow. This ratio used to fix the flow investments to each other. Values < 1 set the input flow lower than the output and > 1 will set the input flow higher than the output flow. If None no relation will be set.
- initial_storage_level (numeric) – The content of the storage in the first time step of optimization.
- balanced (boolian) – Couple storage level of first and last time step. (Total inflow and total outflow are balanced.)
- loss_rate (numeric (sequence or scalar)) – The relative loss of the storage capacity from between two consecutive timesteps.
- inflow_conversion_factor (numeric (sequence or scalar)) – The relative conversion factor, i.e. efficiency associated with the inflow of the storage.
- outflow_conversion_factor (numeric (sequence or scalar)) – see: inflow_conversion_factor
- min_storage_level (numeric (sequence or scalar)) – The minimum storaged energy of the storage as fraction of the nominal storage capacity (between 0 and 1). To set different values in every time step use a sequence.
- max_storage_level (numeric (sequence or scalar)) – see: min_storage_level
- investment (
oemof.solph.options.Investment
object) – Object indicating if a nominal_value of the flow is determined by the optimization problem. Note: This will refer all attributes to an investment variable instead of to the nominal_storage_capacity. The nominal_storage_capacity should not be set (or set to None) if an investment object is used.
Note
- The following sets, variables, constraints and objective parts are created
GenericStorageBlock
(if no Investment object present)GenericInvestmentStorageBlock
(if Investment object present)
Examples
Basic usage examples of the GenericStorage with a random selection of attributes. See the Flow class for all Flow attributes.
>>> from oemof import solph
>>> my_bus = solph.Bus('my_bus')
>>> my_storage = solph.components.GenericStorage( ... label='storage', ... nominal_storage_capacity=1000, ... inputs={my_bus: solph.Flow(nominal_value=200, variable_costs=10)}, ... outputs={my_bus: solph.Flow(nominal_value=200)}, ... loss_rate=0.01, ... initial_storage_level=0, ... max_storage_level = 0.9, ... inflow_conversion_factor=0.9, ... outflow_conversion_factor=0.93)
>>> my_investment_storage = solph.components.GenericStorage( ... label='storage', ... investment=solph.Investment(ep_costs=50), ... inputs={my_bus: solph.Flow()}, ... outputs={my_bus: solph.Flow()}, ... loss_rate=0.02, ... initial_storage_level=None, ... invest_relation_input_capacity=1/6, ... invest_relation_output_capacity=1/6, ... inflow_conversion_factor=1, ... outflow_conversion_factor=0.8)
-
class
oemof.solph.components.
GenericStorageBlock
(*args, **kwargs)[source]¶ Bases:
pyomo.core.base.block.SimpleBlock
Storage without an
Investment
object.The following sets are created: (-> see basic sets at
Model
)- STORAGES
- A set with all
Storage
objects, which do not have an - attr:investment of type
Investment
.
- A set with all
- STORAGES_BALANCED
- A set of all
Storage
objects, with ‘balanced’ attribute set to True. - STORAGES_WITH_INVEST_FLOW_REL
- A set with all
Storage
objects with two investment flows coupled with the ‘invest_relation_input_output’ attribute.
The following variables are created:
- capacity
- Capacity (level) for every storage and timestep. The value for the capacity at the beginning is set by the parameter initial_capacity or not set if initial_capacity is None. The variable of storage s and timestep t can be accessed by: om.Storage.capacity[s, t]
The following constraints are created:
- Set last time step to the initial capacity if
balanced == True
- Storage balance
om.Storage.balance[n, t]
- Connect the invest variables of the input and the output flow.
symbol explanation attribute energy currently stored capacity
nominal capacity of the energy storage nominal_storage_capacity
state before initial time step initial_storage_level
minimum allowed storage min_storage_level[t]
maximum allowed storage max_storage_level[t]
fraction of lost energy (e.g. leakage) per time loss_rate[t]
energy flowing in inputs
energy flowing out outputs
conversion factor (i.e. efficiency) when storing energy inflow_conversion_factor[t]
conversion factor when (i.e. efficiency) taking stored energy outflow_conversion_factor[t]
length of the time step The following parts of the objective function are created:
Nothing added to the objective function.
-
CONSTRAINT_GROUP
= True¶
-
class
oemof.solph.components.
OffsetTransformer
(*args, **kwargs)[source]¶ Bases:
oemof.network.Transformer
An object with one input and one output.
Parameters: coefficients (tuple) – Tuple containing the first two polynomial coefficients i.e. the y-intersection and slope of a linear equation. The tuple values can either be a scalar or a sequence with length of time horizon for simulation. Notes
- The sets, variables, constraints and objective parts are created
Examples
>>> from oemof import solph
>>> bel = solph.Bus(label='bel') >>> bth = solph.Bus(label='bth')
>>> ostf = solph.components.OffsetTransformer( ... label='ostf', ... inputs={bel: solph.Flow( ... nominal_value=60, min=0.5, max=1.0, ... nonconvex=solph.NonConvex())}, ... outputs={bth: solph.Flow()}, ... coefficients=(20, 0.5))
>>> type(ostf) <class 'oemof.solph.components.OffsetTransformer'>
-
class
oemof.solph.components.
OffsetTransformerBlock
(*args, **kwargs)[source]¶ Bases:
pyomo.core.base.block.SimpleBlock
Block for the relation of nodes with type
OffsetTransformer
The following constraints are created:
Variables (V) and Parameters (P)¶ symbol attribute type explanation flow[n, o, t]
V Power of output flow[i, n, t]
V Power of input status[i, n, t]
V binary status variable of nonconvex input flow coefficients[1][n, t]
P linear coefficient 1 (slope) coefficients[0][n, t]
P linear coefficient 0 (y-intersection) -
CONSTRAINT_GROUP
= True¶
-
Additional constraints to be used in an oemof energy model. This file is part of project oemof (github.com/oemof/oemof). It’s copyrighted by the contributors recorded in the version control history of the file, available from its original location oemof/oemof/solph/constraints.py
SPDX-License-Identifier: MIT
-
oemof.solph.constraints.
emission_limit
(om, flows=None, limit=None)[source]¶ Short handle for generic_integral_limit() with keyword=”emission_factor”.
Note
Flow objects required an attribute “emission_factor”!
-
oemof.solph.constraints.
equate_variables
(model, var1, var2, factor1=1, name=None)[source]¶ Adds a constraint to the given model that set two variables to equal adaptable by a factor.
The following constraints are build:
Parameters: - var1 (pyomo.environ.Var) – First variable, to be set to equal with Var2 and multiplied with factor1.
- var2 (pyomo.environ.Var) – Second variable, to be set equal to (Var1 * factor1).
- factor1 (float) – Factor to define the proportion between the variables.
- name (str) – Optional name for the equation e.g. in the LP file. By default the name is: equate + string representation of var1 and var2.
- model (oemof.solph.Model) – Model to which the constraint is added.
Examples
The following example shows how to define a transmission line in the investment mode by connecting both investment variables. Note that the equivalent periodical costs (epc) of the line are 40. You could also add them to one line and set them to 0 for the other line.
>>> import pandas as pd >>> from oemof import solph >>> date_time_index = pd.date_range('1/1/2012', periods=5, freq='H') >>> energysystem = solph.EnergySystem(timeindex=date_time_index) >>> bel1 = solph.Bus(label='electricity1') >>> bel2 = solph.Bus(label='electricity2') >>> energysystem.add(bel1, bel2) >>> energysystem.add(solph.Transformer( ... label='powerline_1_2', ... inputs={bel1: solph.Flow()}, ... outputs={bel2: solph.Flow( ... investment=solph.Investment(ep_costs=20))})) >>> energysystem.add(solph.Transformer( ... label='powerline_2_1', ... inputs={bel2: solph.Flow()}, ... outputs={bel1: solph.Flow( ... investment=solph.Investment(ep_costs=20))})) >>> om = solph.Model(energysystem) >>> line12 = energysystem.groups['powerline_1_2'] >>> line21 = energysystem.groups['powerline_2_1'] >>> solph.constraints.equate_variables( ... om, ... om.InvestmentFlow.invest[line12, bel2], ... om.InvestmentFlow.invest[line21, bel1])
-
oemof.solph.constraints.
generic_integral_limit
(om, keyword, flows=None, limit=None)[source]¶ Set a global limit for flows weighted by attribute called keyword. The attribute named by keyword has to be added to every flow you want to take into account.
Total value of keyword attributes after optimization can be retrieved calling the
om.oemof.solph.Model.integral_limit_${keyword}()
.Parameters: - om (oemof.solph.Model) – Model to which constraints are added.
- flows (dict) – Dictionary holding the flows that should be considered in constraint. Keys are (source, target) objects of the Flow. If no dictionary is given all flows containing the keyword attribute will be used.
- keyword (attribute to consider) –
- limit (numeric) – Absolute limit of keyword attribute for the energy system.
Note
Flow objects required an attribute named like keyword!
Constraint:
With F_I being the set of flows considered for the integral limit and T being the set of time steps.
The symbols used are defined as follows (with Variables (V) and Parameters (P)):
V power flow
at time step
P weight given to Flow named according to keyword :math:` au(t)` P width of time step
P global limit given by keyword limit
-
oemof.solph.constraints.
investment_limit
(model, limit=None)[source]¶ Set an absolute limit for the total investment costs of an investment optimization problem:
Parameters: - model (oemof.solph.Model) – Model to which the constraint is added
- limit (float) – Absolute limit of the investment (i.e. RHS of constraint)
This module is designed to hold custom components with their classes and associated individual constraints (blocks) and groupings. Therefore this module holds the class definition and the block directly located by each other.
This file is part of project oemof (github.com/oemof/oemof). It’s copyrighted by the contributors recorded in the version control history of the file, available from its original location oemof/oemof/solph/custom.py
SPDX-License-Identifier: MIT
-
class
oemof.solph.custom.
ElectricalBus
(*args, **kwargs)[source]¶ Bases:
oemof.solph.network.Bus
A electrical bus object. Every node has to be connected to Bus. This Bus is used in combination with ElectricalLine objects for linear optimal power flow (lopf) calculations.
Parameters: - slack (boolean) – If True Bus is slack bus for network
- v_max (numeric) – Maximum value of voltage angle at electrical bus
- v_min (numeric) – Mininum value of voltag angle at electrical bus
- Note (This component is experimental. Use it with care.) –
Notes
- The following sets, variables, constraints and objective parts are created
- The objects are also used inside:
-
class
oemof.solph.custom.
ElectricalLine
(*args, **kwargs)[source]¶ Bases:
oemof.solph.network.Flow
An ElectricalLine to be used in linear optimal power flow calculations. based on angle formulation. Check out the Notes below before using this component!
Parameters: - reactance (float or array of floats) – Reactance of the line to be modelled
- Note (This component is experimental. Use it with care.) –
Notes
- To use this object the connected buses need to be of the type
ElectricalBus
. - It does not work together with flows that have set the attr.`nonconvex`, i.e. unit commitment constraints are not possible
- Input and output of this component are set equal, therefore just use either only the input or the output to parameterize.
- Default attribute min of in/outflows is overwritten by -1 if not set differently by the user
- The following sets, variables, constraints and objective parts are created
-
class
oemof.solph.custom.
ElectricalLineBlock
(*args, **kwargs)[source]¶ Bases:
pyomo.core.base.block.SimpleBlock
Block for the linear relation of nodes with type class:.ElectricalLine
Note: This component is experimental. Use it with care.
The following constraints are created:
- Linear relation
om.ElectricalLine.electrical_flow[n,t]
TODO: Add equate constraint of flows
The following variable are created:
TODO: Add voltage angle variable
TODO: Add fix slack bus voltage angle to zero constraint / bound
TODO: Add tests
-
CONSTRAINT_GROUP
= True¶
- Linear relation
-
class
oemof.solph.custom.
GenericCAES
(*args, **kwargs)[source]¶ Bases:
oemof.solph.network.Transformer
Component GenericCAES to model arbitrary compressed air energy storages.
The full set of equations is described in: Kaldemeyer, C.; Boysen, C.; Tuschy, I. A Generic Formulation of Compressed Air Energy Storage as Mixed Integer Linear Program – Unit Commitment of Specific Technical Concepts in Arbitrary Market Environments Materials Today: Proceedings 00 (2018) 0000–0000 [currently in review]
Parameters: - electrical_input (dict) – Dictionary with key-value-pair of oemof.Bus and oemof.Flow object for the electrical input.
- fuel_input (dict) – Dictionary with key-value-pair of oemof.Bus and oemof.Flow object for the fuel input.
- electrical_output (dict) – Dictionary with key-value-pair of oemof.Bus and oemof.Flow object for the electrical output.
- Note (This component is experimental. Use it with care.) –
Notes
- The following sets, variables, constraints and objective parts are created
GenericCAES
TODO: Add description for constraints. See referenced paper until then!
Examples
>>> from oemof import solph >>> bel = solph.Bus(label='bel') >>> bth = solph.Bus(label='bth') >>> bgas = solph.Bus(label='bgas') >>> # dictionary with parameters for a specific CAES plant >>> concept = { ... 'cav_e_in_b': 0, ... 'cav_e_in_m': 0.6457267578, ... 'cav_e_out_b': 0, ... 'cav_e_out_m': 0.3739636077, ... 'cav_eta_temp': 1.0, ... 'cav_level_max': 211.11, ... 'cmp_p_max_b': 86.0918959849, ... 'cmp_p_max_m': 0.0679999932, ... 'cmp_p_min': 1, ... 'cmp_q_out_b': -19.3996965679, ... 'cmp_q_out_m': 1.1066036114, ... 'cmp_q_tes_share': 0, ... 'exp_p_max_b': 46.1294016678, ... 'exp_p_max_m': 0.2528340303, ... 'exp_p_min': 1, ... 'exp_q_in_b': -2.2073411014, ... 'exp_q_in_m': 1.129249765, ... 'exp_q_tes_share': 0, ... 'tes_eta_temp': 1.0, ... 'tes_level_max': 0.0} >>> # generic compressed air energy storage (caes) plant >>> caes = solph.custom.GenericCAES( ... label='caes', ... electrical_input={bel: solph.Flow()}, ... fuel_input={bgas: solph.Flow()}, ... electrical_output={bel: solph.Flow()}, ... params=concept, fixed_costs=0) >>> type(caes) <class 'oemof.solph.custom.GenericCAES'>
-
class
oemof.solph.custom.
GenericCAESBlock
(*args, **kwargs)[source]¶ Bases:
pyomo.core.base.block.SimpleBlock
Block for nodes of class:.GenericCAES.
Note: This component is experimental. Use it with care.
The following constraints are created:
Table: Symbols and attribute names of variables and parameters
Variables (V) and Parameters (P)¶ symbol attribute type explanation cmp_st[n,t]
V Status of compression cmp_p[n,t]
V Compression power cmp_p_max[n,t]
V Max. compression power cmp_q_out_sum[n,t]
V Summed heat flow in compression cmp_q_waste[n,t]
V Waste heat flow from compression exp_st[n,t]
V Status of expansion (binary) exp_p[n,t]
V Expansion power exp_p_max[n,t]
V Max. expansion power exp_q_in_sum[n,t]
V Summed heat flow in expansion exp_q_fuel_in[n,t]
V Heat (external) flow into expansion exp_q_add_in[n,t]
V Additional heat flow into expansion cav_level[n,t]
V Filling level if CAE cav_e_in[n,t]
V Exergy flow into CAS cav_e_out[n,t]
V Exergy flow from CAS tes_level[n,t]
V Filling level of Thermal Energy Storage (TES) tes_e_in[n,t]
V Heat flow into TES tes_e_out[n,t]
V Heat flow from TES cmp_p_max_b[n,t]
P Specific y-intersection cmp_q_out_b[n,t]
P Specific y-intersection exp_p_max_b[n,t]
P Specific y-intersection exp_q_in_b[n,t]
P Specific y-intersection cav_e_in_b[n,t]
P Specific y-intersection cav_e_out_b[n,t]
P Specific y-intersection cmp_p_max_m[n,t]
P Specific slope cmp_q_out_m[n,t]
P Specific slope exp_p_max_m[n,t]
P Specific slope exp_q_in_m[n,t]
P Specific slope cav_e_in_m[n,t]
P Specific slope cav_e_out_m[n,t]
P Specific slope cmp_p_min[n,t]
P Min. compression power cmp_q_tes_share[n,t]
P Ratio between waste heat flow and heat flow into TES exp_q_tes_share[n,t]
P Ratio between external heat flow into expansion and heat flows from TES and additional source m.timeincrement[n,t]
P Time interval length tes_level_max[n,t]
P Max. filling level of TES cav_level_max[n,t]
P Max. filling level of TES cav_eta_tmp[n,t]
P Temporal efficiency (loss factor to take intertemporal losses into account) flow[list(n.electrical_input.keys())[0], n, t]
P Electr. power input into compression flow[n, list(n.electrical_output.keys())[0], t]
P Electr. power output of expansion flow[list(n.fuel_input.keys())[0], n, t]
P Heat input (external) into Expansion -
CONSTRAINT_GROUP
= True¶
-
-
class
oemof.solph.custom.
Link
(*args, **kwargs)[source]¶ Bases:
oemof.solph.network.Transformer
A Link object with 1…2 inputs and 1…2 outputs.
Parameters: - conversion_factors (dict) – Dictionary containing conversion factors for conversion of each flow. Keys are the connected tuples (input, output) bus objects. The dictionary values can either be a scalar or a sequence with length of time horizon for simulation.
- Note (This component is experimental. Use it with care.) –
Notes
- The sets, variables, constraints and objective parts are created
Examples
>>> from oemof import solph >>> bel0 = solph.Bus(label="el0") >>> bel1 = solph.Bus(label="el1")
>>> link = solph.custom.Link( ... label="transshipment_link", ... inputs={bel0: solph.Flow(), bel1: solph.Flow()}, ... outputs={bel0: solph.Flow(), bel1: solph.Flow()}, ... conversion_factors={(bel0, bel1): 0.92, (bel1, bel0): 0.99}) >>> print(sorted([x[1][5] for x in link.conversion_factors.items()])) [0.92, 0.99]
>>> type(link) <class 'oemof.solph.custom.Link'>
>>> sorted([str(i) for i in link.inputs]) ['el0', 'el1']
>>> link.conversion_factors[(bel0, bel1)][3] 0.92
-
class
oemof.solph.custom.
LinkBlock
(*args, **kwargs)[source]¶ Bases:
pyomo.core.base.block.SimpleBlock
Block for the relation of nodes with type
Link
Note: This component is experimental. Use it with care.
The following constraints are created:
TODO: Add description for constraints TODO: Add tests
-
CONSTRAINT_GROUP
= True¶
-
-
class
oemof.solph.custom.
SinkDSM
(demand, capacity_up, capacity_down, method, shift_interval=None, delay_time=None, cost_dsm_up=0, cost_dsm_down=0, **kwargs)[source]¶ Bases:
oemof.solph.network.Sink
Demand Side Management implemented as Sink with flexibility potential.
Based on the paper by Zerrahn, Alexander and Schill, Wolf-Peter (2015): On the representation of demand-side management in power system models, in: Energy (84), pp. 840-845, 10.1016/j.energy.2015.03.037, accessed 17.09.2019, pp. 842-843.
SinkDSM adds additional constraints that allow to shift energy in certain time window constrained by
capacity_up
andcapacity_down
.Parameters: - demand (numeric) – original electrical demand
- capacity_up (int or array) – maximum DSM capacity that may be increased
- capacity_down (int or array) – maximum DSM capacity that may be reduced
- method ('interval' , 'delay') –
Choose one of the DSM modelling approaches. Read notes about which parameters to be applied for which approach.
interval :
Simple model in which the load shift must be compensated in a predefined fixed interval (shift_interval
is mandatory). Within time windows of the lengthshift_interval
DSM up and down shifts are balanced. SeeSinkDSMIntervalBlock
for details.delay :
Sophisticated model based on the formulation by Zerrahn & Schill (2015). The load-shift of the component must be compensated in a predefined delay-time (delay_time
is mandatory). For details seeSinkDSMDelayBlock
. - shift_interval (int) – Only used when
method
is set to ‘interval’. Otherwise, can be None. It’s the interval in which betweenand
have to be compensated.
- delay_time (int) – Only used when
method
is set to ‘delay’. Otherwise, can be None. Length of symmetrical time windows aroundin which
and
have to be compensated.
- cost_dsm_up (
int
) – Cost per unit of DSM activity that increases the demand - cost_dsm_down (
int
) – Cost per unit of DSM activity that decreases the demand
Note
- This component is a candidate component. It’s implemented as a custom component for users that like to use and test the component at early stage. Please report issues to improve the component.
- As many constraints and dependencies are created in method ‘delay’, computational cost might be high with a large ‘delay_time’ and with model of high temporal resolution
- Using
method
‘delay’ might result in demand shifts that exceed the specified delay time by activating up and down simultaneously in the time steps between to DSM events. - It’s not recommended to assign cost to the flow that connects
SinkDSM
with a bus. Instead, usecost_dsm_up
orcost_dsm_down
-
class
oemof.solph.custom.
SinkDSMDelayBlock
(*args, **kwargs)[source]¶ Bases:
pyomo.core.base.block.SimpleBlock
Constraints for SinkDSM with “delay” method
The following constraints are created for method = ‘delay’:
Table: Symbols and attribute names of variables and parameters
Variables (V) and Parameters (P)¶ symbol attribute type explanation dsm_do[g,t,tt]
V DSM up shift (additional load) dsm_up[g,t]
V DSM down shift (less load) flow[g,t]
V Energy flowing in from electrical bus delay_time
P Delay time for load shift demand[t]
P Electrical demand series capacity_down[tt]
P Capacity DSM down shift capacity_up[tt]
P Capacity DSM up shift P Time steps -
CONSTRAINT_GROUP
= True¶
-
-
class
oemof.solph.custom.
SinkDSMIntervalBlock
(*args, **kwargs)[source]¶ Bases:
pyomo.core.base.block.SimpleBlock
Constraints for SinkDSM with “interval” method
The following constraints are created for method = ‘interval’:
Table: Symbols and attribute names of variables and parameters
Variables (V) and Parameters (P)¶ symbol attribute type explanation capacity_up
V DSM up shift capacity_down
V DSM down shift inputs
V Energy flowing in from electrical bus demand[t]
P Electrical demand series capacity_down[tt]
P Capacity DSM down shift capacity capacity_up[tt]
P Capacity DSM up shift shift_interval
P Shift interval P Time steps -
CONSTRAINT_GROUP
= True¶
-
list: Groupings needed on an energy system for it to work with solph.
If you want to use solph on an energy system, you need to create it with these groupings specified like this:
from oemof.network import EnergySystem import solph
energy_system = EnergySystem(groupings=solph.GROUPINGS)
This file is part of project oemof (github.com/oemof/oemof). It’s copyrighted by the contributors recorded in the version control history of the file, available from its original location oemof/oemof/solph/groupings.py
SPDX-License-Identifier: MIT
-
oemof.solph.groupings.
constraint_grouping
(node, fallback=<function <lambda>>)[source]¶ Grouping function for constraints.
This function can be passed in a list to
groupings
ofoemof.solph.network.EnergySystem
.Parameters: - node (
Node <oemof.network.Node
) – The node for which the figure out a constraint group. - fallback (callable, optional) – A function of one argument. If node doesn’t have a constraint_group attribute, this is used to group the node instead. Defaults to not group the node at all.
- node (
Solph Optimization Models
This file is part of project oemof (github.com/oemof/oemof). It’s copyrighted by the contributors recorded in the version control history of the file, available from its original location oemof/oemof/solph/models.py
SPDX-License-Identifier: MIT
-
class
oemof.solph.models.
BaseModel
(energysystem, **kwargs)[source]¶ Bases:
pyomo.core.base.PyomoModel.ConcreteModel
The BaseModel for other solph-models (Model, MultiPeriodModel, etc.)
Parameters: - energysystem (EnergySystem object) – Object that holds the nodes of an oemof energy system graph
- constraint_groups (list (optional)) – Solph looks for these groups in the given energy system and uses them
to create the constraints of the optimization problem.
Defaults to
Model.CONSTRAINTS
- objective_weighting (array like (optional)) – Weights used for temporal objective function expressions. If nothing is passed timeincrement will be used which is calculated from the freq length of the energy system timeindex .
- auto_construct (boolean) – If this value is true, the set, variables, constraints, etc. are added, automatically when instantiating the model. For sequential model building process set this value to False and use methods _add_parent_block_sets, _add_parent_block_variables, _add_blocks, _add_objective
- Attributes –
- ----------- –
- timeincrement (sequence) – Time increments.
- flows (dict) – Flows of the model.
- name (str) – Name of the model.
- es (solph.EnergySystem) – Energy system of the model.
- meta (pyomo.opt.results.results_.SolverResults or None) – Solver results.
- dual (.. or None) –
- rc (.. or None) –
-
CONSTRAINT_GROUPS
= []¶
-
receive_duals
()[source]¶ Method sets solver suffix to extract information about dual variables from solver. Shadow prices (duals) and reduced costs (rc) are set as attributes of the model.
-
solve
(solver='cbc', solver_io='lp', **kwargs)[source]¶ Takes care of communication with solver to solve the model.
Parameters: - solver (string) – solver to be used e.g. “glpk”,”gurobi”,”cplex”
- solver_io (string) – pyomo solver interface file format: “lp”,”python”,”nl”, etc.
- **kwargs (keyword arguments) – Possible keys can be set see below:
Other Parameters: - solve_kwargs (dict) – Other arguments for the pyomo.opt.SolverFactory.solve() method Example : {“tee”:True}
- cmdline_options (dict) – Dictionary with command line options for solver e.g. {“mipgap”:”0.01”} results in “–mipgap 0.01” {“interior”:” “} results in “–interior” Gurobi solver takes numeric parameter values such as {“method”: 2}
-
class
oemof.solph.models.
Model
(energysystem, **kwargs)[source]¶ Bases:
oemof.solph.models.BaseModel
An energy system model for operational and investment optimization.
Parameters: - energysystem (EnergySystem object) – Object that holds the nodes of an oemof energy system graph
- constraint_groups (list) – Solph looks for these groups in the given energy system and uses them
to create the constraints of the optimization problem.
Defaults to
Model.CONSTRAINTS
- following basic sets are created** (**The) –
- NODES – A set with all nodes of the given energy system.
- TIMESTEPS – A set with all timesteps of the given time horizon.
- FLOWS – A 2 dimensional set with all flows. Index: (source, target)
- following basic variables are created** (**The) –
- flow – Flow from source to target indexed by FLOWS, TIMESTEPS. Note: Bounds of this variable are set depending on attributes of the corresponding flow object.
-
CONSTRAINT_GROUPS
= [<class 'oemof.solph.blocks.Bus'>, <class 'oemof.solph.blocks.Transformer'>, <class 'oemof.solph.blocks.InvestmentFlow'>, <class 'oemof.solph.blocks.Flow'>, <class 'oemof.solph.blocks.NonConvexFlow'>]¶
Classes used to model energy supply systems within solph.
Classes are derived from oemof core network classes and adapted for specific optimization tasks. An energy system is modelled as a graph/network of nodes with very specific constraints on which types of nodes are allowed to be connected.
This file is part of project oemof (github.com/oemof/oemof). It’s copyrighted by the contributors recorded in the version control history of the file, available from its original location oemof/oemof/solph/network.py
SPDX-License-Identifier: MIT
-
class
oemof.solph.network.
Bus
(*args, **kwargs)[source]¶ Bases:
oemof.network.Bus
A balance object. Every node has to be connected to Bus.
Notes
- The following sets, variables, constraints and objective parts are created
-
class
oemof.solph.network.
EnergySystem
(**kwargs)[source]¶ Bases:
oemof.energy_system.EnergySystem
A variant of
EnergySystem
specially tailored to solph.In order to work in tandem with solph, instances of this class always use
solph.GROUPINGS
. If custom groupings are supplied via the groupings keyword argument,solph.GROUPINGS
is prepended to those.If you know what you are doing and want to use solph without
solph.GROUPINGS
, you can just usecore's EnergySystem
directly.
-
class
oemof.solph.network.
Flow
(**kwargs)[source]¶ Bases:
oemof.network.Edge
Defines a flow between two nodes.
Keyword arguments are used to set the attributes of this flow. Parameters which are handled specially are noted below. For the case where a parameter can be either a scalar or a sequence, a scalar value will be converted to a sequence containing the scalar value at every index. This sequence is then stored under the paramter’s key.
Parameters: - nominal_value (numeric) – The nominal value of the flow. If this value is set the corresponding optimization variable of the flow object will be bounded by this value multiplied with min(lower bound)/max(upper bound).
- max (numeric (sequence or scalar)) – Normed maximum value of the flow. The flow absolute maximum will be
calculated by multiplying
nominal_value
withmax
- min (numeric (sequence or scalar)) – Nominal minimum value of the flow (see
max
). - actual_value (numeric (sequence or scalar)) – Specific value for the flow variable. Will be multiplied with the
nominal_value
to get the absolute value. Iffixed
is set toTrue
the flow variable will be fixed toactual_value * nominal_value
, i.e. this value is set exogenous. - positive_gradient (
dict
, default:{‘ub’: None, ‘costs’: 0}
) –A dictionary containing the following two keys:
’ub’
: numeric (sequence, scalar or None), the normed upper bound on the positive difference (flow[t-1] < flow[t]
) of two consecutive flow values.’costs`
: numeric (scalar or None), the gradient cost per unit.
- negative_gradient (
dict
, default:{‘ub’: None, ‘costs’: 0}
) –A dictionary containing the following two keys:
’ub’
: numeric (sequence, scalar or None), the normed upper bound on the negative difference (flow[t-1] > flow[t]
) of two consecutive flow values.’costs`
: numeric (scalar or None), the gradient cost per unit.
- summed_max (numeric) – Specific maximum value summed over all timesteps. Will be multiplied with the nominal_value to get the absolute limit.
- summed_min (numeric) – see above
- variable_costs (numeric (sequence or scalar)) – The costs associated with one unit of the flow. If this is set the costs will be added to the objective expression of the optimization problem.
- fixed (boolean) – Boolean value indicating if a flow is fixed during the optimization
problem to its ex-ante set value. Used in combination with the
actual_value
. - investment (
Investment
) – Object indicating if a nominal_value of the flow is determined by the optimization problem. Note: This will refer all attributes to an investment variable instead of to the nominal_value. The nominal_value should not be set (or set to None) if an investment object is used. - nonconvex (
NonConvex
) – If a nonconvex flow object is added here, the flow constraints will be altered significantly as the mathematical model for the flow will be different, i.e. constraint etc. fromNonConvexFlow
will be used instead ofFlow
. Note: at the moment this does not work if the investment attribute is set .
Notes
- The following sets, variables, constraints and objective parts are created
Flow
InvestmentFlow
(additionally if Investment object is present)NonConvexFlow
(If- nonconvex object is present, CAUTION: replaces
Flow
class and a MILP will be build)
Examples
Creating a fixed flow object:
>>> f = Flow(actual_value=[10, 4, 4], fixed=True, variable_costs=5) >>> f.variable_costs[2] 5 >>> f.actual_value[2] 4
Creating a flow object with time-depended lower and upper bounds:
>>> f1 = Flow(min=[0.2, 0.3], max=0.99, nominal_value=100) >>> f1.max[1] 0.99
-
class
oemof.solph.network.
Sink
(*args, **kwargs)[source]¶ Bases:
oemof.network.Sink
An object with one input flow.
-
class
oemof.solph.network.
Source
(*args, **kwargs)[source]¶ Bases:
oemof.network.Source
An object with one output flow.
-
class
oemof.solph.network.
Transformer
(*args, **kwargs)[source]¶ Bases:
oemof.network.Transformer
A linear Transformer object with n inputs and n outputs.
Parameters: conversion_factors (dict) – Dictionary containing conversion factors for conversion of each flow. Keys are the connected bus objects. The dictionary values can either be a scalar or a sequence with length of time horizon for simulation. Examples
Defining an linear transformer:
>>> from oemof import solph >>> bgas = solph.Bus(label='natural_gas') >>> bcoal = solph.Bus(label='hard_coal') >>> bel = solph.Bus(label='electricity') >>> bheat = solph.Bus(label='heat')
>>> trsf = solph.Transformer( ... label='pp_gas_1', ... inputs={bgas: solph.Flow(), bcoal: solph.Flow()}, ... outputs={bel: solph.Flow(), bheat: solph.Flow()}, ... conversion_factors={bel: 0.3, bheat: 0.5, ... bgas: 0.8, bcoal: 0.2}) >>> print(sorted([x[1][5] for x in trsf.conversion_factors.items()])) [0.2, 0.3, 0.5, 0.8]
>>> type(trsf) <class 'oemof.solph.network.Transformer'>
>>> sorted([str(i) for i in trsf.inputs]) ['hard_coal', 'natural_gas']
>>> trsf_new = solph.Transformer( ... label='pp_gas_2', ... inputs={bgas: solph.Flow()}, ... outputs={bel: solph.Flow(), bheat: solph.Flow()}, ... conversion_factors={bel: 0.3, bheat: 0.5}) >>> trsf_new.conversion_factors[bgas][3] 1
Notes
- The following sets, variables, constraints and objective parts are created
Optional classes to be added to a network class. This file is part of project oemof (github.com/oemof/oemof). It’s copyrighted by the contributors recorded in the version control history of the file, available from its original location oemof/oemof/solph/options.py
SPDX-License-Identifier: MIT
-
class
oemof.solph.options.
Investment
(maximum=inf, minimum=0, ep_costs=0, existing=0)[source]¶ Bases:
object
Parameters: - maximum (float) – Maximum of the additional invested capacity
- minimum (float) – Minimum of the additional invested capacity
- ep_costs (float) – Equivalent periodical costs for the investment, if period is one year these costs are equal to the equivalent annual costs.
- existing (float) – Existing / installed capacity. The invested capacity is added on top of this value.
-
class
oemof.solph.options.
NonConvex
(**kwargs)[source]¶ Bases:
object
Parameters: - startup_costs (numeric (sequence or scalar)) – Costs associated with a start of the flow (representing a unit).
- shutdown_costs (numeric (sequence or scalar)) – Costs associated with the shutdown of the flow (representing a unit).
- activity_costs (numeric (sequence or scalar)) – Costs associated with the active operation of the flow, independently from the actual output.
- minimum_uptime (numeric (1 or positive integer)) – Minimum time that a flow must be greater then its minimum flow after startup. Be aware that minimum up and downtimes can contradict each other and may lead to infeasible problems.
- minimum_downtime (numeric (1 or positive integer)) – Minimum time a flow is forced to zero after shutting down. Be aware that minimum up and downtimes can contradict each other and may to infeasible problems.
- maximum_startups (numeric (0 or positive integer)) – Maximum number of start-ups.
- maximum_shutdowns (numeric (0 or positive integer)) – Maximum number of shutdowns.
- initial_status (numeric (0 or 1)) – Integer value indicating the status of the flow in the first time step (0 = off, 1 = on). For minimum up and downtimes, the initial status is set for the respective values in the edge regions e.g. if a minimum uptime of four timesteps is defined, the initial status is fixed for the four first and last timesteps of the optimization period. If both, up and downtimes are defined, the initial status is set for the maximum of both e.g. for six timesteps if a minimum downtime of six timesteps is defined in addition to a four timestep minimum uptime.
-
max_up_down
¶ Compute or return the _max_up_down attribute.
Plumbing stuff.
This file is part of project oemof (github.com/oemof/oemof). It’s copyrighted by the contributors recorded in the version control history of the file, available from its original location oemof/oemof/solph/plumbing.py
SPDX-License-Identifier: MIT
-
oemof.solph.plumbing.
sequence
(sequence_or_scalar)[source]¶ Tests if an object is sequence (except string) or scalar and returns a the original sequence if object is a sequence and a ‘emulated’ sequence object of class _Sequence if object is a scalar or string.
Parameters: sequence_or_scalar (array-like, None, int, float) – Examples
>>> sequence([1,2]) [1, 2]
>>> x = sequence(10) >>> x[0] 10
>>> x[10] 10 >>> print(x) [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10]
oemof.tools package¶
This module can be used to check the installation. This is not an illustrated example.
This file is part of project oemof (github.com/oemof/oemof). It’s copyrighted by the contributors recorded in the version control history of the file, available from its original location oemof/tests/test_installation.py
SPDX-License-Identifier: MIT
Module to collect useful functions for economic calculation.
This file is part of project oemof (github.com/oemof/oemof). It’s copyrighted by the contributors recorded in the version control history of the file, available from its original location oemof/oemof/tools/economics.py
SPDX-License-Identifier: MIT
-
oemof.tools.economics.
annuity
(capex, n, wacc, u=None, cost_decrease=0)[source]¶ - Calculates the annuity of an initial investment ‘capex’, considering the
cost of capital ‘wacc’ during a project horizon ‘n’
In case of a single initial investment, the employed formula reads:
annuity = capex cdot
- rac{(wacc cdot (1+wacc)^n)}
- {((1 + wacc)^n - 1)}
In case of repeated investments (due to replacements) at fixed intervals ‘u’, the formula yields:
annuity = capex cdot
- rac{(wacc cdot (1+wacc)^n)}
- {((1 + wacc)^n - 1)} cdot left(
rac{1 - left( rac{(1-cost_decrease)}
{(1+wacc)}- ight)^n}
- {1 - left(
rac{(1-cost_decrease)}{(1+wacc)}
ight)^u} ight)
- capex : float
- Capital expenditure for first investment. Net Present Value (NPV) or Net Present Cost (NPC) of investment
- n : int
- Horizon of the analysis, or number of years the annuity wants to be obtained for (n>=1)
- wacc : float
- Weighted average cost of capital (0<wacc<1)
- u : int
- Lifetime of the investigated investment. Might be smaller than the analysis horizon, ‘n’, meaning it will have to be replaced. Takes value ‘n’ if not specified otherwise (u>=1)
- cost_decrease : float
- Annual rate of cost decrease (due to, e.g., price experience curve). This only influences the result for investments corresponding to replacements, whenever u<n. Takes value 0, if not specified otherwise (0<cost_decrease<1)
- float
- annuity
This is a collection of helper functions which work on their own and can be used by various classes. If there are too many helper-functions, they will be sorted in different modules.
This file is part of project oemof (github.com/oemof/oemof). It’s copyrighted by the contributors recorded in the version control history of the file, available from its original location oemof/oemof/tools/helpers.py
SPDX-License-Identifier: MIT
-
oemof.tools.helpers.
extend_basic_path
(subfolder)[source]¶ Returns a path based on the basic oemof path and creates it if necessary. The subfolder is the name of the path extension.
-
oemof.tools.helpers.
flatten
(d, parent_key='', sep='_')[source]¶ Flatten dictionary by compressing keys.
- See: https://stackoverflow.com/questions/6027558/
- flatten-nested-python-dictionaries-compressing-keys
d : dictionary sep : separator for flattening keys
Returns: Return type: dict
Helpers to log your modeling process with oemof.
This file is part of project oemof (github.com/oemof/oemof). It’s copyrighted by the contributors recorded in the version control history of the file, available from its original location oemof/oemof/tools/logger.py
SPDX-License-Identifier: MIT
-
oemof.tools.logger.
check_git_branch
()[source]¶ Passes the used branch and commit to the logger
The following test reacts on a local system different than on Travis-CI. Therefore, a try/except test is created.
>>> from oemof.tools import logger >>> try: ... v = logger.check_git_branch() ... except FileNotFoundError: ... v = 'dsfafasdfsdf' >>> type(v) <class 'str'>
-
oemof.tools.logger.
check_version
()[source]¶ Returns the actual version number of the used oemof version.
>>> from oemof.tools import logger >>> v = logger.check_version() >>> int(v.split('.')[0]) 0
-
oemof.tools.logger.
define_logging
(logpath=None, logfile='oemof.log', file_format=None, screen_format=None, file_datefmt=None, screen_datefmt=None, screen_level=20, file_level=10, log_version=True, log_path=True, timed_rotating=None)[source]¶ Initialise customisable logger.
Parameters: - logfile (str) – Name of the log file, default: oemof.log
- logpath (str) – The path for log files. By default a “.oemof’ folder is created in your home directory with subfolder called ‘log_files’.
- file_format (str) – Format of the file output. Default: “%(asctime)s - %(levelname)s - %(module)s - %(message)s”
- screen_format (str) – Format of the screen output. Default: “%(asctime)s-%(levelname)s-%(message)s”
- file_datefmt (str) – Format of the datetime in the file output. Default: None
- screen_datefmt (str) – Format of the datetime in the screen output. Default: “%H:%M:%S”
- screen_level (int) – Level of logging to stdout. Default: 20 (logging.INFO)
- file_level (int) – Level of logging to file. Default: 10 (logging.DEBUG)
- log_version (boolean) – If True the actual version or commit is logged while initialising the logger.
- log_path (boolean) – If True the used file path is logged while initialising the logger.
- timed_rotating (dict) – Option to pass parameters to the TimedRotatingFileHandler.
Returns: str
Return type: Place where the log file is stored.
Notes
By default the INFO level is printed on the screen and the DEBUG level in a file, but you can easily configure the logger. Every module that wants to create logging messages has to import the logging module. The oemof logger module has to be imported once to initialise it.
Examples
To define the default logger you have to import the python logging library and this function. The first logging message should be the path where the log file is saved to.
>>> import logging >>> from oemof.tools import logger >>> mypath = logger.define_logging( ... log_path=True, log_version=True, timed_rotating={'backupCount': 4}, ... screen_level=logging.ERROR, screen_datefmt = "no_date") >>> mypath[-9:] 'oemof.log' >>> logging.debug("Hallo")
Submodules¶
oemof.energy_system module¶
Basic EnergySystem class
This file is part of project oemof (github.com/oemof/oemof). It’s copyrighted by the contributors recorded in the version control history of the file, available from its original location oemof/oemof/energy_system.py
SPDX-License-Identifier: MIT
-
class
oemof.energy_system.
EnergySystem
(**kwargs)[source]¶ Bases:
object
Defining an energy supply system to use oemof’s solver libraries.
Note
The list of regions is not necessary to use the energy system with solph.
Parameters: - entities (list of
Entity
, optional) – A list containing the already existingEntities
that should be part of the energy system. Stored in theentities
attribute. Defaults to [] if not supplied. - timeindex (pandas.datetimeindex) – Define the time range and increment for the energy system.
- groupings (list) – The elements of this list are used to construct
Groupings
or they are used directly if they are instances ofGrouping
. These groupings are then used to aggregate the entities added to this energy system intogroups
. By default, there’ll always be one group for eachuid
containing exactly the entity with the givenuid
. See the examples for more information.
-
entities
¶ A list containing the
Entities
that comprise the energy system. If thisEnergySystem
is set as theregistry
attribute, which is done automatically onEnergySystem
construction, newly createdEntities
are automatically added to this list on construction.Type: list of Entity
-
groups
¶ Type: dict
-
results
¶ A dictionary holding the results produced by the energy system. Is None while no results are produced. Currently only set after a call to
optimize()
after which it holds the return value ofom.results()
. See the documentation of that method for a detailed description of the structure of the results dictionary.Type: dictionary
-
timeindex
¶ Define the time range and increment for the energy system. This is an optional attribute but might be import for other functions/methods that use the EnergySystem class as an input parameter.
Type: pandas.index, optional
Examples
Regardles of additional groupings,
entities
will always be grouped by theiruid
:>>> from oemof.network import Entity >>> from oemof.network import Bus, Sink >>> es = EnergySystem() >>> bus = Bus(label='electricity') >>> es.add(bus) >>> bus is es.groups['electricity'] True
For simple user defined groupings, you can just supply a function that computes a key from an
entity
and the resulting groups will be sets ofentities
stored under the returned keys, like in this example, whereentities
are grouped by their type:>>> es = EnergySystem(groupings=[type]) >>> buses = set(Bus(label="Bus {}".format(i)) for i in range(9)) >>> es.add(*buses) >>> components = set(Sink(label="Component {}".format(i)) ... for i in range(9)) >>> es.add(*components) >>> buses == es.groups[Bus] True >>> components == es.groups[Sink] True
-
groups
-
nodes
¶
-
signals
= {<function EnergySystem.add>: <blinker.base.NamedSignal object at 0x7f13133dc7f0; <function EnergySystem.add>>}¶ A dictionary of blinker signals emitted by energy systems.
Currently only one signal is supported. This signal is emitted whenever a Node <oemof.network.Node> is add`ed to an energy system. The signal’s `sender is set to the node <oemof.network.Node> that got added to the energy system so that nodes <oemof.network.Node> have an easy way to only receive signals for when they themselves get added to an energy system.
- entities (list of
oemof.graph module¶
Modules for creating and analysing energy system graphs.
This file is part of project oemof (github.com/oemof/oemof). It’s copyrighted by the contributors recorded in the version control history of the file, available from its original location oemof/oemof/graph.py
SPDX-License-Identifier: MIT
-
oemof.graph.
create_nx_graph
(energy_system=None, remove_nodes=None, filename=None, remove_nodes_with_substrings=None, remove_edges=None)[source]¶ Create a networkx.DiGraph for the passed energy system and plot it. See http://networkx.readthedocs.io/en/latest/ for more information.
Parameters: - energy_system (oemof.solph.network.EnergySystem) –
- filename (str) – Absolute filename (with path) to write your graph in the graphml format. If no filename is given no file will be written.
- remove_nodes (list of strings) – Nodes to be removed e.g. [‘node1’, node2’)]
- remove_nodes_with_substrings (list of strings) – Nodes that contain substrings to be removed e.g. [‘elec’, ‘heat’)]
- remove_edges (list of string tuples) – Edges to be removed e.g. [(‘resource_gas’, ‘gas_balance’)]
Examples
>>> import os >>> import pandas as pd >>> from oemof.solph import (Bus, Sink, Transformer, Flow, EnergySystem) >>> import oemof.graph as grph >>> datetimeindex = pd.date_range('1/1/2017', periods=3, freq='H') >>> es = EnergySystem(timeindex=datetimeindex) >>> b_gas = Bus(label='b_gas', balanced=False) >>> bel1 = Bus(label='bel1') >>> bel2 = Bus(label='bel2') >>> demand_el = Sink(label='demand_el', ... inputs = {bel1: Flow(nominal_value=85, ... actual_value=[0.5, 0.25, 0.75], ... fixed=True)}) >>> pp_gas = Transformer(label=('pp', 'gas'), ... inputs={b_gas: Flow()}, ... outputs={bel1: Flow(nominal_value=41, ... variable_costs=40)}, ... conversion_factors={bel1: 0.5}) >>> line_to2 = Transformer(label='line_to2', ... inputs={bel1: Flow()}, outputs={bel2: Flow()}) >>> line_from2 = Transformer(label='line_from2', ... inputs={bel2: Flow()}, outputs={bel1: Flow()}) >>> es.add(b_gas, bel1, demand_el, pp_gas, bel2, line_to2, line_from2) >>> my_graph = grph.create_nx_graph(es) >>> # export graph as .graphml for programs like Yed where it can be >>> # sorted and customized. this is especially helpful for large graphs >>> # grph.create_nx_graph(es, filename="my_graph.graphml") >>> [my_graph.has_node(n) ... for n in ['b_gas', 'bel1', "('pp', 'gas')", 'demand_el', 'tester']] [True, True, True, True, False] >>> list(nx.attracting_components(my_graph)) [{'demand_el'}] >>> sorted(list(nx.strongly_connected_components(my_graph))[1]) ['bel1', 'bel2', 'line_from2', 'line_to2'] >>> new_graph = grph.create_nx_graph(energy_system=es, ... remove_nodes_with_substrings=['b_'], ... remove_nodes=["('pp', 'gas')"], ... remove_edges=[('bel2', 'line_from2')], ... filename='test_graph') >>> [new_graph.has_node(n) ... for n in ['b_gas', 'bel1', "('pp', 'gas')", 'demand_el', 'tester']] [False, True, False, True, False] >>> my_graph.has_edge("('pp', 'gas')", 'bel1') True >>> new_graph.has_edge('bel2', 'line_from2') False >>> os.remove('test_graph.graphml')
Notes
Needs graphviz and networkx (>= v.1.11) to work properly. Tested on Ubuntu 16.04 x64 and solydxk (debian 9).
oemof.groupings module¶
All you need to create groups of stuff in your energy system.
This file is part of project oemof (github.com/oemof/oemof). It’s copyrighted by the contributors recorded in the version control history of the file, available from its original location oemof/oemof/groupings.py
SPDX-License-Identifier: MIT
-
oemof.groupings.
DEFAULT
= <oemof.groupings.Grouping object>¶ The default
Grouping
.This one is always present in an
energy system
. It stores everyentity
under itsuid
and raises an error if anotherentity
with the sameuid
get’s added to theenergy system
.
-
class
oemof.groupings.
Flows
(key=None, constant_key=None, filter=None, **kwargs)[source]¶ Bases:
oemof.groupings.Nodes
Specialises
Grouping
to group the flows connected tonodes
intosets
. Note that this specifically means that thekey
, andvalue
functions act on a set of flows.
-
class
oemof.groupings.
FlowsWithNodes
(key=None, constant_key=None, filter=None, **kwargs)[source]¶ Bases:
oemof.groupings.Nodes
Specialises
Grouping
to act on the flows connected tonodes
and createsets
of(source, target, flow)
tuples. Note that this specifically means that thekey
, andvalue
functions act on sets like these.
-
class
oemof.groupings.
Grouping
(key=None, constant_key=None, filter=None, **kwargs)[source]¶ Bases:
object
Used to aggregate
entities
in anenergy system
intogroups
.The way
Groupings
work is that eachGrouping
g
of an energy system is called whenever anentity
is added to the energy system (and for eachentity
already present, if the energy system is created with existing enties). The callg(e, groups)
, wheree
is anentity
andgroups
is a dictionary mapping group keys to groups, then uses the three functionskey
,value
andmerge
in the following way:key(e)
is called to obtain a keyk
under which the group should be stored,value(e)
is called to obtain a valuev
(the actual group) to store underk
,- if you supplied a
filter()
argument,v
isfiltered
using that function, - otherwise, if there is not yet anything stored under
groups[k]
,groups[k]
is set tov
. Otherwisemerge
is used to figure out how to mergev
into the old value ofgroups[k]
, i.e.groups[k]
is set tomerge(v, groups[k])
.
Instead of trying to use this class directly, have a look at its subclasses, like
Nodes
, which should cater for most use cases.Notes
When overriding methods using any of the constructor parameters, you don’t have access to
self
in the corresponding function. If you need access toself
, subclassGrouping
and override the methods in the subclass.A
Grouping
may be called more than once on the same objecte
, so one should make sure that user definedGrouping
g
is idempotent, i.e.g(e, g(e, d)) == g(e, d)
.Parameters: - key (callable or hashable) – Specifies (if not callable) or extracts (if callable) a
key
for eachentity
of theenergy system
. - constant_key (hashable (optional)) – Specifies a constant
key
. Keys specified using this parameter are not called but taken as is. - value (callable, optional) – Overrides the default behaviour of
value
. - filter (callable, optional) – If supplied, whatever is returned by
value()
isfiltered
through this. Mostly useful in conjunction with static (i.e. non-callable)keys
. Seefilter()
for more details. - merge (callable, optional) – Overrides the default behaviour of
merge
.
-
filter
(group)[source]¶ Filter
the group returned byvalue()
before storing it.Should return a boolean value. If the
group
returned byvalue()
isiterable
, this function is used (via Python’sbuiltin filter
) to select the values which should be retained ingroup
. Ifgroup
is notiterable
, it is simply called ongroup
itself and the return value decides whethergroup
is stored (True
) or not (False
).
-
key
(node)[source]¶ Obtain a key under which to store the group.
You have to supply this method yourself using the
key
parameter when creatingGrouping
instances.Called for every
node
of the energy system. Expected to return the key (i.e. a validhashable
) under which the groupvalue(node)
will be stored. If it should be added to more than one group, return alist
(or any other non-hashable
,iterable
) containing the group keys.Return
None
if you don’t want to storee
in a group.
-
merge
(new, old)[source]¶ Merge a known
old
group with anew
one.This method is called if there is already a value stored under
group[key(e)]
. In that case,merge(value(e), group[key(e)])
is called and should return the new group to store underkey(e)
.The default behaviour is to raise an error if
new
andold
are not identical.
-
value
(e)[source]¶ Generate the group obtained from
e
.This methd returns the actual group obtained from
e
. Likekey
, it is called for everye
in the energy system. If there is no group stored underkey(e)
,groups[key(e)]
is set tovalue(e)
. Otherwisemerge(value(e), groups[key(e)])
is called.The default returns the
entity
itself.
-
class
oemof.groupings.
Nodes
(key=None, constant_key=None, filter=None, **kwargs)[source]¶ Bases:
oemof.groupings.Grouping
oemof.network module¶
This package (along with its subpackages) contains the classes used to model energy systems. An energy system is modelled as a graph/network of entities with very specific constraints on which types of entities are allowed to be connected.
This file is part of project oemof (github.com/oemof/oemof). It’s copyrighted by the contributors recorded in the version control history of the file, available from its original location oemof/oemof/network.py
SPDX-License-Identifier: MIT
-
class
oemof.network.
Bus
(*args, **kwargs)[source]¶ Bases:
oemof.network.Node
-
class
oemof.network.
Component
(*args, **kwargs)[source]¶ Bases:
oemof.network.Node
-
class
oemof.network.
Edge
(input=None, output=None, flow=None, values=None, **kwargs)[source]¶ Bases:
oemof.network.Node
Bus`es/:class:`Component`s are always connected by an :class:`Edge
.Edge`s connect a single non-:class:`Edge
Node with another. They are directed and have a (sequence of) value(s) attached to them so they can be used to represent a flow from a source/an input to a target/an output.Parameters: - output (input,) –
- values (flow,) – The (list of) object(s) representing the values flowing from this edge’s input into its output. Note that these two names are aliases of each other, so flow and values are mutually exclusive.
- that all of these parameters are also set as attributes with the same (Note) –
- name. –
-
flow
¶
-
classmethod
from_object
(o)[source]¶ Creates an Edge instance from a single object.
This method inspects its argument and does something different depending on various cases:
- If o is an instance of Edge, o is returned unchanged.
- If o is a Mapping, the instance is created by calling klass(**o),
- In all other cases, o will be used as the values keyword argument to `Edge`s constructor.
-
input
¶
-
output
¶
-
class
oemof.network.
EdgeLabel
(input, output)¶ Bases:
tuple
Create new instance of EdgeLabel(input, output)
-
input
¶ Alias for field number 0
-
output
¶ Alias for field number 1
-
-
class
oemof.network.
Entity
(**kwargs)[source]¶ Bases:
object
The most abstract type of vertex in an energy system graph. Since each entity in an energy system has to be uniquely identifiable and connected (either via input or via output) to at least one other entity, these properties are collected here so that they are shared with descendant classes.
Parameters: - uid (string or tuple) – Unique component identifier of the entity.
- inputs (list) – List of Entities acting as input to this Entity.
- outputs (list) – List of Entities acting as output from this Entity.
- geo_data (shapely.geometry object) – Geo-spatial data with informations for location/region-shape. The geometry can be a polygon/multi-polygon for regions, a line fore transport objects or a point for objects such as transformer sources.
-
registry
¶ The central registry keeping track of all
Node's
created. If this is None,Node
instances are not kept track of. Assign anEnergySystem
to this attribute to have it become the anode
registry, i.e. allnodes
created are added to itsnodes
property on construction.Type: EnergySystem
-
optimization_options
= {}¶
-
registry
= None
-
class
oemof.network.
Inputs
(target)[source]¶ Bases:
collections.abc.MutableMapping
A special helper to map n1.inputs[n2] to n2.outputs[n1].
-
class
oemof.network.
Node
(*args, **kwargs)[source]¶ Bases:
object
Represents a Node in an energy system graph.
Abstract superclass of the two general types of nodes of an energy system graph, collecting attributes and operations common to all types of nodes. Users should neither instantiate nor subclass this, but use
Component
,Bus
,Edge
or one of their subclasses instead.Parameters: - label (hashable, optional) – Used as the string representation of this node. If this parameter is
not an instance of
str
it will be converted to a string and the result will be used as this node’slabel
, which should be unique with respect to the other nodes in the energy system graph this node belongs to. If this parameter is not supplied, the string representation of this node will instead be generated based on this nodes class and id. - inputs (list or dict, optional) – Either a list of this nodes’ input nodes or a dictionary mapping input nodes to corresponding inflows (i.e. input values).
- outputs (list or dict, optional) – Either a list of this nodes’ output nodes or a dictionary mapping output nodes to corresponding outflows (i.e. output values).
-
__slots__
¶ See the Python documentation on __slots__ for more information.
Type: str or iterable of str
-
inputs
¶ Dictionary mapping input
Nodes
n
toEdge`s from :obj:`n
intoself
. Ifself
is anEdge
, returns a dict containing theEdge
’s single input node as the key and the flow as the value.Type: dict
-
label
¶ If this node was given a label on construction, this attribute holds the actual object passed as a parameter. Otherwise
node.label
is a synonym forstr(node)
.Type: object
-
outputs
¶ Dictionary mapping output
Nodes
n
toEdges
fromself
inton
. Ifself
is anEdge
, returns a dict containing theEdge
’s single output node as the key and the flow as the value.Type: dict
-
registry
= None¶
- label (hashable, optional) – Used as the string representation of this node. If this parameter is
not an instance of
-
class
oemof.network.
Outputs
(source)[source]¶ Bases:
collections.UserDict
Helper that intercepts modifications to update Inputs symmetrically.
-
class
oemof.network.
Sink
(*args, **kwargs)[source]¶ Bases:
oemof.network.Component
-
class
oemof.network.
Source
(*args, **kwargs)[source]¶ Bases:
oemof.network.Component
-
class
oemof.network.
Transformer
(*args, **kwargs)[source]¶ Bases:
oemof.network.Component