diff --git a/config_copy.yml b/config_copy.yml index f972a46..c36cc0c 100644 --- a/config_copy.yml +++ b/config_copy.yml @@ -1,6 +1,6 @@ name: ControlModelM2_sim -max_time: 100 -num_trials: 2 +max_time: 15 +num_trials: 1 network_params: generator: barabasi_albert_graph n: 100 diff --git a/run.py b/run.py index 7d57b95..dd3abdd 100644 --- a/run.py +++ b/run.py @@ -11,5 +11,5 @@ def run(model, params=None): if __name__ == "__main__": - soil = Model(dump=True) + soil = Model(dump=False) run(soil) diff --git a/server.py b/server.py index a8d0417..46f36ed 100644 --- a/server.py +++ b/server.py @@ -12,6 +12,7 @@ import logging import logging import threading import io +import networkx as nx from datetime import timedelta from contextlib import contextmanager @@ -88,7 +89,6 @@ class SocketHandler(tornado.websocket.WebSocketHandler): self.send_log('INFO.soil', 'Using config: {name}'.format(name=self.config['name'])) self.name = self.config['name'] - self.run_simulation() settings = [] @@ -114,7 +114,7 @@ class SocketHandler(tornado.websocket.WebSocketHandler): logger.info('Trial {} requested!'.format(msg['data'])) self.send_log('INFO.user', 'Trial {} requested!'.format(msg['data'])) self.write_message({'type': 'get_trial', - 'data': self.application.model.get_trial(self.name, msg['data']) }) + 'data': self.get_trial( int(msg['data'] )) }) elif msg['type'] == 'run_simulation': self.send_log('INFO.soil', 'Running new simulation for {name}'.format(name=self.config['name'])) @@ -147,7 +147,7 @@ class SocketHandler(tornado.websocket.WebSocketHandler): def run_simulation(self): # Run simulation and capture logs with self.logging(self.application.model.name): - self.application.model.run(self.config) + self.simulation = self.application.model.run(self.config) trials = [] for i in range(self.config['num_trials']): @@ -155,6 +155,10 @@ class SocketHandler(tornado.websocket.WebSocketHandler): self.write_message({'type': 'trials', 'data': trials }) + def get_trial(self, trial): + G = self.simulation[trial].history_to_graph() + return nx.node_link_data(G) + @contextmanager def logging(self, logger): self.capture_logging = True diff --git a/templates/js/socket.js b/templates/js/socket.js index bfa031d..3acc133 100755 --- a/templates/js/socket.js +++ b/templates/js/socket.js @@ -21,27 +21,27 @@ ws.onmessage = function(message) { break; case 'get_trial': - console.log(msg['data']); - GraphVisualization.import(convertJSON(msg['data']['graph']), msg['data']['models'], function() { + //console.log(msg['data']); + GraphVisualization.import(convertJSON(msg['data']), function() { $('#load').hide(); reset_configuration(); set_configuration(); $('#home_menu').click(function() { setTimeout(function() { reset_timeline(); - set_timeline(msg['data']['graph']); + set_timeline(msg['data']); }, 1000); }); reset_timeline(); - set_timeline(msg['data']['graph']); + set_timeline(msg['data']); }); $('#charts .chart').removeClass('no-data'); - set_chart_nodes(msg['data']['graph'], chart_nodes) - set_chart_attrs(msg['data']['graph'], chart_attrs, $('.config-item #properties').val()) + set_chart_nodes(msg['data'], chart_nodes) + set_chart_attrs(msg['data'], chart_attrs, $('.config-item #properties').val()) $('.config-item #properties').change(function() { chart_attrs.destroy(); chart_attrs = create_chart(width_chart, height_chart, 'Time', 'Attributes', '#chart_attrs'); - set_chart_attrs(msg['data']['graph'], chart_attrs, $('.config-item #properties').val()) + set_chart_attrs(msg['data'], chart_attrs, $('.config-item #properties').val()) }); break; diff --git a/templates/js/template.js b/templates/js/template.js index 8990082..15eb1df 100644 --- a/templates/js/template.js +++ b/templates/js/template.js @@ -82,7 +82,7 @@ var initGUI = function(model_params) { addSliderInput(model_params[option]['label'], model_params[option]['value']); break; default: - console.log('Input type not defined!'); + console.log(model_params[option]['type'] + ' not defined!'); break; } } diff --git a/templates/js/visualization.js b/templates/js/visualization.js index bcca4f0..49a8598 100644 --- a/templates/js/visualization.js +++ b/templates/js/visualization.js @@ -13,7 +13,8 @@ // Private constants var focus_opacity = 0.1, - radius = 8; + radius = 8, + required_node = ['id', 'index', 'label', 'px', 'py', 'spells', 'weight', 'x', 'y']; // Private variables var width, @@ -43,6 +44,13 @@ return ( this > min && this <= max ) || ( min === 0 && this === 0 ); }; + Number.prototype.type = function() { + if ( typeof(this) === 'number' ) + return ( Number.isInteger(this) ) ? 'int' : 'float'; + else + return false; + } + var lastFocusNode; var _helpers = { set_node: function(node, property, time) { @@ -118,7 +126,14 @@ link.style('opacity', function(o) { return o.source.index == d.index || o.target.index == d.index ? 1 : focus_opacity; }); + }, + push_once: function(array, item, key) { + for (var i in array) { + if ( array[i][key] == item[key] ) return false; } + array.push(item); + return true; + } } @@ -253,6 +268,22 @@ self.GraphVisualization.statistics = statistics } + function get_models(graph) { + + var models = { 'dynamic': [], 'static': [] } + + graph['nodes'].forEach(function(node) { + for ( var att in node ) { + if (!required_node.includes(att)) { + if ( Array.isArray(node[att]) ) _helpers.push_once(models['dynamic'], { 'title': att, 'type': node[att][0][0].type() }, 'title'); + else _helpers.push_once(models['static'], { 'title': att, 'type': typeof(node[att]) }, 'title'); + } + } + }); + + return models; + } + /** * Public API @@ -287,24 +318,21 @@ * A function that imports the graph and the attributes of all the nodes. * * @param {object} json The json structure of the graph. - * @param {object} attributes Definition of the attributes of the nodes - * (statics and dynamics). * @param {object} callback A function called at the end. */ - function importJSON(json, attributes, callback) { + function importJSON(json, callback) { reset() graph = json; - model = attributes; // Create the graph itself Graph(); self.GraphVisualization.nodes = graph.nodes.length; self.GraphVisualization.links = graph.links.length; - self.GraphVisualization.model = model; + self.GraphVisualization.model = get_models(json); // Draw graph with default property and time for the first time - update_data(model.dynamic[0].title, 0); + update_data(self.GraphVisualization.model.dynamic[0].title, 0); if (callback) { callback(); } } diff --git a/visualization.py b/visualization.py index 14ef3c5..7aa165c 100644 --- a/visualization.py +++ b/visualization.py @@ -1,5 +1,4 @@ import os -import networkx as nx from server import VisualizationElement from soil.simulation import SoilSimulation from xml.etree import ElementTree @@ -7,10 +6,11 @@ from xml.etree import ElementTree class Model(): - def __init__(self, dump=True, dir_path='output'): + def __init__(self, dump=False, dir_path='output'): self.name = 'soil' self.dump = dump self.dir_path = dir_path + self.simulation = list() def run(self, config): name = config['name'] @@ -22,40 +22,11 @@ class Model(): print('Dumping results to {} : {}'.format(sim.dir_path, sim.dump)) - sim.run_simulation() - - - def get_trial(self, name, trial): - graph = nx.read_gexf(os.path.join(self.dir_path, name, '{}_trial_{}.gexf'.format(name, trial))) - - attributes = self.get_attributes(os.path.join(self.dir_path, name, '{}_trial_{}.gexf'.format(name, trial))) - json = {} - json['graph'] = nx.node_link_data(graph) - json['models'] = attributes - return json + return sim.run_simulation() def reset(self): pass - def get_attributes(self, path_gexf): - attributes = {} - tree = ElementTree.parse(path_gexf) - root = tree.getroot() - - ns = { 'gexf': 'http://www.gexf.net/1.2draft' } - - for mode in root[0].findall('gexf:attributes', ns): - attributes[mode.attrib['mode']] = [] - for attribute in mode: - values = { - 'id' : attribute.attrib['id'], - 'title' : attribute.attrib['title'], - 'type' : attribute.attrib['type'] - } - attributes[mode.attrib['mode']].append(values) - - return attributes - class GraphVisualization(VisualizationElement): package_includes = []