pyepwmorph 2.2.0__tar.gz → 3.0.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.
- {pyepwmorph-2.2.0 → pyepwmorph-3.0.0}/PKG-INFO +45 -48
- {pyepwmorph-2.2.0 → pyepwmorph-3.0.0}/README.md +38 -39
- pyepwmorph-3.0.0/pyepwmorph/__init__.py +8 -0
- {pyepwmorph-2.2.0 → pyepwmorph-3.0.0}/pyepwmorph/models/access.py +13 -19
- {pyepwmorph-2.2.0 → pyepwmorph-3.0.0}/pyepwmorph/models/assemble.py +3 -6
- {pyepwmorph-2.2.0 → pyepwmorph-3.0.0}/pyepwmorph/models/coordinate.py +7 -38
- {pyepwmorph-2.2.0 → pyepwmorph-3.0.0}/pyepwmorph/models/custom.py +0 -1
- pyepwmorph-3.0.0/pyepwmorph/morph/procedures.py +561 -0
- pyepwmorph-3.0.0/pyepwmorph/tools/cache.py +324 -0
- {pyepwmorph-2.2.0 → pyepwmorph-3.0.0}/pyepwmorph/tools/configuration.py +51 -18
- {pyepwmorph-2.2.0 → pyepwmorph-3.0.0}/pyepwmorph/tools/io.py +47 -27
- pyepwmorph-3.0.0/pyepwmorph/tools/psychrometrics.py +185 -0
- pyepwmorph-3.0.0/pyepwmorph/tools/solar.py +268 -0
- pyepwmorph-3.0.0/pyepwmorph/tools/utilities.py +407 -0
- pyepwmorph-3.0.0/pyepwmorph/tools/workflow.py +448 -0
- pyepwmorph-3.0.0/pyproject.toml +93 -0
- pyepwmorph-2.2.0/pyepwmorph/__init__.py +0 -8
- pyepwmorph-2.2.0/pyepwmorph/morph/procedures.py +0 -563
- pyepwmorph-2.2.0/pyepwmorph/tools/cache.py +0 -271
- pyepwmorph-2.2.0/pyepwmorph/tools/ladybug_psychrometrics.py +0 -531
- pyepwmorph-2.2.0/pyepwmorph/tools/solar.py +0 -326
- pyepwmorph-2.2.0/pyepwmorph/tools/utilities.py +0 -455
- pyepwmorph-2.2.0/pyepwmorph/tools/workflow.py +0 -569
- pyepwmorph-2.2.0/pyproject.toml +0 -66
- {pyepwmorph-2.2.0 → pyepwmorph-3.0.0}/.gitignore +0 -0
- {pyepwmorph-2.2.0 → pyepwmorph-3.0.0}/LICENSE +0 -0
- {pyepwmorph-2.2.0 → pyepwmorph-3.0.0}/pyepwmorph/models/__init__.py +0 -0
- {pyepwmorph-2.2.0 → pyepwmorph-3.0.0}/pyepwmorph/morph/__init__.py +0 -0
- {pyepwmorph-2.2.0 → pyepwmorph-3.0.0}/pyepwmorph/tools/__init__.py +0 -0
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyepwmorph
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: A python package to enable simple and easy gathering of climate model data and morphing of EPW files
|
|
5
5
|
Project-URL: Homepage, https://github.com/justinfmccarty/pyepwmorph
|
|
6
6
|
Project-URL: Issues, https://github.com/justinfmccarty/pyepwmorph/issues
|
|
7
|
+
Project-URL: Changelog, https://github.com/justinfmccarty/pyepwmorph/blob/main/CHANGELOG.md
|
|
7
8
|
Author-email: Justin McCarty <mccarty.justin.f@gmail.com>
|
|
8
9
|
License: MIT
|
|
9
10
|
License-File: LICENSE
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
12
|
Classifier: License :: OSI Approved :: MIT License
|
|
11
13
|
Classifier: Operating System :: OS Independent
|
|
12
14
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -14,21 +16,17 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
14
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
|
|
17
21
|
Requires-Python: >=3.9
|
|
18
22
|
Requires-Dist: dask>=2023.5.0
|
|
19
|
-
Requires-Dist: distributed>=2023.5.0
|
|
20
23
|
Requires-Dist: gcsfs>=2023.5.0
|
|
21
24
|
Requires-Dist: intake-esm>=2023.6.0
|
|
22
25
|
Requires-Dist: intake>=0.6.0
|
|
23
|
-
Requires-Dist:
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist: meteocalc>=1.1.0
|
|
26
|
-
Requires-Dist: numpy>=1.20.0
|
|
27
|
-
Requires-Dist: pandas>=1.3.0
|
|
26
|
+
Requires-Dist: numpy>=1.24
|
|
27
|
+
Requires-Dist: pandas>=2.2
|
|
28
28
|
Requires-Dist: pvlib<1.0.0,>=0.13.0
|
|
29
29
|
Requires-Dist: pyarrow>=13.0.0
|
|
30
|
-
Requires-Dist: skyfield>=1.40
|
|
31
|
-
Requires-Dist: timezonefinder>=6.0.0
|
|
32
30
|
Requires-Dist: xarray>=2022.3.0
|
|
33
31
|
Provides-Extra: dev
|
|
34
32
|
Requires-Dist: build>=0.10; extra == 'dev'
|
|
@@ -43,7 +41,7 @@ Description-Content-Type: text/markdown
|
|
|
43
41
|
|
|
44
42
|
[](LICENSE)
|
|
45
43
|
[](https://www.python.org/downloads/)
|
|
46
|
-
[](https://pypi.org/project/pyepwmorph/)
|
|
47
45
|
|
|
48
46
|
A Python package for morphing EnergyPlus Weather (EPW) files with climate model data. Supports CMIP6 projections from Google Cloud and custom CSV-based model data for both future and historical scenarios.
|
|
49
47
|
|
|
@@ -128,19 +126,21 @@ results = workflow.morphing_workflow(
|
|
|
128
126
|
|
|
129
127
|
Custom CSVs should have a `date` column (parseable by pandas) and a column named after the CMIP6 variable (e.g. `tas`, `tasmax`). Rows should be monthly.
|
|
130
128
|
|
|
129
|
+
The reference and target scenarios must cover **different years**, the same way the CMIP6 `historical` and `sspXXX` experiments do. The two series are concatenated before the baseline and target periods are sliced out, so overlapping years get averaged together and weaken the climate signal. Keep `baseline_range` inside the years the reference scenario covers.
|
|
130
|
+
|
|
131
131
|
## Climate scenarios
|
|
132
132
|
|
|
133
|
-
| Scenario
|
|
134
|
-
|
|
135
|
-
| Best Case Scenario
|
|
136
|
-
| Middle of the Road
|
|
137
|
-
| Upper Middle Scenario | ssp370 | Regional rivalry, slow convergence
|
|
138
|
-
| Worst Case Scenario
|
|
133
|
+
| Scenario | SSP | Description | Expected warming |
|
|
134
|
+
| --------------------- | ------ | --------------------------------------- | ---------------- |
|
|
135
|
+
| Best Case Scenario | ssp126 | Strong mitigation, renewable transition | ~1.8 C by 2100 |
|
|
136
|
+
| Middle of the Road | ssp245 | Moderate mitigation efforts | ~2.7 C by 2100 |
|
|
137
|
+
| Upper Middle Scenario | ssp370 | Regional rivalry, slow convergence | ~3.6 C by 2100 |
|
|
138
|
+
| Worst Case Scenario | ssp585 | Fossil-fueled development | ~4.4 C by 2100 |
|
|
139
139
|
|
|
140
140
|
## Morphing variables
|
|
141
141
|
|
|
142
142
|
- **Temperature** -- dry bulb temperature (shift + stretch)
|
|
143
|
-
- **Humidity** -- relative humidity
|
|
143
|
+
- **Humidity** -- relative humidity, stretched in specific humidity space
|
|
144
144
|
- **Pressure** -- atmospheric pressure (shift)
|
|
145
145
|
- **Wind** -- wind speed (stretch)
|
|
146
146
|
- **Clouds and Radiation** -- global/diffuse/direct radiation and sky cover
|
|
@@ -148,14 +148,16 @@ Custom CSVs should have a `date` column (parseable by pandas) and a column named
|
|
|
148
148
|
|
|
149
149
|
### Variable dependencies
|
|
150
150
|
|
|
151
|
-
Some variables
|
|
151
|
+
Some variables cannot be morphed on their own:
|
|
152
152
|
|
|
153
153
|
- **Humidity** requires Temperature and Pressure
|
|
154
154
|
- **Dew Point** requires Temperature, Humidity, and Pressure
|
|
155
155
|
|
|
156
|
+
Dependencies are added automatically and are **written to the output file**. Asking for `Dew Point` alone therefore returns an EPW with morphed pressure, temperature, relative humidity, and dew point, which keeps the file internally consistent. `MorphConfig.resolved_variables` shows exactly what will be written, in the order it is computed.
|
|
157
|
+
|
|
156
158
|
## Caching
|
|
157
159
|
|
|
158
|
-
Climate model data is cached locally after the first download
|
|
160
|
+
Climate model data is cached locally after the first download, and the cache is consulted before the remote catalogue is opened so a hit costs no network traffic.
|
|
159
161
|
|
|
160
162
|
```python
|
|
161
163
|
import pyepwmorph.models.access as access
|
|
@@ -164,6 +166,13 @@ stats = access.get_cmip6_cache_stats()
|
|
|
164
166
|
access.clear_cmip6_cache()
|
|
165
167
|
```
|
|
166
168
|
|
|
169
|
+
Cache entries live in the per-user cache directory (`~/Library/Caches/pyepwmorph` on macOS, `~/.cache/pyepwmorph` on Linux, `%LOCALAPPDATA%\pyepwmorph` on Windows) and are keyed by location, pathway, variable, model sources, and time slices. Two environment variables override the defaults:
|
|
170
|
+
|
|
171
|
+
| Variable | Purpose | Default |
|
|
172
|
+
| ------------------------- | ------------------------------ | ------- |
|
|
173
|
+
| `PYEPWMORPH_CACHE_DIR` | Where cache files are written | per-user cache directory |
|
|
174
|
+
| `PYEPWMORPH_CACHE_MAX_MB` | Size cap before old files go | 500 |
|
|
175
|
+
|
|
167
176
|
## Available climate models
|
|
168
177
|
|
|
169
178
|
```python
|
|
@@ -179,51 +188,39 @@ git clone https://github.com/justinfmccarty/pyepwmorph.git
|
|
|
179
188
|
cd pyepwmorph
|
|
180
189
|
uv sync --extra dev
|
|
181
190
|
|
|
182
|
-
# Run tests
|
|
183
|
-
pytest
|
|
191
|
+
# Run tests (fully offline)
|
|
192
|
+
uv run pytest
|
|
184
193
|
|
|
185
194
|
# Run with coverage
|
|
186
|
-
pytest --cov=pyepwmorph
|
|
195
|
+
uv run pytest --cov=pyepwmorph
|
|
196
|
+
|
|
197
|
+
# Lint
|
|
198
|
+
uv run ruff check pyepwmorph tests gui
|
|
187
199
|
```
|
|
188
200
|
|
|
189
201
|
### Releases
|
|
202
|
+
|
|
190
203
|
```bash
|
|
191
204
|
./release.sh [patch|minor|major]
|
|
192
205
|
```
|
|
193
206
|
|
|
194
|
-
|
|
195
|
-
```bash
|
|
196
|
-
gh release create v2.2.0 \
|
|
197
|
-
--title "v2.2.0" \
|
|
198
|
-
--notes "## What's new
|
|
207
|
+
The script refuses to run on a dirty tree, off `main`, with failing lint or tests, or without a matching `CHANGELOG.md` section. It bumps the version, tags, and pushes; creating the GitHub Release then triggers the PyPI publish workflow.
|
|
199
208
|
|
|
200
|
-
|
|
209
|
+
```bash
|
|
210
|
+
gh release create v3.0.0 \
|
|
211
|
+
--title "v3.0.0" \
|
|
212
|
+
--notes "See CHANGELOG.md."
|
|
201
213
|
```
|
|
202
214
|
|
|
215
|
+
## Changes
|
|
203
216
|
|
|
204
|
-
|
|
205
|
-
## Breaking changes in v2.0.0
|
|
206
|
-
|
|
207
|
-
- **License changed** from GPL-3.0 to MIT.
|
|
208
|
-
- **`future_years`** parameter renamed to **`target_years`** across the API.
|
|
209
|
-
The old name is still accepted with a deprecation warning.
|
|
210
|
-
- **`MorphConfig`** accepts new parameters: `data_source`, `custom_data`,
|
|
211
|
-
`reference_scenario`, and `target_years`.
|
|
212
|
-
- **EPW I/O** (`pyepwmorph.tools.io`) rewritten with stricter validation.
|
|
213
|
-
Files that are not exactly 8760 data rows will now raise `ValueError`.
|
|
214
|
-
The lat/lon parsing bug in the standalone `epw_location()` function has
|
|
215
|
-
been fixed.
|
|
216
|
-
- **`coordinate_cmip6_data`** now accepts an optional `time_slices` dict
|
|
217
|
-
for custom temporal bounds instead of hardcoded 1960-2014 / 2015-2100.
|
|
218
|
-
- **Removed** `requirements.txt`, `environment.yml`, `.bumpversion.cfg`.
|
|
219
|
-
Use `uv sync` or `pip install .` instead.
|
|
220
|
-
- **Per-module `__version__`** strings removed. Use
|
|
221
|
-
`pyepwmorph.__version__` or `importlib.metadata.version("pyepwmorph")`.
|
|
217
|
+
See [CHANGELOG.md](CHANGELOG.md) for the full history. The most recent release corrects several morphing calculations, so morphed humidity, dew point, wind speed, cloud cover, and direct/diffuse radiation all differ from files produced by earlier versions.
|
|
222
218
|
|
|
223
219
|
## Requirements
|
|
224
220
|
|
|
225
221
|
- Python >= 3.9
|
|
226
|
-
-
|
|
222
|
+
- pandas >= 2.2
|
|
223
|
+
- Internet connection (for CMIP6 data download; the custom CSV workflow runs offline)
|
|
227
224
|
|
|
228
225
|
## License
|
|
229
226
|
|
|
@@ -233,6 +230,6 @@ MIT License. See [LICENSE](LICENSE).
|
|
|
233
230
|
|
|
234
231
|
```text
|
|
235
232
|
McCarty, J. (2026). pyepwmorph: A Python package for climate-informed
|
|
236
|
-
EPW file morphing. Version
|
|
233
|
+
EPW file morphing. Version 3.0.0.
|
|
237
234
|
https://github.com/justinfmccarty/pyepwmorph
|
|
238
235
|
```
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](LICENSE)
|
|
4
4
|
[](https://www.python.org/downloads/)
|
|
5
|
-
[](https://pypi.org/project/pyepwmorph/)
|
|
6
6
|
|
|
7
7
|
A Python package for morphing EnergyPlus Weather (EPW) files with climate model data. Supports CMIP6 projections from Google Cloud and custom CSV-based model data for both future and historical scenarios.
|
|
8
8
|
|
|
@@ -87,19 +87,21 @@ results = workflow.morphing_workflow(
|
|
|
87
87
|
|
|
88
88
|
Custom CSVs should have a `date` column (parseable by pandas) and a column named after the CMIP6 variable (e.g. `tas`, `tasmax`). Rows should be monthly.
|
|
89
89
|
|
|
90
|
+
The reference and target scenarios must cover **different years**, the same way the CMIP6 `historical` and `sspXXX` experiments do. The two series are concatenated before the baseline and target periods are sliced out, so overlapping years get averaged together and weaken the climate signal. Keep `baseline_range` inside the years the reference scenario covers.
|
|
91
|
+
|
|
90
92
|
## Climate scenarios
|
|
91
93
|
|
|
92
|
-
| Scenario
|
|
93
|
-
|
|
94
|
-
| Best Case Scenario
|
|
95
|
-
| Middle of the Road
|
|
96
|
-
| Upper Middle Scenario | ssp370 | Regional rivalry, slow convergence
|
|
97
|
-
| Worst Case Scenario
|
|
94
|
+
| Scenario | SSP | Description | Expected warming |
|
|
95
|
+
| --------------------- | ------ | --------------------------------------- | ---------------- |
|
|
96
|
+
| Best Case Scenario | ssp126 | Strong mitigation, renewable transition | ~1.8 C by 2100 |
|
|
97
|
+
| Middle of the Road | ssp245 | Moderate mitigation efforts | ~2.7 C by 2100 |
|
|
98
|
+
| Upper Middle Scenario | ssp370 | Regional rivalry, slow convergence | ~3.6 C by 2100 |
|
|
99
|
+
| Worst Case Scenario | ssp585 | Fossil-fueled development | ~4.4 C by 2100 |
|
|
98
100
|
|
|
99
101
|
## Morphing variables
|
|
100
102
|
|
|
101
103
|
- **Temperature** -- dry bulb temperature (shift + stretch)
|
|
102
|
-
- **Humidity** -- relative humidity
|
|
104
|
+
- **Humidity** -- relative humidity, stretched in specific humidity space
|
|
103
105
|
- **Pressure** -- atmospheric pressure (shift)
|
|
104
106
|
- **Wind** -- wind speed (stretch)
|
|
105
107
|
- **Clouds and Radiation** -- global/diffuse/direct radiation and sky cover
|
|
@@ -107,14 +109,16 @@ Custom CSVs should have a `date` column (parseable by pandas) and a column named
|
|
|
107
109
|
|
|
108
110
|
### Variable dependencies
|
|
109
111
|
|
|
110
|
-
Some variables
|
|
112
|
+
Some variables cannot be morphed on their own:
|
|
111
113
|
|
|
112
114
|
- **Humidity** requires Temperature and Pressure
|
|
113
115
|
- **Dew Point** requires Temperature, Humidity, and Pressure
|
|
114
116
|
|
|
117
|
+
Dependencies are added automatically and are **written to the output file**. Asking for `Dew Point` alone therefore returns an EPW with morphed pressure, temperature, relative humidity, and dew point, which keeps the file internally consistent. `MorphConfig.resolved_variables` shows exactly what will be written, in the order it is computed.
|
|
118
|
+
|
|
115
119
|
## Caching
|
|
116
120
|
|
|
117
|
-
Climate model data is cached locally after the first download
|
|
121
|
+
Climate model data is cached locally after the first download, and the cache is consulted before the remote catalogue is opened so a hit costs no network traffic.
|
|
118
122
|
|
|
119
123
|
```python
|
|
120
124
|
import pyepwmorph.models.access as access
|
|
@@ -123,6 +127,13 @@ stats = access.get_cmip6_cache_stats()
|
|
|
123
127
|
access.clear_cmip6_cache()
|
|
124
128
|
```
|
|
125
129
|
|
|
130
|
+
Cache entries live in the per-user cache directory (`~/Library/Caches/pyepwmorph` on macOS, `~/.cache/pyepwmorph` on Linux, `%LOCALAPPDATA%\pyepwmorph` on Windows) and are keyed by location, pathway, variable, model sources, and time slices. Two environment variables override the defaults:
|
|
131
|
+
|
|
132
|
+
| Variable | Purpose | Default |
|
|
133
|
+
| ------------------------- | ------------------------------ | ------- |
|
|
134
|
+
| `PYEPWMORPH_CACHE_DIR` | Where cache files are written | per-user cache directory |
|
|
135
|
+
| `PYEPWMORPH_CACHE_MAX_MB` | Size cap before old files go | 500 |
|
|
136
|
+
|
|
126
137
|
## Available climate models
|
|
127
138
|
|
|
128
139
|
```python
|
|
@@ -138,51 +149,39 @@ git clone https://github.com/justinfmccarty/pyepwmorph.git
|
|
|
138
149
|
cd pyepwmorph
|
|
139
150
|
uv sync --extra dev
|
|
140
151
|
|
|
141
|
-
# Run tests
|
|
142
|
-
pytest
|
|
152
|
+
# Run tests (fully offline)
|
|
153
|
+
uv run pytest
|
|
143
154
|
|
|
144
155
|
# Run with coverage
|
|
145
|
-
pytest --cov=pyepwmorph
|
|
156
|
+
uv run pytest --cov=pyepwmorph
|
|
157
|
+
|
|
158
|
+
# Lint
|
|
159
|
+
uv run ruff check pyepwmorph tests gui
|
|
146
160
|
```
|
|
147
161
|
|
|
148
162
|
### Releases
|
|
163
|
+
|
|
149
164
|
```bash
|
|
150
165
|
./release.sh [patch|minor|major]
|
|
151
166
|
```
|
|
152
167
|
|
|
153
|
-
|
|
154
|
-
```bash
|
|
155
|
-
gh release create v2.2.0 \
|
|
156
|
-
--title "v2.2.0" \
|
|
157
|
-
--notes "## What's new
|
|
168
|
+
The script refuses to run on a dirty tree, off `main`, with failing lint or tests, or without a matching `CHANGELOG.md` section. It bumps the version, tags, and pushes; creating the GitHub Release then triggers the PyPI publish workflow.
|
|
158
169
|
|
|
159
|
-
|
|
170
|
+
```bash
|
|
171
|
+
gh release create v3.0.0 \
|
|
172
|
+
--title "v3.0.0" \
|
|
173
|
+
--notes "See CHANGELOG.md."
|
|
160
174
|
```
|
|
161
175
|
|
|
176
|
+
## Changes
|
|
162
177
|
|
|
163
|
-
|
|
164
|
-
## Breaking changes in v2.0.0
|
|
165
|
-
|
|
166
|
-
- **License changed** from GPL-3.0 to MIT.
|
|
167
|
-
- **`future_years`** parameter renamed to **`target_years`** across the API.
|
|
168
|
-
The old name is still accepted with a deprecation warning.
|
|
169
|
-
- **`MorphConfig`** accepts new parameters: `data_source`, `custom_data`,
|
|
170
|
-
`reference_scenario`, and `target_years`.
|
|
171
|
-
- **EPW I/O** (`pyepwmorph.tools.io`) rewritten with stricter validation.
|
|
172
|
-
Files that are not exactly 8760 data rows will now raise `ValueError`.
|
|
173
|
-
The lat/lon parsing bug in the standalone `epw_location()` function has
|
|
174
|
-
been fixed.
|
|
175
|
-
- **`coordinate_cmip6_data`** now accepts an optional `time_slices` dict
|
|
176
|
-
for custom temporal bounds instead of hardcoded 1960-2014 / 2015-2100.
|
|
177
|
-
- **Removed** `requirements.txt`, `environment.yml`, `.bumpversion.cfg`.
|
|
178
|
-
Use `uv sync` or `pip install .` instead.
|
|
179
|
-
- **Per-module `__version__`** strings removed. Use
|
|
180
|
-
`pyepwmorph.__version__` or `importlib.metadata.version("pyepwmorph")`.
|
|
178
|
+
See [CHANGELOG.md](CHANGELOG.md) for the full history. The most recent release corrects several morphing calculations, so morphed humidity, dew point, wind speed, cloud cover, and direct/diffuse radiation all differ from files produced by earlier versions.
|
|
181
179
|
|
|
182
180
|
## Requirements
|
|
183
181
|
|
|
184
182
|
- Python >= 3.9
|
|
185
|
-
-
|
|
183
|
+
- pandas >= 2.2
|
|
184
|
+
- Internet connection (for CMIP6 data download; the custom CSV workflow runs offline)
|
|
186
185
|
|
|
187
186
|
## License
|
|
188
187
|
|
|
@@ -192,6 +191,6 @@ MIT License. See [LICENSE](LICENSE).
|
|
|
192
191
|
|
|
193
192
|
```text
|
|
194
193
|
McCarty, J. (2026). pyepwmorph: A Python package for climate-informed
|
|
195
|
-
EPW file morphing. Version
|
|
194
|
+
EPW file morphing. Version 3.0.0.
|
|
196
195
|
https://github.com/justinfmccarty/pyepwmorph
|
|
197
196
|
```
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""pyepwmorph -- Climate model data gathering and EPW file morphing."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
__version__ = version("pyepwmorph")
|
|
7
|
+
except PackageNotFoundError: # running from a source tree that was never installed
|
|
8
|
+
__version__ = "0.0.0+unknown"
|
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
# coding=utf-8
|
|
2
1
|
"""
|
|
3
2
|
various scripts for accessing different flavors of climate models
|
|
4
3
|
"""
|
|
5
4
|
|
|
6
|
-
import warnings
|
|
7
|
-
|
|
8
5
|
from pyepwmorph.tools import cache
|
|
9
6
|
|
|
10
|
-
warnings.filterwarnings("ignore")
|
|
11
|
-
|
|
12
7
|
__author__ = "Justin McCarty"
|
|
13
|
-
__copyright__ = "Copyright 2023"
|
|
8
|
+
__copyright__ = "Copyright 2023-2026"
|
|
14
9
|
__credits__ = ["Justin McCarty"]
|
|
15
10
|
__license__ = "MIT"
|
|
16
11
|
|
|
@@ -46,12 +41,12 @@ def access_cmip6_data(models, pathway, variable):
|
|
|
46
41
|
import intake
|
|
47
42
|
|
|
48
43
|
# NOTE: No caching at this level because the data contains lazy dask arrays
|
|
49
|
-
# that reference the full global grid. Caching happens in
|
|
44
|
+
# that reference the full global grid. Caching happens in workflow.py once
|
|
50
45
|
# the data has been spatially selected and computed for a specific location.
|
|
51
|
-
|
|
46
|
+
|
|
52
47
|
table_id = 'Amon' # atmospheric variables (A) saved at monthly resolution (mon)
|
|
53
48
|
member_id = 'r1i1p1f1'
|
|
54
|
-
|
|
49
|
+
|
|
55
50
|
# Fetch from Google Cloud
|
|
56
51
|
gcsfs.GCSFileSystem(token='anon')
|
|
57
52
|
# datastore_json = 'pangeo-cmip6.json'
|
|
@@ -64,7 +59,7 @@ def access_cmip6_data(models, pathway, variable):
|
|
|
64
59
|
|
|
65
60
|
# convert data catalog into a dictionary of xarray datasets
|
|
66
61
|
dataset_dict = model_search.to_dataset_dict(zarr_kwargs={'consolidated': True, 'decode_times': False})
|
|
67
|
-
|
|
62
|
+
|
|
68
63
|
return dataset_dict
|
|
69
64
|
|
|
70
65
|
|
|
@@ -84,19 +79,18 @@ def build_accessible_data_list():
|
|
|
84
79
|
import gcsfs
|
|
85
80
|
import intake
|
|
86
81
|
gcsfs.GCSFileSystem(token='anon')
|
|
87
|
-
# datastore_json = 'pangeo-cmip6.json'
|
|
88
82
|
esm_data = intake.open_esm_datastore("https://storage.googleapis.com/cmip6/pangeo-cmip6.json")
|
|
89
|
-
|
|
83
|
+
return sorted(esm_data.df['source_id'].unique().tolist())
|
|
90
84
|
|
|
91
85
|
|
|
92
86
|
def clear_cmip6_cache():
|
|
93
87
|
"""
|
|
94
88
|
Clear all cached CMIP6 data.
|
|
95
|
-
|
|
89
|
+
|
|
96
90
|
This clears location-specific cached data that has been processed and stored
|
|
97
|
-
for faster repeated access. This is useful for development or when you want
|
|
91
|
+
for faster repeated access. This is useful for development or when you want
|
|
98
92
|
to free up disk space.
|
|
99
|
-
|
|
93
|
+
|
|
100
94
|
Examples
|
|
101
95
|
--------
|
|
102
96
|
>>> clear_cmip6_cache()
|
|
@@ -107,11 +101,11 @@ def clear_cmip6_cache():
|
|
|
107
101
|
def get_cmip6_cache_stats():
|
|
108
102
|
"""
|
|
109
103
|
Get statistics about the CMIP6 data cache.
|
|
110
|
-
|
|
104
|
+
|
|
111
105
|
Returns information about cache size, number of files, and individual
|
|
112
106
|
file details for development and monitoring purposes. The cache stores
|
|
113
107
|
location-specific processed data.
|
|
114
|
-
|
|
108
|
+
|
|
115
109
|
Returns
|
|
116
110
|
-------
|
|
117
111
|
dict
|
|
@@ -122,10 +116,10 @@ def get_cmip6_cache_stats():
|
|
|
122
116
|
- max_size_mb: Maximum allowed cache size
|
|
123
117
|
- usage_percent: Percentage of max size used
|
|
124
118
|
- files: List of individual file details
|
|
125
|
-
|
|
119
|
+
|
|
126
120
|
Examples
|
|
127
121
|
--------
|
|
128
122
|
>>> stats = get_cmip6_cache_stats()
|
|
129
123
|
>>> print(f"Cache using {stats['total_size_mb']} MB ({stats['usage_percent']}%)")
|
|
130
124
|
"""
|
|
131
|
-
return cache.get_cache_stats()
|
|
125
|
+
return cache.get_cache_stats()
|
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
# coding=utf-8
|
|
2
1
|
"""
|
|
3
2
|
This module creates ensembles from multiple model inputs downloaded for a single pathway and variable.
|
|
4
3
|
This is what enables the slicing of the data from a percentile point of view.
|
|
5
4
|
"""
|
|
6
5
|
import pandas as pd
|
|
7
6
|
import xarray as xr
|
|
8
|
-
from pyepwmorph.tools import utilities
|
|
9
|
-
import warnings
|
|
10
7
|
|
|
11
|
-
|
|
8
|
+
from pyepwmorph.tools import utilities
|
|
12
9
|
|
|
13
10
|
__author__ = "Justin McCarty"
|
|
14
|
-
__copyright__ = "Copyright 2023"
|
|
11
|
+
__copyright__ = "Copyright 2023-2026"
|
|
15
12
|
__credits__ = ["Justin McCarty"]
|
|
16
13
|
__license__ = "MIT"
|
|
17
14
|
|
|
@@ -111,4 +108,4 @@ def calc_model_climatologies(baseline_range, future_range, baseline_data, future
|
|
|
111
108
|
future_means = utilities.monthly_means(future_data).rename(variable)
|
|
112
109
|
|
|
113
110
|
|
|
114
|
-
return baseline_means, future_means
|
|
111
|
+
return baseline_means, future_means
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# coding=utf-8
|
|
2
1
|
"""Coordinate CMIP6 model data into a standard grid and time system.
|
|
3
2
|
|
|
4
3
|
Climate model data comes in varied grid systems and temporal indices.
|
|
@@ -7,16 +6,10 @@ it is fed to the morphing algorithm.
|
|
|
7
6
|
"""
|
|
8
7
|
|
|
9
8
|
import logging
|
|
10
|
-
import time
|
|
11
|
-
import warnings
|
|
12
9
|
|
|
13
10
|
import dask
|
|
14
|
-
import pandas as pd
|
|
15
11
|
import xarray as xr
|
|
16
12
|
|
|
17
|
-
from pyepwmorph.tools import cache
|
|
18
|
-
|
|
19
|
-
warnings.filterwarnings("ignore")
|
|
20
13
|
logger = logging.getLogger(__name__)
|
|
21
14
|
|
|
22
15
|
__author__ = "Justin McCarty"
|
|
@@ -64,30 +57,17 @@ def coordinate_cmip6_data(
|
|
|
64
57
|
-------
|
|
65
58
|
dict
|
|
66
59
|
Dictionary of computed xarray Datasets keyed by source name.
|
|
60
|
+
|
|
61
|
+
Notes
|
|
62
|
+
-----
|
|
63
|
+
Caching lives in ``workflow.compile_climate_model_data`` so that a cache
|
|
64
|
+
hit avoids opening the remote catalogue at all. Calling this function
|
|
65
|
+
directly always recomputes.
|
|
67
66
|
"""
|
|
68
67
|
slices = {**DEFAULT_TIME_SLICES}
|
|
69
68
|
if time_slices:
|
|
70
69
|
slices.update(time_slices)
|
|
71
70
|
|
|
72
|
-
source_ids = list(dset_dict.keys())
|
|
73
|
-
model_names = []
|
|
74
|
-
for source_id in source_ids:
|
|
75
|
-
parts = source_id.split('.')
|
|
76
|
-
if len(parts) >= 3:
|
|
77
|
-
model_names.append(parts[2])
|
|
78
|
-
else:
|
|
79
|
-
model_names.append(source_id)
|
|
80
|
-
|
|
81
|
-
cached_data = cache.get_cached_coordinate_data(
|
|
82
|
-
latitude=latitude,
|
|
83
|
-
longitude=longitude,
|
|
84
|
-
pathway=pathway,
|
|
85
|
-
variable=variable,
|
|
86
|
-
source_id=model_names,
|
|
87
|
-
)
|
|
88
|
-
if cached_data is not None:
|
|
89
|
-
return cached_data
|
|
90
|
-
|
|
91
71
|
time_start, time_end = slices.get(pathway, DEFAULT_SSP_SLICE)
|
|
92
72
|
|
|
93
73
|
ds_dict = {}
|
|
@@ -118,15 +98,4 @@ def coordinate_cmip6_data(
|
|
|
118
98
|
|
|
119
99
|
ds_dict[name] = ds
|
|
120
100
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
cache.save_coordinate_to_cache(
|
|
124
|
-
data=datasets,
|
|
125
|
-
latitude=latitude,
|
|
126
|
-
longitude=longitude,
|
|
127
|
-
pathway=pathway,
|
|
128
|
-
variable=variable,
|
|
129
|
-
source_id=model_names,
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
return datasets
|
|
101
|
+
return dask.compute(ds_dict)[0]
|