wrfrun 0.1.8__py3-none-any.whl → 0.2.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 (53) hide show
  1. wrfrun/cli.py +131 -0
  2. wrfrun/core/base.py +52 -19
  3. wrfrun/core/config.py +257 -170
  4. wrfrun/core/error.py +8 -1
  5. wrfrun/core/replay.py +1 -1
  6. wrfrun/core/server.py +91 -71
  7. wrfrun/data.py +14 -16
  8. wrfrun/extension/goos_sst/__init__.py +5 -5
  9. wrfrun/extension/goos_sst/core.py +4 -1
  10. wrfrun/extension/goos_sst/res/Vtable.ERA_GOOS_SST +1 -1
  11. wrfrun/extension/goos_sst/res/__init__.py +17 -0
  12. wrfrun/extension/goos_sst/utils.py +21 -5
  13. wrfrun/extension/littler/__init__.py +57 -1
  14. wrfrun/extension/littler/{utils.py → core.py} +329 -43
  15. wrfrun/extension/utils.py +24 -22
  16. wrfrun/model/__init__.py +24 -1
  17. wrfrun/model/plot.py +259 -36
  18. wrfrun/model/utils.py +19 -9
  19. wrfrun/model/wrf/__init__.py +41 -0
  20. wrfrun/model/wrf/core.py +229 -101
  21. wrfrun/model/wrf/exec_wrap.py +49 -35
  22. wrfrun/model/wrf/geodata.py +2 -1
  23. wrfrun/model/wrf/namelist.py +78 -4
  24. wrfrun/model/wrf/{_metgrid.py → utils.py} +38 -3
  25. wrfrun/model/wrf/vtable.py +9 -5
  26. wrfrun/res/__init__.py +22 -7
  27. wrfrun/res/config/config.template.toml +57 -0
  28. wrfrun/res/{config.toml.template → config/wrf.template.toml} +7 -46
  29. wrfrun/res/run.template.sh +10 -0
  30. wrfrun/res/scheduler/lsf.template +5 -0
  31. wrfrun/res/{job_scheduler → scheduler}/pbs.template +1 -1
  32. wrfrun/res/{job_scheduler → scheduler}/slurm.template +2 -1
  33. wrfrun/run.py +39 -27
  34. wrfrun/scheduler/__init__.py +35 -0
  35. wrfrun/scheduler/env.py +44 -0
  36. wrfrun/scheduler/lsf.py +49 -0
  37. wrfrun/scheduler/pbs.py +50 -0
  38. wrfrun/scheduler/script.py +72 -0
  39. wrfrun/scheduler/slurm.py +50 -0
  40. wrfrun/scheduler/utils.py +14 -0
  41. wrfrun/utils.py +8 -3
  42. wrfrun/workspace/__init__.py +38 -0
  43. wrfrun/workspace/core.py +94 -0
  44. wrfrun/workspace/wrf.py +165 -0
  45. {wrfrun-0.1.8.dist-info → wrfrun-0.2.0.dist-info}/METADATA +3 -2
  46. wrfrun-0.2.0.dist-info/RECORD +62 -0
  47. wrfrun-0.2.0.dist-info/entry_points.txt +3 -0
  48. wrfrun/model/wrf/_ndown.py +0 -39
  49. wrfrun/pbs.py +0 -86
  50. wrfrun/res/run.sh.template +0 -16
  51. wrfrun/workspace.py +0 -88
  52. wrfrun-0.1.8.dist-info/RECORD +0 -51
  53. {wrfrun-0.1.8.dist-info → wrfrun-0.2.0.dist-info}/WHEEL +0 -0
wrfrun/cli.py ADDED
@@ -0,0 +1,131 @@
1
+ import argparse
2
+ import sys
3
+ from os import makedirs
4
+ from os.path import abspath, dirname, exists
5
+ from shutil import copyfile
6
+
7
+ import tomli
8
+ import tomli_w
9
+
10
+ from .core import WRFRunConfig
11
+ from .res import CONFIG_MAIN_TOML_TEMPLATE, CONFIG_WRF_TOML_TEMPLATE
12
+ from .utils import logger
13
+
14
+ MODEL_MAP = {
15
+ "wrf": CONFIG_WRF_TOML_TEMPLATE
16
+ }
17
+
18
+
19
+ wrfrun_config = WRFRunConfig("./.wrfrun")
20
+
21
+
22
+ def _entry_init(args: argparse.Namespace):
23
+ """
24
+ Initialize a wrfrun project.
25
+
26
+ :param args: Arguments namespace.
27
+ :type args: argparse.Namespace
28
+ """
29
+ args = vars(args)
30
+
31
+ project_name = args["name"]
32
+ models = args["models"]
33
+
34
+ if exists(project_name):
35
+ logger.error(f"{project_name} already exists.")
36
+ exit(1)
37
+
38
+ makedirs(f"{project_name}/configs")
39
+ makedirs(f"{project_name}/data")
40
+
41
+ copyfile(wrfrun_config.parse_resource_uri(CONFIG_MAIN_TOML_TEMPLATE), f"{project_name}/config.toml")
42
+
43
+ if models is not None:
44
+ for _model in models:
45
+ src_path = wrfrun_config.parse_resource_uri(MODEL_MAP[_model])
46
+ copyfile(src_path, f"{project_name}/configs/{_model}.toml")
47
+
48
+ logger.info(f"Created project {project_name}.")
49
+ logger.info(f"Use command `wrfrun add MODEL_NAME` to add a new model to project.")
50
+
51
+
52
+ def _entry_model(args: argparse.Namespace):
53
+ """
54
+ Manage models used by wrfrun project.
55
+
56
+ :param args: Arguments namespace.
57
+ :type args: argparse.Namespace
58
+ """
59
+ args = vars(args)
60
+ new_models = args["add"]
61
+ config_path = args["config"]
62
+
63
+ if not exists(config_path):
64
+ logger.error(f"Can't find '{config_path}', initialize this project first.")
65
+ exit(1)
66
+
67
+ model_config_map = {
68
+ "wrf": CONFIG_WRF_TOML_TEMPLATE
69
+ }
70
+
71
+ for _new_model in new_models:
72
+ if _new_model not in model_config_map:
73
+ logger.error(f"Unknow model type: '{_new_model}'")
74
+ exit(1)
75
+
76
+ config_dir_path = f"{abspath(dirname(config_path))}/configs"
77
+
78
+ if not exists(config_dir_path):
79
+ makedirs(config_dir_path)
80
+
81
+ with open(config_path, "rb") as f:
82
+ main_config = tomli.load(f)
83
+
84
+ for _new_model in new_models:
85
+ if _new_model not in main_config["model"]:
86
+ main_config["model"][_new_model] = {
87
+ "note": "Config of this model is generated by wrfrun cli command. DO NOT ADD CUSTOM CONFIG IN THIS SECTION because they may be overwrite by wrfrun cli tools.",
88
+ "use": True,
89
+ "include": f"./configs/{_new_model}.toml"
90
+ }
91
+
92
+ else:
93
+ if not ("use" in main_config["model"] and main_config["model"]["use"]):
94
+ main_config["model"][_new_model] = {
95
+ "note": "Config of this model is generated by wrfrun cli command. DO NOT ADD CUSTOM CONFIG IN THIS SECTION because they may be overwrite by wrfrun cli tools.",
96
+ "use": True,
97
+ "include": f"./configs/{_new_model}.toml"
98
+ }
99
+
100
+ for _new_model in new_models:
101
+ copyfile(wrfrun_config.parse_resource_uri(model_config_map[_new_model]), f"{config_dir_path}/{_new_model}.toml")
102
+
103
+ with open(config_path, "wb") as f:
104
+ tomli_w.dump(main_config, f)
105
+
106
+ logger.info(f"Added models: {new_models}")
107
+
108
+
109
+ def main_entry():
110
+ """
111
+ CLI entry point.
112
+ """
113
+
114
+ args_parser = argparse.ArgumentParser()
115
+ subparsers = args_parser.add_subparsers(title="Subcommands", description="Valid Subcommands", help="Subcommands")
116
+
117
+ init_parser = subparsers.add_parser("init", help="Initialize a wrfrun project.", add_help=True)
118
+ init_parser.add_argument("-n", "--name", type=str, required=True, help="Name of the wrfrun project.")
119
+ init_parser.add_argument("--models", nargs="*", type=str, help="List of models to use.", choices=["wrf"])
120
+ init_parser.set_defaults(func=_entry_init)
121
+
122
+ model_parser = subparsers.add_parser("model", help="Manage models used by wrfrun project.", add_help=True)
123
+ model_parser.add_argument("-c", "--config", type=str, default="config.toml", help="Path of the main config file.")
124
+ model_parser.add_argument("-a", "--add", nargs="+", required=True, type=str, help="Add models to the project.")
125
+ model_parser.set_defaults(func=_entry_model)
126
+
127
+ args = args_parser.parse_args(args=None if sys.argv[1:] else ["--help"])
128
+ args.func(args)
129
+
130
+
131
+ __all__ = ["main_entry"]
wrfrun/core/base.py CHANGED
@@ -13,14 +13,15 @@ Defines what :class:`ExecutableBase <Executable>` is, how it works and how ``wrf
13
13
  FileConfigDict
14
14
  ExecutableClassConfig
15
15
  ExecutableConfig
16
- _ExecutableConfigRecord
16
+ ExecutableConfigRecord
17
+ create_recorder
17
18
  ExecutableBase
18
19
 
19
20
  Executable
20
21
  **********
21
22
 
22
23
  While ``wrfrun`` aims to provide Python interfaces to various Numerical Weather Prediction model,
23
- it is important to provide a clear standard about how should a external executable file be implemented in ``wrfrun``.
24
+ it is important to provide a clear standard about how should an external executable file be implemented in ``wrfrun``.
24
25
  ``wrfrun`` provides a class called :class:`ExecutableBase`, which is the parent class for all ``Executable`` classes.
25
26
  It not only provide the method to execute external programs,
26
27
  but also:
@@ -54,7 +55,7 @@ from typing import Optional, TypedDict, Union
54
55
  import numpy as np
55
56
 
56
57
  from .config import WRFRUNConfig
57
- from .error import CommandError, ConfigError, OutputFileError
58
+ from .error import CommandError, ConfigError, OutputFileError, RecordError
58
59
  from ..utils import check_path, logger
59
60
 
60
61
 
@@ -154,7 +155,9 @@ class InputFileType(Enum):
154
155
 
155
156
  class FileConfigDict(TypedDict):
156
157
  """
157
- This dict is used to store information about the file, including its path, the path it will be copied or moved to, its new name, etc. This dict contains following keys:
158
+ This dict is used to store information about the file, including its path,
159
+ the path it will be copied or moved to, its new name, etc.
160
+ This dict contains following keys:
158
161
 
159
162
  .. py:attribute:: file_path
160
163
  :type: str
@@ -174,7 +177,8 @@ class FileConfigDict(TypedDict):
174
177
  .. py:attribute:: is_data
175
178
  :type: bool
176
179
 
177
- If the file is data. If not, ``wrfrun`` will treat it as a config file, and always save it to ``.replay`` file when recording the simulation.
180
+ If the file is data. If not, ``wrfrun`` will treat it as a config file,
181
+ and always save it to ``.replay`` file when recording the simulation.
178
182
 
179
183
  .. py:attribute:: is_output
180
184
  :type: bool
@@ -273,14 +277,14 @@ class ExecutableConfig(TypedDict):
273
277
  custom_config: Optional[dict]
274
278
 
275
279
 
276
- class _ExecutableConfigRecord:
280
+ class ExecutableConfigRecord:
277
281
  """
278
282
  A class to helps store configs of various executables and exports them to a file.
279
283
  """
280
284
  _instance = None
281
285
  _initialized = False
282
286
 
283
- def __init__(self, save_path: Optional[str] = None, include_data=False):
287
+ def __init__(self, save_path: str, include_data=False):
284
288
  """
285
289
 
286
290
  :param save_path: Save path of the exported config file.
@@ -300,9 +304,8 @@ class _ExecutableConfigRecord:
300
304
  self.save_path = save_path
301
305
  self.include_data = include_data
302
306
 
303
- self.work_path = WRFRUNConfig.parse_resource_uri(WRFRUNConfig.WRFRUN_REPLAY_WORK_PATH)
307
+ self.work_path = WRFRUNConfig.parse_resource_uri(WRFRUNConfig.WRFRUN_WORKSPACE_REPLAY)
304
308
  self.content_path = f"{self.work_path}/config_and_data"
305
- check_path(self.content_path)
306
309
 
307
310
  self._recorded_config = []
308
311
  self._name_count = {}
@@ -315,15 +318,20 @@ class _ExecutableConfigRecord:
315
318
 
316
319
  return cls._instance
317
320
 
318
- def reinit(self, save_path: Optional[str] = None, include_data=False):
321
+ @classmethod
322
+ def reinit(cls, save_path: str, include_data=False):
319
323
  """
320
324
  Reinitialize this instance.
321
325
 
326
+ :param save_path: Save path of the exported config file.
327
+ :type save_path: str
328
+ :param include_data: If includes input data.
329
+ :type include_data: bool
322
330
  :return: New instance.
323
- :rtype: _ExecutableConfigRecord
331
+ :rtype: ExecutableConfigRecord
324
332
  """
325
- self._initialized = False
326
- return _ExecutableConfigRecord(save_path, include_data)
333
+ cls._initialized = False
334
+ return cls(save_path, include_data)
327
335
 
328
336
  def record(self, exported_config: ExecutableConfig):
329
337
  """
@@ -348,7 +356,7 @@ class _ExecutableConfigRecord:
348
356
  self._name_count[name] = 1
349
357
  index = 1
350
358
 
351
- data_save_uri = f"{WRFRUNConfig.WRFRUN_REPLAY_WORK_PATH}/{name}/{index}"
359
+ data_save_uri = f"{WRFRUNConfig.WRFRUN_WORKSPACE_REPLAY}/{name}/{index}"
352
360
  data_save_path = f"{self.content_path}/{name}/{index}"
353
361
  makedirs(data_save_path)
354
362
 
@@ -414,7 +422,25 @@ class _ExecutableConfigRecord:
414
422
  logger.info(f"Replay config exported to {self.save_path}")
415
423
 
416
424
 
417
- ExecConfigRecorder = _ExecutableConfigRecord()
425
+ ExecConfigRecorder: Optional[ExecutableConfigRecord] = None
426
+
427
+
428
+ def create_recorder(save_path: str, include_data=False) -> ExecutableConfigRecord:
429
+ """
430
+ Create a recorder to record simulations.
431
+
432
+ :param save_path: Save path of the exported config file.
433
+ :type save_path: str
434
+ :param include_data: If includes input data.
435
+ :type include_data: bool
436
+ :return: Recorder instance.
437
+ :rtype: ExecutableConfigRecord
438
+ """
439
+ global ExecConfigRecorder
440
+
441
+ ExecConfigRecorder = ExecutableConfigRecord.reinit(save_path, include_data)
442
+
443
+ return ExecConfigRecorder
418
444
 
419
445
 
420
446
  class ExecutableBase:
@@ -585,25 +611,27 @@ class ExecutableBase:
585
611
 
586
612
  You can give more information with a ``FileConfigDict``.
587
613
 
614
+ >>> from wrfrun.workspace.wrf import get_wrf_workspace_path
588
615
  >>> file_dict: FileConfigDict = {
589
616
  ... "file_path": "data/custom_file.nc",
590
- ... "save_path": f"{WRFRUNConfig.WPS_WORK_PATH}",
617
+ ... "save_path": get_wrf_workspace_path("wps"),
591
618
  ... "save_name": "custom_file.nc",
592
619
  ... "is_data": True,
593
620
  ... "is_output": False
594
621
  ... }
595
622
  >>> self.add_input_files(file_dict)
596
623
 
624
+ >>> from wrfrun.workspace.wrf import get_wrf_workspace_path
597
625
  >>> file_dict_1: FileConfigDict = {
598
626
  ... "file_path": "data/custom_file",
599
- ... "save_path": f"{WRFRUNConfig.WPS_WORK_PATH}/geogrid",
627
+ ... "save_path": f"{get_wrf_workspace_path('wps')}/geogrid",
600
628
  ... "save_name": "GEOGRID.TBL",
601
629
  ... "is_data": False,
602
630
  ... "is_output": False
603
631
  ... }
604
632
  >>> file_dict_2: FileConfigDict = {
605
633
  ... "file_path": "data/custom_file",
606
- ... "save_path": f"{WRFRUNConfig.WPS_WORK_PATH}/outputs",
634
+ ... "save_path": f"{get_wrf_workspace_path('wps')}/outputs",
607
635
  ... "save_name": "test_file",
608
636
  ... "is_data": True,
609
637
  ... "is_output": True
@@ -842,7 +870,12 @@ class ExecutableBase:
842
870
  self.after_exec()
843
871
 
844
872
  if not WRFRUNConfig.IS_IN_REPLAY and WRFRUNConfig.IS_RECORDING:
873
+ global ExecConfigRecorder
874
+ if ExecConfigRecorder is None:
875
+ logger.error(f"Trying to record simulation before create the recorder.")
876
+ raise RecordError(f"Trying to record simulation before create the recorder.")
877
+
845
878
  ExecConfigRecorder.record(self.export_config())
846
879
 
847
880
 
848
- __all__ = ["ExecutableBase", "FileConfigDict", "InputFileType", "ExecutableConfig", "ExecutableClassConfig", "ExecConfigRecorder"]
881
+ __all__ = ["ExecutableBase", "FileConfigDict", "InputFileType", "ExecutableConfig", "ExecutableClassConfig", "ExecConfigRecorder", "create_recorder"]