Skip to content

Instantly share code, notes, and snippets.

@joshcarr
Forked from mbostock/.block
Last active February 9, 2020 20:01
Show Gist options
  • Select an option

  • Save joshcarr/d70db1e169db6dc8702e to your computer and use it in GitHub Desktop.

Select an option

Save joshcarr/d70db1e169db6dc8702e to your computer and use it in GitHub Desktop.
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['psy', 'util'], factory);
} else {
// Browser globals
root.comf = factory(root.psy, root.util);
}
}(this, function (psy, util) {
var pow = Math.pow;
var exp = Math.exp;
var max = Math.max;
var abs = Math.abs;
var sqrt = Math.sqrt;
var comf = {};
// if (typeof module !== 'undefined' && module.exports) {
// var psy = require('./psychrometrics.js').psy
// var util = require('./util.js').util
// module.exports.comf = comf;
// }
comf.between = function (x, l, r) {
return (x > l && x < r);
};
comf.globeTemperature = function(tw, tr, ta) {
// calculate composite globe temperature
return 0.7 * tw + 0.2 * tr + 0.1 * ta;
};
comf.adaptiveComfortASH55 = function(ta, tr, runningMean, vel) {
var r = {};
var to = (ta + tr) / 2;
var coolingEffect = 0;
if (vel > 0.3 & to >= 25) {
// calculate cooling effect of elevated air speed
// when top > 25 degC.
switch (vel) {
case 0.6:
coolingEffect = 1.2;
break;
case 0.9:
coolingEffect = 1.8;
break;
case 1.2:
coolingEffect = 2.2;
break;
}
}
var tComf = 0.31 * runningMean + 17.8;
r.tComf80Lower = tComf - 3.5;
r.tComf80Upper = tComf + 3.5 + coolingEffect;
r.tComf90Lower = tComf - 2.5;
r.tComf90Upper = tComf + 2.5 + coolingEffect;
var acceptability80, acceptability90;
if (comf.between(to, r.tComf90Lower, r.tComf90Upper)) {
// compliance at 80% and 90% levels
acceptability80 = acceptability90 = true;
} else if (comf.between(to, r.tComf80Lower, r.tComf80Upper)) {
// compliance at 80% only
acceptability80 = true;
acceptability90 = false;
} else {
// neither
acceptability80 = acceptability90 = false;
}
r.acceptability90 = acceptability90;
r.acceptability80 = acceptability80;
return r;
};
comf.pmvElevatedAirspeed = function(ta, tr, vel, rh, met, clo, wme) {
// returns pmv at elevated airspeed (>0.15m/s)
var r = {};
var set = comf.pierceSET(ta, tr, vel, rh, met , clo, wme);
var ta_adj,
pmv,
ce;
if (vel <= 0.15) {
pmv = comf.pmv(ta, tr, vel, rh, met, clo, wme);
ta_adj = ta;
ce = 0;
} else {
var ta_adj_l = -200;
var ta_adj_r = 200;
var eps = 0.001; // precision of ta_adj
var fn = function(t){
return (set - comf.pierceSET(t, tr, 0.15, rh, met, clo, wme));
};
ta_adj = util.secant(ta_adj_l, ta_adj_r, fn, eps);
if (isNaN(ta_adj)) {
ta_adj = util.bisect(ta_adj_l, ta_adj_r, fn, eps, 0);
}
pmv = comf.pmv(ta_adj, tr, 0.15, rh, met, clo, wme);
ce = Math.abs(ta - ta_adj);
}
r.pmv = pmv.pmv;
r.ppd = pmv.ppd;
r.set = set;
r.ta_adj = ta_adj;
r.cooling_effect = ce;
return r;
};
comf.pmv = function(ta, tr, vel, rh, met, clo, wme) {
// returns [pmv, ppd]
// ta, air temperature (°C)
// tr, mean radiant temperature (°C)
// vel, relative air velocity (m/s)
// rh, relative humidity (%) Used only this way to input humidity level
// met, metabolic rate (met)
// clo, clothing (clo)
// wme, external work, normally around 0 (met)
var pa, icl, m, w, mw, fcl, hcf, taa, tra, tcla, p1, p2, p3, p4,
p5, xn, xf, eps, hcn, hc, tcl, hl1, hl2, hl3, hl4, hl5, hl6,
ts, pmv, ppd, n;
pa = rh * 10 * exp(16.6536 - 4030.183 / (ta + 235));
icl = 0.155 * clo; //thermal insulation of the clothing in M2K/W
m = met * 58.15; //metabolic rate in W/M2
w = wme * 58.15; //external work in W/M2
mw = m - w; //internal heat production in the human body
if (icl <= 0.078) fcl = 1 + (1.29 * icl);
else fcl = 1.05 + (0.645 * icl);
//heat transf. coeff. by forced convection
hcf = 12.1 * sqrt(vel);
taa = ta + 273;
tra = tr + 273;
tcla = taa + (35.5 - ta) / (3.5 * icl + 0.1);
p1 = icl * fcl;
p2 = p1 * 3.96;
p3 = p1 * 100;
p4 = p1 * taa;
p5 = 308.7 - 0.028 * mw + p2 * pow(tra / 100, 4);
xn = tcla / 100;
xf = tcla / 50;
eps = 0.00015;
n = 0;
while (abs(xn - xf) > eps) {
xf = (xf + xn) / 2;
hcn = 2.38 * pow(abs(100.0 * xf - taa), 0.25);
if (hcf > hcn) hc = hcf;
else hc = hcn;
xn = (p5 + p4 * hc - p2 * pow(xf, 4)) / (100 + p3 * hc);
++n;
if (n > 150) {
alert('Max iterations exceeded');
return 1;
}
}
tcl = 100 * xn - 273;
// heat loss diff. through skin
hl1 = 3.05 * 0.001 * (5733 - (6.99 * mw) - pa);
// heat loss by sweating
if (mw > 58.15) hl2 = 0.42 * (mw - 58.15);
else hl2 = 0;
// latent respiration heat loss
hl3 = 1.7 * 0.00001 * m * (5867 - pa);
// dry respiration heat loss
hl4 = 0.0014 * m * (34 - ta);
// heat loss by radiation
hl5 = 3.96 * fcl * (pow(xn, 4) - pow(tra / 100, 4));
// heat loss by convection
hl6 = fcl * hc * (tcl - ta);
ts = 0.303 * exp(-0.036 * m) + 0.028;
pmv = ts * (mw - hl1 - hl2 - hl3 - hl4 - hl5 - hl6);
ppd = 100.0 - 95.0 * exp(-0.03353 * pow(pmv, 4.0) - 0.2179 * pow(pmv, 2.0));
var r = {};
r.pmv = pmv;
r.ppd = ppd;
return r;
};
comf.FindSaturatedVaporPressureTorr = function(T) {
//calculates Saturated Vapor Pressure (Torr) at Temperature T (C)
return exp(18.6686 - 4030.183 / (T + 235.0));
};
comf.pierceSET = function(ta, tr, vel, rh, met, clo, wme) {
var TempSkinNeutral, TempBodyNeutral, SkinBloodFlowNeutral, TempSkin, TempCore,
SkinBloodFlow, MSHIV, ALFA, ESK, PressureInAtmospheres, TIMEH, LTIME, DELTA, RCL,
FACL, LR, RM, M, WCRIT, ICL, CHC, CHCA, CHCV, CHR, CTC, TOP, TCL, DRY, HFCS, ERES,
CRES, SCR, SSK, TCSK, TB, SKSIG, WARMS, COLDS, WARMC, COLDC, CRSIG, WARMB, COLDB,
REGSW, BDSIG, REA, RECL, EMAX, PRSW, PWET, EDIF, RA, TCL_OLD, TCCR, DTSK, DTCR, ERSW,
X, X_OLD, CHCS, TIM, STORE, HSK, RN, ECOMF, EREQ, HD, HE, W, PSSK, CHRS, CTCS,
RCLOS, RCLS, FACLS, FCLS, IMS, ICLS, RAS, REAS, RECLS, HD_S, HE_S;
var VaporPressure = rh * comf.FindSaturatedVaporPressureTorr(ta) / 100;
var AirVelocity = max(vel, 0.1);
var KCLO = 0.25;
var BODYWEIGHT = 69.9;
var BODYSURFACEAREA = 1.8258;
var METFACTOR = 58.2;
var SBC = 0.000000056697; // Stefan-Boltzmann constant (W/m2K4)
var CSW = 170;
var CDIL = 120;
var CSTR = 0.5;
TempSkinNeutral = 33.7; //setpoint (neutral) value for Tsk
TempCoreNeutral = 36.49; //setpoint value for Tcr
TempBodyNeutral = 36.49; //setpoint for Tb (.1*TempSkinNeutral + .9*TempCoreNeutral)
SkinBloodFlowNeutral = 6.3; //neutral value for SkinBloodFlow
//INITIAL VALUES - start of 1st experiment
TempSkin = TempSkinNeutral;
TempCore = TempCoreNeutral;
SkinBloodFlow = SkinBloodFlowNeutral;
MSHIV = 0.0;
ALFA = 0.1;
ESK = 0.1 * met;
//Start new experiment here (for graded experiments)
//UNIT CONVERSIONS (from input variables)
var p = psy.PROP.Patm / 1000; // TH : interface?
PressureInAtmospheres = p * 0.009869;
LTIME = 60.0;
TIMEH = LTIME / 60.0;
RCL = 0.155 * clo;
// AdjustICL(RCL, Conditions); TH: I don't think this is used in the software
FACL = 1.0 + 0.15 * clo; //% INCREASE IN BODY SURFACE AREA DUE TO CLOTHING
LR = 2.2 / PressureInAtmospheres; //Lewis Relation is 2.2 at sea level
RM = met * METFACTOR;
M = met * METFACTOR;
if (clo <= 0) {
WCRIT = 0.38 * pow(AirVelocity, -0.29);
ICL = 1.0;
} else {
WCRIT = 0.59 * pow(AirVelocity, -0.08);
ICL = 0.45;
}
CHC = 3.0 * pow(PressureInAtmospheres, 0.53);
CHCV = 8.600001 * pow((AirVelocity * PressureInAtmospheres), 0.53);
CHC = max(CHC, CHCV);
//initial estimate of Tcl
CHR = 4.7;
CTC = CHR + CHC;
RA = 1.0 / (FACL * CTC); //resistance of air layer to dry heat transfer
TOP = (CHR * tr + CHC * ta) / CTC;
TCL = TOP + (TempSkin - TOP) / (CTC * (RA + RCL));
// ======================== BEGIN ITERATION
//
// Tcl and CHR are solved iteratively using: H(Tsk - To) = CTC(Tcl - To),
// where H = 1/(Ra + Rcl) and Ra = 1/Facl*CTC
//
TCL_OLD = TCL;
var flag = true;
for (TIM = 1; TIM <= LTIME; TIM++) {
do {
if (flag) {
TCL_OLD = TCL;
CHR = 4.0 * SBC * pow(((TCL + tr) / 2.0 + 273.15), 3.0) * 0.72;
CTC = CHR + CHC;
RA = 1.0 / (FACL * CTC); //resistance of air layer to dry heat transfer
TOP = (CHR * tr + CHC * ta) / CTC;
}
TCL = (RA * TempSkin + RCL * TOP) / (RA + RCL);
flag = true;
} while (abs(TCL - TCL_OLD) > 0.01);
flag = false;
DRY = (TempSkin - TOP) / (RA + RCL);
HFCS = (TempCore - TempSkin) * (5.28 + 1.163 * SkinBloodFlow);
ERES = 0.0023 * M * (44.0 - VaporPressure);
CRES = 0.0014 * M * (34.0 - ta);
SCR = M - HFCS - ERES - CRES - wme;
SSK = HFCS - DRY - ESK;
TCSK = 0.97 * ALFA * BODYWEIGHT;
TCCR = 0.97 * (1 - ALFA) * BODYWEIGHT;
DTSK = (SSK * BODYSURFACEAREA) / (TCSK * 60.0); //deg C per minute
DTCR = SCR * BODYSURFACEAREA / (TCCR * 60.0); //deg C per minute
TempSkin = TempSkin + DTSK;
TempCore = TempCore + DTCR;
TB = ALFA * TempSkin + (1 - ALFA) * TempCore;
SKSIG = TempSkin - TempSkinNeutral;
WARMS = (SKSIG > 0) * SKSIG;
COLDS = ((-1.0 * SKSIG) > 0) * (-1.0 * SKSIG);
CRSIG = (TempCore - TempCoreNeutral);
WARMC = (CRSIG > 0) * CRSIG;
COLDC = ((-1.0 * CRSIG) > 0) * (-1.0 * CRSIG);
BDSIG = TB - TempBodyNeutral;
WARMB = (BDSIG > 0) * BDSIG;
COLDB = ((-1.0 * BDSIG) > 0) * (-1.0 * BDSIG);
SkinBloodFlow = (SkinBloodFlowNeutral + CDIL * WARMC) / (1 + CSTR * COLDS);
if (SkinBloodFlow > 90.0) SkinBloodFlow = 90.0;
if (SkinBloodFlow < 0.5) SkinBloodFlow = 0.5;
REGSW = CSW * WARMB * exp(WARMS / 10.7);
if (REGSW > 500.0) REGSW = 500.0;
ERSW = 0.68 * REGSW;
REA = 1.0 / (LR * FACL * CHC); //evaporative resistance of air layer
RECL = RCL / (LR * ICL); //evaporative resistance of clothing (icl=.45)
EMAX = (comf.FindSaturatedVaporPressureTorr(TempSkin) - VaporPressure) / (REA + RECL);
PRSW = ERSW / EMAX;
PWET = 0.06 + 0.94 * PRSW;
EDIF = PWET * EMAX - ERSW;
ESK = ERSW + EDIF;
if (PWET > WCRIT) {
PWET = WCRIT;
PRSW = WCRIT / 0.94;
ERSW = PRSW * EMAX;
EDIF = 0.06 * (1.0 - PRSW) * EMAX;
ESK = ERSW + EDIF;
}
if (EMAX < 0) {
EDIF = 0;
ERSW = 0;
PWET = WCRIT;
PRSW = WCRIT;
ESK = EMAX;
}
ESK = ERSW + EDIF;
MSHIV = 19.4 * COLDS * COLDC;
M = RM + MSHIV;
ALFA = 0.0417737 + 0.7451833 / (SkinBloodFlow + 0.585417);
}
//Define new heat flow terms, coeffs, and abbreviations
STORE = M - wme - CRES - ERES - DRY - ESK; //rate of body heat storage
HSK = DRY + ESK; //total heat loss from skin
RN = M - wme; //net metabolic heat production
ECOMF = 0.42 * (RN - (1 * METFACTOR));
if (ECOMF < 0.0) ECOMF = 0.0; //from Fanger
EREQ = RN - ERES - CRES - DRY;
EMAX = EMAX * WCRIT;
HD = 1.0 / (RA + RCL);
HE = 1.0 / (REA + RECL);
W = PWET;
PSSK = comf.FindSaturatedVaporPressureTorr(TempSkin);
// Definition of ASHRAE standard environment... denoted "S"
CHRS = CHR;
if (met < 0.85) {
CHCS = 3.0;
} else {
CHCS = 5.66 * pow(((met - 0.85)), 0.39);
if (CHCS < 3.0) CHCS = 3.0;
}
CTCS = CHCS + CHRS;
RCLOS = 1.52 / ((met - wme / METFACTOR) + 0.6944) - 0.1835;
RCLS = 0.155 * RCLOS;
FACLS = 1.0 + KCLO * RCLOS;
FCLS = 1.0 / (1.0 + 0.155 * FACLS * CTCS * RCLOS);
IMS = 0.45;
ICLS = IMS * CHCS / CTCS * (1 - FCLS) / (CHCS / CTCS - FCLS * IMS);
RAS = 1.0 / (FACLS * CTCS);
REAS = 1.0 / (LR * FACLS * CHCS);
RECLS = RCLS / (LR * ICLS);
HD_S = 1.0 / (RAS + RCLS);
HE_S = 1.0 / (REAS + RECLS);
// SET* (standardized humidity, clo, Pb, and CHC)
// determined using Newton//s iterative solution
// FNERRS is defined in the GENERAL SETUP section above
DELTA = 0.0001;
var ERR1, ERR2;
var dx = 100.0;
X_OLD = TempSkin - HSK / HD_S; //lower bound for SET
while (abs(dx) > 0.01) {
ERR1 = (HSK - HD_S * (TempSkin - X_OLD) - W * HE_S * (PSSK - 0.5 * comf.FindSaturatedVaporPressureTorr(X_OLD)));
ERR2 = (HSK - HD_S * (TempSkin - (X_OLD + DELTA)) - W * HE_S * (PSSK - 0.5 * comf.FindSaturatedVaporPressureTorr((X_OLD + DELTA))));
X = X_OLD - DELTA * ERR1 / (ERR2 - ERR1);
dx = X - X_OLD;
X_OLD = X;
}
return X;
};
comf.schiavonClo = function(ta6) {
var clo_r;
if(!isCelsius) ta6 = util.FtoC(ta6);
if (ta6 < -5) {
clo_r = 1;
} else if (ta6 < 5) {
clo_r = 0.818 - 0.0364 * ta6;
} else if (ta6 < 26) {
clo_r = Math.pow(10, -0.1635 - 0.0066 * ta6);
} else {
clo_r = 0.46;
}
return clo_r;
};
comf.adaptiveComfortEN15251 = function(ta, tr, runningMean, vel) {
var tComfLow,
tComfILower,
tComfIUpper,
tComfIILower,
tComfIIUpper,
tComfIIILower,
tComfIIIUpper;
var to = (ta + tr) / 2;
var coolingEffect = 0;
if (vel >= 0.2 && to > 25) {
// calculate cooling effect of elevated air speed
// when top > 25 degC.
coolingEffect = 1.7856 * Math.log(vel) + 2.9835;
}
var tComf = 0.33 * runningMean + 18.8;
if(runningMean > 15){
tComfILower = tComf - 2;
tComfIUpper = tComf + 2 + coolingEffect;
tComfIILower = tComf - 3;
tComfIIUpper = tComf + 3 + coolingEffect;
tComfIIILower = tComf - 4;
tComfIIIUpper = tComf + 4 + coolingEffect;
} else if (12.73 < runningMean && runningMean < 15){
tComfLow = 0.33 * 15 + 18.8;
tComfILower = tComfLow - 2;
tComfIUpper = tComf + 2 + coolingEffect;
tComfIILower = tComfLow - 3;
tComfIIUpper = tComf + 3 + coolingEffect;
tComfIIILower = tComfLow - 4;
tComfIIIUpper = tComf + 4 + coolingEffect;
} else {
tComfLow = 0.33 * 15 + 18.8;
tComfILower = tComfLow - 2;
tComfIUpper = tComf + 2;
tComfIILower = tComfLow - 3;
tComfIIUpper = tComf + 3 + coolingEffect;
tComfIIILower = tComfLow - 4;
tComfIIIUpper = tComf + 4 + coolingEffect;
}
var acceptabilityI, acceptabilityII, acceptabilityIII;
if (comf.between(to, tComfILower, tComfIUpper)) {
// compliance at all levels
acceptabilityI = acceptabilityII = acceptabilityIII = true;
} else if (comf.between(to, tComfIILower, tComfIIUpper)) {
// compliance at II and III only
acceptabilityII = acceptabilityIII = true;
acceptabilityI = false;
} else if (comf.between(to, tComfIIILower, tComfIIIUpper)) {
// compliance at III only
acceptabilityIII = true;
acceptabilityI = acceptabilityII = false;
} else {
// neither
acceptabilityI = acceptabilityII = acceptabilityIII = false;
}
var r = {};
r.acceptabilityI = acceptabilityI;
r.acceptabilityII = acceptabilityII;
r.acceptabilityIII = acceptabilityIII;
r.tComfILower = tComfILower;
r.tComfIILower = tComfIILower;
r.tComfIIILower = tComfIIILower;
r.tComfIUpper = tComfIUpper;
r.tComfIIUpper = tComfIIUpper;
r.tComfIIIUpper = tComfIIIUpper;
return r;
// return [[acceptabilityIII, tComfIIILower, tComfIIIUpper],
// [acceptabilityII, tComfIILower, tComfIIUpper],
// [acceptabilityI, tComfILower, tComfIUpper]];
};
return comf;
}));
sepalLength sepalWidth petalLength petalWidth species
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5.0 3.6 1.4 0.2 setosa
5.4 3.9 1.7 0.4 setosa
4.6 3.4 1.4 0.3 setosa
5.0 3.4 1.5 0.2 setosa
4.4 2.9 1.4 0.2 setosa
4.9 3.1 1.5 0.1 setosa
5.4 3.7 1.5 0.2 setosa
4.8 3.4 1.6 0.2 setosa
4.8 3.0 1.4 0.1 setosa
4.3 3.0 1.1 0.1 setosa
5.8 4.0 1.2 0.2 setosa
5.7 4.4 1.5 0.4 setosa
5.4 3.9 1.3 0.4 setosa
5.1 3.5 1.4 0.3 setosa
5.7 3.8 1.7 0.3 setosa
5.1 3.8 1.5 0.3 setosa
5.4 3.4 1.7 0.2 setosa
5.1 3.7 1.5 0.4 setosa
4.6 3.6 1.0 0.2 setosa
5.1 3.3 1.7 0.5 setosa
4.8 3.4 1.9 0.2 setosa
5.0 3.0 1.6 0.2 setosa
5.0 3.4 1.6 0.4 setosa
5.2 3.5 1.5 0.2 setosa
5.2 3.4 1.4 0.2 setosa
4.7 3.2 1.6 0.2 setosa
4.8 3.1 1.6 0.2 setosa
5.4 3.4 1.5 0.4 setosa
5.2 4.1 1.5 0.1 setosa
5.5 4.2 1.4 0.2 setosa
4.9 3.1 1.5 0.2 setosa
5.0 3.2 1.2 0.2 setosa
5.5 3.5 1.3 0.2 setosa
4.9 3.6 1.4 0.1 setosa
4.4 3.0 1.3 0.2 setosa
5.1 3.4 1.5 0.2 setosa
5.0 3.5 1.3 0.3 setosa
4.5 2.3 1.3 0.3 setosa
4.4 3.2 1.3 0.2 setosa
5.0 3.5 1.6 0.6 setosa
5.1 3.8 1.9 0.4 setosa
4.8 3.0 1.4 0.3 setosa
5.1 3.8 1.6 0.2 setosa
4.6 3.2 1.4 0.2 setosa
5.3 3.7 1.5 0.2 setosa
5.0 3.3 1.4 0.2 setosa
7.0 3.2 4.7 1.4 versicolor
6.4 3.2 4.5 1.5 versicolor
6.9 3.1 4.9 1.5 versicolor
5.5 2.3 4.0 1.3 versicolor
6.5 2.8 4.6 1.5 versicolor
5.7 2.8 4.5 1.3 versicolor
6.3 3.3 4.7 1.6 versicolor
4.9 2.4 3.3 1.0 versicolor
6.6 2.9 4.6 1.3 versicolor
5.2 2.7 3.9 1.4 versicolor
5.0 2.0 3.5 1.0 versicolor
5.9 3.0 4.2 1.5 versicolor
6.0 2.2 4.0 1.0 versicolor
6.1 2.9 4.7 1.4 versicolor
5.6 2.9 3.6 1.3 versicolor
6.7 3.1 4.4 1.4 versicolor
5.6 3.0 4.5 1.5 versicolor
5.8 2.7 4.1 1.0 versicolor
6.2 2.2 4.5 1.5 versicolor
5.6 2.5 3.9 1.1 versicolor
5.9 3.2 4.8 1.8 versicolor
6.1 2.8 4.0 1.3 versicolor
6.3 2.5 4.9 1.5 versicolor
6.1 2.8 4.7 1.2 versicolor
6.4 2.9 4.3 1.3 versicolor
6.6 3.0 4.4 1.4 versicolor
6.8 2.8 4.8 1.4 versicolor
6.7 3.0 5.0 1.7 versicolor
6.0 2.9 4.5 1.5 versicolor
5.7 2.6 3.5 1.0 versicolor
5.5 2.4 3.8 1.1 versicolor
5.5 2.4 3.7 1.0 versicolor
5.8 2.7 3.9 1.2 versicolor
6.0 2.7 5.1 1.6 versicolor
5.4 3.0 4.5 1.5 versicolor
6.0 3.4 4.5 1.6 versicolor
6.7 3.1 4.7 1.5 versicolor
6.3 2.3 4.4 1.3 versicolor
5.6 3.0 4.1 1.3 versicolor
5.5 2.5 4.0 1.3 versicolor
5.5 2.6 4.4 1.2 versicolor
6.1 3.0 4.6 1.4 versicolor
5.8 2.6 4.0 1.2 versicolor
5.0 2.3 3.3 1.0 versicolor
5.6 2.7 4.2 1.3 versicolor
5.7 3.0 4.2 1.2 versicolor
5.7 2.9 4.2 1.3 versicolor
6.2 2.9 4.3 1.3 versicolor
5.1 2.5 3.0 1.1 versicolor
5.7 2.8 4.1 1.3 versicolor
6.3 3.3 6.0 2.5 virginica
5.8 2.7 5.1 1.9 virginica
7.1 3.0 5.9 2.1 virginica
6.3 2.9 5.6 1.8 virginica
6.5 3.0 5.8 2.2 virginica
7.6 3.0 6.6 2.1 virginica
4.9 2.5 4.5 1.7 virginica
7.3 2.9 6.3 1.8 virginica
6.7 2.5 5.8 1.8 virginica
7.2 3.6 6.1 2.5 virginica
6.5 3.2 5.1 2.0 virginica
6.4 2.7 5.3 1.9 virginica
6.8 3.0 5.5 2.1 virginica
5.7 2.5 5.0 2.0 virginica
5.8 2.8 5.1 2.4 virginica
6.4 3.2 5.3 2.3 virginica
6.5 3.0 5.5 1.8 virginica
7.7 3.8 6.7 2.2 virginica
7.7 2.6 6.9 2.3 virginica
6.0 2.2 5.0 1.5 virginica
6.9 3.2 5.7 2.3 virginica
5.6 2.8 4.9 2.0 virginica
7.7 2.8 6.7 2.0 virginica
6.3 2.7 4.9 1.8 virginica
6.7 3.3 5.7 2.1 virginica
7.2 3.2 6.0 1.8 virginica
6.2 2.8 4.8 1.8 virginica
6.1 3.0 4.9 1.8 virginica
6.4 2.8 5.6 2.1 virginica
7.2 3.0 5.8 1.6 virginica
7.4 2.8 6.1 1.9 virginica
7.9 3.8 6.4 2.0 virginica
6.4 2.8 5.6 2.2 virginica
6.3 2.8 5.1 1.5 virginica
6.1 2.6 5.6 1.4 virginica
7.7 3.0 6.1 2.3 virginica
6.3 3.4 5.6 2.4 virginica
6.4 3.1 5.5 1.8 virginica
6.0 3.0 4.8 1.8 virginica
6.9 3.1 5.4 2.1 virginica
6.7 3.1 5.6 2.4 virginica
6.9 3.1 5.1 2.3 virginica
5.8 2.7 5.1 1.9 virginica
6.8 3.2 5.9 2.3 virginica
6.7 3.3 5.7 2.5 virginica
6.7 3.0 5.2 2.3 virginica
6.3 2.5 5.0 1.9 virginica
6.5 3.0 5.2 2.0 virginica
6.2 3.4 5.4 2.3 virginica
5.9 3.0 5.1 1.8 virginica
<!DOCTYPE html>
<meta charset="utf-8">
<title>Thermal Comfort Zone Scatter Plot</title>
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.dot {
stroke: none;
opacity: 0.35;
}
path {
fill:none;
stroke-width:1px;
}
.axis path{
fill:none;
stroke: black;
}
.axis line{
fill:none;
stroke: rgba(0,0,0,0.1);
}
/*.axis {
font-size:10pt;
font-family:sans-serif;
}*/
.box-texts, .hover-box-texts{
font-size:10pt;
font-family:sans-serif;
}
circle.outer{
stroke:red;
stroke-width:2px;
fill: none;
}
circle.inner{
stroke:red;
stroke-width:2px;
fill: red;
}
path.rh100{
stroke:Black;
}
path.rhline{
stroke:Gray;
}
path.w{
fill: White;
}
rect.chartbg{
fill: rgb(220,220,220);
fill-opacity: 0.0;
}
rect.w{
fill: White;
}
path.comfortzone{
fill: rgb(0,0,100);
fill-opacity: 0.5;
}
path.comfortzone-temphum{
fill: rgb(0,0,100);
fill-opacity: 0.5;
}
path.comfortzoneover{
-webkit-transition-property: fill;
-webkit-transition-duration: .5s;
-webkit-transition-timing-function: ease-in;
-moz-transition-property: fill;
-moz-transition-duration: .5s;
-moz-transition-timing-function: ease-in;
transition-property: fill;
transition-duration: .5s;
transition-timing-function: ease-in;
fill: rgb(0,0,250);
fill-opacity: 0.5;
}
path.psyregion {
fill: rgb(200,200,200);
fill-opacity: 0.0;
}
/*.tick {
fill:none;
stroke: black;
opacity: 0.3;
stroke-dasharray: 5,5;
}*/
path.comfbound90{
stroke:Black;
}
path.comfbound80{
stroke:Gray;
}
path.comfort80 {
fill: DodgerBlue;
fill-opacity: 0.2;
}
path.comfort80over {
-webkit-transition-property: fill;
-webkit-transition-duration: .5s;
-webkit-transition-timing-function: ease-in;
-moz-transition-property: fill;
-moz-transition-duration: .5s;
-moz-transition-timing-function: ease-in;
transition-property: fill;
transition-duration: .5s;
transition-timing-function: ease-in;
fill: DeepSkyBlue;
fill-opacity: 0.2;
}
path.comfort90 {
fill: rgb(0,0,100);
fill-opacity: 0.5;
}
path.comfort90over {
-webkit-transition-property: fill;
-webkit-transition-duration: .5s;
-webkit-transition-timing-function: ease-in;
-moz-transition-property: fill;
-moz-transition-duration: .5s;
-moz-transition-timing-function: ease-in;
transition-property: fill;
transition-duration: .5s;
transition-timing-function: ease-in;
fill: rgb(0,0,250);
fill-opacity: 0.5;
}
</style>
<body>
<div id="chart-div"></div>
<div id="temphumchart-div"></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="util.js"></script>
<script src="psychrometrics.js"></script>
<script src="comfortmodels.js"></script>
<script src="temphumchart.js"></script>
<script>
function getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
}
function drawTempHumidityChart(){
var d = {"ta": 25, "tr": 25, "vel": 0.1, "rh": 50, "met": 1.2, "clo": 0.5, "wme": 0.0 },
data = [],
tempHumChart = new TemperatureHumidityChart(),
bound = tempHumChart.findComfortBoundary(d, 0.5);
tempHumChart.drawChart();
tempHumChart.drawComfortRegion(bound);
for (var i = 0; i < 500; i++) {
data.push({
ta: getRandomArbitrary(20,30),
rh: getRandomArbitrary(20,60)
})
};
tempHumChart.drawPoints(data)
}
function drawScatterplot(){
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 500 - margin.left - margin.right,
height = 480 - margin.top - margin.bottom;
var x = d3.scale.linear()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var data = [];
for (var i = 0; i < 500; i++) {
data.push({
temperature: getRandomArbitrary(20,30),
humidity: getRandomArbitrary(20,60),
location: "1842 All Locations Except Lobbies"
})
};
for (var i = 0; i < 500; i++) {
data.push({
temperature: getRandomArbitrary(25,35),
humidity: getRandomArbitrary(15,40),
location: "1842 Lobbies"
})
};
for (var i = 0; i < 500; i++) {
data.push({
temperature: getRandomArbitrary(10,35),
humidity: getRandomArbitrary(10,50),
location: "Mountain View External"
})
};
data.forEach(function(d) {
d.temperature = +d.temperature;
d.humidity = +d.humidity;
});
x.domain([0,80]).nice();
y.domain([0, 40]).nice();
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("Humidity (%RH)");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Temperature (deg C)")
svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 2.75)
.attr("cx", function(d) { return x(d.humidity); })
.attr("cy", function(d) { return y(d.temperature); })
.style("fill", function(d) { return color(d.location); });
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
}
drawTempHumidityChart();
</script>
</body>
var d = {
ta: '',
tr: '',
vel: '',
rh: '',
met: '',
clo: '',
trm: '',
vel_a: ''
};
var d_cache = {
ta: '',
tr: '',
vel: '',
rh: '',
met: '',
clo: '',
trm: '',
vel_a: ''
};
var keys = ["ta", "tr", "vel", "rh", "met", "clo", "trm", "vel_a"];
$(document).ready(function() {
var cloArticles = [{
article: 'Men\'s underwear',
clo: 0.04
}, {
article: 'Women\'s underwear',
clo: 0.03
}, {
article: 'Bra',
clo: 0.01
}, {
article: 'T-shirt',
clo: 0.08
}, {
article: 'Full slip',
clo: 0.16
}, {
article: 'Half slip',
clo: 0.14
}, {
article: 'Long underwear top',
clo: 0.2
}, {
article: 'Long underwear bottoms',
clo: 0.15
}, {
article: 'Shoes or sandals',
clo: 0.02
}, {
article: 'Slippers',
clo: 0.03
}, {
article: 'Knee socks (thick)',
clo: 0.06
}, {
article: 'Ankle socks',
clo: 0.02
}, {
article: 'Calf length socks',
clo: 0.03
}, {
article: 'Panty hose',
clo: 0.02
}, {
article: 'Boots',
clo: 0.1
}, {
article: 'Sleeveless scoop-neck blouse',
clo: 0.12
}, {
article: 'Short-sleeve dress shirt',
clo: 0.19
}, {
article: 'Long-sleeve dress shirt',
clo: 0.25
}, {
article: 'Long-sleeve flannel shirt',
clo: 0.34
}, {
article: 'Short-sleeve knit shirt',
clo: 0.17
}, {
article: 'Long-sleeve sweat shirt',
clo: 0.34
}, {
article: 'Short shorts',
clo: 0.06
}, {
article: 'Walking shorts',
clo: 0.08
}, {
article: 'Thin trousers',
clo: 0.15
}, {
article: 'Thick trousers',
clo: 0.24
}, {
article: 'Sweatpants',
clo: 0.28
}, {
article: 'Overalls',
clo: 0.30
}, {
article: 'Coveralls',
clo: 0.49
}, {
article: 'Thin skirt',
clo: 0.14
}, {
article: 'Thick skirt',
clo: 0.23
}, {
article: 'Long-sleeve shirtdress (thin)',
clo: 0.33
}, {
article: 'Long-sleeve shirtdress (thick)',
clo: 0.47
}, {
article: 'Short-sleeve shirtdress',
clo: 0.29
}, {
article: 'Sleeveless, scoop-neck shirt (thin)',
clo: 0.23
}, {
article: 'Sleeveless, scoop-neck shirt (thick)',
clo: 0.27
}, {
article: 'Sleeveless vest (thin)',
clo: 0.13
}, {
article: 'Sleeveless vest (thick)',
clo: 0.22
}, {
article: 'Longsleeve shirt (thin)',
clo: 0.25
}, {
article: 'Longsleeve shirt (thick)',
clo: 0.36
}, {
article: 'Single-breasted coat (thin)',
clo: 0.36
}, {
article: 'Single-breasted coat (thick)',
clo: 0.44
}, {
article: 'Double-breasted coat (thin)',
clo: 0.42
}, {
article: 'Double-breasted coat (thick)',
clo: 0.48
}, {
article: 'Sleeveless vest (thin)',
clo: 0.1
}, {
article: 'Sleeveless vest (thick)',
clo: 0.17
}, {
article: 'Sleeveless short gown (thin)',
clo: 0.18
}, {
article: 'Sleeveless long gown (thin)',
clo: 0.2
}, {
article: 'Short-sleeve hospital gown',
clo: 0.31
}, {
article: 'Short-sleeve short robe (thin)',
clo: 0.34
}, {
article: 'Long-sleeve long gown',
clo: 0.46
}, {
article: 'Long-sleeve short wrap robe (thick)',
clo: 0.48
}, {
article: 'Short-sleeve pajamas',
clo: 0.42
}, {
article: 'Long-sleeve pajamas (thick)',
clo: 0.57
}, {
article: 'Long-sleeve long wrap robe (thick)',
clo: 0.69
}, {
article: 'Metal chair',
clo: 0.00
}, {
article: 'Wooden stool',
clo: 0.01
}, {
article: 'Standard office chair',
clo: 0.10
}, {
article: 'Executive chair',
clo: 0.15
}];
var cloEnsembles = [{
clothing: 'Typical summer indoor clothing: 0.5',
clo: 0.5
}, {
clothing: 'Typical winter indoor clothing: 1.0',
clo: 1.0
}, {
clothing: 'Trousers, short-sleeve shirt, socks, shoes, underwear (SSU): 0.57',
clo: 0.57
}, {
clothing: 'Trousers, long-sleeve shirt, SSU: 0.61',
clo: 0.61
}, {
clothing: 'Jacket, Trousers, long-sleeve shirt, SSU: 0.96',
clo: 0.96
}, {
clothing: 'Knee-length skirt, short-sleeve shirt, sandals, underwear: 0.54',
clo: 0.54
}, {
clothing: 'Knee-length skirt, long-sleeve shirt, full slip, SSU: 0.67',
clo: 0.67
}, {
clothing: 'Walking shorts, short-sleeve shirt, SSU: 0.36',
clo: 0.36
}, {
clothing: 'Sweat pants, long-sleeve sweatshirt, SSU: 0.74',
clo: 0.74
}];
var actData = [{
activity: 'Standing, relaxed: 1.2',
met: 1.2
}, {
activity: 'Seated, quiet: 1.0',
met: 1.0
}, {
activity: 'Sleeping: 0.7',
met: 0.7
}, {
activity: 'Reclining: 0.8',
met: 0.8
}, {
activity: 'Walking 2mph (3.2kmh): 2.0',
met: 2.0
}, {
activity: 'Walking 3mph (4.8kmh): 2.6',
met: 2.6
}, {
activity: 'Walking 4mph (6.4kmh): 3.8',
met: 3.8
}, {
activity: 'Reading, seated: 1.0',
met: 1.0
}, {
activity: 'Writing: 1.0',
met: 1.0
}, {
activity: 'Typing: 1.1',
met: 1.1
}, {
activity: 'Filing, seated: 1.2',
met: 1.2
}, {
activity: 'Filing, standing: 1.4',
met: 1.4
}, {
activity: 'Walking about: 1.7',
met: 1.7
}, {
activity: 'Lifting/packing: 2.1',
met: 2.1
}, {
activity: 'Driving a car: 1.5',
met: 1.5
}, {
activity: 'Flying aircraft, routine: 1.2',
met: 1.2
}, {
activity: 'Flying aircraft, combat: 2.4',
met: 2.4
}, {
activity: 'Driving, heavy vehicle: 3.2',
met: 3.2
}, {
activity: 'Cooking: 1.8',
met: 1.8
}, {
activity: 'House cleaning: 2.7',
met: 2.7
}, {
activity: 'Seated, heavy limb movement: 2.2',
met: 2.2
}, {
activity: 'Table sawing: 1.8',
met: 1.8
}, {
activity: 'Light machine work: 2.2',
met: 2.2
}, {
activity: 'Heavy machine work: 4.0',
met: 4.0
}, {
activity: 'Handling 100lb (45 kg) bags: 4.0',
met: 4.0
}, {
activity: 'Pick and shovel work: 4.4',
met: 4.4
}, {
activity: 'Dancing: 3.4',
met: 3.4
}, {
activity: 'Calisthenics: 3.5',
met: 3.5
}, {
activity: 'Tennis: 3.8',
met: 3.8
}, {
activity: 'Basketball: 6.3',
met: 6.3
}, {
activity: 'Wrestling: 7.8',
met: 7.8
}];
var cloSelect = document.getElementById('cloSelect');
cloSelect.onchange = function() {
document.getElementById('clo').value = cloSelect.value;
update();
}
cloEnsembles.forEach(function(element) {
cloSelect.options.add(new Option(element.clothing, element.clo));
});
var cloMultiSelect = document.getElementById('cloMultiSelect');
cloArticles.forEach(function(element) {
cloMultiSelect.options.add(new Option(element.article, element.clo));
});
var actSelect = document.getElementById('actSelect');
actSelect.onchange = function() {
document.getElementById('met').value = actSelect.value;
update();
};
actData.forEach(function(element) {
actSelect.options.add(new Option(element.activity, element.met));
});
var velaSelect = document.getElementById('vel_a')
velaSelect.onchange = function() {
update();
var coolingEffect;
if (d.vel_a == 0.3) {
coolingEffect = 0
} else if (d.vel_a == 0.6) {
coolingEffect = 1.2
} else if (d.vel_a == 0.9) {
coolingEffect = 1.8
} else if (d.vel_a == 1.2) {
coolingEffect = 2.2
}
ac.redrawBounds(coolingEffect)
}
$(function() {
$(".multiselect").multiselect({
sortable: false,
searchable: false,
dividerLocation: 0.5
});
});
$('#adaptive-inputs, #adaptive-note, #psychtop-note, #temphum-note, #chart-div-adaptive, #temphumchart-div').hide();
window.isCelsius = true;
window.humUnit = 'rh';
setDefaults();
update();
bc.drawChart();
var bound = bc.findComfortBoundary(d, 0.5)
bc.drawComfortRegion(bound);
bc.drawPoint();
pc.drawChart();
var json = [{ "db": d.ta,
"hr": pc.getHumRatio(d.ta, d.rh) }];
var b = pc.findComfortBoundary(d, 0.5);
pc.drawComfortRegion(b);
pc.drawPoint(json);
ac.drawChart();
ac.drawPoint([d]);
});
$(function() {
$('#globedialog').dialog({
autoOpen: false,
height: 300,
width: 400,
modal: true,
resizable: false,
buttons: {
"Set mean radiant temperature": function() {
var tr = parseFloat($('#mrt-result').val());
if (!isCelsius) tr = util.CtoF(tr);
$('#tr').val(tr);
$(this).dialog("close");
update();
}
}
});
$('#ERFdialog').dialog({
autoOpen: false,
height: 535,
width: 500,
modal: true,
resizable: true,
buttons: {
"Calculate": function(){
var alt = parseFloat($('#alt').val());
var az = parseFloat($('#az').val());
var posture = $('#posture').val();
var Idir = parseFloat($('#Idir').val());
var tsol = parseFloat($('#tsol').val());
var fsvv = parseFloat($('#fsvv').val());
var fbes = parseFloat($('#fbes').val());
var asa = parseFloat($('#asa').val());
var Rfloor = parseFloat($('#Rfloor').val());
var r = ERF(alt, az, posture, Idir, tsol, fsvv, fbes, asa, Rfloor)
$('#erf-result').val(r.ERF.toFixed(1))
if (!isCelsius) r.dMRT = util.CtoF(r.dMRT) - 32
$('#dmrt-result').val(r.dMRT.toFixed(1))
},
"Adjust MRT": function(){
var dmrt = parseFloat($('#dmrt-result').val());
if (!isNaN(dmrt)){
var mrt = parseFloat($('#tr').val());
$('#tr').val((mrt + dmrt).toFixed(1));
$(this).dialog("close");
update();
}
},
"Help": function(){
},
"Close": function() {
$(this).dialog("close");
}
}
});
$('#localdialog').dialog({
autoOpen: false,
height: 600,
width: 432,
modal: true,
resizable: false,
});
$('#LEEDdialog').dialog({
autoOpen: false,
height: 700,
width: 500,
modal: true,
resizable: true,
});
$('#link').button({}).click(function() {
if ($('#tr-input').is(':hidden')) {
$('#ta-lab').html('<a class="mainlink" href="http://en.wikipedia.org/wiki/Dry-bulb_temperature" target="_new">Air temperature</a>');
$('#globeTemp').removeAttr('disabled');
$('#tr-input, #tr-lab').show();
} else {
$('#ta-lab').html('<a class="mainlink" href="http://en.wikipedia.org/wiki/Operative_temperature" target="_new">Operative temperature</a>');
$('#globeTemp').attr('disabled', 'disabled');
$('#tr-input, #tr-lab').hide();
}
});
$('#local-control').button();
$('#radio').buttonset();
$('.leed-buttons').buttonset();
$('#customClo').button({
icons: {
primary: 'ui-icon-person'
}
}).click(function() {
$('#customCloToggle').toggle('fast');
if ($('#leedInterface').is(':checked')) {
$('#leedInterfaceToggle').toggle('fast');
$('#leedInterface').removeAttr('checked');
$('#leedInterface').button('refresh');
$('#unitsToggle').removeAttr('disabled');
}
});
$('#dynamicClo').button({
icons: {
primary: 'ui-icon-person'
}
}).click(function() {
$('#dynamicCloToggle').toggle('fast');
});
$('#leedInterface').button({
icons: {
primary: 'ui-icon-document'
}
}).click(function() {
$('#leedInterfaceToggle').toggle('fast');
if (isCelsius) {
toggleUnits();
update();
}
if ($('#leedInterface').is(':checked')) {
$('#unitsToggle').attr('disabled', 'disabled');
} else {
$('#unitsToggle').removeAttr('disabled');
}
if ($('#customClo').is(':checked')) {
$('#customCloToggle').toggle('fast');
$('#customClo').removeAttr('checked');
$('#customClo').button('refresh');
}
});
$('#leed-winter').click(function() {
var spaceType = $('#leed-spacetype').val()
var ctype = $('#leed-cooling').is(':checked') ? "cooling" : "heating"
setDataSeason(spaceType, ctype, "winter")
});
$('#leed-spring').click(function() {
var spaceType = $('#leed-spacetype').val()
var ctype = $('#leed-cooling').is(':checked') ? "cooling" : "heating"
setDataSeason(spaceType, ctype, "spring")
});
$('#leed-summer').click(function() {
var spaceType = $('#leed-spacetype').val()
var ctype = $('#leed-cooling').is(':checked') ? "cooling" : "heating"
setDataSeason(spaceType, ctype, "summer")
});
$('#leed-fall').click(function() {
var spaceType = $('#leed-spacetype').val()
var ctype = $('#leed-cooling').is(':checked') ? "cooling" : "heating"
setDataSeason(spaceType, ctype, "fall")
});
$('#leed-submit').button().click(function() {
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", "/comforttool-static/html/leed.html");
xmlhttp.send();
xmlhttp.onload = function(e) {
leed_html = xmlhttp.responseText;
doc = createDocument(leed_html);
openwindow = openDocument(doc);
generateTables(openwindow);
$(openwindow.document.getElementsByClassName("box-texts")).remove();
}
});
$('button').button();
$('.buttons').buttonset();
$('#ta, #tr, #trm').spinner({
step: 0.1,
min: 0,
max: 120,
numberFormat: "n"
});
$('#vel').spinner({
step: 0.01,
min: 0,
max: 4,
numberFormat: "n"
});
$('#clo').spinner({
step: 0.05,
min: 0.0,
max: 10,
numberFormat: "n"
});
$('#met').spinner({
step: 0.05,
min: 1,
max: 2,
numberFormat: "n"
});
$('#rh').spinner({
step: 1,
min: 0,
max: 100,
numberFormat: "n"
});
$('#vel_a').selectmenu({
width: 165
});
$('select#humidity-spec').selectmenu({
width: 200
});
$('select#model-type').selectmenu({
width: 200
});
$('select#cloSelect').selectmenu({
width: 200
});
$('select#actSelect').selectmenu({
width: 200
});
$('select#chartSelect').selectmenu({
width: 350
});
});
$('#humidity-spec').change(function() {
var v = $('#humidity-spec').val();
var ta = parseFloat($('#ta').val());
if (!isCelsius) ta = util.FtoC(ta);
var maxVapPress = parseFloat(psy.satpress(ta));
var maxHumRatio = psy.humratio(psy.PROP.Patm, maxVapPress);
var rh = parseFloat($('#rh').val());
if (!isCelsius & (window.humUnit == 'wetbulb' | window.humUnit == 'dewpoint')) rh = util.FtoC(rh);
if (window.humUnit == 'vappress') if (!isCelsius) rh *= 2953;
else rh *= 1000;
if (v == 'rh') {
$('#rh').val(psy.convert(rh, ta, window.humUnit, 'rh'));
$('#rh-unit').html(' %');
$('#rh').spinner({
step: 1,
min: 0,
max: 100,
numberFormat: "n"
});
} else if (v == 'dewpoint') {
if (isCelsius) {
$('#rh').val(psy.convert(rh, ta, window.humUnit, 'dewpoint'));
$('#rh-unit').html(' &deg;C');
} else {
$('#rh').val(util.CtoF(psy.convert(rh, ta, window.humUnit, 'dewpoint')));
$('#rh-unit').html(' &deg;F');
}
$('#rh').spinner({
step: 0.1,
min: 0,
max: 100,
numberFormat: "n"
});
} else if (v == 'wetbulb') {
if (isCelsius) {
$('#rh').val(psy.convert(rh, ta, window.humUnit, 'wetbulb'));
$('#rh-unit').html(' &deg;C');
} else {
$('#rh').val(util.CtoF(psy.convert(rh, ta, window.humUnit, 'wetbulb')));
$('#rh-unit').html(' &deg;F');
}
$('#rh').spinner({
step: 0.1,
min: 0,
max: 100,
numberFormat: "n"
});
} else if (v == 'w') {
$('#rh').val(psy.convert(rh, ta, window.humUnit, 'w'));
$('#rh-unit').html('');
$('#rh').spinner({
step: 0.001,
min: 0,
max: maxHumRatio
});
} else if (v == 'vappress') {
if (isCelsius) {
$('#rh').val(psy.convert(rh, ta, window.humUnit, 'vappress') / 1000);
$('#rh-unit').html(' KPa');
} else {
$('#rh').val(psy.convert(rh, ta, window.humUnit, 'vappress') / 2953);
$('#rh-unit').html(' in HG');
}
$('#rh').spinner({
step: 0.01,
min: 0,
max: maxVapPress / 1000.0
});
}
window.humUnit = v;
});
$('#link').click(function() {
$('#tr').val($('#ta').val());
});
$('.inputbox').keydown(function(event) {
if (event.keyCode == 13) {
var inputs = $('.inputbox:visible:enabled');
var nextBox = inputs.index(this) + 1;
if (nextBox == inputs.length) nextBox = 0;
inputs[nextBox].focus();
}
});
$('.in').click(function() {
update();
});
$('.inputbox').focusout(function() {
update();
});
$('#unitsToggle').click(function() {
toggleUnits();
update();
});
$('#setDefaults').click(function() {
setDefaults();
update();
});
$('#specPressure').click(function() {
var customPressure = prompt('Enter atmospheric pressure in Pascals');
if (customPressure != '' && customPressure != null) {
customPressure = parseFloat(customPressure)
if (!isNaN(customPressure) && customPressure >= 60000 && customPressure <= 108000) {
psy.PROP.Patm = customPressure
pc.redraw_rh_lines()
update()
} else {
window.alert('The entered atmospheric pressure is invalid. It must be in the range of 60,000 to 108,000 pascals.')
}
}
});
$('#globeTemp').click(function() {
var container = $('#globedialog');
$.ajax({
url: '/comforttool-static/html/globetemp.html',
success: function(data) {
$('#globedialog').html(data);
if (!isCelsius) {
$('#ta-g').val('77')
$('#vel-g').val('20')
$('#tglobe').val('77')
$('#diameter').val('6')
$('#g-ta-unit').html(' &deg;F')
$('#g-vel-unit').html(' fpm')
$('#g-tglobe-unit').html(' &deg;F')
$('#g-globediam-unit').html(' in')
$('#g-mrt-unit').html(' &deg;F')
}
},
async: false
});
container.dialog("open");
updateGlobe();
$('.input-dialog').focusout(function() {
updateGlobe();
});
});
$('#ERF').click(function() {
var container = $('#ERFdialog');
$.ajax({
url: '/comforttool-static/html/erf.html',
success: function(data) {
$('#ERFdialog').html(data);
$('#posture').selectmenu({
width: 90
});
},
async: false
});
if (!isCelsius){
$('#dmrt-unit').html('&deg;F')
}
container.dialog("open");
});
$('#localDisc').click(function() {
var container = $('#localdialog');
$.ajax({
url: '/comforttool-static/html/localdisc.html',
success: function(data) {
$('#localdialog').html(data);
if (!isCelsius) {
$('.tempunit').html(' &deg;F')
$('.velunit').html(' fpm')
$('#T_head').val('77')
$('#T_ankle').val('77')
$('#T_floor').val('77')
$('#T_op').val('77')
$('#local_vel').val('20')
}
},
async: false
});
container.dialog("open");
$('.input-dialog-local').focusout(function() {
updateLocalDisc();
});
});
$('#LEED-help').click(function() {
var container = $('#LEEDdialog');
$.ajax({
url: '/comforttool-static/html/leed-help.html',
success: function(data) {
$('#LEEDdialog').html(data);
},
async: false
});
container.dialog("open");
});
$('#setClo').click(function() {
setClo();
update();
});
$('#addToEnsembles').click(function() {
addToEnsembles();
});
$('#setDynamicClo').click(function() {
var ta6 = $('#taOut6').val();
var clo_r = comf.schiavonClo(ta6);
$('#clo').val(clo_r.toFixed(2));
update();
});
$('#model-type').change(function() {
$('#pmv-out-label').html('PMV');
$('#local-control-div').hide();
$('#localDisc').removeAttr('disabled');
model = $('#model-type').val();
if (model == 'pmvElevatedAirspeed') {
$('#pmv-inputs, #pmv-outputs, #cloInput, #actInput, #humidity-spec-cont, #chart-div, #chartSelect-cont, #pmv-notes').show();
$('#adaptive-note, #adaptive-inputs, #adaptive-outputs, #chart-div-adaptive, #chart-title-adaptive, #temphumchart-div, #temphumchart-title').hide();
if (model == 'pmvElevatedAirspeed') {
$('#pmv-elev-outputs, #local-control-div').show();
$('#pmv-out-label').html('PMV Adjusted');
} else {
$('#pmv-elev-outputs').hide();
}
} else if (model == 'adaptiveComfort') {
$('#pmv-inputs, #pmv-elev-inputs, #local-control-div, #pmv-outputs, #pmv-elev-outputs, #cloInput').hide()
$('#actInput, #humidity-spec-cont, #chart-div, #temphumchart-div, #pmv-notes, #chartSelect-cont').hide();
$('#adaptive-note, #adaptive-inputs, #adaptive-outputs, #chart-div-adaptive, #chart-title-adaptive').show();
$('#localDisc').attr('disabled', 'disabled');
}
update();
});
$("#chartSelect").change(function(){
chart = $("#chartSelect").val();
if (chart == "psychta" || chart == "psychtop"){
$("#chart-div").show();
$("#temphumchart-div").hide();
if (chart == "psychta") {
$("#psychta-note").show();
$("#psychtop-note, #temphum-note").hide();
$("#db-axis-C-label").text("Drybulb Temperature [°C]");
$("#db-axis-F-label").text("Drybulb Temperature [°F]");
if ($('#link').is(':checked')) {
$('#labelforlink').show();
} else {
$('#ta-lab').html('<a class="mainlink" href="http://en.wikipedia.org/wiki/Dry-bulb_temperature" target="_new">Air temperature</a>');
$('#globeTemp').removeAttr('disabled');
$('#tr-input, #tr-lab, #labelforlink').show();
}
//$(".comfortzone").css("fill", "rgb(0,0,100)")
} else if (chart == "psychtop") {
$("#psychtop-note").show();
$("#psychta-note, #temphum-note").hide();
$("#db-axis-C-label").text("Operative Temperature [°C]");
$("#db-axis-F-label").text("Operative Temperature [°F]");
$('#ta-lab').html('<a class="mainlink" href="http://en.wikipedia.org/wiki/Operative_temperature" target="_new">Operative temperature</a>');
$('#globeTemp').attr('disabled', 'disabled');
$('#tr-input, #tr-lab, #labelforlink').hide();
//$(".comfortzone").css("fill", "rgb(0,0,0)")
}
} else if (chart == "temphum") {
$("#temphumchart-div, #temphum-note").show();
$("#chart-div, #psychta-note, #psychtop-note").hide();
if ($('#link').is(':checked')) {
$('#labelforlink').show();
} else {
$('#ta-lab').html('<a class="mainlink" href="http://en.wikipedia.org/wiki/Dry-bulb_temperature" target="_new">Air temperature</a>');
$('#globeTemp').removeAttr('disabled');
$('#tr-input, #tr-lab, #labelforlink').show();
}
}
update();
});
function toggleUnits() {
var v, el;
var hs = $('#humidity-spec').val();
isCelsius = !isCelsius;
if (isCelsius) {
$('.tempunit').each(function() {
$(this).html(' &deg;C');
});
$('#ta, #tr, #trm').each(function() {
v = util.FtoC($(this).val());
$(this).val(v.toFixed(1));
});
$('#vel-unit').html(' m/s');
v = $('#vel').val();
$('#vel').val(v / 196.9).spinner({
step: 0.01,
min: 0,
max: 3,
numberFormat: 'n'
});
if (hs == 'dewpoint' || hs == 'wetbulb') {
$('#rh-unit').html(' &deg;C');
v = (util.FtoC($('#rh').val()));
$('#rh').val(v.toFixed(1));
} else if (hs == 'vappress') {
$('#rh-unit').html(' KPa');
v = $('#rh').val() * 2.953;
$('#rh').val(v.toFixed(2));
}
} else {
$('.tempunit').each(function() {
$(this).html(' &deg;F');
});
$('#ta, #tr, #trm').each(function() {
v = util.CtoF($(this).val());
$(this).val(v.toFixed(1));
});
$('#vel-unit, #vel-a-unit').html(' fpm');
v = $('#vel').val();
$('#vel').val(v * 196.9).spinner({
step: 1,
min: 0,
max: 300,
numberFormat: 'n'
});
if (hs == 'dewpoint' || hs == 'wetbulb') {
$('#rh-unit').html(' &deg;F');
v = (util.CtoF($('#rh').val()));
$('#rh').val(v.toFixed(1));
} else if (hs == 'vappress') {
$('#rh-unit').html(' in HG');
v = $('#rh').val() / 2.953;
$('#rh').val(v.toFixed(2));
}
}
pc.toggleUnits(isCelsius);
bc.toggleUnits(isCelsius);
ac.toggleUnits(isCelsius);
}
function setClo() {
var clo = 0;
var opt = document.getElementById('cloMultiSelect').options;
for (var i = 0; i < opt.length; i++) {
if (opt[i].selected) clo += parseFloat(opt[i].value);
}
document.getElementById('clo').value = clo.toFixed(2);
}
function addToEnsembles() {
var items = [];
var ensembleClo = 0;
var opt = document.getElementById('cloMultiSelect').options;
for (var i = 0; i < opt.length; i++) {
if (opt[i].selected) {
items.push(opt[i].text);
ensembleClo += parseFloat(opt[i].value);
}
}
cloSelect.options.add(new Option(items.join(', '), ensembleClo.toFixed(2)));
}
function update() {
if ($('#link').is(':checked') || $("#chartSelect").val() == "psychtop") {
$('#tr').val($('#ta').val());
}
keys.forEach(function(element) {
d_cache[element] = d[element];
var e = document.getElementById(element).value
e = e.replace(/,/g, '.')
d[element] = parseFloat(e);
});
d.wme = 0;
if (!isCelsius) {
d.ta = util.FtoC(d.ta);
d.tr = util.FtoC(d.tr);
d.trm = util.FtoC(d.trm);
d.vel /= 196.9;
if (window.humUnit == 'wetbulb' || window.humUnit == 'dewpoint') d.rh = util.FtoC(d.rh);
else if (window.humUnit == 'vappress') d.rh *= 2953;
} else {
if (window.humUnit == 'vappress') d.rh *= 1000;
}
d.rh = psy.convert(d.rh, d.ta, window.humUnit, 'rh');
model = document.getElementById('model-type').value;
if (model == 'pmvElevatedAirspeed') {
r = comf.pmvElevatedAirspeed(d.ta, d.tr, d.vel, d.rh, d.met, d.clo, 0);
if (!isCelsius){
r.set = util.CtoF(r.set)
}
renderPmvElevResults(r);
calcPmvElevCompliance(d, r);
if ($('#chart-div').is(':visible')) {
var b = pc.findComfortBoundary(d, 0.5)
pc.redrawComfortRegion(b);
var pointdata = [{
"db": d.ta,
"hr": pc.getHumRatio(d.ta, d.rh)
}]
pc.redrawPoint(pointdata);
} else if ($('#temphumchart-div').is(':visible')) {
var b = bc.findComfortBoundary(d, 0.5)
bc.redrawComfortRegion(b);
bc.redrawPoint();
};
} else if (model == 'adaptiveComfort') {
r = comf.adaptiveComfortASH55(d.ta, d.tr, d.trm, d.vel_a);
renderAdaptiveResults(r);
calcAdaptiveCompliance(d, r);
ac.redrawPoint([d])
}
}
function renderPmvResults(r) {
$('#pmv-res').html(r.pmv.toFixed(2));
$('#ppd-res').html(r.ppd.toFixed(0));
var sensation = util.getSensation(r.pmv);
$('#sensation').html(sensation);
$('#SET').html(r.set.toFixed(1));
}
function renderPmvElevResults(r) {
renderPmvResults(r);
if (!isCelsius) {
r.ta_adj = util.CtoF(r.ta_adj);
r.cooling_effect = util.CtoF(r.cooling_effect) - 32;
}
$('#ta-still').html(r.ta_adj.toFixed(1));
$('#cooling-effect').html(r.cooling_effect.toFixed(1));
}
function renderAdaptiveResults(r) {
var to = (parseFloat($('#ta').val()) + parseFloat($('#tr').val())) / 2;
if (!isCelsius) {
r.tComf90Upper = util.CtoF(r.tComf90Upper);
r.tComf90Lower = util.CtoF(r.tComf90Lower);
r.tComf80Upper = util.CtoF(r.tComf80Upper);
r.tComf80Lower = util.CtoF(r.tComf80Lower);
}
$('#limits80').html('Operative temperature: ' + r.tComf80Lower.toFixed(1) + ' to ' + r.tComf80Upper.toFixed(1));
$('#limits90').html('Operative temperature: ' + r.tComf90Lower.toFixed(1) + ' to ' + r.tComf90Upper.toFixed(1));
if (r.acceptability90) {
$('#sensation80, #sensation90').html('Comfortable');
} else if (r.acceptability80) {
$('#sensation80').html('Comfortable');
if (to < r.tComfUpper90) {
$('#sensation90').html('Too cool');
} else {
$('#sensation90').html('Too warm');
}
} else if (to < r.tComfLower80) {
$('#sensation80, #sensation90').html('Too cool');
} else {
$('#sensation80, #sensation90').html('Too warm');
}
}
function calcPmvCompliance(d, r) {
var pmv_comply = Math.abs(r.pmv) <= 0.5;
var met_comply = d.met <= 2 && d.met >= 1;
var clo_comply = d.clo <= 1.5;
var local_control = $('#local-control').is(':checked');
var special_msg = '';
comply = true;
if (!met_comply) {
comply = false;
special_msg += '&#8627; Metabolic rates below 1.0 or above 2.0 are not covered by this standard<br>';
}
if (!clo_comply) {
comply = false;
special_msg += '&#8627; Clo values above 1.5 are not covered by this standard<br>';
}
if (elev_airspeed && d.clo > 0.7) {
comply = false;
special_msg += '&#8627; Elevated air speeds with clo > 0.7 are not covered by this standard<br>';
}
if (!pmv_comply) {
comply = false;
}
renderCompliance(comply, special_msg);
}
function calcPmvElevCompliance(d, r) {
var pmv_comply = (Math.abs(r.pmv) <= 0.5);
var met_comply = d.met <= 2 && d.met >= 1;
var clo_comply = d.clo <= 1.5;
var local_control = $('#local-control').is(':checked');
var special_msg = '';
var compliance_ranges, unit_t, unit_v;
comply = true;
if (!met_comply) {
comply = false;
special_msg += '&#8627; Metabolic rates below 1.0 or above 2.0 are not covered by this Standard<br>';
}
if (!clo_comply) {
comply = false;
special_msg += '&#8627; Clo values above 1.5 are not covered by this Standard<br>';
}
compliance_ranges = getComplianceRanges(d, r, local_control);
if (!isNaN(compliance_ranges.vel_max)){
if (d.vel > compliance_ranges.vel_max && local_control) {
comply = false;
special_msg += '&#8627; Air speed exceeds limit set by standard<br>';
}
if (d.vel > compliance_ranges.vel_max && !local_control) {
comply = false;
special_msg += '&#8627; Maximum air speed has been limited due to no occupant control<br>';
}
}
if (!pmv_comply) {
comply = false;
}
if (d.vel > 0.15) {
$("#pmv-out-label").html('PMV with elevated air speed')
$("#ppd-out-label").html('PPD with elevated air speed')
$("#pmv-elev-outputs").show();
} else {
$("#pmv-out-label").html('PMV')
$("#ppd-out-label").html('PPD')
$("#pmv-elev-outputs").hide();
}
renderCompliance(comply, special_msg);
}
function calcAdaptiveCompliance(d, r) {
var comply = true;
var special_msg = '';
if (d.trm > 33.5 || d.trm < 10) {
comply = false;
special_msg += '&#8627; Prevailing mean outdoor temperatures above ' + (isCelsius ? '33.5&deg;C ' : '92.3&deg;F ')
+ 'or below ' + (isCelsius ? '10&deg;C ' : '50&deg;F ') + 'are not covered by Standard-55<br>';
}
if ((d.ta + d.tr) / 2 < 25 & d.vel_a > 0.3) {
special_msg += '&#8627; The cooling effect of air speed is used only when the operative temperature is above ' + (isCelsius ? '25&deg;C' : '77&deg;F');
}
if (!r.acceptability80) comply = false;
renderCompliance(comply, special_msg);
}
function getComplianceRanges(d, r, local_control) {
var a = {};
var found_lower = false;
var found_upper = false;
var c;
for (var v = 0; v <= 1.2; v+=0.01){
c = comf.pmvElevatedAirspeed(d.ta, d.tr, v, d.rh, d.met, d.clo, 0).pmv
if (c < 0.5 && c > -0.5){
a.vel_min = v;
found_lower = true;
break
}
}
for (var v = 1.2; v >= 0; v-=0.01){
c = comf.pmvElevatedAirspeed(d.ta, d.tr, v, d.rh, d.met, d.clo, 0).pmv
if (c > -0.5 && c < 0.5){
a.vel_max = v;
found_upper = true;
break
}
}
if (!local_control) {
var to = (d.ta + d.tr) / 2;
if (to > 25.5) {
a.vel_max = Math.min(a.vel_max, 0.8);
} else if (to < 22.5) {
a.vel_max = Math.min(a.vel_max, 0.15);
} else {
a.vel_max = Math.min(a.vel_max, 50.49 - 4.4047 * to + 0.096425 * to * to);
}
}
a.vel_min = Math.min(a.vel_max, a.vel_min)
a.vel_max = Math.max(a.vel_max, a.vel_min)
if (!found_upper || !found_lower || a.vel_max < a.vel_min){
a.vel_max = Number.NaN;
a.vel_min = Number.NaN;
}
return a
}
function renderCompliance(comply, special_msg) {
var comply_msg = '&#10004; &nbsp;Complies with ASHRAE Standard 55-2010';
var no_comply_msg = '&#10008 &nbsp; Does not comply with ASHRAE Standard 55-2010';
$('#vel-range').html('');
if (comply) {
$('#comply-msg').html(comply_msg);
$('#comply-msg').css('color', 'green')
$('#special-msg').html(special_msg);
} else {
$('#comply-msg').html(no_comply_msg);
$('#comply-msg').css('color', 'red')
$('#special-msg').html(special_msg);
}
}
function setDefaults() {
if (!isCelsius) toggleUnits();
var hs = $('#humidity-spec').val();
var rh = psy.convert(50, 25, 'rh', hs)
if (hs == 'vappress') {
rh /= 1000;
}
var defaults = {
ta: 25,
tr: 25,
vel: 0.10,
rh: rh.toFixed(psy.PREC[hs]),
met: 1.2,
clo: 0.5,
trm: 29,
vel_a: 0.3
};
keys.forEach(function(element) {
document.getElementById(element).value = defaults[element];
});
}
function updateGlobe() {
var ta = parseFloat($('#ta-g').val());
var vel = parseFloat($('#vel-g').val());
var tglobe = parseFloat($('#tglobe').val());
var diameter = parseFloat($('#diameter').val());
var emissivity = parseFloat($('#emissivity').val());
if (!isCelsius) {
ta = util.FtoC(ta)
vel /= 196.9
tglobe = util.FtoC(tglobe)
diameter *= 0.0254
}
var tr = psy.globetemp(ta, vel, tglobe, diameter, emissivity);
if (!isCelsius) tr = util.CtoF(tr)
$('#mrt-result').val(tr.toFixed(1));
}
function createDocument(html) {
var doc = document.implementation.createHTMLDocument('');
doc.body.innerHTML = html;
return doc
}
function openDocument(doc) {
var openwindow = window.open()
openwindow.document.write(doc.documentElement.innerHTML);
return openwindow
}
var pc = new function() {
this.margin = 40
this.rbmargin = 60
this.width = 580
this.height = 500
this.db_min = 10
this.db_max = 36
this.init = function(){
this.db_extent = [this.db_min, this.db_max]
this.db_scale = d3.scale.linear()
.range([this.margin, this.width - this.rbmargin])
.domain(this.db_extent)
this.db_extent_F = [util.CtoF(this.db_min), util.CtoF(this.db_max)]
this.db_scale_F = d3.scale.linear()
.range([this.margin, this.width - this.rbmargin])
.domain(this.db_extent_F)
this.hr_extent = [0, 30]
this.hr_scale = d3.scale.linear()
.range([this.height - this.rbmargin, this.rbmargin])
.domain(this.hr_extent)
this.bColorRange = d3.scale.linear().domain([-3,0]).range([d3.rgb('#4A40FF'), d3.rgb('#C2C2C2')]);
this.rColorRange = d3.scale.linear().domain([0,3]).range([d3.rgb('#C2C2C2'), d3.rgb('#FF4040')]);
this.pline = d3.svg.line()
.x(function(d) {
return this.db_scale(d.db)
})
.y(function(d) {
return this.hr_scale(1000 * d.hr)
})
}
this.init()
this.drawChart = function() {
var db_axis = d3.svg.axis().scale(pc.db_scale)
var db_axis_F = d3.svg.axis().scale(pc.db_scale_F)
var hr_axis = d3.svg.axis().scale(pc.hr_scale).orient("right")
var line = d3.svg.line()
.x(function(d) {
return pc.db_scale(d.db)
})
.y(function(d) {
return pc.hr_scale(1000 * d.hr)
})
.interpolate('cardinal')
d3.select("#chart-div")
.append("svg")
.attr("class", "svg-psych").attr("id", "svg-psych")
.attr("width", pc.width)
.attr("height", pc.height)
pc.svg = d3.select(".svg-psych")
pc.svg.append("defs")
.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("x", "0")
.attr("y", "0")
.attr("width", pc.width - pc.margin - pc.rbmargin)
.attr("height", pc.height - pc.margin - pc.rbmargin - 20)
.attr("transform", "translate(" + pc.margin + "," + pc.rbmargin + ")")
pc.draw_rh_lines()
// basic frame of the box
pc.svg.append("svg:a")
.attr("xlink:href", "http://en.wikipedia.org/wiki/Dry-bulb_temperature")
.append("text")
.text("t")
.attr("class", "box-texts")
.attr("transform", "translate(" + pc.margin + "," + (pc.rbmargin + 10) + ")")
pc.svg.append("svg:a")
.attr("xlink:href", "http://en.wikipedia.org/wiki/Dry-bulb_temperature")
.append("text").text("db")
.attr("class", "box-texts")
.attr("id", "box-db")
.attr("transform", "translate(" + (pc.margin + 5) + "," + (pc.rbmargin + 10) + ")")
document.getElementById("box-db").style.fontSize = "9px"
pc.svg.append("text")
.text("°C")
.attr("class", "box-texts")
.attr("id", "unit-dbt")
.attr("transform", "translate(" + (pc.margin + 65) + "," + (pc.rbmargin + 10) + ")")
pc.svg.append("svg:a")
.attr("xlink:href", "http://en.wikipedia.org/wiki/Relative_humidity")
.append("text")
.text("rh")
.attr("class", "box-texts")
.attr("transform", "translate(" + pc.margin + "," + (pc.rbmargin + 30) + ")")
pc.svg.append("text")
.text("%")
.attr("class", "box-texts")
.attr("id", "unit-rh")
.attr("transform", "translate(" + (pc.margin + 65) + "," + (pc.rbmargin + 30) + ")")
pc.svg.append("svg:a")
.attr("xlink:href", "http://en.wikipedia.org/wiki/Humidity#Absolute_humidity")
.append("text")
.text("W")
.attr("class", "box-texts")
.attr("transform", "translate(" + pc.margin + "," + (pc.rbmargin + 50) + ")")
pc.svg.append("text")
.text("a")
.attr("class", "box-texts")
.attr("id", "box-a")
.attr("transform", "translate(" + (pc.margin + 12) + "," + (pc.rbmargin + 50) + ")")
document.getElementById("box-a").style.fontSize = "9px"
pc.svg.append("text")
.text("g")
.attr("class", "box-texts")
.attr("id", "unit-hr1")
.attr("transform", "translate(" + (pc.margin + 65) + "," + (pc.rbmargin + 50) + ")")
pc.svg.append("text")
.text("w")
.attr("class", "box-texts")
.attr("id", "unit-hr2")
.attr("transform", "translate(" + (pc.margin + 76) + "," + (pc.rbmargin + 50) + ")")
document.getElementById("unit-hr2").style.fontSize = "9px"
pc.svg.append("text")
.text("/kg")
.attr("class", "box-texts")
.attr("id", "unit-hr3")
.attr("transform", "translate(" + (pc.margin + 83) + "," + (pc.rbmargin + 50) + ")")
pc.svg.append("text")
.text("da")
.attr("class", "box-texts")
.attr("id", "unit-hr4")
.attr("transform", "translate(" + (pc.margin + 105) + "," + (pc.rbmargin + 50) + ")")
document.getElementById("unit-hr4").style.fontSize = "9px"
pc.svg.append("svg:a")
.attr("xlink:href", "http://en.wikipedia.org/wiki/Wet-bulb_temperature")
.append("text")
.text("t")
.attr("class", "box-texts")
.attr("transform", "translate(" + pc.margin + "," + (pc.rbmargin + 70) + ")")
pc.svg.append("svg:a")
.attr("xlink:href", "http://en.wikipedia.org/wiki/Wet-bulb_temperature")
.append("text")
.text("wb")
.attr("class", "box-texts")
.attr("id", "wb")
.attr("transform", "translate(" + (pc.margin + 5) + "," + (pc.rbmargin + 70) + ")")
document.getElementById("wb").style.fontSize = "9px"
pc.svg.append("text")
.text("°C")
.attr("class", "box-texts")
.attr("id", "unit-wbt")
.attr("transform", "translate(" + (pc.margin + 65) + "," + (pc.rbmargin + 70) + ")")
pc.svg.append("svg:a")
.attr("xlink:href", "http://en.wikipedia.org/wiki/Dew_point")
.append("text")
.text("t")
.attr("class", "box-texts")
.attr("transform", "translate(" + pc.margin + "," + (pc.rbmargin + 90) + ")")
pc.svg.append("svg:a")
.attr("xlink:href", "http://en.wikipedia.org/wiki/Dew_point")
.append("text")
.text("dp")
.attr("class", "box-texts")
.attr("id", "box-dp")
.attr("transform", "translate(" + (pc.margin + 5) + "," + (pc.rbmargin + 90) + ")")
document.getElementById("box-dp").style.fontSize = "9px"
pc.svg.append("text")
.text("°C")
.attr("class", "box-texts")
.attr("id", "unit-dew")
.attr("transform", "translate(" + (pc.margin + 65) + "," + (pc.rbmargin + 90) + ")")
pc.svg.append("svg:a")
.attr("xlink:href", "http://en.wikipedia.org/wiki/Enthalpy")
.append("text")
.text("h")
.attr("class", "box-texts")
.attr("transform", "translate(" + pc.margin + "," + (pc.rbmargin + 110) + ")")
pc.svg.append("text")
.text("kJ/kg")
.attr("class", "box-texts")
.attr("id", "unit-ent")
.attr("transform", "translate(" + (pc.margin + 65) + "," + (pc.rbmargin + 110) + ")")
// this is for the initial values, set to 0.0
pc.svg.append("text")
.text("0.0")
.attr("class", "box-texts")
.attr("id", "box-dbt")
.attr("transform", "translate(" + (pc.margin + 32) + "," + (pc.rbmargin + 10) + ")")
pc.svg.append("text")
.text("0.0")
.attr("class", "box-texts")
.attr("id", "box-rh")
.attr("transform", "translate(" + (pc.margin + 32) + "," + (pc.rbmargin + 30) + ")")
pc.svg.append("text")
.text("0.0")
.attr("class", "box-texts")
.attr("id", "box-hr")
.attr("transform", "translate(" + (pc.margin + 32) + "," + (pc.rbmargin + 50) + ")")
pc.svg.append("text")
.text("0.0")
.attr("class", "box-texts")
.attr("id", "box-wbt")
.attr("transform", "translate(" + (pc.margin + 32) + "," + (pc.rbmargin + 70) + ")")
pc.svg.append("text")
.text("0.0")
.attr("class", "box-texts")
.attr("id", "box-dew")
.attr("transform", "translate(" + (pc.margin + 32) + "," + (pc.rbmargin + 90) + ")")
pc.svg.append("text")
.text("0.0")
.attr("class", "box-texts")
.attr("id", "box-ent")
.attr("transform", "translate(" + (pc.margin + 32) + "," + (pc.rbmargin + 110) + ")")
pc.svg.append("g")
.attr("class", "db axis")
.attr("id", "db-axis-C")
.attr("transform", "translate(0," + (pc.height - pc.rbmargin) + ")")
.call(db_axis)
pc.svg.append("g")
.attr("class", "db axis")
.attr("id", "db-axis-F")
.attr("opacity", "0")
.attr("transform", "translate(0," + (pc.height - pc.rbmargin) + ")")
.call(db_axis_F)
pc.svg.append("g")
.attr("class", "hr axis")
.attr("transform", "translate(" + (pc.width - pc.rbmargin) + ",0)")
.call(hr_axis)
d3.select("#db-axis-C")
.append("text")
.text("Dry-bulb Temperature [°C]")
.attr("class", "db-unit").attr("id", "db-axis-C-label")
.attr("x", (pc.width / 2) - 1.9 * pc.margin)
.attr("y", pc.rbmargin / 1.3)
d3.select("#db-axis-F")
.append("text")
.text("Dry-bulb Temperature [°F]")
.attr("class", "db-unit").attr("id", "db-axis-F-label")
.attr("x", (pc.width / 2) - 1.9 * pc.margin)
.attr("y", pc.rbmargin / 1.3)
d3.select(".hr.axis")
.append("text")
.attr("id", "hr-text")
.attr("transform", "rotate (-90, -43, 0) translate(-360,90)")
.append("tspan")
.text("Humidity Ratio [g")
.attr("id", "hr-unit0")
d3.select("#hr-text")
.append("tspan")
.text("w")
.style("baseline-shift", "sub")
d3.select("#hr-text")
.append("tspan")
.text(" / kg")
.attr("id", "hr-unit1")
d3.select("#hr-text")
.append("tspan")
.text("da")
.style("baseline-shift", "sub")
d3.select("#hr-text")
.append("tspan")
.text("]")
// drawing the background
var psybound = pc.findPsyBoundary();
pc.drawPsyRegion(psybound);
}
// calculate the values and draws the numbers in the box
this.mousemove = function() {
var mouseDBT = pc.db_scale.invert(d3.mouse(this)[0])
var mouseHR = pc.hr_scale.invert(d3.mouse(this)[1])
var mouseVP = (psy.PROP.Patm * mouseHR / 1000) / (0.62198 + mouseHR / 1000)
var mouseEnt = (1.006 * mouseDBT + mouseHR * (2501 + 1.86 * mouseDBT)) / 1000
var mouseRH = mouseVP / psy.satpress(mouseDBT) * 100
var mouseWBT = psy.wetbulb(mouseDBT, mouseHR / 1000)
var mouseDew = -35.957 - 1.8726 * Math.log(mouseVP) + 1.1689 * Math.pow(Math.log(mouseVP), 2)
if (mouseVP <= 0.01) {
mouseDew = -35.0
}
if (mouseRH >= 100) {
mouseRH = 100
}
if (!isCelsius) {
mouseDBT = util.CtoF(mouseDBT)
mouseDew = util.CtoF(mouseDew)
mouseWBT = util.CtoF(mouseWBT)
mouseEnt *= 0.43
}
d3.select("#box-dbt").text(mouseDBT.toFixed(1))
d3.select("#box-rh").text(mouseRH.toFixed(1))
d3.select("#box-hr").text(mouseHR.toFixed(1))
d3.select("#box-wbt").text(mouseWBT.toFixed(1))
d3.select("#box-dew").text(mouseDew.toFixed(1))
d3.select("#box-ent").text(mouseEnt.toFixed(1))
}
this.convertBox = function() {
var dbt = parseFloat($('#box-dbt').text())
var wbt = parseFloat($('#box-wbt').text())
var dew = parseFloat($('#box-dew').text())
var ent = parseFloat($('#box-ent').text())
if (isCelsius) {
dbt = util.FtoC(dbt)
wbt = util.FtoC(wbt)
dew = util.FtoC(dew)
ent /= 0.43
$('#unit-dbt').text('°C')
$('#unit-wbt').text('°C')
$('#unit-dew').text('°C')
$('#unit-ent').text('kJ/kg')
$('#unit-hr1').text('g')
$('#unit-hr3').text('/kg')
$('#box-db-unit').text('°C')
$('#box-mrt-unit').text('°C')
$('#box-vel-unit').text('m/s')
} else {
dbt = util.CtoF(dbt)
wbt = util.CtoF(wbt)
dew = util.CtoF(dew)
ent *= 0.43
$('#unit-dbt').text('°F')
$('#unit-wbt').text('°F')
$('#unit-dew').text('°F')
$('#unit-ent').text('btu/lb')
$('#unit-hr1').text('lb')
$('#unit-hr3').text('/klb')
$('#box-db-unit').text('°F')
$('#box-mrt-unit').text('°F')
$('#box-vel-unit').text('fpm')
}
$('#box-dbt').text(dbt.toFixed(1))
$('#box-wbt').text(wbt.toFixed(1))
$('#box-dew').text(dew.toFixed(1))
$('#box-ent').text(ent.toFixed(1))
}
this.drawComfortRegion = function(data) {
var el = d3.select("path.comfortzone")[0][0]
if (el){
d3.select("path.comfortzone")
.attr("d", pc.pline(data) + "Z")
} else {
pc.svg.insert("path", "circle")
.attr("clip-path", "url(#clip)")
.attr("d", pc.pline(data) + "Z")
.attr("class", "comfortzone")
.on("mouseover", function() {
d3.select(this).attr("class", "comfortzoneover");
})
.on("mouseout", function() {
d3.select(this).attr("class", "comfortzone");
})
.on("mousemove", pc.mousemove)
}
}
this.remove_rh_lines = function(){
d3.selectAll(".rhline").remove()
d3.select(".rh100").remove()
}
this.draw_rh_lines = function(){
// dynamic way of drawing rh lines
for (var i=100; i>=10; i-=10){
RHline = []
for (var t = pc.db_min; t <= pc.db_max; t += 0.5){
RHline.push({"db": t, "hr": pc.getHumRatio(t, i)})
}
if (i==100){
d3.select("svg").append("path")
.attr("d", pc.pline(RHline))
.attr("class", "rh100")
.attr("clip-path", "url(#clip)")
} else {
d3.select("svg").append("path")
.attr("d", pc.pline(RHline))
.attr("class", "rhline")
.attr("clip-path", "url(#clip)")
}
}
}
this.redraw_rh_lines = function(){
pc.remove_rh_lines()
pc.draw_rh_lines()
pc.clearChart()
var json = [{ "db": d.ta,
"hr": pc.getHumRatio(d.ta, d.rh) }];
var b = pc.findComfortBoundary(d, 0.5);
pc.drawComfortRegion(b);
pc.drawPoint(json);
}
this.redrawComfortRegion = function(data) {
d3.select("path.comfortzone")
.transition()
.attr("d", pc.pline(data) + "Z")
}
// Background
this.drawPsyRegion = function(data) {
d3.select("svg").append("path")
.attr("clip-path", "url(#clip)")
.attr("d", pc.pline(data) + "Z")
.attr("class", "psyregion")
.on("mousemove", pc.mousemove)
}
this.drawPoint = function(data) {
pc.svg.append("circle")
.attr("class", "outer")
.attr("clip-path", "url(#clip)")
.attr("r", 12)
pc.svg.append("circle")
.attr("class", "inner")
.attr("clip-path", "url(#clip)")
.attr("r", 2)
d3.selectAll("circle")
.attr("cx", pc.db_scale(data[0].db))
.attr("cy", pc.hr_scale(1000 * data[0].hr))
}
this.getColor = function(pmv) {
if (pmv > 0){
return pc.rColorRange(pmv)
}else{
return pc.bColorRange(pmv)
}
}
this.clearChart = function(){
d3.selectAll('circle')
.remove()
d3.selectAll('.comfortzone')
.remove()
}
this.initDisplay = function(){
pc.svg.append("text")
.text("Air temperature")
.attr("class", "hover-box-texts")
.attr("transform", "translate(" + pc.margin + "," + (pc.rbmargin + 140) + ")")
pc.svg.append("text")
.text("MRT")
.attr("class", "hover-box-texts")
.attr("transform", "translate(" + pc.margin + "," + (pc.rbmargin + 160) + ")")
pc.svg.append("text")
.text("Air velocity")
.attr("class", "hover-box-texts")
.attr("transform", "translate(" + pc.margin + "," + (pc.rbmargin + 180) + ")")
pc.svg.append("text")
.text("Relative humidity")
.attr("class", "hover-box-texts")
.attr("transform", "translate(" + pc.margin + "," + (pc.rbmargin + 200) + ")")
pc.svg.append("text")
.text("Metabolic Rate")
.attr("class", "hover-box-texts")
.attr("transform", "translate(" + pc.margin + "," + (pc.rbmargin + 220) + ")")
pc.svg.append("text")
.text("Clothing level")
.attr("class", "hover-box-texts")
.attr("transform", "translate(" + pc.margin + "," + (pc.rbmargin + 240) + ")")
pc.svg.append("text")
.text("PMV")
.attr("class", "hover-box-texts")
.attr("transform", "translate(" + pc.margin + "," + (pc.rbmargin + 260) + ")")
pc.svg.append("text")
.attr("id", "db-value")
.attr("class", "hover-box-texts")
.attr("transform", "translate(" + (pc.margin + 120) + "," + (pc.rbmargin + 140) + ")")
pc.svg.append("text")
.attr("id", "mrt-value")
.attr("class", "hover-box-texts")
.attr("transform", "translate(" + (pc.margin + 120) + "," + (pc.rbmargin + 160) + ")")
pc.svg.append("text")
.attr("id", "vel-value")
.attr("class", "hover-box-texts")
.attr("transform", "translate(" + (pc.margin + 120) + "," + (pc.rbmargin + 180) + ")")
pc.svg.append("text")
.attr("id", "rh-value")
.attr("class", "hover-box-texts")
.attr("transform", "translate(" + (pc.margin + 120) + "," + (pc.rbmargin + 200) + ")")
pc.svg.append("text")
.attr("id", "met-value")
.attr("class", "hover-box-texts")
.attr("transform", "translate(" + (pc.margin + 120) + "," + (pc.rbmargin + 220) + ")")
pc.svg.append("text")
.attr("id", "clo-value")
.attr("class", "hover-box-texts")
.attr("transform", "translate(" + (pc.margin + 120) + "," + (pc.rbmargin + 240) + ")")
pc.svg.append("text")
.attr("id", "pmv-value")
.attr("class", "hover-box-texts")
.attr("transform", "translate(" + (pc.margin + 120) + "," + (pc.rbmargin + 260) + ")")
pc.svg.append("text")
.text("°C")
.attr("id", "box-db-unit")
.attr("class", "hover-box-texts")
.attr("transform", "translate(" + (pc.margin + 160) + "," + (pc.rbmargin + 140) + ")")
pc.svg.append("text")
.text("°C")
.attr("id", "box-mrt-unit")
.attr("class", "hover-box-texts")
.attr("transform", "translate(" + (pc.margin + 160) + "," + (pc.rbmargin + 160) + ")")
pc.svg.append("text")
.text("m/s")
.attr("id", "box-vel-unit")
.attr("class", "hover-box-texts")
.attr("transform", "translate(" + (pc.margin + 160) + "," + (pc.rbmargin + 180) + ")")
pc.svg.append("text")
.text("%")
.attr("id", "box-rh-unit")
.attr("class", "hover-box-texts")
.attr("transform", "translate(" + (pc.margin + 160) + "," + (pc.rbmargin + 200) + ")")
pc.svg.append("text")
.text("met")
.attr("id", "box-met-unit")
.attr("class", "hover-box-texts")
.attr("transform", "translate(" + (pc.margin + 160) + "," + (pc.rbmargin + 220) + ")")
pc.svg.append("text")
.text("clo")
.attr("id", "box-clo-unit")
.attr("class", "hover-box-texts")
.attr("transform", "translate(" + (pc.margin + 160) + "," + (pc.rbmargin + 240) + ")")
}
this.drawPoints = function(data) {
data.forEach(function(d){
pc.svg.append("circle")
.attr("clip-path", "url(#clip)")
.attr("r", 3)
.attr("fill", pc.getColor(d.pmv.pmv))
.attr("stroke", "gray")
.attr("stroke-width", "1")
.attr("cx", pc.db_scale(d.ta))
.attr("cy", pc.hr_scale(1000 * d.hr))
.on("mouseover", function() {
d3.select('#rh-value').text(d.rh.toFixed(1))
d3.select('#met-value').text(d.met.toFixed(2))
d3.select('#clo-value').text(d.clo.toFixed(2))
d3.select('#pmv-value').text(d.pmv.pmv.toFixed(2))
if (isCelsius) {
d3.select('#db-value').text(d.ta.toFixed(1))
d3.select('#mrt-value').text(d.tr.toFixed(1))
d3.select('#vel-value').text(d.vel.toFixed(2))
} else {
d3.select('#db-value').text(util.CtoF(d.ta).toFixed(1))
d3.select('#mrt-value').text(util.CtoF(d.tr).toFixed(1))
d3.select('#vel-value').text(util.CtoF(d.vel).toFixed(0))
}
d3.select(this)
.attr("r", "6");
var boundary = pc.findComfortBoundary(d, 0.5)
pc.drawComfortRegion(boundary)
d3.select('.comfortzone')
.on('mouseover', function(){
d3.select(this)
.attr('class','comfortzone')
})
.attr('opacity','0.4')
})
.on("mouseout", function() {
d3.select(this)
.attr("r", "3");
})
});
}
this.redrawPoint = function(data) {
d3.selectAll("circle")
.attr("cx", pc.db_scale(data[0].db))
.attr("cy", pc.hr_scale(1000 * data[0].hr))
}
this.getHumRatio = function(db, rh) {
return psy.humratio(psy.PROP.Patm, rh * psy.satpress(db) / 100)
}
this.findComfortBoundary = function(d, pmvlimit) {
var boundary = []
function rhclos(rhx, target) {
return function(db) {
if($("#chartSelect").val() == "psychtop"){
return comf.pmvElevatedAirspeed(db, db, d.vel, rhx, d.met, d.clo, 0).pmv - target
} else {
return comf.pmvElevatedAirspeed(db, d.tr, d.vel, rhx, d.met, d.clo, 0).pmv - target
}
}
}
function solve(rhx, target) {
var epsilon = 0.001 // ta precision
var a = -50
var b = 50
var fn = rhclos(rhx, target)
t = util.secant(a, b, fn, epsilon)
if (isNaN(t)) {
t = util.bisect(a, b, fn, epsilon, 0)
}
return {
"db": t,
"hr": pc.getHumRatio(t, rhx)
}
}
var incr = 10;
for (var rhx = 0; rhx <= 100; rhx += incr) {
boundary.push(solve(rhx, -pmvlimit))
}
while (true) {
t += 0.5
boundary.push({
"db": t,
"hr": pc.getHumRatio(t, 100)
})
if (comf.pmvElevatedAirspeed(t, d.tr, d.vel, rhx, d.met, d.clo, 0).pmv > pmvlimit) break
}
for (var rhx = 100; rhx >= 0; rhx -= incr) {
boundary.push(solve(rhx, pmvlimit))
}
return boundary
}
this.findPsyBoundary = function() {
var psyboundary = []
psyboundary.push({
"db": 10,
"hr": 0
})
psyboundary.push({
"db": 10,
"hr": pc.getHumRatio(10, 100)
})
t = 10
while (true) {
t += 0.5
psyboundary.push({
"db": t,
"hr": pc.getHumRatio(t, 100)
})
if (pc.getHumRatio(t, 100) > 30) break
}
psyboundary.push({
"db": 36,
"hr": 30
})
psyboundary.push({
"db": 36,
"hr": 0
})
return psyboundary
}
this.toggleUnits = function(isCelsius) {
if (isCelsius) {
d3.select("#db-axis-C").attr("opacity", "100")
d3.select("#db-axis-F").attr("opacity", "0")
document.getElementById('hr-unit0').textContent = "Humidity Ratio [g"
document.getElementById('hr-unit1').textContent = "/ kg"
} else {
d3.select("#db-axis-C").attr("opacity", "0")
d3.select("#db-axis-F").attr("opacity", "100")
document.getElementById('hr-unit0').textContent = "Humidity Ratio [lb"
document.getElementById('hr-unit1').textContent = "/ klb"
}
this.convertBox()
}
}
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([ 'util'], factory);
} else {
// Browser globals
root.psy = factory( root.util);
}
}(this, function ( util) {
var psy = {};
psy.PROP = {
Patm: 101325.0,
CpAir: 1004.0,
CpWat: 4186.0,
CpVap: 1805.0,
Hfg: 2501000.0,
RAir: 287.055,
TKelConv: 273.15
};
psy.PREC = {
rh: 0,
wetbulb: 1,
w: 3,
dewpoint: 1,
vappress: 1
};
psy.convert = function(x, tdb, origin, target) {
switch (origin) {
case 'rh':
a = this.tdb_rh(tdb, x);
break;
case 'wetbulb':
a = this.tdb_twb(tdb, x);
break;
case 'w':
a = this.tdb_w(tdb, x);
break;
case 'dewpoint':
a = this.tdb_dewpoint(tdb, x);
break;
case 'vappress':
a = this.tdb_vappress(tdb, x);
break;
}
switch (target) {
case 'rh':
return a.rh;
case 'wetbulb':
return a.wetbulb;
case 'w':
return a.w;
case 'dewpoint':
return a.dewpoint;
case 'vappress':
return a.vappress;
}
};
psy.tdb_rh = function(tdb, rh) {
var a = {};
var psat = this.satpress(tdb);
a.rh = parseFloat(rh);
a.vappress = rh / 100 * psat;
a.w = this.humratio(this.PROP.Patm, a.vappress);
a.wetbulb = this.wetbulb(tdb, a.w);
a.dewpoint = this.dewpoint(a.w);
return a;
};
psy.tdb_twb = function(tdb, twb) {
var a = {};
var psat = this.satpress(twb);
var PROP = this.PROP;
var wstar = this.humratio(PROP.Patm, psat);
a.wetbulb = twb;
a.w = ((PROP.Hfg + (PROP.CpVap - PROP.CpWat) * twb) * wstar - PROP.CpAir * (tdb - twb)) / (PROP.Hfg + PROP.CpVap * tdb - PROP.CpWat * twb);
psat = this.satpress(tdb);
a.rh = 100 * this.relhum(PROP.Patm, psat, a.w);
a.dewpoint = this.dewpoint(a.w);
a.vappress = a.rh / 100 * psat;
return a;
};
psy.tdb_w = function(tdb, w) {
var a = {};
var psat = this.satpress(tdb);
a.w = w;
a.rh = 100 * psy.relhum(this.PROP.Patm, psat, w);
if (a.rh > 100) a.rh = Number.NaN;
a.wetbulb = this.wetbulb(tdb, w);
a.dewpoint = this.dewpoint(w);
a.vappress = a.rh / 100 * psat;
return a;
};
psy.tdb_dewpoint = function(tdb, dewpoint) {
var w_l = 0.00001,
w_r = 0.2,
eps = 0.0001;
var fn = function(w) {
var dpstar = psy.dewpoint(w);
return dewpoint - dpstar;
};
var w = util.bisect(w_l, w_r, fn, eps, 0);
return psy.tdb_w(tdb, w);
};
psy.tdb_vappress = function(tdb, vappress) {
var psat = this.satpress(tdb);
var rh = 100 * vappress / psat;
return psy.tdb_rh(tdb, rh);
};
psy.drybulb = function(h, w) {
var PROP = this.PROP;
return (H - PROP.Hfg * w) / (PROP.CpAir + PROP.CpVap * w);
};
psy.wetbulb = function(tdb, w) {
var psatStar, wStar, fn, eps = 0.01,
wetbulb_l = -100,
wetbulb_r = 200;
var PROP = this.PROP;
fn = function(t) {
psatStar = psy.satpress(t);
wStar = psy.humratio(PROP.Patm, psatStar);
newW = ((PROP.Hfg - PROP.CpWat - PROP.CpVap * t) * wStar - PROP.CpAir * (tdb - t)) / (PROP.Hfg + PROP.CpVap * tdb - PROP.CpWat * t);
return (w - newW);
};
return util.bisect(wetbulb_l, wetbulb_r, fn, eps, 0);
};
psy.satpress = function(tdb) {
var tKel = tdb + this.PROP.TKelConv,
C1 = -5674.5359,
C2 = 6.3925247,
C3 = -0.9677843 * Math.pow(10, -2),
C4 = 0.62215701 * Math.pow(10, -6),
C5 = 0.20747825 * Math.pow(10, -8),
C6 = -0.9484024 * Math.pow(10, -12),
C7 = 4.1635019,
C8 = -5800.2206,
C9 = 1.3914993,
C10 = -0.048640239,
C11 = 0.41764768 * Math.pow(10, -4),
C12 = -0.14452093 * Math.pow(10, -7),
C13 = 6.5459673,
pascals;
if (tKel < 273.15) {
pascals = Math.exp(C1 / tKel + C2 + tKel * (C3 + tKel * (C4 + tKel * (C5 + C6 * tKel))) + C7 * Math.log(tKel));
} else if (tKel >= 273.15) {
pascals = Math.exp(C8 / tKel + C9 + tKel * (C10 + tKel * (C11 + tKel * C12)) + C13 * Math.log(tKel));
}
return pascals;
};
psy.relhum = function(patm, psat, humRatio) {
var pw, rh;
pw = patm * humRatio / (0.62198 + humRatio);
rh = pw / psat;
return rh;
};
psy.humratio = function(patm, pw) {
// ASHRAE Fundamentals 2009: 0.621945
return 0.62198 * pw / (patm - pw);
};
psy.enthalpy = function(tdb, w) {
var hDryAir, hSatVap, h;
var PROP = this.PROP;
hDryAir = PROP.CpAir * tdb;
hSatVap = PROP.Hfg + PROP.CpVap * tdb;
h = hDryAir + w * hSatVap;
return h;
};
psy.rhodry = function(tdb, w) {
var PROP = this.PROP;
var pAir = 0.62198 * PROP.Patm / (0.62198 + w);
return pAir / PROP.RAir / (tdb + PROP.TKelConv);
};
psy.rhomoist = function(rhodry, w) {
return rhodry * (1 + w);
};
psy.enthsat = function(tdb) {
var psat = this.satpress(tdb);
var w = this.humratio(this.PROP.Patm, psat);
return this.enthalpy(tdb, w);
};
psy.dewpoint = function(w) {
var pw = this.PROP.Patm * w / (0.62198 + w);
return this.sattemp(pw);
};
psy.sattemp = function(p) {
var tsat_l = 0,
tsat_r = 500,
eps = 0.0001,
psat;
var fn = function(t) {
return (p - psy.satpress(t));
};
return util.bisect(tsat_l, tsat_r, fn, eps, 0);
};
psy.tairsat = function(hsat) {
var tsat_l = 0,
tsat_r = 1000,
eps = 0.01;
var fn = function(t) {
return (hsat - psy.enthsat(t));
};
return util.bisect(tsat_l, tsat_r, fn, eps, 0);
};
psy.globetemp = function(ta, vel, tglobe, diameter, emissivity) {
pow = Math.pow;
return pow(pow(tglobe + 273, 4) + (1.1 * pow(10, 8) * pow(vel, 0.6)) / (emissivity * pow(diameter, 0.4)) * (tglobe - ta), 0.25) - 273;
};
return psy;
}));
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['psy', 'comf', 'util'], factory);
} else {
// Browser globals
root.TemperatureHumidityChart = factory(root.psy, root.comf, root.util);
}
}(this, function (psy, comf, util) {
// ----- CODE to draw the comfort zone on a chart with Dry-Bulb Temp on the x-axis and Relative Humidity on the y-axis -----
var TemperatureHumidityChart = function() {
// set up viewport
this.margin = 60;
this.rbmargin = 40;
this.width = 580;
this.height = 500;
this.db_min = 10;
this.db_max = 36;
this.db_extent = [this.db_min, this.db_max];
this.db_scale = d3.scale.linear()
.range([this.margin, this.width - this.rbmargin])
.domain(this.db_extent);
this.db_extent_F = [util.CtoF(this.db_min), util.CtoF(this.db_max)];
this.db_scale_F = d3.scale.linear()
.range([this.margin, this.width - this.rbmargin])
.domain(this.db_extent_F);
this.rh_extent = [0, 100];
this.rh_scale = d3.scale.linear()
.range([this.height - this.margin, this.rbmargin])
.domain(this.rh_extent);
// defining a poliline
this.pline = d3.svg.line()
.x(function(d) {
return this.db_scale(d.db);
})
.y(function(d) {
return this.rh_scale(d.rh);
});
this.drawChart = function() {
var _this = this;
var db_axis = d3.svg.axis().scale(this.db_scale);
var db_axis_F = d3.svg.axis().scale(this.db_scale_F);
var rh_axis = d3.svg.axis().scale(this.rh_scale).orient("left");
var line = d3.svg.line()
.x(function(d) {
return _this.db_scale(d.db);
})
.y(function(d) {
return _this.rh_scale(d.rh);
})
.interpolate('cardinal');
// drawing svg
d3.select("#temphumchart-div")
.append("svg")
.attr("class", "svg-temphum").attr("id", "svg-temphum")
.attr("width", this.width)
.attr("height", this.height);
this.svg = d3.select(".svg-temphum");
// ClipPath
this.svg
.append("defs")
.append("clipPath")
.attr("id", "clip_th")
.append("rect")
.attr("x", "0")
.attr("y", "0")
.attr("width", this.width - this.margin - this.rbmargin)
.attr("height", this.height - this.margin - this.rbmargin)
.attr("transform", "translate(" + this.margin + "," + this.rbmargin + ")");
// Drawing the axes
this.svg
.append("g")
.attr("class", "db axis")
.attr("id", "db-axis-C-temphum")
.attr("transform", "translate(0," + (this.height - this.margin) + ")")
.call(db_axis.tickSubdivide(0).tickSize(-(this.height - this.margin - this.rbmargin), 0).tickPadding(5));
this.svg
.append("g")
.attr("class", "db axis")
.attr("id", "db-axis-F-temphum")
.attr("opacity", "0")
.attr("transform", "translate(0," + (this.height - this.margin) + ")")
.call(db_axis_F.tickSubdivide(0).tickSize(-(this.height - this.margin - this.rbmargin), 0).tickPadding(5));
this.svg
.append("g")
.attr("class", "rh axis")
.attr("transform", "translate(" + (this.margin) + ",0)")
.call(rh_axis.tickSubdivide(0).tickSize(-(this.width - this.margin - this.rbmargin), 0).tickPadding(5));
// giving labels to the axes
d3.select("#db-axis-C-temphum")
.append("text")
.text("Dry-bulb Temperature [°C]")
.attr("class", "db-unit")
.attr("x", (this.width / 2) - 50)
.attr("y", this.margin / 1.6);
d3.select("#db-axis-F-temphum")
.append("text")
.text("Dry-bulb Temperature [°F]")
.attr("class", "db-unit")
.attr("x", (this.width / 2) - 50)
.attr("y", this.margin / 1.6);
d3.select(".rh.axis")
.append("text")
.attr("id", "rh-text")
.text("Relative Humidity [%]")
.attr("transform", "rotate (-90, -35, 0) translate(-350)");
};
this.drawComfortRegion = function(data) {
d3.select(".svg-temphum")
.append("path")
.attr("clip-path", "url(#clip_th)")
.attr("d", this.pline(data) + "Z")
.attr("class", "comfortzone-temphum").attr("id", "temphum-comfortzone")
.on("mouseover", function() {
d3.select(this).attr("class", "comfortzoneover");
})
.on("mouseout", function() {
d3.select(this).attr("class", "comfortzone-temphum");
});
};
this.redrawComfortRegion = function(data) {
d3.select(".comfortzone-temphum")
.transition()
.attr("d", this.pline(data) + "Z");
};
this.drawPoint = function() {
this.svg
.append("circle")
.attr("class", "outer")
.attr("clip-path", "url(#clip_th)")
.attr("r", 12);
this.svg
.append("circle")
.attr("clip-path", "url(#clip_th)")
.attr("class", "inner")
.attr("r", 2);
d3.selectAll("circle")
.attr("cx", this.db_scale(d.ta))
.attr("cy", this.rh_scale(d.rh));
};
this.drawPoints = function(data) {
var _this = this;
this.svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 2.75)
.attr("cx", function (d) {
return _this.db_scale(d.ta);
})
.attr("cy", function (d) {
return _this.rh_scale(d.rh);
});
};
this.redrawPoint = function() {
d3.selectAll("circle")
.transition()
.attr("cx", this.db_scale(d.ta))
.attr("cy", this.rh_scale(d.rh));
};
this.getHumRatio = function(db, rh) {
return psy.humratio(psy.PROP.Patm, rh * psy.satpress(db) / 100);
};
this.findComfortBoundary = function(d, pmvlimit) {
var boundary = [];
function solve(rh, target) {
var epsilon = 0.001;
var a = -50;
var b = 50;
var fn = function(db) {
return (comf.pmvElevatedAirspeed(db, d.tr, d.vel, rh, d.met, d.clo, d.wme).pmv - target);
};
//t = util.bisect(a, b, fn, epsilon, target)
t = util.secant(a, b, fn, epsilon);
return {
"db": t,
"rh": rh
};
}
for (rh = 10; rh <= 60; rh += 10) {
boundary.push(solve(rh, -pmvlimit));
}
for (rh = 60; rh >= 10; rh -= 10) {
boundary.push(solve(rh, pmvlimit));
}
return boundary;
};
this.toggleUnits = function(isCelsius) {
if (isCelsius) {
d3.select("#db-axis-C-temphum").attr("opacity", "100");
d3.select("#db-axis-F-temphum").attr("opacity", "0");
} else {
d3.select("#db-axis-C-temphum").attr("opacity", "0");
d3.select("#db-axis-F-temphum").attr("opacity", "100");
}
};
};
return TemperatureHumidityChart;
}));
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], factory);
} else {
// Browser globals
root.util = factory();
}
}(this, function () {
var util = {};
util.bisect = function(a, b, fn, epsilon, target) {
var a_T, b_T, midpoint, midpoint_T;
while (Math.abs(b - a) > 2 * epsilon) {
midpoint = (b + a) / 2;
a_T = fn(a);
b_T = fn(b);
midpoint_T = fn(midpoint);
if ((a_T - target) * (midpoint_T - target) < 0) b = midpoint;
else if ((b_T - target) * (midpoint_T - target) < 0) a = midpoint;
else return -999;
}
return midpoint;
};
util.secant = function(a, b, fn, epsilon) {
// root-finding only
var f1 = fn(a);
if (Math.abs(f1) <= epsilon) return a;
var f2 = fn(b);
if (Math.abs(f2) <= epsilon) return b;
var slope, c, f3;
for (var i = 0; i < 100; i++){
slope = (f2 - f1) / (b - a);
c = b - f2/slope;
f3 = fn(c);
if (Math.abs(f3) < epsilon) return c;
a = b;
b = c;
f1 = f2;
f2 = f3;
}
return NaN;
};
util.getSensation = function(pmv) {
if (pmv < -2.5) return 'Cold';
else if (pmv < -1.5) return 'Cool';
else if (pmv < -0.5) return 'Slightly Cool';
else if (pmv < 0.5) return 'Neutral';
else if (pmv < 1.5) return 'Slightly Warm';
else if (pmv < 2.5) return 'Warm';
else return 'Hot';
};
util.CtoF = function(x){
return x * 9 / 5 + 32;
};
util.FtoC = function(x) {
return (x - 32) * 5 / 9;
};
return util;
}));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment