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