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.
- vortex/__init__.py +75 -47
- vortex/algo/__init__.py +3 -2
- vortex/algo/components.py +944 -618
- vortex/algo/mpitools.py +802 -497
- vortex/algo/mpitools_templates/__init__.py +1 -0
- 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 +436 -227
- vortex/data/outflow.py +15 -15
- vortex/data/providers.py +185 -163
- vortex/data/resources.py +48 -42
- vortex/data/stores.py +540 -417
- vortex/data/sync_templates/__init__.py +0 -0
- 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 +416 -275
- vortex/nwp/algo/fpserver.py +683 -307
- vortex/nwp/algo/ifsnaming.py +205 -145
- vortex/nwp/algo/ifsroot.py +215 -122
- vortex/nwp/algo/monitoring.py +137 -76
- vortex/nwp/algo/mpitools.py +330 -190
- vortex/nwp/algo/odbtools.py +637 -353
- vortex/nwp/algo/oopsroot.py +454 -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 +110 -121
- 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 +341 -162
- vortex/tools/lfi.py +423 -216
- vortex/tools/listings.py +109 -40
- vortex/tools/names.py +218 -156
- vortex/tools/net.py +655 -299
- vortex/tools/parallelism.py +93 -61
- vortex/tools/prestaging.py +55 -31
- vortex/tools/schedulers.py +172 -105
- vortex/tools/services.py +403 -334
- vortex/tools/storage.py +293 -358
- vortex/tools/surfex.py +24 -24
- vortex/tools/systems.py +1234 -643
- vortex/tools/targets.py +156 -100
- vortex/util/__init__.py +1 -1
- vortex/util/config.py +378 -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.1.0.dist-info/METADATA +67 -0
- vortex_nwp-2.1.0.dist-info/RECORD +144 -0
- {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.1.0.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.1.0.dist-info/licenses}/LICENSE +0 -0
- {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.1.0.dist-info}/top_level.txt +0 -0
vortex/nwp/algo/eps.py
CHANGED
|
@@ -29,27 +29,27 @@ class Svect(IFSParallel):
|
|
|
29
29
|
"""Singular vectors computation."""
|
|
30
30
|
|
|
31
31
|
_footprint = dict(
|
|
32
|
-
info=
|
|
33
|
-
attr
|
|
34
|
-
kind
|
|
35
|
-
values
|
|
36
|
-
remap
|
|
32
|
+
info="Computation of the singular vectors.",
|
|
33
|
+
attr=dict(
|
|
34
|
+
kind=dict(
|
|
35
|
+
values=["svectors", "svector", "sv", "svect", "svarpe"],
|
|
36
|
+
remap=dict(autoremap="first"),
|
|
37
37
|
),
|
|
38
|
-
conf
|
|
39
|
-
type
|
|
40
|
-
optional
|
|
41
|
-
default
|
|
38
|
+
conf=dict(
|
|
39
|
+
type=int,
|
|
40
|
+
optional=True,
|
|
41
|
+
default=601,
|
|
42
42
|
),
|
|
43
|
-
xpname
|
|
44
|
-
optional
|
|
45
|
-
default
|
|
43
|
+
xpname=dict(
|
|
44
|
+
optional=True,
|
|
45
|
+
default="SVEC",
|
|
46
46
|
),
|
|
47
|
-
)
|
|
47
|
+
),
|
|
48
48
|
)
|
|
49
49
|
|
|
50
50
|
@property
|
|
51
51
|
def realkind(self):
|
|
52
|
-
return
|
|
52
|
+
return "svector"
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
class Combi(BlindRun, DrHookDecoMixin, EcGribDecoMixin):
|
|
@@ -59,7 +59,7 @@ class Combi(BlindRun, DrHookDecoMixin, EcGribDecoMixin):
|
|
|
59
59
|
|
|
60
60
|
def execute(self, rh, opts):
|
|
61
61
|
"""Standard Combi execution."""
|
|
62
|
-
namsec = self.setlink(initrole=
|
|
62
|
+
namsec = self.setlink(initrole="Namelist", initkind="namelist")
|
|
63
63
|
namsec[0].rh.container.cat()
|
|
64
64
|
super().execute(rh, opts)
|
|
65
65
|
|
|
@@ -68,28 +68,44 @@ class Combi(BlindRun, DrHookDecoMixin, EcGribDecoMixin):
|
|
|
68
68
|
raise NotImplementedError("Abstract property")
|
|
69
69
|
|
|
70
70
|
def _addNmod(self, namrh, msg):
|
|
71
|
-
namrh.contents[
|
|
71
|
+
namrh.contents["NAMMOD"]["NMOD"] = self.nmod
|
|
72
72
|
logger.info("NMOD set to %d: %s.", self.nmod, msg)
|
|
73
73
|
|
|
74
74
|
def _analysis_cp(self, nb, msg):
|
|
75
75
|
# Copy the analysis
|
|
76
|
-
initsec = self.setlink(initkind=
|
|
77
|
-
radical = re.sub(
|
|
76
|
+
initsec = self.setlink(initkind="analysis")
|
|
77
|
+
radical = re.sub(
|
|
78
|
+
r"^(.*?)\d+$", r"\1", initsec[0].rh.container.localpath()
|
|
79
|
+
)
|
|
78
80
|
for num in footprints.util.rangex(1, nb):
|
|
79
|
-
self.system.cp(
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
self.system.cp(
|
|
82
|
+
initsec[0].rh.container.localpath(),
|
|
83
|
+
radical + "{:03d}".format(num),
|
|
84
|
+
fmt=initsec[0].rh.container.actualfmt,
|
|
85
|
+
intent=intent.INOUT,
|
|
86
|
+
)
|
|
82
87
|
logger.info("Copy the analysis for the %d %s.", nb, msg)
|
|
83
88
|
|
|
84
89
|
def _coeff_picking(self, kind, msg):
|
|
85
90
|
# Pick up the coeff in the namelist
|
|
86
|
-
for namsec in self.context.sequence.effective_inputs(kind=
|
|
91
|
+
for namsec in self.context.sequence.effective_inputs(kind="namelist"):
|
|
87
92
|
namsec.rh.reset_contents()
|
|
88
|
-
if
|
|
89
|
-
logger.info(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
if "NAMCOEF" + kind.upper() in namsec.rh.contents:
|
|
94
|
+
logger.info(
|
|
95
|
+
"Extract the "
|
|
96
|
+
+ msg
|
|
97
|
+
+ " coefficient from the updated namelist."
|
|
98
|
+
)
|
|
99
|
+
coeff = {
|
|
100
|
+
"rcoef" + kind: float(
|
|
101
|
+
namsec.rh.contents["NAMCOEF" + kind.upper()][
|
|
102
|
+
"RCOEF" + kind.upper()
|
|
103
|
+
]
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
self.system.json_dump(
|
|
107
|
+
coeff, "coeff" + kind + ".out", indent=4, cls=ShellEncoder
|
|
108
|
+
)
|
|
93
109
|
|
|
94
110
|
|
|
95
111
|
class CombiPert(Combi):
|
|
@@ -97,9 +113,9 @@ class CombiPert(Combi):
|
|
|
97
113
|
|
|
98
114
|
_abstract = True
|
|
99
115
|
_footprint = dict(
|
|
100
|
-
attr
|
|
101
|
-
nbpert
|
|
102
|
-
type
|
|
116
|
+
attr=dict(
|
|
117
|
+
nbpert=dict(
|
|
118
|
+
type=int,
|
|
103
119
|
),
|
|
104
120
|
)
|
|
105
121
|
)
|
|
@@ -109,15 +125,18 @@ class CombiPert(Combi):
|
|
|
109
125
|
super().prepare(rh, opts)
|
|
110
126
|
|
|
111
127
|
# Tweak the namelists
|
|
112
|
-
for namsec in self.context.sequence.effective_inputs(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
128
|
+
for namsec in self.context.sequence.effective_inputs(
|
|
129
|
+
role=re.compile("Namelist"), kind="namelist"
|
|
130
|
+
):
|
|
131
|
+
logger.info(
|
|
132
|
+
"Add the NBPERT coefficient to the NAMENS namelist entry"
|
|
133
|
+
)
|
|
134
|
+
namsec.rh.contents["NAMENS"]["NBPERT"] = self.nbpert
|
|
116
135
|
namsec.rh.save()
|
|
117
136
|
|
|
118
137
|
|
|
119
138
|
#: Definition of a named tuple that holds informations on SV for a given zone
|
|
120
|
-
_SvInfoTuple = collections.namedtuple(
|
|
139
|
+
_SvInfoTuple = collections.namedtuple("SvInfoTuple", ["available", "expected"])
|
|
121
140
|
|
|
122
141
|
|
|
123
142
|
class CombiSV(CombiPert):
|
|
@@ -125,10 +144,10 @@ class CombiSV(CombiPert):
|
|
|
125
144
|
|
|
126
145
|
_abstract = True
|
|
127
146
|
_footprint = dict(
|
|
128
|
-
attr
|
|
129
|
-
info_fname
|
|
130
|
-
default
|
|
131
|
-
optional
|
|
147
|
+
attr=dict(
|
|
148
|
+
info_fname=dict(
|
|
149
|
+
default="singular_vectors_info.json",
|
|
150
|
+
optional=True,
|
|
132
151
|
),
|
|
133
152
|
)
|
|
134
153
|
)
|
|
@@ -140,76 +159,111 @@ class CombiSV(CombiPert):
|
|
|
140
159
|
# Check the number of singular vectors and link them in succession
|
|
141
160
|
nbVectTmp = collections.OrderedDict()
|
|
142
161
|
totalVects = 0
|
|
143
|
-
svec_sections = self.context.sequence.filtered_inputs(
|
|
162
|
+
svec_sections = self.context.sequence.filtered_inputs(
|
|
163
|
+
role="SingularVectors", kind="svector"
|
|
164
|
+
)
|
|
144
165
|
for svecsec in svec_sections:
|
|
145
|
-
c_match = re.match(
|
|
146
|
-
|
|
166
|
+
c_match = re.match(
|
|
167
|
+
r"^([^+,.]+)[+,.][^+,.]+[+,.][^+,.]+(.*)$",
|
|
168
|
+
svecsec.rh.container.localpath(),
|
|
169
|
+
)
|
|
147
170
|
if c_match is None:
|
|
148
|
-
logger.critical(
|
|
149
|
-
|
|
171
|
+
logger.critical(
|
|
172
|
+
"The SV name is not formated correctly: %s",
|
|
173
|
+
svecsec.rh.container.actualpath(),
|
|
174
|
+
)
|
|
150
175
|
(radical, suffix) = c_match.groups()
|
|
151
176
|
zone = svecsec.rh.resource.zone
|
|
152
177
|
nbVectTmp.setdefault(zone, [0, 0])
|
|
153
178
|
nbVectTmp[zone][1] += 1 # Expected
|
|
154
|
-
if svecsec.stage ==
|
|
179
|
+
if svecsec.stage == "get":
|
|
155
180
|
totalVects += 1
|
|
156
181
|
nbVectTmp[zone][0] += 1 # Available
|
|
157
|
-
self.system.softlink(
|
|
158
|
-
|
|
182
|
+
self.system.softlink(
|
|
183
|
+
svecsec.rh.container.localpath(),
|
|
184
|
+
radical + "{:03d}".format(totalVects) + suffix,
|
|
185
|
+
)
|
|
159
186
|
# Convert the temporary dictionary to a dictionary of tuples
|
|
160
187
|
nbVect = collections.OrderedDict()
|
|
161
188
|
for k, v in nbVectTmp.items():
|
|
162
189
|
nbVect[k] = _SvInfoTuple(*v)
|
|
163
|
-
logger.info(
|
|
164
|
-
|
|
165
|
-
|
|
190
|
+
logger.info(
|
|
191
|
+
"Number of vectors :\n"
|
|
192
|
+
+ "\n".join(
|
|
193
|
+
[
|
|
194
|
+
"- {0:8s}: {1.available:3d} ({1.expected:3d} expected).".format(
|
|
195
|
+
z, n
|
|
196
|
+
)
|
|
197
|
+
for z, n in nbVect.items()
|
|
198
|
+
]
|
|
199
|
+
)
|
|
200
|
+
)
|
|
166
201
|
# Writing the singular vectors per areas in a json file
|
|
167
202
|
self.system.json_dump(nbVect, self.info_fname)
|
|
168
203
|
|
|
169
204
|
# Tweak the namelists
|
|
170
|
-
namsecs = self.context.sequence.effective_inputs(
|
|
205
|
+
namsecs = self.context.sequence.effective_inputs(
|
|
206
|
+
role=re.compile("Namelist"), kind="namelist"
|
|
207
|
+
)
|
|
171
208
|
for namsec in namsecs:
|
|
172
|
-
namsec.rh.contents[
|
|
173
|
-
namsec.rh.contents[
|
|
174
|
-
namsec.rh.contents[
|
|
209
|
+
namsec.rh.contents["NAMMOD"]["LVS"] = True
|
|
210
|
+
namsec.rh.contents["NAMMOD"]["LANAP"] = False
|
|
211
|
+
namsec.rh.contents["NAMMOD"]["LBRED"] = False
|
|
175
212
|
logger.info("Added to NVSZONE namelist entry")
|
|
176
|
-
namsec.rh.contents[
|
|
213
|
+
namsec.rh.contents["NAMOPTI"]["NVSZONE"] = [
|
|
177
214
|
v.available for v in nbVect.values() if v.available
|
|
178
215
|
] # Zones with 0 vectors are discarded
|
|
179
216
|
|
|
180
|
-
nbVectNam = namsec.rh.contents[
|
|
217
|
+
nbVectNam = namsec.rh.contents["NAMENS"]["NBVECT"]
|
|
181
218
|
if int(nbVectNam) != totalVects:
|
|
182
|
-
logger.warning(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
219
|
+
logger.warning(
|
|
220
|
+
"%s singular vectors expected but only %d accounted for.",
|
|
221
|
+
nbVectNam,
|
|
222
|
+
totalVects,
|
|
223
|
+
)
|
|
224
|
+
logger.info(
|
|
225
|
+
"Update the total number of vectors in the NBVECT namelist entry"
|
|
226
|
+
)
|
|
227
|
+
namsec.rh.contents["NAMENS"]["NBVECT"] = totalVects
|
|
228
|
+
|
|
229
|
+
actualZones = [
|
|
230
|
+
k for k, v in nbVect.items() if v.available
|
|
231
|
+
] # Zones with 0 vectors are discarded
|
|
188
232
|
nbzone = len(actualZones)
|
|
189
|
-
namsec.rh.contents[
|
|
190
|
-
namsec.rh.contents[
|
|
191
|
-
nbrc = len(namsec.rh.contents[
|
|
233
|
+
namsec.rh.contents["NAMOPTI"]["NBZONE"] = nbzone
|
|
234
|
+
namsec.rh.contents["NAMOPTI"]["CNOMZONE"] = actualZones
|
|
235
|
+
nbrc = len(namsec.rh.contents["NAMOPTI"].RC)
|
|
192
236
|
if nbrc != nbzone:
|
|
193
|
-
logger.critical(
|
|
194
|
-
|
|
237
|
+
logger.critical(
|
|
238
|
+
"%d zones but NAMOPTI/RC has length %d" % (nbzone, nbrc)
|
|
239
|
+
)
|
|
240
|
+
nbrl = len(namsec.rh.contents["NAMOPTI"].RL)
|
|
195
241
|
if nbrl != nbzone:
|
|
196
|
-
logger.critical(
|
|
242
|
+
logger.critical(
|
|
243
|
+
"%d zones but NAMOPTI/RL has length %d" % (nbzone, nbrl)
|
|
244
|
+
)
|
|
197
245
|
|
|
198
246
|
self._addNmod(namsec.rh, "combination of the SV")
|
|
199
247
|
namsec.rh.save()
|
|
200
248
|
|
|
201
249
|
# Copy the analysis to give all the perturbations a basis
|
|
202
|
-
self._analysis_cp(self.nbpert,
|
|
250
|
+
self._analysis_cp(self.nbpert, "perturbations")
|
|
203
251
|
|
|
204
252
|
|
|
205
253
|
class CombiSVunit(CombiSV):
|
|
206
254
|
"""Combine the unit SV to create the raw perturbations by gaussian sampling."""
|
|
207
255
|
|
|
208
256
|
_footprint = dict(
|
|
209
|
-
attr
|
|
210
|
-
kind
|
|
211
|
-
values
|
|
212
|
-
|
|
257
|
+
attr=dict(
|
|
258
|
+
kind=dict(
|
|
259
|
+
values=[
|
|
260
|
+
"sv2unitpert",
|
|
261
|
+
"init",
|
|
262
|
+
"combi_init",
|
|
263
|
+
],
|
|
264
|
+
remap=dict(
|
|
265
|
+
combi_init="init",
|
|
266
|
+
),
|
|
213
267
|
),
|
|
214
268
|
)
|
|
215
269
|
)
|
|
@@ -226,10 +280,14 @@ class CombiSVnorm(CombiSV):
|
|
|
226
280
|
"""
|
|
227
281
|
|
|
228
282
|
_footprint = dict(
|
|
229
|
-
attr
|
|
230
|
-
kind
|
|
231
|
-
values
|
|
232
|
-
|
|
283
|
+
attr=dict(
|
|
284
|
+
kind=dict(
|
|
285
|
+
values=[
|
|
286
|
+
"sv2normedpert",
|
|
287
|
+
"optim",
|
|
288
|
+
"combi_optim",
|
|
289
|
+
],
|
|
290
|
+
remap=dict(autoremap="first"),
|
|
233
291
|
),
|
|
234
292
|
)
|
|
235
293
|
)
|
|
@@ -237,7 +295,7 @@ class CombiSVnorm(CombiSV):
|
|
|
237
295
|
def postfix(self, rh, opts):
|
|
238
296
|
"""Post processing cleaning."""
|
|
239
297
|
# Pick up the coeff in the namelist
|
|
240
|
-
self._coeff_picking(
|
|
298
|
+
self._coeff_picking("vs", "SV")
|
|
241
299
|
super().postfix(rh, opts)
|
|
242
300
|
|
|
243
301
|
@property
|
|
@@ -249,19 +307,23 @@ class CombiIC(Combi):
|
|
|
249
307
|
"""Combine the SV and AE or breeding perturbations to create the initial conditions."""
|
|
250
308
|
|
|
251
309
|
_footprint = dict(
|
|
252
|
-
attr
|
|
253
|
-
kind
|
|
254
|
-
values
|
|
255
|
-
|
|
310
|
+
attr=dict(
|
|
311
|
+
kind=dict(
|
|
312
|
+
values=[
|
|
313
|
+
"pert2ic",
|
|
314
|
+
"sscales",
|
|
315
|
+
"combi_sscales",
|
|
316
|
+
],
|
|
317
|
+
remap=dict(autoremap="first"),
|
|
256
318
|
),
|
|
257
|
-
nbic
|
|
258
|
-
alias
|
|
259
|
-
type
|
|
319
|
+
nbic=dict(
|
|
320
|
+
alias=("nbruns",),
|
|
321
|
+
type=int,
|
|
260
322
|
),
|
|
261
|
-
nbpert
|
|
262
|
-
type
|
|
263
|
-
optional
|
|
264
|
-
default
|
|
323
|
+
nbpert=dict(
|
|
324
|
+
type=int,
|
|
325
|
+
optional=True,
|
|
326
|
+
default=0,
|
|
265
327
|
),
|
|
266
328
|
)
|
|
267
329
|
)
|
|
@@ -275,80 +337,121 @@ class CombiIC(Combi):
|
|
|
275
337
|
super().prepare(rh, opts)
|
|
276
338
|
|
|
277
339
|
# Tweak the namelist
|
|
278
|
-
namsec = self.setlink(initrole=
|
|
279
|
-
nammod = namsec[0].rh.contents[
|
|
340
|
+
namsec = self.setlink(initrole="Namelist", initkind="namelist")
|
|
341
|
+
nammod = namsec[0].rh.contents["NAMMOD"]
|
|
280
342
|
|
|
281
343
|
# The footprint's value is always preferred to the calculated one
|
|
282
344
|
nbPert = self.nbpert
|
|
283
345
|
|
|
284
346
|
# Dealing with singular vectors
|
|
285
|
-
sv_sections = self.context.sequence.effective_inputs(role=
|
|
286
|
-
nammod[
|
|
347
|
+
sv_sections = self.context.sequence.effective_inputs(role="CoeffSV")
|
|
348
|
+
nammod["LVS"] = bool(sv_sections)
|
|
287
349
|
if sv_sections:
|
|
288
|
-
logger.info(
|
|
289
|
-
|
|
290
|
-
|
|
350
|
+
logger.info(
|
|
351
|
+
"Add the SV coefficient to the NAMCOEFVS namelist entry."
|
|
352
|
+
)
|
|
353
|
+
namcoefvs = namsec[0].rh.contents.newblock("NAMCOEFVS")
|
|
354
|
+
namcoefvs["RCOEFVS"] = sv_sections[0].rh.contents["rcoefvs"]
|
|
291
355
|
# The mean value may be present among the SV inputs: remove it
|
|
292
|
-
svsecs = [
|
|
293
|
-
|
|
294
|
-
|
|
356
|
+
svsecs = [
|
|
357
|
+
sec
|
|
358
|
+
for sec in self.context.sequence.effective_inputs(
|
|
359
|
+
role="SVPerturbedState"
|
|
360
|
+
)
|
|
361
|
+
or [
|
|
362
|
+
sec
|
|
363
|
+
for sec in self.context.sequence.effective_inputs(
|
|
364
|
+
role="PerturbedState"
|
|
365
|
+
)
|
|
366
|
+
if "ICHR" in sec.rh.container.filename
|
|
367
|
+
]
|
|
368
|
+
if sec.rh.resource.number
|
|
369
|
+
]
|
|
295
370
|
nbPert = nbPert or len(svsecs)
|
|
296
371
|
|
|
297
372
|
# Dealing with breeding method's inputs
|
|
298
|
-
bd_sections = self.context.sequence.effective_inputs(
|
|
299
|
-
|
|
373
|
+
bd_sections = self.context.sequence.effective_inputs(
|
|
374
|
+
role="CoeffBreeding"
|
|
375
|
+
)
|
|
376
|
+
nammod["LBRED"] = bool(bd_sections)
|
|
300
377
|
if bd_sections:
|
|
301
|
-
logger.info(
|
|
302
|
-
|
|
303
|
-
|
|
378
|
+
logger.info(
|
|
379
|
+
"Add the breeding coefficient to the NAMCOEFBM namelist entry."
|
|
380
|
+
)
|
|
381
|
+
namcoefbm = namsec[0].rh.contents.newblock("NAMCOEFBM")
|
|
382
|
+
namcoefbm["RCOEFBM"] = bd_sections[0].rh.contents["rcoefbm"]
|
|
304
383
|
nbBd = len(
|
|
305
|
-
self.context.sequence.effective_inputs(
|
|
384
|
+
self.context.sequence.effective_inputs(
|
|
385
|
+
role="BreedingPerturbedState"
|
|
386
|
+
)
|
|
306
387
|
or [
|
|
307
388
|
sec
|
|
308
|
-
for sec in self.context.sequence.effective_inputs(
|
|
309
|
-
|
|
389
|
+
for sec in self.context.sequence.effective_inputs(
|
|
390
|
+
role="PerturbedState"
|
|
391
|
+
)
|
|
392
|
+
if "BMHR" in sec.rh.container.filename
|
|
310
393
|
]
|
|
311
394
|
)
|
|
312
395
|
# symmetric perturbations except if analysis: one more file
|
|
313
396
|
# or zero if one control ic (hypothesis: odd nbic)
|
|
314
|
-
nbPert = nbPert or (
|
|
315
|
-
|
|
316
|
-
|
|
397
|
+
nbPert = nbPert or (
|
|
398
|
+
nbBd - 1
|
|
399
|
+
if nbBd == self.nbic + 1
|
|
400
|
+
or (nbBd == self.nbic and self.nbic % 2 != 0)
|
|
401
|
+
else self.nbic // 2
|
|
402
|
+
)
|
|
317
403
|
|
|
318
404
|
# Dealing with initial conditions from the assimilation ensemble
|
|
319
405
|
# the mean value may be present among the AE inputs: remove it
|
|
320
|
-
aesecs = [
|
|
321
|
-
|
|
322
|
-
|
|
406
|
+
aesecs = [
|
|
407
|
+
sec
|
|
408
|
+
for sec in self.context.sequence.effective_inputs(
|
|
409
|
+
role=("AEPerturbedState", "ModelState")
|
|
410
|
+
)
|
|
411
|
+
if sec.rh.resource.number
|
|
412
|
+
]
|
|
413
|
+
nammod["LANAP"] = bool(aesecs)
|
|
323
414
|
nbAe = len(aesecs)
|
|
324
415
|
nbPert = nbPert or nbAe
|
|
325
416
|
# If less AE members (but nor too less) than ic to build
|
|
326
417
|
if nbAe < nbPert <= 2 * nbAe:
|
|
327
|
-
logger.info(
|
|
328
|
-
|
|
329
|
-
|
|
418
|
+
logger.info(
|
|
419
|
+
"%d AE perturbations needed, %d AE members available: the first ones are duplicated.",
|
|
420
|
+
nbPert,
|
|
421
|
+
nbAe,
|
|
422
|
+
)
|
|
423
|
+
prefix = aesecs[0].rh.container.filename.split("_")[0]
|
|
330
424
|
for num in range(nbAe, nbPert):
|
|
331
|
-
self.system.softlink(
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
logger.info(
|
|
337
|
-
|
|
425
|
+
self.system.softlink(
|
|
426
|
+
aesecs[num - nbAe].rh.container.filename,
|
|
427
|
+
prefix + "_{:03d}".format(num + 1),
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
logger.info(
|
|
431
|
+
"NAMMOD namelist summary: LANAP=%s, LVS=%s, LBRED=%s.",
|
|
432
|
+
*[nammod[k] for k in ("LANAP", "LVS", "LBRED")],
|
|
433
|
+
)
|
|
434
|
+
logger.info(
|
|
435
|
+
"Add the NBPERT=%d coefficient to the NAMENS namelist entry.",
|
|
436
|
+
nbPert,
|
|
437
|
+
)
|
|
438
|
+
namsec[0].rh.contents["NAMENS"]["NBPERT"] = nbPert
|
|
338
439
|
|
|
339
440
|
# symmectric perturbations ?
|
|
340
441
|
if nbPert < self.nbic - 1:
|
|
341
|
-
namsec[0].rh.contents[
|
|
442
|
+
namsec[0].rh.contents["NAMENS"]["LMIRROR"] = True
|
|
342
443
|
logger.info("Add LMIRROR=.TRUE. to the NAMENS namelist entry.")
|
|
343
|
-
elif
|
|
344
|
-
|
|
444
|
+
elif (
|
|
445
|
+
nbPert != 1
|
|
446
|
+
): # 1 pert, 2 ic is possible without mirror adding the control
|
|
447
|
+
namsec[0].rh.contents["NAMENS"]["LMIRROR"] = False
|
|
345
448
|
logger.info("Add LMIRROR=.FALSE. to the NAMENS namelist entry.")
|
|
346
449
|
|
|
347
450
|
self._addNmod(namsec[0].rh, "final combination of the perturbations")
|
|
348
451
|
namsec[0].rh.save()
|
|
349
452
|
|
|
350
453
|
# Copy the analysis to give all the members a basis
|
|
351
|
-
self._analysis_cp(self.nbic - 1,
|
|
454
|
+
self._analysis_cp(self.nbic - 1, "perturbed states")
|
|
352
455
|
|
|
353
456
|
|
|
354
457
|
class CombiBreeding(CombiPert):
|
|
@@ -358,10 +461,14 @@ class CombiBreeding(CombiPert):
|
|
|
358
461
|
"""
|
|
359
462
|
|
|
360
463
|
_footprint = dict(
|
|
361
|
-
attr
|
|
362
|
-
kind
|
|
363
|
-
values
|
|
364
|
-
|
|
464
|
+
attr=dict(
|
|
465
|
+
kind=dict(
|
|
466
|
+
values=[
|
|
467
|
+
"fc2bredpert",
|
|
468
|
+
"breeding",
|
|
469
|
+
"combi_breeding",
|
|
470
|
+
],
|
|
471
|
+
remap=dict(autoremap="first"),
|
|
365
472
|
),
|
|
366
473
|
)
|
|
367
474
|
)
|
|
@@ -375,25 +482,31 @@ class CombiBreeding(CombiPert):
|
|
|
375
482
|
super().prepare(rh, opts)
|
|
376
483
|
|
|
377
484
|
# Consistent naming with the Fortran execution
|
|
378
|
-
hst_sections = self.context.sequence.effective_inputs(
|
|
485
|
+
hst_sections = self.context.sequence.effective_inputs(
|
|
486
|
+
kind=("pert", "historic")
|
|
487
|
+
)
|
|
379
488
|
for num, hst in enumerate(hst_sections):
|
|
380
|
-
self.system.softlink(
|
|
381
|
-
|
|
382
|
-
|
|
489
|
+
self.system.softlink(
|
|
490
|
+
hst.rh.container.localpath(),
|
|
491
|
+
re.sub(r"^(.*?)\d+$", r"\1", hst.rh.container.localpath())
|
|
492
|
+
+ "{:03d}.grb".format(num + 1),
|
|
493
|
+
)
|
|
383
494
|
logger.info("Rename the %d grib files consecutively.", num)
|
|
384
495
|
|
|
385
496
|
# Tweak the namelist
|
|
386
|
-
namsec = self.setlink(initrole=
|
|
387
|
-
namsec[0].rh.contents[
|
|
388
|
-
namsec[0].rh.contents[
|
|
389
|
-
namsec[0].rh.contents[
|
|
390
|
-
self._addNmod(
|
|
497
|
+
namsec = self.setlink(initrole="Namelist", initkind="namelist")
|
|
498
|
+
namsec[0].rh.contents["NAMMOD"]["LBRED"] = True
|
|
499
|
+
namsec[0].rh.contents["NAMMOD"]["LANAP"] = False
|
|
500
|
+
namsec[0].rh.contents["NAMMOD"]["LVS"] = False
|
|
501
|
+
self._addNmod(
|
|
502
|
+
namsec[0].rh, "compute the coefficient of the bred modes"
|
|
503
|
+
)
|
|
391
504
|
namsec[0].rh.save()
|
|
392
505
|
|
|
393
506
|
def postfix(self, rh, opts):
|
|
394
507
|
"""Post processing cleaning."""
|
|
395
508
|
# Pick up the coeff in the namelist
|
|
396
|
-
self._coeff_picking(
|
|
509
|
+
self._coeff_picking("bm", "breeding")
|
|
397
510
|
super().postfix(rh, opts)
|
|
398
511
|
|
|
399
512
|
|
|
@@ -404,13 +517,16 @@ class SurfCombiIC(BlindRun):
|
|
|
404
517
|
"""
|
|
405
518
|
|
|
406
519
|
_footprint = dict(
|
|
407
|
-
attr
|
|
408
|
-
kind
|
|
409
|
-
values
|
|
410
|
-
|
|
520
|
+
attr=dict(
|
|
521
|
+
kind=dict(
|
|
522
|
+
values=[
|
|
523
|
+
"surf_pert2ic",
|
|
524
|
+
"surf2ic",
|
|
525
|
+
],
|
|
526
|
+
remap=dict(autoremap="first"),
|
|
411
527
|
),
|
|
412
|
-
member
|
|
413
|
-
type
|
|
528
|
+
member=dict(
|
|
529
|
+
type=int,
|
|
414
530
|
),
|
|
415
531
|
)
|
|
416
532
|
)
|
|
@@ -419,15 +535,17 @@ class SurfCombiIC(BlindRun):
|
|
|
419
535
|
"""Set some variables according to target definition."""
|
|
420
536
|
super().prepare(rh, opts)
|
|
421
537
|
|
|
422
|
-
icsec = self.setlink(
|
|
423
|
-
|
|
538
|
+
icsec = self.setlink(
|
|
539
|
+
initrole=("SurfaceAnalysis", "SurfaceInitialCondition"),
|
|
540
|
+
initkind="ic",
|
|
541
|
+
)
|
|
424
542
|
actualdate = icsec[0].rh.resource.date
|
|
425
543
|
seed = int(actualdate.ymdh) + (actualdate.hour + 1) * (self.member + 1)
|
|
426
544
|
|
|
427
545
|
# Tweak the namelist
|
|
428
|
-
namsec = self.setlink(initrole=
|
|
546
|
+
namsec = self.setlink(initrole="Namelist", initkind="namelist")
|
|
429
547
|
logger.info("ISEED added to NAMSFC namelist entry: %d", seed)
|
|
430
|
-
namsec[0].rh.contents[
|
|
548
|
+
namsec[0].rh.contents["NAMSFC"]["ISEED"] = seed
|
|
431
549
|
namsec[0].rh.save()
|
|
432
550
|
|
|
433
551
|
|
|
@@ -435,27 +553,30 @@ class Clustering(BlindRun, EcGribDecoMixin):
|
|
|
435
553
|
"""Select by clustering a sample of members among the whole set."""
|
|
436
554
|
|
|
437
555
|
_footprint = dict(
|
|
438
|
-
attr
|
|
439
|
-
kind
|
|
440
|
-
values
|
|
441
|
-
|
|
556
|
+
attr=dict(
|
|
557
|
+
kind=dict(
|
|
558
|
+
values=[
|
|
559
|
+
"clustering",
|
|
560
|
+
"clust",
|
|
561
|
+
],
|
|
562
|
+
remap=dict(autoremap="first"),
|
|
442
563
|
),
|
|
443
|
-
fileoutput
|
|
444
|
-
optional
|
|
445
|
-
default
|
|
564
|
+
fileoutput=dict(
|
|
565
|
+
optional=True,
|
|
566
|
+
default="_griblist",
|
|
446
567
|
),
|
|
447
|
-
nbclust
|
|
448
|
-
type
|
|
568
|
+
nbclust=dict(
|
|
569
|
+
type=int,
|
|
449
570
|
),
|
|
450
|
-
nbmembers
|
|
451
|
-
type
|
|
452
|
-
optional
|
|
453
|
-
access
|
|
571
|
+
nbmembers=dict(
|
|
572
|
+
type=int,
|
|
573
|
+
optional=True,
|
|
574
|
+
access="rwx",
|
|
454
575
|
),
|
|
455
|
-
gribfilter_tasks
|
|
456
|
-
type
|
|
457
|
-
optional
|
|
458
|
-
default
|
|
576
|
+
gribfilter_tasks=dict(
|
|
577
|
+
type=int,
|
|
578
|
+
optional=True,
|
|
579
|
+
default=8,
|
|
459
580
|
),
|
|
460
581
|
)
|
|
461
582
|
)
|
|
@@ -464,34 +585,49 @@ class Clustering(BlindRun, EcGribDecoMixin):
|
|
|
464
585
|
"""Set some variables according to target definition."""
|
|
465
586
|
super().prepare(rh, opts)
|
|
466
587
|
|
|
467
|
-
grib_sections = self.context.sequence.effective_inputs(
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
588
|
+
grib_sections = self.context.sequence.effective_inputs(
|
|
589
|
+
role="ModelState", kind="gridpoint"
|
|
590
|
+
)
|
|
591
|
+
avail_json = self.context.sequence.effective_inputs(
|
|
592
|
+
role="AvailableMembers", kind="mbpopulation"
|
|
593
|
+
)
|
|
471
594
|
|
|
472
595
|
# If no population file is here, just do a sort on the file list,
|
|
473
596
|
# otherwise use the population list
|
|
474
597
|
if avail_json:
|
|
475
|
-
population = avail_json[0].rh.contents.data[
|
|
598
|
+
population = avail_json[0].rh.contents.data["population"]
|
|
476
599
|
self.nbmembers = len(population)
|
|
477
600
|
file_list = list()
|
|
478
601
|
terms_set = set()
|
|
479
602
|
for elt in population:
|
|
480
603
|
sublist_ids = list()
|
|
481
|
-
for
|
|
604
|
+
for i, grib in enumerate(grib_sections):
|
|
482
605
|
# If the grib file matches, let's go
|
|
483
|
-
if all(
|
|
484
|
-
|
|
606
|
+
if all(
|
|
607
|
+
[
|
|
608
|
+
grib.rh.wide_key_lookup(key, exports=True) == value
|
|
609
|
+
for (key, value) in elt.items()
|
|
610
|
+
]
|
|
611
|
+
):
|
|
485
612
|
sublist_ids.append(i)
|
|
486
613
|
# Stack the gribs in file_list
|
|
487
|
-
file_list.extend(
|
|
488
|
-
|
|
489
|
-
|
|
614
|
+
file_list.extend(
|
|
615
|
+
sorted(
|
|
616
|
+
[
|
|
617
|
+
str(grib_sections[i].rh.container.localpath())
|
|
618
|
+
for i in sublist_ids
|
|
619
|
+
]
|
|
620
|
+
)
|
|
621
|
+
)
|
|
622
|
+
terms_set.update(
|
|
623
|
+
[grib_sections[i].rh.resource.term for i in sublist_ids]
|
|
624
|
+
)
|
|
490
625
|
for i in reversed(sublist_ids):
|
|
491
626
|
del grib_sections[i]
|
|
492
627
|
else:
|
|
493
|
-
file_list = sorted(
|
|
494
|
-
|
|
628
|
+
file_list = sorted(
|
|
629
|
+
[str(grib.rh.container.localpath()) for grib in grib_sections]
|
|
630
|
+
)
|
|
495
631
|
terms_set = {grib.rh.resource.term for grib in grib_sections}
|
|
496
632
|
|
|
497
633
|
# determine what terms are available to the clustering algorithm
|
|
@@ -501,86 +637,120 @@ class Clustering(BlindRun, EcGribDecoMixin):
|
|
|
501
637
|
cluststep = delta.pop().hour
|
|
502
638
|
else:
|
|
503
639
|
cluststep = -999
|
|
504
|
-
logger.error(
|
|
505
|
-
logger.error(
|
|
506
|
-
logger.error(
|
|
640
|
+
logger.error("Terms are not evenly spaced. What should we do ?")
|
|
641
|
+
logger.error("Terms=" + str(terms) + "delta=" + str(delta))
|
|
642
|
+
logger.error(
|
|
643
|
+
"Continuing with little hope and cluststep = %d", cluststep
|
|
644
|
+
)
|
|
507
645
|
clustdeb = terms[0].hour
|
|
508
646
|
clustfin = terms[-1].hour
|
|
509
|
-
logger.info(
|
|
647
|
+
logger.info(
|
|
648
|
+
"clustering deb=%d fin=%d step=%d", clustdeb, clustfin, cluststep
|
|
649
|
+
)
|
|
510
650
|
|
|
511
651
|
# Deal with xGribs
|
|
512
|
-
file_list_cat = [f +
|
|
513
|
-
parallel_grib_filter(
|
|
514
|
-
|
|
652
|
+
file_list_cat = [f + ".concatenated" for f in file_list]
|
|
653
|
+
parallel_grib_filter(
|
|
654
|
+
self.context,
|
|
655
|
+
file_list,
|
|
656
|
+
file_list_cat,
|
|
657
|
+
cat=True,
|
|
658
|
+
nthreads=self.gribfilter_tasks,
|
|
659
|
+
)
|
|
515
660
|
|
|
516
661
|
if self.nbmembers is None or self.nbmembers > self.nbclust:
|
|
517
|
-
|
|
518
662
|
# Tweak the namelist
|
|
519
|
-
namsec = self.setlink(initrole=
|
|
520
|
-
logger.info(
|
|
521
|
-
|
|
663
|
+
namsec = self.setlink(initrole="Namelist", initkind="namelist")
|
|
664
|
+
logger.info(
|
|
665
|
+
"NBRCLUST added to NAMCLUST namelist entry: %d", self.nbclust
|
|
666
|
+
)
|
|
667
|
+
namsec[0].rh.contents["NAMCLUST"]["NBRCLUST"] = self.nbclust
|
|
522
668
|
if self.nbmembers is not None:
|
|
523
|
-
logger.info(
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
669
|
+
logger.info(
|
|
670
|
+
"NBRMB added to NAMCLUST namelist entry: %d",
|
|
671
|
+
self.nbmembers,
|
|
672
|
+
)
|
|
673
|
+
namsec[0].rh.contents["NAMCLUST"]["NBRMB"] = self.nbmembers
|
|
674
|
+
logger.info(
|
|
675
|
+
"Setting namelist macros ECHDEB=%d ECHFIN=%d ECHSTEP=%d",
|
|
676
|
+
clustdeb,
|
|
677
|
+
clustfin,
|
|
678
|
+
cluststep,
|
|
679
|
+
)
|
|
680
|
+
namsec[0].rh.contents.setmacro("ECHDEB", clustdeb)
|
|
681
|
+
namsec[0].rh.contents.setmacro("ECHFIN", clustfin)
|
|
682
|
+
namsec[0].rh.contents.setmacro("ECHSTEP", cluststep)
|
|
530
683
|
namsec[0].rh.save()
|
|
531
684
|
namsec[0].rh.container.cat()
|
|
532
685
|
|
|
533
|
-
with open(self.fileoutput,
|
|
534
|
-
optFile.write(
|
|
686
|
+
with open(self.fileoutput, "w") as optFile:
|
|
687
|
+
optFile.write("\n".join(file_list_cat))
|
|
535
688
|
|
|
536
689
|
def execute(self, rh, opts):
|
|
537
690
|
# If the number of members is big enough -> normal processing
|
|
538
691
|
if self.nbmembers is None or self.nbmembers > self.nbclust:
|
|
539
|
-
logger.info(
|
|
540
|
-
|
|
692
|
+
logger.info(
|
|
693
|
+
"Normal clustering run (%d members, %d clusters)",
|
|
694
|
+
self.nbmembers,
|
|
695
|
+
self.nbclust,
|
|
696
|
+
)
|
|
541
697
|
super().execute(rh, opts)
|
|
542
698
|
# if not, generate face outputs
|
|
543
699
|
else:
|
|
544
|
-
logger.info(
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
700
|
+
logger.info(
|
|
701
|
+
"Generating fake outputs with %d members", self.nbmembers
|
|
702
|
+
)
|
|
703
|
+
with open("ASCII_CLUST", "w") as fdcl:
|
|
704
|
+
fdcl.write(
|
|
705
|
+
"\n".join(
|
|
706
|
+
[
|
|
707
|
+
"{0:3d} {1:3d} {0:3d}".format(i, 1)
|
|
708
|
+
for i in range(1, self.nbmembers + 1)
|
|
709
|
+
]
|
|
710
|
+
)
|
|
711
|
+
)
|
|
712
|
+
with open("ASCII_RMCLUST", "w") as fdrm:
|
|
713
|
+
fdrm.write(
|
|
714
|
+
"\n".join([str(i) for i in range(1, self.nbmembers + 1)])
|
|
715
|
+
)
|
|
716
|
+
with open("ASCII_POPCLUST", "w") as fdpop:
|
|
717
|
+
fdpop.write("\n".join(["1"] * self.nbmembers))
|
|
552
718
|
|
|
553
719
|
def postfix(self, rh, opts):
|
|
554
720
|
"""Create a JSON with all the clustering informations."""
|
|
555
|
-
avail_json = self.context.sequence.effective_inputs(
|
|
556
|
-
|
|
721
|
+
avail_json = self.context.sequence.effective_inputs(
|
|
722
|
+
role="AvailableMembers", kind="mbpopulation"
|
|
723
|
+
)
|
|
557
724
|
# If no population file is here, does nothing
|
|
558
725
|
if avail_json:
|
|
559
726
|
logger.info("Creating a JSON output...")
|
|
560
727
|
# Read the clustering information
|
|
561
|
-
if self.system.path.exists(
|
|
728
|
+
if self.system.path.exists("ASCII_CLUST"):
|
|
562
729
|
# New format for clustering outputs
|
|
563
|
-
with open(
|
|
730
|
+
with open("ASCII_CLUST") as fdcl:
|
|
564
731
|
cluster_members = list()
|
|
565
732
|
cluster_sizes = list()
|
|
566
733
|
for l in [l.split() for l in fdcl.readlines()]:
|
|
567
734
|
cluster_members.append(int(l[0]))
|
|
568
735
|
cluster_sizes.append(int(l[1]))
|
|
569
736
|
else:
|
|
570
|
-
with open(
|
|
737
|
+
with open("ASCII_RMCLUST") as fdrm:
|
|
571
738
|
cluster_members = [int(m) for m in fdrm.readlines()]
|
|
572
|
-
with open(
|
|
739
|
+
with open("ASCII_POPCLUST") as fdpop:
|
|
573
740
|
cluster_sizes = [int(s) for s in fdpop.readlines()]
|
|
574
741
|
# Update the population JSON
|
|
575
742
|
mycontent = copy.deepcopy(avail_json[0].rh.contents)
|
|
576
|
-
mycontent.data[
|
|
577
|
-
mycontent.data[
|
|
743
|
+
mycontent.data["resource_kind"] = "mbsample"
|
|
744
|
+
mycontent.data["drawing"] = list()
|
|
578
745
|
for member_no, cluster_size in zip(cluster_members, cluster_sizes):
|
|
579
|
-
mycontent.data[
|
|
580
|
-
|
|
746
|
+
mycontent.data["drawing"].append(
|
|
747
|
+
copy.copy(mycontent.data["population"][member_no - 1])
|
|
748
|
+
)
|
|
749
|
+
mycontent.data["drawing"][-1]["cluster_size"] = cluster_size
|
|
581
750
|
# Create a clustering output file
|
|
582
|
-
new_container = footprints.proxy.container(
|
|
583
|
-
|
|
751
|
+
new_container = footprints.proxy.container(
|
|
752
|
+
filename="clustering_output.json", actualfmt="json"
|
|
753
|
+
)
|
|
584
754
|
mycontent.rewrite(new_container)
|
|
585
755
|
|
|
586
756
|
super().postfix(rh, opts)
|
|
@@ -590,13 +760,15 @@ class Addpearp(BlindRun):
|
|
|
590
760
|
"""Add the selected PEARP perturbations to the deterministic AROME initial conditions."""
|
|
591
761
|
|
|
592
762
|
_footprint = dict(
|
|
593
|
-
attr
|
|
594
|
-
kind
|
|
595
|
-
values
|
|
596
|
-
|
|
763
|
+
attr=dict(
|
|
764
|
+
kind=dict(
|
|
765
|
+
values=[
|
|
766
|
+
"addpearp",
|
|
767
|
+
],
|
|
768
|
+
remap=dict(autoremap="first"),
|
|
597
769
|
),
|
|
598
|
-
nbpert
|
|
599
|
-
type
|
|
770
|
+
nbpert=dict(
|
|
771
|
+
type=int,
|
|
600
772
|
),
|
|
601
773
|
)
|
|
602
774
|
)
|
|
@@ -606,8 +778,8 @@ class Addpearp(BlindRun):
|
|
|
606
778
|
super().prepare(rh, opts)
|
|
607
779
|
|
|
608
780
|
# Tweak the namelist
|
|
609
|
-
namsec = self.setlink(initrole=
|
|
781
|
+
namsec = self.setlink(initrole="Namelist", initkind="namelist")
|
|
610
782
|
logger.info("NBE added to NAMIC namelist entry: %d", self.nbpert)
|
|
611
|
-
namsec[0].rh.contents[
|
|
783
|
+
namsec[0].rh.contents["NAMIC"]["NBPERT"] = self.nbpert
|
|
612
784
|
namsec[0].rh.save()
|
|
613
785
|
namsec[0].rh.container.cat()
|