vortex-nwp 2.0.0b1__py3-none-any.whl → 2.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. vortex/__init__.py +75 -47
  2. vortex/algo/__init__.py +3 -2
  3. vortex/algo/components.py +944 -618
  4. vortex/algo/mpitools.py +802 -497
  5. vortex/algo/mpitools_templates/__init__.py +1 -0
  6. vortex/algo/serversynctools.py +34 -33
  7. vortex/config.py +19 -22
  8. vortex/data/__init__.py +9 -3
  9. vortex/data/abstractstores.py +593 -655
  10. vortex/data/containers.py +217 -162
  11. vortex/data/contents.py +65 -39
  12. vortex/data/executables.py +93 -102
  13. vortex/data/flow.py +40 -34
  14. vortex/data/geometries.py +228 -132
  15. vortex/data/handlers.py +436 -227
  16. vortex/data/outflow.py +15 -15
  17. vortex/data/providers.py +185 -163
  18. vortex/data/resources.py +48 -42
  19. vortex/data/stores.py +540 -417
  20. vortex/data/sync_templates/__init__.py +0 -0
  21. vortex/gloves.py +114 -87
  22. vortex/layout/__init__.py +1 -8
  23. vortex/layout/contexts.py +150 -84
  24. vortex/layout/dataflow.py +353 -202
  25. vortex/layout/monitor.py +264 -128
  26. vortex/nwp/__init__.py +5 -2
  27. vortex/nwp/algo/__init__.py +14 -5
  28. vortex/nwp/algo/assim.py +205 -151
  29. vortex/nwp/algo/clim.py +683 -517
  30. vortex/nwp/algo/coupling.py +447 -225
  31. vortex/nwp/algo/eda.py +437 -229
  32. vortex/nwp/algo/eps.py +403 -231
  33. vortex/nwp/algo/forecasts.py +416 -275
  34. vortex/nwp/algo/fpserver.py +683 -307
  35. vortex/nwp/algo/ifsnaming.py +205 -145
  36. vortex/nwp/algo/ifsroot.py +215 -122
  37. vortex/nwp/algo/monitoring.py +137 -76
  38. vortex/nwp/algo/mpitools.py +330 -190
  39. vortex/nwp/algo/odbtools.py +637 -353
  40. vortex/nwp/algo/oopsroot.py +454 -273
  41. vortex/nwp/algo/oopstests.py +90 -56
  42. vortex/nwp/algo/request.py +287 -206
  43. vortex/nwp/algo/stdpost.py +878 -522
  44. vortex/nwp/data/__init__.py +22 -4
  45. vortex/nwp/data/assim.py +125 -137
  46. vortex/nwp/data/boundaries.py +121 -68
  47. vortex/nwp/data/climfiles.py +193 -211
  48. vortex/nwp/data/configfiles.py +73 -69
  49. vortex/nwp/data/consts.py +426 -401
  50. vortex/nwp/data/ctpini.py +59 -43
  51. vortex/nwp/data/diagnostics.py +94 -66
  52. vortex/nwp/data/eda.py +50 -51
  53. vortex/nwp/data/eps.py +195 -146
  54. vortex/nwp/data/executables.py +440 -434
  55. vortex/nwp/data/fields.py +63 -48
  56. vortex/nwp/data/gridfiles.py +183 -111
  57. vortex/nwp/data/logs.py +250 -217
  58. vortex/nwp/data/modelstates.py +180 -151
  59. vortex/nwp/data/monitoring.py +72 -99
  60. vortex/nwp/data/namelists.py +254 -202
  61. vortex/nwp/data/obs.py +400 -308
  62. vortex/nwp/data/oopsexec.py +22 -20
  63. vortex/nwp/data/providers.py +90 -65
  64. vortex/nwp/data/query.py +71 -82
  65. vortex/nwp/data/stores.py +49 -36
  66. vortex/nwp/data/surfex.py +136 -137
  67. vortex/nwp/syntax/__init__.py +1 -1
  68. vortex/nwp/syntax/stdattrs.py +173 -111
  69. vortex/nwp/tools/__init__.py +2 -2
  70. vortex/nwp/tools/addons.py +22 -17
  71. vortex/nwp/tools/agt.py +24 -12
  72. vortex/nwp/tools/bdap.py +16 -5
  73. vortex/nwp/tools/bdcp.py +4 -1
  74. vortex/nwp/tools/bdm.py +3 -0
  75. vortex/nwp/tools/bdmp.py +14 -9
  76. vortex/nwp/tools/conftools.py +728 -378
  77. vortex/nwp/tools/drhook.py +12 -8
  78. vortex/nwp/tools/grib.py +65 -39
  79. vortex/nwp/tools/gribdiff.py +22 -17
  80. vortex/nwp/tools/ifstools.py +82 -42
  81. vortex/nwp/tools/igastuff.py +167 -143
  82. vortex/nwp/tools/mars.py +14 -2
  83. vortex/nwp/tools/odb.py +234 -125
  84. vortex/nwp/tools/partitioning.py +61 -37
  85. vortex/nwp/tools/satrad.py +27 -12
  86. vortex/nwp/util/async.py +83 -55
  87. vortex/nwp/util/beacon.py +10 -10
  88. vortex/nwp/util/diffpygram.py +174 -86
  89. vortex/nwp/util/ens.py +144 -63
  90. vortex/nwp/util/hooks.py +30 -19
  91. vortex/nwp/util/taskdeco.py +28 -24
  92. vortex/nwp/util/usepygram.py +278 -172
  93. vortex/nwp/util/usetnt.py +31 -17
  94. vortex/sessions.py +72 -39
  95. vortex/syntax/__init__.py +1 -1
  96. vortex/syntax/stdattrs.py +410 -171
  97. vortex/syntax/stddeco.py +31 -22
  98. vortex/toolbox.py +327 -192
  99. vortex/tools/__init__.py +11 -2
  100. vortex/tools/actions.py +110 -121
  101. vortex/tools/addons.py +111 -92
  102. vortex/tools/arm.py +42 -22
  103. vortex/tools/compression.py +72 -69
  104. vortex/tools/date.py +11 -4
  105. vortex/tools/delayedactions.py +242 -132
  106. vortex/tools/env.py +75 -47
  107. vortex/tools/folder.py +342 -171
  108. vortex/tools/grib.py +341 -162
  109. vortex/tools/lfi.py +423 -216
  110. vortex/tools/listings.py +109 -40
  111. vortex/tools/names.py +218 -156
  112. vortex/tools/net.py +655 -299
  113. vortex/tools/parallelism.py +93 -61
  114. vortex/tools/prestaging.py +55 -31
  115. vortex/tools/schedulers.py +172 -105
  116. vortex/tools/services.py +403 -334
  117. vortex/tools/storage.py +293 -358
  118. vortex/tools/surfex.py +24 -24
  119. vortex/tools/systems.py +1234 -643
  120. vortex/tools/targets.py +156 -100
  121. vortex/util/__init__.py +1 -1
  122. vortex/util/config.py +378 -327
  123. vortex/util/empty.py +2 -2
  124. vortex/util/helpers.py +56 -24
  125. vortex/util/introspection.py +18 -12
  126. vortex/util/iosponge.py +8 -4
  127. vortex/util/roles.py +4 -6
  128. vortex/util/storefunctions.py +39 -13
  129. vortex/util/structs.py +3 -3
  130. vortex/util/worker.py +29 -17
  131. vortex_nwp-2.1.0.dist-info/METADATA +67 -0
  132. vortex_nwp-2.1.0.dist-info/RECORD +144 -0
  133. {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.1.0.dist-info}/WHEEL +1 -1
  134. vortex/layout/appconf.py +0 -109
  135. vortex/layout/jobs.py +0 -1276
  136. vortex/layout/nodes.py +0 -1424
  137. vortex/layout/subjobs.py +0 -464
  138. vortex_nwp-2.0.0b1.dist-info/METADATA +0 -50
  139. vortex_nwp-2.0.0b1.dist-info/RECORD +0 -146
  140. {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.1.0.dist-info/licenses}/LICENSE +0 -0
  141. {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.1.0.dist-info}/top_level.txt +0 -0
@@ -5,11 +5,17 @@ Abstract base class for any AlgoComponent leveraging the Arpege/IFS code.
5
5
  from bronx.fancies import loggers
6
6
  import footprints
7
7
 
8
- from vortex.algo.components import Parallel, ParallelIoServerMixin, AlgoComponentError
8
+ from vortex.algo.components import (
9
+ Parallel,
10
+ ParallelIoServerMixin,
11
+ AlgoComponentError,
12
+ )
9
13
  from vortex.syntax.stdattrs import model
10
14
  from vortex.tools import grib
11
15
 
12
- from . import ifsnaming # @UnusedImport
16
+ # footprints import
17
+ from . import ifsnaming as ifsnaming
18
+
13
19
  from ..syntax.stdattrs import algo_member
14
20
  from ..tools import satrad, drhook
15
21
 
@@ -19,105 +25,127 @@ __all__ = []
19
25
  logger = loggers.getLogger(__name__)
20
26
 
21
27
 
22
- class IFSParallel(Parallel, ParallelIoServerMixin,
23
- satrad.SatRadDecoMixin, drhook.DrHookDecoMixin, grib.EcGribDecoMixin):
28
+ class IFSParallel(
29
+ Parallel,
30
+ ParallelIoServerMixin,
31
+ satrad.SatRadDecoMixin,
32
+ drhook.DrHookDecoMixin,
33
+ grib.EcGribDecoMixin,
34
+ ):
24
35
  """Abstract IFSModel parallel algo components."""
25
36
 
26
37
  _abstract = True
27
38
  _footprint = [
28
- model, algo_member,
39
+ model,
40
+ algo_member,
29
41
  dict(
30
- info = 'Abstract AlgoComponent for anything based on Arpege/IFS.',
31
- attr = dict(
32
- kind = dict(
33
- info = 'The kind of processing we want the Arpege/IFS binary to perform.',
34
- default = 'ifsrun',
35
- doc_zorder = 90,
42
+ info="Abstract AlgoComponent for anything based on Arpege/IFS.",
43
+ attr=dict(
44
+ kind=dict(
45
+ info="The kind of processing we want the Arpege/IFS binary to perform.",
46
+ default="ifsrun",
47
+ doc_zorder=90,
36
48
  ),
37
- model = dict(
38
- values = ['arpege', 'arp', 'arp_court', 'aladin', 'ald',
39
- 'arome', 'aro', 'aearp', 'pearp', 'ifs', 'alaro', 'harmoniearome']
49
+ model=dict(
50
+ values=[
51
+ "arpege",
52
+ "arp",
53
+ "arp_court",
54
+ "aladin",
55
+ "ald",
56
+ "arome",
57
+ "aro",
58
+ "aearp",
59
+ "pearp",
60
+ "ifs",
61
+ "alaro",
62
+ "harmoniearome",
63
+ ]
40
64
  ),
41
- ioname = dict(
42
- default = 'nwpioserv',
65
+ ioname=dict(
66
+ default="nwpioserv",
43
67
  ),
44
- binarysingle = dict(
45
- default = 'basicnwp',
68
+ binarysingle=dict(
69
+ default="basicnwp",
46
70
  ),
47
- conf = dict(
48
- info = 'The configuration number given to Arpege/IFS.',
49
- type = int,
50
- optional = True,
51
- default = 1,
52
- doc_visibility = footprints.doc.visibility.ADVANCED,
71
+ conf=dict(
72
+ info="The configuration number given to Arpege/IFS.",
73
+ type=int,
74
+ optional=True,
75
+ default=1,
76
+ doc_visibility=footprints.doc.visibility.ADVANCED,
53
77
  ),
54
- timescheme = dict(
55
- info = 'The timescheme that will be used by Arpege/IFS model.',
56
- optional = True,
57
- default = 'sli',
58
- values = ['eul', 'eulerian', 'sli', 'semilag'],
59
- remap = dict(
60
- eulerian = 'eul',
61
- semilag = 'sli'
62
- ),
63
- doc_visibility = footprints.doc.visibility.ADVANCED,
78
+ timescheme=dict(
79
+ info="The timescheme that will be used by Arpege/IFS model.",
80
+ optional=True,
81
+ default="sli",
82
+ values=["eul", "eulerian", "sli", "semilag"],
83
+ remap=dict(eulerian="eul", semilag="sli"),
84
+ doc_visibility=footprints.doc.visibility.ADVANCED,
64
85
  ),
65
- timestep = dict(
66
- info = 'The timestep of the Arpege/IFS model.',
67
- type = float,
68
- optional = True,
69
- default = 600.,
86
+ timestep=dict(
87
+ info="The timestep of the Arpege/IFS model.",
88
+ type=float,
89
+ optional=True,
90
+ default=600.0,
70
91
  ),
71
- fcterm = dict(
72
- info = 'The forecast term of the Arpege/IFS model.',
73
- type = int,
74
- optional = True,
75
- default = 0,
92
+ fcterm=dict(
93
+ info="The forecast term of the Arpege/IFS model.",
94
+ type=int,
95
+ optional=True,
96
+ default=0,
76
97
  ),
77
- fcunit = dict(
78
- info = 'The unit used in the *fcterm* attribute.',
79
- optional = True,
80
- default = 'h',
81
- values = ['h', 'hour', 't', 'step', 'timestep'],
82
- remap = dict(
83
- hour = 'h',
84
- step = 't',
85
- timestep = 't',
86
- )
98
+ fcunit=dict(
99
+ info="The unit used in the *fcterm* attribute.",
100
+ optional=True,
101
+ default="h",
102
+ values=["h", "hour", "t", "step", "timestep"],
103
+ remap=dict(
104
+ hour="h",
105
+ step="t",
106
+ timestep="t",
107
+ ),
87
108
  ),
88
- xpname = dict(
89
- info = 'The default labelling of files used in Arpege/IFS model.',
90
- optional = True,
91
- default = 'XPVT',
92
- doc_visibility = footprints.doc.visibility.ADVANCED,
109
+ xpname=dict(
110
+ info="The default labelling of files used in Arpege/IFS model.",
111
+ optional=True,
112
+ default="XPVT",
113
+ doc_visibility=footprints.doc.visibility.ADVANCED,
93
114
  ),
94
- )
95
- )
115
+ ),
116
+ ),
96
117
  ]
97
118
 
98
119
  def fstag(self):
99
120
  """Extend default tag with ``kind`` value."""
100
- return super().fstag() + '.' + self.kind
121
+ return super().fstag() + "." + self.kind
122
+
123
+ def _mpitool_attributes(self, opts):
124
+ conf_dict = super()._mpitool_attributes(opts)
125
+ conf_dict.update({"mplbased": True})
126
+ return conf_dict
101
127
 
102
128
  def valid_executable(self, rh):
103
129
  """Be sure that the specifed executable is ifsmodel compatible."""
104
130
  valid = super().valid_executable(rh)
105
131
  try:
106
- return valid and bool(rh.resource.realkind == 'ifsmodel')
132
+ return valid and bool(rh.resource.realkind == "ifsmodel")
107
133
  except (ValueError, TypeError):
108
134
  return False
109
135
 
110
136
  def spawn_hook(self):
111
137
  """Usually a good habit to dump the fort.4 namelist."""
112
138
  super().spawn_hook()
113
- if self.system.path.exists('fort.4'):
114
- self.system.subtitle('{:s} : dump namelist <fort.4>'.format(self.realkind))
115
- self.system.cat('fort.4', output=False)
139
+ if self.system.path.exists("fort.4"):
140
+ self.system.subtitle(
141
+ "{:s} : dump namelist <fort.4>".format(self.realkind)
142
+ )
143
+ self.system.cat("fort.4", output=False)
116
144
 
117
145
  def spawn_command_options(self):
118
146
  """Dictionary provided for command line factory."""
119
147
  return dict(
120
- name=(self.xpname + 'xxxx')[:4].upper(),
148
+ name=(self.xpname + "xxxx")[:4].upper(),
121
149
  conf=self.conf,
122
150
  timescheme=self.timescheme,
123
151
  timestep=self.timestep,
@@ -133,19 +161,18 @@ class IFSParallel(Parallel, ParallelIoServerMixin,
133
161
  :param actualfmt: The format of the target file.
134
162
  :param dict kwargs: Any argument you may see fit.
135
163
  """
136
- nc_args = dict(model=self.model,
137
- conf=self.conf,
138
- xpname=self.xpname)
164
+ nc_args = dict(model=self.model, conf=self.conf, xpname=self.xpname)
139
165
  nc_args.update(kwargs)
140
- nc = footprints.proxy.ifsnamingconv(kind=kind,
141
- actualfmt=actualfmt,
142
- cycle=rh.resource.cycle,
143
- **nc_args)
166
+ nc = footprints.proxy.ifsnamingconv(
167
+ kind=kind, actualfmt=actualfmt, cycle=rh.resource.cycle, **nc_args
168
+ )
144
169
  if nc is None:
145
170
  raise AlgoComponentError("No IFSNamingConvention was found.")
146
171
  return nc
147
172
 
148
- def do_climfile_fixer(self, rh, convkind, actualfmt=None, geo=None, **kwargs):
173
+ def do_climfile_fixer(
174
+ self, rh, convkind, actualfmt=None, geo=None, **kwargs
175
+ ):
149
176
  """Is it necessary to fix the climatology file ? (i.e link in the appropriate file).
150
177
 
151
178
  :param rh: The binary's ResourceHandler.
@@ -155,16 +182,27 @@ class IFSParallel(Parallel, ParallelIoServerMixin,
155
182
  :param dict kwargs: Any argument you may see fit (used to create and call
156
183
  the IFSNamingConvention object.
157
184
  """
158
- nc = self.naming_convention(kind=convkind, rh=rh, actualfmt=actualfmt, **kwargs)
185
+ nc = self.naming_convention(
186
+ kind=convkind, rh=rh, actualfmt=actualfmt, **kwargs
187
+ )
159
188
  nc_args = dict()
160
189
  if geo:
161
- nc_args['area'] = geo.area
190
+ nc_args["area"] = geo.area
162
191
  nc_args.update(kwargs)
163
- return not self.system.path.exists(nc(** nc_args))
164
-
165
- def climfile_fixer(self, rh, convkind,
166
- month, geo=None, notgeo=None, actualfmt=None,
167
- inputrole=None, inputkind=None, **kwargs):
192
+ return not self.system.path.exists(nc(**nc_args))
193
+
194
+ def climfile_fixer(
195
+ self,
196
+ rh,
197
+ convkind,
198
+ month,
199
+ geo=None,
200
+ notgeo=None,
201
+ actualfmt=None,
202
+ inputrole=None,
203
+ inputkind=None,
204
+ **kwargs,
205
+ ):
168
206
  """Fix the climatology files (by choosing the appropriate month, geometry, ...)
169
207
 
170
208
  :param rh: The binary's ResourceHandler.
@@ -179,19 +217,25 @@ class IFSParallel(Parallel, ParallelIoServerMixin,
179
217
  the IFSNamingConvention object).
180
218
  """
181
219
  if geo is not None and notgeo is not None:
182
- raise ValueError('*geo* and *notgeo* cannot be provided together.')
220
+ raise ValueError("*geo* and *notgeo* cannot be provided together.")
183
221
 
184
222
  def check_month(actualrh):
185
- return bool(hasattr(actualrh.resource, 'month') and
186
- actualrh.resource.month == month)
223
+ return bool(
224
+ hasattr(actualrh.resource, "month")
225
+ and actualrh.resource.month == month
226
+ )
187
227
 
188
228
  def check_month_and_geo(actualrh):
189
- return (check_month(actualrh) and
190
- actualrh.resource.geometry.tag == geo.tag)
229
+ return (
230
+ check_month(actualrh)
231
+ and actualrh.resource.geometry.tag == geo.tag
232
+ )
191
233
 
192
234
  def check_month_and_notgeo(actualrh):
193
- return (check_month(actualrh) and
194
- actualrh.resource.geometry.tag != notgeo.tag)
235
+ return (
236
+ check_month(actualrh)
237
+ and actualrh.resource.geometry.tag != notgeo.tag
238
+ )
195
239
 
196
240
  if geo:
197
241
  checker = check_month_and_geo
@@ -200,23 +244,41 @@ class IFSParallel(Parallel, ParallelIoServerMixin,
200
244
  else:
201
245
  checker = check_month
202
246
 
203
- nc = self.naming_convention(kind=convkind, rh=rh, actualfmt=actualfmt, **kwargs)
247
+ nc = self.naming_convention(
248
+ kind=convkind, rh=rh, actualfmt=actualfmt, **kwargs
249
+ )
204
250
  nc_args = dict()
205
251
  if geo:
206
- nc_args['area'] = geo.area
252
+ nc_args["area"] = geo.area
207
253
  nc_args.update(kwargs)
208
- target_name = nc(** nc_args)
254
+ target_name = nc(**nc_args)
209
255
 
210
256
  self.system.remove(target_name)
211
257
 
212
- logger.info("Linking in the %s file (%s) for month %s.", convkind, target_name, month)
213
- rc = self.setlink(initrole=inputrole, initkind=inputkind, inittest=checker,
214
- initname=target_name)
258
+ logger.info(
259
+ "Linking in the %s file (%s) for month %s.",
260
+ convkind,
261
+ target_name,
262
+ month,
263
+ )
264
+ rc = self.setlink(
265
+ initrole=inputrole,
266
+ initkind=inputkind,
267
+ inittest=checker,
268
+ initname=target_name,
269
+ )
215
270
  return target_name if rc else None
216
271
 
217
- def all_localclim_fixer(self, rh, month, convkind='targetclim', actualfmt=None,
218
- inputrole=('LocalClim', 'TargetClim', 'BDAPClim'),
219
- inputkind='clim_bdap', **kwargs):
272
+ def all_localclim_fixer(
273
+ self,
274
+ rh,
275
+ month,
276
+ convkind="targetclim",
277
+ actualfmt=None,
278
+ inputrole=("LocalClim", "TargetClim", "BDAPClim"),
279
+ inputkind="clim_bdap",
280
+ **kwargs,
281
+ ):
220
282
  """Fix all the local/BDAP climatology files (by choosing the appropriate month)
221
283
 
222
284
  :param rh: The binary's ResourceHandler.
@@ -231,19 +293,33 @@ class IFSParallel(Parallel, ParallelIoServerMixin,
231
293
  """
232
294
 
233
295
  def check_month(actualrh):
234
- return bool(hasattr(actualrh.resource, 'month') and
235
- actualrh.resource.month == month)
296
+ return bool(
297
+ hasattr(actualrh.resource, "month")
298
+ and actualrh.resource.month == month
299
+ )
236
300
 
237
- nc = self.naming_convention(kind=convkind, rh=rh, actualfmt=actualfmt, **kwargs)
301
+ nc = self.naming_convention(
302
+ kind=convkind, rh=rh, actualfmt=actualfmt, **kwargs
303
+ )
238
304
  dealtwith = list()
239
305
 
240
- for tclimrh in [x.rh for x in self.context.sequence.effective_inputs(
241
- role=inputrole, kind=inputkind,
242
- ) if x.rh.resource.month == month]:
306
+ for tclimrh in [
307
+ x.rh
308
+ for x in self.context.sequence.effective_inputs(
309
+ role=inputrole,
310
+ kind=inputkind,
311
+ )
312
+ if x.rh.resource.month == month
313
+ ]:
243
314
  thisclim = tclimrh.container.localpath()
244
315
  thisname = nc(area=tclimrh.resource.geometry.area)
245
316
  if thisclim != thisname:
246
- logger.info("Linking in the %s to %s for month %s.", thisclim, thisname, month)
317
+ logger.info(
318
+ "Linking in the %s to %s for month %s.",
319
+ thisclim,
320
+ thisname,
321
+ month,
322
+ )
247
323
  self.system.symlink(thisclim, thisname)
248
324
  dealtwith.append(thisname)
249
325
 
@@ -251,13 +327,17 @@ class IFSParallel(Parallel, ParallelIoServerMixin,
251
327
 
252
328
  def find_namelists(self, opts=None):
253
329
  """Find any namelists candidates in actual context inputs."""
254
- return [x.rh
255
- for x in self.context.sequence.effective_inputs(kind=('namelist', 'namelistfp'))]
330
+ return [
331
+ x.rh
332
+ for x in self.context.sequence.effective_inputs(
333
+ kind=("namelist", "namelistfp")
334
+ )
335
+ ]
256
336
 
257
337
  def _set_nam_macro(self, namcontents, namlocal, macro, value):
258
338
  """Set a namelist macro and log it!"""
259
339
  namcontents.setmacro(macro, value)
260
- logger.info('Setup macro %s=%s in %s', macro, str(value), namlocal)
340
+ logger.info("Setup macro %s=%s in %s", macro, str(value), namlocal)
261
341
 
262
342
  def prepare_namelist_delta(self, rh, namcontents, namlocal):
263
343
  """Apply a namelist delta depending on the cycle of the binary."""
@@ -268,34 +348,47 @@ class IFSParallel(Parallel, ParallelIoServerMixin,
268
348
  nam_updated = False
269
349
  # For cy41 onward, replace some namelist macros with the command line
270
350
  # arguments
271
- if rh.resource.cycle >= 'cy41':
272
- if 'NAMARG' in namcontents:
351
+ if rh.resource.cycle >= "cy41":
352
+ if "NAMARG" in namcontents:
273
353
  opts_arg = self.spawn_command_options()
274
- self._set_nam_macro(namcontents, namlocal, 'CEXP', opts_arg['name'])
275
- self._set_nam_macro(namcontents, namlocal, 'TIMESTEP', opts_arg['timestep'])
276
- fcstop = '{:s}{:d}'.format(opts_arg['fcunit'], opts_arg['fcterm'])
277
- self._set_nam_macro(namcontents, namlocal, 'FCSTOP', fcstop)
354
+ self._set_nam_macro(
355
+ namcontents, namlocal, "CEXP", opts_arg["name"]
356
+ )
357
+ self._set_nam_macro(
358
+ namcontents, namlocal, "TIMESTEP", opts_arg["timestep"]
359
+ )
360
+ fcstop = "{:s}{:d}".format(
361
+ opts_arg["fcunit"], opts_arg["fcterm"]
362
+ )
363
+ self._set_nam_macro(namcontents, namlocal, "FCSTOP", fcstop)
278
364
  nam_updated = True
279
365
  else:
280
- logger.info('No NAMARG block in %s', namlocal)
366
+ logger.info("No NAMARG block in %s", namlocal)
281
367
 
282
368
  if self.member is not None:
283
- for macro_name in ('MEMBER', 'PERTURB'):
284
- self._set_nam_macro(namcontents, namlocal, macro_name, self.member)
369
+ for macro_name in ("MEMBER", "PERTURB"):
370
+ self._set_nam_macro(
371
+ namcontents, namlocal, macro_name, self.member
372
+ )
285
373
  nam_updated = True
286
374
  return nam_updated
287
375
 
288
376
  def prepare_namelists(self, rh, opts=None):
289
377
  """Update each of the namelists."""
290
378
  namcandidates = self.find_namelists(opts)
291
- self.system.subtitle('Namelist candidates')
379
+ self.system.subtitle("Namelist candidates")
292
380
  for nam in namcandidates:
293
381
  nam.quickview()
294
382
  for namrh in namcandidates:
295
383
  namc = namrh.contents
296
- if self.prepare_namelist_delta(rh, namc, namrh.container.actualpath()):
384
+ if self.prepare_namelist_delta(
385
+ rh, namc, namrh.container.actualpath()
386
+ ):
297
387
  if namc.dumps_needs_update:
298
- logger.info('Rewritting the %s namelists file.', namrh.container.actualpath())
388
+ logger.info(
389
+ "Rewritting the %s namelists file.",
390
+ namrh.container.actualpath(),
391
+ )
299
392
  namc.rewrite(namrh.container)
300
393
 
301
394
  def prepare(self, rh, opts):
@@ -306,6 +399,6 @@ class IFSParallel(Parallel, ParallelIoServerMixin,
306
399
 
307
400
  def execute_single(self, rh, opts):
308
401
  """Standard IFS-Like execution parallel execution."""
309
- if rh.resource.cycle < 'cy46':
310
- self.system.ls(output='dirlst')
402
+ if rh.resource.cycle < "cy46":
403
+ self.system.ls(output="dirlst")
311
404
  super().execute_single(rh, opts)