nomogram 0.3.0__tar.gz → 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. nomogram-0.5.0/PKG-INFO +199 -0
  2. nomogram-0.5.0/README.md +165 -0
  3. {nomogram-0.3.0 → nomogram-0.5.0}/pyproject.toml +5 -1
  4. {nomogram-0.3.0 → nomogram-0.5.0}/pyproject.toml.orig +10 -1
  5. nomogram-0.5.0/src/nomogram/__init__.py +72 -0
  6. nomogram-0.5.0/src/nomogram/_logo.py +228 -0
  7. {nomogram-0.3.0 → nomogram-0.5.0}/src/nomogram/catalog.py +1525 -169
  8. {nomogram-0.3.0 → nomogram-0.5.0}/src/nomogram/comparison.py +177 -8
  9. nomogram-0.5.0/src/nomogram/data/NOTICE.md +40 -0
  10. nomogram-0.5.0/src/nomogram/data/catalog/atmosphere.toml +1590 -0
  11. nomogram-0.5.0/src/nomogram/data/catalog/core.toml +336 -0
  12. nomogram-0.5.0/src/nomogram/data/catalog/moist-air.toml +2396 -0
  13. {nomogram-0.3.0 → nomogram-0.5.0}/src/nomogram/data/catalog/radio.toml +1 -21
  14. {nomogram-0.3.0 → nomogram-0.5.0}/src/nomogram/data/catalog/sea-water.toml +16 -14
  15. nomogram-0.5.0/src/nomogram/data/catalog/verifications.json +205 -0
  16. {nomogram-0.3.0 → nomogram-0.5.0}/src/nomogram/data/catalog/water-vapour.toml +232 -6
  17. nomogram-0.5.0/src/nomogram/data/semantics.json +1695 -0
  18. {nomogram-0.3.0 → nomogram-0.5.0}/src/nomogram/dimensions.py +126 -26
  19. {nomogram-0.3.0 → nomogram-0.5.0}/src/nomogram/display.py +89 -6
  20. nomogram-0.5.0/src/nomogram/expression.py +725 -0
  21. {nomogram-0.3.0 → nomogram-0.5.0}/src/nomogram/inversion.py +222 -56
  22. {nomogram-0.3.0 → nomogram-0.5.0}/src/nomogram/model.py +561 -41
  23. nomogram-0.5.0/src/nomogram/plot.py +955 -0
  24. {nomogram-0.3.0 → nomogram-0.5.0}/src/nomogram/relations.py +735 -54
  25. {nomogram-0.3.0 → nomogram-0.5.0}/src/nomogram/search.py +158 -37
  26. {nomogram-0.3.0 → nomogram-0.5.0}/src/nomogram/semantics.py +29 -9
  27. nomogram-0.3.0/PKG-INFO +0 -139
  28. nomogram-0.3.0/README.md +0 -105
  29. nomogram-0.3.0/src/nomogram/__init__.py +0 -17
  30. nomogram-0.3.0/src/nomogram/data/NOTICE.md +0 -28
  31. nomogram-0.3.0/src/nomogram/data/catalog/core.toml +0 -184
  32. nomogram-0.3.0/src/nomogram/data/catalog/moist-air.toml +0 -323
  33. nomogram-0.3.0/src/nomogram/data/catalog/verifications.json +0 -133
  34. nomogram-0.3.0/src/nomogram/data/semantics.json +0 -816
  35. nomogram-0.3.0/src/nomogram/expression.py +0 -384
  36. nomogram-0.3.0/src/nomogram/plot.py +0 -237
  37. {nomogram-0.3.0 → nomogram-0.5.0}/LICENSE +0 -0
  38. {nomogram-0.3.0 → nomogram-0.5.0}/src/nomogram/data/catalog/humidity.toml +0 -0
  39. {nomogram-0.3.0 → nomogram-0.5.0}/src/nomogram/py.typed +0 -0
  40. {nomogram-0.3.0 → nomogram-0.5.0}/src/nomogram/units.py +0 -0
@@ -0,0 +1,199 @@
1
+ Metadata-Version: 2.4
2
+ Name: nomogram
3
+ Version: 0.5.0
4
+ Summary: Repository of scientific formulas: notations, units, validity domains
5
+ Keywords: scientific computing,formulas,units,dimensional analysis,physics,thermodynamics,geoscience,atmospheric science,meteorology,hydrology,psychrometrics
6
+ Author: miki4ia
7
+ Author-email: miki4ia <miki4ia@gmail.com>
8
+ License-Expression: BSD-3-Clause AND CC-BY-4.0 AND CC0-1.0
9
+ License-File: LICENSE
10
+ License-File: src/nomogram/data/NOTICE.md
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Scientific/Engineering
19
+ Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
20
+ Classifier: Topic :: Scientific/Engineering :: Hydrology
21
+ Classifier: Topic :: Scientific/Engineering :: Physics
22
+ Classifier: Typing :: Typed
23
+ Requires-Dist: numpy>=2.0
24
+ Requires-Dist: pint>=0.24.4
25
+ Requires-Dist: sympy>=1.13
26
+ Requires-Dist: matplotlib>=3.10.7 ; extra == 'viz'
27
+ Requires-Python: >=3.12
28
+ Project-URL: Homepage, https://github.com/miki4iaml/nomogram
29
+ Project-URL: Repository, https://github.com/miki4iaml/nomogram
30
+ Project-URL: Issues, https://github.com/miki4iaml/nomogram/issues
31
+ Project-URL: Changelog, https://github.com/miki4iaml/nomogram/blob/main/CHANGELOG.md
32
+ Provides-Extra: viz
33
+ Description-Content-Type: text/markdown
34
+
35
+ <p align="center">
36
+ <picture>
37
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/miki4iaml/nomogram/v0.5.0/docs/images/banner-dark.svg">
38
+ <img alt="nomogram" src="https://raw.githubusercontent.com/miki4iaml/nomogram/v0.5.0/docs/images/banner-light.svg" width="480">
39
+ </picture>
40
+ </p>
41
+
42
+ **English** | [Français](https://github.com/miki4iaml/nomogram/blob/v0.5.0/README.fr.md)
43
+
44
+ [![CI](https://github.com/miki4iaml/nomogram/actions/workflows/ci.yml/badge.svg?branch=develop)](https://github.com/miki4iaml/nomogram/actions/workflows/ci.yml)
45
+ [![CodeQL](https://github.com/miki4iaml/nomogram/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/miki4iaml/nomogram/actions/workflows/github-code-scanning/codeql)
46
+ [![codecov](https://codecov.io/gh/miki4iaml/nomogram/graph/badge.svg?branch=develop)](https://codecov.io/gh/miki4iaml/nomogram)
47
+ [![Python](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fmiki4iaml%2Fnomogram%2Fdevelop%2Fpyproject.toml)](https://github.com/miki4iaml/nomogram/blob/v0.5.0/pyproject.toml)
48
+ [![License](https://img.shields.io/github/license/miki4iaml/nomogram)](https://github.com/miki4iaml/nomogram/blob/v0.5.0/LICENSE)
49
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.23373134.svg)](https://doi.org/10.5281/zenodo.23373134)
50
+ [![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
51
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
52
+ [![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
53
+ [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-fe5196)](https://www.conventionalcommits.org/en/v1.0.0/)
54
+
55
+ **Formulas as data, not as code.**
56
+
57
+ nomogram does not hard-code functions: it catalogs formulas, each one
58
+
59
+ - **meaningful**: QUDT quantity kinds and units, links to CF and
60
+ Wikidata, its source, its validity domain when stated;
61
+ - **checked**: dimensions and units on loading, equivalences proved
62
+ symbolically, values cross-checked against references;
63
+ - **ready to use**: compute, compose, invert, plot, and compare with its
64
+ alternatives.
65
+
66
+ Built for trust, not for speed: to show that a calculation is right, and
67
+ where it comes from.
68
+
69
+ Pure Python, built on SymPy, numpy and pint; it works on an offline
70
+ workstation (no online engine).
71
+
72
+ > Early stage project (alpha).
73
+
74
+ ## Installation
75
+
76
+ ```
77
+ pip install nomogram # core
78
+ pip install "nomogram[viz]" # with plots (matplotlib)
79
+ ```
80
+
81
+ ## Installation from source
82
+
83
+ ```
84
+ git clone https://github.com/miki4iaml/nomogram.git
85
+ cd nomogram
86
+ uv sync
87
+ ```
88
+
89
+ Python 3.12 or later. Plots need the `viz` extra (matplotlib):
90
+ `uv sync --extra viz`.
91
+
92
+ ## Example
93
+
94
+ Every saturation vapour pressure formulation of the catalog against the
95
+ IAPWS reference, from 0 to 60 °C and within 1 %; curves stop where a formulation leaves its
96
+ stated validity domain, and the figure takes the option set of nomogram's
97
+ documentation ([script](https://github.com/miki4iaml/nomogram/blob/v0.5.0/docs/examples/saturation_vapour_pressure.py)):
98
+
99
+ ```python
100
+ import numpy as np
101
+ import nomogram as nomo
102
+ from nomogram.units import Q_
103
+
104
+ catalog = nomo.catalog.catalog()
105
+ formulations = catalog.formulations_of("SaturationVapourPressureOverWater")
106
+ reference = catalog.formulations["iapws_sr1_86"]
107
+ temperatures = Q_(np.linspace(-60, 110, 341), "degC")
108
+
109
+ table = nomo.comparison.compare(
110
+ formulations, reference, {"ThermodynamicTemperature": temperatures}
111
+ )
112
+ with nomo.display.display_options(nomo.display.DisplayOptions.DOCUMENTATION):
113
+ nomo.plot.plot_deviations(table, xlim=(0, 60), ylim=(-1.0, 1.0)) # extra viz
114
+ ```
115
+
116
+ ![Deviation of saturation vapour pressure formulations from IAPWS](https://raw.githubusercontent.com/miki4iaml/nomogram/v0.5.0/docs/images/saturation-vapour-pressure.png)
117
+
118
+ ## Usage
119
+
120
+ The table holds the values and the relative deviations, by formulation;
121
+ values outside a formulation's validity domain are NaN unless asked
122
+ otherwise (`outside="ignore"`). Formulations also chain, for instance
123
+ the absolute humidity from any saturation vapour pressure formulation:
124
+
125
+ ```python
126
+ ha = nomo.comparison.compose(
127
+ catalog.formulations["mass_concentration_ideal_gas"], {"e_s": reference}
128
+ )
129
+ ha.evaluate(RH=Q_(100, "percent"), T=Q_(20, "degC")).to("g/m^3") # 17.29
130
+ ```
131
+
132
+ `compare` gives each value to the input of its quantity kind. A
133
+ formulation with several inputs of one kind, such as a scheme
134
+ interpolating between two levels (the target pressure p and the
135
+ pressures p_1 and p_2 of the levels), compares once all but one of them
136
+ are fixed, which `compare` asks for: `partial` fixes inputs as `functools.partial` fixes arguments,
137
+ here the two levels, and leaves a function of p alone, evaluated,
138
+ compared and plotted like any formulation
139
+ ([Humidity between two levels](https://github.com/miki4iaml/nomogram/blob/v0.5.0/docs/vertical.md#humidity-between-two-levels)).
140
+ It also fixes what is known in advance, such as the levels of a sounding,
141
+ and names the curve in legends:
142
+
143
+ ```python
144
+ levels = {"p_1": Q_(850, "hPa"), "p_2": Q_(500, "hPa"),
145
+ "T_1": Q_(278, "K"), "T_2": Q_(252, "K")}
146
+ in_ln_p = nomo.comparison.partial(
147
+ catalog.formulations["log_pressure_interpolation_temperature"],
148
+ label="T linear in ln p", **levels,
149
+ )
150
+ in_ln_p.evaluate(p=Q_(700, "hPa")).to("K") # 268.49
151
+ ```
152
+
153
+ Formulations are found by words, wildcards and fields, whatever their
154
+ notation ([Finding formulations](https://github.com/miki4iaml/nomogram/blob/v0.5.0/docs/search.md)):
155
+
156
+ ```python
157
+ catalog.search(variable="rho*") # ρ in every notation
158
+ catalog.search(kind="VapourPressure", source="buck*")
159
+ ```
160
+
161
+ Each formulation keeps its units, validity domain, temperature scale and
162
+ references. The vocabulary is mapped in [docs/rosetta.md](https://github.com/miki4iaml/nomogram/blob/v0.5.0/docs/rosetta.md);
163
+ [How nomogram earns trust](https://github.com/miki4iaml/nomogram/blob/v0.5.0/docs/approach.md) explains what is checked and
164
+ how, and [Displaying formulas](https://github.com/miki4iaml/nomogram/blob/v0.5.0/docs/display.md) shows formulas in notebooks,
165
+ Markdown and plots. [Sea water](https://github.com/miki4iaml/nomogram/blob/v0.5.0/docs/sea-water.md) shows the salinity
166
+ scales and the vapour pressure of sea water, and what its 2 % lowering
167
+ changes at the sea surface. [The logo](https://github.com/miki4iaml/nomogram/blob/v0.5.0/docs/logo.md) is a working
168
+ nomogram.
169
+
170
+ ## Contributing
171
+
172
+ References to integrate, information or corrections about catalog
173
+ entries, new formulas and code are all welcome: see the
174
+ [ways to contribute](https://github.com/miki4iaml/nomogram/blob/v0.5.0/.github/CONTRIBUTING.md#ways-to-contribute) and
175
+ the [catalog guide](https://github.com/miki4iaml/nomogram/blob/v0.5.0/docs/catalog.md).
176
+
177
+ ## Data sources
178
+
179
+ nomogram's units come from QUDT, and so do its quantity kinds, except
180
+ those QUDT lacks, which nomogram defines as specializations of QUDT
181
+ kinds. nomogram links some kinds to CF standard names, which come, with
182
+ their canonical units, from the CF standard name table. The package
183
+ bundles a subset of each source:
184
+
185
+ - QUDT — Quantities, Units, Dimensions and Types, by QUDT.org
186
+ (<https://qudt.org>), release 3.5.2, licensed under
187
+ [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/);
188
+ - the [CF standard name table](https://cfconventions.org/Data/cf-standard-names/95/build/cf-standard-name-table.html),
189
+ version 95, [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/);
190
+ - the UCUM codes of the units, as QUDT gives them: © Regenstrief
191
+ Institute, Inc., [UCUM License](https://ucum.org/license).
192
+
193
+ What nomogram selects, changes and adds is listed in
194
+ [NOTICE.md](https://github.com/miki4iaml/nomogram/blob/v0.5.0/src/nomogram/data/NOTICE.md).
195
+
196
+ ## License
197
+
198
+ Code: [BSD-3-Clause](https://github.com/miki4iaml/nomogram/blob/v0.5.0/LICENSE). Bundled data: see
199
+ [Data sources](#data-sources).
@@ -0,0 +1,165 @@
1
+ <p align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/miki4iaml/nomogram/v0.5.0/docs/images/banner-dark.svg">
4
+ <img alt="nomogram" src="https://raw.githubusercontent.com/miki4iaml/nomogram/v0.5.0/docs/images/banner-light.svg" width="480">
5
+ </picture>
6
+ </p>
7
+
8
+ **English** | [Français](https://github.com/miki4iaml/nomogram/blob/v0.5.0/README.fr.md)
9
+
10
+ [![CI](https://github.com/miki4iaml/nomogram/actions/workflows/ci.yml/badge.svg?branch=develop)](https://github.com/miki4iaml/nomogram/actions/workflows/ci.yml)
11
+ [![CodeQL](https://github.com/miki4iaml/nomogram/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/miki4iaml/nomogram/actions/workflows/github-code-scanning/codeql)
12
+ [![codecov](https://codecov.io/gh/miki4iaml/nomogram/graph/badge.svg?branch=develop)](https://codecov.io/gh/miki4iaml/nomogram)
13
+ [![Python](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fmiki4iaml%2Fnomogram%2Fdevelop%2Fpyproject.toml)](https://github.com/miki4iaml/nomogram/blob/v0.5.0/pyproject.toml)
14
+ [![License](https://img.shields.io/github/license/miki4iaml/nomogram)](https://github.com/miki4iaml/nomogram/blob/v0.5.0/LICENSE)
15
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.23373134.svg)](https://doi.org/10.5281/zenodo.23373134)
16
+ [![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
17
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
18
+ [![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
19
+ [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-fe5196)](https://www.conventionalcommits.org/en/v1.0.0/)
20
+
21
+ **Formulas as data, not as code.**
22
+
23
+ nomogram does not hard-code functions: it catalogs formulas, each one
24
+
25
+ - **meaningful**: QUDT quantity kinds and units, links to CF and
26
+ Wikidata, its source, its validity domain when stated;
27
+ - **checked**: dimensions and units on loading, equivalences proved
28
+ symbolically, values cross-checked against references;
29
+ - **ready to use**: compute, compose, invert, plot, and compare with its
30
+ alternatives.
31
+
32
+ Built for trust, not for speed: to show that a calculation is right, and
33
+ where it comes from.
34
+
35
+ Pure Python, built on SymPy, numpy and pint; it works on an offline
36
+ workstation (no online engine).
37
+
38
+ > Early stage project (alpha).
39
+
40
+ ## Installation
41
+
42
+ ```
43
+ pip install nomogram # core
44
+ pip install "nomogram[viz]" # with plots (matplotlib)
45
+ ```
46
+
47
+ ## Installation from source
48
+
49
+ ```
50
+ git clone https://github.com/miki4iaml/nomogram.git
51
+ cd nomogram
52
+ uv sync
53
+ ```
54
+
55
+ Python 3.12 or later. Plots need the `viz` extra (matplotlib):
56
+ `uv sync --extra viz`.
57
+
58
+ ## Example
59
+
60
+ Every saturation vapour pressure formulation of the catalog against the
61
+ IAPWS reference, from 0 to 60 °C and within 1 %; curves stop where a formulation leaves its
62
+ stated validity domain, and the figure takes the option set of nomogram's
63
+ documentation ([script](https://github.com/miki4iaml/nomogram/blob/v0.5.0/docs/examples/saturation_vapour_pressure.py)):
64
+
65
+ ```python
66
+ import numpy as np
67
+ import nomogram as nomo
68
+ from nomogram.units import Q_
69
+
70
+ catalog = nomo.catalog.catalog()
71
+ formulations = catalog.formulations_of("SaturationVapourPressureOverWater")
72
+ reference = catalog.formulations["iapws_sr1_86"]
73
+ temperatures = Q_(np.linspace(-60, 110, 341), "degC")
74
+
75
+ table = nomo.comparison.compare(
76
+ formulations, reference, {"ThermodynamicTemperature": temperatures}
77
+ )
78
+ with nomo.display.display_options(nomo.display.DisplayOptions.DOCUMENTATION):
79
+ nomo.plot.plot_deviations(table, xlim=(0, 60), ylim=(-1.0, 1.0)) # extra viz
80
+ ```
81
+
82
+ ![Deviation of saturation vapour pressure formulations from IAPWS](https://raw.githubusercontent.com/miki4iaml/nomogram/v0.5.0/docs/images/saturation-vapour-pressure.png)
83
+
84
+ ## Usage
85
+
86
+ The table holds the values and the relative deviations, by formulation;
87
+ values outside a formulation's validity domain are NaN unless asked
88
+ otherwise (`outside="ignore"`). Formulations also chain, for instance
89
+ the absolute humidity from any saturation vapour pressure formulation:
90
+
91
+ ```python
92
+ ha = nomo.comparison.compose(
93
+ catalog.formulations["mass_concentration_ideal_gas"], {"e_s": reference}
94
+ )
95
+ ha.evaluate(RH=Q_(100, "percent"), T=Q_(20, "degC")).to("g/m^3") # 17.29
96
+ ```
97
+
98
+ `compare` gives each value to the input of its quantity kind. A
99
+ formulation with several inputs of one kind, such as a scheme
100
+ interpolating between two levels (the target pressure p and the
101
+ pressures p_1 and p_2 of the levels), compares once all but one of them
102
+ are fixed, which `compare` asks for: `partial` fixes inputs as `functools.partial` fixes arguments,
103
+ here the two levels, and leaves a function of p alone, evaluated,
104
+ compared and plotted like any formulation
105
+ ([Humidity between two levels](https://github.com/miki4iaml/nomogram/blob/v0.5.0/docs/vertical.md#humidity-between-two-levels)).
106
+ It also fixes what is known in advance, such as the levels of a sounding,
107
+ and names the curve in legends:
108
+
109
+ ```python
110
+ levels = {"p_1": Q_(850, "hPa"), "p_2": Q_(500, "hPa"),
111
+ "T_1": Q_(278, "K"), "T_2": Q_(252, "K")}
112
+ in_ln_p = nomo.comparison.partial(
113
+ catalog.formulations["log_pressure_interpolation_temperature"],
114
+ label="T linear in ln p", **levels,
115
+ )
116
+ in_ln_p.evaluate(p=Q_(700, "hPa")).to("K") # 268.49
117
+ ```
118
+
119
+ Formulations are found by words, wildcards and fields, whatever their
120
+ notation ([Finding formulations](https://github.com/miki4iaml/nomogram/blob/v0.5.0/docs/search.md)):
121
+
122
+ ```python
123
+ catalog.search(variable="rho*") # ρ in every notation
124
+ catalog.search(kind="VapourPressure", source="buck*")
125
+ ```
126
+
127
+ Each formulation keeps its units, validity domain, temperature scale and
128
+ references. The vocabulary is mapped in [docs/rosetta.md](https://github.com/miki4iaml/nomogram/blob/v0.5.0/docs/rosetta.md);
129
+ [How nomogram earns trust](https://github.com/miki4iaml/nomogram/blob/v0.5.0/docs/approach.md) explains what is checked and
130
+ how, and [Displaying formulas](https://github.com/miki4iaml/nomogram/blob/v0.5.0/docs/display.md) shows formulas in notebooks,
131
+ Markdown and plots. [Sea water](https://github.com/miki4iaml/nomogram/blob/v0.5.0/docs/sea-water.md) shows the salinity
132
+ scales and the vapour pressure of sea water, and what its 2 % lowering
133
+ changes at the sea surface. [The logo](https://github.com/miki4iaml/nomogram/blob/v0.5.0/docs/logo.md) is a working
134
+ nomogram.
135
+
136
+ ## Contributing
137
+
138
+ References to integrate, information or corrections about catalog
139
+ entries, new formulas and code are all welcome: see the
140
+ [ways to contribute](https://github.com/miki4iaml/nomogram/blob/v0.5.0/.github/CONTRIBUTING.md#ways-to-contribute) and
141
+ the [catalog guide](https://github.com/miki4iaml/nomogram/blob/v0.5.0/docs/catalog.md).
142
+
143
+ ## Data sources
144
+
145
+ nomogram's units come from QUDT, and so do its quantity kinds, except
146
+ those QUDT lacks, which nomogram defines as specializations of QUDT
147
+ kinds. nomogram links some kinds to CF standard names, which come, with
148
+ their canonical units, from the CF standard name table. The package
149
+ bundles a subset of each source:
150
+
151
+ - QUDT — Quantities, Units, Dimensions and Types, by QUDT.org
152
+ (<https://qudt.org>), release 3.5.2, licensed under
153
+ [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/);
154
+ - the [CF standard name table](https://cfconventions.org/Data/cf-standard-names/95/build/cf-standard-name-table.html),
155
+ version 95, [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/);
156
+ - the UCUM codes of the units, as QUDT gives them: © Regenstrief
157
+ Institute, Inc., [UCUM License](https://ucum.org/license).
158
+
159
+ What nomogram selects, changes and adds is listed in
160
+ [NOTICE.md](https://github.com/miki4iaml/nomogram/blob/v0.5.0/src/nomogram/data/NOTICE.md).
161
+
162
+ ## License
163
+
164
+ Code: [BSD-3-Clause](https://github.com/miki4iaml/nomogram/blob/v0.5.0/LICENSE). Bundled data: see
165
+ [Data sources](#data-sources).
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nomogram"
3
- version = "0.3.0"
3
+ version = "0.5.0"
4
4
  description = "Repository of scientific formulas: notations, units, validity domains"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -120,6 +120,9 @@ fail_under = 80
120
120
  show_missing = true
121
121
  exclude_also = ['if __name__ == "__main__":']
122
122
 
123
+ [tool.ruff.format]
124
+ exclude = ["*.md"]
125
+
123
126
  [tool.ruff.lint]
124
127
  extend-select = [
125
128
  "ARG",
@@ -138,6 +141,7 @@ extend-select = [
138
141
 
139
142
  [tool.codespell]
140
143
  skip = "*.fr.md,uv.lock"
144
+ ignore-words-list = "flate,alph,mesures"
141
145
 
142
146
  [tool.repo-review.ignore]
143
147
  PC110 = "ruff format runs as a local hook through uv run --frozen (single version source: uv.lock)"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nomogram"
3
- version = "0.3.0"
3
+ version = "0.5.0"
4
4
  description = "Repository of scientific formulas: notations, units, validity domains"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -102,6 +102,12 @@ fail_under = 80
102
102
  show_missing = true
103
103
  exclude_also = ['if __name__ == "__main__":']
104
104
 
105
+ [tool.ruff.format]
106
+ # ruff also formats the code blocks of Markdown files when run without
107
+ # arguments; the documents keep their own layout (aligned comments), and
108
+ # tests/test_examples.py reads the values their code comments state.
109
+ exclude = ["*.md"]
110
+
105
111
  [tool.ruff.lint]
106
112
  # On top of ruff's default selection, which Dependabot bumps surface in CI.
107
113
  extend-select = ["ARG", "B", "C4", "PIE", "PT", "PTH", "RET", "T20"]
@@ -114,6 +120,9 @@ extend-select = ["ARG", "B", "C4", "PIE", "PT", "PTH", "RET", "T20"]
114
120
 
115
121
  [tool.codespell]
116
122
  skip = "*.fr.md,uv.lock"
123
+ # Format terms: the Flate filter of PDF, the ALPH chunk of WebP; the
124
+ # French name of the BIPM (Bureau International des Poids et Mesures).
125
+ ignore-words-list = "flate,alph,mesures"
117
126
 
118
127
  [tool.repo-review.ignore]
119
128
  PC110 = "ruff format runs as a local hook through uv run --frozen (single version source: uv.lock)"
@@ -0,0 +1,72 @@
1
+ # SPDX-FileCopyrightText: 2026 Miki
2
+ # SPDX-License-Identifier: BSD-3-Clause
3
+
4
+ """nomogram: a repository of scientific formulas.
5
+
6
+ Submodules are imported on demand, so that ``import nomogram`` stays
7
+ light: :mod:`nomogram.units` (pint), :mod:`nomogram.expression`
8
+ (SymPy), :mod:`nomogram.semantics` (bundled QUDT and CF subsets),
9
+ :mod:`nomogram.model` and :mod:`nomogram.catalog` (formulations),
10
+ :mod:`nomogram.dimensions`, :mod:`nomogram.comparison`,
11
+ :mod:`nomogram.inversion`, :mod:`nomogram.relations`,
12
+ :mod:`nomogram.search`, :mod:`nomogram.display` and :mod:`nomogram.plot`.
13
+
14
+ Each is also an attribute of the package, imported the first time it is
15
+ used (PEP 562)::
16
+
17
+ import nomogram as nomo
18
+
19
+ formulations = nomo.catalog.catalog().formulations_of("VapourPressure")
20
+ """
21
+
22
+ from importlib import import_module
23
+ from importlib.metadata import version
24
+ from typing import TYPE_CHECKING
25
+
26
+ if TYPE_CHECKING:
27
+ from nomogram import catalog as catalog
28
+ from nomogram import comparison as comparison
29
+ from nomogram import dimensions as dimensions
30
+ from nomogram import display as display
31
+ from nomogram import expression as expression
32
+ from nomogram import inversion as inversion
33
+ from nomogram import model as model
34
+ from nomogram import plot as plot
35
+ from nomogram import relations as relations
36
+ from nomogram import search as search
37
+ from nomogram import semantics as semantics
38
+ from nomogram import units as units
39
+
40
+ __all__ = [
41
+ "__version__",
42
+ "catalog",
43
+ "comparison",
44
+ "dimensions",
45
+ "display",
46
+ "expression",
47
+ "inversion",
48
+ "model",
49
+ "plot",
50
+ "relations",
51
+ "search",
52
+ "semantics",
53
+ "units",
54
+ ]
55
+
56
+ #: Submodules reachable as attributes of the package.
57
+ _SUBMODULES = frozenset(__all__) - {"__version__"}
58
+
59
+ __version__ = version("nomogram")
60
+
61
+
62
+ def __getattr__(name: str) -> object:
63
+ """Import a submodule the first time it is used as an attribute."""
64
+ if name in _SUBMODULES:
65
+ return import_module(f"{__name__}.{name}")
66
+ msg = f"module {__name__!r} has no attribute {name!r}"
67
+ raise AttributeError(msg)
68
+
69
+
70
+ def __dir__() -> list[str]:
71
+ """The names of the package, submodules included before their import."""
72
+ return sorted({*globals(), *_SUBMODULES})