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,660 @@
1
+ """
2
+ A set of AlgoComponents interrogating various databases.
3
+ """
4
+
5
+ import copy
6
+
7
+ from bronx.fancies import loggers
8
+ from bronx.stdtypes.date import Time
9
+ import footprints
10
+
11
+ from vortex.algo.components import (
12
+ AlgoComponent,
13
+ AlgoComponentDecoMixin,
14
+ Expresso,
15
+ BlindRun,
16
+ )
17
+ from vortex.algo.components import algo_component_deco_mixin_autodoc
18
+ from vortex.syntax.stdattrs import a_date
19
+ from vortex.tools.systems import ExecutionError
20
+ from ..tools.bdap import (
21
+ BDAPrequest_actual_command,
22
+ BDAPGetError,
23
+ BDAPRequestConfigurationError,
24
+ )
25
+ from ..tools.bdmp import BDMPrequest_actual_command, BDMPGetError
26
+ from ..tools.bdcp import BDCPrequest_actual_command, BDCPGetError
27
+ from ..tools.bdm import BDMGetError, BDMRequestConfigurationError, BDMError
28
+ from ..tools.mars import MarsGetError, callMarsExtract, findMarsExtractCommand
29
+ from ..data.obs import ObsMapContent
30
+
31
+ #: No automatic export
32
+ __all__ = []
33
+
34
+ logger = loggers.getLogger(__name__)
35
+
36
+
37
+ class GetBDAPResource(AlgoComponent):
38
+ """Algo component to get BDAP resources considering a BDAP query file."""
39
+
40
+ _footprint = dict(
41
+ info="Algo component to get BDAP files.",
42
+ attr=dict(
43
+ kind=dict(
44
+ values=["get_bdap"],
45
+ ),
46
+ date=a_date,
47
+ target_bdap=dict(
48
+ default="OPER",
49
+ optional=True,
50
+ values=["OPER", "INTE"],
51
+ ),
52
+ terms=dict(
53
+ info="A forecast term or a list of terms (rangex will be used to expand the string)",
54
+ alias=("term",),
55
+ ),
56
+ command=dict(
57
+ default="dap3",
58
+ optional=True,
59
+ values=["dap3", "dap3_dev"],
60
+ ),
61
+ ),
62
+ )
63
+
64
+ def execute_single(self, rh, opts):
65
+ """
66
+ Launch the BDAP request(s).
67
+ The results of each request are stored in a directory local_directory to avoid
68
+ files to be overwritten.
69
+ """
70
+
71
+ # Determine the target BDAP
72
+ int_bdap = self.target_bdap == "INTE"
73
+
74
+ # Look for the input queries
75
+ input_queries = self.context.sequence.effective_inputs(
76
+ role="Query",
77
+ kind="bdap_query",
78
+ )
79
+
80
+ rc_all = True
81
+
82
+ for input_query in input_queries:
83
+ for term in [Time(t) for t in footprints.util.rangex(self.terms)]:
84
+ # Launch each input queries in a dedicated file
85
+ # (to check that the files do not overwrite each other)
86
+ query_file = input_query.rh.container.abspath
87
+ local_directory = "_".join(
88
+ [query_file, self.date.ymdhms, term.fmtraw]
89
+ )
90
+
91
+ with self.system.cdcontext(local_directory, create=True):
92
+ # Determine the command to be launched
93
+ actual_command = BDAPrequest_actual_command(
94
+ command=self.command,
95
+ date=self.date,
96
+ term=term,
97
+ query=query_file,
98
+ int_extraenv=int_bdap,
99
+ )
100
+ logger.info(
101
+ " ".join(["BDAP extract command:", actual_command])
102
+ )
103
+ logger.info("The %s directive file contains:", query_file)
104
+ self.system.cat(query_file, output=False)
105
+ # Launch the BDAP request
106
+ rc = self.system.spawn(
107
+ [
108
+ actual_command,
109
+ ],
110
+ shell=True,
111
+ output=False,
112
+ fatal=False,
113
+ )
114
+
115
+ if not rc:
116
+ logger.exception(
117
+ "Problem during the BDAP request of %s.", query_file
118
+ )
119
+ if self.system.path.isfile("DIAG_BDAP"):
120
+ raise BDAPRequestConfigurationError
121
+ else:
122
+ raise BDAPGetError
123
+
124
+ rc_all = rc_all and rc
125
+
126
+ if not rc_all:
127
+ logger.exception("Problem during the BDAP request.")
128
+
129
+ return rc_all
130
+
131
+
132
+ class GetBDMPResource(AlgoComponent):
133
+ """Algo component to get BDMP resources considering a BDMP query file."""
134
+
135
+ _footprint = dict(
136
+ info="Algo component to get BDMP files.",
137
+ attr=dict(
138
+ kind=dict(
139
+ values=["get_bdmp"],
140
+ ),
141
+ target_bdmp=dict(
142
+ default="OPER",
143
+ optional=True,
144
+ values=["OPER", "INTE", "ARCH"],
145
+ ),
146
+ command=dict(
147
+ default="bdmp_lecture",
148
+ optional=True,
149
+ values=["bdmp_lecture", "bdmp_lecture_pg", "bdmp_lecture_ora"],
150
+ ),
151
+ ),
152
+ )
153
+
154
+ def execute_single(self, rh, opts):
155
+ """
156
+ Launch the BDMP request(s).
157
+ The results of each request are stored in a directory local_directory to avoid
158
+ files to be overwritten.
159
+ """
160
+
161
+ # Look for the input queries
162
+ input_queries = self.context.sequence.effective_inputs(
163
+ role="Query",
164
+ kind="bdmp_query",
165
+ )
166
+
167
+ rc_all = True
168
+
169
+ for input_query in input_queries:
170
+ # Get information on the query file
171
+ query_file = input_query.rh.container.abspath
172
+ logger.info("The %s directive file contains:", query_file)
173
+ self.system.cat(query_file, output=False)
174
+
175
+ # Construct the name of the temporary directory
176
+ local_directory = "_".join([query_file, "extract"])
177
+
178
+ # Determine the command to be launched
179
+ actual_command = BDMPrequest_actual_command(
180
+ command=self.command,
181
+ query=query_file,
182
+ target_bdmp=self.target_bdmp,
183
+ )
184
+ logger.info(" ".join(["BDMP extract command:", actual_command]))
185
+
186
+ # Launch the BDMP request
187
+ with self.system.cdcontext(local_directory, create=True):
188
+ rc = self.system.spawn(
189
+ [
190
+ actual_command,
191
+ ],
192
+ shell=True,
193
+ output=False,
194
+ fatal=False,
195
+ )
196
+
197
+ if not rc:
198
+ logger.exception(
199
+ "Problem during the BDMP request of %s.", query_file
200
+ )
201
+ raise BDMPGetError
202
+
203
+ rc_all = rc_all and rc
204
+
205
+ if not rc_all:
206
+ logger.exception("Problem during the BDMP request.")
207
+
208
+ return rc_all
209
+
210
+
211
+ class GetBDCPResource(AlgoComponent):
212
+ """Algo component to get BDCP resources considering a BDCP query file."""
213
+
214
+ _footprint = dict(
215
+ info="Algo component to get BDCP files.",
216
+ attr=dict(
217
+ kind=dict(
218
+ values=["get_bdcp"],
219
+ ),
220
+ target_bdcp=dict(
221
+ default="OPER",
222
+ optional=True,
223
+ values=["OPER"],
224
+ ),
225
+ command=dict(
226
+ default="extraction_directives",
227
+ optional=True,
228
+ values=["extraction_directives"],
229
+ ),
230
+ ),
231
+ )
232
+
233
+ def execute_single(self, rh, opts):
234
+ """
235
+ Launch the BDCP request(s).
236
+ The name of the output and log files are fixed by the AlgoComponent
237
+ according to the attributes of each request.
238
+ """
239
+
240
+ # Look for the input queries
241
+ input_queries = self.context.sequence.effective_inputs(
242
+ role="Query",
243
+ kind="bdcp_query",
244
+ )
245
+
246
+ rc_all = True
247
+
248
+ for input_query in input_queries:
249
+ # Get information on the query file
250
+ query_file = input_query.rh.container.abspath
251
+ logger.info("The %s directive file contains:", query_file)
252
+ self.system.cat(query_file, output=False)
253
+
254
+ # Construct the name of the output and log files
255
+ local_directory = "_".join([query_file, "extract"])
256
+ output_file = "extract.out"
257
+ output_log = "extract.out.diag"
258
+
259
+ # Determine the command to be launched
260
+ actual_command = BDCPrequest_actual_command(
261
+ command=self.command,
262
+ query_file=query_file,
263
+ output_file=output_file,
264
+ )
265
+ logger.info(" ".join(["BDMP extract command:", actual_command]))
266
+
267
+ # Launch the BDCP request
268
+ with self.system.cdcontext(local_directory, create=True):
269
+ rc = self.system.spawn(
270
+ [
271
+ actual_command,
272
+ ],
273
+ shell=True,
274
+ output=False,
275
+ fatal=False,
276
+ )
277
+ # Cat the log file
278
+ logger.info("Content of the log file:")
279
+ self.system.cat(output_log, output=False)
280
+
281
+ if not rc:
282
+ logger.exception(
283
+ "Problem during the BDCP request of %s.", query_file
284
+ )
285
+ raise BDCPGetError
286
+
287
+ rc_all = rc_all and rc
288
+
289
+ if not rc_all:
290
+ logger.exception("Problem during the BDCP request.")
291
+
292
+ return rc_all
293
+
294
+
295
+ @algo_component_deco_mixin_autodoc
296
+ class _GetBDMDecoMixin(AlgoComponentDecoMixin):
297
+ """Class variables and methods usefull for BDM extractions."""
298
+
299
+ _MIXIN_EXTRA_FOOTPRINTS = [
300
+ footprints.Footprint(
301
+ attr=dict(
302
+ date=a_date,
303
+ pwd_file=dict(
304
+ default="/usr/local/sopra/neons_pwd",
305
+ values=["/usr/local/sopra/neons_pwd"],
306
+ optional=True,
307
+ ),
308
+ fatal=dict(
309
+ type=bool,
310
+ default=False,
311
+ values=[True, False],
312
+ optional=True,
313
+ ),
314
+ defaut_queryname=dict(
315
+ default="vortexdefault_query_name",
316
+ doc_visibility=footprints.doc.visibility.GURU,
317
+ optional=True,
318
+ ),
319
+ )
320
+ )
321
+ ]
322
+
323
+ def _verbose_env_export(self, varname, value):
324
+ self.env.setvar(varname, value)
325
+ logger.info(
326
+ "Setting environment variable %s = %s", varname, str(value)
327
+ )
328
+
329
+ def _prepare_commons(self, rh, opts):
330
+ """
331
+ Prepare the launch of the script
332
+ """
333
+ # Some exports to be done
334
+ self._verbose_env_export("PWD_FILE", self.pwd_file)
335
+ self._verbose_env_export("DMT_DATE_PIVOT", self.date.ymdhms)
336
+
337
+ _MIXIN_PREPARE_HOOKS = (_prepare_commons,)
338
+
339
+ def _spawn_command_options_extend(self, prev):
340
+ prev["query"] = self.defaut_queryname
341
+ return prev
342
+
343
+ _MIXIN_CLI_OPTS_EXTEND = (_spawn_command_options_extend,)
344
+
345
+ def _execute_commons(self, rh, opts):
346
+ """Launch the BDM request(s).
347
+
348
+ The results of each request are stored in a directory local_directory
349
+ to avoid files overwritten by others
350
+ """
351
+
352
+ # Look for the input queries
353
+ input_queries = self._get_input_queries()
354
+ # Initialize some variables
355
+ rc_all = True
356
+
357
+ # Loop over the query files
358
+ for input_query in input_queries:
359
+ # Find out the temporary directory name
360
+ query_filename = input_query.rh.container.filename
361
+ query_abspath = input_query.rh.container.abspath
362
+ loc_dir = self._local_directory(query_filename)
363
+ # Launch an execution for each input queries in a dedicated directory
364
+ # (to check that the files do not overwrite one another)
365
+ with self.system.cdcontext(loc_dir, create=True):
366
+ # Make the links needed
367
+ self.system.symlink(query_abspath, self.defaut_queryname)
368
+ # Cat the query content
369
+ logger.info("The %s directive file contains:", query_filename)
370
+ self.system.cat(self.defaut_queryname, output=False)
371
+ # Launch the BDM request and catch
372
+ try:
373
+ super(self.mixin_execute_companion(), self).execute(
374
+ rh, opts
375
+ )
376
+ except ExecutionError:
377
+ rc_all = False
378
+ logger.error(
379
+ "Problem during the BDM request of %s.", query_filename
380
+ )
381
+ if self.fatal:
382
+ raise BDMGetError(
383
+ "Problem during the BDM request of {}.".format(
384
+ query_filename
385
+ )
386
+ )
387
+ # Delete the links
388
+ self.system.rm(self.defaut_queryname)
389
+ self.system.dir(output=False, fatal=False)
390
+
391
+ if not rc_all:
392
+ logger.error(
393
+ "At least one of the BDM request failed. Please check the logs above."
394
+ )
395
+
396
+ _MIXIN_EXECUTE_OVERWRITE = _execute_commons
397
+
398
+ def _postfix_commons(self, rh, opts):
399
+ """Concatenate the batormap from the different tasks and check if there is no duplicated entries."""
400
+
401
+ # BATORMAP concatenation
402
+ # Determine the name of the batormap produced by the execution in the different directories
403
+ input_queries = self._get_input_queries()
404
+ local_dir = [
405
+ self._local_directory(input_query.rh.container.filename)
406
+ for input_query in input_queries
407
+ ]
408
+ temp_files = []
409
+ for directory in local_dir:
410
+ glob_files = self.system.glob("/".join([directory, "*batormap*"]))
411
+ for element in glob_files:
412
+ temp_files.append(element)
413
+ # Initialize the resulting batormap file
414
+ obsmap_filename = "_".join(["OBSMAP", self.date.ymdhms])
415
+ content = []
416
+ # Check if a batormap is already present in the directory (from previous extract)
417
+ if self.system.path.isfile(obsmap_filename):
418
+ temp_files.append(obsmap_filename)
419
+ # Loop over the directories to concatenate the batormap
420
+ for a_file in temp_files:
421
+ file_container = footprints.proxy.container(local=a_file)
422
+ content_tmp = ObsMapContent()
423
+ content_tmp.slurp(file_container)
424
+ content.append(content_tmp)
425
+ out_content = ObsMapContent()
426
+ out_content.merge(unique=True, *content)
427
+ out_content.sort()
428
+ out_container = footprints.proxy.container(local=obsmap_filename)
429
+ out_content.rewrite(out_container)
430
+ out_container.close()
431
+ logger.info("Content of the global batormap:")
432
+ self.system.cat(out_container.filename, output=False)
433
+
434
+ # Listing concatenation
435
+ # Initialize the resulting file
436
+ listing_filename = "OULOUTPUT"
437
+ # Determine the name of the listing files produced by the execution
438
+ listing_files = []
439
+ for directory in local_dir:
440
+ glob_files = self.system.glob(
441
+ "/".join([directory, listing_filename])
442
+ )
443
+ for element in glob_files:
444
+ listing_files.append(element)
445
+ # Check if a listing is already present and has to be merged with the other
446
+ if self.system.path.isfile(listing_filename):
447
+ temp_listing = ".".join([listing_filename, "tmp"])
448
+ self.system.mv(listing_filename, temp_listing)
449
+ listing_files.append(temp_listing)
450
+ # Concatenate the listings
451
+ self.system.cat(*listing_files, output=listing_filename)
452
+
453
+ _MIXIN_POSTFIX_HOOKS = (_postfix_commons,)
454
+
455
+
456
+ class GetBDMBufr(Expresso, _GetBDMDecoMixin):
457
+ """Algo component to get BDM resources considering a BDM query file."""
458
+
459
+ _footprint = dict(
460
+ info="Algo component to get BDM BUFR.",
461
+ attr=dict(
462
+ kind=dict(
463
+ values=["get_bdm_bufr"],
464
+ ),
465
+ db_file_bdm=dict(
466
+ default="/usr/local/sopra/neons_db_bdm",
467
+ values=[
468
+ "/usr/local/sopra/neons_db_bdm",
469
+ "/usr/local/sopra/neons_db_bdm.archi",
470
+ "/usr/local/sopra/neons_db_bdm.intgr",
471
+ ],
472
+ optional=True,
473
+ ),
474
+ extra_env_opt=dict(
475
+ values=["RECHERCHE", "OPERATIONNEL", "OPER"],
476
+ default="OPER",
477
+ optional=True,
478
+ ),
479
+ shlib_path=dict(
480
+ default="/usr/local/lib",
481
+ optional=True,
482
+ ),
483
+ interpreter=dict(
484
+ default="awk",
485
+ values=["awk"],
486
+ optional=True,
487
+ ),
488
+ ),
489
+ )
490
+
491
+ def _local_directory(self, query_filename):
492
+ return "_".join(["BUFR", query_filename, self.date.ymdhms])
493
+
494
+ def _get_input_queries(self):
495
+ """Returns the list of queries to process."""
496
+ return self.context.sequence.effective_inputs(
497
+ role="Query",
498
+ kind="bdm_query",
499
+ )
500
+
501
+ def prepare(self, rh, opts):
502
+ """
503
+ Prepare the launch of the script
504
+ """
505
+ # Do the standard pre-treatment
506
+ super().prepare(rh, opts)
507
+
508
+ # Some exports to be done
509
+ self._verbose_env_export("EXTR_ENV", self.extra_env_opt)
510
+ self._verbose_env_export("DB_FILE_BDM", self.db_file_bdm)
511
+ self._verbose_env_export(
512
+ "SHLIB_PATH", ":".join(["$SHLIB_PATH", self.shlib_path])
513
+ )
514
+
515
+ # Check if query files are present
516
+ input_queries = self._get_input_queries()
517
+ if len(input_queries) < 1:
518
+ logger.exception(
519
+ "No query file found for the BDM extraction. Stop."
520
+ )
521
+ raise BDMRequestConfigurationError(
522
+ "No query file found for the BDM extraction"
523
+ )
524
+
525
+
526
+ class GetBDMOulan(BlindRun, _GetBDMDecoMixin):
527
+ """Algo component to get BDM files using Oulan."""
528
+
529
+ _footprint = dict(
530
+ info="Algo component to get BDM files using Oulan.",
531
+ attr=dict(
532
+ kind=dict(
533
+ values=["get_bdm_oulan"],
534
+ ),
535
+ db_file=dict(
536
+ default="/usr/local/sopra/neons_db",
537
+ values=["/usr/local/sopra/neons_db"],
538
+ optional=True,
539
+ ),
540
+ defaut_queryname=dict(
541
+ default="NAMELIST",
542
+ ),
543
+ ),
544
+ )
545
+
546
+ def _local_directory(self, query_filename):
547
+ return "_".join(["Oulan", query_filename, self.date.ymdhms])
548
+
549
+ def _get_input_queries(self):
550
+ """Returns the list of namelists to process."""
551
+ return self.context.sequence.effective_inputs(
552
+ role="NamelistOulan",
553
+ kind="namutil",
554
+ )
555
+
556
+ def prepare(self, rh, opts):
557
+ """Prepare the execution of the Oulan extraction binary."""
558
+ # Do the standard pre-treatment
559
+ super().prepare(rh, opts)
560
+
561
+ # Export additional variables
562
+ self._verbose_env_export("DB_FILE", self.db_file)
563
+
564
+ # Check if namelists are present
565
+ input_namelists = self._get_input_queries()
566
+ if len(input_namelists) < 1:
567
+ logger.error("No Oulan namelist found. Stop.")
568
+ raise BDMError("No Oulan namelist found.")
569
+
570
+
571
+ class GetMarsResource(AlgoComponent):
572
+ """AlgoComponent to get Mars resources using a Mars query file"""
573
+
574
+ _footprint = dict(
575
+ info="AlgoComponent to get a Mars resource",
576
+ attr=dict(
577
+ kind=dict(
578
+ values=[
579
+ "get_mars",
580
+ ]
581
+ ),
582
+ date=a_date,
583
+ substitutions=dict(
584
+ info="A dictionary of values to be substituted",
585
+ type=footprints.stdtypes.FPDict,
586
+ default=footprints.stdtypes.FPDict(),
587
+ optional=True,
588
+ ),
589
+ command=dict(optional=True),
590
+ fatal=dict(type=bool, default=True, optional=True),
591
+ ),
592
+ )
593
+
594
+ def execute(self, rh, opts):
595
+ """
596
+ Launch the Mars request(s).
597
+ The results of each requests are stored in a directory to avoid
598
+ files to be overwritten.
599
+ """
600
+ # Look for input queries
601
+ input_queries = self.context.sequence.effective_inputs(
602
+ role="Query",
603
+ kind="mars_query",
604
+ )
605
+ if len(input_queries) < 1:
606
+ logger.exception(
607
+ "No query file found for the Mars extraction. Stop."
608
+ )
609
+ raise MarsGetError("No query file found for the Mars extraction")
610
+
611
+ rc_all = True
612
+
613
+ # Find the command to be launched
614
+ actual_command = findMarsExtractCommand(
615
+ sh=self.system, command=self.command
616
+ )
617
+
618
+ # Prepare the substitutions' dictionnary
619
+ dictkeyvalue = copy.deepcopy(self.substitutions)
620
+ if self.date is not None:
621
+ dictkeyvalue["YYYYMMDDHH"] = self.date.ymdh
622
+ dictkeyvalue["YYYYMMDD"] = self.date.ymd
623
+ dictkeyvalue["HH"] = self.date.hh
624
+
625
+ # For each input query, extract the files
626
+ for input_query in input_queries:
627
+ # Prepare the query file used
628
+ query_content = input_query.rh.contents
629
+ query_content.setitems(dictkeyvalue)
630
+ input_query.rh.save()
631
+ # Launch each input queries in a dedicated file
632
+ # (to check that the files do not overwrite each other)
633
+ query_file_path = input_query.rh.container.abspath
634
+ local_directory = "_".join([query_file_path, self.date.ymdhms])
635
+ logger.info(
636
+ "Here is the content of the query file %s (after substitution):",
637
+ query_file_path,
638
+ )
639
+ self.system.cat(query_file_path, output=False)
640
+ with self.system.cdcontext(local_directory, create=True):
641
+ # Launch the command
642
+ rc = callMarsExtract(
643
+ sh=self.system,
644
+ query_file=query_file_path,
645
+ fatal=self.fatal,
646
+ command=actual_command,
647
+ )
648
+ if not rc:
649
+ if self.fatal:
650
+ logger.error(
651
+ "Problem during the Mars request of %s",
652
+ query_file_path,
653
+ )
654
+ raise MarsGetError
655
+ else:
656
+ logger.warning(
657
+ "Problem during the Mars request of %s",
658
+ query_file_path,
659
+ )
660
+ rc_all = rc_all and rc