Skip to content

Commit

Permalink
Merge pull request #223 from dylanjm/resourcegraph
Browse files Browse the repository at this point in the history
Resourcegraph
  • Loading branch information
dgarrett622 authored Nov 7, 2022
2 parents 3fc4137 + f8dac41 commit 18ae727
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 3 deletions.
1 change: 1 addition & 0 deletions dependencies.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<dependencies>
<main>
<networkx/>
<dill>0.3.5</dill>
<dispatches source='pip' optional='True'>1.0</dispatches>
<pyomo source='forge'/>
Expand Down
133 changes: 133 additions & 0 deletions src/NetworkPlot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Copyright 2020, Battelle Energy Alliance, LLC
# ALL RIGHTS RESERVED
"""
This module defines logic to create a resource utilization network
graph for HERON Simulations.
"""
import networkx as nx
import matplotlib as mpl
mpl.use('Agg') # Prevents the module from blocking while plotting
import matplotlib.pyplot as plt


class NetworkPlot:
"""
Represents a network graph visualization of the resources found
in a HERON system.
"""

def __init__(self, components: list) -> None:
"""
Initialize the network plot.
@ In, components, list, the components defined by the input file.
@ Out, None
"""
self._components = components
self._resources = set()
self._producers_and_consumers = set()
self._capacities = {}
self._edges = []
self._graph = nx.DiGraph()
self._find_nodes_and_edges()
self._plot_graph()
self._build_table()

def _find_nodes_and_edges(self) -> None:
"""
Iterate over the components to determine nodes and their
associated directional edges.
@ In, None
@ Out, None
"""
for c in self._components:
self._producers_and_consumers.add(c.name)

rsc_in = c.get_inputs()
for ri in rsc_in:
self._resources.add(ri)
self._graph.add_edge(ri, c.name)
self._edges.append((ri, c.name))

rsc_out = c.get_outputs()
for ro in rsc_out:
self._resources.add(ro)
self._graph.add_edge(c.name, ro)
self._edges.append((c.name, ro))

def _build_table(self) -> None:
"""
Table should have two major sections: economic info and optimization parameters
Economic info:
- Cash flows (just the names?)
- Lifetime?
Optimization settings:
- dispatch (fixed, independent, dependent)
- optimized, swept, or fixed?
- capacity (optimization bounds, sweep values, or fixed value)
@ In, None
@ Out, None
"""
col_labels = ['Dispatchable?', 'Governed?']
cell_text = []
row_labels = []

for c in self._components:
row_labels.append(c.name)
cell_text.append([c.is_dispatchable(), c.is_governed()])

plt.table(cell_text, rowLabels=row_labels, colLabels=col_labels, loc='bottom')

def _plot_graph(self) -> None:
"""
Plots and formats the graph
@ In, None
@ Out, None
"""
tech_options = { # TODO make this something that can be done dynamically
"node_size": 1000,
"node_color": "#FCEDDA",
"edgecolors": "#FCEDDA",
"linewidths": 1
}

resrc_options = {
"node_size": 1500,
"node_color": "#EE4E34",
"edgecolors": "#EE4E34",
"linewidths": 1
}

label_options = {
"font_size": 8,
"font_weight": "normal",
"font_color": "black",
}

edge_options = {
'edge_color': 'black',
"width": 1,
'arrows': True,
'arrowsize': 20
}

fig, ax = plt.subplots(figsize=(7,7))
pos = nx.spring_layout(self._graph)

nx.draw_networkx_nodes(self._graph, pos, nodelist=list(self._resources), **resrc_options)
nx.draw_networkx_nodes(self._graph, pos, nodelist=list(self._producers_and_consumers), **tech_options)
nx.draw_networkx_labels(self._graph, pos, **label_options)
nx.draw_networkx_edges(self._graph, pos, node_size=1500, **edge_options)

ax.axis('off')
fig.set_facecolor('darkgrey')

def save(self, filename: str) -> None:
"""
Save resource graph to file
@ In, filename, str, path to file
@ Out, None
"""
plt.savefig(filename)
21 changes: 18 additions & 3 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from HERON.src.base import Base
from HERON.src.Moped import MOPED
from HERON.src.Herd import HERD
from HERON.src.NetworkPlot import NetworkPlot

from ravenframework.MessageHandler import MessageHandler

Expand All @@ -45,7 +46,7 @@ def __init__(self):
'suppressErrs': False,})
self.messageHandler = messageHandler

def read_input(self, name):
def read_input(self, name: str) -> None:
"""
Loads data from input
@ In, name, str, name of file to read from
Expand All @@ -68,7 +69,7 @@ def __repr__(self):
"""
return '<HERON Simulation>'

def print_me(self, tabs=0, tab=' '):
def print_me(self, tabs=0, tab=' ') -> None:
"""
Prints info about self.
@ In, tabs, int, number of tabs to insert
Expand All @@ -86,6 +87,19 @@ def print_me(self, tabs=0, tab=' '):
for source in self._sources:
source.print_me(tabs=tabs+1, tab=tab)

def plot_resource_graph(self) -> None:
"""
Plots the resource graph of the HERON simulation using components
from the input file.
@ In, None
@ Out, None
"""
if self._case.debug['enabled']: # TODO do this every time?
graph = NetworkPlot(self._components)
img_path = os.path.join(self._input_dir, 'network.png')
graph.save(img_path)

def create_raven_workflow(self, case=None):
"""
Loads, modifies, and writes a RAVEN template workflow based on the Case.
Expand Down Expand Up @@ -154,13 +168,14 @@ def main():
sim.read_input(args.xml_input_file) # TODO expand to use arguments?
# print details
sim.print_me()
sim.plot_resource_graph()

if sim._case._workflow == 'standard':
sim.create_raven_workflow()
elif sim._case._workflow == 'MOPED':
sim.run_moped_workflow()
elif sim._case._workflow == 'DISPATCHES':
sim.run_dispatches_workflow()
# TODO someday? sim.run()


if __name__ == '__main__':
Expand Down
4 changes: 4 additions & 0 deletions tests/integration_tests/mechanics/debug_mode/tests
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
type = Exists
output = 'Debug_Run_o/dispatch_id0_y10_c0.png Debug_Run_o/dispatch_id0_y11_c0.png Debug_Run_o/dispatch_id1_y10_c0.png Debug_Run_o/dispatch_id1_y11_c0.png'
[../]
[./debug_plot]
type = Exists
output = 'network.png'
[../]
[../]
[]

Expand Down

0 comments on commit 18ae727

Please sign in to comment.