Commit 79ddcdb7 authored by Loic Hausammann's avatar Loic Hausammann Committed by Peter W. Draper
Browse files

Cell graph

parent a1ff7d34
......@@ -270,6 +270,18 @@ if test "$enable_debugging_checks" = "yes"; then
AC_DEFINE([SWIFT_DEBUG_CHECKS],1,[Enable expensive debugging])
fi
# Check if cell graph is on.
AC_ARG_ENABLE([cell-graph],
[AS_HELP_STRING([--enable-cell-graph],
[Activate the cell graph @<:@yes/no@:>@]
)],
[enable_cell_graph="$enableval"],
[enable_cell_graph="no"]
)
if test "$enable_cell_graph" = "yes"; then
AC_DEFINE([SWIFT_CELL_GRAPH],1,[Enable cell graph])
fi
# Check if using our custom icbrtf is enalbled.
AC_ARG_ENABLE([custom-icbrtf],
[AS_HELP_STRING([--enable-custom-icbrtf],
......
.. AnalysisTools
Loic Hausammann 20th March 2019
.. _analysistools:
Analysis Tools
==============
Task dependencies
-----------------
At the beginning of each simulation the file ``dependency_graph.csv`` is generated and can be transformed into a ``dot`` and a ``png`` file with the script ``tools/plot_task_dependencies.py``.
It requires the ``dot`` package that is available in the library graphviz.
This script has also the possibility to generate a list of function calls for each task with the option ``--with-calls`` (this list may be incomplete).
You can convert the ``dot`` file into a ``png`` with the following command
``dot -Tpng dependency_graph.dot -o dependency_graph.png`` or directly read it with the python module ``xdot`` with ``python -m xdot dependency_graph.dot``.
Cell graph
----------
An interactive graph of the cells is available with the configuration option ``--enable-cell-graph``.
During a run, SWIFT will generate a ``cell_hierarchy_*.csv`` file per MPI rank.
The command ``tools/make_cell_hierarchy.sh cell_hierarchy_*.csv`` merges the files together and generates the file ``cell_hierarchy.html``
that contains the graph and can be read with your favorite web browser.
With chrome, you cannot access the files directly, you will need to either access them through an existing server (e.g. public http provided by your university)
or install ``npm`` and then run the following commands
.. code-block:: bash
npm install http-server -g
http-server .
Now you can open the web page ``http://localhost:8080/cell_hierarchy.html``.
......@@ -9,11 +9,8 @@ Task System
This section of the documentation includes information on the task system
available in SWIFT, as well as how to implement your own task.
SWIFT can produce a graph containing all the dependencies using graphviz.
At the beginning of each simulation a ``csv`` file is generated and can be transformed into a ``png`` with the script ``tools/plot_task_dependencies.py``.
This script has also the possibility to generate a list of function calls for each task with the option ``--with-calls``.
You can convert the ``dot`` file into a ``png`` with the following command
``dot -Tpng dependency_graph.dot -o dependency_graph.png`` or directly read it with the python module ``xdot`` with ``python -m xdot dependency_graph.dot``.
SWIFT can produce a graph containing all the dependencies.
Everything is described in :ref:`_analysistools`.
.. toctree::
......
......@@ -26,3 +26,4 @@ difference is the parameter file that will need to be adapted for SWIFT.
NewOption/index
Task/index
VELOCIraptorInterface/index
AnalysisTools/index
......@@ -687,9 +687,12 @@ struct cell {
/*! The maximal depth of this cell and its progenies */
char maxdepth;
#ifdef SWIFT_DEBUG_CHECKS
#if defined(SWIFT_DEBUG_CHECKS) || defined(SWIFT_CELL_GRAPH)
/* Cell ID (for debugging) */
int cellID;
#endif
#ifdef SWIFT_DEBUG_CHECKS
/*! The list of tasks that have been executed on this cell */
char tasks_executed[64];
......
......@@ -2932,6 +2932,7 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs,
#endif
scheduler_write_dependencies(&e->sched, e->verbose);
space_write_cell_hierarchy(e->s);
if (e->nodeID == 0) scheduler_write_task_level(&e->sched);
/* Run the 0th time-step */
......
......@@ -82,7 +82,7 @@ int space_extra_gparts = space_extra_gparts_default;
/*! Expected maximal number of strays received at a rebuild */
int space_expected_max_nr_strays = space_expected_max_nr_strays_default;
#ifdef SWIFT_DEBUG_CHECKS
#if defined(SWIFT_DEBUG_CHECKS) || defined(SWIFT_CELL_GRAPH)
int last_cell_id;
#endif
......@@ -255,7 +255,7 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements,
c->grav.ti_end_max = -1;
c->stars.ti_end_min = -1;
c->stars.ti_end_max = -1;
#ifdef SWIFT_DEBUG_CHECKS
#if defined(SWIFT_DEBUG_CHECKS) || defined(SWIFT_CELL_GRAPH)
c->cellID = 0;
#endif
if (s->with_self_gravity)
......@@ -560,7 +560,7 @@ void space_regrid(struct space *s, int verbose) {
c->mpi.grav.send = NULL;
#endif // WITH_MPI
if (s->with_self_gravity) c->grav.multipole = &s->multipoles_top[cid];
#ifdef SWIFT_DEBUG_CHECKS
#if defined(SWIFT_DEBUG_CHECKS) || defined(SWIFT_CELL_GRAPH)
c->cellID = -last_cell_id;
last_cell_id++;
#endif
......@@ -1536,7 +1536,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
c->grav.ti_old_multipole = ti_current;
c->stars.ti_old_part = ti_current;
#ifdef SWIFT_DEBUG_CHECKS
#if defined(SWIFT_DEBUG_CHECKS) || defined(SWIFT_CELL_GRAPH)
c->cellID = -last_cell_id;
last_cell_id++;
#endif
......@@ -2729,7 +2729,7 @@ void space_split_recursive(struct space *s, struct cell *c,
#ifdef WITH_MPI
cp->mpi.tag = -1;
#endif // WITH_MPI
#ifdef SWIFT_DEBUG_CHECKS
#if defined(SWIFT_DEBUG_CHECKS) || defined(SWIFT_CELL_GRAPH)
cp->cellID = last_cell_id++;
#endif
}
......@@ -3962,7 +3962,7 @@ void space_init(struct space *s, struct swift_params *params,
/* Init the space lock. */
if (lock_init(&s->lock) != 0) error("Failed to create space spin-lock.");
#ifdef SWIFT_DEBUG_CHECKS
#if defined(SWIFT_DEBUG_CHECKS) || defined(SWIFT_CELL_GRAPH)
last_cell_id = 1;
#endif
......@@ -4536,3 +4536,85 @@ void space_struct_restore(struct space *s, FILE *stream) {
s->nr_sparts, 1);
#endif
}
#define root_cell_id 0
/**
* @brief write a single cell in a csv file.
*
* @param s The #space.
* @param f The file to use (already open).
* @param c The current #cell.
*/
void space_write_cell(const struct space *s, FILE *f, const struct cell *c) {
#ifdef SWIFT_CELL_GRAPH
if (c == NULL) return;
/* Get parent ID */
int parent = root_cell_id;
if (c->parent != NULL) parent = c->parent->cellID;
/* Get super ID */
char superID[100] = "";
if (c->super != NULL) sprintf(superID, "%i", c->super->cellID);
/* Get hydro super ID */
char hydro_superID[100] = "";
if (c->hydro.super != NULL)
sprintf(hydro_superID, "%i", c->hydro.super->cellID);
/* Write line for current cell */
fprintf(f, "%i,%i,%i,", c->cellID, parent, c->nodeID);
fprintf(f, "%i,%i,%i,%s,%s,%g,%g,%g,%g,%g,%g, ", c->hydro.count,
c->stars.count, c->grav.count, superID, hydro_superID, c->loc[0],
c->loc[1], c->loc[2], c->width[0], c->width[1], c->width[2]);
fprintf(f, "%g, %g\n", c->hydro.h_max, c->stars.h_max);
/* Write children */
for (int i = 0; i < 8; i++) {
space_write_cell(s, f, c->progeny[i]);
}
#endif
}
/**
* @brief Write a csv file containing the cell hierarchy
*
* @param s The #space.
*/
void space_write_cell_hierarchy(const struct space *s) {
#ifdef SWIFT_CELL_GRAPH
/* Open file */
char filename[200];
sprintf(filename, "cell_hierarchy_%04i.csv", engine_rank);
FILE *f = fopen(filename, "w");
if (f == NULL) error("Error opening task level file.");
const int root_id = root_cell_id;
/* Write header */
if (engine_rank == 0) {
fprintf(f, "name,parent,mpi_rank,");
fprintf(f,
"hydro_count,stars_count,gpart_count,super,hydro_super,"
"loc1,loc2,loc3,width1,width2,width3,");
fprintf(f, "hydro_h_max,stars_h_max\n");
/* Write root data */
fprintf(f, "%i, ,-1,", root_id);
fprintf(f, "%li,%li,%li, , , , , , , , , ", s->nr_parts, s->nr_sparts,
s->nr_gparts);
fprintf(f, ",\n");
}
/* Write all the top level cells (and their children) */
for (int i = 0; i < s->nr_cells; i++) {
struct cell *c = &s->cells_top[i];
if (c->nodeID == engine_rank) space_write_cell(s, f, c);
}
/* Cleanup */
fclose(f);
#endif
}
......@@ -331,5 +331,6 @@ void space_free_cells(struct space *s);
void space_struct_dump(struct space *s, FILE *stream);
void space_struct_restore(struct space *s, FILE *stream);
void space_write_cell_hierarchy(const struct space *s);
#endif /* SWIFT_SPACE_H */
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Cell Hierarchy</title>
<style>
div.tooltip {
position: absolute;
text-align: left;
width: 180px;
height: 80px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
.node {
cursor: pointer;
}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 3px;
}
.node text {
font: 12px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
</style>
</head>
<div class="ui-widget">
<input id="search">
<button type="button" onclick="searchNode()">Search</button>
<progress value="0" max="100" id="progress">0</progress>
</div>
<body>
<!-- load the d3.js library -->
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
// ************** Generate the tree diagram *****************
var margin = {top: 20, right: 60, bottom: 20, left: 60},
width = 1800 - margin.right - margin.left,
height = 900 - margin.top - margin.bottom;
var i = 0,
duration = 750,
root;
var tree = d3.tree()
.size([10*width, height]);
// append the svg object to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.call(d3.zoom().on("zoom", function () {
svg.attr("transform", d3.event.transform)
}))
.append("g")
.attr("transform", "translate("
+ margin.left + "," + margin.top + ")")
// load the external data
d3.csv("cell_hierarchy.csv").then(function(data) {
// *********** Convert flat data into a nice tree ***************
// create a name: node map
var dataMap = data.reduce(function(map, node) {
map[node.name] = node;
return map;
}, {});
// create the tree array
var treeData = [];
data.forEach(function(node) {
// add to parent
var parent = dataMap[node.parent];
if (parent) {
// create child array if it doesn't exist
(parent.children || (parent.children = []))
// add node to child array
.push(node);
} else {
// parent is null or missing
treeData.push(node);
}
});
root = d3.hierarchy(treeData[0], function(d) { return d.children; });
root.x0 = width / 2;
root.y0 = 0;
// Collapse after the second level
root.children.forEach(collapse);
update(root);
var N = root.children.length
var size = 1.
if (N > 64)
size = 15 * N / 1664
tree = tree.size([size * width, height]);
update(root)
});
d3.select(self.frameElement).style("height", "500px");
function update(source) {
// Assigns the x and y position for the nodes
var treeData = tree(root);
// Compute the new tree layout.
var nodes = treeData.descendants(),
links = treeData.descendants().slice(1);
// Normalize for fixed-depth.
nodes.forEach(function(d){ d.y = d.depth * 100});
// ****************** Nodes section ***************************
// Update the nodes...
var node = svg.selectAll('g.node')
.data(nodes, function(d) {return d.id || (d.id = ++i); });
// Enter any new modes at the parent's previous position.
var nodeEnter = node.enter().append('g')
.attr('class', 'node')
.attr("transform", function(d) {
return "translate(" + source.x0 + "," + source.y0 + ")";
})
.on('click', click)
// add tool tip for ps -eo pid,ppid,pcpu,size,comm,ruser,s
.on("mouseover", function(d) {
var n = d.data
div.transition()
.duration(200)
.style("opacity", .9);
div .html(
"ID: " + n.name + "<br/>" +
"MPI rank:" + n.mpi_rank + "<br/>" +
"Part: " + n.hydro_count + "<br/>" +
"Spart: " + n.stars_count + "<br/>" +
"Super: " + n.super + "<br/>" +
"Super Hydro: " + n.hydro_super + "<br/>" +
"Loc: " + n.loc1 + ", " + n.loc2 + ", " + n.loc3 + "<br/>" +
"Width: " + n.width1 + ", " + n.width2 + ", " + n.width3 + "<br/>" +
"Hydro h_max: " + n.hydro_h_max + "<br/>" +
"Stars h_max: " + n.stars_h_max + "<br/>"
)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px")
// change here to change the tool tip box size
.style("height", 180 + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
// Add Circle for the nodes
nodeEnter.append('circle')
.attr('class', 'node')
.attr('r', 1e-6)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
});
// Add labels for the nodes
nodeEnter.append('text')
.attr("dy", ".35em")
.attr("x", -55)
.attr("text-anchor", "start")
.attr("transform", "rotate(90)")
.text(function(d) { return d.data.name; });
// add the tool tip
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// UPDATE
var nodeUpdate = nodeEnter.merge(node);
// Transition to the proper position for the node
nodeUpdate.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
// Update the node attributes and style
nodeUpdate.select('circle.node')
.attr('r', 10)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
})
.attr('cursor', 'pointer');
// Remove any exiting nodes
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + source.x + "," + source.y + ")";
})
.remove();
// On exit reduce the node circles size to 0
nodeExit.select('circle')
.attr('r', 1e-6);
// On exit reduce the opacity of text labels
nodeExit.select('text')
.style('fill-opacity', 1e-6);
// ****************** links section ***************************
// Update the links...
var link = svg.selectAll('path.link')
.data(links, function(d) { return d.id; });
// Enter any new links at the parent's previous position.
var linkEnter = link.enter().insert('path', "g")
.attr("class", "link")
.attr('d', function(d){
var o = {x: source.x0, y: source.y0}
return diagonal(o, o)
});
// UPDATE
var linkUpdate = linkEnter.merge(link);
// Transition back to the parent element position
linkUpdate.transition()
.duration(duration)
.attr('d', function(d){ return diagonal(d, d.parent) });
// Remove any exiting links
var linkExit = link.exit().transition()
.duration(duration)
.attr('d', function(d) {
var o = {x: source.x, y: source.y}
return diagonal(o, o)
})
.remove();
// Store the old positions for transition.
nodes.forEach(function(d){
d.x0 = d.x;
d.y0 = d.y;
});
// Creates a curved (diagonal) path from parent to the child nodes
function diagonal(s, d) {
path = `M ${s.x} ${s.y}
C ${(s.x + d.x) / 2} ${s.y},
${(s.x + d.x) / 2} ${d.y},
${d.x} ${d.y}`
return path
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
}
// Collapse the node and all it's children
function collapse(d) {
if(d.children) {
d._children = d.children
d._children.forEach(collapse)
d.children = null
}
}
// open the node and all it's parent
function openNode(d) {
if (d._children) {
d.children = d._children
d._children = null
}
if (d.parent)
openNode(d.parent)
}
var selectedVal;
function searchSubNode(node) {
if (node.data.name == selectedVal) {
openNode(node)
update(root)
return true
}
var found = false
if (node.children) {
var N = node.children.length
for(var i = 0; i < N; i++) {
found = searchSubNode(node.children[i])
if (found) {
break
}
}
}
if (found)
return found
if (node._children) {
var N = node._children.length
for(var i = 0; i < N; i++) {
found = searchSubNode(node._children[i])
if (found) {
break
}
}
}
return found
}
function searchNode() {
//find the node
selectedVal = document.getElementById('search').value;
var progress = document.getElementById("progress");
progress.innerHTML = "0%"
progress.value = 0
var N = root.children.length
for(var i = 0; i < N; i++) {
var found = searchSubNode(root.children[i], selectedVal)
if (found) {
progress.innerHTML = "100%"