Added dew point; implemented actual vapour pressure, but don't have test data.

This commit is contained in:
Simon Brooke 2026-08-12 13:02:43 +01:00
parent ce32cd285e
commit 970a320a84
8 changed files with 118 additions and 16 deletions

View file

@ -25,8 +25,8 @@ The 'packet', in the terminology of this library, is a map which has bindings
for some of the following keywords: for some of the following keywords:
1. `:absolute-humidity` *grammes per metre<sup>3</sup>.* 1. `:absolute-humidity` *grammes per metre<sup>3</sup>.*
2. `:actual-vapour-pressure` *not yet implemented* 2. `:actual-vapour-pressure` *Pascals*
1. `:dew-point` *not yet implemented* 1. `:dew-point-celsius`
2. `:pressure-pascals` 2. `:pressure-pascals`
3. `:pressure-millibars` 3. `:pressure-millibars`
4. `:saturation-vapour-pressure` *Pascals* 4. `:saturation-vapour-pressure` *Pascals*

View file

@ -1,7 +1,8 @@
(ns ^{:author "simon" (ns ^{:author "simon"
:authority "https://www.weather.gov/media/epz/wxcalc/vaporPressure.pdf" :authority "https://www.weather.gov/media/epz/wxcalc/vaporPressure.pdf"
:doc "Compute actual vapour pressure."} humidity.actual-vp) :doc "Compute actual vapour pressure."}
humidity.actual-vp
(:require [humidity.utils :refer [expt]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; ;;;
@ -23,3 +24,21 @@
;;; USA. ;;; USA.
;;; ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^:const magic-numbers
"The magic numbers (empirical constants) from the derived expression. It
bothers me that all these formulae depend on 'empirical constants', *and*
that there is no commonality between the empirical constants of different
formulae."
;; 1000 because are units are Pascals
[0 6.11 1000 7.5 237.3])
(def C magic-numbers)
(defn actual-vapour-pressure-fn
"Compute actual vapour pressure in Pascals given dew point in &deg; Celsius."
[& {:keys [dew-point-celsius]}]
(when dew-point-celsius
(* (C 1) (expt (C 2)
(/ (* (C 3) dew-point-celsius)
(+ (C 4) dew-point-celsius))))))

View file

@ -1,4 +1,5 @@
(ns ^{:doc "Constants used in humidity calculations." (ns ^{:doc "Constants used in humidity calculations."
:authority ["https://journals.ametsoc.org/view/journals/apme/35/4/1520-0450_1996_035_0601_imfaos_2_0_co_2.xml"]
:author "simon"} humidity.constants) :author "simon"} humidity.constants)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -30,6 +31,10 @@
"Base of natural logarithms" "Base of natural logarithms"
2.71828) 2.71828)
(def ^:const magnus-coefficients
"See https://www.omnicalculator.com/physics/dew-point#how-to-calculate-dew-point-how-to-calculate-relative-humidity"
{:a 17.625 :b 243.04})
(def ^:const Pa (def ^:const Pa
"Standard atmospheric pressure, in Pascals. I don't know why this isn't "Standard atmospheric pressure, in Pascals. I don't know why this isn't
100,000, but it isn't. 100,000, but it isn't.

View file

@ -2,7 +2,9 @@
:author "simon"} :author "simon"}
humidity.core humidity.core
(:require [humidity.absolute :refer [abs-humidity]] (:require [humidity.absolute :refer [abs-humidity]]
[humidity.actual-vp :refer [actual-vapour-pressure-fn]]
[humidity.constants :refer [celsius-offset Pa]] [humidity.constants :refer [celsius-offset Pa]]
[humidity.dew-point :refer [dew-point-celsius-fn]]
[humidity.relative :refer [rel-humidity]] [humidity.relative :refer [rel-humidity]]
[humidity.saturation-vp :refer [saturation-vp]] [humidity.saturation-vp :refer [saturation-vp]]
[humidity.utils :refer [?assoc]] [humidity.utils :refer [?assoc]]
@ -32,7 +34,8 @@
(defn resolve-humidity (defn resolve-humidity
"Resolve humidity equations given the data in this packet." "Resolve humidity equations given the data in this packet."
[& {:keys [absolute-humidity pressure-pascals pressure-millibars [& {:keys [absolute-humidity actual-vapour-pressure dew-point-celsius
pressure-pascals pressure-millibars
relative-humidity saturation-vapour-pressure temperature relative-humidity saturation-vapour-pressure temperature
temperature-celsius temperature-kelvin volume temperature-celsius temperature-kelvin volume
wet-bulb-temperature] :as packet}] wet-bulb-temperature] :as packet}]
@ -43,6 +46,10 @@
(?assoc packet (?assoc packet
:absolute-humidity (when-not absolute-humidity :absolute-humidity (when-not absolute-humidity
(abs-humidity packet)) (abs-humidity packet))
:actual-vapour-pressure (when-not actual-vapour-pressure
(actual-vapour-pressure-fn packet))
:dew-point-celsius (when-not dew-point-celsius
(dew-point-celsius-fn packet))
:pressure-pascals (when-not pressure-pascals :pressure-pascals (when-not pressure-pascals
(if pressure-millibars (if pressure-millibars
(* pressure-millibars 100) Pa)) (* pressure-millibars 100) Pa))

View file

@ -0,0 +1,34 @@
(ns ^{:author "simon"
:authority ["https://www.omnicalculator.com/physics/dew-point#what-is-dew-point-dew-point-definition"
"https://en.wikipedia.org/wiki/Dew_point#Calculating_the_dew_point"]
:doc "Function to compute the dew point, in &deg; Celsius, given temperature
in &deg; Celsius and relative humidity percentage."} humidity.dew-point
(:require [clojure.math :refer [log]]
[humidity.constants :refer [magnus-coefficients]]))
(def ^:const magic-numbers
"The magic numbers (empirical constants) from the derived expression. It
bothers me that all these formulae depend on 'empirical constants', *and*
that there is no commonality between the empirical constants of different
formulae."
[0 6.11 10 7.5 237.3])
(def C magic-numbers)
(defn- alpha
"See https://www.omnicalculator.com/physics/dew-point#how-to-calculate-dew-point-how-to-calculate-relative-humidity"
[T RH]
(let [a (:a magnus-coefficients)
b (:b magnus-coefficients)]
(+ (log (/ RH 100)) (/ (* a T) (+ b T)))))
;; Ts = (b × α(T,RH)) / (a - α(T,RH))
(defn dew-point-celsius-fn
"Compute and return the dew point, in &deg; Celsius, given temperature
in &deg; Celsius and relative humidity percentage; or `nil` if this data is
not available."
[&{:keys [temperature-celsius relative-humidity]}]
(when (and temperature-celsius relative-humidity)
(let [a' (alpha temperature-celsius relative-humidity)]
(/ (* (:b magnus-coefficients) a')
(- (:a magnus-coefficients) a')))))

View file

@ -2,7 +2,7 @@
:authority "https://en.wikipedia.org/wiki/Gas_laws" :authority "https://en.wikipedia.org/wiki/Gas_laws"
:doc "In computing the relative humidity of air after it has warmed, I need :doc "In computing the relative humidity of air after it has warmed, I need
to account for the change in volume. By Charles' Law, given stable to account for the change in volume. By Charles' Law, given stable
pressure, volume is proportinal to absolute temperature (i.e. V=Tk, where K pressure, volume is proportional to absolute temperature (i.e. V=Tk, where K
is a constant). However, the sources I have read do not give that constant, is a constant). However, the sources I have read do not give that constant,
for air. But seeing what I'm interested in is that amount of air which is for air. But seeing what I'm interested in is that amount of air which is
contained in a cubic metre at standard temperature and pressure, then *I contained in a cubic metre at standard temperature and pressure, then *I
@ -14,7 +14,7 @@
(:require [humidity.constants :refer [celsius-offset Pa]])) (:require [humidity.constants :refer [celsius-offset Pa]]))
(defn volume-fn (defn volume-fn
"Compute the volume, in m<sup>3</sup> of a quantity of air which would be "Compute the volume, in m<sup>3</sup>, of a quantity of air which would be
1m<sup>3</sup> at standard temperature and pressure, given temperature in 1m<sup>3</sup> at standard temperature and pressure, given temperature in
&deg; Kelvin and pressure in Pascals." &deg; Kelvin and pressure in Pascals."
[& {:keys [temperature-kelvin pressure-pascals]}] [& {:keys [temperature-kelvin pressure-pascals]}]

View file

@ -26,14 +26,16 @@
(deftest resolution-test (deftest resolution-test
(testing "resolution") (testing "resolution")
(let [expected {:relative-humidity 100, (let [expected {:absolute-humidity 3953.618101887827,
:temperature-celsius 35,
:pressure-pascals 101325, :pressure-pascals 101325,
:temperature-celsius 35,
:dew-point-celsius 35.00000000000001,
:pressure-millibars 1013.25, :pressure-millibars 1013.25,
:saturation-vapour-pressure 5622.488734516426, :volume 1.1281347245103424,
:temperature-kelvin 308.15, :temperature-kelvin 308.15,
:absolute-humidity 3953.618101887827, :relative-humidity 100,
:volume 1.1281347245103424} :saturation-vapour-pressure 5622.488734516426,
:actual-vapour-pressure 4765.10094206357}
packet {:relative-humidity 100 :temperature-celsius 35} packet {:relative-humidity 100 :temperature-celsius 35}
actual (resolution packet)] actual (resolution packet)]
(is (= actual expected)))) (is (= actual expected))))

View file

@ -1 +1,36 @@
(ns humidity.dew-point-test) (ns humidity.dew-point-test
(:require [clojure.test :refer [deftest is testing]]
[humidity.constants :refer [celsius-offset]]
[humidity.dew-point :refer [dew-point-celsius-fn]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Copyright (C) 2026 Simon Brooke
;;;
;;; This program is free software; you can redistribute it and/or
;;; modify it under the terms of the GNU General Public License
;;; as published by the Free Software Foundation; either version 2
;;; of the License, or (at your option) any later version.
;;;
;;; This program is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with this program; if not, write to the Free Software
;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
;;; USA.
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(deftest dew-point-test
(testing "definitional"
(let [min-expected 34.9
max-expected 35.1
actual (dew-point-celsius-fn :relative-humidity 100
:temperature-celsius 35)]
(is (< min-expected actual max-expected))))
(testing "missing data"
(let [expected nil
actual (dew-point-celsius-fn :relative-humidity 100)]
(is (= expected actual)))))