Ab sofort ist der Login auf der Weboberfläche von git.fh-muenster.de bevorzugt über FH Muenster SSO möglich.

Commit a680c8ab authored by Christian Klemm's avatar Christian Klemm

Extensive update to v0.0.4: see changeloggs for details

parent b8652d45
......@@ -23,6 +23,11 @@ from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
import plotly.graph_objects as go
import subprocess
import os
import base64
from dash_canvas import DashCanvas
import io
from PIL import Image
def return_component_value(ID, df_table):
"""Returns data for the graph.
......@@ -66,6 +71,19 @@ dates = df_table["date"].tolist()
list_of_components = df_table.columns.values
list_of_components = list_of_components[1:len(list_of_components)]
#Import Image
graph_png = 'results/graph.gv.png'
test_base64 = base64.b64encode(open(graph_png, 'rb').read()).decode('ascii')
# Sets Canvas width in dependency from the image height
imgdata = base64.b64decode(test_base64)
im = Image.open(io.BytesIO(imgdata))
width, height = im.size
canvas_width = (800/height)*width
if canvas_width > 2300:
canvas_width = 2300
print(height)
print(width)
# loading external resources (stylesheets)
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
......@@ -75,10 +93,15 @@ options = dict(
# Creating the application, which is running the interactive page
demo_app = dash.Dash(__name__, **options)
demo_app.layout = html.Div(
children=[
# Creates the Headline
html.H1(children='Spreadsheet Energy System Model Generator - Interactive Results'),
# Creates the Sub-Headline
html.Div(children='''Summary of the modelling:'''),
#Creates Table 1
......@@ -94,6 +117,16 @@ demo_app.layout = html.Div(
}
}
),
# Includes Image
DashCanvas(id='graph',
tool='line',
lineWidth=5,
#hide_buttons=['line', 'zoom', 'pan'],
lineColor='red',
image_content='data:image/png;base64,{}'.format(test_base64),
width=canvas_width),
# Creates the Sub-Headline
html.Div(
children=[html.Div("-"),
......@@ -165,7 +198,8 @@ demo_app.layout = html.Div(
}
}
}
)
),
]
)
......
......@@ -64,97 +64,95 @@ from oemof.tools import logger
import os
from program_files import (create_objects,
create_results,
create_results,
create_energy_system,
optimize_model)
optimize_model,
create_graph)
# DEFINES PATH OF INPUT DATA
scenario_file = os.path.join(os.path.dirname(__file__),'scenario.xlsx')
scenario_file = os.path.join(os.path.dirname(__file__), 'scenario.xlsx')
# DEFINES PATH OF OUTPUR DATA
result_path = os.path.join(os.path.dirname(__file__)+'/results')
# DEFINES PATH OF OUTPUT DATA
result_path = os.path.join(os.path.dirname(__file__) + '/results')
# DEFINES A LOGGING FILE
log_path = os.path.join(os.path.dirname(__file__)+'/results')
log_path = os.path.join(os.path.dirname(__file__) + '/results')
logger.define_logging(logpath=log_path)
# IMPORTS DATA FROM THE EXCEL FILE AND RETURNS IT AS DICTIONARY
nodes_data = create_energy_system.import_scenario(filepath = scenario_file)
nodes_data = create_energy_system.import_scenario(filepath=scenario_file)
# CREATES AN ENERGYSYSTEM AS DEFINED IN THE SCENARIO FILE
esys = create_energy_system.define_energy_system(filepath = scenario_file,
nodes_data = nodes_data)
esys = create_energy_system.define_energy_system(nodes_data=nodes_data)
# CREATES THE LIST OF COMPONENTS
nodes = []
# CREATES BUS OBJECTS, EXCESS SINKS, AND SHORTAGE SOURCES AS DEFINED IN THE
# SCENARIO FILE AND ADDS THEM TO THE lIST OF COMPONENTS
busd = create_objects.buses(nodes_data = nodes_data,
nodes = nodes)
busd = create_objects.buses(nodes_data=nodes_data,
nodes=nodes)
# CREATES SOURCE OBJECTS AS DEFINED IN THE SCENARIO FILE AND ADDS THEM TO
# THE lIST OF COMPONENTS
create_objects.sources(nodes_data = nodes_data,
nodes = nodes,
bus = busd,
filepath = scenario_file)
create_objects.sources(nodes_data=nodes_data,
nodes=nodes,
bus=busd,
filepath=scenario_file)
# CREATES SINK OBJECTS AS DEFINED IN THE SCENARIO FILE AND ADDS THEM TO
# THE lIST OF COMPONENTS
create_objects.sinks(nodes_data = nodes_data,
bus = busd,
nodes = nodes,
filepath = scenario_file)
create_objects.sinks(nodes_data=nodes_data,
bus=busd,
nodes=nodes,
filepath=scenario_file)
# CREATES TRANSFORMER OBJECTS AS DEFINED IN THE SCENARIO FILE AND ADDS THEM TO
# THE lIST OF COMPONENTS
create_objects.transformers(nodes_data = nodes_data,
bus = busd,
nodes = nodes)
create_objects.transformers(nodes_data=nodes_data,
bus=busd,
nodes=nodes)
# CREATES STORAGE OBJECTS AS DEFINED IN THE SCENARIO FILE AND ADDS THEM TO
# THE lIST OF COMPONENTS
create_objects.storages(nodes_data = nodes_data,
bus = busd,
nodes = nodes)
create_objects.storages(nodes_data=nodes_data,
bus=busd,
nodes=nodes)
# CREATES LINK OBJECTS AS DEFINED IN THE SCENARIO FILE AND ADDS THEM TO
# THE lIST OF COMPONENTS
create_objects.links(nodes_data = nodes_data,
bus = busd,
nodes = nodes)
create_objects.links(nodes_data=nodes_data,
bus=busd,
nodes=nodes)
# ADDS THE COMPONENTS TO THE ENERGYSYSTEM
my_nodes = nodes
esys.add(*my_nodes)
# PRINTS A GRAPH OF THE ENERGY SYSTEM
create_graph.create_graph(filepath=result_path,
nodes_data=nodes_data,
legend=False)
# OPTIMIZES THE ENERGYSYSTEM AND RETURNS THE OPTIMIZED ENERGY SYSTEM
om = optimize_model.least_cost_model(nodes_data = nodes_data,
energy_system = esys)
om = optimize_model.least_cost_model(nodes_data=nodes_data,
energy_system=esys)
# SHOWS AND SAVES RESULTS OF THE OPTIMIZED MODEL / POST-PROCESSING
create_results.xlsx(nodes_data = nodes_data,
optimization_model = om,
energy_system = esys,
create_results.xlsx(nodes_data=nodes_data,
optimization_model=om,
energy_system=esys,
filepath=result_path)
create_results.statistics(nodes_data = nodes_data,
optimization_model = om,
energy_system = esys)
create_results.statistics(nodes_data=nodes_data,
optimization_model=om,
energy_system=esys)
###### PREPARES RESULTS FOR PLOTLY
create_results.prepare_plotly_results(nodes_data = nodes_data,
optimization_model = om,
energy_system = esys,
result_path = result_path)
logging.info(' '+'--------------------------------------------------------')
logging.info(' '+'Modelling and optimization successfully completed!')
create_results.prepare_plotly_results(nodes_data=nodes_data,
optimization_model=om,
energy_system=esys,
result_path=result_path)
logging.info(' ' + '--------------------------------------------------------')
logging.info(' ' + 'Modelling and optimization successfully completed!')
......@@ -136,6 +136,20 @@ listed in the following table.
| snow cover, old | 0.45 - 0.70 |
+---------------------------+--------------------------------+
**Wind Turbines**
For the modelilng of wind turbines, the weather data set must
include wind speeds. The wind speeds must be available for a
measurement height of 10 m in the unit m/s.
The system data of the wind turbine to be modelled are obtained
from the `"oedb" <https://github.com/wind-python/windpowerlib/tree/dev/windpowerlib/oedb>`_
database.
Sinks
^^^^^
......@@ -324,7 +338,7 @@ Installation
- go to the `Python download page <https://www.python.org/downloads/>`_
- chose a Python version (e.g., "Python 3.7.6") and klick "download"
- chose a Python version (e.g., "Python 3.7.6") and click "download"
- download an installer (e.g., "Windows x86-64 executable installer")
- execute the installer on your computer
......@@ -482,7 +496,9 @@ defined if the parameter "technology" is set on "photovoltaic". The following pa
- **comment**: Space for an individual comment, e.g. an indication of which measure this component belongs to.
- **active**: Specifies whether the source shall be included to the model. 0 = inactive, 1 = active.
- **output**: Specifies which bus the source is connected to.
- **technology**: Technology type of source. Input options: "photovoltaic", "wind", "other". Time series are automatically generated for photovoltaic systems and wind turbines. If "other" is selected, a time series must be provided in the "time_series" sheet.
- **technology**: Technology type of source. Input options: "photovoltaic", "windpower", "other". Time series are automatically generated for photovoltaic systems and wind turbines. If "other" is selected, a time series must be provided in the "time_series" sheet.
- **Turbine Model (Windpower ONLY)**: Reference wind turbine model. Possible turbine types are listed `here <https://github.com/wind-python/windpowerlib/blob/dev/windpowerlib/oedb/turbine_data.csv>`_.
- **Hub Height (Windpower ONLY)**: Hub height of the wind turbine. Which hub heights are possible for the selected reference turbine can be viewed `here <https://github.com/wind-python/windpowerlib/blob/dev/windpowerlib/oedb/turbine_data.csv>`_.
- **variable costs/(CU/kWh)**: Defines the variable costs incurred for a kWh of energy drawn from the source.
- **existing capacity/(kW)**: Existing capacity of the source before possible investments.
- **min. investment capacity/(kW)**: Minimum capacity to be installed in case of an investment.
......@@ -791,6 +807,13 @@ or
- **Possible Error Cause**: The column "annual demand" was not filled in correctly for a sink.
- **Debugging**: Make sure to use the "annual demand" column for SLP and Richardson sinks and the "nominal value" column for time series sinks.
..warning::
AttributeError: 'str' object has no attribute 'is_variable_type'
- **Possible Error Cause**: The cost value for an activated excess sink or shortage source was not correctly specified in the bus sheet
- **Debugging**: Make sure that all excess/sortage prices consist of real numbers. Also check for typos.
Your error message is not included? Do not hesitate to contact the developers.
......
......@@ -17,14 +17,14 @@ import warnings
import pandas as pd
from copy import deepcopy
#from windpowerlib import ModelChain as WindpowerlibModelChain
#from windpowerlib import (
# TurbineClusterModelChain as WindpowerlibClusterModelChain,
#)
#from windpowerlib import WindTurbine as WindpowerlibWindTurbine
#from windpowerlib import WindFarm as WindpowerlibWindFarm
#from windpowerlib import WindTurbineCluster as WindpowerlibWindTurbineCluster
#from windpowerlib import get_turbine_types
from windpowerlib import ModelChain as WindpowerlibModelChain
from windpowerlib import (
TurbineClusterModelChain as WindpowerlibClusterModelChain,
)
from windpowerlib import WindTurbine as WindpowerlibWindTurbine
from windpowerlib import WindFarm as WindpowerlibWindFarm
from windpowerlib import WindTurbineCluster as WindpowerlibWindTurbineCluster
from windpowerlib import get_turbine_types
from pvlib.modelchain import ModelChain as PvlibModelChain
from pvlib.pvsystem import PVSystem as PvlibPVSystem
......
This diff is collapsed.
# -*- coding: utf-8 -*-
"""
Power plant classes for specific weather dependent renewable energy resources.
Power plant classes act as data holders for the attributes making up a
power plant's specification. These classes should only contain little logic.
Computing the actual feed-in provided by a power plant is done by the models
(see models.py). The model the feed-in is calculated with is specified in
the `model` attribute.
"""
from abc import ABC, abstractmethod
from feedinlib.models import Pvlib, WindpowerlibTurbine
class Base(ABC):
"""
The base class of feedinlib power plants.
The class mainly serves as a data container for power plant attributes.
Actual calculation of feed-in provided by the power plant is done by the
chosen model. See model.py module for implemented models.
This base class is an abstract class serving as a blueprint for classes
that implement weather dependent renewable energy power plants. It
forces implementors to implement certain properties and methods.
Parameters
----------
model : :class:`~feedinlib.models.Base` subclass or instance
The `model` parameter defines the feed-in model used to calculate
the power plant feed-in.
If a class (or in general, any instance of :class:`type`) is
provided, it is used to create the model instance encapsulating the
actual mathematical model used to calculate the feed-in provided by
this power plant.
In any other case, the provided object is used directly. Note
though, that a reference to this power plant is saved in the
provided object, so sharing model instances between two power plant
objects is not a good idea, as the second power plant will
overwrite the reference to the first.
The non-class version is only provided for users who need the extra
flexibility of controlling model instantiation and who know what
they are doing. In general, you'll want to provide a class for this
parameter or just go with the default for the specific subclass you
are using.
**attributes :
Besides `model` parameter provided attributes hold the technical
specification used to define the power plant. See
`power_plant_parameters` parameter in respective model's
:meth:`feedin` method for further information on the model's
required and optional plant parameters.
Raises
------
AttributeError
In case an attribute listed in the given model's required
parameters is not present in the `parameters` parameter.
"""
def __init__(self, **attributes):
"""
"""
model = attributes.pop("model")
if isinstance(model, type):
model = model(**attributes)
self.model = model
self.parameters = attributes
# check if all power plant attributes required by the respective model
# are provided
self._check_models_power_plant_requirements(attributes.keys())
@abstractmethod
def feedin(self, weather, **kwargs):
"""
Calculates power plant feed-in in Watt.
This method delegates the actual computation to the model's
:meth:`feedin` method while giving you the opportunity to override
some of the inputs used to calculate the feed-in.
If the respective model does calculate AC and DC feed-in, AC feed-in
is returned by default. See the model's :meth:`feedin` method for
information on how to overwrite this default behaviour.
Parameters
----------
weather :
Weather data to calculate feed-in. Check the `weather` parameter
of the respective model's :meth:`feedin` method for required
weather data parameters and format.
**kwargs :
Keyword arguments for respective model's feed-in calculation.
Check the keyword arguments of the model's :meth:`feedin` for
further information.
Returns
-------
feedin : :pandas:`pandas.Series<series>`
Series with power plant feed-in in Watt.
"""
model = kwargs.pop("model", self.model)
# in case a different model used to calculate feed-in than originally
# assigned is given, self.model is overwritten and required power plant
# parameters for new model are checked
if not model == self.model:
model = model(**self.parameters)
self.model = model
self._check_models_power_plant_requirements(self.parameters.keys())
# check if all arguments required by the feed-in model are given
keys = kwargs.keys()
for k in model.requires:
if not k in keys:
raise AttributeError(
"The specified model '{model}' requires model "
"parameter '{k}' but it's not provided as an "
"argument.".format(k=k, model=model)
)
# call respective model's feed-in method
return model.feedin(
weather=weather, power_plant_parameters=self.parameters, **kwargs
)
def _check_models_power_plant_requirements(self, parameters):
"""
Checks if given model's required power plant parameters are provided.
An error is raised if the attributes required by the given model are
not contained in the provided parameters in `parameters`.
Parameters
-----------
parameters : list(str)
List of provided power plant parameters.
Raises
------
AttributeError
In case an attribute listed in the given model's required
parameters is not present in the `parameters` parameter.
"""
try:
# call the given model's check function if implemented
self.model._power_plant_requires_check(parameters)
except NotImplementedError:
for k in self.required:
if k not in parameters:
raise AttributeError(
"The specified model '{model}' requires power plant "
"parameter '{k}' but it's not provided as an "
"argument.".format(k=k, model=self.model)
)
@property
def required(self):
"""
The power plant parameters the specified model requires.
Check the model's :attr:`power_plant_requires` attribute for further
information.
"""
return self.model.power_plant_requires
class Photovoltaic(Base):
"""
Class to define a standard set of PV system attributes.
The Photovoltaic class serves as a data container for PV system attributes.
Actual calculation of feed-in provided by the PV system is done by the
chosen PV model. So far there is only one PV model,
:class:`~.models.Pvlib`.
Parameters
----------
model : A subclass or instance of subclass of \
:class:`~.models.PhotovoltaicModelBase`
The `model` parameter defines the feed-in model used to calculate
the PV system feed-in. It defaults to
:class:`~feedinlib.models.Pvlib` which is currently the only
implemented photovoltaic model.
`model` is used as the `model` parameter for :class:`Base`.
**attributes :
PV system parameters. See `power_plant_parameters` parameter
in respective model's :func:`feedin` method for further
information on the model's required and optional plant parameters.
As the :class:`~.models.Pvlib` model is currently the only
implemented photovoltaic model see `power_plant_parameters` parameter
:meth:`~.models.Pvlib.feedin` for further information.
"""
def __init__(self, model=Pvlib, **attributes):
"""
"""
super().__init__(model=model, **attributes)
def feedin(self, weather, scaling=None, **kwargs):
"""
Calculates PV system feed-in in Watt.
The feed-in can further be scaled by PV system area or peak power using
the `scaling` parameter.
This method delegates the actual computation to the model's
:meth:`feedin` method while giving you the opportunity to override
some of the inputs used to calculate the feed-in. As the
:class:`~.models.Pvlib` model is currently the only
implemented photovoltaic model see
:meth:`~.models.Pvlib.feedin` for further information on
feed-in calculation.
If the respective model does calculate AC and DC feed-in, AC feed-in
is returned by default. See the model's :meth:`feedin` method for
information on how to overwrite this default behaviour.
Parameters
----------
weather :
Weather data to calculate feed-in. Check the `weather` parameter
of the respective model's :meth:`feedin` method for required
weather data parameters and format.
scaling : str
Specifies what feed-in is scaled by. Possible options are
'peak_power' and 'area'. Defaults to None in which case feed-in is
not scaled.
**kwargs
Keyword arguments for respective model's feed-in calculation.
Check the keyword arguments of the model's :meth:`feedin` method
for further information.
Returns
-------
:pandas:`pandas.Series<series>`
Series with PV system feed-in in Watt.
"""
# delegate feed-in calculation
feedin = super().feedin(weather=weather, **kwargs)
# scale feed-in
if scaling:
feedin_scaling = {
"peak_power": lambda feedin: feedin / float(self.peak_power),
"area": lambda feedin: feedin / float(self.area),
}
return feedin_scaling[scaling](feedin)
return feedin
@property
def area(self):
"""
Area of PV system in :math:`m^2`.
See :attr:`pv_system_area` attribute of your chosen model for further
information on how the area is calculated.
"""
return self.model.pv_system_area
@property
def peak_power(self):
"""
Peak power of PV system in Watt.
See :attr:`pv_system_peak_power` attribute of your chosen model for
further information and specifications on how the peak power is
calculated.
"""
return self.model.pv_system_peak_power
class WindPowerPlant(Base):
"""
Class to define a standard set of wind power plant attributes.
The WindPowerPlant class serves as a data container for wind power plant
attributes. Actual calculation of feed-in provided by the wind power plant
is done by the chosen wind power model. So far there are two wind power
models, :class:`~.models.WindpowerlibTurbine` and
:class:`~.models.WindpowerlibTurbineCluster`. The
:class:`~.models.WindpowerlibTurbine` model should be used for
single wind turbines, whereas the
:class:`~.models.WindpowerlibTurbineCluster` model can be used
for wind farm and wind turbine cluster calculations.
Parameters
----------
model : A subclass or instance of subclass of \
:class:`feedinlib.models.WindpowerModelBase`
The `model` parameter defines the feed-in model used to calculate
the wind power plant feed-in. It defaults to
:class:`~.models.WindpowerlibTurbine`.
`model` is used as the `model` parameter for :class:`Base`.
**attributes :
Wind power plant parameters. See `power_plant_parameters` parameter
in respective model's :meth:`feedin` method for further
information on the model's required and optional plant parameters.
"""
def __init__(self, model=WindpowerlibTurbine, **attributes):