anemoi-datasets 0.5.7__py3-none-any.whl → 0.5.10__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 (124) hide show
  1. anemoi/datasets/__init__.py +11 -3
  2. anemoi/datasets/__main__.py +2 -3
  3. anemoi/datasets/_version.py +2 -2
  4. anemoi/datasets/commands/__init__.py +2 -3
  5. anemoi/datasets/commands/cleanup.py +9 -0
  6. anemoi/datasets/commands/compare.py +3 -3
  7. anemoi/datasets/commands/copy.py +38 -68
  8. anemoi/datasets/commands/create.py +20 -5
  9. anemoi/datasets/commands/finalise-additions.py +9 -0
  10. anemoi/datasets/commands/finalise.py +9 -0
  11. anemoi/datasets/commands/init-additions.py +9 -0
  12. anemoi/datasets/commands/init.py +9 -0
  13. anemoi/datasets/commands/inspect.py +3 -1
  14. anemoi/datasets/commands/load-additions.py +9 -0
  15. anemoi/datasets/commands/load.py +9 -0
  16. anemoi/datasets/commands/patch.py +9 -0
  17. anemoi/datasets/commands/publish.py +9 -0
  18. anemoi/datasets/commands/scan.py +9 -0
  19. anemoi/datasets/compute/__init__.py +8 -0
  20. anemoi/datasets/compute/recentre.py +3 -2
  21. anemoi/datasets/create/__init__.py +61 -10
  22. anemoi/datasets/create/check.py +4 -3
  23. anemoi/datasets/create/chunks.py +3 -2
  24. anemoi/datasets/create/config.py +5 -5
  25. anemoi/datasets/create/functions/__init__.py +22 -7
  26. anemoi/datasets/create/functions/filters/__init__.py +2 -1
  27. anemoi/datasets/create/functions/filters/empty.py +3 -2
  28. anemoi/datasets/create/functions/filters/noop.py +2 -2
  29. anemoi/datasets/create/functions/filters/pressure_level_relative_humidity_to_specific_humidity.py +3 -2
  30. anemoi/datasets/create/functions/filters/pressure_level_specific_humidity_to_relative_humidity.py +3 -2
  31. anemoi/datasets/create/functions/filters/rename.py +16 -10
  32. anemoi/datasets/create/functions/filters/rotate_winds.py +3 -2
  33. anemoi/datasets/create/functions/filters/single_level_dewpoint_to_relative_humidity.py +3 -2
  34. anemoi/datasets/create/functions/filters/single_level_relative_humidity_to_dewpoint.py +3 -2
  35. anemoi/datasets/create/functions/filters/single_level_relative_humidity_to_specific_humidity.py +2 -2
  36. anemoi/datasets/create/functions/filters/single_level_specific_humidity_to_relative_humidity.py +2 -2
  37. anemoi/datasets/create/functions/filters/speeddir_to_uv.py +3 -2
  38. anemoi/datasets/create/functions/filters/unrotate_winds.py +3 -2
  39. anemoi/datasets/create/functions/filters/uv_to_speeddir.py +3 -2
  40. anemoi/datasets/create/functions/sources/__init__.py +2 -2
  41. anemoi/datasets/create/functions/sources/accumulations.py +10 -4
  42. anemoi/datasets/create/functions/sources/constants.py +3 -2
  43. anemoi/datasets/create/functions/sources/empty.py +3 -2
  44. anemoi/datasets/create/functions/sources/forcings.py +3 -2
  45. anemoi/datasets/create/functions/sources/grib.py +2 -2
  46. anemoi/datasets/create/functions/sources/hindcasts.py +3 -2
  47. anemoi/datasets/create/functions/sources/mars.py +97 -17
  48. anemoi/datasets/create/functions/sources/netcdf.py +3 -2
  49. anemoi/datasets/create/functions/sources/opendap.py +2 -2
  50. anemoi/datasets/create/functions/sources/recentre.py +3 -2
  51. anemoi/datasets/create/functions/sources/source.py +3 -2
  52. anemoi/datasets/create/functions/sources/tendencies.py +3 -2
  53. anemoi/datasets/create/functions/sources/xarray/__init__.py +8 -2
  54. anemoi/datasets/create/functions/sources/xarray/coordinates.py +3 -2
  55. anemoi/datasets/create/functions/sources/xarray/field.py +3 -2
  56. anemoi/datasets/create/functions/sources/xarray/fieldlist.py +12 -2
  57. anemoi/datasets/create/functions/sources/xarray/flavour.py +2 -2
  58. anemoi/datasets/create/functions/sources/xarray/grid.py +2 -2
  59. anemoi/datasets/create/functions/sources/xarray/metadata.py +3 -2
  60. anemoi/datasets/create/functions/sources/xarray/time.py +2 -2
  61. anemoi/datasets/create/functions/sources/xarray/variable.py +6 -6
  62. anemoi/datasets/create/functions/sources/xarray_kerchunk.py +2 -2
  63. anemoi/datasets/create/functions/sources/xarray_zarr.py +2 -2
  64. anemoi/datasets/create/functions/sources/zenodo.py +2 -2
  65. anemoi/datasets/create/input/__init__.py +3 -17
  66. anemoi/datasets/create/input/action.py +3 -2
  67. anemoi/datasets/create/input/concat.py +3 -2
  68. anemoi/datasets/create/input/context.py +3 -2
  69. anemoi/datasets/create/input/data_sources.py +3 -2
  70. anemoi/datasets/create/input/empty.py +3 -2
  71. anemoi/datasets/create/input/filter.py +3 -2
  72. anemoi/datasets/create/input/function.py +3 -2
  73. anemoi/datasets/create/input/join.py +3 -2
  74. anemoi/datasets/create/input/misc.py +3 -2
  75. anemoi/datasets/create/input/pipe.py +3 -2
  76. anemoi/datasets/create/input/repeated_dates.py +3 -2
  77. anemoi/datasets/create/input/result.py +154 -6
  78. anemoi/datasets/create/input/step.py +4 -2
  79. anemoi/datasets/create/input/template.py +3 -2
  80. anemoi/datasets/create/input/trace.py +3 -2
  81. anemoi/datasets/create/patch.py +9 -1
  82. anemoi/datasets/create/persistent.py +3 -2
  83. anemoi/datasets/create/size.py +3 -2
  84. anemoi/datasets/create/statistics/__init__.py +3 -2
  85. anemoi/datasets/create/statistics/summary.py +3 -2
  86. anemoi/datasets/create/utils.py +15 -2
  87. anemoi/datasets/create/writer.py +3 -2
  88. anemoi/datasets/create/zarr.py +3 -2
  89. anemoi/datasets/data/__init__.py +27 -1
  90. anemoi/datasets/data/concat.py +5 -1
  91. anemoi/datasets/data/dataset.py +216 -37
  92. anemoi/datasets/data/debug.py +4 -1
  93. anemoi/datasets/data/ensemble.py +4 -1
  94. anemoi/datasets/data/fill_missing.py +165 -0
  95. anemoi/datasets/data/forwards.py +23 -1
  96. anemoi/datasets/data/grids.py +236 -58
  97. anemoi/datasets/data/indexing.py +4 -1
  98. anemoi/datasets/data/interpolate.py +4 -1
  99. anemoi/datasets/data/join.py +12 -9
  100. anemoi/datasets/data/masked.py +36 -10
  101. anemoi/datasets/data/merge.py +180 -0
  102. anemoi/datasets/data/misc.py +18 -3
  103. anemoi/datasets/data/missing.py +4 -1
  104. anemoi/datasets/data/rescale.py +4 -1
  105. anemoi/datasets/data/select.py +4 -1
  106. anemoi/datasets/data/statistics.py +4 -1
  107. anemoi/datasets/data/stores.py +66 -3
  108. anemoi/datasets/data/subset.py +6 -1
  109. anemoi/datasets/data/unchecked.py +4 -1
  110. anemoi/datasets/data/xy.py +20 -5
  111. anemoi/datasets/dates/__init__.py +9 -7
  112. anemoi/datasets/dates/groups.py +3 -1
  113. anemoi/datasets/fields.py +3 -1
  114. anemoi/datasets/grids.py +86 -2
  115. anemoi/datasets/testing.py +3 -2
  116. anemoi/datasets/utils/__init__.py +8 -0
  117. anemoi/datasets/utils/fields.py +2 -2
  118. {anemoi_datasets-0.5.7.dist-info → anemoi_datasets-0.5.10.dist-info}/METADATA +11 -29
  119. anemoi_datasets-0.5.10.dist-info/RECORD +124 -0
  120. {anemoi_datasets-0.5.7.dist-info → anemoi_datasets-0.5.10.dist-info}/WHEEL +1 -1
  121. anemoi_datasets-0.5.7.dist-info/RECORD +0 -122
  122. {anemoi_datasets-0.5.7.dist-info → anemoi_datasets-0.5.10.dist-info}/LICENSE +0 -0
  123. {anemoi_datasets-0.5.7.dist-info → anemoi_datasets-0.5.10.dist-info}/entry_points.txt +0 -0
  124. {anemoi_datasets-0.5.7.dist-info → anemoi_datasets-0.5.10.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,11 @@
1
- # (C) Copyright 2020 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
9
 
10
10
 
11
11
  import importlib
@@ -21,6 +21,8 @@ def assert_is_fieldlist(obj):
21
21
 
22
22
  def import_function(name, kind):
23
23
 
24
+ from anemoi.transform.filters import filter_registry
25
+
24
26
  name = name.replace("-", "_")
25
27
 
26
28
  plugins = {}
@@ -30,8 +32,21 @@ def import_function(name, kind):
30
32
  if name in plugins:
31
33
  return plugins[name].load()
32
34
 
33
- module = importlib.import_module(
34
- f".{kind}.{name}",
35
- package=__name__,
36
- )
37
- return module.execute
35
+ try:
36
+ module = importlib.import_module(
37
+ f".{kind}.{name}",
38
+ package=__name__,
39
+ )
40
+ return module.execute
41
+ except ModuleNotFoundError:
42
+ pass
43
+
44
+ if kind == "filters":
45
+ if filter_registry.lookup(name, return_none=True):
46
+
47
+ def proc(context, data, *args, **kwargs):
48
+ return filter_registry.create(name, *args, **kwargs)(data)
49
+
50
+ return proc
51
+
52
+ raise ValueError(f"Unknown {kind} '{name}'")
@@ -1,7 +1,8 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
@@ -1,11 +1,12 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
+
9
10
 
10
11
  import earthkit.data as ekd
11
12
 
@@ -1,11 +1,11 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
9
 
10
10
 
11
11
  def execute(context, input, *args, **kwargs):
@@ -1,11 +1,12 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
+
9
10
 
10
11
  from collections import defaultdict
11
12
 
@@ -1,11 +1,12 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
+
9
10
 
10
11
  from collections import defaultdict
11
12
 
@@ -1,11 +1,12 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
+
9
10
 
10
11
  import re
11
12
 
@@ -24,7 +25,9 @@ class RenamedFieldMapping:
24
25
  def __init__(self, field, what, renaming):
25
26
  self.field = field
26
27
  self.what = what
27
- self.renaming = renaming
28
+ self.renaming = {}
29
+ for k, v in renaming.items():
30
+ self.renaming[k] = {str(a): str(b) for a, b in v.items()}
28
31
 
29
32
  def metadata(self, key=None, **kwargs):
30
33
  if key is None:
@@ -51,16 +54,19 @@ class RenamedFieldFormat:
51
54
  format (str): A string that defines the new name of the field.
52
55
  """
53
56
 
54
- def __init__(self, field, format):
57
+ def __init__(self, field, what, format):
55
58
  self.field = field
59
+ self.what = what
56
60
  self.format = format
57
61
  self.bits = re.findall(r"{(\w+)}", format)
58
62
 
59
- def metadata(self, key, **kwargs):
60
- value = self.field.metadata(key, **kwargs)
61
- if "{" + key + "}" in self.format:
62
- bits = {b: self.field.metadata(b, **kwargs) for b in self.bits}
63
- return self.format.format(**bits)
63
+ def metadata(self, *args, **kwargs):
64
+ value = self.field.metadata(*args, **kwargs)
65
+ if args:
66
+ assert len(args) == 1
67
+ if args[0] == self.what:
68
+ bits = {b: self.field.metadata(b, **kwargs) for b in self.bits}
69
+ return self.format.format(**bits)
64
70
  return value
65
71
 
66
72
  def __getattr__(self, name):
@@ -69,6 +75,6 @@ class RenamedFieldFormat:
69
75
 
70
76
  def execute(context, input, what="param", **kwargs):
71
77
  if what in kwargs and isinstance(kwargs[what], str):
72
- return FieldArray([RenamedFieldFormat(fs, kwargs[what]) for fs in input])
78
+ return FieldArray([RenamedFieldFormat(fs, what, kwargs[what]) for fs in input])
73
79
 
74
80
  return FieldArray([RenamedFieldMapping(fs, what, kwargs) for fs in input])
@@ -1,11 +1,12 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
+
9
10
 
10
11
  from collections import defaultdict
11
12
 
@@ -1,11 +1,12 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
+
9
10
 
10
11
  from collections import defaultdict
11
12
 
@@ -1,11 +1,12 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
+
9
10
 
10
11
  from collections import defaultdict
11
12
 
@@ -1,11 +1,11 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
9
 
10
10
 
11
11
  import numpy as np
@@ -1,11 +1,11 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
9
 
10
10
 
11
11
  import numpy as np
@@ -1,11 +1,12 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
+
9
10
 
10
11
  from collections import defaultdict
11
12
 
@@ -1,11 +1,12 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
+
9
10
 
10
11
  from collections import defaultdict
11
12
 
@@ -1,11 +1,12 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
+
9
10
 
10
11
  from collections import defaultdict
11
12
 
@@ -1,11 +1,11 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
9
 
10
10
  import glob
11
11
  import logging
@@ -1,11 +1,12 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
+
9
10
  import datetime
10
11
  import logging
11
12
  import warnings
@@ -327,7 +328,9 @@ def _compute_accumulations(
327
328
  _member(field),
328
329
  )
329
330
  values = field.values # optimisation
330
- assert accumulations[key], key
331
+ if key not in accumulations:
332
+ raise ValueError(f"Key not found: {key}. Is it an accumulation field?")
333
+
331
334
  for a in accumulations[key]:
332
335
  a.add(field, values)
333
336
 
@@ -370,12 +373,15 @@ def accumulations(context, dates, **request):
370
373
 
371
374
  user_accumulation_period = request.pop("accumulation_period", 6)
372
375
 
376
+ # If `data_accumulation_period` is not set, this means that the accumulations are from the start
377
+ # of the forecast.
378
+
373
379
  KWARGS = {
374
380
  ("od", "oper"): dict(patch=_scda),
375
381
  ("od", "elda"): dict(base_times=(6, 18)),
376
382
  ("ea", "oper"): dict(data_accumulation_period=1, base_times=(6, 18)),
377
383
  ("ea", "enda"): dict(data_accumulation_period=3, base_times=(6, 18)),
378
- ("rr", "oper"): dict(data_accumulation_period=3, base_times=(0, 3, 6, 9, 12, 15, 18, 21)),
384
+ ("rr", "oper"): dict(base_times=(0, 3, 6, 9, 12, 15, 18, 21)),
379
385
  ("l5", "oper"): dict(data_accumulation_period=1, base_times=(0,)),
380
386
  }
381
387
 
@@ -1,11 +1,12 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
+
9
10
  from earthkit.data import from_source
10
11
 
11
12
 
@@ -1,11 +1,12 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
+
9
10
 
10
11
  import earthkit.data as ekd
11
12
 
@@ -1,11 +1,12 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
+
9
10
  from earthkit.data import from_source
10
11
 
11
12
 
@@ -1,11 +1,11 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
9
 
10
10
 
11
11
  import glob
@@ -1,11 +1,12 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
+
9
10
  import logging
10
11
 
11
12
  from earthkit.data.core.fieldlist import MultiFieldList
@@ -1,12 +1,14 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
+
9
10
  import datetime
11
+ import re
10
12
 
11
13
  from anemoi.utils.humanize import did_you_mean
12
14
  from earthkit.data import from_source
@@ -31,6 +33,25 @@ def _date_to_datetime(d):
31
33
  return datetime.datetime.fromisoformat(d)
32
34
 
33
35
 
36
+ def expand_to_by(x):
37
+
38
+ if isinstance(x, (str, int)):
39
+ return expand_to_by(str(x).split("/"))
40
+
41
+ if len(x) == 3 and x[1] == "to":
42
+ start = int(x[0])
43
+ end = int(x[2])
44
+ return list(range(start, end + 1))
45
+
46
+ if len(x) == 5 and x[1] == "to" and x[3] == "by":
47
+ start = int(x[0])
48
+ end = int(x[2])
49
+ by = int(x[4])
50
+ return list(range(start, end + 1, by))
51
+
52
+ return x
53
+
54
+
34
55
  def normalise_time_delta(t):
35
56
  if isinstance(t, datetime.timedelta):
36
57
  assert t == datetime.timedelta(hours=t.hours), t
@@ -42,25 +63,48 @@ def normalise_time_delta(t):
42
63
  return t
43
64
 
44
65
 
66
+ def _normalise_time(t):
67
+ t = int(t)
68
+ if t < 100:
69
+ t * 100
70
+ return "{:04d}".format(t)
71
+
72
+
45
73
  def _expand_mars_request(request, date, request_already_using_valid_datetime=False, date_key="date"):
46
74
  requests = []
47
- step = to_list(request.get("step", [0]))
48
- for s in step:
75
+
76
+ user_step = to_list(expand_to_by(request.get("step", [0])))
77
+ user_time = None
78
+ user_date = None
79
+
80
+ if not request_already_using_valid_datetime:
81
+ user_time = request.get("time")
82
+ if user_time is not None:
83
+ user_time = to_list(user_time)
84
+ user_time = [_normalise_time(t) for t in user_time]
85
+
86
+ user_date = request.get(date_key)
87
+ if user_date is not None:
88
+ assert isinstance(user_date, str), user_date
89
+ user_date = re.compile("^{}$".format(user_date.replace("-", "").replace("?", ".")))
90
+
91
+ for step in user_step:
49
92
  r = request.copy()
50
93
 
51
94
  if not request_already_using_valid_datetime:
52
95
 
53
- if isinstance(s, str) and "-" in s:
54
- assert s.count("-") == 1, s
96
+ if isinstance(step, str) and "-" in step:
97
+ assert step.count("-") == 1, step
98
+
55
99
  # this takes care of the cases where the step is a period such as 0-24 or 12-24
56
- hours = int(str(s).split("-")[-1])
100
+ hours = int(str(step).split("-")[-1])
57
101
 
58
102
  base = date - datetime.timedelta(hours=hours)
59
103
  r.update(
60
104
  {
61
105
  date_key: base.strftime("%Y%m%d"),
62
106
  "time": base.strftime("%H%M"),
63
- "step": s,
107
+ "step": step,
64
108
  }
65
109
  )
66
110
 
@@ -69,12 +113,28 @@ def _expand_mars_request(request, date, request_already_using_valid_datetime=Fal
69
113
  if isinstance(r[pproc], (list, tuple)):
70
114
  r[pproc] = "/".join(str(x) for x in r[pproc])
71
115
 
116
+ if user_date is not None:
117
+ if not user_date.match(r[date_key]):
118
+ continue
119
+
120
+ if user_time is not None:
121
+ # It time is provided by the user, we only keep the requests that match the time
122
+ if r["time"] not in user_time:
123
+ continue
124
+
72
125
  requests.append(r)
73
126
 
127
+ # assert requests, requests
128
+
74
129
  return requests
75
130
 
76
131
 
77
- def factorise_requests(dates, *requests, request_already_using_valid_datetime=False, date_key="date"):
132
+ def factorise_requests(
133
+ dates,
134
+ *requests,
135
+ request_already_using_valid_datetime=False,
136
+ date_key="date",
137
+ ):
78
138
  updates = []
79
139
  for req in requests:
80
140
  # req = normalise_request(req)
@@ -87,6 +147,9 @@ def factorise_requests(dates, *requests, request_already_using_valid_datetime=Fa
87
147
  date_key=date_key,
88
148
  )
89
149
 
150
+ if not updates:
151
+ return
152
+
90
153
  compressed = Availability(updates)
91
154
  for r in compressed.iterate():
92
155
  for k, v in r.items():
@@ -177,13 +240,24 @@ MARS_KEYS = [
177
240
  ]
178
241
 
179
242
 
180
- def mars(context, dates, *requests, request_already_using_valid_datetime=False, date_key="date", **kwargs):
243
+ def mars(
244
+ context,
245
+ dates,
246
+ *requests,
247
+ request_already_using_valid_datetime=False,
248
+ date_key="date",
249
+ **kwargs,
250
+ ):
251
+
181
252
  if not requests:
182
253
  requests = [kwargs]
183
254
 
184
255
  for r in requests:
256
+ param = r.get("param", [])
257
+ if not isinstance(param, (list, tuple)):
258
+ param = [param]
185
259
  # check for "Norway bug" where yaml transforms 'no' into False, etc.
186
- for p in r.get("param", []):
260
+ for p in param:
187
261
  if p is False:
188
262
  raise ValueError(
189
263
  "'param' cannot be 'False'. If you wrote 'param: no' or 'param: off' in yaml, you may want to use quotes?"
@@ -197,12 +271,18 @@ def mars(context, dates, *requests, request_already_using_valid_datetime=False,
197
271
  "'param' cannot be 'True'. If you wrote 'param: on' in yaml, you may want to use quotes?"
198
272
  )
199
273
 
200
- requests = factorise_requests(
201
- dates,
202
- *requests,
203
- request_already_using_valid_datetime=request_already_using_valid_datetime,
204
- date_key=date_key,
205
- )
274
+ if len(dates) == 0: # When using `repeated_dates`
275
+ assert len(requests) == 1, requests
276
+ assert "date" in requests[0], requests[0]
277
+ if isinstance(requests[0]["date"], datetime.date):
278
+ requests[0]["date"] = requests[0]["date"].strftime("%Y%m%d")
279
+ else:
280
+ requests = factorise_requests(
281
+ dates,
282
+ *requests,
283
+ request_already_using_valid_datetime=request_already_using_valid_datetime,
284
+ date_key=date_key,
285
+ )
206
286
 
207
287
  requests = list(requests)
208
288
 
@@ -1,11 +1,12 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
+
9
10
 
10
11
  from .xarray import load_many
11
12
 
@@ -1,11 +1,11 @@
1
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
2
  #
3
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
4
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
5
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
6
7
  # granted to it by virtue of its status as an intergovernmental organisation
7
8
  # nor does it submit to any jurisdiction.
8
- #
9
9
 
10
10
 
11
11
  from .xarray import load_many