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