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,644 @@
1
+ """
2
+ Generic Resources and Contents to work with namelists.
3
+ """
4
+
5
+ import re
6
+
7
+ from bronx.fancies import loggers
8
+ from bronx.stdtypes.date import Time, Date
9
+ from bronx.datagrip.namelist import NO_SORTING, NamelistSet, NamelistParser
10
+ from footprints.stdtypes import FPList
11
+ from vortex import sessions
12
+ from vortex.data.outflow import ModelResource, StaticResource
13
+ from vortex.data.outflow import ModelGeoResource
14
+ from vortex.data.contents import AlmostDictContent, IndexedTable
15
+ from vortex.syntax.stdattrs import binaries, term, cutoff
16
+ from vortex.syntax.stddeco import namebuilding_insert
17
+ from vortex.tools import env
18
+ from ..syntax.stdattrs import gvar
19
+
20
+ #: No automatic export
21
+ __all__ = []
22
+
23
+ logger = loggers.getLogger(__name__)
24
+
25
+ KNOWN_NAMELIST_MACROS = {'NPROC', 'NBPROC', 'NBPROC_IO', 'NCPROC', 'NDPROC',
26
+ 'NBPROCIN', 'NBPROCOUT', 'IDAT', 'CEXP',
27
+ 'TIMESTEP', 'FCSTOP', 'NMODVAL', 'NBE', 'SEED',
28
+ 'MEMBER', 'NUMOD', 'OUTPUTID', 'NRESX', 'PERTURB',
29
+ 'JOUR', 'RES', 'LLADAJ', 'LLADMON', 'LLFLAG',
30
+ 'LLARO', 'LLVRP', 'LLCAN'}
31
+
32
+
33
+ class NamelistPack(ModelResource):
34
+ """
35
+ Class for all kinds of namelists
36
+ """
37
+ _footprint = [
38
+ gvar,
39
+ dict(
40
+ info = 'A whole Namelist pack',
41
+ attr = dict(
42
+ kind = dict(
43
+ values = ['namelistpack']
44
+ ),
45
+ gvar = dict(
46
+ values = ['NAMELIST_' + x.upper() for x in binaries],
47
+ default = 'namelist_[binary]'
48
+ ),
49
+ model = dict(
50
+ optional = True,
51
+ ),
52
+ binary = dict(
53
+ optional = True,
54
+ values = binaries,
55
+ default = '[model]',
56
+ ),
57
+ )
58
+ )
59
+ ]
60
+
61
+ @property
62
+ def realkind(self):
63
+ return 'namelistpack'
64
+
65
+ def gget_urlquery(self):
66
+ """GGET specific query : ``dir_extract``."""
67
+ return 'dir_extract=1'
68
+
69
+
70
+ class NamelistContentError(ValueError):
71
+ pass
72
+
73
+
74
+ class NamelistContent(AlmostDictContent):
75
+ """Fortran namelist including namelist blocks."""
76
+
77
+ def __init__(self, **kw):
78
+ """
79
+ Initialize default namelist content with optional parameters:
80
+ * macros : pre-defined macros for all namelist blocks
81
+ * remove : elements to remove from the contents
82
+ * parser : a namelist parser object (a default one will be built otherwise)
83
+ """
84
+ kw.setdefault('macros', {k: None for k in KNOWN_NAMELIST_MACROS})
85
+ kw.setdefault('remove', set())
86
+ kw.setdefault('parser', None)
87
+ kw.setdefault('data', NamelistSet())
88
+ super().__init__(**kw)
89
+ self._declaredmacros = set(self._macros.keys())
90
+
91
+ def toremove(self, bname):
92
+ """Add an entry to the list of blocks to be removed."""
93
+ self._remove.add(bname)
94
+
95
+ def rmblocks(self):
96
+ """Returns the list of blocks to get rid off."""
97
+ return self._remove
98
+
99
+ def macros(self):
100
+ """Returns the dictionary of macros already registered."""
101
+ return self._macros.copy()
102
+
103
+ def setmacro(self, item, value):
104
+ """Set macro value for further substitution."""
105
+ self._data.setmacro(item, value)
106
+ self._macros[item] = value
107
+
108
+ @property
109
+ def dumps_needs_update(self):
110
+ """Tells wether something as changed in the namelist's dump."""
111
+ return self._data.dumps_needs_update
112
+
113
+ def dumps(self, sorting=NO_SORTING):
114
+ """
115
+ Returns the namelist contents as a string.
116
+ Sorting option **sorting** (from bronx.datagrip.namelist):
117
+
118
+ * NO_SORTING;
119
+ * FIRST_ORDER_SORTING => sort all keys within blocks;
120
+ * SECOND_ORDER_SORTING => sort only within indexes or attributes of the same key.
121
+
122
+ """
123
+ return self._data.dumps(sorting=sorting)
124
+
125
+ def merge(self, delta, rmkeys=None, rmblocks=None, clblocks=None):
126
+ """Merge of the current namelist content with the set of namelist blocks provided."""
127
+ if isinstance(delta, NamelistContent):
128
+ if rmblocks is None and hasattr(delta, 'rmblocks'):
129
+ rmblocks = delta.rmblocks()
130
+ actualdelta = delta.data
131
+ else:
132
+ actualdelta = delta
133
+ self._data.merge(actualdelta,
134
+ rmkeys=rmkeys, rmblocks=rmblocks, clblocks=clblocks)
135
+
136
+ def slurp(self, container):
137
+ """Get data from the ``container`` namelist."""
138
+ if not self._parser:
139
+ self._parser = NamelistParser(macros=self._declaredmacros)
140
+ with container.preferred_decoding(byte=False):
141
+ container.rewind()
142
+ try:
143
+ namset = self._parser.parse(container.read())
144
+ except (ValueError, OSError) as e:
145
+ raise NamelistContentError('Could not parse container contents: {!s}'.format(e))
146
+ self._data = namset
147
+ for macro, value in self._macros.items():
148
+ self._data.setmacro(macro, value)
149
+
150
+ def rewrite(self, container, sorting=NO_SORTING):
151
+ """
152
+ Write the namelist contents in the specified container.
153
+ Sorting option **sorting** (from bronx.datagrip.namelist):
154
+
155
+ * NO_SORTING;
156
+ * FIRST_ORDER_SORTING => sort all keys within blocks;
157
+ * SECOND_ORDER_SORTING => sort only within indexes or attributes of the same key.
158
+
159
+ """
160
+ container.close()
161
+ with container.iod_context():
162
+ with container.preferred_decoding(byte=False):
163
+ container.write(self.dumps(sorting=sorting))
164
+
165
+
166
+ class Namelist(ModelResource):
167
+ """
168
+ Class for all kinds of namelists
169
+ """
170
+ _footprint = [
171
+ gvar,
172
+ dict(
173
+ info = 'Namelist from binary pack',
174
+ attr = dict(
175
+ kind = dict(
176
+ values = ['namelist']
177
+ ),
178
+ clscontents = dict(
179
+ default = NamelistContent
180
+ ),
181
+ gvar = dict(
182
+ values = ['NAMELIST_' + x.upper() for x in binaries],
183
+ default = 'namelist_[binary]'
184
+ ),
185
+ source = dict(
186
+ info = 'The namelist name within the namelist pack.',
187
+ optional = True,
188
+ default = 'namel_[binary]',
189
+ doc_zorder = 50
190
+ ),
191
+ model = dict(
192
+ optional = True,
193
+ ),
194
+ binary = dict(
195
+ optional = True,
196
+ values = binaries,
197
+ default = '[model]',
198
+ ),
199
+ date = dict(
200
+ type = Date,
201
+ optional = True,
202
+ )
203
+ )
204
+ )
205
+ ]
206
+
207
+ @property
208
+ def realkind(self):
209
+ return 'namelist'
210
+
211
+ def _find_source(self):
212
+ sources = self.source.split('|')
213
+ if len(sources) == 1:
214
+ source = sources[0].split(':')[0]
215
+ else:
216
+ # Check that the date argument was provided.:
217
+ if self.date is None:
218
+ raise AttributeError('The date argument should be provided when dealing ' +
219
+ 'with time based namelist sources.')
220
+ datedSource = {}
221
+ for s in sources:
222
+ dateNsource = s.split(':')
223
+ if dateNsource[0]:
224
+ if len(dateNsource) == 2:
225
+ date = Date(dateNsource[1], year=self.date.year)
226
+ else:
227
+ date = Date(self.date.year, 1, 1)
228
+ if date not in datedSource.keys():
229
+ datedSource[date] = dateNsource[0]
230
+ else:
231
+ logger.warning('%s already begins the %s, %s is ignored.',
232
+ datedSource[date],
233
+ date.strftime('%d of %b.'), dateNsource[0])
234
+ datedSource = sorted(datedSource.items(), reverse=True)
235
+ source = datedSource[0][1]
236
+ for dateNsource in datedSource:
237
+ if self.date >= dateNsource[0]:
238
+ source = dateNsource[1]
239
+ break
240
+ logger.info('The consistent source is %s', source)
241
+
242
+ return source
243
+
244
+ def gget_urlquery(self):
245
+ """GGET specific query : ``extract``."""
246
+ return 'extract=' + self._find_source()
247
+
248
+
249
+ class NamelistDelta(Namelist):
250
+ """
251
+ Class for namelist deltas (i.e. small bits of namelists).
252
+ """
253
+
254
+ _footprint = dict(
255
+ attr = dict(
256
+ kind = dict(
257
+ values = ['namdelta', 'deltanam', ]
258
+ ),
259
+ source = dict(
260
+ default = 'deltanam.[binary]',
261
+ ),
262
+ )
263
+ )
264
+
265
+ @property
266
+ def realkind(self):
267
+ return 'namdelta'
268
+
269
+
270
+ class NamelistUtil(Namelist):
271
+ """
272
+ Class for namelists utilities
273
+ """
274
+ _footprint = dict(
275
+ info = 'Namelist from utilities pack',
276
+ attr = dict(
277
+ kind = dict(
278
+ values = ['namelist_util', 'namutil'],
279
+ remap = dict(autoremap = 'first'),
280
+ ),
281
+ gvar = dict(
282
+ values = ['NAMELIST_UTILITIES'],
283
+ default = 'namelist_utilities'
284
+ ),
285
+ binary = dict(
286
+ values = ['batodb', 'utilities', 'odbtools'],
287
+ default = 'utilities',
288
+ optional = True,
289
+ ),
290
+ )
291
+ )
292
+
293
+
294
+ class NamelistTerm(Namelist):
295
+ """
296
+ Class for all the terms dependent namelists
297
+ """
298
+ _footprint = [
299
+ term,
300
+ dict(
301
+ info = 'Terms dependent namelist',
302
+ attr = dict(
303
+ kind = dict(
304
+ values = ['namterm']
305
+ )
306
+ )
307
+ )
308
+ ]
309
+
310
+ def incoming_xxt_fixup(self, attr, key=None, prefix=None):
311
+ """Fix as best as possible the ``xxt.def`` file."""
312
+
313
+ regex = re.compile(r',(.*)$')
314
+ myenv = env.current()
315
+ suffix = regex.search(myenv.VORTEX_XXT_DEF)
316
+ if suffix:
317
+ fp = suffix.group(1)
318
+ else:
319
+ fp = None
320
+
321
+ try:
322
+ with open('xxt.def') as f:
323
+ lines = f.readlines()
324
+ except OSError:
325
+ logger.error('Could not open file xxt.def')
326
+ raise
327
+
328
+ select = lines[self.term.hour].split()[2]
329
+
330
+ if not re.match(r'undef', select):
331
+ if fp:
332
+ rgx = re.compile(key + r'(.*)$')
333
+ sfx = rgx.search(select)
334
+ if sfx:
335
+ s = sfx.group(1)
336
+ else:
337
+ s = ''
338
+ return ''.join((key, '_', fp, s))
339
+ else:
340
+ return select
341
+ else:
342
+ logger.error('Fullpos namelist id not defined for term %s', self.term)
343
+
344
+ def incoming_namelist_fixup(self, attr, key=None):
345
+ """Fix as best as possible the namelist term extensions."""
346
+
347
+ val = getattr(self, attr)
348
+ r1 = re.compile(r'^(.*\/)?(' + key + r'.*_fp|cpl)$')
349
+ r2 = re.compile(r'^(.*\/)?(' + key + r'.*_fp)(\..*)$')
350
+ r3 = re.compile(r'^(.*\/)?(' + key + r'.*_p)$')
351
+
352
+ fixed = 0
353
+
354
+ for r in (r1, r2, r3):
355
+ s = r.search(val)
356
+ if s:
357
+ fixed = 1
358
+ (dirpath, base) = (s.group(1), s.group(2))
359
+ if dirpath is None:
360
+ dirpath = ''
361
+ ext = ''
362
+ if r == r3:
363
+ if self.term.hour == 0:
364
+ p = '0'
365
+ elif self.term.hour % 6 == 0:
366
+ p = '6'
367
+ elif self.term.hour % 3 == 0:
368
+ p = '3'
369
+ else:
370
+ p = '1'
371
+ else:
372
+ if self.term.hour == 0:
373
+ p = '0'
374
+ else:
375
+ p = ''
376
+ if r == r2:
377
+ ext = s.group(3)
378
+ if ext is None:
379
+ ext = ''
380
+
381
+ if fixed:
382
+ return dirpath + base + p + ext
383
+ else:
384
+ return val
385
+
386
+
387
+ class NamelistSelect(NamelistTerm):
388
+ """
389
+ Class for the select namelists
390
+ """
391
+ _footprint = [
392
+ dict(
393
+ info = 'Select namelist for fullpos ',
394
+ attr = dict(
395
+ kind = dict(
396
+ values = ['namselect', ]
397
+ )
398
+ )
399
+ )
400
+ ]
401
+
402
+ @property
403
+ def realkind(self):
404
+ return 'namselect'
405
+
406
+ def gget_urlquery(self):
407
+ """GGET specific query : ``extract``."""
408
+ myenv = env.current()
409
+ if myenv.true('VORTEX_XXT_DEF'):
410
+ return 'extract=' + self.incoming_xxt_fixup('source', 'select')
411
+ else:
412
+ return 'extract={:s}'.format(self.source)
413
+
414
+
415
+ class NamelistFullPos(NamelistTerm):
416
+ """
417
+ Class for the fullpos term dependent namelists
418
+ """
419
+ _footprint = [
420
+ dict(
421
+ info = 'Namelist for offline fullpos ',
422
+ attr = dict(
423
+ kind = dict(
424
+ values = ['namelistfp', ]
425
+ )
426
+ )
427
+ )
428
+ ]
429
+
430
+ @property
431
+ def realkind(self):
432
+ return 'namelistfp'
433
+
434
+ def gget_urlquery(self):
435
+ """GGET specific query : ``extract``."""
436
+ return 'extract=' + self.incoming_namelist_fixup('source', 'namel')
437
+
438
+
439
+ class NamelistFpServerObject(Namelist):
440
+ """Class for a fullpos server object's namelists."""
441
+
442
+ _footprint = dict(
443
+ info = 'Namelist for a fullpos server object',
444
+ attr = dict(
445
+ kind = dict(
446
+ values = ['namelist_fpobject', ]
447
+ ),
448
+ fp_conf = dict(
449
+ info = 'The FPCONF setting associated with this object.',
450
+ type = int,
451
+ optional = True,
452
+ ),
453
+ fp_cmodel = dict(
454
+ info = 'The CMODEL setting associated with this object.',
455
+ optional = True,
456
+ ),
457
+ fp_lextern = dict(
458
+ info = 'The LEXTERN setting associated with this object.',
459
+ type = bool,
460
+ optional = True,
461
+ ),
462
+ fp_terms = dict(
463
+ info = ('Apply this object only on a subset of the input '
464
+ 'data (based on term)'),
465
+ type = FPList,
466
+ optional = True,
467
+ )
468
+ )
469
+ )
470
+
471
+ @property
472
+ def realkind(self):
473
+ return 'namelist_fpobject'
474
+
475
+
476
+ class XXTContent(IndexedTable):
477
+ """Indexed table of selection namelist used by inlined fullpos forecasts."""
478
+
479
+ def __init__(self, *kargs, **kwargs):
480
+ super().__init__(*kargs, **kwargs)
481
+ self._cachedomains = None
482
+ self._cachedomains_term = None
483
+
484
+ def fmtkey(self, key):
485
+ """Reshape entry keys of the internal dictionary as a :class:`~bronx.stdtypes.date.Time` value."""
486
+ key = Time(key)
487
+ return key.fmthm
488
+
489
+ def xxtpos(self, n, g, x):
490
+ """
491
+ Return value in position ``n`` for the ``term`` occurence defined in ``g`` or ``x``.
492
+ * ``g`` stands for a guess dictionary.
493
+ * ``x`` stands for an extra dictionary.
494
+
495
+ These naming convention refer to the footprints resolve mechanism.
496
+ """
497
+ t = g.get('term', x.get('term', None))
498
+ if t is None:
499
+ return None
500
+ else:
501
+ value = None
502
+ try:
503
+ t = Time(t)
504
+ except (ValueError, TypeError):
505
+ return None
506
+ tkey = self.get(t.fmthm, self.get(str(t.hour), None))
507
+ if tkey is None:
508
+ logger.warning('No entry found in the XXT file for term = %s.', t.fmthm)
509
+ else:
510
+ try:
511
+ value = tkey[n]
512
+ except IndexError:
513
+ return None
514
+ return value
515
+
516
+ def xxtnam(self, g, x):
517
+ """Return local namelist filename according to first column."""
518
+ return self.xxtpos(0, g, x)
519
+
520
+ def xxtsrc(self, g, x):
521
+ """Return local namelist source in gco set according to second column."""
522
+ return self.xxtpos(1, g, x)
523
+
524
+ def mapdomains(self, maxterm=None, where=None):
525
+ """Return a map of domains associated for each term in selection namelists."""
526
+ mapdom = dict()
527
+ allterms = sorted([Time(x) for x in self.keys()])
528
+ if maxterm is None:
529
+ if allterms:
530
+ maxterm = allterms[-1]
531
+ else:
532
+ maxterm = -1
533
+ maxterm = Time(maxterm)
534
+
535
+ if (self._cachedomains is None) or (self._cachedomains_term != maxterm):
536
+
537
+ select_seen = dict()
538
+ for a_term in [x for x in allterms if x <= maxterm]:
539
+ tvalue = self.get(a_term.fmthm, self.get(str(a_term.hour), None))
540
+ sh = sessions.system()
541
+ if tvalue[0] is not None:
542
+ local_guesses = [tvalue[0], 'fpselect_' + a_term.fmthm]
543
+ if where:
544
+ local_guesses = [sh.path.join(where, g) for g in local_guesses]
545
+ local_guesses = [g for g in local_guesses if sh.path.exists(g)]
546
+ if local_guesses:
547
+ # Do not waste time on duplicated selects...
548
+ if tvalue[1] not in select_seen:
549
+ fortp = NamelistParser()
550
+ with open(local_guesses[0]) as fd:
551
+ xx = fortp.parse(fd.read())
552
+ domains = set()
553
+ for nb in xx.values():
554
+ for domlist in [y for x, y in nb.items() if x.startswith('CLD')]:
555
+ domains = domains | set(domlist.pop().split(':'))
556
+ select_seen[tvalue[1]] = domains
557
+ else:
558
+ domains = select_seen[tvalue[1]]
559
+ mapdom[a_term.fmthm] = list(domains)
560
+ if a_term.minute == 0:
561
+ mapdom[str(a_term.hour)] = list(domains)
562
+
563
+ self._cachedomains_term = maxterm
564
+ self._cachedomains = mapdom
565
+
566
+ else:
567
+ mapdom = self._cachedomains
568
+
569
+ return dict(term=mapdom)
570
+
571
+
572
+ class NamelistSelectDef(StaticResource):
573
+ """Utility, so-called xxt file."""
574
+ _footprint = [
575
+ cutoff,
576
+ gvar,
577
+ dict(
578
+ info = 'xxt.def file from namelist pack',
579
+ attr = dict(
580
+ gvar = dict(
581
+ values = ['NAMELIST_' + x.upper() for x in binaries],
582
+ default = 'namelist_[binary]'
583
+ ),
584
+ source = dict(
585
+ optional = True,
586
+ ),
587
+ binary = dict(
588
+ optional = True,
589
+ values = binaries,
590
+ default = '[model]',
591
+ ),
592
+ kind = dict(
593
+ values = ['xxtdef', 'namselectdef']
594
+ ),
595
+ clscontents = dict(
596
+ default = XXTContent
597
+ )
598
+ ),
599
+ bind = ['gvar', 'source']
600
+ )
601
+ ]
602
+
603
+ _source_map = dict(assim='xxt.def.assim', )
604
+
605
+ @property
606
+ def realkind(self):
607
+ return 'namselectdef'
608
+
609
+ def gget_urlquery(self):
610
+ """GGET specific query : ``extract``."""
611
+ if self.source is None:
612
+ thesource = self._source_map.get(self.cutoff, 'xxt.def')
613
+ else:
614
+ thesource = self.source
615
+ return 'extract=' + thesource
616
+
617
+
618
+ @namebuilding_insert('src', lambda s: s.target)
619
+ class GeoBlocks(ModelGeoResource):
620
+ """Extract of a namelist containing Geometry blocks."""
621
+
622
+ _footprint = dict(
623
+ attr = dict(
624
+ kind = dict(
625
+ info = "Geometry blocks of namelist.",
626
+ values = ['geoblocks']
627
+ ),
628
+ clscontents = dict(
629
+ default = NamelistContent,
630
+ ),
631
+ target = dict(
632
+ info = "Scope that should use these blocks.",
633
+ ),
634
+ nativefmt = dict(
635
+ optional = True,
636
+ values = ['nam'],
637
+ default = 'nam',
638
+ ),
639
+ )
640
+ )
641
+
642
+ @property
643
+ def realkind(self):
644
+ return 'geoblocks'