earth2studio 0.1.0__py3-none-any.whl

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 (68) hide show
  1. earth2studio/__init__.py +17 -0
  2. earth2studio/data/__init__.py +26 -0
  3. earth2studio/data/arco.py +300 -0
  4. earth2studio/data/base.py +51 -0
  5. earth2studio/data/cds.py +385 -0
  6. earth2studio/data/gfs.py +356 -0
  7. earth2studio/data/hrrr.py +227 -0
  8. earth2studio/data/ifs.py +285 -0
  9. earth2studio/data/rand.py +74 -0
  10. earth2studio/data/rx.py +298 -0
  11. earth2studio/data/utils.py +126 -0
  12. earth2studio/data/xr.py +105 -0
  13. earth2studio/io/__init__.py +21 -0
  14. earth2studio/io/base.py +69 -0
  15. earth2studio/io/kv.py +197 -0
  16. earth2studio/io/netcdf4.py +238 -0
  17. earth2studio/io/xarray.py +171 -0
  18. earth2studio/io/zarr.py +200 -0
  19. earth2studio/lexicon/__init__.py +21 -0
  20. earth2studio/lexicon/arco.py +132 -0
  21. earth2studio/lexicon/base.py +121 -0
  22. earth2studio/lexicon/cds.py +136 -0
  23. earth2studio/lexicon/gfs.py +139 -0
  24. earth2studio/lexicon/hrrr.py +114 -0
  25. earth2studio/lexicon/ifs.py +116 -0
  26. earth2studio/models/auto.py +146 -0
  27. earth2studio/models/batch.py +227 -0
  28. earth2studio/models/dx/__init__.py +21 -0
  29. earth2studio/models/dx/base.py +86 -0
  30. earth2studio/models/dx/climatenet.py +144 -0
  31. earth2studio/models/dx/corrdiff.py +502 -0
  32. earth2studio/models/dx/identity.py +68 -0
  33. earth2studio/models/dx/precipitation_afno.py +159 -0
  34. earth2studio/models/nn/afno_precip.py +69 -0
  35. earth2studio/models/nn/climatenet_conv.py +482 -0
  36. earth2studio/models/px/__init__.py +23 -0
  37. earth2studio/models/px/base.py +109 -0
  38. earth2studio/models/px/dlwp.py +371 -0
  39. earth2studio/models/px/fcn.py +233 -0
  40. earth2studio/models/px/fengwu.py +406 -0
  41. earth2studio/models/px/pangu.py +733 -0
  42. earth2studio/models/px/persistence.py +190 -0
  43. earth2studio/models/px/sfno.py +299 -0
  44. earth2studio/models/px/utils.py +30 -0
  45. earth2studio/perturbation/__init__.py +22 -0
  46. earth2studio/perturbation/base.py +48 -0
  47. earth2studio/perturbation/brown.py +85 -0
  48. earth2studio/perturbation/bv.py +108 -0
  49. earth2studio/perturbation/gaussian.py +54 -0
  50. earth2studio/perturbation/spherical.py +242 -0
  51. earth2studio/perturbation/utils.py +133 -0
  52. earth2studio/perturbation/zero.py +48 -0
  53. earth2studio/run.py +243 -0
  54. earth2studio/statistics/__init__.py +20 -0
  55. earth2studio/statistics/base.py +72 -0
  56. earth2studio/statistics/moments.py +275 -0
  57. earth2studio/statistics/rmse.py +98 -0
  58. earth2studio/statistics/utils.py +51 -0
  59. earth2studio/statistics/weights.py +46 -0
  60. earth2studio/utils/__init__.py +21 -0
  61. earth2studio/utils/coords.py +308 -0
  62. earth2studio/utils/time.py +75 -0
  63. earth2studio/utils/type.py +26 -0
  64. earth2studio-0.1.0.dist-info/LICENSE +202 -0
  65. earth2studio-0.1.0.dist-info/METADATA +173 -0
  66. earth2studio-0.1.0.dist-info/RECORD +68 -0
  67. earth2studio-0.1.0.dist-info/WHEEL +5 -0
  68. earth2studio-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,17 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.
2
+ # SPDX-FileCopyrightText: All rights reserved.
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ __version__ = "0.1.0"
@@ -0,0 +1,26 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.
2
+ # SPDX-FileCopyrightText: All rights reserved.
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ from .base import DataSource # noqa
18
+ from .arco import ARCO # noq
19
+ from .cds import CDS # noq
20
+ from .gfs import GFS # noq
21
+ from .hrrr import HRRR # noq
22
+ from .ifs import IFS # noq
23
+ from .rand import Random # noqa
24
+ from .rx import LandSeaMask, SurfaceGeoPotential, CosineSolarZenith # noqa
25
+ from .xr import DataArrayFile, DataSetFile # noqa
26
+ from .utils import fetch_data, prep_data_array
@@ -0,0 +1,300 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.
2
+ # SPDX-FileCopyrightText: All rights reserved.
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ import os
18
+ import pathlib
19
+ import shutil
20
+ from datetime import datetime, timedelta
21
+
22
+ import fsspec
23
+ import gcsfs
24
+ import numpy as np
25
+ import xarray as xr
26
+ import zarr
27
+ from loguru import logger
28
+ from modulus.distributed.manager import DistributedManager
29
+ from tqdm import tqdm
30
+
31
+ from earth2studio.data.utils import prep_data_inputs
32
+ from earth2studio.lexicon import ARCOLexicon
33
+ from earth2studio.utils.type import TimeArray, VariableArray
34
+
35
+ LOCAL_CACHE = os.path.join(os.path.expanduser("~"), ".cache", "earth2studio")
36
+
37
+
38
+ class ARCO:
39
+ """Analysis-Ready, Cloud Optimized (ARCO) is a data store of ERA5 re-analysis data
40
+ currated by Google. This data is stored in Zarr format and contains 31 surface and
41
+ pressure level variables (for 37 pressure levels) on a 0.25 degree lat lon grid.
42
+ Temporal resolution is 1 hour.
43
+
44
+ Parameters
45
+ ----------
46
+ cache : bool, optional
47
+ Cache data source on local memory, by default True
48
+ verbose : bool, optional
49
+ Print download progress, by default True
50
+
51
+ Warning
52
+ -------
53
+ This is a remote data source and can potentially download a large amount of data
54
+ to your local machine for large requests.
55
+
56
+ Note
57
+ ----
58
+ Additional information on the data repository can be referenced here:
59
+
60
+ - https://cloud.google.com/storage/docs/public-datasets/era5
61
+ """
62
+
63
+ ARCO_LAT = np.linspace(90, -90, 721)
64
+ ARCO_LON = np.linspace(0, 359.75, 1440)
65
+
66
+ def __init__(self, cache: bool = True, verbose: bool = True):
67
+ self._cache = cache
68
+ self._verbose = verbose
69
+
70
+ if self._cache:
71
+ gcstore = fsspec.get_mapper(
72
+ "gs://gcp-public-data-arco-era5/ar/full_37-1h-0p25deg-chunk-1.zarr-v3",
73
+ target_protocol="gs",
74
+ cache_storage=self.cache,
75
+ target_options={"anon": True, "default_block_size": 2**20},
76
+ )
77
+ else:
78
+ gcs = gcsfs.GCSFileSystem(cache_timeout=-1)
79
+ gcstore = gcsfs.GCSMap(
80
+ "gs://gcp-public-data-arco-era5/ar/full_37-1h-0p25deg-chunk-1.zarr-v3",
81
+ gcs=gcs,
82
+ )
83
+ self.zarr_group = zarr.open(gcstore, mode="r")
84
+
85
+ def __call__(
86
+ self,
87
+ time: datetime | list[datetime] | TimeArray,
88
+ variable: str | list[str] | VariableArray,
89
+ ) -> xr.DataArray:
90
+ """Function to get data.
91
+
92
+ Parameters
93
+ ----------
94
+ time : datetime | list[datetime] | TimeArray
95
+ Timestamps to return data for (UTC).
96
+ variable : str | list[str] | VariableArray
97
+ String, list of strings or array of strings that refer to variables to
98
+ return. Must be in the ARCO lexicon.
99
+
100
+ Returns
101
+ -------
102
+ xr.DataArray
103
+ ERA5 weather data array from ARCO
104
+ """
105
+ time, variable = prep_data_inputs(time, variable)
106
+ # Create cache dir if doesnt exist
107
+ pathlib.Path(self.cache).mkdir(parents=True, exist_ok=True)
108
+
109
+ # Make sure input time is valid
110
+ self._validate_time(time)
111
+
112
+ # Fetch index file for requested time
113
+ data_arrays = []
114
+ for t0 in time:
115
+ data_array = self.fetch_arco_dataarray(t0, variable)
116
+ data_arrays.append(data_array)
117
+
118
+ # Delete cache if needed
119
+ if not self._cache:
120
+ shutil.rmtree(self.cache)
121
+
122
+ return xr.concat(data_arrays, dim="time")
123
+
124
+ def fetch_arco_dataarray(
125
+ self,
126
+ time: datetime,
127
+ variables: list[str],
128
+ ) -> xr.DataArray:
129
+ """Retrives ARCO data array for given date time by downloading a lat lon array
130
+ from the Zarr store
131
+
132
+ Parameters
133
+ ----------
134
+ time : datetime
135
+ Date time to fetch
136
+ variables : list[str]
137
+ list of atmosphric variables to fetch. Must be supported in ARCO lexicon
138
+
139
+ Returns
140
+ -------
141
+ xr.DataArray
142
+ ARCO data array for given date time
143
+ """
144
+ arcoda = xr.DataArray(
145
+ data=np.empty((1, len(variables), len(self.ARCO_LAT), len(self.ARCO_LON))),
146
+ dims=["time", "variable", "lat", "lon"],
147
+ coords={
148
+ "time": [time],
149
+ "variable": variables,
150
+ "lat": self.ARCO_LAT,
151
+ "lon": self.ARCO_LON,
152
+ },
153
+ )
154
+
155
+ # Load levels coordinate system from Zarr store and check
156
+ level_coords = self.zarr_group["level"][:]
157
+ # Get time index (vanilla zarr doesnt support date indices)
158
+ time_index = self._get_time_index(time)
159
+
160
+ # TODO: Add MP here
161
+ for i, variable in enumerate(
162
+ tqdm(
163
+ variables, desc=f"Fetching ARCO for {time}", disable=(not self._verbose)
164
+ )
165
+ ):
166
+ logger.debug(
167
+ f"Fetching ARCO zarr array for variable: {variable} at {time.isoformat()}"
168
+ )
169
+ try:
170
+ arco_name, modifier = ARCOLexicon[variable]
171
+ except KeyError as e:
172
+ logger.error(f"variable id {variable} not found in ARCO lexicon")
173
+ raise e
174
+
175
+ arco_variable, level = arco_name.split("::")
176
+
177
+ # special variables
178
+ if variable == "tp06":
179
+ arcoda[0, i] = modifier(self._fetch_tp06(time))
180
+ continue
181
+
182
+ shape = self.zarr_group[arco_variable].shape
183
+ # Static variables
184
+ if len(shape) == 2:
185
+ arcoda[0, i] = modifier(self.zarr_group[arco_variable][:])
186
+ # Surface variable
187
+ elif len(shape) == 3:
188
+ arcoda[0, i] = modifier(self.zarr_group[arco_variable][time_index])
189
+ # Atmospheric variable
190
+ else:
191
+ level_index = np.where(level_coords == int(level))[0][0]
192
+ arcoda[0, i] = modifier(
193
+ self.zarr_group[arco_variable][time_index, level_index]
194
+ )
195
+
196
+ return arcoda
197
+
198
+ def _fetch_tp06(self, time: datetime) -> np.array:
199
+ """Handle special tp06 variable"""
200
+ tp06_array = np.zeros((self.ARCO_LAT.shape[0], self.ARCO_LON.shape[0]))
201
+ # Accumulate over past 6 hours
202
+ for i in range(6):
203
+ time_index = self._get_time_index(time - timedelta(hours=i))
204
+ tp06_array += self.zarr_group["total_precipitation"][time_index]
205
+
206
+ return tp06_array
207
+
208
+ @property
209
+ def cache(self) -> str:
210
+ """Get the appropriate cache location."""
211
+ cache_location = os.path.join(LOCAL_CACHE, "arco")
212
+ if not self._cache:
213
+ cache_location = os.path.join(
214
+ cache_location, f"tmp_{DistributedManager().rank}"
215
+ )
216
+ return cache_location
217
+
218
+ @classmethod
219
+ def _validate_time(cls, times: list[datetime]) -> None:
220
+ """Verify if date time is valid for ARCO
221
+
222
+ Parameters
223
+ ----------
224
+ times : list[datetime]
225
+ list of date times to fetch data
226
+ """
227
+ for time in times:
228
+ if not (time - datetime(1900, 1, 1)).total_seconds() % 3600 == 0:
229
+ raise ValueError(
230
+ f"Requested date time {time} needs to be 1 hour interval for ARCO"
231
+ )
232
+
233
+ if time < datetime(year=1940, month=1, day=1):
234
+ raise ValueError(
235
+ f"Requested date time {time} needs to be after January 1st, 1940 for ARCO"
236
+ )
237
+
238
+ if time >= datetime(year=2023, month=11, day=10):
239
+ raise ValueError(
240
+ f"Requested date time {time} needs to be before November 10th, 2023 for ARCO"
241
+ )
242
+
243
+ # if not self.available(time):
244
+ # raise ValueError(f"Requested date time {time} not available in ARCO")
245
+
246
+ @classmethod
247
+ def _get_time_index(cls, time: datetime) -> int:
248
+ """Small little index converter to go from datetime to integer index.
249
+ We don't need to do with with xarray, but since we are vanilla zaar for speed
250
+ this conversion must be manual.
251
+
252
+ Parameters
253
+ ----------
254
+ time : datetime
255
+ Input date time
256
+
257
+ Returns
258
+ -------
259
+ int
260
+ Time coordinate index of ARCO data
261
+ """
262
+ start_date = datetime(year=1900, month=1, day=1)
263
+ duration = time - start_date
264
+ return int(divmod(duration.total_seconds(), 3600)[0])
265
+
266
+ @classmethod
267
+ def available(cls, time: datetime | np.datetime64) -> bool:
268
+ """Checks if given date time is avaliable in the ARCO data source
269
+
270
+ Parameters
271
+ ----------
272
+ time : datetime | np.datetime64
273
+ Date time to access
274
+
275
+ Returns
276
+ -------
277
+ bool
278
+ If date time is avaiable
279
+ """
280
+ if isinstance(time, np.datetime64): # np.datetime64 -> datetime
281
+ _unix = np.datetime64(0, "s")
282
+ _ds = np.timedelta64(1, "s")
283
+ time = datetime.utcfromtimestamp((time - _unix) / _ds)
284
+
285
+ # Offline checks
286
+ try:
287
+ cls._validate_time([time])
288
+ except ValueError:
289
+ return False
290
+
291
+ gcs = gcsfs.GCSFileSystem(cache_timeout=-1)
292
+ gcstore = gcsfs.GCSMap(
293
+ "gs://gcp-public-data-arco-era5/ar/full_37-1h-0p25deg-chunk-1.zarr-v3",
294
+ gcs=gcs,
295
+ )
296
+ zarr_group = zarr.open(gcstore, mode="r")
297
+ # Load time coordinate system from Zarr store and check
298
+ time_index = cls._get_time_index(time)
299
+ max_index = zarr_group["time"][-1]
300
+ return time_index >= 0 and time_index <= max_index
@@ -0,0 +1,51 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.
2
+ # SPDX-FileCopyrightText: All rights reserved.
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ from datetime import datetime
18
+ from typing import Protocol, runtime_checkable
19
+
20
+ import xarray as xr
21
+
22
+ from earth2studio.utils.type import TimeArray, VariableArray
23
+
24
+
25
+ @runtime_checkable
26
+ class DataSource(Protocol):
27
+ """Data source interface."""
28
+
29
+ def __call__(
30
+ self,
31
+ time: datetime | list[datetime] | TimeArray,
32
+ variable: str | list[str] | VariableArray,
33
+ ) -> xr.DataArray:
34
+ """Function to get data.
35
+
36
+ Parameters
37
+ ----------
38
+ time : datetime | list[datetime] | TimeArray
39
+ Datetime, list of datetimes or array of np.datetime64 to return data for.
40
+ variable : str | list[str] | VariableArray
41
+ String, list of strings or array of strings that refer to variables to
42
+ return.
43
+
44
+ Returns
45
+ -------
46
+ xr.DataArray
47
+ An xarray data-array with the dimensions [time, channel, ....]. The coords
48
+ should be provided. Time coordinate should be a datetime array and the
49
+ channel coordinate should be array of strings with Earth2Studio channel ids.
50
+ """
51
+ pass