anemoi-utils 0.4.10__py3-none-any.whl → 0.4.12__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.
Potentially problematic release.
This version of anemoi-utils might be problematic. Click here for more details.
- anemoi/utils/_version.py +2 -2
- anemoi/utils/caching.py +85 -35
- anemoi/utils/commands/requests.py +6 -2
- anemoi/utils/config.py +17 -0
- anemoi/utils/dates.py +13 -6
- anemoi/utils/devtools.py +83 -0
- anemoi/utils/grids.py +97 -0
- anemoi/utils/humanize.py +33 -0
- {anemoi_utils-0.4.10.dist-info → anemoi_utils-0.4.12.dist-info}/LICENSE +1 -1
- {anemoi_utils-0.4.10.dist-info → anemoi_utils-0.4.12.dist-info}/METADATA +4 -3
- {anemoi_utils-0.4.10.dist-info → anemoi_utils-0.4.12.dist-info}/RECORD +14 -12
- {anemoi_utils-0.4.10.dist-info → anemoi_utils-0.4.12.dist-info}/WHEEL +1 -1
- {anemoi_utils-0.4.10.dist-info → anemoi_utils-0.4.12.dist-info}/entry_points.txt +0 -0
- {anemoi_utils-0.4.10.dist-info → anemoi_utils-0.4.12.dist-info}/top_level.txt +0 -0
anemoi/utils/_version.py
CHANGED
anemoi/utils/caching.py
CHANGED
|
@@ -14,47 +14,31 @@ import os
|
|
|
14
14
|
import time
|
|
15
15
|
from threading import Lock
|
|
16
16
|
|
|
17
|
+
import numpy as np
|
|
18
|
+
|
|
17
19
|
LOCK = Lock()
|
|
18
20
|
CACHE = {}
|
|
19
21
|
|
|
20
22
|
|
|
21
|
-
def
|
|
22
|
-
|
|
23
|
-
key = json.dumps(key, sort_keys=True)
|
|
24
|
-
m = hashlib.md5()
|
|
25
|
-
m.update(key.encode("utf-8"))
|
|
26
|
-
m = m.hexdigest()
|
|
27
|
-
|
|
28
|
-
if m in CACHE:
|
|
29
|
-
return CACHE[m]
|
|
30
|
-
|
|
31
|
-
path = os.path.join(os.path.expanduser("~"), ".cache", "anemoi", collection)
|
|
32
|
-
os.makedirs(path, exist_ok=True)
|
|
33
|
-
|
|
34
|
-
filename = os.path.join(path, m)
|
|
35
|
-
if os.path.exists(filename):
|
|
36
|
-
with open(filename, "r") as f:
|
|
37
|
-
data = json.load(f)
|
|
38
|
-
if expires is None or data["expires"] > time.time():
|
|
39
|
-
if data["key"] == key:
|
|
40
|
-
return data["value"]
|
|
41
|
-
|
|
42
|
-
value = proc()
|
|
43
|
-
data = {"key": key, "value": value}
|
|
44
|
-
if expires is not None:
|
|
45
|
-
data["expires"] = time.time() + expires
|
|
23
|
+
def _get_cache_path(collection):
|
|
24
|
+
return os.path.join(os.path.expanduser("~"), ".cache", "anemoi", collection)
|
|
46
25
|
|
|
47
|
-
with open(filename, "w") as f:
|
|
48
|
-
json.dump(data, f)
|
|
49
26
|
|
|
50
|
-
|
|
51
|
-
|
|
27
|
+
def clean_cache(collection="default"):
|
|
28
|
+
global CACHE
|
|
29
|
+
CACHE = {}
|
|
30
|
+
path = _get_cache_path(collection)
|
|
31
|
+
if not os.path.exists(path):
|
|
32
|
+
return
|
|
33
|
+
for filename in os.listdir(path):
|
|
34
|
+
os.remove(os.path.join(path, filename))
|
|
52
35
|
|
|
53
36
|
|
|
54
|
-
class
|
|
55
|
-
"""
|
|
37
|
+
class Cacher:
|
|
38
|
+
"""This class implements a simple caching mechanism.
|
|
39
|
+
Private class, do not use directly"""
|
|
56
40
|
|
|
57
|
-
def __init__(self, collection
|
|
41
|
+
def __init__(self, collection, expires):
|
|
58
42
|
self.collection = collection
|
|
59
43
|
self.expires = expires
|
|
60
44
|
|
|
@@ -64,11 +48,77 @@ class cached:
|
|
|
64
48
|
|
|
65
49
|
def wrapped(*args, **kwargs):
|
|
66
50
|
with LOCK:
|
|
67
|
-
return cache(
|
|
51
|
+
return self.cache(
|
|
68
52
|
(full, args, kwargs),
|
|
69
53
|
lambda: func(*args, **kwargs),
|
|
70
|
-
self.collection,
|
|
71
|
-
self.expires,
|
|
72
54
|
)
|
|
73
55
|
|
|
74
56
|
return wrapped
|
|
57
|
+
|
|
58
|
+
def cache(self, key, proc):
|
|
59
|
+
|
|
60
|
+
key = json.dumps(key, sort_keys=True)
|
|
61
|
+
m = hashlib.md5()
|
|
62
|
+
m.update(key.encode("utf-8"))
|
|
63
|
+
m = m.hexdigest()
|
|
64
|
+
|
|
65
|
+
if m in CACHE:
|
|
66
|
+
return CACHE[m]
|
|
67
|
+
|
|
68
|
+
path = _get_cache_path(self.collection)
|
|
69
|
+
|
|
70
|
+
filename = os.path.join(path, m) + self.ext
|
|
71
|
+
if os.path.exists(filename):
|
|
72
|
+
data = self.load(filename)
|
|
73
|
+
if self.expires is None or data["expires"] > time.time():
|
|
74
|
+
if data["key"] == key:
|
|
75
|
+
return data["value"]
|
|
76
|
+
|
|
77
|
+
value = proc()
|
|
78
|
+
data = {"key": key, "value": value}
|
|
79
|
+
if self.expires is not None:
|
|
80
|
+
data["expires"] = time.time() + self.expires
|
|
81
|
+
|
|
82
|
+
os.makedirs(path, exist_ok=True)
|
|
83
|
+
temp_filename = self.save(filename, data)
|
|
84
|
+
os.rename(temp_filename, filename)
|
|
85
|
+
|
|
86
|
+
CACHE[m] = value
|
|
87
|
+
return value
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class JsonCacher(Cacher):
|
|
91
|
+
ext = ""
|
|
92
|
+
|
|
93
|
+
def save(self, path, data):
|
|
94
|
+
temp_path = path + ".tmp"
|
|
95
|
+
with open(temp_path, "w") as f:
|
|
96
|
+
json.dump(data, f)
|
|
97
|
+
return temp_path
|
|
98
|
+
|
|
99
|
+
def load(self, path):
|
|
100
|
+
with open(path, "r") as f:
|
|
101
|
+
return json.load(f)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class NpzCacher(Cacher):
|
|
105
|
+
ext = ".npz"
|
|
106
|
+
|
|
107
|
+
def save(self, path, data):
|
|
108
|
+
temp_path = path + ".tmp.npz"
|
|
109
|
+
np.savez(temp_path, **data)
|
|
110
|
+
return temp_path
|
|
111
|
+
|
|
112
|
+
def load(self, path):
|
|
113
|
+
return np.load(path, allow_pickle=True)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
# PUBLIC API
|
|
117
|
+
def cached(collection="default", expires=None, encoding="json"):
|
|
118
|
+
"""Decorator to cache the result of a function.
|
|
119
|
+
|
|
120
|
+
Default is to use a json file to store the cache, but you can also use npz files
|
|
121
|
+
to cache dict of numpy arrays.
|
|
122
|
+
|
|
123
|
+
"""
|
|
124
|
+
return dict(json=JsonCacher, npz=NpzCacher)[encoding](collection, expires)
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
# nor does it submit to any jurisdiction.
|
|
7
7
|
|
|
8
8
|
import json
|
|
9
|
+
import sys
|
|
9
10
|
|
|
10
11
|
from anemoi.utils.mars.requests import print_request
|
|
11
12
|
|
|
@@ -22,8 +23,11 @@ class Requests(Command):
|
|
|
22
23
|
command_parser.add_argument("--only-one-field", action="store_true")
|
|
23
24
|
|
|
24
25
|
def run(self, args):
|
|
25
|
-
|
|
26
|
-
requests = json.load(
|
|
26
|
+
if args.input == "-":
|
|
27
|
+
requests = json.load(sys.stdin)
|
|
28
|
+
else:
|
|
29
|
+
with open(args.input) as f:
|
|
30
|
+
requests = json.load(f)
|
|
27
31
|
|
|
28
32
|
if args.only_one_field:
|
|
29
33
|
for r in requests:
|
anemoi/utils/config.py
CHANGED
|
@@ -223,6 +223,23 @@ def load_any_dict_format(path) -> dict:
|
|
|
223
223
|
if path.endswith(".toml"):
|
|
224
224
|
with open(path, "rb") as f:
|
|
225
225
|
return tomllib.load(f)
|
|
226
|
+
|
|
227
|
+
if path == "-":
|
|
228
|
+
import sys
|
|
229
|
+
|
|
230
|
+
config = sys.stdin.read()
|
|
231
|
+
|
|
232
|
+
parsers = [(yaml.safe_load, "yaml"), (json.loads, "json"), (tomllib.loads, "toml")]
|
|
233
|
+
|
|
234
|
+
for parser, parser_type in parsers:
|
|
235
|
+
try:
|
|
236
|
+
LOG.debug(f"Trying {parser_type} parser for stdin")
|
|
237
|
+
return parser(config)
|
|
238
|
+
except Exception:
|
|
239
|
+
pass
|
|
240
|
+
|
|
241
|
+
raise ValueError("Failed to parse configuration from stdin")
|
|
242
|
+
|
|
226
243
|
except (json.JSONDecodeError, yaml.YAMLError, tomllib.TOMLDecodeError) as e:
|
|
227
244
|
LOG.warning(f"Failed to parse config file {path}", exc_info=e)
|
|
228
245
|
raise ValueError(f"Failed to parse config file {path} [{e}]")
|
anemoi/utils/dates.py
CHANGED
|
@@ -155,12 +155,19 @@ def as_timedelta(frequency) -> datetime.timedelta:
|
|
|
155
155
|
unit = {"h": "hours", "d": "days", "s": "seconds", "m": "minutes"}[unit]
|
|
156
156
|
return datetime.timedelta(**{unit: v})
|
|
157
157
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
158
|
+
if re.match(r"^\d+:\d+(:\d+)?$", frequency):
|
|
159
|
+
m = frequency.split(":")
|
|
160
|
+
if len(m) == 2:
|
|
161
|
+
return datetime.timedelta(hours=int(m[0]), minutes=int(m[1]))
|
|
162
|
+
|
|
163
|
+
if len(m) == 3:
|
|
164
|
+
return datetime.timedelta(hours=int(m[0]), minutes=int(m[1]), seconds=int(m[2]))
|
|
165
|
+
|
|
166
|
+
if re.match(r"^\d+ days?, \d+:\d+:\d+$", frequency):
|
|
167
|
+
m = frequency.split(", ")
|
|
168
|
+
days = int(m[0].split()[0])
|
|
169
|
+
hms = m[1].split(":")
|
|
170
|
+
return datetime.timedelta(days=days, hours=int(hms[0]), minutes=int(hms[1]), seconds=int(hms[2]))
|
|
164
171
|
|
|
165
172
|
# ISO8601
|
|
166
173
|
try:
|
anemoi/utils/devtools.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# (C) Copyright 2024 Anemoi contributors.
|
|
2
|
+
#
|
|
3
|
+
# This software is licensed under the terms of the Apache Licence Version 2.0
|
|
4
|
+
# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
|
|
5
|
+
#
|
|
6
|
+
# In applying this licence, ECMWF does not waive the privileges and immunities
|
|
7
|
+
# granted to it by virtue of its status as an intergovernmental organisation
|
|
8
|
+
# nor does it submit to any jurisdiction.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
import cartopy.crs as ccrs
|
|
12
|
+
import cartopy.feature as cfeature
|
|
13
|
+
import matplotlib.pyplot as plt
|
|
14
|
+
import matplotlib.tri as tri
|
|
15
|
+
import numpy as np
|
|
16
|
+
|
|
17
|
+
"""FOR DEVELOPMENT PURPOSES ONLY
|
|
18
|
+
|
|
19
|
+
This module contains
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
# TODO: use earthkit-plots
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def fix(lons):
|
|
27
|
+
return np.where(lons > 180, lons - 360, lons)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def plot_values(
|
|
31
|
+
values, latitudes, longitudes, title=None, missing_value=None, min_value=None, max_value=None, **kwargs
|
|
32
|
+
):
|
|
33
|
+
|
|
34
|
+
_, ax = plt.subplots(subplot_kw={"projection": ccrs.PlateCarree()})
|
|
35
|
+
ax.coastlines()
|
|
36
|
+
ax.add_feature(cfeature.BORDERS, linestyle=":")
|
|
37
|
+
|
|
38
|
+
missing_values = np.isnan(values)
|
|
39
|
+
|
|
40
|
+
if missing_value is None:
|
|
41
|
+
values = values[~missing_values]
|
|
42
|
+
longitudes = longitudes[~missing_values]
|
|
43
|
+
latitudes = latitudes[~missing_values]
|
|
44
|
+
else:
|
|
45
|
+
values = np.where(missing_values, missing_value, values)
|
|
46
|
+
|
|
47
|
+
if max_value is not None:
|
|
48
|
+
values = np.where(values > max_value, max_value, values)
|
|
49
|
+
|
|
50
|
+
if min_value is not None:
|
|
51
|
+
values = np.where(values < min_value, min_value, values)
|
|
52
|
+
|
|
53
|
+
triangulation = tri.Triangulation(fix(longitudes), latitudes)
|
|
54
|
+
|
|
55
|
+
levels = kwargs.pop("levels", 10)
|
|
56
|
+
|
|
57
|
+
_ = ax.tricontourf(triangulation, values, levels=levels, transform=ccrs.PlateCarree())
|
|
58
|
+
|
|
59
|
+
options = dict(
|
|
60
|
+
levels=levels,
|
|
61
|
+
colors="black",
|
|
62
|
+
linewidths=0.5,
|
|
63
|
+
transform=ccrs.PlateCarree(),
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
options.update(kwargs)
|
|
67
|
+
|
|
68
|
+
ax.tricontour(
|
|
69
|
+
triangulation,
|
|
70
|
+
values,
|
|
71
|
+
**options,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
if title is not None:
|
|
75
|
+
ax.set_title(title)
|
|
76
|
+
|
|
77
|
+
return ax
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def plot_field(field, title=None, **kwargs):
|
|
81
|
+
values = field.to_numpy(flatten=True)
|
|
82
|
+
latitudes, longitudes = field.grid_points()
|
|
83
|
+
return plot_values(values, latitudes, longitudes, title=title, **kwargs)
|
anemoi/utils/grids.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# (C) Copyright 2025 Anemoi contributors.
|
|
2
|
+
#
|
|
3
|
+
# This software is licensed under the terms of the Apache Licence Version 2.0
|
|
4
|
+
# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
|
|
5
|
+
#
|
|
6
|
+
# In applying this licence, ECMWF does not waive the privileges and immunities
|
|
7
|
+
# granted to it by virtue of its status as an intergovernmental organisation
|
|
8
|
+
# nor does it submit to any jurisdiction.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
"""Utilities for working with grids.
|
|
12
|
+
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
import os
|
|
17
|
+
from io import BytesIO
|
|
18
|
+
|
|
19
|
+
import numpy as np
|
|
20
|
+
import requests
|
|
21
|
+
|
|
22
|
+
from .caching import cached
|
|
23
|
+
|
|
24
|
+
LOG = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
GRIDS_URL_PATTERN = "https://get.ecmwf.int/repository/anemoi/grids/grid-{name}.npz"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def xyz_to_latlon(x, y, z):
|
|
31
|
+
return (
|
|
32
|
+
np.rad2deg(np.arcsin(np.minimum(1.0, np.maximum(-1.0, z)))),
|
|
33
|
+
np.rad2deg(np.arctan2(y, x)),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def latlon_to_xyz(lat, lon, radius=1.0):
|
|
38
|
+
# https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#From_geodetic_to_ECEF_coordinates
|
|
39
|
+
# We assume that the Earth is a sphere of radius 1 so N(phi) = 1
|
|
40
|
+
# We assume h = 0
|
|
41
|
+
#
|
|
42
|
+
phi = np.deg2rad(lat)
|
|
43
|
+
lda = np.deg2rad(lon)
|
|
44
|
+
|
|
45
|
+
cos_phi = np.cos(phi)
|
|
46
|
+
cos_lda = np.cos(lda)
|
|
47
|
+
sin_phi = np.sin(phi)
|
|
48
|
+
sin_lda = np.sin(lda)
|
|
49
|
+
|
|
50
|
+
x = cos_phi * cos_lda * radius
|
|
51
|
+
y = cos_phi * sin_lda * radius
|
|
52
|
+
z = sin_phi * radius
|
|
53
|
+
|
|
54
|
+
return x, y, z
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def nearest_grid_points(source_latitudes, source_longitudes, target_latitudes, target_longitudes):
|
|
58
|
+
from scipy.spatial import cKDTree
|
|
59
|
+
|
|
60
|
+
source_xyz = latlon_to_xyz(source_latitudes, source_longitudes)
|
|
61
|
+
source_points = np.array(source_xyz).transpose()
|
|
62
|
+
|
|
63
|
+
target_xyz = latlon_to_xyz(target_latitudes, target_longitudes)
|
|
64
|
+
target_points = np.array(target_xyz).transpose()
|
|
65
|
+
|
|
66
|
+
_, indices = cKDTree(source_points).query(target_points, k=1)
|
|
67
|
+
return indices
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@cached(collection="grids", encoding="npz")
|
|
71
|
+
def _grids(name):
|
|
72
|
+
from anemoi.utils.config import load_config
|
|
73
|
+
|
|
74
|
+
user_path = load_config().get("utils", {}).get("grids_path")
|
|
75
|
+
if user_path:
|
|
76
|
+
path = os.path.expanduser(os.path.join(user_path, f"grid-{name}.npz"))
|
|
77
|
+
if os.path.exists(path):
|
|
78
|
+
LOG.warning("Loading grids from custom user path %s", path)
|
|
79
|
+
with open(path, "rb") as f:
|
|
80
|
+
return f.read()
|
|
81
|
+
else:
|
|
82
|
+
LOG.warning("Custom user path %s does not exist", path)
|
|
83
|
+
|
|
84
|
+
url = GRIDS_URL_PATTERN.format(name=name.lower())
|
|
85
|
+
LOG.warning("Downloading grids from %s", url)
|
|
86
|
+
response = requests.get(url)
|
|
87
|
+
response.raise_for_status()
|
|
88
|
+
return response.content
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def grids(name):
|
|
92
|
+
if name.endswith(".npz"):
|
|
93
|
+
return dict(np.load(name))
|
|
94
|
+
|
|
95
|
+
data = _grids(name)
|
|
96
|
+
npz = np.load(BytesIO(data))
|
|
97
|
+
return dict(npz)
|
anemoi/utils/humanize.py
CHANGED
|
@@ -689,3 +689,36 @@ def print_dates(dates) -> None:
|
|
|
689
689
|
A list of dates, as datetime objects or strings.
|
|
690
690
|
"""
|
|
691
691
|
print(compress_dates(dates))
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
def make_list_int(value) -> list:
|
|
695
|
+
"""Convert a string like "1/2/3" or "1/to/3" or "1/to/10/by/2" to a list of integers.
|
|
696
|
+
|
|
697
|
+
Parameters
|
|
698
|
+
----------
|
|
699
|
+
value : str, list, tuple, int
|
|
700
|
+
The value to convert to a list of integers.
|
|
701
|
+
|
|
702
|
+
Returns
|
|
703
|
+
-------
|
|
704
|
+
list
|
|
705
|
+
A list of integers.
|
|
706
|
+
"""
|
|
707
|
+
if isinstance(value, str):
|
|
708
|
+
if "/" not in value:
|
|
709
|
+
return [int(value)]
|
|
710
|
+
bits = value.split("/")
|
|
711
|
+
if len(bits) == 3 and bits[1].lower() == "to":
|
|
712
|
+
value = list(range(int(bits[0]), int(bits[2]) + 1, 1))
|
|
713
|
+
|
|
714
|
+
elif len(bits) == 5 and bits[1].lower() == "to" and bits[3].lower() == "by":
|
|
715
|
+
value = list(range(int(bits[0]), int(bits[2]) + int(bits[4]), int(bits[4])))
|
|
716
|
+
|
|
717
|
+
if isinstance(value, list):
|
|
718
|
+
return value
|
|
719
|
+
if isinstance(value, tuple):
|
|
720
|
+
return value
|
|
721
|
+
if isinstance(value, int):
|
|
722
|
+
return [value]
|
|
723
|
+
|
|
724
|
+
raise ValueError(f"Cannot make list from {value}")
|
|
@@ -186,7 +186,7 @@
|
|
|
186
186
|
same "printed page" as the copyright notice for easier
|
|
187
187
|
identification within third-party archives.
|
|
188
188
|
|
|
189
|
-
Copyright
|
|
189
|
+
Copyright 2024-2025 Anemoi Contributors
|
|
190
190
|
|
|
191
191
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
192
|
you may not use this file except in compliance with the License.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: anemoi-utils
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.12
|
|
4
4
|
Summary: A package to hold various functions to support training of ML models on ECMWF data.
|
|
5
5
|
Author-email: "European Centre for Medium-Range Weather Forecasts (ECMWF)" <software.support@ecmwf.int>
|
|
6
6
|
License: Apache License
|
|
@@ -191,7 +191,7 @@ License: Apache License
|
|
|
191
191
|
same "printed page" as the copyright notice for easier
|
|
192
192
|
identification within third-party archives.
|
|
193
193
|
|
|
194
|
-
Copyright
|
|
194
|
+
Copyright 2024-2025 Anemoi Contributors
|
|
195
195
|
|
|
196
196
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
197
197
|
you may not use this file except in compliance with the License.
|
|
@@ -226,6 +226,7 @@ Requires-Python: >=3.9
|
|
|
226
226
|
License-File: LICENSE
|
|
227
227
|
Requires-Dist: aniso8601
|
|
228
228
|
Requires-Dist: importlib-metadata; python_version < "3.10"
|
|
229
|
+
Requires-Dist: numpy
|
|
229
230
|
Requires-Dist: python-dateutil
|
|
230
231
|
Requires-Dist: pyyaml
|
|
231
232
|
Requires-Dist: tomli; python_version < "3.11"
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
anemoi/utils/__init__.py,sha256=0u0eIdu5-H1frf6V4KHpNmlh_SS-bJnxjzIejlsLqdw,702
|
|
2
2
|
anemoi/utils/__main__.py,sha256=5NW2A3OgTimB4ptwYThivIRSeCrvabMuvnr8mmnVx0E,715
|
|
3
|
-
anemoi/utils/_version.py,sha256=
|
|
4
|
-
anemoi/utils/caching.py,sha256=
|
|
3
|
+
anemoi/utils/_version.py,sha256=sg-lGJqAzZSE-Cr-6X2XM20x4P3h_gsFXHtA5vFf4qs,413
|
|
4
|
+
anemoi/utils/caching.py,sha256=UkHQOKcBoB6xni83qgTEGfkyam7iqu1YiuBLZQIn9RM,3208
|
|
5
5
|
anemoi/utils/checkpoints.py,sha256=q8QqKlZ6qChjzEfq7KM1gVXuyqgsVRGIb4dJFtkGk58,7774
|
|
6
6
|
anemoi/utils/cli.py,sha256=rmMP60VY3em99rQP6TCrKibMngWwVe5h_0GDcf16c5U,4117
|
|
7
7
|
anemoi/utils/compatibility.py,sha256=0_nIcbdQbNMrS6AkqrBgJGJlSJXW8R23ncaZaDwdJ4c,2190
|
|
8
|
-
anemoi/utils/config.py,sha256=
|
|
9
|
-
anemoi/utils/dates.py,sha256=
|
|
8
|
+
anemoi/utils/config.py,sha256=ADCksBl6qrJpU9eebVebwkthNanzgCOxBgzlqCUTwbg,10962
|
|
9
|
+
anemoi/utils/dates.py,sha256=brsBEje1w8sJ1RbVJl-_nxbSUYsn1h0zaF6EY54zpDs,12785
|
|
10
|
+
anemoi/utils/devtools.py,sha256=Mns5vU9o2HrO4zS1e0-W4gBIhk8xHrhcB7wLR_q6OiA,2172
|
|
10
11
|
anemoi/utils/grib.py,sha256=zBICyOsYtR_9px1C5UDT6wL_D6kiIhUi_00kjFmas5c,3047
|
|
12
|
+
anemoi/utils/grids.py,sha256=tqhH8ZiRS9Re7xQZHVKtl8bBtqH_kDVOGHfDTxu3RuI,2708
|
|
11
13
|
anemoi/utils/hindcasts.py,sha256=TEYDmrZUajuhp_dfWeg6z5c6XfntE-mwugUQJyAgUco,1419
|
|
12
|
-
anemoi/utils/humanize.py,sha256=
|
|
14
|
+
anemoi/utils/humanize.py,sha256=ZD5UMD7m79I1h_IoIQFnd4FZR5K9VARw9cVaNrD1QdM,17579
|
|
13
15
|
anemoi/utils/logs.py,sha256=o0xXiO2BdG_bZkljxxI2TKlCiA5QbWHgAUlYM53lirE,1058
|
|
14
16
|
anemoi/utils/provenance.py,sha256=SqOiNoY1y36Zec83Pjt7OhihbwxMyknscfmogHCuriA,10894
|
|
15
17
|
anemoi/utils/registry.py,sha256=Iit_CfTGuoVffXkZA2A5mUXb4AdGIUX9TpnUqWT4HJ0,4291
|
|
@@ -20,16 +22,16 @@ anemoi/utils/text.py,sha256=Xfr_3wvsjg7m-BwvdJVz1bV6f5KNMnGIIFRtXaiMfbs,10496
|
|
|
20
22
|
anemoi/utils/timer.py,sha256=Twnr3GZu-n0WzgboELRKJWs87qyDYqy6Dwr9cQ_JG18,1803
|
|
21
23
|
anemoi/utils/commands/__init__.py,sha256=O5W3yHZywRoAqmRUioAr3zMCh0hGVV18wZYGvc00ioM,698
|
|
22
24
|
anemoi/utils/commands/config.py,sha256=zt4PFATYJ-zs0C5mpUlrQ4Fj5m1kM3CcsszUP1VBbzA,816
|
|
23
|
-
anemoi/utils/commands/requests.py,sha256=
|
|
25
|
+
anemoi/utils/commands/requests.py,sha256=NZPtQ2-PEtj7w5nNzTq5Tvj5axtsVAeuTUOKIQt-Faw,1555
|
|
24
26
|
anemoi/utils/mars/__init__.py,sha256=kvbu-gSaYI9jSNEzfQltrtHPVIameYGoLjOJKwI7x_U,1723
|
|
25
27
|
anemoi/utils/mars/mars.yaml,sha256=R0dujp75lLA4wCWhPeOQnzJ45WZAYLT8gpx509cBFlc,66
|
|
26
28
|
anemoi/utils/mars/requests.py,sha256=0khe_mbq4GNueR_B8fGPTBoWHtCfjQvtoKXOSVm6La4,759
|
|
27
29
|
anemoi/utils/remote/__init__.py,sha256=-_AA1xm9GpagW5zP0PGpz-3SRKEUjw_AGSNd_bhuh7g,11639
|
|
28
30
|
anemoi/utils/remote/s3.py,sha256=hykbVlh1_aFI00FWjgm_FWIMfVCTFiQf_cq8_gAo31s,11976
|
|
29
31
|
anemoi/utils/remote/ssh.py,sha256=3lqFpY9CEor_DvIK9ZxSmj3rND-366Sm9R3Vw61sWSs,4695
|
|
30
|
-
anemoi_utils-0.4.
|
|
31
|
-
anemoi_utils-0.4.
|
|
32
|
-
anemoi_utils-0.4.
|
|
33
|
-
anemoi_utils-0.4.
|
|
34
|
-
anemoi_utils-0.4.
|
|
35
|
-
anemoi_utils-0.4.
|
|
32
|
+
anemoi_utils-0.4.12.dist-info/LICENSE,sha256=8HznKF1Vi2IvfLsKNE5A2iVyiri3pRjRPvPC9kxs6qk,11354
|
|
33
|
+
anemoi_utils-0.4.12.dist-info/METADATA,sha256=-9E31lmUv9bhpu_e4ZlLRgWtZdrRdeAo5KFzfJg42HU,15255
|
|
34
|
+
anemoi_utils-0.4.12.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
35
|
+
anemoi_utils-0.4.12.dist-info/entry_points.txt,sha256=LENOkn88xzFQo-V59AKoA_F_cfYQTJYtrNTtf37YgHY,60
|
|
36
|
+
anemoi_utils-0.4.12.dist-info/top_level.txt,sha256=DYn8VPs-fNwr7fNH9XIBqeXIwiYYd2E2k5-dUFFqUz0,7
|
|
37
|
+
anemoi_utils-0.4.12.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|