Tweaking of README, intro, and tests.

This commit is contained in:
Simon Brooke 2026-08-14 13:10:12 +01:00
parent a377b3a845
commit 7bc8a845ab
5 changed files with 227 additions and 27 deletions

View file

@ -12,10 +12,11 @@ I'm using this code to explore human survivability given global warming. The fol
2. [The 'everyone dies' event comes home](https://www.journeyman.cc/blog/posts-output/2026-07-15-Everyone-dies-UK/) 2. [The 'everyone dies' event comes home](https://www.journeyman.cc/blog/posts-output/2026-07-15-Everyone-dies-UK/)
3. [Extreme Heat](https://www.journeyman.cc/blog/posts-output/2026-07-31-Extreme-Heat/) 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/) 4. [Analysis of 2026 heatwaves UK](https://www.journeyman.cc/blog/posts-output/2026-08-04-Analysis-of-2026-heatwaves-UK/)
5. [Exploration of what happened on 26th June this year](https://www.journeyman.cc/blog/posts-output/2026-08-13-Exploration-of-what-happened-on-26th-June-this-year/)
## Status ## Disclaimer
Alpha-quality code. It more or less works but it isn't at all polished. This library is supplied in the hope that it may be useful, but results computed with it should not be taken as authoritative but should be checked against other sources. I do not have enough expertise to guarantee its results are correct.
## General architecture ## General architecture
@ -29,13 +30,13 @@ for some of the following keywords:
1. `:dew-point-celsius` 1. `:dew-point-celsius`
2. `:pressure-pascals` 2. `:pressure-pascals`
3. `:pressure-millibars` 3. `:pressure-millibars`
4. `:saturation-vapour-pressure` *Pascals* 4. `:relative-humidity` *percentage, 0...100.*
5. `:relative-humidity` *percentage, 0...100.* 5. `:saturation-vapour-pressure` *Pascals*
6. `:temperature` *assumed to be ° Celsius* 6. `:temperature` *assumed to be ° Celsius*
7. `:temperature-celsius` 7. `:temperature-celsius`
8. `:temperature-kelvin` 8. `:temperature-kelvin`
9. `:volume` *in metres<sup>3</sup>, relative to 1 at standard temperature and pressure.* 9. `:volume` *in metres<sup>3</sup>, relative to 1 at standard temperature and pressure.*
10. `:wet-bulb-temperature` *in &deg; Celsius.* 10. `:wet-bulb-temperature-celsius` *in &deg; Celsius.*
These keywords represent variables in the pressure equations, and they are all 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 to a degree inter-related; so if you have (or can assume) some of them, you can
@ -52,9 +53,58 @@ humidity.core=> (resolution :relative-humidity 100 :temperature-celsius 35)
{: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} {: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}
``` ```
At present there are no checks to ensure that the packet makes sense. ### Naming
Associated with each variable there is a namespace which contains a function that takes a packet, and, provided that packet contains enough information to compute that variable, returns a numeric value, else `nil`. Associated with each variable there is a namespace of the same name which contains a function, whose name is the name of the variable with `-fn` appended, that takes a packet, and, provided that packet contains enough information to compute that variable, returns a numeric value, else `nil`.
### Validation
A packet may be validated by calling the function `validate`. This recalculates all values in the packet, and, by default, tolerates discrepancies of up to 1%. This tolerance can be varied by binding the dynamic variable `*validation-limit*`. Validation merely checks that the values within the packet are consistent with one another, and does not properly account for pressure, so will not be valid at altitude.
```clojure
humidity.core=> (validate {:absolute-humidity 3953.618101887827,
#_=> :actual-vapour-pressure 4765.10094206357,
#_=> :dew-point-celsius 35.00000000000001,
#_=> :pressure-millibars 1013.25,
#_=> :pressure-pascals 101325,
#_=> :relative-humidity 100,
#_=> :saturation-vapour-pressure 5622.488734516426,
#_=> :temperature-celsius 35,
#_=> :temperature-kelvin 308.15,
#_=> :volume 1.1281347245103424,
#_=> :wet-bulb-temperature-celsius 35.11625928298915}
#_=> )
true
humidity.core=> (validate {:absolute-humidity 3953.618101887827,
#_=> :actual-vapour-pressure 4765.10094206357,
#_=> :dew-point-celsius 35.00000000000001,
#_=> :pressure-millibars 1013.25,
#_=> :pressure-pascals 101325,
#_=> :relative-humidity 95, ;; <-- intentionally wrong
#_=> :saturation-vapour-pressure 5622.488734516426,
#_=> :temperature-celsius 35,
#_=> :temperature-kelvin 308.15,
#_=> :volume 1.1281347245103424,
#_=> :wet-bulb-temperature-celsius 35.11625928298915}
#_=> )
ERROR: key: :dew-point-celsius; a: 35.00000000000001; b 34.07737599732603; errror: 2.857142925262451%.
false
humidity.core=> (binding [*validation-limit* 5]
#_=> (validate {:absolute-humidity 3953.618101887827,
#_=> :actual-vapour-pressure 4765.10094206357,
#_=> :dew-point-celsius 35.00000000000001,
#_=> :pressure-millibars 1013.25,
#_=> :pressure-pascals 101325,
#_=> :relative-humidity 95, ;; <-- intentionally wrong
#_=> :saturation-vapour-pressure 5622.488734516426,
#_=> :temperature-celsius 35,
#_=> :temperature-kelvin 308.15,
#_=> :volume 1.1281347245103424,
#_=> :wet-bulb-temperature-celsius 35.11625928298915}))
true
```
## Developing ## Developing
@ -66,6 +116,8 @@ lein repl
and hook in your preferred editor as appropriate for that editor. and hook in your preferred editor as appropriate for that editor.
Full documentation is available [here](https://www.journeyman.cc/humidity).
## Testing ## Testing
Tests are reasonably complets but all could do with more examples, especially around the crucial 35&deg; C wet bulb area and towards the extremities of normal weather range. If you are contributing additional code, I would appreciate it being accompanied by tests. Tests are reasonably complets but all could do with more examples, especially around the crucial 35&deg; C wet bulb area and towards the extremities of normal weather range. If you are contributing additional code, I would appreciate it being accompanied by tests.

128
doc/intro.md Normal file
View file

@ -0,0 +1,128 @@
# Introduction
## Disclaimer
This library is supplied in the hope that it may be useful, but results computed with it should not be taken as authoritative but should be checked against other sources. I do not have enough expertise to guarantee its results are correct.
## General architecture
### The packet
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 metre<sup>3</sup>.*
2. `:actual-vapour-pressure` *Pascals*
1. `:dew-point-celsius`
2. `:pressure-pascals`
3. `:pressure-millibars`
4. `:relative-humidity` *percentage, 0...100.*
5. `:saturation-vapour-pressure` *Pascals*
6. `:temperature` *assumed to be &deg; Celsius*
7. `:temperature-celsius`
8. `:temperature-kelvin`
9. `:volume` *in metres<sup>3</sup>, relative to 1 at standard temperature and pressure.*
10. `:wet-bulb-temperature-celsius` *in &deg; 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
compute others.
Rather than having a tangle of functions all of which call one another, which seemed likely to result in horrible messes and potential infinite recursion, there is one central function, `humidity.core/resolution`, which takes a partial packet and repeatedly tries to compute the values for more variables until it can compute no more, and then returns the enhanced packet.
Thus:
```clojure
humidity.core=> (use 'humidity.core :reload)
nil
humidity.core=> (resolution :relative-humidity 100 :temperature-celsius 35)
{: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}
```
### Naming
Associated with each variable there is a namespace of the same name which contains a function, whose name is the name of the variable with `-fn` appended, that takes a packet, and, provided that packet contains enough information to compute that variable, returns a numeric value, else `nil`.
### Validation
A packet may be validated by calling the function `validate`. This recalculates all values in the packet, and, by default, tolerates discrepancies of up to 1%. This tolerance can be varied by binding the dynamic variable `*validation-limit*`. Validation merely checks that the values within the packet are consistent with one another, and does not properly account for pressure, so will not be valid at altitude.
```clojure
humidity.core=> (validate {:absolute-humidity 3953.618101887827,
#_=> :actual-vapour-pressure 4765.10094206357,
#_=> :dew-point-celsius 35.00000000000001,
#_=> :pressure-millibars 1013.25,
#_=> :pressure-pascals 101325,
#_=> :relative-humidity 100,
#_=> :saturation-vapour-pressure 5622.488734516426,
#_=> :temperature-celsius 35,
#_=> :temperature-kelvin 308.15,
#_=> :volume 1.1281347245103424,
#_=> :wet-bulb-temperature-celsius 35.11625928298915}
#_=> )
true
humidity.core=> (validate {:absolute-humidity 3953.618101887827,
#_=> :actual-vapour-pressure 4765.10094206357,
#_=> :dew-point-celsius 35.00000000000001,
#_=> :pressure-millibars 1013.25,
#_=> :pressure-pascals 101325,
#_=> :relative-humidity 95, ;; <-- intentionally wrong
#_=> :saturation-vapour-pressure 5622.488734516426,
#_=> :temperature-celsius 35,
#_=> :temperature-kelvin 308.15,
#_=> :volume 1.1281347245103424,
#_=> :wet-bulb-temperature-celsius 35.11625928298915}
#_=> )
ERROR: key: :dew-point-celsius; a: 35.00000000000001; b 34.07737599732603; errror: 2.857142925262451%.
false
humidity.core=> (binding [*validation-limit* 5]
#_=> (validate {:absolute-humidity 3953.618101887827,
#_=> :actual-vapour-pressure 4765.10094206357,
#_=> :dew-point-celsius 35.00000000000001,
#_=> :pressure-millibars 1013.25,
#_=> :pressure-pascals 101325,
#_=> :relative-humidity 95, ;; <-- intentionally wrong
#_=> :saturation-vapour-pressure 5622.488734516426,
#_=> :temperature-celsius 35,
#_=> :temperature-kelvin 308.15,
#_=> :volume 1.1281347245103424,
#_=> :wet-bulb-temperature-celsius 35.11625928298915}))
true
```
## Developing
This is a fairly standard [leiningen](https://leiningen.org/) project. I anticipate that most Clojure developers will have leininen installed, even if they no longer use it much. Start a repl with
```
lein repl
```
and hook in your preferred editor as appropriate for that editor.
Full documentation is available [here](https://www.journeyman.cc/humidity).
## Testing
Tests are reasonably complets but all could do with more examples, especially around the crucial 35&deg; C wet bulb area and towards the extremities of normal weather range. If you are contributing additional code, I would appreciate it being accompanied by tests.
I suggest running tests using
```
lein cloverage
```
As this gives not only pass/fail output, but also test coverage output.
Current test results are [here](cloverage/index.html), for comparison.
## Contributing
Pull requests are welcomed, provided they do not contain AI generated code. I'm not going to waste time debugging Claude's mess, my own is bad enough.
## License
Copyright © 2026 Simon Brooke. Licensed under the GNU General Public License,
version 2.0 or (at your option) any later version.

View file

@ -1,10 +1,10 @@
(defproject humidity "0.1.1-SNAPSHOT" (defproject humidity "0.1.1-SNAPSHOT"
:cloverage {:output "docs/cloverage" :cloverage {:output "docs/humidity/cloverage"
:ns-exclude-regex [#"beowulf\.gendoc" #"beowulf\.scratch"]} :ns-exclude-regex [#"beowulf\.gendoc" #"beowulf\.scratch"]}
:codox {:metadata {:doc "**TODO**: write docs" :codox {:metadata {:doc "**TODO**: write docs"
:doc/format :markdown} :doc/format :markdown}
:output-path "docs/codox" :output-path "docs/humidity/"
:source-uri "https://github.com/simon-brooke/humidity/blob/master/{filepath}#L{line}"} :source-uri "https://git.journeyman.cc/simon/humidity/src/branch/main/{filepath}#L{line}"}
:dependencies [[org.clojure/clojure "1.12.5"] :dependencies [[org.clojure/clojure "1.12.5"]
[io.github.nextjournal/clerk "0.18.1158"]] [io.github.nextjournal/clerk "0.18.1158"]]
:deploy-repositories [["releases" :clojars] :deploy-repositories [["releases" :clojars]
@ -18,4 +18,4 @@
:plugins [[lein-ancient "1.0.0"] :plugins [[lein-ancient "1.0.0"]
[lein-cloverage "1.2.2"] [lein-cloverage "1.2.2"]
[lein-codox "0.10.8"]] [lein-codox "0.10.8"]]
:url "https://git.journeyman.cc/simon/humidity") :url "https://www.journeyman.cc/humidity/")

View file

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

View file

@ -1,5 +1,6 @@
(ns humidity.saturation-vp-test (ns humidity.saturation-vp-test
(:require [clojure.test :refer [deftest is testing]] (:require [clojure.test :refer [deftest is testing]]
[humidity.constants :refer [celsius-offset]]
[humidity.saturation-vapour-pressure :refer [saturation-vapour-pressure-fn]])) [humidity.saturation-vapour-pressure :refer [saturation-vapour-pressure-fn]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -28,4 +29,8 @@
(let [min-expected 5622 (let [min-expected 5622
max-expected 5629 max-expected 5629
actual (saturation-vapour-pressure-fn {:temperature-celsius 35})] actual (saturation-vapour-pressure-fn {:temperature-celsius 35})]
(is (< min-expected actual max-expected))))) (is (< min-expected actual max-expected)))
(let [min-expected 5622
max-expected 5629
actual (saturation-vapour-pressure-fn {:temperature-kelvin (+ celsius-offset 35)})]
(is (< min-expected actual max-expected)))))