1
0
mirror of https://github.com/gsi-upm/soil synced 2024-11-22 19:22:29 +00:00
This commit is contained in:
Tasio Mendez 2018-02-01 13:46:51 +01:00
parent 563dc8dc4c
commit c93f3fafc7
6 changed files with 335 additions and 49 deletions

25
config_copy.yml Normal file
View File

@ -0,0 +1,25 @@
name: ControlModelM2_sim
max_time: 100
num_trials: 2
network_params:
generator: barabasi_albert_graph
n: 100
m: 2
network_agents:
- agent_type: ControlModelM2
weight: 0.1
state:
id: 1
- agent_type: SpreadModelM2
weight: 0.9
state:
id: 0
environment_params:
prob_neutral_making_denier: 0.035
prob_infect: 0.075
prob_cured_healing_infected: 0.035
prob_cured_vaccinate_neutral: 0.035
prob_vaccinated_healing_infected: 0.035
prob_vaccinated_vaccinate_neutral: 0.035
prob_generate_anti_rumor: 0.035
standard_variance: 0.055

View File

@ -17,7 +17,16 @@ html, body {
}
.navbar {
box-shadow: 0px -2px 5px 3px rgba(0, 0, 0, .3);
box-shadow: 0px 0px 5px 2px rgba(0, 0, 0, .2)
}
.nav.navbar-right {
margin-right: 10px !important;
}
.dropdown-menu > li > a:hover {
background-color: #d4d3d3;
cursor: pointer;
}
.node {
@ -49,6 +58,10 @@ svg#graph, #configuration {
padding: 15px;
border-left: none !important;
overflow: auto;
display: flex;
flex-direction: column;
align-items: inherit;
justify-content: space-evenly;
}
button {
@ -86,7 +99,13 @@ button.pressed {
background-color: white;
}
.config-item {
hr {
margin-top: 15px !important;
margin-bottom: 15px !important;
width: 100%;
}
#update .config-item {
margin-top: 15px !important;
}
@ -203,6 +222,16 @@ button.pressed {
padding: 5px 2px;
}
#percentTable .no-data-table {
font-size: 10px;
justify-content: center;
align-items: center;
display: flex;
flex: 1;
height: 100%;
font-weight: 100;
}
hr {
margin-top: 15px !important;
margin-bottom: 15px !important;
@ -304,6 +333,19 @@ table#link-distance .max {
padding: 15px !important;
height: 100%;
overflow-y: auto;
overflow-x: hidden;
}
#wrapper-settings.none {
font-weight: bold;
display: flex;
flex: 1;
justify-content: center;
align-items: center;
}
#wrapper-settings.none:before {
content: 'No configuration provided';
}
#wrapper-settings .btn-group button:focus {
@ -329,3 +371,30 @@ table#link-distance .max {
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
background-color: #ccc;
}
/** CHARTS **/
#charts {
height: 100%;
padding-left: 0 !important;
padding-top: 15px !important;
padding-bottom: 15px !important;
}
.chart {
height: 50%;
}
.chart.no-data:before {
content: 'No data';
position: absolute;
font-size: 10px;
padding-bottom: 35px;
}
.chart.no-data {
font-weight: bold;
display: flex;
flex: 1;
justify-content: center;
align-items: center;
}

View File

@ -20,6 +20,10 @@
<script type="text/javascript" src="http://d3js.org/d3.v3.js"></script>
<script type="text/javascript" src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<!-- C3.js // D3-based reusable chart library -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.18/c3.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.18/c3.min.js"></script>
<!-- JAVASCRIPTS -->
<script type="text/javascript" src="js/visualization.js"></script>
<script type="text/javascript" src="js/timeline.js"></script>
@ -41,6 +45,11 @@
play,
slider;
var width_chart = (window.innerWidth - 30) / 2 - 15,
height_chart = (window.innerHeight - 230) / 2,
chart_nodes,
chart_attrs;
window.onload = function() {
"use strict";
@ -56,6 +65,7 @@
$('#update #file').change(function() {
var file = $('#file')[0].files[0];
$('.console').append('<br/>');
self.GraphVisualization.reset();
$('#load').show();
@ -83,11 +93,6 @@
});
// Select 'trials'
$('.config-item #trials').change(function() {
_socket.send($(this).val(), 'get_trial');
});
// Select 'attributes'
$('.config-item #properties').change(function() {
self.GraphVisualization.update_graph($(this).val(), slider.value(), function() {
@ -95,8 +100,38 @@
})
});
$('[data-toggle="tooltip"]').tooltip()
chart_nodes = c3.generate({
size: {
width: width_chart,
height: height_chart
},
data: {
columns: [],
type: 'area-spline'
},
axis: {
x: { label: 'Time' },
y: { label: 'Number of nodes' }
},
point: { show: false },
bindto: '#chart_nodes'
});
chart_attrs = c3.generate({
size: {
width: width_chart,
height: height_chart
},
data: {
columns: [],
type: 'area-spline'
},
axis: {
x: { label: 'Time' },
y: { label: 'Attributes' }
},
point: { show: false },
bindto: '#chart_attrs'
});
}
///]]
@ -113,7 +148,7 @@
<div class="col-sm-9 console">
Please, upload a YAML file that defines all the parameters of a simulation. <br/>
If you don't know how to write the file, please visit this page:<br/>
http://soilsim.readthedocs.io/en/latest/quickstart.html<br/><br/>
http://soilsim.readthedocs.io/en/latest/quickstart.html<br/>
</div>
<!-- //CONSOLE -->
@ -127,13 +162,15 @@
</form>
<!-- //Load File -->
<!-- TRIALS -->
<!-- Atributos -->
<div class="config-item">
Trials:
<select id="trials" class="form-control form-control-sm">
Attributes:
<select id="properties" class="form-control form-control-sm">
<optgroup id="properties-dynamic" label="Dynamics"></optgroup>
<optgroup id="properties-static" label="Statics"></optgroup>
</select>
</div>
<!-- //TRIALS -->
<!-- //Atributos -->
</div>
</div>
@ -141,19 +178,23 @@
<nav class="navbar navbar-default navbar-fixed-bottom">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="#">Brand</a>
<a class="navbar-brand" href="#">{{ model_name }}</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li data-target="#myCarousel" data-slide-to="0" class="active"><a href='#'>Home</a></li>
<li data-target="#myCarousel" data-slide-to="1"><a href="#" onclick="">Settings</a></li>
<li data-target="#myCarousel" data-slide-to="0" class="active" id="home_menu"><a href='#'>Home</a></li>
<li data-target="#myCarousel" data-slide-to="1" id="settings_menu"><a href="#">Settings &amp; Charts</a></li>
<li class="dropdown" id="trials">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Trials <span class="caret"></span></a>
<ul class="dropdown-menu"></ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Run simulation</a></li>
</ul>
</div>
<script type="text/javascript">
$('.nav li').click(function() {
$('.nav li[data-target="#myCarousel"]').click(function() {
$('.nav li').removeClass('active');
$(this).addClass('active');
});
@ -191,17 +232,9 @@
<!-- //Graph Info -->
<!-- PROPIEDADES -->
<div class="config-item">
Propiedades:
<select id="properties" class="form-control form-control-sm">
<optgroup id="properties-dynamic" label="Dynamics"></optgroup>
<optgroup id="properties-static" label="Statics"></optgroup>
</select>
</div>
<hr />
<div class="config-item">
<table id="percentTable">
<tbody><tr><th></th></tr></tbody>
<tbody><tr><th class="no-data-table">No data</th></tr></tbody>
</table>
</div>
<hr />
@ -271,10 +304,13 @@
<!-- //Wrapper Graph Container -->
<!-- Wrapper Settings -->
<div class="item settings">
<div class="item" id="settings">
<div class="container-fluid">
<div class="col-sm-6" id="charts"></div>
<div class="col-sm-6" id="wrapper-settings"></div>
<div class="col-sm-6" id="charts">
<div id="chart_nodes" class="chart no-data"></div>
<div id="chart_attrs" class="chart no-data"></div>
</div>
<div class="col-sm-6 none" id="wrapper-settings"></div>
</div>
</div>
@ -284,7 +320,5 @@
</div>
</body>
</html>

View File

@ -16,6 +16,7 @@ ws.onmessage = function(message) {
switch(msg['type']) {
case 'trials':
$('#load').removeClass('loader');
reset_trials();
set_trials(msg['data']);
break;
@ -25,13 +26,22 @@ ws.onmessage = function(message) {
$('#load').hide();
reset_configuration();
set_configuration();
$('#home_menu').click(function() {
setTimeout(function() {
reset_timeline();
set_timeline(msg['data']['graph']);
}, 1000);
});
reset_timeline();
set_timeline(msg['data']['graph']);
});
$('#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())
break;
case 'settings':
//console.log(msg['data']);
$('#wrapper-settings').empty().removeClass('none');
initGUI(msg['data']);
break;
@ -67,12 +77,26 @@ var _socket = {
var set_trials = function(trials) {
for ( i in trials ) {
$('<option>').val(i).text(trials[i]).appendTo('select#trials');
var list_item = $('<li>').appendTo('.dropdown#trials .dropdown-menu');
$('<a>').val(i).text(trials[i]).appendTo(list_item);
}
// Select 'trials'
$('.dropdown#trials li a').click(function() {
var a = $('.dropdown-toggle .caret');
$('.dropdown-toggle').text($(this).text() + ' ').append(a);
_socket.send($(this).val(), 'get_trial');
});
// Request first trial as default
_socket.send(0, 'get_trial')
};
var reset_trials = function() {
// 'Trials' selector
$('.dropdown-menu').empty();
var a = $('.dropdown-toggle .caret');
$('.dropdown-toggle').text('Trials ').append(a);
}
var convertJSON = function(json) {
json.links.forEach(function(link) {
link.source = json.nodes[link.source]
@ -156,7 +180,6 @@ var reset_configuration = function() {
// 'Link Distance' slider
$('#link-distance-slider').slider('disable').slider('setValue', 30);
}
var set_timeline = function(graph) {
@ -278,3 +301,42 @@ var get_limits = function(graph) {
})
return [min, max];
}
var set_chart_nodes = function(graph, chart) {
var [min, max] = get_limits(graph);
var data = ['nodes']
for (var i = min; i <= max; i++) {
data.push(this.GraphVisualization.get_nodes(i));
}
chart.load({
unload: true,
columns: [data]
});
}
var set_chart_attrs = function(graph, chart, property) {
var [min, max] = get_limits(graph);
var data_tmp = {}
for (var i = min; i <= max; i++) {
this.GraphVisualization.get_attributes(property, i, function(object) {
for (var value in object) {
if (!data_tmp[value]) {
var time = 0
for (var done in data_tmp)
time = (data_tmp[done].length > time) ? data_tmp[done].length - 1 : time
data_tmp[value] = Array(time).fill(0);
}
data_tmp[value].push(object[value]);
}
});
}
var data = $.map(data_tmp, function(value, index) {
value.splice(0,0,index);
return [value];
});
chart.load({
unload: true,
columns: data
});
chart.axis.labels({y: property});
}

View File

@ -37,14 +37,35 @@ var initGUI = function(model_params) {
input.slider().on('change', function(slideEvt) {
current_value.text(slideEvt.value.newValue);
});
button_down.click(function() {
var timeout, interval;
button_down.on('mousedown', function() {
input.slider('setValue', input.slider('getValue') - 0.001);
current_value.text(input.slider('getValue'));
timeout = setTimeout(function() {
interval = setInterval(function() {
input.slider('setValue', input.slider('getValue') - 0.001);
current_value.text(input.slider('getValue'));
}, 30);
}, 500);
});
button_up.click(function() {
button_down.on('mouseup', function() {
clearTimeout(timeout);
clearInterval(interval);
});
button_up.on('mousedown', function() {
input.slider('setValue', input.slider('getValue') + 0.001);
current_value.text(input.slider('getValue'));
})
timeout = setTimeout(function() {
interval = setInterval(function() {
input.slider('setValue', input.slider('getValue') + 0.001);
current_value.text(input.slider('getValue'));
}, 30);
}, 500);
});
button_up.on('mouseup', function() {
clearTimeout(timeout);
clearInterval(interval);
});
};
var addTextBox = function(param, obj) {

View File

@ -37,22 +37,26 @@
node; // Circles for the nodes
Number.prototype.between = function(min, max) {
var min = (min) ? min : Math.max(),
max = (max) ? max : Math.min();
var min = (min || min === 0) ? min : Math.max(),
max = (max || max === 0) ? max : Math.min();
return this > min && this <= max;
return ( this > min && this <= max ) || ( min === 0 && this === 0 );
};
var lastFocusNode;
var _helpers = {
set_node: function(node, property) {
set_node: function(node, property, time) {
// Add nodes if data has more nodes than before
node.enter().append('circle')
.attr('class', 'node')
.attr('r', radius)
.style('fill', function (d) {
if ( Array.isArray(d[property]) ) {
return color(d[property][0][0]);
var color_node = color(d[property][0][0]);
d[property].forEach(function(p) {
if ( time.between(p[1], p[2]) ) color_node = color(p[0]);
});
return color_node;
} else {
return color(d[property]);
}
@ -82,7 +86,11 @@
.attr('r', radius)
.style('fill', function (d) {
if ( Array.isArray(d[property]) ) {
return color(d[property][0][0]);
var color_node = color(d[property][0][0]);
d[property].forEach(function(p) {
if ( time.between(p[1], p[2]) ) color_node = color(p[0]);
});
return color_node;
} else {
return color(d[property]);
}
@ -226,7 +234,7 @@
// Do the same with the circles for the nodes - no
node = gnodes.selectAll('.node').data(data_node);
_helpers.set_node(node, property);
_helpers.set_node(node, property, time);
// Node Attributes
var statistics = {}
@ -234,10 +242,10 @@
data_node.forEach(function(n) {
// Count node properties
if ( Array.isArray(n[property]) ) {
statistics[n[property][0][0]] = (!statistics[n[property][0][0]]) ? 1 : statistics[n[property][0][0]] + 1;
} else {
statistics[n[property]] = (!statistics[n[property]]) ? 1 : statistics[n[property]] + 1;
}
n[property].forEach(function(p) {
if ( time.between(p[1], p[2]) ) statistics[p[0]] = (!statistics[p[0]]) ? 1 : statistics[p[0]] + 1;
});
} else { statistics[n[property]] = (!statistics[n[property]]) ? 1 : statistics[n[property]] + 1; }
});
for ( i in statistics ) {
statistics[i] = (statistics[i] / data_node.length * 100).toFixed(2);
@ -396,6 +404,71 @@
}
}
/**
* Get attributes at one moment given.
* A function that get the attributes of all nodes at a specific time.
*
* @param {object} time Instant of time.
* @param {object} callback A function called at the end.
* @return {object} object An object with the number of nodes.
*/
function get_attributes(property, time, callback) {
var attrs = {}
graph.nodes.forEach(function(node) {
if (Array.isArray(node.spells)) {
node.spells.forEach( function(d) {
if ( time.between(d[0], d[1]) ) {
if (Array.isArray(node[property])) {
node[property].forEach( function(p) {
if ( time.between(p[1], p[2]) ) attrs[p[0]] = (!attrs[p[0]]) ? 1 : attrs[p[0]] + 1;
});
} else { attrs[node[property]] = (!attrs[node[property]]) ? 1 : attrs[node[property]] + 1; }
}
});
} else {
if (Array.isArray(node[property])) {
node[property].forEach( function(p) {
if ( time.between(p[1], p[2]) ) attrs[p[0]] = (!attrs[p[0]]) ? 1 : attrs[p[0]] + 1;
});
} else { attrs[node[property]] = (!attrs[node[property]]) ? 1 : attrs[node[property]] + 1; }
}
});
if (callback) { callback(attrs); }
else { return attrs }
}
/**
* Get nodes at one moment given.
* A function that get the number of nodes at a specific time.
*
* @param {object} time Instant of time.
* @param {object} callback A function called at the end.
* @return {object} number The number of nodes.
*/
function get_nodes(time, callback) {
var total_nodes = 0;
graph.nodes.forEach(function(node) {
if (Array.isArray(node.spells)) {
node.spells.forEach( function(d) {
if ( time.between(d[0], d[1]) ) { total_nodes++; }
});
} else {
total_nodes++;
}
});
if (callback) { callback(total_nodes); }
else { return total_nodes }
}
/**
* Exporting
@ -418,6 +491,8 @@
// Getters
color: color,
get_attributes: get_attributes,
get_nodes: get_nodes,
// Statistics
statistics: {},