Skip to content

Commit

Permalink
Merge pull request #46 from oemof/feature/node_dict_warning
Browse files Browse the repository at this point in the history
Improve warnings, e.g. add warning for experimental node access API and fix other warnings that are
not meaningful for the user.
  • Loading branch information
p-snft authored Jan 11, 2024
2 parents e246c86 + b550134 commit 21d727f
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 77 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
18 changes: 13 additions & 5 deletions src/oemof/network/energy_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -86,9 +87,9 @@ class EnergySystem:
<oemof.core.network.Entity>` will always be grouped by their :attr:`uid
<oemof.core.network.Entity.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
Expand All @@ -100,7 +101,7 @@ class EnergySystem:
>>> bus is es.groups['electricity']
False
>>> es.groups['electricity']
"<oemof.network.network.nodes.Bus: 'electricity'>"
"<oemof.network.network.nodes.Node: 'electricity'>"
For simple user defined groupings, you can just supply a function that
computes a key from an :class:`entity <oemof.core.network.Entity>` and the
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
112 changes: 60 additions & 52 deletions src/oemof/network/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
SPDX-License-Identifier: MIT
"""

import warnings

import networkx as nx


Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
12 changes: 5 additions & 7 deletions tests/test_energy_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down
27 changes: 15 additions & 12 deletions tests/test_network_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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")
Expand Down Expand Up @@ -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="<B1>")
self.es.add(n1)
assert self.es.node["<B1>"] == n1
n2 = Node(label="<B2>")
self.es.add(n2)
assert self.es.node["<B2>"] == n2
n3 = Node(label="<TF1>", inputs=[n1], outputs=[n2])
self.es.add(n3)
assert self.es.node["<TF1>"] == n3
with pytest.warns(
match="API to access nodes by label is experimental"
):
n1 = Node(label="<B1>")
self.es.add(n1)
assert self.es.node["<B1>"] == n1
n2 = Node(label="<B2>")
self.es.add(n2)
assert self.es.node["<B2>"] == n2
n3 = Node(label="<TF1>", inputs=[n1], outputs=[n2])
self.es.add(n3)
assert self.es.node["<TF1>"] == n3


def test_deprecated_classes():
Expand Down

0 comments on commit 21d727f

Please sign in to comment.