wrfrun 0.1.7__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 (46) hide show
  1. wrfrun/__init__.py +3 -0
  2. wrfrun/core/__init__.py +5 -0
  3. wrfrun/core/base.py +680 -0
  4. wrfrun/core/config.py +717 -0
  5. wrfrun/core/error.py +80 -0
  6. wrfrun/core/replay.py +113 -0
  7. wrfrun/core/server.py +212 -0
  8. wrfrun/data.py +418 -0
  9. wrfrun/extension/__init__.py +1 -0
  10. wrfrun/extension/littler/__init__.py +1 -0
  11. wrfrun/extension/littler/utils.py +599 -0
  12. wrfrun/extension/utils.py +66 -0
  13. wrfrun/model/__init__.py +7 -0
  14. wrfrun/model/base.py +14 -0
  15. wrfrun/model/plot.py +54 -0
  16. wrfrun/model/utils.py +34 -0
  17. wrfrun/model/wrf/__init__.py +6 -0
  18. wrfrun/model/wrf/_metgrid.py +71 -0
  19. wrfrun/model/wrf/_ndown.py +39 -0
  20. wrfrun/model/wrf/core.py +805 -0
  21. wrfrun/model/wrf/exec_wrap.py +101 -0
  22. wrfrun/model/wrf/geodata.py +301 -0
  23. wrfrun/model/wrf/namelist.py +377 -0
  24. wrfrun/model/wrf/scheme.py +311 -0
  25. wrfrun/model/wrf/vtable.py +65 -0
  26. wrfrun/pbs.py +86 -0
  27. wrfrun/plot/__init__.py +1 -0
  28. wrfrun/plot/wps.py +188 -0
  29. wrfrun/res/__init__.py +22 -0
  30. wrfrun/res/config.toml.template +136 -0
  31. wrfrun/res/extension/plotgrids.ncl +216 -0
  32. wrfrun/res/job_scheduler/pbs.template +6 -0
  33. wrfrun/res/job_scheduler/slurm.template +6 -0
  34. wrfrun/res/namelist/namelist.input.da_wrfvar.template +261 -0
  35. wrfrun/res/namelist/namelist.input.dfi.template +260 -0
  36. wrfrun/res/namelist/namelist.input.real.template +256 -0
  37. wrfrun/res/namelist/namelist.input.wrf.template +256 -0
  38. wrfrun/res/namelist/namelist.wps.template +44 -0
  39. wrfrun/res/namelist/parame.in.template +11 -0
  40. wrfrun/res/run.sh.template +16 -0
  41. wrfrun/run.py +264 -0
  42. wrfrun/utils.py +257 -0
  43. wrfrun/workspace.py +88 -0
  44. wrfrun-0.1.7.dist-info/METADATA +67 -0
  45. wrfrun-0.1.7.dist-info/RECORD +46 -0
  46. wrfrun-0.1.7.dist-info/WHEEL +4 -0
@@ -0,0 +1,805 @@
1
+ from os import listdir
2
+ from os.path import abspath, basename, exists
3
+ from shutil import copyfile, move, rmtree
4
+ from typing import Optional
5
+
6
+ from wrfrun.core import ExecutableBase, FileConfigDict, InputFileError, NamelistError, WRFRUNConfig, WRFRUNExecDB
7
+ from wrfrun.utils import logger
8
+ from ._metgrid import reconcile_namelist_metgrid
9
+ from ._ndown import process_after_ndown
10
+ from .namelist import prepare_dfi_namelist, prepare_wps_namelist, prepare_wrf_namelist, prepare_wrfda_namelist
11
+ from .vtable import VtableFiles
12
+ from ..base import NamelistName
13
+
14
+
15
+ def _check_namelist_preparation():
16
+ if len(WRFRUNConfig.get_namelist("wps")) == 0:
17
+ prepare_wps_namelist()
18
+
19
+ if len(WRFRUNConfig.get_namelist("wrf")) == 0:
20
+ prepare_wrf_namelist()
21
+
22
+ if len(WRFRUNConfig.get_namelist("wrfda")) == 0:
23
+ prepare_wrfda_namelist()
24
+
25
+
26
+ class GeoGrid(ExecutableBase):
27
+ """
28
+ Execute "geogrid.exe".
29
+ """
30
+
31
+ def __init__(self, geogrid_tbl_file: Optional[str] = None, core_num: Optional[int] = None):
32
+ """
33
+ Execute "geogrid.exe".
34
+
35
+ :param geogrid_tbl_file: Custom GEOGRID.TBL file path. Defaults to None.
36
+ :type geogrid_tbl_file: str
37
+ :param core_num: An positive integer number of used core numbers. ``mpirun`` will be used to execute geogrid.exe if ``core_num != None``.
38
+ :type core_num: int
39
+ """
40
+ if isinstance(core_num, int) and core_num <= 0:
41
+ logger.warning(f"`core_num` should be greater than 0")
42
+ core_num = None
43
+
44
+ if core_num is None:
45
+ mpi_use = False
46
+ mpi_cmd = None
47
+ mpi_core_num = core_num
48
+
49
+ else:
50
+ mpi_use = True
51
+ mpi_cmd = "mpirun"
52
+ mpi_core_num = core_num
53
+
54
+ super().__init__(name="geogrid", cmd="./geogrid.exe", work_path=WRFRUNConfig.WPS_WORK_PATH, mpi_use=mpi_use, mpi_cmd=mpi_cmd, mpi_core_num=mpi_core_num)
55
+
56
+ self.geogrid_tbl_file = geogrid_tbl_file
57
+
58
+ _check_namelist_preparation()
59
+
60
+ def generate_custom_config(self):
61
+ """
62
+ Get and store namelist.
63
+ :return:
64
+ :rtype:
65
+ """
66
+ self.custom_config.update(
67
+ {
68
+ "namelist": WRFRUNConfig.get_namelist("wps"),
69
+ "geogrid_tbl_file": self.geogrid_tbl_file
70
+ }
71
+ )
72
+
73
+ def load_custom_config(self):
74
+ WRFRUNConfig.update_namelist(self.custom_config["namelist"], "wps")
75
+ self.geogrid_tbl_file = self.custom_config["geogrid_tbl_file"]
76
+
77
+ def before_exec(self):
78
+ WRFRUNConfig.check_wrfrun_context(True)
79
+ WRFRUNConfig.WRFRUN_WORK_STATUS = "geogrid"
80
+
81
+ if not WRFRUNConfig.IS_IN_REPLAY:
82
+ if self.geogrid_tbl_file is not None:
83
+ tbl_file: FileConfigDict = {
84
+ "file_path": self.geogrid_tbl_file, "save_path": f"{WRFRUNConfig.WPS_WORK_PATH}/geogrid",
85
+ "save_name": "GEOGRID.TBL", "is_data": False, "is_output": False
86
+ }
87
+ self.add_input_files(tbl_file)
88
+
89
+ super().before_exec()
90
+
91
+ WRFRUNConfig.write_namelist(f"{WRFRUNConfig.WPS_WORK_PATH}/{NamelistName.WPS}", "wps")
92
+
93
+ def after_exec(self):
94
+ if not WRFRUNConfig.IS_IN_REPLAY:
95
+ self.add_output_files(save_path=self._log_save_path, startswith="geogrid.log", outputs=NamelistName.WPS)
96
+ self.add_output_files(save_path=self._output_save_path, startswith="geo_em")
97
+
98
+ super().after_exec()
99
+
100
+ logger.info(f"All geogrid output files have been copied to {WRFRUNConfig.parse_resource_uri(self._output_save_path)}")
101
+
102
+
103
+ class LinkGrib(ExecutableBase):
104
+ """
105
+ Run command: "./link_grib.csh".
106
+ """
107
+
108
+ def __init__(self, grib_dir_path: str):
109
+ """
110
+ Execute "link_grib.csh".
111
+
112
+ :param grib_dir_path: GRIB data path. Absolute path is recommended.
113
+ :type grib_dir_path: str
114
+ """
115
+ self._link_grib_input_path = "./input_grib_data_dir"
116
+
117
+ super().__init__(name="link_grib", cmd=["./link_grib.csh", f"{self._link_grib_input_path}/*", "."], work_path=WRFRUNConfig.WPS_WORK_PATH)
118
+ self.grib_dir_path = grib_dir_path
119
+
120
+ def generate_custom_config(self):
121
+ self.class_config["class_args"] = (self.grib_dir_path,)
122
+
123
+ def replay(self):
124
+ self()
125
+
126
+ def before_exec(self):
127
+ if not WRFRUNConfig.IS_IN_REPLAY:
128
+
129
+ logger.debug(f"Input data are: {self.grib_dir_path}")
130
+ _grib_dir_path = abspath(self.grib_dir_path)
131
+
132
+ if not exists(_grib_dir_path):
133
+ logger.error(f"GRIB file directory not found: {_grib_dir_path}")
134
+ raise FileNotFoundError(f"GRIB file directory not found: {_grib_dir_path}")
135
+
136
+ save_path = f"{WRFRUNConfig.WPS_WORK_PATH}/{self._link_grib_input_path}"
137
+ save_path = WRFRUNConfig.parse_resource_uri(save_path)
138
+ if exists(save_path):
139
+ rmtree(save_path)
140
+
141
+ for _file in listdir(_grib_dir_path):
142
+ _file_config: FileConfigDict = {
143
+ "file_path": f"{_grib_dir_path}/{_file}",
144
+ "save_path": f"{WRFRUNConfig.WPS_WORK_PATH}/{self._link_grib_input_path}",
145
+ "save_name": _file, "is_data": True, "is_output": False,
146
+ }
147
+ self.add_input_files(_file_config)
148
+
149
+ super().before_exec()
150
+
151
+
152
+ class UnGrib(ExecutableBase):
153
+ """
154
+ Execute "ungrib.exe".
155
+ """
156
+
157
+ def __init__(self, vtable_file: Optional[str] = None, input_data_path: Optional[str] = None):
158
+ """
159
+ Execute "ungrib.exe".
160
+
161
+ :param vtable_file: Path of the Vtable file "ungrib.exe" used.
162
+ :type vtable_file: str
163
+ :param input_data_path: Path of the directory stores input GRIB2 files.
164
+ :type input_data_path: str
165
+ """
166
+ super().__init__(name="ungrib", cmd="./ungrib.exe", work_path=WRFRUNConfig.WPS_WORK_PATH)
167
+
168
+ self.vtable_file = vtable_file
169
+ self.input_data_path = input_data_path
170
+
171
+ _check_namelist_preparation()
172
+
173
+ def call_link_grib(self):
174
+ """
175
+ Execute "link_grib.csh" if needed.
176
+
177
+ :return:
178
+ :rtype:
179
+ """
180
+ if self.input_data_path is None:
181
+ self.input_data_path = WRFRUNConfig.get_input_data_path()
182
+
183
+ LinkGrib(self.input_data_path)()
184
+
185
+ def generate_custom_config(self):
186
+ """
187
+ Get and store namelist.
188
+
189
+ :return:
190
+ :rtype:
191
+ """
192
+ self.custom_config.update(
193
+ {
194
+ "namelist": WRFRUNConfig.get_namelist("wps"),
195
+ "vtable_file": self.vtable_file
196
+ }
197
+ )
198
+
199
+ def load_custom_config(self):
200
+ self.vtable_file = self.custom_config["vtable_file"]
201
+ WRFRUNConfig.update_namelist(self.custom_config["namelist"], "wps")
202
+
203
+ def replay(self):
204
+ super().__call__()
205
+
206
+ def before_exec(self):
207
+ WRFRUNConfig.check_wrfrun_context(True)
208
+ WRFRUNConfig.WRFRUN_WORK_STATUS = "ungrib"
209
+
210
+ if not WRFRUNConfig.IS_IN_REPLAY:
211
+ if self.vtable_file is None:
212
+ self.vtable_file = VtableFiles.ERA_PL
213
+
214
+ _file_config: FileConfigDict = {
215
+ "file_path": self.vtable_file,
216
+ "save_path": WRFRUNConfig.WPS_WORK_PATH,
217
+ "save_name": "Vtable",
218
+ "is_data": False,
219
+ "is_output": False
220
+ }
221
+ self.add_input_files(_file_config)
222
+
223
+ super().before_exec()
224
+
225
+ WRFRUNConfig.write_namelist(f"{WRFRUNConfig.WPS_WORK_PATH}/{NamelistName.WPS}", "wps")
226
+
227
+ def after_exec(self):
228
+ if not WRFRUNConfig.IS_IN_REPLAY:
229
+ self.add_output_files(output_dir=WRFRUNConfig.get_ungrib_out_dir_path(), save_path=self._output_save_path, startswith=WRFRUNConfig.get_ungrib_out_prefix())
230
+ self.add_output_files(save_path=self._log_save_path, outputs=["ungrib.log", "namelist.wps"])
231
+
232
+ super().after_exec()
233
+
234
+ logger.info(f"All ungrib output files have been copied to {WRFRUNConfig.parse_resource_uri(self._output_save_path)}")
235
+
236
+ def __call__(self):
237
+ self.call_link_grib()
238
+
239
+ super().__call__()
240
+
241
+
242
+ class MetGrid(ExecutableBase):
243
+ """
244
+ Execute "metgrid.exe".
245
+ """
246
+
247
+ def __init__(self, geogrid_data_path: Optional[str] = None, ungrib_data_path: Optional[str] = None, core_num: Optional[int] = None):
248
+ """
249
+ Execute "metgrid.exe".
250
+
251
+ :param geogrid_data_path: Directory path of outputs from geogrid.exe. If None, tries to use the output path specified by config file.
252
+ :type geogrid_data_path: str
253
+ :param ungrib_data_path: Directory path of outputs from ungrib.exe. If None, tries to use the output path specified by config file.
254
+ :type ungrib_data_path: str
255
+ :param core_num: An positive integer number of used core numbers. ``mpirun`` will be used to execute geogrid.exe if ``core_num != None``.
256
+ :type core_num: int
257
+ """
258
+ if isinstance(core_num, int) and core_num <= 0:
259
+ logger.warning(f"`core_num` should be greater than 0")
260
+ core_num = None
261
+
262
+ if core_num is None:
263
+ mpi_use = False
264
+ mpi_cmd = None
265
+ mpi_core_num = core_num
266
+
267
+ else:
268
+ mpi_use = True
269
+ mpi_cmd = "mpirun"
270
+ mpi_core_num = core_num
271
+
272
+ super().__init__(name="metgrid", cmd="./metgrid.exe", work_path=WRFRUNConfig.WPS_WORK_PATH, mpi_use=mpi_use, mpi_cmd=mpi_cmd, mpi_core_num=mpi_core_num)
273
+
274
+ self.geogrid_data_path = geogrid_data_path
275
+ self.ungrib_data_path = ungrib_data_path
276
+
277
+ _check_namelist_preparation()
278
+
279
+ def generate_custom_config(self):
280
+ """
281
+ Get and store namelist.
282
+ :return:
283
+ :rtype:
284
+ """
285
+ self.custom_config.update(
286
+ {
287
+ "geogrid_data_path": self.geogrid_data_path,
288
+ "ungrib_data_path": self.ungrib_data_path,
289
+ "namelist": WRFRUNConfig.get_namelist("wps"),
290
+ }
291
+ )
292
+
293
+ def load_custom_config(self):
294
+ self.geogrid_data_path = self.custom_config["geogrid_data_path"]
295
+ self.ungrib_data_path = self.custom_config["ungrib_data_path"]
296
+ WRFRUNConfig.update_namelist(self.custom_config["namelist"], "wps")
297
+
298
+ def before_exec(self):
299
+ WRFRUNConfig.check_wrfrun_context(True)
300
+ WRFRUNConfig.WRFRUN_WORK_STATUS = "metgrid"
301
+
302
+ if not WRFRUNConfig.IS_IN_REPLAY and not WRFRUNConfig.FAKE_SIMULATION_MODE:
303
+ # check input of metgrid.exe
304
+ # try to search input files in the output path if workspace is clear.
305
+ file_list = listdir(WRFRUNConfig.parse_resource_uri(WRFRUNConfig.WPS_WORK_PATH))
306
+
307
+ if "geo_em.d01.nc" not in file_list:
308
+
309
+ if self.geogrid_data_path is None:
310
+ self.geogrid_data_path = f"{WRFRUNConfig.WRFRUN_OUTPUT_PATH}/geogrid"
311
+ geogrid_data_path = WRFRUNConfig.parse_resource_uri(self.geogrid_data_path)
312
+
313
+ if not exists(geogrid_data_path) or "geo_em.d01.nc" not in listdir(geogrid_data_path):
314
+ logger.error(f"Can't find geogrid outputs in WPS_WORK_PATH and your outputs directory, which is essential to run metgrid")
315
+ raise FileNotFoundError(f"Can't find geogrid outputs in WPS_WORK_PATH and your outputs directory, which is essential to run metgrid")
316
+
317
+ else:
318
+ geogrid_file_list = [x for x in listdir(geogrid_data_path) if x.startswith("geo_em.d")]
319
+ for _file in geogrid_file_list:
320
+ _file_config = {
321
+ "file_path": f"{self.geogrid_data_path}/{_file}",
322
+ "save_path": WRFRUNConfig.WPS_WORK_PATH,
323
+ "save_name": _file,
324
+ "is_data": True,
325
+ "is_output": True
326
+ }
327
+ self.add_input_files(_file_config)
328
+
329
+ ungrib_output_dir = WRFRUNConfig.parse_resource_uri(WRFRUNConfig.get_ungrib_out_dir_path())
330
+ if basename(ungrib_output_dir) not in file_list or len(listdir(ungrib_output_dir)) == 0:
331
+
332
+ if self.ungrib_data_path is None:
333
+ self.ungrib_data_path = f"{WRFRUNConfig.WRFRUN_OUTPUT_PATH}/ungrib"
334
+
335
+ ungrib_data_path = WRFRUNConfig.parse_resource_uri(self.ungrib_data_path)
336
+
337
+ if not exists(ungrib_data_path) or len(listdir(ungrib_data_path)) == 0:
338
+ logger.error(f"Can't find ungrib outputs in WPS_WORK_PATH and your outputs directory, which is essential to run metgrid")
339
+ raise FileNotFoundError(f"Can't find ungrib outputs in WPS_WORK_PATH and your outputs directory, which is essential to run metgrid")
340
+
341
+ else:
342
+ ungrib_file_list = [x for x in listdir(ungrib_data_path)]
343
+ for _file in ungrib_file_list:
344
+ _file_config: FileConfigDict = {
345
+ "file_path": f"{self.ungrib_data_path}/{_file}",
346
+ "save_path": WRFRUNConfig.get_ungrib_out_dir_path(),
347
+ "save_name": _file,
348
+ "is_data": True,
349
+ "is_output": True
350
+ }
351
+ self.add_input_files(_file_config)
352
+
353
+ super().before_exec()
354
+
355
+ WRFRUNConfig.write_namelist(f"{WRFRUNConfig.WPS_WORK_PATH}/{NamelistName.WPS}", "wps")
356
+
357
+ def after_exec(self):
358
+ if not WRFRUNConfig.IS_IN_REPLAY:
359
+ self.add_output_files(save_path=self._log_save_path, startswith="metgrid.log", outputs="namelist.wps")
360
+ self.add_output_files(save_path=self._output_save_path, startswith="met_em")
361
+
362
+ super().after_exec()
363
+
364
+ logger.info(f"All metgrid output files have been copied to {WRFRUNConfig.parse_resource_uri(self._output_save_path)}")
365
+
366
+
367
+ class Real(ExecutableBase):
368
+ """
369
+ Execute "real.exe".
370
+ """
371
+
372
+ def __init__(self, metgrid_data_path: Optional[str] = None, core_num: Optional[int] = None):
373
+ """
374
+ Execute "real.exe".
375
+
376
+ :param metgrid_data_path: Path of the directory stores outputs from "metgrid.exe".
377
+ :type metgrid_data_path: str
378
+ :param core_num: An positive integer number of used core numbers. ``mpirun`` will be used to execute geogrid.exe if ``core_num != None``.
379
+ :type core_num: int
380
+ """
381
+ if isinstance(core_num, int) and core_num <= 0:
382
+ logger.warning(f"`core_num` should be greater than 0")
383
+ core_num = None
384
+
385
+ if core_num is None:
386
+ mpi_use = False
387
+ mpi_cmd = None
388
+ mpi_core_num = core_num
389
+
390
+ else:
391
+ mpi_use = True
392
+ mpi_cmd = "mpirun"
393
+ mpi_core_num = core_num
394
+
395
+ _check_namelist_preparation()
396
+
397
+ super().__init__(name="real", cmd="./real.exe", work_path=WRFRUNConfig.WRF_WORK_PATH, mpi_use=mpi_use, mpi_cmd=mpi_cmd, mpi_core_num=mpi_core_num)
398
+
399
+ self.metgrid_data_path = metgrid_data_path
400
+
401
+ def generate_custom_config(self):
402
+ self.custom_config["metgrid_data_path"] = self.metgrid_data_path
403
+ self.custom_config.update(
404
+ {
405
+ "namelist": WRFRUNConfig.get_namelist("wrf"),
406
+ "metgrid_data_path": self.metgrid_data_path
407
+ }
408
+ )
409
+
410
+ def load_custom_config(self):
411
+ self.metgrid_data_path = self.custom_config["metgrid_data_path"]
412
+ WRFRUNConfig.update_namelist(self.custom_config["namelist"], "wrf")
413
+
414
+ def before_exec(self):
415
+ WRFRUNConfig.check_wrfrun_context(True)
416
+ WRFRUNConfig.WRFRUN_WORK_STATUS = "real"
417
+
418
+ if not WRFRUNConfig.IS_IN_REPLAY and not WRFRUNConfig.FAKE_SIMULATION_MODE:
419
+ if self.metgrid_data_path is None:
420
+ self.metgrid_data_path = f"{WRFRUNConfig.WRFRUN_OUTPUT_PATH}/metgrid"
421
+
422
+ metgrid_data_path = WRFRUNConfig.parse_resource_uri(self.metgrid_data_path)
423
+ reconcile_namelist_metgrid(metgrid_data_path)
424
+
425
+ file_list = [x for x in listdir(metgrid_data_path) if x.startswith("met_em")]
426
+ for _file in file_list:
427
+ _file_config: FileConfigDict = {
428
+ "file_path": f"{self.metgrid_data_path}/{_file}",
429
+ "save_path": WRFRUNConfig.WRF_WORK_PATH,
430
+ "save_name": _file,
431
+ "is_data": True,
432
+ "is_output": True
433
+ }
434
+ self.add_input_files(_file_config)
435
+
436
+ super().before_exec()
437
+
438
+ WRFRUNConfig.write_namelist(f"{WRFRUNConfig.WRF_WORK_PATH}/{NamelistName.WRF}", "wrf")
439
+
440
+ def after_exec(self):
441
+ if not WRFRUNConfig.IS_IN_REPLAY:
442
+ self.add_output_files(save_path=self._output_save_path, startswith=("wrfbdy", "wrfinput", "wrflow"))
443
+ self.add_output_files(save_path=self._log_save_path, startswith="rsl.", outputs="namelist.input")
444
+
445
+ super().after_exec()
446
+
447
+ logger.info(f"All real output files have been copied to {WRFRUNConfig.parse_resource_uri(self._output_save_path)}")
448
+
449
+
450
+ class WRF(ExecutableBase):
451
+ """
452
+ Execute "wrf.exe".
453
+ """
454
+
455
+ def __init__(self, input_file_dir_path: Optional[str] = None, restart_file_dir_path: Optional[str] = None, save_restarts=False, core_num: Optional[int] = None):
456
+ """
457
+ Execute "wrf.exe"
458
+
459
+ :param input_file_dir_path: Path of the directory that stores input data for "wrf.exe".
460
+ :type input_file_dir_path: str
461
+ :param restart_file_dir_path: Path of the directory that stores restart files for "wrf.exe".
462
+ :type restart_file_dir_path: str
463
+ :param save_restarts: If saving restart files from "wrf.exe". Defaults to False.
464
+ :type save_restarts: bool
465
+ :param core_num: An positive integer number of used core numbers. ``mpirun`` will be used to execute geogrid.exe if ``core_num != None``.
466
+ :type core_num: int
467
+ """
468
+ if isinstance(core_num, int) and core_num <= 0:
469
+ logger.warning(f"`core_num` should be greater than 0")
470
+ core_num = None
471
+
472
+ if core_num is None:
473
+ mpi_use = False
474
+ mpi_cmd = None
475
+ mpi_core_num = core_num
476
+
477
+ else:
478
+ mpi_use = True
479
+ mpi_cmd = "mpirun"
480
+ mpi_core_num = core_num
481
+
482
+ _check_namelist_preparation()
483
+
484
+ super().__init__(name="wrf", cmd="./wrf.exe", work_path=WRFRUNConfig.WRF_WORK_PATH, mpi_use=mpi_use, mpi_cmd=mpi_cmd, mpi_core_num=mpi_core_num)
485
+
486
+ self.input_file_dir_path = input_file_dir_path
487
+ self.restart_file_dir_path = restart_file_dir_path
488
+ self.save_restarts = save_restarts
489
+
490
+ def generate_custom_config(self):
491
+ self.custom_config.update(
492
+ {
493
+ "input_file_dir_path": self.input_file_dir_path,
494
+ "restart_file_dir_path": self.restart_file_dir_path,
495
+ "namelist": WRFRUNConfig.get_namelist("wrf")
496
+ }
497
+ )
498
+
499
+ def load_custom_config(self):
500
+ self.input_file_dir_path = self.custom_config["input_file_dir_path"]
501
+ self.restart_file_dir_path = self.custom_config["restart_file_dir_path"]
502
+ WRFRUNConfig.update_namelist(self.custom_config["namelist"], "wrf")
503
+
504
+ def before_exec(self):
505
+ WRFRUNConfig.check_wrfrun_context(True)
506
+ # help wrfrun to make sure the input file is from real or ndown.
507
+ last_work_status = WRFRUNConfig.WRFRUN_WORK_STATUS
508
+ if last_work_status not in ["real", "ndown"]:
509
+ last_work_status = ""
510
+ WRFRUNConfig.WRFRUN_WORK_STATUS = "wrf"
511
+
512
+ if not WRFRUNConfig.IS_IN_REPLAY and not WRFRUNConfig.FAKE_SIMULATION_MODE:
513
+ if self.input_file_dir_path is None:
514
+ if last_work_status == "":
515
+ # assume we already have outputs from real.exe.
516
+ self.input_file_dir_path = f"{WRFRUNConfig.WRFRUN_OUTPUT_PATH}/real"
517
+ is_output = False
518
+ else:
519
+ self.input_file_dir_path = f"{WRFRUNConfig.WRFRUN_OUTPUT_PATH}/{last_work_status}"
520
+ is_output = True
521
+
522
+ else:
523
+ is_output = False
524
+
525
+ input_file_dir_path = WRFRUNConfig.parse_resource_uri(self.input_file_dir_path)
526
+
527
+ if exists(input_file_dir_path):
528
+ file_list = [x for x in listdir(input_file_dir_path) if x != "logs"]
529
+
530
+ for _file in file_list:
531
+ _file_config: FileConfigDict = {
532
+ "file_path": f"{self.input_file_dir_path}/{_file}",
533
+ "save_path": WRFRUNConfig.WRF_WORK_PATH,
534
+ "save_name": _file,
535
+ "is_data": True,
536
+ "is_output": is_output
537
+ }
538
+ self.add_input_files(_file_config)
539
+
540
+ if WRFRUNConfig.get_model_config("wrf")["restart_mode"]:
541
+ if self.restart_file_dir_path is None:
542
+ logger.error("You need to specify the restart file if you want to restart WRF.")
543
+ raise InputFileError("You need to specify the restart file if you want to restart WRF.")
544
+
545
+ restart_file_dir_path = WRFRUNConfig.parse_resource_uri(self.restart_file_dir_path)
546
+
547
+ if not exists(restart_file_dir_path):
548
+ logger.error(f"Restart files not found: {restart_file_dir_path}")
549
+ raise FileNotFoundError(f"Restart files not found: {restart_file_dir_path}")
550
+
551
+ file_list = [x for x in listdir(restart_file_dir_path) if x.startswith("wrfrst")]
552
+ for _file in file_list:
553
+ _file_config: FileConfigDict = {
554
+ "file_path": f"{self.restart_file_dir_path}/{_file}",
555
+ "save_path": WRFRUNConfig.WRF_WORK_PATH,
556
+ "save_name": _file,
557
+ "is_data": True,
558
+ "is_output": False
559
+ }
560
+ self.add_input_files(_file_config)
561
+
562
+ super().before_exec()
563
+
564
+ WRFRUNConfig.write_namelist(f"{WRFRUNConfig.WRF_WORK_PATH}/{NamelistName.WRF}", "wrf")
565
+
566
+ def after_exec(self):
567
+ if not WRFRUNConfig.IS_IN_REPLAY:
568
+ self.add_output_files(save_path=self._log_save_path, startswith="rsl.", outputs="namelist.input")
569
+ self.add_output_files(save_path=self._output_save_path, startswith="wrfout")
570
+ if self.save_restarts:
571
+ restart_save_path = f"{self._output_save_path}/restart"
572
+ self.add_output_files(save_path=restart_save_path, startswith="wrfrst", no_file_error=False)
573
+
574
+ super().after_exec()
575
+
576
+ logger.info(f"All wrf output files have been copied to {WRFRUNConfig.parse_resource_uri(self._output_save_path)}")
577
+
578
+
579
+ class DFI(ExecutableBase):
580
+ """
581
+ Execute "wrf.exe" to run DFI.
582
+ """
583
+
584
+ def __init__(self, input_file_dir_path: Optional[str] = None, update_real_output=True, core_num: Optional[int] = None):
585
+ """
586
+ Execute "wrf.exe" to run DFI.
587
+
588
+ :param input_file_dir_path: Path of the directory that stores input data for "wrf.exe".
589
+ :type input_file_dir_path: str
590
+ :param update_real_output: If update the corresponding file in real.exe output directory.
591
+ :type update_real_output: bool
592
+ :param core_num: An positive integer number of used core numbers. ``mpirun`` will be used to execute geogrid.exe if ``core_num != None``.
593
+ :type core_num: int
594
+ """
595
+ if isinstance(core_num, int) and core_num <= 0:
596
+ logger.warning(f"`core_num` should be greater than 0")
597
+ core_num = None
598
+
599
+ if core_num is None:
600
+ mpi_use = False
601
+ mpi_cmd = None
602
+ mpi_core_num = core_num
603
+
604
+ else:
605
+ mpi_use = True
606
+ mpi_cmd = "mpirun"
607
+ mpi_core_num = core_num
608
+
609
+ super().__init__(name="dfi", cmd="./wrf.exe", work_path=WRFRUNConfig.WRF_WORK_PATH, mpi_use=mpi_use, mpi_cmd=mpi_cmd, mpi_core_num=mpi_core_num)
610
+
611
+ self.input_file_dir_path = input_file_dir_path
612
+ self.update_real_output = update_real_output
613
+
614
+ def generate_custom_config(self):
615
+ self.custom_config.update(
616
+ {
617
+ "input_file_dir_path": self.input_file_dir_path,
618
+ "update_real_output": self.update_real_output,
619
+ "namelist": WRFRUNConfig.get_namelist("dfi")
620
+ }
621
+ )
622
+
623
+ def load_custom_config(self):
624
+ self.input_file_dir_path = self.custom_config["input_file_dir_path"]
625
+ self.update_real_output = self.custom_config["update_real_output"]
626
+
627
+ if not WRFRUNConfig.register_custom_namelist_id("dfi"):
628
+ logger.error("Can't register namelist for DFI.")
629
+ raise NamelistError("Can't register namelist for DFI.")
630
+ WRFRUNConfig.update_namelist(self.custom_config["namelist"], "dfi")
631
+
632
+ def before_exec(self):
633
+ WRFRUNConfig.check_wrfrun_context(True)
634
+ WRFRUNConfig.WRFRUN_WORK_STATUS = "dfi"
635
+
636
+ if not WRFRUNConfig.IS_IN_REPLAY and not WRFRUNConfig.FAKE_SIMULATION_MODE:
637
+ # prepare config
638
+ if self.input_file_dir_path is None:
639
+ self.input_file_dir_path = f"{WRFRUNConfig.WRFRUN_OUTPUT_PATH}/real"
640
+ is_output = True
641
+
642
+ else:
643
+ is_output = False
644
+
645
+ input_file_dir_path = WRFRUNConfig.parse_resource_uri(self.input_file_dir_path)
646
+
647
+ if exists(input_file_dir_path):
648
+ file_list = [x for x in listdir(input_file_dir_path) if x != "logs"]
649
+
650
+ for _file in file_list:
651
+ _file_config: FileConfigDict = {
652
+ "file_path": f"{self.input_file_dir_path}/{_file}",
653
+ "save_path": WRFRUNConfig.WRF_WORK_PATH,
654
+ "save_name": _file,
655
+ "is_data": True,
656
+ "is_output": is_output
657
+ }
658
+ self.add_input_files(_file_config)
659
+
660
+ if not WRFRUNConfig.register_custom_namelist_id("dfi"):
661
+ logger.error("Can't register namelist for DFI.")
662
+ raise NamelistError("Can't register namelist for DFI.")
663
+
664
+ prepare_dfi_namelist()
665
+
666
+ super().before_exec()
667
+ WRFRUNConfig.write_namelist(f"{WRFRUNConfig.WRF_WORK_PATH}/{NamelistName.WRF}", "dfi")
668
+
669
+ def after_exec(self):
670
+ if not WRFRUNConfig.IS_IN_REPLAY:
671
+ self.add_output_files(save_path=self._log_save_path, startswith="rsl.", outputs="namelist.input")
672
+ self.add_output_files(save_path=self._output_save_path, startswith="wrfinput_initialized_")
673
+
674
+ super().after_exec()
675
+
676
+ parsed_output_save_path = WRFRUNConfig.parse_resource_uri(self._output_save_path)
677
+ if self.update_real_output and not WRFRUNConfig.FAKE_SIMULATION_MODE:
678
+ real_dir_path = WRFRUNConfig.parse_resource_uri(self.input_file_dir_path)
679
+
680
+ move(f"{real_dir_path}/wrfinput_d01", f"{real_dir_path}/wrfinput_d01_before_dfi")
681
+ copyfile(f"{parsed_output_save_path}/wrfinput_initialized_d01", f"{real_dir_path}/wrfinput_d01")
682
+ logger.info(f"Replace real.exe output 'wrfinput_d01' with outputs, old file has been renamed as 'wrfinput_d01_before_dfi'")
683
+
684
+ logger.info(f"All DFI output files have been copied to {parsed_output_save_path}")
685
+
686
+
687
+ class NDown(ExecutableBase):
688
+ """
689
+ Execute "ndown.exe".
690
+ """
691
+
692
+ def __init__(self, wrfout_file_path: str, real_output_dir_path: Optional[str] = None, update_namelist=True, core_num: Optional[int] = None):
693
+ """
694
+ Execute "ndown.exe".
695
+
696
+ :param wrfout_file_path: wrfout file path.
697
+ :type wrfout_file_path: str
698
+ :param real_output_dir_path: Path of the directory that contains output of "real.exe".
699
+ :type real_output_dir_path: str
700
+ :param update_namelist: If update wrf's namelist for the final integral.
701
+ :type update_namelist: bool
702
+ :param core_num:
703
+ :type core_num:
704
+ """
705
+ if isinstance(core_num, int) and core_num <= 0:
706
+ logger.warning(f"`core_num` should be greater than 0")
707
+ core_num = None
708
+
709
+ if core_num is None:
710
+ mpi_use = False
711
+ mpi_cmd = None
712
+ mpi_core_num = core_num
713
+
714
+ else:
715
+ mpi_use = True
716
+ mpi_cmd = "mpirun"
717
+ mpi_core_num = core_num
718
+
719
+ _check_namelist_preparation()
720
+
721
+ super().__init__(name="ndown", cmd="./ndown.exe", work_path=WRFRUNConfig.WRF_WORK_PATH, mpi_use=mpi_use, mpi_cmd=mpi_cmd, mpi_core_num=mpi_core_num)
722
+
723
+ self.wrfout_file_path = wrfout_file_path
724
+ self.real_output_dir_path = real_output_dir_path
725
+ self.update_namelist = update_namelist
726
+
727
+ def generate_custom_config(self):
728
+ self.class_config["class_args"] = (self.wrfout_file_path,)
729
+ self.custom_config.update(
730
+ {
731
+ "real_output_dir_path": self.real_output_dir_path,
732
+ "update_namelist": self.update_namelist,
733
+ "namelist": WRFRUNConfig.get_namelist("wrf"),
734
+ }
735
+ )
736
+
737
+ def load_custom_config(self):
738
+ self.real_output_dir_path = self.custom_config["real_output_dir_path"]
739
+ self.update_namelist = self.custom_config["update_namelist"]
740
+ WRFRUNConfig.update_namelist(self.custom_config["namelist"])
741
+
742
+ def before_exec(self):
743
+ WRFRUNConfig.check_wrfrun_context(True)
744
+ WRFRUNConfig.WRFRUN_WORK_STATUS = "ndown"
745
+
746
+ # we need to make sure time_control.io_form_auxinput2 is 2.
747
+ # which means the format of input stream 2 is NetCDF.
748
+ WRFRUNConfig.update_namelist({"time_control": {"io_form_auxinput2": 2}}, "wrf")
749
+
750
+ if self.real_output_dir_path is None:
751
+ self.real_output_dir_path = f"{WRFRUNConfig.WRFRUN_OUTPUT_PATH}/real"
752
+ is_output = True
753
+
754
+ else:
755
+ is_output = False
756
+
757
+ wrfndi_file_config: FileConfigDict = {
758
+ "file_path": f"{self.real_output_dir_path}/wrfinput_d02",
759
+ "save_path": WRFRUNConfig.WRF_WORK_PATH,
760
+ "save_name": "wrfndi_d02",
761
+ "is_data": True,
762
+ "is_output": is_output
763
+ }
764
+ wrfout_file_config: FileConfigDict = {
765
+ "file_path": self.wrfout_file_path,
766
+ "save_path": WRFRUNConfig.WRF_WORK_PATH,
767
+ "save_name": "wrfout_d01",
768
+ "is_data": True,
769
+ "is_output": False
770
+ }
771
+
772
+ self.add_input_files([wrfndi_file_config, wrfout_file_config])
773
+
774
+ super().before_exec()
775
+
776
+ WRFRUNConfig.write_namelist(f"{WRFRUNConfig.WRF_WORK_PATH}/{NamelistName.WRF}", "wrf")
777
+
778
+ def after_exec(self):
779
+ self.add_output_files(save_path=self._log_save_path, startswith="rsl.", outputs="namelist.input")
780
+ self.add_output_files(save_path=self._output_save_path, outputs=["wrfinput_d02", "wrfbdy_d02"])
781
+ # also save other outputs of real.exe, so WRF can directly use them.
782
+ self.add_output_files(output_dir=f"{WRFRUNConfig.WRFRUN_OUTPUT_PATH}/real", save_path=self._output_save_path,
783
+ startswith="wrflowinp_", no_file_error=False)
784
+
785
+ super().after_exec()
786
+
787
+ parsed_output_save_path = WRFRUNConfig.parse_resource_uri(self._output_save_path)
788
+
789
+ move(f"{parsed_output_save_path}/wrfinput_d02", f"{parsed_output_save_path}/wrfinput_d01")
790
+ move(f"{parsed_output_save_path}/wrfbdy_d02", f"{parsed_output_save_path}/wrfbdy_d01")
791
+
792
+ logger.info(f"All ndown output files have been copied to {parsed_output_save_path}")
793
+
794
+ if self.update_namelist:
795
+ process_after_ndown()
796
+
797
+
798
+ class_list = [GeoGrid, LinkGrib, UnGrib, MetGrid, Real, WRF, NDown]
799
+ class_id_list = ["geogrid", "link_grib", "ungrib", "metgrid", "real", "wrf", "ndown"]
800
+
801
+ for _class, _id in zip(class_list, class_id_list):
802
+ if not WRFRUNExecDB.is_registered(_id):
803
+ WRFRUNExecDB.register_exec(_id, _class)
804
+
805
+ __all__ = ["GeoGrid", "LinkGrib", "UnGrib", "MetGrid", "Real", "WRF", "DFI", "NDown"]