vortex-nwp 2.0.0b1__py3-none-any.whl → 2.0.0b2__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 (139) hide show
  1. vortex/__init__.py +59 -45
  2. vortex/algo/__init__.py +3 -2
  3. vortex/algo/components.py +940 -614
  4. vortex/algo/mpitools.py +802 -497
  5. vortex/algo/serversynctools.py +34 -33
  6. vortex/config.py +19 -22
  7. vortex/data/__init__.py +9 -3
  8. vortex/data/abstractstores.py +593 -655
  9. vortex/data/containers.py +217 -162
  10. vortex/data/contents.py +65 -39
  11. vortex/data/executables.py +93 -102
  12. vortex/data/flow.py +40 -34
  13. vortex/data/geometries.py +228 -132
  14. vortex/data/handlers.py +428 -225
  15. vortex/data/outflow.py +15 -15
  16. vortex/data/providers.py +185 -163
  17. vortex/data/resources.py +48 -42
  18. vortex/data/stores.py +544 -413
  19. vortex/gloves.py +114 -87
  20. vortex/layout/__init__.py +1 -8
  21. vortex/layout/contexts.py +150 -84
  22. vortex/layout/dataflow.py +353 -202
  23. vortex/layout/monitor.py +264 -128
  24. vortex/nwp/__init__.py +5 -2
  25. vortex/nwp/algo/__init__.py +14 -5
  26. vortex/nwp/algo/assim.py +205 -151
  27. vortex/nwp/algo/clim.py +683 -517
  28. vortex/nwp/algo/coupling.py +447 -225
  29. vortex/nwp/algo/eda.py +437 -229
  30. vortex/nwp/algo/eps.py +403 -231
  31. vortex/nwp/algo/forecasts.py +420 -271
  32. vortex/nwp/algo/fpserver.py +683 -307
  33. vortex/nwp/algo/ifsnaming.py +205 -145
  34. vortex/nwp/algo/ifsroot.py +210 -122
  35. vortex/nwp/algo/monitoring.py +132 -76
  36. vortex/nwp/algo/mpitools.py +321 -191
  37. vortex/nwp/algo/odbtools.py +617 -353
  38. vortex/nwp/algo/oopsroot.py +449 -273
  39. vortex/nwp/algo/oopstests.py +90 -56
  40. vortex/nwp/algo/request.py +287 -206
  41. vortex/nwp/algo/stdpost.py +878 -522
  42. vortex/nwp/data/__init__.py +22 -4
  43. vortex/nwp/data/assim.py +125 -137
  44. vortex/nwp/data/boundaries.py +121 -68
  45. vortex/nwp/data/climfiles.py +193 -211
  46. vortex/nwp/data/configfiles.py +73 -69
  47. vortex/nwp/data/consts.py +426 -401
  48. vortex/nwp/data/ctpini.py +59 -43
  49. vortex/nwp/data/diagnostics.py +94 -66
  50. vortex/nwp/data/eda.py +50 -51
  51. vortex/nwp/data/eps.py +195 -146
  52. vortex/nwp/data/executables.py +440 -434
  53. vortex/nwp/data/fields.py +63 -48
  54. vortex/nwp/data/gridfiles.py +183 -111
  55. vortex/nwp/data/logs.py +250 -217
  56. vortex/nwp/data/modelstates.py +180 -151
  57. vortex/nwp/data/monitoring.py +72 -99
  58. vortex/nwp/data/namelists.py +254 -202
  59. vortex/nwp/data/obs.py +400 -308
  60. vortex/nwp/data/oopsexec.py +22 -20
  61. vortex/nwp/data/providers.py +90 -65
  62. vortex/nwp/data/query.py +71 -82
  63. vortex/nwp/data/stores.py +49 -36
  64. vortex/nwp/data/surfex.py +136 -137
  65. vortex/nwp/syntax/__init__.py +1 -1
  66. vortex/nwp/syntax/stdattrs.py +173 -111
  67. vortex/nwp/tools/__init__.py +2 -2
  68. vortex/nwp/tools/addons.py +22 -17
  69. vortex/nwp/tools/agt.py +24 -12
  70. vortex/nwp/tools/bdap.py +16 -5
  71. vortex/nwp/tools/bdcp.py +4 -1
  72. vortex/nwp/tools/bdm.py +3 -0
  73. vortex/nwp/tools/bdmp.py +14 -9
  74. vortex/nwp/tools/conftools.py +728 -378
  75. vortex/nwp/tools/drhook.py +12 -8
  76. vortex/nwp/tools/grib.py +65 -39
  77. vortex/nwp/tools/gribdiff.py +22 -17
  78. vortex/nwp/tools/ifstools.py +82 -42
  79. vortex/nwp/tools/igastuff.py +167 -143
  80. vortex/nwp/tools/mars.py +14 -2
  81. vortex/nwp/tools/odb.py +234 -125
  82. vortex/nwp/tools/partitioning.py +61 -37
  83. vortex/nwp/tools/satrad.py +27 -12
  84. vortex/nwp/util/async.py +83 -55
  85. vortex/nwp/util/beacon.py +10 -10
  86. vortex/nwp/util/diffpygram.py +174 -86
  87. vortex/nwp/util/ens.py +144 -63
  88. vortex/nwp/util/hooks.py +30 -19
  89. vortex/nwp/util/taskdeco.py +28 -24
  90. vortex/nwp/util/usepygram.py +278 -172
  91. vortex/nwp/util/usetnt.py +31 -17
  92. vortex/sessions.py +72 -39
  93. vortex/syntax/__init__.py +1 -1
  94. vortex/syntax/stdattrs.py +410 -171
  95. vortex/syntax/stddeco.py +31 -22
  96. vortex/toolbox.py +327 -192
  97. vortex/tools/__init__.py +11 -2
  98. vortex/tools/actions.py +125 -59
  99. vortex/tools/addons.py +111 -92
  100. vortex/tools/arm.py +42 -22
  101. vortex/tools/compression.py +72 -69
  102. vortex/tools/date.py +11 -4
  103. vortex/tools/delayedactions.py +242 -132
  104. vortex/tools/env.py +75 -47
  105. vortex/tools/folder.py +342 -171
  106. vortex/tools/grib.py +311 -149
  107. vortex/tools/lfi.py +423 -216
  108. vortex/tools/listings.py +109 -40
  109. vortex/tools/names.py +218 -156
  110. vortex/tools/net.py +632 -298
  111. vortex/tools/parallelism.py +93 -61
  112. vortex/tools/prestaging.py +55 -31
  113. vortex/tools/schedulers.py +172 -105
  114. vortex/tools/services.py +402 -333
  115. vortex/tools/storage.py +293 -358
  116. vortex/tools/surfex.py +24 -24
  117. vortex/tools/systems.py +1211 -631
  118. vortex/tools/targets.py +156 -100
  119. vortex/util/__init__.py +1 -1
  120. vortex/util/config.py +377 -327
  121. vortex/util/empty.py +2 -2
  122. vortex/util/helpers.py +56 -24
  123. vortex/util/introspection.py +18 -12
  124. vortex/util/iosponge.py +8 -4
  125. vortex/util/roles.py +4 -6
  126. vortex/util/storefunctions.py +39 -13
  127. vortex/util/structs.py +3 -3
  128. vortex/util/worker.py +29 -17
  129. vortex_nwp-2.0.0b2.dist-info/METADATA +66 -0
  130. vortex_nwp-2.0.0b2.dist-info/RECORD +142 -0
  131. {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.0.0b2.dist-info}/WHEEL +1 -1
  132. vortex/layout/appconf.py +0 -109
  133. vortex/layout/jobs.py +0 -1276
  134. vortex/layout/nodes.py +0 -1424
  135. vortex/layout/subjobs.py +0 -464
  136. vortex_nwp-2.0.0b1.dist-info/METADATA +0 -50
  137. vortex_nwp-2.0.0b1.dist-info/RECORD +0 -146
  138. {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.0.0b2.dist-info}/LICENSE +0 -0
  139. {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.0.0b2.dist-info}/top_level.txt +0 -0
@@ -13,12 +13,32 @@ from footprints.stdtypes import FPTuple
13
13
  import footprints
14
14
  from taylorism import Boss
15
15
 
16
- from vortex.layout.monitor import BasicInputMonitor, AutoMetaGang, MetaGang, EntrySt, GangSt
17
- from vortex.algo.components import AlgoComponentDecoMixin, AlgoComponentError, algo_component_deco_mixin_autodoc
18
- from vortex.algo.components import TaylorRun, BlindRun, ParaBlindRun, Parallel, Expresso
16
+ from vortex.layout.monitor import (
17
+ BasicInputMonitor,
18
+ AutoMetaGang,
19
+ MetaGang,
20
+ EntrySt,
21
+ GangSt,
22
+ )
23
+ from vortex.algo.components import (
24
+ AlgoComponentDecoMixin,
25
+ AlgoComponentError,
26
+ algo_component_deco_mixin_autodoc,
27
+ )
28
+ from vortex.algo.components import (
29
+ TaylorRun,
30
+ BlindRun,
31
+ ParaBlindRun,
32
+ Parallel,
33
+ Expresso,
34
+ )
19
35
  from vortex.syntax.stdattrs import DelayedEnvValue, FmtInt
20
36
  from vortex.tools.grib import EcGribDecoMixin
21
- from vortex.tools.parallelism import TaylorVortexWorker, VortexWorkerBlindRun, ParallelResultParser
37
+ from vortex.tools.parallelism import (
38
+ TaylorVortexWorker,
39
+ VortexWorkerBlindRun,
40
+ ParallelResultParser,
41
+ )
22
42
  from vortex.tools.systems import ExecutionError
23
43
 
24
44
  from ..tools.grib import GRIBFilter
@@ -37,66 +57,57 @@ class _FA2GribWorker(VortexWorkerBlindRun):
37
57
  """
38
58
 
39
59
  _footprint = dict(
40
- attr = dict(
41
- kind = dict(
42
- values = ['fa2grib']
43
- ),
60
+ attr=dict(
61
+ kind=dict(values=["fa2grib"]),
44
62
  # Progrid parameters
45
- fortnam = dict(),
46
- fortinput = dict(),
47
- compact = dict(),
48
- timeshift = dict(
49
- type = int
50
- ),
51
- timeunit = dict(
52
- type = int
53
- ),
54
- numod = dict(
55
- type = int
56
- ),
57
- sciz = dict(
58
- type = int
59
- ),
60
- scizoffset = dict(
61
- type = int,
62
- optional = True
63
- ),
63
+ fortnam=dict(),
64
+ fortinput=dict(),
65
+ compact=dict(),
66
+ timeshift=dict(type=int),
67
+ timeunit=dict(type=int),
68
+ numod=dict(type=int),
69
+ sciz=dict(type=int),
70
+ scizoffset=dict(type=int, optional=True),
64
71
  # Input/Output data
65
- file_in = dict(),
66
- file_out = dict(),
67
- member = dict(
68
- type = FmtInt,
69
- optional = True,
70
- )
72
+ file_in=dict(),
73
+ file_out=dict(),
74
+ member=dict(
75
+ type=FmtInt,
76
+ optional=True,
77
+ ),
71
78
  )
72
79
  )
73
80
 
74
81
  def vortex_task(self, **kwargs):
75
-
76
82
  logger.info("Starting the Fa2Grib processing for tag=%s", self.name)
77
83
 
78
- thisoutput = 'GRIDOUTPUT'
84
+ thisoutput = "GRIDOUTPUT"
79
85
  rdict = dict(rc=True)
80
86
 
81
87
  # First, check that the hooks were applied
82
- for thisinput in [x for x in self.context.sequence.inputs()
83
- if x.rh.container.localpath() == self.file_in]:
88
+ for thisinput in [
89
+ x
90
+ for x in self.context.sequence.inputs()
91
+ if x.rh.container.localpath() == self.file_in
92
+ ]:
84
93
  if thisinput.rh.delayhooks:
85
94
  thisinput.rh.apply_get_hooks()
86
95
 
87
96
  # Jump into a working directory
88
97
  cwd = self.system.pwd()
89
- tmpwd = self.system.path.join(cwd, self.file_out + '.process.d')
98
+ tmpwd = self.system.path.join(cwd, self.file_out + ".process.d")
90
99
  self.system.mkdir(tmpwd)
91
100
  self.system.cd(tmpwd)
92
101
 
93
102
  # Build the local namelist block
94
- nb = NamelistBlock(name='NAML')
103
+ nb = NamelistBlock(name="NAML")
95
104
  nb.NBDOM = 1
96
105
  nb.CHOPER = self.compact
97
106
  nb.INUMOD = self.numod
98
107
  if self.scizoffset is not None:
99
- nb.ISCIZ = self.scizoffset + (self.member if self.member is not None else 0)
108
+ nb.ISCIZ = self.scizoffset + (
109
+ self.member if self.member is not None else 0
110
+ )
100
111
  else:
101
112
  if self.sciz:
102
113
  nb.ISCIZ = self.sciz
@@ -104,28 +115,31 @@ class _FA2GribWorker(VortexWorkerBlindRun):
104
115
  nb.IHCTPI = self.timeshift
105
116
  if self.timeunit:
106
117
  nb.ITUNIT = self.timeunit
107
- nb['CLFSORT(1)'] = thisoutput
108
- nb['CDNOMF(1)'] = self.fortinput
109
- with open(self.fortnam, 'w') as namfd:
118
+ nb["CLFSORT(1)"] = thisoutput
119
+ nb["CDNOMF(1)"] = self.fortinput
120
+ with open(self.fortnam, "w") as namfd:
110
121
  namfd.write(nb.dumps())
111
122
 
112
123
  # Finally set the actual init file
113
- self.system.softlink(self.system.path.join(cwd, self.file_in),
114
- self.fortinput)
124
+ self.system.softlink(
125
+ self.system.path.join(cwd, self.file_in), self.fortinput
126
+ )
115
127
 
116
128
  # Standard execution
117
129
  list_name = self.system.path.join(cwd, self.file_out + ".listing")
118
130
  try:
119
131
  self.local_spawn(list_name)
120
132
  except ExecutionError as e:
121
- rdict['rc'] = e
133
+ rdict["rc"] = e
122
134
 
123
135
  # Freeze the current output
124
136
  if self.system.path.exists(thisoutput):
125
- self.system.move(thisoutput, self.system.path.join(cwd, self.file_out))
137
+ self.system.move(
138
+ thisoutput, self.system.path.join(cwd, self.file_out)
139
+ )
126
140
  else:
127
- logger.warning('Missing some grib output: %s', self.file_out)
128
- rdict['rc'] = False
141
+ logger.warning("Missing some grib output: %s", self.file_out)
142
+ rdict["rc"] = False
129
143
 
130
144
  # Final cleaning
131
145
  self.system.cd(cwd)
@@ -133,8 +147,12 @@ class _FA2GribWorker(VortexWorkerBlindRun):
133
147
 
134
148
  if self.system.path.exists(self.file_out):
135
149
  # Deal with promised resources
136
- expected = [x for x in self.context.sequence.outputs()
137
- if x.rh.provider.expected and x.rh.container.localpath() == self.file_out]
150
+ expected = [
151
+ x
152
+ for x in self.context.sequence.outputs()
153
+ if x.rh.provider.expected
154
+ and x.rh.container.localpath() == self.file_out
155
+ ]
138
156
  for thispromise in expected:
139
157
  thispromise.put(incache=True)
140
158
 
@@ -150,35 +168,32 @@ class _GribFilterWorker(TaylorVortexWorker):
150
168
  """
151
169
 
152
170
  _footprint = dict(
153
- attr = dict(
154
- kind = dict(
155
- values = ['gribfilter']
156
- ),
171
+ attr=dict(
172
+ kind=dict(values=["gribfilter"]),
157
173
  # Filter settings
158
- filters = dict(
159
- type = FPTuple,
174
+ filters=dict(
175
+ type=FPTuple,
160
176
  ),
161
- concatenate = dict(
162
- type = bool,
177
+ concatenate=dict(
178
+ type=bool,
163
179
  ),
164
180
  # Put files if they are expected
165
- put_promises = dict(
166
- type = bool,
167
- optional = True,
168
- default = True,
181
+ put_promises=dict(
182
+ type=bool,
183
+ optional=True,
184
+ default=True,
169
185
  ),
170
186
  # Input/Output data
171
- file_in = dict(),
172
- file_outfmt = dict(),
173
- file_outintent = dict(
174
- optional = True,
175
- default = 'in',
187
+ file_in=dict(),
188
+ file_outfmt=dict(),
189
+ file_outintent=dict(
190
+ optional=True,
191
+ default="in",
176
192
  ),
177
193
  )
178
194
  )
179
195
 
180
196
  def vortex_task(self, **kwargs):
181
-
182
197
  logger.info("Starting the GribFiltering for tag=%s", self.file_in)
183
198
 
184
199
  rdict = dict(rc=True)
@@ -186,7 +201,7 @@ class _GribFilterWorker(TaylorVortexWorker):
186
201
  # Create the filtering object and add filters
187
202
  gfilter = GRIBFilter(concatenate=self.concatenate)
188
203
  if self.filters:
189
- gfilter.add_filters(* list(self.filters))
204
+ gfilter.add_filters(*list(self.filters))
190
205
 
191
206
  # Process the input file
192
207
  newfiles = gfilter(self.file_in, self.file_outfmt, self.file_outintent)
@@ -194,24 +209,37 @@ class _GribFilterWorker(TaylorVortexWorker):
194
209
  if newfiles:
195
210
  if self.put_promises:
196
211
  # Deal with promised resources
197
- allpromises = [x for x in self.context.sequence.outputs()
198
- if x.rh.provider.expected]
212
+ allpromises = [
213
+ x
214
+ for x in self.context.sequence.outputs()
215
+ if x.rh.provider.expected
216
+ ]
199
217
  for newfile in newfiles:
200
- expected = [x for x in allpromises
201
- if x.rh.container.localpath() == newfile]
218
+ expected = [
219
+ x
220
+ for x in allpromises
221
+ if x.rh.container.localpath() == newfile
222
+ ]
202
223
  for thispromise in expected:
203
224
  thispromise.put(incache=True)
204
225
  else:
205
- logger.warning('No file has been generated.')
206
- rdict['rc'] = False
226
+ logger.warning("No file has been generated.")
227
+ rdict["rc"] = False
207
228
 
208
229
  logger.info("GribFiltering is done for tag=%s", self.name)
209
230
 
210
231
  return rdict
211
232
 
212
233
 
213
- def parallel_grib_filter(context, inputs, outputs, intents=(),
214
- cat=False, filters=FPTuple(), nthreads=8):
234
+ def parallel_grib_filter(
235
+ context,
236
+ inputs,
237
+ outputs,
238
+ intents=(),
239
+ cat=False,
240
+ filters=FPTuple(),
241
+ nthreads=8,
242
+ ):
215
243
  """A simple method that calls the GRIBFilter class in parallel.
216
244
 
217
245
  :param vortex.layout.contexts.Context context: the current context
@@ -223,26 +251,58 @@ def parallel_grib_filter(context, inputs, outputs, intents=(),
223
251
  :param int nthreads: the maximum number of tasks used concurently (8 by default)
224
252
  """
225
253
  if not cat and len(filters) == 0:
226
- raise AlgoComponentError("cat must be true or filters must be provided")
254
+ raise AlgoComponentError(
255
+ "cat must be true or filters must be provided"
256
+ )
227
257
  if len(inputs) != len(outputs):
228
- raise AlgoComponentError("inputs and outputs must have the same length")
258
+ raise AlgoComponentError(
259
+ "inputs and outputs must have the same length"
260
+ )
229
261
  if len(intents) != len(outputs):
230
- intents = FPTuple(['in', ] * len(outputs))
231
- boss = Boss(scheduler=footprints.proxy.scheduler(limit='threads', max_threads=nthreads))
232
- common_i = dict(kind='gribfilter', filters=filters, concatenate=cat, put_promises=False)
262
+ intents = FPTuple(
263
+ [
264
+ "in",
265
+ ]
266
+ * len(outputs)
267
+ )
268
+ boss = Boss(
269
+ scheduler=footprints.proxy.scheduler(
270
+ limit="threads", max_threads=nthreads
271
+ )
272
+ )
273
+ common_i = dict(
274
+ kind="gribfilter", filters=filters, concatenate=cat, put_promises=False
275
+ )
233
276
  for ifile, ofile, intent in zip(inputs, outputs, intents):
234
- logger.info("%s -> %s (intent: %s) added to the GRIBfilter task's list",
235
- ifile, ofile, intent)
236
- boss.set_instructions(common_i, dict(name=[ifile, ],
237
- file_in=[ifile, ],
238
- file_outfmt=[ofile, ],
239
- file_outintent=[intent, ]))
277
+ logger.info(
278
+ "%s -> %s (intent: %s) added to the GRIBfilter task's list",
279
+ ifile,
280
+ ofile,
281
+ intent,
282
+ )
283
+ boss.set_instructions(
284
+ common_i,
285
+ dict(
286
+ name=[
287
+ ifile,
288
+ ],
289
+ file_in=[
290
+ ifile,
291
+ ],
292
+ file_outfmt=[
293
+ ofile,
294
+ ],
295
+ file_outintent=[
296
+ intent,
297
+ ],
298
+ ),
299
+ )
240
300
  boss.make_them_work()
241
301
  boss.wait_till_finished()
242
302
  logger.info("All files are processed.")
243
303
  report = boss.get_report()
244
304
  prp = ParallelResultParser(context)
245
- for r in report['workers_report']:
305
+ for r in report["workers_report"]:
246
306
  if isinstance(prp(r), Exception):
247
307
  raise AlgoComponentError("An error occurred in GRIBfilter.")
248
308
 
@@ -251,60 +311,60 @@ class Fa2Grib(ParaBlindRun):
251
311
  """Standard FA conversion, e.g. with PROGRID as a binary resource."""
252
312
 
253
313
  _footprint = dict(
254
- attr = dict(
255
- kind = dict(
256
- values = ['fa2grib'],
314
+ attr=dict(
315
+ kind=dict(
316
+ values=["fa2grib"],
257
317
  ),
258
- timeout = dict(
259
- type = int,
260
- optional = True,
261
- default = 300,
318
+ timeout=dict(
319
+ type=int,
320
+ optional=True,
321
+ default=300,
262
322
  ),
263
- refreshtime = dict(
264
- type = int,
265
- optional = True,
266
- default = 20,
323
+ refreshtime=dict(
324
+ type=int,
325
+ optional=True,
326
+ default=20,
267
327
  ),
268
- fatal = dict(
269
- type = bool,
270
- optional = True,
271
- default = True,
328
+ fatal=dict(
329
+ type=bool,
330
+ optional=True,
331
+ default=True,
272
332
  ),
273
- fortnam = dict(
274
- optional = True,
275
- default = 'fort.4',
333
+ fortnam=dict(
334
+ optional=True,
335
+ default="fort.4",
276
336
  ),
277
- fortinput = dict(
278
- optional = True,
279
- default = 'fort.11',
337
+ fortinput=dict(
338
+ optional=True,
339
+ default="fort.11",
280
340
  ),
281
- compact = dict(
282
- optional = True,
283
- default = DelayedEnvValue('VORTEX_GRIB_COMPACT', 'L'),
341
+ compact=dict(
342
+ optional=True,
343
+ default=DelayedEnvValue("VORTEX_GRIB_COMPACT", "L"),
284
344
  ),
285
- timeshift = dict(
286
- type = int,
287
- optional = True,
288
- default = DelayedEnvValue('VORTEX_GRIB_SHIFT', 0),
345
+ timeshift=dict(
346
+ type=int,
347
+ optional=True,
348
+ default=DelayedEnvValue("VORTEX_GRIB_SHIFT", 0),
289
349
  ),
290
- timeunit = dict(
291
- type = int,
292
- optional = True,
293
- default = DelayedEnvValue('VORTEX_GRIB_TUNIT', 1),
350
+ timeunit=dict(
351
+ type=int,
352
+ optional=True,
353
+ default=DelayedEnvValue("VORTEX_GRIB_TUNIT", 1),
294
354
  ),
295
- numod = dict(
296
- type = int,
297
- optional = True,
298
- default = DelayedEnvValue('VORTEX_GRIB_NUMOD', 221),
355
+ numod=dict(
356
+ type=int,
357
+ optional=True,
358
+ default=DelayedEnvValue("VORTEX_GRIB_NUMOD", 221),
299
359
  ),
300
- sciz = dict(
301
- type = int,
302
- optional = True,
303
- default = DelayedEnvValue('VORTEX_GRIB_SCIZ', 0),
360
+ sciz=dict(
361
+ type=int,
362
+ optional=True,
363
+ default=DelayedEnvValue("VORTEX_GRIB_SCIZ", 0),
304
364
  ),
305
- scizoffset = dict(
306
- type = int,
307
- optional = True,
365
+ scizoffset=dict(
366
+ type=int,
367
+ optional=True,
308
368
  ),
309
369
  )
310
370
  )
@@ -314,7 +374,9 @@ class Fa2Grib(ParaBlindRun):
314
374
  super().prepare(rh, opts)
315
375
  self.system.remove(self.fortinput)
316
376
  self.env.DR_HOOK_NOT_MPI = 1
317
- self.system.subtitle('{:s} : directory listing (pre-run)'.format(self.realkind))
377
+ self.system.subtitle(
378
+ "{:s} : directory listing (pre-run)".format(self.realkind)
379
+ )
318
380
  self.system.dir(output=False, fatal=False)
319
381
 
320
382
  def execute(self, rh, opts):
@@ -324,34 +386,64 @@ class Fa2Grib(ParaBlindRun):
324
386
 
325
387
  common_i = self._default_common_instructions(rh, opts)
326
388
  # Update the common instructions
327
- common_i.update(dict(fortnam=self.fortnam, fortinput=self.fortinput,
328
- compact=self.compact, numod=self.numod,
329
- sciz=self.sciz, scizoffset=self.scizoffset,
330
- timeshift=self.timeshift, timeunit=self.timeunit))
389
+ common_i.update(
390
+ dict(
391
+ fortnam=self.fortnam,
392
+ fortinput=self.fortinput,
393
+ compact=self.compact,
394
+ numod=self.numod,
395
+ sciz=self.sciz,
396
+ scizoffset=self.scizoffset,
397
+ timeshift=self.timeshift,
398
+ timeunit=self.timeunit,
399
+ )
400
+ )
331
401
  tmout = False
332
402
 
333
403
  # Monitor for the input files
334
- bm = BasicInputMonitor(self.context, caching_freq=self.refreshtime,
335
- role='Gridpoint', kind='gridpoint')
404
+ bm = BasicInputMonitor(
405
+ self.context,
406
+ caching_freq=self.refreshtime,
407
+ role="Gridpoint",
408
+ kind="gridpoint",
409
+ )
336
410
  with bm:
337
411
  while not bm.all_done or len(bm.available) > 0:
338
-
339
412
  while bm.available:
340
413
  s = bm.pop_available().section
341
414
  file_in = s.rh.container.localpath()
342
415
  # Find the name of the output file
343
416
  if s.rh.provider.member is not None:
344
- file_out = 'GRIB{:s}_{!s}+{:s}'.format(s.rh.resource.geometry.area,
345
- s.rh.provider.member,
346
- s.rh.resource.term.fmthm)
417
+ file_out = "GRIB{:s}_{!s}+{:s}".format(
418
+ s.rh.resource.geometry.area,
419
+ s.rh.provider.member,
420
+ s.rh.resource.term.fmthm,
421
+ )
347
422
  else:
348
- file_out = 'GRIB{:s}+{:s}'.format(s.rh.resource.geometry.area,
349
- s.rh.resource.term.fmthm)
350
- logger.info("Adding input file %s to the job list", file_in)
351
- self._add_instructions(common_i,
352
- dict(name=[file_in, ],
353
- file_in=[file_in, ], file_out=[file_out, ],
354
- member=[s.rh.provider.member, ]))
423
+ file_out = "GRIB{:s}+{:s}".format(
424
+ s.rh.resource.geometry.area,
425
+ s.rh.resource.term.fmthm,
426
+ )
427
+ logger.info(
428
+ "Adding input file %s to the job list", file_in
429
+ )
430
+ self._add_instructions(
431
+ common_i,
432
+ dict(
433
+ name=[
434
+ file_in,
435
+ ],
436
+ file_in=[
437
+ file_in,
438
+ ],
439
+ file_out=[
440
+ file_out,
441
+ ],
442
+ member=[
443
+ s.rh.provider.member,
444
+ ],
445
+ ),
446
+ )
355
447
 
356
448
  if not (bm.all_done or len(bm.available) > 0):
357
449
  # Timeout ?
@@ -364,42 +456,47 @@ class Fa2Grib(ParaBlindRun):
364
456
 
365
457
  self._default_post_execute(rh, opts)
366
458
 
367
- for failed_file in [e.section.rh.container.localpath() for e in bm.failed.values()]:
368
- logger.error("We were unable to fetch the following file: %s", failed_file)
459
+ for failed_file in [
460
+ e.section.rh.container.localpath() for e in bm.failed.values()
461
+ ]:
462
+ logger.error(
463
+ "We were unable to fetch the following file: %s", failed_file
464
+ )
369
465
  if self.fatal:
370
- self.delayed_exception_add(IOError("Unable to fetch {:s}".format(failed_file)),
371
- traceback=False)
466
+ self.delayed_exception_add(
467
+ IOError("Unable to fetch {:s}".format(failed_file)),
468
+ traceback=False,
469
+ )
372
470
 
373
471
  if tmout:
374
472
  raise OSError("The waiting loop timed out")
375
473
 
376
474
 
377
475
  class StandaloneGRIBFilter(TaylorRun):
378
-
379
476
  _footprint = dict(
380
- attr = dict(
381
- kind = dict(
382
- values = ['gribfilter'],
477
+ attr=dict(
478
+ kind=dict(
479
+ values=["gribfilter"],
383
480
  ),
384
- timeout = dict(
385
- type = int,
386
- optional = True,
387
- default = 300,
481
+ timeout=dict(
482
+ type=int,
483
+ optional=True,
484
+ default=300,
388
485
  ),
389
- refreshtime = dict(
390
- type = int,
391
- optional = True,
392
- default = 20,
486
+ refreshtime=dict(
487
+ type=int,
488
+ optional=True,
489
+ default=20,
393
490
  ),
394
- concatenate = dict(
395
- type = bool,
396
- default = False,
397
- optional = True,
491
+ concatenate=dict(
492
+ type=bool,
493
+ default=False,
494
+ optional=True,
398
495
  ),
399
- fatal = dict(
400
- type = bool,
401
- optional = True,
402
- default = True,
496
+ fatal=dict(
497
+ type=bool,
498
+ optional=True,
499
+ default=True,
403
500
  ),
404
501
  )
405
502
  )
@@ -407,40 +504,63 @@ class StandaloneGRIBFilter(TaylorRun):
407
504
  def prepare(self, rh, opts):
408
505
  """Set some variables according to target definition."""
409
506
  super().prepare(rh, opts)
410
- self.system.subtitle('{:s} : directory listing (pre-run)'.format(self.realkind))
507
+ self.system.subtitle(
508
+ "{:s} : directory listing (pre-run)".format(self.realkind)
509
+ )
411
510
  self.system.dir(output=False, fatal=False)
412
511
 
413
512
  def execute(self, rh, opts):
414
-
415
513
  # We re-serialise data because footprints don't like dictionaries
416
- filters = [json.dumps(x.rh.contents.data)
417
- for x in self.context.sequence.effective_inputs(role='GRIBFilteringRequest',
418
- kind='filtering_request')]
514
+ filters = [
515
+ json.dumps(x.rh.contents.data)
516
+ for x in self.context.sequence.effective_inputs(
517
+ role="GRIBFilteringRequest", kind="filtering_request"
518
+ )
519
+ ]
419
520
  filters = FPTuple(filters)
420
521
 
421
522
  self._default_pre_execute(rh, opts)
422
523
 
423
524
  common_i = self._default_common_instructions(rh, opts)
424
525
  # Update the common instructions
425
- common_i.update(dict(concatenate=self.concatenate,
426
- filters=filters))
526
+ common_i.update(dict(concatenate=self.concatenate, filters=filters))
427
527
  tmout = False
428
528
 
429
529
  # Monitor for the input files
430
- bm = BasicInputMonitor(self.context, caching_freq=self.refreshtime,
431
- role='Gridpoint', kind='gridpoint')
530
+ bm = BasicInputMonitor(
531
+ self.context,
532
+ caching_freq=self.refreshtime,
533
+ role="Gridpoint",
534
+ kind="gridpoint",
535
+ )
432
536
  with bm:
433
537
  while not bm.all_done or len(bm.available) > 0:
434
-
435
538
  while bm.available:
436
539
  s = bm.pop_available().section
437
540
  file_in = s.rh.container.localpath()
438
- file_outfmt = re.sub(r'^(.*?)((:?\.[^.]*)?)$', r'\1_{filtername:s}\2', file_in)
439
-
440
- logger.info("Adding input file %s to the job list", file_in)
441
- self._add_instructions(common_i,
442
- dict(name=[file_in, ],
443
- file_in=[file_in, ], file_outfmt=[file_outfmt, ]))
541
+ file_outfmt = re.sub(
542
+ r"^(.*?)((:?\.[^.]*)?)$",
543
+ r"\1_{filtername:s}\2",
544
+ file_in,
545
+ )
546
+
547
+ logger.info(
548
+ "Adding input file %s to the job list", file_in
549
+ )
550
+ self._add_instructions(
551
+ common_i,
552
+ dict(
553
+ name=[
554
+ file_in,
555
+ ],
556
+ file_in=[
557
+ file_in,
558
+ ],
559
+ file_outfmt=[
560
+ file_outfmt,
561
+ ],
562
+ ),
563
+ )
444
564
 
445
565
  if not (bm.all_done or len(bm.available) > 0):
446
566
  # Timeout ?
@@ -453,11 +573,17 @@ class StandaloneGRIBFilter(TaylorRun):
453
573
 
454
574
  self._default_post_execute(rh, opts)
455
575
 
456
- for failed_file in [e.section.rh.container.localpath() for e in bm.failed.values()]:
457
- logger.error("We were unable to fetch the following file: %s", failed_file)
576
+ for failed_file in [
577
+ e.section.rh.container.localpath() for e in bm.failed.values()
578
+ ]:
579
+ logger.error(
580
+ "We were unable to fetch the following file: %s", failed_file
581
+ )
458
582
  if self.fatal:
459
- self.delayed_exception_add(IOError("Unable to fetch {:s}".format(failed_file)),
460
- traceback=False)
583
+ self.delayed_exception_add(
584
+ IOError("Unable to fetch {:s}".format(failed_file)),
585
+ traceback=False,
586
+ )
461
587
 
462
588
  if tmout:
463
589
  raise OSError("The waiting loop timed out")
@@ -467,24 +593,24 @@ class AddField(BlindRun):
467
593
  """Miscellaneous manipulation on input FA resources."""
468
594
 
469
595
  _footprint = dict(
470
- attr = dict(
471
- kind = dict(
472
- values = ['addcst', 'addconst', 'addfield'],
473
- remap = dict(
474
- addconst = 'addcst',
596
+ attr=dict(
597
+ kind=dict(
598
+ values=["addcst", "addconst", "addfield"],
599
+ remap=dict(
600
+ addconst="addcst",
475
601
  ),
476
602
  ),
477
- fortnam = dict(
478
- optional = True,
479
- default = 'fort.4',
603
+ fortnam=dict(
604
+ optional=True,
605
+ default="fort.4",
480
606
  ),
481
- fortinput = dict(
482
- optional = True,
483
- default = 'fort.11',
607
+ fortinput=dict(
608
+ optional=True,
609
+ default="fort.11",
484
610
  ),
485
- fortoutput = dict(
486
- optional = True,
487
- default = 'fort.12',
611
+ fortoutput=dict(
612
+ optional=True,
613
+ default="fort.12",
488
614
  ),
489
615
  )
490
616
  )
@@ -499,21 +625,32 @@ class AddField(BlindRun):
499
625
  """Loop on the various initial conditions provided."""
500
626
 
501
627
  # Is there any namelist provided ?
502
- namrh = [x.rh for x in self.context.sequence.effective_inputs(role=('Namelist'),
503
- kind='namelist')]
628
+ namrh = [
629
+ x.rh
630
+ for x in self.context.sequence.effective_inputs(
631
+ role=("Namelist"), kind="namelist"
632
+ )
633
+ ]
504
634
  if namrh:
505
635
  self.system.softlink(namrh[0].container.localpath(), self.fortnam)
506
636
  else:
507
- logger.warning('Do not find any namelist for %s', self.kind)
637
+ logger.warning("Do not find any namelist for %s", self.kind)
508
638
 
509
639
  # Look for some sources files
510
- srcrh = [x.rh for x in self.context.sequence.effective_inputs(role=('Gridpoint', 'Sources'),
511
- kind='gridpoint')]
640
+ srcrh = [
641
+ x.rh
642
+ for x in self.context.sequence.effective_inputs(
643
+ role=("Gridpoint", "Sources"), kind="gridpoint"
644
+ )
645
+ ]
512
646
  srcrh.sort(key=lambda rh: rh.resource.term)
513
647
 
514
648
  for r in srcrh:
515
- self.system.title('Loop on domain {:s} and term {:s}'.format(r.resource.geometry.area,
516
- r.resource.term.fmthm))
649
+ self.system.title(
650
+ "Loop on domain {:s} and term {:s}".format(
651
+ r.resource.geometry.area, r.resource.term.fmthm
652
+ )
653
+ )
517
654
 
518
655
  # Some cleaning
519
656
  self.system.remove(self.fortinput)
@@ -524,11 +661,11 @@ class AddField(BlindRun):
524
661
  self.system.cp(r.container.localpath(), self.fortoutput)
525
662
 
526
663
  # Standard execution
527
- opts['loop'] = r.resource.term
664
+ opts["loop"] = r.resource.term
528
665
  super().execute(rh, opts)
529
666
 
530
667
  # Some cleaning
531
- self.system.rmall('DAPDIR', self.fortinput, self.fortoutput)
668
+ self.system.rmall("DAPDIR", self.fortinput, self.fortoutput)
532
669
 
533
670
  def postfix(self, rh, opts):
534
671
  """Post add cleaning."""
@@ -538,14 +675,18 @@ class AddField(BlindRun):
538
675
 
539
676
  class DegradedDiagPEError(AlgoComponentError):
540
677
  """Exception raised when some of the members are missing in the calculations."""
678
+
541
679
  def __init__(self, ginfo, missings):
542
680
  super().__init__()
543
681
  self._ginfo = ginfo
544
682
  self._missings = missings
545
683
 
546
684
  def __str__(self):
547
- outstr = "Missing input data for geometry={0.area:s}, term={1!s}:\n".format(self._ginfo['geometry'],
548
- self._ginfo['term'])
685
+ outstr = (
686
+ "Missing input data for geometry={0.area:s}, term={1!s}:\n".format(
687
+ self._ginfo["geometry"], self._ginfo["term"]
688
+ )
689
+ )
549
690
  for k, missing in self._missings.items():
550
691
  for member in missing:
551
692
  outstr += "{:s}: member #{!s}\n".format(k, member)
@@ -554,80 +695,97 @@ class DegradedDiagPEError(AlgoComponentError):
554
695
 
555
696
  class DiagPE(BlindRun, DrHookDecoMixin, EcGribDecoMixin):
556
697
  """Execution of diagnostics on grib input (ensemble forecasts specific)."""
698
+
557
699
  _footprint = dict(
558
- attr = dict(
559
- kind = dict(
560
- values = ['diagpe'],
700
+ attr=dict(
701
+ kind=dict(
702
+ values=["diagpe"],
561
703
  ),
562
- method = dict(
563
- info = 'The method used to compute the diagnosis',
564
- values = ['neighbour'],
704
+ method=dict(
705
+ info="The method used to compute the diagnosis",
706
+ values=["neighbour"],
565
707
  ),
566
- numod = dict(
567
- type = int,
568
- info = 'The GRIB model number',
569
- optional = True,
570
- default = DelayedEnvValue('VORTEX_GRIB_NUMOD', 118),
708
+ numod=dict(
709
+ type=int,
710
+ info="The GRIB model number",
711
+ optional=True,
712
+ default=DelayedEnvValue("VORTEX_GRIB_NUMOD", 118),
571
713
  ),
572
- timeout = dict(
573
- type = int,
574
- optional = True,
575
- default = 900,
714
+ timeout=dict(
715
+ type=int,
716
+ optional=True,
717
+ default=900,
576
718
  ),
577
- refreshtime = dict(
578
- type = int,
579
- optional = True,
580
- default = 20,
719
+ refreshtime=dict(
720
+ type=int,
721
+ optional=True,
722
+ default=20,
581
723
  ),
582
- missinglimit = dict(
583
- type = int,
584
- optional = True,
585
- default = 0,
724
+ missinglimit=dict(
725
+ type=int,
726
+ optional=True,
727
+ default=0,
586
728
  ),
587
- waitlimit = dict(
588
- type = int,
589
- optional = True,
590
- default = 900,
729
+ waitlimit=dict(
730
+ type=int,
731
+ optional=True,
732
+ default=900,
591
733
  ),
592
- fatal = dict(
593
- type = bool,
594
- optional = True,
595
- default = True,
734
+ fatal=dict(
735
+ type=bool,
736
+ optional=True,
737
+ default=True,
596
738
  ),
597
- gribfilter_tasks = dict(
598
- type = int,
599
- optional = True,
600
- default = 8,
739
+ gribfilter_tasks=dict(
740
+ type=int,
741
+ optional=True,
742
+ default=8,
601
743
  ),
602
744
  ),
603
745
  )
604
746
 
605
- _method2output_map = dict(neighbour='GRIB_PE_VOISIN')
747
+ _method2output_map = dict(neighbour="GRIB_PE_VOISIN")
606
748
 
607
749
  def spawn_hook(self):
608
750
  """Usually a good habit to dump the fort.4 namelist."""
609
751
  super().spawn_hook()
610
- if self.system.path.exists('fort.4'):
611
- self.system.subtitle('{:s} : dump namelist <fort.4>'.format(self.realkind))
612
- self.system.cat('fort.4', output=False)
613
-
614
- def _actual_execute(self, gmembers, ifilters, filters, basedate, finalterm, rh, opts, gang):
752
+ if self.system.path.exists("fort.4"):
753
+ self.system.subtitle(
754
+ "{:s} : dump namelist <fort.4>".format(self.realkind)
755
+ )
756
+ self.system.cat("fort.4", output=False)
615
757
 
616
- mygeometry = gang.info['geometry']
617
- myterm = gang.info['term']
758
+ def _actual_execute(
759
+ self, gmembers, ifilters, filters, basedate, finalterm, rh, opts, gang
760
+ ):
761
+ mygeometry = gang.info["geometry"]
762
+ myterm = gang.info["term"]
618
763
 
619
- self.system.title('Start processing for geometry={:s}, term={!s}.'.
620
- format(mygeometry.area, myterm))
764
+ self.system.title(
765
+ "Start processing for geometry={:s}, term={!s}.".format(
766
+ mygeometry.area, myterm
767
+ )
768
+ )
621
769
 
622
770
  # Find out what is the common set of members
623
- members = set(gmembers) # gmembers is mutable: we need a copy of it (hence the explicit set())
771
+ members = set(
772
+ gmembers
773
+ ) # gmembers is mutable: we need a copy of it (hence the explicit set())
624
774
  missing_members = dict()
625
775
  for subgang in gang.memberslist:
626
- smembers = {s.section.rh.provider.member for s in subgang.memberslist
627
- if s.state == EntrySt.available}
628
- ufomembers = {s.section.rh.provider.member for s in subgang.memberslist
629
- if s.state == EntrySt.ufo}
630
- missing_members[subgang.nickname] = gmembers - smembers - ufomembers
776
+ smembers = {
777
+ s.section.rh.provider.member
778
+ for s in subgang.memberslist
779
+ if s.state == EntrySt.available
780
+ }
781
+ ufomembers = {
782
+ s.section.rh.provider.member
783
+ for s in subgang.memberslist
784
+ if s.state == EntrySt.ufo
785
+ }
786
+ missing_members[subgang.nickname] = (
787
+ gmembers - smembers - ufomembers
788
+ )
631
789
  members &= smembers
632
790
  # Record an error
633
791
  if members != gmembers:
@@ -636,7 +794,9 @@ class DiagPE(BlindRun, DrHookDecoMixin, EcGribDecoMixin):
636
794
  if self.fatal:
637
795
  self.delayed_exception_add(newexc, traceback=False)
638
796
  else:
639
- logger.info("Fatal is false consequently no exception is recorded. It would look like this:")
797
+ logger.info(
798
+ "Fatal is false consequently no exception is recorded. It would look like this:"
799
+ )
640
800
  print(newexc)
641
801
  members = sorted(members)
642
802
 
@@ -647,68 +807,113 @@ class DiagPE(BlindRun, DrHookDecoMixin, EcGribDecoMixin):
647
807
  # If needed, concatenate or filter the "superset" files
648
808
  supersets = list()
649
809
  for subgang in gang.memberslist:
650
- supersets.extend([(s.section.rh.container.localpath(),
651
- re.sub(r'^[a-zA-Z]+_(.*)$', r'\1', s.section.rh.container.localpath()))
652
- for s in subgang.memberslist
653
- if s.section.role == 'GridpointSuperset'])
654
- supersets_todo = [(s, t) for s, t in supersets
655
- if not self.system.path.exists(t)]
810
+ supersets.extend(
811
+ [
812
+ (
813
+ s.section.rh.container.localpath(),
814
+ re.sub(
815
+ r"^[a-zA-Z]+_(.*)$",
816
+ r"\1",
817
+ s.section.rh.container.localpath(),
818
+ ),
819
+ )
820
+ for s in subgang.memberslist
821
+ if s.section.role == "GridpointSuperset"
822
+ ]
823
+ )
824
+ supersets_todo = [
825
+ (s, t) for s, t in supersets if not self.system.path.exists(t)
826
+ ]
656
827
  if supersets_todo:
657
828
  if len(ifilters):
658
- parallel_grib_filter(self.context,
659
- [s for s, t in supersets_todo],
660
- [t for s, t in supersets_todo],
661
- filters=ifilters, nthreads=self.gribfilter_tasks)
829
+ parallel_grib_filter(
830
+ self.context,
831
+ [s for s, t in supersets_todo],
832
+ [t for s, t in supersets_todo],
833
+ filters=ifilters,
834
+ nthreads=self.gribfilter_tasks,
835
+ )
662
836
  else:
663
- parallel_grib_filter(self.context,
664
- [s for s, t in supersets_todo],
665
- [t for s, t in supersets_todo],
666
- cat=True, nthreads=self.gribfilter_tasks)
837
+ parallel_grib_filter(
838
+ self.context,
839
+ [s for s, t in supersets_todo],
840
+ [t for s, t in supersets_todo],
841
+ cat=True,
842
+ nthreads=self.gribfilter_tasks,
843
+ )
667
844
 
668
845
  # Tweak the namelist
669
- namsec = self.setlink(initrole='Namelist', initkind='namelist', initname='fort.4')
670
- for nam in [x.rh for x in namsec if 'NAM_PARAM' in x.rh.contents]:
671
- logger.info("Substitute the date (%s) to AAAAMMJJHH namelist entry", basedate.ymdh)
672
- nam.contents['NAM_PARAM']['AAAAMMJJHH'] = basedate.ymdh
673
- logger.info("Substitute the number of members (%d) to NBRUN namelist entry", len(members))
674
- nam.contents['NAM_PARAM']['NBRUN'] = len(members)
675
- logger.info("Substitute the the number of terms to NECH(0) namelist entry")
676
- nam.contents['NAM_PARAM']['NECH(0)'] = 1
677
- logger.info("Substitute the ressource term to NECH(1) namelist entry")
846
+ namsec = self.setlink(
847
+ initrole="Namelist", initkind="namelist", initname="fort.4"
848
+ )
849
+ for nam in [x.rh for x in namsec if "NAM_PARAM" in x.rh.contents]:
850
+ logger.info(
851
+ "Substitute the date (%s) to AAAAMMJJHH namelist entry",
852
+ basedate.ymdh,
853
+ )
854
+ nam.contents["NAM_PARAM"]["AAAAMMJJHH"] = basedate.ymdh
855
+ logger.info(
856
+ "Substitute the number of members (%d) to NBRUN namelist entry",
857
+ len(members),
858
+ )
859
+ nam.contents["NAM_PARAM"]["NBRUN"] = len(members)
860
+ logger.info(
861
+ "Substitute the the number of terms to NECH(0) namelist entry"
862
+ )
863
+ nam.contents["NAM_PARAM"]["NECH(0)"] = 1
864
+ logger.info(
865
+ "Substitute the ressource term to NECH(1) namelist entry"
866
+ )
678
867
  # NB: term should be expressed in minutes
679
- nam.contents['NAM_PARAM']['NECH(1)'] = int(myterm)
680
- nam.contents['NAM_PARAM']['ECHFINALE'] = finalterm.hour
868
+ nam.contents["NAM_PARAM"]["NECH(1)"] = int(myterm)
869
+ nam.contents["NAM_PARAM"]["ECHFINALE"] = finalterm.hour
681
870
  # Now, update the model number for the GRIB files
682
- logger.info("Substitute the model number (%d) to namelist entry", self.numod)
683
- nam.contents['NAM_PARAM']['NMODELE'] = self.numod
871
+ logger.info(
872
+ "Substitute the model number (%d) to namelist entry",
873
+ self.numod,
874
+ )
875
+ nam.contents["NAM_PARAM"]["NMODELE"] = self.numod
684
876
  # Add the NAM_PARAMPE block
685
- if 'NAM_NMEMBRES' in nam.contents:
877
+ if "NAM_NMEMBRES" in nam.contents:
686
878
  # Cleaning is needed...
687
- del nam.contents['NAM_NMEMBRES']
688
- newblock = nam.contents.newblock('NAM_NMEMBRES')
879
+ del nam.contents["NAM_NMEMBRES"]
880
+ newblock = nam.contents.newblock("NAM_NMEMBRES")
689
881
  for i, member in enumerate(members):
690
- newblock['NMEMBRES({:d})'.format(i + 1)] = int(member)
882
+ newblock["NMEMBRES({:d})".format(i + 1)] = int(member)
691
883
  # We are done with the namelist
692
884
  nam.save()
693
885
 
694
886
  # Standard execution
695
- opts['loop'] = myterm
887
+ opts["loop"] = myterm
696
888
  super().execute(rh, opts)
697
889
 
698
- actualname = r'{:s}_{:s}\+{:s}'.format(self._method2output_map[self.method],
699
- mygeometry.area, myterm.fmthm)
890
+ actualname = r"{:s}_{:s}\+{:s}".format(
891
+ self._method2output_map[self.method], mygeometry.area, myterm.fmthm
892
+ )
700
893
  # Find out the output file and filter it
701
894
  filtered_out = list()
702
895
  if len(filters):
703
- for candidate in [f for f in self.system.glob(self._method2output_map[self.method] + '*')
704
- if re.match(actualname, f)]:
896
+ for candidate in [
897
+ f
898
+ for f in self.system.glob(
899
+ self._method2output_map[self.method] + "*"
900
+ )
901
+ if re.match(actualname, f)
902
+ ]:
705
903
  logger.info("Starting GRIB filtering on %s.", candidate)
706
- filtered_out.extend(filters(candidate, candidate + '_{filtername:s}'))
904
+ filtered_out.extend(
905
+ filters(candidate, candidate + "_{filtername:s}")
906
+ )
707
907
 
708
908
  # The diagnostic output may be promised
709
- expected = [x for x in self.promises
710
- if (re.match(actualname, x.rh.container.localpath()) or
711
- x.rh.container.localpath() in filtered_out)]
909
+ expected = [
910
+ x
911
+ for x in self.promises
912
+ if (
913
+ re.match(actualname, x.rh.container.localpath())
914
+ or x.rh.container.localpath() in filtered_out
915
+ )
916
+ ]
712
917
  for thispromise in expected:
713
918
  thispromise.put(incache=True)
714
919
 
@@ -718,20 +923,30 @@ class DiagPE(BlindRun, DrHookDecoMixin, EcGribDecoMixin):
718
923
  # Intialise a GRIBFilter for output files (at least try to)
719
924
  gfilter = GRIBFilter(concatenate=False)
720
925
  # We re-serialise data because footprints don't like dictionaries
721
- ofilters = [x.rh.contents.data
722
- for x in self.context.sequence.effective_inputs(role='GRIBFilteringRequest',
723
- kind='filtering_request')]
926
+ ofilters = [
927
+ x.rh.contents.data
928
+ for x in self.context.sequence.effective_inputs(
929
+ role="GRIBFilteringRequest", kind="filtering_request"
930
+ )
931
+ ]
724
932
  gfilter.add_filters(ofilters)
725
933
 
726
934
  # Do we need to filter input files ?
727
935
  # We re-serialise data because footprints don't like dictionaries
728
- ifilters = [json.dumps(x.rh.contents.data)
729
- for x in self.context.sequence.effective_inputs(role='GRIBInputFilteringRequest')]
936
+ ifilters = [
937
+ json.dumps(x.rh.contents.data)
938
+ for x in self.context.sequence.effective_inputs(
939
+ role="GRIBInputFilteringRequest"
940
+ )
941
+ ]
730
942
 
731
943
  # Monitor for the input files
732
- bm = BasicInputMonitor(self.context, caching_freq=self.refreshtime,
733
- role=(re.compile(r'^Gridpoint'), 'Sources'),
734
- kind='gridpoint')
944
+ bm = BasicInputMonitor(
945
+ self.context,
946
+ caching_freq=self.refreshtime,
947
+ role=(re.compile(r"^Gridpoint"), "Sources"),
948
+ kind="gridpoint",
949
+ )
735
950
  # Check that the date is consistent among inputs
736
951
  basedates = set()
737
952
  members = set()
@@ -739,19 +954,29 @@ class DiagPE(BlindRun, DrHookDecoMixin, EcGribDecoMixin):
739
954
  basedates.add(rhI.resource.date)
740
955
  members.add(rhI.provider.member)
741
956
  if len(basedates) > 1:
742
- raise AlgoComponentError('The date must be consistent among the input resources')
957
+ raise AlgoComponentError(
958
+ "The date must be consistent among the input resources"
959
+ )
743
960
  basedate = basedates.pop()
744
961
  # Setup BasicGangs
745
962
  basicmeta = AutoMetaGang()
746
- basicmeta.autofill(bm, ('term', 'safeblock', 'geometry'),
747
- allowmissing=self.missinglimit, waitlimit=self.waitlimit)
963
+ basicmeta.autofill(
964
+ bm,
965
+ ("term", "safeblock", "geometry"),
966
+ allowmissing=self.missinglimit,
967
+ waitlimit=self.waitlimit,
968
+ )
748
969
  # Find out what are the terms, domains and blocks
749
970
  geometries = set()
750
971
  terms = collections.defaultdict(set)
751
972
  blocks = collections.defaultdict(set)
752
973
  reverse = dict()
753
974
  for m in basicmeta.memberslist:
754
- (geo, term, block) = (m.info['geometry'], m.info['term'], m.info['safeblock'])
975
+ (geo, term, block) = (
976
+ m.info["geometry"],
977
+ m.info["term"],
978
+ m.info["safeblock"],
979
+ )
755
980
  geometries.add(geo)
756
981
  terms[geo].add(term)
757
982
  blocks[geo].add(block)
@@ -766,20 +991,25 @@ class DiagPE(BlindRun, DrHookDecoMixin, EcGribDecoMixin):
766
991
  for i_term, term in enumerate(terms[geometry]):
767
992
  elementary_meta = MetaGang()
768
993
  elementary_meta.info = dict(geometry=geometry, term=term)
769
- cterms = [terms[geometry][i] for i in range(i_term,
770
- min(i_term + 2, nterms))]
994
+ cterms = [
995
+ terms[geometry][i]
996
+ for i in range(i_term, min(i_term + 2, nterms))
997
+ ]
771
998
  for inside_term in cterms:
772
999
  for inside_block in blocks[geometry]:
773
1000
  try:
774
- elementary_meta.add_member(reverse[(geometry, inside_term, inside_block)])
1001
+ elementary_meta.add_member(
1002
+ reverse[(geometry, inside_term, inside_block)]
1003
+ )
775
1004
  except KeyError:
776
- raise KeyError("Something is wrong in the inputs: check again !")
1005
+ raise KeyError(
1006
+ "Something is wrong in the inputs: check again !"
1007
+ )
777
1008
  complexmeta.add_member(elementary_meta)
778
1009
  complexgangs[geometry].append(elementary_meta)
779
1010
 
780
1011
  # Now, starts monitoring everything
781
1012
  with bm:
782
-
783
1013
  current_gang = dict()
784
1014
  for geometry in geometries:
785
1015
  try:
@@ -788,23 +1018,40 @@ class DiagPE(BlindRun, DrHookDecoMixin, EcGribDecoMixin):
788
1018
  current_gang[geometry] = None
789
1019
 
790
1020
  while any([g is not None for g in current_gang.values()]):
791
-
792
- for geometry, a_gang in [(g, current_gang[g]) for g in geometries
793
- if (current_gang[g] is not None and
794
- current_gang[g].state is not GangSt.ufo)]:
795
-
796
- self._actual_execute(members, ifilters, gfilter, basedate,
797
- terms[geometry][-1], rh, opts, a_gang)
1021
+ for geometry, a_gang in [
1022
+ (g, current_gang[g])
1023
+ for g in geometries
1024
+ if (
1025
+ current_gang[g] is not None
1026
+ and current_gang[g].state is not GangSt.ufo
1027
+ )
1028
+ ]:
1029
+ self._actual_execute(
1030
+ members,
1031
+ ifilters,
1032
+ gfilter,
1033
+ basedate,
1034
+ terms[geometry][-1],
1035
+ rh,
1036
+ opts,
1037
+ a_gang,
1038
+ )
798
1039
 
799
1040
  # Next one
800
1041
  try:
801
- current_gang[geometry] = complexgangs[geometry].popleft()
1042
+ current_gang[geometry] = complexgangs[
1043
+ geometry
1044
+ ].popleft()
802
1045
  except IndexError:
803
1046
  current_gang[geometry] = None
804
1047
 
805
- if not (bm.all_done or any(gang is not None and
806
- gang.state is not GangSt.ufo
807
- for gang in current_gang.values())):
1048
+ if not (
1049
+ bm.all_done
1050
+ or any(
1051
+ gang is not None and gang.state is not GangSt.ufo
1052
+ for gang in current_gang.values()
1053
+ )
1054
+ ):
808
1055
  # Timeout ?
809
1056
  bm.is_timedout(self.timeout, IOError)
810
1057
  # Wait a little bit :-)
@@ -816,40 +1063,41 @@ class DiagPE(BlindRun, DrHookDecoMixin, EcGribDecoMixin):
816
1063
  class _DiagPIDecoMixin(AlgoComponentDecoMixin):
817
1064
  """Class variables and methods usefull for DiagPI."""
818
1065
 
819
- _MIXIN_EXTRA_FOOTPRINTS = [footprints.Footprint(
820
- attr=dict(
821
- kind=dict(
822
- values=['diagpi', 'diaglabo'],
823
- ),
824
- numod=dict(
825
- info='The GRIB model number',
826
- type=int,
827
- optional=True,
828
- default=DelayedEnvValue('VORTEX_GRIB_NUMOD', 62),
829
- ),
830
- gribcat=dict(
831
- type=bool,
832
- optional=True,
833
- default=False
834
- ),
835
- gribfilter_tasks=dict(
836
- type=int,
837
- optional=True,
838
- default=8,
1066
+ _MIXIN_EXTRA_FOOTPRINTS = [
1067
+ footprints.Footprint(
1068
+ attr=dict(
1069
+ kind=dict(
1070
+ values=["diagpi", "diaglabo"],
1071
+ ),
1072
+ numod=dict(
1073
+ info="The GRIB model number",
1074
+ type=int,
1075
+ optional=True,
1076
+ default=DelayedEnvValue("VORTEX_GRIB_NUMOD", 62),
1077
+ ),
1078
+ gribcat=dict(type=bool, optional=True, default=False),
1079
+ gribfilter_tasks=dict(
1080
+ type=int,
1081
+ optional=True,
1082
+ default=8,
1083
+ ),
839
1084
  ),
840
- ),
841
- )]
1085
+ )
1086
+ ]
842
1087
 
843
1088
  def _prepare_pihook(self, rh, opts):
844
1089
  """Set some variables according to target definition."""
845
1090
 
846
1091
  # Check for input files to concatenate
847
1092
  if self.gribcat:
848
- srcsec = self.context.sequence.effective_inputs(role=('Gridpoint', 'Sources',
849
- 'Preview', 'Previous'),
850
- kind='gridpoint')
1093
+ srcsec = self.context.sequence.effective_inputs(
1094
+ role=("Gridpoint", "Sources", "Preview", "Previous"),
1095
+ kind="gridpoint",
1096
+ )
851
1097
  cat_list_in = [sec for sec in srcsec if not sec.rh.is_expected()]
852
- outsec = self.context.sequence.effective_inputs(role='GridpointOutputPrepare')
1098
+ outsec = self.context.sequence.effective_inputs(
1099
+ role="GridpointOutputPrepare"
1100
+ )
853
1101
  cat_list_out = [sec for sec in outsec if not sec.rh.is_expected()]
854
1102
  self._automatic_cat(cat_list_in, cat_list_out)
855
1103
 
@@ -863,13 +1111,15 @@ class _DiagPIDecoMixin(AlgoComponentDecoMixin):
863
1111
 
864
1112
  def _spawn_pihook(self):
865
1113
  """Usually a good habit to dump the fort.4 namelist."""
866
- if self.system.path.exists('fort.4'):
867
- self.system.subtitle('{:s} : dump namelist <fort.4>'.format(self.realkind))
868
- self.system.cat('fort.4', output=False)
1114
+ if self.system.path.exists("fort.4"):
1115
+ self.system.subtitle(
1116
+ "{:s} : dump namelist <fort.4>".format(self.realkind)
1117
+ )
1118
+ self.system.cat("fort.4", output=False)
869
1119
 
870
- _MIXIN_PREPARE_HOOKS = (_prepare_pihook, )
871
- _MIXIN_POSTFIX_HOOKS = (_postfix_pihook, )
872
- _MIXIN_SPAWN_HOOKS = (_spawn_pihook, )
1120
+ _MIXIN_PREPARE_HOOKS = (_prepare_pihook,)
1121
+ _MIXIN_POSTFIX_HOOKS = (_postfix_pihook,)
1122
+ _MIXIN_SPAWN_HOOKS = (_spawn_pihook,)
873
1123
 
874
1124
  def _automatic_cat(self, list_in, list_out):
875
1125
  """Concatenate the *list_in* and *list_out* input files."""
@@ -877,27 +1127,42 @@ class _DiagPIDecoMixin(AlgoComponentDecoMixin):
877
1127
  inputs = []
878
1128
  outputs = []
879
1129
  intents = []
880
- for (seclist, intent) in zip((list_in, list_out), ('in', 'inout')):
1130
+ for seclist, intent in zip((list_in, list_out), ("in", "inout")):
881
1131
  for isec in seclist:
882
- tmpin = isec.rh.container.localpath() + '.tmpcat'
883
- self.system.move(isec.rh.container.localpath(), tmpin, fmt='grib')
1132
+ tmpin = isec.rh.container.localpath() + ".tmpcat"
1133
+ self.system.move(
1134
+ isec.rh.container.localpath(), tmpin, fmt="grib"
1135
+ )
884
1136
  inputs.append(tmpin)
885
1137
  outputs.append(isec.rh.container.localpath())
886
1138
  intents.append(intent)
887
- parallel_grib_filter(self.context, inputs, outputs, intents,
888
- cat=True, nthreads=self.gribfilter_tasks)
1139
+ parallel_grib_filter(
1140
+ self.context,
1141
+ inputs,
1142
+ outputs,
1143
+ intents,
1144
+ cat=True,
1145
+ nthreads=self.gribfilter_tasks,
1146
+ )
889
1147
  for ifile in inputs:
890
- self.system.rm(ifile, fmt='grib')
1148
+ self.system.rm(ifile, fmt="grib")
891
1149
 
892
1150
  def _batch_filter(self, candidates):
893
1151
  """If no promises are made, the GRIB are filtered at once at the end."""
894
1152
  # We re-serialise data because footprints don't like dictionaries
895
- filters = [json.dumps(x.rh.contents.data)
896
- for x in self.context.sequence.effective_inputs(role='GRIBFilteringRequest',
897
- kind='filtering_request')]
898
- parallel_grib_filter(self.context,
899
- candidates, [f + '_{filtername:s}' for f in candidates],
900
- filters=FPTuple(filters), nthreads=self.gribfilter_tasks)
1153
+ filters = [
1154
+ json.dumps(x.rh.contents.data)
1155
+ for x in self.context.sequence.effective_inputs(
1156
+ role="GRIBFilteringRequest", kind="filtering_request"
1157
+ )
1158
+ ]
1159
+ parallel_grib_filter(
1160
+ self.context,
1161
+ candidates,
1162
+ [f + "_{filtername:s}" for f in candidates],
1163
+ filters=FPTuple(filters),
1164
+ nthreads=self.gribfilter_tasks,
1165
+ )
901
1166
 
902
1167
  def _execute_picommons(self, rh, opts):
903
1168
  """Loop on the various grib files provided."""
@@ -906,39 +1171,60 @@ class _DiagPIDecoMixin(AlgoComponentDecoMixin):
906
1171
  gfilter = GRIBFilter(concatenate=False)
907
1172
  gfilter.add_filters(self.context)
908
1173
 
909
- srcsec = self.context.sequence.effective_inputs(role=('Gridpoint', 'Sources'),
910
- kind='gridpoint')
1174
+ srcsec = self.context.sequence.effective_inputs(
1175
+ role=("Gridpoint", "Sources"), kind="gridpoint"
1176
+ )
911
1177
  srcsec.sort(key=lambda s: s.rh.resource.term)
912
1178
 
913
- outsec = self.context.sequence.effective_inputs(role='GridpointOutputPrepare')
1179
+ outsec = self.context.sequence.effective_inputs(
1180
+ role="GridpointOutputPrepare"
1181
+ )
914
1182
  if outsec:
915
1183
  outsec.sort(key=lambda s: s.rh.resource.term)
916
1184
 
917
1185
  for sec in srcsec:
918
1186
  r = sec.rh
919
- self.system.title('Loop on domain {:s} and term {:s}'.format(r.resource.geometry.area,
920
- r.resource.term.fmthm))
1187
+ self.system.title(
1188
+ "Loop on domain {:s} and term {:s}".format(
1189
+ r.resource.geometry.area, r.resource.term.fmthm
1190
+ )
1191
+ )
921
1192
  # Tweak the namelist
922
- namsec = self.setlink(initrole='Namelist', initkind='namelist', initname='fort.4')
923
- for nam in [x.rh for x in namsec if 'NAM_PARAM' in x.rh.contents]:
924
- logger.info("Substitute the date (%s) to AAAAMMJJHH namelist entry", r.resource.date.ymdh)
925
- nam.contents['NAM_PARAM']['AAAAMMJJHH'] = r.resource.date.ymdh
926
- logger.info("Substitute the the number of terms to NECH(0) namelist entry")
927
- nam.contents['NAM_PARAM']['NECH(0)'] = 1
928
- logger.info("Substitute the ressource term to NECH(1) namelist entry")
1193
+ namsec = self.setlink(
1194
+ initrole="Namelist", initkind="namelist", initname="fort.4"
1195
+ )
1196
+ for nam in [x.rh for x in namsec if "NAM_PARAM" in x.rh.contents]:
1197
+ logger.info(
1198
+ "Substitute the date (%s) to AAAAMMJJHH namelist entry",
1199
+ r.resource.date.ymdh,
1200
+ )
1201
+ nam.contents["NAM_PARAM"]["AAAAMMJJHH"] = r.resource.date.ymdh
1202
+ logger.info(
1203
+ "Substitute the the number of terms to NECH(0) namelist entry"
1204
+ )
1205
+ nam.contents["NAM_PARAM"]["NECH(0)"] = 1
1206
+ logger.info(
1207
+ "Substitute the ressource term to NECH(1) namelist entry"
1208
+ )
929
1209
  # NB: term should be expressed in minutes
930
- nam.contents['NAM_PARAM']['NECH(1)'] = int(r.resource.term)
1210
+ nam.contents["NAM_PARAM"]["NECH(1)"] = int(r.resource.term)
931
1211
  # Add the member number in a dedicated namelist block
932
1212
  if r.provider.member is not None:
933
- mblock = nam.contents.newblock('NAM_PARAMPE')
934
- mblock['NMEMBER'] = int(r.provider.member)
1213
+ mblock = nam.contents.newblock("NAM_PARAMPE")
1214
+ mblock["NMEMBER"] = int(r.provider.member)
935
1215
  # Now, update the model number for the GRIB files
936
- if 'NAM_DIAG' in nam.contents:
1216
+ if "NAM_DIAG" in nam.contents:
937
1217
  nmod = self.numod
938
- logger.info("Substitute the model number (%d) to namelist entry", nmod)
939
- for namk in ('CONV', 'BR', 'HIV', 'ECHOT', 'ICA', 'PSN'):
940
- if namk in nam.contents['NAM_DIAG'] and nam.contents['NAM_DIAG'][namk] != 0:
941
- nam.contents['NAM_DIAG'][namk] = nmod
1218
+ logger.info(
1219
+ "Substitute the model number (%d) to namelist entry",
1220
+ nmod,
1221
+ )
1222
+ for namk in ("CONV", "BR", "HIV", "ECHOT", "ICA", "PSN"):
1223
+ if (
1224
+ namk in nam.contents["NAM_DIAG"]
1225
+ and nam.contents["NAM_DIAG"][namk] != 0
1226
+ ):
1227
+ nam.contents["NAM_DIAG"][namk] = nmod
942
1228
  # We are done with the namelist
943
1229
  nam.save()
944
1230
 
@@ -948,46 +1234,65 @@ class _DiagPIDecoMixin(AlgoComponentDecoMixin):
948
1234
  # Expect the input grib file to be here
949
1235
  if sec.rh.is_expected():
950
1236
  cat_list_in.append(sec)
951
- self.grab(sec, comment='diagpi source')
1237
+ self.grab(sec, comment="diagpi source")
952
1238
  if outsec:
953
1239
  out = outsec.pop(0)
954
1240
  assert out.rh.resource.term == sec.rh.resource.term
955
1241
  if out.rh.is_expected():
956
1242
  cat_list_out.append(out)
957
- self.grab(out, comment='diagpi output')
1243
+ self.grab(out, comment="diagpi output")
958
1244
 
959
1245
  # Also link in previous grib files in order to compute some winter diagnostics
960
- srcpsec = [x
961
- for x in self.context.sequence.effective_inputs(role=('Preview', 'Previous'),
962
- kind='gridpoint')
963
- if x.rh.resource.term < r.resource.term]
1246
+ srcpsec = [
1247
+ x
1248
+ for x in self.context.sequence.effective_inputs(
1249
+ role=("Preview", "Previous"), kind="gridpoint"
1250
+ )
1251
+ if x.rh.resource.term < r.resource.term
1252
+ ]
964
1253
  for pr in srcpsec:
965
1254
  if pr.rh.is_expected():
966
1255
  cat_list_in.append(pr)
967
- self.grab(pr, comment='diagpi additional source for winter diag')
1256
+ self.grab(
1257
+ pr, comment="diagpi additional source for winter diag"
1258
+ )
968
1259
 
969
1260
  self._automatic_cat(cat_list_in, cat_list_out)
970
1261
 
971
1262
  # Standard execution
972
- opts['loop'] = r.resource.term
1263
+ opts["loop"] = r.resource.term
973
1264
  super(self.mixin_execute_companion(), self).execute(rh, opts)
974
1265
 
975
- actualname = r'GRIB[-_A-Z]+{:s}\+{:s}(?:_member\d+)?$'.format(r.resource.geometry.area,
976
- r.resource.term.fmthm)
1266
+ actualname = r"GRIB[-_A-Z]+{:s}\+{:s}(?:_member\d+)?$".format(
1267
+ r.resource.geometry.area, r.resource.term.fmthm
1268
+ )
977
1269
  # Find out the output file and filter it
978
1270
  filtered_out = list()
979
1271
  if len(gfilter):
980
- for candidate in [f for f in self.system.glob('GRIB*') if re.match(actualname, f)]:
1272
+ for candidate in [
1273
+ f
1274
+ for f in self.system.glob("GRIB*")
1275
+ if re.match(actualname, f)
1276
+ ]:
981
1277
  if len(self.promises):
982
- logger.info("Starting GRIB filtering on %s.", candidate)
983
- filtered_out.extend(gfilter(candidate, candidate + '_{filtername:s}'))
1278
+ logger.info(
1279
+ "Starting GRIB filtering on %s.", candidate
1280
+ )
1281
+ filtered_out.extend(
1282
+ gfilter(candidate, candidate + "_{filtername:s}")
1283
+ )
984
1284
  else:
985
1285
  self._delayed_filtering.append(candidate)
986
1286
 
987
1287
  # The diagnostic output may be promised
988
- expected = [x for x in self.promises
989
- if (re.match(actualname, x.rh.container.localpath()) or
990
- x.rh.container.localpath() in filtered_out)]
1288
+ expected = [
1289
+ x
1290
+ for x in self.promises
1291
+ if (
1292
+ re.match(actualname, x.rh.container.localpath())
1293
+ or x.rh.container.localpath() in filtered_out
1294
+ )
1295
+ ]
991
1296
  for thispromise in expected:
992
1297
  thispromise.put(incache=True)
993
1298
 
@@ -996,11 +1301,13 @@ class _DiagPIDecoMixin(AlgoComponentDecoMixin):
996
1301
 
997
1302
  class DiagPI(BlindRun, _DiagPIDecoMixin, EcGribDecoMixin):
998
1303
  """Execution of diagnostics on grib input (deterministic forecasts specific)."""
1304
+
999
1305
  pass
1000
1306
 
1001
1307
 
1002
1308
  class DiagPIMPI(Parallel, _DiagPIDecoMixin, EcGribDecoMixin):
1003
1309
  """Execution of diagnostics on grib input (deterministic forecasts specific)."""
1310
+
1004
1311
  pass
1005
1312
 
1006
1313
 
@@ -1008,23 +1315,23 @@ class Fa2GaussGrib(BlindRun, DrHookDecoMixin):
1008
1315
  """Standard FA conversion, e.g. with GOBPTOUT as a binary resource."""
1009
1316
 
1010
1317
  _footprint = dict(
1011
- attr = dict(
1012
- kind = dict(
1013
- values = ['fa2gaussgrib'],
1318
+ attr=dict(
1319
+ kind=dict(
1320
+ values=["fa2gaussgrib"],
1014
1321
  ),
1015
- fortinput = dict(
1016
- optional = True,
1017
- default = 'PFFPOS_FIELDS',
1322
+ fortinput=dict(
1323
+ optional=True,
1324
+ default="PFFPOS_FIELDS",
1018
1325
  ),
1019
- numod = dict(
1020
- type = int,
1021
- optional = True,
1022
- default = DelayedEnvValue('VORTEX_GRIB_NUMOD', 212),
1326
+ numod=dict(
1327
+ type=int,
1328
+ optional=True,
1329
+ default=DelayedEnvValue("VORTEX_GRIB_NUMOD", 212),
1023
1330
  ),
1024
- verbose = dict(
1025
- type = bool,
1026
- optional = True,
1027
- default = False,
1331
+ verbose=dict(
1332
+ type=bool,
1333
+ optional=True,
1334
+ default=False,
1028
1335
  ),
1029
1336
  )
1030
1337
  )
@@ -1032,35 +1339,43 @@ class Fa2GaussGrib(BlindRun, DrHookDecoMixin):
1032
1339
  def execute(self, rh, opts):
1033
1340
  """Loop on the various initial conditions provided."""
1034
1341
 
1035
- thisoutput = 'GRID_' + self.fortinput[7:14] + '1'
1342
+ thisoutput = "GRID_" + self.fortinput[7:14] + "1"
1036
1343
 
1037
- gpsec = self.context.sequence.effective_inputs(role=('Historic', 'ModelState'))
1344
+ gpsec = self.context.sequence.effective_inputs(
1345
+ role=("Historic", "ModelState")
1346
+ )
1038
1347
  gpsec.sort(key=lambda s: s.rh.resource.term)
1039
1348
 
1040
1349
  for sec in gpsec:
1041
1350
  r = sec.rh
1042
1351
 
1043
- self.system.title('Loop on files: {:s}'.format(r.container.localpath()))
1352
+ self.system.title(
1353
+ "Loop on files: {:s}".format(r.container.localpath())
1354
+ )
1044
1355
 
1045
1356
  # Some preventive cleaning
1046
1357
  self.system.remove(thisoutput)
1047
- self.system.remove('fort.4')
1358
+ self.system.remove("fort.4")
1048
1359
 
1049
1360
  # Build the local namelist block
1050
- nb = NamelistBlock(name='NAML')
1361
+ nb = NamelistBlock(name="NAML")
1051
1362
  nb.NBDOM = 1
1052
1363
  nb.INUMOD = self.numod
1053
1364
 
1054
- nb['LLBAVE'] = self.verbose
1055
- nb['CDNOMF(1)'] = self.fortinput
1056
- with open('fort.4', 'w') as namfd:
1365
+ nb["LLBAVE"] = self.verbose
1366
+ nb["CDNOMF(1)"] = self.fortinput
1367
+ with open("fort.4", "w") as namfd:
1057
1368
  namfd.write(nb.dumps())
1058
1369
 
1059
- self.system.header('{:s} : local namelist {:s} dump'.format(self.realkind, 'fort.4'))
1060
- self.system.cat('fort.4', output=False)
1370
+ self.system.header(
1371
+ "{:s} : local namelist {:s} dump".format(
1372
+ self.realkind, "fort.4"
1373
+ )
1374
+ )
1375
+ self.system.cat("fort.4", output=False)
1061
1376
 
1062
1377
  # Expect the input FP file source to be there...
1063
- self.grab(sec, comment='fullpos source')
1378
+ self.grab(sec, comment="fullpos source")
1064
1379
 
1065
1380
  # Finally set the actual init file
1066
1381
  self.system.softlink(r.container.localpath(), self.fortinput)
@@ -1070,10 +1385,13 @@ class Fa2GaussGrib(BlindRun, DrHookDecoMixin):
1070
1385
 
1071
1386
  # Freeze the current output
1072
1387
  if self.system.path.exists(thisoutput):
1073
- self.system.move(thisoutput, 'GGRID' + r.container.localpath()[6:], fmt='grib')
1388
+ self.system.move(
1389
+ thisoutput,
1390
+ "GGRID" + r.container.localpath()[6:],
1391
+ fmt="grib",
1392
+ )
1074
1393
  else:
1075
- logger.warning('Missing some grib output for %s',
1076
- thisoutput)
1394
+ logger.warning("Missing some grib output for %s", thisoutput)
1077
1395
 
1078
1396
  # Some cleaning
1079
1397
  self.system.rmall(self.fortinput)
@@ -1081,47 +1399,53 @@ class Fa2GaussGrib(BlindRun, DrHookDecoMixin):
1081
1399
 
1082
1400
  class Reverser(BlindRun, DrHookDecoMixin):
1083
1401
  """Compute the initial state for Ctpini."""
1402
+
1084
1403
  _footprint = dict(
1085
- info = "Compute initial state for Ctpini.",
1086
- attr = dict(
1087
- kind = dict(
1088
- values = ['reverser'],
1404
+ info="Compute initial state for Ctpini.",
1405
+ attr=dict(
1406
+ kind=dict(
1407
+ values=["reverser"],
1089
1408
  ),
1090
- param_iter = dict(
1091
- type = int,
1409
+ param_iter=dict(
1410
+ type=int,
1092
1411
  ),
1093
- condlim = dict(
1094
- type = int,
1412
+ condlim=dict(
1413
+ type=int,
1095
1414
  ),
1096
- ano_type = dict(
1097
- type = int,
1415
+ ano_type=dict(
1416
+ type=int,
1098
1417
  ),
1099
- )
1418
+ ),
1100
1419
  )
1101
1420
 
1102
1421
  def prepare(self, rh, opts):
1103
1422
  # Get info about the directives files directory
1104
- directives = self.context.sequence.effective_inputs(role='Directives',
1105
- kind='ctpini_directives_file')
1423
+ directives = self.context.sequence.effective_inputs(
1424
+ role="Directives", kind="ctpini_directives_file"
1425
+ )
1106
1426
  if len(directives) < 1:
1107
1427
  logger.error("No directive file found. Stop")
1108
1428
  raise ValueError("No directive file found.")
1109
1429
  if len(directives) > 1:
1110
- logger.warning("Multiple directive files found. This is strange...")
1430
+ logger.warning(
1431
+ "Multiple directive files found. This is strange..."
1432
+ )
1111
1433
  # Substitute values in the simili namelist
1112
- param = self.context.sequence.effective_inputs(role='Param')
1434
+ param = self.context.sequence.effective_inputs(role="Param")
1113
1435
  if len(param) < 1:
1114
1436
  logger.error("No parameter file found. Stop")
1115
1437
  raise ValueError("No parameter file found.")
1116
1438
  elif len(param) > 1:
1117
- logger.warning("Multiple files for parameter, the first %s is taken",
1118
- param[0].rh.container.filename)
1439
+ logger.warning(
1440
+ "Multiple files for parameter, the first %s is taken",
1441
+ param[0].rh.container.filename,
1442
+ )
1119
1443
  param = param[0].rh
1120
1444
  paramct = param.contents
1121
1445
  dictkeyvalue = dict()
1122
- dictkeyvalue[r'param_iter'] = str(self.param_iter)
1123
- dictkeyvalue[r'condlim'] = str(self.condlim)
1124
- dictkeyvalue[r'ano_type'] = str(self.ano_type)
1446
+ dictkeyvalue[r"param_iter"] = str(self.param_iter)
1447
+ dictkeyvalue[r"condlim"] = str(self.condlim)
1448
+ dictkeyvalue[r"ano_type"] = str(self.ano_type)
1125
1449
  paramct.setitems(dictkeyvalue)
1126
1450
  param.save()
1127
1451
  logger.info("Here is the parameter file (after substitution):")
@@ -1132,11 +1456,13 @@ class Reverser(BlindRun, DrHookDecoMixin):
1132
1456
 
1133
1457
  class DegradedEnsembleDiagError(AlgoComponentError):
1134
1458
  """Exception raised when some of the members are missing."""
1459
+
1135
1460
  pass
1136
1461
 
1137
1462
 
1138
1463
  class FailedEnsembleDiagError(DegradedEnsembleDiagError):
1139
1464
  """Exception raised when too many members are missing."""
1465
+
1140
1466
  pass
1141
1467
 
1142
1468
 
@@ -1144,29 +1470,29 @@ class PyEnsembleDiag(Expresso):
1144
1470
  """Execution of diagnostics on grib input (ensemble forecasts specific)."""
1145
1471
 
1146
1472
  _footprint = dict(
1147
- attr = dict(
1148
- kind = dict(
1149
- values = ['py_diag_ens'],
1473
+ attr=dict(
1474
+ kind=dict(
1475
+ values=["py_diag_ens"],
1150
1476
  ),
1151
- timeout = dict(
1152
- type = int,
1153
- optional = True,
1154
- default = 1200,
1477
+ timeout=dict(
1478
+ type=int,
1479
+ optional=True,
1480
+ default=1200,
1155
1481
  ),
1156
- refreshtime = dict(
1157
- type = int,
1158
- optional = True,
1159
- default = 20,
1482
+ refreshtime=dict(
1483
+ type=int,
1484
+ optional=True,
1485
+ default=20,
1160
1486
  ),
1161
- missinglimit = dict(
1162
- type = int,
1163
- optional = True,
1164
- default = 0,
1487
+ missinglimit=dict(
1488
+ type=int,
1489
+ optional=True,
1490
+ default=0,
1165
1491
  ),
1166
- waitlimit = dict(
1167
- type = int,
1168
- optional = True,
1169
- default = 900,
1492
+ waitlimit=dict(
1493
+ type=int,
1494
+ optional=True,
1495
+ default=900,
1170
1496
  ),
1171
1497
  ),
1172
1498
  )
@@ -1179,39 +1505,47 @@ class PyEnsembleDiag(Expresso):
1179
1505
  """Prepare options for the resource's command line."""
1180
1506
  return self._cl_args
1181
1507
 
1182
- def _actual_execute(self, rh, opts, input_rhs, ** infos):
1508
+ def _actual_execute(self, rh, opts, input_rhs, **infos):
1183
1509
  """Actually run the script for a specific bunch of input files (**inpu_rhs**)."""
1184
- output_fname = ('ensdiag_{safeblock:s}_{geometry.tag:s}_{term.fmthm}.grib'
1185
- .format(** infos))
1186
- self._cl_args = dict(flowconf='flowconf.json',
1187
- output=output_fname)
1510
+ output_fname = (
1511
+ "ensdiag_{safeblock:s}_{geometry.tag:s}_{term.fmthm}.grib".format(
1512
+ **infos
1513
+ )
1514
+ )
1515
+ self._cl_args = dict(flowconf="flowconf.json", output=output_fname)
1188
1516
 
1189
1517
  # Create the JSON file that will be ingested by the script
1190
1518
  self.system.json_dump(
1191
- dict(date=input_rhs[0].resource.date.ymdhm,
1192
- term=infos['term'].fmthm,
1193
- geometry=infos['geometry'].tag,
1194
- area=infos['geometry'].area,
1195
- block=infos['safeblock'],
1196
- grib_files=[r.container.localpath() for r in input_rhs],
1197
- ),
1198
- self._cl_args['flowconf']
1519
+ dict(
1520
+ date=input_rhs[0].resource.date.ymdhm,
1521
+ term=infos["term"].fmthm,
1522
+ geometry=infos["geometry"].tag,
1523
+ area=infos["geometry"].area,
1524
+ block=infos["safeblock"],
1525
+ grib_files=[r.container.localpath() for r in input_rhs],
1526
+ ),
1527
+ self._cl_args["flowconf"],
1199
1528
  )
1200
1529
 
1201
1530
  # Actualy run the post-processing script
1202
1531
  super().execute(rh, opts)
1203
1532
 
1204
1533
  # The diagnostic output may be promised
1205
- for thispromise in [x for x in self.promises
1206
- if output_fname == x.rh.container.localpath()]:
1534
+ for thispromise in [
1535
+ x
1536
+ for x in self.promises
1537
+ if output_fname == x.rh.container.localpath()
1538
+ ]:
1207
1539
  thispromise.put(incache=True)
1208
1540
 
1209
1541
  @staticmethod
1210
1542
  def _gang_txt_id(gang):
1211
1543
  """A string that identifies the input data currently being processed."""
1212
- return ("term={term.fmthm:s}, " +
1213
- "geometry={geometry.tag:s} " +
1214
- "and block={safeblock:s}").format(** gang.info)
1544
+ return (
1545
+ "term={term.fmthm:s}, "
1546
+ + "geometry={geometry.tag:s} "
1547
+ + "and block={safeblock:s}"
1548
+ ).format(**gang.info)
1215
1549
 
1216
1550
  def _handle_gang_rescue(self, gang):
1217
1551
  """If some of the entries are missing, create a delayed exception."""
@@ -1220,24 +1554,31 @@ class PyEnsembleDiag(Expresso):
1220
1554
  self.system.subtitle("WARNING: Missing data for " + txt_id)
1221
1555
  for st in (EntrySt.ufo, EntrySt.failed, EntrySt.expected):
1222
1556
  if gang.members[st]:
1223
- print('Here is the list of Resource Handler with status < {:s} >:'
1224
- .format(st))
1557
+ print(
1558
+ "Here is the list of Resource Handler with status < {:s} >:".format(
1559
+ st
1560
+ )
1561
+ )
1225
1562
  for i, e in enumerate(gang.members[st]):
1226
1563
  e.section.rh.quickview(nb=i + 1, indent=1)
1227
1564
  self.delayed_exception_add(
1228
- FailedEnsembleDiagError("Too many inputs are missing for " + txt_id)
1229
- if gang.state == GangSt.failed else
1230
- DegradedEnsembleDiagError("Some of the inputs are missing for " + txt_id),
1231
- traceback=False
1565
+ FailedEnsembleDiagError(
1566
+ "Too many inputs are missing for " + txt_id
1567
+ )
1568
+ if gang.state == GangSt.failed
1569
+ else DegradedEnsembleDiagError(
1570
+ "Some of the inputs are missing for " + txt_id
1571
+ ),
1572
+ traceback=False,
1232
1573
  )
1233
1574
 
1234
1575
  def execute(self, rh, opts):
1235
1576
  """Loop on the various grib files provided."""
1236
1577
 
1237
1578
  # Monitor for the input files
1238
- bm = BasicInputMonitor(self.context,
1239
- caching_freq=self.refreshtime,
1240
- role='Gridpoint')
1579
+ bm = BasicInputMonitor(
1580
+ self.context, caching_freq=self.refreshtime, role="Gridpoint"
1581
+ )
1241
1582
 
1242
1583
  # Check that the date is consistent among inputs
1243
1584
  basedates = set()
@@ -1246,12 +1587,18 @@ class PyEnsembleDiag(Expresso):
1246
1587
  basedates.add(rhI.resource.date)
1247
1588
  members.add(rhI.provider.member)
1248
1589
  if len(basedates) > 1:
1249
- raise AlgoComponentError('The date must be consistent among the input resources')
1590
+ raise AlgoComponentError(
1591
+ "The date must be consistent among the input resources"
1592
+ )
1250
1593
 
1251
1594
  # Setup BasicGangs
1252
1595
  basicmeta = AutoMetaGang()
1253
- basicmeta.autofill(bm, ('term', 'safeblock', 'geometry'),
1254
- allowmissing=self.missinglimit, waitlimit=self.waitlimit)
1596
+ basicmeta.autofill(
1597
+ bm,
1598
+ ("term", "safeblock", "geometry"),
1599
+ allowmissing=self.missinglimit,
1600
+ waitlimit=self.waitlimit,
1601
+ )
1255
1602
 
1256
1603
  # Now, starts monitoring everything
1257
1604
  with bm:
@@ -1263,13 +1610,20 @@ class PyEnsembleDiag(Expresso):
1263
1610
  available = thegang.members[EntrySt.available]
1264
1611
  self._handle_gang_rescue(thegang)
1265
1612
 
1266
- self._actual_execute(rh, opts,
1267
- [e.section.rh for e in available],
1268
- **thegang.info)
1613
+ self._actual_execute(
1614
+ rh,
1615
+ opts,
1616
+ [e.section.rh for e in available],
1617
+ **thegang.info,
1618
+ )
1269
1619
 
1270
1620
  self.system.highlight("Done with " + txt_id)
1271
1621
 
1272
- if not bm.all_done and basicmeta.has_ufo() and not basicmeta.has_pcollectable():
1622
+ if (
1623
+ not bm.all_done
1624
+ and basicmeta.has_ufo()
1625
+ and not basicmeta.has_pcollectable()
1626
+ ):
1273
1627
  # Timeout ?
1274
1628
  tmout = bm.is_timedout(self.timeout)
1275
1629
  if tmout:
@@ -1280,6 +1634,8 @@ class PyEnsembleDiag(Expresso):
1280
1634
 
1281
1635
  # Warn for failed gangs
1282
1636
  if basicmeta.members[GangSt.failed]:
1283
- self.system.title("One or several (term, geometry, block) group(s) could not be processed")
1637
+ self.system.title(
1638
+ "One or several (term, geometry, block) group(s) could not be processed"
1639
+ )
1284
1640
  for thegang in basicmeta.members[GangSt.failed]:
1285
1641
  self._handle_gang_rescue(thegang)