vortex-nwp 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. vortex/__init__.py +159 -0
  2. vortex/algo/__init__.py +13 -0
  3. vortex/algo/components.py +2462 -0
  4. vortex/algo/mpitools.py +1953 -0
  5. vortex/algo/mpitools_templates/__init__.py +1 -0
  6. vortex/algo/mpitools_templates/envelope_wrapper_default.tpl +27 -0
  7. vortex/algo/mpitools_templates/envelope_wrapper_mpiauto.tpl +29 -0
  8. vortex/algo/mpitools_templates/wrapstd_wrapper_default.tpl +18 -0
  9. vortex/algo/serversynctools.py +171 -0
  10. vortex/config.py +112 -0
  11. vortex/data/__init__.py +19 -0
  12. vortex/data/abstractstores.py +1510 -0
  13. vortex/data/containers.py +835 -0
  14. vortex/data/contents.py +622 -0
  15. vortex/data/executables.py +275 -0
  16. vortex/data/flow.py +119 -0
  17. vortex/data/geometries.ini +2689 -0
  18. vortex/data/geometries.py +799 -0
  19. vortex/data/handlers.py +1230 -0
  20. vortex/data/outflow.py +67 -0
  21. vortex/data/providers.py +487 -0
  22. vortex/data/resources.py +207 -0
  23. vortex/data/stores.py +1390 -0
  24. vortex/data/sync_templates/__init__.py +0 -0
  25. vortex/gloves.py +309 -0
  26. vortex/layout/__init__.py +20 -0
  27. vortex/layout/contexts.py +577 -0
  28. vortex/layout/dataflow.py +1220 -0
  29. vortex/layout/monitor.py +969 -0
  30. vortex/nwp/__init__.py +14 -0
  31. vortex/nwp/algo/__init__.py +21 -0
  32. vortex/nwp/algo/assim.py +537 -0
  33. vortex/nwp/algo/clim.py +1086 -0
  34. vortex/nwp/algo/coupling.py +831 -0
  35. vortex/nwp/algo/eda.py +840 -0
  36. vortex/nwp/algo/eps.py +785 -0
  37. vortex/nwp/algo/forecasts.py +886 -0
  38. vortex/nwp/algo/fpserver.py +1303 -0
  39. vortex/nwp/algo/ifsnaming.py +463 -0
  40. vortex/nwp/algo/ifsroot.py +404 -0
  41. vortex/nwp/algo/monitoring.py +263 -0
  42. vortex/nwp/algo/mpitools.py +694 -0
  43. vortex/nwp/algo/odbtools.py +1258 -0
  44. vortex/nwp/algo/oopsroot.py +916 -0
  45. vortex/nwp/algo/oopstests.py +220 -0
  46. vortex/nwp/algo/request.py +660 -0
  47. vortex/nwp/algo/stdpost.py +1641 -0
  48. vortex/nwp/data/__init__.py +30 -0
  49. vortex/nwp/data/assim.py +380 -0
  50. vortex/nwp/data/boundaries.py +314 -0
  51. vortex/nwp/data/climfiles.py +521 -0
  52. vortex/nwp/data/configfiles.py +153 -0
  53. vortex/nwp/data/consts.py +954 -0
  54. vortex/nwp/data/ctpini.py +149 -0
  55. vortex/nwp/data/diagnostics.py +209 -0
  56. vortex/nwp/data/eda.py +147 -0
  57. vortex/nwp/data/eps.py +432 -0
  58. vortex/nwp/data/executables.py +1045 -0
  59. vortex/nwp/data/fields.py +111 -0
  60. vortex/nwp/data/gridfiles.py +380 -0
  61. vortex/nwp/data/logs.py +584 -0
  62. vortex/nwp/data/modelstates.py +363 -0
  63. vortex/nwp/data/monitoring.py +193 -0
  64. vortex/nwp/data/namelists.py +696 -0
  65. vortex/nwp/data/obs.py +840 -0
  66. vortex/nwp/data/oopsexec.py +74 -0
  67. vortex/nwp/data/providers.py +207 -0
  68. vortex/nwp/data/query.py +206 -0
  69. vortex/nwp/data/stores.py +160 -0
  70. vortex/nwp/data/surfex.py +337 -0
  71. vortex/nwp/syntax/__init__.py +9 -0
  72. vortex/nwp/syntax/stdattrs.py +437 -0
  73. vortex/nwp/tools/__init__.py +10 -0
  74. vortex/nwp/tools/addons.py +40 -0
  75. vortex/nwp/tools/agt.py +67 -0
  76. vortex/nwp/tools/bdap.py +59 -0
  77. vortex/nwp/tools/bdcp.py +41 -0
  78. vortex/nwp/tools/bdm.py +24 -0
  79. vortex/nwp/tools/bdmp.py +54 -0
  80. vortex/nwp/tools/conftools.py +1661 -0
  81. vortex/nwp/tools/drhook.py +66 -0
  82. vortex/nwp/tools/grib.py +294 -0
  83. vortex/nwp/tools/gribdiff.py +104 -0
  84. vortex/nwp/tools/ifstools.py +203 -0
  85. vortex/nwp/tools/igastuff.py +273 -0
  86. vortex/nwp/tools/mars.py +68 -0
  87. vortex/nwp/tools/odb.py +657 -0
  88. vortex/nwp/tools/partitioning.py +258 -0
  89. vortex/nwp/tools/satrad.py +71 -0
  90. vortex/nwp/util/__init__.py +6 -0
  91. vortex/nwp/util/async.py +212 -0
  92. vortex/nwp/util/beacon.py +40 -0
  93. vortex/nwp/util/diffpygram.py +447 -0
  94. vortex/nwp/util/ens.py +279 -0
  95. vortex/nwp/util/hooks.py +139 -0
  96. vortex/nwp/util/taskdeco.py +85 -0
  97. vortex/nwp/util/usepygram.py +697 -0
  98. vortex/nwp/util/usetnt.py +101 -0
  99. vortex/proxy.py +6 -0
  100. vortex/sessions.py +374 -0
  101. vortex/syntax/__init__.py +9 -0
  102. vortex/syntax/stdattrs.py +867 -0
  103. vortex/syntax/stddeco.py +185 -0
  104. vortex/toolbox.py +1117 -0
  105. vortex/tools/__init__.py +20 -0
  106. vortex/tools/actions.py +523 -0
  107. vortex/tools/addons.py +316 -0
  108. vortex/tools/arm.py +96 -0
  109. vortex/tools/compression.py +325 -0
  110. vortex/tools/date.py +27 -0
  111. vortex/tools/ddhpack.py +10 -0
  112. vortex/tools/delayedactions.py +782 -0
  113. vortex/tools/env.py +541 -0
  114. vortex/tools/folder.py +834 -0
  115. vortex/tools/grib.py +738 -0
  116. vortex/tools/lfi.py +953 -0
  117. vortex/tools/listings.py +423 -0
  118. vortex/tools/names.py +637 -0
  119. vortex/tools/net.py +2124 -0
  120. vortex/tools/odb.py +10 -0
  121. vortex/tools/parallelism.py +368 -0
  122. vortex/tools/prestaging.py +210 -0
  123. vortex/tools/rawfiles.py +10 -0
  124. vortex/tools/schedulers.py +480 -0
  125. vortex/tools/services.py +940 -0
  126. vortex/tools/storage.py +996 -0
  127. vortex/tools/surfex.py +61 -0
  128. vortex/tools/systems.py +3976 -0
  129. vortex/tools/targets.py +440 -0
  130. vortex/util/__init__.py +9 -0
  131. vortex/util/config.py +1122 -0
  132. vortex/util/empty.py +24 -0
  133. vortex/util/helpers.py +216 -0
  134. vortex/util/introspection.py +69 -0
  135. vortex/util/iosponge.py +80 -0
  136. vortex/util/roles.py +49 -0
  137. vortex/util/storefunctions.py +129 -0
  138. vortex/util/structs.py +26 -0
  139. vortex/util/worker.py +162 -0
  140. vortex_nwp-2.0.0.dist-info/METADATA +67 -0
  141. vortex_nwp-2.0.0.dist-info/RECORD +144 -0
  142. vortex_nwp-2.0.0.dist-info/WHEEL +5 -0
  143. vortex_nwp-2.0.0.dist-info/licenses/LICENSE +517 -0
  144. vortex_nwp-2.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,363 @@
1
+ """
2
+ Resources to handle any NWP model state variable.
3
+ """
4
+
5
+ import re
6
+
7
+ from bronx.fancies import loggers
8
+
9
+ from vortex.data.flow import GeoFlowResource
10
+ from vortex.syntax.stdattrs import term_deco, term
11
+ from vortex.syntax.stddeco import namebuilding_insert
12
+ from bronx.stdtypes.date import Time
13
+
14
+ from ..tools.igastuff import archive_suffix
15
+ from vortex.data.geometries import CurvlinearGeometry
16
+
17
+ #: No automatic export
18
+ __all__ = []
19
+
20
+ logger = loggers.getLogger(__name__)
21
+
22
+
23
+ @namebuilding_insert("src", lambda s: [s.filling, s.model])
24
+ class AbstractAnalysis(GeoFlowResource):
25
+ """Analysis resource.
26
+
27
+ It can be an atmospheric, surface or full analysis (full = atmospheric + surface).
28
+ """
29
+
30
+ _abstract = True
31
+ _footprint = dict(
32
+ info="Analysis",
33
+ attr=dict(
34
+ kind=dict(values=["analysis", "analyse", "atm_analysis"]),
35
+ nativefmt=dict(
36
+ values=["fa", "grib", "lfi", "netcdf", "txt", "unknown"],
37
+ default="fa",
38
+ ),
39
+ filtering=dict(
40
+ info="The filtering that was applied during the generating process.",
41
+ optional=True,
42
+ values=["dfi"],
43
+ doc_zorder=-5,
44
+ ),
45
+ filling=dict(
46
+ info="The content/coverage of the analysis.",
47
+ optional=True,
48
+ default="full",
49
+ values=[
50
+ "surface",
51
+ "surf",
52
+ "atmospheric",
53
+ "atm",
54
+ "full",
55
+ "soil",
56
+ ],
57
+ remap=dict(
58
+ surface="surf",
59
+ atmospheric="atm",
60
+ ),
61
+ ),
62
+ ),
63
+ )
64
+
65
+ @property
66
+ def realkind(self):
67
+ return "analysis"
68
+
69
+
70
+ class Analysis3D(AbstractAnalysis):
71
+ """3D Analysis resource (i.e. the resource has no term attribute).
72
+
73
+ The data is assumed to be valid for **date** (i.e. term = 0).
74
+ """
75
+
76
+ _footprint = [
77
+ term,
78
+ dict(
79
+ attr=dict(
80
+ term=dict(
81
+ values=[
82
+ Time(0),
83
+ ],
84
+ optional=True,
85
+ default=Time(0),
86
+ )
87
+ )
88
+ ),
89
+ ]
90
+
91
+ def archive_basename(self):
92
+ """OP ARCHIVE specific naming convention."""
93
+ ananame = "analyse"
94
+ if "surf" in self.filling:
95
+ if re.match("aladin|arome", self.model):
96
+ ananame = "analyse_surf"
97
+ elif self.model == "surfex":
98
+ ananame = "analyse"
99
+ elif self.model in ("hycom", "mfwam"):
100
+ ananame = (
101
+ "(prefix:modelkey)(termfix:modelkey)(suffix:modelkey)"
102
+ )
103
+ else:
104
+ ananame = "analyse_surface1"
105
+
106
+ if self.filtering is not None:
107
+ if "aladin" in self.model:
108
+ ananame = "ANALYSE_DFI"
109
+
110
+ if self.model == "surfex":
111
+ ananame += ".sfx"
112
+
113
+ return ananame
114
+
115
+ def olive_basename(self):
116
+ """OLIVE specific naming convention."""
117
+ olivename_map = {
118
+ "atm": "TRAJ" + self.model[:4].upper() + "+0000",
119
+ "surf": "surfanalyse",
120
+ "full": "analyse",
121
+ }
122
+ if self.model != "arpege":
123
+ olivename_map["surf"] = "analyse"
124
+ if self.model == "surfex":
125
+ olivename_map = {
126
+ k: x + ".sfx" for k, x in olivename_map.items()
127
+ }
128
+ return olivename_map[self.filling]
129
+
130
+ def iga_pathinfo(self):
131
+ """Standard path information for IGA inline cache."""
132
+ if self.model == "arome":
133
+ if self.filling == "surf":
134
+ directory = "fic_day"
135
+ else:
136
+ directory = "workdir/analyse"
137
+ elif self.model == "arpege":
138
+ if self.filling == "surf":
139
+ directory = "workdir/analyse"
140
+ else:
141
+ directory = "autres"
142
+ elif self.model in ("hycom", "mfwam"):
143
+ if self.filling == "surf":
144
+ directory = "guess"
145
+ elif self.model == "surfex":
146
+ directory = "fic_day"
147
+ else:
148
+ if self.filling == "surf":
149
+ directory = "autres"
150
+ else:
151
+ directory = "workdir/analyse"
152
+ return dict(
153
+ fmt=directory,
154
+ model=self.model,
155
+ nativefmt=self.nativefmt,
156
+ )
157
+
158
+
159
+ class Analysis4D(AbstractAnalysis):
160
+ """4D Analysis resource (i.e. the resource has a term attribute)."""
161
+
162
+ _footprint = [
163
+ term_deco,
164
+ dict(
165
+ attr=dict(
166
+ term=dict(
167
+ outcast=[
168
+ Time(0),
169
+ ]
170
+ )
171
+ )
172
+ ),
173
+ ]
174
+
175
+
176
+ class InitialCondition(AbstractAnalysis):
177
+ """
178
+ Class for initial condition resources : anything from which a model run can be performed.
179
+ """
180
+
181
+ _footprint = dict(
182
+ info="Initial condition",
183
+ attr=dict(
184
+ kind=dict(
185
+ values=["initial_condition", "ic", "starting_point"],
186
+ remap=dict(autoremap="first"),
187
+ ),
188
+ ),
189
+ )
190
+
191
+ @property
192
+ def term(self):
193
+ """Fake term for duck typing."""
194
+ return Time(0)
195
+
196
+ @property
197
+ def realkind(self):
198
+ return "ic"
199
+
200
+ def olive_basename(self):
201
+ """OLIVE specific naming convention."""
202
+ logger.warning(
203
+ "The member number is only known by the provider, so the generic historic name is returned."
204
+ )
205
+ return "ICMSH" + self.model[:4].upper() + "+" + self.term.fmthour
206
+
207
+ def archive_basename(self):
208
+ """OP ARCHIVE specific naming convention."""
209
+ return "ICFC_(memberfix:member)"
210
+
211
+
212
+ class Historic(GeoFlowResource):
213
+ """
214
+ Class for historical state of a model (e.g. from a forecast).
215
+ """
216
+
217
+ _footprint = [
218
+ term_deco,
219
+ dict(
220
+ info="Historic forecast file",
221
+ attr=dict(
222
+ kind=dict(
223
+ values=["historic", "modelstate"],
224
+ remap=dict(modelstate="historic"),
225
+ ),
226
+ subset=dict(
227
+ # Dummy argument but avoid priority related messages with footprints
228
+ info="With Historical files, leave subset empty...",
229
+ optional=True,
230
+ values=[
231
+ None,
232
+ ],
233
+ ),
234
+ nativefmt=dict(
235
+ values=["fa", "grib", "lfi", "netcdf", "unknown", "nc"],
236
+ remap=dict(nc="netcdf"),
237
+ default="fa",
238
+ ),
239
+ ),
240
+ ),
241
+ ]
242
+
243
+ @property
244
+ def realkind(self):
245
+ return "historic"
246
+
247
+ def archive_basename(self):
248
+ """OP ARCHIVE specific naming convention."""
249
+ if self.model in ("mfwam", "hycom"):
250
+ prefix = "(prefix:modelkey)"
251
+ midfix = ""
252
+ else:
253
+ prefix = "(icmshfix:modelkey)"
254
+ midfix = "(histfix:igakey)"
255
+ termfix = "(termfix:modelkey)"
256
+ suffix = "(suffix:modelkey)"
257
+
258
+ if self.geometry.lam and re.match(
259
+ "testms1|testmp1|testmp2", self.geometry.area
260
+ ):
261
+ suffix = ".r" + archive_suffix(self.model, self.cutoff, self.date)
262
+
263
+ if self.model == "mocage":
264
+ prefix = "HM"
265
+ midfix = self.geometry.area
266
+ if self.nativefmt == "netcdf":
267
+ suffix = ".nc"
268
+
269
+ return prefix + midfix + termfix + suffix
270
+
271
+ def olive_basename(self):
272
+ """OLIVE specific naming convention."""
273
+ if self.model == "mesonh":
274
+ return ".".join(
275
+ (
276
+ self.model.upper(),
277
+ self.geometry.area[:4].upper() + "+" + self.term.fmthour,
278
+ self.nativefmt,
279
+ )
280
+ )
281
+ else:
282
+ return "ICMSH" + self.model[:4].upper() + "+" + self.term.fmthour
283
+
284
+ def _geo2basename_info(self, add_stretching=True):
285
+ """Return an array describing the geometry for the Vortex's name builder."""
286
+ if (
287
+ isinstance(self.geometry, CurvlinearGeometry)
288
+ and self.model == "hycom"
289
+ ):
290
+ # return the old naming convention for surges restart files
291
+ lgeo = [self.geometry.area, self.geometry.rnice]
292
+ return lgeo
293
+ else:
294
+ return super()._geo2basename_info(add_stretching=add_stretching)
295
+
296
+
297
+ @namebuilding_insert("filtername", lambda s: s.subset)
298
+ class HistoricSubset(GeoFlowResource):
299
+ """
300
+ Class for a subset of the historical state of a model (e.g. from a forecast).
301
+ """
302
+
303
+ _footprint = [
304
+ term_deco,
305
+ dict(
306
+ info="Subset of an historic forecast file",
307
+ attr=dict(
308
+ kind=dict(
309
+ values=["historic", "modelstate"],
310
+ remap=dict(modelstate="historic"),
311
+ ),
312
+ subset=dict(
313
+ info="The subset of fields contained in this data.",
314
+ ),
315
+ nativefmt=dict(
316
+ values=["fa", "grib", "lfi", "netcdf", "unknown", "nc"],
317
+ remap=dict(nc="netcdf"),
318
+ default="fa",
319
+ ),
320
+ ),
321
+ ),
322
+ ]
323
+
324
+ @property
325
+ def realkind(self):
326
+ return "historic"
327
+
328
+
329
+ class BiasDFI(GeoFlowResource):
330
+ """
331
+ Class for some kind of DFI bias (please add proper documentation).
332
+ """
333
+
334
+ _footprint = [
335
+ term_deco,
336
+ dict(
337
+ info="DFI bias file",
338
+ attr=dict(
339
+ kind=dict(
340
+ values=["biasdfi", "dfibias"],
341
+ remap=dict(dfibias="biasdfi"),
342
+ ),
343
+ nativefmt=dict(
344
+ values=["fa"],
345
+ default="fa",
346
+ ),
347
+ ),
348
+ ),
349
+ ]
350
+
351
+ @property
352
+ def realkind(self):
353
+ return "biasdfi"
354
+
355
+ def archive_basename(self):
356
+ """OP ARCHIVE specific naming convention."""
357
+ return "BIASDFI+{:04d}".format(self.term.hour)
358
+
359
+ def olive_basename(self):
360
+ """OLIVE specific naming convention."""
361
+ return "BIASDFI{:s}+{:04d}".format(
362
+ self.model[:4].upper(), self.term.hour
363
+ )
@@ -0,0 +1,193 @@
1
+ """
2
+ Various Resources to handle data produce by the obserations monitoring.
3
+ """
4
+
5
+ from bronx.fancies import loggers
6
+
7
+ from vortex.data.flow import FlowResource
8
+ from vortex.syntax.stddeco import namebuilding_append, namebuilding_insert
9
+ from .consts import GenvModelResource
10
+
11
+ #: Automatic export of Observations class
12
+ __all__ = []
13
+
14
+ logger = loggers.getLogger(__name__)
15
+
16
+
17
+ @namebuilding_insert("src", lambda s: [s.stage, s.obs])
18
+ class Monitoring(FlowResource):
19
+ """Abstract monitoring resource."""
20
+
21
+ _abstract = True
22
+ _footprint = dict(
23
+ info="Observations monitoring file",
24
+ attr=dict(
25
+ kind=dict(
26
+ values=[
27
+ "monitoring",
28
+ ],
29
+ ),
30
+ nativefmt=dict(
31
+ values=["ascii", "binary", "txt", "bin"],
32
+ remap=dict(ascii="txt", binary="bin"),
33
+ ),
34
+ stage=dict(
35
+ values=["can", "surf", "surface", "atm", "atmospheric"],
36
+ remap=dict(can="surf", surface="surf", atmospheric="atm"),
37
+ info="The processing stage of the ODB base.",
38
+ ),
39
+ obs=dict(
40
+ values=["all", "used"],
41
+ info="The processing part of the ODB base.",
42
+ ),
43
+ ),
44
+ )
45
+
46
+ @property
47
+ def realkind(self):
48
+ return "monitoring"
49
+
50
+
51
+ class MntObsThreshold(GenvModelResource):
52
+ """Observations threshold file.
53
+
54
+ A GenvKey can be given.
55
+ """
56
+
57
+ _footprint = dict(
58
+ info="Observations threshold",
59
+ attr=dict(
60
+ kind=dict(values=["obs_threshold"]),
61
+ gvar=dict(default="monitoring_seuils_obs"),
62
+ source=dict(),
63
+ ),
64
+ )
65
+
66
+ @property
67
+ def realkind(self):
68
+ return "obs_threshold"
69
+
70
+ def gget_urlquery(self):
71
+ """GGET specific query : ``extract``."""
72
+ return "extract=" + self.source
73
+
74
+
75
+ @namebuilding_insert("period", lambda s: s.periodicity)
76
+ class MntCumulStat(Monitoring):
77
+ """Accumulated statistics file."""
78
+
79
+ _footprint = dict(
80
+ info="Monthly accumulated statistics",
81
+ attr=dict(
82
+ kind=dict(values=["accumulated_stats"]),
83
+ nativefmt=dict(
84
+ values=["binary", "bin"], default="bin", optional=True
85
+ ),
86
+ periodicity=dict(
87
+ values=["monthly", "weekly_on_mondays", "weekly_on_sundays"],
88
+ default="monthly",
89
+ optional=True,
90
+ ),
91
+ ),
92
+ )
93
+
94
+ @property
95
+ def realkind(self):
96
+ return "accumulated_stats"
97
+
98
+
99
+ @namebuilding_append("src", lambda s: s.monitor)
100
+ class MntStat(Monitoring):
101
+ """Monitoring statistics file."""
102
+
103
+ _footprint = dict(
104
+ info="Monitoring statistics",
105
+ attr=dict(
106
+ kind=dict(values=["monitoring_stats"]),
107
+ nativefmt=dict(
108
+ values=["ascii", "txt"], default="txt", optional=True
109
+ ),
110
+ monitor=dict(
111
+ values=["bias", "analysis"],
112
+ remap=dict(cy="analysis", deb="bias"),
113
+ ),
114
+ ),
115
+ )
116
+
117
+ @property
118
+ def realkind(self):
119
+ return "monitoring_stats"
120
+
121
+
122
+ class MntGrossErrors(Monitoring):
123
+ """Gross errors file."""
124
+
125
+ _footprint = dict(
126
+ info="Gross errors",
127
+ attr=dict(
128
+ kind=dict(values=["gross_errors"]),
129
+ nativefmt=dict(
130
+ values=["ascii", "txt"], default="txt", optional=True
131
+ ),
132
+ ),
133
+ )
134
+
135
+ @property
136
+ def realkind(self):
137
+ return "gross_errors"
138
+
139
+
140
+ class MntNbMessages(Monitoring):
141
+ """Number of messages for each observations type"""
142
+
143
+ _footprint = dict(
144
+ info="Obs messages",
145
+ attr=dict(
146
+ kind=dict(values=["nbmessages"]),
147
+ nativefmt=dict(
148
+ values=["ascii", "txt"], default="txt", optional=True
149
+ ),
150
+ ),
151
+ )
152
+
153
+ @property
154
+ def realkind(self):
155
+ return "nbmessages"
156
+
157
+
158
+ class MntMissingObs(Monitoring):
159
+ """Missing observations."""
160
+
161
+ _footprint = dict(
162
+ info="Missing observations",
163
+ attr=dict(
164
+ kind=dict(values=["missing_obs"]),
165
+ nativefmt=dict(
166
+ values=["ascii", "txt"], default="txt", optional=True
167
+ ),
168
+ ),
169
+ )
170
+
171
+ @property
172
+ def realkind(self):
173
+ return "missing_obs"
174
+
175
+
176
+ class MntObsLocation(Monitoring):
177
+ """Observations location."""
178
+
179
+ _footprint = dict(
180
+ info="Observations location",
181
+ attr=dict(
182
+ kind=dict(values=["obslocation"]),
183
+ nativefmt=dict(
184
+ values=["obslocationpack"],
185
+ default="obslocationpack",
186
+ optional=True,
187
+ ),
188
+ ),
189
+ )
190
+
191
+ @property
192
+ def realkind(self):
193
+ return "obslocation"