vortex-nwp 2.0.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.
- vortex/__init__.py +159 -0
- vortex/algo/__init__.py +13 -0
- vortex/algo/components.py +2462 -0
- vortex/algo/mpitools.py +1953 -0
- vortex/algo/mpitools_templates/__init__.py +1 -0
- vortex/algo/mpitools_templates/envelope_wrapper_default.tpl +27 -0
- vortex/algo/mpitools_templates/envelope_wrapper_mpiauto.tpl +29 -0
- vortex/algo/mpitools_templates/wrapstd_wrapper_default.tpl +18 -0
- vortex/algo/serversynctools.py +171 -0
- vortex/config.py +112 -0
- vortex/data/__init__.py +19 -0
- vortex/data/abstractstores.py +1510 -0
- vortex/data/containers.py +835 -0
- vortex/data/contents.py +622 -0
- vortex/data/executables.py +275 -0
- vortex/data/flow.py +119 -0
- vortex/data/geometries.ini +2689 -0
- vortex/data/geometries.py +799 -0
- vortex/data/handlers.py +1230 -0
- vortex/data/outflow.py +67 -0
- vortex/data/providers.py +487 -0
- vortex/data/resources.py +207 -0
- vortex/data/stores.py +1390 -0
- vortex/data/sync_templates/__init__.py +0 -0
- vortex/gloves.py +309 -0
- vortex/layout/__init__.py +20 -0
- vortex/layout/contexts.py +577 -0
- vortex/layout/dataflow.py +1220 -0
- vortex/layout/monitor.py +969 -0
- vortex/nwp/__init__.py +14 -0
- vortex/nwp/algo/__init__.py +21 -0
- vortex/nwp/algo/assim.py +537 -0
- vortex/nwp/algo/clim.py +1086 -0
- vortex/nwp/algo/coupling.py +831 -0
- vortex/nwp/algo/eda.py +840 -0
- vortex/nwp/algo/eps.py +785 -0
- vortex/nwp/algo/forecasts.py +886 -0
- vortex/nwp/algo/fpserver.py +1303 -0
- vortex/nwp/algo/ifsnaming.py +463 -0
- vortex/nwp/algo/ifsroot.py +404 -0
- vortex/nwp/algo/monitoring.py +263 -0
- vortex/nwp/algo/mpitools.py +694 -0
- vortex/nwp/algo/odbtools.py +1258 -0
- vortex/nwp/algo/oopsroot.py +916 -0
- vortex/nwp/algo/oopstests.py +220 -0
- vortex/nwp/algo/request.py +660 -0
- vortex/nwp/algo/stdpost.py +1641 -0
- vortex/nwp/data/__init__.py +30 -0
- vortex/nwp/data/assim.py +380 -0
- vortex/nwp/data/boundaries.py +314 -0
- vortex/nwp/data/climfiles.py +521 -0
- vortex/nwp/data/configfiles.py +153 -0
- vortex/nwp/data/consts.py +954 -0
- vortex/nwp/data/ctpini.py +149 -0
- vortex/nwp/data/diagnostics.py +209 -0
- vortex/nwp/data/eda.py +147 -0
- vortex/nwp/data/eps.py +432 -0
- vortex/nwp/data/executables.py +1045 -0
- vortex/nwp/data/fields.py +111 -0
- vortex/nwp/data/gridfiles.py +380 -0
- vortex/nwp/data/logs.py +584 -0
- vortex/nwp/data/modelstates.py +363 -0
- vortex/nwp/data/monitoring.py +193 -0
- vortex/nwp/data/namelists.py +696 -0
- vortex/nwp/data/obs.py +840 -0
- vortex/nwp/data/oopsexec.py +74 -0
- vortex/nwp/data/providers.py +207 -0
- vortex/nwp/data/query.py +206 -0
- vortex/nwp/data/stores.py +160 -0
- vortex/nwp/data/surfex.py +337 -0
- vortex/nwp/syntax/__init__.py +9 -0
- vortex/nwp/syntax/stdattrs.py +437 -0
- vortex/nwp/tools/__init__.py +10 -0
- vortex/nwp/tools/addons.py +40 -0
- vortex/nwp/tools/agt.py +67 -0
- vortex/nwp/tools/bdap.py +59 -0
- vortex/nwp/tools/bdcp.py +41 -0
- vortex/nwp/tools/bdm.py +24 -0
- vortex/nwp/tools/bdmp.py +54 -0
- vortex/nwp/tools/conftools.py +1661 -0
- vortex/nwp/tools/drhook.py +66 -0
- vortex/nwp/tools/grib.py +294 -0
- vortex/nwp/tools/gribdiff.py +104 -0
- vortex/nwp/tools/ifstools.py +203 -0
- vortex/nwp/tools/igastuff.py +273 -0
- vortex/nwp/tools/mars.py +68 -0
- vortex/nwp/tools/odb.py +657 -0
- vortex/nwp/tools/partitioning.py +258 -0
- vortex/nwp/tools/satrad.py +71 -0
- vortex/nwp/util/__init__.py +6 -0
- vortex/nwp/util/async.py +212 -0
- vortex/nwp/util/beacon.py +40 -0
- vortex/nwp/util/diffpygram.py +447 -0
- vortex/nwp/util/ens.py +279 -0
- vortex/nwp/util/hooks.py +139 -0
- vortex/nwp/util/taskdeco.py +85 -0
- vortex/nwp/util/usepygram.py +697 -0
- vortex/nwp/util/usetnt.py +101 -0
- vortex/proxy.py +6 -0
- vortex/sessions.py +374 -0
- vortex/syntax/__init__.py +9 -0
- vortex/syntax/stdattrs.py +867 -0
- vortex/syntax/stddeco.py +185 -0
- vortex/toolbox.py +1117 -0
- vortex/tools/__init__.py +20 -0
- vortex/tools/actions.py +523 -0
- vortex/tools/addons.py +316 -0
- vortex/tools/arm.py +96 -0
- vortex/tools/compression.py +325 -0
- vortex/tools/date.py +27 -0
- vortex/tools/ddhpack.py +10 -0
- vortex/tools/delayedactions.py +782 -0
- vortex/tools/env.py +541 -0
- vortex/tools/folder.py +834 -0
- vortex/tools/grib.py +738 -0
- vortex/tools/lfi.py +953 -0
- vortex/tools/listings.py +423 -0
- vortex/tools/names.py +637 -0
- vortex/tools/net.py +2124 -0
- vortex/tools/odb.py +10 -0
- vortex/tools/parallelism.py +368 -0
- vortex/tools/prestaging.py +210 -0
- vortex/tools/rawfiles.py +10 -0
- vortex/tools/schedulers.py +480 -0
- vortex/tools/services.py +940 -0
- vortex/tools/storage.py +996 -0
- vortex/tools/surfex.py +61 -0
- vortex/tools/systems.py +3976 -0
- vortex/tools/targets.py +440 -0
- vortex/util/__init__.py +9 -0
- vortex/util/config.py +1122 -0
- vortex/util/empty.py +24 -0
- vortex/util/helpers.py +216 -0
- vortex/util/introspection.py +69 -0
- vortex/util/iosponge.py +80 -0
- vortex/util/roles.py +49 -0
- vortex/util/storefunctions.py +129 -0
- vortex/util/structs.py +26 -0
- vortex/util/worker.py +162 -0
- vortex_nwp-2.0.0.dist-info/METADATA +67 -0
- vortex_nwp-2.0.0.dist-info/RECORD +144 -0
- vortex_nwp-2.0.0.dist-info/WHEEL +5 -0
- vortex_nwp-2.0.0.dist-info/licenses/LICENSE +517 -0
- vortex_nwp-2.0.0.dist-info/top_level.txt +1 -0
vortex/nwp/util/ens.py
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A collection of utility functions used in the context of Ensemble forecasts.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import io
|
|
6
|
+
import json
|
|
7
|
+
import re
|
|
8
|
+
import time
|
|
9
|
+
|
|
10
|
+
from bronx.compat import random
|
|
11
|
+
from bronx.fancies import loggers
|
|
12
|
+
from bronx.stdtypes.date import Period
|
|
13
|
+
|
|
14
|
+
from vortex import sessions
|
|
15
|
+
from vortex.data.stores import FunctionStoreCallbackError
|
|
16
|
+
from vortex.util import helpers
|
|
17
|
+
|
|
18
|
+
#: No automatic export
|
|
19
|
+
__all__ = []
|
|
20
|
+
|
|
21
|
+
logger = loggers.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def drawingfunction(options):
|
|
25
|
+
"""Draw a random sample from a *set* of values.
|
|
26
|
+
|
|
27
|
+
This function is designed to be executed by a
|
|
28
|
+
:obj:`vortex.data.stores.FunctionStore` object.
|
|
29
|
+
|
|
30
|
+
The *set* of values is computed using the resource's argument:
|
|
31
|
+
*set = [resource.start, resource.start + resource.nbset - 1]*. If
|
|
32
|
+
*resource.start* does not exists, *resource.start=1* is assumed.
|
|
33
|
+
|
|
34
|
+
The size of the sample is given by the *nblot* argument of the resource
|
|
35
|
+
|
|
36
|
+
The random generator is initialised using the resource's date. Consequently,
|
|
37
|
+
for a given date, the drawing is reproducible.
|
|
38
|
+
|
|
39
|
+
:param dict options: All the options passed to the store plus anything from
|
|
40
|
+
the query part of the URI.
|
|
41
|
+
|
|
42
|
+
:return: Content of a :obj:`nwp.data.ens.Sample` resource
|
|
43
|
+
|
|
44
|
+
:rtype: A file like object
|
|
45
|
+
"""
|
|
46
|
+
rhdict = options.get("rhandler", None)
|
|
47
|
+
if rhdict:
|
|
48
|
+
date = rhdict["resource"]["date"]
|
|
49
|
+
rgen = random.Random()
|
|
50
|
+
rgen.seed(int(date[:-2]))
|
|
51
|
+
nbsample = rhdict["resource"].get("nbsample", 0)
|
|
52
|
+
if not nbsample:
|
|
53
|
+
raise FunctionStoreCallbackError(
|
|
54
|
+
"The resource must hold a non-null nbsample attribute"
|
|
55
|
+
)
|
|
56
|
+
population = rhdict["resource"].get("population", [])
|
|
57
|
+
if not population:
|
|
58
|
+
raise FunctionStoreCallbackError(
|
|
59
|
+
"The resource must hold a non-empty population attribute"
|
|
60
|
+
)
|
|
61
|
+
nbset = len(population)
|
|
62
|
+
|
|
63
|
+
tirage = rgen.sample(
|
|
64
|
+
population * (nbsample // nbset), (nbsample // nbset) * nbset
|
|
65
|
+
) + rgen.sample(population, nbsample % nbset)
|
|
66
|
+
logger.info(
|
|
67
|
+
"List of random elements: %s", ", ".join([str(x) for x in tirage])
|
|
68
|
+
)
|
|
69
|
+
else:
|
|
70
|
+
raise FunctionStoreCallbackError("no resource handler here :-(")
|
|
71
|
+
# NB: The result have to be a file like object !
|
|
72
|
+
outdict = dict(
|
|
73
|
+
vapp=rhdict["provider"].get("vapp", None),
|
|
74
|
+
vconf=rhdict["provider"].get("vconf", None),
|
|
75
|
+
cutoff=rhdict["resource"].get("cutoff", None),
|
|
76
|
+
date=rhdict["resource"].get("date", None),
|
|
77
|
+
resource_kind=rhdict["resource"].get("kind", None),
|
|
78
|
+
drawing=tirage,
|
|
79
|
+
population=population,
|
|
80
|
+
)
|
|
81
|
+
if rhdict["provider"].get("experiment", None) is not None:
|
|
82
|
+
outdict["experiment"] = rhdict["provider"]["experiment"]
|
|
83
|
+
return io.BytesIO(json.dumps(outdict, indent=4).encode(encoding="utf_8"))
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _checkingfunction_dict(options):
|
|
87
|
+
"""
|
|
88
|
+
Internal function that returns a dictionnary that describes the available
|
|
89
|
+
inputs.
|
|
90
|
+
"""
|
|
91
|
+
rhdict = options.get("rhandler", None)
|
|
92
|
+
if rhdict:
|
|
93
|
+
# If no nbsample is provided, easy to achieve...
|
|
94
|
+
nbsample = rhdict["resource"].get("nbsample", None)
|
|
95
|
+
# ...and if no explicit minimum of resources, nbsample is the minimum
|
|
96
|
+
nbmin = int(
|
|
97
|
+
options.get(
|
|
98
|
+
"min",
|
|
99
|
+
[
|
|
100
|
+
(0 if nbsample is None else nbsample),
|
|
101
|
+
],
|
|
102
|
+
).pop()
|
|
103
|
+
)
|
|
104
|
+
if nbsample is not None and nbsample < nbmin:
|
|
105
|
+
logger.warning(
|
|
106
|
+
"%d resources needed, %d required: sin of gluttony ?",
|
|
107
|
+
nbsample,
|
|
108
|
+
nbmin,
|
|
109
|
+
)
|
|
110
|
+
# What to look for ?
|
|
111
|
+
checkrole = rhdict["resource"].get("checkrole", None)
|
|
112
|
+
if not checkrole:
|
|
113
|
+
raise FunctionStoreCallbackError(
|
|
114
|
+
"The resource must hold a non-empty checkrole attribute"
|
|
115
|
+
)
|
|
116
|
+
rolematch = re.match(r"(\w+)(?:\+(\w+))?$", checkrole)
|
|
117
|
+
cur_t = sessions.current()
|
|
118
|
+
if rolematch:
|
|
119
|
+
ctx = cur_t.context
|
|
120
|
+
checklist = [
|
|
121
|
+
sec.rh
|
|
122
|
+
for sec in ctx.sequence.filtered_inputs(
|
|
123
|
+
role=rolematch.group(1)
|
|
124
|
+
)
|
|
125
|
+
]
|
|
126
|
+
mandatorylist = (
|
|
127
|
+
[
|
|
128
|
+
sec.rh
|
|
129
|
+
for sec in ctx.sequence.filtered_inputs(
|
|
130
|
+
role=rolematch.group(2)
|
|
131
|
+
)
|
|
132
|
+
]
|
|
133
|
+
if rolematch.group(2)
|
|
134
|
+
else []
|
|
135
|
+
)
|
|
136
|
+
else:
|
|
137
|
+
raise FunctionStoreCallbackError(
|
|
138
|
+
"checkrole is not properly formatted"
|
|
139
|
+
)
|
|
140
|
+
# Other options
|
|
141
|
+
nretries = int(
|
|
142
|
+
options.get(
|
|
143
|
+
"nretries",
|
|
144
|
+
[
|
|
145
|
+
0,
|
|
146
|
+
],
|
|
147
|
+
).pop()
|
|
148
|
+
)
|
|
149
|
+
retry_wait = Period(
|
|
150
|
+
options.get(
|
|
151
|
+
"retry_wait",
|
|
152
|
+
[
|
|
153
|
+
"PT5M",
|
|
154
|
+
],
|
|
155
|
+
).pop()
|
|
156
|
+
)
|
|
157
|
+
comp_delay = Period(
|
|
158
|
+
options.get(
|
|
159
|
+
"comp_delay",
|
|
160
|
+
[
|
|
161
|
+
0,
|
|
162
|
+
],
|
|
163
|
+
).pop()
|
|
164
|
+
)
|
|
165
|
+
fakecheck = options.get(
|
|
166
|
+
"fakecheck",
|
|
167
|
+
[
|
|
168
|
+
False,
|
|
169
|
+
],
|
|
170
|
+
).pop()
|
|
171
|
+
|
|
172
|
+
def _retry_cond(the_ntries, the_acceptable_time):
|
|
173
|
+
return (
|
|
174
|
+
the_acceptable_time is None and the_ntries <= nretries
|
|
175
|
+
) or (
|
|
176
|
+
the_acceptable_time
|
|
177
|
+
and (time.time() - the_acceptable_time)
|
|
178
|
+
< comp_delay.total_seconds()
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# Ok let's work...
|
|
182
|
+
ntries = 0
|
|
183
|
+
acceptable_time = None
|
|
184
|
+
found = []
|
|
185
|
+
while _retry_cond(ntries, acceptable_time):
|
|
186
|
+
if ntries:
|
|
187
|
+
logger.info(
|
|
188
|
+
"Let's sleep %d sec. before the next check round...",
|
|
189
|
+
retry_wait.total_seconds(),
|
|
190
|
+
)
|
|
191
|
+
cur_t.sh.sleep(retry_wait.total_seconds())
|
|
192
|
+
ntries += 1
|
|
193
|
+
try:
|
|
194
|
+
logger.info("Starting an input check...")
|
|
195
|
+
found, candidates = helpers.colorfull_input_checker(
|
|
196
|
+
nbmin,
|
|
197
|
+
checklist,
|
|
198
|
+
mandatory=mandatorylist,
|
|
199
|
+
fakecheck=fakecheck,
|
|
200
|
+
)
|
|
201
|
+
if acceptable_time is None and (found or nbmin == 0):
|
|
202
|
+
acceptable_time = time.time()
|
|
203
|
+
if comp_delay.total_seconds() and len(found) != len(
|
|
204
|
+
candidates
|
|
205
|
+
):
|
|
206
|
+
logger.info(
|
|
207
|
+
"The minimum required size was reached (nbmin=%d). "
|
|
208
|
+
+ "That's great but we are waiting a little longer "
|
|
209
|
+
+ "(for at most %d sec.)",
|
|
210
|
+
nbmin,
|
|
211
|
+
comp_delay.total_seconds(),
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
if len(found) == len(candidates):
|
|
215
|
+
# No need to wait any longer...
|
|
216
|
+
break
|
|
217
|
+
except helpers.InputCheckerError as e:
|
|
218
|
+
if not _retry_cond(ntries, acceptable_time):
|
|
219
|
+
raise FunctionStoreCallbackError(
|
|
220
|
+
"The input checher failed ({!s})".format(e)
|
|
221
|
+
)
|
|
222
|
+
return found
|
|
223
|
+
else:
|
|
224
|
+
raise FunctionStoreCallbackError("no resource handler here :-(\n")
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def checkingfunction(options):
|
|
228
|
+
"""Check what are the available resources and returns the list.
|
|
229
|
+
|
|
230
|
+
This function is designed to be executed by a
|
|
231
|
+
:obj:`vortex.data.stores.FunctionStore` object.
|
|
232
|
+
|
|
233
|
+
The *checkrole* resource attribute is used to look into the current context
|
|
234
|
+
in order to establish the list of resources that will checked.
|
|
235
|
+
|
|
236
|
+
:param dict options: All the options passed to the store plus anything from
|
|
237
|
+
the query part of the URI.
|
|
238
|
+
|
|
239
|
+
:return: Content of a :obj:`nwp.data.ens.PopulationList` resource
|
|
240
|
+
|
|
241
|
+
:rtype: A file like object
|
|
242
|
+
"""
|
|
243
|
+
rhdict = options.get("rhandler", None)
|
|
244
|
+
avail_list = _checkingfunction_dict(options)
|
|
245
|
+
outdict = dict(
|
|
246
|
+
vapp=rhdict["provider"].get("vapp", None),
|
|
247
|
+
vconf=rhdict["provider"].get("vconf", None),
|
|
248
|
+
cutoff=rhdict["resource"].get("cutoff", None),
|
|
249
|
+
date=rhdict["resource"].get("date", None),
|
|
250
|
+
resource_kind=rhdict["resource"].get("kind", None),
|
|
251
|
+
population=avail_list,
|
|
252
|
+
)
|
|
253
|
+
if rhdict["provider"].get("experiment", None) is not None:
|
|
254
|
+
outdict["experiment"] = rhdict["provider"]["experiment"]
|
|
255
|
+
return io.BytesIO(json.dumps(outdict, indent=4).encode(encoding="utf_8"))
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def safedrawingfunction(options):
|
|
259
|
+
"""Combined called to :func:`checkingfunction` and :func:`drawingfunction`.
|
|
260
|
+
|
|
261
|
+
See the documentation of these two functions for more details.
|
|
262
|
+
"""
|
|
263
|
+
checkedlist = _checkingfunction_dict(options)
|
|
264
|
+
options["rhandler"]["resource"]["population"] = checkedlist
|
|
265
|
+
return drawingfunction(options)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def unsafedrawingfunction(options):
|
|
269
|
+
"""Combined called to :func:`checkingfunction` and :func:`drawingfunction`...
|
|
270
|
+
but with a big lie on the checking: no real check, all the resources are assumed ok.
|
|
271
|
+
|
|
272
|
+
See the documentation of these two functions for more details.
|
|
273
|
+
"""
|
|
274
|
+
options["fakecheck"] = [
|
|
275
|
+
True,
|
|
276
|
+
]
|
|
277
|
+
checkedlist = _checkingfunction_dict(options)
|
|
278
|
+
options["rhandler"]["resource"]["population"] = checkedlist
|
|
279
|
+
return drawingfunction(options)
|
vortex/nwp/util/hooks.py
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Some useful hooks.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import collections.abc
|
|
6
|
+
import functools
|
|
7
|
+
|
|
8
|
+
from bronx.fancies import loggers
|
|
9
|
+
from bronx.stdtypes.date import Date, Period, Time
|
|
10
|
+
|
|
11
|
+
from ..data.query import StaticCutoffDispenser
|
|
12
|
+
|
|
13
|
+
#: No automatic export
|
|
14
|
+
__all__ = []
|
|
15
|
+
|
|
16
|
+
logger = loggers.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def update_namelist(t, rh, *completive_rh):
|
|
20
|
+
"""Update namelist with resource handler(s) given in **completive_rh**."""
|
|
21
|
+
touched = False
|
|
22
|
+
for crh in completive_rh:
|
|
23
|
+
if not isinstance(crh, (list, tuple)):
|
|
24
|
+
crh = [
|
|
25
|
+
crh,
|
|
26
|
+
]
|
|
27
|
+
for arh in crh:
|
|
28
|
+
logger.info(
|
|
29
|
+
"Merging: {!r} :\n{:s}".format(
|
|
30
|
+
arh.container, arh.contents.dumps()
|
|
31
|
+
)
|
|
32
|
+
)
|
|
33
|
+
rh.contents.merge(arh.contents)
|
|
34
|
+
touched = True
|
|
35
|
+
if touched:
|
|
36
|
+
rh.save()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def concatenate(t, rh, *rhlist):
|
|
40
|
+
"""Concatenate *rhlist* after *rh*."""
|
|
41
|
+
blocksize = 32 * 1024 * 1024 # 32Mb
|
|
42
|
+
rh.container.close()
|
|
43
|
+
with rh.container.iod_context():
|
|
44
|
+
myfh = rh.container.iodesc(mode="ab")
|
|
45
|
+
for crh in rhlist:
|
|
46
|
+
if not isinstance(crh, (list, tuple)):
|
|
47
|
+
crh = [
|
|
48
|
+
crh,
|
|
49
|
+
]
|
|
50
|
+
for arh in crh:
|
|
51
|
+
logger.info("Appending %s to self.", str(arh.container))
|
|
52
|
+
with arh.container.iod_context():
|
|
53
|
+
afh = arh.container.iodesc(mode="rb")
|
|
54
|
+
stuff = afh.read(blocksize)
|
|
55
|
+
while stuff:
|
|
56
|
+
myfh.write(stuff)
|
|
57
|
+
stuff = afh.read(blocksize)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def insert_cutoffs(t, rh, rh_cutoff_source, fuse_per_obstype=False):
|
|
61
|
+
"""Read the cutoff from *rh_cutoff_source* and feed them into *rh*.
|
|
62
|
+
|
|
63
|
+
If *fuse_per_obstype* is ``True``, the latest cutoff of a given obstype
|
|
64
|
+
will be used for all the occurences of this obstype.
|
|
65
|
+
"""
|
|
66
|
+
# rh_cutoff_source may be a list
|
|
67
|
+
if isinstance(rh_cutoff_source, list):
|
|
68
|
+
if rh_cutoff_source:
|
|
69
|
+
rh_cutoff_source = rh_cutoff_source[0]
|
|
70
|
+
else:
|
|
71
|
+
ValueError("The resource handler's list is empty.")
|
|
72
|
+
# Get the CutoffDispenser
|
|
73
|
+
import vortex.tools.listings
|
|
74
|
+
|
|
75
|
+
assert vortex.tools.listings
|
|
76
|
+
if rh_cutoff_source.container.actualfmt == "bdmbufr_listing":
|
|
77
|
+
c_disp_callback = functools.partial(
|
|
78
|
+
rh_cutoff_source.contents.data.cutoffs_dispenser,
|
|
79
|
+
fuse_per_obstype=fuse_per_obstype,
|
|
80
|
+
)
|
|
81
|
+
else:
|
|
82
|
+
raise RuntimeError(
|
|
83
|
+
"Incompatible < {!s} > ressource handler".format(rh_cutoff_source)
|
|
84
|
+
)
|
|
85
|
+
# Fill the gaps in the original request
|
|
86
|
+
rh.contents.add_cutoff_info(c_disp_callback())
|
|
87
|
+
# Actually save the result to file
|
|
88
|
+
rh.save()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _new_static_cutoff_dispencer(base_date, cutoffs_def):
|
|
92
|
+
def x_period(p):
|
|
93
|
+
try:
|
|
94
|
+
return Period(p)
|
|
95
|
+
except ValueError:
|
|
96
|
+
return Period(Time(p))
|
|
97
|
+
|
|
98
|
+
if not isinstance(base_date, Date):
|
|
99
|
+
base_date = Date(base_date)
|
|
100
|
+
if isinstance(cutoffs_def, collections.abc.Mapping):
|
|
101
|
+
cutoffs_def = {
|
|
102
|
+
(k if isinstance(k, Period) else x_period(k)): v
|
|
103
|
+
for k, v in cutoffs_def.items()
|
|
104
|
+
}
|
|
105
|
+
cutoffs = {base_date + k: v for k, v in cutoffs_def.items()}
|
|
106
|
+
c_disp = StaticCutoffDispenser(max(cutoffs.keys()), cutoffs)
|
|
107
|
+
else:
|
|
108
|
+
if not isinstance(cutoffs_def, Period):
|
|
109
|
+
cutoffs_def = x_period(cutoffs_def)
|
|
110
|
+
c_disp = StaticCutoffDispenser(base_date + cutoffs_def)
|
|
111
|
+
return c_disp
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def insert_static_cutoffs(t, rh, base_date, cutoffs_def):
|
|
115
|
+
"""Compute the cutoff from *cutoffs_def* and feed them into *rh*.
|
|
116
|
+
|
|
117
|
+
:param base_date: The current analysis time
|
|
118
|
+
:param cutoffs_def: The cutoff time represented as time offset with respect
|
|
119
|
+
to *base_date*. *cutoffs_defs* may be a single value or
|
|
120
|
+
a dictionary. If *cutoffs_def* is a dictionary, it
|
|
121
|
+
associates a cutoff with a list of `obstypes`.
|
|
122
|
+
"""
|
|
123
|
+
# Fill the gaps in the original request
|
|
124
|
+
rh.contents.add_cutoff_info(
|
|
125
|
+
_new_static_cutoff_dispencer(base_date, cutoffs_def)
|
|
126
|
+
)
|
|
127
|
+
# Actually save the result to files
|
|
128
|
+
rh.save()
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def arpifs_obs_error_correl_legacy2oops(t, rh):
|
|
132
|
+
"""Convert a constant file that contains observation errors correlations."""
|
|
133
|
+
if rh.resource.realkind != "correlations":
|
|
134
|
+
raise ValueError("Incompatible resource: {!s}".format(rh))
|
|
135
|
+
if rh.contents[0].startswith("SIGMAO"):
|
|
136
|
+
logger.warning("Non conversion is needed...")
|
|
137
|
+
else:
|
|
138
|
+
rh.contents[:0] = ["SIGMAO unused\n", "1 1.2\n", "CORRELATIONS\n"]
|
|
139
|
+
rh.save()
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A collection of Tasks decorators (to add usual inputs/outputs to existing classes).
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from bronx.fancies import loggers
|
|
6
|
+
|
|
7
|
+
from vortex import toolbox
|
|
8
|
+
|
|
9
|
+
#: No automatic export
|
|
10
|
+
__all__ = []
|
|
11
|
+
|
|
12
|
+
logger = loggers.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def process_needs_lfi_stuff(*kargs, **kwargs):
|
|
16
|
+
"""
|
|
17
|
+
Decorator that update the tasks's ``process`` method in order to retrieve the
|
|
18
|
+
things needed with the FA/LFI file format.
|
|
19
|
+
|
|
20
|
+
Example (the self.conf.cycle Genv/Uenv cycle will be used)::
|
|
21
|
+
|
|
22
|
+
@process_needs_lfi_stuff
|
|
23
|
+
class MyTask(Task):
|
|
24
|
+
|
|
25
|
+
def process(self)
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
Example (the self.conf.arpege_cycle Genv/Uenv cycle will be used)::
|
|
29
|
+
|
|
30
|
+
@process_needs_lfi_stuff(cyclekey='arpege_cycle')
|
|
31
|
+
class MyOtherTask(Task):
|
|
32
|
+
|
|
33
|
+
def process(self)
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
cyclekey = kwargs.pop("cyclekey", "cycle")
|
|
38
|
+
|
|
39
|
+
def decorate_process(cls):
|
|
40
|
+
"""Decorator for Task: get LFI stuff before calling process."""
|
|
41
|
+
original_process = getattr(cls, "process", None)
|
|
42
|
+
if original_process is not None:
|
|
43
|
+
|
|
44
|
+
def process(self, *args, **kwargs):
|
|
45
|
+
_get_lfi_stuff(self, cyclekey)
|
|
46
|
+
original_process(self, *args, **kwargs)
|
|
47
|
+
|
|
48
|
+
process.__doc__ = original_process.__doc__
|
|
49
|
+
cls.process = process
|
|
50
|
+
return cls
|
|
51
|
+
|
|
52
|
+
if kargs:
|
|
53
|
+
return decorate_process(kargs[0])
|
|
54
|
+
else:
|
|
55
|
+
return decorate_process
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _get_lfi_stuff(self, cyclekey):
|
|
59
|
+
"""Get LFI stuff method (called from process)."""
|
|
60
|
+
if "early-fetch" in self.steps or "fetch" in self.steps:
|
|
61
|
+
actualcycle = getattr(self.conf, cyclekey)
|
|
62
|
+
|
|
63
|
+
self.sh.title("Toolbox input tblfiscripts")
|
|
64
|
+
toolbox.input(
|
|
65
|
+
role="LFIScripts",
|
|
66
|
+
genv=actualcycle,
|
|
67
|
+
kind="lfiscripts",
|
|
68
|
+
local="usualtools/tools.lfi.tgz",
|
|
69
|
+
)
|
|
70
|
+
self.sh.title("Toolbox input tbiopoll")
|
|
71
|
+
toolbox.input(
|
|
72
|
+
role="IOPoll",
|
|
73
|
+
format="unknown",
|
|
74
|
+
genv=actualcycle,
|
|
75
|
+
kind="iopoll",
|
|
76
|
+
language="perl",
|
|
77
|
+
local="usualtools/io_poll",
|
|
78
|
+
)
|
|
79
|
+
self.sh.title("Toolbox input tblfitools")
|
|
80
|
+
toolbox.input(
|
|
81
|
+
role="LFITOOLS",
|
|
82
|
+
genv=actualcycle,
|
|
83
|
+
kind="lfitools",
|
|
84
|
+
local="usualtools/lfitools",
|
|
85
|
+
)
|