UNPKG

velitherm

Version:

Basic Thermodynamics Equations for Soaring Flight (from velivole.fr/meteo.guru)

463 lines 13.9 kB
/** * velivole.fr/meteo.guru Basic Thermodynamics Equations for Soaring Flight * * Copyright © 2022 Momtchil Momtchev <momtchil@momtchev.com> * * Licensed under the LGPL License, Version 3.0 (the "License") * You may not use this file except in compliance with the License. * You may obtain a copy of the License at: * https://www.gnu.org/licenses/lgpl-3.0.en.html * * All methods use: * * Pressure in hPa * * Temperature in °C * * Height in meters * * Relative humidity in % from 0 to 100 * * Specific humidity in g/kg * * Mixing ratio in g/kg */ export var velitherm = 'velitherm'; /** * Earth's average gravity acceleration (m/s2) * * @const * @type {number} */ export var G = 9.81; /** * The thermal capacity of air (J/kg) * * @const * @type {number} */ export var Cp = 1005; /** * The enthalpy of vaporization of water (J/kg) * * @const * @type {number} */ export var L = 2.5e6; /** * The adiabatic lapse rate of dry air (°C/m) * * @const * @type {number} */ export var gamma = 0.00976; /** * The average sea level pressure (hPa) * * @const * @type {number} */ export var P0 = 1013.25; /** * The temperature of the ICAO standard atmosphere (°C) * * @const * @type {number} */ export var T0 = 15; /** * The specific gas constant of dry air J/(kg*K) * * @const * @type {number} */ export var Rd = 287.058; /** * The specific gas constant of water vapor J/(kg*K) * * @const * @type {number} */ export var Rv = 461.495; /** * Molar mass of dry air kg/mol * * @const * @type {number} */ export var Md = 0.0289652; /** * Molar mass of water vapor kg/mol * * @const * @type {number} */ export var Mv = 0.018016; /** * Universal gas constant J/(kg*mol) * * @const * @type {number} */ export var R = 8.31446; /** * Absolute zero in °C * * @const * @type {number} */ export var K = -273.15; /** * Number of feets in one meter * * @const * @type {number} */ export var feetPerMeter = 3.28084; /** * Altitude from pressure using the barometric formula and ICAO's definition * of standard atmosphere. * * This is a very rough approximation that is an ICAO standard. * It is used when calculating QNH. * It does not take into account the pressure and temperature of the day. * * @param {number} pressure Pressure * @param {number} [pressure0] Optional sea-level pressure of the day * @returns {number} */ export function altitudeFromStandardPressure(pressure, pressure0) { if (pressure0 === void 0) { pressure0 = P0; } return 44330.0 * (1.0 - Math.pow(pressure / pressure0, 1 / 5.255)); } /** * Pressure from altitude using the barometric formula and ICAO's definition * of standard atmosphere. * * This is a very rough approximation that is an ICAO standard. It is used * when calculating QNH. * It does not take into account the pressure and temperature of the day. * * @param {number} height Height * @param {number} [pressure0] Optional sea-level pressure of the day * @returns {number} */ export function pressureFromStandardAltitude(height, pressure0) { if (pressure0 === void 0) { pressure0 = P0; } return pressure0 * Math.pow(1 - height / 44330.0, 5.255); } /** * Altitude from pressure using the hypsometric formula. * * This is a better equation that takes into account the pressure and the * temperature of the day. * It is not a standard and different weather institutions use slightly * different parameters. * It is used when calculating the QFF. * * @param {number} pressure Pressure * @param {number} [pressure0] Optional sea-level pressure of the day * @param {number} [temp] Optional average temperature from the ground * to the given level * @returns {number} */ export function altitudeFromPressure(pressure, pressure0, temp) { if (pressure0 === void 0) { pressure0 = P0; } if (temp === void 0) { temp = T0; } return (Math.pow(pressure0 / pressure, 1.0 / 5.257) - 1) * (temp - K) / 0.0065; } /** * Pressure from altitude using the hypsometric formula. * * This is a better equation that takes into account the pressure and * the temperature of the day. * It is not a standard and different weather institutions use slightly * different parameters. * It is used when calculating the QFF. * * @param {number} height Height * @param {number} [pressure0] Optional sea-level pressure of the day * @param {number} [temp] Optional average temperature from the ground * to the given level * @returns {number} */ export function pressureFromAltitude(height, pressure0, temp) { if (pressure0 === void 0) { pressure0 = P0; } if (temp === void 0) { temp = T0; } return pressure0 * Math.pow(1.0 - 0.0065 * height / (temp - K + 0.0065 * height), 5.257); } /** * (Saturation) Water vapor pressure. * * Clausius–Clapeyron equation - the most fundamental equation in weather * science. * * This is the Magnus-Tetens approximation. * * @param {number} temp Temperature * @returns {number} */ export function waterVaporSaturationPressure(temp) { if (temp === void 0) { temp = T0; } return 6.1078 * Math.exp(17.27 * temp / (temp + 237.3)); } /** * Relative humidity from specific humidity. * * This is from the Magnus-Tetens approximation. * * @param {number} specificHumidity Specific humidity * @param {number} [pressure] Optional pressure * @param {number} [temp] Optional temperature * @returns {number} */ export function relativeHumidity(specificHumidity, pressure, temp) { if (pressure === void 0) { pressure = P0; } if (temp === void 0) { temp = T0; } return specificHumidity / (6.22 * waterVaporSaturationPressure(temp) / pressure); } var Sonntag_1990_b = 17.62; var Sonntag_1990_c = 243.12; /** * Dew point from relative humidity. * * Approximation of the Magnus equation with the Sonntag 1990 coefficients. * * @param {number} relativeHumidity Relative humidity * @param {number} [temp] Optional temperature * @returns {number} */ export function dewPoint(relativeHumidity, temp) { if (temp === void 0) { temp = T0; } var gamma = Math.log(relativeHumidity / 100) + Sonntag_1990_b * temp / (Sonntag_1990_c + temp); return Sonntag_1990_c * gamma / (Sonntag_1990_b - gamma); } /** * Relative humidity from dew point. * * Approximation of the Magnus equation with the Sonntag 1990 coefficients. * * @param {number} dewPoint Relative humidity * @param {number} [temp] Optional temperature * @returns {number} */ export function relativeHumidityFromDewPoint(dewPoint, temp) { if (temp === void 0) { temp = T0; } var gamma = dewPoint * Sonntag_1990_b / (dewPoint + Sonntag_1990_c); return Math.exp(gamma - Sonntag_1990_b * temp / (Sonntag_1990_c + temp)) * 100; } /** * Mixing ratio from specific humidity. * * Analytic equation from the definition. * * @param {number} specificHumidity Specific humidity * @returns {number} */ export function mixingRatio(specificHumidity) { return specificHumidity / (1 - specificHumidity / 1000); } /** * Specific humidity from mixing ratio. * * Analytic equation from the definition. * * @param {number} mixingRatio Mixing ratio * @returns {number} */ export function specificHumidityFromMixingRatio(mixingRatio) { return mixingRatio / (1 + mixingRatio / 1000); } /** * Specific humidity from relative humidity. * * Approximation of the Magnus equation with the Sonntag 1990 coefficients. * * @param {number} relativeHumidity Relative humidity * @param {number} [pressure] Optional pressure * @param {number} [temp] Optional temperature * @returns {number} */ export function specificHumidity(relativeHumidity, pressure, temp) { if (pressure === void 0) { pressure = P0; } if (temp === void 0) { temp = T0; } return relativeHumidity / 100 * (0.622 * waterVaporSaturationPressure(temp) / pressure) * 1000; } /** * Air density. * * Analytic equation from Avogadro's Law. * * @param {number} relativeHumidity Relative humidity * @param {number} [pressure] Optional pressure * @param {number} [temp] Optional temperature * @returns {number} */ export function airDensity(relativeHumidity, pressure, temp) { if (pressure === void 0) { pressure = P0; } if (temp === void 0) { temp = T0; } var Psat = waterVaporSaturationPressure(temp); var Pv = relativeHumidity / 100 * Psat; var Pd = pressure - Pv; return 100 * (Pd * Md + Pv * Mv) / (R * (temp - K)); } /** * Lifted Condensation Level. * * This is the altitude at which a mechanically lifted air parcel from * the ground will condensate. * * It corresponds to the cloud base level when the clouds are formed by * mechanical lifting. * * This approximation is known as the Espy equation with the Stull coefficient. * * @param {number} temp Temperature at 2m * @param {number} dewPoint Dew point at 2m * @returns {number} */ export function LCL(temp, dewPoint) { return 126.7 * (temp - dewPoint); } /** * Moist adiabatic lapse rate from pressure and temperature. * * Copied from Roland Stull, Practical Meteorology * (copylefted, available online). * * Rather complex approximation based on the Magnus-Tetens equation and * the barometric equation. * * @param {number} temp Temperature * @param {number} [pressure] Optional pressure * @returns {number} */ export function gammaMoist(temp, pressure) { if (pressure === void 0) { pressure = P0; } var tK = temp - K; var es = 6.113 * Math.exp(5423 * (-1 / K - 1 / tK)); var rs = 0.622 * es / (pressure - es); var gamma = G * 1e-3 * (1 + 8711 * rs / tK) / (1 + 1.35e7 * rs / (tK * tK)); return gamma; } var HCR = 1.4; /** * Adiabatic expansion rate from pressure change rate. * * This equation allows to calculate the expansion ratio of an air parcel * from the the previous pressure and the new pressure. * * An adiabatic expansion is an isentropic process that is governed by * the Ideal gas law in general and the constant entropy relationship in * particular: * (P / P0) = (V / V0) ^ gamma * Where P=pressure, V=volume, gamma=heat capacity ratio (1.4 for air, * a diatomic gas) * * Analytic equation. * * @param {number} volume0 Old volume * @param {number} pressure New pressure * @param {number} pressure0 Old pressure * @returns {number} */ export function adiabaticExpansion(volume0, pressure, pressure0) { if (pressure0 === void 0) { pressure0 = P0; } return volume0 * Math.pow(pressure0 / pressure, 1 / HCR); } /** * Adiabatic cooling rate from pressure change rate. * * This equation allows to calculate the cooling ratio of an air parcel from the * the previous pressure and the new pressure. * * It is by combining this equation with the barometric equation * that the adiabatic lapse rate of dry air can be obtained. * * An adiabatic expansion is an isentropic process that is governed by * the Ideal gas law in general and the constant entropy relationship * in particular: * (P / P0) = (V / V0) ^ gamma * Where P=pressure, V=volume, gamma=heat capacity ratio (1.4 for air, * a diatomic gas) * * Keep in mind that if you intend to use this method to calculate a rate * relative to height in meters, you will need very precise altitude * calculations for good results. As the dry adiabatic rate is a constant * that does not depend on the temperature or the pressure, most of the time * you will be better off simply using the `gamma` constant. * * https://en.wikipedia.org/wiki/Ideal_gas_law contains a very good * introduction to this subject. * * Analytic equation. * * @example * // Compute the adiabatic cooling per meter * // when rising from 0m AMSL to 100m AMSL starting at 15°C * * const gamma = (15 - velitherm.adiabaticCooling(15, * velitherm.pressureFromStandardAltitude(100), * velitherm.pressureFromStandardAltitude(0)) * ) / 100; * * // It should be very close to the provided constant * assert(Math.abs(gamma - velitherm.gamma) < 1e-5) * * @param {number} temp0 Old temperature * @param {number} pressure New pressure * @param {number} pressure0 Old pressure * @returns {number} */ export function adiabaticCooling(temp0, pressure, pressure0) { if (pressure0 === void 0) { pressure0 = P0; } return (temp0 - K) * Math.pow(pressure / pressure0, (HCR - 1) / HCR) + K; } /** * Convert a Flight Level to pressure * * Flight levels are defined as pressure and not as a fixed * altitude. This means that a flight level can always be converted * to pressure without needing any other information in a fully * deterministic way. * * A flight level of 115 means 11500 feet measured by barometer * using the ICAO standard atmosphere. * * The returned pressure can then be converted to altitude using * any of the above functions, taking into account the MSL pressure and * temperature variations. * * @param {number} FL Flight Level * @returns {number} */ export function pressureFromFL(FL) { return pressureFromStandardAltitude(FL * 100 / feetPerMeter, P0); } /** * Convert pressure to Flight Level. * * Flight levels are defined as pressure and not as a fixed * altitude. This means that a flight level can always be converted * to pressure without needing any other information in a fully * deterministic way. * * A flight level of 115 means 11500 feet measured by barometer * using the ICAO standard atmosphere. * * The returned Flight Level can be a fractional number, this should * be rounded to the closest integer as there are no fractional flight levels. * * @param {number} P pressure * @returns {number} */ export function FLFromPressure(P) { return altitudeFromStandardPressure(P, P0) * feetPerMeter / 100; } //# sourceMappingURL=index.js.map