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/core/config.py
DELETED
|
@@ -1,923 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
wrfrun.core.config
|
|
3
|
-
##################
|
|
4
|
-
|
|
5
|
-
All classes in this module is used to manage various configurations of ``wrfrun`` and NWP model.
|
|
6
|
-
|
|
7
|
-
.. autosummary::
|
|
8
|
-
:toctree: generated/
|
|
9
|
-
|
|
10
|
-
WRFRunConfig
|
|
11
|
-
_WRFRunConstants
|
|
12
|
-
_WRFRunNamelist
|
|
13
|
-
_WRFRunResources
|
|
14
|
-
init_wrfrun_config
|
|
15
|
-
get_wrfrun_config
|
|
16
|
-
set_register_func
|
|
17
|
-
|
|
18
|
-
WRFRunConfig
|
|
19
|
-
************
|
|
20
|
-
|
|
21
|
-
A comprehensive class which provides interfaces to access various configurations and resources.
|
|
22
|
-
It inherits from three classes: :class:`_WRFRunResources`, :class:`_WRFRunConstants` and :class:`_WRFRunNamelist`.
|
|
23
|
-
Users can use the global variable ``WRFRUNConfig``, which is the instance of this class being created when users import ``wrfrun``.
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
# TODO:
|
|
27
|
-
# 1. NEW FEATURE: Allow reading work directory from config file.
|
|
28
|
-
# 2. The first one will be a break change, fix the following errors.
|
|
29
|
-
# 3. The structure of wrfrun may need to be changed again.
|
|
30
|
-
|
|
31
|
-
import threading
|
|
32
|
-
from copy import deepcopy
|
|
33
|
-
from os import environ, makedirs
|
|
34
|
-
from os.path import abspath, dirname, exists
|
|
35
|
-
from shutil import copyfile
|
|
36
|
-
from sys import platform
|
|
37
|
-
from typing import Callable, Optional, Tuple, Union
|
|
38
|
-
|
|
39
|
-
import f90nml
|
|
40
|
-
import tomli
|
|
41
|
-
import tomli_w
|
|
42
|
-
|
|
43
|
-
from .error import ModelNameError, NamelistError, NamelistIDError, ResourceURIError, WRFRunContextError, ConfigError
|
|
44
|
-
from ..utils import logger
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
class _WRFRunResources:
|
|
48
|
-
"""
|
|
49
|
-
Manage resource files used by wrfrun components
|
|
50
|
-
"""
|
|
51
|
-
|
|
52
|
-
def __init__(self):
|
|
53
|
-
"""
|
|
54
|
-
This class manage resource files used by wrfrun components.
|
|
55
|
-
|
|
56
|
-
These resources include various configuration files from NWP as well as those provided by ``wrfrun`` itself.
|
|
57
|
-
Since their actual file paths may vary depending on the installation environment,
|
|
58
|
-
``wrfrun`` maps them using URIs to ensure consistent access regardless of the environment.
|
|
59
|
-
The URI always starts with the prefix string ``:WRFRUN_`` and ends with ``:``.
|
|
60
|
-
|
|
61
|
-
To register custom URIs, user can use :meth:`_WRFRunResources.register_resource_uri`,
|
|
62
|
-
which will check if the provided URI is valid.
|
|
63
|
-
|
|
64
|
-
To convert any possible URIs in a string, user can use :meth:`_WRFRunResources.parse_resource_uri`
|
|
65
|
-
|
|
66
|
-
For more information about how to use resource files, please see :class:`WRFRunConfig`,
|
|
67
|
-
which inherits this class.
|
|
68
|
-
"""
|
|
69
|
-
self._resource_namespace_db = {}
|
|
70
|
-
|
|
71
|
-
def check_resource_uri(self, unique_uri: str) -> bool:
|
|
72
|
-
"""
|
|
73
|
-
Check if the URI has been registered.
|
|
74
|
-
|
|
75
|
-
``wrfrun`` uses unique URIs to represent resource files. If you want to register a custom URI, you need to check if it's available.
|
|
76
|
-
|
|
77
|
-
:param unique_uri: Unique URI represents the resource.
|
|
78
|
-
:type unique_uri: str
|
|
79
|
-
:return: True or False.
|
|
80
|
-
:rtype: bool
|
|
81
|
-
"""
|
|
82
|
-
if unique_uri in self._resource_namespace_db:
|
|
83
|
-
return True
|
|
84
|
-
|
|
85
|
-
else:
|
|
86
|
-
return False
|
|
87
|
-
|
|
88
|
-
def register_resource_uri(self, unique_uri: str, res_space_path: str):
|
|
89
|
-
"""
|
|
90
|
-
Register a resource path with a URI. The URI should start with ``:WRFRUN_`` ,end with ``:`` and hasn't been registered yet,
|
|
91
|
-
otherwise an exception :class:`ResourceURIError` will be raised.
|
|
92
|
-
|
|
93
|
-
:param unique_uri: Unique URI represents the resource. It must start with ``:WRFRUN_`` and end with ``:``. For example, ``":WRFRUN_WORK_PATH:"``.
|
|
94
|
-
:type unique_uri: str
|
|
95
|
-
:param res_space_path: REAL absolute path of your resource path. For example, "$HOME/.config/wrfrun/res".
|
|
96
|
-
:type res_space_path: str
|
|
97
|
-
"""
|
|
98
|
-
if not (unique_uri.startswith(":WRFRUN_") and unique_uri.endswith(":")):
|
|
99
|
-
logger.error(f"Can't register resource URI: '{unique_uri}'. It should start with ':WRFRUN_' and end with ':'.")
|
|
100
|
-
raise ResourceURIError(f"Can't register resource URI: '{unique_uri}'. It should start with ':WRFRUN_' and end with ':'.")
|
|
101
|
-
|
|
102
|
-
if unique_uri in self._resource_namespace_db:
|
|
103
|
-
logger.error(f"Resource URI '{unique_uri}' exists.")
|
|
104
|
-
raise ResourceURIError(f"Resource URI '{unique_uri}' exists.")
|
|
105
|
-
|
|
106
|
-
logger.debug(f"Register URI '{unique_uri}' to '{res_space_path}'")
|
|
107
|
-
self._resource_namespace_db[unique_uri] = res_space_path
|
|
108
|
-
|
|
109
|
-
def parse_resource_uri(self, resource_path: str) -> str:
|
|
110
|
-
"""
|
|
111
|
-
Return the converted string by parsing the URI string in it.
|
|
112
|
-
Normal path will be returned with no change.
|
|
113
|
-
|
|
114
|
-
If the URI hasn't been registered, an exception :class:`ResourceURIError` will be raised.
|
|
115
|
-
|
|
116
|
-
:param resource_path: Resource path string which may contain URI string.
|
|
117
|
-
:type resource_path: str
|
|
118
|
-
:return: Real resource path.
|
|
119
|
-
:rtype: str
|
|
120
|
-
|
|
121
|
-
For example, you can get the real path of ``wrfrun`` workspace with this method:
|
|
122
|
-
|
|
123
|
-
>>> workspace_path = f"{WRFRUNConfig.WRFRUN_WORKSPACE_ROOT}/WPS" # ":WRFRUN_WORKSPACE_PATH:/WPS"
|
|
124
|
-
>>> real_path = WRFRUNConfig.parse_resource_uri(workspace_path) # should be a valid path like: "/home/syize/.config/wrfrun/workspace/WPS"
|
|
125
|
-
|
|
126
|
-
"""
|
|
127
|
-
if not resource_path.startswith(":WRFRUN_"):
|
|
128
|
-
return resource_path
|
|
129
|
-
|
|
130
|
-
res_namespace_string = resource_path.split(":")[1]
|
|
131
|
-
res_namespace_string = f":{res_namespace_string}:"
|
|
132
|
-
|
|
133
|
-
if res_namespace_string in self._resource_namespace_db:
|
|
134
|
-
resource_path = resource_path.replace(res_namespace_string, self._resource_namespace_db[res_namespace_string])
|
|
135
|
-
|
|
136
|
-
if not resource_path.startswith(":WRFRUN_"):
|
|
137
|
-
return resource_path
|
|
138
|
-
|
|
139
|
-
else:
|
|
140
|
-
return self.parse_resource_uri(resource_path)
|
|
141
|
-
|
|
142
|
-
else:
|
|
143
|
-
logger.error(f"Unknown resource URI: '{res_namespace_string}'")
|
|
144
|
-
raise ResourceURIError(f"Unknown resource URI: '{res_namespace_string}'")
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
class _WRFRunConstants:
|
|
148
|
-
"""
|
|
149
|
-
Define all variables that will be used by other components.
|
|
150
|
-
"""
|
|
151
|
-
|
|
152
|
-
def __init__(self, work_dir: str):
|
|
153
|
-
"""
|
|
154
|
-
Define all variables that will be used by other components.
|
|
155
|
-
|
|
156
|
-
These variables are related to ``wrfrun`` installation environments, configuration files and more.
|
|
157
|
-
They are defined either directly or mapped using URIs to ensure consistent access across all components.
|
|
158
|
-
|
|
159
|
-
:param work_dir: ``wrfrun`` work directory path.
|
|
160
|
-
:type work_dir: str
|
|
161
|
-
"""
|
|
162
|
-
# check system
|
|
163
|
-
if platform != "linux":
|
|
164
|
-
logger.debug(f"Not Linux system!")
|
|
165
|
-
|
|
166
|
-
if work_dir != "" or platform != "linux":
|
|
167
|
-
# set temporary dir path
|
|
168
|
-
self._WRFRUN_TEMP_PATH = abspath(f"{work_dir}/tmp")
|
|
169
|
-
self._WRFRUN_HOME_PATH = abspath(work_dir)
|
|
170
|
-
|
|
171
|
-
else:
|
|
172
|
-
# the path we may need to store temp files,
|
|
173
|
-
# don't worry, it will be deleted once the system reboots
|
|
174
|
-
self._WRFRUN_TEMP_PATH = "/tmp/wrfrun"
|
|
175
|
-
user_home_path = f"{environ['HOME']}"
|
|
176
|
-
|
|
177
|
-
# WRF may need a large disk space to store output, we can't run wrf in /tmp,
|
|
178
|
-
# so we will create a folder in $HOME/.config to run wrf.
|
|
179
|
-
# we need to check if we're running as a root user
|
|
180
|
-
if user_home_path in ["/", "/root", ""]:
|
|
181
|
-
logger.warning(f"User's home path is '{user_home_path}', which means you are running this program as a root user")
|
|
182
|
-
logger.warning("It's not recommended to use wrfrun as a root user")
|
|
183
|
-
logger.warning("Set user_home_path as /root")
|
|
184
|
-
user_home_path = "/root"
|
|
185
|
-
|
|
186
|
-
self._WRFRUN_HOME_PATH = f"{user_home_path}/.config/wrfrun"
|
|
187
|
-
|
|
188
|
-
# workspace root path
|
|
189
|
-
self._WRFRUN_WORKSPACE_ROOT = f"{self._WRFRUN_HOME_PATH}/workspace"
|
|
190
|
-
self._WRFRUN_WORKSPACE_MODEL = f"{self._WRFRUN_WORKSPACE_ROOT}/model"
|
|
191
|
-
self._WRFRUN_WORKSPACE_REPLAY = f"{self._WRFRUN_WORKSPACE_ROOT}/replay"
|
|
192
|
-
|
|
193
|
-
# record WRF progress status
|
|
194
|
-
self._WRFRUN_WORK_STATUS = ""
|
|
195
|
-
|
|
196
|
-
# record context status
|
|
197
|
-
self._WRFRUN_CONTEXT_STATUS = False
|
|
198
|
-
|
|
199
|
-
self._WRFRUN_OUTPUT_PATH = ":WRFRUN_OUTPUT_PATH:"
|
|
200
|
-
self._WRFRUN_RESOURCE_PATH = ":WRFRUN_RESOURCE_PATH:"
|
|
201
|
-
|
|
202
|
-
self.IS_IN_REPLAY = False
|
|
203
|
-
|
|
204
|
-
self.IS_RECORDING = False
|
|
205
|
-
|
|
206
|
-
# in this mode, wrfrun will do all things except call the numerical model.
|
|
207
|
-
# all output rules will also not be executed.
|
|
208
|
-
self.FAKE_SIMULATION_MODE = False
|
|
209
|
-
|
|
210
|
-
def _get_uri_map(self) -> dict[str, str]:
|
|
211
|
-
"""
|
|
212
|
-
Return URIs and their values.
|
|
213
|
-
``wrfrun`` will use this to register uri when initialize config.
|
|
214
|
-
|
|
215
|
-
:return: A dict in which URIs are keys and their values are dictionary values.
|
|
216
|
-
:rtype: dict
|
|
217
|
-
"""
|
|
218
|
-
return {
|
|
219
|
-
self.WRFRUN_TEMP_PATH: self._WRFRUN_TEMP_PATH,
|
|
220
|
-
self.WRFRUN_HOME_PATH: self._WRFRUN_HOME_PATH,
|
|
221
|
-
self.WRFRUN_WORKSPACE_ROOT: self._WRFRUN_WORKSPACE_ROOT,
|
|
222
|
-
self.WRFRUN_WORKSPACE_MODEL: self._WRFRUN_WORKSPACE_MODEL,
|
|
223
|
-
self.WRFRUN_WORKSPACE_REPLAY: self._WRFRUN_WORKSPACE_REPLAY,
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
@property
|
|
227
|
-
def WRFRUN_WORKSPACE_REPLAY(self) -> str:
|
|
228
|
-
"""
|
|
229
|
-
Path (URI) to store related files of ``wrfrun`` replay functionality.
|
|
230
|
-
|
|
231
|
-
:return: URI.
|
|
232
|
-
:rtype: str
|
|
233
|
-
"""
|
|
234
|
-
return ":WRFRUN_WORKSPACE_REPLAY:"
|
|
235
|
-
|
|
236
|
-
@property
|
|
237
|
-
def WRFRUN_TEMP_PATH(self) -> str:
|
|
238
|
-
"""
|
|
239
|
-
Path to store ``wrfrun`` temporary files.
|
|
240
|
-
|
|
241
|
-
:return: URI
|
|
242
|
-
:rtype: str
|
|
243
|
-
"""
|
|
244
|
-
return ":WRFRUN_TEMP_PATH:"
|
|
245
|
-
|
|
246
|
-
@property
|
|
247
|
-
def WRFRUN_HOME_PATH(self) -> str:
|
|
248
|
-
"""
|
|
249
|
-
Root path of all others directories. .
|
|
250
|
-
|
|
251
|
-
:return: URI
|
|
252
|
-
:rtype: str
|
|
253
|
-
"""
|
|
254
|
-
return ":WRFRUN_HOME_PATH:"
|
|
255
|
-
|
|
256
|
-
@property
|
|
257
|
-
def WRFRUN_WORKSPACE_ROOT(self) -> str:
|
|
258
|
-
"""
|
|
259
|
-
Path of the root workspace.
|
|
260
|
-
|
|
261
|
-
:return: URI
|
|
262
|
-
:rtype: str
|
|
263
|
-
"""
|
|
264
|
-
return ":WRFRUN_WORKSPACE_ROOT:"
|
|
265
|
-
|
|
266
|
-
@property
|
|
267
|
-
def WRFRUN_WORKSPACE_MODEL(self) -> str:
|
|
268
|
-
"""
|
|
269
|
-
Path of the model workspace, in which ``wrfrun`` runs numerical models.
|
|
270
|
-
|
|
271
|
-
:return: URI
|
|
272
|
-
:rtype: str
|
|
273
|
-
"""
|
|
274
|
-
return ":WRFRUN_WORKSPACE_MODEL:"
|
|
275
|
-
|
|
276
|
-
@property
|
|
277
|
-
def WRFRUN_WORK_STATUS(self) -> str:
|
|
278
|
-
"""
|
|
279
|
-
``wrfrun`` work status.
|
|
280
|
-
|
|
281
|
-
This attribute can be changed by ``Executable`` to reflect the current work progress of ``wrfrun``.
|
|
282
|
-
The returned string is the name of ``Executable``.
|
|
283
|
-
|
|
284
|
-
:return: A string reflect the current work progress.
|
|
285
|
-
:rtype: str
|
|
286
|
-
"""
|
|
287
|
-
return self._WRFRUN_WORK_STATUS
|
|
288
|
-
|
|
289
|
-
@WRFRUN_WORK_STATUS.setter
|
|
290
|
-
def WRFRUN_WORK_STATUS(self, value: str):
|
|
291
|
-
"""
|
|
292
|
-
Set ``wrfrun`` work status.
|
|
293
|
-
|
|
294
|
-
``wrfrun`` recommends ``Executable`` set the status string with their name,
|
|
295
|
-
so to avoid the possible conflicts with other ``Executable`` and the user can easily understand the current work progress.
|
|
296
|
-
|
|
297
|
-
:param value: A string represents the work status.
|
|
298
|
-
:type value: str
|
|
299
|
-
"""
|
|
300
|
-
if not isinstance(value, str):
|
|
301
|
-
value = str(value)
|
|
302
|
-
self._WRFRUN_WORK_STATUS = value
|
|
303
|
-
|
|
304
|
-
@property
|
|
305
|
-
def WRFRUN_OUTPUT_PATH(self) -> str:
|
|
306
|
-
"""
|
|
307
|
-
The root path to store all outputs of the ``wrfrun`` and NWP model.
|
|
308
|
-
|
|
309
|
-
:return: URI
|
|
310
|
-
:rtype: str
|
|
311
|
-
"""
|
|
312
|
-
return self._WRFRUN_OUTPUT_PATH
|
|
313
|
-
|
|
314
|
-
@property
|
|
315
|
-
def WRFRUN_RESOURCE_PATH(self) -> str:
|
|
316
|
-
"""
|
|
317
|
-
The root path of all ``wrfrun`` resource files.
|
|
318
|
-
|
|
319
|
-
:return: URI
|
|
320
|
-
:rtype: str
|
|
321
|
-
"""
|
|
322
|
-
return self._WRFRUN_RESOURCE_PATH
|
|
323
|
-
|
|
324
|
-
def check_wrfrun_context(self, error=False) -> bool:
|
|
325
|
-
"""
|
|
326
|
-
Check if in WRFRun context or not.
|
|
327
|
-
|
|
328
|
-
:param error: An exception :class:`WRFRunContextError` will be raised if ``error==True`` when we are not in WRFRun context.
|
|
329
|
-
:type error: bool
|
|
330
|
-
:return: True or False.
|
|
331
|
-
:rtype: bool
|
|
332
|
-
"""
|
|
333
|
-
if self._WRFRUN_CONTEXT_STATUS:
|
|
334
|
-
return self._WRFRUN_CONTEXT_STATUS
|
|
335
|
-
|
|
336
|
-
if not error:
|
|
337
|
-
logger.warning("You are using wrfrun without entering `WRFRun` context, which may cause some functions don't work.")
|
|
338
|
-
return self._WRFRUN_CONTEXT_STATUS
|
|
339
|
-
|
|
340
|
-
logger.error("You need to enter `WRFRun` context to use wrfrun.")
|
|
341
|
-
raise WRFRunContextError("You need to enter `WRFRun` context to use wrfrun.")
|
|
342
|
-
|
|
343
|
-
def set_wrfrun_context(self, status: bool):
|
|
344
|
-
"""
|
|
345
|
-
Change ``WRFRun`` context status to True or False.
|
|
346
|
-
|
|
347
|
-
:param status: ``True`` or ``False``.
|
|
348
|
-
:type status: bool
|
|
349
|
-
"""
|
|
350
|
-
self._WRFRUN_CONTEXT_STATUS = status
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
class _WRFRunNamelist:
|
|
354
|
-
"""
|
|
355
|
-
Manage all namelist configurations of NWP models.
|
|
356
|
-
"""
|
|
357
|
-
|
|
358
|
-
def __init__(self):
|
|
359
|
-
"""
|
|
360
|
-
Manage all namelist configurations of NWP models.
|
|
361
|
-
|
|
362
|
-
This class provides interfaces to read and write various namelist values of NWP model.
|
|
363
|
-
Namelist values are stored in a dictionary with a unique ``namelist_id`` to avoid being messed up with other namelist.
|
|
364
|
-
|
|
365
|
-
If you want to use a new ``namelist_id`` other than the defaults to store namelist,
|
|
366
|
-
you can register a new ``namelist_id`` with :meth:`_WRFRunNamelist.register_custom_namelist_id`.
|
|
367
|
-
"""
|
|
368
|
-
self._namelist_dict = {}
|
|
369
|
-
self._namelist_id_list = ("param", "geog_static_data", "wps", "wrf", "wrfda")
|
|
370
|
-
|
|
371
|
-
def register_custom_namelist_id(self, namelist_id: str) -> bool:
|
|
372
|
-
"""
|
|
373
|
-
Register a unique ``namelist_id`` so you can read, update and write namelist with it later.
|
|
374
|
-
|
|
375
|
-
:param namelist_id: A unique namelist id.
|
|
376
|
-
:type namelist_id: str
|
|
377
|
-
:return: True if register successfully, else False.
|
|
378
|
-
:rtype: bool
|
|
379
|
-
"""
|
|
380
|
-
if namelist_id in self._namelist_id_list:
|
|
381
|
-
return False
|
|
382
|
-
|
|
383
|
-
else:
|
|
384
|
-
self._namelist_id_list += (namelist_id,)
|
|
385
|
-
return True
|
|
386
|
-
|
|
387
|
-
def unregister_custom_namelist_id(self, namelist_id: str):
|
|
388
|
-
"""
|
|
389
|
-
Unregister a ``namelist_id``.
|
|
390
|
-
If unregister successfully, all values of this namelist will be deleted.
|
|
391
|
-
|
|
392
|
-
:param namelist_id: A unique namelist id.
|
|
393
|
-
:type namelist_id: str
|
|
394
|
-
"""
|
|
395
|
-
if namelist_id not in self._namelist_id_list:
|
|
396
|
-
return
|
|
397
|
-
|
|
398
|
-
self.delete_namelist(namelist_id)
|
|
399
|
-
self._namelist_id_list = tuple(set(self._namelist_id_list) - {namelist_id, })
|
|
400
|
-
|
|
401
|
-
def check_namelist_id(self, namelist_id: str) -> bool:
|
|
402
|
-
"""
|
|
403
|
-
Check if a ``namelist_id`` is registered.
|
|
404
|
-
|
|
405
|
-
:param namelist_id: A ``namelist_id``.
|
|
406
|
-
:type namelist_id: str
|
|
407
|
-
:return: True if the ``namelist_id`` is registered, else False.
|
|
408
|
-
:rtype: bool
|
|
409
|
-
"""
|
|
410
|
-
if namelist_id in self._namelist_id_list:
|
|
411
|
-
return True
|
|
412
|
-
else:
|
|
413
|
-
return False
|
|
414
|
-
|
|
415
|
-
def read_namelist(self, file_path: str, namelist_id: str):
|
|
416
|
-
"""
|
|
417
|
-
Read namelist values from a file and store them with the ``namelist_id``.
|
|
418
|
-
|
|
419
|
-
If ``wrfrun`` can't read the file, ``FileNotFoundError`` will be raised.
|
|
420
|
-
If ``namelist_id`` isn't registered, :class:`NamelistIDError` will be raised.
|
|
421
|
-
|
|
422
|
-
:param file_path: Namelist file path.
|
|
423
|
-
:type file_path: str
|
|
424
|
-
:param namelist_id: Registered ``namelist_id``.
|
|
425
|
-
:type namelist_id: str
|
|
426
|
-
"""
|
|
427
|
-
# check the file path
|
|
428
|
-
if not exists(file_path):
|
|
429
|
-
logger.error(f"File not found: {file_path}")
|
|
430
|
-
raise FileNotFoundError
|
|
431
|
-
|
|
432
|
-
if namelist_id not in self._namelist_id_list:
|
|
433
|
-
logger.error(f"Unknown namelist id: {namelist_id}, register it first.")
|
|
434
|
-
raise NamelistIDError(f"Unknown namelist id: {namelist_id}, register it first.")
|
|
435
|
-
|
|
436
|
-
self._namelist_dict[namelist_id] = f90nml.read(file_path).todict()
|
|
437
|
-
|
|
438
|
-
def write_namelist(self, save_path: str, namelist_id: str, overwrite=True):
|
|
439
|
-
"""
|
|
440
|
-
Write namelist values of a ``namelist_id`` to a file.
|
|
441
|
-
|
|
442
|
-
:param save_path: Target file path.
|
|
443
|
-
:type save_path: str
|
|
444
|
-
:param namelist_id: Registered ``namelist_id``.
|
|
445
|
-
:type namelist_id: str
|
|
446
|
-
:param overwrite: If overwrite the existed file.
|
|
447
|
-
:type overwrite: bool
|
|
448
|
-
"""
|
|
449
|
-
if namelist_id not in self._namelist_id_list:
|
|
450
|
-
logger.error(f"Unknown namelist id: {namelist_id}, register it first.")
|
|
451
|
-
raise NamelistIDError(f"Unknown namelist id: {namelist_id}, register it first.")
|
|
452
|
-
|
|
453
|
-
if namelist_id not in self._namelist_dict:
|
|
454
|
-
logger.error(f"Can't found custom namelist '{namelist_id}', maybe you forget to read it first")
|
|
455
|
-
raise NamelistError(f"Can't found custom namelist '{namelist_id}', maybe you forget to read it first")
|
|
456
|
-
|
|
457
|
-
f90nml.Namelist(self._namelist_dict[namelist_id]).write(save_path, force=overwrite)
|
|
458
|
-
|
|
459
|
-
def update_namelist(self, new_values: Union[str, dict], namelist_id: str):
|
|
460
|
-
"""
|
|
461
|
-
Update namelist values of a ``namelist_id``.
|
|
462
|
-
|
|
463
|
-
You can give the path of a whole namelist file or a file only contains values you want to change.
|
|
464
|
-
|
|
465
|
-
>>> namelist_file = "./namelist.wps"
|
|
466
|
-
>>> WRFRUNConfig.update_namelist(namelist_file, namelist_id="wps")
|
|
467
|
-
|
|
468
|
-
>>> namelist_file = "./namelist.wrf"
|
|
469
|
-
>>> WRFRUNConfig.update_namelist(namelist_file, namelist_id="wrf")
|
|
470
|
-
|
|
471
|
-
You can also give a dict contains values you want to change.
|
|
472
|
-
|
|
473
|
-
>>> namelist_values = {"ungrib": {"prefix": "./output/FILE"}}
|
|
474
|
-
>>> WRFRUNConfig.update_namelist(namelist_values, namelist_id="wps")
|
|
475
|
-
|
|
476
|
-
>>> namelist_values = {"time_control": {"debug_level": 100}}
|
|
477
|
-
>>> WRFRUNConfig.update_namelist(namelist_values, namelist_id="wrf")
|
|
478
|
-
|
|
479
|
-
:param new_values: The path of a namelist file, or a dict contains namelist values.
|
|
480
|
-
:type new_values: str | dict
|
|
481
|
-
:param namelist_id: Registered ``namelist_id``.
|
|
482
|
-
:type namelist_id: str
|
|
483
|
-
"""
|
|
484
|
-
if namelist_id not in self._namelist_id_list:
|
|
485
|
-
logger.error(f"Unknown namelist id: {namelist_id}, register it first.")
|
|
486
|
-
raise NamelistIDError(f"Unknown namelist id: {namelist_id}, register it first.")
|
|
487
|
-
|
|
488
|
-
elif namelist_id not in self._namelist_dict:
|
|
489
|
-
self._namelist_dict[namelist_id] = new_values
|
|
490
|
-
return
|
|
491
|
-
|
|
492
|
-
else:
|
|
493
|
-
reference = self._namelist_dict[namelist_id]
|
|
494
|
-
|
|
495
|
-
if isinstance(new_values, str):
|
|
496
|
-
if not exists(new_values):
|
|
497
|
-
logger.error(f"File not found: {new_values}")
|
|
498
|
-
raise FileNotFoundError(f"File not found: {new_values}")
|
|
499
|
-
new_values = f90nml.read(new_values).todict()
|
|
500
|
-
|
|
501
|
-
for key in new_values:
|
|
502
|
-
if key in reference:
|
|
503
|
-
reference[key].update(new_values[key])
|
|
504
|
-
else:
|
|
505
|
-
reference[key] = new_values[key]
|
|
506
|
-
|
|
507
|
-
def get_namelist(self, namelist_id: str) -> dict:
|
|
508
|
-
"""
|
|
509
|
-
Get namelist values of a ``namelist_id``.
|
|
510
|
-
|
|
511
|
-
:param namelist_id: Registered ``namelist_id``.
|
|
512
|
-
:type namelist_id: str
|
|
513
|
-
:return: A dictionary which contains namelist values.
|
|
514
|
-
:rtype: dict
|
|
515
|
-
"""
|
|
516
|
-
if namelist_id not in self._namelist_id_list:
|
|
517
|
-
logger.error(f"Unknown namelist id: {namelist_id}, register it first.")
|
|
518
|
-
raise NamelistIDError(f"Unknown namelist id: {namelist_id}, register it first.")
|
|
519
|
-
elif namelist_id not in self._namelist_dict:
|
|
520
|
-
logger.error(f"Can't found custom namelist '{namelist_id}', maybe you forget to read it first")
|
|
521
|
-
raise NamelistError(f"Can't found custom namelist '{namelist_id}', maybe you forget to read it first")
|
|
522
|
-
else:
|
|
523
|
-
return deepcopy(self._namelist_dict[namelist_id])
|
|
524
|
-
|
|
525
|
-
def delete_namelist(self, namelist_id: str):
|
|
526
|
-
"""
|
|
527
|
-
Delete namelist values of a ``namelist_id``.
|
|
528
|
-
|
|
529
|
-
:param namelist_id: Registered ``namelist_id``.
|
|
530
|
-
:type namelist_id: str
|
|
531
|
-
"""
|
|
532
|
-
if namelist_id not in self._namelist_id_list:
|
|
533
|
-
logger.error(f"Unknown namelist id: {namelist_id}, register it first.")
|
|
534
|
-
raise ValueError(f"Unknown namelist id: {namelist_id}, register it first.")
|
|
535
|
-
|
|
536
|
-
if namelist_id not in self._namelist_dict:
|
|
537
|
-
return
|
|
538
|
-
|
|
539
|
-
self._namelist_dict.pop(namelist_id)
|
|
540
|
-
|
|
541
|
-
def check_namelist(self, namelist_id: str) -> bool:
|
|
542
|
-
"""
|
|
543
|
-
Check if a namelist has been registered and loaded.
|
|
544
|
-
|
|
545
|
-
:param namelist_id: Registered ``namelist_id``.
|
|
546
|
-
:type namelist_id: str
|
|
547
|
-
:return: ``True`` if it is registered and loaded, else ``False``.
|
|
548
|
-
:rtype: bool
|
|
549
|
-
"""
|
|
550
|
-
if namelist_id in self._namelist_id_list and namelist_id in self._namelist_dict:
|
|
551
|
-
return True
|
|
552
|
-
|
|
553
|
-
else:
|
|
554
|
-
return False
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
_URI_REGISTER_FUNC_LIST: list[Callable[["WRFRunConfig"], None]] = []
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
class WRFRunConfig(_WRFRunConstants, _WRFRunNamelist, _WRFRunResources):
|
|
561
|
-
"""
|
|
562
|
-
Class to manage wrfrun config, runtime constants, namelists and resource files.
|
|
563
|
-
"""
|
|
564
|
-
_instance = None
|
|
565
|
-
_initialized = False
|
|
566
|
-
_lock = threading.Lock()
|
|
567
|
-
|
|
568
|
-
def __init__(self, work_dir: str):
|
|
569
|
-
"""
|
|
570
|
-
This class provides various interfaces to access ``wrfrun``'s config, namelist values of NWP models,
|
|
571
|
-
runtime constants and resource files by inheriting from:
|
|
572
|
-
:class:`_WRFRunConstants`, :class:`_WRFRunNamelist` and :class:`_WRFRunResources`.
|
|
573
|
-
|
|
574
|
-
An instance of this class called ``WRFRUNConfig`` will be created after the user import ``wrfrun``,
|
|
575
|
-
and you should use the instance to access configs or other things instead of creating another instance.
|
|
576
|
-
|
|
577
|
-
:param work_dir: ``wrfrun`` work directory path.
|
|
578
|
-
:type work_dir: str
|
|
579
|
-
"""
|
|
580
|
-
if self._initialized:
|
|
581
|
-
return
|
|
582
|
-
|
|
583
|
-
with self._lock:
|
|
584
|
-
global _URI_REGISTER_FUNC_LIST
|
|
585
|
-
|
|
586
|
-
self._initialized = True
|
|
587
|
-
|
|
588
|
-
_WRFRunConstants.__init__(self, work_dir)
|
|
589
|
-
_WRFRunNamelist.__init__(self)
|
|
590
|
-
_WRFRunResources.__init__(self)
|
|
591
|
-
|
|
592
|
-
self._config = {}
|
|
593
|
-
|
|
594
|
-
self._config_template_file_path = None
|
|
595
|
-
|
|
596
|
-
self._register_wrfrun_uris()
|
|
597
|
-
|
|
598
|
-
for _fun in _URI_REGISTER_FUNC_LIST:
|
|
599
|
-
_fun(self)
|
|
600
|
-
|
|
601
|
-
_URI_REGISTER_FUNC_LIST = []
|
|
602
|
-
|
|
603
|
-
def __new__(cls, *args, **kwargs):
|
|
604
|
-
if cls._instance is None:
|
|
605
|
-
cls._instance = super().__new__(cls)
|
|
606
|
-
|
|
607
|
-
return cls._instance
|
|
608
|
-
|
|
609
|
-
def __getattribute__(self, item):
|
|
610
|
-
if item not in ("_initialized", "_instance", "_lock", "__class__"):
|
|
611
|
-
if not object.__getattribute__(self, "_initialized"):
|
|
612
|
-
logger.error(f"`WRFRUNConfig` hasn't been initialized.")
|
|
613
|
-
logger.error(f"Use `WRFRun` to load config automatically, or use function `init_wrfrun_config` to load config manually.")
|
|
614
|
-
raise ConfigError(f"`WRFRUNConfig` hasn't been initialized.")
|
|
615
|
-
|
|
616
|
-
return object.__getattribute__(self, item)
|
|
617
|
-
|
|
618
|
-
@classmethod
|
|
619
|
-
def from_config_file(cls, config_file: str) -> "WRFRunConfig":
|
|
620
|
-
"""
|
|
621
|
-
Read the config file and reinitialize.
|
|
622
|
-
|
|
623
|
-
:param config_file: Config file path.
|
|
624
|
-
:type config_file: str
|
|
625
|
-
:return: New instance
|
|
626
|
-
:rtype: WRFRunConfig
|
|
627
|
-
"""
|
|
628
|
-
cls._initialized = False
|
|
629
|
-
|
|
630
|
-
with open(config_file, "rb") as f:
|
|
631
|
-
config = tomli.load(f)
|
|
632
|
-
|
|
633
|
-
instance = cls(work_dir=config["work_dir"])
|
|
634
|
-
instance.load_wrfrun_config(config_file)
|
|
635
|
-
|
|
636
|
-
return instance
|
|
637
|
-
|
|
638
|
-
def _register_wrfrun_uris(self):
|
|
639
|
-
for key, value in self._get_uri_map().items():
|
|
640
|
-
self.register_resource_uri(key, value)
|
|
641
|
-
|
|
642
|
-
def set_config_template_path(self, file_path: str):
|
|
643
|
-
"""
|
|
644
|
-
Set file path of the config template file.
|
|
645
|
-
|
|
646
|
-
:param file_path: Template file path.
|
|
647
|
-
:type file_path: str
|
|
648
|
-
"""
|
|
649
|
-
self._config_template_file_path = file_path
|
|
650
|
-
|
|
651
|
-
def load_wrfrun_config(self, config_path: Optional[str] = None):
|
|
652
|
-
"""
|
|
653
|
-
Load ``wrfrun`` config from a config file.
|
|
654
|
-
|
|
655
|
-
If the config path is invalid, ``wrfrun`` will create a new config file at the same place,
|
|
656
|
-
and raise ``FileNotFoundError``.
|
|
657
|
-
|
|
658
|
-
If you don't give the config path, ``wrfrun`` will create a new config file under the current directory,
|
|
659
|
-
and read it.
|
|
660
|
-
|
|
661
|
-
:param config_path: TOML config file. Defaults to None.
|
|
662
|
-
:type config_path: str
|
|
663
|
-
"""
|
|
664
|
-
config_template_path = self.parse_resource_uri(self._config_template_file_path)
|
|
665
|
-
|
|
666
|
-
if config_path is not None:
|
|
667
|
-
if not exists(config_path):
|
|
668
|
-
logger.error(f"Config file doesn't exist, copy template config to {config_path}")
|
|
669
|
-
logger.error("Please modify it.")
|
|
670
|
-
|
|
671
|
-
if not exists(dirname(config_path)):
|
|
672
|
-
makedirs(dirname(config_path))
|
|
673
|
-
|
|
674
|
-
copyfile(config_template_path, config_path)
|
|
675
|
-
raise FileNotFoundError(config_path)
|
|
676
|
-
else:
|
|
677
|
-
logger.info("Read config template since you doesn't give config file")
|
|
678
|
-
logger.info("A new config file has been saved to './config.toml', you can change and use it latter")
|
|
679
|
-
|
|
680
|
-
copyfile(config_template_path, "./config.toml")
|
|
681
|
-
config_path = "./config.toml"
|
|
682
|
-
|
|
683
|
-
with open(config_path, "rb") as f:
|
|
684
|
-
self._config = tomli.load(f)
|
|
685
|
-
|
|
686
|
-
config_dir_path = abspath(dirname(config_path))
|
|
687
|
-
|
|
688
|
-
# merge model config.
|
|
689
|
-
keys_list = list(self._config["model"].keys())
|
|
690
|
-
for model_key in keys_list:
|
|
691
|
-
|
|
692
|
-
# skip the key that isn't model.
|
|
693
|
-
if model_key == "debug_level":
|
|
694
|
-
continue
|
|
695
|
-
|
|
696
|
-
if "use" not in self._config["model"][model_key]:
|
|
697
|
-
continue
|
|
698
|
-
|
|
699
|
-
if self._config["model"][model_key]["use"]:
|
|
700
|
-
include_file = self._config["model"][model_key]["include"]
|
|
701
|
-
if include_file[0] != "/":
|
|
702
|
-
include_file = f"{config_dir_path}/{include_file}"
|
|
703
|
-
|
|
704
|
-
with open(include_file, "rb") as f:
|
|
705
|
-
self._config["model"][model_key] = tomli.load(f)
|
|
706
|
-
|
|
707
|
-
else:
|
|
708
|
-
self._config["model"].pop(model_key)
|
|
709
|
-
|
|
710
|
-
# register URI for output directory.
|
|
711
|
-
output_path = abspath(self["output_path"])
|
|
712
|
-
self.register_resource_uri(self.WRFRUN_OUTPUT_PATH, output_path)
|
|
713
|
-
|
|
714
|
-
# some additional check
|
|
715
|
-
if self._config["input_data_path"] == "":
|
|
716
|
-
logger.warning("It seems you forget to set 'input_data_path', set it to 'data'.")
|
|
717
|
-
self._config["input_data_path"] = "data"
|
|
718
|
-
|
|
719
|
-
def save_wrfrun_config(self, save_path: str):
|
|
720
|
-
"""
|
|
721
|
-
Save ``wrfrun``'s config to a file.
|
|
722
|
-
|
|
723
|
-
:param save_path: Save path of the config.
|
|
724
|
-
:type save_path: str
|
|
725
|
-
"""
|
|
726
|
-
save_path = self.parse_resource_uri(save_path)
|
|
727
|
-
|
|
728
|
-
path_dir = dirname(save_path)
|
|
729
|
-
if not exists(path_dir):
|
|
730
|
-
makedirs(path_dir)
|
|
731
|
-
|
|
732
|
-
with open(save_path, "wb") as f:
|
|
733
|
-
tomli_w.dump(self._config, f)
|
|
734
|
-
|
|
735
|
-
def __getitem__(self, item: str):
|
|
736
|
-
"""
|
|
737
|
-
You can access ``wrfrun`` config like the way to access values in a dictionary.
|
|
738
|
-
|
|
739
|
-
For example
|
|
740
|
-
|
|
741
|
-
>>> model_config = WRFRUNConfig["model"] # get all model configs.
|
|
742
|
-
|
|
743
|
-
:param item: Keys.
|
|
744
|
-
:type item: str
|
|
745
|
-
"""
|
|
746
|
-
if len(self._config) == 0:
|
|
747
|
-
logger.error("Attempt to read value before load config")
|
|
748
|
-
raise RuntimeError("Attempt to read value before load config")
|
|
749
|
-
|
|
750
|
-
return deepcopy(self._config[item])
|
|
751
|
-
|
|
752
|
-
def __setitem__(self, key: str, value):
|
|
753
|
-
if key == "model":
|
|
754
|
-
logger.error(f"Use `update_model_config` to change model configurations.")
|
|
755
|
-
raise KeyError(f"Use `update_model_config` to change model configurations.")
|
|
756
|
-
|
|
757
|
-
if key in self._config:
|
|
758
|
-
self._config[key] = value
|
|
759
|
-
|
|
760
|
-
else:
|
|
761
|
-
logger.error(f"Can't find key '{key}' in your config.")
|
|
762
|
-
raise KeyError(f"Can't find key '{key}' in your config.")
|
|
763
|
-
|
|
764
|
-
def get_input_data_path(self) -> str:
|
|
765
|
-
"""
|
|
766
|
-
Get the path of directory in which stores the input data.
|
|
767
|
-
|
|
768
|
-
:return: Directory path.
|
|
769
|
-
:rtype: str
|
|
770
|
-
"""
|
|
771
|
-
return deepcopy(self["input_data_path"])
|
|
772
|
-
|
|
773
|
-
def get_model_config(self, model_name: str) -> dict:
|
|
774
|
-
"""
|
|
775
|
-
Get the config of a NWP model.
|
|
776
|
-
|
|
777
|
-
An exception :class:`ModelNameError` will be raised if the config can't be found.
|
|
778
|
-
|
|
779
|
-
:param model_name: Name of the model. For example, ``wrf``.
|
|
780
|
-
:type model_name: str
|
|
781
|
-
:return: A dictionary.
|
|
782
|
-
:rtype: dict
|
|
783
|
-
"""
|
|
784
|
-
if model_name not in self["model"]:
|
|
785
|
-
logger.error(f"Config of model '{model_name}' isn't found in your config file.")
|
|
786
|
-
raise ModelNameError(f"Config of model '{model_name}' isn't found in your config file.")
|
|
787
|
-
|
|
788
|
-
return deepcopy(self["model"][model_name])
|
|
789
|
-
|
|
790
|
-
def update_model_config(self, model_name: str, value: dict):
|
|
791
|
-
"""
|
|
792
|
-
Update the config of a NWP model.
|
|
793
|
-
|
|
794
|
-
An exception :class:`ModelNameError` will be raised if the config can't be found.
|
|
795
|
-
|
|
796
|
-
:param model_name: Name of the model. For example, ``wrf``.
|
|
797
|
-
:type model_name: str
|
|
798
|
-
:param value: Dictionary contains new values.
|
|
799
|
-
:type value: dict
|
|
800
|
-
"""
|
|
801
|
-
if model_name not in self["model"]:
|
|
802
|
-
logger.error(f"Config of model '{model_name}' isn't found in your config file.")
|
|
803
|
-
raise ModelNameError(f"Config of model '{model_name}' isn't found in your config file.")
|
|
804
|
-
|
|
805
|
-
self["model"][model_name] = self["model"][model_name] | value
|
|
806
|
-
|
|
807
|
-
def get_log_path(self) -> str:
|
|
808
|
-
"""
|
|
809
|
-
Get the directory path to save logs.
|
|
810
|
-
|
|
811
|
-
:return: A directory path.
|
|
812
|
-
:rtype: str
|
|
813
|
-
"""
|
|
814
|
-
return self["log_path"]
|
|
815
|
-
|
|
816
|
-
def get_socket_server_config(self) -> Tuple[str, int]:
|
|
817
|
-
"""
|
|
818
|
-
Get settings of the socket server.
|
|
819
|
-
|
|
820
|
-
:return: ("host", port)
|
|
821
|
-
:rtype: tuple
|
|
822
|
-
"""
|
|
823
|
-
return self["server_host"], self["server_port"]
|
|
824
|
-
|
|
825
|
-
def get_job_scheduler_config(self) -> dict:
|
|
826
|
-
"""
|
|
827
|
-
Get configs of job scheduler.
|
|
828
|
-
|
|
829
|
-
:return: A dict object.
|
|
830
|
-
:rtype: dict
|
|
831
|
-
"""
|
|
832
|
-
return deepcopy(self["job_scheduler"])
|
|
833
|
-
|
|
834
|
-
def get_core_num(self) -> int:
|
|
835
|
-
"""
|
|
836
|
-
Get the number of CPU cores to be used.
|
|
837
|
-
:return: Core numbers
|
|
838
|
-
:rtype: int
|
|
839
|
-
"""
|
|
840
|
-
return self["core_num"]
|
|
841
|
-
|
|
842
|
-
def write_namelist(self, save_path: str, namelist_id: str, overwrite=True):
|
|
843
|
-
"""
|
|
844
|
-
Write namelist values of a ``namelist_id`` to a file.
|
|
845
|
-
This method is overwritten to convert URIs in ``save_path`` first.
|
|
846
|
-
|
|
847
|
-
:param save_path: Target file path.
|
|
848
|
-
:type save_path: str
|
|
849
|
-
:param namelist_id: Registered ``namelist_id``.
|
|
850
|
-
:type namelist_id: str
|
|
851
|
-
:param overwrite: If overwrite the existed file.
|
|
852
|
-
:type overwrite: bool
|
|
853
|
-
"""
|
|
854
|
-
save_path = self.parse_resource_uri(save_path)
|
|
855
|
-
super().write_namelist(save_path, namelist_id, overwrite)
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
WRFRUNConfig = WRFRunConfig.__new__(WRFRunConfig)
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
def set_register_func(func: Callable[["WRFRunConfig"], None]):
|
|
862
|
-
"""
|
|
863
|
-
Set the function to register URIs.
|
|
864
|
-
|
|
865
|
-
These functions should accept ``WRFRUNConfig`` instance,
|
|
866
|
-
and will be called when initializing ``WRFRUNConfig``.
|
|
867
|
-
|
|
868
|
-
If ``WRFRUNConfig`` has been initialized, ``func`` will be called immediately.
|
|
869
|
-
|
|
870
|
-
Normal users should use :meth:`WRFRunConfig.register_resource_uri`,
|
|
871
|
-
because ``WRFRUNConfig`` should (and must) be initialized.
|
|
872
|
-
|
|
873
|
-
:param func: Functions to register URIs.
|
|
874
|
-
:type func: Callable
|
|
875
|
-
"""
|
|
876
|
-
global _URI_REGISTER_FUNC_LIST
|
|
877
|
-
|
|
878
|
-
if object.__getattribute__(WRFRUNConfig, "_initialized"):
|
|
879
|
-
func(WRFRUNConfig)
|
|
880
|
-
|
|
881
|
-
else:
|
|
882
|
-
if func not in _URI_REGISTER_FUNC_LIST:
|
|
883
|
-
_URI_REGISTER_FUNC_LIST.append(func)
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
def init_wrfrun_config(config_file: str) -> WRFRunConfig:
|
|
887
|
-
"""
|
|
888
|
-
Initialize ``WRFRUNConfig`` with the given config file.
|
|
889
|
-
|
|
890
|
-
:param config_file: Config file path.
|
|
891
|
-
:type config_file: str
|
|
892
|
-
:return: ``WRFRUNConfig`` instance.
|
|
893
|
-
:rtype: WRFRunConfig
|
|
894
|
-
"""
|
|
895
|
-
global WRFRUNConfig
|
|
896
|
-
|
|
897
|
-
logger.info(f"Initialize `WRFRUNConfig` with config: {config_file}")
|
|
898
|
-
|
|
899
|
-
WRFRUNConfig = WRFRunConfig.from_config_file(config_file)
|
|
900
|
-
|
|
901
|
-
return WRFRUNConfig
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
def get_wrfrun_config() -> WRFRunConfig:
|
|
905
|
-
"""
|
|
906
|
-
Get ``WRFRUNConfig`` instance.
|
|
907
|
-
|
|
908
|
-
An exception :class:`ConfigError` will be raised if you haven't initialized it.
|
|
909
|
-
|
|
910
|
-
:return: ``WRFRUNConfig`` instance.
|
|
911
|
-
:rtype: WRFRunConfig
|
|
912
|
-
"""
|
|
913
|
-
global WRFRUNConfig
|
|
914
|
-
|
|
915
|
-
if WRFRUNConfig is None:
|
|
916
|
-
logger.error(f"`WRFRUNConfig` hasn't been initialized.")
|
|
917
|
-
logger.error(f"Use `WRFRun` to load config automatically, or use function `init_wrfrun_config` to load config manually.")
|
|
918
|
-
raise ConfigError(f"`WRFRUNConfig` hasn't been initialized.")
|
|
919
|
-
|
|
920
|
-
return WRFRUNConfig
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
__all__ = ["WRFRunConfig", "WRFRUNConfig", "init_wrfrun_config", "get_wrfrun_config", "set_register_func"]
|