vortex-nwp 2.0.0b1__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 (146) hide show
  1. vortex/__init__.py +135 -0
  2. vortex/algo/__init__.py +12 -0
  3. vortex/algo/components.py +2136 -0
  4. vortex/algo/mpitools.py +1648 -0
  5. vortex/algo/mpitools_templates/envelope_wrapper_default.tpl +27 -0
  6. vortex/algo/mpitools_templates/envelope_wrapper_mpiauto.tpl +29 -0
  7. vortex/algo/mpitools_templates/wrapstd_wrapper_default.tpl +18 -0
  8. vortex/algo/serversynctools.py +170 -0
  9. vortex/config.py +115 -0
  10. vortex/data/__init__.py +13 -0
  11. vortex/data/abstractstores.py +1572 -0
  12. vortex/data/containers.py +780 -0
  13. vortex/data/contents.py +596 -0
  14. vortex/data/executables.py +284 -0
  15. vortex/data/flow.py +113 -0
  16. vortex/data/geometries.ini +2689 -0
  17. vortex/data/geometries.py +703 -0
  18. vortex/data/handlers.py +1021 -0
  19. vortex/data/outflow.py +67 -0
  20. vortex/data/providers.py +465 -0
  21. vortex/data/resources.py +201 -0
  22. vortex/data/stores.py +1271 -0
  23. vortex/gloves.py +282 -0
  24. vortex/layout/__init__.py +27 -0
  25. vortex/layout/appconf.py +109 -0
  26. vortex/layout/contexts.py +511 -0
  27. vortex/layout/dataflow.py +1069 -0
  28. vortex/layout/jobs.py +1276 -0
  29. vortex/layout/monitor.py +833 -0
  30. vortex/layout/nodes.py +1424 -0
  31. vortex/layout/subjobs.py +464 -0
  32. vortex/nwp/__init__.py +11 -0
  33. vortex/nwp/algo/__init__.py +12 -0
  34. vortex/nwp/algo/assim.py +483 -0
  35. vortex/nwp/algo/clim.py +920 -0
  36. vortex/nwp/algo/coupling.py +609 -0
  37. vortex/nwp/algo/eda.py +632 -0
  38. vortex/nwp/algo/eps.py +613 -0
  39. vortex/nwp/algo/forecasts.py +745 -0
  40. vortex/nwp/algo/fpserver.py +927 -0
  41. vortex/nwp/algo/ifsnaming.py +403 -0
  42. vortex/nwp/algo/ifsroot.py +311 -0
  43. vortex/nwp/algo/monitoring.py +202 -0
  44. vortex/nwp/algo/mpitools.py +554 -0
  45. vortex/nwp/algo/odbtools.py +974 -0
  46. vortex/nwp/algo/oopsroot.py +735 -0
  47. vortex/nwp/algo/oopstests.py +186 -0
  48. vortex/nwp/algo/request.py +579 -0
  49. vortex/nwp/algo/stdpost.py +1285 -0
  50. vortex/nwp/data/__init__.py +12 -0
  51. vortex/nwp/data/assim.py +392 -0
  52. vortex/nwp/data/boundaries.py +261 -0
  53. vortex/nwp/data/climfiles.py +539 -0
  54. vortex/nwp/data/configfiles.py +149 -0
  55. vortex/nwp/data/consts.py +929 -0
  56. vortex/nwp/data/ctpini.py +133 -0
  57. vortex/nwp/data/diagnostics.py +181 -0
  58. vortex/nwp/data/eda.py +148 -0
  59. vortex/nwp/data/eps.py +383 -0
  60. vortex/nwp/data/executables.py +1039 -0
  61. vortex/nwp/data/fields.py +96 -0
  62. vortex/nwp/data/gridfiles.py +308 -0
  63. vortex/nwp/data/logs.py +551 -0
  64. vortex/nwp/data/modelstates.py +334 -0
  65. vortex/nwp/data/monitoring.py +220 -0
  66. vortex/nwp/data/namelists.py +644 -0
  67. vortex/nwp/data/obs.py +748 -0
  68. vortex/nwp/data/oopsexec.py +72 -0
  69. vortex/nwp/data/providers.py +182 -0
  70. vortex/nwp/data/query.py +217 -0
  71. vortex/nwp/data/stores.py +147 -0
  72. vortex/nwp/data/surfex.py +338 -0
  73. vortex/nwp/syntax/__init__.py +9 -0
  74. vortex/nwp/syntax/stdattrs.py +375 -0
  75. vortex/nwp/tools/__init__.py +10 -0
  76. vortex/nwp/tools/addons.py +35 -0
  77. vortex/nwp/tools/agt.py +55 -0
  78. vortex/nwp/tools/bdap.py +48 -0
  79. vortex/nwp/tools/bdcp.py +38 -0
  80. vortex/nwp/tools/bdm.py +21 -0
  81. vortex/nwp/tools/bdmp.py +49 -0
  82. vortex/nwp/tools/conftools.py +1311 -0
  83. vortex/nwp/tools/drhook.py +62 -0
  84. vortex/nwp/tools/grib.py +268 -0
  85. vortex/nwp/tools/gribdiff.py +99 -0
  86. vortex/nwp/tools/ifstools.py +163 -0
  87. vortex/nwp/tools/igastuff.py +249 -0
  88. vortex/nwp/tools/mars.py +56 -0
  89. vortex/nwp/tools/odb.py +548 -0
  90. vortex/nwp/tools/partitioning.py +234 -0
  91. vortex/nwp/tools/satrad.py +56 -0
  92. vortex/nwp/util/__init__.py +6 -0
  93. vortex/nwp/util/async.py +184 -0
  94. vortex/nwp/util/beacon.py +40 -0
  95. vortex/nwp/util/diffpygram.py +359 -0
  96. vortex/nwp/util/ens.py +198 -0
  97. vortex/nwp/util/hooks.py +128 -0
  98. vortex/nwp/util/taskdeco.py +81 -0
  99. vortex/nwp/util/usepygram.py +591 -0
  100. vortex/nwp/util/usetnt.py +87 -0
  101. vortex/proxy.py +6 -0
  102. vortex/sessions.py +341 -0
  103. vortex/syntax/__init__.py +9 -0
  104. vortex/syntax/stdattrs.py +628 -0
  105. vortex/syntax/stddeco.py +176 -0
  106. vortex/toolbox.py +982 -0
  107. vortex/tools/__init__.py +11 -0
  108. vortex/tools/actions.py +457 -0
  109. vortex/tools/addons.py +297 -0
  110. vortex/tools/arm.py +76 -0
  111. vortex/tools/compression.py +322 -0
  112. vortex/tools/date.py +20 -0
  113. vortex/tools/ddhpack.py +10 -0
  114. vortex/tools/delayedactions.py +672 -0
  115. vortex/tools/env.py +513 -0
  116. vortex/tools/folder.py +663 -0
  117. vortex/tools/grib.py +559 -0
  118. vortex/tools/lfi.py +746 -0
  119. vortex/tools/listings.py +354 -0
  120. vortex/tools/names.py +575 -0
  121. vortex/tools/net.py +1790 -0
  122. vortex/tools/odb.py +10 -0
  123. vortex/tools/parallelism.py +336 -0
  124. vortex/tools/prestaging.py +186 -0
  125. vortex/tools/rawfiles.py +10 -0
  126. vortex/tools/schedulers.py +413 -0
  127. vortex/tools/services.py +871 -0
  128. vortex/tools/storage.py +1061 -0
  129. vortex/tools/surfex.py +61 -0
  130. vortex/tools/systems.py +3396 -0
  131. vortex/tools/targets.py +384 -0
  132. vortex/util/__init__.py +9 -0
  133. vortex/util/config.py +1071 -0
  134. vortex/util/empty.py +24 -0
  135. vortex/util/helpers.py +184 -0
  136. vortex/util/introspection.py +63 -0
  137. vortex/util/iosponge.py +76 -0
  138. vortex/util/roles.py +51 -0
  139. vortex/util/storefunctions.py +103 -0
  140. vortex/util/structs.py +26 -0
  141. vortex/util/worker.py +150 -0
  142. vortex_nwp-2.0.0b1.dist-info/LICENSE +517 -0
  143. vortex_nwp-2.0.0b1.dist-info/METADATA +50 -0
  144. vortex_nwp-2.0.0b1.dist-info/RECORD +146 -0
  145. vortex_nwp-2.0.0b1.dist-info/WHEEL +5 -0
  146. vortex_nwp-2.0.0b1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,354 @@
1
+ """Utility classes to read and compare IFS/Arpege listings."""
2
+
3
+ import copy
4
+ import re
5
+ from collections import OrderedDict, defaultdict, deque
6
+
7
+ import footprints
8
+ from arpifs_listings import cost_functions, jo_tables, listings, norms
9
+ from bronx.stdtypes.date import Date
10
+ from vortex.data.contents import FormatAdapterAbstractImplementation
11
+
12
+ from . import addons
13
+
14
+ #: No automatic export
15
+ __all__ = []
16
+
17
+
18
+ def use_in_shell(sh, **kw):
19
+ """Extend current shell with the arpifs_listings interface defined by optional arguments."""
20
+ kw['shell'] = sh
21
+ return footprints.proxy.addon(**kw)
22
+
23
+
24
+ class ArpIfsListingDiff_Result:
25
+ """Holds the detailed results of a listing comparison."""
26
+
27
+ def __init__(self, norms_eq, jos_eq, jos_diff):
28
+ self._norms_eq = norms_eq
29
+ self._jos_eq = jos_eq
30
+ self._jos_diff = jos_diff
31
+
32
+ def __str__(self):
33
+ return '{:s} | NormsOk={:b} JoTablesOk={:b}>'.format(
34
+ repr(self).rstrip('>'),
35
+ all(self._norms_eq.values()),
36
+ all(self._jos_eq.values())
37
+ )
38
+
39
+ def differences(self):
40
+ """Print a summary of the listing comparison."""
41
+ # print() # activation breaks test_arpifs_listings_integration.py
42
+ if self._norms_eq:
43
+ if all(self._norms_eq.values()):
44
+ print("Norms check succeeded for all steps.")
45
+ else:
46
+ print("Norms check succeeded for steps:\n {:s}".format(
47
+ "\n ".join([str(k) for k, v in self._norms_eq.items() if v])))
48
+ print("Norms check FAILED for steps:\n {:s}".format(
49
+ "\n ".join([str(k) for k, v in self._norms_eq.items() if not v])))
50
+ else:
51
+ print("No norms found in the new listing or no matching norms.")
52
+ # print() # activation breaks test_arpifs_listings_integration.py
53
+ if self._jos_eq:
54
+ diffprinted = False
55
+ for k, v in self._jos_eq.items():
56
+ if v:
57
+ print("JoTable check succeeded for: {:s}".format(k))
58
+ else:
59
+ print("JoTable check FAILED for: {:s}".format(k))
60
+ if not diffprinted:
61
+ todo = self._jos_diff[k]
62
+ for otype_k, otype_v in todo.items():
63
+ for sensor_k, sensor_v in otype_v.items():
64
+ for var_k, var_v in sensor_v.items():
65
+ if var_k == 'GLOBAL':
66
+ continue
67
+ print(" > {:s} > {:s} > {:4s} : d_n={:<9d} d_jo={:f}".format(
68
+ otype_k, sensor_k, var_k,
69
+ var_v['n']['diff'], var_v['jo']['diff']))
70
+ diffprinted = True
71
+ else:
72
+ print("No Jo-Tables were found or the number of Jo-Tables do not match.")
73
+
74
+
75
+ class ArpIfsListingDiff_Status:
76
+ """Holds the status of a listing comparison."""
77
+
78
+ def __init__(self, norms_eq, jos_eq, jos_diff):
79
+ self._norms_ok = all(norms_eq.values())
80
+ self._jos_ok = all(jos_eq.values())
81
+ self._result = ArpIfsListingDiff_Result(norms_eq, jos_eq, jos_diff)
82
+
83
+ def __str__(self):
84
+ return '{:s} | rc={:b}>'.format(repr(self).rstrip('>'), bool(self))
85
+
86
+ @property
87
+ def result(self):
88
+ """Return the detailed results of the comparison."""
89
+ return self._result
90
+
91
+ def __bool__(self):
92
+ return bool(self._norms_ok and self._jos_ok)
93
+
94
+
95
+ class ArpIfsListingsTool(addons.Addon):
96
+ """Interface to arpifs_listings (designed as a shell Addon)."""
97
+
98
+ _footprint = dict(
99
+ info='Default arpifs_listings interface',
100
+ attr=dict(
101
+ kind=dict(
102
+ values=['arpifs_listings'],
103
+ ),
104
+ )
105
+ )
106
+
107
+ def arpifslist_diff(self, listing1, listing2):
108
+ """Difference between two Arpege/IFS listing files.
109
+
110
+ Only Spectral/Gridpoint norms and JO-tables are compared.
111
+
112
+ :param listing1: first file to compare
113
+ :param listing2: second file to compare
114
+ :rtype: :class:`ArpIfsListingDiff_Status`
115
+ """
116
+
117
+ with open(listing1) as fh1:
118
+ l1_slurp = [l.rstrip("\n") for l in fh1]
119
+ with open(listing2) as fh2:
120
+ l2_slurp = [l.rstrip("\n") for l in fh2]
121
+ l1_normset = norms.NormsSet(l1_slurp)
122
+ l2_normset = norms.NormsSet(l2_slurp)
123
+ l1_jos = jo_tables.JoTables(listing1, l1_slurp)
124
+ l2_jos = jo_tables.JoTables(listing2, l2_slurp)
125
+
126
+ # The reference listing may contain more norms compared to the second one
127
+ norms_eq = OrderedDict()
128
+ if len(l2_normset):
129
+ if not l2_normset.steps_equal(l1_normset):
130
+ l1_tdict = OrderedDict()
131
+ for n in l1_normset:
132
+ l1_tdict[n.format_step()] = n
133
+ l2_tdict = OrderedDict()
134
+ for n in l2_normset:
135
+ l2_tdict[n.format_step()] = n
136
+ ikeys = set(l1_tdict.keys()) & set(l2_tdict.keys())
137
+ for k in ikeys:
138
+ norms_eq[k] = l1_tdict[k] == l2_tdict[k]
139
+ else:
140
+ for i, n in enumerate(l2_normset):
141
+ k = n.format_step()
142
+ norms_eq[k] = n == l1_normset[i]
143
+
144
+ jos_eq = OrderedDict()
145
+ jos_diff = OrderedDict()
146
+ if len(l2_jos):
147
+ if not l1_jos == l2_jos:
148
+ # If the JoTables list is not consistent: do nothing
149
+ if list(l1_jos.keys()) == list(l2_jos.keys()):
150
+ for table1, table2 in zip(l1_jos.values(), l2_jos.values()):
151
+ jos_eq[table1.name] = table1 == table2
152
+ if not jos_eq[table1.name]:
153
+ jos_diff[table1.name] = OrderedDict()
154
+ # We only save differences when deltaN or deltaJo != 0
155
+ for otype_k, otype_v in table2.compute_diff(table1).items():
156
+ otype_tmp = OrderedDict()
157
+ for sensor_k, sensor_v in otype_v.items():
158
+ sensor_tmp = OrderedDict()
159
+ for k, v in sensor_v.items():
160
+ if v['n']['diff'] != 0 or v['jo']['diff'] != 0:
161
+ sensor_tmp[k] = v
162
+ if len(sensor_tmp):
163
+ otype_tmp[sensor_k] = sensor_tmp
164
+ if len(otype_tmp):
165
+ jos_diff[table1.name][otype_k] = otype_tmp
166
+ else:
167
+ for k in l1_jos.keys():
168
+ jos_eq[k] = True
169
+
170
+ return ArpIfsListingDiff_Status(norms_eq, jos_eq, jos_diff)
171
+
172
+
173
+ class ArpifsListingsFormatAdapter(FormatAdapterAbstractImplementation):
174
+ _footprint = dict(
175
+ attr=dict(
176
+ format=dict(
177
+ values=['ARPIFSLIST', ],
178
+ ),
179
+ )
180
+ )
181
+
182
+ def __init__(self, *kargs, **kwargs):
183
+ super().__init__(*kargs, **kwargs)
184
+ self._lines = None
185
+ self._normset = None
186
+ self._jotables = None
187
+ self._costs = None
188
+ self._end_is_reached = None
189
+ if not self.fmtdelayedopen:
190
+ self.normset
191
+ self.jotables
192
+ self.costs
193
+ self.flush_lines()
194
+
195
+ @property
196
+ def lines(self):
197
+ """Return an array populated with the listing file lines."""
198
+ if self._lines is None:
199
+ with open(self.filename, self.openmode, encoding='utf-8', errors='replace') as f:
200
+ self._lines = [l.rstrip("\n") for l in f] # to remove trailing '\n'
201
+ return self._lines
202
+
203
+ def flush_lines(self):
204
+ """By defaults, listing lines are cached (that consumes memory). This method clear the cache."""
205
+ self._lines = None
206
+
207
+ @property
208
+ def end_is_reached(self):
209
+ """Return whether the end of CNT0 was reached."""
210
+ if self._end_is_reached is None:
211
+ self._end_is_reached = False
212
+ for line in self.lines:
213
+ if any([p in line for p in listings.OutputListing.patterns['end_is_reached']]):
214
+ self._end_is_reached = True
215
+ break
216
+ return self._end_is_reached
217
+
218
+ @property
219
+ def normset(self):
220
+ """Return a :class:`arpifs_listings.norms.NormsSet` object."""
221
+ if self._normset is None:
222
+ self._normset = norms.NormsSet(self.lines)
223
+ if not self.fmtdelayedopen:
224
+ self.flush_lines()
225
+ return self._normset
226
+
227
+ @property
228
+ def jotables(self):
229
+ """Return a :class:`arpifs_listings.jo_tables.JoTables` object."""
230
+ if self._jotables is None:
231
+ self._jotables = jo_tables.JoTables(self.filename, self.lines)
232
+ if not self.fmtdelayedopen:
233
+ self.flush_lines()
234
+ return self._jotables
235
+
236
+ @property
237
+ def cost_functions(self):
238
+ """Return a :class:`arpifs_listings.jo_tables.JoTables` object."""
239
+ if self._costs is None:
240
+ self._costs = cost_functions.CostFunctions(self.filename, self.lines)
241
+ if not self.fmtdelayedopen:
242
+ self.flush_lines()
243
+ return self._costs
244
+
245
+ def __len__(self):
246
+ """The number of lines in the listing."""
247
+ return len(self.lines)
248
+
249
+
250
+ class ListBasedCutoffDispenser:
251
+ """
252
+ From a dictionary of cutoff times (probably read from an extraction listing,
253
+ see :class:`BdmBufrListingsFormatAdapter`), for a given *obstype*, find the
254
+ best suited cutoff time.
255
+
256
+ The __call__ method takes a unique *obstype* argument. It will return the
257
+ best suited cutoff time for this particular *obstype*. N.B: If no exact
258
+ match is found, the latest cutoff time will be used.
259
+ """
260
+
261
+ def __init__(self, cutoffs, fuse_per_obstype=False):
262
+ self._cutoffs = cutoffs
263
+ f_cutoffs = {}
264
+ for k, dates in cutoffs.items():
265
+ f_dates = [d for d in dates if d is not None]
266
+ if f_dates:
267
+ f_cutoffs[k] = f_dates
268
+ if f_cutoffs:
269
+ self._max_cutoff = max([max(dates) for dates in f_cutoffs.values()])
270
+ else:
271
+ self._max_cutoff = None
272
+ self._default_cutoffs = defaultdict(lambda: self._max_cutoff)
273
+ self._default_cutoffs.update({k: max(dates)
274
+ for k, dates in f_cutoffs.items()})
275
+ self._fuse_per_obstype = fuse_per_obstype
276
+
277
+ @property
278
+ def max_cutoff(self):
279
+ """The latest cutoff time(accoss any available obstypes)."""
280
+ return self._max_cutoff
281
+
282
+ @property
283
+ def default_cutoffs(self):
284
+ """A dictionary of the latest cutoff time for each of the obstypes."""
285
+ return self._default_cutoffs
286
+
287
+ def __call__(self, obstype):
288
+ """Find the best suited cutoff time for *obstype*."""
289
+ obstype = obstype.lower()
290
+ if self._cutoffs.get(obstype, None) and not self._fuse_per_obstype:
291
+ item = self._cutoffs[obstype].popleft()
292
+ return item or self.default_cutoffs[obstype]
293
+ else:
294
+ return self.default_cutoffs[obstype]
295
+
296
+
297
+ class BdmBufrListingsFormatAdapter(FormatAdapterAbstractImplementation):
298
+ """Read the content of a BDM extraction output listing."""
299
+
300
+ _footprint = dict(
301
+ attr=dict(
302
+ format=dict(
303
+ values=['BDMBUFR_LISTING', ],
304
+ ),
305
+ )
306
+ )
307
+
308
+ _RE_OBSTYPE_GRP = re.compile(r"^.*tentative\s+(?:d')?extraction\s+pour\s+'?(?P<obstype>\w+)'?\b",
309
+ re.IGNORECASE)
310
+ _RE_OBSTYPE_CUT = re.compile(r"^.*cutoff\s+pour\s+'?(?P<obstype>\w+)'?\s*:\s*(?P<datetime>\d+)\b",
311
+ re.IGNORECASE)
312
+
313
+ def __init__(self, *kargs, **kwargs):
314
+ super().__init__(*kargs, **kwargs)
315
+ self._lines = None
316
+ self._cutoffs = defaultdict(deque)
317
+ if not self.fmtdelayedopen:
318
+ self.lines
319
+
320
+ @property
321
+ def lines(self):
322
+ """Return an array populated with the listing file lines."""
323
+ if self._lines is None:
324
+ with open(self.filename, self.openmode, encoding='utf-8', errors='replace') as f:
325
+ self._lines = [l.rstrip("\n") for l in f] # to remove trailing '\n'
326
+ return self._lines
327
+
328
+ @property
329
+ def cutoffs(self):
330
+ """
331
+ A dictionary of cutoff times for all of the obstypes available in the
332
+ listing.
333
+ """
334
+ if not self._cutoffs:
335
+ cur_obstype = None
336
+ for line in self.lines:
337
+ l_match = self._RE_OBSTYPE_GRP.match(line)
338
+ if l_match:
339
+ if cur_obstype is not None:
340
+ self._cutoffs[cur_obstype].append(None)
341
+ cur_obstype = l_match.group('obstype').lower()
342
+ if cur_obstype:
343
+ l_match = self._RE_OBSTYPE_CUT.match(line)
344
+ if l_match and l_match.group('obstype').lower() == cur_obstype:
345
+ self._cutoffs[cur_obstype].append(Date(l_match.group('datetime')))
346
+ cur_obstype = None
347
+ if cur_obstype is not None:
348
+ self._cutoffs[cur_obstype].append(None)
349
+ return self._cutoffs
350
+
351
+ def cutoffs_dispenser(self, fuse_per_obstype=False):
352
+ """Return a new :class:`CutoffDispenser` object."""
353
+ return ListBasedCutoffDispenser(copy.deepcopy(self.cutoffs),
354
+ fuse_per_obstype=fuse_per_obstype)