Source code for oemof.solph.components._transformer

# -*- coding: utf-8 -*-

"""
solph version of oemof.network.Transformer including
sets, variables, constraints and parts of the objective function
for TransformerBlock objects.

SPDX-FileCopyrightText: Uwe Krien <krien@uni-bremen.de>
SPDX-FileCopyrightText: Simon Hilpert
SPDX-FileCopyrightText: Cord Kaldemeyer
SPDX-FileCopyrightText: Patrik Schönfeldt
SPDX-FileCopyrightText: Stephan Günther
SPDX-FileCopyrightText: Birgit Schachler
SPDX-FileCopyrightText: jnnr
SPDX-FileCopyrightText: jmloenneberga
SPDX-FileCopyrightText: David Fuhrländer
SPDX-FileCopyrightText: Johannes Röder

SPDX-License-Identifier: MIT

"""

from oemof.network import network as on
from pyomo.core import BuildAction
from pyomo.core import Constraint
from pyomo.core.base.block import ScalarBlock

from oemof.solph._helpers import warn_if_missing_attribute
from oemof.solph._plumbing import sequence


[docs]class Transformer(on.Transformer): """A linear converter 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 transformer: >>> 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.Transformer( ... 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._transformer.Transformer'> >>> sorted([str(i) for i in trsf.inputs]) ['hard_coal', 'natural_gas'] >>> trsf_new = solph.components.Transformer( ... 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 * :py:class:`~oemof.solph.components._transformer.TransformerBlock` """ def __init__( self, label=None, inputs=None, outputs=None, conversion_factors=None, custom_attributes=None, ): self.label = label if inputs is None: warn_if_missing_attribute(self, "inputs") inputs = {} if outputs is None: warn_if_missing_attribute(self, "outputs") outputs = {} if custom_attributes is None: custom_attributes = {} super().__init__( label=label, inputs=inputs, outputs=outputs, **custom_attributes, ) if conversion_factors is None: conversion_factors = {} self.conversion_factors = { k: sequence(v) for k, v in conversion_factors.items() } missing_conversion_factor_keys = ( set(self.outputs) | set(self.inputs) ) - set(self.conversion_factors) for cf in missing_conversion_factor_keys: self.conversion_factors[cf] = sequence(1)
[docs] def constraint_group(self): return TransformerBlock
[docs]class TransformerBlock(ScalarBlock): r""" Block for the linear relation of nodes with type :class:`~oemof.solph.network.transformer.Transformer` **The following constraints are created:** Linear relation `om.Transformer.relation[i,o,t]` .. math:: P_{i}(t) \cdot \eta_{o}(t) = P_{o}(t) \cdot \eta_{i}(t), \\ \forall t \in \textrm{TIMESTEPS}, \\ \forall i \in \textrm{INPUTS}, \\ \forall o \in \textrm{OUTPUTS} 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 step t. ====================== ============================ ==================== symbol attribute explanation ====================== ============================ ==================== :math:`P_{i}(t)` `flow[i, n, t]` Transformer, inflow :math:`P_{o}(t)` `flow[n, o, t]` Transformer, outflow :math:`\eta_{i}(t)` `conversion_factor[i, n, t]` Inflow, efficiency :math:`\eta_{o}(t)` `conversion_factor[n, o, t]` Outflow, efficiency ====================== ============================ ==================== """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def _create(self, group=None): """Creates the linear constraint for the class:`TransformerBlock` block. Parameters ---------- group : list List of oemof.solph.components.Transformers objects for which the linear relation of inputs and outputs is created e.g. group = [trsf1, trsf2, trsf3, ...]. Note that the relation is created for all existing relations of all inputs and all outputs of the transformer. The components inside the list need to hold an attribute `conversion_factors` of type dict containing the conversion factors for all inputs to outputs. """ if group is None: return None m = self.parent_block() in_flows = {n: [i for i in n.inputs.keys()] for n in group} out_flows = {n: [o for o in n.outputs.keys()] for n in group} self.relation = Constraint( [ (n, i, o, t) for t in m.TIMESTEPS for n in group for o in out_flows[n] for i in in_flows[n] ], noruleinit=True, ) def _input_output_relation(block): for t in m.TIMESTEPS: for n in group: for o in out_flows[n]: for i in in_flows[n]: try: lhs = ( m.flow[i, n, t] * n.conversion_factors[o][t] ) rhs = ( m.flow[n, o, t] * n.conversion_factors[i][t] ) except ValueError: raise ValueError( "Error in constraint creation", "source: {0}, target: {1}".format( n.label, o.label ), ) block.relation.add((n, i, o, t), (lhs == rhs)) self.relation_build = BuildAction(rule=_input_output_relation)