diff --git a/README.md b/README.md
index 62720af..01b8bd5 100644
--- a/README.md
+++ b/README.md
@@ -13,10 +13,8 @@ I'm using this code to explore human survivability given global warming. The fol
3. [Extreme Heat](https://www.journeyman.cc/blog/posts-output/2026-07-31-Extreme-Heat/)
4. [Analysis of 2026 heatwaves UK](https://www.journeyman.cc/blog/posts-output/2026-08-04-Analysis-of-2026-heatwaves-UK/)
-
## Status
-
Alpha-quality code. It more or less works but it isn't at all polished.
## General architecture
@@ -26,16 +24,18 @@ Alpha-quality code. It more or less works but it isn't at all polished.
The 'packet', in the terminology of this library, is a map which has bindings
for some of the following keywords:
-1. `:absolute-humidity`
+1. `:absolute-humidity` *grammes per metre3.*
2. `:actual-vapour-pressure` *not yet implemented*
1. `:dew-point` *not yet implemented*
2. `:pressure-pascals`
3. `:pressure-millibars`
-4. `:saturation-vapour-pressure`
-5. `:relative-humidity`
-6. `:temperature` *assumed to be celsius*
+4. `:saturation-vapour-pressure` *Pascals*
+5. `:relative-humidity` *percentage, 0...100.*
+6. `:temperature` *assumed to be ° Celsius*
7. `:temperature-celsius`
8. `:temperature-kelvin`
+9. `:volume` *in metres3, relative to 1 at standard temperature and pressure.*
+10. `:wet-bulb-temperature` *in ° Celsius.*
These keywords represent variables in the pressure equations, and they are all
to a degree inter-related; so if you have (or can assume) some of them, you can
diff --git a/src/humidity/actual_vp.clj b/src/humidity/actual_vp.clj
index 2776a94..0c2ff87 100644
--- a/src/humidity/actual_vp.clj
+++ b/src/humidity/actual_vp.clj
@@ -1,4 +1,6 @@
-(ns humidity.actual-vp)
+(ns ^{:author "simon"
+ :authority "https://www.weather.gov/media/epz/wxcalc/vaporPressure.pdf"
+ :doc "Compute actual vapour pressure."} humidity.actual-vp)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
diff --git a/src/humidity/core.clj b/src/humidity/core.clj
index ed9700e..aaaa6d3 100644
--- a/src/humidity/core.clj
+++ b/src/humidity/core.clj
@@ -6,6 +6,7 @@
[humidity.relative :refer [rel-humidity]]
[humidity.saturation-vp :refer [saturation-vp]]
[humidity.utils :refer [?assoc]]
+ [humidity.volume :refer [volume-fn]]
[humidity.wet-bulb :as wb]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -33,7 +34,8 @@
"Resolve humidity equations given the data in this packet."
[& {:keys [absolute-humidity pressure-pascals pressure-millibars
relative-humidity saturation-vapour-pressure temperature
- temperature-celsius temperature-kelvin wet-bulb-temperature] :as packet}]
+ temperature-celsius temperature-kelvin volume
+ wet-bulb-temperature] :as packet}]
(println packet)
(if temperature (resolve-humidity
(assoc (dissoc packet :temperature)
@@ -46,7 +48,8 @@
(* pressure-millibars 100) Pa))
:pressure-millibars (when-not pressure-millibars
(if pressure-pascals
- (/ pressure-pascals 100) (/ 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
@@ -59,6 +62,7 @@
(and temperature-celsius
(not temperature-kelvin))
(+ temperature-celsius celsius-offset))
+ :volume (when-not volume (volume-fn packet))
:wet-bulb-temperature (when-not wet-bulb-temperature
(wb/wet-bulb-temperature packet)))))
diff --git a/src/humidity/volume.clj b/src/humidity/volume.clj
new file mode 100644
index 0000000..185365b
--- /dev/null
+++ b/src/humidity/volume.clj
@@ -0,0 +1,22 @@
+(ns ^{:author "simon"
+ :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
+ 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
+ think* I can just take (temperature-kelvin/celsius-offset) as the temperature
+ component.
+
+ Gay-Lussac's law says pretty much the same for pressure, so again *I think* I
+ can take (pressure-pascals/Pa) as the pressure component."} humidity.volume
+ (:require [humidity.constants :refer [celsius-offset Pa]]))
+
+(defn volume-fn
+ "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]}]
+ (when (and temperature-kelvin pressure-pascals)
+ (* (/ temperature-kelvin celsius-offset) (/ pressure-pascals Pa))))
diff --git a/test/humidity/core_test.clj b/test/humidity/core_test.clj
index ef48072..e029375 100644
--- a/test/humidity/core_test.clj
+++ b/test/humidity/core_test.clj
@@ -1,7 +1,7 @@
(ns humidity.core-test
(:require
- [clojure.test :refer [deftest is testing]]
- [humidity.core :refer [resolution]]))
+ [clojure.test :refer [deftest is testing]]
+ [humidity.core :refer [resolution]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
@@ -27,12 +27,13 @@
(deftest resolution-test
(testing "resolution")
(let [expected {:relative-humidity 100,
- :temperature-celsius 35,
- :pressure-pascals 101325,
- :pressure-millibars 4053/4,
- :saturation-vapour-pressure 5622.488734516426,
- :temperature-kelvin 308.15,
- :absolute-humidity 3953.618101887827}
- packet {:relative-humidity 100 :temperature-celsius 35}
- actual (resolution packet)]
- (is (= actual expected))))
+ :temperature-celsius 35,
+ :pressure-pascals 101325,
+ :pressure-millibars 1013.25,
+ :saturation-vapour-pressure 5622.488734516426,
+ :temperature-kelvin 308.15,
+ :absolute-humidity 3953.618101887827,
+ :volume 1.1281347245103424}
+ packet {:relative-humidity 100 :temperature-celsius 35}
+ actual (resolution packet)]
+ (is (= actual expected))))
diff --git a/test/humidity/volume_test.clj b/test/humidity/volume_test.clj
new file mode 100644
index 0000000..1ef5071
--- /dev/null
+++ b/test/humidity/volume_test.clj
@@ -0,0 +1,41 @@
+(ns humidity.volume-test
+ (:require [clojure.test :refer [deftest is testing]]
+ [humidity.constants :refer [celsius-offset Pa]]
+ [humidity.volume :refer [volume-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 relative-humidity-test
+ (testing "definitional"
+ (let [min-expected 0.99
+ max-expected 1.01
+ actual (volume-fn :pressure-pascals Pa :temperature-kelvin celsius-offset)]
+ (is (< min-expected actual max-expected))))
+ (testing "Example from metoffice 26th May data."
+ (let [min-expected 1.14
+ max-expected 1.15
+ actual (volume-fn :pressure-pascals 102600 :temperature-kelvin (+ 35.1 celsius-offset))]
+ (is (< min-expected actual max-expected)))
+ (testing "Missing data returns nil"
+ (let [expected nil
+ actual (volume-fn :pressure-pascals 102600 :temperature-celsius 35.1)]
+ (is (= actual expected))))))