oemof.solph.components

Sink

solph version of oemof.network.Sink

class oemof.solph.components._sink.Sink(label=None, *, inputs, parent_node=None, custom_properties=None)[source]

Bases: Node

A component which is designed for one input flow.

Parameters:
  • label (str or tuple) – String holding the label of the Sink object. The label of each object must be unique.

  • inputs (dict) – A dictionary mapping input nodes to corresponding inflows (i.e. input values).

Examples

Defining a Sink:

>>> from oemof import solph
>>> bel = solph.buses.Bus(label='electricity')
>>> electricity_export = solph.components.Sink(
...    label='el_export',
...    inputs={bel: solph.flows.Flow()})
constraint_group()[source]

Source

solph version of oemof.network.Source

class oemof.solph.components._source.Source(label=None, *, outputs, parent_node=None, custom_properties=None)[source]

Bases: Node

A component which is designed for one output flow.

Parameters:
  • label (str or tuple) – String holding the label of the Source object. The label of each object must be unique.

  • outputs (dict) – A dictionary mapping input nodes to corresponding outflows (i.e. output values).

  • custom_properties (dict) – Additional keyword arguments for use in user-defined equations or for information purposes.

Examples

Defining a Source:

>>> from oemof import solph
>>> bel = solph.buses.Bus(label='electricity')
>>> pv_plant = solph.components.Source(
...    label='pp_pv',
...    outputs={bel: solph.flows.Flow()})
>>> type(pv_plant)
<class 'oemof.solph.components._source.Source'>
>>> pv_plant.label
'pp_pv'
>>> str(pv_plant.outputs[bel].output)
'electricity'
>>> bgas = solph.buses.Bus(label='gas')
>>> gas_source = solph.components.Source(
...    label='gas_import',
...    outputs={bgas: solph.flows.Flow()},
...    custom_properties={"emission": 201})  # g/kWh
>>> gas_source.custom_properties["emission"]
201
constraint_group()[source]

Converter

solph version of oemof.network.Converter including sets, variables, constraints and parts of the objective function for ConverterBlock objects.

class oemof.solph.components._converter.Converter(label=None, inputs=None, outputs=None, parent_node=None, conversion_factors=None, custom_properties=None)[source]

Bases: Node

A linear ConverterBlock object with n inputs and n outputs.

Node object that relates any number of inflow and outflows with conversion factors. Inputs and outputs must be given as dictinaries.

Parameters:
  • inputs (dict) – Dictionary with inflows. Keys must be the starting node(s) of the inflow(s).

  • outputs (dict) – Dictionary with outflows. Keys must be the ending node(s) of the outflow(s).

  • conversion_factors (dict) – Dictionary containing conversion factors for conversion of each flow. Keys must be the connected nodes (typically Buses). The dictionary values can either be a scalar or an iterable with individual conversion factors for each time step. Default: 1. If no conversion_factor is given for an in- or outflow, the conversion_factor is set to 1.

Examples

Defining an linear converter:

>>> from oemof import solph
>>> bgas = solph.buses.Bus(label='natural_gas')
>>> bcoal = solph.buses.Bus(label='hard_coal')
>>> bel = solph.buses.Bus(label='electricity')
>>> bheat = solph.buses.Bus(label='heat')
>>> trsf = solph.components.Converter(
...    label='pp_gas_1',
...    inputs={bgas: solph.flows.Flow(), bcoal: solph.flows.Flow()},
...    outputs={bel: solph.flows.Flow(), bheat: solph.flows.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.components._converter.Converter'>
>>> sorted([str(i) for i in trsf.inputs])
['hard_coal', 'natural_gas']
>>> trsf_new = solph.components.Converter(
...    label='pp_gas_2',
...    inputs={bgas: solph.flows.Flow()},
...    outputs={bel: solph.flows.Flow(), bheat: solph.flows.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
constraint_group()[source]
class oemof.solph.components._converter.ConverterBlock(*args, **kwds)[source]

Bases: ScalarBlock

Block for the linear relation of nodes with type ConverterBlock

The following sets are created: (-> see basic sets at Model )

CONVERTERS

A set with all Converter objects.

The following constraints are created:

Linear relation om.ConverterBlock.relation[i,o,t]
\[\begin{split}P_{i}(t) \cdot \eta_{o}(t) = P_{o}(t) \cdot \eta_{i}(t), \\ \forall t \in \textrm{TIMESTEPS}, \\ \forall n \in \textrm{CONVERTERS}, \\ \forall i \in \textrm{INPUTS}, \\ \forall o \in \textrm{OUTPUTS}\end{split}\]

While INPUTS is the set of Bus objects connected with the input of the Transformer and OUPUTS the set of Bus objects connected with the output of the Transformer. The constraint above will be created for all combinations of INPUTS and OUTPUTS for all TIMESTEPS. A Transformer with two inflows and two outflows for one day with an hourly resolution will lead to 96 constraints.

The index :math: n is the index for the Transformer node itself. Therefore, a flow[i, n, t] is a flow from the Bus i to the Transformer n at time index p, t.

symbol

attribute

explanation

\(P_{i,n}(p, t)\)

flow[i, n, t]

Converter, inflow

\(P_{n,o}(p, t)\)

flow[n, o, t]

Converter, outflow

\(\eta_{i}(t)\)

conversion_factor[i, n, t]

Inflow, efficiency

\(\eta_{o}(t)\)

conversion_factor[n, o, t]

Outflow, efficiency

extractionTurbineCHP

ExtractionTurbineCHP and associated individual constraints (blocks) and groupings.

class oemof.solph.components._extraction_turbine_chp.ExtractionTurbineCHP(conversion_factor_full_condensation, label=None, inputs=None, outputs=None, parent_node=None, conversion_factors=None, custom_properties=None)[source]

Bases: Converter

A CHP with an extraction turbine in a linear model. For a more detailled modelling approach providing more options, also 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.

Notes

The following sets, variables, constraints and objective parts are created
  • ExtractionTurbineCHPBlock

Examples

>>> from oemof import solph
>>> bel = solph.buses.Bus(label='electricityBus')
>>> bth = solph.buses.Bus(label='heatBus')
>>> bgas = solph.buses.Bus(label='commodityBus')
>>> et_chp = solph.components.ExtractionTurbineCHP(
...    label='variable_chp_gas',
...    inputs={bgas: solph.flows.Flow(nominal_capacity=10e10)},
...    outputs={bel: solph.flows.Flow(), bth: solph.flows.Flow()},
...    conversion_factors={bel: 0.3, bth: 0.5},
...    conversion_factor_full_condensation={bel: 0.5})
constraint_group()[source]
class oemof.solph.components._extraction_turbine_chp.ExtractionTurbineCHPBlock(*args, **kwds)[source]

Bases: ScalarBlock

Block for all instances of _ExtractionTurbineCHP

Variables

The following variables are used:

  • \(\dot H_{Fuel}\)

    Fuel input flow, represented in code as flow[i, n, t]

  • \(P_{el}\)

    Electric power outflow, represented in code as flow[n, main_output, t]

  • \(\dot Q_{th}\)

    Thermal output flow, represented in code as flow[n, tapped_output, t]

Parameters

The following parameters are created as attributes of om.ExtractionTurbineCHP:

  • \(\eta_{el,woExtr}\)

    Electric efficiency without heat extraction, represented in code as conversion_factor_full_condensation[n, t]

  • \(\eta_{el,maxExtr}\)

    Electric efficiency with maximal heat extraction, represented in code as conversion_factors[main_output][n, t]

  • \(\eta_{th,maxExtr}\)

    Thermal efficiency with maximal heat extraction, represented in code as conversion_factors[tapped_output][n, t]

Constraints

The following constraints are created for all instances of oemof.solph.components.ExtractionTurbineCHP:

\[\begin{split}& (1)\dot H_{Fuel}(t) = \frac{P_{el}(t) + \dot Q_{th}(t) \cdot \beta(t)} {\eta_{el,woExtr}(t)} \\ & (2)P_{el}(t) \geq \dot Q_{th}(t) \cdot C_b\end{split}\]

where:

\[\beta(t) = \frac{\eta_{el,woExtr}(t) - \eta_{el,maxExtr}(t)}{\eta_{th,maxExtr}(t)}\]

and:

\[C_b = \frac{\eta_{el,maxExtr}(t)}{\eta_{th,maxExtr}(t)}\]

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

\(\dot H_{Fuel}\)

flow[i, n, t]

V

fuel input flow

\(P_{el}\)

flow[n, main_output, t]

V

electric power

\(\dot Q_{th}\)

flow[n, tapped_output, t]

V

thermal output

\(\beta\)

main_flow_loss_index[n, t]

P

power loss index

\(\eta_{el,woExtr}\)

conversion_factor_full_condensation[n, t]

P

electric efficiency

without heat extraction

\(\eta_{el,maxExtr}\)

conversion_factors[main_output][n, t]

P

electric efficiency

with max heat extraction

\(\eta_{th,maxExtr}\)

conversion_factors[tapped_output][n, t]

P

thermal efficiency with

maximal heat extraction

CONSTRAINT_GROUP = True

GenericCHP

GenericCHP and associated individual constraints (blocks) and groupings.

class oemof.solph.components._generic_chp.GenericCHP(fuel_input, electrical_output, heat_output, beta, back_pressure, parent_node=None, label=None, custom_properties=None)[source]

Bases: Node

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.solph.Bus and oemof.solph.Flow objects for the fuel input.

  • electrical_output (dict) – Dictionary with key-value-pair of oemof.solph.Bus and oemof.solph.Flow objects 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.solph.Bus and oemof.solph.Flow objects 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.buses.Bus(label='electricityBus')
>>> bth = solph.buses.Bus(label='heatBus')
>>> bgas = solph.buses.Bus(label='commodityBus')
>>> ccet = solph.components.GenericCHP(
...    label='combined_cycle_extraction_turbine',
...    fuel_input={bgas: solph.flows.Flow(
...        custom_properties={"H_L_FG_share_max": [0.183]})},
...    electrical_output={bel: solph.flows.Flow(
...        custom_properties={
...            "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.flows.Flow(
...        custom_properties={"Q_CW_min": [10.552]})},
...    beta=[0.122], back_pressure=False)
>>> type(ccet)
<class 'oemof.solph.components._generic_chp.GenericCHP'>
property alphas

Compute or return the _alphas attribute.

constraint_group()[source]
class oemof.solph.components._generic_chp.GenericCHPBlock(*args, **kwds)[source]

Bases: ScalarBlock

Block for the relation of the \(n\) nodes with type class:.GenericCHP.

The following constraints are created:

\[\begin{split}& (1)\qquad \dot{H}_F(t) = fuel\ input \\ & (2)\qquad \dot{Q}(t) = heat\ output \\ & (3)\qquad P_{el}(t) = power\ output\\ & (4)\qquad \dot{H}_F(t) = \alpha_0(t) \cdot Y(t) + \alpha_1(t) \cdot P_{el,woDH}(t)\\ & (5)\qquad \dot{H}_F(t) = \alpha_0(t) \cdot Y(t) + \alpha_1(t) \cdot ( P_{el}(t) + \beta \cdot \dot{Q}(t) )\\ & (6)\qquad \dot{H}_F(t) \leq Y(t) \cdot \frac{P_{el, max, woDH}(t)}{\eta_{el,max,woDH}(t)}\\ & (7)\qquad \dot{H}_F(t) \geq Y(t) \cdot \frac{P_{el, min, woDH}(t)}{\eta_{el,min,woDH}(t)}\\ & (8)\qquad \dot{H}_{L,FG,max}(t) = \dot{H}_F(t) \cdot \dot{H}_{L,FG,sharemax}(t)\\ & (9)\qquad \dot{H}_{L,FG,min}(t) = \dot{H}_F(t) \cdot \dot{H}_{L,FG,sharemin}(t)\\ & (10)\qquad P_{el}(t) + \dot{Q}(t) + \dot{H}_{L,FG,max}(t) + \dot{Q}_{CW, min}(t) \cdot Y(t) = / \leq \dot{H}_F(t)\\\end{split}\]

where \(= / \leq\) depends on the CHP being back pressure or not.

The coefficients \(\alpha_0\) and \(\alpha_1\) can be determined given the efficiencies maximal/minimal load:

\[\begin{split}& \eta_{el,max,woDH}(t) = \frac{P_{el,max,woDH}(t)}{\alpha_0(t) \cdot Y(t) + \alpha_1(t) \cdot P_{el,max,woDH}(t)}\\ & \eta_{el,min,woDH}(t) = \frac{P_{el,min,woDH}(t)}{\alpha_0(t) \cdot Y(t) + \alpha_1(t) \cdot P_{el,min,woDH}(t)}\\\end{split}\]

For the attribute \(\dot{H}_{L,FG,min}\) being not None, e.g. for a motoric CHP, the following is created:

Constraint:

\[\begin{split}& (11)\qquad P_{el}(t) + \dot{Q}(t) + \dot{H}_{L,FG,min}(t) + \dot{Q}_{CW, min}(t) \cdot Y(t) \geq \dot{H}_F(t)\\[10pt]\end{split}\]

The symbols used are defined as follows (with Variables (V) and Parameters (P)):

math. symbol

attribute

type

explanation

\(\dot{H}_{F}\)

H_F[n,t]

V

input of enthalpy through fuel input

\(P_{el}\)

P[n,t]

V

provided electric power

\(P_{el,woDH}\)

P_woDH[n,t]

V

electric power without district heating

\(P_{el,min,woDH}\)

P_min_woDH[n,t]

P

min. electric power without district heating

\(P_{el,max,woDH}\)

P_max_woDH[n,t]

P

max. electric power without district heating

\(\dot{Q}\)

Q[n,t]

V

provided heat

\(\dot{Q}_{CW, min}\)

Q_CW_min[n,t]

P

minimal therm. condenser load to cooling water

\(\dot{H}_{L,FG,min}\)

H_L_FG_min[n,t]

V

flue gas enthalpy loss at min heat extraction

\(\dot{H}_{L,FG,max}\)

H_L_FG_max[n,t]

V

flue gas enthalpy loss at max heat extraction

\(\dot{H}_{L,FG,sharemin}\)

H_L_FG_share_min[n,t]

P

share of flue gas loss at min heat extraction

\(\dot{H}_{L,FG,sharemax}\)

H_L_FG_share_max[n,t]

P

share of flue gas loss at max heat extraction

\(Y\)

Y[n,t]

V

status variable on/off

\(\alpha_0\)

n.alphas[0][n,t]

P

coefficient describing efficiency

\(\alpha_1\)

n.alphas[1][n,t]

P

coefficient describing efficiency

\(\beta\)

beta[n,t]

P

power loss index

\(\eta_{el,min,woDH}\)

Eta_el_min_woDH[n,t]

P

el. eff. at min. fuel flow w/o distr. heating

\(\eta_{el,max,woDH}\)

Eta_el_max_woDH[n,t]

P

el. eff. at max. fuel flow w/o distr. heating

CONSTRAINT_GROUP = True

GenericStorage

GenericStorage and associated individual constraints (blocks) and groupings.

class oemof.solph.components._generic_storage.GenericInvestmentStorageBlock(*args, **kwds)[source]

Bases: ScalarBlock

Block for all storages with Investment being not None. See Investment for all parameters of the Investment class.

Variables

All Storages are indexed by \(n\) (denoting the respective storage unit), which is omitted in the following for the sake of convenience. The following variables are created as attributes of om.GenericInvestmentStorageBlock:

  • \(P_i(p, t)\)

    Inflow of the storage (created in oemof.solph.models.Model).

  • \(P_o(p, t)\)

    Outflow of the storage (created in oemof.solph.models.Model).

  • \(E(t)\)

    Current storage content (Absolute level of stored energy).

  • \(E_{invest}(p)\)

    Invested (nominal) capacity of the storage in period p.

  • \(E_{total}(p)\)

    Total installed (nominal) capacity of the storage in period p.

  • \(E_{old}(p)\)

    Old (nominal) capacity of the storage to be decommissioned in period p.

  • \(E_{old,exo}(p)\)

    Exogenous old (nominal) capacity of the storage to be decommissioned in period p; existing capacity reaching its lifetime.

  • \(E_{old,endo}(p)\)

    Endogenous old (nominal) capacity of the storage to be decommissioned in period p; endgenous investments reaching their lifetime.

  • \(E(-1)\)

    Initial storage content (before timestep 0). Not applicable for a multi-period model.

  • \(b_{invest}(p)\)

    Binary variable for the status of the investment, if nonconvex is True.

Constraints

The following constraints are created for all investment storages:

Storage balance (Same as for GenericStorageBlock)

\[\begin{split}E(t) = &E(t-1) \cdot (1 - \beta(t)) ^{\tau(t)/(t_u)} \\ &- \gamma(t)\cdot (E_{total}(p)) \cdot {\tau(t)/(t_u)}\\ &- \delta(t) \cdot {\tau(t)/(t_u)}\\ &- \frac{\dot{E}_o(p, t))}{\eta_o(t)} \cdot \tau(t) + \dot{E}_i(p, t) \cdot \eta_i(t) \cdot \tau(t)\end{split}\]

Total storage capacity (p > 0 for multi-period model only)

\[\begin{split}& if \quad p=0:\\ & E_{total}(p) = E_{exist} + E_{invest}(p)\\ &\\ & else:\\ & E_{total}(p) = E_{total}(p-1) + E_{invest}(p) - E_{old}(p)\\ &\\ & \forall p \in \textrm{PERIODS}\end{split}\]

Old storage capacity (p > 0 for multi-period model only)

\[\begin{split}& E_{old}(p) = E_{old,exo}(p) + E_{old,end}(p)\\ &\\ & if \quad p=0:\\ & E_{old,end}(p) = 0\\ &\\ & else \quad if \quad l \leq year(p):\\ & E_{old,end}(p) = E_{invest}(p_{comm})\\ &\\ & else:\\ & E_{old,end}(p)\\ &\\ & if \quad p=0:\\ & E_{old,exo}(p) = 0\\ &\\ & else \quad if \quad l - a \leq year(p):\\ & E_{old,exo}(p) = E_{exist} (*)\\ &\\ & else:\\ & E_{old,exo}(p) = 0\\ &\\ & \forall p \in \textrm{PERIODS}\end{split}\]

where:

  • (*) is only performed for the first period the condition is True. A decommissioning flag is then set to True to prevent having falsely added old capacity in future periods.

  • \(year(p)\) is the year corresponding to period p

  • \(p_{comm}\) is the commissioning period of the storage

Depending on the attribute nonconvex, the constraints for the bounds of the decision variable \(E_{invest}(p)\) are different:

  • nonconvex = False

\[\begin{split}& E_{invest, min}(p) \le E_{invest}(p) \le E_{invest, max}(p) \\ & \forall p \in \textrm{PERIODS}\end{split}\]
  • nonconvex = True

\[\begin{split}& E_{invest, min}(p) \cdot b_{invest}(p) \le E_{invest}(p)\\ & E_{invest}(p) \le E_{invest, max}(p) \cdot b_{invest}(p)\\ & \forall p \in \textrm{PERIODS}\end{split}\]

The following constraints are created depending on the attributes of the GenericStorage:

  • initial_storage_level is None; not applicable for multi-period model

    Constraint for a variable initial storage content:

\[E(-1) \le E_{exist} + E_{invest}(0)\]
  • initial_storage_level is not None; not applicable for multi-period model

    An initial value for the storage content is given:

\[E(-1) = (E_{invest}(0) + E_{exist}) \cdot c(-1)\]
  • balanced=True; not applicable for multi-period model

    The energy content of storage of the first and the last timestep are set equal:

\[E(-1) = E(t_{last})\]
  • invest_relation_input_capacity is not None

    Connect the invest variables of the storage and the input flow:

\[\begin{split}& P_{i,total}(p) = E_{total}(p) \cdot r_{cap,in} \\ & \forall p \in \textrm{PERIODS}\end{split}\]
  • invest_relation_output_capacity is not None

    Connect the invest variables of the storage and the output flow:

\[\begin{split}& P_{o,total}(p) = E_{total}(p) \cdot r_{cap,out}\\ & \forall p \in \textrm{PERIODS}\end{split}\]
  • invest_relation_input_output is not None

    Connect the invest variables of the input and the output flow:

\[\begin{split}& P_{i,total}(p) = P_{o,total}(p) \cdot r_{in,out}\\ & \forall p \in \textrm{PERIODS}\end{split}\]
  • max_storage_level

    Rule for upper bound constraint for the storage content:

\[\begin{split}& E(t) \leq E_{total}(p) \cdot c_{max}(t)\\ & \forall p, t \in \textrm{TIMEINDEX}\end{split}\]
  • min_storage_level

    Rule for lower bound constraint for the storage content:

\[\begin{split}& E(t) \geq E_{total}(p) \cdot c_{min}(t)\\ & \forall p, t \in \textrm{TIMEINDEX}\end{split}\]

Objective function

Objective terms for a standard model and a multi-period model differ quite strongly. Besides, the part of the objective function added by the investment storages also depends on whether a convex or nonconvex investment option is selected. The following parts of the objective function are created:

Standard model

  • nonconvex = False

    \[E_{invest}(0) \cdot c_{invest,var}(0)\]
  • nonconvex = True

    \[\begin{split}E_{invest}(0) \cdot c_{invest,var}(0) + c_{invest,fix}(0) \cdot b_{invest}(0)\\\end{split}\]

Where 0 denotes the 0th (investment) period since in a standard model, there is only this one period.

Multi-period model

  • nonconvex = False

    \[\begin{split}& E_{invest}(p) \cdot A(c_{invest,var}(p), l, ir) \cdot \frac {1}{ANF(d, ir)} \cdot DF^{-p}\\ & \forall p \in \textrm{PERIODS}\end{split}\]

In case, the remaining lifetime of a storage is greater than 0 and attribute use_remaining_value of the energy system is True, the difference in value for the investment period compared to the last period of the optimization horizon is accounted for as an adder to the investment costs:

\[\begin{split}& E_{invest}(p) \cdot (A(c_{invest,var}(p), l_{r}, ir) - A(c_{invest,var}(|P|), l_{r}, ir)\\ & \cdot \frac {1}{ANF(l_{r}, ir)} \cdot DF^{-|P|}\\ &\\ & \forall p \in \textrm{PERIODS}\end{split}\]
  • nonconvex = True

    \[\begin{split}& (E_{invest}(p) \cdot A(c_{invest,var}(p), l, ir) \cdot \frac {1}{ANF(d, ir)}\\ & + c_{invest,fix}(p) \cdot b_{invest}(p)) \cdot DF^{-p} \\ & \forall p \in \textrm{PERIODS}\end{split}\]

In case, the remaining lifetime of a storage is greater than 0 and attribute use_remaining_value of the energy system is True, the difference in value for the investment period compared to the last period of the optimization horizon is accounted for as an adder to the investment costs:

\[\begin{split}& (E_{invest}(p) \cdot (A(c_{invest,var}(p), l_{r}, ir) - A(c_{invest,var}(|P|), l_{r}, ir)\\ & \cdot \frac {1}{ANF(l_{r}, ir)} \cdot DF^{-|P|}\\ & + (c_{invest,fix}(p) - c_{invest,fix}(|P|)) \cdot b_{invest}(p)) \cdot DF^{-p}\\ &\\ & \forall p \in \textrm{PERIODS}\end{split}\]
  • fixed_costs not None for investments

    \[\begin{split}& \sum_{pp=year(p)}^{limit_{end}} E_{invest}(p) \cdot c_{fixed}(pp) \cdot DF^{-pp}) \cdot DF^{-p}\\ & \forall p \in \textrm{PERIODS}\end{split}\]
  • fixed_costs not None for existing capacity

    \[\sum_{pp=0}^{limit_{exo}} E_{exist} \cdot c_{fixed}(pp) \cdot DF^{-pp}\]

where:

  • \(A(c_{invest,var}(p), l, ir)\) A is the annuity for investment expenses \(c_{invest,var}(p)\), lifetime \(l\) and interest rate \(ir\).

  • \(l_{r}\) is the remaining lifetime at the end of the optimization horizon (in case it is greater than 0 and smaller than the actual lifetime).

  • \(ANF(d, ir)\) is the annuity factor for duration \(d\) and interest rate \(ir\).

  • \(d=min\{year_{max} - year(p), l\}\) defines the number of years within the optimization horizon that investment annuities are accounted for.

  • \(year(p)\) denotes the start year of period \(p\).

  • \(year_{max}\) denotes the last year of the optimization horizon, i.e. at the end of the last period.

  • \(limit_{end}=min\{year_{max}, year(p) + l\}\) is used as an upper bound to ensure fixed costs for endogenous investments to occur within the optimization horizon.

  • \(limit_{exo}=min\{year_{max}, l - a\}\) is used as an upper bound to ensure fixed costs for existing capacities to occur within the optimization horizon. \(a\) is the initial age of an asset.

  • \(DF=(1+dr)\) is the discount factor.

The annuity / annuity factor hereby is:

\[\begin{split}& A(c_{invest,var}(p), l, ir) = c_{invest,var}(p) \cdot \frac {(1+ir)^l \cdot ir} {(1+ir)^l - 1}\\ &\\ & ANF(d, ir)=\frac {(1+ir)^d \cdot ir} {(1+ir)^d - 1}\end{split}\]

They are retrieved, using oemof.tools.economics annuity function. The interest rate \(ir\) for the annuity is defined as weighted average costs of capital (wacc) and assumed constant over time.

The overall summed cost expressions for all InvestmentFlowBlock objects can be accessed by

  • om.GenericInvestmentStorageBlock.investment_costs,

  • om.GenericInvestmentStorageBlock.fixed_costs and

  • om.GenericInvestmentStorageBlock.costs.

Their values after optimization can be retrieved by

  • om.GenericInvestmentStorageBlock.investment_costs(),

  • om.GenericInvestmentStorageBlock.period_investment_costs (yielding a dict keyed by periods); note: this is not a Pyomo expression, but calculated,

  • om.GenericInvestmentStorageBlock.fixed_costs() and

  • om.GenericInvestmentStorageBlock.costs().

Table 11 List of Variables

symbol

attribute

explanation

\(P_i(p, t)\)

flow[i[n], n, p, t]

Inflow of the storage

\(P_o(p, t)\)

flow[n, o[n], p, t]

Outflow of the storage

\(E(t)\)

storage_content[n, t]

Current storage content (current absolute stored energy)

\(E_{loss}(t)\)

storage_losses[n, t]

Current storage losses (absolute losses per time step)

\(E_{invest}(p)\)

invest[n, p]

Invested (nominal) capacity of the storage

\(E_{old}(p)\)

old[n, p]

Old (nominal) capacity of the storage
to be decommissioned in period p

\(E_{old,exo}(p)\)

old_exo[n, p]

Old (nominal) capacity of the storage
to be decommissioned in period p
which was exogenously given by \(E_{exist}\)

\(E_{old,end}(p)\)

old_end[n, p]

Old (nominal) capacity of the storage
to be decommissioned in period p
which was endogenously determined by \(E_{invest}(p_{comm})\)
where \(p_{comm}\) is the commissioning period

\(E(-1)\)

init_cap[n]

Initial storage capacity (before timestep 0)

\(b_{invest}(p)\)

invest_status[i, o, p]

Binary variable for the status of investment

\(P_{i,invest}(p)\)

InvestmentFlowBlock.invest[i[n], n, p]

Invested (nominal) inflow (InvestmentFlowBlock)

\(P_{o,invest}\)

InvestmentFlowBlock.invest[n, o[n]]

Invested (nominal) outflow (InvestmentFlowBlock)

Table 12 List of Parameters

symbol

attribute

explanation

\(E_{exist}\)

flows[i, o].investment.existing

Existing storage capacity

\(E_{invest,min}\)

flows[i, o].investment.minimum

Minimum investment value

\(E_{invest,max}\)

flows[i, o].investment.maximum

Maximum investment value

\(P_{i,exist}\)

flows[i[n], n].investment.existing

Existing inflow capacity

\(P_{o,exist}\)

flows[n, o[n]].investment.existing

Existing outflow capacity

\(c_{invest,var}\)

flows[i, o].investment.ep_costs

Variable investment costs

\(c_{invest,fix}\)

flows[i, o].investment.offset

Fix investment costs

\(c_{fixed}\)

flows[i, o].investment.fixed_costs

Fixed costs; only allowed in multi-period model

\(r_{cap,in}\)

invest_relation_input_capacity

Relation of storage capacity and nominal inflow

\(r_{cap,out}\)

invest_relation_output_capacity

Relation of storage capacity and nominal outflow

\(r_{in,out}\)

invest_relation_input_output

Relation of nominal in- and outflow

\(\beta(t)\)

loss_rate[t]

Fraction of lost energy as share of \(E(t)\) per hour

\(\gamma(t)\)

fixed_losses_relative[t]

Fixed loss of energy relative to \(E_{invest} + E_{exist}\) per hour

\(\delta(t)\)

fixed_losses_absolute[t]

Absolute fixed loss of energy per hour

\(\eta_i(t)\)

inflow_conversion_factor[t]

Conversion factor (i.e. efficiency) when storing energy

\(\eta_o(t)\)

outflow_conversion_factor[t]

Conversion factor when (i.e. efficiency) taking stored energy

\(c(-1)\)

initial_storage_level

Initial relative storage content (before timestep 0)

\(c_{max}\)

flows[i, o].maximum[t]

Normed maximum value of storage content

\(c_{min}\)

flows[i, o].minimum[t]

Normed minimum value of storage content

\(l\)

flows[i, o].investment.lifetime

Lifetime for investments in storage capacity

\(a\)

flows[i, o].investment.age

Initial age of existing capacity / energy

\(\tau(t)\)

Duration of time step

\(t_u\)

Time unit of losses \(\beta(t)\), \(\gamma(t)\), \(\delta(t)\) and timeincrement \(\tau(t)\)

CONSTRAINT_GROUP = True
class oemof.solph.components._generic_storage.GenericStorage(label=None, inputs=None, outputs=None, parent_node=None, nominal_capacity=None, nominal_storage_capacity=None, initial_storage_level=None, invest_relation_input_output=None, invest_relation_input_capacity=None, invest_relation_output_capacity=None, min_storage_level=0, max_storage_level=1, balanced=True, loss_rate=0, fixed_losses_relative=0, fixed_losses_absolute=0, inflow_conversion_factor=1, outflow_conversion_factor=1, constant_soc_until=None, fraction_saturation_charging=None, fixed_costs=0, storage_costs=None, lifetime_inflow=None, lifetime_outflow=None, custom_properties=None)[source]

Bases: Node

Component GenericStorage to model with basic characteristics of storages.

The GenericStorage is designed for one input and one output.

Parameters:
  • nominal_capacity (numeric, \(E_{nom}\) or) – oemof.solph.options.Investment object Absolute nominal capacity of the storage, fixed value or object describing parameter of investment optimisations.

  • invest_relation_input_capacity (numeric (iterable or scalar) or None, \(r_{cap,in}\)) – Ratio between the investment variable of the input flow and the investment variable of the storage: \(\dot{E}_{in,invest} = E_{invest} \cdot r_{cap,in}\)

  • invest_relation_output_capacity (numeric (iterable or scalar) or None, \(r_{cap,out}\)) – Ratio between the investment variable of the output flow and the investment variable of the storage: \(\dot{E}_{out,invest} = E_{invest} \cdot r_{cap,out}\)

  • invest_relation_input_output (numeric (iterable or scalar) or None, \(r_{in,out}\)) – Ratio between the investment variable of the input flow and the investment variable of the output 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 set to None no relation will be set: \(\dot{E}_{in,invest} = \dot{E}_{out,invest} \cdot r_{in,out}\)

  • initial_storage_level (numeric, \(c(-1)\)) – The relative storage content in the timestep before the first time step of optimization (between 0 and 1).

    Note: When investment mode is used in a multi-period model, initial_storage_level is not supported. Storage output is forced to zero until the storage unit is invested in.

  • balanced (boolean) – Couple storage level of first and last time step. (Total inflow and total outflow are balanced.)

  • loss_rate (numeric (iterable or scalar)) – The relative loss of the storage content per hour.

  • fixed_losses_relative (numeric (iterable or scalar), \(\gamma(t)\)) – Losses per hour that are independent of the storage content but proportional to nominal storage capacity.

    Note: Fixed losses are not supported in investment mode.

  • fixed_losses_absolute (numeric (iterable or scalar), \(\delta(t)\)) – Losses per hour that are independent of storage content and independent of nominal storage capacity.

    Note: Fixed losses are not supported in investment mode.

  • inflow_conversion_factor (numeric (iterable or scalar), \(\eta_i(t)\)) – The relative conversion factor, i.e. efficiency associated with the inflow of the storage.

  • outflow_conversion_factor (numeric (iterable or scalar), \(\eta_o(t)\)) – see: inflow_conversion_factor

  • min_storage_level (numeric (iterable or scalar), \(c_{min}(t)\)) – The normed minimum storage content as fraction of the nominal storage capacity or the capacity that has been invested into (between 0 and 1). To set different values in every time step use a sequence.

  • max_storage_level (numeric (iterable or scalar), \(c_{max}(t)\)) – see: min_storage_level

  • constant_soc_until (float) – The proportional charge level between 0 and 1 at which the linear reduction in charging power begins. Up to this charge level, the charging power remains constant, after which it drops linearly to the value specified by fraction_saturation_charging.

  • fraction_saturation_charging (float) – The fraction of charging capacity shortly before the storage tank is completely full. This value is therefore between 0 and 1, where 1 means that there is no reduction in charging power as the SOC increases.

  • storage_costs (numeric (iterable or scalar), \(c_{storage}(t)\)) – Cost (per energy) for having energy in the storage, starting from time point \(t_{1}\). (\(t_{0}\) is left out to avoid counting it twice if balanced=True.)

  • lifetime_inflow (int, \(n_{in}\)) – Determine the lifetime of an inflow; only applicable for multi-period models which can invest in storage capacity and have an invest_relation_input_capacity defined

  • lifetime_outflow (int, \(n_{in}\)) – Determine the lifetime of an outflow; only applicable for multi-period models which can invest in storage capacity and have an invest_relation_output_capacity defined

Notes

The following sets, variables, constraints and objective parts are created

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.buses.Bus('my_bus')
>>> my_storage = solph.components.GenericStorage(
...     label='storage',
...     nominal_capacity=1000,
...     inputs={my_bus: solph.flows.Flow(nominal_capacity=200, variable_costs=10)},
...     outputs={my_bus: solph.flows.Flow(nominal_capacity=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',
...     nominal_capacity=solph.Investment(ep_costs=50),
...     inputs={my_bus: solph.flows.Flow(nominal_capacity=solph.Investment())},
...     outputs={my_bus: solph.flows.Flow(nominal_capacity=solph.Investment())},
...     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)
constraint_group()[source]
class oemof.solph.components._generic_storage.GenericStorageBlock(*args, **kwds)[source]

Bases: ScalarBlock

Storage without an Investment object.

The following sets are created: (-> see basic sets at Model )

STORAGES

A set with all GenericStorage objects, which do not have an investment of type Investment.

STORAGES_BALANCED

A set of all GenericStorage objects, with ‘balanced’ attribute set to True.

STORAGES_WITH_INVEST_FLOW_REL

A set with all GenericStorage objects with two investment flows coupled with the ‘invest_relation_input_output’ attribute.

The following variables are created:

storage_content

Storage content for every storage and timestep. The value for the storage content at the beginning is set by the parameter initial_storage_level or not set if initial_storage_level is None. The variable of storage s and timestep t can be accessed by: om.GenericStorageBlock.storage_content[s, t]

intra_storage_delta

Storage content for every storage and timestep of typical periods (only used in TSAM-mode). The variable of storage s and timestep t can be accessed by: om.GenericStorageBlock.intra_storage_delta[s, k, t]

The following constraints are created:

Set storage_content of last time step to one at t=0 if balanced == True
\[E(t_{last}) = E(-1)\]
Storage losses om.Storage.losses[n, t]
\[\begin{split}E_{loss}(t) = &E(t-1) \cdot 1 - (1 - \beta(t))^{\tau(t)/(t_u)} \\ &- \gamma(t)\cdot E_{nom} \cdot {\tau(t)/(t_u)}\\ &- \delta(t) \cdot {\tau(t)/(t_u)}\end{split}\]
Storage balance om.Storage.balance[n, t]
\[\begin{split}E(t) = &E(t-1) - E_{loss}(t)\\ &- \frac{\dot{E}_o(p, t)}{\eta_o(t)} \cdot \tau(t)\\ &+ \dot{E}_i(p, t) \cdot \eta_i(t) \cdot \tau(t)\end{split}\]
Connect the invest variables of the input and the output flow.
\[\begin{split}InvestmentFlowBlock.invest(source(n), n, p) + existing = \\ (InvestmentFlowBlock.invest(n, target(n), p) + existing) \\ * invest\_relation\_input\_output(n) \\ \forall n \in \textrm{INVEST\_REL\_IN\_OUT} \\ \forall p \in \textrm{PERIODS}\end{split}\]

Apply soc-dependent charging power. These Constraints are build if om.constant_soc_until[n, t] and om.fraction_saturation_charging[n, t] are set. The equation follows the basic linear equation: $y = a*x + b$

\[\begin{split}a = -\frac{P_{\max} \cdot (1-f_{end})}{E_{\nom} \cdot (1-f_{lim})} \\ b = P_{\max} \cdot \frac{1 - f_{end} \cdot f_{lim}}{1-f_{lim}}\end{split}\]

symbol

explanation

attribute

\(E(t)\)

energy currently stored

storage_content

\(E_{nom}\)

nominal capacity of the energy storage

nominal_storage_capacity

\(c(-1)\)

state before initial time step

initial_storage_level

\(c_{min}(t)\)

minimum allowed storage

min_storage_level[t]

\(c_{max}(t)\)

maximum allowed storage

max_storage_level[t]

\(\beta(t)\)

fraction of lost energy as share of \(E(t)\) per hour

loss_rate[t]

\(\gamma(t)\)

fixed loss of energy per hour relative to \(E_{nom}\)

fixed_losses_relative[t]

\(\delta(t)\)

absolute fixed loss of energy per hour

fixed_losses_absolute[t]

\(\dot{E}_i(t)\)

energy flowing in

inputs

\(\dot{E}_o(t)\)

energy flowing out

outputs

\(\eta_i(t)\)

conversion factor (i.e. efficiency) when storing energy

inflow_conversion_factor[t]

\(\eta_o(t)\)

conversion factor when (i.e. efficiency) taking stored energy

outflow_conversion_factor[t]

\(\tau(t)\)

duration of time step

\(t_u\)

time unit of losses \(\beta(t)\), \(\gamma(t)\) \(\delta(t)\) and timeincrement \(\tau(t)\)

\(c_{storage}(t)\)

costs of having energy stored

storage_costs

The following parts of the objective function are created:

  • attr:

    storage_costs not 0

    \[\sum_{t \in \textrm{TIMEPOINTS} > 0} c_{storage}(t) \cdot E(t)\]
  • fixed_costs not 0

    \[\displaystyle \sum_{pp=0}^{year_{max}} E_{nom} \cdot c_{fixed}(pp)\]
where \(year_{max}\) denotes the last year of the optimization

horizon, i.e. at the end of the last period.

CONSTRAINT_GROUP = True

OffsetConverter

OffsetConverter and associated individual constraints (blocks) and groupings.

class oemof.solph.components._offset_converter.OffsetConverter(inputs, outputs, parent_node=None, label=None, conversion_factors=None, normed_offsets=None, coefficients=None, custom_properties=None)[source]

Bases: Node

An object with one input and multiple outputs and two coefficients per output to model part load behaviour. The output must contain a NonConvex object.

Parameters:
  • conversion_factors (dict, (\(m(t)\))) – Dict containing the respective bus as key and as value the parameter \(m(t)\). It represents the slope of a linear equation with respect to the NonConvex flow. The value can either be a scalar or a sequence with length of time horizon for simulation.

  • normed_offsets (dict, (\(y_\text{0,normed}(t)\))) – Dict containing the respective bus as key and as value the parameter \(y_\text{0,normed}(t)\). It represents the y-intercept with respect to the NonConvex flow divided by the nominal_capacity of the NonConvex flow (this is for internal purposes). The value can either be a scalar or a sequence with length of time horizon for simulation.

Notes

\(m(t)\) and \(y_\text{0,normed}(t)\) can be calculated as follows:

\[ \begin{align}\begin{aligned}m = \frac{(l_{max}/\eta_{max}-l_{min}/\eta_{min}}{l_{max}-l_{min}}\\y_\text{0,normed} = \frac{1}{\eta_{max}} - m\end{aligned}\end{align} \]

Where \(l_{max}\) and \(l_{min}\) are the maximum and minimum partload share (e.g. 1.0 and 0.5) with reference to the NonConvex flow and \(\eta_{max}\) and \(\eta_{min}\) are the respective efficiencies/conversion factors at these partloads. Alternatively, you can use the inbuilt methods:

You can import these methods from the oemof.solph.components level:

>>> from oemof.solph.components import slope_offset_from_nonconvex_input
>>> from oemof.solph.components import slope_offset_from_nonconvex_output
The sets, variables, constraints and objective parts are created

Examples

>>> from oemof import solph
>>> bel = solph.buses.Bus(label='bel')
>>> bth = solph.buses.Bus(label='bth')
>>> l_nominal = 60
>>> l_max = 1
>>> l_min = 0.5
>>> eta_max = 0.5
>>> eta_min = 0.3
>>> slope = (l_max / eta_max - l_min / eta_min) / (l_max - l_min)
>>> offset = 1 / eta_max - slope

Or use the provided method as explained in the previous section:

>>> _slope, _offset = slope_offset_from_nonconvex_output(
...     l_max, l_min, eta_max, eta_min
... )
>>> slope == _slope
True
>>> offset == _offset
True
>>> ostf = solph.components.OffsetConverter(
...    label='ostf',
...    inputs={bel: solph.flows.Flow()},
...    outputs={bth: solph.flows.Flow(
...         nominal_capacity=l_nominal, minimum=l_min, maximum=l_max,
...         nonconvex=solph.NonConvex())},
...    conversion_factors={bel: slope},
...    normed_offsets={bel: offset},
... )
>>> type(ostf)
<class 'oemof.solph.components._offset_converter.OffsetConverter'>

The input required to operate at minimum load, can be computed from the slope and offset:

>>> input_at_min = ostf.conversion_factors[bel][0] * l_min + ostf.normed_offsets[bel][0] * l_max
>>> input_at_min * l_nominal
100.0

The same can be done for the input at nominal load:

>>> input_at_max = l_max * (ostf.conversion_factors[bel][0] + ostf.normed_offsets[bel][0])
>>> input_at_max * l_nominal
120.0
constraint_group()[source]
normed_offset_and_conversion_factors_from_coefficients(coefficients)[source]

Calculate slope and offset for new API from the old API coefficients.

Parameters:

coefficients (tuple) – tuple holding the coefficients (offset, slope) for the old style OffsetConverter.

Returns:

tuple – A tuple holding the slope and the offset for the new OffsetConverter API.

plot_partload(bus, tstep)[source]

Create a matplotlib figure of the flow to nonconvex flow relation.

Parameters:
  • bus (oemof.solph.Bus) – Bus, to which the NOT-nonconvex input or output is connected to.

  • tstep (int) – Timestep to generate the figure for.

Returns:

tuple – A tuple with the matplotlib figure and axes objects.

class oemof.solph.components._offset_converter.OffsetConverterBlock(*args, **kwds)[source]

Bases: ScalarBlock

Block for the relation of nodes with type OffsetConverter

The following constraints are created:

\[\begin{split}& P(p, t) = P_\text{ref}(p, t) \cdot m(t) + P_\text{nom,ref}(p) \cdot Y_\text{ref}(t) \cdot y_\text{0,normed}(t) \\\end{split}\]

The symbols used are defined as follows (with Variables (V) and Parameters (P)):

symbol

attribute

type

explanation

\(P(t)\)

flow[i,n,p,t] or flow[n,o,p,t]

V

Non-nonconvex flows at input or output

\(P_{in}(t)\)

flow[i,n,p,t] or flow[n,o,p,t]

V

nonconvex flow of converter

\(Y(t)\)

V

Binary status variable of nonconvex flow

\(P_{nom}(t)\)

V

Nominal value (max. capacity) of the nonconvex flow

\(m(t)\)

conversion_factors[i][n,t] or conversion_factors[o][n,t]

P

Linear coefficient 1 (slope) of a Non-nonconvex flows

\(y_\text{0,normed}(t)\)

normed_offsets[i][n,t] or normed_offsets[o][n,t]

P

Linear coefficient 0 (y-intersection)/P_{nom}(t) of Non-nonconvex flows

Note that \(P_{nom}(t) \cdot Y(t)\) is merged into one variable, called status_nominal[n, o, p, t].

CONSTRAINT_GROUP = True
oemof.solph.components._offset_converter.slope_offset_from_nonconvex_input(max_load, min_load, eta_at_max, eta_at_min)[source]

Calculate the slope and the offset with max and min given for input

The reference is the input flow here. That means, the NonConvex flow is specified at one of the input flows. The max_load and the min_load are the max and the min specifications for the NonConvex flow. eta_at_max and eta_at_min are the efficiency values of a different flow, e.g. an output, with respect to the max_load and min_load operation points.

\[ \begin{align}\begin{aligned}\begin{split}\text{slope} = \frac{ \text{max} \cdot \eta_\text{at max} - \text{min} \cdot \eta_\text{at min} }{\text{max} - \text{min}}\\\end{split}\\\text{offset} = \eta_\text{at,max} - \text{slope}\end{aligned}\end{align} \]
Parameters:
  • max_load (float) – Maximum load value, e.g. 1

  • min_load (float) – Minimum load value, e.g. 0.5

  • eta_at_max (float) – Efficiency at maximum load.

  • eta_at_min (float) – Efficiency at minimum load.

Returns:

tuple – slope and offset

Example

>>> from oemof import solph
>>> max_load = 1
>>> min_load = 0.5
>>> eta_at_min = 0.4
>>> eta_at_max = 0.3

With the input load being at 100 %, in this example, the efficiency should be 30 %. With the input load being at 50 %, it should be 40 %. We can calcualte slope and the offset which is normed to the nominal capacity of the referenced flow (in this case the input flow) always.

>>> slope, offset = solph.components.slope_offset_from_nonconvex_input(
...     max_load, min_load, eta_at_max, eta_at_min
... )
>>> input_flow = 10
>>> input_flow_nominal = 10
>>> output_flow = slope * input_flow + offset * input_flow_nominal

We can then calculate with the OffsetConverter input output relation, what the resulting efficiency is. At max operating conditions it should be identical to the efficiency we put in initially. Analogously, we apply this to the minimal load point.

>>> round(output_flow / input_flow, 3) == eta_at_max
True
>>> input_flow = 5
>>> output_flow = slope * input_flow + offset * input_flow_nominal
>>> round(output_flow / input_flow, 3) == eta_at_min
True
oemof.solph.components._offset_converter.slope_offset_from_nonconvex_output(max_load, min_load, eta_at_max, eta_at_min)[source]

Calculate the slope and the offset with max and min given for output.

The reference is the output flow here. That means, the NonConvex flow is specified at one of the output flows. The max_load and the min_load are the max and the min specifications for the NonConvex flow. eta_at_max and eta_at_min are the efficiency values of a different flow, e.g. an input, with respect to the max_load and min_load operation points.

\[ \begin{align}\begin{aligned}\begin{split}\text{slope} = \frac{ \frac{\text{max}}{\eta_\text{at max}} - \frac{\text{min}}{\eta_\text{at min}} }{\text{max} - \text{min}}\\\end{split}\\\text{offset} = \frac{1}{\eta_\text{at,max}} - \text{slope}\end{aligned}\end{align} \]
Parameters:
  • max_load (float) – Maximum load value, e.g. 1

  • min_load (float) – Minimum load value, e.g. 0.5

  • eta_at_max (float) – Efficiency at maximum load.

  • eta_at_min (float) – Efficiency at minimum load.

Returns:

tuple – slope and offset

Example

>>> from oemof import solph
>>> max_load = 1
>>> min_load = 0.5
>>> eta_at_min = 0.7
>>> eta_at_max = 0.8

With the output load being at 100 %, in this example, the efficiency should be 80 %. With the input load being at 50 %, it should be 70 %. We can calcualte slope and the offset, which is normed to the nominal capacity of the referenced flow (in this case the output flow) always.

>>> slope, offset = solph.components.slope_offset_from_nonconvex_output(
...     max_load, min_load, eta_at_max, eta_at_min
... )
>>> output_flow = 10
>>> output_flow_nominal = 10
>>> input_flow = slope * output_flow + offset * output_flow_nominal

We can then calculate with the OffsetConverter input output relation, what the resulting efficiency is. At max operating conditions it should be identical to the efficiency we put in initially. Analogously, we apply this to the minimal load point.

>>> round(output_flow / input_flow, 3) == eta_at_max
True
>>> output_flow = 5
>>> input_flow = slope * output_flow + offset * output_flow_nominal
>>> round(output_flow / input_flow, 3) == eta_at_min
True

experimental.GenericCAES

In-development generic compressed air energy storage.

class oemof.solph.components.experimental._generic_caes.GenericCAES(label, *, electrical_input, fuel_input, electrical_output, params, parent_node=None, custom_properties=None)[source]

Bases: Node

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

Examples

>>> from oemof import solph
>>> bel = solph.buses.Bus(label='bel')
>>> bth = solph.buses.Bus(label='bth')
>>> bgas = solph.buses.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.components.experimental.GenericCAES(
...    label='caes',
...    electrical_input={bel: solph.flows.Flow()},
...    fuel_input={bgas: solph.flows.Flow()},
...    electrical_output={bel: solph.flows.Flow()},
...    params=concept)
>>> type(caes)
<class 'oemof.solph.components.experimental._generic_caes.GenericCAES'>
constraint_group()[source]
class oemof.solph.components.experimental._generic_caes.GenericCAESBlock(*args, **kwds)[source]

Bases: ScalarBlock

Block for nodes of class:.GenericCAES.

Note: This component is experimental. Use it with care.

The following constraints are created:

\[\begin{split}& (1) \qquad P_{cmp}(t) = electrical\_input (t) \quad \forall t \in T \\ & (2) \qquad P_{cmp\_max}(t) = m_{cmp\_max} \cdot CAS_{fil}(t-1) + b_{cmp\_max} \quad \forall t \in\left[1, t_{max}\right] \\ & (3) \qquad P_{cmp\_max}(t) = b_{cmp\_max} \quad \forall t \notin\left[1, t_{max}\right] \\ & (4) \qquad P_{cmp}(t) \leq P_{cmp\_max}(t) \quad \forall t \in T \\ & (5) \qquad P_{cmp}(t) \geq P_{cmp\_min} \cdot ST_{cmp}(t) \quad \forall t \in T \\ & (6) \qquad P_{cmp}(t) = m_{cmp\_max} \cdot CAS_{fil\_max} + b_{cmp\_max} \cdot ST_{cmp}(t) \quad \forall t \in T \\ & (7) \qquad \dot{Q}_{cmp}(t) = m_{cmp\_q} \cdot P_{cmp}(t) + b_{cmp\_q} \cdot ST_{cmp}(t) \quad \forall t \in T \\ & (8) \qquad \dot{Q}_{cmp}(t) = \dot{Q}_{cmp_out}(t) + \dot{Q}_{tes\_in}(t) \quad \forall t \in T \\ & (9) \qquad r_{cmp\_tes} \cdot\dot{Q}_{cmp\_out}(t) = \left(1-r_{cmp\_tes}\right) \dot{Q}_{tes\_in}(t) \quad \forall t \in T \\ & (10) \quad\; P_{exp}(t) = electrical\_output (t) \quad \forall t \in T \\ & (11) \quad\; P_{exp\_max}(t) = m_{exp\_max} CAS_{fil}(t-1) + b_{exp\_max} \quad \forall t \in\left[1, t_{\max }\right] \\ & (12) \quad\; P_{exp\_max}(t) = b_{exp\_max} \quad \forall t \notin\left[1, t_{\max }\right] \\ & (13) \quad\; P_{exp}(t) \leq P_{exp\_max}(t) \quad \forall t \in T \\ & (14) \quad\; P_{exp}(t) \geq P_{exp\_min}(t) \cdot ST_{exp}(t) \quad \forall t \in T \\ & (15) \quad\; P_{exp}(t) \leq m_{exp\_max} \cdot CAS_{fil\_max} + b_{exp\_max} \cdot ST_{exp}(t) \quad \forall t \in T \\ & (16) \quad\; \dot{Q}_{exp}(t) = m_{exp\_q} \cdot P_{exp}(t) + b_{cxp\_q} \cdot ST_{cxp}(t) \quad \forall t \in T \\ & (17) \quad\; \dot{Q}_{exp\_in}(t) = fuel\_input(t) \quad \forall t \in T \\ & (18) \quad\; \dot{Q}_{exp}(t) = \dot{Q}_{exp\_in}(t) + \dot{Q}_{tes\_out}(t)+\dot{Q}_{cxp\_add}(t) \quad \forall t \in T \\ & (19) \quad\; r_{exp\_tes} \cdot \dot{Q}_{exp\_in}(t) = (1 - r_{exp\_tes})(\dot{Q}_{tes\_out}(t) + \dot{Q}_{exp\_add}(t)) \quad \forall t \in T \\ & (20) \quad\; \dot{E}_{cas\_in}(t) = m_{cas\_in}\cdot P_{cmp}(t) + b_{cas\_in}\cdot ST_{cmp}(t) \quad \forall t \in T \\ & (21) \quad\; \dot{E}_{cas\_out}(t) = m_{cas\_out}\cdot P_{cmp}(t) + b_{cas\_out}\cdot ST_{cmp}(t) \quad \forall t \in T \\ & (22) \quad\; \eta_{cas\_tmp} \cdot CAS_{fil}(t) = CAS_{fil}(t-1) + \tau\left(\dot{E}_{cas\_in}(t) - \dot{E}_{cas\_out}(t)\right) \quad \forall t \in\left[1, t_{max}\right] \\ & (23) \quad\; \eta_{cas\_tmp} \cdot CAS_{fil}(t) = \tau\left(\dot{E}_{cas\_in}(t) - \dot{E}_{cas\_out}(t)\right) \quad \forall t \notin\left[1, t_{max}\right] \\ & (24) \quad\; CAS_{fil}(t) \leq CAS_{fil\_max} \quad \forall t \in T \\ & (25) \quad\; TES_{fil}(t) = TES_{fil}(t-1) + \tau\left(\dot{Q}_{tes\_in}(t) - \dot{Q}_{tes\_out}(t)\right) \quad \forall t \in\left[1, t_{max}\right] \\ & (26) \quad\; TES_{fil}(t) = \tau\left(\dot{Q}_{tes\_in}(t) - \dot{Q}_{tes\_out}(t)\right) \quad \forall t \notin\left[1, t_{max}\right] \\ & (27) \quad\; TES_{fil}(t) \leq TES_{fil\_max} \quad \forall t \in T \\ &\end{split}\]

Table: Symbols and attribute names of variables and parameters

Table 13 Variables (V) and Parameters (P)

symbol

attribute

type

explanation

\(ST_{cmp}\)

cmp_st[n,t]

V

Status of compression

\({P}_{cmp}\)

cmp_p[n,t]

V

Compression power

\({P}_{cmp\_max}\)

cmp_p_max[n,t]

V

Max. compression power

\(\dot{Q}_{cmp}\)

cmp_q_out_sum[n,t]

V

Summed heat flow in compression

\(\dot{Q}_{cmp\_out}\)

cmp_q_waste[n,t]

V

Waste heat flow from compression

\(ST_{exp}(t)\)

exp_st[n,t]

V

Status of expansion (binary)

\(P_{exp}(t)\)

exp_p[n,t]

V

Expansion power

\(P_{exp\_max}(t)\)

exp_p_max[n,t]

V

Max. expansion power

\(\dot{Q}_{exp}(t)\)

exp_q_in_sum[n,t]

V

Summed heat flow in expansion

\(\dot{Q}_{exp\_in}(t)\)

exp_q_fuel_in[n,t]

V

Heat (external) flow into expansion

\(\dot{Q}_{exp\_add}(t)\)

exp_q_add_in[n,t]

V

Additional heat flow into expansion

\(CAV_{fil}(t)\)

cav_level[n,t]

V

Filling level if CAE

\(\dot{E}_{cas\_in}(t)\)

cav_e_in[n,t]

V

Exergy flow into CAS

\(\dot{E}_{cas\_out}(t)\)

cav_e_out[n,t]

V

Exergy flow from CAS

\(TES_{fil}(t)\)

tes_level[n,t]

V

Filling level of Thermal Energy Storage (TES)

\(\dot{Q}_{tes\_in}(t)\)

tes_e_in[n,t]

V

Heat flow into TES

\(\dot{Q}_{tes\_out}(t)\)

tes_e_out[n,t]

V

Heat flow from TES

\(b_{cmp\_max}\)

cmp_p_max_b[n,t]

P

Specific y-intersection

\(b_{cmp\_q}\)

cmp_q_out_b[n,t]

P

Specific y-intersection

\(b_{exp\_max}\)

exp_p_max_b[n,t]

P

Specific y-intersection

\(b_{exp\_q}\)

exp_q_in_b[n,t]

P

Specific y-intersection

\(b_{cas\_in}\)

cav_e_in_b[n,t]

P

Specific y-intersection

\(b_{cas\_out}\)

cav_e_out_b[n,t]

P

Specific y-intersection

\(m_{cmp\_max}\)

cmp_p_max_m[n,t]

P

Specific slope

\(m_{cmp\_q}\)

cmp_q_out_m[n,t]

P

Specific slope

\(m_{exp\_max}\)

exp_p_max_m[n,t]

P

Specific slope

\(m_{exp\_q}\)

exp_q_in_m[n,t]

P

Specific slope

\(m_{cas\_in}\)

cav_e_in_m[n,t]

P

Specific slope

\(m_{cas\_out}\)

cav_e_out_m[n,t]

P

Specific slope

\(P_{cmp\_min}\)

cmp_p_min[n,t]

P

Min. compression power

\(r_{cmp\_tes}\)

cmp_q_tes_share[n,t]

P

Ratio between waste heat flow and heat flow into TES

\(r_{exp\_tes}\)

exp_q_tes_share[n,t]

P

Ratio between external heat flow into expansion
and heat flows from TES and additional source

\(\tau\)

m.timeincrement[n,t]

P

Time interval length

\(TES_{fil\_max}\)

tes_level_max[n,t]

P

Max. filling level of TES

\(CAS_{fil\_max}\)

cav_level_max[n,t]

P

Max. filling level of TES

\(\tau\)

cav_eta_tmp[n,t]

P

Temporal efficiency
(loss factor to take intertemporal losses into account)

\(electrical\_input\)

flow[list(n.electrical_input.keys())[0], p, n, t]

P

Electr. power input into compression

\(electrical\_output\)

flow[n, list(n.electrical_output.keys())[0], p, t]

P

Electr. power output of expansion

\(fuel\_input\)

flow[list(n.fuel_input.keys())[0], n, p, t]

P

Heat input (external) into Expansion

CONSTRAINT_GROUP = True

experimental.PiecewiseLinearConverter

In-development Converter with piecewise linear efficiencies.

class oemof.solph.components.experimental._piecewise_linear_converter.PiecewiseLinearConverter(label, *, inputs, outputs, conversion_function, in_breakpoints, pw_repn, parent_node=None, custom_properties=None)[source]

Bases: Node

Component to model an energy converter with one input and one output and an arbitrary piecewise linear conversion function.

Parameters:
  • in_breakpoints (list) – List containing the domain breakpoints, i.e. the breakpoints for the incoming flow.

  • conversion_function (func) – The function describing the relation between incoming flow and outgoing flow which is to be approximated.

  • pw_repn (string) – Choice of piecewise representation that is passed to pyomo.environ.Piecewise

Examples

>>> import oemof.solph as solph
>>> b_gas = solph.buses.Bus(label='biogas')
>>> b_el = solph.buses.Bus(label='electricity')
>>> pwltf = solph.components.experimental.PiecewiseLinearConverter(
...    label='pwltf',
...    inputs={b_gas: solph.flows.Flow(
...    nominal_capacity=100,
...    variable_costs=1)},
...    outputs={b_el: solph.flows.Flow()},
...    in_breakpoints=[0,25,50,75,100],
...    conversion_function=lambda x: x**2,
...    pw_repn='CC')
>>> type(pwltf)
<class 'oemof.solph.components.experimental._piecewise_linear_converter.PiecewiseLinearConverter'>
constraint_group()[source]
class oemof.solph.components.experimental._piecewise_linear_converter.PiecewiseLinearConverterBlock(*args, **kwds)[source]

Bases: ScalarBlock

Block for the relation of nodes with type PiecewiseLinearConverter

The following constraints are created:

CONSTRAINT_GROUP = True