From a2d1032f430f74a4bb56fa8b93eaa1f1bf3ff4a2 Mon Sep 17 00:00:00 2001 From: SabineHaas Date: Fri, 9 Aug 2024 15:26:22 +0200 Subject: [PATCH 01/18] Typos --- src/oemof/tabular/facades/commodity.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oemof/tabular/facades/commodity.py b/src/oemof/tabular/facades/commodity.py index 0659ee10..3c19aab2 100644 --- a/src/oemof/tabular/facades/commodity.py +++ b/src/oemof/tabular/facades/commodity.py @@ -16,11 +16,11 @@ class Commodity(Source, Facade): 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 timehorzion + 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_paramerters: dict (optional) + output_parameters: dict (optional) Parameters to set on the output edge of the component (see. oemof.solph Edge/Flow class for possible arguments) From dc4c86482c9989ccb1d3a58da3a689f5d8db1933 Mon Sep 17 00:00:00 2001 From: SabineHaas Date: Fri, 9 Aug 2024 15:28:33 +0200 Subject: [PATCH 02/18] Add facade CommodityGHG and ConversionGHG --- src/oemof/tabular/facades/commodity_ghg.py | 202 ++++++++++++++++++++ src/oemof/tabular/facades/conversion_ghg.py | 118 ++++++++++++ 2 files changed, 320 insertions(+) create mode 100644 src/oemof/tabular/facades/commodity_ghg.py create mode 100644 src/oemof/tabular/facades/conversion_ghg.py diff --git a/src/oemof/tabular/facades/commodity_ghg.py b/src/oemof/tabular/facades/commodity_ghg.py new file mode 100644 index 00000000..5e2bd6e7 --- /dev/null +++ b/src/oemof/tabular/facades/commodity_ghg.py @@ -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_. + 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"): + 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) diff --git a/src/oemof/tabular/facades/conversion_ghg.py b/src/oemof/tabular/facades/conversion_ghg.py new file mode 100644 index 00000000..931f447c --- /dev/null +++ b/src/oemof/tabular/facades/conversion_ghg.py @@ -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_. + + 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) From b9b3f6a070213425a4cbfd0f75c0cc16cf08d61b Mon Sep 17 00:00:00 2001 From: SabineHaas Date: Fri, 9 Aug 2024 15:29:48 +0200 Subject: [PATCH 03/18] Add tests for facades CommodityGHG and ConversionGHG --- tests/test_constraints.py | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/test_constraints.py b/tests/test_constraints.py index 6111e93d..ce6a244c 100644 --- a/tests/test_constraints.py +++ b/tests/test_constraints.py @@ -11,7 +11,9 @@ from oemof.tabular.facades import ( BackpressureTurbine, Commodity, + CommodityGHG, Conversion, + ConversionGHG, Dispatchable, Excess, ExtractionTurbine, @@ -319,6 +321,25 @@ def test_commodity(self): self.compare_to_reference_lp("commodity.lp") + def test_commodity_ghg(self): + r""" """ + bus_gas = solph.Bus("gas") + bus_co2 = solph.Bus("co2") + + 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, + ) + self.energysystem.add(bus_gas, commodity) + + self.compare_to_reference_lp("commodity_ghg.lp") + def test_conversion(self): r""" """ bus_biomass = solph.Bus("biomass") @@ -337,6 +358,27 @@ def test_conversion(self): self.compare_to_reference_lp("conversion.lp") + def test_conversion_ghg(self): + r""" """ + bus_biomass = solph.Bus("biomass") + bus_heat = solph.Bus("heat") + bus_co2 = solph.Bus("co2") + + 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, + ) + self.energysystem.add(bus_heat, bus_biomass, bus_co2, conversion) + + self.compare_to_reference_lp("conversion_ghg.lp") + def test_dispatchable(self): bus = solph.Bus("electricity") From 06c8d13403956423cacfe20902f6a76b8c358cd4 Mon Sep 17 00:00:00 2001 From: SabineHaas Date: Fri, 9 Aug 2024 15:30:44 +0200 Subject: [PATCH 04/18] Add CommodityGHG and ConversionGHG to typemap --- src/oemof/tabular/facades/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/oemof/tabular/facades/__init__.py b/src/oemof/tabular/facades/__init__.py index ff64bb39..4114c87d 100644 --- a/src/oemof/tabular/facades/__init__.py +++ b/src/oemof/tabular/facades/__init__.py @@ -4,7 +4,9 @@ from .backpressure_turbine import BackpressureTurbine from .commodity import Commodity +from .commodity_ghg import CommodityGHG from .conversion import Conversion +from .conversion_ghg import ConversionGHG from .dispatchable import Dispatchable from .excess import Excess from .extraction_turbine import ExtractionTurbine @@ -22,7 +24,9 @@ "bus": Bus, "heatpump": HeatPump, "commodity": Commodity, + "commodity_ghg": CommodityGHG, "conversion": Conversion, + "conversion_ghg": ConversionGHG, "dispatchable": Dispatchable, "electrical bus": ElectricalBus, "electrical line": ElectricalLine, From 0271cb2079803cb0854e7e1af83e1ce799216fc0 Mon Sep 17 00:00:00 2001 From: SabineHaas Date: Fri, 9 Aug 2024 15:31:12 +0200 Subject: [PATCH 05/18] Add lp files for tests --- tests/_files/lp_files/commodity_ghg.lp | 51 +++++++++++++++ tests/_files/lp_files/conversion_ghg.lp | 86 +++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 tests/_files/lp_files/commodity_ghg.lp create mode 100644 tests/_files/lp_files/conversion_ghg.lp diff --git a/tests/_files/lp_files/commodity_ghg.lp b/tests/_files/lp_files/commodity_ghg.lp new file mode 100644 index 00000000..4ea8b2b4 --- /dev/null +++ b/tests/_files/lp_files/commodity_ghg.lp @@ -0,0 +1,51 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++10 flow(gas_commodity_gas_0_0) ++10 flow(gas_commodity_gas_0_1) ++10 flow(gas_commodity_gas_0_2) + +s.t. + +c_e_BusBlock_balance(gas_0_0)_: ++1 flow(gas_commodity_gas_0_0) += 0 + +c_e_BusBlock_balance(gas_0_1)_: ++1 flow(gas_commodity_gas_0_1) += 0 + +c_e_BusBlock_balance(gas_0_2)_: ++1 flow(gas_commodity_gas_0_2) += 0 + +c_u_SimpleFlowBlock_full_load_time_max_constr(gas_commodity_gas)_: ++1 flow(gas_commodity_gas_0_0) ++1 flow(gas_commodity_gas_0_1) ++1 flow(gas_commodity_gas_0_2) +<= 1000 + +c_e_CommodityGHGBlock_relation(gas_commodity_co2_0_0)_: ++56 flow(gas_commodity_gas_0_0) +-1 flow(gas_commodity_co2_0_0) += 0 + +c_e_CommodityGHGBlock_relation(gas_commodity_co2_0_1)_: ++56 flow(gas_commodity_gas_0_1) +-1 flow(gas_commodity_co2_0_1) += 0 + +c_e_CommodityGHGBlock_relation(gas_commodity_co2_0_2)_: ++56 flow(gas_commodity_gas_0_2) +-1 flow(gas_commodity_co2_0_2) += 0 + +bounds + 0 <= flow(gas_commodity_gas_0_0) <= 900.0 + 0 <= flow(gas_commodity_gas_0_1) <= 500.0 + 0 <= flow(gas_commodity_gas_0_2) <= 400.0 + 0 <= flow(gas_commodity_co2_0_0) <= +inf + 0 <= flow(gas_commodity_co2_0_1) <= +inf + 0 <= flow(gas_commodity_co2_0_2) <= +inf +end diff --git a/tests/_files/lp_files/conversion_ghg.lp b/tests/_files/lp_files/conversion_ghg.lp new file mode 100644 index 00000000..6d75b53f --- /dev/null +++ b/tests/_files/lp_files/conversion_ghg.lp @@ -0,0 +1,86 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++0 ONE_VAR_CONSTANT + +s.t. + +c_e_BusBlock_balance(biomass_0_0)_: ++1 flow(biomass_biomass_plant_0_0) += 0 + +c_e_BusBlock_balance(biomass_0_1)_: ++1 flow(biomass_biomass_plant_0_1) += 0 + +c_e_BusBlock_balance(biomass_0_2)_: ++1 flow(biomass_biomass_plant_0_2) += 0 + +c_e_BusBlock_balance(co2_0_0)_: ++1 flow(biomass_plant_co2_0_0) += 0 + +c_e_BusBlock_balance(co2_0_1)_: ++1 flow(biomass_plant_co2_0_1) += 0 + +c_e_BusBlock_balance(co2_0_2)_: ++1 flow(biomass_plant_co2_0_2) += 0 + +c_e_BusBlock_balance(heat_0_0)_: ++1 flow(biomass_plant_heat_0_0) += 0 + +c_e_BusBlock_balance(heat_0_1)_: ++1 flow(biomass_plant_heat_0_1) += 0 + +c_e_BusBlock_balance(heat_0_2)_: ++1 flow(biomass_plant_heat_0_2) += 0 + +c_e_ConverterBlock_relation(biomass_plant_biomass_heat_0_0)_: ++0.4 flow(biomass_biomass_plant_0_0) +-1 flow(biomass_plant_heat_0_0) += 0 + +c_e_ConverterBlock_relation(biomass_plant_biomass_co2_0_0)_: ++56 flow(biomass_biomass_plant_0_0) +-1 flow(biomass_plant_co2_0_0) += 0 + +c_e_ConverterBlock_relation(biomass_plant_biomass_heat_0_1)_: ++0.4 flow(biomass_biomass_plant_0_1) +-1 flow(biomass_plant_heat_0_1) += 0 + +c_e_ConverterBlock_relation(biomass_plant_biomass_co2_0_1)_: ++56 flow(biomass_biomass_plant_0_1) +-1 flow(biomass_plant_co2_0_1) += 0 + +c_e_ConverterBlock_relation(biomass_plant_biomass_heat_0_2)_: ++0.4 flow(biomass_biomass_plant_0_2) +-1 flow(biomass_plant_heat_0_2) += 0 + +c_e_ConverterBlock_relation(biomass_plant_biomass_co2_0_2)_: ++56 flow(biomass_biomass_plant_0_2) +-1 flow(biomass_plant_co2_0_2) += 0 + +bounds + 1 <= ONE_VAR_CONSTANT <= 1 + 0 <= flow(biomass_biomass_plant_0_0) <= +inf + 0 <= flow(biomass_biomass_plant_0_1) <= +inf + 0 <= flow(biomass_biomass_plant_0_2) <= +inf + 0 <= flow(biomass_plant_heat_0_0) <= 100 + 0 <= flow(biomass_plant_heat_0_1) <= 100 + 0 <= flow(biomass_plant_heat_0_2) <= 100 + 0 <= flow(biomass_plant_co2_0_0) <= +inf + 0 <= flow(biomass_plant_co2_0_1) <= +inf + 0 <= flow(biomass_plant_co2_0_2) <= +inf +end From 8780416280d6e1a7a14c2b9a18b26f2fecf2ce78 Mon Sep 17 00:00:00 2001 From: SabineHaas Date: Fri, 9 Aug 2024 15:32:13 +0200 Subject: [PATCH 06/18] Add example data for CommodityGHG and ConversionGHG facades --- .../examples/datapackages/GHG/README.md | 5 + .../datapackages/GHG/data/elements/bus.csv | 6 + .../GHG/data/elements/commodityGHG_import.csv | 3 + .../GHG/data/elements/conversionGHG.csv | 2 + .../datapackages/GHG/data/elements/excess.csv | 2 + .../datapackages/GHG/data/elements/load.csv | 2 + .../GHG/data/elements/shortage.csv | 2 + .../GHG/data/sequences/load_profile.csv | 4 + .../datapackages/GHG/datapackage.json | 325 ++++++++++++++++++ .../datapackages/GHG/scripts/infer.py | 25 ++ .../tabular/examples/scripting/compute.py | 1 + 11 files changed, 377 insertions(+) create mode 100644 src/oemof/tabular/examples/datapackages/GHG/README.md create mode 100644 src/oemof/tabular/examples/datapackages/GHG/data/elements/bus.csv create mode 100644 src/oemof/tabular/examples/datapackages/GHG/data/elements/commodityGHG_import.csv create mode 100644 src/oemof/tabular/examples/datapackages/GHG/data/elements/conversionGHG.csv create mode 100644 src/oemof/tabular/examples/datapackages/GHG/data/elements/excess.csv create mode 100644 src/oemof/tabular/examples/datapackages/GHG/data/elements/load.csv create mode 100644 src/oemof/tabular/examples/datapackages/GHG/data/elements/shortage.csv create mode 100644 src/oemof/tabular/examples/datapackages/GHG/data/sequences/load_profile.csv create mode 100644 src/oemof/tabular/examples/datapackages/GHG/datapackage.json create mode 100644 src/oemof/tabular/examples/datapackages/GHG/scripts/infer.py diff --git a/src/oemof/tabular/examples/datapackages/GHG/README.md b/src/oemof/tabular/examples/datapackages/GHG/README.md new file mode 100644 index 00000000..ad9b3e8a --- /dev/null +++ b/src/oemof/tabular/examples/datapackages/GHG/README.md @@ -0,0 +1,5 @@ +# Green house gases (GHG) example for oemof-tabular + +Run `scripts/infer.py` from the datapackage root directory to add the +meta data file `datapackage.json` after updating the resources of the +datapackage. diff --git a/src/oemof/tabular/examples/datapackages/GHG/data/elements/bus.csv b/src/oemof/tabular/examples/datapackages/GHG/data/elements/bus.csv new file mode 100644 index 00000000..a1eb6e35 --- /dev/null +++ b/src/oemof/tabular/examples/datapackages/GHG/data/elements/bus.csv @@ -0,0 +1,6 @@ +name;type;balanced +co2;bus;false +ch4;bus;false +n2o;bus;false +el_bus;bus;true +gas_bus;bus;true diff --git a/src/oemof/tabular/examples/datapackages/GHG/data/elements/commodityGHG_import.csv b/src/oemof/tabular/examples/datapackages/GHG/data/elements/commodityGHG_import.csv new file mode 100644 index 00000000..07999f6c --- /dev/null +++ b/src/oemof/tabular/examples/datapackages/GHG/data/elements/commodityGHG_import.csv @@ -0,0 +1,3 @@ +name;type;carrier;amount;bus;emission_bus_0;marginal_cost;output_parameters;emission_factor_co2 +green_gas_import;commodity_ghg;gas;300;gas_bus;co2;2;{"max": [1, 1, 1]};0 +fossile_gas_import;commodity_ghg;gas;200;gas_bus;co2;10;{"max": [1, 0, 0]};56 diff --git a/src/oemof/tabular/examples/datapackages/GHG/data/elements/conversionGHG.csv b/src/oemof/tabular/examples/datapackages/GHG/data/elements/conversionGHG.csv new file mode 100644 index 00000000..9db64f03 --- /dev/null +++ b/src/oemof/tabular/examples/datapackages/GHG/data/elements/conversionGHG.csv @@ -0,0 +1,2 @@ +name;type;carrier;tech;capacity;from_bus;to_bus;emission_bus_0;emission_bus_1;emission_bus_2;marginal_cost;efficiency;emission_factor_co2;emission_factor_ch4;emission_factor_n2o +gtGHG;conversion_ghg;electricity;gt;300;gas_bus;el_bus;co2;ch4;n2o;10;0.5;56;0.4;0.017 diff --git a/src/oemof/tabular/examples/datapackages/GHG/data/elements/excess.csv b/src/oemof/tabular/examples/datapackages/GHG/data/elements/excess.csv new file mode 100644 index 00000000..38f2b05d --- /dev/null +++ b/src/oemof/tabular/examples/datapackages/GHG/data/elements/excess.csv @@ -0,0 +1,2 @@ +name;type;bus;capacity +gas_excess;excess;gas_bus;1000000000 diff --git a/src/oemof/tabular/examples/datapackages/GHG/data/elements/load.csv b/src/oemof/tabular/examples/datapackages/GHG/data/elements/load.csv new file mode 100644 index 00000000..97ecea55 --- /dev/null +++ b/src/oemof/tabular/examples/datapackages/GHG/data/elements/load.csv @@ -0,0 +1,2 @@ +name;amount;profile;type;bus +demand0;100;electricity-load-profile;load;el_bus diff --git a/src/oemof/tabular/examples/datapackages/GHG/data/elements/shortage.csv b/src/oemof/tabular/examples/datapackages/GHG/data/elements/shortage.csv new file mode 100644 index 00000000..63b5bb56 --- /dev/null +++ b/src/oemof/tabular/examples/datapackages/GHG/data/elements/shortage.csv @@ -0,0 +1,2 @@ +name;type;carrier;tech;bus;capacity;marginal_cost +el_shortage;shortage;electricity;shortage;el_bus;100000000;100000 diff --git a/src/oemof/tabular/examples/datapackages/GHG/data/sequences/load_profile.csv b/src/oemof/tabular/examples/datapackages/GHG/data/sequences/load_profile.csv new file mode 100644 index 00000000..ea470ad8 --- /dev/null +++ b/src/oemof/tabular/examples/datapackages/GHG/data/sequences/load_profile.csv @@ -0,0 +1,4 @@ +timeindex,electricity-load-profile +2011-01-01T00:00:00Z,1 +2011-01-01T01:00:00Z,0.5 +2011-01-01T02:00:00Z,0.1 diff --git a/src/oemof/tabular/examples/datapackages/GHG/datapackage.json b/src/oemof/tabular/examples/datapackages/GHG/datapackage.json new file mode 100644 index 00000000..6050686a --- /dev/null +++ b/src/oemof/tabular/examples/datapackages/GHG/datapackage.json @@ -0,0 +1,325 @@ +{ + "profile": "tabular-data-package", + "name": "GHG-test", + "oemof_tabular_version": "0.0.6dev", + "resources": [ + { + "path": "data/elements/bus.csv", + "profile": "tabular-data-resource", + "name": "bus", + "format": "csv", + "mediatype": "text/csv", + "encoding": "utf-8", + "schema": { + "fields": [ + { + "name": "name", + "type": "string", + "format": "default" + }, + { + "name": "type", + "type": "string", + "format": "default" + }, + { + "name": "balanced", + "type": "boolean", + "format": "default" + } + ], + "missingValues": [ + "" + ], + "primaryKey": "name", + "foreignKeys": [] + } + }, + { + "path": "data/elements/conversionGHG.csv", + "profile": "tabular-data-resource", + "name": "conversionGHG", + "format": "csv", + "mediatype": "text/csv", + "encoding": "utf-8", + "schema": { + "fields": [ + { + "name": "name", + "type": "string", + "format": "default" + }, + { + "name": "type", + "type": "string", + "format": "default" + }, + { + "name": "carrier", + "type": "string", + "format": "default" + }, + { + "name": "tech", + "type": "string", + "format": "default" + }, + { + "name": "capacity", + "type": "integer", + "format": "default" + }, + { + "name": "from_bus", + "type": "string", + "format": "default" + }, + { + "name": "to_bus", + "type": "string", + "format": "default" + }, + { + "name": "emission_bus_0", + "type": "string", + "format": "default" + }, + { + "name": "emission_bus_1", + "type": "string", + "format": "default" + }, + { + "name": "emission_bus_2", + "type": "string", + "format": "default" + }, + { + "name": "marginal_cost", + "type": "integer", + "format": "default" + }, + { + "name": "efficiency", + "type": "number", + "format": "default" + }, + { + "name": "emission_factor_co2", + "type": "number", + "format": "default" + }, + { + "name": "emission_factor_ch4", + "type": "number", + "format": "default" + }, + { + "name": "emission_factor_n2o", + "type": "number", + "format": "default" + } + ], + "foreignKeys": [ + { + "fields": "from_bus", + "reference": { + "resource": "bus", + "fields": "name" + } + }, + { + "fields": "to_bus", + "reference": { + "resource": "bus", + "fields": "name" + } + }, + { + "fields": "emission_bus_0", + "reference": { + "resource": "bus", + "fields": "name" + } + }, + { + "fields": "emission_bus_1", + "reference": { + "resource": "bus", + "fields": "name" + } + }, + { + "fields": "emission_bus_2", + "reference": { + "resource": "bus", + "fields": "name" + } + } + ], + "missingValues": [ + "" + ], + "primaryKey": "name" + } + }, + { + "path": "data/elements/commodityGHG_import.csv", + "profile": "tabular-data-resource", + "name": "commodityGHG_import", + "format": "csv", + "mediatype": "text/csv", + "encoding": "utf-8", + "schema": { + "fields": [ + { + "name": "name", + "type": "string", + "format": "default" + }, + { + "name": "type", + "type": "string", + "format": "default" + }, + { + "name": "carrier", + "type": "string", + "format": "default" + }, + { + "name": "amount", + "type": "integer", + "format": "default" + }, + { + "name": "bus", + "type": "string", + "format": "default" + }, + { + "name": "emission_bus_0", + "type": "string", + "format": "default" + }, + { + "name": "marginal_cost", + "type": "integer", + "format": "default" + }, + { + "name": "output_parameters", + "type": "object", + "format": "default" + }, + { + "name": "emission_factor_co2", + "type": "number", + "format": "default" + } + ], + "missingValues": [ + "" + ], + "primaryKey": "name", + "foreignKeys": [ + { + "fields": "bus", + "reference": { + "resource": "bus", + "fields": "name" + } + }, + { + "fields": "emission_bus_0", + "reference": { + "resource": "bus", + "fields": "name" + } + } + ] + } + }, + { + "path": "data/elements/load.csv", + "profile": "tabular-data-resource", + "name": "load", + "format": "csv", + "mediatype": "text/csv", + "encoding": "utf-8", + "schema": { + "fields": [ + { + "name": "name", + "type": "string", + "format": "default" + }, + { + "name": "amount", + "type": "integer", + "format": "default" + }, + { + "name": "profile", + "type": "string", + "format": "default" + }, + { + "name": "type", + "type": "string", + "format": "default" + }, + { + "name": "bus", + "type": "string", + "format": "default" + } + ], + "missingValues": [ + "" + ], + "primaryKey": "name", + "foreignKeys": [ + { + "fields": "bus", + "reference": { + "resource": "bus", + "fields": "name" + } + }, + { + "fields": "profile", + "reference": { + "resource": "load_profile" + } + } + ] + } + }, + { + "path": "data/sequences/load_profile.csv", + "profile": "tabular-data-resource", + "name": "load_profile", + "format": "csv", + "mediatype": "text/csv", + "encoding": "utf-8", + "schema": { + "fields": [ + { + "name": "timeindex", + "type": "datetime", + "format": "default" + }, + { + "name": "electricity-load-profile", + "type": "number", + "format": "default" + } + ], + "missingValues": [ + "" + ] + } + } + ] +} diff --git a/src/oemof/tabular/examples/datapackages/GHG/scripts/infer.py b/src/oemof/tabular/examples/datapackages/GHG/scripts/infer.py new file mode 100644 index 00000000..87aa7d77 --- /dev/null +++ b/src/oemof/tabular/examples/datapackages/GHG/scripts/infer.py @@ -0,0 +1,25 @@ +""" +Run this script from the root directory of the datapackage to update +or create meta data. +""" + +from oemof.tabular.datapackage import building + +# This part is for testing only: It allows to pass +# the filename of inferred metadata other than the default. +if "kwargs" not in locals(): + kwargs = {} + +building.infer_metadata( + package_name="GHG-test", + foreign_keys={ + "bus": ["commodity_ghg", "load", "excess", "gas_import"], + "profile": ["load"], + "from_bus": ["conversion_ghg"], + "to_bus": ["conversion_ghg"], + "emission_bus_0": ["conversion_ghg", "commodity_ghg"], + "emission_bus_1": ["conversion_ghg"], + "emission_bus_2": ["conversion_ghg"], + }, + **kwargs, +) diff --git a/src/oemof/tabular/examples/scripting/compute.py b/src/oemof/tabular/examples/scripting/compute.py index 18f1303b..250230df 100644 --- a/src/oemof/tabular/examples/scripting/compute.py +++ b/src/oemof/tabular/examples/scripting/compute.py @@ -19,6 +19,7 @@ "investment_multi_period", "foreignkeys", "emission_constraint", + # "GHG", ] for example in examples: print("Running compute example with datapackage {}".format(example)) From 61ad5c57e1e0c3d53070d17578e8db66a4116baf Mon Sep 17 00:00:00 2001 From: SabineHaas Date: Fri, 9 Aug 2024 15:41:43 +0200 Subject: [PATCH 07/18] Add to changelog and authors --- AUTHORS.rst | 1 + CHANGELOG.rst | 1 + 2 files changed, 2 insertions(+) diff --git a/AUTHORS.rst b/AUTHORS.rst index 761a1d08..9fd7ba93 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -15,3 +15,4 @@ Authors * Julian Endres * Felix Maurer * Pierre-Francois Duc +* Sabine Haas diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ceebef4f..3c3fbc83 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,6 +9,7 @@ Unreleased Features * Improve the function to infer package metadata `#173 `_ +* Add facades `CommodityGHG` and `ConversionGHG` to enable multiple output flows (emissions) `#180 `_ Fixes From d7b58a221c6f56c36c07a5580a0493d4da67b36f Mon Sep 17 00:00:00 2001 From: SabineHaas Date: Fri, 9 Aug 2024 15:43:49 +0200 Subject: [PATCH 08/18] Compute GHG --- src/oemof/tabular/examples/scripting/compute.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/tabular/examples/scripting/compute.py b/src/oemof/tabular/examples/scripting/compute.py index 250230df..8d85b461 100644 --- a/src/oemof/tabular/examples/scripting/compute.py +++ b/src/oemof/tabular/examples/scripting/compute.py @@ -19,7 +19,7 @@ "investment_multi_period", "foreignkeys", "emission_constraint", - # "GHG", + "GHG", ] for example in examples: print("Running compute example with datapackage {}".format(example)) From dd1f923dcfe1120e2a3a75988dd453230a4e528e Mon Sep 17 00:00:00 2001 From: SabineHaas Date: Mon, 12 Aug 2024 10:11:25 +0200 Subject: [PATCH 09/18] Adapt commodityGHG test, use negative emissions --- tests/_files/lp_files/commodity_ghg.lp | 18 +++++++++++++++--- tests/test_constraints.py | 4 ++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/tests/_files/lp_files/commodity_ghg.lp b/tests/_files/lp_files/commodity_ghg.lp index 4ea8b2b4..d6057e70 100644 --- a/tests/_files/lp_files/commodity_ghg.lp +++ b/tests/_files/lp_files/commodity_ghg.lp @@ -8,6 +8,18 @@ objective: s.t. +c_e_BusBlock_balance(co2_0_0)_: ++1 flow(gas_commodity_co2_0_0) += 0 + +c_e_BusBlock_balance(co2_0_1)_: ++1 flow(gas_commodity_co2_0_1) += 0 + +c_e_BusBlock_balance(co2_0_2)_: ++1 flow(gas_commodity_co2_0_2) += 0 + c_e_BusBlock_balance(gas_0_0)_: +1 flow(gas_commodity_gas_0_0) = 0 @@ -27,17 +39,17 @@ c_u_SimpleFlowBlock_full_load_time_max_constr(gas_commodity_gas)_: <= 1000 c_e_CommodityGHGBlock_relation(gas_commodity_co2_0_0)_: -+56 flow(gas_commodity_gas_0_0) +-2 flow(gas_commodity_gas_0_0) -1 flow(gas_commodity_co2_0_0) = 0 c_e_CommodityGHGBlock_relation(gas_commodity_co2_0_1)_: -+56 flow(gas_commodity_gas_0_1) +-2 flow(gas_commodity_gas_0_1) -1 flow(gas_commodity_co2_0_1) = 0 c_e_CommodityGHGBlock_relation(gas_commodity_co2_0_2)_: -+56 flow(gas_commodity_gas_0_2) +-2 flow(gas_commodity_gas_0_2) -1 flow(gas_commodity_co2_0_2) = 0 diff --git a/tests/test_constraints.py b/tests/test_constraints.py index ce6a244c..e9b723a7 100644 --- a/tests/test_constraints.py +++ b/tests/test_constraints.py @@ -334,9 +334,9 @@ def test_commodity_ghg(self): amount=1000, marginal_cost=10, output_parameters={"max": [0.9, 0.5, 0.4]}, - emission_factor_co2=56, + emission_factor_co2=-2, ) - self.energysystem.add(bus_gas, commodity) + self.energysystem.add(bus_gas, bus_co2, commodity) self.compare_to_reference_lp("commodity_ghg.lp") From a11c2e5668406610fc83398ba5eb8aa6fb93cdb8 Mon Sep 17 00:00:00 2001 From: SabineHaas Date: Mon, 12 Aug 2024 11:12:35 +0200 Subject: [PATCH 10/18] GHG flow commodityGHG bidirectional to allow negative emission factors --- src/oemof/tabular/facades/commodity_ghg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/tabular/facades/commodity_ghg.py b/src/oemof/tabular/facades/commodity_ghg.py index 5e2bd6e7..86262814 100644 --- a/src/oemof/tabular/facades/commodity_ghg.py +++ b/src/oemof/tabular/facades/commodity_ghg.py @@ -88,7 +88,7 @@ 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()}) + self.outputs.update({bus: Flow(bidirectional=True)}) kwargs.pop(key) def init_emission_factors(self, buses, kwargs): From 3937ee93332912ad1c1636d75a6bad3924eb3c4c Mon Sep 17 00:00:00 2001 From: SabineHaas Date: Mon, 12 Aug 2024 11:12:47 +0200 Subject: [PATCH 11/18] Delete pass statement --- src/oemof/tabular/facades/commodity_ghg.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/oemof/tabular/facades/commodity_ghg.py b/src/oemof/tabular/facades/commodity_ghg.py index 86262814..5f4c3535 100644 --- a/src/oemof/tabular/facades/commodity_ghg.py +++ b/src/oemof/tabular/facades/commodity_ghg.py @@ -191,7 +191,6 @@ def _emission_relation(block): 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( From 18e0b4d4bf57268001d0364f86a317bf029af534 Mon Sep 17 00:00:00 2001 From: SabineHaas Date: Mon, 12 Aug 2024 11:13:29 +0200 Subject: [PATCH 12/18] Adapt values of test files --- .../datapackages/GHG/data/elements/commodityGHG_import.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/tabular/examples/datapackages/GHG/data/elements/commodityGHG_import.csv b/src/oemof/tabular/examples/datapackages/GHG/data/elements/commodityGHG_import.csv index 07999f6c..fc5e7c20 100644 --- a/src/oemof/tabular/examples/datapackages/GHG/data/elements/commodityGHG_import.csv +++ b/src/oemof/tabular/examples/datapackages/GHG/data/elements/commodityGHG_import.csv @@ -1,3 +1,3 @@ name;type;carrier;amount;bus;emission_bus_0;marginal_cost;output_parameters;emission_factor_co2 -green_gas_import;commodity_ghg;gas;300;gas_bus;co2;2;{"max": [1, 1, 1]};0 +green_gas_import;commodity_ghg;gas;300;gas_bus;co2;20;{"max": [1, 1, 1]};-56 fossile_gas_import;commodity_ghg;gas;200;gas_bus;co2;10;{"max": [1, 0, 0]};56 From 7cc9e31b62a1f7cd7a5c5fa03f71c00758c7ef93 Mon Sep 17 00:00:00 2001 From: SabineHaas Date: Tue, 13 Aug 2024 11:16:07 +0200 Subject: [PATCH 13/18] Mention "green house gases" in docstrings for connection to class name --- src/oemof/tabular/facades/commodity_ghg.py | 10 +++++----- src/oemof/tabular/facades/conversion_ghg.py | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/oemof/tabular/facades/commodity_ghg.py b/src/oemof/tabular/facades/commodity_ghg.py index 5f4c3535..badee68a 100644 --- a/src/oemof/tabular/facades/commodity_ghg.py +++ b/src/oemof/tabular/facades/commodity_ghg.py @@ -13,7 +13,7 @@ @dataclasses.dataclass(unsafe_hash=False, frozen=False, eq=False) class CommodityGHG(Commodity): r""" - Commodity element with one output and additionally emission outputs. + Commodity element with one output and additionally green house gas outputs. Parameters ---------- @@ -34,12 +34,12 @@ class CommodityGHG(Commodity): Notes ----- - Emission buses are defined by starting with 'emission_bus', see Examples - section. + Emission buses carring the green house gases (GHG) are defined by starting + with 'emission_bus', see Examples section. Emission factors are defined by the following naming convention: 'emission_factor_. - The realation between the main output (`bus`) and the emissions are set via - :class:`~oemof.tabular.facades.commodity_ghg.CommodityGHGBlock`. + The realation between the main output (`bus`) and the emission buses are + set via :class:`~oemof.tabular.facades.commodity_ghg.CommodityGHGBlock`. For additional constraints set through `output_parameters` see oemof.solph.Flow class. diff --git a/src/oemof/tabular/facades/conversion_ghg.py b/src/oemof/tabular/facades/conversion_ghg.py index 931f447c..fbd18f41 100644 --- a/src/oemof/tabular/facades/conversion_ghg.py +++ b/src/oemof/tabular/facades/conversion_ghg.py @@ -11,7 +11,7 @@ @dataclasses.dataclass(unsafe_hash=False, frozen=False, eq=False) class ConversionGHG(Conversion): r""" - Conversion unit with one input, one output and emission outputs. + Conversion unit with one input, one output and green house gas outputs. Cost parameters like `carrier_cost` are associated with `from_bus` like in Conversion facade. @@ -53,8 +53,8 @@ class ConversionGHG(Conversion): Notes ----- - Emission buses are defined by starting with 'emission_bus', see Examples - section. + Emission buses carring the green house gases (GHG) are defined by starting + with 'emission_bus', see Examples section. Emission factors are defined by the following naming convention: 'emission_factor_. From 322a9aac2196579ac125d1a514b985759849aca7 Mon Sep 17 00:00:00 2001 From: SabineHaas Date: Tue, 13 Aug 2024 11:32:04 +0200 Subject: [PATCH 14/18] Add new facades to documentation --- docs/usage.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/usage.rst b/docs/usage.rst index 00f7d608..46cdb11f 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -56,9 +56,11 @@ Currently we provide the following facades: * :py:class:`~oemof.tabular.facades.ExtractionTurbine` * :py:class:`~oemof.tabular.facades.Commodity` * :py:class:`~oemof.tabular.facades.Conversion` -* :py:class:`~oemof.tabular.facades.Load`. +* :py:class:`~oemof.tabular.facades.Load` * :py:class:`~oemof.tabular.facades.Link` * :py:class:`~oemof.tabular.facades.Excess` +* :py:class:`~oemof.tabular.facades.CommodityGHG`: a commodity unit with green house gases +* :py:class:`~oemof.tabular.facades.ConversionGHG`: a conversion unit with green house gases. These can be mixed with all oemof solph classes if your are scripting. From 6e01808c246e003bc872c2fbc518f2285edec77c Mon Sep 17 00:00:00 2001 From: SabineHaas Date: Tue, 13 Aug 2024 11:32:50 +0200 Subject: [PATCH 15/18] Skip test_examples_datapackages_scripts_infer for GHG facades --- tests/test_examples.py | 54 ++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/tests/test_examples.py b/tests/test_examples.py index 4e10e694..4d21a3c3 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -91,32 +91,40 @@ def test_examples_datapackages_scripts_infer(): script_path = datapackage_path / "scripts" / script if script_path.exists(): - print( - "Running infer script for {} ...".format(example_datapackage) - ) - exec( - "kwargs = {} \n" - f"kwargs['path'] = '{datapackage_path}' \n" - f"kwargs['metadata_filename'] = " - f"'datapackage_{example_datapackage}.json' \n" - + open(script_path).read(), - ) + if script_path.parts[-3] == "GHG": + print( + "Example 'GHG' is not tested so far with infer.py script." + ) + else: + print( + "Running infer script for {} ...".format( + example_datapackage + ) + ) + exec( + "kwargs = {} \n" + f"kwargs['path'] = '{datapackage_path}' \n" + f"kwargs['metadata_filename'] = " + f"'datapackage_{example_datapackage}.json' \n" + + open(script_path).read(), + ) - # Move metadata string to .oemof directory - test_filepath = ( - datapackage_path / f"datapackage_{example_datapackage}.json" - ) - new_filepath = ( - pathlib.PosixPath(helpers.extend_basic_path("metadata")) - / f"datapackage_{example_datapackage}.json" - ) - os.rename(test_filepath, new_filepath) + # Move metadata string to .oemof directory + test_filepath = ( + datapackage_path + / f"datapackage_{example_datapackage}.json" + ) + new_filepath = ( + pathlib.PosixPath(helpers.extend_basic_path("metadata")) + / f"datapackage_{example_datapackage}.json" + ) + os.rename(test_filepath, new_filepath) - ref_filepath = datapackage_path / "datapackage.json" + ref_filepath = datapackage_path / "datapackage.json" - with open(new_filepath) as new_file: - with open(ref_filepath) as ref_file: - compare_json_files(new_file, ref_file) + with open(new_filepath) as new_file: + with open(ref_filepath) as ref_file: + compare_json_files(new_file, ref_file) def test_custom_foreign_keys(monkeypatch): From 056e0d4dc3615ad4beabffb88f03c02164c8b7d0 Mon Sep 17 00:00:00 2001 From: SabineHaas Date: Wed, 14 Aug 2024 10:46:48 +0200 Subject: [PATCH 16/18] Adapt test lp file to bidirectional flow of commodityGHG --- tests/_files/lp_files/commodity_ghg.lp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/_files/lp_files/commodity_ghg.lp b/tests/_files/lp_files/commodity_ghg.lp index d6057e70..48399040 100644 --- a/tests/_files/lp_files/commodity_ghg.lp +++ b/tests/_files/lp_files/commodity_ghg.lp @@ -54,10 +54,10 @@ c_e_CommodityGHGBlock_relation(gas_commodity_co2_0_2)_: = 0 bounds + -inf <= flow(gas_commodity_co2_0_0) <= +inf + -inf <= flow(gas_commodity_co2_0_1) <= +inf + -inf <= flow(gas_commodity_co2_0_2) <= +inf 0 <= flow(gas_commodity_gas_0_0) <= 900.0 0 <= flow(gas_commodity_gas_0_1) <= 500.0 0 <= flow(gas_commodity_gas_0_2) <= 400.0 - 0 <= flow(gas_commodity_co2_0_0) <= +inf - 0 <= flow(gas_commodity_co2_0_1) <= +inf - 0 <= flow(gas_commodity_co2_0_2) <= +inf end From c0f20388b27d5290a64aeeabed2a92072d71b5a6 Mon Sep 17 00:00:00 2001 From: SabineHaas Date: Wed, 14 Aug 2024 10:47:23 +0200 Subject: [PATCH 17/18] Rename variable to avoid missunderstandings --- src/oemof/tabular/facades/commodity_ghg.py | 5 +++-- src/oemof/tabular/facades/conversion_ghg.py | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/oemof/tabular/facades/commodity_ghg.py b/src/oemof/tabular/facades/commodity_ghg.py index badee68a..74b92c1d 100644 --- a/src/oemof/tabular/facades/commodity_ghg.py +++ b/src/oemof/tabular/facades/commodity_ghg.py @@ -86,9 +86,10 @@ def __init__(self, **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()): + for key, value in list(kwargs.items()): if key.startswith("emission_bus"): - self.outputs.update({bus: Flow(bidirectional=True)}) + # then value is a solph.Bus object and is added to self.outputs + self.outputs.update({value: Flow(bidirectional=True)}) kwargs.pop(key) def init_emission_factors(self, buses, kwargs): diff --git a/src/oemof/tabular/facades/conversion_ghg.py b/src/oemof/tabular/facades/conversion_ghg.py index fbd18f41..472b4bb6 100644 --- a/src/oemof/tabular/facades/conversion_ghg.py +++ b/src/oemof/tabular/facades/conversion_ghg.py @@ -101,9 +101,10 @@ def __init__(self, **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()): + for key, value in list(kwargs.items()): if key.startswith("emission_bus"): - self.outputs.update({bus: Flow()}) + # then value is a solph.Bus object and is added to self.outputs + self.outputs.update({value: Flow()}) kwargs.pop(key) def init_emission_factors(self, buses, kwargs): From 51bb27219fa43bd7ccf71a1edcfebb65a634b1fa Mon Sep 17 00:00:00 2001 From: SabineHaas Date: Wed, 14 Aug 2024 10:53:40 +0200 Subject: [PATCH 18/18] Add warnings for missing emission bus oder emission factor --- src/oemof/tabular/facades/commodity_ghg.py | 21 ++++++++++++----- src/oemof/tabular/facades/conversion_ghg.py | 25 ++++++++++++++++++--- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/oemof/tabular/facades/commodity_ghg.py b/src/oemof/tabular/facades/commodity_ghg.py index 74b92c1d..a454608b 100644 --- a/src/oemof/tabular/facades/commodity_ghg.py +++ b/src/oemof/tabular/facades/commodity_ghg.py @@ -98,9 +98,18 @@ def init_emission_factors(self, buses, kwargs): 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] + try: + bus = [ + bus + for bus in buses.items() + if bus[1].label == bus_label + ][0][1] + except IndexError: + raise Warning( + f"Emission factor is given for a non-existent emission" + f" bus: '{bus_label}'. Check your inputs for " + f"'{self.label}' of type '{self.type}'. " + ) emission_factors.update({bus: sequence(value)}) kwargs.pop(key) return emission_factors @@ -194,9 +203,9 @@ def _emission_relation(block): except KeyError: raise KeyError( "Error in constraint creation", - "source: {0}, target: {1}".format( - n.label, o.label - ), + "source: {0}, target: {1}. You supposedly " + "forgot to define an emission factor for " + "this target.".format(n.label, o.label), ) self.relation_build = BuildAction(rule=_emission_relation) diff --git a/src/oemof/tabular/facades/conversion_ghg.py b/src/oemof/tabular/facades/conversion_ghg.py index 472b4bb6..2e34530c 100644 --- a/src/oemof/tabular/facades/conversion_ghg.py +++ b/src/oemof/tabular/facades/conversion_ghg.py @@ -112,8 +112,27 @@ def init_emission_factors(self, buses, kwargs): 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] + try: + bus = [ + bus + for bus in buses.items() + if bus[1].label == bus_label + ][0][1] + except IndexError: + raise Warning( + f"Emission factor is given for a non-existent emission" + f" bus: '{bus_label}'. Check your inputs for " + f"'{self.label}' of type '{self.type}'. " + ) self.conversion_factors.update({bus: sequence(value)}) kwargs.pop(key) + + # check that every bus has a conversion factor, otherwise an error + # occurs in oemof.solph.components._converter.py + if not len(self.outputs) + len(self.inputs) == len( + self.conversion_factors + ): + raise Warning( + f"Every emission_bus needs an emission_factor. Check your " + f"inputs for '{self.label}' of type '{self.type}'." + )