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
@@ -32,14 +32,14 @@ logger = loggers.getLogger(__name__)
32
32
 
33
33
 
34
34
  class Raw2OdbExecutionError(ExecutionError):
35
-
36
35
  def __init__(self, odb_database):
37
36
  self.odb_database = odb_database
38
- super().__init__('Raw2odb execution failed.')
37
+ super().__init__("Raw2odb execution failed.")
39
38
 
40
39
  def __str__(self):
41
- return ("Error while running bator for ODB database < {:s} >"
42
- .format(self.odb_database))
40
+ return "Error while running bator for ODB database < {:s} >".format(
41
+ self.odb_database
42
+ )
43
43
 
44
44
 
45
45
  class Bateur(VortexWorkerBlindRun):
@@ -54,18 +54,18 @@ class Bateur(VortexWorkerBlindRun):
54
54
  info="Bateur: launches a single bator execution in a parallel context",
55
55
  attr=dict(
56
56
  base=dict(
57
- info = 'name of the odb database to process',
57
+ info="name of the odb database to process",
58
58
  ),
59
59
  workdir=dict(
60
- info = 'working directory of the run',
60
+ info="working directory of the run",
61
61
  ),
62
- inputsize = dict(
63
- info = 'input files total size in bytes',
64
- type = int,
65
- default = 0,
66
- )
67
- )
68
- )
62
+ inputsize=dict(
63
+ info="input files total size in bytes",
64
+ type=int,
65
+ default=0,
66
+ ),
67
+ ),
68
+ ),
69
69
  ]
70
70
 
71
71
  @property
@@ -73,135 +73,168 @@ class Bateur(VortexWorkerBlindRun):
73
73
  return self.memory * 1024 * 1024
74
74
 
75
75
  def vortex_task(self, **kwargs):
76
- odb_drv = odb.OdbDriver(self.cycle,
77
- self.system, self.system.env)
78
- self.system.cd('wkdir_' + self.base)
76
+ odb_drv = odb.OdbDriver(self.cycle, self.system, self.system.env)
77
+ self.system.cd("wkdir_" + self.base)
79
78
 
80
- dbpath = self.system.path.join(self.workdir, 'ECMA.' + self.base)
79
+ dbpath = self.system.path.join(self.workdir, "ECMA." + self.base)
81
80
  listpath = self.system.path.join(self.workdir, "listing." + self.base)
82
81
 
83
- odb_drv.fix_db_path('ecma', dbpath)
82
+ odb_drv.fix_db_path("ecma", dbpath)
84
83
 
85
- real_time = - time.time()
84
+ real_time = -time.time()
86
85
  start_time = utcnow().isoformat()
87
86
  rdict = dict(rc=True)
88
87
  try:
89
88
  self.local_spawn(listpath)
90
89
  except ExecutionError:
91
- rdict['rc'] = Raw2OdbExecutionError(self.base)
90
+ rdict["rc"] = Raw2OdbExecutionError(self.base)
92
91
  real_time += time.time()
93
92
 
94
93
  if self.system.memory_info is not None:
95
- realMem = self.system.memory_info.children_maxRSS('B')
96
- memRatio = (realMem / float(self.memory_in_bytes)) if self.memory_in_bytes > 0 else None
94
+ realMem = self.system.memory_info.children_maxRSS("B")
95
+ memRatio = (
96
+ (realMem / float(self.memory_in_bytes))
97
+ if self.memory_in_bytes > 0
98
+ else None
99
+ )
97
100
  else:
98
101
  realMem = None
99
102
  memRatio = None
100
103
 
101
- rdict['synthesis'] = dict(base=self.base,
102
- inputsize=self.inputsize,
103
- mem_expected=self.memory_in_bytes,
104
- mem_real=realMem,
105
- mem_ratio=memRatio,
106
- time_expected=self.expected_time,
107
- time_start=start_time,
108
- time_real=real_time,
109
- time_ratio=(real_time / float(self.expected_time)
110
- if self.expected_time > 0 else None),
111
- sched_id=self.scheduler_ticket,
112
- )
104
+ rdict["synthesis"] = dict(
105
+ base=self.base,
106
+ inputsize=self.inputsize,
107
+ mem_expected=self.memory_in_bytes,
108
+ mem_real=realMem,
109
+ mem_ratio=memRatio,
110
+ time_expected=self.expected_time,
111
+ time_start=start_time,
112
+ time_real=real_time,
113
+ time_ratio=(
114
+ real_time / float(self.expected_time)
115
+ if self.expected_time > 0
116
+ else None
117
+ ),
118
+ sched_id=self.scheduler_ticket,
119
+ )
113
120
 
114
121
  # Save a copy of io assign map in the new database
115
122
  if self.system.path.isdir(dbpath):
116
- self.system.cp(self.system.path.join(self.workdir, 'odb_db_template', 'IOASSIGN'),
117
- self.system.path.join(dbpath, 'IOASSIGN'))
123
+ self.system.cp(
124
+ self.system.path.join(
125
+ self.workdir, "odb_db_template", "IOASSIGN"
126
+ ),
127
+ self.system.path.join(dbpath, "IOASSIGN"),
128
+ )
118
129
  else:
119
- logger.warning('ODB database not created: ' + self.base)
130
+ logger.warning("ODB database not created: " + self.base)
120
131
 
121
132
  return rdict
122
133
 
123
134
 
124
- class Raw2ODBparallel(ParaBlindRun, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin):
135
+ class Raw2ODBparallel(
136
+ ParaBlindRun, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin
137
+ ):
125
138
  """Convert raw observations files to ODB using taylorism."""
126
139
 
127
140
  _footprint = dict(
128
- attr = dict(
129
- kind = dict(
130
- values = ['raw2odb', 'bufr2odb', 'obsoul2odb'],
131
- remap = dict(
132
- bufr2odb = 'raw2odb',
133
- obsoul2odb = 'raw2odb',
134
- )
141
+ attr=dict(
142
+ kind=dict(
143
+ values=["raw2odb", "bufr2odb", "obsoul2odb"],
144
+ remap=dict(
145
+ bufr2odb="raw2odb",
146
+ obsoul2odb="raw2odb",
147
+ ),
135
148
  ),
136
- engine = dict(
137
- values = ['blind', 'parallel'] # parallel -> for backward compatibility
149
+ engine=dict(
150
+ values=[
151
+ "blind",
152
+ "parallel",
153
+ ] # parallel -> for backward compatibility
138
154
  ),
139
- ioassign = dict(
140
- optional = False,
155
+ ioassign=dict(
156
+ optional=False,
141
157
  ),
142
- lamflag = dict(
143
- info = 'Activate LAMFLAG (i.e work for Limited Area Model)',
144
- type = bool,
145
- optional = True,
146
- default = False,
158
+ lamflag=dict(
159
+ info="Activate LAMFLAG (i.e work for Limited Area Model)",
160
+ type=bool,
161
+ optional=True,
162
+ default=False,
147
163
  ),
148
- ontime = dict(
149
- info = "Check observation's resources date vs own data attribute.",
150
- type = bool,
151
- optional = True,
152
- default = True,
164
+ ontime=dict(
165
+ info="Check observation's resources date vs own data attribute.",
166
+ type=bool,
167
+ optional=True,
168
+ default=True,
153
169
  ),
154
- mapall = dict(
155
- info = "All observation files must be accounted for in an obsmap file. ",
156
- type = bool,
157
- optional = True,
158
- default = False,
170
+ mapall=dict(
171
+ info="All observation files must be accounted for in an obsmap file. ",
172
+ type=bool,
173
+ optional=True,
174
+ default=False,
159
175
  ),
160
- maponly = dict(
161
- info = ("Work only with observation files listed in the obsmap files. " +
162
- "(if False, obsmap entries may be automatically generated)."),
163
- type = bool,
164
- optional = True,
165
- default = False,
176
+ maponly=dict(
177
+ info=(
178
+ "Work only with observation files listed in the obsmap files. "
179
+ + "(if False, obsmap entries may be automatically generated)."
180
+ ),
181
+ type=bool,
182
+ optional=True,
183
+ default=False,
166
184
  ),
167
- member = dict(
168
- info = ("The current member's number " +
169
- "(may be omitted in deterministic configurations)."),
170
- optional = True,
171
- type = int,
185
+ member=dict(
186
+ info=(
187
+ "The current member's number "
188
+ + "(may be omitted in deterministic configurations)."
189
+ ),
190
+ optional=True,
191
+ type=int,
172
192
  ),
173
- dataid = dict(
174
- info = ("The ODB databases created by Bator contain an identifier " +
175
- "that is specified as a command-line argument. This " +
176
- "switch tweaks the way the command-line argument is " +
177
- "generated."),
178
- values = ['empty', 'hh'],
179
- default = 'hh',
180
- optional = True
193
+ dataid=dict(
194
+ info=(
195
+ "The ODB databases created by Bator contain an identifier "
196
+ + "that is specified as a command-line argument. This "
197
+ + "switch tweaks the way the command-line argument is "
198
+ + "generated."
199
+ ),
200
+ values=["empty", "hh"],
201
+ default="hh",
202
+ optional=True,
181
203
  ),
182
- ntasks = dict(
183
- info = ("The maximum number of allowed concurrent task for "
184
- "parallel execution."),
185
- default = 1,
186
- optional = True,
204
+ ntasks=dict(
205
+ info=(
206
+ "The maximum number of allowed concurrent task for "
207
+ "parallel execution."
208
+ ),
209
+ default=1,
210
+ optional=True,
187
211
  ),
188
- maxmemory = dict(
189
- info = "The maximum amount of usable memory (in GiB)",
190
- type = int,
191
- optional = True,
212
+ maxmemory=dict(
213
+ info="The maximum amount of usable memory (in GiB)",
214
+ type=int,
215
+ optional=True,
216
+ ),
217
+ parallel_const=dict(
218
+ info=(
219
+ "Constant that are used to predict execution time and "
220
+ + "memory consumption for a given ODB database."
221
+ ),
222
+ type=footprints.FPDict,
223
+ optional=True,
192
224
  ),
193
- parallel_const = dict(
194
- info = ("Constant that are used to predict execution time and " +
195
- "memory consumption for a given ODB database."),
196
- type = footprints.FPDict,
197
- optional = True,
198
- )
199
225
  )
200
226
  )
201
227
 
202
- _donot_link_roles = ['Observations', 'Obsmap',
203
- 'IOPoll', 'LFIScripts', 'LFITOOLS',
204
- 'Binary', 'Bator', 'Batodb']
228
+ _donot_link_roles = [
229
+ "Observations",
230
+ "Obsmap",
231
+ "IOPoll",
232
+ "LFIScripts",
233
+ "LFITOOLS",
234
+ "Binary",
235
+ "Bator",
236
+ "Batodb",
237
+ ]
205
238
 
206
239
  def __init__(self, *kargs, **kwargs):
207
240
  super().__init__(*kargs, **kwargs)
@@ -215,16 +248,25 @@ class Raw2ODBparallel(ParaBlindRun, odb.OdbComponentDecoMixin, drhook.DrHookDeco
215
248
  """Return the maximum amount of usable memory (in MiB)."""
216
249
  if self._effective_maxmem is None:
217
250
  if self.maxmemory:
218
- self._effective_maxmem = self.maxmemory * 1024 # maxmemory in GB
251
+ self._effective_maxmem = (
252
+ self.maxmemory * 1024
253
+ ) # maxmemory in GB
219
254
  else:
220
- sys_maxmem = self.system.memory_info.system_RAM('MiB')
255
+ sys_maxmem = self.system.memory_info.system_RAM("MiB")
221
256
  # System memory minus 20% or minus 4GB
222
- self._effective_maxmem = max(sys_maxmem * .8, sys_maxmem - 4 * 1024)
257
+ self._effective_maxmem = max(
258
+ sys_maxmem * 0.8, sys_maxmem - 4 * 1024
259
+ )
223
260
  return self._effective_maxmem
224
261
 
225
262
  def input_obs(self):
226
263
  """Find out which are the usable observations."""
227
- obsall = [x for x in self.context.sequence.effective_inputs(kind='observations')]
264
+ obsall = [
265
+ x
266
+ for x in self.context.sequence.effective_inputs(
267
+ kind="observations"
268
+ )
269
+ ]
228
270
  obsall.sort(key=lambda s: s.rh.resource.part)
229
271
 
230
272
  # Looking for valid raw observations
@@ -232,24 +274,34 @@ class Raw2ODBparallel(ParaBlindRun, odb.OdbComponentDecoMixin, drhook.DrHookDeco
232
274
  obsok = list()
233
275
  for secobs in obsall:
234
276
  rhobs = secobs.rh
235
- if rhobs.resource.nativefmt == 'odb':
236
- logger.warning('Observations set [%s] is ODB ready',
237
- rhobs.resource.part)
277
+ if rhobs.resource.nativefmt == "odb":
278
+ logger.warning(
279
+ "Observations set [%s] is ODB ready", rhobs.resource.part
280
+ )
238
281
  continue
239
282
  if rhobs.container.totalsize < sizemin:
240
- logger.warning('Observations set [%s] is far too small: %d',
241
- rhobs.resource.part, rhobs.container.totalsize)
283
+ logger.warning(
284
+ "Observations set [%s] is far too small: %d",
285
+ rhobs.resource.part,
286
+ rhobs.container.totalsize,
287
+ )
242
288
  else:
243
- logger.info('Observations set [%s] has size: %d',
244
- rhobs.resource.part, int(rhobs.container.totalsize))
289
+ logger.info(
290
+ "Observations set [%s] has size: %d",
291
+ rhobs.resource.part,
292
+ int(rhobs.container.totalsize),
293
+ )
245
294
  obsok.append(Foo(rh=rhobs, refdata=list(), mapped=False))
246
295
 
247
296
  # Check the observations dates
248
297
  for obs in [obs for obs in obsok if obs.rh.resource.date != self.date]:
249
- logger.warning('Observation [%s] %s [time mismatch: %s / %s]',
250
- 'discarded' if self.ontime else 'is questionable',
251
- obs.rh.resource.part, obs.rh.resource.date.isoformat(),
252
- self.date.isoformat())
298
+ logger.warning(
299
+ "Observation [%s] %s [time mismatch: %s / %s]",
300
+ "discarded" if self.ontime else "is questionable",
301
+ obs.rh.resource.part,
302
+ obs.rh.resource.date.isoformat(),
303
+ self.date.isoformat(),
304
+ )
253
305
  if self.ontime:
254
306
  obsok = [obs for obs in obsok if obs.rh.resource.date == self.date]
255
307
 
@@ -258,24 +310,34 @@ class Raw2ODBparallel(ParaBlindRun, odb.OdbComponentDecoMixin, drhook.DrHookDeco
258
310
  def _retrieve_refdatainfo(self, obslist):
259
311
  """Look for refdata resources and link their content with the obslist."""
260
312
  refmap = dict()
261
- refall = list(self.context.sequence.effective_inputs(kind='refdata'))
313
+ refall = list(self.context.sequence.effective_inputs(kind="refdata"))
262
314
  for rdata in refall:
263
- logger.info('Inspect refdata ' + rdata.rh.container.localpath())
315
+ logger.info("Inspect refdata " + rdata.rh.container.localpath())
264
316
  self.system.subtitle(rdata.role)
265
317
  rdata.rh.container.cat()
266
318
  for item in rdata.rh.contents:
267
- refmap[(item.fmt.lower(), item.data, item.instr)] = (rdata.rh, item)
319
+ refmap[(item.fmt.lower(), item.data, item.instr)] = (
320
+ rdata.rh,
321
+ item,
322
+ )
268
323
 
269
324
  # Build actual refdata
270
325
  for obs in obslist:
271
326
  thispart = obs.rh.resource.part
272
327
  thisfmt = obs.rh.container.actualfmt.lower()
273
- logger.info(' '.join(('Building information for [', thisfmt, '/', thispart, ']')))
328
+ logger.info(
329
+ " ".join(
330
+ ("Building information for [", thisfmt, "/", thispart, "]")
331
+ )
332
+ )
274
333
 
275
334
  # Gather equivalent refdata lines
276
- if not self.system.path.exists('norefdata.' + thispart) and (
277
- not self.env.VORTEX_OBSDB_NOREF or
278
- not re.search(thispart, self.env.VORTEX_OBSDB_NOREF, re.IGNORECASE)):
335
+ if not self.system.path.exists("norefdata." + thispart) and (
336
+ not self.env.VORTEX_OBSDB_NOREF
337
+ or not re.search(
338
+ thispart, self.env.VORTEX_OBSDB_NOREF, re.IGNORECASE
339
+ )
340
+ ):
279
341
  for k, v in refmap.items():
280
342
  x_fmt, x_data = k[:2]
281
343
  if x_fmt == thisfmt and x_data == thispart:
@@ -290,18 +352,26 @@ class Raw2ODBparallel(ParaBlindRun, odb.OdbComponentDecoMixin, drhook.DrHookDeco
290
352
  rdata, item = refmap[thiskey]
291
353
  thismap.refdata.append(rdata.contents.formatted_data(item))
292
354
  else:
293
- logger.warning('Creating automatic refdata entry for ' + str(thiskey))
294
- item = ObsRefItem(imap.data, imap.fmt, imap.instr, self.date.ymd, self.date.hh)
355
+ logger.warning(
356
+ "Creating automatic refdata entry for " + str(thiskey)
357
+ )
358
+ item = ObsRefItem(
359
+ imap.data, imap.fmt, imap.instr, self.date.ymd, self.date.hh
360
+ )
295
361
  if refall:
296
- thismap.refdata.append(refall[0].rh.contents.formatted_data(item))
362
+ thismap.refdata.append(
363
+ refall[0].rh.contents.formatted_data(item)
364
+ )
297
365
  else:
298
- logger.error('No default for formatting data %s', item)
366
+ logger.error("No default for formatting data %s", item)
299
367
  thismap.refdata.append(ObsRefContent.formatted_data(item))
300
368
 
301
369
  @staticmethod
302
370
  def _new_obspack_item():
303
371
  """Create a now entry in obspack."""
304
- return Foo(mapping=list(), standalone=False, refdata=list(), obsfile=dict())
372
+ return Foo(
373
+ mapping=list(), standalone=False, refdata=list(), obsfile=dict()
374
+ )
305
375
 
306
376
  def prepare(self, rh, opts):
307
377
  """Get a look at raw observations input files."""
@@ -310,22 +380,30 @@ class Raw2ODBparallel(ParaBlindRun, odb.OdbComponentDecoMixin, drhook.DrHookDeco
310
380
  cycle = rh.resource.cycle
311
381
 
312
382
  # First create the proper IO assign table for any of the resulting ECMA databases
313
- self.odb_create_db('ECMA', 'odb_db_template')
314
- self.env.IOASSIGN = sh.path.abspath(sh.path.join('odb_db_template', 'IOASSIGN'))
383
+ self.odb_create_db("ECMA", "odb_db_template")
384
+ self.env.IOASSIGN = sh.path.abspath(
385
+ sh.path.join("odb_db_template", "IOASSIGN")
386
+ )
315
387
 
316
388
  # Looking for input observations
317
389
  obsok = self.input_obs()
318
390
 
319
391
  # Building refdata map for direct access to (fmt, data, instr) entries
320
- if cycle < 'cy42_op1':
392
+ if cycle < "cy42_op1":
321
393
  # Refdata information is not needed anymore with cy42_op1
322
394
  refmap, refall = self._retrieve_refdatainfo(obsok)
323
395
 
324
396
  # Looking for obs maps
325
397
  mapitems = list()
326
- for omsec in self.context.sequence.effective_inputs(kind='obsmap'):
327
- logger.info(' '.join(('Gathering information from map',
328
- omsec.rh.container.localpath())))
398
+ for omsec in self.context.sequence.effective_inputs(kind="obsmap"):
399
+ logger.info(
400
+ " ".join(
401
+ (
402
+ "Gathering information from map",
403
+ omsec.rh.container.localpath(),
404
+ )
405
+ )
406
+ )
329
407
  sh.subtitle(omsec.role)
330
408
  omsec.rh.container.cat()
331
409
  mapitems.extend(omsec.rh.contents)
@@ -333,12 +411,21 @@ class Raw2ODBparallel(ParaBlindRun, odb.OdbComponentDecoMixin, drhook.DrHookDeco
333
411
  self.obspack = defaultdict(self._new_obspack_item) # Reset the obspack
334
412
  for imap in mapitems:
335
413
  # Match observation files and obsmap entries + Various checks
336
- logger.info('Inspect ' + str(imap))
337
- candidates = [obs for obs in obsok
338
- if (obs.rh.resource.part == imap.data and
339
- obs.rh.container.actualfmt.lower() == imap.fmt.lower())]
414
+ logger.info("Inspect " + str(imap))
415
+ candidates = [
416
+ obs
417
+ for obs in obsok
418
+ if (
419
+ obs.rh.resource.part == imap.data
420
+ and obs.rh.container.actualfmt.lower() == imap.fmt.lower()
421
+ )
422
+ ]
340
423
  if not candidates:
341
- errmsg = 'No input obsfile could match [data:{:s}/fmt:{:s}]'.format(imap.data, imap.fmt)
424
+ errmsg = (
425
+ "No input obsfile could match [data:{:s}/fmt:{:s}]".format(
426
+ imap.data, imap.fmt
427
+ )
428
+ )
342
429
  if self.mapall:
343
430
  raise ValueError(errmsg)
344
431
  else:
@@ -348,38 +435,44 @@ class Raw2ODBparallel(ParaBlindRun, odb.OdbComponentDecoMixin, drhook.DrHookDeco
348
435
  # Build the obspack entry
349
436
  thismap = self.obspack[imap.odb]
350
437
  thismap.mapping.append(imap)
351
- thismap.obsfile[imap.fmt.upper() + '.' + imap.data] = candidates[-1]
438
+ thismap.obsfile[imap.fmt.upper() + "." + imap.data] = candidates[
439
+ -1
440
+ ]
352
441
  # Map refdata and obsmap entries
353
- if cycle < 'cy42_op1':
442
+ if cycle < "cy42_op1":
354
443
  # Refdata information is not needed anymore with cy42_op1
355
444
  self._map_refdatainfo(refmap, refall, imap, thismap)
356
445
 
357
446
  # Deal with observations that are not described in the obsmap
358
447
  for notmap in [obs for obs in obsok if not obs.mapped]:
359
448
  thispart = notmap.rh.resource.part
360
- logger.info('Inspect not mapped obs ' + thispart)
449
+ logger.info("Inspect not mapped obs " + thispart)
361
450
  if thispart not in self.obspack:
362
451
  thisfmt = notmap.rh.container.actualfmt.upper()
363
- thismsg = 'standalone obs entry [data:{:s} / fmt:{:s}]'.format(thispart, thisfmt)
452
+ thismsg = "standalone obs entry [data:{:s} / fmt:{:s}]".format(
453
+ thispart, thisfmt
454
+ )
364
455
  if self.maponly:
365
- logger.warning('Ignore ' + thismsg)
456
+ logger.warning("Ignore " + thismsg)
366
457
  else:
367
- logger.warning('Active ' + thismsg)
458
+ logger.warning("Active " + thismsg)
368
459
  thismap = self.obspack[thispart]
369
460
  thismap.standalone = thisfmt
370
- thismap.mapping.append(ObsMapItem(thispart, thispart, thisfmt, thispart))
461
+ thismap.mapping.append(
462
+ ObsMapItem(thispart, thispart, thisfmt, thispart)
463
+ )
371
464
  thismap.refdata = notmap.refdata
372
- thismap.obsfile[thisfmt.upper() + '.' + thispart] = notmap
465
+ thismap.obsfile[thisfmt.upper() + "." + thispart] = notmap
373
466
 
374
467
  # Informations about timeslots
375
468
  logger.info("The timeslot definition is: %s", str(self.slots))
376
- if cycle < 'cy42_op1':
469
+ if cycle < "cy42_op1":
377
470
  # ficdate is not needed anymore with cy42_op1...
378
- self.slots.as_file(self.date, 'ficdate')
471
+ self.slots.as_file(self.date, "ficdate")
379
472
  else:
380
473
  # From cy42_op1 onward, we only need environment variables
381
474
  for var, value in self.slots.as_environment().items():
382
- logger.info('Setting env %s = %s', var, str(value))
475
+ logger.info("Setting env %s = %s", var, str(value))
383
476
  self.env[var] = value
384
477
 
385
478
  # Let ancestors handling most of the env setting
@@ -388,37 +481,48 @@ class Raw2ODBparallel(ParaBlindRun, odb.OdbComponentDecoMixin, drhook.DrHookDeco
388
481
  BATOR_NBPOOL=self.npool,
389
482
  BATODB_NBPOOL=self.npool,
390
483
  BATOR_NBSLOT=self.slots.nslot,
391
- BATODB_NBSLOT=self.slots.nslot,)
484
+ BATODB_NBSLOT=self.slots.nslot,
485
+ )
392
486
  self.env.default(
393
487
  TIME_INIT_YYYYMMDD=self.date.ymd,
394
- TIME_INIT_HHMMSS=self.date.hm + '00',)
488
+ TIME_INIT_HHMMSS=self.date.hm + "00",
489
+ )
395
490
  if self.lamflag:
396
- for lamvar in ('BATOR_LAMFLAG', 'BATODB_LAMFLAG'):
397
- logger.info('Setting env %s = %d', lamvar, 1)
491
+ for lamvar in ("BATOR_LAMFLAG", "BATODB_LAMFLAG"):
492
+ logger.info("Setting env %s = %d", lamvar, 1)
398
493
  self.env[lamvar] = 1
399
494
 
400
495
  if self.member is not None:
401
- for nam in self.context.sequence.effective_inputs(kind=('namelist', 'namelistfp')):
402
- nam.rh.contents.setmacro('MEMBER', self.member)
403
- logger.info('Setup macro MEMBER=%s in %s', self.member, nam.rh.container.actualpath())
496
+ for nam in self.context.sequence.effective_inputs(
497
+ kind=("namelist", "namelistfp")
498
+ ):
499
+ nam.rh.contents.setmacro("MEMBER", self.member)
500
+ logger.info(
501
+ "Setup macro MEMBER=%s in %s",
502
+ self.member,
503
+ nam.rh.container.actualpath(),
504
+ )
404
505
  if nam.rh.contents.dumps_needs_update:
405
506
  nam.rh.save()
406
507
 
407
508
  def spawn_command_options(self):
408
509
  """Any data useful to build the command line."""
409
510
  opts_dict = super().spawn_command_options()
410
- opts_dict['dataid'] = self.dataid
411
- opts_dict['date'] = self.date
511
+ opts_dict["dataid"] = self.dataid
512
+ opts_dict["date"] = self.date
412
513
  return opts_dict
413
514
 
414
515
  def _default_pre_execute(self, rh, opts):
415
516
  """Change default initialisation to use LongerFirstScheduler"""
416
517
  # Start the task scheduler
417
- self._boss = Boss(verbose=self.verbose,
418
- scheduler=footprints.proxy.scheduler(limit='threads+memory',
419
- max_threads=self.ntasks,
420
- max_memory=self.effective_maxmem,
421
- ))
518
+ self._boss = Boss(
519
+ verbose=self.verbose,
520
+ scheduler=footprints.proxy.scheduler(
521
+ limit="threads+memory",
522
+ max_threads=self.ntasks,
523
+ max_memory=self.effective_maxmem,
524
+ ),
525
+ )
422
526
  self._boss.make_them_work()
423
527
 
424
528
  def execute(self, rh, opts):
@@ -430,10 +534,15 @@ class Raw2ODBparallel(ParaBlindRun, odb.OdbComponentDecoMixin, drhook.DrHookDeco
430
534
  sh = self.system
431
535
  cycle = rh.resource.cycle
432
536
 
433
- batnam = [x.rh for x in self.context.sequence.effective_inputs(role='NamelistBatodb')]
537
+ batnam = [
538
+ x.rh
539
+ for x in self.context.sequence.effective_inputs(
540
+ role="NamelistBatodb"
541
+ )
542
+ ]
434
543
  # Give a glance to the actual namelist
435
544
  if batnam:
436
- sh.subtitle('Namelist Raw2ODB')
545
+ sh.subtitle("Namelist Raw2ODB")
437
546
  batnam[0].container.cat()
438
547
 
439
548
  self.obsmapout = list() # Reset the obsmapout
@@ -442,167 +551,277 @@ class Raw2ODBparallel(ParaBlindRun, odb.OdbComponentDecoMixin, drhook.DrHookDeco
442
551
  workdir = sh.pwd()
443
552
 
444
553
  for odbset, thispack in self.obspack.items():
445
- odbname = self.virtualdb.upper() + '.' + odbset
446
- sh.title('Cocooning ODB set: ' + odbname)
447
- with sh.cdcontext('wkdir_' + odbset, create=True):
448
-
449
- for inpt in [s for s in self.context.sequence.inputs() if s.stage == 'get']:
554
+ odbname = self.virtualdb.upper() + "." + odbset
555
+ sh.title("Cocooning ODB set: " + odbname)
556
+ with sh.cdcontext("wkdir_" + odbset, create=True):
557
+ for inpt in [
558
+ s
559
+ for s in self.context.sequence.inputs()
560
+ if s.stage == "get"
561
+ ]:
450
562
  if inpt.role not in self._donot_link_roles:
451
- logger.info('creating softlink: %s -> %s', inpt.rh.container.localpath(),
452
- sh.path.join(workdir, inpt.rh.container.localpath()))
453
- sh.softlink(sh.path.join(workdir, inpt.rh.container.localpath()),
454
- inpt.rh.container.localpath())
455
-
456
- if cycle < 'cy42_op1':
563
+ logger.info(
564
+ "creating softlink: %s -> %s",
565
+ inpt.rh.container.localpath(),
566
+ sh.path.join(
567
+ workdir, inpt.rh.container.localpath()
568
+ ),
569
+ )
570
+ sh.softlink(
571
+ sh.path.join(
572
+ workdir, inpt.rh.container.localpath()
573
+ ),
574
+ inpt.rh.container.localpath(),
575
+ )
576
+
577
+ if cycle < "cy42_op1":
457
578
  # Special stuff for cy < 42
458
- logger.info('creating softlink for ficdate.')
459
- sh.softlink(sh.path.join(workdir, 'ficdate'), 'ficdate')
579
+ logger.info("creating softlink for ficdate.")
580
+ sh.softlink(sh.path.join(workdir, "ficdate"), "ficdate")
460
581
 
461
582
  odb_input_size = 0
462
583
  for obsname, obsinfo in thispack.obsfile.items():
463
- logger.info('creating softlink: %s -> %s', obsname,
464
- sh.path.join(workdir, obsinfo.rh.container.localpath()))
465
- sh.softlink(sh.path.join(workdir, obsinfo.rh.container.localpath()),
466
- obsname)
467
- if thispack.standalone and cycle < 'cy42_op1':
468
- logger.info('creating softlink: %s -> %s', thispack.standalone,
469
- sh.path.join(workdir, obsinfo.rh.container.localpath()))
470
- sh.softlink(sh.path.join(workdir, obsinfo.rh.container.localpath()),
471
- thispack.standalone)
584
+ logger.info(
585
+ "creating softlink: %s -> %s",
586
+ obsname,
587
+ sh.path.join(
588
+ workdir, obsinfo.rh.container.localpath()
589
+ ),
590
+ )
591
+ sh.softlink(
592
+ sh.path.join(
593
+ workdir, obsinfo.rh.container.localpath()
594
+ ),
595
+ obsname,
596
+ )
597
+ if thispack.standalone and cycle < "cy42_op1":
598
+ logger.info(
599
+ "creating softlink: %s -> %s",
600
+ thispack.standalone,
601
+ sh.path.join(
602
+ workdir, obsinfo.rh.container.localpath()
603
+ ),
604
+ )
605
+ sh.softlink(
606
+ sh.path.join(
607
+ workdir, obsinfo.rh.container.localpath()
608
+ ),
609
+ thispack.standalone,
610
+ )
472
611
 
473
612
  odb_input_size += obsinfo.rh.container.totalsize
474
613
 
475
614
  # Fill the actual refdata according to information gathered in prepare stage
476
- if cycle < 'cy42_op1':
615
+ if cycle < "cy42_op1":
477
616
  if thispack.refdata:
478
- with open('refdata', 'w') as fd:
617
+ with open("refdata", "w") as fd:
479
618
  for rdentry in thispack.refdata:
480
619
  fd.write(str(rdentry + "\n"))
481
- sh.subtitle('Local refdata for: {:s}'.format(odbname))
482
- sh.cat('refdata', output=False)
620
+ sh.subtitle("Local refdata for: {:s}".format(odbname))
621
+ sh.cat("refdata", output=False)
483
622
  # Drive bator with a batormap file (from cy42_op1 onward)
484
623
  else:
485
- with open('batormap', 'w') as fd:
624
+ with open("batormap", "w") as fd:
486
625
  for mapentry in sorted(thispack.mapping):
487
- fd.write(str(ObsMapContent.formatted_data(mapentry) + '\n'))
488
- sh.subtitle('Local batormap for: {:s}'.format(odbname))
489
- sh.cat('batormap', output=False)
626
+ fd.write(
627
+ str(
628
+ ObsMapContent.formatted_data(mapentry)
629
+ + "\n"
630
+ )
631
+ )
632
+ sh.subtitle("Local batormap for: {:s}".format(odbname))
633
+ sh.cat("batormap", output=False)
490
634
 
491
635
  self.obsmapout.extend(thispack.mapping)
492
636
 
493
637
  # Compute the expected memory and time
494
638
  if isinstance(self.parallel_const, dict):
495
- pconst = self.parallel_const.get(odbset,
496
- self.parallel_const.get('default', (999999., 1.)))
497
- offsets = self.parallel_const.get('offset', (0., 0.)) # In MiB for the memory
639
+ pconst = self.parallel_const.get(
640
+ odbset,
641
+ self.parallel_const.get("default", (999999.0, 1.0)),
642
+ )
643
+ offsets = self.parallel_const.get(
644
+ "offset", (0.0, 0.0)
645
+ ) # In MiB for the memory
498
646
  else:
499
- pconst = (999999., 1.)
500
- offsets = (0., 0.)
647
+ pconst = (999999.0, 1.0)
648
+ offsets = (0.0, 0.0)
501
649
  bTime = (odb_input_size * pconst[1] / 1048576) + offsets[1]
502
- bMemory = odb_input_size * pconst[0] + (offsets[0] * 1024 * 1024)
503
- bMemory = bMemory / 1024. / 1024.
650
+ bMemory = odb_input_size * pconst[0] + (
651
+ offsets[0] * 1024 * 1024
652
+ )
653
+ bMemory = bMemory / 1024.0 / 1024.0
504
654
  if bMemory > self.effective_maxmem:
505
- logger.info("For %s, the computed memory needs exceed the node limit.", odbset)
506
- logger.info("Memory requirement reseted to %d (originally %d.)",
507
- int(self.effective_maxmem), int(bMemory))
655
+ logger.info(
656
+ "For %s, the computed memory needs exceed the node limit.",
657
+ odbset,
658
+ )
659
+ logger.info(
660
+ "Memory requirement reseted to %d (originally %d.)",
661
+ int(self.effective_maxmem),
662
+ int(bMemory),
663
+ )
508
664
  bMemory = self.effective_maxmem
509
- scheduler_instructions['name'].append('ODB_database_{:s}'.format(odbset))
510
- scheduler_instructions['base'].append(odbset)
511
- scheduler_instructions['memory'].append(bMemory)
512
- scheduler_instructions['expected_time'].append(bTime)
513
- scheduler_instructions['inputsize'].append(odb_input_size)
665
+ scheduler_instructions["name"].append(
666
+ "ODB_database_{:s}".format(odbset)
667
+ )
668
+ scheduler_instructions["base"].append(odbset)
669
+ scheduler_instructions["memory"].append(bMemory)
670
+ scheduler_instructions["expected_time"].append(bTime)
671
+ scheduler_instructions["inputsize"].append(odb_input_size)
514
672
 
515
- sh.title('Launching Bator using taylorism...')
673
+ sh.title("Launching Bator using taylorism...")
516
674
  self._default_pre_execute(rh, opts)
517
675
  common_i = self._default_common_instructions(rh, opts)
518
676
  # Update the common instructions
519
- common_i.update(dict(workdir=workdir, cycle=cycle, ))
677
+ common_i.update(
678
+ dict(
679
+ workdir=workdir,
680
+ cycle=cycle,
681
+ )
682
+ )
520
683
 
521
684
  self._add_instructions(common_i, scheduler_instructions)
522
685
 
523
686
  post_opts = copy.copy(opts)
524
- post_opts['synthesis'] = self.para_synthesis
687
+ post_opts["synthesis"] = self.para_synthesis
525
688
  self._default_post_execute(rh, post_opts)
526
689
 
527
690
  def _default_rc_action(self, rh, opts, report, rc):
528
691
  super()._default_rc_action(rh, opts, report, rc)
529
- my_report = report['report'].get('synthesis', None)
692
+ my_report = report["report"].get("synthesis", None)
530
693
  if my_report:
531
- opts['synthesis'][my_report.pop('base')] = my_report
694
+ opts["synthesis"][my_report.pop("base")] = my_report
532
695
 
533
696
  def postfix(self, rh, opts):
534
697
  """Post conversion cleaning."""
535
698
  sh = self.system
536
699
 
537
700
  # Remove empty ECMA databases from the output obsmap
538
- self.obsmapout = [x for x in self.obsmapout
539
- if (sh.path.isdir('ECMA.' + x.odb) and
540
- sh.path.isdir('ECMA.' + x.odb + '/1'))]
701
+ self.obsmapout = [
702
+ x
703
+ for x in self.obsmapout
704
+ if (
705
+ sh.path.isdir("ECMA." + x.odb)
706
+ and sh.path.isdir("ECMA." + x.odb + "/1")
707
+ )
708
+ ]
541
709
 
542
710
  # At least one non-empty database is needed...
543
- self.algoassert(self.obsmapout, "At least one non-empty ODB database is expected")
711
+ self.algoassert(
712
+ self.obsmapout, "At least one non-empty ODB database is expected"
713
+ )
544
714
 
545
715
  # Generate the output bator_map
546
- with open('batodb_map.out', 'w') as fd:
716
+ with open("batodb_map.out", "w") as fd:
547
717
  for x in sorted(self.obsmapout):
548
- fd.write(str(ObsMapContent.formatted_data(x) + '\n'))
718
+ fd.write(str(ObsMapContent.formatted_data(x) + "\n"))
549
719
 
550
720
  # Generate a global refdata (if cycle allows it and if possible)
551
- if rh.resource.cycle < 'cy42_op1':
552
- rdrh_dict = {y.rh.resource.part: y.rh
553
- for y in self.context.sequence.effective_inputs(kind='refdata')
554
- if y.rh.resource.part != 'all'}
555
- with open('refdata_global', 'w') as rdg:
721
+ if rh.resource.cycle < "cy42_op1":
722
+ rdrh_dict = {
723
+ y.rh.resource.part: y.rh
724
+ for y in self.context.sequence.effective_inputs(kind="refdata")
725
+ if y.rh.resource.part != "all"
726
+ }
727
+ with open("refdata_global", "w") as rdg:
556
728
  for x in sorted(self.obsmapout):
557
- if (x.data in rdrh_dict and
558
- sh.path.getsize(rdrh_dict[x.data].container.localpath()) > 0):
559
- with open(rdrh_dict[x.data].container.localpath()) as rdl:
729
+ if (
730
+ x.data in rdrh_dict
731
+ and sh.path.getsize(
732
+ rdrh_dict[x.data].container.localpath()
733
+ )
734
+ > 0
735
+ ):
736
+ with open(
737
+ rdrh_dict[x.data].container.localpath()
738
+ ) as rdl:
560
739
  rdg.write(rdl.readline())
561
- elif (sh.path.exists('refdata.' + x.data) and
562
- sh.path.getsize('refdata.' + x.data) > 0):
563
- with open('refdata.' + x.data) as rdl:
740
+ elif (
741
+ sh.path.exists("refdata." + x.data)
742
+ and sh.path.getsize("refdata." + x.data) > 0
743
+ ):
744
+ with open("refdata." + x.data) as rdl:
564
745
  rdg.write(rdl.readline())
565
746
  else:
566
- logger.info("Unable to create a global refdata entry for data=" + x.data)
747
+ logger.info(
748
+ "Unable to create a global refdata entry for data="
749
+ + x.data
750
+ )
567
751
 
568
- sh.json_dump(self.para_synthesis, 'parallel_exec_synthesis.json')
752
+ sh.json_dump(self.para_synthesis, "parallel_exec_synthesis.json")
569
753
 
570
754
  # Print the parallel execution summary
571
- sh.subtitle('Here is the parallel execution synthesis: memory aspects')
572
- header = 'Database InputSize(MiB) PredMem(GiB) RealMem(GiB) Real/Pred Ratio'
573
- rfmt = '{:8s} {:>15.0f} {:>12.1f} {:>12.1f} {:>15.2f}'
755
+ sh.subtitle("Here is the parallel execution synthesis: memory aspects")
756
+ header = "Database InputSize(MiB) PredMem(GiB) RealMem(GiB) Real/Pred Ratio"
757
+ rfmt = "{:8s} {:>15.0f} {:>12.1f} {:>12.1f} {:>15.2f}"
574
758
  print(header)
575
759
  for row in sorted(self.para_synthesis.keys()):
576
760
  srep = self.para_synthesis[row]
577
- print(rfmt.format(row,
578
- convert_bytes_in_unit(srep['inputsize'], 'MiB'),
579
- convert_bytes_in_unit(srep['mem_expected'], 'GiB'),
580
- (99.99 if srep['mem_real'] is None else
581
- convert_bytes_in_unit(srep['mem_real'], 'GiB')),
582
- (99.99 if srep['mem_ratio'] is None else
583
- srep['mem_ratio'])))
584
-
585
- sh.subtitle('Here is the parallel execution synthesis: elapsed time aspects')
586
- header = 'Database InputSize(MiB) PredTime(s) RealTime(s) Real/Pred Ratio'
587
- rfmt = '{:8s} {:>15.0f} {:>11.1f} {:>11.1f} {:>15.2f}'
761
+ print(
762
+ rfmt.format(
763
+ row,
764
+ convert_bytes_in_unit(srep["inputsize"], "MiB"),
765
+ convert_bytes_in_unit(srep["mem_expected"], "GiB"),
766
+ (
767
+ 99.99
768
+ if srep["mem_real"] is None
769
+ else convert_bytes_in_unit(srep["mem_real"], "GiB")
770
+ ),
771
+ (
772
+ 99.99
773
+ if srep["mem_ratio"] is None
774
+ else srep["mem_ratio"]
775
+ ),
776
+ )
777
+ )
778
+
779
+ sh.subtitle(
780
+ "Here is the parallel execution synthesis: elapsed time aspects"
781
+ )
782
+ header = (
783
+ "Database InputSize(MiB) PredTime(s) RealTime(s) Real/Pred Ratio"
784
+ )
785
+ rfmt = "{:8s} {:>15.0f} {:>11.1f} {:>11.1f} {:>15.2f}"
588
786
  print(header)
589
787
  for row in sorted(self.para_synthesis.keys()):
590
788
  srep = self.para_synthesis[row]
591
- print(rfmt.format(row,
592
- convert_bytes_in_unit(srep['inputsize'], 'MiB'),
593
- srep['time_expected'], srep['time_real'],
594
- (99.99 if srep['time_ratio'] is None else srep['time_ratio'])))
595
-
596
- sh.subtitle('Here is the parallel execution synthesis: timeline')
597
- header = 'Database StartTime(UTC) PredMem(GiB) RealTime(s) ExecSlot'
598
- rfmt = '{:8s} {:>40s} {:>11.1f} {:>12.1f} {:>8s}'
789
+ print(
790
+ rfmt.format(
791
+ row,
792
+ convert_bytes_in_unit(srep["inputsize"], "MiB"),
793
+ srep["time_expected"],
794
+ srep["time_real"],
795
+ (
796
+ 99.99
797
+ if srep["time_ratio"] is None
798
+ else srep["time_ratio"]
799
+ ),
800
+ )
801
+ )
802
+
803
+ sh.subtitle("Here is the parallel execution synthesis: timeline")
804
+ header = "Database StartTime(UTC) PredMem(GiB) RealTime(s) ExecSlot"
805
+ rfmt = "{:8s} {:>40s} {:>11.1f} {:>12.1f} {:>8s}"
599
806
  print(header)
600
- for (row, srep) in sorted(self.para_synthesis.items(), key=lambda x: x[1]['time_start']):
601
- print(rfmt.format(row, srep['time_start'],
602
- convert_bytes_in_unit(srep['mem_expected'], 'GiB'),
603
- srep['time_real'], str(srep['sched_id'])))
807
+ for row, srep in sorted(
808
+ self.para_synthesis.items(), key=lambda x: x[1]["time_start"]
809
+ ):
810
+ print(
811
+ rfmt.format(
812
+ row,
813
+ srep["time_start"],
814
+ convert_bytes_in_unit(srep["mem_expected"], "GiB"),
815
+ srep["time_real"],
816
+ str(srep["sched_id"]),
817
+ )
818
+ )
604
819
 
605
- print('\nThe memory limit was set to: {:.1f} GiB'.format(self.effective_maxmem / 1024.))
820
+ print(
821
+ "\nThe memory limit was set to: {:.1f} GiB".format(
822
+ self.effective_maxmem / 1024.0
823
+ )
824
+ )
606
825
 
607
826
  super().postfix(rh, opts)
608
827
 
@@ -611,22 +830,22 @@ class OdbAverage(Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin):
611
830
  """TODO the father of this component is very much welcome."""
612
831
 
613
832
  _footprint = dict(
614
- attr = dict(
615
- kind = dict(
616
- values = ['average'],
833
+ attr=dict(
834
+ kind=dict(
835
+ values=["average"],
617
836
  ),
618
- binarysingle = dict(
619
- default = 'basicobsort',
837
+ binarysingle=dict(
838
+ default="basicobsort",
620
839
  ),
621
- ioassign = dict(),
622
- outdb = dict(
623
- optional = True,
624
- default = 'ccma',
625
- value = ['ecma', 'ccma'],
840
+ ioassign=dict(),
841
+ outdb=dict(
842
+ optional=True,
843
+ default="ccma",
844
+ value=["ecma", "ccma"],
626
845
  ),
627
- maskname = dict(
628
- optional = True,
629
- default = 'mask4x4.txt',
846
+ maskname=dict(
847
+ optional=True,
848
+ default="mask4x4.txt",
630
849
  ),
631
850
  )
632
851
  )
@@ -637,10 +856,14 @@ class OdbAverage(Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin):
637
856
  sh = self.system
638
857
 
639
858
  # Looking for input observations
640
- obsall = [x for x in self.lookupodb() if x.rh.resource.layout.lower() == 'ecma']
859
+ obsall = [
860
+ x
861
+ for x in self.lookupodb()
862
+ if x.rh.resource.layout.lower() == "ecma"
863
+ ]
641
864
  # One database at a time
642
865
  if len(obsall) != 1:
643
- raise ValueError('One and only one ECMA input should be here')
866
+ raise ValueError("One and only one ECMA input should be here")
644
867
  self.bingo = ecma = obsall[0]
645
868
 
646
869
  # First create a fake CCMA
@@ -654,10 +877,10 @@ class OdbAverage(Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin):
654
877
 
655
878
  self.odb.ioassign_gather(ecma_path, ccma_path)
656
879
 
657
- ecma_pool = sh.path.join(ecma_path, '1')
880
+ ecma_pool = sh.path.join(ecma_path, "1")
658
881
  if not sh.path.isdir(ecma_pool):
659
- logger.error('The input ECMA base is empty')
660
- self.abort('No ECMA input')
882
+ logger.error("The input ECMA base is empty")
883
+ self.abort("No ECMA input")
661
884
  return
662
885
 
663
886
  self.odb.create_poolmask(self.layout_new, ccma_path)
@@ -688,15 +911,18 @@ class OdbAverage(Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin):
688
911
 
689
912
  sh = self.system
690
913
 
691
- mask = [x.rh for x in self.context.sequence.effective_inputs(kind='atmsmask')]
914
+ mask = [
915
+ x.rh
916
+ for x in self.context.sequence.effective_inputs(kind="atmsmask")
917
+ ]
692
918
  if not mask:
693
- raise ValueError('Could not find any MASK input')
919
+ raise ValueError("Could not find any MASK input")
694
920
 
695
921
  # Have a look to mask file
696
922
  if mask[0].container.localpath() != self.maskname:
697
923
  sh.softlink(mask[0].container.localpath(), self.maskname)
698
924
 
699
- sh.subtitle('Mask')
925
+ sh.subtitle("Mask")
700
926
  mask[0].container.cat()
701
927
 
702
928
  # Standard execution
@@ -707,13 +933,19 @@ class OdbAverage(Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin):
707
933
  sh = self.system
708
934
 
709
935
  with sh.cdcontext(self.layout_new):
710
- for ccma in sh.glob('{:s}.*'.format(self.layout_new)):
711
- slurp = sh.cat(ccma, outsplit=False).replace(self.layout_new, self.layout_in)
712
- with open(ccma.replace(self.layout_new, self.layout_in), 'w') as fd:
936
+ for ccma in sh.glob("{:s}.*".format(self.layout_new)):
937
+ slurp = sh.cat(ccma, outsplit=False).replace(
938
+ self.layout_new, self.layout_in
939
+ )
940
+ with open(
941
+ ccma.replace(self.layout_new, self.layout_in), "w"
942
+ ) as fd:
713
943
  fd.write(str(slurp))
714
944
  sh.rm(ccma)
715
945
 
716
- sh.mv(self.layout_new, self.layout_in + '.' + self.bingo.rh.resource.part)
946
+ sh.mv(
947
+ self.layout_new, self.layout_in + "." + self.bingo.rh.resource.part
948
+ )
717
949
 
718
950
  super().postfix(rh, opts)
719
951
 
@@ -722,30 +954,36 @@ class OdbCompress(Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin):
722
954
  """Take a screening ODB ECMA database and create the compressed CCMA database."""
723
955
 
724
956
  _footprint = dict(
725
- attr = dict(
726
- kind = dict(
727
- values = ['odbcompress'],
957
+ attr=dict(
958
+ kind=dict(
959
+ values=["odbcompress"],
728
960
  ),
729
- ioassign = dict(),
961
+ ioassign=dict(),
730
962
  )
731
963
  )
732
964
 
733
965
  def prepare(self, rh, opts):
734
966
  """Find any ODB candidate in input files and fox ODB env accordingly."""
735
967
 
736
- obsall = [x for x in self.lookupodb() if x.rh.resource.layout.lower() == 'ecma']
968
+ obsall = [
969
+ x
970
+ for x in self.lookupodb()
971
+ if x.rh.resource.layout.lower() == "ecma"
972
+ ]
737
973
  if len(obsall) > 1:
738
- obsvirtual = [o for o in obsall if o.rh.resource.part == 'virtual']
974
+ obsvirtual = [o for o in obsall if o.rh.resource.part == "virtual"]
739
975
  if len(obsvirtual) != 1:
740
- raise ValueError('One and only one virtual database must be provided')
976
+ raise ValueError(
977
+ "One and only one virtual database must be provided"
978
+ )
741
979
  ecma = obsvirtual[0]
742
980
  elif len(obsall) == 1:
743
981
  ecma = obsall[0]
744
982
  else:
745
- raise ValueError('No ECMA database provided')
983
+ raise ValueError("No ECMA database provided")
746
984
 
747
985
  # First create a fake CCMA
748
- self.layout_new = 'ccma'
986
+ self.layout_new = "ccma"
749
987
  ccma_path = self.odb_create_db(self.layout_new)
750
988
  self.odb.fix_db_path(self.layout_new, ccma_path)
751
989
 
@@ -757,7 +995,7 @@ class OdbCompress(Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin):
757
995
 
758
996
  self.odb.create_poolmask(self.layout_new, ccma_path)
759
997
 
760
- self.odb_rw_or_overwrite_method(* obsall)
998
+ self.odb_rw_or_overwrite_method(*obsall)
761
999
 
762
1000
  # Let ancesters handling most of the env setting
763
1001
  super().prepare(rh, opts)
@@ -777,14 +1015,14 @@ class OdbMatchup(Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin):
777
1015
  """Report some information from post-minim CCMA to post-screening ECMA base."""
778
1016
 
779
1017
  _footprint = dict(
780
- attr = dict(
781
- kind = dict(
782
- values = ['matchup'],
1018
+ attr=dict(
1019
+ kind=dict(
1020
+ values=["matchup"],
783
1021
  ),
784
- fcmalayout = dict(
785
- optional = True,
786
- value = ['ecma', 'ccma', 'CCMA', 'ECMA'],
787
- remap = dict(CCMA ='ccma', ECMA = 'ecma'),
1022
+ fcmalayout=dict(
1023
+ optional=True,
1024
+ value=["ecma", "ccma", "CCMA", "ECMA"],
1025
+ remap=dict(CCMA="ccma", ECMA="ecma"),
788
1026
  ),
789
1027
  )
790
1028
  )
@@ -796,31 +1034,40 @@ class OdbMatchup(Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin):
796
1034
 
797
1035
  # Looking for input observations
798
1036
  obsscr_virtual = [
799
- x for x in self.lookupodb()
800
- if x.rh.resource.stage.startswith('screen') and x.rh.resource.part == 'virtual'
1037
+ x
1038
+ for x in self.lookupodb()
1039
+ if x.rh.resource.stage.startswith("screen")
1040
+ and x.rh.resource.part == "virtual"
801
1041
  ]
802
1042
  obsscr_parts = [
803
- x for x in self.lookupodb()
804
- if x.rh.resource.stage.startswith('screen') and x.rh.resource.part != 'virtual'
1043
+ x
1044
+ for x in self.lookupodb()
1045
+ if x.rh.resource.stage.startswith("screen")
1046
+ and x.rh.resource.part != "virtual"
805
1047
  ]
806
1048
  obscompressed = [
807
- x for x in self.lookupodb()
808
- if x.rh.resource.stage.startswith('min') or x.rh.resource.stage.startswith('traj')
1049
+ x
1050
+ for x in self.lookupodb()
1051
+ if x.rh.resource.stage.startswith("min")
1052
+ or x.rh.resource.stage.startswith("traj")
809
1053
  ]
810
1054
 
811
1055
  # One database at a time
812
1056
  if not obsscr_virtual:
813
- raise ValueError('Could not find any ODB screening input')
1057
+ raise ValueError("Could not find any ODB screening input")
814
1058
  if not obscompressed:
815
- raise ValueError('Could not find any ODB minim input')
1059
+ raise ValueError("Could not find any ODB minim input")
816
1060
 
817
1061
  # Set actual layout and path
818
1062
  ecma = obsscr_virtual.pop(0)
819
1063
  ccma = obscompressed.pop(0)
820
1064
  self.layout_screening = ecma.rh.resource.layout
821
1065
  self.layout_compressed = ccma.rh.resource.layout
822
- self.layout_fcma = (self.layout_compressed if self.fcmalayout is None
823
- else self.fcmalayout)
1066
+ self.layout_fcma = (
1067
+ self.layout_compressed
1068
+ if self.fcmalayout is None
1069
+ else self.fcmalayout
1070
+ )
824
1071
  ecma_path = sh.path.abspath(ecma.rh.container.localpath())
825
1072
  ccma_path = sh.path.abspath(ccma.rh.container.localpath())
826
1073
 
@@ -829,14 +1076,17 @@ class OdbMatchup(Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin):
829
1076
  self.odb.ioassign_gather(ccma_path, ecma_path)
830
1077
 
831
1078
  # Ok, but why ???
832
- sh.cp(sh.path.join(ecma_path, 'ECMA.dd'), sh.path.join(ccma_path, 'ECMA.dd'))
1079
+ sh.cp(
1080
+ sh.path.join(ecma_path, "ECMA.dd"),
1081
+ sh.path.join(ccma_path, "ECMA.dd"),
1082
+ )
833
1083
 
834
1084
  # Let ancesters handling most of the env setting
835
1085
  super().prepare(rh, opts)
836
1086
 
837
1087
  # Fix the input database intent
838
1088
  self.odb_rw_or_overwrite_method(ecma)
839
- self.odb_rw_or_overwrite_method(* obsscr_parts)
1089
+ self.odb_rw_or_overwrite_method(*obsscr_parts)
840
1090
 
841
1091
  def spawn_command_options(self):
842
1092
  """Prepare command line options to binary."""
@@ -850,45 +1100,45 @@ class OdbMatchup(Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin):
850
1100
  )
851
1101
 
852
1102
 
853
- class OdbReshuffle(Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin):
1103
+ class OdbReshuffle(
1104
+ Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin
1105
+ ):
854
1106
  """Take a bunch of ECMA databases and create new ones with an updated number of pools."""
855
1107
 
856
1108
  _footprint = dict(
857
- attr = dict(
858
- kind = dict(
859
- values = ['reshuffle'],
1109
+ attr=dict(
1110
+ kind=dict(
1111
+ values=["reshuffle"],
860
1112
  ),
861
1113
  )
862
1114
  )
863
1115
 
864
- _OUT_DIRECTORY = 'reshuffled'
865
- _BARE_OUT_LAYOUT = 'ccma'
1116
+ _OUT_DIRECTORY = "reshuffled"
1117
+ _BARE_OUT_LAYOUT = "ccma"
866
1118
 
867
1119
  def prepare(self, rh, opts):
868
1120
  """Find ODB candidates in input files."""
869
1121
 
870
1122
  # Looking for input observations
871
1123
  obs_in_virtual = [
872
- x for x in self.lookupodb()
873
- if x.rh.resource.part == 'virtual'
1124
+ x for x in self.lookupodb() if x.rh.resource.part == "virtual"
874
1125
  ]
875
1126
  if obs_in_virtual:
876
- raise ValueError('Do not input a Virtual database')
1127
+ raise ValueError("Do not input a Virtual database")
877
1128
  self.obs_in_parts = [
878
- x for x in self.lookupodb()
879
- if x.rh.resource.part != 'virtual'
1129
+ x for x in self.lookupodb() if x.rh.resource.part != "virtual"
880
1130
  ]
881
1131
 
882
1132
  # Find the input layout
883
1133
  in_layout = {x.rh.resource.layout for x in self.obs_in_parts}
884
1134
  if len(in_layout) != 1:
885
- raise ValueError('Incoherent layout in input databases or no input databases')
1135
+ raise ValueError(
1136
+ "Incoherent layout in input databases or no input databases"
1137
+ )
886
1138
  self.layout_in = in_layout.pop()
887
1139
 
888
1140
  # Some extra settings
889
- self.env.update(
890
- TO_ODB_FULL=1
891
- )
1141
+ self.env.update(TO_ODB_FULL=1)
892
1142
 
893
1143
  # prepare the ouputs' directory
894
1144
  self.system.mkdir(self._OUT_DIRECTORY)
@@ -899,12 +1149,17 @@ class OdbReshuffle(Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin):
899
1149
  """Loop on available databases."""
900
1150
  sh = self.system
901
1151
  for a_db in self.obs_in_parts:
902
- sh.subtitle('Dealing with {:s}'.format(a_db.rh.container.localpath()))
1152
+ sh.subtitle(
1153
+ "Dealing with {:s}".format(a_db.rh.container.localpath())
1154
+ )
903
1155
 
904
1156
  ecma_path = sh.path.abspath(a_db.rh.container.localpath())
905
- ccma_path = sh.path.abspath(sh.path.join(self._OUT_DIRECTORY,
906
- '.'.join([self.layout_in.upper(),
907
- a_db.rh.resource.part])))
1157
+ ccma_path = sh.path.abspath(
1158
+ sh.path.join(
1159
+ self._OUT_DIRECTORY,
1160
+ ".".join([self.layout_in.upper(), a_db.rh.resource.part]),
1161
+ )
1162
+ )
908
1163
  self.odb_create_db(self._BARE_OUT_LAYOUT, dbpath=ccma_path)
909
1164
  self.odb.fix_db_path(self.layout_in, ecma_path)
910
1165
  self.odb.fix_db_path(self._BARE_OUT_LAYOUT, ccma_path)
@@ -917,15 +1172,20 @@ class OdbReshuffle(Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin):
917
1172
  super().execute(rh, opts)
918
1173
 
919
1174
  # CCMA -> ECMA
920
- self.odb.change_layout(self._BARE_OUT_LAYOUT, self.layout_in, ccma_path)
1175
+ self.odb.change_layout(
1176
+ self._BARE_OUT_LAYOUT, self.layout_in, ccma_path
1177
+ )
921
1178
 
922
1179
  def postfix(self, rh, opts):
923
1180
  """Create a virtual database for output data."""
924
- self.system.subtitle('Creating the virtual database')
925
- virtual_db = self.odb_merge_if_needed(self.obs_in_parts,
926
- subdir=self._OUT_DIRECTORY)
927
- logger.info('The output virtual DB was created: %s',
928
- self.system.path.join(self._OUT_DIRECTORY, virtual_db))
1181
+ self.system.subtitle("Creating the virtual database")
1182
+ virtual_db = self.odb_merge_if_needed(
1183
+ self.obs_in_parts, subdir=self._OUT_DIRECTORY
1184
+ )
1185
+ logger.info(
1186
+ "The output virtual DB was created: %s",
1187
+ self.system.path.join(self._OUT_DIRECTORY, virtual_db),
1188
+ )
929
1189
 
930
1190
  def spawn_command_options(self):
931
1191
  """Prepare command line options to binary."""
@@ -936,14 +1196,16 @@ class OdbReshuffle(Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin):
936
1196
  )
937
1197
 
938
1198
 
939
- class FlagsCompute(Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin):
1199
+ class FlagsCompute(
1200
+ Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin
1201
+ ):
940
1202
  """Compute observations flags."""
941
1203
 
942
1204
  _footprint = dict(
943
- info = 'Computation of observations flags.',
944
- attr = dict(
945
- kind = dict(
946
- values = ['flagscomp'],
1205
+ info="Computation of observations flags.",
1206
+ attr=dict(
1207
+ kind=dict(
1208
+ values=["flagscomp"],
947
1209
  ),
948
1210
  ),
949
1211
  )
@@ -952,23 +1214,25 @@ class FlagsCompute(Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin):
952
1214
  """Spawn the binary for each of the input databases."""
953
1215
  # Look for the input databases
954
1216
  input_databases = self.context.sequence.effective_inputs(
955
- role='ECMA',
956
- kind='observations',
1217
+ role="ECMA",
1218
+ kind="observations",
957
1219
  )
958
1220
  # Check that there is at least one database
959
1221
  if len(input_databases) < 1:
960
- raise AttributeError('No database in input. Stop.')
1222
+ raise AttributeError("No database in input. Stop.")
961
1223
 
962
1224
  for input_database in input_databases:
963
1225
  ecma = input_database.rh
964
1226
  ecma_filename = ecma.container.filename
965
1227
  # Environment variable to set DB path
966
1228
  self.odb.fix_db_path(ecma.resource.layout, ecma.container.abspath)
967
- self.env.setvar('ODB_ECMA', ecma_filename)
968
- logger.info('Variable %s set to %s.', 'ODB_ECMA', ecma_filename)
1229
+ self.env.setvar("ODB_ECMA", ecma_filename)
1230
+ logger.info("Variable %s set to %s.", "ODB_ECMA", ecma_filename)
969
1231
  # Path to the IOASSIGN file
970
- self.env.IOASSIGN = self.system.path.join(ecma.container.abspath, 'IOASSIGN')
1232
+ self.env.IOASSIGN = self.system.path.join(
1233
+ ecma.container.abspath, "IOASSIGN"
1234
+ )
971
1235
  # Let ancesters handling most of the env setting
972
1236
  super().execute(rh, opts)
973
1237
  # Rename the output file according to the name of the part of the observations treated
974
- self.system.mv('BDM_CQ', '_'.join(['BDM_CQ', ecma.resource.part]))
1238
+ self.system.mv("BDM_CQ", "_".join(["BDM_CQ", ecma.resource.part]))