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.
- wrfrun/__init__.py +3 -0
- wrfrun/core/__init__.py +5 -0
- wrfrun/core/base.py +680 -0
- wrfrun/core/config.py +717 -0
- wrfrun/core/error.py +80 -0
- wrfrun/core/replay.py +113 -0
- wrfrun/core/server.py +212 -0
- wrfrun/data.py +418 -0
- wrfrun/extension/__init__.py +1 -0
- wrfrun/extension/littler/__init__.py +1 -0
- wrfrun/extension/littler/utils.py +599 -0
- wrfrun/extension/utils.py +66 -0
- wrfrun/model/__init__.py +7 -0
- wrfrun/model/base.py +14 -0
- wrfrun/model/plot.py +54 -0
- wrfrun/model/utils.py +34 -0
- wrfrun/model/wrf/__init__.py +6 -0
- wrfrun/model/wrf/_metgrid.py +71 -0
- wrfrun/model/wrf/_ndown.py +39 -0
- wrfrun/model/wrf/core.py +805 -0
- wrfrun/model/wrf/exec_wrap.py +101 -0
- wrfrun/model/wrf/geodata.py +301 -0
- wrfrun/model/wrf/namelist.py +377 -0
- wrfrun/model/wrf/scheme.py +311 -0
- wrfrun/model/wrf/vtable.py +65 -0
- wrfrun/pbs.py +86 -0
- wrfrun/plot/__init__.py +1 -0
- wrfrun/plot/wps.py +188 -0
- wrfrun/res/__init__.py +22 -0
- wrfrun/res/config.toml.template +136 -0
- wrfrun/res/extension/plotgrids.ncl +216 -0
- wrfrun/res/job_scheduler/pbs.template +6 -0
- wrfrun/res/job_scheduler/slurm.template +6 -0
- wrfrun/res/namelist/namelist.input.da_wrfvar.template +261 -0
- wrfrun/res/namelist/namelist.input.dfi.template +260 -0
- wrfrun/res/namelist/namelist.input.real.template +256 -0
- wrfrun/res/namelist/namelist.input.wrf.template +256 -0
- wrfrun/res/namelist/namelist.wps.template +44 -0
- wrfrun/res/namelist/parame.in.template +11 -0
- wrfrun/res/run.sh.template +16 -0
- wrfrun/run.py +264 -0
- wrfrun/utils.py +257 -0
- wrfrun/workspace.py +88 -0
- wrfrun-0.1.7.dist-info/METADATA +67 -0
- wrfrun-0.1.7.dist-info/RECORD +46 -0
- wrfrun-0.1.7.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
from datetime import datetime, timedelta
|
|
2
|
+
from os.path import exists
|
|
3
|
+
from typing import Union
|
|
4
|
+
|
|
5
|
+
from wrfrun.core import WRFRUNConfig
|
|
6
|
+
from wrfrun.res import NAMELIST_DFI, NAMELIST_WPS, NAMELIST_WRF, NAMELIST_WRFDA
|
|
7
|
+
from wrfrun.utils import logger
|
|
8
|
+
from .scheme import *
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _check_start_end_date(max_dom: int, start_date: Union[datetime, list[datetime]], end_date: Union[datetime, list[datetime]]) -> tuple[list[datetime], list[datetime]]:
|
|
12
|
+
"""
|
|
13
|
+
Format start date and end date.
|
|
14
|
+
|
|
15
|
+
:param max_dom: Domain number.
|
|
16
|
+
:type max_dom: int
|
|
17
|
+
:param start_date: Date list parsed from the config file.
|
|
18
|
+
:type start_date: datetime | list
|
|
19
|
+
:param end_date: Date list parsed from the config file.
|
|
20
|
+
:type end_date: datetime | list
|
|
21
|
+
:return: Formated date list.
|
|
22
|
+
:rtype: list
|
|
23
|
+
"""
|
|
24
|
+
if isinstance(start_date, datetime):
|
|
25
|
+
start_date = [start_date for _ in range(max_dom)]
|
|
26
|
+
elif isinstance(start_date, list):
|
|
27
|
+
if len(start_date) != max_dom:
|
|
28
|
+
logger.error(f"You have {max_dom} domains, but you only give {len(start_date)} dates for `start_date`.")
|
|
29
|
+
raise ValueError(f"You have {max_dom} domains, but you only give {len(start_date)} dates for `start_date`.")
|
|
30
|
+
|
|
31
|
+
if isinstance(end_date, datetime):
|
|
32
|
+
end_date = [end_date for _ in range(max_dom)]
|
|
33
|
+
elif isinstance(end_date, list):
|
|
34
|
+
if len(end_date) != max_dom:
|
|
35
|
+
logger.error(f"You have {max_dom} domains, but you only give {len(end_date)} dates for `start_date`.")
|
|
36
|
+
raise ValueError(f"You have {max_dom} domains, but you only give {len(end_date)} dates for `start_date`.")
|
|
37
|
+
|
|
38
|
+
return start_date, end_date
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def prepare_wps_namelist():
|
|
42
|
+
"""
|
|
43
|
+
This function read WPS template namelist and update its value based on the config file and user custom namelist.
|
|
44
|
+
|
|
45
|
+
"""
|
|
46
|
+
# prepare namelist
|
|
47
|
+
WRFRUNConfig.read_namelist(WRFRUNConfig.parse_resource_uri(NAMELIST_WPS), "wps")
|
|
48
|
+
wrf_config = WRFRUNConfig.get_model_config("wrf")
|
|
49
|
+
|
|
50
|
+
# get domain number
|
|
51
|
+
max_dom = wrf_config["domain"]["domain_num"]
|
|
52
|
+
|
|
53
|
+
# get start_date and end_date
|
|
54
|
+
start_date = wrf_config["time"]["start_date"]
|
|
55
|
+
end_date = wrf_config["time"]["end_date"]
|
|
56
|
+
|
|
57
|
+
start_date, end_date = _check_start_end_date(max_dom, start_date, end_date)
|
|
58
|
+
start_date = [x.strftime("%Y-%m-%d_%H:%M:%S") for x in start_date]
|
|
59
|
+
end_date = [x.strftime("%Y-%m-%d_%H:%M:%S") for x in end_date]
|
|
60
|
+
|
|
61
|
+
# get input data time interval
|
|
62
|
+
interval_seconds = wrf_config["time"]["input_data_interval"]
|
|
63
|
+
|
|
64
|
+
# generate update settings based on the config file
|
|
65
|
+
update_value = {
|
|
66
|
+
"share": {
|
|
67
|
+
"max_dom": max_dom,
|
|
68
|
+
"start_date": start_date,
|
|
69
|
+
"end_date": end_date,
|
|
70
|
+
"interval_seconds": interval_seconds
|
|
71
|
+
},
|
|
72
|
+
"geogrid": {
|
|
73
|
+
"parent_grid_ratio": wrf_config["domain"]["parent_grid_ratio"],
|
|
74
|
+
"i_parent_start": wrf_config["domain"]["i_parent_start"],
|
|
75
|
+
"j_parent_start": wrf_config["domain"]["j_parent_start"],
|
|
76
|
+
"e_we": wrf_config["domain"]["e_we"],
|
|
77
|
+
"e_sn": wrf_config["domain"]["e_sn"],
|
|
78
|
+
"dx": wrf_config["domain"]["dx"],
|
|
79
|
+
"dy": wrf_config["domain"]["dy"],
|
|
80
|
+
"ref_lat": wrf_config["domain"]["ref_lat"],
|
|
81
|
+
"ref_lon": wrf_config["domain"]["ref_lon"],
|
|
82
|
+
"stand_lon": wrf_config["domain"]["stand_lon"],
|
|
83
|
+
"geog_data_path": wrf_config["geog_data_path"]
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# # use loop to process config of map_proj
|
|
88
|
+
for key in wrf_config["domain"]["map_proj"]:
|
|
89
|
+
if key == "name":
|
|
90
|
+
update_value["geogrid"]["map_proj"] = wrf_config["domain"]["map_proj"][key]
|
|
91
|
+
else:
|
|
92
|
+
update_value["geogrid"][key] = wrf_config["domain"]["map_proj"][key]
|
|
93
|
+
|
|
94
|
+
# # update namelist
|
|
95
|
+
WRFRUNConfig.update_namelist(update_value, "wps")
|
|
96
|
+
|
|
97
|
+
# # update settings from custom namelist
|
|
98
|
+
if wrf_config["user_wps_namelist"] != "" and exists(wrf_config["user_wps_namelist"]):
|
|
99
|
+
WRFRUNConfig.update_namelist(wrf_config["user_wps_namelist"], "wps")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def prepare_wrf_namelist():
|
|
103
|
+
"""
|
|
104
|
+
This function read WRF template namelist and update its value based on the config file and user custom namelist.
|
|
105
|
+
|
|
106
|
+
"""
|
|
107
|
+
# read template namelist
|
|
108
|
+
WRFRUNConfig.read_namelist(WRFRUNConfig.parse_resource_uri(NAMELIST_WRF), "wrf")
|
|
109
|
+
|
|
110
|
+
# wrf config from config
|
|
111
|
+
wrf_config = WRFRUNConfig.get_model_config("wrf")
|
|
112
|
+
|
|
113
|
+
# get debug level
|
|
114
|
+
debug_level = WRFRUNConfig["model"]["debug_level"]
|
|
115
|
+
|
|
116
|
+
# get domain number, start_date and end_date
|
|
117
|
+
max_dom = wrf_config["domain"]["domain_num"]
|
|
118
|
+
start_date = wrf_config["time"]["start_date"]
|
|
119
|
+
end_date = wrf_config["time"]["end_date"]
|
|
120
|
+
|
|
121
|
+
start_date, end_date = _check_start_end_date(max_dom, start_date, end_date)
|
|
122
|
+
|
|
123
|
+
# get the time interval of input data and output data
|
|
124
|
+
input_data_interval = wrf_config["time"]["input_data_interval"]
|
|
125
|
+
output_data_interval = wrf_config["time"]["output_data_interval"]
|
|
126
|
+
|
|
127
|
+
# get restart settings
|
|
128
|
+
restart = wrf_config["restart_mode"]
|
|
129
|
+
restart_interval = wrf_config["time"]["restart_interval"]
|
|
130
|
+
if restart_interval < 0:
|
|
131
|
+
restart_interval = output_data_interval
|
|
132
|
+
|
|
133
|
+
# get the time step of integral
|
|
134
|
+
time_step = wrf_config["time"]["time_step"]
|
|
135
|
+
|
|
136
|
+
# calculate run hours
|
|
137
|
+
run_hours = end_date[0] - start_date[0]
|
|
138
|
+
run_hours = run_hours.days * 24 + run_hours.seconds // 3600
|
|
139
|
+
|
|
140
|
+
# calculate dx and dy for each domain
|
|
141
|
+
dx = wrf_config["domain"]["dx"]
|
|
142
|
+
dy = wrf_config["domain"]["dy"]
|
|
143
|
+
parent_grid_ratio = wrf_config["domain"]["parent_grid_ratio"]
|
|
144
|
+
dx = [dx // ratio for ratio in parent_grid_ratio]
|
|
145
|
+
dy = [dy // ratio for ratio in parent_grid_ratio]
|
|
146
|
+
|
|
147
|
+
# prepare update values
|
|
148
|
+
update_values = {
|
|
149
|
+
"time_control": {
|
|
150
|
+
# make sure run days, minutes, and seconds are 0
|
|
151
|
+
"run_days": 0,
|
|
152
|
+
"run_minutes": 0,
|
|
153
|
+
"run_seconds": 0,
|
|
154
|
+
"run_hours": run_hours,
|
|
155
|
+
"start_year": [_date.year for _date in start_date],
|
|
156
|
+
"start_month": [_date.month for _date in start_date],
|
|
157
|
+
"start_day": [_date.day for _date in start_date],
|
|
158
|
+
"start_hour": [_date.hour for _date in start_date],
|
|
159
|
+
"start_minute": [_date.minute for _date in start_date],
|
|
160
|
+
"start_second": [_date.second for _date in start_date],
|
|
161
|
+
"end_year": [_date.year for _date in end_date],
|
|
162
|
+
"end_month": [_date.month for _date in end_date],
|
|
163
|
+
"end_day": [_date.day for _date in end_date],
|
|
164
|
+
"end_hour": [_date.hour for _date in end_date],
|
|
165
|
+
"end_minute": [_date.minute for _date in end_date],
|
|
166
|
+
"end_second": [_date.second for _date in end_date],
|
|
167
|
+
"interval_seconds": input_data_interval,
|
|
168
|
+
"history_interval": [output_data_interval for _ in range(max_dom)],
|
|
169
|
+
"auxinput4_interval": [input_data_interval // 60 for _ in range(max_dom)],
|
|
170
|
+
"restart": restart,
|
|
171
|
+
"restart_interval": restart_interval,
|
|
172
|
+
"debug_level": debug_level,
|
|
173
|
+
},
|
|
174
|
+
"domains": {
|
|
175
|
+
"max_dom": max_dom,
|
|
176
|
+
"time_step": time_step,
|
|
177
|
+
"parent_grid_ratio": parent_grid_ratio,
|
|
178
|
+
"i_parent_start": wrf_config["domain"]["i_parent_start"],
|
|
179
|
+
"j_parent_start": wrf_config["domain"]["j_parent_start"],
|
|
180
|
+
"e_we": wrf_config["domain"]["e_we"],
|
|
181
|
+
"e_sn": wrf_config["domain"]["e_sn"],
|
|
182
|
+
"dx": dx,
|
|
183
|
+
"dy": dy,
|
|
184
|
+
|
|
185
|
+
},
|
|
186
|
+
"physics": {}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
# and we need to check the physics scheme option
|
|
190
|
+
long_wave_scheme = {
|
|
191
|
+
"ra_lw_physics": [SchemeLongWave.get_scheme_id(wrf_config["scheme"]["long_wave_scheme"]["name"]) for _ in range(max_dom)]
|
|
192
|
+
}
|
|
193
|
+
# # and other related options
|
|
194
|
+
long_wave_scheme.update(wrf_config["scheme"]["long_wave_scheme"]["option"])
|
|
195
|
+
# update
|
|
196
|
+
update_values["physics"].update(long_wave_scheme)
|
|
197
|
+
|
|
198
|
+
short_wave_scheme = {
|
|
199
|
+
"ra_sw_physics": [SchemeShortWave.get_scheme_id(wrf_config["scheme"]["short_wave_scheme"]["name"]) for _ in range(max_dom)]
|
|
200
|
+
}
|
|
201
|
+
# # and other related options
|
|
202
|
+
short_wave_scheme.update(
|
|
203
|
+
wrf_config["scheme"]["short_wave_scheme"]["option"])
|
|
204
|
+
# update
|
|
205
|
+
update_values["physics"].update(short_wave_scheme)
|
|
206
|
+
|
|
207
|
+
cumulus_scheme = {
|
|
208
|
+
"cu_physics": [SchemeCumulus.get_scheme_id(wrf_config["scheme"]["cumulus_scheme"]["name"]) for _ in range(max_dom)]
|
|
209
|
+
}
|
|
210
|
+
# # and other related options
|
|
211
|
+
cumulus_scheme.update(wrf_config["scheme"]["cumulus_scheme"]["option"])
|
|
212
|
+
# update
|
|
213
|
+
update_values["physics"].update(cumulus_scheme)
|
|
214
|
+
|
|
215
|
+
pbl_scheme = {
|
|
216
|
+
"bl_pbl_physics": [SchemePBL.get_scheme_id(wrf_config["scheme"]["pbl_scheme"]["name"]) for _ in range(max_dom)]
|
|
217
|
+
}
|
|
218
|
+
# # and other related options
|
|
219
|
+
pbl_scheme.update(wrf_config["scheme"]["pbl_scheme"]["option"])
|
|
220
|
+
# update
|
|
221
|
+
update_values["physics"].update(pbl_scheme)
|
|
222
|
+
|
|
223
|
+
land_surface_scheme = {
|
|
224
|
+
"sf_surface_physics": [SchemeLandSurfaceModel.get_scheme_id(wrf_config["scheme"]["land_surface_scheme"]["name"]) for _ in range(max_dom)]
|
|
225
|
+
}
|
|
226
|
+
# # and other related options
|
|
227
|
+
land_surface_scheme.update(
|
|
228
|
+
wrf_config["scheme"]["land_surface_scheme"]["option"])
|
|
229
|
+
# update
|
|
230
|
+
update_values["physics"].update(land_surface_scheme)
|
|
231
|
+
|
|
232
|
+
surface_layer_scheme = {
|
|
233
|
+
"sf_sfclay_physics": [SchemeSurfaceLayer.get_scheme_id(wrf_config["scheme"]["surface_layer_scheme"]["name"]) for _ in range(max_dom)]
|
|
234
|
+
}
|
|
235
|
+
# # and other related options
|
|
236
|
+
surface_layer_scheme.update(
|
|
237
|
+
wrf_config["scheme"]["surface_layer_scheme"]["option"])
|
|
238
|
+
# update
|
|
239
|
+
update_values["physics"].update(surface_layer_scheme)
|
|
240
|
+
|
|
241
|
+
# update namelist
|
|
242
|
+
WRFRUNConfig.update_namelist(update_values, "wrf")
|
|
243
|
+
|
|
244
|
+
# read user real namelist and update value
|
|
245
|
+
user_namelist_data = wrf_config["user_wrf_namelist"]
|
|
246
|
+
if user_namelist_data != "" and exists(user_namelist_data):
|
|
247
|
+
WRFRUNConfig.update_namelist(user_namelist_data, "wrf")
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def prepare_dfi_namelist():
|
|
251
|
+
"""Generate namelist data for DFI running
|
|
252
|
+
|
|
253
|
+
"""
|
|
254
|
+
# Read template namelist
|
|
255
|
+
WRFRUNConfig.read_namelist(WRFRUNConfig.parse_resource_uri(NAMELIST_DFI), "dfi")
|
|
256
|
+
|
|
257
|
+
wrf_config = WRFRUNConfig.get_model_config("wrf")
|
|
258
|
+
|
|
259
|
+
# Read start date and end date
|
|
260
|
+
start_date = wrf_config["time"]["start_date"]
|
|
261
|
+
start_date = start_date[0] if isinstance(start_date, list) else start_date
|
|
262
|
+
input_data_interval = wrf_config["time"]["input_data_interval"]
|
|
263
|
+
time_step = wrf_config["time"]["time_step"]
|
|
264
|
+
# calculate dfi date because:
|
|
265
|
+
# dfi start date is 1 hour earlier than start_date
|
|
266
|
+
# dfi end date is 30 minutes later than start_date
|
|
267
|
+
dfi_start_date = start_date - timedelta(hours=1)
|
|
268
|
+
dfi_end_date = start_date + timedelta(minutes=30)
|
|
269
|
+
|
|
270
|
+
# calculate dx and dy for each domain
|
|
271
|
+
dx = wrf_config["domain"]["dx"]
|
|
272
|
+
dy = wrf_config["domain"]["dy"]
|
|
273
|
+
parent_grid_ratio = wrf_config["domain"]["parent_grid_ratio"]
|
|
274
|
+
dx = [dx // ratio for ratio in parent_grid_ratio]
|
|
275
|
+
dy = [dy // ratio for ratio in parent_grid_ratio]
|
|
276
|
+
|
|
277
|
+
# Construct update value
|
|
278
|
+
update_value = {
|
|
279
|
+
"time_control": {
|
|
280
|
+
# make sure run days, hours, minutes, seconds is 0
|
|
281
|
+
"run_days": 0,
|
|
282
|
+
"run_hours": 0,
|
|
283
|
+
"run_minutes": 0,
|
|
284
|
+
"run_seconds": 0,
|
|
285
|
+
# start date and end date are same
|
|
286
|
+
"start_year": [start_date.year],
|
|
287
|
+
"start_month": [start_date.month],
|
|
288
|
+
"start_day": [start_date.day],
|
|
289
|
+
"start_hour": [start_date.hour],
|
|
290
|
+
"start_minute": [start_date.minute],
|
|
291
|
+
"start_second": [start_date.second],
|
|
292
|
+
"end_year": [start_date.year],
|
|
293
|
+
"end_month": [start_date.month],
|
|
294
|
+
"end_day": [start_date.day],
|
|
295
|
+
"end_hour": [start_date.hour],
|
|
296
|
+
"end_minute": [start_date.minute],
|
|
297
|
+
"end_second": [start_date.second],
|
|
298
|
+
"interval_seconds": input_data_interval,
|
|
299
|
+
"auxinput4_interval_s": [input_data_interval, ]
|
|
300
|
+
},
|
|
301
|
+
"domains": {
|
|
302
|
+
# make sure max_dom = 1
|
|
303
|
+
"time_step": time_step,
|
|
304
|
+
"max_dom": 1,
|
|
305
|
+
"time_step_dfi": time_step if time_step < 90 else 90,
|
|
306
|
+
"parent_grid_ratio": parent_grid_ratio,
|
|
307
|
+
"i_parent_start": wrf_config["domain"]["i_parent_start"],
|
|
308
|
+
"j_parent_start": wrf_config["domain"]["j_parent_start"],
|
|
309
|
+
"e_we": wrf_config["domain"]["e_we"],
|
|
310
|
+
"e_sn": wrf_config["domain"]["e_sn"],
|
|
311
|
+
"dx": dx,
|
|
312
|
+
"dy": dy,
|
|
313
|
+
},
|
|
314
|
+
"dfi_control": {
|
|
315
|
+
# set dfi date
|
|
316
|
+
"dfi_bckstop_year": dfi_start_date.year,
|
|
317
|
+
"dfi_bckstop_month": dfi_start_date.month,
|
|
318
|
+
"dfi_bckstop_day": dfi_start_date.day,
|
|
319
|
+
"dfi_bckstop_hour": dfi_start_date.hour,
|
|
320
|
+
"dfi_bckstop_minute": dfi_start_date.minute,
|
|
321
|
+
"dfi_bckstop_second": dfi_start_date.second,
|
|
322
|
+
"dfi_fwdstop_year": dfi_end_date.year,
|
|
323
|
+
"dfi_fwdstop_month": dfi_end_date.month,
|
|
324
|
+
"dfi_fwdstop_day": dfi_end_date.day,
|
|
325
|
+
"dfi_fwdstop_hour": dfi_end_date.hour,
|
|
326
|
+
"dfi_fwdstop_minute": dfi_end_date.minute,
|
|
327
|
+
"dfi_fwdstop_second": dfi_end_date.second,
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
# update namelist data
|
|
332
|
+
WRFRUNConfig.update_namelist(update_value, "dfi")
|
|
333
|
+
|
|
334
|
+
# read user wrf namelist and update value
|
|
335
|
+
user_namelist_data = wrf_config["user_wrf_namelist"]
|
|
336
|
+
if user_namelist_data != "" and exists(user_namelist_data):
|
|
337
|
+
WRFRUNConfig.update_namelist(user_namelist_data, "dfi")
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def prepare_wrfda_namelist():
|
|
341
|
+
"""Generate namelist for da_wrfvar.exe
|
|
342
|
+
|
|
343
|
+
"""
|
|
344
|
+
# read template namelist
|
|
345
|
+
WRFRUNConfig.read_namelist(WRFRUNConfig.parse_resource_uri(NAMELIST_WRFDA), "wrfda")
|
|
346
|
+
|
|
347
|
+
wrf_config = WRFRUNConfig.get_model_config("wrf")
|
|
348
|
+
|
|
349
|
+
# get wrf start date
|
|
350
|
+
start_date = wrf_config["time"]["start_date"]
|
|
351
|
+
start_date = start_date[0] if isinstance(start_date, list) else start_date
|
|
352
|
+
|
|
353
|
+
# generate update value
|
|
354
|
+
update_value = {
|
|
355
|
+
"wrfvar18": {
|
|
356
|
+
"analysis_date": start_date.strftime("%Y-%m-%d_%H:%M:%S.0000")
|
|
357
|
+
},
|
|
358
|
+
"wrfvar21": {
|
|
359
|
+
# one hour before wrf start date
|
|
360
|
+
"time_window_min": (start_date - timedelta(hours=1)).strftime("%Y-%m-%d_%H:%M:%S.0000")
|
|
361
|
+
},
|
|
362
|
+
"wrfvar22": {
|
|
363
|
+
# one hour after wrf start date
|
|
364
|
+
"time_window_max": (start_date + timedelta(hours=1)).strftime("%Y-%m-%d_%H:%M:%S.0000")
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
# update namelist
|
|
369
|
+
WRFRUNConfig.update_namelist(update_value, "wrfda")
|
|
370
|
+
|
|
371
|
+
# read user wrfda namelist and update value
|
|
372
|
+
user_namelist_data = wrf_config["user_wrfda_namelist"]
|
|
373
|
+
if user_namelist_data != "" and exists(user_namelist_data):
|
|
374
|
+
WRFRUNConfig.update_namelist(user_namelist_data, "wrfda")
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
__all__ = ["prepare_wrf_namelist", "prepare_wps_namelist", "prepare_wrfda_namelist", "prepare_dfi_namelist"]
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This file contains function to determine schemes for WRF
|
|
3
|
+
"""
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
from wrfrun.utils import logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass()
|
|
10
|
+
class SchemeLongWave:
|
|
11
|
+
"""
|
|
12
|
+
Long wave physics schemes.
|
|
13
|
+
"""
|
|
14
|
+
OFF = 0
|
|
15
|
+
RRTM = 1
|
|
16
|
+
CAM = 3
|
|
17
|
+
RRTMG = 4
|
|
18
|
+
NEW_GODDARD = 5
|
|
19
|
+
FLG = 7
|
|
20
|
+
RRTMG_K = 14
|
|
21
|
+
FAST_RRTMG = 24 # for GPU and MIC
|
|
22
|
+
HELD_SUAREZ = 31
|
|
23
|
+
GFDL = 99
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def get_scheme_id(cls, key: str = "rrtm"):
|
|
27
|
+
"""Get the corresponding integer label for long wave radiation scheme
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
key (str, optional): Name of long wave radiation scheme. Defaults to "rrtm".
|
|
31
|
+
"""
|
|
32
|
+
# Here is the map of scheme name and integer label in WRF
|
|
33
|
+
# Reference link: https://www2.mmm.ucar.edu/wrf/users/wrf_users_guide/build/html/physics.html#longwave-radiation-schemes
|
|
34
|
+
integer_label_map = {
|
|
35
|
+
"off": cls.OFF,
|
|
36
|
+
"rrtm": cls.RRTM,
|
|
37
|
+
"cam": cls.CAM,
|
|
38
|
+
"rrtmg": cls.RRTMG,
|
|
39
|
+
"new-goddard": cls.NEW_GODDARD,
|
|
40
|
+
"flg": cls.FLG,
|
|
41
|
+
"rrtmg-k": cls.RRTMG_K,
|
|
42
|
+
"fast-rrtmg": cls.FAST_RRTMG,
|
|
43
|
+
"held-suarez": cls.HELD_SUAREZ,
|
|
44
|
+
"gfdl": cls.GFDL
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# check if key is in map
|
|
48
|
+
if key not in integer_label_map:
|
|
49
|
+
logger.error(
|
|
50
|
+
f"Key error: {key}. Valid key: {list(integer_label_map.keys())}"
|
|
51
|
+
)
|
|
52
|
+
raise KeyError
|
|
53
|
+
|
|
54
|
+
return integer_label_map[key]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass()
|
|
58
|
+
class SchemeShortWave:
|
|
59
|
+
"""
|
|
60
|
+
Short wave physics schemes.
|
|
61
|
+
"""
|
|
62
|
+
OFF = 0
|
|
63
|
+
DUDHIA = 1
|
|
64
|
+
GODDARD = 2
|
|
65
|
+
CAM = 3
|
|
66
|
+
RRTMG = 4
|
|
67
|
+
NEW_GODDARD = 5
|
|
68
|
+
FLG = 7
|
|
69
|
+
RRTMG_K = 14
|
|
70
|
+
FAST_RRTMG = 24
|
|
71
|
+
EARTH_HELD_SUAREZ_FORCING = 31
|
|
72
|
+
GFDL = 99
|
|
73
|
+
|
|
74
|
+
@classmethod
|
|
75
|
+
def get_scheme_id(cls, key: str = "rrtmg"):
|
|
76
|
+
"""Get corresponding integer label for short wave radiation scheme
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
key (str, optional): Name of short wave radiation scheme. Defaults to "rrtmg".
|
|
80
|
+
"""
|
|
81
|
+
# Here is the map of scheme name and integer label in WRF
|
|
82
|
+
# Reference link: https://www2.mmm.ucar.edu/wrf/users/wrf_users_guide/build/html/physics.html#shortwave-radiation-schemes
|
|
83
|
+
integer_label_map = {
|
|
84
|
+
"off": cls.OFF,
|
|
85
|
+
"dudhia": cls.DUDHIA,
|
|
86
|
+
"goddard": cls.GODDARD,
|
|
87
|
+
"cam": cls.CAM,
|
|
88
|
+
"rrtmg": cls.RRTMG,
|
|
89
|
+
"new-goddard": cls.NEW_GODDARD,
|
|
90
|
+
"flg": cls.FLG,
|
|
91
|
+
"rrtmg-k": cls.RRTMG_K,
|
|
92
|
+
"fast-rrtmg": cls.FAST_RRTMG,
|
|
93
|
+
"earth-hs-force": cls.EARTH_HELD_SUAREZ_FORCING,
|
|
94
|
+
"gfdl": cls.GODDARD
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# check if key is in map
|
|
98
|
+
if key not in integer_label_map:
|
|
99
|
+
logger.error(
|
|
100
|
+
f"Key error: {key}. Valid key: {list(integer_label_map.keys())}"
|
|
101
|
+
)
|
|
102
|
+
raise KeyError
|
|
103
|
+
|
|
104
|
+
return integer_label_map[key]
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@dataclass()
|
|
108
|
+
class SchemeCumulus:
|
|
109
|
+
"""
|
|
110
|
+
Cumulus parameterization schemes.
|
|
111
|
+
"""
|
|
112
|
+
OFF = 0
|
|
113
|
+
KAIN_FRITSCH = 1
|
|
114
|
+
BMJ = 2
|
|
115
|
+
GRELL_FREITAS = 3
|
|
116
|
+
OLD_SAS = 4
|
|
117
|
+
GRELL_3 = 5
|
|
118
|
+
TIEDTKE = 6
|
|
119
|
+
ZHANG_MCFARLANE = 7
|
|
120
|
+
KF_CUP = 10
|
|
121
|
+
MULTI_SCALE_KF = 11
|
|
122
|
+
KIAPS_SAS = 14
|
|
123
|
+
NEW_TIEDTKE = 16
|
|
124
|
+
GRELL_DEVENYI = 93
|
|
125
|
+
NSAS = 96
|
|
126
|
+
OLD_KF = 99
|
|
127
|
+
|
|
128
|
+
@classmethod
|
|
129
|
+
def get_scheme_id(cls, key: str = "kf"):
|
|
130
|
+
"""Get corresponding integer label for cumulus parameterization scheme
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
key (str, optional): Name of cumulus parameterization scheme. Defaults to "kf".
|
|
134
|
+
"""
|
|
135
|
+
# Here is the map of scheme name and integer label in WRF
|
|
136
|
+
# Reference link: https://www2.mmm.ucar.edu/wrf/users/wrf_users_guide/build/html/physics.html#cumulus-parameterization
|
|
137
|
+
integer_label_map = {
|
|
138
|
+
"off": cls.OFF,
|
|
139
|
+
"kf": cls.KAIN_FRITSCH,
|
|
140
|
+
"bmj": cls.BMJ,
|
|
141
|
+
"gf": cls.GRELL_FREITAS,
|
|
142
|
+
"old-sas": cls.OLD_SAS,
|
|
143
|
+
"grell-3": cls.GRELL_3,
|
|
144
|
+
"tiedtke": cls.TIEDTKE,
|
|
145
|
+
"zmf": cls.ZHANG_MCFARLANE,
|
|
146
|
+
"kf-cup": cls.KF_CUP,
|
|
147
|
+
"mkf": cls.MULTI_SCALE_KF,
|
|
148
|
+
"kiaps-sas": cls.KIAPS_SAS,
|
|
149
|
+
"nt": cls.NEW_TIEDTKE,
|
|
150
|
+
"gd": cls.GRELL_DEVENYI,
|
|
151
|
+
"nsas": cls.NSAS,
|
|
152
|
+
"old-kf": cls.OLD_KF
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
# check if key is in map
|
|
156
|
+
if key not in integer_label_map:
|
|
157
|
+
logger.error(
|
|
158
|
+
f"Key error: {key}. Valid key: {list(integer_label_map.keys())}"
|
|
159
|
+
)
|
|
160
|
+
raise KeyError
|
|
161
|
+
|
|
162
|
+
return integer_label_map[key]
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@dataclass()
|
|
166
|
+
class SchemePBL:
|
|
167
|
+
"""
|
|
168
|
+
PBL physics schemes.
|
|
169
|
+
"""
|
|
170
|
+
OFF = 0
|
|
171
|
+
YSU = 1
|
|
172
|
+
MYJ = 2
|
|
173
|
+
QNSE_EDMF = 4
|
|
174
|
+
MYNN2 = 5
|
|
175
|
+
MYNN3 = 6
|
|
176
|
+
ACM2 = 7
|
|
177
|
+
BOULAC = 8
|
|
178
|
+
UW = 9
|
|
179
|
+
TEMF = 10
|
|
180
|
+
SHIN_HONG = 11
|
|
181
|
+
GBM = 12
|
|
182
|
+
EEPS = 16
|
|
183
|
+
KEPS = 17
|
|
184
|
+
MRF = 99
|
|
185
|
+
|
|
186
|
+
@classmethod
|
|
187
|
+
def get_scheme_id(cls, key: str = "ysu"):
|
|
188
|
+
"""Get corresponding integer label for PBL scheme
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
key (str, optional): Name of PBL scheme. Defaults to "ysu".
|
|
192
|
+
"""
|
|
193
|
+
# Here is the map of scheme name and integer label in WRF
|
|
194
|
+
# Reference link: https://www2.mmm.ucar.edu/wrf/users/wrf_users_guide/build/html/physics.html#pbl-scheme-options
|
|
195
|
+
integer_label_map = {
|
|
196
|
+
"off": cls.OFF,
|
|
197
|
+
"ysu": cls.YSU,
|
|
198
|
+
"myj": cls.MYJ,
|
|
199
|
+
"qe": cls.QNSE_EDMF,
|
|
200
|
+
"mynn2": cls.MYNN2,
|
|
201
|
+
"mynn3": cls.MYNN3,
|
|
202
|
+
"acm2": cls.ACM2,
|
|
203
|
+
"boulac": cls.BOULAC,
|
|
204
|
+
"uw": cls.UW,
|
|
205
|
+
"temf": cls.TEMF,
|
|
206
|
+
"shin-hong": cls.SHIN_HONG,
|
|
207
|
+
"gbm": cls.GBM,
|
|
208
|
+
"eeps": cls.EEPS,
|
|
209
|
+
"keps": cls.KEPS,
|
|
210
|
+
"mrf": cls.MRF
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
# check if key is in map
|
|
214
|
+
if key not in integer_label_map:
|
|
215
|
+
logger.error(
|
|
216
|
+
f"Key error: {key}. Valid key: {list(integer_label_map.keys())}"
|
|
217
|
+
)
|
|
218
|
+
raise KeyError
|
|
219
|
+
|
|
220
|
+
return integer_label_map[key]
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@dataclass()
|
|
224
|
+
class SchemeLandSurfaceModel:
|
|
225
|
+
"""
|
|
226
|
+
Land surface model physics schemes.
|
|
227
|
+
"""
|
|
228
|
+
OFF = 0
|
|
229
|
+
SLAB = 1
|
|
230
|
+
NOAH = 2
|
|
231
|
+
RUC = 3
|
|
232
|
+
NOAH_MP = 4
|
|
233
|
+
CLM4 = 5
|
|
234
|
+
PLEIM_XIU = 7
|
|
235
|
+
SSIB = 8
|
|
236
|
+
|
|
237
|
+
@classmethod
|
|
238
|
+
def get_scheme_id(cls, key: str = "noah"):
|
|
239
|
+
"""Get corresponding integer label for land surface scheme
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
key (str, optional): Name of land surface scheme. Defaults to "noah".
|
|
243
|
+
"""
|
|
244
|
+
# Here is the map of scheme name and integer label in WRF
|
|
245
|
+
# Reference link: https://www2.mmm.ucar.edu/wrf/users/wrf_users_guide/build/html/physics.html#lsm-scheme-details-and-references
|
|
246
|
+
integer_label_map = {
|
|
247
|
+
"off": cls.OFF,
|
|
248
|
+
"slab": cls.SLAB,
|
|
249
|
+
"noah": cls.NOAH,
|
|
250
|
+
"ruc": cls.RUC,
|
|
251
|
+
"noah-mp": cls.NOAH_MP,
|
|
252
|
+
"clm4": cls.CLM4,
|
|
253
|
+
"px": cls.PLEIM_XIU,
|
|
254
|
+
"ssib": cls.SSIB
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
# check if key is in map
|
|
258
|
+
if key not in integer_label_map:
|
|
259
|
+
logger.error(
|
|
260
|
+
f"Key error: {key}. Valid key: {list(integer_label_map.keys())}"
|
|
261
|
+
)
|
|
262
|
+
raise KeyError
|
|
263
|
+
|
|
264
|
+
return integer_label_map[key]
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
@dataclass()
|
|
268
|
+
class SchemeSurfaceLayer:
|
|
269
|
+
"""
|
|
270
|
+
Surface layer physics schemes.
|
|
271
|
+
"""
|
|
272
|
+
OFF = 0
|
|
273
|
+
MM5 = 1
|
|
274
|
+
MONIN_OBUKHOV = 2
|
|
275
|
+
QNSE = 4
|
|
276
|
+
MYNN = 5
|
|
277
|
+
PLEIM_XIU = 7
|
|
278
|
+
TEMF = 10
|
|
279
|
+
OLD_MM5 = 91
|
|
280
|
+
|
|
281
|
+
@classmethod
|
|
282
|
+
def get_scheme_id(cls, key: str = "mm5"):
|
|
283
|
+
"""Get corresponding integer label for surface layer scheme
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
key (str, optional): Name of surface layer scheme. Defaults to "mo".
|
|
287
|
+
"""
|
|
288
|
+
# Here is the map of scheme name and integer label in WRF
|
|
289
|
+
# Reference link: https://www2.mmm.ucar.edu/wrf/users/wrf_users_guide/build/html/namelist_variables.html
|
|
290
|
+
integer_label_map = {
|
|
291
|
+
"off": cls.OFF,
|
|
292
|
+
"mm5": cls.MM5,
|
|
293
|
+
"mo": cls.MONIN_OBUKHOV,
|
|
294
|
+
"qnse": cls.QNSE,
|
|
295
|
+
"mynn": cls.MYNN,
|
|
296
|
+
"px": cls.PLEIM_XIU,
|
|
297
|
+
"temf": cls.TEMF,
|
|
298
|
+
"old-mm5": cls.OLD_MM5
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
# check if key is in map
|
|
302
|
+
if key not in integer_label_map:
|
|
303
|
+
logger.error(
|
|
304
|
+
f"Key error: {key}. Valid key: {list(integer_label_map.keys())}"
|
|
305
|
+
)
|
|
306
|
+
raise KeyError
|
|
307
|
+
|
|
308
|
+
return integer_label_map[key]
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
__all__ = ["SchemeCumulus", "SchemeLandSurfaceModel", "SchemeLongWave", "SchemePBL", "SchemeShortWave", "SchemeSurfaceLayer"]
|