Skip to content
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 warning for experimental node access API #46

Merged
merged 6 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading