psychart
Version:
View air conditions on a psychrometric chart
141 lines (140 loc) • 5.07 kB
JavaScript
import * as SMath from 'smath';
import Psychrolib from 'psychrolib';
/**
* Represents a single air condition using several states.
*/
export class PsyState {
state;
options;
/**
* Calculation tolerance
*/
static TOL = 0.01;
/**
* Dry Bulb
*/
db;
/**
* Relative Humidity
*/
rh;
/**
* Wet Bulb
*/
wb;
/**
* Dew Point
*/
dp;
/**
* Humidity Ratio
*/
hr;
/**
* Vapor Pressure
*/
vp;
/**
* Moist Air Enthalpy
*/
h;
/**
* Moist Air Volume
*/
v;
/**
* Degree of Saturation
*/
s;
/**
* Standard Atmospheric Air Pressure
*/
atm;
/**
* Maximum Humidity Ratio
*/
hrMax;
/**
* Initialize a new psychrometric state.
*/
constructor(state, options) {
this.state = state;
this.options = options;
Psychrolib.SetUnitSystem(options.unitSystem === 'IP' ? Psychrolib.IP : Psychrolib.SI);
this.atm = Psychrolib.GetStandardAtmPressure(options.altitude);
this.hrMax = Psychrolib.GetHumRatioFromTDewPoint(options.dpMax, this.atm);
this.db = state.db;
switch (state.measurement) {
case ('dbrh'): {
this.rh = state.other;
[this.hr, this.wb, this.dp, this.vp, this.h, this.v, this.s] = Psychrolib.CalcPsychrometricsFromRelHum(state.db, state.other, this.atm);
break;
}
case ('dbwb'): {
this.wb = state.other;
[this.hr, this.dp, this.rh, this.vp, this.h, this.v, this.s] = Psychrolib.CalcPsychrometricsFromTWetBulb(state.db, state.other, this.atm);
break;
}
case ('dbdp'): {
this.dp = state.other;
[this.hr, this.wb, this.rh, this.vp, this.h, this.v, this.s] = Psychrolib.CalcPsychrometricsFromTDewPoint(state.db, state.other, this.atm);
break;
}
case ('dbhr'): {
this.dp = Psychrolib.GetTDewPointFromHumRatio(state.db, state.other, this.atm);
[this.hr, this.wb, this.rh, this.vp, this.h, this.v, this.s] = Psychrolib.CalcPsychrometricsFromTDewPoint(state.db, this.dp, this.atm);
if (!SMath.approx(this.hr, state.other) && Math.abs(SMath.error(this.hr, state.other)) > PsyState.TOL) {
throw new Error(`Error in psychrolib computation. Expected "${state.other}" but found "${this.hr}" for ${state.measurement}.`);
}
break;
}
case ('dbh'): {
this.hr = Psychrolib.GetHumRatioFromEnthalpyAndTDryBulb(state.other, state.db);
this.dp = Psychrolib.GetTDewPointFromHumRatio(state.db, this.hr, this.atm);
[this.hr, this.wb, this.rh, this.vp, this.h, this.v, this.s] = Psychrolib.CalcPsychrometricsFromTDewPoint(state.db, this.dp, this.atm);
if (!SMath.approx(this.h, state.other) && Math.abs(SMath.error(this.h, state.other)) > PsyState.TOL) {
throw new Error(`Error in psychrolib computation. Expected "${state.other}" but found "${this.h}" for ${state.measurement}.`);
}
break;
}
default: {
throw new Error(`Invalid measurement type ${state.measurement}.`);
}
}
}
/**
* Convert this psychrometric state to an X-Y coordinate on a psychrometric chart.
*/
toXY() {
// Determine if additional padding is needed to show axis names
const fontPad = this.options.showAxisNames ? (1.5 * this.options.font.size) : 0;
// The lower-left location
const origin = {
x: this.options.padding.x + (this.options.flipXY ? fontPad : 0),
y: this.options.padding.y + (this.options.flipXY ? fontPad : 0),
};
// The upper-right location
const bound = {
x: this.options.size.x - this.options.padding.x - (this.options.flipXY ? 0 : fontPad),
y: this.options.size.y - this.options.padding.y - (this.options.flipXY ? 0 : fontPad),
};
if (this.options.flipXY) {
return {
x: SMath.clamp(SMath.translate(this.hr, 0, this.hrMax, origin.x, bound.x), origin.x, bound.x),
y: SMath.clamp(SMath.translate(this.db, this.options.dbMin, this.options.dbMax, bound.y, origin.y), origin.y, bound.y)
};
}
else {
return {
x: SMath.clamp(SMath.translate(this.db, this.options.dbMin, this.options.dbMax, origin.x, bound.x), origin.x, bound.x),
y: SMath.clamp(SMath.translate(this.hr, 0, this.hrMax, bound.y, origin.y), origin.y, bound.y)
};
}
}
/**
* Calculate the dry bulb temperature of dry air with enthalpy `h`
*/
static getDryBulbWithEnthalpy(h) {
return Psychrolib.GetTDryBulbFromEnthalpyAndHumRatio(h, 0);
}
}