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
vortex/tools/addons.py ADDED
@@ -0,0 +1,316 @@
1
+ """
2
+ Abstract classes for System addons.
3
+ """
4
+
5
+ from collections import defaultdict
6
+
7
+ from bronx.fancies import loggers
8
+ from bronx.syntax.decorators import nicedeco
9
+ import footprints
10
+
11
+ from vortex.config import get_from_config_w_default
12
+ from vortex.layout import contexts
13
+ from vortex.tools.env import Environment
14
+ from vortex.tools.systems import OSExtended
15
+
16
+ logger = loggers.getLogger(__name__)
17
+
18
+ #: No automatic export
19
+ __all__ = []
20
+
21
+
22
+ class Addon(footprints.FootprintBase):
23
+ """Root class for any :class:`Addon` system subclasses."""
24
+
25
+ _abstract = True
26
+ _collector = ("addon",)
27
+ _footprint = dict(
28
+ info="Default add-on",
29
+ attr=dict(
30
+ kind=dict(),
31
+ sh=dict(
32
+ type=OSExtended,
33
+ alias=("shell",),
34
+ access="rwx-weak",
35
+ ),
36
+ env=dict(
37
+ type=Environment,
38
+ optional=True,
39
+ default=None,
40
+ access="rwx",
41
+ doc_visibility=footprints.doc.visibility.ADVANCED,
42
+ ),
43
+ cfginfo=dict(
44
+ optional=True,
45
+ default="[kind]",
46
+ doc_visibility=footprints.doc.visibility.ADVANCED,
47
+ ),
48
+ cmd=dict(
49
+ optional=True,
50
+ default=None,
51
+ access="rwx",
52
+ ),
53
+ path=dict(
54
+ optional=True,
55
+ default=None,
56
+ access="rwx",
57
+ ),
58
+ cycle=dict(
59
+ optional=True,
60
+ default=None,
61
+ access="rwx",
62
+ ),
63
+ toolkind=dict(optional=True, default=None),
64
+ ),
65
+ )
66
+
67
+ def __init__(self, *args, **kw):
68
+ """Abstract Addon initialisation."""
69
+ logger.debug("Abstract Addon init %s", self.__class__)
70
+ super().__init__(*args, **kw)
71
+ self.sh.extend(self)
72
+ self._context_cache = defaultdict(dict)
73
+ self._cmd_xperms_cache = set()
74
+ if self.env is None:
75
+ self.env = Environment(active=False, clear=True)
76
+ clsenv = self.__class__.__dict__
77
+ for k in [x for x in clsenv.keys() if x.isupper()]:
78
+ self.env[k] = clsenv[k]
79
+ if self.path is None:
80
+ self.path = get_from_config_w_default(
81
+ section="nwp-tools",
82
+ key=self.kind,
83
+ default=None,
84
+ )
85
+
86
+ @classmethod
87
+ def in_shell(cls, shell):
88
+ """Grep any active instance of that class in the specified shell."""
89
+ lx = [x for x in shell.search if isinstance(x, cls)]
90
+ return lx[0] if lx else None
91
+
92
+ def _query_context(self):
93
+ """Return the path and cmd for the current context.
94
+
95
+ Results are cached so that the context's localtracker is explored only once.
96
+
97
+ .. note:: We use the localtracker instead of the sequence because, in
98
+ multistep jobs, the localtracker is preserved between steps. It's
99
+ less elegant but it plays nice with MTOOL.
100
+ """
101
+ ctxtag = contexts.Context.tag_focus()
102
+ if ctxtag not in self._context_cache and self.toolkind is not None:
103
+ ltrack = contexts.current().localtracker
104
+ # NB: 'str' is important because local might be in unicode...
105
+ candidates = [
106
+ str(self.sh.path.realpath(local))
107
+ for local, entry in ltrack.items()
108
+ if (
109
+ entry.latest_rhdict("get")
110
+ .get("resource", dict())
111
+ .get("kind", "")
112
+ == self.toolkind
113
+ )
114
+ ]
115
+ if candidates:
116
+ realpath = candidates.pop()
117
+ self._context_cache[ctxtag] = dict(
118
+ path=self.sh.path.dirname(realpath),
119
+ cmd=self.sh.path.basename(realpath),
120
+ )
121
+ return self._context_cache[ctxtag]
122
+
123
+ @property
124
+ def actual_path(self):
125
+ """The path that should be used in the current context."""
126
+ infos = self._query_context()
127
+ ctxpath = infos.get("path", None)
128
+ return self.path if ctxpath is None else ctxpath
129
+
130
+ @property
131
+ def actual_cmd(self):
132
+ """The cmd that should be used in the current context."""
133
+ infos = self._query_context()
134
+ ctxcmd = infos.get("cmd", None)
135
+ return self.cmd if ctxcmd is None else ctxcmd
136
+
137
+ def _spawn_commons(self, cmd, **kw):
138
+ """Internal method setting local environment and calling standard shell spawn."""
139
+
140
+ # Is there a need for an interpreter ?
141
+ if "interpreter" in kw:
142
+ cmd.insert(0, kw.pop("interpreter"))
143
+ else:
144
+ # The first element of the command line needs to be executable
145
+ if cmd[0] not in self._cmd_xperms_cache:
146
+ self._cmd_xperms_cache.add(cmd[0])
147
+ self.sh.xperm(cmd[0], force=True)
148
+
149
+ # Overwrite global module env values with specific ones
150
+ with self.sh.env.clone() as localenv:
151
+ localenv.verbose(True, self.sh)
152
+ localenv.update(self.env)
153
+
154
+ # Check if a pipe is requested
155
+ inpipe = kw.pop("inpipe", False)
156
+
157
+ # Ask the attached shell to run the addon command
158
+ if inpipe:
159
+ kw.setdefault("stdout", True)
160
+ rc = self.sh.popen(cmd, **kw)
161
+ else:
162
+ rc = self.sh.spawn(cmd, **kw)
163
+
164
+ return rc
165
+
166
+ def _spawn(self, cmd, **kw):
167
+ """Internal method setting local environment and calling standard shell spawn."""
168
+
169
+ # Insert the actual tool command as first argument
170
+ cmd.insert(0, self.actual_cmd)
171
+ if self.actual_path is not None:
172
+ cmd[0] = self.actual_path + "/" + cmd[0]
173
+
174
+ return self._spawn_commons(cmd, **kw)
175
+
176
+ def _spawn_wrap(self, cmd, **kw):
177
+ """Internal method setting local environment and calling standard shell spawn."""
178
+
179
+ # Insert the tool path before the first argument
180
+ if self.actual_path is not None:
181
+ cmd[0] = self.actual_path + "/" + cmd[0]
182
+
183
+ return self._spawn_commons(cmd, **kw)
184
+
185
+
186
+ class FtrawEnableAddon(Addon):
187
+ """Root class for any :class:`Addon` system subclasses that needs to override rawftput."""
188
+
189
+ _abstract = True
190
+ _footprint = dict(
191
+ info="Default add-on with rawftput support.",
192
+ attr=dict(
193
+ rawftshell=dict(
194
+ info="Path to ftserv's concatenation shell",
195
+ optional=True,
196
+ default=None,
197
+ access="rwx",
198
+ doc_visibility=footprints.doc.visibility.GURU,
199
+ ),
200
+ ),
201
+ )
202
+
203
+ def __init__(self, *args, **kw):
204
+ """Abstract Addon initialisation."""
205
+ logger.debug("Abstract Addon init %s", self.__class__)
206
+ super().__init__(*args, **kw)
207
+ # If needed, look in the config file for the rawftshell
208
+ if self.rawftshell is None:
209
+ self.rawftshell = get_from_config_w_default(
210
+ section="rawftshell",
211
+ key=self.kind,
212
+ default=None,
213
+ )
214
+
215
+
216
+ class AddonGroup(footprints.FootprintBase):
217
+ """Root class for any :class:`AddonGroup` system subclasses.
218
+
219
+ An AddonGroup is not really an Addon... it just loads a bunch of other
220
+ Addons or AddonGroups into the current shell.
221
+ """
222
+
223
+ _abstract = True
224
+ _collector = ("addon",)
225
+ _footprint = dict(
226
+ info="Default add-on group",
227
+ attr=dict(
228
+ kind=dict(),
229
+ sh=dict(
230
+ type=OSExtended,
231
+ alias=("shell",),
232
+ ),
233
+ env=dict(
234
+ type=Environment,
235
+ optional=True,
236
+ default=None,
237
+ doc_visibility=footprints.doc.visibility.ADVANCED,
238
+ ),
239
+ cycle=dict(
240
+ optional=True,
241
+ default=None,
242
+ ),
243
+ verboseload=dict(
244
+ optional=True,
245
+ default=True,
246
+ type=bool,
247
+ ),
248
+ ),
249
+ )
250
+
251
+ _addonslist = None
252
+
253
+ def __init__(self, *args, **kw):
254
+ """Abstract Addon initialisation."""
255
+ logger.debug("Abstract Addon init %s", self.__class__)
256
+ super().__init__(*args, **kw)
257
+ self._addons_load()
258
+
259
+ def _addons_load(self):
260
+ if self._addonslist is None:
261
+ raise RuntimeError(
262
+ "the _addonslist classe variable must be overriden."
263
+ )
264
+ self._load_addons_from_list(self._addonslist)
265
+
266
+ def _load_addons_from_list(self, addons):
267
+ if self.verboseload:
268
+ logger.info("Loading the %s Addons group.", self.kind)
269
+ for addon in addons:
270
+ _shadd = footprints.proxy.addon(
271
+ kind=addon,
272
+ sh=self.sh,
273
+ env=self.env,
274
+ cycle=self.cycle,
275
+ verboseload=self.verboseload,
276
+ )
277
+ if self.verboseload:
278
+ logger.info("%s Addon is: %s", addon, repr(_shadd))
279
+
280
+
281
+ def require_external_addon(*addons):
282
+ """
283
+ A method decorator usable in addons, that will check if addons listed in
284
+ **addons** are properly loaded in the parent System object.
285
+
286
+ If not, a :class:`RuntimeError` exception will be raised.
287
+ """
288
+
289
+ @nicedeco
290
+ def r_addon_decorator(method):
291
+ def decorated(self, *kargs, **kwargs):
292
+ # Create a cache in self... ugly but efficient !
293
+ if not hasattr(self, "_require_external_addon_check_cache"):
294
+ setattr(self, "_require_external_addon_check_cache", set())
295
+ ko_addons = set()
296
+ loaded_addons = None
297
+ for addon in addons:
298
+ if addon in self._require_external_addon_check_cache:
299
+ continue
300
+ if loaded_addons is None:
301
+ loaded_addons = self.sh.loaded_addons()
302
+ if addon in loaded_addons:
303
+ self._require_external_addon_check_cache.add(addon)
304
+ else:
305
+ ko_addons.add(addon)
306
+ if ko_addons:
307
+ raise RuntimeError(
308
+ "The following addons are needed to use the {:s} method: {:s}".format(
309
+ method.__name__, ", ".join(ko_addons)
310
+ )
311
+ )
312
+ return method(self, *kargs, **kwargs)
313
+
314
+ return decorated
315
+
316
+ return r_addon_decorator
vortex/tools/arm.py ADDED
@@ -0,0 +1,96 @@
1
+ """
2
+ Module needed to work with ARM tools such as Forge.
3
+ """
4
+
5
+ from bronx.fancies import loggers
6
+
7
+ from vortex.util.config import load_template
8
+
9
+ #: No automatic export
10
+ __all__ = ["ArmForgeTool"]
11
+
12
+ logger = loggers.getLogger(__name__)
13
+
14
+
15
+ class ArmForgeTool:
16
+ """Work with the ARM tools such as DDT & MAP."""
17
+
18
+ def __init__(self, ticket):
19
+ """
20
+ :param ticket: The current Vortex' session ticket.
21
+ """
22
+ self._t = ticket
23
+ self._sh = self._t.sh
24
+ self._config = self._sh.default_target.items("armtools")
25
+ self._ddtpath = self._sh.env.get("VORTEX_ARM_DDT_PATH", None)
26
+ self._mappath = self._sh.env.get("VORTEX_ARM_MAP_PATH", None)
27
+ self._forgedir = self._sh.env.get(
28
+ "VORTEX_ARM_FORGE_DIR", self.config.get("forgedir", None)
29
+ )
30
+ self._forgeversion = self._sh.env.get(
31
+ "VORTEX_ARM_FORGE_VERSION", self.config.get("forgeversion", 999999)
32
+ )
33
+ self._forgeversion = int(self._forgeversion)
34
+ if self._forgedir and self._ddtpath is None:
35
+ self._ddtpath = self._sh.path.join(self._forgedir, "bin", "ddt")
36
+ if self._forgedir and self._mappath is None:
37
+ self._mappath = self._sh.path.join(self._forgedir, "bin", "map")
38
+
39
+ @property
40
+ def config(self):
41
+ """The configuration dictionary."""
42
+ return self._config
43
+
44
+ @property
45
+ def ddtpath(self):
46
+ """The path to the DDT debuger executable."""
47
+ if self._ddtpath is None:
48
+ raise RuntimeError(
49
+ "DDT requested but the DDT path is not configured."
50
+ )
51
+ return self._ddtpath
52
+
53
+ @property
54
+ def mappath(self):
55
+ """The path to the MAP profiler executable."""
56
+ if self._mappath is None:
57
+ raise RuntimeError(
58
+ "MAP requested but the MAP path is not configured."
59
+ )
60
+ return self._mappath
61
+
62
+ def _dump_forge_session(self, sources=(), workdir=None):
63
+ """Create the ARM Forge's session file to list source directories."""
64
+ targetfile = "armforge-vortex-session-file.ddt"
65
+ if workdir:
66
+ targetfile = self._sh.path.join(workdir, targetfile)
67
+ tpl = load_template(
68
+ self._t,
69
+ "@armforge-session-conf.tpl",
70
+ encoding="utf-8",
71
+ version=self._forgeversion,
72
+ )
73
+ sconf = tpl.substitute(
74
+ sourcedirs="\n".join(
75
+ [
76
+ " <directory>{:s}</directory>".format(d)
77
+ for d in sources
78
+ ]
79
+ )
80
+ )
81
+ with open(targetfile, "w") as fhs:
82
+ fhs.write(sconf)
83
+ return targetfile
84
+
85
+ def ddt_prefix_cmd(self, sources=(), workdir=None):
86
+ """Generate the prefix command required to start DDT."""
87
+ if sources:
88
+ return [
89
+ self.ddtpath,
90
+ "--session={:s}".format(
91
+ self._dump_forge_session(sources, workdir=workdir)
92
+ ),
93
+ "--connect",
94
+ ]
95
+ else:
96
+ return [self.ddtpath, "--connect"]