diff --git a/setup.py b/setup.py index 3fb19fb..c67ee1a 100644 --- a/setup.py +++ b/setup.py @@ -77,7 +77,7 @@ def read(*names, **kwargs): # eg: 'keyword1', 'keyword2', 'keyword3', ], python_requires=">=3.7", - install_requires=["pandas", "blinker", "dill", "networkx"], + install_requires=["pandas", "blinker", "dill", "networkx", "oemof.tools"], extras_require={ "dev": ["pytest"], # eg: diff --git a/src/oemof/network/energy_system.py b/src/oemof/network/energy_system.py index ce45c83..3b91818 100644 --- a/src/oemof/network/energy_system.py +++ b/src/oemof/network/energy_system.py @@ -22,6 +22,7 @@ import blinker import dill as pickle +from oemof.tools import debugging from oemof.network.groupings import DEFAULT as BY_UID from oemof.network.groupings import Entities @@ -86,9 +87,9 @@ class EnergySystem: ` will always be grouped by their :attr:`uid `: - >>> from oemof.network.network import Bus, Sink + >>> from oemof.network.network import Node >>> es = EnergySystem() - >>> bus = Bus(label='electricity') + >>> bus = Node(label='electricity') >>> es.add(bus) >>> bus is es.groups['electricity'] True @@ -100,7 +101,7 @@ class EnergySystem: >>> bus is es.groups['electricity'] False >>> es.groups['electricity'] - "" + "" For simple user defined groupings, you can just supply a function that computes a key from an :class:`entity ` and the @@ -110,12 +111,14 @@ class EnergySystem: their `type`: >>> es = EnergySystem(groupings=[type]) - >>> buses = set(Bus(label="Bus {}".format(i)) for i in range(9)) + >>> buses = set(Node(label="Node {}".format(i)) for i in range(9)) >>> es.add(*buses) + >>> class Sink(Node): + ... pass >>> components = set(Sink(label="Component {}".format(i)) ... for i in range(9)) >>> es.add(*components) - >>> buses == es.groups[Bus] + >>> buses == es.groups[Node] True >>> components == es.groups[Sink] True @@ -195,6 +198,11 @@ def groups(self): @property def node(self): + msg = ( + "The API to access nodes by label is experimental" + " and might change without prior notice." + ) + warnings.warn(msg, debugging.ExperimentalFeatureWarning) return self._nodes @property diff --git a/src/oemof/network/graph.py b/src/oemof/network/graph.py index d8b7024..2b9295c 100644 --- a/src/oemof/network/graph.py +++ b/src/oemof/network/graph.py @@ -12,6 +12,8 @@ SPDX-License-Identifier: MIT """ +import warnings + import networkx as nx @@ -47,21 +49,21 @@ def create_nx_graph( -------- >>> import os >>> import pandas as pd - >>> from oemof.network.network import Bus, Sink, Transformer + >>> from oemof.network.network import Node >>> from oemof.network.energy_system import EnergySystem >>> import oemof.network.graph as grph >>> datetimeindex = pd.date_range('1/1/2017', periods=3, freq='H') >>> es = EnergySystem(timeindex=datetimeindex) - >>> b_gas = Bus(label='b_gas') - >>> bel1 = Bus(label='bel1') - >>> bel2 = Bus(label='bel2') - >>> demand_el = Sink(label='demand_el', inputs = [bel1]) - >>> pp_gas = Transformer(label=('pp', 'gas'), - ... inputs=[b_gas], - ... outputs=[bel1]) - >>> line_to2 = Transformer(label='line_to2', inputs=[bel1], outputs=[bel2]) - >>> line_from2 = Transformer(label='line_from2', - ... inputs=[bel2], outputs=[bel1]) + >>> b_gas = Node(label='b_gas') + >>> bel1 = Node(label='bel1') + >>> bel2 = Node(label='bel2') + >>> demand_el = Node(label='demand_el', inputs = [bel1]) + >>> pp_gas = Node(label=('pp', 'gas'), + ... inputs=[b_gas], + ... outputs=[bel1]) + >>> line_to2 = Node(label='line_to2', inputs=[bel1], outputs=[bel2]) + >>> line_from2 = Node(label='line_from2', + ... inputs=[bel2], outputs=[bel1]) >>> es.add(b_gas, bel1, demand_el, pp_gas, bel2, line_to2, line_from2) >>> my_graph = grph.create_nx_graph(es) >>> # export graph as .graphml for programs like Yed where it can be @@ -93,46 +95,52 @@ def create_nx_graph( Needs graphviz and networkx (>= v.1.11) to work properly. Tested on Ubuntu 16.04 x64 and solydxk (debian 9). """ - # construct graph from nodes and flows - grph = nx.DiGraph() - - # add nodes - for label in energy_system.node.keys(): - grph.add_node(str(label), label=str(label)) - - # add labeled flows on directed edge if an optimization_model has been - # passed or undirected edge otherwise - for n in energy_system.nodes: - for i in n.inputs.keys(): - weight = getattr( - energy_system.flows()[(i, n)], "nominal_value", None - ) - if weight is None: - grph.add_edge(str(i.label), str(n.label)) - else: - grph.add_edge( - str(i.label), str(n.label), weigth=format(weight, ".2f") + with warnings.catch_warnings(): + # suppress ExperimentalFeatureWarnungs + warnings.simplefilter("ignore") + + # construct graph from nodes and flows + grph = nx.DiGraph() + + # add nodes + for label in energy_system.node.keys(): + grph.add_node(str(label), label=str(label)) + + # add labeled flows on directed edge if an optimization_model has been + # passed or undirected edge otherwise + for n in energy_system.nodes: + for i in n.inputs.keys(): + weight = getattr( + energy_system.flows()[(i, n)], "nominal_value", None ) - - # remove nodes and edges based on precise labels - if remove_nodes is not None: - grph.remove_nodes_from(remove_nodes) - if remove_edges is not None: - grph.remove_edges_from(remove_edges) - - # remove nodes based on substrings - if remove_nodes_with_substrings is not None: - for i in remove_nodes_with_substrings: - remove_nodes = [ - str(label) - for label in energy_system.node.keys() - if i in str(label) - ] + if weight is None: + grph.add_edge(str(i.label), str(n.label)) + else: + grph.add_edge( + str(i.label), + str(n.label), + weigth=format(weight, ".2f"), + ) + + # remove nodes and edges based on precise labels + if remove_nodes is not None: grph.remove_nodes_from(remove_nodes) - - if filename is not None: - if filename[-8:] != ".graphml": - filename = filename + ".graphml" - nx.write_graphml(grph, filename) - - return grph + if remove_edges is not None: + grph.remove_edges_from(remove_edges) + + # remove nodes based on substrings + if remove_nodes_with_substrings is not None: + for i in remove_nodes_with_substrings: + remove_nodes = [ + str(label) + for label in energy_system.node.keys() + if i in str(label) + ] + grph.remove_nodes_from(remove_nodes) + + if filename is not None: + if filename[-8:] != ".graphml": + filename = filename + ".graphml" + nx.write_graphml(grph, filename) + + return grph diff --git a/tests/test_energy_system.py b/tests/test_energy_system.py index 6a5f533..9616ccf 100644 --- a/tests/test_energy_system.py +++ b/tests/test_energy_system.py @@ -15,24 +15,24 @@ import pytest -from oemof.network import energy_system as es +from oemof.network.energy_system import EnergySystem from oemof.network.network import Edge from oemof.network.network.nodes import Node def test_ensys_init(): node = Node("label") - ensys = es.EnergySystem(nodes=[node]) + ensys = EnergySystem(nodes=[node]) assert node in ensys.nodes with pytest.warns(FutureWarning): - ensys = es.EnergySystem(entities=[node]) + ensys = EnergySystem(entities=[node]) assert node in ensys.nodes class TestsEnergySystem: def setup_method(self): - self.es = es.EnergySystem() + self.es = EnergySystem() def test_add_nodes(self): assert not self.es.nodes @@ -91,9 +91,7 @@ def subscriber(sender, **kwargs): subscriber.called = False - es.EnergySystem.signals[es.EnergySystem.add].connect( - subscriber, sender=node - ) + EnergySystem.signals[EnergySystem.add].connect(subscriber, sender=node) self.es.add(node) assert subscriber.called, ( "\nExpected `subscriber.called` to be `True`.\n" diff --git a/tests/test_network_classes.py b/tests/test_network_classes.py index 3dba933..81e7ab6 100644 --- a/tests/test_network_classes.py +++ b/tests/test_network_classes.py @@ -19,7 +19,7 @@ import pytest -from oemof.network.energy_system import EnergySystem as EnSys +from oemof.network.energy_system import EnergySystem from oemof.network.network import Bus from oemof.network.network import Sink from oemof.network.network import Source @@ -31,7 +31,7 @@ class TestsNode: def setup_method(self): - self.energysystem = EnSys() + self.energysystem = EnergySystem() def test_entity_initialisation(self): entity = Entity(label="foo") @@ -328,18 +328,21 @@ def test_flow_setter(self): class TestsEnergySystemNodesIntegration: def setup_method(self): - self.es = EnSys() + self.es = EnergySystem() def test_entity_registration(self): - n1 = Node(label="") - self.es.add(n1) - assert self.es.node[""] == n1 - n2 = Node(label="") - self.es.add(n2) - assert self.es.node[""] == n2 - n3 = Node(label="", inputs=[n1], outputs=[n2]) - self.es.add(n3) - assert self.es.node[""] == n3 + with pytest.warns( + match="API to access nodes by label is experimental" + ): + n1 = Node(label="") + self.es.add(n1) + assert self.es.node[""] == n1 + n2 = Node(label="") + self.es.add(n2) + assert self.es.node[""] == n2 + n3 = Node(label="", inputs=[n1], outputs=[n2]) + self.es.add(n3) + assert self.es.node[""] == n3 def test_deprecated_classes():