wrfrun 0.2.0__py3-none-any.whl → 0.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.
- wrfrun/__init__.py +8 -3
- wrfrun/cli.py +69 -29
- wrfrun/core/__init__.py +27 -10
- wrfrun/core/_config.py +308 -0
- wrfrun/core/_constant.py +236 -0
- wrfrun/core/_exec_db.py +105 -0
- wrfrun/core/_namelist.py +287 -0
- wrfrun/core/_record.py +178 -0
- wrfrun/core/_resource.py +172 -0
- wrfrun/core/base.py +132 -406
- wrfrun/core/core.py +196 -0
- wrfrun/core/error.py +28 -2
- wrfrun/core/replay.py +10 -96
- wrfrun/core/server.py +52 -27
- wrfrun/core/type.py +171 -0
- wrfrun/data.py +304 -139
- wrfrun/extension/goos_sst/__init__.py +2 -2
- wrfrun/extension/goos_sst/core.py +9 -14
- wrfrun/extension/goos_sst/res/__init__.py +0 -1
- wrfrun/extension/goos_sst/utils.py +50 -44
- wrfrun/extension/littler/core.py +105 -88
- wrfrun/extension/utils.py +4 -3
- wrfrun/log.py +117 -0
- wrfrun/model/__init__.py +11 -7
- wrfrun/model/constants.py +52 -0
- wrfrun/model/palm/__init__.py +30 -0
- wrfrun/model/palm/core.py +145 -0
- wrfrun/model/palm/namelist.py +33 -0
- wrfrun/model/plot.py +99 -119
- wrfrun/model/type.py +116 -0
- wrfrun/model/utils.py +9 -20
- wrfrun/model/wrf/__init__.py +4 -9
- wrfrun/model/wrf/core.py +246 -161
- wrfrun/model/wrf/exec_wrap.py +13 -12
- wrfrun/model/wrf/geodata.py +116 -100
- wrfrun/model/wrf/log.py +103 -0
- wrfrun/model/wrf/namelist.py +90 -73
- wrfrun/model/wrf/plot.py +102 -0
- wrfrun/model/wrf/scheme.py +108 -52
- wrfrun/model/wrf/utils.py +39 -25
- wrfrun/model/wrf/vtable.py +35 -3
- wrfrun/plot/__init__.py +20 -0
- wrfrun/plot/wps.py +90 -73
- wrfrun/res/__init__.py +103 -5
- wrfrun/res/config/config.template.toml +8 -0
- wrfrun/res/config/palm.template.toml +23 -0
- wrfrun/run.py +105 -77
- wrfrun/scheduler/__init__.py +1 -0
- wrfrun/scheduler/lsf.py +3 -2
- wrfrun/scheduler/pbs.py +3 -2
- wrfrun/scheduler/script.py +17 -5
- wrfrun/scheduler/slurm.py +3 -2
- wrfrun/scheduler/utils.py +14 -2
- wrfrun/utils.py +88 -199
- wrfrun/workspace/__init__.py +8 -5
- wrfrun/workspace/core.py +20 -12
- wrfrun/workspace/palm.py +137 -0
- wrfrun/workspace/wrf.py +16 -15
- wrfrun-0.3.0.dist-info/METADATA +240 -0
- wrfrun-0.3.0.dist-info/RECORD +78 -0
- wrfrun/core/config.py +0 -923
- wrfrun/model/base.py +0 -14
- wrfrun-0.2.0.dist-info/METADATA +0 -68
- wrfrun-0.2.0.dist-info/RECORD +0 -62
- {wrfrun-0.2.0.dist-info → wrfrun-0.3.0.dist-info}/WHEEL +0 -0
- {wrfrun-0.2.0.dist-info → wrfrun-0.3.0.dist-info}/entry_points.txt +0 -0
wrfrun/utils.py
CHANGED
|
@@ -1,82 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
from os import chdir, getcwd, makedirs, environ
|
|
5
|
-
from os.path import exists
|
|
6
|
-
from shutil import rmtree
|
|
7
|
-
from time import time
|
|
8
|
-
from typing import Optional, List, Dict
|
|
9
|
-
|
|
10
|
-
from rich.logging import RichHandler
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def set_logger(logger_list: List[str], logger_level: Optional[Dict] = None):
|
|
14
|
-
"""
|
|
15
|
-
This function will replace all handlers of each logger in ``logger_list`` with RichHandler.
|
|
16
|
-
If there are some custom handlers in logger, they will be replaced too.
|
|
17
|
-
|
|
18
|
-
:param logger_list: A list contains loggers.
|
|
19
|
-
:type logger_list: list
|
|
20
|
-
:param logger_level: You can specify the log level in ``logger_level``, with the name of logger is the key, and the level of logger is the value.
|
|
21
|
-
Default if None, with which all loggers' level will be set to ``logging.WARNING``.
|
|
22
|
-
:type logger_level: list | None
|
|
23
|
-
:return:
|
|
24
|
-
:rtype:
|
|
25
|
-
"""
|
|
26
|
-
formatter = logging.Formatter(
|
|
27
|
-
"%(name)s :: %(message)s", datefmt="%Y-%m-%d %H:%M:%S"
|
|
28
|
-
)
|
|
29
|
-
# use rich handler
|
|
30
|
-
handler = RichHandler()
|
|
31
|
-
handler.setFormatter(formatter)
|
|
1
|
+
"""
|
|
2
|
+
wrfrun.utils
|
|
3
|
+
############
|
|
32
4
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
_logger = logging.getLogger(logger_name)
|
|
36
|
-
for _handler in _logger.handlers:
|
|
37
|
-
_logger.removeHandler(_handler)
|
|
38
|
-
_logger.addHandler(handler)
|
|
5
|
+
.. autosummary::
|
|
6
|
+
:toctree: generated/
|
|
39
7
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
# init wrfrun logger
|
|
47
|
-
logger = logging.getLogger("wrfrun")
|
|
48
|
-
# check environment variables and set logger level
|
|
49
|
-
if "WRFRUN_DEBUG_MODE" in environ and environ["WRFRUN_DEBUG_MODE"]:
|
|
50
|
-
_logger_level = logging.DEBUG
|
|
51
|
-
else:
|
|
52
|
-
_logger_level = logging.INFO
|
|
53
|
-
set_logger(["wrfrun", ], {"wrfrun": _logger_level})
|
|
8
|
+
check_path
|
|
9
|
+
rectify_domain_size
|
|
10
|
+
_calculate_domain_shape
|
|
11
|
+
calculate_domain_shape
|
|
12
|
+
_check_domain_shape
|
|
13
|
+
check_domain_shape
|
|
54
14
|
|
|
15
|
+
Utility submodule.
|
|
16
|
+
"""
|
|
55
17
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
This function will replace all handlers of each logger with ``rich.logging.RichHandler``.
|
|
60
|
-
Use this carefully.
|
|
61
|
-
|
|
62
|
-
:return:
|
|
63
|
-
:rtype:
|
|
64
|
-
"""
|
|
65
|
-
set_logger(
|
|
66
|
-
["cdsapi", "cfgrib", "datapi"],
|
|
67
|
-
{
|
|
68
|
-
"cdsapi": logging.INFO,
|
|
69
|
-
"cfgrib": logging.ERROR,
|
|
70
|
-
"datapi": logging.INFO
|
|
71
|
-
}
|
|
72
|
-
)
|
|
18
|
+
from os import makedirs
|
|
19
|
+
from os.path import exists
|
|
20
|
+
from shutil import rmtree
|
|
73
21
|
|
|
74
22
|
|
|
75
23
|
def check_path(*args, force=False):
|
|
76
|
-
"""
|
|
77
|
-
|
|
78
|
-
Returns:
|
|
24
|
+
"""
|
|
25
|
+
Check and create all the path in args.
|
|
79
26
|
|
|
27
|
+
:param args: Path list.
|
|
28
|
+
:type args: list[str]
|
|
29
|
+
:param force: If ``True``, delete existed directory and create a new.
|
|
30
|
+
:type force: bool
|
|
80
31
|
"""
|
|
81
32
|
for _path in args:
|
|
82
33
|
if exists(_path) and force:
|
|
@@ -85,32 +36,16 @@ def check_path(*args, force=False):
|
|
|
85
36
|
makedirs(_path)
|
|
86
37
|
|
|
87
38
|
|
|
88
|
-
def logger_add_file_handler(log_path: str):
|
|
89
|
-
"""Add file handler to logger
|
|
90
|
-
|
|
91
|
-
Args:
|
|
92
|
-
log_path (str): Folder to place log file
|
|
93
|
-
"""
|
|
94
|
-
# check log save path
|
|
95
|
-
check_path(log_path)
|
|
96
|
-
|
|
97
|
-
# add file handler
|
|
98
|
-
file_handler = logging.FileHandler(
|
|
99
|
-
f"{log_path}/{datetime.fromtimestamp(time()).strftime('%Y-%m-%d %H:%M:%S')}.log")
|
|
100
|
-
file_handler.setFormatter(logging.Formatter(
|
|
101
|
-
"%(asctime)s - %(name)s - %(levelname)s :: %(message)s", datefmt="%m-%d %H:%M:%S"))
|
|
102
|
-
logger.addHandler(file_handler)
|
|
103
|
-
|
|
104
|
-
|
|
105
39
|
def rectify_domain_size(point_num: int, nest_ratio: int) -> int:
|
|
106
|
-
"""
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
40
|
+
"""
|
|
41
|
+
Rectify domain size.
|
|
42
|
+
|
|
43
|
+
:param point_num: Point number of one side.
|
|
44
|
+
:type point_num: int
|
|
45
|
+
:param nest_ratio: The nesting ratio relative to the domain's parent.
|
|
46
|
+
:type nest_ratio: int
|
|
47
|
+
:return: New size.
|
|
48
|
+
:rtype: int
|
|
114
49
|
"""
|
|
115
50
|
# calculate remainder
|
|
116
51
|
point_num_mod = (point_num - 1) % nest_ratio
|
|
@@ -123,22 +58,23 @@ def rectify_domain_size(point_num: int, nest_ratio: int) -> int:
|
|
|
123
58
|
point_num -= point_num_mod
|
|
124
59
|
else:
|
|
125
60
|
# # point_num_mod is closer to nest_ratio than (nest_ratio - 1)
|
|
126
|
-
point_num +=
|
|
61
|
+
point_num += nest_ratio - point_num_mod
|
|
127
62
|
|
|
128
63
|
return point_num
|
|
129
64
|
|
|
130
65
|
|
|
131
66
|
def _calculate_domain_shape(step: float, resolution: int, grid_resolution=110, nest_ratio=1) -> int:
|
|
132
|
-
"""
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
67
|
+
"""
|
|
68
|
+
Calculate domain shape based on its area.
|
|
69
|
+
|
|
70
|
+
:param step: Length of the side. Unit: degree.
|
|
71
|
+
:type step: float
|
|
72
|
+
:param resolution: Resolution of domain. Unit: km.
|
|
73
|
+
:type resolution: int
|
|
74
|
+
:param grid_resolution: Resolution of grid. Unit: km. Defaults to 110.
|
|
75
|
+
:param nest_ratio: The nesting ratio relative to the domain’s parent.
|
|
76
|
+
:return: ``(length of x, length of y)``.
|
|
77
|
+
:rtype: int
|
|
142
78
|
"""
|
|
143
79
|
# calculate res based on doamin area and resolution
|
|
144
80
|
res = int(step * grid_resolution) // resolution
|
|
@@ -148,37 +84,39 @@ def _calculate_domain_shape(step: float, resolution: int, grid_resolution=110, n
|
|
|
148
84
|
return rectify_domain_size(res, nest_ratio=nest_ratio)
|
|
149
85
|
|
|
150
86
|
|
|
151
|
-
def calculate_domain_shape(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
87
|
+
def calculate_domain_shape(
|
|
88
|
+
lon_step: float, lat_step: float, resolution: int, grid_resolution=110, nest_ratio=1
|
|
89
|
+
) -> tuple[int, int]:
|
|
90
|
+
"""
|
|
91
|
+
Calculate domain shape based on its area.
|
|
92
|
+
|
|
93
|
+
:param lon_step: Length in X direction. Unit: degree.
|
|
94
|
+
:type lon_step: float
|
|
95
|
+
:param lat_step: Length in Y direction. Unit: degree.
|
|
96
|
+
:type lat_step: float
|
|
97
|
+
:param resolution: Resolution of domain. Unit: km.
|
|
98
|
+
:type resolution: int
|
|
99
|
+
:param grid_resolution: Resolution of grid. Unit: km. Defaults to 110.
|
|
100
|
+
:param nest_ratio: The nesting ratio relative to the domain’s parent.
|
|
101
|
+
:return: ``(length of x, length of y)``.
|
|
102
|
+
:rtype: tuple[int, int]
|
|
163
103
|
"""
|
|
164
|
-
|
|
165
104
|
return (
|
|
166
|
-
_calculate_domain_shape(
|
|
167
|
-
|
|
168
|
-
_calculate_domain_shape(
|
|
169
|
-
lat_step, resolution, grid_resolution=grid_resolution, nest_ratio=nest_ratio)
|
|
105
|
+
_calculate_domain_shape(lon_step, resolution, grid_resolution=grid_resolution, nest_ratio=nest_ratio),
|
|
106
|
+
_calculate_domain_shape(lat_step, resolution, grid_resolution=grid_resolution, nest_ratio=nest_ratio),
|
|
170
107
|
)
|
|
171
108
|
|
|
172
109
|
|
|
173
110
|
def _check_domain_shape(point_num: int, nest_ratio: int) -> bool:
|
|
174
|
-
"""
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
111
|
+
"""
|
|
112
|
+
Check domain shape.
|
|
113
|
+
|
|
114
|
+
:param point_num: Point number of one side.
|
|
115
|
+
:type point_num: int
|
|
116
|
+
:param nest_ratio: The nesting ratio relative to the domain’s parent.
|
|
117
|
+
:type nest_ratio: int
|
|
118
|
+
:return: True if pass check else False.
|
|
119
|
+
:rtype: bool
|
|
182
120
|
"""
|
|
183
121
|
if (point_num - 1) % nest_ratio != 0:
|
|
184
122
|
return False
|
|
@@ -187,76 +125,27 @@ def _check_domain_shape(point_num: int, nest_ratio: int) -> bool:
|
|
|
187
125
|
|
|
188
126
|
|
|
189
127
|
def check_domain_shape(x_point_num: int, y_point_num: int, nest_ratio: int) -> tuple[bool, bool]:
|
|
190
|
-
"""
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
128
|
+
"""
|
|
129
|
+
Check domain shape.
|
|
130
|
+
|
|
131
|
+
:param x_point_num: Point number of X side.
|
|
132
|
+
:type x_point_num: int
|
|
133
|
+
:param y_point_num: Point number of Y side.
|
|
134
|
+
:type y_point_num: int
|
|
135
|
+
:param nest_ratio: The nesting ratio relative to the domain's parent.
|
|
136
|
+
:type nest_ratio: int
|
|
137
|
+
:return: Tuple of bool. True if pass check else False
|
|
138
|
+
:rtype: tuple[bool, bool]
|
|
199
139
|
"""
|
|
200
140
|
return (
|
|
201
141
|
_check_domain_shape(x_point_num, nest_ratio=nest_ratio),
|
|
202
|
-
_check_domain_shape(y_point_num, nest_ratio=nest_ratio)
|
|
142
|
+
_check_domain_shape(y_point_num, nest_ratio=nest_ratio),
|
|
203
143
|
)
|
|
204
144
|
|
|
205
145
|
|
|
206
|
-
|
|
207
|
-
""
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
if status.returncode != 0:
|
|
213
|
-
# print command
|
|
214
|
-
command = status.args
|
|
215
|
-
logger.error(f"Failed to exec command: {command}")
|
|
216
|
-
|
|
217
|
-
# print log
|
|
218
|
-
logger.error(f"====== stdout ======")
|
|
219
|
-
logger.error(status.stdout.decode())
|
|
220
|
-
logger.error(f"====== ====== ======")
|
|
221
|
-
logger.error(f"====== stderr ======")
|
|
222
|
-
logger.error(status.stderr.decode())
|
|
223
|
-
logger.error(f"====== ====== ======")
|
|
224
|
-
|
|
225
|
-
# raise error
|
|
226
|
-
raise RuntimeError(f"Failed to exec command: '{command}'. Please check the log above.")
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
def call_subprocess(command: list[str], work_path: Optional[str] = None, print_output=False):
|
|
230
|
-
"""
|
|
231
|
-
Execute the given command in system shell.
|
|
232
|
-
|
|
233
|
-
:param command: A list contains the command and parameters to be executed.
|
|
234
|
-
:type command: list
|
|
235
|
-
:param work_path: The work path of the command.
|
|
236
|
-
If None, works in current directory.
|
|
237
|
-
:type work_path: str | None
|
|
238
|
-
:param print_output: If print standard output and error in the logger.
|
|
239
|
-
:type print_output: bool
|
|
240
|
-
:return:
|
|
241
|
-
:rtype:
|
|
242
|
-
"""
|
|
243
|
-
if work_path is not None:
|
|
244
|
-
origin_path = getcwd()
|
|
245
|
-
chdir(work_path)
|
|
246
|
-
else:
|
|
247
|
-
origin_path = None
|
|
248
|
-
|
|
249
|
-
status = subprocess.run(' '.join(command), shell=True, capture_output=True)
|
|
250
|
-
|
|
251
|
-
if origin_path is not None:
|
|
252
|
-
chdir(origin_path)
|
|
253
|
-
|
|
254
|
-
check_subprocess_status(status)
|
|
255
|
-
|
|
256
|
-
if print_output:
|
|
257
|
-
logger.info(status.stdout.decode())
|
|
258
|
-
logger.warning(status.stderr.decode())
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
__all__ = ["logger", "check_path", "logger_add_file_handler", "calculate_domain_shape", "rectify_domain_size", "check_domain_shape",
|
|
262
|
-
"check_subprocess_status", "call_subprocess", "set_logger", "unify_logger_format"]
|
|
146
|
+
__all__ = [
|
|
147
|
+
"check_path",
|
|
148
|
+
"calculate_domain_shape",
|
|
149
|
+
"rectify_domain_size",
|
|
150
|
+
"check_domain_shape",
|
|
151
|
+
]
|
wrfrun/workspace/__init__.py
CHANGED
|
@@ -9,6 +9,7 @@ Submodules
|
|
|
9
9
|
|
|
10
10
|
================================= ===========================================================
|
|
11
11
|
:doc:`core </api/workspace.core>` Core functions of this submodule.
|
|
12
|
+
:doc:`palm </api/workspace.palm>` Functions to prepare workspace for PALM model.
|
|
12
13
|
:doc:`wrf </api/workspace.wrf>` Functions to prepare workspace for WPS/WRF model.
|
|
13
14
|
================================= ===========================================================
|
|
14
15
|
|
|
@@ -16,15 +17,16 @@ Workspace
|
|
|
16
17
|
*********
|
|
17
18
|
|
|
18
19
|
``workspace`` is a collection of several directories where ``wrfrun``, extensions and numerical model works.
|
|
19
|
-
These directories and their purpose are listed below.
|
|
20
|
+
These directories and their purpose are listed below. ``$ROOT`` is the root work path set in config file,
|
|
21
|
+
or ``$HOME/.config/wrfrun``.
|
|
20
22
|
|
|
21
23
|
=================================== ===========================================================
|
|
22
24
|
Director Path Purpose
|
|
23
25
|
=================================== ===========================================================
|
|
24
|
-
|
|
25
|
-
``$
|
|
26
|
-
``$
|
|
27
|
-
``$
|
|
26
|
+
``$ROOT`` Main work directory.
|
|
27
|
+
``$ROOT/tmp`` Store temporary files.
|
|
28
|
+
``$ROOT/replay`` Work directory for :doc:`replay </api/core.replay>`.
|
|
29
|
+
``$ROOT/workspace`` Work directory for numerical models.
|
|
28
30
|
=================================== ===========================================================
|
|
29
31
|
|
|
30
32
|
.. toctree::
|
|
@@ -32,6 +34,7 @@ Director Path Purpose
|
|
|
32
34
|
:hidden:
|
|
33
35
|
|
|
34
36
|
core <workspace.core>
|
|
37
|
+
palm <workspace.palm>
|
|
35
38
|
wrf <workspace.wrf>
|
|
36
39
|
"""
|
|
37
40
|
|
wrfrun/workspace/core.py
CHANGED
|
@@ -8,13 +8,16 @@ Core functions to prepare ``wrfrun`` workspace.
|
|
|
8
8
|
:toctree: generated/
|
|
9
9
|
|
|
10
10
|
prepare_workspace
|
|
11
|
+
check_workspace
|
|
11
12
|
"""
|
|
12
13
|
|
|
13
14
|
from os.path import exists
|
|
14
15
|
from shutil import rmtree
|
|
15
16
|
|
|
16
|
-
from wrfrun.core import
|
|
17
|
-
from wrfrun.
|
|
17
|
+
from wrfrun.core import WRFRUN
|
|
18
|
+
from wrfrun.log import check_path, logger
|
|
19
|
+
|
|
20
|
+
from .palm import prepare_palm_workspace
|
|
18
21
|
from .wrf import check_wrf_workspace, prepare_wrf_workspace
|
|
19
22
|
|
|
20
23
|
|
|
@@ -34,16 +37,17 @@ def prepare_workspace():
|
|
|
34
37
|
|
|
35
38
|
1. :doc:`WPS/WRF model </api/workspace.wrf>`
|
|
36
39
|
"""
|
|
37
|
-
WRFRUNConfig =
|
|
38
|
-
logger.info(f"Initialize main workspace.")
|
|
40
|
+
WRFRUNConfig = WRFRUN.config
|
|
39
41
|
|
|
40
42
|
wrfrun_temp_path = WRFRUNConfig.parse_resource_uri(WRFRUNConfig.WRFRUN_TEMP_PATH)
|
|
41
43
|
workspace_path = WRFRUNConfig.parse_resource_uri(WRFRUNConfig.WRFRUN_WORKSPACE_ROOT)
|
|
42
44
|
replay_work_path = WRFRUNConfig.parse_resource_uri(WRFRUNConfig.WRFRUN_WORKSPACE_REPLAY)
|
|
43
45
|
output_path = WRFRUNConfig.parse_resource_uri(WRFRUNConfig.WRFRUN_OUTPUT_PATH)
|
|
44
46
|
|
|
47
|
+
logger.info(f"Initialize main workspace at: {workspace_path}")
|
|
48
|
+
|
|
45
49
|
if exists(workspace_path):
|
|
46
|
-
logger.info(
|
|
50
|
+
logger.info("Remove old files in workspace.")
|
|
47
51
|
rmtree(workspace_path)
|
|
48
52
|
|
|
49
53
|
# check folder
|
|
@@ -51,12 +55,14 @@ def prepare_workspace():
|
|
|
51
55
|
check_path(replay_work_path)
|
|
52
56
|
check_path(output_path)
|
|
53
57
|
|
|
54
|
-
func_map = {
|
|
55
|
-
"wrf": prepare_wrf_workspace
|
|
56
|
-
}
|
|
58
|
+
func_map = {"wrf": prepare_wrf_workspace, "palm": prepare_palm_workspace}
|
|
57
59
|
model_configs = WRFRUNConfig["model"]
|
|
58
60
|
|
|
59
61
|
for model_name in model_configs:
|
|
62
|
+
if model_name not in func_map:
|
|
63
|
+
logger.warning(f"Function to prepare '{model_name}' workspace not found, workspace may be incomplete")
|
|
64
|
+
continue
|
|
65
|
+
|
|
60
66
|
func_map[model_name](model_configs[model_name])
|
|
61
67
|
|
|
62
68
|
|
|
@@ -67,7 +73,7 @@ def check_workspace() -> bool:
|
|
|
67
73
|
:return: ``True`` if workspace exists, ``False`` otherwise.
|
|
68
74
|
:rtype: bool
|
|
69
75
|
"""
|
|
70
|
-
WRFRUNConfig =
|
|
76
|
+
WRFRUNConfig = WRFRUN.config
|
|
71
77
|
|
|
72
78
|
wrfrun_temp_path = WRFRUNConfig.parse_resource_uri(WRFRUNConfig.WRFRUN_TEMP_PATH)
|
|
73
79
|
workspace_path = WRFRUNConfig.parse_resource_uri(WRFRUNConfig.WRFRUN_WORKSPACE_ROOT)
|
|
@@ -77,15 +83,17 @@ def check_workspace() -> bool:
|
|
|
77
83
|
flag = True
|
|
78
84
|
flag = flag & exists(wrfrun_temp_path) & exists(replay_work_path) & exists(output_path) & exists(workspace_path)
|
|
79
85
|
|
|
80
|
-
func_map = {
|
|
81
|
-
"wrf": check_wrf_workspace
|
|
82
|
-
}
|
|
86
|
+
func_map = {"wrf": check_wrf_workspace}
|
|
83
87
|
model_configs = WRFRUNConfig["model"]
|
|
84
88
|
|
|
85
89
|
for model_name in model_configs:
|
|
86
90
|
if model_name == "debug_level":
|
|
87
91
|
continue
|
|
88
92
|
|
|
93
|
+
if model_name not in func_map:
|
|
94
|
+
logger.info(f"Function to check '{model_name}' workspace not found, skip")
|
|
95
|
+
continue
|
|
96
|
+
|
|
89
97
|
flag = flag & func_map[model_name](model_configs[model_name])
|
|
90
98
|
|
|
91
99
|
return True
|
wrfrun/workspace/palm.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
wrfrun.workspace.palm
|
|
3
|
+
#####################
|
|
4
|
+
|
|
5
|
+
Functions to prepare workspace for PALM model.
|
|
6
|
+
|
|
7
|
+
.. autosummary::
|
|
8
|
+
:toctree: generated/
|
|
9
|
+
|
|
10
|
+
get_palm_workspace_path
|
|
11
|
+
prepare_palm_workspace
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from os import remove, symlink
|
|
15
|
+
from os.path import abspath, exists, islink
|
|
16
|
+
from shutil import copyfile, move, rmtree
|
|
17
|
+
from typing import Literal
|
|
18
|
+
|
|
19
|
+
from wrfrun.core import WRFRUN, WRFRunConfig
|
|
20
|
+
from wrfrun.log import logger
|
|
21
|
+
from wrfrun.utils import check_path
|
|
22
|
+
|
|
23
|
+
WORKSPACE_PALM = ""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _register_palm_workspace_uri(wrfrun_config: WRFRunConfig):
|
|
27
|
+
"""
|
|
28
|
+
This function doesn't register any URI.
|
|
29
|
+
|
|
30
|
+
This is a hook to initializes some global strings.
|
|
31
|
+
|
|
32
|
+
:param wrfrun_config: ``WRFRunConfig`` instance.
|
|
33
|
+
:type wrfrun_config: WRFRunConfig
|
|
34
|
+
"""
|
|
35
|
+
global WORKSPACE_PALM
|
|
36
|
+
|
|
37
|
+
WORKSPACE_PALM = f"{wrfrun_config.WRFRUN_WORKSPACE_MODEL}/PALM"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
WRFRUN.set_config_register_func(_register_palm_workspace_uri)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_palm_workspace_path(node: Literal["root", "job", "input", "output"] = "root") -> str:
|
|
44
|
+
"""
|
|
45
|
+
Get workspace of PALM model.
|
|
46
|
+
|
|
47
|
+
:param node: Which dir.
|
|
48
|
+
:type node: str
|
|
49
|
+
:return: Workspace path.
|
|
50
|
+
:rtype: str
|
|
51
|
+
"""
|
|
52
|
+
match node:
|
|
53
|
+
case "root":
|
|
54
|
+
return WORKSPACE_PALM
|
|
55
|
+
|
|
56
|
+
case "job":
|
|
57
|
+
return f"{WORKSPACE_PALM}/job"
|
|
58
|
+
|
|
59
|
+
case "input":
|
|
60
|
+
return f"{WORKSPACE_PALM}/job/wrfrun/INPUT"
|
|
61
|
+
|
|
62
|
+
case "output":
|
|
63
|
+
return f"{WORKSPACE_PALM}/job/wrfrun/OUTPUT"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def prepare_palm_workspace(model_config: dict):
|
|
67
|
+
"""
|
|
68
|
+
Initialize workspace for PALM model.
|
|
69
|
+
|
|
70
|
+
This function will check following paths,
|
|
71
|
+
and create them or delete old files inside:
|
|
72
|
+
|
|
73
|
+
1. ``$HOME/.config/wrfrun/model/PALM``
|
|
74
|
+
|
|
75
|
+
:param model_config: Model config.
|
|
76
|
+
:type model_config: dict
|
|
77
|
+
"""
|
|
78
|
+
logger.info("Initialize workspace for PALM.")
|
|
79
|
+
|
|
80
|
+
WRFRUNConfig = WRFRUN.config
|
|
81
|
+
|
|
82
|
+
palm_path = model_config["palm_path"]
|
|
83
|
+
config_id = model_config["config_identifier"]
|
|
84
|
+
config_file = model_config["config_file_path"]
|
|
85
|
+
|
|
86
|
+
palm_path = abspath(palm_path)
|
|
87
|
+
|
|
88
|
+
if not (exists(palm_path) and exists(f"{palm_path}/bin")):
|
|
89
|
+
logger.error("Your PALM path is wrong.")
|
|
90
|
+
raise FileNotFoundError("Your PALM path is wrong.")
|
|
91
|
+
|
|
92
|
+
palm_work_path = WRFRUNConfig.parse_resource_uri(WORKSPACE_PALM)
|
|
93
|
+
workspace_job_path = f"{palm_work_path}/job"
|
|
94
|
+
workspace_input_path = f"{workspace_job_path}/wrfrun/INPUT"
|
|
95
|
+
check_path(palm_work_path, workspace_job_path, workspace_input_path, force=True)
|
|
96
|
+
|
|
97
|
+
if not exists(f"{palm_path}/bin/palmrun"):
|
|
98
|
+
logger.error("Script 'palmrun' not found in your PALM dir.")
|
|
99
|
+
raise FileNotFoundError("Script 'palmrun' not found in your PALM dir.")
|
|
100
|
+
|
|
101
|
+
symlink(f"{palm_path}/bin/palmrun", f"{palm_work_path}/palmrun")
|
|
102
|
+
|
|
103
|
+
# we need some tricks to hack palm runtime directory.
|
|
104
|
+
job_path = f"{palm_path}/JOBS"
|
|
105
|
+
if exists(job_path):
|
|
106
|
+
if islink(job_path):
|
|
107
|
+
remove(job_path)
|
|
108
|
+
|
|
109
|
+
else:
|
|
110
|
+
new_job_path = f"{job_path}_wrfrun_bak"
|
|
111
|
+
if exists(new_job_path):
|
|
112
|
+
rmtree(new_job_path)
|
|
113
|
+
|
|
114
|
+
logger.warning(f"You have an existed PALM JOBS dir: {job_path}")
|
|
115
|
+
logger.warning(f"wrfrun has renamed it to: {new_job_path}")
|
|
116
|
+
logger.warning("If you have important files in it, please backup it to other positions,")
|
|
117
|
+
logger.warning("cause wrfrun may delete the renamed depository in the future.")
|
|
118
|
+
|
|
119
|
+
move(job_path, new_job_path)
|
|
120
|
+
|
|
121
|
+
symlink(workspace_job_path, job_path)
|
|
122
|
+
|
|
123
|
+
# PALM config file.
|
|
124
|
+
if config_file == "":
|
|
125
|
+
if not exists(f"{palm_path}/.palm.config.default"):
|
|
126
|
+
logger.error(f"Can't find the default config file: '{palm_path}/.palm.config.default', please provide one.")
|
|
127
|
+
raise FileNotFoundError(
|
|
128
|
+
f"Can't find the default config file: '{palm_path}/.palm.config.default', please provide one."
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
copyfile(f"{palm_path}/.palm.config.default", f"{palm_work_path}/.palm.config.{config_id}")
|
|
132
|
+
|
|
133
|
+
else:
|
|
134
|
+
symlink(abspath(config_file), f"{palm_work_path}/.palm.config.{config_id}")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
__all__ = ["get_palm_workspace_path", "prepare_palm_workspace"]
|