# -*- coding: utf-8 -*-
"""
Created on Mon Jul 20 15:53:14 2015
@author: uwe
"""
import pickle
import logging
import os
from oemof.core.network import Entity
from oemof.core.network.entities.components import transports as transport
from oemof.solph.optimization_model import OptimizationModel as OM
[docs]class EnergySystem:
r"""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 :class:`Entity <oemof.core.network.Entity>`, optional
A list containing the already existing :class:`Entities
<oemof.core.network.Entity>` that should be part of the energy system.
Stored in the :attr:`entities` attribute.
Defaults to `[]` if not supplied.
simulation : core.energy_system.Simulation object
Simulation object that contains all necessary attributes to start the
solver library. Defined in the :py:class:`Simulation
<oemof.core.energy_system.Simulation>` class.
regions : list of core.energy_system.Region objects
List of regions defined in the :py:class:`Region
<oemof.core.energy_system.Simulation>` class.
year : integer
Define the time for the energy system.
Attributes
----------
entities : list of :class:`Entity <oemof.core.network.Entity>`
A list containing the :class:`Entities <oemof.core.network.Entity>`
that comprise the energy system. If this :class:`EnergySystem` is
set as the :attr:`registry <oemof.core.network.Entity.registry>`
attribute, which is done automatically on :class:`EnergySystem`
construction, newly created :class:`Entities
<oemof.core.network.Entity>` are automatically added to this list on
construction.
simulation : core.energy_system.Simulation object
Simulation object that contains all necessary attributes to start the
solver library. Defined in the :py:class:`Simulation
<oemof.core.energy_system.Simulation>` class.
regions : list of core.energy_system.Region objects
List of regions defined in the :py:class:`Region
<oemof.core.energy_system.Simulation>` class.
results : dictionary
A dictionary holding the results produced by the energy system.
Is `None` while no results are produced.
Currently only set after a call to :meth:`optimize` after which it
holds the return value of :meth:`om.results()
<oemof.solph.optimization_model.OptimizationModel.results>`.
"""
def __init__(self, **kwargs):
for attribute in ['regions', 'entities', 'simulation']:
setattr(self, attribute, kwargs.get(attribute, []))
Entity.registry = self
self.results = None
self.year = kwargs.get('year')
# TODO: Condense signature (use Buse)
[docs] def connect(self, bus1, bus2, in_max, out_max, eta, transport_class):
"""Create two transport objects to connect two buses of the same type
in both directions.
Parameters
----------
bus1, bus2 : core.network.Bus object
Two buses to be connected.
eta : float
Constant efficiency of the transport.
in_max : float
Maximum input the transport can handle, in $MW$.
out_max : float
Maximum output which can possibly be obtained when using the
transport, in $MW$.
transport_class class
Transport class to use for the connection
"""
if not transport_class == transport.Simple:
logging.error('')
raise(TypeError(
"Sorry, `EnergySystem.connect` currently only works with" +
"a `transport_class` argument of" + str(transport.Simple)))
for bus_a, bus_b in [(bus1, bus2), (bus2, bus1)]:
uid = ('transport',) + bus_a.uid + bus_b.uid
transport_class(uid=uid, outputs=[bus_a], inputs=[bus_b],
out_max=[out_max], in_max=[in_max], eta=[eta])
# TODO: Add concept to make it possible to use another solver library.
[docs] def optimize(self, om=None):
"""Start optimizing the energy system using solph.
Parameters
----------
om : :class:`OptimizationModel <oemof.solph.optimization_model.OptimizationModel>`, optional
The optimization model used to optimize the :class:`EnergySystem`.
If not given, an :class:`OptimizationModel
<oemof.solph.optimization_model.OptimizationModel>` instance local
to this method is created using the current :class:`EnergySystem`
instance as an argument.
You only need to supply this if you want to observe any side
effects that solving has on the `om`.
Returns
-------
self : :class:`EnergySystem`
"""
if om is None:
om = OM(energysystem=self)
om.solve(solver=self.simulation.solver, debug=self.simulation.debug,
tee=self.simulation.stream_solver_output,
duals=self.simulation.duals)
self.results = om.results()
return self
[docs] def dump(self, dpath=None, filename=None, keep_weather=True):
r""" Dump an EnergySystem instance.
"""
if dpath is None:
bpath = os.path.join(os.path.expanduser("~"), '.oemof')
if not os.path.isdir(bpath):
os.mkdir(bpath)
dpath = os.path.join(bpath, 'dumps')
if not os.path.isdir(dpath):
os.mkdir(dpath)
if filename is None:
filename = 'es_dump.oemof'
pickle.dump(self.__dict__, open(os.path.join(dpath, filename), 'wb'))
msg = ('Attributes dumped to: {0}'.format(os.path.join(
dpath, filename)))
logging.debug(msg)
return msg
[docs] def restore(self, dpath=None, filename=None):
r""" Restore an EnergySystem instance.
"""
logging.info(
"Restoring attributes will overwrite existing attributes.")
if dpath is None:
dpath = os.path.join(os.path.expanduser("~"), '.oemof', 'dumps')
if filename is None:
filename = 'es_dump.oemof'
self.__dict__ = pickle.load(open(os.path.join(dpath, filename), "rb"))
msg = ('Attributes restored from: {0}'.format(os.path.join(
dpath, filename)))
logging.debug(msg)
return msg
[docs]class Region:
r"""Defining a region within an energy supply system.
Note
----
The list of regions is not necessary to use the energy system with solph.
Parameters
----------
entities : list of core.network objects
List of all objects of the energy system. All class descriptions can
be found in the :py:mod:`oemof.core.network` package.
name : string
A unique name to identify the region. If possible use typical names for
regions and english names for countries.
code : string
A short unique name to identify the region.
geom : shapely.geometry object
The geometry representing the region must be a polygon or a multi
polygon.
Attributes
----------
entities : list of core.network objects
List of all objects of the energy system. All class descriptions can
be found in the :py:mod:`oemof.core.network` package.
name : string
A unique name to identify the region. If possible use typical names for
regions and english names for countries.
geom : shapely.geometry object
The geometry representing the region must be a polygon or a multi
polygon.
"""
def __init__(self, **kwargs):
self.entities = [] # list of entities
self.add_entities(kwargs.get('entities', []))
self.name = kwargs.get('name')
self.geom = kwargs.get('geom')
self._code = kwargs.get('code')
# TODO: oder sollte das ein setter sein? Yupp.
[docs] def add_entities(self, entities):
"""Add a list of entities to the existing list of entities.
For every entity added to a region the region attribute of the entity
is set
Parameters
----------
entities : list of core.network objects
List of all objects of the energy system that belongs to area
covered by the polygon of the region. All class descriptions can
be found in the :py:mod:`oemof.core.network` package.
"""
# TODO: prevent duplicate entries
self.entities.extend(entities)
for entity in entities:
if self not in entity.regions:
entity.regions.append(self)
@property
def code(self):
"""Creating a short code based on the region name if no code is set."""
if self._code is None:
name_parts = self.name.replace('_', ' ').split(' ', 1)
self._code = ''
for part in name_parts:
self._code += part[:1].upper() + part[1:3]
return self._code
[docs]class Simulation:
r"""Defining the simulation related parameters according to the solver lib.
Parameters
----------
solver : string
Name of the solver supported by the used solver library.
(e.g. 'glpk', 'gurobi')
debug : boolean
Set the chosen solver to debug (verbose) mode to get more information.
stream_solver_output : boolean
If True, solver output is streamed in python console
duals : boolean
If True, results of dual variables and reduced costs will be saved
objective_options : dictionary
'function': function to use from
:py:mod:`oemof.solph.predefined_objectives`
'cost_objects': list of str(`class`) elements. Objects of type `class`
are include in cost terms of objective function.
'revenue_objects': list of str(`class`) elements. . Objects of type
`class` are include in revenue terms of
objective function.
timesteps : list or sequence object
Timesteps to be simulated or optimized in the used library
relaxed : boolean
If True, integer variables will be relaxed
(only relevant for milp-problems)
"""
def __init__(self, **kwargs):
''
self.solver = kwargs.get('solver', 'glpk')
self.debug = kwargs.get('debug', False)
self.stream_solver_output = kwargs.get('stream_solver_output', False)
self.objective_options = kwargs.get('objective_options', {})
self.duals = kwargs.get('duals', False)
self.timesteps = kwargs.get('timesteps')
self.relaxed = kwargs.get('relaxed', False)
if self.timesteps is None:
raise ValueError('No timesteps defined!')