Using oemof.tabular.facades

This ipython-notebook is designed to describe the usage and functionality of the facades that are based on the oemof.solph package. If you are scripting and writing your own model you can easily use the classes provided by solph. The main potential of the facades is to provide an easy interface when defining the energy system in a input datapackage or any other tabular source. To see how you can do this have a look at the model-from-tabular-data.ipynb example.

NOTE: Numeric values in this example are not representative for any energy system and just randomly selected. Also this model is not necessarily a feasible optimization problem. It is only designed for illustration of class usage.

Author:

Simon.Hilpert (@uni-flensburg.de), Europa Universitaet Flensburg, March 2019

Python Imports

[1]:
import pandas as pd

from oemof.solph import EnergySystem, Model, Bus
import oemof.tabular.facades as fc

# for simplicity we just use 3 timesteps
es = EnergySystem(timeindex=pd.date_range('2018', periods=3, freq='H'))

Bus

First we will create the required buses for this example. As these objects will be used when instantiating the components, we assign them to python variables and add these to the energy system object es using ist .add() method. The balanced (default: True) argument can be used to relax the energy balance for a bus, i.e. all inputs do not have to sum with all output flows to zero any longer.

[2]:
elec_bus = Bus(label="elec_bus")
elec_bus_1 = Bus(label="elec_bus_1")
heat_bus = Bus(label="heat_bus")
fuel_bus = Bus(label="fuel_bus", balanced=True)

es.add(elec_bus, elec_bus_1, heat_bus, fuel_bus)

Volatile

This class can be used to model PV oder Wind power plants. The equations for this component are:

\[x_{wind}^{flow} = c_{wind}^{capacity} \cdot c_{wind}^{profile}(t) \qquad \forall t \in T\]

Where \(x_{wind}^{flow}\) denotes the production (endogenous variable) of the volatile object to the bus.

[3]:
es.add(
    fc.Volatile(
        label="wind",
        carrier="wind",
        tech="onshore",
        capacity=150,
        bus=elec_bus,
        profile=[0.2, 0.3, 0.25],
    )
)

Volatile component investment

If the investment mode is used, i.e. capacity_cost attribute not None and capacity attribute set to None the right hand side of the equation changes as the exogenous variable \(c^{capacity}\) is now replaced by an endognous variable.

\[x^{f,ow}_{wind} \leq x^{capacity}_{wind} \cdot c^{profile}_{wind}(t) \qquad \forall t \in T\]
[4]:
es.add(
    fc.Volatile(
        label="wind_invest",
        carrier="wind",
        tech="onshore",
        capacity_cost=200,
        exapandable=True,
        bus=elec_bus,
        profile=[0.2, 0.3, 0.25],
    )
)

Dispatchable

The Dispatchble component works very similar to the volatile component. The only difference here is the \(\leq\) sign in the constraint, which allows to dispatch the power within the limit of the lower and upper bounds.

\[x_{flow}^{elec\_bus} \leq c_{ccgt}^{capacity} \cdot c_{ccgt}^{profile}(t) \qquad \forall t \in T\]

NOTE:

You can also set the parameters for the output of this component by using the argument output_parameters. To see all options see: oemof Flow

[5]:
es.add(
    fc.Dispatchable(
        bus=elec_bus,
        label="ccgt",
        carrier="gas",
        tech="ccgt",
        capacity=100,
        marginal_cost=25,
        output_parameters={
            'summed_min': 1000,
            'summed_max': 2000}
    )
)

Reservoir

The reservoir component inherit from the GenericStorage. However the input of the reservoir is not taken from the bus but defined as an absolute inflow to the reservoir from a source. This source object is created under the hood interanally when a Reservoir object is instantiated.

[6]:
es.add(
    fc.Reservoir(
        bus=elec_bus,
        label="rsv",
        carrier="hydro",
        tech="reservoir",
        capacity=150,
        storage_capacity=1500,
        efficiency=0.9,
        profile=[10, 5, 3],
        initial_storage_level=1, # oemof.solph arguments
        balanced=False # oemof.solph argument
    )
)

Add the subnodes of the Reservoir to energy system (hopefully soon obsolete). When reading from datapackage resource this is not necessary as the datapackage reader takes care of this.

[7]:
es.add(*es.groups['rsv'].subnodes)

Storage

The Storage component is based on the GenericStorage class of oemof.solph. Therefore you may use all arguments that exist for the parent class in addition to the ones defined for the component itself.

[8]:
es.add(
    fc.Storage(
        label="storage",
        bus=elec_bus,
        carrier="lithium",
        tech="battery",
        capacity_cost=10,
        expandable=True,
        invest_relation_output_capacity=1/6, # oemof.solph
        marginal_cost=5,
        balanced=True, # oemof.solph argument
        initial_storage_level=1, # oemof.solph argument
        max_storage_level=[0.9, 0.95, 0.8], # oemof.solph argument
    )
)

Extraction and Backpressure Turbine

The extraction turbine facade directly inherit from the ExtractionTurbineCHP class where as the backpressure facade directly inherit from the Transformer class.

Both components may also be used in the investment mode by setting the capacity costs. The capacity cost are related to the electrical output of the component, i.e. Euro/MWhel.

[9]:
es.add(
    fc.ExtractionTurbine(
        label="ext",
        electricity_bus=elec_bus,
        heat_bus=heat_bus,
        fuel_bus=fuel_bus,
        carrier='gas',
        tech='ext',
        capacity=10,
        condensing_efficiency=0.5,
        electric_efficiency=0.4,
        thermal_efficiency=0.3
    )
)

es.add(
    fc.BackpressureTurbine(
        label="bp",
        electricity_bus=elec_bus,
        heat_bus=heat_bus,
        fuel_bus=fuel_bus,
        carrier='gas',
        tech='bp',
        capacity=10,
        electric_efficiency=0.4,
        thermal_efficiency=0.3
    )
)

Conversion

The conversion component is a simplified interface to the Transformer class with the restriction of 1 input and 1 output.

[10]:
es.add(
    fc.Conversion(
        label='pth',
        from_bus=elec_bus,
        to_bus=heat_bus,
        carrier='electricity',
        tech='hp',
        capacity=10,
        capacity_cost=54,
        expandable=True,
        capacity_potential=20,
        efficiency=0.9,
        thermal_efficiency=0.3
    )
)

Commodity

A commodity can be used to model a limited source for the complete time horizon of the problem.

\[\sum_t x^{flow}_{fuel}(t) \leq c^{amount}_{fuel}\]
[11]:
es.add(
    fc.Commodity(
        label='fuel',
        bus=fuel_bus,
        amount=1000000,
        carrier='fuel',
        tech='commodity',
        marginal_cost=0.5
    )
)

Load

The load is similar to the Volatile component, except that it acts as a sink (1-input). Therefore you may also use the argument input_parameters to adapt its behaviour.

[13]:
es.add(
    fc.Load(
        label="elec_load",
        bus=elec_bus,
        amount=500e3,
        profile=[0.4, 0.1, 0.5]))

es.add(
    fc.Load(
        label="heat_load",
        bus=heat_bus,
        amount=200e3,
        profile=[0.1, 0.23, 0.7]))

Adding Other oemof.solph Components

As the energy system and all components are solph based objects you may add any other oemof.solph object to your energy system. This is straight forward when you are scripting. The full levearge of the facades is however gained when using the datapackage reader. For this datapackage reader only facades are working in a proper way.

Create Model and Inspect

[14]:
m = Model(es)

# uncommet to get lp-file (path will be of this file)
# m.write(io_options={'symbolic_solver_labels': True})

WARNING: Element rsv-inflow already exists in set NODES; no action taken.
[15]:
m.InvestmentFlow.pprint()
InvestmentFlow : Size=1, Index=None, Active=True
    8 Set Declarations
        FIXED_FLOWS : Dim=0, Dimen=1, Size=0, Domain=None, Ordered=False, Bounds=(None, None)
            []
        FLOWS : Dim=0, Dimen=2, Size=3, Domain=None, Ordered=False, Bounds=None
            [("<oemof.solph.network.Bus: 'elec_bus'>", "<oemof.tabular.facades.Storage: 'storage'>"), ("<oemof.tabular.facades.Conversion: 'pth'>", "<oemof.solph.network.Bus: 'heat_bus'>"), ("<oemof.tabular.facades.Storage: 'storage'>", "<oemof.solph.network.Bus: 'elec_bus'>")]
        MIN_FLOWS : Dim=0, Dimen=1, Size=0, Domain=None, Ordered=False, Bounds=(None, None)
            []
        SUMMED_MAX_FLOWS : Dim=0, Dimen=1, Size=0, Domain=None, Ordered=False, Bounds=(None, None)
            []
        SUMMED_MIN_FLOWS : Dim=0, Dimen=1, Size=0, Domain=None, Ordered=False, Bounds=(None, None)
            []
        fixed_index : Dim=0, Dimen=2, Size=0, Domain=None, Ordered=False, Bounds=None
            Virtual
        max_index : Dim=0, Dimen=3, Size=9, Domain=None, Ordered=False, Bounds=None
            Virtual
        min_index : Dim=0, Dimen=2, Size=0, Domain=None, Ordered=False, Bounds=None
            Virtual

    1 Var Declarations
        invest : Size=3, Index=InvestmentFlow.FLOWS
            Key                                                                                     : Lower : Value : Upper : Fixed : Stale : Domain
            ("<oemof.solph.network.Bus: 'elec_bus'>", "<oemof.tabular.facades.Storage: 'storage'>") :     0 :  None :   inf : False :  True : NonNegativeReals
             ("<oemof.tabular.facades.Conversion: 'pth'>", "<oemof.solph.network.Bus: 'heat_bus'>") :     0 :  None :    20 : False :  True : NonNegativeReals
            ("<oemof.tabular.facades.Storage: 'storage'>", "<oemof.solph.network.Bus: 'elec_bus'>") :     0 :  None :   inf : False :  True : NonNegativeReals

    1 Expression Declarations
        investment_costs : Size=1, Index=None
            Key  : Expression
            None : 54*InvestmentFlow.invest[pth,heat_bus] + 10*InvestmentFlow.invest[elec_bus,storage]

    5 Constraint Declarations
        fixed : Size=0, Index=InvestmentFlow.fixed_index, Active=True
            Key : Lower : Body : Upper : Active
        max : Size=9, Index=InvestmentFlow.max_index, Active=True
            Key                                                                                        : Lower : Body                                                               : Upper : Active
            ("<oemof.solph.network.Bus: 'elec_bus'>", "<oemof.tabular.facades.Storage: 'storage'>", 0) :  -Inf : flow[elec_bus,storage,0] - InvestmentFlow.invest[elec_bus,storage] :   0.0 :   True
            ("<oemof.solph.network.Bus: 'elec_bus'>", "<oemof.tabular.facades.Storage: 'storage'>", 1) :  -Inf : flow[elec_bus,storage,1] - InvestmentFlow.invest[elec_bus,storage] :   0.0 :   True
            ("<oemof.solph.network.Bus: 'elec_bus'>", "<oemof.tabular.facades.Storage: 'storage'>", 2) :  -Inf : flow[elec_bus,storage,2] - InvestmentFlow.invest[elec_bus,storage] :   0.0 :   True
             ("<oemof.tabular.facades.Conversion: 'pth'>", "<oemof.solph.network.Bus: 'heat_bus'>", 0) :  -Inf :  flow[pth,heat_bus,0] - (10 + InvestmentFlow.invest[pth,heat_bus]) :   0.0 :   True
             ("<oemof.tabular.facades.Conversion: 'pth'>", "<oemof.solph.network.Bus: 'heat_bus'>", 1) :  -Inf :  flow[pth,heat_bus,1] - (10 + InvestmentFlow.invest[pth,heat_bus]) :   0.0 :   True
             ("<oemof.tabular.facades.Conversion: 'pth'>", "<oemof.solph.network.Bus: 'heat_bus'>", 2) :  -Inf :  flow[pth,heat_bus,2] - (10 + InvestmentFlow.invest[pth,heat_bus]) :   0.0 :   True
            ("<oemof.tabular.facades.Storage: 'storage'>", "<oemof.solph.network.Bus: 'elec_bus'>", 0) :  -Inf : flow[storage,elec_bus,0] - InvestmentFlow.invest[storage,elec_bus] :   0.0 :   True
            ("<oemof.tabular.facades.Storage: 'storage'>", "<oemof.solph.network.Bus: 'elec_bus'>", 1) :  -Inf : flow[storage,elec_bus,1] - InvestmentFlow.invest[storage,elec_bus] :   0.0 :   True
            ("<oemof.tabular.facades.Storage: 'storage'>", "<oemof.solph.network.Bus: 'elec_bus'>", 2) :  -Inf : flow[storage,elec_bus,2] - InvestmentFlow.invest[storage,elec_bus] :   0.0 :   True
        min : Size=0, Index=InvestmentFlow.min_index, Active=True
            Key : Lower : Body : Upper : Active
        summed_max : Size=0, Index=InvestmentFlow.SUMMED_MAX_FLOWS, Active=True
            Key : Lower : Body : Upper : Active
        summed_min : Size=0, Index=InvestmentFlow.SUMMED_MIN_FLOWS, Active=True
            Key : Lower : Body : Upper : Active

    15 Declarations: FLOWS FIXED_FLOWS SUMMED_MAX_FLOWS SUMMED_MIN_FLOWS MIN_FLOWS invest fixed_index fixed max_index max min_index min summed_max summed_min investment_costs
[16]:
m.Flow.pprint()
Flow : Size=1, Index=None, Active=True
    11 Set Declarations
        INTEGER_FLOWS : Dim=0, Dimen=1, Size=0, Domain=None, Ordered=False, Bounds=(None, None)
            []
        NEGATIVE_GRADIENT_FLOWS : Dim=0, Dimen=1, Size=0, Domain=None, Ordered=False, Bounds=(None, None)
            []
        POSITIVE_GRADIENT_FLOWS : Dim=0, Dimen=1, Size=0, Domain=None, Ordered=False, Bounds=(None, None)
            []
        SUMMED_MAX_FLOWS : Dim=0, Dimen=2, Size=2, Domain=None, Ordered=False, Bounds=None
            [("<oemof.tabular.facades.Dispatchable: 'ccgt'>", "<oemof.solph.network.Bus: 'elec_bus'>"), ("<oemof.tabular.facades.Commodity: 'fuel'>", "<oemof.solph.network.Bus: 'fuel_bus'>")]
        SUMMED_MIN_FLOWS : Dim=0, Dimen=2, Size=1, Domain=None, Ordered=False, Bounds=None
            [("<oemof.tabular.facades.Dispatchable: 'ccgt'>", "<oemof.solph.network.Bus: 'elec_bus'>")]
        integer_flow_constr_index : Dim=0, Dimen=2, Size=0, Domain=None, Ordered=False, Bounds=None
            Virtual
        integer_flow_index : Dim=0, Dimen=2, Size=0, Domain=None, Ordered=False, Bounds=None
            Virtual
        negative_gradient_constr_index : Dim=0, Dimen=2, Size=0, Domain=None, Ordered=False, Bounds=None
            Virtual
        negative_gradient_index : Dim=0, Dimen=2, Size=0, Domain=None, Ordered=False, Bounds=None
            Virtual
        positive_gradient_constr_index : Dim=0, Dimen=2, Size=0, Domain=None, Ordered=False, Bounds=None
            Virtual
        positive_gradient_index : Dim=0, Dimen=2, Size=0, Domain=None, Ordered=False, Bounds=None
            Virtual

    3 Var Declarations
        integer_flow : Size=0, Index=Flow.integer_flow_index
            Key : Lower : Value : Upper : Fixed : Stale : Domain
        negative_gradient : Size=0, Index=Flow.negative_gradient_index
            Key : Lower : Value : Upper : Fixed : Stale : Domain
        positive_gradient : Size=0, Index=Flow.positive_gradient_index
            Key : Lower : Value : Upper : Fixed : Stale : Domain

    5 Constraint Declarations
        integer_flow_constr : Size=0, Index=Flow.integer_flow_constr_index, Active=True
            Key : Lower : Body : Upper : Active
        negative_gradient_constr : Size=0, Index=Flow.negative_gradient_constr_index, Active=True
            Key : Lower : Body : Upper : Active
        positive_gradient_constr : Size=0, Index=Flow.positive_gradient_constr_index, Active=True
            Key : Lower : Body : Upper : Active
        summed_max : Size=2, Index=Flow.SUMMED_MAX_FLOWS, Active=True
            Key                                                                                       : Lower : Body                                                                  : Upper     : Active
            ("<oemof.tabular.facades.Dispatchable: 'ccgt'>", "<oemof.solph.network.Bus: 'elec_bus'>") :  -Inf : flow[ccgt,elec_bus,0] + flow[ccgt,elec_bus,1] + flow[ccgt,elec_bus,2] :  200000.0 :   True
               ("<oemof.tabular.facades.Commodity: 'fuel'>", "<oemof.solph.network.Bus: 'fuel_bus'>") :  -Inf : flow[fuel,fuel_bus,0] + flow[fuel,fuel_bus,1] + flow[fuel,fuel_bus,2] : 1000000.0 :   True
        summed_min : Size=1, Index=Flow.SUMMED_MIN_FLOWS, Active=True
            Key                                                                                       : Lower    : Body                                                                  : Upper : Active
            ("<oemof.tabular.facades.Dispatchable: 'ccgt'>", "<oemof.solph.network.Bus: 'elec_bus'>") : 100000.0 : flow[ccgt,elec_bus,0] + flow[ccgt,elec_bus,1] + flow[ccgt,elec_bus,2] :  +Inf :   True

    4 BuildAction Declarations
        negative_gradient_build : Size=0, Index=None, Active=True
        positive_gradient_build : Size=0, Index=None, Active=True
        summed_max_build : Size=0, Index=None, Active=True
        summed_min_build : Size=0, Index=None, Active=True

    23 Declarations: SUMMED_MAX_FLOWS SUMMED_MIN_FLOWS NEGATIVE_GRADIENT_FLOWS POSITIVE_GRADIENT_FLOWS INTEGER_FLOWS positive_gradient_index positive_gradient negative_gradient_index negative_gradient integer_flow_index integer_flow summed_max summed_max_build summed_min summed_min_build positive_gradient_constr_index positive_gradient_constr positive_gradient_build negative_gradient_constr_index negative_gradient_constr negative_gradient_build integer_flow_constr_index integer_flow_constr