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.
- clero-0.1.0/LICENSE +21 -0
- clero-0.1.0/PKG-INFO +224 -0
- clero-0.1.0/README.md +188 -0
- clero-0.1.0/pyproject.toml +46 -0
- clero-0.1.0/setup.cfg +4 -0
- clero-0.1.0/src/clero/__init__.py +21 -0
- clero-0.1.0/src/clero/_checkpoint.py +48 -0
- clero-0.1.0/src/clero/_gplfr_runtime.py +519 -0
- clero-0.1.0/src/clero/_model_bundle/gplfr_state.npz +0 -0
- clero-0.1.0/src/clero/_model_bundle/manifest.json +544 -0
- clero-0.1.0/src/clero/_torch_runtime.py +326 -0
- clero-0.1.0/src/clero/climate_analysis/__init__.py +78 -0
- clero-0.1.0/src/clero/climate_analysis/_io.py +19 -0
- clero-0.1.0/src/clero/climate_analysis/_style.py +32 -0
- clero-0.1.0/src/clero/climate_analysis/diagnostics.py +72 -0
- clero-0.1.0/src/clero/climate_analysis/grid.py +97 -0
- clero-0.1.0/src/clero/climate_analysis/maps.py +334 -0
- clero-0.1.0/src/clero/climate_analysis/profiles.py +203 -0
- clero-0.1.0/src/clero/climate_analysis/scalars.py +34 -0
- clero-0.1.0/src/clero/climate_analysis/summary_stats.py +83 -0
- clero-0.1.0/src/clero/inference.py +214 -0
- clero-0.1.0/src/clero/inputs.py +100 -0
- clero-0.1.0/src/clero.egg-info/PKG-INFO +224 -0
- clero-0.1.0/src/clero.egg-info/SOURCES.txt +29 -0
- clero-0.1.0/src/clero.egg-info/dependency_links.txt +1 -0
- clero-0.1.0/src/clero.egg-info/requires.txt +19 -0
- clero-0.1.0/src/clero.egg-info/top_level.txt +1 -0
- clero-0.1.0/tests/test_climate_analysis.py +208 -0
- clero-0.1.0/tests/test_demos.py +15 -0
- clero-0.1.0/tests/test_orbit.py +21 -0
- 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
|
+

|
|
40
|
+

|
|
41
|
+

|
|
42
|
+

|
|
43
|
+

|
|
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
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
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,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"))
|