diff --git a/README.md b/README.md
index 01b8bd5..8df7dd6 100644
--- a/README.md
+++ b/README.md
@@ -25,8 +25,8 @@ The 'packet', in the terminology of this library, is a map which has bindings
for some of the following keywords:
1. `:absolute-humidity` *grammes per metre3.*
-2. `:actual-vapour-pressure` *not yet implemented*
-1. `:dew-point` *not yet implemented*
+2. `:actual-vapour-pressure` *Pascals*
+1. `:dew-point-celsius`
2. `:pressure-pascals`
3. `:pressure-millibars`
4. `:saturation-vapour-pressure` *Pascals*
diff --git a/src/humidity/actual_vp.clj b/src/humidity/actual_vp.clj
index 0c2ff87..e04fc27 100644
--- a/src/humidity/actual_vp.clj
+++ b/src/humidity/actual_vp.clj
@@ -1,7 +1,8 @@
(ns ^{:author "simon"
- :authority "https://www.weather.gov/media/epz/wxcalc/vaporPressure.pdf"
- :doc "Compute actual vapour pressure."} humidity.actual-vp)
-
+ :authority "https://www.weather.gov/media/epz/wxcalc/vaporPressure.pdf"
+ :doc "Compute actual vapour pressure."}
+ humidity.actual-vp
+ (:require [humidity.utils :refer [expt]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
@@ -23,3 +24,21 @@
;;; 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 ° Celsius."
+ [& {:keys [dew-point-celsius]}]
+ (when dew-point-celsius
+ (* (C 1) (expt (C 2)
+ (/ (* (C 3) dew-point-celsius)
+ (+ (C 4) dew-point-celsius))))))
diff --git a/src/humidity/constants.clj b/src/humidity/constants.clj
index 2173cc6..eaaa48d 100644
--- a/src/humidity/constants.clj
+++ b/src/humidity/constants.clj
@@ -1,4 +1,5 @@
(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)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -30,6 +31,10 @@
"Base of natural logarithms"
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
"Standard atmospheric pressure, in Pascals. I don't know why this isn't
100,000, but it isn't.
diff --git a/src/humidity/core.clj b/src/humidity/core.clj
index aaaa6d3..e9d94a0 100644
--- a/src/humidity/core.clj
+++ b/src/humidity/core.clj
@@ -2,7 +2,9 @@
:author "simon"}
humidity.core
(:require [humidity.absolute :refer [abs-humidity]]
+ [humidity.actual-vp :refer [actual-vapour-pressure-fn]]
[humidity.constants :refer [celsius-offset Pa]]
+ [humidity.dew-point :refer [dew-point-celsius-fn]]
[humidity.relative :refer [rel-humidity]]
[humidity.saturation-vp :refer [saturation-vp]]
[humidity.utils :refer [?assoc]]
@@ -32,7 +34,8 @@
(defn resolve-humidity
"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
temperature-celsius temperature-kelvin volume
wet-bulb-temperature] :as packet}]
@@ -43,13 +46,17 @@
(?assoc packet
:absolute-humidity (when-not absolute-humidity
(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
(if pressure-millibars
(* pressure-millibars 100) Pa))
:pressure-millibars (when-not pressure-millibars
(if pressure-pascals
- (float (/ pressure-pascals 100))
- (float (/ Pa 100))))
+ (float (/ pressure-pascals 100))
+ (float (/ Pa 100))))
:relative-humidity (when-not relative-humidity
(rel-humidity packet))
:saturation-vapour-pressure (when-not saturation-vapour-pressure
diff --git a/src/humidity/dew_point.clj b/src/humidity/dew_point.clj
new file mode 100644
index 0000000..ac97e67
--- /dev/null
+++ b/src/humidity/dew_point.clj
@@ -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 ° Celsius, given temperature
+ in ° 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 ° Celsius, given temperature
+ in ° 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')))))
diff --git a/src/humidity/volume.clj b/src/humidity/volume.clj
index 185365b..bfc827b 100644
--- a/src/humidity/volume.clj
+++ b/src/humidity/volume.clj
@@ -2,7 +2,7 @@
:authority "https://en.wikipedia.org/wiki/Gas_laws"
: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
- 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,
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
@@ -14,7 +14,7 @@
(:require [humidity.constants :refer [celsius-offset Pa]]))
(defn volume-fn
- "Compute the volume, in m3 of a quantity of air which would be
+ "Compute the volume, in m3, of a quantity of air which would be
1m3 at standard temperature and pressure, given temperature in
° Kelvin and pressure in Pascals."
[& {:keys [temperature-kelvin pressure-pascals]}]
diff --git a/test/humidity/core_test.clj b/test/humidity/core_test.clj
index e029375..34e24b9 100644
--- a/test/humidity/core_test.clj
+++ b/test/humidity/core_test.clj
@@ -26,14 +26,16 @@
(deftest resolution-test
(testing "resolution")
- (let [expected {:relative-humidity 100,
- :temperature-celsius 35,
+ (let [expected {:absolute-humidity 3953.618101887827,
:pressure-pascals 101325,
+ :temperature-celsius 35,
+ :dew-point-celsius 35.00000000000001,
:pressure-millibars 1013.25,
- :saturation-vapour-pressure 5622.488734516426,
+ :volume 1.1281347245103424,
:temperature-kelvin 308.15,
- :absolute-humidity 3953.618101887827,
- :volume 1.1281347245103424}
+ :relative-humidity 100,
+ :saturation-vapour-pressure 5622.488734516426,
+ :actual-vapour-pressure 4765.10094206357}
packet {:relative-humidity 100 :temperature-celsius 35}
actual (resolution packet)]
(is (= actual expected))))
diff --git a/test/humidity/dew_point_test.clj b/test/humidity/dew_point_test.clj
index 4bfbe4e..84e9e9c 100644
--- a/test/humidity/dew_point_test.clj
+++ b/test/humidity/dew_point_test.clj
@@ -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)))))