vortex-nwp 2.2.0__py3-none-any.whl → 2.3.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 CHANGED
@@ -50,13 +50,14 @@ from .toolbox import promise as promise
50
50
  from .toolbox import diff as diff
51
51
  from .toolbox import defaults as defaults
52
52
  from .toolbox import algo as task
53
+ from .toolbox import VortexForceComplete as VortexForceComplete
53
54
 
54
55
  from . import nwp as nwp # footprints import
55
56
 
56
- __version__ = "2.2.0"
57
+ __version__ = "2.3.0"
57
58
  __prompt__ = "Vortex v-" + __version__ + ":"
58
59
 
59
- __nextversion__ = "2.2.1"
60
+ __nextversion__ = "2.3.1"
60
61
  __tocinfoline__ = "VORTEX core package"
61
62
 
62
63
  __all__ = [
vortex/algo/components.py CHANGED
@@ -1548,12 +1548,25 @@ class Expresso(ExecutableAlgoComponent):
1548
1548
  attr=dict(
1549
1549
  interpreter=dict(
1550
1550
  info="The interpreter needed to run the script.",
1551
- values=["current", "awk", "ksh", "bash", "perl", "python"],
1551
+ values=[
1552
+ "current",
1553
+ "awk",
1554
+ "ksh",
1555
+ "bash",
1556
+ "perl",
1557
+ "python",
1558
+ "singularity",
1559
+ ],
1552
1560
  ),
1553
1561
  interpreter_path=dict(
1554
1562
  info="The interpreter command.",
1555
1563
  optional=True,
1556
1564
  ),
1565
+ interpreter_args=dict(
1566
+ info="Some options to pass to the expresso",
1567
+ optional=True,
1568
+ default="",
1569
+ ),
1557
1570
  engine=dict(values=["exec", "launch"]),
1558
1571
  ),
1559
1572
  )
@@ -1581,11 +1594,16 @@ class Expresso(ExecutableAlgoComponent):
1581
1594
  )
1582
1595
 
1583
1596
  def _interpreter_args_fix(self, rh, opts):
1597
+ cmd_opts = shlex.split(self.interpreter_args)
1598
+
1584
1599
  absexec = self.absexcutable(rh.container.localpath())
1585
1600
  if self.interpreter == "awk":
1586
- return ["-f", absexec]
1601
+ return [*cmd_opts, "-f", absexec]
1602
+ if self.interpreter == "singularity":
1603
+ return ["run", *cmd_opts, absexec]
1587
1604
  else:
1588
1605
  return [
1606
+ *cmd_opts,
1589
1607
  absexec,
1590
1608
  ]
1591
1609
 
vortex/algo/mpitools.py CHANGED
@@ -544,10 +544,6 @@ class MpiTool(footprints.FootprintBase):
544
544
  """When group are defined, associate each MPI rank with a "real" slot."""
545
545
  if self._ranks_map_cache is None:
546
546
  self._complex_ranks_map = False
547
- if not self.envelope:
548
- raise RuntimeError(
549
- "Ranks mapping should always be used within an envelope."
550
- )
551
547
  # First deal with bingroups
552
548
  ranks_map = dict()
553
549
  has_bin_groups = not all([b.group is None for b in self.binaries])
@@ -624,7 +620,7 @@ class MpiTool(footprints.FootprintBase):
624
620
  if self._complex_ranks_map or do_bin_distribution:
625
621
  if not self.envelope:
626
622
  raise RuntimeError(
627
- "Ranks mapping shoudl always be used within an envelope."
623
+ "Ranks mapping should always be used within an envelope."
628
624
  )
629
625
  if do_bin_distribution:
630
626
  if not self._supports_manual_ranks_mapping:
@@ -1404,7 +1400,10 @@ class SRun(MpiTool):
1404
1400
  ),
1405
1401
  optmap=dict(
1406
1402
  default=footprints.FPDict(
1407
- nn="nodes", nnp="ntasks-per-node", np="ntasks"
1403
+ nn="nodes",
1404
+ nnp="ntasks-per-node",
1405
+ np="ntasks",
1406
+ openmp="cpus-per-task",
1408
1407
  )
1409
1408
  ),
1410
1409
  slurmversion=dict(type=int, optional=True),
@@ -106,6 +106,34 @@ class Script(Executable):
106
106
  return self.rawopts
107
107
 
108
108
 
109
+ class SingularityImage(Executable):
110
+ """Singularity container image executable."""
111
+
112
+ _footprint = dict(
113
+ attr=dict(
114
+ kind=dict(
115
+ values=["image"],
116
+ ),
117
+ cmd=dict(
118
+ info="Command to run inside the container",
119
+ optional=True,
120
+ default="",
121
+ ),
122
+ ),
123
+ )
124
+
125
+ @property
126
+ def realkind(self):
127
+ return "image"
128
+
129
+ # Called by AlgoComponent.spawn_command_line
130
+ def command_line(self, **opts):
131
+ return " ".join(
132
+ ["--{:s} {:s}".format(opt, val) for opt, val in opts.items()]
133
+ + [self.cmd]
134
+ )
135
+
136
+
109
137
  class GnuScript(Executable):
110
138
  """Basic interpreted executable with standard command line arguments."""
111
139
 
@@ -659,6 +659,13 @@ area = indonesie1
659
659
  resolution = 2.50
660
660
  runit = km
661
661
 
662
+ [asscomindonesie2sp]
663
+ info = Arome asscom14 - indonesie2 spectral geometry
664
+ kind = projected
665
+ area = indonesie2
666
+ resolution = 2.50
667
+ runit = km
668
+
662
669
  [lace20km]
663
670
  info = Coupling grid for LACE partners
664
671
  kind = projected
@@ -673,6 +680,13 @@ area = lace
673
680
  resolution = 8.0
674
681
  runit = km
675
682
 
683
+ [lace47]
684
+ info = LACE canari domain
685
+ kind = projected
686
+ area = lace47
687
+ resolution = 4.71
688
+ runit = km
689
+
676
690
  [afgt20km]
677
691
  info = Coupling grid for afgt-Praha
678
692
  kind = projected
@@ -1201,15 +1215,15 @@ nlon = 641
1201
1215
  [paris1s100]
1202
1216
  info = PARIS 0.01
1203
1217
  kind = lonlat
1204
- area = paris1s100
1218
+ area = PARIS1S100
1205
1219
  resolution = 0.01
1206
- nlat = 221
1207
- nlon = 336
1220
+ nlat = 187
1221
+ nlon = 279
1208
1222
 
1209
1223
  [medalp1s100]
1210
1224
  info = MEDALP 0.01
1211
1225
  kind = lonlat
1212
- area = medalp1s100
1226
+ area = MEDALP1S100
1213
1227
  resolution = 0.01
1214
1228
  nlat = 481
1215
1229
  nlon = 501
@@ -1677,6 +1691,17 @@ nlat = 172800
1677
1691
  latmax = 83.999
1678
1692
  latmin = -55.999
1679
1693
 
1694
+ [srtm_01s]
1695
+ info = alternative to no_arctics7.5s used to create arome-500m PGD clim
1696
+ kind = lonlat
1697
+ area = srtm
1698
+ resolution = 1
1699
+ runit = s
1700
+ nlon = 999
1701
+ nlat = 999
1702
+ latmax = 999
1703
+ latmin = 999
1704
+
1680
1705
  [europe_sw3s]
1681
1706
  kind = lonlat
1682
1707
  area = europe_sw
@@ -2468,39 +2493,39 @@ nlon = 896
2468
2493
  resolution = 0.05
2469
2494
  runit = deg
2470
2495
 
2471
- [hycom3dmedoc]
2472
- info = Shom HYCOM3D Med-occidental at 1/60
2496
+ [hycom3dmednowe]
2497
+ info = Shom HYCOM3D Med-NorthWest at 1/60
2473
2498
  kind = lonlat
2474
- area = medoc
2475
- nlat = 331
2476
- nlon = 661
2499
+ area = mednowe
2500
+ nlat = 437
2501
+ nlon = 761
2477
2502
  resolution = 0.017
2478
2503
  runit = deg
2479
2504
 
2480
- [hycom3dmedor]
2481
- info = Shom HYCOM3D Med-oriental at 1/60
2505
+ [hycom3dmedsowe]
2506
+ info = Shom HYCOM3D Med-SouthWest at 1/60
2482
2507
  kind = lonlat
2483
- area = medor
2484
- nlat = 437
2485
- nlon = 645
2508
+ area = medsowe
2509
+ nlat = 481
2510
+ nlon = 1080
2486
2511
  resolution = 0.017
2487
2512
  runit = deg
2488
2513
 
2489
- [hycom3dmedio]
2490
- info = Shom HYCOM3D Med-ionian at 1/60
2514
+ [hycom3dmedea]
2515
+ info = Shom HYCOM3D Med-East at 1/60
2491
2516
  kind = lonlat
2492
- area = medio
2493
- nlat = 557
2494
- nlon = 781
2517
+ area = medea
2518
+ nlat = 706
2519
+ nlon = 855
2495
2520
  resolution = 0.017
2496
2521
  runit = deg
2497
2522
 
2498
- [hycom3dgibal]
2499
- info = Shom HYCOM3D Gibraltair Strait-Alboran Sea at 1/60
2523
+ [hycom3dmedinte]
2524
+ info = Shom HYCOM3D Med-Interior at 1/60
2500
2525
  kind = lonlat
2501
- area = gibal
2502
- nlat = 361
2503
- nlon = 571
2526
+ area = medinte
2527
+ nlat = 935
2528
+ nlon = 781
2504
2529
  resolution = 0.017
2505
2530
  runit = deg
2506
2531
 
@@ -2575,6 +2600,13 @@ area = corsica
2575
2600
  resolution = 2.5
2576
2601
  runit = km
2577
2602
 
2603
+ [nl2500]
2604
+ info = HARMONIE-AROME 2.5km NL domain for DAVAI
2605
+ kind = projected
2606
+ area = nl
2607
+ resolution = 2.5
2608
+ runit = km
2609
+
2578
2610
  [cors1s40]
2579
2611
  info = Corsica 0.025 deg
2580
2612
  kind = lonlat
@@ -2669,6 +2701,39 @@ area = chmh2325
2669
2701
  resolution = 2.33
2670
2702
  runit = km
2671
2703
 
2704
+ [armcu]
2705
+ info = MUSC Single point ARMCU
2706
+ kind = unstructured
2707
+ area = armcu
2708
+
2709
+ [global31c1]
2710
+ info = ARPEGE T31C1.0 geometry (from Mitraillette tests)
2711
+ kind = gauss
2712
+ area = france
2713
+ truncation = 31
2714
+ stretching = 1.0
2715
+
2716
+ [global30c24]
2717
+ info = ARPEGE T30C2.4 geometry (from Mitraillette tests)
2718
+ kind = gauss
2719
+ area = france
2720
+ truncation = 30
2721
+ stretching = 2.4
2722
+
2723
+ [portugal21km]
2724
+ info = Mitraillette Portugal grid
2725
+ kind = projected
2726
+ area = portugal
2727
+ resolution = 21.3
2728
+ runit = km
2729
+
2730
+ [occitanie4km]
2731
+ info = Mitraillette Occitanie grid
2732
+ kind = projected
2733
+ area = occitanie
2734
+ resolution = 4.4
2735
+ runit = km
2736
+
2672
2737
  #R ===================
2673
2738
  #R Combined geometries
2674
2739
  #R ===================
vortex/data/providers.py CHANGED
@@ -64,13 +64,6 @@ class Provider(footprints.FootprintBase):
64
64
  fastkeys={"namespace"},
65
65
  )
66
66
 
67
- def __init__(self, *args, **kw):
68
- logger.debug("Abstract provider init %s", self.__class__)
69
- super().__init__(*args, **kw)
70
-
71
- if not self.username:
72
- self.username = vortex.ticket().glove.user
73
-
74
67
  def _str_more(self):
75
68
  """Additional information to print representation."""
76
69
  try:
@@ -92,7 +85,9 @@ class Provider(footprints.FootprintBase):
92
85
 
93
86
  def netuser_name(self, resource): # @UnusedVariable
94
87
  """Abstract method."""
95
- return self.username
88
+ if self.username is not None:
89
+ return self.username
90
+ return vortex.ticket().glove.user
96
91
 
97
92
  def pathname(self, resource):
98
93
  """Abstract method."""
@@ -124,10 +119,11 @@ class Provider(footprints.FootprintBase):
124
119
  The different operations of the algorithm can be redefined by subclasses.
125
120
  """
126
121
  username = self.netuser_name(resource)
122
+ netloc = self.netloc(resource)
127
123
  fullnetloc = (
128
124
  "{:s}@{:s}".format(username, self.netloc(resource))
129
- if username
130
- else self.netloc(resource)
125
+ if (username and netloc)
126
+ else netloc
131
127
  )
132
128
  logger.debug(
133
129
  "scheme %s netloc %s normpath %s urlquery %s",
@@ -279,6 +275,44 @@ def set_namespace_from_cache_settings(usecache, usearchive):
279
275
  return ".".join(("vortex", domain, "fr"))
280
276
 
281
277
 
278
+ class Git(Provider):
279
+ _footprint = dict(
280
+ info="Git provider",
281
+ attr=dict(
282
+ ref=dict(
283
+ type=str,
284
+ optional=True,
285
+ default=None,
286
+ info="The reference's SHA-1 hash id",
287
+ ),
288
+ repo=dict(
289
+ type=str,
290
+ info="Path to the Git repository",
291
+ ),
292
+ path=dict(
293
+ type=str,
294
+ info="File path within the repository",
295
+ ),
296
+ ),
297
+ )
298
+
299
+ def scheme(self, resource):
300
+ return "git"
301
+
302
+ def urlquery(self, resource):
303
+ return (
304
+ f"repo={self.repo}&ref={self.ref}"
305
+ if self.ref
306
+ else f"repo={self.repo}"
307
+ )
308
+
309
+ def basename(self, resource):
310
+ return self.path
311
+
312
+ def pathname(sef, resource):
313
+ return "."
314
+
315
+
282
316
  class Vortex(Provider):
283
317
  """Main provider of the toolbox, using a fix-size path and a dedicated name factory."""
284
318
 
vortex/data/stores.py CHANGED
@@ -8,8 +8,11 @@ Store objects use the :mod:`footprints` mechanism.
8
8
  import copy
9
9
  import ftplib
10
10
  import io
11
+ from pathlib import Path
11
12
  import os
12
13
  import re
14
+ import shutil
15
+ import subprocess
13
16
 
14
17
  from bronx.fancies import loggers
15
18
  import footprints
@@ -33,6 +36,13 @@ __all__ = []
33
36
 
34
37
  logger = loggers.getLogger(__name__)
35
38
 
39
+ try:
40
+ import pygit2
41
+
42
+ NO_PYGIT2 = False
43
+ except ImportError:
44
+ NO_PYGIT2 = True
45
+
36
46
 
37
47
  def get_cache_location():
38
48
  try:
@@ -335,7 +345,6 @@ class Finder(Store):
335
345
  def _ftpinfos(self, remote, **kwargs):
336
346
  args = kwargs.copy()
337
347
  args["hostname"] = self.hostname()
338
- args["logname"] = remote["username"]
339
348
  port = self.hostname().netport
340
349
  if port is not None:
341
350
  args["port"] = port
@@ -422,6 +431,98 @@ class Finder(Store):
422
431
  return rc
423
432
 
424
433
 
434
+ class GitStore(Store):
435
+ _footprint = dict(
436
+ info="A store to access Git repositories",
437
+ attr=dict(
438
+ scheme=dict(
439
+ values=["git"],
440
+ ),
441
+ ),
442
+ )
443
+
444
+ def gitget(self, remote, local, options):
445
+ if NO_PYGIT2:
446
+ raise ModuleNotFoundError(
447
+ "pygit2 is not installed in the current environment"
448
+ )
449
+ annex_cache_path = Path(remote["query"]["repo"][0])
450
+
451
+ # If no git reference is provided, only make a copy of the
452
+ # worktree
453
+ if "ref" not in remote["query"]:
454
+ shutil.copy(
455
+ src=Path(annex_cache_path) / remote["path"].lstrip("/"),
456
+ dst=local,
457
+ )
458
+ return local
459
+
460
+ assert "ref" in remote["query"]
461
+ path = remote["path"].lstrip("/")
462
+ repo = pygit2.Repository(annex_cache_path)
463
+ oid = repo.revparse_single(remote["query"]["ref"][0])
464
+ obj = oid.peel(pygit2.Tree) / path
465
+ if obj.filemode == pygit2.enums.FileMode.LINK:
466
+ # Here we need to discriminate between links that point
467
+ # to git-annex managed files and all other symlinks.
468
+
469
+ # If the file is a git-annex link, work out the location
470
+ # of the data within the .git/annex dir and copy this
471
+ # file into the cwd with the right name
472
+ if ".git/annex/objects" in obj.data.decode("ASCII"):
473
+ gitannex_key = Path(obj.data.decode("ASCII")).name
474
+ subprocess.run(
475
+ args=["git-annex", "get", "--key", gitannex_key],
476
+ cwd=str(annex_cache_path),
477
+ )
478
+ gitannex_content_location = subprocess.run(
479
+ args=["git-annex", "contentlocation", gitannex_key],
480
+ cwd=str(annex_cache_path),
481
+ capture_output=True,
482
+ encoding="ASCII",
483
+ ).stdout
484
+ shutil.copy(
485
+ src=annex_cache_path / Path(gitannex_content_location),
486
+ dst=local,
487
+ follow_symlinks=True,
488
+ )
489
+ return local
490
+ os.symlink(
491
+ src=obj.data.decode("ASCII"),
492
+ dst=local,
493
+ )
494
+ return local
495
+
496
+ if obj.filemode == pygit2.enums.FileMode.BLOB:
497
+ with open(local, "wb") as dst:
498
+ # Could also use pygit2.BlobIO to stream content
499
+ # without having to load the entire blob data in
500
+ # memory:
501
+ #
502
+ # with pygit2.BlobIO(obj) as src:
503
+ # shutil.copyfileobj(fsrc=src, fdst=dst)
504
+ dst.write(obj.data)
505
+ return local
506
+
507
+ if obj.filemode == pygit2.enums.FileMode.TREE:
508
+ if local.endswith("/"):
509
+ localpath = "."
510
+ else:
511
+ localpath = local
512
+ os.mkdir(local)
513
+ for subobj in obj:
514
+ r = {
515
+ "query": {"ref": remote["query"]["ref"]},
516
+ "path": str(Path(path) / subobj.name),
517
+ }
518
+ self.gitget(
519
+ remote=r,
520
+ local=str(Path(localpath) / subobj.name),
521
+ options=None,
522
+ )
523
+ return local
524
+
525
+
425
526
  class _VortexStackedStorageMixin:
426
527
  """Mixin class that adds utility functions to work with stacked data."""
427
528
 
@@ -985,7 +1086,10 @@ class VortexCacheMtStore(_VortexCacheBaseStore):
985
1086
  except config.ConfigurationError:
986
1087
  cacheloc = os.path.join(os.environ["HOME"], ".vortex.d")
987
1088
 
988
- if self.username != self.system.glove.user:
1089
+ current_vortex_user = self.system.glove.user
1090
+ cacheloc = cacheloc.replace("%usr%", current_vortex_user)
1091
+
1092
+ if self.username != current_vortex_user:
989
1093
  return os.path.join(cacheloc, self.username)
990
1094
 
991
1095
  return cacheloc
@@ -1205,6 +1309,9 @@ class VortexStackStore(_AbstractVortexStackMultiStore):
1205
1309
  """Go through the various stacked stores."""
1206
1310
  return [f"{self.netloc.firstname}.stacked-cache-mt.fr"]
1207
1311
 
1312
+ def alternates_fpextras(self):
1313
+ return dict(username=self.username)
1314
+
1208
1315
 
1209
1316
  class VortexVsopStackStore(_AbstractVortexStackMultiStore):
1210
1317
  """Store intended to read and write data into VORTEX R&D stacks."""
@@ -1336,7 +1443,7 @@ class PromiseCacheStore(VortexCacheMtStore):
1336
1443
  )
1337
1444
 
1338
1445
  @property
1339
- def cache_promise(self):
1446
+ def cache_entry(self):
1340
1447
  return os.path.join(super().cache_entry, "promise")
1341
1448
 
1342
1449
  @staticmethod
vortex/nwp/algo/assim.py CHANGED
@@ -5,7 +5,7 @@ AlgoComponents dedicated to computations related to Data Assimilation systems.
5
5
  from bronx.fancies import loggers
6
6
  from bronx.stdtypes.date import Date
7
7
 
8
- from vortex.algo.components import BlindRun, Parallel
8
+ from vortex.algo.components import BlindRun
9
9
  from vortex.syntax.stdattrs import a_date
10
10
  from .ifsroot import IFSParallel
11
11
  from ..tools import odb, drhook
@@ -16,36 +16,6 @@ __all__ = []
16
16
  logger = loggers.getLogger(__name__)
17
17
 
18
18
 
19
- class MergeVarBC(Parallel):
20
- """Merge two VarBC files.
21
-
22
- The VarBC file resulting from the MergeVarBC contains all the items of the
23
- first VarBC file plus any new item that would be present in the second file.
24
- """
25
-
26
- _footprint = dict(
27
- attr=dict(
28
- kind=dict(
29
- values=["mergevarbc"],
30
- ),
31
- varbcout=dict(
32
- optional=True,
33
- default="VARBC.cycle_out",
34
- ),
35
- )
36
- )
37
-
38
- def prepare(self, rh, opts):
39
- """Find any ODB candidate in input files."""
40
-
41
- sh = self.system
42
-
43
- sh.touch(self.varbcout)
44
-
45
- # Let ancesters doing real stuff
46
- super().prepare(rh, opts)
47
-
48
-
49
19
  class Anamix(IFSParallel):
50
20
  """Merge the surface and atmospheric analyses into a single file"""
51
21
 
@@ -112,6 +112,8 @@ class Forecast(IFSParallel):
112
112
  # Possibly fix post-processing clim files
113
113
  self.all_localclim_fixer(rh, thismonth)
114
114
 
115
+ self.grab(analysis, comment="analysis")
116
+
115
117
  # File linking for IAU increments
116
118
  #
117
119
  # In the case of a forecast with IAU, the IFS executable
@@ -884,3 +886,31 @@ class OfflineSurfex(Parallel, DrHookDecoMixin):
884
886
  if namsec.rh.contents.dumps_needs_update:
885
887
  namsec.rh.save()
886
888
  logger.info("Namelist dump: \n%s", namsec.rh.container.read())
889
+
890
+
891
+ class MUSCForecast(Forecast):
892
+ """Forecast for MUSC single-column model."""
893
+
894
+ _footprint = dict(
895
+ info="Run a forecast with a MUSC single-column model.",
896
+ attr=dict(
897
+ kind=dict(
898
+ values=["musc"],
899
+ ),
900
+ ),
901
+ )
902
+
903
+ def postfix(self, rh, opts):
904
+ """Post forecast information and cleaning."""
905
+ sh = self.system
906
+ # rename specific output files with hours term on 4 digits for compatibility for fmth formatting
907
+ fmt = re.compile(r"Out\.(?P<termh>\d{3})\.\d{4}\.lfa$")
908
+ for f in [
909
+ fmt.match(f)
910
+ for f in sh.listdir()
911
+ if f.startswith("Out.") and f.endswith(".lfa")
912
+ ]:
913
+ sh.rename(
914
+ f.string, "Out.{:>04}.0000.lfa".format(int(f.group("termh")))
915
+ )
916
+ super().postfix(rh, opts)
@@ -91,7 +91,7 @@ class IFSParallel(
91
91
  ),
92
92
  fcterm=dict(
93
93
  info="The forecast term of the Arpege/IFS model.",
94
- type=int,
94
+ type=float,
95
95
  optional=True,
96
96
  default=0,
97
97
  ),
@@ -346,24 +346,6 @@ class IFSParallel(
346
346
  # be done by an extra class ... and it could be generalized to mpi
347
347
  # setup by the way !
348
348
  nam_updated = False
349
- # For cy41 onward, replace some namelist macros with the command line
350
- # arguments
351
- if rh.resource.cycle >= "cy41":
352
- if "NAMARG" in namcontents:
353
- opts_arg = self.spawn_command_options()
354
- self._set_nam_macro(
355
- namcontents, namlocal, "CEXP", opts_arg["name"]
356
- )
357
- self._set_nam_macro(
358
- namcontents, namlocal, "TIMESTEP", opts_arg["timestep"]
359
- )
360
- fcstop = "{:s}{:d}".format(
361
- opts_arg["fcunit"], opts_arg["fcterm"]
362
- )
363
- self._set_nam_macro(namcontents, namlocal, "FCSTOP", fcstop)
364
- nam_updated = True
365
- else:
366
- logger.info("No NAMARG block in %s", namlocal)
367
349
 
368
350
  if self.member is not None:
369
351
  for macro_name in ("MEMBER", "PERTURB"):
@@ -371,7 +353,39 @@ class IFSParallel(
371
353
  namcontents, namlocal, macro_name, self.member
372
354
  )
373
355
  nam_updated = True
374
- return nam_updated
356
+
357
+ if rh.resource.cycle < "cy41":
358
+ return nam_updated
359
+
360
+ if "NAMARG" not in namcontents:
361
+ logger.info("No NAMARG block in %s", namlocal)
362
+ return nam_updated
363
+
364
+ # For cy41 onward, replace some namelist macros with the command line
365
+ # arguments
366
+ opts_arg = self.spawn_command_options()
367
+ self._set_nam_macro(namcontents, namlocal, "CEXP", opts_arg["name"])
368
+ self._set_nam_macro(
369
+ namcontents, namlocal, "TIMESTEP", opts_arg["timestep"]
370
+ )
371
+
372
+ if self.fcunit == "t":
373
+ fcstop = "t{:d}".format(int(self.fcterm))
374
+ elif self.fcterm.is_integer():
375
+ # Round number of hours
376
+ fcstop = "h{:d}".format(int(self.fcterm))
377
+ else:
378
+ # IFS expects the forecast term to be given as an integer,
379
+ # whether this integer represents hours or timesteps. This
380
+ # means terms that are not round hours (e.g. 01:45) can only
381
+ # be expressed as a number of timesteps.
382
+ # See http://gitlab.meteo.fr/cnrm-gmap/vortex/-/issues/9
383
+ nsteps = int(self.fcterm * 3600 // self.timestep)
384
+ fcstop = "t{:d}".format(nsteps)
385
+ logger.info(f"Converting {self.fcterm} hours into {nsteps}")
386
+
387
+ self._set_nam_macro(namcontents, namlocal, "FCSTOP", fcstop)
388
+ return True
375
389
 
376
390
  def prepare_namelists(self, rh, opts=None):
377
391
  """Update each of the namelists."""
@@ -4,7 +4,7 @@ AlgoComponents dedicated to computations related to observations monitoring.
4
4
 
5
5
  from bronx.fancies import loggers
6
6
 
7
- from vortex.algo.components import Parallel
7
+ from vortex.algo.components import BlindRun
8
8
  from vortex.syntax.stdattrs import a_date, a_model, a_cutoff
9
9
  from ..tools import odb, drhook
10
10
 
@@ -15,7 +15,7 @@ logger = loggers.getLogger(__name__)
15
15
 
16
16
 
17
17
  class OdbMonitoring(
18
- Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin
18
+ BlindRun, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin
19
19
  ):
20
20
  """Compute monitoring statistics."""
21
21
 
@@ -16,7 +16,7 @@ from taylorism import Boss
16
16
 
17
17
  from vortex.tools.systems import ExecutionError
18
18
 
19
- from vortex.algo.components import Parallel, ParaBlindRun
19
+ from vortex.algo.components import Parallel, ParaBlindRun, BlindRun
20
20
  from vortex.tools.parallelism import VortexWorkerBlindRun
21
21
 
22
22
  from ..syntax.stdattrs import arpifs_cycle
@@ -1217,7 +1217,7 @@ class OdbReshuffle(
1217
1217
 
1218
1218
 
1219
1219
  class FlagsCompute(
1220
- Parallel, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin
1220
+ BlindRun, odb.OdbComponentDecoMixin, drhook.DrHookDecoMixin
1221
1221
  ):
1222
1222
  """Compute observations flags."""
1223
1223
 
vortex/nwp/data/consts.py CHANGED
@@ -2,12 +2,15 @@
2
2
  Various Resources for constant files used in NWP.
3
3
  """
4
4
 
5
+ import random
6
+
5
7
  import footprints
8
+ from footprints import FPList
6
9
  from ..syntax.stdattrs import gvar
7
10
  from vortex.data.contents import DataRaw, JsonDictContent, TextContent
8
11
  from vortex.data.geometries import GaussGeometry, LonlatGeometry
9
12
  from vortex.data.outflow import ModelGeoResource, ModelResource, StaticResource
10
- from vortex.syntax.stdattrs import month_deco
13
+ from vortex.syntax.stdattrs import date_deco, member, month_deco
11
14
  from vortex.syntax.stddeco import (
12
15
  namebuilding_append,
13
16
  namebuilding_delete,
@@ -180,6 +183,63 @@ class RtCoef(GenvModelResource):
180
183
  return "rtcoef"
181
184
 
182
185
 
186
+ class RtCoefMulti(GenvModelResource):
187
+ """
188
+ RtCoeff Satellite coefficients, randomly chosen depending on the member of an ensemble.
189
+
190
+ Reproducibility is ensured by the stability of the random generation for a given date (ymdh) and member.
191
+ With member=0 or None, or with choices=0, the choice is always 0, even if 0 is excluded.
192
+ """
193
+
194
+ _footprint = [
195
+ date_deco,
196
+ member,
197
+ dict(
198
+ info="Set of satellite coefficients",
199
+ attr=dict(
200
+ kind=dict(
201
+ values=["rtcoef_multi", "mwave_rtcoef_multi"],
202
+ ),
203
+ choices=dict(
204
+ info="Number of choices to choose from (0..choices-1)",
205
+ type=int,
206
+ optional=True,
207
+ ),
208
+ excluded=dict(
209
+ info="List of values excluded from choice",
210
+ type=footprints.stdtypes.FPList,
211
+ optional=True,
212
+ default=FPList([]),
213
+ ),
214
+ gvar=dict(
215
+ info="Will be modified by the random choice",
216
+ default="[kind]_0",
217
+ access="rwx",
218
+ ),
219
+ ),
220
+ ),
221
+ ]
222
+
223
+ def __init__(self, *args, **kw):
224
+ super().__init__(*args, **kw)
225
+ if self.member is None or self.member == 0 or self.choices == 0:
226
+ choice = 0
227
+ else:
228
+ # a random generator entirely determined by the date
229
+ rgen = random.Random(int(self.date.ymdh))
230
+ # drawing must be reproducible for a given member:
231
+ # generate 'member' values, but only keep the last
232
+ choice = rgen.choices(
233
+ [n for n in range(self.choices) if n not in self.excluded],
234
+ k=self.member,
235
+ )[-1]
236
+ self.gvar = self.gvar[:-1] + str(choice)
237
+
238
+ @property
239
+ def realkind(self):
240
+ return "rtcoef"
241
+
242
+
183
243
  class RRTM(GenvModelResource):
184
244
  """
185
245
  Class of a tar-zip file of coefficients for radiative transfers computations.
@@ -145,10 +145,7 @@ _surfex_diag_decofp = footprints.DecorativeFootprint(
145
145
  ]
146
146
  ),
147
147
  nativefmt=dict(
148
- values=[
149
- "netcdf",
150
- "grib",
151
- ],
148
+ values=["netcdf", "grib", "fa"],
152
149
  default="netcdf",
153
150
  optional=True,
154
151
  ),
@@ -185,6 +182,7 @@ class ObjTrack(GeoFlowResource):
185
182
  values=[
186
183
  "json",
187
184
  "hdf5",
185
+ "tar",
188
186
  "foo",
189
187
  ],
190
188
  default="foo",
@@ -1045,11 +1045,11 @@ class ArpIfsForecastTermConfTool(ConfTool):
1045
1045
  The forecast term can be retrieved:
1046
1046
 
1047
1047
  >>> print(ct.fcterm('assim', 6))
1048
- 6
1048
+ 6.0
1049
1049
  >>> print(ct.fcterm('production', 0))
1050
- 102
1050
+ 102.0
1051
1051
  >>> print(ct.fcterm('production', 12))
1052
- 24
1052
+ 24.0
1053
1053
 
1054
1054
  If nothing is defined it crashes:
1055
1055
 
@@ -1451,11 +1451,12 @@ class ArpIfsForecastTermConfTool(ConfTool):
1451
1451
  return self._lookup_rangex_cache[(what_desc, cutoff, hh)]
1452
1452
 
1453
1453
  def fcterm(self, cutoff, hh):
1454
- """The forecast term for **cutoff** and **hh**."""
1454
+ """The forecast term for **cutoff** and **hh** as a float or int."""
1455
1455
  fcterm = self._cutoff_hh_lookup("fcterm", cutoff, hh)
1456
- if isinstance(fcterm, Time) and fcterm.minute == 0:
1457
- return fcterm.hour
1456
+ if isinstance(fcterm, Time):
1457
+ return fcterm.hour + (fcterm.minute / 60)
1458
1458
  else:
1459
+ # fcterm is an int representing nb of timesteps
1459
1460
  return fcterm
1460
1461
 
1461
1462
  def hist_terms(self, cutoff, hh):
vortex/tools/systems.py CHANGED
@@ -67,7 +67,6 @@ from vortex.tools.compression import CompressionPipeline
67
67
  from vortex.tools.env import Environment
68
68
  from vortex.tools.net import AssistedSsh, AutoRetriesFtp, DEFAULT_FTP_PORT
69
69
  from vortex.tools.net import FtpConnectionPool, LinuxNetstats, StdFtp
70
- import vortex.tools.storage
71
70
  from vortex import config
72
71
 
73
72
  #: No automatic export
@@ -1161,6 +1160,7 @@ class OSExtended(System):
1161
1160
  logger.warning(
1162
1161
  "Bad return code [%d] for %s", p.returncode, str(args)
1163
1162
  )
1163
+ self.dump_spawn_to_script(args)
1164
1164
  if isinstance(output, bool) and output:
1165
1165
  sys.stderr.write(p_err.decode(plocale, "replace"))
1166
1166
  if fatal:
@@ -1183,6 +1183,31 @@ class OSExtended(System):
1183
1183
 
1184
1184
  return rc
1185
1185
 
1186
+ def dump_spawn_to_script(self, args):
1187
+ """Dump spawn environment to a script that can be executed 'out-of-vortex'."""
1188
+ script = [
1189
+ "#!/bin/bash",
1190
+ "",
1191
+ 'SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )',
1192
+ "cd $SCRIPT_DIR",
1193
+ "",
1194
+ "ulimit -s unlimited",
1195
+ "",
1196
+ ]
1197
+ # env vars
1198
+ for k in sorted(self.env.keys()):
1199
+ if not (k.startswith("SLURM") or k.startswith("MTOOL")):
1200
+ script.append('export {}="{}"'.format(k, self.env[k]))
1201
+ # command line
1202
+ script.append("\n" + " ".join(args))
1203
+ # replace any MTOOL dirs from spool to abort
1204
+ for i, line in enumerate(script):
1205
+ script[i] = line.replace("spool/spool_", "abort/dump_")
1206
+ # write to file
1207
+ with open("spawn_dump.sh", "w") as o:
1208
+ for l in script:
1209
+ o.write(l + "\n")
1210
+
1186
1211
  def getlogname(self):
1187
1212
  """Be sure to get the actual login name."""
1188
1213
  return passwd.getpwuid(self._os.getuid())[0]
@@ -1860,10 +1885,15 @@ class OSExtended(System):
1860
1885
  """Return a cache object for the FtSpool."""
1861
1886
  if self._ftspool_cache is not None:
1862
1887
  return self._ftspool_cache
1888
+ try:
1889
+ cacheloc = config.from_config(section="data-tree", key="rootdir")
1890
+ except config.ConfigurationError:
1891
+ cacheloc = os.path.join(os.environ["HOME"], ".vortex.d")
1892
+
1863
1893
  self._ftspool_cache = footprints.proxy.cache(
1864
1894
  entry=os.path.join(
1865
- vortex.data.stores.get_cache_location(), "ftspool"
1866
- ),
1895
+ cacheloc.replace("%usr%", self.glove.user), "ftspool"
1896
+ )
1867
1897
  )
1868
1898
  return self._ftspool_cache
1869
1899
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vortex-nwp
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: A Python library to write Numerical Weather Prediction pipelines components
5
5
  Author-email: The Vortex Team <vortex.support@meteo.fr>
6
6
  License: CECILL-C
@@ -14,6 +14,7 @@ Requires-Dist: footprints
14
14
  Requires-Dist: taylorism
15
15
  Requires-Dist: tomli
16
16
  Requires-Dist: arpifs_listings
17
+ Requires-Dist: importlib_metadata; python_version < "3.8"
17
18
  Provides-Extra: docs
18
19
  Requires-Dist: sphinx; extra == "docs"
19
20
  Requires-Dist: sphinx-book-theme; extra == "docs"
@@ -21,6 +22,8 @@ Requires-Dist: sphinx-copybutton; extra == "docs"
21
22
  Provides-Extra: dev
22
23
  Requires-Dist: ruff==0.9.1; extra == "dev"
23
24
  Requires-Dist: pytest; extra == "dev"
25
+ Provides-Extra: git
26
+ Requires-Dist: pygit2; extra == "git"
24
27
  Dynamic: license-file
25
28
 
26
29
  ## vortex
@@ -1,12 +1,12 @@
1
- vortex/__init__.py,sha256=MVCUcPlaXXTr7csgYIUfa02hrrJMiNL7VoawK8vKFdg,4374
1
+ vortex/__init__.py,sha256=iF0Qyy8b-ZIznlaDuFfFtuA3PU4d3OxQZVYM1TyU7fQ,4438
2
2
  vortex/config.py,sha256=YEpcc81wR94jvn36fPONmKc1tM4ap0s8Rc7v3KfDUok,2948
3
3
  vortex/gloves.py,sha256=GKz27S8eLfRlk8fNqVL_z1gsQ8zvEj73p5uVi11ou00,8487
4
4
  vortex/proxy.py,sha256=OlPrVUJS5FoKt5pX8ApN1crFFDj8RJAqhDEilwvfrYU,127
5
5
  vortex/sessions.py,sha256=zWVhsFGKl-SovyVAZz3mpvmojjHROt5iyERjwA6kudQ,10017
6
6
  vortex/toolbox.py,sha256=fDTvfghg8Yl7ggAwhIaY3V6owO3V1IG9LBf6F97A_50,42385
7
7
  vortex/algo/__init__.py,sha256=I_9COn_QBRbwvbqhs0X3CdHeR97NZhBacIqwKjnVBTg,359
8
- vortex/algo/components.py,sha256=JvznzerMhxLxbKZRWtQPIX7y-JjTAMwcIzG9TAXB7Sc,89460
9
- vortex/algo/mpitools.py,sha256=hMVKPqu3z4ohGt9OtKCiDRNK6RlVBWgjelDoNaDH5hw,74235
8
+ vortex/algo/components.py,sha256=BodJA593f0OM1Tj5BAh-u-p55IbL81rwQR_g_Ee-mL0,89990
9
+ vortex/algo/mpitools.py,sha256=4J-qGZ4olkgq6kpC2F81ogXeksXp4030ARg-QbCdQ9Y,74154
10
10
  vortex/algo/serversynctools.py,sha256=fPel0txVHsrUfk6VFaeKa0D6i21fOskIAR_BbByBv9g,5601
11
11
  vortex/algo/mpitools_templates/__init__.py,sha256=Jbw903aPqVKF-AaSoB-mGMxthSvm88O_yqGoGmf_S_U,18
12
12
  vortex/algo/mpitools_templates/envelope_wrapper_default.tpl,sha256=4VhkDx_YbOYywKQ82HIxRJXGcDpLuOgqcY7Edx9Rxyw,453
@@ -16,15 +16,15 @@ vortex/data/__init__.py,sha256=XaHof5W6oCalr4X6oYAK4zW3z3F6mKuwdbKxmmmDraY,560
16
16
  vortex/data/abstractstores.py,sha256=eghm3HSlUcZM8AGdx0js4Nt_IdxEiYffOmqS5gkpVsQ,55297
17
17
  vortex/data/containers.py,sha256=Qny5rqwXEdDWxG4JlC8KcsSAsHK3pp8qukZj1PTFBOM,26004
18
18
  vortex/data/contents.py,sha256=ZrwlJfOvkTemzikFRgYBQH3ApC-TPNrbZxpZstzDdbY,19079
19
- vortex/data/executables.py,sha256=FeR3SA2wW97zAQXwWVecZ0v6VYT5L_3K1Czuv33Bk74,6723
19
+ vortex/data/executables.py,sha256=aFIf0gTEw1z60SlznfDzGG58vzE4191ObDrASt0hszw,7381
20
20
  vortex/data/flow.py,sha256=P1itBnA8jaoCWnVQjqbD_Pf26rpzud1JdwSLECDnDl4,3008
21
- vortex/data/geometries.ini,sha256=J7hX5hYWqwBjdUAul6q8j10U6b3I-QEHrFJ98LBTQXM,52805
21
+ vortex/data/geometries.ini,sha256=W2yDVXwG5J0amjRt3xHjOrFr_xI7h3v75WFQcFda16w,54103
22
22
  vortex/data/geometries.py,sha256=q-0xmfBFN_TrMaojljO0wEpEwY9w1cbUuoBo49cvYhc,28221
23
23
  vortex/data/handlers.py,sha256=bChvizJnN5zxQxVf0oeUqcw_Wj-jnVszSqT1ovVUXFA,50525
24
24
  vortex/data/outflow.py,sha256=IPKJkn75lRvhSqN5969TuhRAPnsZKlrWR4Cmw6VBFDs,1475
25
- vortex/data/providers.py,sha256=IA_7JT8TNZy-2Dtk71JTICvdA5rqrzHLsEtkdU1z2g4,15860
25
+ vortex/data/providers.py,sha256=s1CHOuogjzyPUHfK6gPpKU3IDCtySLTjPgkmuC4xiJA,16614
26
26
  vortex/data/resources.py,sha256=UOwvQDTJxS9z66Aa7JM2nWM3IxrMWvY3L2J9D5w3sZw,6348
27
- vortex/data/stores.py,sha256=LOxzqZmzLkOGTgbAuPTajSOdvEbUzXhxbSy6pTOdRG0,44092
27
+ vortex/data/stores.py,sha256=e4fhTTHcnh7FhZ21M93bH5ixl_owArSMKlvlPIxHIF4,47735
28
28
  vortex/data/sync_templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
29
  vortex/layout/__init__.py,sha256=aZsDhVJrd3-648vw-UESI_2BLxyGl71sHdyrg9cL638,826
30
30
  vortex/layout/contexts.py,sha256=GkysEtOOUj3nQvGUWrFjOOjzv7SmWFPUSLTH_pMMycI,20363
@@ -32,18 +32,18 @@ vortex/layout/dataflow.py,sha256=SKOUJKbtftW4POStjyBqNrqjvfbutsNHnWQ-p_75l_8,447
32
32
  vortex/layout/monitor.py,sha256=DIUXoW242tg5B6fFEEe2Og-NOxli1T_xpcNWFvTaWXQ,34316
33
33
  vortex/nwp/__init__.py,sha256=UfB2il-lEjF87PyVO9sGxjcuy3OYhZwskZw3z52VYAE,311
34
34
  vortex/nwp/algo/__init__.py,sha256=_it4GhWxay_WAy0qr2IxLdIqsjWxXcsSSB7wx1BitB4,577
35
- vortex/nwp/algo/assim.py,sha256=vt3UjvAik4xBl_QC-Yj1CxY4vaiAYvGzzf27XwLoykA,15922
35
+ vortex/nwp/algo/assim.py,sha256=HIyOAkzWc8U42ttWy7nYpHiDoBjTp9WoO7F_SQau9v8,15209
36
36
  vortex/nwp/algo/clim.py,sha256=Z9QwUfosH0X_xTg6mam68qWIvw6Ts6CZIpc7Nb0NO1s,37511
37
37
  vortex/nwp/algo/coupling.py,sha256=6KQ_YB7PCvVbt6c2VEtpCzwIyWRTjLfu54mNpKnXL9Q,29755
38
38
  vortex/nwp/algo/eda.py,sha256=Wf2mt1r6v_ZYDz4BFyt-4JP1XAsO8sZW3O1UAOSRzmU,30746
39
39
  vortex/nwp/algo/eps.py,sha256=JCBFiaFF9TQ-x7Szgw44Cd39tRD9184UXqOtoT6aRCs,26812
40
- vortex/nwp/algo/forecasts.py,sha256=sqs0IETanpfS8q1f1Pl3mQZtSNbfOIUOidmcduGp8EU,30063
40
+ vortex/nwp/algo/forecasts.py,sha256=qgpSYYsD0C9agBusBX38ulDesw01uU_QvOc28gspNCI,30971
41
41
  vortex/nwp/algo/fpserver.py,sha256=98HQZsdQE22spy_mIE1G-NORQMMjfA5grEjeIMSlP9w,50163
42
42
  vortex/nwp/algo/ifsnaming.py,sha256=WfpSpRJ7ua3ihqv8Y4UkrvE4pb0CkNWKIlW9EjY-2ao,11188
43
- vortex/nwp/algo/ifsroot.py,sha256=Mmp0sZtexP5ob3iUFTiVbgVWog7ubAYPut0cAB0vbbg,14230
44
- vortex/nwp/algo/monitoring.py,sha256=JVPZPw_I5QRVtYvvSYpJpBy5RRPhqBQjcAFfIkvKuHA,8893
43
+ vortex/nwp/algo/ifsroot.py,sha256=RIJzn1r9uZ5Cno3WHQ6swEZUAlmndvYRtn9TQ3BVIXc,14771
44
+ vortex/nwp/algo/monitoring.py,sha256=qwWupx4ITJx4vaQVfl3XhWdkaq2T8bNwk72Ur4v-Xw8,8893
45
45
  vortex/nwp/algo/mpitools.py,sha256=QO3I9iMdY69znnA6hJy8XUAzBoejDpsVMQFRo3YzXro,24876
46
- vortex/nwp/algo/odbtools.py,sha256=xltXS1lpxeeJXeCGnFiqFUhGqMI6BlZzKcSLhsnd2cs,44094
46
+ vortex/nwp/algo/odbtools.py,sha256=SjYCYseLqrxlPtEHu4APwSZoCPwjl2rLvT1XC2kvql4,44104
47
47
  vortex/nwp/algo/oopsroot.py,sha256=es3p7RPOixQT74TcglJX1q8MswxveVnLZa84kyhu3uM,33806
48
48
  vortex/nwp/algo/oopstests.py,sha256=owQAS7_fGbUGZGzMsJek32dbb3JQ6dafSVBrdmveQ-Q,6654
49
49
  vortex/nwp/algo/request.py,sha256=y5N9RUfKoPXR6EbSely37Zz6d-2HYyfV3UCvvoenqbY,22809
@@ -53,9 +53,9 @@ vortex/nwp/data/assim.py,sha256=Dv5nmRYDDzYopC6xIwAl92HFTIz3k18C1jFE_IhYzuU,1009
53
53
  vortex/nwp/data/boundaries.py,sha256=xZoIriM5L-Q66_am6rOD2ZO0s-31iAUeTE7NZIXXvHI,8114
54
54
  vortex/nwp/data/climfiles.py,sha256=hlpe51wc-qrLFV_PX3vCedfLJu8Ko4VHa_qoVmA-Wcg,12610
55
55
  vortex/nwp/data/configfiles.py,sha256=0c3VvM25dhU6-QEJs5Wo4IckIVjXFHc-tTEzh1Bho1c,3760
56
- vortex/nwp/data/consts.py,sha256=riUtekoj3ozBgjxW6xEzhEaWWA7QvZFhU52Y3JS02Eg,21871
56
+ vortex/nwp/data/consts.py,sha256=LCVZEqONqgDSeWbGSVYTIcdsFnvs0tpuoA3C_fYaEJU,23894
57
57
  vortex/nwp/data/ctpini.py,sha256=wEpJG7Px2hb7Vudn4eY2vJHKi8YYO99Sd6UJAeT5kMI,3537
58
- vortex/nwp/data/diagnostics.py,sha256=4ZP1w0ls2FIH59441rTCYDrp0JFfRJ7tiLDWmrdvuwM,5054
58
+ vortex/nwp/data/diagnostics.py,sha256=Ckemxx6O7-nhOLKyZK3R8dlwMdEAcbSPEC2H4lk3Ikk,5040
59
59
  vortex/nwp/data/eda.py,sha256=rVQ35SpHVdHAQ10-LhyGy1k6P9Dbd6os8Crz2I4ThvY,4112
60
60
  vortex/nwp/data/eps.py,sha256=nLKTEpEeMxV9oE_sf87xihspMqwzXA8r9uJPP6PdycE,11861
61
61
  vortex/nwp/data/executables.py,sha256=xFRHzE5rK71m7gkd-LgMO3KSs0uBeCLqW-8vmedMHqQ,25036
@@ -80,7 +80,7 @@ vortex/nwp/tools/bdap.py,sha256=GdqC4QclVHzZUNQGYQPu9EmFPNEEBadDNGsLmrE2XUs,1689
80
80
  vortex/nwp/tools/bdcp.py,sha256=cDlkIVZW9grF2YnieIoiwsDhPxQsJIcaBuOCu6rsCzo,986
81
81
  vortex/nwp/tools/bdm.py,sha256=e9o3IpR9C4GGd7NhXWt0bpogrcVkqn_hbEJBtThSV5g,360
82
82
  vortex/nwp/tools/bdmp.py,sha256=tlsaPvsPtKSXrq2T7X_xtAdWGZ3Hg2qJUJfH8Fetx3E,1506
83
- vortex/nwp/tools/conftools.py,sha256=bCZ1vUMc1fkuzFEKw95KDXD9Fs4-uDg546RHuJyJmtU,59494
83
+ vortex/nwp/tools/conftools.py,sha256=Awnf9Tied8qeWhv24GXbe3VF9DWPYScnPXjN2Viez-I,59578
84
84
  vortex/nwp/tools/drhook.py,sha256=8BgHw1UGxgviLche73XUltuUtkN9rqjBPy4oBLyG4KY,1972
85
85
  vortex/nwp/tools/grib.py,sha256=Uur8z4NjGoRXV4Via-63F9Px59FKG5UxiLM9yMgNkUs,10262
86
86
  vortex/nwp/tools/gribdiff.py,sha256=VD4nH06DuFheE9HsFT1UXWnvAC5vUfVBPeqQhCK4wbo,3137
@@ -125,7 +125,7 @@ vortex/tools/schedulers.py,sha256=7JtfYfSJgViytM7T26B61V8W83dvNwkDWdb2jY_Bn-A,15
125
125
  vortex/tools/services.py,sha256=hprRFmLcQRLVGDUnFxUFHaEB7ll1tNdt38hRQz8SWF8,29795
126
126
  vortex/tools/storage.py,sha256=e2GGTN_fl2wDNdEeYoD2V7jmwpZecb8u32rYvnqPMZk,34522
127
127
  vortex/tools/surfex.py,sha256=qbCIGt-dmfIRWRRMjOQnzc9sGNnt8PIFSqQYT77trhw,1408
128
- vortex/tools/systems.py,sha256=Zu04yTVIZU0hCiRH_STmdGuknjVd8dNsxXWr87k_5aE,149081
128
+ vortex/tools/systems.py,sha256=fU46ZU6feCPkyCYti2Cs_jWbbYWv4zSBZskJwfSy_8U,150269
129
129
  vortex/tools/targets.py,sha256=-n2uMoEFdz3AeoSd1IGhlr7dYYb8xantUeh9_ggk9iI,14896
130
130
  vortex/util/__init__.py,sha256=Zt0OASbKbNnnwUqFHFoq5Mk13sGYSQhxqh7bUvJH6Y8,198
131
131
  vortex/util/config.py,sha256=KJHq0GMNRlwzh2WUdpwn1FIKCuAXla7nPRe7j3IZGcI,40045
@@ -137,8 +137,8 @@ vortex/util/roles.py,sha256=9un_QAijaMn5iTS7PrdoWI5_NNw7uHxMWTnyhc5aNzg,1150
137
137
  vortex/util/storefunctions.py,sha256=uSfG-G_A88iJf3DwFBd-j0rw6eJta8opfRT39aQHsHM,3615
138
138
  vortex/util/structs.py,sha256=vapErq0MNhiKlsnjrv_a5M0Rn29KbP3WE_oiy4Hfwb8,683
139
139
  vortex/util/worker.py,sha256=zp8f2tx4SXwf1v55XMdYLAx7n3vSlg8PRGrkHgnfdmg,4721
140
- vortex_nwp-2.2.0.dist-info/licenses/LICENSE,sha256=ewBJPmWAcQqtBPrydH10tt6ECkcYP3b1o2RfH85pJF0,21863
141
- vortex_nwp-2.2.0.dist-info/METADATA,sha256=Grzw9y3IoZf2MSjJhgvC7uE6_Zd7c7IlZh1hLi__8es,2185
142
- vortex_nwp-2.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
143
- vortex_nwp-2.2.0.dist-info/top_level.txt,sha256=3xfbSD7kw8xKl0jk4GNHsOPKbhubstfWHPl6bxHciRQ,7
144
- vortex_nwp-2.2.0.dist-info/RECORD,,
140
+ vortex_nwp-2.3.0.dist-info/licenses/LICENSE,sha256=ewBJPmWAcQqtBPrydH10tt6ECkcYP3b1o2RfH85pJF0,21863
141
+ vortex_nwp-2.3.0.dist-info/METADATA,sha256=IYexH93RR5LZEqUCVzTfE5IIlngnReB61R5T7bHcErs,2301
142
+ vortex_nwp-2.3.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
143
+ vortex_nwp-2.3.0.dist-info/top_level.txt,sha256=3xfbSD7kw8xKl0jk4GNHsOPKbhubstfWHPl6bxHciRQ,7
144
+ vortex_nwp-2.3.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5