Public
Edited
Feb 12
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof selectedNodes = Inputs.table(filteredNodes, {required: false, value: filteredNodes})
Insert cell
Insert cell
Insert cell
namespaceTotal
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
nodeTotal
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
gpuTotal
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
usedGpus = PieChart(gpuTotal, {
name: d => d.name,
value: d => d.value,
width: 600,
height: 600
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
nodes = new Promise(async (resolve, reject) => {
const transport = new clientjs.HTTPTransport("https://portal.nrp-nautilus.io/rpc");
const client = new clientjs.Client(new clientjs.RequestManager([transport]));
const results = await client.request({method: 'guest.ListNodeInfo', params: []})
let nodes = {}
for (var rawDataRow of rawData) {
nodes[rawDataRow.node] = {
Name: rawDataRow.node || "Unknown",
Region: "Unknown",
Zone: "Unknown",
Taints: "Unknown",
GPUType: "Unknown"
}
}
for (var node of results['Nodes']) {
nodes[node.Name] = {
Name: node.Name,
Region: node.Region,
Zone: node.Zone,
Taints: node.Taints?.map(t => t.key + '=' + t.value)?.join(','),
GPUType: node.GPUType || "Unknown"
}
}
resolve(nodes)
})
Insert cell
filteredNodes = Object.entries(nodes).map(([key, value]) => value).filter((row) =>
(new RegExp(nodeNameFilter)).test(row.Name) &&
(new RegExp(nodeRegionFilter)).test(row.Region) &&
(new RegExp(nodeZoneFilter)).test(row.Zone) &&
(new RegExp(nodeGPUFilter)).test(row.GPUType) &&
(new RegExp(nodeFPGAFilter)).test(row.FPGAType) &&
(new RegExp(nodeTaintsFilter)).test(row.Taints)
)
Insert cell
namespaceTotal = Object.entries(filteredData.reduce((acc, row) => {
let output = acc
output[row.namespace] = acc[row.namespace] || {}
output[row.namespace]['name'] = row.namespace
output[row.namespace][row.resource] = output[row.namespace][row.resource] ? output[row.namespace][row.resource] : 0
output[row.namespace][row.resource] += row.value
return output
}, {})).map(([key, namespace]) => ({
name: namespace.name,
pi: namespaces[namespace.name].PI,
institution: namespaces[namespace.name].Institution,
description: namespaces[namespace.name].Description,
cpu: namespace.cpu || 0,
gpu: namespace.gpu || 0,
memory: namespace.memory || 0
}))
Insert cell
nodeTotal = Object.entries(filteredData.reduce((acc, row) => {
let output = acc
output[row.node] = acc[row.node] || {}
output[row.node]['name'] = row.node
output[row.node][row.resource] = output[row.node][row.resource] ? output[row.node][row.resource] : 0
output[row.node][row.resource] += row.value
return output
}, {})).map(([key, node]) => ({
name: node.name,
cpu: node.cpu || 0,
gpu: node.gpu || 0,
memory: node.memory || 0,
gputype: nodes[node.name].GPUType
}))
Insert cell
gpuTotal = Object.entries(nodeTotal.reduce((acc, row) => {
let output = acc
output[row.gputype] = acc[row.gputype] || 0
output[row.gputype] += row.gpu
return output
}, {})).map(([key, value]) => ({
name: key,
value: value
}))
Insert cell
filteredData = rawData.filter((data) => {
return selectedNamespaces.some((selectedNamespace) => selectedNamespace.Name === data.namespace)
}).filter((data) => {
return selectedNodes.some((selectedNode) => selectedNode.Name === data.node)
})
Insert cell
filteredNamespaces = Object.entries(namespaces).map(([key, value]) => value).filter((row) =>
(new RegExp(namespaceNameFilter)).test(row.Name) &&
(new RegExp(namespaceInstitutionFilter)).test(row.Institution) &&
(new RegExp(namespaceUserInstitutionFilter)).test(row.UserInstitutions)
)
Insert cell
// Copyright 2018-2023 Observable, Inc.
// Released under the ISC license.
// https://observablehq.com/@d3/pie-chart
function PieChart(data, {
name = ([x]) => x, // given d in data, returns the (ordinal) label
value = ([, y]) => y, // given d in data, returns the (quantitative) value
title, // given d in data, returns the title text
width = 600, // outer width, in pixels
height = 600, // outer height, in pixels
innerRadius = 0, // inner radius of pie, in pixels (non-zero for donut)
outerRadius = Math.min(width, height) / 2, // outer radius of pie, in pixels
labelRadius = (innerRadius * 0.2 + outerRadius * 0.8), // center radius of labels
format = ",", // a format specifier for values (in the label)
names, // array of names (the domain of the color scale)
colors, // array of colors for names
stroke = innerRadius > 0 ? "none" : "white", // stroke separating widths
strokeWidth = 1, // width of stroke separating wedges
strokeLinejoin = "round", // line join of stroke separating wedges
padAngle = stroke === "none" ? 1 / outerRadius : 0, // angular separation between wedges, in radians
} = {}) {

data.sort((a,b) => {
return parseInt(b.hours) - parseInt(a.hours);
});
// Compute values.
const N = d3.map(data, name);
const V = d3.map(data, value);
const I = d3.range(N.length).filter(i => !isNaN(V[i]));

// Unique the names.
if (names === undefined) names = N;
names = new d3.InternSet(names);

// Chose a default color scheme based on cardinality.
if (colors === undefined) colors = d3.schemeSpectral[names.size];
if (colors === undefined) colors = d3.quantize(t => d3.interpolateSpectral(t * 0.8 + 0.1), names.size);

// Construct scales.
const color = d3.scaleOrdinal(names, colors);

// Compute titles.
if (title === undefined) {
const formatValue = d3.format(format);
title = i => `${formatValue(V[i])}\n${N[i]}`;
} else {
const O = d3.map(data, d => d);
const T = title;
title = i => T(O[i], i, data);
}

// Construct arcs.
const arcs = d3.pie().padAngle(padAngle).sort(null).value(i => V[i])(I);
const arc = d3.arc().innerRadius(innerRadius).outerRadius(outerRadius);
const arcLabel = d3.arc().innerRadius(labelRadius).outerRadius(labelRadius);
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-width / 2, -height / 2, width, height])
.attr("style", "max-width: 100%; height: auto; height: intrinsic;");

svg.append("g")
.attr("stroke", stroke)
.attr("stroke-width", strokeWidth)
.attr("stroke-linejoin", strokeLinejoin)
.selectAll("path")
.data(arcs)
.join("path")
.attr("fill", d => color(N[d.data]))
.attr("d", arc)
.append("title")
.text(d => title(d.data));

svg.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 12)
.attr("text-anchor", "middle")
.selectAll("text")
.data(arcs)
.join("text")
.attr("transform", d => `translate(${arcLabel.centroid(d)})`)
.selectAll("tspan")
.data((d, i) => {
const lines = `${title(d.data)}`.split(/\n/);
return (d.endAngle - d.startAngle) > 0.25 ? lines : lines.slice(0, 1);
})
.join("tspan")
.attr("x", 0)
.attr("y", (_, i) => `${i * 1.1}em`)
.attr("font-weight", (_, i) => i ? null : "bold")
.text(d => d);

return Object.assign(svg.node(), {scales: {color}});
}
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more