vortex-nwp 2.0.0b1__py3-none-any.whl → 2.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 (141) hide show
  1. vortex/__init__.py +75 -47
  2. vortex/algo/__init__.py +3 -2
  3. vortex/algo/components.py +944 -618
  4. vortex/algo/mpitools.py +802 -497
  5. vortex/algo/mpitools_templates/__init__.py +1 -0
  6. vortex/algo/serversynctools.py +34 -33
  7. vortex/config.py +19 -22
  8. vortex/data/__init__.py +9 -3
  9. vortex/data/abstractstores.py +593 -655
  10. vortex/data/containers.py +217 -162
  11. vortex/data/contents.py +65 -39
  12. vortex/data/executables.py +93 -102
  13. vortex/data/flow.py +40 -34
  14. vortex/data/geometries.py +228 -132
  15. vortex/data/handlers.py +436 -227
  16. vortex/data/outflow.py +15 -15
  17. vortex/data/providers.py +185 -163
  18. vortex/data/resources.py +48 -42
  19. vortex/data/stores.py +540 -417
  20. vortex/data/sync_templates/__init__.py +0 -0
  21. vortex/gloves.py +114 -87
  22. vortex/layout/__init__.py +1 -8
  23. vortex/layout/contexts.py +150 -84
  24. vortex/layout/dataflow.py +353 -202
  25. vortex/layout/monitor.py +264 -128
  26. vortex/nwp/__init__.py +5 -2
  27. vortex/nwp/algo/__init__.py +14 -5
  28. vortex/nwp/algo/assim.py +205 -151
  29. vortex/nwp/algo/clim.py +683 -517
  30. vortex/nwp/algo/coupling.py +447 -225
  31. vortex/nwp/algo/eda.py +437 -229
  32. vortex/nwp/algo/eps.py +403 -231
  33. vortex/nwp/algo/forecasts.py +416 -275
  34. vortex/nwp/algo/fpserver.py +683 -307
  35. vortex/nwp/algo/ifsnaming.py +205 -145
  36. vortex/nwp/algo/ifsroot.py +215 -122
  37. vortex/nwp/algo/monitoring.py +137 -76
  38. vortex/nwp/algo/mpitools.py +330 -190
  39. vortex/nwp/algo/odbtools.py +637 -353
  40. vortex/nwp/algo/oopsroot.py +454 -273
  41. vortex/nwp/algo/oopstests.py +90 -56
  42. vortex/nwp/algo/request.py +287 -206
  43. vortex/nwp/algo/stdpost.py +878 -522
  44. vortex/nwp/data/__init__.py +22 -4
  45. vortex/nwp/data/assim.py +125 -137
  46. vortex/nwp/data/boundaries.py +121 -68
  47. vortex/nwp/data/climfiles.py +193 -211
  48. vortex/nwp/data/configfiles.py +73 -69
  49. vortex/nwp/data/consts.py +426 -401
  50. vortex/nwp/data/ctpini.py +59 -43
  51. vortex/nwp/data/diagnostics.py +94 -66
  52. vortex/nwp/data/eda.py +50 -51
  53. vortex/nwp/data/eps.py +195 -146
  54. vortex/nwp/data/executables.py +440 -434
  55. vortex/nwp/data/fields.py +63 -48
  56. vortex/nwp/data/gridfiles.py +183 -111
  57. vortex/nwp/data/logs.py +250 -217
  58. vortex/nwp/data/modelstates.py +180 -151
  59. vortex/nwp/data/monitoring.py +72 -99
  60. vortex/nwp/data/namelists.py +254 -202
  61. vortex/nwp/data/obs.py +400 -308
  62. vortex/nwp/data/oopsexec.py +22 -20
  63. vortex/nwp/data/providers.py +90 -65
  64. vortex/nwp/data/query.py +71 -82
  65. vortex/nwp/data/stores.py +49 -36
  66. vortex/nwp/data/surfex.py +136 -137
  67. vortex/nwp/syntax/__init__.py +1 -1
  68. vortex/nwp/syntax/stdattrs.py +173 -111
  69. vortex/nwp/tools/__init__.py +2 -2
  70. vortex/nwp/tools/addons.py +22 -17
  71. vortex/nwp/tools/agt.py +24 -12
  72. vortex/nwp/tools/bdap.py +16 -5
  73. vortex/nwp/tools/bdcp.py +4 -1
  74. vortex/nwp/tools/bdm.py +3 -0
  75. vortex/nwp/tools/bdmp.py +14 -9
  76. vortex/nwp/tools/conftools.py +728 -378
  77. vortex/nwp/tools/drhook.py +12 -8
  78. vortex/nwp/tools/grib.py +65 -39
  79. vortex/nwp/tools/gribdiff.py +22 -17
  80. vortex/nwp/tools/ifstools.py +82 -42
  81. vortex/nwp/tools/igastuff.py +167 -143
  82. vortex/nwp/tools/mars.py +14 -2
  83. vortex/nwp/tools/odb.py +234 -125
  84. vortex/nwp/tools/partitioning.py +61 -37
  85. vortex/nwp/tools/satrad.py +27 -12
  86. vortex/nwp/util/async.py +83 -55
  87. vortex/nwp/util/beacon.py +10 -10
  88. vortex/nwp/util/diffpygram.py +174 -86
  89. vortex/nwp/util/ens.py +144 -63
  90. vortex/nwp/util/hooks.py +30 -19
  91. vortex/nwp/util/taskdeco.py +28 -24
  92. vortex/nwp/util/usepygram.py +278 -172
  93. vortex/nwp/util/usetnt.py +31 -17
  94. vortex/sessions.py +72 -39
  95. vortex/syntax/__init__.py +1 -1
  96. vortex/syntax/stdattrs.py +410 -171
  97. vortex/syntax/stddeco.py +31 -22
  98. vortex/toolbox.py +327 -192
  99. vortex/tools/__init__.py +11 -2
  100. vortex/tools/actions.py +110 -121
  101. vortex/tools/addons.py +111 -92
  102. vortex/tools/arm.py +42 -22
  103. vortex/tools/compression.py +72 -69
  104. vortex/tools/date.py +11 -4
  105. vortex/tools/delayedactions.py +242 -132
  106. vortex/tools/env.py +75 -47
  107. vortex/tools/folder.py +342 -171
  108. vortex/tools/grib.py +341 -162
  109. vortex/tools/lfi.py +423 -216
  110. vortex/tools/listings.py +109 -40
  111. vortex/tools/names.py +218 -156
  112. vortex/tools/net.py +655 -299
  113. vortex/tools/parallelism.py +93 -61
  114. vortex/tools/prestaging.py +55 -31
  115. vortex/tools/schedulers.py +172 -105
  116. vortex/tools/services.py +403 -334
  117. vortex/tools/storage.py +293 -358
  118. vortex/tools/surfex.py +24 -24
  119. vortex/tools/systems.py +1234 -643
  120. vortex/tools/targets.py +156 -100
  121. vortex/util/__init__.py +1 -1
  122. vortex/util/config.py +378 -327
  123. vortex/util/empty.py +2 -2
  124. vortex/util/helpers.py +56 -24
  125. vortex/util/introspection.py +18 -12
  126. vortex/util/iosponge.py +8 -4
  127. vortex/util/roles.py +4 -6
  128. vortex/util/storefunctions.py +39 -13
  129. vortex/util/structs.py +3 -3
  130. vortex/util/worker.py +29 -17
  131. vortex_nwp-2.1.0.dist-info/METADATA +67 -0
  132. vortex_nwp-2.1.0.dist-info/RECORD +144 -0
  133. {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.1.0.dist-info}/WHEEL +1 -1
  134. vortex/layout/appconf.py +0 -109
  135. vortex/layout/jobs.py +0 -1276
  136. vortex/layout/nodes.py +0 -1424
  137. vortex/layout/subjobs.py +0 -464
  138. vortex_nwp-2.0.0b1.dist-info/METADATA +0 -50
  139. vortex_nwp-2.0.0b1.dist-info/RECORD +0 -146
  140. {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.1.0.dist-info/licenses}/LICENSE +0 -0
  141. {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.1.0.dist-info}/top_level.txt +0 -0
vortex/tools/grib.py CHANGED
@@ -10,6 +10,7 @@ It also provdes an AlgoComponent's Mixin to properly setup the environment
10
10
  when using the grib_api or ecCodes libraries.
11
11
  """
12
12
 
13
+ from pathlib import Path
13
14
  from urllib import parse as urlparse
14
15
 
15
16
  import re
@@ -19,7 +20,10 @@ import footprints
19
20
 
20
21
  from . import addons
21
22
  from vortex.config import get_from_config_w_default
22
- from vortex.algo.components import AlgoComponentDecoMixin, algo_component_deco_mixin_autodoc
23
+ from vortex.algo.components import (
24
+ AlgoComponentDecoMixin,
25
+ algo_component_deco_mixin_autodoc,
26
+ )
23
27
  from vortex.tools.net import DEFAULT_FTP_PORT
24
28
 
25
29
  #: No automatic export
@@ -30,7 +34,7 @@ logger = loggers.getLogger(__name__)
30
34
 
31
35
  def use_in_shell(sh, **kw):
32
36
  """Extend current shell with the LFI interface defined by optional arguments."""
33
- kw['shell'] = sh
37
+ kw["shell"] = sh
34
38
  return footprints.proxy.addon(**kw)
35
39
 
36
40
 
@@ -38,13 +42,14 @@ class GRIB_Tool(addons.FtrawEnableAddon):
38
42
  """
39
43
  Handle multipart-GRIB files properly.
40
44
  """
45
+
41
46
  _footprint = dict(
42
- info = 'Default GRIB system interface',
43
- attr = dict(
44
- kind = dict(
45
- values = ['grib'],
47
+ info="Default GRIB system interface",
48
+ attr=dict(
49
+ kind=dict(
50
+ values=["grib"],
46
51
  ),
47
- )
52
+ ),
48
53
  )
49
54
 
50
55
  def _std_grib_index_get(self, source):
@@ -55,24 +60,33 @@ class GRIB_Tool(addons.FtrawEnableAddon):
55
60
  xgrib_index_get = _std_grib_index_get
56
61
 
57
62
  def _std_grib_index_write(self, destination, gribpaths):
58
- gribparts = [str(urlparse.urlunparse(('file', '', path, '', '', '')))
59
- for path in gribpaths]
63
+ gribparts = [
64
+ str(urlparse.urlunparse(("file", "", path, "", "", "")))
65
+ for path in gribpaths
66
+ ]
60
67
  tmpfile = self.sh.safe_fileaddsuffix(destination)
61
- with open(tmpfile, 'w') as fd:
62
- fd.write('\n'.join(gribparts))
68
+ with open(tmpfile, "w") as fd:
69
+ fd.write("\n".join(gribparts))
63
70
  return self.sh.move(tmpfile, destination)
64
71
 
65
72
  def is_xgrib(self, source):
66
73
  """Check if the given ``source`` is a multipart-GRIB file."""
67
74
  rc = False
68
75
  if source and isinstance(source, str) and self.sh.path.exists(source):
69
- with open(source, 'rb') as fd:
70
- rc = fd.read(7) == b'file://'
76
+ with open(source, "rb") as fd:
77
+ rc = fd.read(7) == b"file://"
71
78
  return rc
72
79
 
73
- def _backend_cp(self, source, destination, smartcp_threshold=0, intent='in'):
74
- return self.sh.cp(source, destination,
75
- smartcp_threshold=smartcp_threshold, intent=intent, smartcp=True)
80
+ def _backend_cp(
81
+ self, source, destination, smartcp_threshold=0, intent="in"
82
+ ):
83
+ return self.sh.cp(
84
+ source,
85
+ destination,
86
+ smartcp_threshold=smartcp_threshold,
87
+ intent=intent,
88
+ smartcp=True,
89
+ )
76
90
 
77
91
  def _backend_rm(self, *args):
78
92
  return self.sh.rm(*args)
@@ -103,8 +117,15 @@ class GRIB_Tool(addons.FtrawEnableAddon):
103
117
 
104
118
  grib_rm = grib_remove = _std_remove
105
119
 
106
- def _std_copy(self, source, destination,
107
- smartcp_threshold=0, intent='in', pack=False, silent=False):
120
+ def _std_copy(
121
+ self,
122
+ source,
123
+ destination,
124
+ smartcp_threshold=0,
125
+ intent="in",
126
+ pack=False,
127
+ silent=False,
128
+ ):
108
129
  """Extended copy for (possibly) multi GRIB file."""
109
130
  # Might be multipart
110
131
  if self.is_xgrib(source):
@@ -112,22 +133,38 @@ class GRIB_Tool(addons.FtrawEnableAddon):
112
133
  if isinstance(destination, str) and not pack:
113
134
  with self.sh.mute_stderr():
114
135
  idx = self._std_grib_index_get(source)
115
- destdir = self.sh.path.abspath(self.sh.path.expanduser(destination) + ".d")
136
+ destdir = self.sh.path.abspath(
137
+ self.sh.path.expanduser(destination) + ".d"
138
+ )
116
139
  rc = rc and self.sh.mkdir(destdir)
117
140
  target_idx = list()
118
- for (i, a_mpart) in enumerate(idx):
119
- target_idx.append(self.sh.path.join(destdir, 'GRIB_mpart{:06d}'.format(i)))
120
- rc = rc and self._backend_cp(a_mpart, target_idx[-1],
121
- smartcp_threshold=smartcp_threshold, intent=intent)
122
- rc = rc and self._std_grib_index_write(destination, target_idx)
123
- if intent == 'in':
141
+ for i, a_mpart in enumerate(idx):
142
+ target_idx.append(
143
+ self.sh.path.join(
144
+ destdir, "GRIB_mpart{:06d}".format(i)
145
+ )
146
+ )
147
+ rc = rc and self._backend_cp(
148
+ a_mpart,
149
+ target_idx[-1],
150
+ smartcp_threshold=smartcp_threshold,
151
+ intent=intent,
152
+ )
153
+ rc = rc and self._std_grib_index_write(
154
+ destination, target_idx
155
+ )
156
+ if intent == "in":
124
157
  self.sh.chmod(destination, 0o444)
125
158
  else:
126
159
  rc = rc and self.xgrib_pack(source, destination)
127
160
  else:
128
161
  # Usual file or file descriptor
129
- rc = self._backend_cp(source, destination,
130
- smartcp_threshold=smartcp_threshold, intent=intent)
162
+ rc = self._backend_cp(
163
+ source,
164
+ destination,
165
+ smartcp_threshold=smartcp_threshold,
166
+ intent=intent,
167
+ )
131
168
  return rc
132
169
 
133
170
  grib_cp = grib_copy = _std_copy
@@ -136,7 +173,7 @@ class GRIB_Tool(addons.FtrawEnableAddon):
136
173
  """Extended mv for (possibly) multi GRIB file."""
137
174
  # Might be multipart
138
175
  if self.is_xgrib(source):
139
- intent = 'inout' if self.sh.access(source, self.sh.W_OK) else 'in'
176
+ intent = "inout" if self.sh.access(source, self.sh.W_OK) else "in"
140
177
  rc = self._std_copy(source, destination, intent=intent)
141
178
  rc = rc and self._std_remove(source)
142
179
  else:
@@ -146,7 +183,9 @@ class GRIB_Tool(addons.FtrawEnableAddon):
146
183
  grib_mv = grib_move = _std_move
147
184
 
148
185
  def _pack_stream(self, source, stdout=True):
149
- cmd = ['cat', ]
186
+ cmd = [
187
+ "cat",
188
+ ]
150
189
  cmd.extend(self._std_grib_index_get(source))
151
190
  return self.sh.popen(cmd, stdout=stdout, bufsize=8192)
152
191
 
@@ -159,14 +198,14 @@ class GRIB_Tool(addons.FtrawEnableAddon):
159
198
  total += size
160
199
  return total
161
200
 
162
- def xgrib_pack(self, source, destination, intent='in'):
201
+ def xgrib_pack(self, source, destination, intent="in"):
163
202
  """Manually pack a multi GRIB."""
164
203
  if isinstance(destination, str):
165
204
  tmpfile = self.sh.safe_fileaddsuffix(destination)
166
- with open(tmpfile, 'wb') as fd:
205
+ with open(tmpfile, "wb") as fd:
167
206
  p = self._pack_stream(source, stdout=fd)
168
207
  self.sh.pclose(p)
169
- if intent == 'in':
208
+ if intent == "in":
170
209
  self.sh.chmod(tmpfile, 0o444)
171
210
  return self.sh.move(tmpfile, destination)
172
211
  else:
@@ -177,13 +216,16 @@ class GRIB_Tool(addons.FtrawEnableAddon):
177
216
  def _std_forcepack(self, source, destination=None):
178
217
  """Returned a path to a packed data."""
179
218
  if self.is_xgrib(source):
180
- destination = (destination if destination else
181
- self.sh.safe_fileaddsuffix(source))
219
+ destination = (
220
+ destination
221
+ if destination
222
+ else self.sh.safe_fileaddsuffix(source)
223
+ )
182
224
  if not self.sh.path.exists(destination):
183
225
  if self.xgrib_pack(source, destination):
184
226
  return destination
185
227
  else:
186
- raise OSError('XGrib packing failed')
228
+ raise OSError("XGrib packing failed")
187
229
  else:
188
230
  return destination
189
231
  else:
@@ -191,8 +233,16 @@ class GRIB_Tool(addons.FtrawEnableAddon):
191
233
 
192
234
  grib_forcepack = _std_forcepack
193
235
 
194
- def _std_ftput(self, source, destination, hostname=None, logname=None,
195
- port=DEFAULT_FTP_PORT, cpipeline=None, sync=False):
236
+ def _std_ftput(
237
+ self,
238
+ source,
239
+ destination,
240
+ hostname=None,
241
+ logname=None,
242
+ port=DEFAULT_FTP_PORT,
243
+ cpipeline=None,
244
+ sync=False,
245
+ ):
196
246
  """On the fly packing and ftp."""
197
247
  if self.is_xgrib(source):
198
248
  if cpipeline is not None:
@@ -202,19 +252,35 @@ class GRIB_Tool(addons.FtrawEnableAddon):
202
252
  if ftp:
203
253
  packed_size = self._packed_size(source)
204
254
  p = self._pack_stream(source)
205
- rc = ftp.put(p.stdout, destination, size=packed_size, exact=True)
255
+ rc = ftp.put(
256
+ p.stdout, destination, size=packed_size, exact=True
257
+ )
206
258
  self.sh.pclose(p)
207
259
  ftp.close()
208
260
  else:
209
261
  rc = False
210
262
  return rc
211
263
  else:
212
- return self.sh.ftput(source, destination, hostname=hostname,
213
- logname=logname, port=port,
214
- cpipeline=cpipeline, sync=sync)
264
+ return self.sh.ftput(
265
+ source,
266
+ destination,
267
+ hostname=hostname,
268
+ logname=logname,
269
+ port=port,
270
+ cpipeline=cpipeline,
271
+ sync=sync,
272
+ )
215
273
 
216
- def _std_rawftput(self, source, destination, hostname=None, logname=None,
217
- port=None, cpipeline=None, sync=False):
274
+ def _std_rawftput(
275
+ self,
276
+ source,
277
+ destination,
278
+ hostname=None,
279
+ logname=None,
280
+ port=None,
281
+ cpipeline=None,
282
+ sync=False,
283
+ ):
218
284
  """Use ftserv as much as possible."""
219
285
  if self.is_xgrib(source):
220
286
  if cpipeline is not None:
@@ -222,52 +288,82 @@ class GRIB_Tool(addons.FtrawEnableAddon):
222
288
  if self.sh.ftraw and self.rawftshell is not None:
223
289
  # Copy the GRIB pieces individually
224
290
  pieces = self.xgrib_index_get(source)
225
- newsources = [str(self.sh.copy2ftspool(piece)) for piece in pieces]
226
- request = newsources[0] + '.request'
227
- with open(request, 'w') as request_fh:
228
- request_fh.writelines('\n'.join(newsources))
291
+ newsources = [
292
+ str(self.sh.copy2ftspool(piece)) for piece in pieces
293
+ ]
294
+ request = newsources[0] + ".request"
295
+ with open(request, "w") as request_fh:
296
+ request_fh.writelines("\n".join(newsources))
229
297
  self.sh.readonly(request)
230
- rc = self.sh.ftserv_put(request, destination,
231
- hostname=hostname, logname=logname, port=port,
232
- specialshell=self.rawftshell, sync=sync)
298
+ rc = self.sh.ftserv_put(
299
+ request,
300
+ destination,
301
+ hostname=hostname,
302
+ logname=logname,
303
+ port=port,
304
+ specialshell=self.rawftshell,
305
+ sync=sync,
306
+ )
233
307
  self.sh.rm(request)
234
308
  return rc
235
309
  else:
236
310
  if port is None:
237
311
  port = DEFAULT_FTP_PORT
238
- return self._std_ftput(source, destination,
239
- hostname=hostname, logname=logname,
240
- port=port, sync=sync)
312
+ return self._std_ftput(
313
+ source,
314
+ destination,
315
+ hostname=hostname,
316
+ logname=logname,
317
+ port=port,
318
+ sync=sync,
319
+ )
241
320
  else:
242
- return self.sh.rawftput(source, destination, hostname=hostname,
243
- logname=logname, port=port,
244
- cpipeline=cpipeline, sync=sync)
321
+ return self.sh.rawftput(
322
+ source,
323
+ destination,
324
+ hostname=hostname,
325
+ logname=logname,
326
+ port=port,
327
+ cpipeline=cpipeline,
328
+ sync=sync,
329
+ )
245
330
 
246
331
  grib_ftput = _std_ftput
247
332
  grib_rawftput = _std_rawftput
248
333
 
249
- def _std_scpput(self, source, destination, hostname, logname=None, cpipeline=None):
334
+ def _std_scpput(
335
+ self, source, destination, hostname, logname=None, cpipeline=None
336
+ ):
250
337
  """On the fly packing and scp."""
251
338
  if self.is_xgrib(source):
252
339
  if cpipeline is not None:
253
340
  raise OSError("It's not allowed to compress xgrib files.")
254
- logname = self.sh.fix_ftuser(hostname, logname, fatal=False, defaults_to_user=False)
341
+ logname = self.sh.fix_ftuser(
342
+ hostname, logname, fatal=False, defaults_to_user=False
343
+ )
255
344
  ssh = self.sh.ssh(hostname, logname)
256
345
  permissions = ssh.get_permissions(source)
257
346
  # remove the .d companion directory (scp_stream removes the destination)
258
347
  # go on on failure : the .d lingers on, but the grib will be self-contained
259
- ssh.remove(destination + '.d')
348
+ ssh.remove(destination + ".d")
260
349
  p = self._pack_stream(source)
261
- rc = ssh.scpput_stream(p.stdout, destination, permissions=permissions)
350
+ rc = ssh.scpput_stream(
351
+ p.stdout, destination, permissions=permissions
352
+ )
262
353
  self.sh.pclose(p)
263
354
  return rc
264
355
  else:
265
- return self.sh.scpput(source, destination, hostname,
266
- logname=logname, cpipeline=cpipeline)
356
+ return self.sh.scpput(
357
+ source,
358
+ destination,
359
+ hostname,
360
+ logname=logname,
361
+ cpipeline=cpipeline,
362
+ )
267
363
 
268
364
  grib_scpput = _std_scpput
269
365
 
270
- @addons.require_external_addon('ecfs')
366
+ @addons.require_external_addon("ecfs")
271
367
  def grib_ecfsput(self, source, target, cpipeline=None, options=None):
272
368
  """Put a grib resource using ECfs.
273
369
 
@@ -282,25 +378,33 @@ class GRIB_Tool(addons.FtrawEnableAddon):
282
378
  raise OSError("It's not allowed to compress xgrib files.")
283
379
  psource = self.sh.safe_fileaddsuffix(source)
284
380
  try:
285
- rc = self.xgrib_pack(source=source,
286
- destination=psource)
381
+ rc = self.xgrib_pack(source=source, destination=psource)
287
382
  dict_args = dict()
288
383
  if rc:
289
- rc, dict_args = self.sh.ecfsput(source=psource,
290
- target=target,
291
- options=options)
384
+ rc, dict_args = self.sh.ecfsput(
385
+ source=psource, target=target, options=options
386
+ )
292
387
  finally:
293
388
  self.sh.rm(psource)
294
389
  return rc, dict_args
295
390
  else:
296
- return self.sh.ecfsput(source=source,
297
- target=target,
298
- options=options,
299
- cpipeline=cpipeline)
300
-
301
- @addons.require_external_addon('ectrans')
302
- def grib_ectransput(self, source, target, gateway=None, remote=None,
303
- cpipeline=None, sync=False):
391
+ return self.sh.ecfsput(
392
+ source=source,
393
+ target=target,
394
+ options=options,
395
+ cpipeline=cpipeline,
396
+ )
397
+
398
+ @addons.require_external_addon("ectrans")
399
+ def grib_ectransput(
400
+ self,
401
+ source,
402
+ target,
403
+ gateway=None,
404
+ remote=None,
405
+ cpipeline=None,
406
+ sync=False,
407
+ ):
304
408
  """Put a grib resource using ECtrans.
305
409
 
306
410
  :param source: source file
@@ -316,25 +420,28 @@ class GRIB_Tool(addons.FtrawEnableAddon):
316
420
  raise OSError("It's not allowed to compress xgrib files.")
317
421
  psource = self.sh.safe_fileaddsuffix(source)
318
422
  try:
319
- rc = self.xgrib_pack(source=source,
320
- destination=psource)
423
+ rc = self.xgrib_pack(source=source, destination=psource)
321
424
  dict_args = dict()
322
425
  if rc:
323
- rc, dict_args = self.sh.raw_ectransput(source=psource,
324
- target=target,
325
- gateway=gateway,
326
- remote=remote,
327
- sync=sync)
426
+ rc, dict_args = self.sh.raw_ectransput(
427
+ source=psource,
428
+ target=target,
429
+ gateway=gateway,
430
+ remote=remote,
431
+ sync=sync,
432
+ )
328
433
  finally:
329
434
  self.sh.rm(psource)
330
435
  return rc, dict_args
331
436
  else:
332
- return self.sh.ectransput(source=source,
333
- target=target,
334
- gateway=gateway,
335
- remote=remote,
336
- cpipeline=cpipeline,
337
- sync=sync)
437
+ return self.sh.ectransput(
438
+ source=source,
439
+ target=target,
440
+ gateway=gateway,
441
+ remote=remote,
442
+ cpipeline=cpipeline,
443
+ sync=sync,
444
+ )
338
445
 
339
446
 
340
447
  @algo_component_deco_mixin_autodoc
@@ -356,23 +463,37 @@ class EcGribDecoMixin(AlgoComponentDecoMixin):
356
463
  gribapi_lib = None
357
464
  if rh is not None:
358
465
  if not isinstance(rh, (list, tuple)):
359
- rh = [rh, ]
466
+ rh = [
467
+ rh,
468
+ ]
360
469
  for a_rh in rh:
361
470
  libs = self.system.ldd(a_rh.container.localpath())
362
471
  a_eccodes_lib = None
363
472
  a_gribapi_lib = None
364
473
  for lib, path in libs.items():
365
- if re.match(r'^libeccodes(?:-[.0-9]+)?\.so(?:\.[.0-9]+)?$', lib):
474
+ if re.match(
475
+ r"^libeccodes(?:-[.0-9]+)?\.so(?:\.[.0-9]+)?$", lib
476
+ ):
366
477
  a_eccodes_lib = path
367
- if re.match(r'^libgrib_api(?:-[.0-9]+)?\.so(?:\.[.0-9]+)?$', lib):
478
+ if re.match(
479
+ r"^libgrib_api(?:-[.0-9]+)?\.so(?:\.[.0-9]+)?$", lib
480
+ ):
368
481
  a_gribapi_lib = path
369
482
  if a_eccodes_lib:
370
- self.algoassert(eccodes_lib is None or (eccodes_lib == a_eccodes_lib),
371
- "ecCodes library inconsistency (rh: {!s})".format(a_rh))
483
+ self.algoassert(
484
+ eccodes_lib is None or (eccodes_lib == a_eccodes_lib),
485
+ "ecCodes library inconsistency (rh: {!s})".format(
486
+ a_rh
487
+ ),
488
+ )
372
489
  eccodes_lib = a_eccodes_lib
373
490
  if a_gribapi_lib:
374
- self.algoassert(gribapi_lib is None or (gribapi_lib == a_gribapi_lib),
375
- "grib_api library inconsistency (rh: {!s})".format(a_rh))
491
+ self.algoassert(
492
+ gribapi_lib is None or (gribapi_lib == a_gribapi_lib),
493
+ "grib_api library inconsistency (rh: {!s})".format(
494
+ a_rh
495
+ ),
496
+ )
376
497
  gribapi_lib = a_gribapi_lib
377
498
  return eccodes_lib, gribapi_lib
378
499
 
@@ -380,72 +501,104 @@ class EcGribDecoMixin(AlgoComponentDecoMixin):
380
501
  """Add axtra definitions/samples to the library path."""
381
502
  for gdef in self.context.sequence.effective_inputs(role=a_role):
382
503
  local_path = gdef.rh.container.localpath()
383
- new_path = (local_path if self.system.path.isdir(local_path)
384
- else self.system.path.dirname(local_path))
504
+ new_path = (
505
+ local_path
506
+ if self.system.path.isdir(local_path)
507
+ else self.system.path.dirname(local_path)
508
+ )
385
509
  # NB: Grib-API doesn't understand relative paths...
386
510
  new_path = self.system.path.abspath(new_path)
387
511
  self.env.setgenericpath(a_var, new_path, pos=0)
388
512
 
389
513
  def _gribapi_envsetup(self, gribapi_lib):
390
514
  """Setup environment variables for grib_api."""
391
- defvar = 'GRIB_DEFINITION_PATH'
392
- samplevar = 'GRIB_SAMPLES_PATH'
515
+ defvar = "GRIB_DEFINITION_PATH"
516
+ samplevar = "GRIB_SAMPLES_PATH"
393
517
  if gribapi_lib is not None:
394
518
  gribapi_root = self.system.path.dirname(gribapi_lib)
395
519
  gribapi_root = self.system.path.split(gribapi_root)[0]
396
- gribapi_share = self.system.path.join(gribapi_root, 'share', 'grib_api')
520
+ gribapi_share = self.system.path.join(
521
+ gribapi_root, "share", "grib_api"
522
+ )
397
523
  if defvar not in self.env:
398
524
  # This one is for compatibility with old versions of the gribapi !
399
- self.env.setgenericpath(defvar,
400
- self.system.path.join(gribapi_root, 'share', 'definitions'))
525
+ self.env.setgenericpath(
526
+ defvar,
527
+ self.system.path.join(
528
+ gribapi_root, "share", "definitions"
529
+ ),
530
+ )
401
531
  # This should be the lastest one:
402
- self.env.setgenericpath(defvar,
403
- self.system.path.join(gribapi_share, 'definitions'))
532
+ self.env.setgenericpath(
533
+ defvar, self.system.path.join(gribapi_share, "definitions")
534
+ )
404
535
  if samplevar not in self.env:
405
536
  # This one is for compatibility with old versions of the gribapi !
406
- self.env.setgenericpath(samplevar,
407
- self.system.path.join(gribapi_root, 'ifs_samples', 'grib1'))
537
+ self.env.setgenericpath(
538
+ samplevar,
539
+ self.system.path.join(
540
+ gribapi_root, "ifs_samples", "grib1"
541
+ ),
542
+ )
408
543
  # This should be the lastest one:
409
- self.env.setgenericpath(samplevar,
410
- self.system.path.join(gribapi_share, 'ifs_samples', 'grib1'))
544
+ self.env.setgenericpath(
545
+ samplevar,
546
+ self.system.path.join(
547
+ gribapi_share, "ifs_samples", "grib1"
548
+ ),
549
+ )
411
550
  else:
412
551
  # Use the default GRIB-API config if the ldd approach fails
413
- self.export('gribapi')
552
+ self.export("gribapi")
414
553
  return defvar, samplevar
415
554
 
416
555
  def gribapi_setup(self, rh, opts):
417
556
  """Setup the grib_api related stuff."""
418
557
  _, gribapi_lib = self._ecgrib_libs_detext(rh)
419
558
  defvar, samplevar = self._gribapi_envsetup(gribapi_lib)
420
- self._ecgrib_additional_config('AdditionalGribAPIDefinitions', defvar)
421
- self._ecgrib_additional_config('AdditionalGribAPISamples', samplevar)
559
+ self._ecgrib_additional_config("AdditionalGribAPIDefinitions", defvar)
560
+ self._ecgrib_additional_config("AdditionalGribAPISamples", samplevar)
422
561
  # Recap
423
562
  for a_var in (defvar, samplevar):
424
- logger.info('After gribapi_setup %s = %s', a_var, self.env.getvar(a_var))
425
-
426
- def _eccodes_envsetup(self, eccodes_lib):
427
- """Setup environment variables for ecCodes."""
428
- dep_warn = ("%s is left unconfigured because the old grib_api's variable is defined." +
429
- "Please remove that !")
430
- eccodes_root = self.system.path.dirname(eccodes_lib)
431
- eccodes_root = self.system.path.split(eccodes_root)[0]
432
- eccodes_share = self.system.path.join(eccodes_root, 'share', 'eccodes')
433
- defvar = 'ECCODES_DEFINITION_PATH'
434
- if defvar not in self.env:
435
- if 'GRIB_DEFINITION_PATH' in self.env:
436
- logger.warning(dep_warn, defvar)
437
- defvar = 'GRIB_DEFINITION_PATH'
438
- else:
439
- self.env.setgenericpath(defvar, self.system.path.join(eccodes_share, 'definitions'))
440
- samplevar = 'ECCODES_SAMPLES_PATH'
441
- if samplevar not in self.env:
442
- if 'GRIB_SAMPLES_PATH' in self.env:
443
- logger.warning(dep_warn, samplevar)
444
- samplevar = 'GRIB_SAMPLES_PATH'
445
- else:
446
- self.env.setgenericpath(samplevar,
447
- self.system.path.join(eccodes_share, 'ifs_samples', 'grib1'))
448
- return defvar, samplevar
563
+ logger.info(
564
+ "After gribapi_setup %s = %s", a_var, self.env.getvar(a_var)
565
+ )
566
+
567
+ def _eccodes_envsetup(
568
+ self,
569
+ eccodes_lib,
570
+ envvar="ECCODES_DEFINITIONS_PATH",
571
+ tgt_path="definitions",
572
+ ):
573
+ """Export envirionment variables required by ECCODES
574
+
575
+ Value is
576
+
577
+ /path/to/eccodes-X.Y.Z/share/eccodes/<target_path>
578
+
579
+ eccodes_lib: Absolute path to the eccodes so file
580
+ envvar: Name of the environment variable to export
581
+ tgt_path: Name of the eccodes install subdirectory to appear
582
+ in the value
583
+ """
584
+ if envvar in self.env:
585
+ return envvar
586
+ if envvar.replace("ECCODES", "GRIB") in self.env:
587
+ logger.warning(
588
+ (
589
+ "%s is left unconfigured because the old grib_api's"
590
+ "variable is defined. ",
591
+ "Please remove that!",
592
+ ),
593
+ envvar,
594
+ )
595
+ return envvar.replace("ECCODES", "GRIB")
596
+ eccodes_root = Path(eccodes_lib).parent.parent
597
+ self.env.setgenericpath(
598
+ envvar,
599
+ str(eccodes_root / "share" / "eccodes" / tgt_path),
600
+ )
601
+ return envvar
449
602
 
450
603
  def eccodes_setup(self, rh, opts, compat=False, fatal=True):
451
604
  """Setup the grib_api related stuff.
@@ -457,31 +610,55 @@ class EcGribDecoMixin(AlgoComponentDecoMixin):
457
610
  # Detect the library's path and setup appropriate variables
458
611
  eccodes_lib, gribapi_lib = self._ecgrib_libs_detext(rh)
459
612
  if eccodes_lib is not None:
460
- defvar, samplevar = self._eccodes_envsetup(eccodes_lib)
613
+ defvar = self._eccodes_envsetup(
614
+ eccodes_lib,
615
+ envvar="ECCODES_DEFINITIONS_PATH",
616
+ tgt_path="definitions",
617
+ )
618
+ subdir = Path("ifs_samples") / (
619
+ "grib1" if rh.resource.cycle < "cy49" else "grib1_mlgrib2"
620
+ )
621
+ samplevar = self._eccodes_envsetup(
622
+ eccodes_lib,
623
+ envvar="ECCODES_SAMPLES_PATH",
624
+ tgt_path=subdir,
625
+ )
461
626
  elif compat:
462
627
  defvar, samplevar = self._gribapi_envsetup(gribapi_lib)
463
628
  else:
464
629
  if fatal:
465
- raise RuntimeError('No suitable configuration found for ecCodes.')
630
+ raise RuntimeError(
631
+ "No suitable configuration found for ecCodes."
632
+ )
466
633
  else:
467
634
  logger.error("ecCodes was not found !")
468
635
  return
469
636
  # Then, inspect the context to look for customised search paths
470
- self._ecgrib_additional_config(('AdditionalGribAPIDefinitions', 'AdditionalEcCodesDefinitions'),
471
- defvar)
472
- self._ecgrib_additional_config(('AdditionalGribAPISamples', 'AdditionalEcCodesSamples'),
473
- samplevar)
637
+ self._ecgrib_additional_config(
638
+ ("AdditionalGribAPIDefinitions", "AdditionalEcCodesDefinitions"),
639
+ defvar,
640
+ )
641
+ self._ecgrib_additional_config(
642
+ ("AdditionalGribAPISamples", "AdditionalEcCodesSamples"), samplevar
643
+ )
474
644
  # Recap
475
645
  for a_var in (defvar, samplevar):
476
- logger.info('After eccodes_setup (compat=%s) : %s = %s', str(compat),
477
- a_var, self.env.getvar(a_var))
646
+ logger.info(
647
+ "After eccodes_setup (compat=%s) : %s = %s",
648
+ str(compat),
649
+ a_var,
650
+ self.env.getvar(a_var),
651
+ )
478
652
 
479
653
  def _ecgrib_mixin_setup(self, rh, opts):
480
- self.eccodes_setup(rh, opts,
481
- compat=self._ECGRIB_SETUP_COMPAT,
482
- fatal=self._ECGRIB_SETUP_FATAL)
654
+ self.eccodes_setup(
655
+ rh,
656
+ opts,
657
+ compat=self._ECGRIB_SETUP_COMPAT,
658
+ fatal=self._ECGRIB_SETUP_FATAL,
659
+ )
483
660
 
484
- _MIXIN_PREPARE_HOOKS = (_ecgrib_mixin_setup, )
661
+ _MIXIN_PREPARE_HOOKS = (_ecgrib_mixin_setup,)
485
662
 
486
663
 
487
664
  class GRIBAPI_Tool(addons.Addon):
@@ -490,12 +667,12 @@ class GRIBAPI_Tool(addons.Addon):
490
667
  """
491
668
 
492
669
  _footprint = dict(
493
- info = 'Default GRIBAPI system interface',
494
- attr = dict(
495
- kind = dict(
496
- values = ['gribapi'],
670
+ info="Default GRIBAPI system interface",
671
+ attr=dict(
672
+ kind=dict(
673
+ values=["gribapi"],
497
674
  ),
498
- )
675
+ ),
499
676
  )
500
677
 
501
678
  def __init__(self, *args, **kw):
@@ -513,17 +690,19 @@ class GRIBAPI_Tool(addons.Addon):
513
690
 
514
691
  def _spawn_wrap(self, cmd, **kw):
515
692
  """Internal method calling standard shell spawn."""
516
- cmd[0] = 'bin' + self.sh.path.sep + cmd[0]
693
+ cmd[0] = "bin" + self.sh.path.sep + cmd[0]
517
694
  return super()._spawn_wrap(cmd, **kw)
518
695
 
519
696
  def _actual_diff(self, grib1, grib2, skipkeys, **kw):
520
697
  """Run the actual GRIBAPI command."""
521
- cmd = ['grib_compare', '-r', '-b', ','.join(skipkeys), grib1, grib2]
522
- kw['fatal'] = False
523
- kw['output'] = False
698
+ cmd = ["grib_compare", "-r", "-b", ",".join(skipkeys), grib1, grib2]
699
+ kw["fatal"] = False
700
+ kw["output"] = False
524
701
  return self._spawn_wrap(cmd, **kw)
525
702
 
526
- def grib_diff(self, grib1, grib2, skipkeys=('generatingProcessIdentifier',), **kw):
703
+ def grib_diff(
704
+ self, grib1, grib2, skipkeys=("generatingProcessIdentifier",), **kw
705
+ ):
527
706
  """
528
707
  Difference between two GRIB files (using the GRIB-API)
529
708
 
@@ -538,15 +717,15 @@ class GRIBAPI_Tool(addons.Addon):
538
717
  """
539
718
 
540
719
  # Are multipart GRIB suported ?
541
- xgrib_support = 'grib' in self.sh.loaded_addons()
720
+ xgrib_support = "grib" in self.sh.loaded_addons()
542
721
  grib1_ori = grib1
543
722
  grib2_ori = grib2
544
723
  if xgrib_support:
545
724
  if self.sh.is_xgrib(grib1):
546
- grib1 = self.sh.safe_fileaddsuffix(grib1_ori) + '_diffcat'
725
+ grib1 = self.sh.safe_fileaddsuffix(grib1_ori) + "_diffcat"
547
726
  self.sh.xgrib_pack(grib1_ori, grib1)
548
727
  if self.sh.is_xgrib(grib2):
549
- grib2 = self.sh.safe_fileaddsuffix(grib2_ori) + '_diffcat'
728
+ grib2 = self.sh.safe_fileaddsuffix(grib2_ori) + "_diffcat"
550
729
  self.sh.xgrib_pack(grib2_ori, grib2)
551
730
 
552
731
  rc = self._actual_diff(grib1, grib2, skipkeys, **kw)