Published
Edited
Mar 18, 2021
1 fork
Importers
Insert cell
md`# Model Input Calibration`
Insert cell
xlsx = require('https://bundle.run/xlsx@0.14.1')
Insert cell
Insert cell
Insert cell
chart = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);
svg.append("g")
.call(yAxis_ticks);

const g = svg.selectAll(".vars")
.data(counts)
.join("g")
.attr("class", "vars")
.attr("transform", d => `translate(0,${y(d.variable) + y.bandwidth()/2})`);
g.each(function(d, i) {
const x = d3.scaleLinear()
// .domain([0, 3]).nice()
.domain([0, 2]).nice()
.range([margin.left, width - margin.right]);
const xAxis = g => g
// .call(d3.axisBottom(x).tickValues([0, 1, 2, 3]).tickFormat(dd => x_lookup[d.variable] [dd]).tickPadding(12))
.call(d3.axisBottom(x).tickValues([0, 1, 2]).tickFormat(dd => x_lookup[d.variable][dd]).tickPadding(12))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").remove())
.call(g => g.selectAll("text").attr("font-weight", "bold"))
.call(g => g.selectAll("text").attr("color", (dd, i) => highlight(d, i)))
.call(g => g.selectAll("text").style("mix-blend-mode", "multiply"));
d3.select(this).selectAll(".dots")
// .data([0, 1, 2, 3])
.data([0, 1, 2])
.join("circle")
.attr("class", "dots")
.style("mix-blend-mode", "multiply")
.attr("stroke", "#00a1b0")
.attr("stroke-width", 1)
.attr("fill", chroma("#00a1b0").brighten(1.5).desaturate())
.attr("fill-opacity", .5)
.attr("cy", 0)
.attr("cx", (d, i) => x(i))
.attr("r", dd => scale(d[dd.toString(16)]))
d3.select(this).selectAll(".points")
// .data([0, 1, 2, 3])
.data([0, 1, 2])
.join("circle")
.attr("class", "points")
.style("mix-blend-mode", "multiply")
// .attr("stroke", "#00a1b0")
// .attr("stroke-width", 2)
.attr("fill", (dd, i) => highlight(d, i))
.attr("cy", 0)
.attr("cx", (d, i) => x(i))
.attr("r", 4)
d3.select(this).append("g").call(xAxis);

})

svg.append("g")
.call(xAxis);

svg.append("g")
.call(yAxis);
svg.selectAll("text").attr("font-size", 16).attr("font-family", "Open Sans")

return svg.node();
}
Insert cell
filename = binary.name.split(".")[0]
Insert cell
input = new Uint8Array(await Files.buffer(binary))
Insert cell
wb = xlsx.read(input, {type: 'array'})
Insert cell
sheets = wb.SheetNames.filter(d => d != "Building_Design_Space_Original" && d != "Building Information" && d != "Baseline")
Insert cell
// data must be structured with typical input fields as column headers, with one column calling out building name
// data = FileAttachment("UT Baseline Inputs@1.csv").csv({typed: true})

Insert cell
excel = wb.Sheets["Baseline"]
Insert cell
data = xlsx.utils.sheet_to_json(excel)
Insert cell
Insert cell
margin = ({top: 80, right: 100, bottom: 50, left: 350})
Insert cell
height = 1500
Insert cell
d3 = require("d3@^6.1")
Insert cell
_ = require('lodash@4.17.15/lodash.js').catch(() => window["_"])
Insert cell
dates = d3.timeMonth.range(new Date(2019, 0, 1), new Date(2019, 11, 30)).map(d3.timeFormat("%b"));
Insert cell
colors = ({"S": "#f79320", "E": "#fed202", "C":"#00a1b0"})
Insert cell
chroma = require('chroma-js')
Insert cell
v_lookup = ({
"WWR": "Window-Wall Ratio (%)",
"WINU": "Window Assembly U (BTU/h-ft2-°F)",
"WINSHGC": "Window SHGC",
"WALLR": "Effective Wall R (h-ft2-°F/BTU)",
"ROOFR": "Effective Roof R (h-ft2-°F/BTU)",
"OCCSCHED": "Occupancy Schedule",
"EQUIPSCHED": "Equipment Usage Schedule",
"LIGHTUSAGE": "Lighting Usage Schedule",
"HEATSCHED": "Heating Schedule",
"COOLSCHED": "Cooling Schedule",
"EPD": "Equipment Power Density (W/ft2)",
"LPD": "Lighting Power Density (W/ft2)",
"HRV": "Sensible ERV Effectiveness",
"ACH": "Air Changes per Hour",
"VENTSCHED": "Ventilation Schedule",
"HUMIDMIN": "Humidity Control Min",
"HUMIDMAX": "Humidity Control Max",
"SUPPTEMPRESET": "Max Supply Air Temp (°F)",
"FANPOWER": "Fan Pressure Drop (in. H2O)",
// "OAAIR": "Outside Air System",
"ECON": "Economizer"
// "CCOP": "Cooling COP",
// "HFFACTOR": "Heating Fuel Factor",
// "HEFACTOR": "Heating Electrical Factor"
})
Insert cell
counts = {

const count = (d, i) => {
const arr = data.map(dd => dd[d]);
return arr.filter(dd => dd === i).length;
}
const keymap = Object.keys(v_lookup).map(d => ({variable: d, "0": count(d, 0), "1": count(d, 1), "2": count(d, 2), "3": count(d, 3)}));
return keymap;

}
Insert cell
energy = d => {
let r = "Low";
switch(d) {
case 0:
r= "High";
break;
case 1:
// r= "Mid-High";
r= "Mid";
break;
case 2:
// r= "Low-High";
r= "Low";
break;
default:
r="Low";
}
return r;
}
Insert cell
y = d3.scaleBand()
.domain(Object.keys(v_lookup).reverse())
.range([height - margin.bottom, margin.top])
.padding(.1)
.paddingOuter(.5)
Insert cell
x = d3.scaleLinear()
// .domain([0, 3]).nice()
.domain([0, 2]).nice()
.range([margin.left, width - margin.right])
Insert cell
xAxis = g => g
.attr("transform", `translate(0,${margin.top})`)
// .call(d3.axisTop(x).tickValues([0, 1, 2, 3]).tickFormat((d, i) => energy(i)).tickPadding(10))
.call(d3.axisTop(x).tickValues([0, 1, 2]).tickFormat((d, i) => energy(i)).tickPadding(10))
.call(g => g.select(".domain").remove())
.call(g => g.select(".tick:first-of-type text").clone()
.attr("y", -50)
.attr("x", -18)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.html("Energy Use Impact"))
Insert cell
yAxis = g => g
.attr("transform", `translate(${5},0)`)
.call(d3.axisRight(y).tickFormat(d => v_lookup[d]))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").remove())
Insert cell
yAxis_ticks = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisRight(y).tickFormat(d => v_lookup[d]))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll("text").remove())
.call(g => g.selectAll(".tick line")
.attr("stroke-opacity", 0.1)
.attr("x2", width - margin.left - margin.right))
Insert cell
scale = d3.scaleLinear()
.domain([0, 25])
.range([0, y.bandwidth()/1.5])
Insert cell
x_lookup = ({
"WWR": ["70%", "50%", "30%", "NA"],
"WINU": ["0.9", "0.6", "0.3", "NA"],
"WINSHGC": ["0.2", "0.4", "0.6", "NA"],
"WALLR": ["5", "15", "25", "NA"],
"ROOFR": ["10", "25", "40", "NA"],
"OCCSCHED": ["Industrial", "Lab", "Office", "NA"],
"EQUIPSCHED": ["Industrial", "Lab", "Office", "NA"],
"LIGHTUSAGE": ["Industrial", "Lab", "Office", "NA"],
"HEATSCHED": ["Industrial", "Lab", "Office", "NA"],
"COOLSCHED": ["Industrial", "Lab", "Office", "NA"],
"EPD": ["6", "3", "1", "NA"],
"LPD": ["2", "1", "0.5", "NA"],
"HRV": ["0.0", "0.3", "0.6", "NA"],
"ACH": ["12", "6", "1", "NA"],
"VENTSCHED": ["High", "Mid", "Low", "NA"],
"HUMIDMIN": ["Yes", "No", "NA", "NA"],
"HUMIDMAX": ["Yes", "No", "NA", "NA"],
"SUPPTEMPRESET": ["55", "60", "65", "NA"],
"FANPOWER": ["8", "6", "4", "NA"],
"OAAIR": ["Once Through", "Recirculating", "NA", "NA"],
"ECON": ["No Economizer", "Fixed Dry Bulb", "NA", "NA"],
"CCOP": ["3", "5", "7", "NA"],
"HFFACTOR": ["0.7", "2.0", "4.0", ">100"],
"HEFACTOR": ["1.0", "6.0", "12.0", ">100"]
})

Insert cell
highlight = (d, i) => {
const objs = Object.entries(d).filter(dd => dd[0] != "variable").sort((a, b) => b[1] - a[1]);
const index = +objs[0][0];
return index === i ? "#00a1b0": "#dfdfdf";

}
Insert cell
serialize = {
const xmlns = "http://www.w3.org/2000/xmlns/";
const xlinkns = "http://www.w3.org/1999/xlink";
const svgns = "http://www.w3.org/2000/svg";
return function serialize(svg) {
svg = svg.cloneNode(true);
const fragment = window.location.href + "#";
const walker = document.createTreeWalker(svg, NodeFilter.SHOW_ELEMENT, null, false);
while (walker.nextNode()) {
for (const attr of walker.currentNode.attributes) {
if (attr.value.includes(fragment)) {
attr.value = attr.value.replace(fragment, "#");
}
}
}
svg.setAttributeNS(xmlns, "xmlns", svgns);
svg.setAttributeNS(xmlns, "xmlns:xlink", xlinkns);
const serializer = new window.XMLSerializer;
const string = serializer.serializeToString(svg);
return new Blob([string], {type: "image/svg+xml"});
};
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more