clero 0.1.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 (31) hide show
  1. clero-0.1.0/LICENSE +21 -0
  2. clero-0.1.0/PKG-INFO +224 -0
  3. clero-0.1.0/README.md +188 -0
  4. clero-0.1.0/pyproject.toml +46 -0
  5. clero-0.1.0/setup.cfg +4 -0
  6. clero-0.1.0/src/clero/__init__.py +21 -0
  7. clero-0.1.0/src/clero/_checkpoint.py +48 -0
  8. clero-0.1.0/src/clero/_gplfr_runtime.py +519 -0
  9. clero-0.1.0/src/clero/_model_bundle/gplfr_state.npz +0 -0
  10. clero-0.1.0/src/clero/_model_bundle/manifest.json +544 -0
  11. clero-0.1.0/src/clero/_torch_runtime.py +326 -0
  12. clero-0.1.0/src/clero/climate_analysis/__init__.py +78 -0
  13. clero-0.1.0/src/clero/climate_analysis/_io.py +19 -0
  14. clero-0.1.0/src/clero/climate_analysis/_style.py +32 -0
  15. clero-0.1.0/src/clero/climate_analysis/diagnostics.py +72 -0
  16. clero-0.1.0/src/clero/climate_analysis/grid.py +97 -0
  17. clero-0.1.0/src/clero/climate_analysis/maps.py +334 -0
  18. clero-0.1.0/src/clero/climate_analysis/profiles.py +203 -0
  19. clero-0.1.0/src/clero/climate_analysis/scalars.py +34 -0
  20. clero-0.1.0/src/clero/climate_analysis/summary_stats.py +83 -0
  21. clero-0.1.0/src/clero/inference.py +214 -0
  22. clero-0.1.0/src/clero/inputs.py +100 -0
  23. clero-0.1.0/src/clero.egg-info/PKG-INFO +224 -0
  24. clero-0.1.0/src/clero.egg-info/SOURCES.txt +29 -0
  25. clero-0.1.0/src/clero.egg-info/dependency_links.txt +1 -0
  26. clero-0.1.0/src/clero.egg-info/requires.txt +19 -0
  27. clero-0.1.0/src/clero.egg-info/top_level.txt +1 -0
  28. clero-0.1.0/tests/test_climate_analysis.py +208 -0
  29. clero-0.1.0/tests/test_demos.py +15 -0
  30. clero-0.1.0/tests/test_orbit.py +21 -0
  31. clero-0.1.0/tests/test_runtime.py +497 -0
clero-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Edward Stevenson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
clero-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,224 @@
1
+ Metadata-Version: 2.4
2
+ Name: clero
3
+ Version: 0.1.0
4
+ Summary: CLERO: the CLimate Emulator for ROcky exoplanets (inference-only runtime)
5
+ Author-email: "Edward T. Stevenson" <es833@cam.ac.uk>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/edstevenson/clero
8
+ Project-URL: Repository, https://github.com/edstevenson/clero
9
+ Project-URL: Documentation, https://edstevenson.github.io/clero/
10
+ Keywords: exoplanets,climate,emulator,Gaussian process,GCM
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Scientific/Engineering :: Astronomy
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: matplotlib>=3.7
21
+ Requires-Dist: numpy>=2.0
22
+ Requires-Dist: scipy>=1.10
23
+ Provides-Extra: gpu
24
+ Requires-Dist: torch; extra == "gpu"
25
+ Provides-Extra: demos
26
+ Requires-Dist: nbformat; extra == "demos"
27
+ Requires-Dist: nbclient; extra == "demos"
28
+ Requires-Dist: ipykernel; extra == "demos"
29
+ Requires-Dist: jupyter; extra == "demos"
30
+ Provides-Extra: docs
31
+ Requires-Dist: pdoc; extra == "docs"
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest; extra == "dev"
34
+ Requires-Dist: clero[demos]; extra == "dev"
35
+ Dynamic: license-file
36
+
37
+ # CLERO (CLimate Emulator for ROcky exoplanets)
38
+
39
+ ![Tests](https://github.com/edstevenson/clero/actions/workflows/tests.yml/badge.svg)
40
+ ![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)
41
+ ![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)
42
+ ![Docs](https://img.shields.io/badge/docs-pdoc-brightgreen.svg)
43
+ ![arXiv](https://img.shields.io/badge/arXiv-TODO-b31b1b.svg)
44
+
45
+ ## What is CLERO
46
+
47
+ CLERO is an exoGCM emulator that takes a hypothetical planet as input and outputs its 3D steady-state climate. It targets tidally locked ocean-covered rocky planets in or near the habitable zone; see [SCOPE.md](SCOPE.md) for detail on its scope of validity.
48
+
49
+ CLERO computes a probability distribution over climates: `predict` returns its mean (our best point estimate of the climate) and `sample` returns draws from the distribution (see [UNCERTAINTY.md](UNCERTAINTY.md)).
50
+
51
+ CLERO is based on Gaussian-process latent factor regression ([GPLFR](https://github.com/edstevenson/GPLFR)) and is trained on [ThousandWorlds](https://github.com/astroautomata/ThousandWorlds).
52
+
53
+ Worked examples in [demos/](demos/).
54
+
55
+ ## Inputs
56
+
57
+
58
+ | key | unit | notes |
59
+ | ------- | --------------- | -------------------------------------------------------------------------------------------------------- |
60
+ | radius | Earth radii | planet radius |
61
+ | gravity | m/s² | surface gravity |
62
+ | P_rot | days | rotation period (assumed = orbital, tidally locked) |
63
+ | P0 | bar | surface pressure |
64
+ | CO2 | volume fraction | atmospheric CO2 |
65
+ | CH4 | volume fraction | atmospheric CH4 |
66
+ | F_star | W/m² | stellar flux at the planet |
67
+ | T_star | K | stellar effective temperature |
68
+ | GCM | str | climate model the prediction targets (optional, defaults to `"um"`; case-insensitive; see options below) |
69
+
70
+
71
+ `CO2 + CH4 <= 1`; the rest of the atmosphere is N2. The validity ranges from [SCOPE.md](SCOPE.md) are available as `clero.CORE_DOMAIN` and `clero.EXTENDED_DOMAIN`.
72
+
73
+ #### GCM options
74
+
75
+ `um`, `exocam` (recommended — the two high-fidelity targets). `exoplasim` is a lower-fidelity exoGCM. `exocam-pre2022` and `lfric` should be avoided at inference time — `exocam-pre2022` is an older ExoCAM version, and `lfric` has few training simulations.
76
+
77
+ For predictions where self-consistency is important (e.g., spatially resolved plots), use a single emulated GCM. Where self-consistency is not needed (e.g., global means), once can average the emulated UM and ExoCAM predictions; this reduces dependence on either GCM's structural biases.
78
+
79
+ ## Outputs
80
+
81
+
82
+ | fields | unit |
83
+ | ------------------------ | ----- |
84
+ | `surface_temperature` | K |
85
+ | `asr`, `olr` | W/m² |
86
+ | `temperature_0..9` | K |
87
+ | `specific_humidity_0..9` | kg/kg |
88
+ | `cloud_fraction_0..9` | 0–1 |
89
+ | `u_0..9`, `v_0..9` | m/s |
90
+
91
+
92
+ The 3D variables (`temperature`, `specific_humidity`, `cloud_fraction`, `u`, `v`) are given on 10 pressure levels, level 0 closest to the surface and level 9 closest to the top of the atmosphere; `clero.climate_analysis.pressure_levels(P0)` returns the level pressures.
93
+
94
+ ## Install
95
+
96
+ ```bash
97
+ pip install clero
98
+ ```
99
+
100
+ Optional extras:
101
+
102
+ ```bash
103
+ pip install "clero[gpu]" # torch, for CUDA batch prediction
104
+ pip install "clero[demos]" # jupyter, to run the notebooks in demos/
105
+ ```
106
+
107
+
108
+
109
+ ## Quickstart
110
+
111
+ ```python
112
+ from clero import Emulator
113
+
114
+ emu = Emulator()
115
+ inputs = {
116
+ "T_star": 3000.0, # K
117
+ "F_star": 1000.0, # W/m^2
118
+ "radius": 1.0, # Earth radii
119
+ "gravity": 9.8, # m/s^2
120
+ "P_rot": 10.0, # days
121
+ "P0": 1.0, # bar
122
+ "CO2": 4.0e-4, # volume fraction
123
+ "CH4": 0.0, # volume fraction
124
+ "GCM": "um",
125
+ }
126
+
127
+ # predict climate
128
+ mean = emu.predict(inputs) # CLERO's best point estimate of the climate
129
+ samples = emu.sample(inputs, n_samples=100, seed=0) # draws from climate distribution
130
+
131
+ print(mean["surface_temperature"].shape) # (32, 64)
132
+ print(samples["surface_temperature"].shape) # (100, 32, 64)
133
+ ```
134
+
135
+ Don't want to spell out every parameter? Start from a bundled preset and override what you need:
136
+
137
+ ```python
138
+ from clero import EARTH, M_EARTH, TRAPPIST1E
139
+ mean = emu.predict({**EARTH, "CO2": 1.0e-3}) # Earth-like planet, overriden to have 1000 ppm CO2
140
+ mean = emu.predict(M_EARTH) # Earth-like but around a 2600 K M dwarf (self-consistent 5 day rotation period)
141
+ mean = emu.predict({**TRAPPIST1E, "CO2": 0.0, "CH4": 0.0}) # TRAPPIST-1e with an N₂-only atmosphere
142
+ ```
143
+
144
+ There's also a walk-through notebook, [demos/quickstart.ipynb](demos/quickstart.ipynb).
145
+
146
+ ## API
147
+
148
+ Full reference (generated from the docstrings): [https://edstevenson.github.io/clero/](https://edstevenson.github.io/clero/)
149
+
150
+ `Emulator` (prediction):
151
+
152
+
153
+ | call | returns | output type |
154
+ | ----------------------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
155
+ | `predict(inputs)` | predict the climate (mean of climate distribution) | `dict[str, ndarray]`, each dict value is `(32, 64)` |
156
+ | `sample(inputs, n_samples=…)` | draws from climate distribution | `dict[str, ndarray]`, each dict value is `(n_samples, 32, 64)` |
157
+ | `to_physical` / `to_model` | move a field dict between physical and model space (see [UNCERTAINTY.md](UNCERTAINTY.md)) | `dict[str, ndarray]`, shapes preserved |
158
+ | `output_names`, `grid_shape` | the 53 field names; `(32, 64)` | `list[str]`; `tuple[int, int]` |
159
+
160
+
161
+ Top-level helpers:
162
+
163
+
164
+ | name | what |
165
+ | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
166
+ | `EARTH`, `M_EARTH`, `TRAPPIST1E` | preset input dicts (see [Quickstart](#quickstart)) |
167
+ | `CORE_DOMAIN`, `EXTENDED_DOMAIN` | `(low, high)` per input, from [SCOPE.md](SCOPE.md) |
168
+ | `orbital_period(F_star, T_star)` | tidally locked rotation period in days from flux and stellar temperature, via empirical stellar relations; prefer a measured period when available |
169
+
170
+
171
+ `clero.climate_analysis` (helper functions for analyzing climates):
172
+
173
+
174
+ | group | functions |
175
+ | -------------------- | --------------------------------------------------------------------------------------------------------------------------- |
176
+ | scalar summaries | `summarize_outputs`, `summary_table`, `global_mean`, `dayside_mean`, `nightside_mean` |
177
+ | vertical structure | `vertical_profile`, `profile_table`, `profile_stats`, `stack_levels`, `pressure_levels` |
178
+ | physical diagnostics | `water_vapor_path`, `net_toa_radiation`, `ice_fraction`, `bond_albedo` |
179
+ | maps & grids | `surface_map`, `zonal_mean`, `meridional_mean`, `map_records`, `grid_records` |
180
+ | plots | `field_map`, `ice_fraction_map`, `net_radiation_map`, `wind_map`, `wind_streamlines`, `zonal_cross_section`, `plot_profile` |
181
+ | axes & weights | `latitude_centers`, `longitude_centers`, `latitude_edges`, `longitude_edges`, `latitude_weights` |
182
+ | io | `write_csv` |
183
+
184
+
185
+ e.g.,
186
+
187
+ ```python
188
+ from clero.climate_analysis import stack_levels
189
+ climate_mean = emu.predict(inputs)
190
+ T = stack_levels(climate_mean, "temperature") # per-level fields -> one (10, 32, 64) array
191
+ ```
192
+
193
+
194
+
195
+ ## Batches and GPU
196
+
197
+ `predict`/`sample` take a batch directly (list of dicts or a column dict). CPU stays pure NumPy:
198
+
199
+ ```python
200
+ mean, variance = emu.predict(
201
+ inputs_list, # list of input dicts
202
+ space="model",
203
+ return_variance=True,
204
+ fields=["surface_temperature"] # subset of outputs
205
+ )
206
+ ```
207
+
208
+ GPU needs torch with CUDA. Build the emulator with a device and the torch state is cached on first use:
209
+
210
+ ```python
211
+ emu = Emulator(device="cuda")
212
+ samples = emu.sample(inputs_list, n_samples=64)
213
+ ```
214
+
215
+
216
+
217
+ ## Citation
218
+
219
+ If you use CLERO, please cite the paper:
220
+
221
+ ```bibtex
222
+ // TODO: add correct CLERO paper citation here when available
223
+ ```
224
+
clero-0.1.0/README.md ADDED
@@ -0,0 +1,188 @@
1
+ # CLERO (CLimate Emulator for ROcky exoplanets)
2
+
3
+ ![Tests](https://github.com/edstevenson/clero/actions/workflows/tests.yml/badge.svg)
4
+ ![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)
5
+ ![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)
6
+ ![Docs](https://img.shields.io/badge/docs-pdoc-brightgreen.svg)
7
+ ![arXiv](https://img.shields.io/badge/arXiv-TODO-b31b1b.svg)
8
+
9
+ ## What is CLERO
10
+
11
+ CLERO is an exoGCM emulator that takes a hypothetical planet as input and outputs its 3D steady-state climate. It targets tidally locked ocean-covered rocky planets in or near the habitable zone; see [SCOPE.md](SCOPE.md) for detail on its scope of validity.
12
+
13
+ CLERO computes a probability distribution over climates: `predict` returns its mean (our best point estimate of the climate) and `sample` returns draws from the distribution (see [UNCERTAINTY.md](UNCERTAINTY.md)).
14
+
15
+ CLERO is based on Gaussian-process latent factor regression ([GPLFR](https://github.com/edstevenson/GPLFR)) and is trained on [ThousandWorlds](https://github.com/astroautomata/ThousandWorlds).
16
+
17
+ Worked examples in [demos/](demos/).
18
+
19
+ ## Inputs
20
+
21
+
22
+ | key | unit | notes |
23
+ | ------- | --------------- | -------------------------------------------------------------------------------------------------------- |
24
+ | radius | Earth radii | planet radius |
25
+ | gravity | m/s² | surface gravity |
26
+ | P_rot | days | rotation period (assumed = orbital, tidally locked) |
27
+ | P0 | bar | surface pressure |
28
+ | CO2 | volume fraction | atmospheric CO2 |
29
+ | CH4 | volume fraction | atmospheric CH4 |
30
+ | F_star | W/m² | stellar flux at the planet |
31
+ | T_star | K | stellar effective temperature |
32
+ | GCM | str | climate model the prediction targets (optional, defaults to `"um"`; case-insensitive; see options below) |
33
+
34
+
35
+ `CO2 + CH4 <= 1`; the rest of the atmosphere is N2. The validity ranges from [SCOPE.md](SCOPE.md) are available as `clero.CORE_DOMAIN` and `clero.EXTENDED_DOMAIN`.
36
+
37
+ #### GCM options
38
+
39
+ `um`, `exocam` (recommended — the two high-fidelity targets). `exoplasim` is a lower-fidelity exoGCM. `exocam-pre2022` and `lfric` should be avoided at inference time — `exocam-pre2022` is an older ExoCAM version, and `lfric` has few training simulations.
40
+
41
+ For predictions where self-consistency is important (e.g., spatially resolved plots), use a single emulated GCM. Where self-consistency is not needed (e.g., global means), once can average the emulated UM and ExoCAM predictions; this reduces dependence on either GCM's structural biases.
42
+
43
+ ## Outputs
44
+
45
+
46
+ | fields | unit |
47
+ | ------------------------ | ----- |
48
+ | `surface_temperature` | K |
49
+ | `asr`, `olr` | W/m² |
50
+ | `temperature_0..9` | K |
51
+ | `specific_humidity_0..9` | kg/kg |
52
+ | `cloud_fraction_0..9` | 0–1 |
53
+ | `u_0..9`, `v_0..9` | m/s |
54
+
55
+
56
+ The 3D variables (`temperature`, `specific_humidity`, `cloud_fraction`, `u`, `v`) are given on 10 pressure levels, level 0 closest to the surface and level 9 closest to the top of the atmosphere; `clero.climate_analysis.pressure_levels(P0)` returns the level pressures.
57
+
58
+ ## Install
59
+
60
+ ```bash
61
+ pip install clero
62
+ ```
63
+
64
+ Optional extras:
65
+
66
+ ```bash
67
+ pip install "clero[gpu]" # torch, for CUDA batch prediction
68
+ pip install "clero[demos]" # jupyter, to run the notebooks in demos/
69
+ ```
70
+
71
+
72
+
73
+ ## Quickstart
74
+
75
+ ```python
76
+ from clero import Emulator
77
+
78
+ emu = Emulator()
79
+ inputs = {
80
+ "T_star": 3000.0, # K
81
+ "F_star": 1000.0, # W/m^2
82
+ "radius": 1.0, # Earth radii
83
+ "gravity": 9.8, # m/s^2
84
+ "P_rot": 10.0, # days
85
+ "P0": 1.0, # bar
86
+ "CO2": 4.0e-4, # volume fraction
87
+ "CH4": 0.0, # volume fraction
88
+ "GCM": "um",
89
+ }
90
+
91
+ # predict climate
92
+ mean = emu.predict(inputs) # CLERO's best point estimate of the climate
93
+ samples = emu.sample(inputs, n_samples=100, seed=0) # draws from climate distribution
94
+
95
+ print(mean["surface_temperature"].shape) # (32, 64)
96
+ print(samples["surface_temperature"].shape) # (100, 32, 64)
97
+ ```
98
+
99
+ Don't want to spell out every parameter? Start from a bundled preset and override what you need:
100
+
101
+ ```python
102
+ from clero import EARTH, M_EARTH, TRAPPIST1E
103
+ mean = emu.predict({**EARTH, "CO2": 1.0e-3}) # Earth-like planet, overriden to have 1000 ppm CO2
104
+ mean = emu.predict(M_EARTH) # Earth-like but around a 2600 K M dwarf (self-consistent 5 day rotation period)
105
+ mean = emu.predict({**TRAPPIST1E, "CO2": 0.0, "CH4": 0.0}) # TRAPPIST-1e with an N₂-only atmosphere
106
+ ```
107
+
108
+ There's also a walk-through notebook, [demos/quickstart.ipynb](demos/quickstart.ipynb).
109
+
110
+ ## API
111
+
112
+ Full reference (generated from the docstrings): [https://edstevenson.github.io/clero/](https://edstevenson.github.io/clero/)
113
+
114
+ `Emulator` (prediction):
115
+
116
+
117
+ | call | returns | output type |
118
+ | ----------------------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
119
+ | `predict(inputs)` | predict the climate (mean of climate distribution) | `dict[str, ndarray]`, each dict value is `(32, 64)` |
120
+ | `sample(inputs, n_samples=…)` | draws from climate distribution | `dict[str, ndarray]`, each dict value is `(n_samples, 32, 64)` |
121
+ | `to_physical` / `to_model` | move a field dict between physical and model space (see [UNCERTAINTY.md](UNCERTAINTY.md)) | `dict[str, ndarray]`, shapes preserved |
122
+ | `output_names`, `grid_shape` | the 53 field names; `(32, 64)` | `list[str]`; `tuple[int, int]` |
123
+
124
+
125
+ Top-level helpers:
126
+
127
+
128
+ | name | what |
129
+ | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
130
+ | `EARTH`, `M_EARTH`, `TRAPPIST1E` | preset input dicts (see [Quickstart](#quickstart)) |
131
+ | `CORE_DOMAIN`, `EXTENDED_DOMAIN` | `(low, high)` per input, from [SCOPE.md](SCOPE.md) |
132
+ | `orbital_period(F_star, T_star)` | tidally locked rotation period in days from flux and stellar temperature, via empirical stellar relations; prefer a measured period when available |
133
+
134
+
135
+ `clero.climate_analysis` (helper functions for analyzing climates):
136
+
137
+
138
+ | group | functions |
139
+ | -------------------- | --------------------------------------------------------------------------------------------------------------------------- |
140
+ | scalar summaries | `summarize_outputs`, `summary_table`, `global_mean`, `dayside_mean`, `nightside_mean` |
141
+ | vertical structure | `vertical_profile`, `profile_table`, `profile_stats`, `stack_levels`, `pressure_levels` |
142
+ | physical diagnostics | `water_vapor_path`, `net_toa_radiation`, `ice_fraction`, `bond_albedo` |
143
+ | maps & grids | `surface_map`, `zonal_mean`, `meridional_mean`, `map_records`, `grid_records` |
144
+ | plots | `field_map`, `ice_fraction_map`, `net_radiation_map`, `wind_map`, `wind_streamlines`, `zonal_cross_section`, `plot_profile` |
145
+ | axes & weights | `latitude_centers`, `longitude_centers`, `latitude_edges`, `longitude_edges`, `latitude_weights` |
146
+ | io | `write_csv` |
147
+
148
+
149
+ e.g.,
150
+
151
+ ```python
152
+ from clero.climate_analysis import stack_levels
153
+ climate_mean = emu.predict(inputs)
154
+ T = stack_levels(climate_mean, "temperature") # per-level fields -> one (10, 32, 64) array
155
+ ```
156
+
157
+
158
+
159
+ ## Batches and GPU
160
+
161
+ `predict`/`sample` take a batch directly (list of dicts or a column dict). CPU stays pure NumPy:
162
+
163
+ ```python
164
+ mean, variance = emu.predict(
165
+ inputs_list, # list of input dicts
166
+ space="model",
167
+ return_variance=True,
168
+ fields=["surface_temperature"] # subset of outputs
169
+ )
170
+ ```
171
+
172
+ GPU needs torch with CUDA. Build the emulator with a device and the torch state is cached on first use:
173
+
174
+ ```python
175
+ emu = Emulator(device="cuda")
176
+ samples = emu.sample(inputs_list, n_samples=64)
177
+ ```
178
+
179
+
180
+
181
+ ## Citation
182
+
183
+ If you use CLERO, please cite the paper:
184
+
185
+ ```bibtex
186
+ // TODO: add correct CLERO paper citation here when available
187
+ ```
188
+
@@ -0,0 +1,46 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "clero"
7
+ version = "0.1.0"
8
+ description = "CLERO: the CLimate Emulator for ROcky exoplanets (inference-only runtime)"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = {text = "MIT"}
12
+ authors = [{name = "Edward T. Stevenson", email = "es833@cam.ac.uk"}]
13
+ keywords = ["exoplanets", "climate", "emulator", "Gaussian process", "GCM"]
14
+ classifiers = [
15
+ "License :: OSI Approved :: MIT License",
16
+ "Programming Language :: Python :: 3.10",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Topic :: Scientific/Engineering :: Astronomy",
21
+ ]
22
+ dependencies = ["matplotlib>=3.7", "numpy>=2.0", "scipy>=1.10"]
23
+
24
+ [project.urls]
25
+ Homepage = "https://github.com/edstevenson/clero"
26
+ Repository = "https://github.com/edstevenson/clero"
27
+ Documentation = "https://edstevenson.github.io/clero/"
28
+
29
+ [project.optional-dependencies]
30
+ gpu = ["torch"]
31
+ demos = ["nbformat", "nbclient", "ipykernel", "jupyter"]
32
+ docs = ["pdoc"]
33
+ dev = ["pytest", "clero[demos]"]
34
+
35
+ [tool.setuptools.packages.find]
36
+ where = ["src"]
37
+ include = ["clero*"]
38
+
39
+ [tool.setuptools.package-data]
40
+ clero = ["_model_bundle/*"]
41
+
42
+ [tool.ruff]
43
+ line-length = 140
44
+
45
+ [tool.ruff.lint]
46
+ select = ["E", "F", "I"]
clero-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,21 @@
1
+ """the **CL**imate **E**mulator for **RO**cky exoplanets.
2
+
3
+ CLERO takes a hypothetical planet and returns its 3D steady-state climate as 53 fields on a
4
+ 32×64 latitude-longitude grid. It targets tidally locked ocean-covered rocky planets in or
5
+ near the habitable zone. See the [README](https://github.com/edstevenson/clero#readme) for
6
+ an introduction.
7
+ """
8
+
9
+ from importlib.metadata import version
10
+
11
+ from . import climate_analysis, inputs
12
+ from .inference import Emulator
13
+ from .inputs import CORE_DOMAIN, EARTH, EXTENDED_DOMAIN, M_EARTH, TRAPPIST1E, orbital_period
14
+
15
+ __version__ = version("clero")
16
+
17
+ __all__ = [ # order is the order on the documentation page
18
+ "Emulator",
19
+ "climate_analysis",
20
+ "inputs",
21
+ ]
@@ -0,0 +1,48 @@
1
+ """Private bundle container helpers for CLERO inference."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ import numpy as np
10
+
11
+ _GPLFR_REQUIRED_FILES = ("manifest.json", "gplfr_state.npz")
12
+ _DEFAULT_CHECKPOINT = Path(__file__).resolve().parent / "_model_bundle"
13
+
14
+
15
+ class _CheckpointBundle:
16
+ def __init__(self, root: Path, manifest: dict[str, Any], gplfr_state: dict[str, np.ndarray]):
17
+ self.root = root
18
+ self.manifest = manifest
19
+ self.gplfr_state = gplfr_state
20
+
21
+ @property
22
+ def input_names(self) -> list[str]:
23
+ return list(self.manifest["input_names"])
24
+
25
+ @property
26
+ def output_names(self) -> list[str]:
27
+ return list(self.manifest["output_names"])
28
+
29
+
30
+ def _load_npz(path: Path) -> dict[str, np.ndarray]:
31
+ with np.load(path, allow_pickle=False) as data:
32
+ return {key: np.asarray(data[key]) for key in data.files}
33
+
34
+
35
+ def _load_checkpoint(path: str | Path | None = None) -> _CheckpointBundle:
36
+ root = _DEFAULT_CHECKPOINT if path is None else Path(path)
37
+ if root.is_file():
38
+ root = root.parent
39
+ manifest_path = root / "manifest.json"
40
+ if not manifest_path.exists():
41
+ raise FileNotFoundError(manifest_path)
42
+ manifest = json.loads(manifest_path.read_text())
43
+ if manifest.get("model_type") != "gplfr_map":
44
+ raise ValueError("CLERO currently supports only gplfr_map bundles")
45
+ for name in _GPLFR_REQUIRED_FILES:
46
+ if not (root / name).exists():
47
+ raise FileNotFoundError(root / name)
48
+ return _CheckpointBundle(root=root, manifest=manifest, gplfr_state=_load_npz(root / "gplfr_state.npz"))