-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add facades CommodityGHG and ConversionGHG #180
Changes from 1 commit
a2d1032
dc4c864
b9b3f6a
06c8d13
0271cb2
8780416
61ad5c5
d7b58a2
dd1f923
a11c2e5
3937ee9
18e0b4d
7cc9e31
322a9aa
6e01808
056e0d4
c0f2038
51bb272
3c6bc94
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
import dataclasses | ||
|
||
from oemof.solph._plumbing import sequence | ||
from oemof.solph.flows import Flow | ||
from pyomo.core import BuildAction, Constraint | ||
from pyomo.core.base.block import ScalarBlock | ||
|
||
from oemof import solph | ||
|
||
from .commodity import Commodity | ||
|
||
|
||
@dataclasses.dataclass(unsafe_hash=False, frozen=False, eq=False) | ||
class CommodityGHG(Commodity): | ||
r""" | ||
Commodity element with one output and additionally emission outputs. | ||
|
||
Parameters | ||
---------- | ||
bus: oemof.solph.Bus | ||
An oemof bus instance where the unit is connected to with its output | ||
amount: numeric | ||
Total available amount to be used within the complete time horizon | ||
of the problem | ||
marginal_cost: numeric | ||
Marginal cost for one unit used commodity | ||
output_parameters: dict (optional) | ||
Parameters to set on the output edge of the component (see. oemof.solph | ||
Edge/Flow class for possible arguments) | ||
|
||
|
||
.. math:: | ||
\sum_{t} x^{flow}(t) \leq c^{amount} | ||
|
||
Notes | ||
----- | ||
Emission buses are defined by starting with 'emission_bus', see Examples | ||
section. | ||
Emission factors are defined by the following naming convention: | ||
'emission_factor_<label_of_emission_bus>. | ||
The realation between the main output (`bus`) and the emissions are set via | ||
:class:`~oemof.tabular.facades.commodity_ghg.CommodityGHGBlock`. | ||
|
||
For additional constraints set through `output_parameters` see | ||
oemof.solph.Flow class. | ||
|
||
Examples | ||
--------- | ||
Defining a ConversionGHG: | ||
|
||
>>> from oemof import solph | ||
|
||
>>> bus_gas = solph.Bus("gas") | ||
>>> bus_co2 = solph.Bus("co2") | ||
>>> bus_gas.type, bus_co2.type = "bus", "bus" | ||
|
||
>>> commodity = CommodityGHG( | ||
... label="gas-commodity", | ||
... bus=bus_gas, | ||
... emission_bus_0=bus_co2, | ||
... carrier="gas", | ||
... amount=1000, | ||
... marginal_cost=10, | ||
... output_parameters={"max": [0.9, 0.5, 0.4]}, | ||
... emission_factor_co2=56) | ||
|
||
>>> commodity.emission_factors[bus_co2].default | ||
56 | ||
""" | ||
|
||
def __init__(self, **kwargs): | ||
|
||
super().__init__( | ||
**kwargs, | ||
) | ||
|
||
buses = { | ||
key: value | ||
for key, value in kwargs.items() | ||
if type(value) is type(solph.Bus()) | ||
} | ||
|
||
self.build_solph_components() | ||
self.init_emission_buses(kwargs) | ||
self.emission_factors = self.init_emission_factors(buses, kwargs) | ||
|
||
def init_emission_buses(self, kwargs): | ||
"""Adds emissions buses as output flows and drops them from kwargs""" | ||
for key, bus in list(kwargs.items()): | ||
if key.startswith("emission_bus"): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you @Bachibouzouk ! I have added warnings in 51bb272. |
||
self.outputs.update({bus: Flow()}) | ||
kwargs.pop(key) | ||
|
||
def init_emission_factors(self, buses, kwargs): | ||
"""Returns emission factors as values in dict with buses as keys""" | ||
emission_factors = {} | ||
for key, value in list(kwargs.items()): | ||
if key.startswith("emission_factor"): | ||
bus_label = key.split("_")[-1] | ||
bus = [ | ||
bus for bus in buses.items() if bus[1].label == bus_label | ||
][0][1] | ||
emission_factors.update({bus: sequence(value)}) | ||
kwargs.pop(key) | ||
return emission_factors | ||
|
||
def constraint_group(self): | ||
return CommodityGHGBlock | ||
|
||
|
||
class CommodityGHGBlock(ScalarBlock): | ||
r""" | ||
Block for the linear relation of nodes with type | ||
:class:`~oemof.tabular.facades.commodity_ghg.CommodityGHGBlock` | ||
|
||
**The following sets are created:** | ||
|
||
CommodityGHGs | ||
A set with all | ||
:class:`~oemof.tabular.facades.commodity_ghg.CommodityGHGBlock` | ||
objects. | ||
|
||
**The following constraints are created:** | ||
|
||
Linear relation :attr:`om.CommodityGHGBlock.relation[o,t]` | ||
.. math:: | ||
P_{n.bus}(p, t) \cdot \eta_{o}(t) = P_{o}(p, t), \\ | ||
\forall p, t \in \textrm{TIMEINDEX}, \\ | ||
\forall n \in \textrm{CommodityGHGs}, \\ | ||
\forall o \in \textrm{OUTPUTS} | ||
|
||
While OUPUTS the set of Bus objects connected with the output of | ||
the CommodityGHG. The constraint above will be created for all OUTPUTS for | ||
all TIMESTEPS. A CommodityGHG with two outflows for one day with an hourly | ||
resolution will lead to 48 constraints. | ||
|
||
The index :math: n is the index for the Source node itself. Therefore, | ||
a `flow[i, n, p, t]` is a flow from the Bus i to the Source n at | ||
time index p, t. | ||
|
||
====================== ============================ ==================== | ||
symbol attribute explanation | ||
====================== ============================ ==================== | ||
:math:`P_{n,n.bus}(p, t)` `flow[n, n.bus, p, t]` CommodityGHG, outflow | ||
|
||
:math:`P_{n,o}(p, t)` `flow[n, o, p, t]` CommodityGHG, outflow | ||
|
||
:math:`\eta_{o}(t)` `emission_factor[n, o, t]` Outflow, efficiency | ||
|
||
====================== ============================ ==================== | ||
|
||
""" | ||
|
||
CONSTRAINT_GROUP = True | ||
|
||
def __init__(self, *args, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
|
||
def _create(self, group=None): | ||
""" | ||
Creates the linear constraint for the class:`CommodityGHGBlock` block. | ||
""" | ||
if group is None: | ||
return None | ||
|
||
m = self.parent_block() | ||
|
||
out_flows = {n: [o for o in n.outputs.keys()] for n in group} | ||
|
||
self.relation = Constraint( | ||
[ | ||
(n, o, p, t) | ||
for p, t in m.TIMEINDEX | ||
for n in group | ||
for o in out_flows[n] | ||
], | ||
noruleinit=True, | ||
) | ||
|
||
def _emission_relation(block): | ||
for p, t in m.TIMEINDEX: | ||
for n in group: | ||
for o in out_flows[n]: | ||
# only emission buses | ||
if o is not n.bus: | ||
try: | ||
lhs = ( | ||
m.flow[n, n.bus, p, t] | ||
* n.emission_factors[o][t] | ||
) | ||
rhs = m.flow[n, o, p, t] | ||
block.relation.add((n, o, p, t), (lhs == rhs)) | ||
except KeyError: | ||
pass | ||
raise KeyError( | ||
"Error in constraint creation", | ||
"source: {0}, target: {1}".format( | ||
n.label, o.label | ||
), | ||
) | ||
|
||
self.relation_build = BuildAction(rule=_emission_relation) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import dataclasses | ||
|
||
from oemof.solph._plumbing import sequence | ||
from oemof.solph.flows import Flow | ||
|
||
from oemof import solph | ||
|
||
from .conversion import Conversion | ||
|
||
|
||
@dataclasses.dataclass(unsafe_hash=False, frozen=False, eq=False) | ||
class ConversionGHG(Conversion): | ||
r""" | ||
Conversion unit with one input, one output and emission outputs. | ||
|
||
Cost parameters like `carrier_cost` are associated with `from_bus` like in | ||
Conversion facade. | ||
The emission factors are also associated to `from_bus` | ||
|
||
|
||
Parameters | ||
---------- | ||
from_bus: oemof.solph.Bus | ||
An oemof bus instance where the conversion unit is connected to with | ||
its input. | ||
to_bus: oemof.solph.Bus | ||
An oemof bus instance where the conversion unit is connected to with | ||
its output. | ||
capacity: numeric | ||
The conversion capacity (output side) of the unit. | ||
efficiency: numeric | ||
Efficiency of the conversion unit (0 <= efficiency <= 1). Default: 1 | ||
marginal_cost: numeric | ||
Marginal cost for one unit of produced output. Default: 0 | ||
carrier_cost: numeric | ||
Carrier cost for one unit of used input. Default: 0 | ||
capacity_cost: numeric | ||
Investment costs per unit of output capacity. | ||
If capacity is not set, this value will be used for optimizing the | ||
conversion output capacity. | ||
expandable: boolean or numeric (binary) | ||
True, if capacity can be expanded within optimization. Default: False. | ||
capacity_potential: numeric | ||
Maximum invest capacity in unit of output capacity. | ||
capacity_minimum: numeric | ||
Minimum invest capacity in unit of output capacity. | ||
input_parameters: dict (optional) | ||
Set parameters on the input edge of the conversion unit | ||
(see oemof.solph for more information on possible parameters) | ||
ouput_parameters: dict (optional) | ||
Set parameters on the output edge of the conversion unit | ||
(see oemof.solph for more information on possible parameters) | ||
|
||
Notes | ||
----- | ||
Emission buses are defined by starting with 'emission_bus', see Examples | ||
section. | ||
Emission factors are defined by the following naming convention: | ||
'emission_factor_<label_of_emission_bus>. | ||
|
||
Examples | ||
--------- | ||
Defining a ConversionGHG: | ||
|
||
>>> from oemof import solph | ||
|
||
>>> bus_biomass = solph.Bus("biomass") | ||
>>> bus_heat = solph.Bus("heat") | ||
>>> bus_co2 = solph.Bus("co2") | ||
|
||
>>> bus_biomass.type, bus_heat.type, bus_co2.type = "bus", "bus", "bus" | ||
|
||
>>> conversion = ConversionGHG( | ||
... label="biomass_plant", | ||
... carrier="biomass", | ||
... tech="st", | ||
... from_bus=bus_biomass, | ||
... to_bus=bus_heat, | ||
... emission_bus_0=bus_co2, | ||
... capacity=100, | ||
... efficiency=0.4, | ||
... emission_factor_co2=56) | ||
>>> conversion.conversion_factors[bus_co2].default | ||
56 | ||
""" | ||
|
||
def __init__(self, **kwargs): | ||
super().__init__( | ||
**kwargs, | ||
) | ||
|
||
buses = { | ||
key: value | ||
for key, value in kwargs.items() | ||
if type(value) is type(solph.Bus()) | ||
} | ||
|
||
self.build_solph_components() # inputs, outputs, conversion_factors | ||
self.init_emission_buses(kwargs) | ||
self.init_emission_factors(buses, kwargs) | ||
|
||
def init_emission_buses(self, kwargs): | ||
"""Adds emissions buses as output flows and drops them from kwargs""" | ||
for key, bus in list(kwargs.items()): | ||
if key.startswith("emission_bus"): | ||
self.outputs.update({bus: Flow()}) | ||
kwargs.pop(key) | ||
|
||
def init_emission_factors(self, buses, kwargs): | ||
"""Adds emission factors as `conversion_factors""" | ||
for key, value in list(kwargs.items()): | ||
if key.startswith("emission_factor"): | ||
bus_label = key.split("_")[-1] | ||
bus = [ | ||
bus for bus in buses.items() if bus[1].label == bus_label | ||
][0][1] | ||
self.conversion_factors.update({bus: sequence(value)}) | ||
kwargs.pop(key) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@SabineHaas - Is there a reason why you used this instead of @dataclass_facade?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I opened a new issue for discussion - as I had this in my mind for quite a while:
#184