wrfrun 0.1.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. wrfrun/__init__.py +3 -0
  2. wrfrun/core/__init__.py +5 -0
  3. wrfrun/core/base.py +680 -0
  4. wrfrun/core/config.py +717 -0
  5. wrfrun/core/error.py +80 -0
  6. wrfrun/core/replay.py +113 -0
  7. wrfrun/core/server.py +212 -0
  8. wrfrun/data.py +418 -0
  9. wrfrun/extension/__init__.py +1 -0
  10. wrfrun/extension/littler/__init__.py +1 -0
  11. wrfrun/extension/littler/utils.py +599 -0
  12. wrfrun/extension/utils.py +66 -0
  13. wrfrun/model/__init__.py +7 -0
  14. wrfrun/model/base.py +14 -0
  15. wrfrun/model/plot.py +54 -0
  16. wrfrun/model/utils.py +34 -0
  17. wrfrun/model/wrf/__init__.py +6 -0
  18. wrfrun/model/wrf/_metgrid.py +71 -0
  19. wrfrun/model/wrf/_ndown.py +39 -0
  20. wrfrun/model/wrf/core.py +805 -0
  21. wrfrun/model/wrf/exec_wrap.py +101 -0
  22. wrfrun/model/wrf/geodata.py +301 -0
  23. wrfrun/model/wrf/namelist.py +377 -0
  24. wrfrun/model/wrf/scheme.py +311 -0
  25. wrfrun/model/wrf/vtable.py +65 -0
  26. wrfrun/pbs.py +86 -0
  27. wrfrun/plot/__init__.py +1 -0
  28. wrfrun/plot/wps.py +188 -0
  29. wrfrun/res/__init__.py +22 -0
  30. wrfrun/res/config.toml.template +136 -0
  31. wrfrun/res/extension/plotgrids.ncl +216 -0
  32. wrfrun/res/job_scheduler/pbs.template +6 -0
  33. wrfrun/res/job_scheduler/slurm.template +6 -0
  34. wrfrun/res/namelist/namelist.input.da_wrfvar.template +261 -0
  35. wrfrun/res/namelist/namelist.input.dfi.template +260 -0
  36. wrfrun/res/namelist/namelist.input.real.template +256 -0
  37. wrfrun/res/namelist/namelist.input.wrf.template +256 -0
  38. wrfrun/res/namelist/namelist.wps.template +44 -0
  39. wrfrun/res/namelist/parame.in.template +11 -0
  40. wrfrun/res/run.sh.template +16 -0
  41. wrfrun/run.py +264 -0
  42. wrfrun/utils.py +257 -0
  43. wrfrun/workspace.py +88 -0
  44. wrfrun-0.1.7.dist-info/METADATA +67 -0
  45. wrfrun-0.1.7.dist-info/RECORD +46 -0
  46. wrfrun-0.1.7.dist-info/WHEEL +4 -0
@@ -0,0 +1,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"]