weather-formulas
Version:
A collection of atmospheric, meteorological weather calculations
361 lines (360 loc) • 13.7 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const constants_1 = __importDefault(require('../constants.cjs'));
const DEW_POINT_VALUATIONS = {
ARDENBUCK_DEFAULT: { a: 6.1121, b: 18.678, c: 257.14, d: 234.5 },
DAVID_BOLTON: { a: 6.112, b: 17.67, c: 234.5, d: 234.5 }, //maximum error of 0.1%, for −30 °C ≤ T ≤ 35°C and 1% < RH < 100%
SONNTAG1990: { a: 6.112, b: 17.62, c: 243.12, d: 234.5 }, //for −45 °C ≤ T ≤ 60 °C (error ±0.35 °C).
PAROSCIENTIFIC: { a: 6.105, b: 17.27, c: 237.7, d: 234.5 }, //for 0 °C ≤ T ≤ 60 °C (error ±0.4 °C).
ARDENBUCK_PLUS: { a: 6.1121, b: 17.368, c: 238.88, d: 234.5 }, //for 0 °C ≤ T ≤ 50 °C (error ≤ 0.05%).
ARDENBUCK_MINUS: { a: 6.1121, b: 17.966, c: 247.15, d: 234.5 } //for −40 °C ≤ T ≤ 0 °C (error ≤ 0.06%).
};
/**
* Gets the Dew Point Valuation by temperature
* @param {number} temperature Temperature in CELCIUS
* @returns {Array<IValuationSet>} Dew Point Valuation
*/
function dewPointValuationsByTemperature(temperature) {
if (temperature < 0) {
return DEW_POINT_VALUATIONS.ARDENBUCK_MINUS;
}
else if (temperature >= 0 && temperature <= 50) {
return DEW_POINT_VALUATIONS.ARDENBUCK_PLUS;
}
else if (temperature > 50) {
return DEW_POINT_VALUATIONS.PAROSCIENTIFIC;
}
else {
return DEW_POINT_VALUATIONS.ARDENBUCK_DEFAULT;
}
}
/**
*
* @param {number} temperature Temperature in K (Kelvin)
* @param {number} humidity Humidity in RH (Relative Humidity)
* @param {IValuationSet} valuationSet The valuation set to use in the calculation
* @returns {number} Dew Point in Kelvin
*/
function dewPointMagnusFormula(temperature, humidity, valuationSet) {
const T = kelvinToCelcius(temperature);
const RH = humidity;
if (valuationSet === null || valuationSet === undefined)
valuationSet = dewPointValuationsByTemperature(T);
const gammaT_RH = Math.log(RH / 100) + ((valuationSet.b * T) / (valuationSet.c + T));
const Tdp = (valuationSet.c * gammaT_RH) / (valuationSet.b - gammaT_RH);
return celciusToKelvin(Tdp);
}
/**
*
* @param {number} temperature Temperature in K (Kelvin)
* @param {number} humidity Humidity in RH (Relative Humidity)
* @param {IValuationSet} valuationSet The valuation set to use in the calculation
* @returns {number} Dew Point in Kelvin
*/
function dewPointArdenBuckEquation(temperature, humidity, valuationSet) {
const T = kelvinToCelcius(temperature);
const RH = humidity;
if (valuationSet === null || valuationSet === undefined)
valuationSet = dewPointValuationsByTemperature(T);
const gamma_T_RH = Math.log((RH / 100) * Math.exp((valuationSet.b - (T / valuationSet.d)) * (T / (valuationSet.c + T))));
const Tdp = (valuationSet.c * gamma_T_RH) / (valuationSet.b - gamma_T_RH);
return celciusToKelvin(Tdp);
}
/**
* Calculate Potential Temperature
* @param {number} temperature Temperature in K (Kelvin)
* @param {number} pressure Pressure in Pa (Pascal)
* @returns {number} Potential Temperature in K (Kelvin)
*/
function potentialTemperature(temperature, pressure) {
const standardPressure = 100000; // Standard pressure in Pa
return temperature * Math.pow(standardPressure / pressure, 0.286);
}
/**
* Calculate Virtual Temperature
* @param {number} temperature Temperature in K (Kelvin)
* @param {number} mixingRatio Mixing Ratio in g/kg (grams per kilogram)
* @returns {number} Virtual Temperature in K (Kelvin)
*/
function virtualTemperature(temperature, mixingRatio) {
return temperature * (1 + 0.61 * (mixingRatio / 1000));
}
/**
*
* @param {number} temperature Temperature in K (Kelvin)
* @param {number} windSpeed Windspeed in M/S (meter per second)
* @returns {number} Wind Chill Index in Kelvin
*/
function windChillIndex(temperature, windSpeed) {
const v = meterPerSecondToKilometerPerHour(windSpeed);
const Ta = kelvinToCelcius(temperature);
const v_exp = (v ** 0.16);
const Twc = 13.12 + (0.6215 * Ta) - (11.37 * v_exp) + (0.3965 * Ta * v_exp);
return celciusToKelvin(Twc);
}
/**
* @param {number} temperature Temperature in K (Kelvin)
* @param {number} humidity Humidity in RH (Relative Humidity)
* @param {number} windspeed Windspeed in M/S (meter per second)
* @returns {number} Apparent Temperature in Kelvin
*/
function australianAapparentTemperature(temperature, humidity, windspeed) {
const Ta = kelvinToCelcius(temperature);
const v = windspeed;
const e = (humidity / 100) * 6.015 * Math.exp((17.27 * Ta) / (237.7 + Ta));
const AT = Ta + (0.33 * e) - (0.7 * v) - 4.00;
return celciusToKelvin(AT);
}
/**
*
* @param {number} temperature Temperature in K (Kelvin)
* @param {number} humidity Humidity in RH (Relative Humidity)
* @returns {number} Heat Index in Kelvin
*/
function heatIndex(temperature, humidity) {
if (humidity > 100 || humidity < 0)
throw ("Not a valid humidity");
const T = kelvinToCelcius(temperature);
const R = humidity;
const c1 = -8.78469475556;
const c2 = 1.61139411;
const c3 = 2.33854883889;
const c4 = -0.14611605;
const c5 = -0.012308094;
const c6 = -0.0164248277778;
const c7 = 0.002211732;
const c8 = 0.00072546;
const c9 = -0.000003582;
const Te2 = (T ** 2);
const Re2 = (R ** 2);
const HI = (c1) + (c2 * T) + (c3 * R) + (c4 * T * R) + (c5 * Te2) + (c6 * Re2) + (c7 * Te2 * R) + (c8 * T * Re2) + (c9 * Te2 * Re2);
return celciusToKelvin(HI);
}
/**
*
* @param {number} heatIndexTemperature Temperature in K (Kelvin)
* @returns {string} Heat Index Warning
*/
function heatIndexText(heatIndexTemperature) {
let thresholds = [
{ lowerLimit: 52, text: "Extreme danger", warning: "Heat stroke is imminent." },
{ lowerLimit: 40, text: "Danger", warning: "Heat cramps and heat exhaustion are likely; heat stroke is probable with continued activity." },
{ lowerLimit: 33, text: "Extreme caution", warning: "Heat cramps and heat exhaustion are possible. Continuing activity could result in heat stroke." },
{ lowerLimit: 26, text: "Caution", warning: "Fatigue is possible with prolonged exposure and activity. Continuing activity could result in heat cramps." }
];
let result = thresholds.find((t) => heatIndexTemperature >= (t.lowerLimit + constants_1.default.KELVIN));
return result === undefined ? null : result;
}
/**
* Gets the Humidex temperature
* @param {number} temperature Temperature in K (Kelvin)
* @param {number} humidity Humidity in RH (Relative Humidity)
* @returns {number} Humidex in Kelvin
*/
function humidex(temperature, humidity) {
const Tair = kelvinToCelcius(temperature);
const Tdew = dewPointMagnusFormula(temperature, humidity);
const e = 6.11 * Math.exp(5417.7530 * ((1 / 273.16) - (1 / Tdew)));
const h = (0.5555) * (e - 10.0);
const humidex = Tair + h;
return celciusToKelvin(humidex);
}
/**
* Gets the Humidex test warning
* @param {number} humidex Temperature in K (Kelvin)
* @returns {string} Humidex Warning
*/
function humidexText(humidex) {
const thresholds = [
{ lowerLimit: 46, text: "Dangerous" },
{ lowerLimit: 40, text: "Great discomfort" },
{ lowerLimit: 30, text: "Some discomfort" }
];
const result = thresholds.find((t) => humidex >= (t.lowerLimit + constants_1.default.KELVIN));
return result === undefined ? null : result;
}
/**
* Calculate the lapse rate
* @param altitude1 Lower or higher altitude 1
* @param T1 Temperature at altitude 1
* @param altitude2 Lower or higher altitude 2
* @param T2 Temperature at altitude 2
* @returns Lapse rate
*/
function calculateLapseRate(altitude1, T1, altitude2, T2) {
return (T2 - T1) / (altitude2 - altitude1); // Kelvin/m
}
/**
* Calculate dynamic lapse rate based on a range of altitude and temperature data
* @param readings Readings data
* @param hours Number of hours to read
* @param filterByLastReading Filter by the datetime in the most recent reading
* @returns Dynamic lapse rate, or default.
*/
function calculateDynamicLapseRate(readings, hours = 24, filterByLastReading = false) {
const filteredReadings = filterReadingsByTimeRange(readings, hours);
let totalLapseRate = 0;
let count = 0;
for (let i = 1; i < filteredReadings.length; i++) {
const T1 = filteredReadings[i - 1].temperature;
const altitude1 = filteredReadings[i - 1].altitude;
const T2 = filteredReadings[i].temperature;
const altitude2 = filteredReadings[i].altitude;
if (altitude2 !== altitude1) {
const lapseRate = calculateLapseRate(altitude1, T1, altitude2, T2);
totalLapseRate += lapseRate;
count++;
}
}
return count > 0 ? totalLapseRate / count : 0.0065; // Default to standard lapse rate if no data
}
/**
*
* @param readings Readings data
* @param hours Hours to filter
* @returns Weighted average temperature
*/
function calculateWeightedAverageTemperature(readings, hours = 24) {
const filteredReadings = filterReadingsByTimeRange(readings, hours);
let totalWeight = 0;
let weightedSum = 0;
for (let i = 1; i < filteredReadings.length; i++) {
const T1 = filteredReadings[i - 1].temperature;
const altitude1 = filteredReadings[i - 1].altitude;
const T2 = filteredReadings[i].temperature;
const altitude2 = filteredReadings[i].altitude;
const weight = Math.abs(altitude2 - altitude1); // Altitude difference as weight
const averageTemperature = (T1 + T2) / 2;
weightedSum += averageTemperature * weight;
totalWeight += weight;
}
return totalWeight > 0 ? weightedSum / totalWeight : readings[0]?.temperature || 288.15; // Default to 15°C in Kelvin if no data
}
/**
*
* @param readings Readings data
* @param hours Filter by humber of hours to read
* @param filterByLastReading Filter by the datetime in the most recent reading
* @returns Readings within the given hours
*/
function filterReadingsByTimeRange(readings, hours, filterByLastReading = false) {
readings.sort((a, b) => a.datetime.getTime() - b.datetime.getTime()); //oldest to newest
const cutoffTime = filterByLastReading ? readings[readings.length - 1].datetime.getTime() - hours * 60 * 60 * 1000 : Date.now() - hours * 60 * 60 * 1000; // Convert hours to milliseconds
const filteredReadings = readings.filter((reading) => reading.datetime.getTime() >= cutoffTime);
return filteredReadings;
}
/**
*
* @param altitude1 Lower or higher altitude 1
* @param T1 Temperature at altitude 1
* @param altitude2 Lower og higher altitude 2
* @param T2 Temperature at altitude 2
* @returns True if inversion is detected
*/
function isTemperatureInversion(altitude1, T1, altitude2, T2) {
const lapseRate = calculateLapseRate(altitude1, T1, altitude2, T2);
return lapseRate > 0;
}
/**
* Adjust pressure to sea level by fixed lapse ratio.
* @param {number} altitude altitude in meters.
* @param {number} temperature temperature at altitude in Celcius|Kelvin|Fahrenheit.
* @param {number} lapseRate Custom lapse rate. Defaults to standard lapse rate.
* @returns Adjusted pressure
*/
function adjustTemperatureByLapseRate(altitude, temperature, lapseRate = constants_1.default.STANDARD_LAPSE_RATE) {
return temperature + lapseRate * altitude;
}
/**
* Convert Kelvin to Celcius
* @param {number} temperature Temperature in K (Kelvin)
* @returns {number} Celcius
*/
function kelvinToCelcius(temperature) {
return temperature - constants_1.default.KELVIN;
}
/**
* Convert Celcius to Kelvin
* @param {number} temperature Temperature in C (Celcius)
* @returns {number} Kelvin
*/
function celciusToKelvin(temperature) {
return roundToTwoDecimals(temperature + constants_1.default.KELVIN);
}
/**
*
* @param celcius Celcius degrees
* @returns Fahrenheit degrees
*/
function celciusToFahrenheit(celcius) {
return (celcius * 9 / 5) + 32;
}
/**
*
* @param fahrenheit Fahrenheit degrees
* @returns Celcius degrees
*/
function fahrenheitToCelcius(fahrenheit) {
return (fahrenheit - 32) * 5 / 9;
}
/**
*
* @param kelvin Kelvin degrees
* @returns Fahrenheit degrees
*/
function kelvinToFahrenheit(kelvin) {
return (kelvin - constants_1.default.KELVIN) * 9 / 5 + 32;
}
/**
*
* @param fahrenheit Fahrenheit degrees
* @returns Kelvin degrees
*/
function fahrenheitToKelvin(fahrenheit) {
return (fahrenheit - 32) * 5 / 9 + constants_1.default.KELVIN;
}
/**
* Round decimal number to two decimals
* @param {number} num Number
* @returns {number} num rounded to two decimals
*/
function roundToTwoDecimals(num) {
return Math.round(num * 100) / 100;
}
/**
* Convert M/S to KM/H
* @param {number} mps Meter Per Second
* @returns {number} KM/H
*/
function meterPerSecondToKilometerPerHour(mps) {
return mps * 3.6;
}
exports.default = {
dewPointMagnusFormula,
dewPointArdenBuckEquation,
windChillIndex,
australianAapparentTemperature,
heatIndex,
heatIndexText,
humidex,
humidexText,
roundToTwoDecimals,
kelvinToCelcius,
celciusToKelvin,
celciusToFahrenheit,
fahrenheitToCelcius,
kelvinToFahrenheit,
fahrenheitToKelvin,
meterPerSecondToKilometerPerHour,
potentialTemperature,
virtualTemperature,
DEW_POINT_VALUATIONS,
calculateLapseRate,
calculateDynamicLapseRate,
calculateWeightedAverageTemperature,
isTemperatureInversion,
adjustTemperatureByLapseRate,
};