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
@@ -20,45 +20,51 @@ __all__ = []
20
20
  logger = loggers.getLogger(__name__)
21
21
 
22
22
 
23
- @namebuilding_insert('src', lambda s: [s.filling, s.model])
23
+ @namebuilding_insert("src", lambda s: [s.filling, s.model])
24
24
  class AbstractAnalysis(GeoFlowResource):
25
25
  """Analysis resource.
26
26
 
27
27
  It can be an atmospheric, surface or full analysis (full = atmospheric + surface).
28
28
  """
29
+
29
30
  _abstract = True
30
31
  _footprint = dict(
31
- info = 'Analysis',
32
- attr = dict(
33
- kind = dict(
34
- values = ['analysis', 'analyse', 'atm_analysis']
35
- ),
36
- nativefmt = dict(
37
- values = ['fa', 'grib', 'lfi', 'netcdf', 'txt', 'unknown'],
38
- default = 'fa',
32
+ info="Analysis",
33
+ attr=dict(
34
+ kind=dict(values=["analysis", "analyse", "atm_analysis"]),
35
+ nativefmt=dict(
36
+ values=["fa", "grib", "lfi", "netcdf", "txt", "unknown"],
37
+ default="fa",
39
38
  ),
40
- filtering = dict(
41
- info = "The filtering that was applied during the generating process.",
42
- optional = True,
43
- values = ['dfi'],
44
- doc_zorder = -5,
39
+ filtering=dict(
40
+ info="The filtering that was applied during the generating process.",
41
+ optional=True,
42
+ values=["dfi"],
43
+ doc_zorder=-5,
45
44
  ),
46
- filling = dict(
47
- info = "The content/coverage of the analysis.",
48
- optional = True,
49
- default = 'full',
50
- values = ['surface', 'surf', 'atmospheric', 'atm', 'full', 'soil'],
51
- remap = dict(
52
- surface = 'surf',
53
- atmospheric = 'atm',
45
+ filling=dict(
46
+ info="The content/coverage of the analysis.",
47
+ optional=True,
48
+ default="full",
49
+ values=[
50
+ "surface",
51
+ "surf",
52
+ "atmospheric",
53
+ "atm",
54
+ "full",
55
+ "soil",
56
+ ],
57
+ remap=dict(
58
+ surface="surf",
59
+ atmospheric="atm",
54
60
  ),
55
- )
56
- )
61
+ ),
62
+ ),
57
63
  )
58
64
 
59
65
  @property
60
66
  def realkind(self):
61
- return 'analysis'
67
+ return "analysis"
62
68
 
63
69
 
64
70
  class Analysis3D(AbstractAnalysis):
@@ -70,71 +76,79 @@ class Analysis3D(AbstractAnalysis):
70
76
  _footprint = [
71
77
  term,
72
78
  dict(
73
- attr = dict(
74
- term = dict(
75
- values = [Time(0), ],
76
- optional = True,
77
- default = Time(0)
79
+ attr=dict(
80
+ term=dict(
81
+ values=[
82
+ Time(0),
83
+ ],
84
+ optional=True,
85
+ default=Time(0),
78
86
  )
79
87
  )
80
- )
88
+ ),
81
89
  ]
82
90
 
83
91
  def archive_basename(self):
84
92
  """OP ARCHIVE specific naming convention."""
85
- ananame = 'analyse'
86
- if 'surf' in self.filling:
87
- if re.match('aladin|arome', self.model):
88
- ananame = 'analyse_surf'
89
- elif self.model == 'surfex':
90
- ananame = 'analyse'
91
- elif self.model in ('hycom', 'mfwam'):
92
- ananame = '(prefix:modelkey)(termfix:modelkey)(suffix:modelkey)'
93
+ ananame = "analyse"
94
+ if "surf" in self.filling:
95
+ if re.match("aladin|arome", self.model):
96
+ ananame = "analyse_surf"
97
+ elif self.model == "surfex":
98
+ ananame = "analyse"
99
+ elif self.model in ("hycom", "mfwam"):
100
+ ananame = (
101
+ "(prefix:modelkey)(termfix:modelkey)(suffix:modelkey)"
102
+ )
93
103
  else:
94
- ananame = 'analyse_surface1'
104
+ ananame = "analyse_surface1"
95
105
 
96
106
  if self.filtering is not None:
97
- if 'aladin' in self.model:
98
- ananame = 'ANALYSE_DFI'
107
+ if "aladin" in self.model:
108
+ ananame = "ANALYSE_DFI"
99
109
 
100
- if self.model == 'surfex':
101
- ananame += '.sfx'
110
+ if self.model == "surfex":
111
+ ananame += ".sfx"
102
112
 
103
113
  return ananame
104
114
 
105
115
  def olive_basename(self):
106
116
  """OLIVE specific naming convention."""
107
- olivename_map = {'atm': 'TRAJ' + self.model[:4].upper() + '+0000',
108
- 'surf': 'surfanalyse',
109
- 'full': 'analyse'}
110
- if self.model != 'arpege':
111
- olivename_map['surf'] = 'analyse'
112
- if self.model == 'surfex':
113
- olivename_map = {k: x + '.sfx' for k, x in olivename_map.items()}
117
+ olivename_map = {
118
+ "atm": "TRAJ" + self.model[:4].upper() + "+0000",
119
+ "surf": "surfanalyse",
120
+ "full": "analyse",
121
+ }
122
+ if self.model != "arpege":
123
+ olivename_map["surf"] = "analyse"
124
+ if self.model == "surfex":
125
+ olivename_map = {
126
+ k: x + ".sfx" for k, x in olivename_map.items()
127
+ }
114
128
  return olivename_map[self.filling]
115
129
 
116
130
  def iga_pathinfo(self):
117
131
  """Standard path information for IGA inline cache."""
118
- if self.model == 'arome':
119
- if self.filling == 'surf':
120
- directory = 'fic_day'
132
+ if self.model == "arome":
133
+ if self.filling == "surf":
134
+ directory = "fic_day"
121
135
  else:
122
- directory = 'workdir/analyse'
123
- elif self.model == 'arpege':
124
- if self.filling == 'surf':
125
- directory = 'workdir/analyse'
136
+ directory = "workdir/analyse"
137
+ elif self.model == "arpege":
138
+ if self.filling == "surf":
139
+ directory = "workdir/analyse"
126
140
  else:
127
- directory = 'autres'
128
- elif self.model in ('hycom', 'mfwam'):
129
- if self.filling == 'surf':
130
- directory = 'guess'
131
- elif self.model == 'surfex':
132
- directory = 'fic_day'
141
+ directory = "autres"
142
+ elif self.model in ("hycom", "mfwam"):
143
+ if self.filling == "surf":
144
+ directory = "guess"
145
+ elif self.model == "surfex":
146
+ directory = "fic_day"
133
147
  else:
134
- if self.filling == 'surf':
135
- directory = 'autres'
148
+ if self.filling == "surf":
149
+ directory = "autres"
136
150
  else:
137
- directory = 'workdir/analyse'
151
+ directory = "workdir/analyse"
138
152
  return dict(
139
153
  fmt=directory,
140
154
  model=self.model,
@@ -148,12 +162,14 @@ class Analysis4D(AbstractAnalysis):
148
162
  _footprint = [
149
163
  term_deco,
150
164
  dict(
151
- attr = dict(
152
- term = dict(
153
- outcast = [Time(0), ]
165
+ attr=dict(
166
+ term=dict(
167
+ outcast=[
168
+ Time(0),
169
+ ]
154
170
  )
155
171
  )
156
- )
172
+ ),
157
173
  ]
158
174
 
159
175
 
@@ -161,14 +177,15 @@ class InitialCondition(AbstractAnalysis):
161
177
  """
162
178
  Class for initial condition resources : anything from which a model run can be performed.
163
179
  """
180
+
164
181
  _footprint = dict(
165
- info = 'Initial condition',
166
- attr = dict(
167
- kind = dict(
168
- values = ['initial_condition', 'ic', 'starting_point'],
169
- remap = dict(autoremap = 'first'),
182
+ info="Initial condition",
183
+ attr=dict(
184
+ kind=dict(
185
+ values=["initial_condition", "ic", "starting_point"],
186
+ remap=dict(autoremap="first"),
170
187
  ),
171
- )
188
+ ),
172
189
  )
173
190
 
174
191
  @property
@@ -178,86 +195,98 @@ class InitialCondition(AbstractAnalysis):
178
195
 
179
196
  @property
180
197
  def realkind(self):
181
- return 'ic'
198
+ return "ic"
182
199
 
183
200
  def olive_basename(self):
184
201
  """OLIVE specific naming convention."""
185
- logger.warning("The member number is only known by the provider, so the generic historic name is returned.")
186
- return 'ICMSH' + self.model[:4].upper() + '+' + self.term.fmthour
202
+ logger.warning(
203
+ "The member number is only known by the provider, so the generic historic name is returned."
204
+ )
205
+ return "ICMSH" + self.model[:4].upper() + "+" + self.term.fmthour
187
206
 
188
207
  def archive_basename(self):
189
208
  """OP ARCHIVE specific naming convention."""
190
- return 'ICFC_(memberfix:member)'
209
+ return "ICFC_(memberfix:member)"
191
210
 
192
211
 
193
212
  class Historic(GeoFlowResource):
194
213
  """
195
214
  Class for historical state of a model (e.g. from a forecast).
196
215
  """
216
+
197
217
  _footprint = [
198
218
  term_deco,
199
219
  dict(
200
- info = 'Historic forecast file',
201
- attr = dict(
202
- kind = dict(
203
- values = ['historic', 'modelstate'],
204
- remap = dict(
205
- modelstate = 'historic'
206
- )
220
+ info="Historic forecast file",
221
+ attr=dict(
222
+ kind=dict(
223
+ values=["historic", "modelstate"],
224
+ remap=dict(modelstate="historic"),
207
225
  ),
208
- subset = dict(
226
+ subset=dict(
209
227
  # Dummy argument but avoid priority related messages with footprints
210
- info = 'With Historical files, leave subset empty...',
211
- optional = True,
212
- values = [None, ],
228
+ info="With Historical files, leave subset empty...",
229
+ optional=True,
230
+ values=[
231
+ None,
232
+ ],
213
233
  ),
214
- nativefmt = dict(
215
- values = ['fa', 'grib', 'lfi', 'netcdf', 'unknown', 'nc'],
216
- remap = dict(nc='netcdf'),
217
- default = 'fa',
234
+ nativefmt=dict(
235
+ values=["fa", "grib", "lfi", "netcdf", "unknown", "nc"],
236
+ remap=dict(nc="netcdf"),
237
+ default="fa",
218
238
  ),
219
- )
220
- )
239
+ ),
240
+ ),
221
241
  ]
222
242
 
223
243
  @property
224
244
  def realkind(self):
225
- return 'historic'
245
+ return "historic"
226
246
 
227
247
  def archive_basename(self):
228
248
  """OP ARCHIVE specific naming convention."""
229
- if self.model in ('mfwam', 'hycom'):
230
- prefix = '(prefix:modelkey)'
231
- midfix = ''
249
+ if self.model in ("mfwam", "hycom"):
250
+ prefix = "(prefix:modelkey)"
251
+ midfix = ""
232
252
  else:
233
- prefix = '(icmshfix:modelkey)'
234
- midfix = '(histfix:igakey)'
235
- termfix = '(termfix:modelkey)'
236
- suffix = '(suffix:modelkey)'
237
-
238
- if self.geometry.lam and re.match('testms1|testmp1|testmp2', self.geometry.area):
239
- suffix = '.r' + archive_suffix(self.model, self.cutoff, self.date)
240
-
241
- if self.model == 'mocage':
242
- prefix = 'HM'
253
+ prefix = "(icmshfix:modelkey)"
254
+ midfix = "(histfix:igakey)"
255
+ termfix = "(termfix:modelkey)"
256
+ suffix = "(suffix:modelkey)"
257
+
258
+ if self.geometry.lam and re.match(
259
+ "testms1|testmp1|testmp2", self.geometry.area
260
+ ):
261
+ suffix = ".r" + archive_suffix(self.model, self.cutoff, self.date)
262
+
263
+ if self.model == "mocage":
264
+ prefix = "HM"
243
265
  midfix = self.geometry.area
244
- if self.nativefmt == 'netcdf':
245
- suffix = '.nc'
266
+ if self.nativefmt == "netcdf":
267
+ suffix = ".nc"
246
268
 
247
269
  return prefix + midfix + termfix + suffix
248
270
 
249
271
  def olive_basename(self):
250
272
  """OLIVE specific naming convention."""
251
- if self.model == 'mesonh':
252
- return '.'.join(
253
- (self.model.upper(), self.geometry.area[:4].upper() + '+' + self.term.fmthour, self.nativefmt)
273
+ if self.model == "mesonh":
274
+ return ".".join(
275
+ (
276
+ self.model.upper(),
277
+ self.geometry.area[:4].upper() + "+" + self.term.fmthour,
278
+ self.nativefmt,
279
+ )
254
280
  )
255
281
  else:
256
- return 'ICMSH' + self.model[:4].upper() + '+' + self.term.fmthour
282
+ return "ICMSH" + self.model[:4].upper() + "+" + self.term.fmthour
257
283
 
258
284
  def _geo2basename_info(self, add_stretching=True):
259
285
  """Return an array describing the geometry for the Vortex's name builder."""
260
- if isinstance(self.geometry, CurvlinearGeometry) and self.model == 'hycom':
286
+ if (
287
+ isinstance(self.geometry, CurvlinearGeometry)
288
+ and self.model == "hycom"
289
+ ):
261
290
  # return the old naming convention for surges restart files
262
291
  lgeo = [self.geometry.area, self.geometry.rnice]
263
292
  return lgeo
@@ -265,70 +294,70 @@ class Historic(GeoFlowResource):
265
294
  return super()._geo2basename_info(add_stretching=add_stretching)
266
295
 
267
296
 
268
- @namebuilding_insert('filtername', lambda s: s.subset)
297
+ @namebuilding_insert("filtername", lambda s: s.subset)
269
298
  class HistoricSubset(GeoFlowResource):
270
299
  """
271
300
  Class for a subset of the historical state of a model (e.g. from a forecast).
272
301
  """
302
+
273
303
  _footprint = [
274
304
  term_deco,
275
305
  dict(
276
- info = 'Subset of an historic forecast file',
277
- attr = dict(
278
- kind = dict(
279
- values = ['historic', 'modelstate'],
280
- remap = dict(
281
- modelstate = 'historic'
282
- )
306
+ info="Subset of an historic forecast file",
307
+ attr=dict(
308
+ kind=dict(
309
+ values=["historic", "modelstate"],
310
+ remap=dict(modelstate="historic"),
283
311
  ),
284
- subset = dict(
285
- info = "The subset of fields contained in this data.",
312
+ subset=dict(
313
+ info="The subset of fields contained in this data.",
286
314
  ),
287
- nativefmt = dict(
288
- values = ['fa', 'grib', 'lfi', 'netcdf', 'unknown', 'nc'],
289
- remap = dict(nc='netcdf'),
290
- default = 'fa',
315
+ nativefmt=dict(
316
+ values=["fa", "grib", "lfi", "netcdf", "unknown", "nc"],
317
+ remap=dict(nc="netcdf"),
318
+ default="fa",
291
319
  ),
292
- )
293
- )
320
+ ),
321
+ ),
294
322
  ]
295
323
 
296
324
  @property
297
325
  def realkind(self):
298
- return 'historic'
326
+ return "historic"
299
327
 
300
328
 
301
329
  class BiasDFI(GeoFlowResource):
302
330
  """
303
331
  Class for some kind of DFI bias (please add proper documentation).
304
332
  """
333
+
305
334
  _footprint = [
306
335
  term_deco,
307
336
  dict(
308
- info = 'DFI bias file',
309
- attr = dict(
310
- kind = dict(
311
- values = ['biasdfi', 'dfibias'],
312
- remap = dict(
313
- dfibias = 'biasdfi'
314
- )
337
+ info="DFI bias file",
338
+ attr=dict(
339
+ kind=dict(
340
+ values=["biasdfi", "dfibias"],
341
+ remap=dict(dfibias="biasdfi"),
315
342
  ),
316
- nativefmt = dict(
317
- values = ['fa'],
318
- default = 'fa',
343
+ nativefmt=dict(
344
+ values=["fa"],
345
+ default="fa",
319
346
  ),
320
- )
321
- )
347
+ ),
348
+ ),
322
349
  ]
323
350
 
324
351
  @property
325
352
  def realkind(self):
326
- return 'biasdfi'
353
+ return "biasdfi"
327
354
 
328
355
  def archive_basename(self):
329
356
  """OP ARCHIVE specific naming convention."""
330
- return 'BIASDFI+{:04d}'.format(self.term.hour)
357
+ return "BIASDFI+{:04d}".format(self.term.hour)
331
358
 
332
359
  def olive_basename(self):
333
360
  """OLIVE specific naming convention."""
334
- return 'BIASDFI{:s}+{:04d}'.format(self.model[:4].upper(), self.term.hour)
361
+ return "BIASDFI{:s}+{:04d}".format(
362
+ self.model[:4].upper(), self.term.hour
363
+ )