diff --git a/app/src/components/callflowSingle.js b/app/src/components/callflowSingle.js
index 398efaf7..a2c080de 100644
--- a/app/src/components/callflowSingle.js
+++ b/app/src/components/callflowSingle.js
@@ -436,7 +436,6 @@ export default {
// Create a map for each dataset mapping the respective mean times.
let map = {};
for (let module_name of module_list) {
- console.log(module_name, this.$store.modules[this.selectedTargetDataset][module_name])
map[module_name] = this.$store.modules[this.selectedTargetDataset][module_name][this.$store.selectedMetric]["mean_time"];
}
diff --git a/app/src/components/singleHistogram/singleHistogram.js b/app/src/components/singleHistogram/singleHistogram.js
index 76bd4ec6..e7c8080b 100644
--- a/app/src/components/singleHistogram/singleHistogram.js
+++ b/app/src/components/singleHistogram/singleHistogram.js
@@ -12,6 +12,7 @@ import tpl from "../../html/histogram.html";
import ToolTip from "./tooltip";
import * as utils from "../utils";
import EventHandler from "../EventHandler";
+import { brush } from "d3";
export default {
template: tpl,
@@ -100,7 +101,6 @@ export default {
this.binContainsProcID = temp[3];
this.logScaleBool = false;
- this.$refs.ToolTip.init(this.svgID);
this.xScale = d3.scaleBand()
.domain(this.xVals)
@@ -128,7 +128,8 @@ export default {
d3.selectAll(".binRank").remove();
d3.selectAll(".lineRank").remove();
d3.selectAll(".tick").remove();
- // this.$refs.ToolTip.clear();
+ d3.selectAll(".brush").remove();
+ this.$refs.ToolTip.clear();
},
visualize(callsite) {
@@ -139,6 +140,7 @@ export default {
this.yAxis();
this.rankLineScale();
this.brushes();
+ this.$refs.ToolTip.init(this.svgID);
},
array_unique(arr) {
@@ -235,6 +237,10 @@ export default {
};
},
+ sanitizeGroupProc(string) {
+ return string.replace("[", "").replace("]", "");
+ },
+
bars() {
let self = this;
this.svg.selectAll(".single-histogram-bar")
@@ -252,6 +258,17 @@ export default {
"stroke-width": "0.2px",
"stroke": "#202020",
})
+ .style("z-index", 1)
+ .on("click", function(d, i) {
+ d3.select(this)
+ .attr("fill", self.$store.runtimeColor.highlight);
+ d3.selectAll(`.lineRank_${i}`)
+ .style("fill", "orange")
+ .style("fill-opacity", 1);
+ let groupProcStr = self.groupProcess(self.binContainsProcID[i]).string;
+ groupProcStr = this.sanitizeGroupProc(groupProcStr);
+ self.$refs.ToolTip.render(groupProcStr, d);
+ })
.on("mouseover", function (d, i) {
d3.select(this)
.attr("fill", self.$store.runtimeColor.highlight);
@@ -259,6 +276,7 @@ export default {
.style("fill", "orange")
.style("fill-opacity", 1);
let groupProcStr = self.groupProcess(self.binContainsProcID[i]).string;
+ groupProcStr = this.sanitizeGroupProc(groupProcStr);
self.$refs.ToolTip.render(groupProcStr, d);
})
.on("mouseout", function (d, i) {
@@ -327,7 +345,8 @@ export default {
const yAxis = d3.axisLeft(this.yScale)
.ticks(10)
.tickFormat((d, i) => {
- return d;
+ if (d % 1 == 0)
+ return d;
});
this.svg.append("text")
@@ -367,8 +386,8 @@ export default {
rankLineScale() {
let rankCount = this.numOfRanks;
- const ranklinescale = d3.scaleLinear()
- .domain([0, rankCount - 1])
+ this.ranklinescale = d3.scaleLinear()
+ .domain([0, rankCount])
.range([this.paddingFactor * this.padding.left, this.xAxisHeight]);
this.freq.forEach((freqVal, idx) => {
@@ -388,44 +407,31 @@ export default {
let cumulativeBinSpace = 0;
groupArray.forEach((group) => {
- let line;
+ let start = 0, end = 0;
if (group.length == 1) {
- var start = group[0];
- var end = start + 1;
- var topX1 = cumulativeBinSpace + binLocation;
- var topX2 = cumulativeBinSpace + binLocation + (1) * widthPerRank;
-
- var botX3 = ranklinescale(start);
- var botX4 = ranklinescale(start);
-
- var topY = this.boxHeight - this.histogramOffset;
- var botY = this.boxHeight;
- cumulativeBinSpace += (1) * widthPerRank;
-
- line = "M" + topX1 + " " + topY +
- "L " + topX2 + " " + topY +
- "L " + botX4 + " " + botY +
- "L " + botX3 + " " + botY;
- } else {
- let start = group[0];
- let end = group[1];
-
- let topX1 = cumulativeBinSpace + binLocation;
- let topX2 = cumulativeBinSpace + (end - start + 1) * widthPerRank + binLocation;
+ start = group[0];
+ end = start + 1;
+ }
+ else {
+ start = group[0];
+ end = group[1] + 1;
+ }
+
+ let topX1 = cumulativeBinSpace + binLocation + widthPerRank;
+ let topX2 = cumulativeBinSpace + (end - start + 1) * widthPerRank + binLocation;
- let botX3 = ranklinescale(start);
- let botX4 = ranklinescale(end);
+ let botX3 = this.ranklinescale(start);
+ let botX4 = this.ranklinescale(end);
- let topY = this.boxHeight - this.histogramOffset;
- let botY = this.boxHeight;
+ let topY = this.boxHeight - this.histogramOffset;
+ let botY = this.boxHeight;
- cumulativeBinSpace += (end - start + 1) * widthPerRank;
+ cumulativeBinSpace += (end - start + 1) * widthPerRank;
- line = "M" + topX1 + " " + topY +
+ const line = "M" + topX1 + " " + topY +
"L " + topX2 + " " + topY +
"L " + botX4 + " " + botY +
"L " + botX3 + " " + botY;
- }
rankLinesG.append("path")
.attr("d", line)
@@ -439,7 +445,7 @@ export default {
}
});
- const rankLineAxis = d3.axisBottom(ranklinescale)
+ const rankLineAxis = d3.axisBottom(this.ranklinescale)
.ticks(10)
.tickFormat((d, i) => {
if (d % 1 == 0)
@@ -477,8 +483,8 @@ export default {
this.brush = d3.brushX()
.extent([
- [this.paddingFactor * this.padding.left, this.histogramHeight],
- [this.paddingFactor * this.padding.left + this.xAxisHeight - (this.paddingFactor) * this.padding.left, this.histogramHeight + this.rankScaleHeight]
+ [this.paddingFactor * this.padding.left, this.yAxisHeight],
+ [this.paddingFactor * this.padding.left + this.xAxisHeight - (this.paddingFactor) * this.padding.left, this.yAxisHeight + this.rankScaleHeight]
])
.on("brush", this.brushing)
.on("end", this.brushend);
@@ -500,24 +506,27 @@ export default {
brushing() {
const brushScale = d3.scaleLinear()
- .domain(this.xScale.domain())
+ .domain([this.xScale.domain()[0], this.xScale.domain()[this.xScale.domain().length -1 ]])
.range(this.xScale.range());
let brushStart = d3.event.selection.map(brushScale.invert)[0];
let brushEnd = d3.event.selection.map(brushScale.invert)[1];
let brushPoints = this.xScale.domain().length;
- this.localBrushStart = Math.floor(brushStart * brushPoints);
- this.localBrushEnd = Math.ceil(brushEnd * brushPoints);
+ let brushMin = this.xScale.domain()[0];
+ let brushMax = this.xScale.domain()[this.xScale.domain().length - 1];
+
+ this.localBrushStart = Math.floor((brushStart - brushMin)/(brushMax - brushMin) * brushPoints);
+ this.localBrushEnd = Math.ceil((brushEnd - brushMin)/(brushMax - brushMin) * brushPoints);
// highlight rank lines that is brush
- this.histogramSVG.selectAll(".binRank").attr("opacity", 0);
+ this.svg.selectAll(".binRank").attr("opacity", 0.5);
for (let i = this.localBrushStart; i < this.localBrushEnd; i++) {
- this.histogramSVG.selectAll(`.bin_${i}`).attr("opacity", 1);
+ this.svg.selectAll(`.bin_${i}`).attr("opacity", 1);
}
if (this.localBrushStart == this.localBrushEnd) {
- this.histogramSVG.selectAll(".binRank").attr("opacity", 1);
+ this.svg.selectAll(".binRank").attr("opacity", 1);
}
},
@@ -525,6 +534,7 @@ export default {
let self = this;
const processIDList = [];
for (let i = this.localBrushStart; i < this.localBrushEnd; i++) {
+ // console.log(self.binContainsProcID)
if (self.binContainsProcID[i] != null) {
const curList = self.binContainsProcID[i];
curList.forEach((processID) => {
@@ -532,10 +542,11 @@ export default {
});
}
}
- self.$socket.emit("split-rank", {
+ self.$socket.emit("split-mpi-rank", {
"dataset": self.$store.selectedDataset,
- "ids": processIDList
+ "ranks": processIDList
});
+
},
}
};
\ No newline at end of file
diff --git a/app/src/components/singleHistogram/tooltip.js b/app/src/components/singleHistogram/tooltip.js
index 72676483..7376bd76 100644
--- a/app/src/components/singleHistogram/tooltip.js
+++ b/app/src/components/singleHistogram/tooltip.js
@@ -6,6 +6,7 @@
*/
import * as d3 from "d3";
+import * as utils from "../utils";
export default {
template: "",
@@ -48,12 +49,29 @@ export default {
render(data, node) {
this.clear();
this.width = data.length * this.fontSize + 10 * this.fontSize;
- var svgScale = d3.scaleLinear().domain([2, 11]).range([50, 150]);
- console.log(d3.select("#" + this.parentID));
+ const svgScale = d3.scaleLinear().domain([2, 11]).range([this.containerWidth, this.containerHeight]);
this.mousePos = d3.mouse(d3.select("#" + this.parentID).node());
this.mousePosX = this.mousePos[0];
this.mousePosY = this.mousePos[1];
this.toolTipG.attr("height", svgScale(10) + "px");
+
+ this.node = node;
+ this.data = data;
+ this.addText("Processes (MPI ranks):" + this.data);
+ },
+
+ optimizeTextHeight(text) {
+ const measure = utils.measure(text);
+ const rows = measure.width/this.containerWidth;
+
+ return {
+ "width": this.containerWidth,
+ "height": rows * measure.height
+ };
+ },
+
+ addText(text) {
+ const measure = this.optimizeTextHeight(text);
this.toolTipRect = this.toolTipG
.append("rect")
.attrs({
@@ -62,9 +80,8 @@ export default {
"stroke": "black",
"rx": "10px",
"fill-opacity": 1,
- "z-index": 100,
- "width": this.containerWidth,
- "height": this.containerHeight,
+ "width": measure.width + 20,
+ "height": measure.height,
})
.attrs({
"x": () => {
@@ -77,19 +94,9 @@ export default {
"y": () => {
return (this.mousePosY) + "px";
}
- });
- this.node = node;
- this.data = data;
- this.processes();
- },
-
- trunc(str, n) {
- str = str.replace(//g, "proc ");
- return (str.length > n) ? str.substr(0, n - 1) + "..." : str;
- },
-
+ })
+ .style("z-index", 2);
- addText(text) {
this.textCount += 1;
this.toolTipText = this.toolTipG
.append("text")
@@ -108,13 +115,13 @@ export default {
return (this.mousePosY) + 2 * this.offset + "px";
}
})
- .text(text);
+ .style("z-index", 2)
+ .text(text)
+ .call(utils.textWrap, measure.width);
},
processes() {
let self = this;
- this.addText("Processes (MPI ranks): " + this.data);
-
},
clear() {
diff --git a/app/src/components/supergraph/encodings/tooltip.js b/app/src/components/supergraph/encodings/tooltip.js
index 24e3014c..b09981d4 100644
--- a/app/src/components/supergraph/encodings/tooltip.js
+++ b/app/src/components/supergraph/encodings/tooltip.js
@@ -14,63 +14,92 @@ export default {
components: {},
data: () => ({
- id: "",
+ id: "supernode-tooltip",
textCount: 0,
textxOffset: 20,
textyOffset: 20,
textPadding: 15,
height: 200,
- margin: 35
+ margin: 35,
+ mousePosX: 0,
+ mousePosY: 0,
+ prevMousePosX: undefined,
+ prevMousePosY: undefined
}),
- sockets: {
- tooltip(data) {
- this.render(data);
- },
- },
-
methods: {
init(id) {
this.id = id;
- this.toolTipDiv = d3.select("#" + this.id);
- this.toolTipG = this.toolTipDiv.append("g");
- this.callgraphOverviewWidth = this.$store.viewWidth;
- this.halfWidth = this.callgraphOverviewWidth / 2;
+ const toolTipDiv = d3.select("#" + this.id);
+ this.toolTipG = toolTipDiv.append("g");
},
+ /**
+ * Set the position for the tooltip SVG. (x-positioning)
+ */
positionX() {
let ret = 0;
- if (this.mousePosX >= this.halfWidth) {
- ret = this.mousePosX - this.halfWidth + this.textxOffset;
- }
- else {
- ret = this.halfWidth - this.mousePosX + this.textxOffset;
+ if (this.mousePosX >= this.$store.viewWidth / 2) {
+ ret = this.mousePosX + this.textxOffset;
}
+ ret = this.mousePosX - this.textxOffset;
return ret;
},
- positionY(node) {
- if (this.mousePosY < node.y) {
- return node.y - this.mousePosY + node.height / 2;
+ /**
+ * Set the position for the tooltip SVG. (y-positioning)
+ */
+ positionY() {
+ let ret = 0;
+ if (this.mousePosY >= this.$store.viewHeight / 2) {
+ ret = this.mousePosY + this.textyOffset;
}
- return this.mousePosY - node.y + node.height / 2;
+ ret = this.mousePosY - this.textyOffset;
+ return ret;
},
+ /**
+ * The below function decides if we have to render the tooltip or not.
+ *
+ * @param {Graph} graph
+ * @param {*} node
+ */
visualize(graph, node) {
- this.clear();
- this.xOffset = this.positionX();
- this.yOffset = this.positionY(node);
- this.nodeHeight = node.height;
- var svgScale = d3.scaleLinear().domain([2, 11]).range([50, 150]);
+ // Set current mouse position.
this.mousePos = d3.mouse(d3.select("#" + this.id).node());
this.mousePosX = this.mousePos[0];
this.mousePosY = this.mousePos[1];
+
+ // Draw the tooltip again only if the distance is more than the width of the supernode.
+ // console.log(this.prevMousePosY, this.prevMousePosX, utils.distanceBtwnPoints(this.mousePosX, this.mousePosY, this.prevMousePosX, this.prevMousePosY))
+ // if (this.prevMousePosX && this.prevMousePosY && utils.distanceBtwnPoints(this.mousePosX, this.mousePosY, this.prevMousePosX, this.prevMousePosY) > 0 ) {
+ this.clear();
+ this.render(graph, node);
+ // }
+
+ // Store the previous mouse positions to calculate the distance.
+ this.prevMousePosX = this.mousePosX;
+ this.prevMousePosY = this.mousePosY;
+ },
+
+ /**
+ *
+ * @param {*} graph
+ * @param {*} node
+ */
+ render(graph, node) {
+ this.xOffset = this.positionX() + 40;
+ this.yOffset = this.positionY() + 40;
+ this.nodeHeight = node.height;
+
+ const svgScale = d3.scaleLinear().domain([2, 11]).range([50, 150]);
+
this.toolTipG.attr("height", svgScale(10) + "px");
this.toolTipRect = this.toolTipG
.append("rect")
.attrs({
- "class": "toolTipContent",
- "fill": "#e0e0e0",
+ "class": "tooltip-container",
+ "fill": "#fff",
"stroke": "black",
"rx": "10px",
"fill-opacity": 1,
@@ -81,22 +110,24 @@ export default {
"x": this.xOffset,
"y": this.yOffset
});
- this.graph = graph;
- this.node = node;
- this.times();
- this.paths();
+ this.runtimeInformation(node);
+ this.pathInformation(node);
},
+ /**
+ * Add a single line of text.
+ *
+ * @param {*} text
+ */
addText(text) {
- let self = this;
this.textCount += 1;
this.toolTipText = this.toolTipG
.append("text")
.style("font-family", "sans-serif")
.style("font-size", "")
.attrs({
- "class": "toolTipContent",
+ "class": "tooltip-content",
"x": () => {
return this.xOffset + this.margin;
},
@@ -107,31 +138,44 @@ export default {
.text(text);
},
- times() {
- this.addText("Name: " + this.trunc(this.node.id, 40));
- // this.addText('Inclusive Time: ' + (this.node['time (inc)'] * 0.000001).toFixed(3) + "s - " + Math.floor(((this.node['time (inc)'] / this.$store.maxIncTime['ensemble']) * 100).toFixed(3)) + "%")
- // this.addText('Exclusive Time: ' + (this.node['time'] * 0.000001).toFixed(3) + "s - " + Math.floor(((this.node['time'] / this.$store.maxExcTime['ensemble']) * 100).toFixed(3)) + "%")
- // this.addText('Inclusive Time: ' + utils.formatRuntimeWithUnits(this.node.actual_time['Inclusive']))
- // this.addText('Exclusive Time: ' + utils.formatRuntimeWithUnits(this.node.actual_time['Exclusive']))
- this.addText("Inclusive Time: " + utils.formatRuntimeWithUnits(this.node["time (inc)"]));
- this.addText("Exclusive Time: " + utils.formatRuntimeWithUnits(this.node["time"]));
- // this.addText('Node value: ' + utils.formatRuntimeWithUnits(this.node.value))
- // this.addText('Node height: ' + this.node.height)
-
+ /**
+ *
+ * @param {*} node
+ */
+ runtimeInformation(node) {
+ this.addText("Name: " + utils.truncNames(node.id, 40));
+ this.addText("Inclusive Time: " + utils.formatRuntimeWithUnits(node["time (inc)"]));
+ this.addText("Exclusive Time: " + utils.formatRuntimeWithUnits(node["time"]));
},
- trunc(str, n) {
- str = str.replace(//g, "proc ");
- return (str.length > n) ? str.substr(0, n - 1) + "..." : str;
- },
+ /**
+ *
+ * @param {*} node
+ */
+ pathInformation(node) {
+ let module_data = {};
+ if (this.$store.selectedMode == "Single") {
+ module_data = this.$store.modules[this.$store.selectedTargetDataset];
+ }
+ else if (this.$store.selectedMode == "Ensemble") {
+ module_data = this.$store.modules["ensemble"];
+ }
+
+ let callsite_data = {};
+ if (this.$store.selectedMode == "Single") {
+ callsite_data = this.$store.callsites[this.$store.selectedTargetDataset];
+ }
+ else if (this.$store.selectedMode == "Ensemble") {
+ callsite_data = this.$store.callsites["ensemble"];
+ }
- paths() {
- let entry_functions = this.$store.modules["ensemble"][this.node.id]["callers"];
+ // TODO : Improve the logic here to not process the string input multiple times.
+ let entry_functions = node[this.$store.selectedTargetDataset]["entry_function"].split(",").map(String);
let entry_function_runtimes = {};
for (let i = 0; i < entry_functions.length; i += 1) {
- let callsite = entry_functions[i].replace("'", "").replace("'", "").replace("[", "").replace("]", "");
- entry_function_runtimes[callsite] = this.$store.callsites["ensemble"][callsite][this.$store.selectedMetric]["mean_time"];
+ let callsite = entry_functions[i].replace("'", "").replace("'", "").replace("[", "").replace("]", "").replace(" ", "");
+ entry_function_runtimes[callsite] = callsite_data[callsite][this.$store.selectedMetric]["mean_time"];
}
// Create items array
@@ -148,14 +192,17 @@ export default {
this.addText("");
this.addText("Entry call sites: ");
+ this.entryFunctionInformation(node, entry_function_data);
+ },
- // TODO: Bug here
+ entryFunctionInformation(node, entry_function_data) {
+ // Needs clean up for sure.
for (var tIndex = 0; tIndex < Math.min(3, entry_function_data.length); tIndex++) {
this.textCount += 1;
- let fromColor = this.$store.color.getColorByValue(entry_function_data[tIndex][1]);
- let toColor = this.$store.color.getColor(this.node);
- let fromFunc = entry_function_data[tIndex][0];
- let toFunc = this.node.id;
+ let toColor = this.$store.runtimeColor.getColorByValue(entry_function_data[tIndex][1]);
+ let fromColor = this.$store.runtimeColor.getColorByValue(node);
+ let toFunc = entry_function_data[tIndex][0];
+ let fromFunc = node.id;
let xOffset = this.xOffset + this.margin;
let yOffset = this.yOffset + this.textyOffset + this.textPadding * this.textCount;
@@ -166,7 +213,7 @@ export default {
"height": this.rectWidth,
"x": xOffset + "px",
"y": yOffset - 10 + "px",
- "class": "toolTipContent"
+ "class": "tooltip-content",
})
.style("fill", fromColor);
@@ -175,16 +222,16 @@ export default {
.attrs({
"x": xOffset + 15 + "px",
"y": yOffset + "px",
- "class": "toolTipContent",
+ "class": "tooltip-content",
})
- .text(this.trunc(fromFunc, 10));
+ .text(utils.truncNames(fromFunc, 10));
this.toolTipG
.append("text")
.attrs({
"x": xOffset + 120 + "px",
"y": yOffset + "px",
- "class": "toolTipContent",
+ "class": "tooltip-content",
})
.text("->");
@@ -195,7 +242,7 @@ export default {
"height": this.rectWidth,
"x": xOffset + 140 + "px",
"y": yOffset - 10 + "px",
- "class": "toolTipContent",
+ "class": "tooltip-content",
})
.style("fill", toColor);
this.toolTipG
@@ -203,20 +250,23 @@ export default {
.attrs({
"x": xOffset + 155 + "px",
"y": yOffset + "px",
- "class": "toolTipContent",
+ "class": "tooltip-content",
})
- .text(this.trunc(toFunc, 10));
+ .text(utils.truncNames(toFunc, 10));
}
let left_callsites = entry_function_data.length - 3;
this.addText("and " + left_callsites + " call sites more.");
-
},
+
+ /**
+ * Clear the content in the tooltip.
+ */
clear() {
- this.textCount = 0;
- d3.selectAll(".toolTipContent").remove();
+ this.textCount = 0;
+ d3.selectAll(".tooltip-container").remove();
+ d3.selectAll(".tooltip-content").remove();
},
-
}
};
\ No newline at end of file
diff --git a/app/src/components/supergraph/nodes.js b/app/src/components/supergraph/nodes.js
index f6a97b27..de57387f 100644
--- a/app/src/components/supergraph/nodes.js
+++ b/app/src/components/supergraph/nodes.js
@@ -136,7 +136,7 @@ export default {
}
this.$refs.Guides.init(this.graph.nodes);
}
- // this.$refs.ToolTip.init(this.$parent.id)
+ this.$refs.ToolTip.init(this.$parent.id);
},
preVis() {
@@ -268,14 +268,14 @@ export default {
},
mouseover(node) {
- // this.$refs.ToolTip.visualize(self.graph, node)
+ this.$refs.ToolTip.visualize(self.graph, node);
if (this.$store.selectedMode == "Ensemble" && this.$store.comparisonMode == false) {
this.$refs.Guides.visualize(node, "temporary");
}
},
mouseout(node) {
- // this.$refs.ToolTip.clear()
+ this.$refs.ToolTip.clear();
if (this.$store.selectedMode == "Ensemble" && this.$store.comparisonMode == false) {
this.$refs.Guides.clear(node, "temporary");
if (this.permanentGuides == false) {
diff --git a/app/src/components/supergraph/supergraph.js b/app/src/components/supergraph/supergraph.js
index 8b6b1821..7ae10c71 100644
--- a/app/src/components/supergraph/supergraph.js
+++ b/app/src/components/supergraph/supergraph.js
@@ -82,38 +82,19 @@ export default {
sockets: {
ensemble_supergraph(data) {
data = JSON.parse(data);
- console.debug("Data: ", data);
- let nodes = [];
- for (let i = 0; i < data.nodes.length; i += 1) {
- console.debug("Node name: ", data.nodes[i].id);
- console.debug("Time (inc): ", data.nodes[i]["time (inc)"]);
- console.debug("Time: ", data.nodes[i]["time"]);
- }
-
- for (let i = 0; i < data.links.length; i += 1) {
- console.debug("Source: ", data.links[i].source);
- console.debug("Target: ", data.links[i].target);
- console.debug("Weight: ", data.links[i].weight);
- }
+ this.debugData(data);
this.render(data);
},
single_supergraph(data) {
data = JSON.parse(data);
- console.debug("Data :", data);
- let nodes = [];
- for (let i = 0; i < data.nodes.length; i += 1) {
- console.debug("Node name: ", data.nodes[i].id);
- console.debug("Time (inc): ", data.nodes[i]["time (inc)"]);
- console.debug("Time: ", data.nodes[i]["time"]);
- }
-
- for (let i = 0; i < data.links.length; i += 1) {
- console.debug("Source: ", data.links[i].source);
- console.debug("Target: ", data.links[i].target);
- console.debug("Weight: ", data.links[i].weight);
- }
+ this.debugData(data);
this.render(data);
+ },
+
+ split_mpi_distribution(data) {
+ data = JSON.parse(data);
+ console.debug("Data: ", data);
}
},
@@ -154,6 +135,21 @@ export default {
this.sankeySVG.call(zoom);
},
+ debugData(data) {
+ console.debug("Data :", data);
+ for (let i = 0; i < data.nodes.length; i += 1) {
+ console.debug("Node name: ", data.nodes[i].id);
+ console.debug("Time (inc): ", data.nodes[i]["time (inc)"]);
+ console.debug("Time: ", data.nodes[i]["time"]);
+ }
+
+ for (let i = 0; i < data.links.length; i += 1) {
+ console.debug("Source: ", data.links[i].source);
+ console.debug("Target: ", data.links[i].target);
+ console.debug("Weight: ", data.links[i].weight);
+ }
+ },
+
clear() {
this.$refs.EnsembleNodes.clear();
this.$refs.EnsembleEdges.clear();
@@ -264,7 +260,7 @@ export default {
// Add intermediate nodes.
postProcess(nodes, edges) {
- console.log("===================Adding intermediate nodes==================");
+ console.debug("===================Adding intermediate nodes==================");
const temp_nodes = nodes.slice();
const temp_edges = edges.slice();
diff --git a/app/src/components/utils.js b/app/src/components/utils.js
index 63ad739a..0490937b 100644
--- a/app/src/components/utils.js
+++ b/app/src/components/utils.js
@@ -135,9 +135,80 @@ export function getGradients(store, node) {
return gradients;
}
+/**
+ * Remove duplicates from an array.
+ * @param {*} arr
+ */
export function removeDuplicates(arr) {
var seen = {};
return arr.filter(function (item) {
return seen.hasOwnProperty(item) ? false : (seen[item] = true);
});
+}
+
+// create a dummy element, apply the appropriate classes,
+// and then measure the element
+export function measure(text) {
+ if (!text || text.length === 0) return { height: 0, width: 0 };
+
+ const container = d3.select("body").append("svg").attr("class", "dummy");
+ container.append("text").attrs({ x: -1000, y: -1000 }).text(text);
+
+ const bbox = container.node().getBBox();
+ container.remove();
+
+ return { height: bbox.height, width: bbox.width };
+}
+
+/**
+ *
+ * @param {*} text
+ * @param {*} width
+ */
+export function textWrap(text, width) {
+ text.each(function () {
+ var text = d3.select(this),
+ words = text.text().split(/\s+/).reverse(),
+ word,
+ line = [],
+ lineNumber = 0,
+ lineHeight = 1.1, // ems
+ x = text.attr("x"),
+ y = text.attr("y"),
+ dy = 0,
+ tspan = text.text(null).append("tspan").attr("dy", dy + "em");
+
+ while ((word = words.pop())) {
+ line.push(word);
+ tspan.text(line.join(" "));
+ if (tspan.node().getComputedTextLength() > width) {
+ line.pop();
+ tspan.text(line.join(" "));
+ line = [word];
+ tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
+ }
+ }
+ });
+}
+
+/**
+ * Calculate the distance between two given points.
+ * @param {Number} x1 1st coordinate (x)
+ * @param {Number} y1 1st coordinate (y)
+ * @param {Number} x2 2nd coordinate (x)
+ * @param {Number} y2 2nd coordinate (y)
+ */
+export function distanceBtwnPoints(x1, y1, x2, y2) {
+ const a = x1 - x2;
+ const b = y1 - y2;
+ return Math.abs(Math.sqrt(a * a + b * b));
+}
+
+/**
+ * Split string to lists by , (paranthesis proof)
+ * @param {*} string
+ */
+export function stringToList(string) {
+ const re = /(:\s|,\s)/; // regular expression with capturing parentheses
+ return string.split(re);
}
\ No newline at end of file
diff --git a/callflow/callflow.py b/callflow/callflow.py
index 431863be..9f533192 100644
--- a/callflow/callflow.py
+++ b/callflow/callflow.py
@@ -268,6 +268,7 @@ def request_single(self, operation):
"supergraph",
"miniHistogram",
"function",
+ "split_mpi_distribution",
]
assert "name" in operation
assert operation["name"] in _OPERATIONS
@@ -304,6 +305,9 @@ def request_single(self, operation):
)
return functionlist.result
+ elif operation_name == "split_mpi_distribution":
+ pass
+
# flake8: noqa: C901
def request_ensemble(self, operation):
"""
diff --git a/callflow/layout/sankey.py b/callflow/layout/sankey.py
index ca0ed703..ed1efa7e 100644
--- a/callflow/layout/sankey.py
+++ b/callflow/layout/sankey.py
@@ -23,7 +23,16 @@ class SankeyLayout:
Sankey layout
"""
- _COLUMNS = ["actual_time", "time (inc)", "module", "name", "time", "type", "module"]
+ _COLUMNS = [
+ "actual_time",
+ "time (inc)",
+ "module",
+ "name",
+ "time",
+ "type",
+ "module",
+ "entry_function",
+ ]
def __init__(
self,
@@ -385,7 +394,7 @@ def _dataset_map(df, nxg, columns=[], tag=""):
if node_dict["type"] == "component-node":
module = node_name.split("=")[0]
callsite = node_name.split("=")[1]
- actual_time = SankeyLayout.callsite_time(
+ agg_time = SankeyLayout.callsite_time(
group_df=target_module_group_df,
module=module,
callsite=callsite,
@@ -396,7 +405,7 @@ def _dataset_map(df, nxg, columns=[], tag=""):
elif node_dict["type"] == "super-node":
module = node_name
callsite = target_module_callsite_map[module].tolist()
- actual_time = SankeyLayout.module_time(
+ agg_time = SankeyLayout.module_time(
group_df=target_module_name_group_df,
module_callsite_map=target_module_callsite_map,
module=module,
@@ -419,7 +428,7 @@ def _dataset_map(df, nxg, columns=[], tag=""):
ret[node_name][column] = module
elif column == "actual_time":
- ret[node_name][column] = actual_time
+ ret[node_name][column] = agg_time
elif column == "name":
ret[node_name][column] = callsite
@@ -427,8 +436,27 @@ def _dataset_map(df, nxg, columns=[], tag=""):
elif column == "type":
ret[node_name][column] = node_dict["type"]
+ elif column == "entry_function":
+ print(
+ SankeyLayout.get_entry_functions(
+ target_module_group_df, node_name
+ )
+ )
+ ret[node_name][column] = SankeyLayout.get_entry_functions(
+ target_module_group_df, node_name
+ )
+
return ret
+ @staticmethod
+ def get_entry_functions(df, module):
+ """
+ Get the entry function of a module from the dataframe.
+ """
+ module_df = df.get_group(module)
+ entry_func_df = module_df.loc[module_df["entry_function"]]
+ return entry_func_df["callees"].unique().tolist()[0]
+
# --------------------------------------------------------------------------
@staticmethod
def edge_type(nxg):
diff --git a/callflow/operations/argparser.py b/callflow/operations/argparser.py
index 90227654..6d753494 100644
--- a/callflow/operations/argparser.py
+++ b/callflow/operations/argparser.py
@@ -180,16 +180,17 @@ def _read_config(args: argparse.Namespace):
LOGGER.debug("Scheme: default")
if "runs" not in json and "profile_format" not in json:
- raise Exception("Either 'runs' or 'profile_format' key must be provided in the config file.")
+ raise Exception(
+ "Either 'runs' or 'profile_format' key must be provided in the config file."
+ )
elif "runs" in json and "profile_format" not in json:
scheme["properties"] = _SCHEME_PROFILE_FORMAT_MAPPER["default"](
json["runs"]
)
elif "runs" not in json and "profile_format" in json:
- scheme["properties"] = _SCHEME_PROFILE_FORMAT_MAPPER[json["profile_format"]](
- json["runs"]
- )
-
+ scheme["properties"] = _SCHEME_PROFILE_FORMAT_MAPPER[
+ json["profile_format"]
+ ](json["runs"])
if "module_map" in json["scheme"]:
scheme["module_callsite_map"] = json["scheme"]["module_map"]
@@ -217,7 +218,7 @@ def _scheme_dataset_map_default(run_props: dict):
name = data["name"]
scheme["runs"].append(name)
scheme["paths"][name] = data["path"]
-
+
# Assert if the profile_format is provided.
if "profile_format" not in data:
raise Exception(f"Profile format not specified for the dataset: {name}")
diff --git a/docs/conf.py b/docs/conf.py
index f0ac147f..7ad0cfd4 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -78,9 +78,10 @@ class CallFlowStyle(DefaultStyle):
pygments_style = "callflow"
-# Set the master_doc to avoid the issue (https://github.com/readthedocs/readthedocs.org/issues/2569)
+# Sets the master_doc variable to avoid the issue (https://github.com/readthedocs/readthedocs.org/issues/2569)
master_doc = "index"
+
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
diff --git a/server/main.py b/server/main.py
index eae90d3e..0e57425e 100644
--- a/server/main.py
+++ b/server/main.py
@@ -362,6 +362,21 @@ def compare(data):
)
emit("compare", result, json=True)
+ @sockets.on("split_mpi_distribution", namespace="/")
+ def split_mpi_rank(data):
+ """
+ Split a single run based on MPI distribution
+ """
+ LOGGER.debug("[Socket request] compare_supergraph {data}")
+ result = self.callflow.request_single(
+ {
+ "name": "split_mpi_distribution",
+ "dataset": data["dataset"],
+ "ranks": data["ranks"],
+ }
+ )
+ emit("split_mpi_distribution", result, json=True)
+
if __name__ == "__main__":
# if verbose, level = 1